using System; using System.Collections.Generic; using System.Linq; using Godot; public class WFC { public static Dictionary>> adjacency = new Dictionary>>(); public static Random rand = new Random(); public enum Direction { Up, Down, Left, Right, None } public static readonly Vector2I[] offsets = { new Vector2I(0, -1), new Vector2I(0, 1), new Vector2I(-1, 0), new Vector2I(1, 0) }; public static readonly Direction[] dirs = { Direction.Up, Direction.Down, Direction.Left, Direction.Right }; public static Dictionary> tileConnections = new Dictionary> { ["t_right"] = new() { Direction.Up, Direction.Down, Direction.Right }, ["t_left"] = new() { Direction.Up, Direction.Down, Direction.Left }, ["t_up"] = new() { Direction.Left, Direction.Right, Direction.Up }, ["t_down"] = new() { Direction.Left, Direction.Right, Direction.Down }, ["end_up"] = new() { Direction.Up }, ["end_down"] = new() { Direction.Down }, ["end_left"] = new() { Direction.Left }, ["end_right"] = new() { Direction.Right }, ["straight_left_right"] = new() { Direction.Left, Direction.Right }, ["straight_up_down"] = new() { Direction.Up, Direction.Down }, ["corner_up_left"] = new() { Direction.Up, Direction.Left }, ["corner_up_right"] = new() { Direction.Up, Direction.Right }, ["corner_down_left"] = new() { Direction.Down, Direction.Left }, ["corner_down_right"] = new() { Direction.Down, Direction.Right }, ["junction"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right }, ["border"] = new() { } }; public static Dictionary weights = new() { ["junction"] = 5f, ["t_up"] = 3f, ["t_down"] = 3f, ["t_left"] = 3f, ["t_right"] = 3f, ["straight_left_right"] = 2f, ["straight_up_down"] = 2f, ["corner_up_left"] = 0.5f, ["corner_up_right"] = 0.5f, ["corner_down_left"] = 0.5f, ["corner_down_right"] = 0.5f, ["end_up"] = 0.2f, ["end_down"] = 0.1f, ["end_left"] = 0.2f, ["end_right"] = 0.3f, ["border"] = 0.0f }; public static Direction Opposite(Direction dir) { return dir switch { Direction.Up => Direction.Down, Direction.Down => Direction.Up, Direction.Left => Direction.Right, Direction.Right => Direction.Left, _ => dir }; } public static bool CanConnect(string a, string b, Direction direction, bool checkWalking) { var aDirs = tileConnections[a]; var bDirs = tileConnections[b]; bool aOpen = aDirs.Contains(direction); bool bOpen = bDirs.Contains(Opposite(direction)); if (checkWalking) return aOpen && bOpen; return aOpen == bOpen; } public static void FillAdjacencies() { foreach (var tile in tileConnections.Keys) { adjacency[tile] = new Dictionary>(); foreach (Direction dir in Enum.GetValues(typeof(Direction))) { var valid = new List(); foreach (var other in tileConnections.Keys) { if (CanConnect(tile, other, dir, false)) valid.Add(other); } adjacency[tile][dir] = valid; } } } public static bool IsWalkable(Tile tile) { var dirs = tileConnections[tile.collapsedMesh]; return dirs.Count > 0 && !dirs.Contains(Direction.None); } public static bool CanWalk(Tile[,] layer, Vector2I from, Vector2I to, Direction dir) { var fromTile = layer[from.X, from.Y]; var toTile = layer[to.X, to.Y]; if (!IsWalkable(toTile)) return false; return CanConnect(fromTile.collapsedMesh, toTile.collapsedMesh, dir, true); } public static bool IsMapConnected(Tile[,] layer, float accessibilityThreshhold) { bool result = false; HashSet visited = new HashSet(); List toCheck = new List(); Vector2I position; toCheck.Add(new Vector2I(1, 1)); int safetyCounter = 0; while (true) { if (toCheck.Count <= 0) break; int index = rand.Next(toCheck.Count); position = toCheck[index]; toCheck[index] = toCheck[^1]; toCheck.RemoveAt(toCheck.Count - 1); if (!visited.Add(position)) continue; for (int i = 0; i < 4; i++) { var next = position + offsets[i]; if (!InBounds(next, layer.GetLength(0), false)) continue; if (CanWalk(layer, position, next, dirs[i])) { toCheck.Add(next); } } safetyCounter++; if (safetyCounter > layer.Length * 2) break; if (visited.Count >= Math.Pow(layer.GetLength(0) - 1, 2) * accessibilityThreshhold) { result = true; break; } } if (safetyCounter > layer.Length * 2) GD.PrintErr("Loop too long"); return result; } public static bool InBounds(Vector2I pos, int layerSize, bool includeBorder = true) { if (includeBorder) { return pos.X >= 0 && pos.Y >= 0 && pos.X < layerSize && pos.Y < layerSize; } else { return pos.X > 0 && pos.Y > 0 && pos.X < layerSize - 1 && pos.Y < layerSize - 1; } } }