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