using UnityEngine; using System.Collections.Generic; public class MapGenerator : MonoBehaviour { // 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 }; public static void Generate(Tile[,] tiles, GameObject[] prefabs, int width, int height, int seed, bool wrapping = false, bool uniqueSolution = true, float barrierProbability = 0f) { Random.InitState(seed); // 1. Generate the solution grid using Net's algorithm var solution = GenerateSolution(width, height, wrapping); // 2. Add barriers var barriers = AddBarriers(solution, width, height, wrapping, barrierProbability); // 3. Create tiles and apply solution CreateTiles(tiles, prefabs, width, height, solution, barriers); // 4. Shuffle (random rotation) ShuffleTiles(tiles, width, height, seed); } private static byte[,] GenerateSolution(int width, int height, bool wrapping) { byte[,] tiles = new byte[width, height]; int cx = width / 2; int cy = height / 2; // Use a priority queue (simplified with List for this example) var possibilities = new List<(int x, int y, int dir)>(); // Start from center AddPossibleDirections(possibilities, cx, cy, width, height, wrapping); while (possibilities.Count > 0) { // Get random possibility int idx = Random.Range(0, possibilities.Count); var (x1, y1, d1) = possibilities[idx]; possibilities.RemoveAt(idx); // Calculate destination int x2 = Wrap(x1 + directions[d1].x, width, wrapping); int y2 = Wrap(y1 + directions[d1].y, height, wrapping); int d2 = (d1 + 2) % 4; // Opposite direction // Skip if destination already has connections if (tiles[x2, y2] != 0) continue; // Make the connection tiles[x1, y1] |= (byte)(1 << d1); tiles[x2, y2] |= (byte)(1 << d2); // Prevent full crosses (Net game rule) if (CountBits(tiles[x1, y1]) == 3) RemoveFullCrossPossibility(possibilities, x1, y1, tiles[x1, y1]); // Add new possibilities from destination AddPossibleDirections(possibilities, x2, y2, width, height, wrapping); } return tiles; } private static void AddPossibleDirections(List<(int x, int y, int dir)> possibilities, int x, int y, int width, int height, bool wrapping) { for (int d = 0; d < 4; d++) { int nx = Wrap(x + directions[d].x, width, wrapping); int ny = Wrap(y + directions[d].y, height, wrapping); // Skip if out of bounds (for non-wrapping) if (!wrapping && (nx < 0 || nx >= width || ny < 0 || ny >= height)) continue; possibilities.Add((x, y, d)); } } private static void RemoveFullCrossPossibility(List<(int x, int y, int dir)> possibilities, int x, int y, byte tile) { // Find the missing direction (the one not in this 3-way tile) int missingDir = 0; for (; missingDir < 4; missingDir++) if ((tile & (1 << missingDir)) == 0) break; // Remove any possibility that would create a full cross for (int i = possibilities.Count - 1; i >= 0; i--) { if (possibilities[i].x == x && possibilities[i].y == y && possibilities[i].dir == missingDir) possibilities.RemoveAt(i); } } private static byte[,] AddBarriers(byte[,] solution, int width, int height, bool wrapping, float probability) { byte[,] barriers = new byte[width, height]; // Add border barriers if not wrapping if (!wrapping) { for (int x = 0; x < width; x++) { barriers[x, 0] |= (byte)(1 << 3); // Up barrier on bottom row barriers[x, height - 1] |= (byte)(1 << 1); // Down barrier on top row } for (int y = 0; y < height; y++) { barriers[0, y] |= (byte)(1 << 2); // Left barrier on first column barriers[width - 1, y] |= (byte)(1 << 0); // Right barrier on last column } } // Add random barriers for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { // Only add barriers where there isn't already a connection for (int d = 0; d < 4; d++) { if ((solution[x, y] & (1 << d)) == 0 && Random.value < probability) { int nx = Wrap(x + directions[d].x, width, wrapping); int ny = Wrap(y + directions[d].y, height, wrapping); barriers[x, y] |= (byte)(1 << d); barriers[nx, ny] |= (byte)(1 << ((d + 2) % 4)); } } } } return barriers; } private static void CreateTiles(Tile[,] tiles, GameObject[] prefabs, int width, int height, byte[,] solution, byte[,] barriers) { float offsetX = -((width - 1) / 2f); float offsetY = ((height - 1) / 2f); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { byte connections = solution[x, y]; string type = GetTileType(connections); int rotation = GetRotation(connections, type); GameObject prefab = FindPrefab(prefabs, type); GameObject tileObj = Instantiate(prefab); tileObj.transform.position = new Vector2(offsetX + x, offsetY - y); Tile tile = tileObj.GetComponent(); tile.X = x; tile.Y = y; tile.baseConnections = GetBaseConnections(type); tile.graphicRotationOffset = GetGraphicOffset(type); 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]; tile.SetConnectionState(true); // Show connected state (green) tiles[x, y] = tile; } } // Set center tile to true tiles[width / 2, height / 2].isCenterTile = true; tiles[width / 2, height / 2].SetConnectionState(true); // Show connected state (green) } private static void ShuffleTiles(Tile[,] tiles, int width, int height, int seed) { Random.InitState(seed); foreach (var tile in tiles) { if (!tile.locked) { // Random rotation (0-3) int rot = Random.Range(0, 4); for (int i = 0; i < rot; i++) tile.RotateClockwise(); tile.SetConnectionState(false); // Show disconnected state (black) } } } private static int Wrap(int value, int max, bool wrapping) { if (!wrapping) return value; return (value + max) % max; } private static int CountBits(byte b) { int count = 0; while (b != 0) { count++; b &= (byte)(b - 1); } return count; } private static string GetTileType(byte connections) { int count = CountBits(connections); switch (count) { case 1: return "Node"; case 2: if ((connections & 0b0101) == 0b0101 || (connections & 0b1010) == 0b1010) return "Straight"; // Opposite directions return "Corner"; case 3: return "TriCorner"; default: return "Empty"; } } private static int GetRotation(byte connections, string type) { switch (type) { case "Node": // Find which direction is set for (int i = 0; i < 4; i++) if ((connections & (1 << i)) != 0) return i; return 0; case "Straight": return (connections & 0b0101) == 0b0101 ? 0 : 1; // Vertical or horizontal case "Corner": // Find first set bit for (int i = 0; i < 4; i++) if ((connections & (1 << i)) != 0) return i; return 0; case "TriCorner": // Find missing direction for (int i = 0; i < 4; i++) if ((connections & (1 << i)) == 0) return i; return 0; default: return 0; } } private static GameObject FindPrefab(GameObject[] prefabs, string type) { foreach (var p in prefabs) { if (p.name.Contains(type)) return p; } Debug.LogError($"No prefab found for type: {type}"); return prefabs[0]; } private static int[] GetBaseConnections(string type) { switch (type) { case "Node": return new int[] { 0 }; // Right case "Straight": return new int[] { 0, 2 }; // Right and Left case "Corner": return new int[] { 0, 1 }; // Right and Up case "TriCorner": return new int[] { 0, 1, 2 }; // Right, Up, Left // case "Cross": return new int[] { 0, 1, 2, 3 }; // All directions default: return new int[0]; } } private static int GetGraphicOffset(string type) { // Adjust if your tile graphics need rotation offsets return 0; } }