From 8adfdca9646bf2a58e3b7e7e84986cdceaf33bad Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 28 Mar 2025 14:40:55 -0400 Subject: [PATCH] edit sprites, game is solvable, solve needs to be offset? --- Assets/Prefabs/TriCorner.prefab | 6 + Assets/Scripts/MapGenerator.cs | 8 ++ Assets/Scripts/Solver.cs | 222 +++++++++++++++++++++----------- Assets/Sprites/LU-A.png | Bin 644 -> 154 bytes Assets/Sprites/LU.png | Bin 638 -> 151 bytes Assets/Sprites/LUR-A.png | Bin 646 -> 158 bytes Assets/Sprites/LUR.png | Bin 638 -> 151 bytes Assets/Sprites/Node-A.png | Bin 638 -> 165 bytes Assets/Sprites/Node.png | Bin 634 -> 165 bytes 9 files changed, 161 insertions(+), 75 deletions(-) diff --git a/Assets/Prefabs/TriCorner.prefab b/Assets/Prefabs/TriCorner.prefab index 3bcdd51f..7b7b6e24 100644 --- a/Assets/Prefabs/TriCorner.prefab +++ b/Assets/Prefabs/TriCorner.prefab @@ -150,6 +150,12 @@ MonoBehaviour: X: 0 Y: 0 RotationState: 0 + SolutionRotation: 0 + SolutionConnections: 0 + Barriers: 0 DisconnectedSprite: {fileID: 21300000, guid: 6372bc43b5bc06643a17e0415a13a216, type: 3} ConnectedSprite: {fileID: 21300000, guid: 96539ea64dfad3e408f1e117e46c2775, type: 3} locked: 0 + isCenterTile: 0 + baseConnections: + graphicRotationOffset: 0 diff --git a/Assets/Scripts/MapGenerator.cs b/Assets/Scripts/MapGenerator.cs index 5dfafce2..6e485c62 100644 --- a/Assets/Scripts/MapGenerator.cs +++ b/Assets/Scripts/MapGenerator.cs @@ -175,6 +175,14 @@ public class MapGenerator : MonoBehaviour tile.ApplyRotation(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.Barriers = barriers[x, y]; diff --git a/Assets/Scripts/Solver.cs b/Assets/Scripts/Solver.cs index 05c011a4..2f1bfaea 100644 --- a/Assets/Scripts/Solver.cs +++ b/Assets/Scripts/Solver.cs @@ -1,95 +1,167 @@ using UnityEngine; +using System.Linq; +using System.Collections.Generic; public class Solver : MonoBehaviour { public static bool CheckSolved(Tile[,] tiles, int width, int height) { - bool solved = true; - for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - tiles[x, y].SetConnectionState(IsTileCorrectlyConnected(tiles, x, y, width, height)); - + // First check if all tiles are in their correct rotation + bool allCorrect = true; 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]; - bool up = tile.HasConnection(0); - bool right = tile.HasConnection(1); - bool down = tile.HasConnection(2); - bool left = tile.HasConnection(3); + // Check all 4 directions + 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 (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; - - if (y < height - 1) - connected &= up == tiles[x, y + 1].HasConnection(2); - 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; + // Only continue if neighbor has matching connection + if (tiles[nx, ny].HasConnection((dir + 2) % 4)) + { + FloodFill(tiles, visited, width, height, nx, ny); + } + } + } } public static bool AutoSolve(Tile[,] tiles, int width, int height) { - bool changed; - int[,] possible = new int[width, height]; + // Create a list of tiles sorted by connection count (most constrained first) + var tileList = tiles.Cast() + .Where(t => !t.locked) + .OrderBy(t => t.GetConnectionCount()) + .ToList(); - // Initialize the possible rotation states of all tiles (4-bit binary) - 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); + return SolveRecursive(tiles, width, height, tileList, 0); } -} + + private static bool SolveRecursive(Tile[,] tiles, int width, int height, List 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 + }; +} \ No newline at end of file diff --git a/Assets/Sprites/LU-A.png b/Assets/Sprites/LU-A.png index c60c46b95c72e8c27a9207832a0eb3402e75e835..2abe7a7007ce1f23861185d411b9d6c5b5a16756 100644 GIT binary patch delta 126 zcmZo+oy9moxtxKy*vT`5gM;JtL;nX13=Bb@E{-7{oyj$Sei$sE@g2}~22WQ%mvv4FO#t2hEFb^? delta 620 zcmV-y0+aok0fYsRBYy#eX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0W=T2c?Ny}000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000~Nklj^~1_lNh ztDh%MoMj+V7ZU@G!KekJ7K~ajYQbQzfW^YXg0>bwO5mX!L^4)CX=(u@@ip3LJ{YxN z)Phk9MlHYz0FvkrMIXDXK>z>%4rN$LW=%~1DgXcg2mk;800000(o>TF00000EGYk1eXDjBXj@*b3#c}2nYz<;ZNWI002)(L_t(YiS5!c3IH$&1HiQOC4R)W z`whZf002ovPDHLkV1nGhEam_J delta 614 zcmV-s0-61n0saJ#BYy#eX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0W&O6U*XFD000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000^NklO_)Sf&KhM00000 z6Mzd>4-Z8LJ!L%r000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$f*!a9 A#sB~S diff --git a/Assets/Sprites/LUR-A.png b/Assets/Sprites/LUR-A.png index 39405e0d78a26bd60ec69060e6dec616f1a326b1..b9ef8482c746b38ba7dc95baf4e66247bb480af7 100644 GIT binary patch delta 130 zcmZo;oyRyqxtf8w*vT`5gM;JtL;nX13=CnOE{-7{oyj$Sei$EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0X8L6^sOoY000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}00011Nklj^~1_lNh ztDh%MoM#|X7ZU@G!KekJ7K~ajYQbQzfKf(9hIrNR(v$=X7#=B)q#z>40^)15(R?s! z!KekJ7K~be6951l8W2H0tlk^|000hUSV?A0O#mtY000O800000007cclK=n!07*qo IM6N<$g8lplZvX%Q diff --git a/Assets/Sprites/LUR.png b/Assets/Sprites/LUR.png index 7151d07f452d7ff973302cd5d56d7f6fd6cad375..9050cc6b62c0b7088c891d151d1cafa6ac412f99 100644 GIT binary patch delta 123 zcmV->0EGYk1eXDjBXj@*b3#c}2nYz<;ZNWI002)(L_t(YiDP{H_%Q=50TWG4AGKiA zf>8@bEf^#gFe)i2(b|q74oC~4d-v`!Fffp$h2`MEgG4H|x3`D0iPtsslj*1hqZW)> dFlqr20046T3-sb@7j*yt002ovPDHLkV1nCvE~fwh delta 614 zcmV-s0-61n0saJ#BYy#eX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0XG$EHmt`0000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000^Nkl8@bEf@?IFv`ft5U(0unlg+az({NVG7?{-jpl<<3q~y%wP4f& zoB#lY+z&ac%~o>&000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$g7|j^ AZvX%Q diff --git a/Assets/Sprites/Node-A.png b/Assets/Sprites/Node-A.png index a8f3b153e83c2bb60419db8e2d5fa3c70a50a44b..dd2d7e14b8103507b432510361f4e7be0896582f 100644 GIT binary patch delta 138 zcmV;50CoTV1f>CxBYyw`b3#c}2nYz<;ZNWI003P{L_t(YiDP{H_%Q=50TWG4AGKiA zf}vpn3m!Sy_3lKP@;GoOE~%j(L_^;Ke348%u(!9Ttp)cE$l#J1B0;3&@PxJ&96Wds sm(EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0WSsgnk4!F000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000^Nklj^~1_lNh ztDh%MoL?YP7ZU@G!KekJ77PIk81ZB@nf1m*n(`#@>}VW}S}1Ys*S} zZ2Px# diff --git a/Assets/Sprites/Node.png b/Assets/Sprites/Node.png index 6ac5eb52fd4eb4f29a7cd83496dac2c3ec281f83..4bb872a60c706fe9d577aa50709391d19204f95b 100644 GIT binary patch delta 138 zcmV;50CoTR1f>CxBYyw`b3#c}2nYz<;ZNWI003P{L_t(YiDP{H_%Q=50TWG4AGKiA zf}vpn3m!Sy_3lKP@;GoOE~%j(L_^;Ke348%u(!9Ttp)e)-NPj{M1n|3Nr|==96Wds sm(EX>4Tx04R}tkv&MmP!xqvQ$>-AgB?^H zGE{M}i;8rtRVYG*P%E_RVDi#GXws0RxHt-~1qXi?s}3&Cx;nTDg5VE`yWphgA|>9J z6k5c1;qgAsyXWxUeSpxYFwN?U1DbA|>10C8=2pd?R|GMP0DmSBlbL1ANm2^F^>t63 zRCiIH<=^*b^{aV{0RfRX&J5Eg-XNacv<=St#9>yFRpN8vNs}%}{K$31<2TMlmj#{~ zHZ$pY;xMsT>|mvXS;^FhXNV)Jrc=I<^;qS+#aXM=SnHnrg`vE@vdnc_qex;AOOPN! zK@DY8U?WPqPJfDpG@U1W{3EVkB9}t0G8j1)P=yBB^@IPx@7Y>~=_xNMlmNP49Oq*U z=-CCDb;tQWcAVx35PSx%^tQj+0Ain{*V|g;2pHG~F0R{}vIku507FlPY|5?_q$Lyz z!220}Qw|us1^U*!xwX!5`T%5TR;e4{;1C!uQueyfyMKE-=k{+;Ykoi2J#x>iW$my4 z000SaNLh0L04^f{04^f|c%?sf00007bV*G`2k8SD0WTVPk|_@W000?uMObu0Z*6U5 zZgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000=Nkl