Beispiel #1
0
        protected ArrayPathFinder(Level level)
            : base(level)
        {
            data = level.Data;
            boxCoordinates = level.BoxCoordinates;
            boxCount = boxCoordinates.Length;
            n = level.Width;
            m = level.Height * level.Width;
            q = new FixedQueue<int>(m);

            firstInside = -1;
            lastInside = -1;
            neighborMap = new int[m][];
            foreach (Coordinate2D coord in level.InsideCoordinates)
            {
                lastInside = coord.Row * n + coord.Column;
                if (firstInside == -1)
                {
                    firstInside = lastInside;
                }

                List<int> neighbors = new List<int>();
                foreach (Coordinate2D neighbor in coord.FourNeighbors)
                {
                    if (level.IsFloor(neighbor))
                    {
                        neighbors.Add(neighbor.Row * n + neighbor.Column);
                    }
                }
                neighborMap[coord.Row * n + coord.Column] = neighbors.ToArray();
            }
            insideCount = lastInside - firstInside + 1;
        }
Beispiel #2
0
        private static void CheckBox(Level level, Array2D<int> noBoxMap, Array2D<bool> simpleDeadlockMap, Coordinate2D coord)
        {
            // Check whether we've visited this square before.
            if (noBoxMap[coord] == 0)
            {
                return;
            }

            // This square is not a no-box square.
            noBoxMap[coord] = 0;

            // Recursively check for other squares that are not no-box squares.
            foreach (Direction direction in Direction.Directions)
            {
                // Get parallel and perpendicular offsets.
                Coordinate2D parallel = direction;
                Coordinate2D perpendicular = Coordinate2D.Transpose(parallel);

                // Check whether we can move the box to the new position.
                if (level.IsFloor(coord - parallel) && level.IsFloor(coord + parallel))
                {
                    Coordinate2D oldBoxCoord = coord;
                    Coordinate2D newBoxCoord = coord + parallel;

                    // Check for the special case of no influence pushes that lead to deadlocks.
                    bool specialCase = false;
                    while (level.IsWall(oldBoxCoord + perpendicular) &&
                        level.IsWall(oldBoxCoord - perpendicular) &&
                        ((level.IsWall(newBoxCoord + perpendicular) &&
                        level.IsWallOrEmpty(newBoxCoord - perpendicular)) ||
                        (level.IsWallOrEmpty(newBoxCoord + perpendicular) &&
                        level.IsWall(newBoxCoord - perpendicular))))
                    {
                        if (level.IsTarget(newBoxCoord))
                        {
                            break;
                        }
                        if (!level.IsFloor(newBoxCoord) || simpleDeadlockMap[newBoxCoord])
                        {
                            specialCase = true;
                            break;
                        }

                        oldBoxCoord += parallel;
                        newBoxCoord += parallel;
                    }

                    // Otherwise recursively check for more squares.
                    if (!specialCase)
                    {
                        CheckBox(level, noBoxMap, simpleDeadlockMap, coord + parallel);
                    }
                }
            }
        }
