Exemplo n.º 1
0
        private static void CheckForCapturedLine(Level level, Array2D<bool> simpleDeadlockMap,
            Coordinate2D coord, Coordinate2D perpendicular, Coordinate2D parallel)
        {
            // Check for straight walls that look like this:
            //     ###########
            //     #.->      #
            // where the dot is the starting position
            // and perpendicular is the direction of the wall
            // and parallel is the direction to search.

            if (!level.IsWall(coord - parallel))
            {
                return;
            }

            bool isCapturedLine = false;
            for (Coordinate2D c = coord; !level.IsTarget(c); c += parallel)
            {
                if (!level.IsWall(c + perpendicular))
                {
                    break;
                }
                if (level.IsWall(c + parallel))
                {
                    isCapturedLine = true;
                    break;
                }
            }

            if (!isCapturedLine)
            {
                return;
            }

            for (Coordinate2D c = coord; !level.IsWall(c); c += parallel)
            {
                simpleDeadlockMap[c] = true;
            }
        }
Exemplo n.º 2
0
 private Coordinate2D GetRandomJustFloorSquareAvoidingPerimeter(State state, Level level)
 {
     List<Coordinate2D> coordList = new List<Coordinate2D>();
     foreach (Coordinate2D coord in level.InsideCoordinates)
     {
         if (level.IsJustFloor(coord))
         {
             bool onPerimeter = false;
             foreach (Coordinate2D neighbor in coord.FourNeighbors)
             {
                 if (level.IsWall(neighbor))
                 {
                     onPerimeter = true;
                     break;
                 }
             }
             if (!onPerimeter)
             {
                 coordList.Add(coord);
             }
         }
     }
     if (coordList.Count == 0)
     {
         return Coordinate2D.Undefined;
     }
     return coordList[state.Random.Next(coordList.Count)];
 }
Exemplo n.º 3
0
        public static Level TrimWalls(Level level)
        {
            // First make a copy.
            Level newLevel = new Level(level);

            // Delete walls that don't contact the interior.
            foreach (Coordinate2D coord in newLevel.Coordinates)
            {
                if (!level.IsWall(coord))
                {
                    continue;
                }

                bool foundInterior = false;
                foreach (Coordinate2D neighbor in coord.EightNeighbors)
                {
                    if (level.IsInside(neighbor))
                    {
                        foundInterior = true;
                        break;
                    }
                }

                if (!foundInterior)
                {
                    newLevel[coord] = Cell.Empty;
                }
            }

            return newLevel;
        }
Exemplo n.º 4
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);
                    }
                }
            }
        }
Exemplo n.º 5
0
        public static Level ShrinkToFit(Level level)
        {
            // Determine unneeded exterior rows and columns.
            int rowMin = level.Height;
            int colMin = level.Width;
            int rowMax = -1;
            int colMax = -1;
            foreach (Coordinate2D coord in level.Coordinates)
            {
                if (level.IsWall(coord))
                {
                    rowMin = Math.Min(rowMin, coord.Row);
                    colMin = Math.Min(colMin, coord.Column);
                    rowMax = Math.Max(rowMax, coord.Row);
                    colMax = Math.Max(colMax, coord.Column);
                }
            }

            Array2D<Cell> data = level.Data.GetSubarray(rowMin, colMin, rowMax - rowMin + 1, colMax - colMin + 1);
            return new Level(data);
        }
Exemplo n.º 6
0
 public static Array2D<bool> GetIslandMap(Level level)
 {
     Array2D<bool> islandMap = new Array2D<bool>(level.Height, level.Width);
     foreach (Coordinate2D coord in islandMap.Coordinates)
     {
         islandMap[coord] = level.IsWall(coord);
     }
     islandMap.FloodFill(FindFirstWall(level), true, false);
     return islandMap;
 }
