public SokobanPosition ClonePull(PointXY p, Direction direction) { var clone = this.Clone(); switch (direction) { case Direction.Left: clone.Player.X = p.X - 1; clone.Player.Y = p.Y; clone.Map[p.X + 1, p.Y] = this.Map[p.X + 1, p.Y] == Constants.STONE ? Constants.EMPTY : Constants.GOAL; clone.Map[p.X, p.Y] = this.Map[p.X, p.Y] == Constants.EMPTY ? Constants.STONE : Constants.GOALSTONE; clone.stoneMovedFrom.X = p.X + 1; clone.stoneMovedFrom.Y = p.Y; clone.stoneMovedTo.X = p.X; clone.stoneMovedTo.Y = p.Y; break; case Direction.Right: clone.Player.X = p.X + 1; clone.Player.Y = p.Y; clone.Map[p.X - 1, p.Y] = this.Map[p.X - 1, p.Y] == Constants.STONE ? Constants.EMPTY : Constants.GOAL; clone.Map[p.X, p.Y] = this.Map[p.X, p.Y] == Constants.EMPTY ? Constants.STONE : Constants.GOALSTONE; clone.stoneMovedFrom.X = p.X - 1; clone.stoneMovedFrom.Y = p.Y; clone.stoneMovedTo.X = p.X; clone.stoneMovedTo.Y = p.Y; break; case Direction.Up: clone.Player.X = p.X; clone.Player.Y = p.Y - 1; clone.Map[p.X, p.Y + 1] = this.Map[p.X, p.Y + 1] == Constants.STONE ? Constants.EMPTY : Constants.GOAL; clone.Map[p.X, p.Y] = this.Map[p.X, p.Y] == Constants.EMPTY ? Constants.STONE : Constants.GOALSTONE; clone.stoneMovedFrom.X = p.X; clone.stoneMovedFrom.Y = p.Y + 1; clone.stoneMovedTo.X = p.X; clone.stoneMovedTo.Y = p.Y; break; case Direction.Down: clone.Player.X = p.X; clone.Player.Y = p.Y + 1; clone.Map[p.X, p.Y - 1] = this.Map[p.X, p.Y - 1] == Constants.STONE ? Constants.EMPTY : Constants.GOAL; clone.Map[p.X, p.Y] = this.Map[p.X, p.Y] == Constants.EMPTY ? Constants.STONE : Constants.GOALSTONE; clone.stoneMovedFrom.X = p.X; clone.stoneMovedFrom.Y = p.Y - 1; clone.stoneMovedTo.X = p.X; clone.stoneMovedTo.Y = p.Y; break; } int stoneIdx = clone.StonesMap[clone.stoneMovedFrom.X, clone.stoneMovedFrom.Y]; clone.StonesMap[clone.stoneMovedTo.X, clone.stoneMovedTo.Y] = (byte)stoneIdx; clone.StonesMap[clone.stoneMovedFrom.X, clone.stoneMovedFrom.Y] = 0; clone.Stones[stoneIdx - 1] = clone.stoneMovedTo; clone.NormalizedPlayer.X = 0; clone.NormalizedPlayer.Y = 0; if (this.Binary != null) { //clone.Binary[2 + py * this.Width + px] = 0; switch (direction) { case Direction.Left: //clone.Binary[2 + py * this.Width + px - 1] = 1; clone.SetBinaryBit(p.X + 1, p.Y, 0); clone.SetBinaryBit(p.X, p.Y, 1); break; case Direction.Right: //clone.Binary[2 + py * this.Width + px + 1] = 1; clone.SetBinaryBit(p.X - 1, p.Y, 0); clone.SetBinaryBit(p.X, p.Y, 1); break; case Direction.Up: //clone.Binary[2 + (py - 1) * this.Width + px] = 1; clone.SetBinaryBit(p.X, p.Y + 1, 0); clone.SetBinaryBit(p.X, p.Y, 1); break; case Direction.Down: //clone.Binary[2 + (py + 1) * this.Width + px] = 1; clone.SetBinaryBit(p.X, p.Y - 1, 0); clone.SetBinaryBit(p.X, p.Y, 1); break; } } return(clone); }
public string GetUniqueId() { bool changed = false; if (this.Binary == null) { this.Binary = new byte[2 + this.Width * this.Height / 8 + 1]; for (int x = 0; x < this.Width; x++) { for (int y = 0; y < this.Height; y++) { this.SetBinaryBit(x, y, (byte)((this.Map[x, y] == Constants.STONE || this.Map[x, y] == Constants.GOALSTONE) ? 1 : 0)); } } changed = true; } if (this.NormalizedPlayer.X == 0 || this.NormalizedPlayer.Y == 0) { Array.Clear(VisitedMap, 0, VisitedMap.Length); Stack[0] = this.Player; VisitedMap[this.Player.X, this.Player.Y] = true; int stackTop = 0; PointXY p; this.NormalizedPlayer.X = this.Width; this.NormalizedPlayer.Y = this.Height; while (stackTop >= 0) { p = Stack[stackTop]; stackTop--; if (p.Y < this.NormalizedPlayer.Y) { this.NormalizedPlayer = p; } if (p.Y == this.NormalizedPlayer.Y && p.X < this.NormalizedPlayer.X) { this.NormalizedPlayer = p; } //try walk left if (!VisitedMap[p.X - 1, p.Y] && (this.Map[p.X - 1, p.Y] == Constants.EMPTY || this.Map[p.X - 1, p.Y] == Constants.GOAL)) { stackTop++; Stack[stackTop].X = p.X - 1; Stack[stackTop].Y = p.Y; VisitedMap[p.X - 1, p.Y] = true; } //try walk right if (!VisitedMap[p.X + 1, p.Y] && (this.Map[p.X + 1, p.Y] == Constants.EMPTY || this.Map[p.X + 1, p.Y] == Constants.GOAL)) { stackTop++; Stack[stackTop].X = p.X + 1; Stack[stackTop].Y = p.Y; VisitedMap[p.X + 1, p.Y] = true; } //try walk up if (!VisitedMap[p.X, p.Y - 1] && (this.Map[p.X, p.Y - 1] == Constants.EMPTY || this.Map[p.X, p.Y - 1] == Constants.GOAL)) { stackTop++; Stack[stackTop].X = p.X; Stack[stackTop].Y = p.Y - 1; VisitedMap[p.X, p.Y - 1] = true; } //try walk down if (!VisitedMap[p.X, p.Y + 1] && (this.Map[p.X, p.Y + 1] == Constants.EMPTY || this.Map[p.X, p.Y + 1] == Constants.GOAL)) { stackTop++; Stack[stackTop].X = p.X; Stack[stackTop].Y = p.Y + 1; VisitedMap[p.X, p.Y + 1] = true; } } this.Binary[0] = (byte)this.NormalizedPlayer.X; this.Binary[1] = (byte)this.NormalizedPlayer.Y; changed = true; } if (IsNullOrWhiteSpace(this.CachedUniqueId) || changed) { this.CachedUniqueId = Convert.ToBase64String(this.Binary); //this.CachedUniqueId = Encoding.Unicode.GetString(this.Binary); //this.CachedUniqueId = BitConverter.ToString(this.Binary).Replace("-", string.Empty); } return(CachedUniqueId); }
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); }