private void AddLevel(State state, Level level, MoveList moveList) { // Optionally reject levels with dead-ends. if (rejectDeadEnds && LevelUtils.HasDeadEnds(level)) { Reject(state, level, "level with dead ends"); return; } // Optionally reject levels with captured targets. if (rejectCapturedTargets && LevelUtils.HasCapturedTargets(level)) { Reject(state, level, "level with captured targets"); return; } // Optionally move the sokoban away from the first box it pushes. MoveList finalMoveList = moveList; if (moveSokoban) { // Record old sokoban coordinate. Coordinate2D oldSokobanCoord = level.SokobanCoordinate; // Find accessible coordinates. PathFinder finder = PathFinder.CreateInstance(level); finder.Find(); if (rejectSokobanOnTarget) { // Move sokoban to first accessible non-target square. foreach (Coordinate2D coord in finder.AccessibleCoordinates) { if (!level.IsTarget(coord)) { level.MoveSokoban(coord); break; } } if (level.IsTarget(level.SokobanCoordinate)) { Reject(state, level, "level with sokoban on target"); return; } } else { // Move sokoban to first accessible square. foreach (Coordinate2D coord in finder.AccessibleCoordinates) { level.MoveSokoban(coord); break; } } // Solve one last time if the sokoban moved. if (oldSokobanCoord != level.SokobanCoordinate) { finalMoveList = FinalSolve(state, level); if (finalMoveList == null) { Reject(state, level, "final solver failed"); return; } } } int moves = finalMoveList.Count; int pushes = LevelUtils.SolutionPushes(finalMoveList); int changes = LevelUtils.SolutionChanges(level, finalMoveList); int minBoxMoves = LevelUtils.MinimumBoxMoves(level, finalMoveList); // Add level to results. LevelInfo info = new LevelInfo(); info.Level = level; info.Moves = moves; info.Pushes = pushes; info.Changes = changes; info.InsideSquares = level.InsideSquares; info.MinimumBoxMoves = minBoxMoves; info.MoveList = finalMoveList; AddResult(state, info); }
private void AddDisplacedBox(State state, Level level) { if (level.Targets > level.Boxes) { AddDisplacedBoxNoTarget(state, level); return; } Coordinate2D boxCoord = GetRandomJustFloorSquare(state, level); if (boxCoord.IsUndefined) { state.Log.DebugPrint("no available squares to add squares to"); return; } level[boxCoord] = Cell.Box; int limit = distance == 0 ? int.MaxValue : distance; List<Coordinate2D> coordList = new List<Coordinate2D>(); foreach (Coordinate2D coord in level.InsideCoordinates) { if (!level.IsTarget(coord)) { int targetDistance = Coordinate2D.GetDiagonalDistance(coord, boxCoord); if (targetDistance <= limit) { coordList.Add(coord); } } } if (coordList.Count == 0) { state.Log.DebugPrint("no available squares to add squares to"); return; } Coordinate2D targetCoord = coordList[state.Random.Next(coordList.Count)]; level[targetCoord] |= Cell.Target; }
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; } }
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); } } } }
public static bool HasCapturedTargets(Level level) { // Get a captured map for the level without any targets. Array2D<bool> simpleDeadlockMap = DeadlockFinder.GetSimpleDeadlockMap(GetEmptyLevel(level)); // Check for captured targets in the original level. foreach (Coordinate2D coord in level.Coordinates) { if (level.IsTarget(coord) && simpleDeadlockMap[coord]) { return true; } } return false; }
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; }