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);
        }
Beispiel #3
0
        public static List <SokobanPosition> 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]);
            return(result);
        }
        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;

                    default:
                        position.Map[x, y] = Constants.EMPTY;
                        break;
                    }
                }
            }

            if (stones.Count != goals.Count)
            {
                throw new Exception("Number of stones is not equal to numer of goals!");
            }

            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;
                    }
                }
            }
        }