public SokobanPosition Clone() { var clone = new SokobanPosition(); clone.Parent = this; clone.Width = this.Width; clone.Height = this.Height; clone.Map = new byte[this.Width, this.Height]; Array.Copy(this.Map, clone.Map, this.Map.Length); clone.Player = this.Player; clone.Goals = this.Goals; clone.Stones = new PointXY[this.Stones.Length]; Array.Copy(this.Stones, clone.Stones, this.Stones.Length); clone.StonesMap = new byte[this.Width, this.Height]; Array.Copy(this.StonesMap, clone.StonesMap, this.StonesMap.Length); clone.NormalizedPlayer = this.NormalizedPlayer; clone.DeadlockMap = this.DeadlockMap; if (this.Binary != null) { clone.Binary = new byte[this.Binary.Length]; Array.Copy(this.Binary, clone.Binary, this.Binary.Length); } return(clone); }
public int DistanceTo(IGamePosition other) { //prefer pushing the same stone in next move SokobanPosition position = (SokobanPosition)other; return((this.stoneMovedTo.X == position.stoneMovedFrom.X && this.stoneMovedTo.Y == position.stoneMovedFrom.Y) ? 1 : 10); }
public static SokobanPosition LoadFromFile(string path) { string[] lines = File.ReadAllLines(path).Where(l => l.Length > 0).ToArray(); SokobanPosition position = new SokobanPosition(); position.Width = lines.Max(l => l.Length); position.Height = lines.Length; position.Map = new byte[position.Width, position.Height]; position.StonesMap = new byte[position.Width, position.Height]; position.NormalizedPlayer.X = 0; position.NormalizedPlayer.Y = 0; List <PointXY> stones = new List <PointXY>(); List <PointXY> goals = new List <PointXY>(); for (int y = 0; y < position.Height; y++) { string line = lines[y]; for (int x = 0; x < line.Length; x++) { PointXY p; p.X = x; p.Y = y; position.StonesMap[x, y] = 0; switch (line[x]) { case '#': position.Map[x, y] = Constants.WALL; break; case '$': position.Map[x, y] = Constants.STONE; stones.Add(p); position.StonesMap[x, y] = (byte)stones.Count; break; case '.': position.Map[x, y] = Constants.GOAL; goals.Add(p); break; case '*': position.Map[x, y] = Constants.GOALSTONE; stones.Add(p); goals.Add(p); position.StonesMap[x, y] = (byte)stones.Count; break; case '+': position.Player.X = x; position.Player.Y = y; break; case '-': position.Map[x, y] = Constants.EMPTY; break; } } } if (stones.Count != goals.Count) { Debug.Log("! 箱子数量与目标点数量不符 !"); } position.Goals = goals.ToArray(); position.Stones = stones.ToArray(); distanceMatrix = new int[position.Stones.Length, position.Stones.Length]; distanceMatrixCopy = new int[position.Stones.Length, position.Stones.Length]; hungarian = new HungarianAlgorithm(position.Stones.Length); return(position); }
private void CreateDeadlockMap() { this.DeadlockMap = new bool[this.Width, this.Height]; bool[,] visited = new bool[this.Width, this.Height]; PointXY[] stack = new PointXY[this.Width * this.Height]; int stackTop = 0; PointXY p; var empty = new SokobanPosition() { Width = this.Width, Height = this.Height }; empty.Map = new byte[this.Width, this.Height]; empty.Stones = new PointXY[0]; empty.Goals = new PointXY[0]; empty.StonesMap = new byte[this.Width, this.Height]; empty.Player = this.Player; // create list of goals var goals = new List <PointXY>(); for (int x = 0; x < this.Width; x++) { for (int y = 0; y < this.Height; y++) { if (this.Map[x, y] == Constants.GOAL || this.Map[x, y] == Constants.GOALSTONE) { PointXY g; g.X = x; g.Y = y; goals.Add(g); } this.DeadlockMap[x, y] = true; if (this.Map[x, y] == Constants.WALL) { empty.Map[x, y] = Constants.WALL; } else { empty.Map[x, y] = Constants.EMPTY; } } } foreach (var checkGoal in goals) { //prepare map with one stone var start = new SokobanPosition() { Width = this.Width, Height = this.Height }; start.Map = new byte[this.Width, this.Height]; start.StonesMap = new byte[this.Width, this.Height]; start.Stones = new PointXY[1]; start.Goals = new PointXY[1]; start.Goals[0] = checkGoal; start.Stones[0] = checkGoal; start.StonesMap[checkGoal.X, checkGoal.Y] = 1; start.Player = this.Player; for (int x = 0; x < this.Width; x++) { for (int y = 0; y < this.Height; y++) { start.Map[x, y] = this.Map[x, y]; if (start.Map[x, y] == Constants.STONE || start.Map[x, y] == Constants.GOAL || start.Map[x, y] == Constants.GOALSTONE) { start.Map[x, y] = Constants.EMPTY; } if (x == checkGoal.X && y == checkGoal.Y) { start.Map[x, y] = Constants.STONE; DeadlockMap[x, y] = false; } } } //search pull posibilities List <SokobanPosition> nodesToCheck = new List <SokobanPosition>(); nodesToCheck.Add(start); HashSet <string> visitedNodes = new HashSet <string>(); while (nodesToCheck.Count > 0) { var node = nodesToCheck.ElementAt(0); nodesToCheck.RemoveAt(0); visitedNodes.Add(node.GetUniqueId()); // add pulls for any reachable player position Array.Clear(visited, 0, visited.Length); stack[0] = node.Player; visited[node.Player.X, node.Player.Y] = true; stackTop = 0; while (stackTop >= 0) { p = stack[stackTop]; stackTop--; //try pull right if ((node.Map[p.X - 1, p.Y] == Constants.STONE) && (node.Map[p.X + 1, p.Y] == Constants.EMPTY)) { var next = node.ClonePull(p, Direction.Right); if (!visitedNodes.Contains(next.GetUniqueId()) && !nodesToCheck.Any(n => n.GetUniqueId() == next.GetUniqueId())) { nodesToCheck.Add(next); this.DeadlockMap[p.X, p.Y] = false; } } //try pull left if ((node.Map[p.X + 1, p.Y] == Constants.STONE) && (node.Map[p.X - 1, p.Y] == Constants.EMPTY)) { var next = node.ClonePull(p, Direction.Left); if (!visitedNodes.Contains(next.GetUniqueId()) && !nodesToCheck.Any(n => n.GetUniqueId() == next.GetUniqueId())) { nodesToCheck.Add(next); this.DeadlockMap[p.X, p.Y] = false; } } //try pull down if ((node.Map[p.X, p.Y - 1] == Constants.STONE) && (node.Map[p.X, p.Y + 1] == Constants.EMPTY)) { var next = node.ClonePull(p, Direction.Down); if (!visitedNodes.Contains(next.GetUniqueId()) && !nodesToCheck.Any(n => n.GetUniqueId() == next.GetUniqueId())) { nodesToCheck.Add(next); this.DeadlockMap[p.X, p.Y] = false; } } //try pull up if ((node.Map[p.X, p.Y + 1] == Constants.STONE) && (node.Map[p.X, p.Y - 1] == Constants.EMPTY)) { var next = node.ClonePull(p, Direction.Up); if (!visitedNodes.Contains(next.GetUniqueId()) && !nodesToCheck.Any(n => n.GetUniqueId() == next.GetUniqueId())) { nodesToCheck.Add(next); this.DeadlockMap[p.X, p.Y] = false; } } //try walk left if (!visited[p.X - 1, p.Y] && (node.Map[p.X - 1, p.Y] == Constants.EMPTY || node.Map[p.X - 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X - 1; stack[stackTop].Y = p.Y; visited[p.X - 1, p.Y] = true; } //try walk right if (!visited[p.X + 1, p.Y] && (node.Map[p.X + 1, p.Y] == Constants.EMPTY || node.Map[p.X + 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X + 1; stack[stackTop].Y = p.Y; visited[p.X + 1, p.Y] = true; } //try walk up if (!visited[p.X, p.Y - 1] && (node.Map[p.X, p.Y - 1] == Constants.EMPTY || node.Map[p.X, p.Y - 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y - 1; visited[p.X, p.Y - 1] = true; } //try walk down if (!visited[p.X, p.Y + 1] && (node.Map[p.X, p.Y + 1] == Constants.EMPTY || node.Map[p.X, p.Y + 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y + 1; visited[p.X, p.Y + 1] = true; } } } } // remove deadlocks outside reachable area Array.Clear(visited, 0, visited.Length); stack[0] = empty.Player; visited[empty.Player.X, empty.Player.Y] = true; stackTop = 0; while (stackTop >= 0) { p = stack[stackTop]; stackTop--; //try walk left if (!visited[p.X - 1, p.Y] && (empty.Map[p.X - 1, p.Y] == Constants.EMPTY || empty.Map[p.X - 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X - 1; stack[stackTop].Y = p.Y; visited[p.X - 1, p.Y] = true; } //try walk right if (!visited[p.X + 1, p.Y] && (empty.Map[p.X + 1, p.Y] == Constants.EMPTY || empty.Map[p.X + 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X + 1; stack[stackTop].Y = p.Y; visited[p.X + 1, p.Y] = true; } //try walk up if (!visited[p.X, p.Y - 1] && (empty.Map[p.X, p.Y - 1] == Constants.EMPTY || empty.Map[p.X, p.Y - 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y - 1; visited[p.X, p.Y - 1] = true; } //try walk down if (!visited[p.X, p.Y + 1] && (empty.Map[p.X, p.Y + 1] == Constants.EMPTY || empty.Map[p.X, p.Y + 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y + 1; visited[p.X, p.Y + 1] = true; } } for (int x = 0; x < this.Width; x++) { for (int y = 0; y < this.Height; y++) { if (!visited[x, y]) { this.DeadlockMap[x, y] = false; } } } }
public static String GetFullPath(SokobanPosition[] path) { List <SokobanPosition> result = new List <SokobanPosition>(); for (int i = 0; i < path.Length - 1; i++) { var start = path[i]; var stop = path[i + 1]; result.Add(start); bool[,] visited = new bool[start.Width, start.Height]; visited[start.Player.X, start.Player.Y] = true; Array.Clear(visited, 0, visited.Length); PointXY[] stack = new PointXY[start.Width * start.Height]; PointXY[,] track = new PointXY[start.Width, start.Height]; track[start.Player.X, start.Player.Y].X = 0; track[start.Player.X, start.Player.Y].Y = 0; int stackTop = 0; stack[0] = start.Player; PointXY p; SokobanPosition found = null; while (stackTop >= 0) { p = stack[stackTop]; stackTop--; //try push left if ((start.Map[p.X - 1, p.Y] == Constants.STONE || start.Map[p.X - 1, p.Y] == Constants.GOALSTONE) && (start.Map[p.X - 2, p.Y] == Constants.EMPTY || start.Map[p.X - 2, p.Y] == Constants.GOAL)) { var next = start.ClonePush(p, Direction.Left); if (next.GetUniqueId() == stop.GetUniqueId()) { found = next; track[p.X - 1, p.Y] = p; break; } } //try push right if ((start.Map[p.X + 1, p.Y] == Constants.STONE || start.Map[p.X + 1, p.Y] == Constants.GOALSTONE) && (start.Map[p.X + 2, p.Y] == Constants.EMPTY || start.Map[p.X + 2, p.Y] == Constants.GOAL)) { var next = start.ClonePush(p, Direction.Right); if (next.GetUniqueId() == stop.GetUniqueId()) { found = next; track[p.X + 1, p.Y] = p; break; } } //try push up if ((start.Map[p.X, p.Y - 1] == Constants.STONE || start.Map[p.X, p.Y - 1] == Constants.GOALSTONE) && (start.Map[p.X, p.Y - 2] == Constants.EMPTY || start.Map[p.X, p.Y - 2] == Constants.GOAL)) { var next = start.ClonePush(p, Direction.Up); if (next.GetUniqueId() == stop.GetUniqueId()) { found = next; track[p.X, p.Y - 1] = p; break; } } //try push down if ((start.Map[p.X, p.Y + 1] == Constants.STONE || start.Map[p.X, p.Y + 1] == Constants.GOALSTONE) && (start.Map[p.X, p.Y + 2] == Constants.EMPTY || start.Map[p.X, p.Y + 2] == Constants.GOAL)) { var next = start.ClonePush(p, Direction.Down); if (next.GetUniqueId() == stop.GetUniqueId()) { found = next; track[p.X, p.Y + 1] = p; break; } } //try walk left if (!visited[p.X - 1, p.Y] && (start.Map[p.X - 1, p.Y] == Constants.EMPTY || start.Map[p.X - 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X - 1; stack[stackTop].Y = p.Y; visited[p.X - 1, p.Y] = true; track[p.X - 1, p.Y] = p; } //try walk right if (!visited[p.X + 1, p.Y] && (start.Map[p.X + 1, p.Y] == Constants.EMPTY || start.Map[p.X + 1, p.Y] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X + 1; stack[stackTop].Y = p.Y; visited[p.X + 1, p.Y] = true; track[p.X + 1, p.Y] = p; } //try walk up if (!visited[p.X, p.Y - 1] && (start.Map[p.X, p.Y - 1] == Constants.EMPTY || start.Map[p.X, p.Y - 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y - 1; visited[p.X, p.Y - 1] = true; track[p.X, p.Y - 1] = p; } //try walk down if (!visited[p.X, p.Y + 1] && (start.Map[p.X, p.Y + 1] == Constants.EMPTY || start.Map[p.X, p.Y + 1] == Constants.GOAL)) { stackTop++; stack[stackTop].X = p.X; stack[stackTop].Y = p.Y + 1; visited[p.X, p.Y + 1] = true; track[p.X, p.Y + 1] = p; } } if (found != null) { var t = stop.Player; List <SokobanPosition> intermediate = new List <SokobanPosition>(); while (!(t.X == start.Player.X && t.Y == start.Player.Y)) { SokobanPosition pos = start.Clone(); pos.Player.X = t.X; pos.Player.Y = t.Y; if (!(t.X == stop.Player.X && t.Y == stop.Player.Y)) { intermediate.Add(pos); } t = track[t.X, t.Y]; } intermediate.Reverse(); result.AddRange(intermediate); } } result.Add(path[path.Length - 1]); //Console.WriteLine(moves); string moves = ""; int currentX = result[0].Player.X; int currentY = result[0].Player.Y; for (int i = 1; i < result.Count; i++) { if (result[i].Player.X - currentX == 1) { moves += "↑"; } if (result[i].Player.X - currentX == -1) { moves += "↓"; } if (result[i].Player.Y - currentY == 1) { moves += "→"; } if (result[i].Player.Y - currentY == -1) { moves += "←"; } currentX = result[i].Player.X; currentY = result[i].Player.Y; } return(moves); }