Beispiel #1
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);
        }
        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 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 (string.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);
        }