public static Coordinate2D FindFirstEmpty(Level level) { foreach (Coordinate2D coord in level.InsideCoordinates) { if (level.IsEmpty(coord)) { return coord; } } return Coordinate2D.Undefined; }
private void AddDisplacedBoxNoTarget(State state, Level level) { if (distance == 0) { AddRandomBox(state, level); return; } List<Coordinate2D> coordList = new List<Coordinate2D>(); foreach (Coordinate2D coord in level.InsideCoordinates) { // Skip occupied coordinates. if (!level.IsEmpty(coord)) { continue; } // Find nearest target. int nearestTargetDistance = int.MaxValue; foreach (Coordinate2D targetCoord in level.TargetCoordinates) { int targetDistance = Coordinate2D.GetDiagonalDistance(coord, targetCoord); nearestTargetDistance = Math.Min(nearestTargetDistance, targetDistance); } // Check whether the nearest target is near enough. if (nearestTargetDistance <= (distance == 0 ? int.MaxValue : distance)) { coordList.Add(coord); } } if (coordList.Count == 0) { state.Log.DebugPrint("no available squares to add squares to"); return; } Coordinate2D boxCoord = coordList[state.Random.Next(coordList.Count)]; level[boxCoord] |= Cell.Box; }
private Coordinate2D GetRandomEmptySquare(State state, Level level) { List<Coordinate2D> coordList = new List<Coordinate2D>(); foreach (Coordinate2D coord in level.InsideCoordinates) { if (level.IsEmpty(coord)) { coordList.Add(coord); } } if (coordList.Count == 0) { return Coordinate2D.Undefined; } return coordList[state.Random.Next(coordList.Count)]; }
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; }
protected Exception Abort(string message) { string bugMessage = String.Format("bug: {0}", message); Log.DebugPrint(bugMessage); Level fullLevel = new Level(level); if (fullLevel.Sokobans == 0 && fullLevel.IsEmpty(current.SokobanRow, current.SokobanColumn)) { fullLevel.AddSokoban(current.SokobanRow, current.SokobanColumn); } Log.DebugPrint(fullLevel.AsText); return new InvalidOperationException(bugMessage); }