edit sprites, game is solvable, solve needs to be offset?
@ -150,6 +150,12 @@ MonoBehaviour:
|
|||||||
X: 0
|
X: 0
|
||||||
Y: 0
|
Y: 0
|
||||||
RotationState: 0
|
RotationState: 0
|
||||||
|
SolutionRotation: 0
|
||||||
|
SolutionConnections: 0
|
||||||
|
Barriers: 0
|
||||||
DisconnectedSprite: {fileID: 21300000, guid: 6372bc43b5bc06643a17e0415a13a216, type: 3}
|
DisconnectedSprite: {fileID: 21300000, guid: 6372bc43b5bc06643a17e0415a13a216, type: 3}
|
||||||
ConnectedSprite: {fileID: 21300000, guid: 96539ea64dfad3e408f1e117e46c2775, type: 3}
|
ConnectedSprite: {fileID: 21300000, guid: 96539ea64dfad3e408f1e117e46c2775, type: 3}
|
||||||
locked: 0
|
locked: 0
|
||||||
|
isCenterTile: 0
|
||||||
|
baseConnections:
|
||||||
|
graphicRotationOffset: 0
|
||||||
|
@ -175,6 +175,14 @@ public class MapGenerator : MonoBehaviour
|
|||||||
|
|
||||||
tile.ApplyRotation(rotation);
|
tile.ApplyRotation(rotation);
|
||||||
tile.SolutionRotation = rotation;
|
tile.SolutionRotation = rotation;
|
||||||
|
// if (type == "TriCorner")
|
||||||
|
// // roate plus 90 degrees
|
||||||
|
// tile.SolutionRotation += 1;
|
||||||
|
// else if (type == "Corner")
|
||||||
|
// // rotate 3 times
|
||||||
|
// tile.SolutionRotation += 4;
|
||||||
|
// // tile.SolutionRotation %= 4;
|
||||||
|
|
||||||
tile.SolutionConnections = connections;
|
tile.SolutionConnections = connections;
|
||||||
tile.Barriers = barriers[x, y];
|
tile.Barriers = barriers[x, y];
|
||||||
|
|
||||||
|
@ -1,95 +1,167 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public class Solver : MonoBehaviour
|
public class Solver : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static bool CheckSolved(Tile[,] tiles, int width, int height)
|
public static bool CheckSolved(Tile[,] tiles, int width, int height)
|
||||||
{
|
{
|
||||||
bool solved = true;
|
// First check if all tiles are in their correct rotation
|
||||||
for (int x = 0; x < width; x++)
|
bool allCorrect = true;
|
||||||
for (int y = 0; y < height; y++)
|
|
||||||
tiles[x, y].SetConnectionState(IsTileCorrectlyConnected(tiles, x, y, width, height));
|
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
foreach (var tile in tiles)
|
||||||
if (!IsTileCorrectlyConnected(tiles, tile.X, tile.Y, width, height))
|
{
|
||||||
solved = false;
|
bool correct = tile.IsCorrectlyOriented();
|
||||||
|
tile.SetConnectionState(correct);
|
||||||
|
if (!correct) allCorrect = false;
|
||||||
|
}
|
||||||
|
|
||||||
return solved;
|
if (!allCorrect) return false;
|
||||||
|
|
||||||
|
// Then verify the entire network is connected
|
||||||
|
return IsNetworkFullyConnected(tiles, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsTileCorrectlyConnected(Tile[,] tiles, int x, int y, int width, int height)
|
private static bool IsNetworkFullyConnected(Tile[,] tiles, int width, int height)
|
||||||
{
|
{
|
||||||
|
// Find the center tile (or first active tile if center isn't marked)
|
||||||
|
int startX = width / 2;
|
||||||
|
int startY = height / 2;
|
||||||
|
|
||||||
|
// If center isn't marked, find first active tile
|
||||||
|
if (!tiles[startX, startY].isCenterTile)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
if (tiles[x, y].GetConnectionCount() > 0)
|
||||||
|
{
|
||||||
|
startX = x;
|
||||||
|
startY = y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool[,] visited = new bool[width, height];
|
||||||
|
FloodFill(tiles, visited, width, height, startX, startY);
|
||||||
|
|
||||||
|
// Check if all tiles with connections were visited
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
if (tiles[x, y].GetConnectionCount() > 0 && !visited[x, y])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FloodFill(Tile[,] tiles, bool[,] visited, int width, int height, int x, int y)
|
||||||
|
{
|
||||||
|
if (x < 0 || x >= width || y < 0 || y >= height || visited[x, y])
|
||||||
|
return;
|
||||||
|
|
||||||
|
visited[x, y] = true;
|
||||||
Tile tile = tiles[x, y];
|
Tile tile = tiles[x, y];
|
||||||
|
|
||||||
bool up = tile.HasConnection(0);
|
// Check all 4 directions
|
||||||
bool right = tile.HasConnection(1);
|
for (int dir = 0; dir < 4; dir++)
|
||||||
bool down = tile.HasConnection(2);
|
{
|
||||||
bool left = tile.HasConnection(3);
|
if (tile.HasConnection(dir))
|
||||||
|
{
|
||||||
|
int nx = x + directions[dir].x;
|
||||||
|
int ny = y + directions[dir].y;
|
||||||
|
|
||||||
|
// Wrap coordinates if needed (you'll need to pass wrapping info to solver)
|
||||||
|
if (nx < 0) nx = width - 1;
|
||||||
|
else if (nx >= width) nx = 0;
|
||||||
|
if (ny < 0) ny = height - 1;
|
||||||
|
else if (ny >= height) ny = 0;
|
||||||
|
|
||||||
bool connected = true;
|
// Only continue if neighbor has matching connection
|
||||||
|
if (tiles[nx, ny].HasConnection((dir + 2) % 4))
|
||||||
if (y < height - 1)
|
{
|
||||||
connected &= up == tiles[x, y + 1].HasConnection(2);
|
FloodFill(tiles, visited, width, height, nx, ny);
|
||||||
else if (up) connected = false;
|
}
|
||||||
|
}
|
||||||
if (x < width - 1)
|
}
|
||||||
connected &= right == tiles[x + 1, y].HasConnection(3);
|
|
||||||
else if (right) connected = false;
|
|
||||||
|
|
||||||
if (y > 0)
|
|
||||||
connected &= down == tiles[x, y - 1].HasConnection(0);
|
|
||||||
else if (down) connected = false;
|
|
||||||
|
|
||||||
if (x > 0)
|
|
||||||
connected &= left == tiles[x - 1, y].HasConnection(1);
|
|
||||||
else if (left) connected = false;
|
|
||||||
|
|
||||||
return connected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool AutoSolve(Tile[,] tiles, int width, int height)
|
public static bool AutoSolve(Tile[,] tiles, int width, int height)
|
||||||
{
|
{
|
||||||
bool changed;
|
// Create a list of tiles sorted by connection count (most constrained first)
|
||||||
int[,] possible = new int[width, height];
|
var tileList = tiles.Cast<Tile>()
|
||||||
|
.Where(t => !t.locked)
|
||||||
|
.OrderBy(t => t.GetConnectionCount())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// Initialize the possible rotation states of all tiles (4-bit binary)
|
return SolveRecursive(tiles, width, height, tileList, 0);
|
||||||
for (int x = 0; x < width; x++)
|
|
||||||
for (int y = 0; y < height; y++)
|
|
||||||
possible[x, y] = 0b1111;
|
|
||||||
|
|
||||||
int limit = width * height * 10; // Prevent infinite loops
|
|
||||||
do
|
|
||||||
{
|
|
||||||
changed = false;
|
|
||||||
|
|
||||||
for (int x = 0; x < width; x++)
|
|
||||||
for (int y = 0; y < height; y++)
|
|
||||||
{
|
|
||||||
int newMask = 0;
|
|
||||||
for (int rot = 0; rot < 4; rot++)
|
|
||||||
{
|
|
||||||
tiles[x, y].RotationState = rot;
|
|
||||||
if (IsTileCorrectlyConnected(tiles, x, y, width, height))
|
|
||||||
newMask |= (1 << rot);
|
|
||||||
}
|
|
||||||
if (newMask != possible[x, y])
|
|
||||||
{
|
|
||||||
possible[x, y] = newMask;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newMask == 0)
|
|
||||||
return false; // No valid direction
|
|
||||||
|
|
||||||
// If there is only one possible direction, lock it in
|
|
||||||
if ((newMask & (newMask - 1)) == 0)
|
|
||||||
{
|
|
||||||
int fixedRot = System.Convert.ToString(newMask, 2).LastIndexOf('1');
|
|
||||||
tiles[x, y].RotationState = fixedRot;
|
|
||||||
tiles[x, y].transform.rotation = Quaternion.Euler(0, 0, -90 * fixedRot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (changed && --limit > 0);
|
|
||||||
|
|
||||||
return CheckSolved(tiles, width, height);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static bool SolveRecursive(Tile[,] tiles, int width, int height, List<Tile> tileList, int index)
|
||||||
|
{
|
||||||
|
if (index >= tileList.Count)
|
||||||
|
return CheckSolved(tiles, width, height);
|
||||||
|
|
||||||
|
Tile tile = tileList[index];
|
||||||
|
|
||||||
|
// Try all possible rotations for this tile
|
||||||
|
for (int rot = 0; rot < 4; rot++)
|
||||||
|
{
|
||||||
|
tile.RotationState = rot;
|
||||||
|
tile.transform.rotation = Quaternion.Euler(0, 0, -90 * rot);
|
||||||
|
|
||||||
|
// Early pruning - check if this rotation causes immediate conflicts
|
||||||
|
if (!HasImmediateConflicts(tiles, width, height, tile))
|
||||||
|
{
|
||||||
|
if (SolveRecursive(tiles, width, height, tileList, index + 1))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasImmediateConflicts(Tile[,] tiles, int width, int height, Tile tile)
|
||||||
|
{
|
||||||
|
int x = tile.X;
|
||||||
|
int y = tile.Y;
|
||||||
|
|
||||||
|
// Check all 4 directions for conflicts
|
||||||
|
for (int dir = 0; dir < 4; dir++)
|
||||||
|
{
|
||||||
|
if (tile.HasConnection(dir))
|
||||||
|
{
|
||||||
|
int nx = x + directions[dir].x;
|
||||||
|
int ny = y + directions[dir].y;
|
||||||
|
|
||||||
|
// Wrap coordinates if needed
|
||||||
|
if (nx < 0) nx = width - 1;
|
||||||
|
else if (nx >= width) nx = 0;
|
||||||
|
if (ny < 0) ny = height - 1;
|
||||||
|
else if (ny >= height) ny = 0;
|
||||||
|
|
||||||
|
Tile neighbor = tiles[nx, ny];
|
||||||
|
int oppositeDir = (dir + 2) % 4;
|
||||||
|
|
||||||
|
// If neighbor is locked and doesn't have matching connection, conflict
|
||||||
|
if (neighbor.locked && !neighbor.HasConnection(oppositeDir))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directions: Right, Up, Left, Down (matches Net game encoding)
|
||||||
|
private static readonly Vector2Int[] directions = {
|
||||||
|
new Vector2Int(1, 0), // R
|
||||||
|
new Vector2Int(0, 1), // U
|
||||||
|
new Vector2Int(-1, 0), // L
|
||||||
|
new Vector2Int(0, -1) // D
|
||||||
|
};
|
||||||
|
}
|
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 154 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 151 B |
Before Width: | Height: | Size: 646 B After Width: | Height: | Size: 158 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 151 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 165 B |
Before Width: | Height: | Size: 634 B After Width: | Height: | Size: 165 B |