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 { Backward, Forward, Left, Right, None, Up, Down } 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.Backward, Direction.Forward, Direction.Left, Direction.Right }; public static Dictionary> tileConnections = new Dictionary> { ["t_right"] = new() { Direction.Backward, Direction.Forward, Direction.Right , Direction.Up}, ["t_left"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Up }, ["t_up"] = new() { Direction.Left, Direction.Right, Direction.Backward, Direction.Up }, ["t_down"] = new() { Direction.Left, Direction.Right, Direction.Forward, Direction.Up }, ["end_up"] = new() { Direction.Backward, Direction.Up }, ["end_down"] = new() { Direction.Forward, Direction.Up }, ["end_left"] = new() { Direction.Left, Direction.Up }, ["end_right"] = new() { Direction.Right, Direction.Up }, ["straight_left_right"] = new() { Direction.Left, Direction.Right, Direction.Up }, ["straight_up_down"] = new() { Direction.Backward, Direction.Forward, Direction.Up }, ["corner_up_left"] = new() { Direction.Backward, Direction.Left, Direction.Up }, ["corner_up_right"] = new() { Direction.Backward, Direction.Right, Direction.Up }, ["corner_down_left"] = new() { Direction.Forward, Direction.Left, Direction.Up }, ["corner_down_right"] = new() { Direction.Forward, Direction.Right, Direction.Up }, ["junction"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Right, Direction.Up}, ["gate"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Right, Direction.Up, Direction.Down} }; public static Dictionary weights = new() { ["junction"] = 3f, ["t_up"] = 3f, ["t_down"] = 3f, ["t_left"] = 3f, ["t_right"] = 3f, ["straight_left_right"] = 2f, ["straight_up_down"] = 2f, ["corner_up_left"] = 0.7f, ["corner_up_right"] = 0.7f, ["corner_down_left"] = 0.7f, ["corner_down_right"] = 0.7f, ["end_up"] = 0.2f, ["end_down"] = 0.1f, ["end_left"] = 0.2f, ["end_right"] = 0.3f, ["gate"] = 0.0f }; public static Direction Opposite(Direction dir) { return dir switch { Direction.Backward => Direction.Forward, Direction.Forward => Direction.Backward, Direction.Left => Direction.Right, Direction.Right => Direction.Left, Direction.Up => Direction.Down, Direction.Down => Direction.Up, _ => 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))) 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; } } return result; } public static bool InBounds(Vector2I pos, int layerSize) { return pos.X > 0 && pos.Y > 0 && pos.X < layerSize && pos.Y < layerSize; } }