Beispiel #3
0
        private static Level TryNormalizeLoops(Level level)
        {
            // A loop is a tunnel that you cannot push a box through.
            // Any square that it is impossible or not useful to push
            // a box to is a no-box square.  All the squares in a loop
            // are no-box squares.  A level may have several disconnected
            // no-box regions.  Within one no-box region we can
            // optimize the path by removing islands that only
            // lengthen the loop without changing its function.
            // Finally, a normalized loop only needs to be one square
            // wide.

            // Copy the level and get island and no-box maps.
            bool modifiedLevel = false;
            Level newLevel = new Level(level);
            Array2D<bool> islandMap = LevelUtils.GetIslandMap(level);
            Array2D<int> noBoxMap = LevelUtils.GetNoBoxMap(level);

            // Any island that only contacts other walls and one or more
            // no-box squares in the same region can be removed.
            foreach (Coordinate2D coord in newLevel.Coordinates)
            {
                if (islandMap[coord])
                {
                    int wallCount = 0;
                    int noBoxCount = 0;
                    int region = 0;
                    foreach (Coordinate2D neighbor in coord.FourNeighbors)
                    {
                        if (newLevel.IsWall(neighbor))
                        {
                            wallCount++;
                        }
                        else if (noBoxMap[neighbor] != 0)
                        {
                            // Check whether we've encountered any no-box regions.
                            if (region == 0)
                            {
                                region = noBoxMap[neighbor];
                            }

                            // Only count no-box squares that match the first region.
                            if (noBoxMap[neighbor] == region)
                            {
                                noBoxCount++;
                            }
                        }
                    }
                    if (wallCount + noBoxCount == 4 && noBoxCount >= 1)
                    {
                        newLevel[coord] = Cell.Empty;
                        noBoxMap[coord] = 1;
                        islandMap[coord] = false;
                        modifiedLevel = true;
                    }
                }
            }

            // Make a map of all the inside squares we want to keep, initialized to false.
            Array2D<bool> keepMap = new Array2D<bool>(newLevel.Height, newLevel.Width);
            foreach (Coordinate2D coord in newLevel.InsideCoordinates)
            {
                if (noBoxMap[coord] != 0)
                {
                    // Keep all the no-box squares that contact box islands.
                    foreach (Coordinate2D neighbor in coord.EightNeighbors)
                    {
                        if (newLevel.IsValid(neighbor) && islandMap[neighbor])
                        {
                            keepMap[coord] = true;
                            break;
                        }
                    }

                    // Keep all the no-box squares that contact box squares.
                    foreach (Coordinate2D neighbor in coord.FourNeighbors)
                    {
                        if (newLevel.IsFloor(neighbor) && noBoxMap[neighbor] == 0)
                        {
                            keepMap[coord] = true;
                            break;
                        }
                    }
                }
                else
                {
                    // Keep all the box squares.
                    keepMap[coord] = true;
                }
            }

            // Fill in the no-box squares that we can safely remove.
            Coordinate2D sokobanCoord = newLevel.SokobanCoordinate;
            foreach (Coordinate2D coord in newLevel.InsideCoordinates)
            {
                if (!keepMap[coord])
                {
                    newLevel[coord] = Cell.Wall;
                    modifiedLevel = true;
                }
            }

            // If the sokoban was on one of the cells we didn't keep,
            // move it to a nearby box square.
            if (!newLevel.IsSokoban(sokobanCoord))
            {
                Level boxLevel = FillBoxSquaresWithBoxes(level, noBoxMap);
                PathFinder finder = PathFinder.CreateInstance(boxLevel);
                finder.Find(sokobanCoord);
                foreach (Coordinate2D coord in finder.AccessibleCoordinates)
                {
                    if (newLevel.IsEmpty(coord))
                    {
                        newLevel[coord] = Cell.Sokoban;
                        break;
                    }
                }
            }

            // Return the new level only if we made changes.
            if (modifiedLevel)
            {
                return newLevel;
            }

            return null;
        }
Beispiel #4
0
        public static bool HasDeadEnds(Level level)
        {
            foreach (Coordinate2D coord in level.InsideCoordinates)
            {
                // Count neighbors.
                int neighbors = 0;
                foreach (Coordinate2D neighbor in coord.FourNeighbors)
                {
                    if (level.IsFloor(neighbor))
                    {
                        neighbors++;
                    }
                }
                if (neighbors == 1)
                {
                    return true;
                }
            }

            return false;
        }
        public DijkstraPathFinder(Level level)
            : base(level)
        {
            data = level.Data;
            rowLimit = level.Height - 1;
            insideCoordinates = level.InsideCoordinates;
            int m = level.Height * level.Width;
            q = new FixedQueue<Vertex>(m);

            // Initialize the vertex map.
            vertexMap = new Array2D<Vertex>(level.Height, level.Width);
            foreach (Coordinate2D coord in level.Coordinates)
            {
                Vertex vertex = new Vertex(coord.Row, coord.Column);
                vertex.Distance = DefaultInaccessible;
                vertex.Visited = true;
                vertexMap[coord.Row, coord.Column] = vertex;
            }

            // Calculate the neighbors of each coordinate.
            foreach (Coordinate2D coord in level.InsideCoordinates)
            {
                Vertex vertex = vertexMap[coord];
                List<Vertex> neighbors = new List<Vertex>();
                foreach (Coordinate2D neighbor in coord.FourNeighbors)
                {
                    if (level.IsFloor(neighbor))
                    {
                        neighbors.Add(vertexMap[neighbor]);
                    }
                }
                vertex.Neighbors = neighbors.ToArray();
            }
        }