Exemplo n.º 7
0
        public static Array2D<bool> GetCapturedMap(Level level)
        {
            // Create a map of cell positions that if occupied correspond
            // to a deadlock.  This is similar to a simple deadlock map
            // but returns a different result if there are no targets.

            Array2D<bool> simpleDeadlockMap = new Array2D<bool>(level.Height, level.Width);

            foreach (Coordinate2D coord in level.InsideCoordinates)
            {
                if (level.IsTarget(coord))
                {
                    continue;
                }
                bool wallUp = level.IsWall(coord + Direction.Up);
                bool wallDown = level.IsWall(coord + Direction.Down);
                bool wallLeft = level.IsWall(coord + Direction.Left);
                bool wallRight = level.IsWall(coord + Direction.Right);
                if (wallUp && (wallLeft || wallRight))
                {
                    simpleDeadlockMap[coord] = true;
                    continue;
                }
                if (wallDown && (wallLeft || wallRight))
                {
                    simpleDeadlockMap[coord] = true;
                    continue;
                }
            }

            foreach (Coordinate2D coord in level.InsideCoordinates)
            {
                if (simpleDeadlockMap[coord])
                {
                    CheckForCapturedLine(level, simpleDeadlockMap, coord, Direction.Up, Direction.Right);
                    CheckForCapturedLine(level, simpleDeadlockMap, coord, Direction.Down, Direction.Right);
                    CheckForCapturedLine(level, simpleDeadlockMap, coord, Direction.Left, Direction.Down);
                    CheckForCapturedLine(level, simpleDeadlockMap, coord, Direction.Right, Direction.Down);
                }
            }

            return simpleDeadlockMap;
        }
Exemplo n.º 8
0
        private static Level TryNormalizeTunnel(Level level, Coordinate2D tunnelCoord, Direction direction)
        {
            // Determine the offsets of the parallel direction.
            Coordinate2D parallel = direction;

            // If the upper half is closed off it's not a tunnel.
            if (level.IsWall(tunnelCoord - parallel))
            {
                return null;
            }

            // Empty out the level.
            Level newLevel = LevelUtils.GetEmptyLevel(level);

            // Add a wall to separate the level into two halves.
            newLevel[tunnelCoord] = Cell.Wall;

            // Find all cells accessible from the upper half.
            PathFinder finder = PathFinder.CreateInstance(newLevel);
            finder.Find(tunnelCoord - parallel);

            // If the lower half is accessible from the upper half we can't normalize.
            if (finder.IsAccessible(tunnelCoord + parallel))
            {
                return null;
            }

            // Create a map of the two regions, initialized to zero.
            Array2D<int> regionMap = new Array2D<int>(newLevel.Height, newLevel.Width);

            // Mark first region in the map.
            foreach (Coordinate2D coord in finder.AccessibleCoordinates)
            {
                regionMap[coord] = 1;
            }

            // Find all cells accessible from the lower half.
            finder.Find(tunnelCoord + parallel);

            // Mark second region in the map.
            foreach (Coordinate2D coord in finder.AccessibleCoordinates)
            {
                regionMap[coord] = 2;
            }

            // See if normalization would result in overlap.
            foreach (Coordinate2D coord in newLevel.Coordinates)
            {
                // For all coordinates in region one.
                if (regionMap[coord] != 1)
                {
                    continue;
                }

                if (coord == tunnelCoord - parallel)
                {
                    // We will have intentional contact right at the match site.
                    continue;
                }

                // Check whether there would be a square in region 1 adjacent to a square in region 2.
                Coordinate2D newCoord = coord + parallel;
                foreach (Coordinate2D neighbor in newCoord.FourNeighbors)
                {
                    if (newLevel.IsValid(neighbor) && regionMap[neighbor] == 2)
                    {
                        // Normalizing would alter the level.
                        return null;
                    }
                }
            }

            // Normalize the level using the region map as our guide.
            Array2D<Cell> data = new Array2D<Cell>(level.Height, level.Width);
            data.SetAll(Cell.Wall);
            foreach (Coordinate2D coord in level.Coordinates)
            {
                if (regionMap[coord] == 1)
                {
                    data[coord] = level[coord];
                }
                else if (regionMap[coord] == 2)
                {
                    data[coord - parallel] = level[coord];
                }
            }

            // Construct the level.
            return new Level(data);
        }
Exemplo n.º 9
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;
        }