Пример #1
0
        public static Level Replace(Level level, int rowOffset, int columnOffset, Level replacement)
        {
            Level newLevel = new Level(level);
            bool lostSokoban = false;
            for (int row = 1; row < replacement.Height - 1; row++)
            {
                for (int column = 1; column < replacement.Width - 1; column++)
                {
                    if (replacement[row, column] != Cell.Undefined)
                    {
                        int rowTarget = row - 1 + rowOffset;
                        int columnTarget = column - 1 + columnOffset;
                        if (!level.IsValid(rowTarget, columnTarget))
                        {
                            return ExpandAndReplace(level, rowOffset, columnOffset, replacement);
                        }
                        if (level.IsSokoban(rowTarget, columnTarget) && !replacement.IsSokoban(row, column))
                        {
                            lostSokoban = true;
                        }
                        newLevel[rowTarget, columnTarget] = replacement[row, column];
                    }
                }
            }

            if (lostSokoban)
            {
                // Find an empty cell to relocate the sokoban to.
                foreach (Coordinate2D coord in replacement.Coordinates)
                {
                    if (replacement[coord] == Cell.Empty)
                    {
                        newLevel.MoveSokoban(coord.Row - 1 + rowOffset, coord.Column - 1 + columnOffset);
                        return newLevel;
                    }
                }
            }

            return newLevel;
        }
Пример #2
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;
        }