Exemplo n.º 1
0
  /**
   * Loads the level collection stored in the passed file.
   *
   * @param collectionFilePath path and name of the collection to load
   * @return the <code>LevelCollection</code> created from the read in data
   * @throws IOException the collection file couldn't be read
   */
  public final LevelCollection getLevelCollectionFromFile(String collectionFilePath)
      throws IOException {

    // ArrayList, for storing the read data.
    List<String> inputData = new ArrayList<String>(1000);

    // Create BufferedReader for the input file.
    BufferedReader levelFile = Utilities.getBufferedReader(collectionFilePath);

    // The file hasn't been found => return null.
    if (levelFile == null) {
      throw new FileNotFoundException(Texts.getText("message.fileMissing", collectionFilePath));
    }

    // Read in line by line of the input data.
    String levelDataRow;
    try {
      while ((levelDataRow = levelFile.readLine()) != null) {
        inputData.add(levelDataRow);
      }
    } finally {
      levelFile.close();
    }

    // Parse the read data and return the collection created from that data.
    // The level collection to be returned.
    LevelCollection levelCollection = dataParser.extractData(inputData, collectionFilePath);

    // Return the collection.
    return levelCollection;
  }
  /**
   * Tries to solve the level by generating all possible no-deadlock board positions and returns the
   * solution path via a global variable.
   */
  protected final void forwardSearch() {

    // Hold a box position and the new box position.
    int boxPosition;
    int newBoxPosition = 0;

    // Object for the current board position
    IBoardPositionMoves currentBoardPositionWithMoves;

    // The board position to be analyzed further.
    IBoardPositionMoves boardPositionToBeAnalyzed;

    // Object only used for avoiding too many casts.
    IBoardPositionMoves oldBoardPositionWithMoves;

    // If a board position is reached that has been reached before this object holds
    // the board position that has been reached before.
    IBoardPosition oldBoardPosition;

    // Pushes lower bound of a board position.
    int currentBoardPositionLowerBound = 0;

    // Number of moves the player has gone for reaching the current board position.
    short numberOfMovesSoFar = 0;

    // Number of moves and pushes of the current best known solution.
    int numberOfMovesBestSolution = Integer.MAX_VALUE;
    int numberOfPushesBestSolution = Integer.MAX_VALUE;

    // Number of the pushed box.
    int pushedBoxNo = 0;

    // Number of pushes of the board positions.
    int numberOfPushesOldBoardPosition = 0;
    int numberOfPushesCurrentBoardPosition = 0;

    // Lowest number of moves of a board position in the queue so far.
    int shortestSolutionPathLengthSoFar = 0;

    // The board position with the lowest estimated solution path length is analyzed further next.
    while ((boardPositionToBeAnalyzed = getBestBoardPosition()) != null && isCancelled() == false) {

      // Set the board position.
      board.setBoardPosition(boardPositionToBeAnalyzed);

      // Determine the reachable squares of the player. These squares are used even after
      // the deadlock detection, hence they are calculated in an extra object.
      playersReachableSquaresMoves.update();

      // Get number of the last pushed box.
      pushedBoxNo = boardPositionToBeAnalyzed.getBoxNo();

      // If no box has been pushed the pushed box number is set to -1, so the tunnel detection isn't
      // performed.
      if (pushedBoxNo == NO_BOX_PUSHED) {
        pushedBoxNo = -1;
      }

      // Calculate the number of pushes of the new board positions that are created.
      numberOfPushesCurrentBoardPosition = boardPositionToBeAnalyzed.getPushesCount() + 1;

      // Loop over all boxes. The last pushed box is considered first.
      for (int boxCounter = -1, boxNo; boxCounter < board.boxCount; boxCounter++) {

        // The last pushed box has already been processed (-> boxCounter = -1)
        if (boxCounter == pushedBoxNo) {
          continue;
        }

        // The last pushed box is considered first. It is checked for being in a tunnel.
        if (boxCounter == -1) {
          boxNo = pushedBoxNo;

          // If the box is in a tunnel only pushes of this box have to be considered!
          if (isBoxInATunnel(pushedBoxNo, boardPositionToBeAnalyzed.getDirection())) {
            boxCounter = board.goalsCount;
          }
        } else {
          boxNo = boxCounter;
        }

        // Get the position of the box
        boxPosition = board.boxData.getBoxPosition(boxNo);

        // Push the box to every direction possible.
        for (int direction = 0; direction < 4; direction++) {

          // Calculate the new box position.
          newBoxPosition = boxPosition + offset[direction];

          // Immediately continue with the next direction if the player can't reach the correct
          // position for pushing or the new box position isn't accessible.
          if (playersReachableSquaresMoves.isSquareReachable(boxPosition - offset[direction])
                  == false
              || board.isAccessibleBox(newBoxPosition) == false) {
            continue;
          }

          // Do push.
          board.pushBox(boxPosition, newBoxPosition);
          board.playerPosition = boxPosition;

          // Calculate the number of moves so far.
          numberOfMovesSoFar =
              (short)
                  (boardPositionToBeAnalyzed.getTotalMovesCount()
                      + playersReachableSquaresMoves.getDistance(boxPosition - offset[direction])
                      + 1);

          // Immediately continue with the next direction if the the board position isn't
          // reached better than the current best solution.
          if (numberOfPushesCurrentBoardPosition > numberOfPushesBestSolution
              || numberOfPushesCurrentBoardPosition == numberOfPushesBestSolution
                  && numberOfMovesSoFar >= numberOfMovesBestSolution) {
            board.pushBoxUndo(newBoxPosition, boxPosition);
            continue;
          }

          // Create object of the current board position.
          currentBoardPositionWithMoves =
              new RelativeBoardPositionMoves(board, boxNo, direction, boardPositionToBeAnalyzed);

          // Try to read the current board position of the hash table.
          oldBoardPosition = positionStorage.getBoardPosition(currentBoardPositionWithMoves);

          // If the board position had already been saved in the hash table it must be checked
          // for being better than the one in the hash table (it may have already been reached
          // by the corral detection hence there has to be a check for a SearchBoardPosition!)
          if (oldBoardPosition instanceof IBoardPositionMoves) {

            // For avoiding too many casts.
            oldBoardPositionWithMoves = (IBoardPositionMoves) oldBoardPosition;

            // Calculate the number of pushes of the old board position.
            numberOfPushesOldBoardPosition = oldBoardPositionWithMoves.getPushesCount();

            // If the current board position has been reached better than the one in the
            // hash table it has to be saved / used instead of the old one.
            if (numberOfPushesCurrentBoardPosition < numberOfPushesOldBoardPosition
                || numberOfPushesCurrentBoardPosition == numberOfPushesOldBoardPosition
                    && numberOfMovesSoFar < oldBoardPositionWithMoves.getTotalMovesCount()) {

              // Save the number of moves in the current board position.
              currentBoardPositionWithMoves.setMovesCount(numberOfMovesSoFar);

              // Replace the old board position by the new one in the hash table.
              positionStorage.storeBoardPosition(currentBoardPositionWithMoves);

              // The current board position is saved for further analyzing.
              storeBoardPosition(currentBoardPositionWithMoves);
            }

            // Undo push and continue with next direction.
            board.pushBoxUndo(newBoxPosition, boxPosition);
            continue;
          }

          /*
           * The board position hasn't already been in the hash table, hence it is a new one.
           */
          currentBoardPositionLowerBound =
              lowerBoundCalcuation.calculatePushesLowerBound(newBoxPosition);

          // Undo push (the player is new positioned for the next board position anyway)
          board.pushBoxUndo(newBoxPosition, boxPosition);

          // Immediately continue with the next direction if the current board position is a
          // deadlock
          // or the estimated solution path length is higher/equal than the best known solution path
          // length.
          if (currentBoardPositionLowerBound == LowerBoundCalculation.DEADLOCK
              || numberOfPushesCurrentBoardPosition == numberOfPushesBestSolution
                  && numberOfMovesSoFar + currentBoardPositionLowerBound
                      >= numberOfMovesBestSolution) {
            continue;
          }

          // Save the number of moves in the current board position.
          currentBoardPositionWithMoves.setMovesCount(numberOfMovesSoFar);

          // If a solution has been found the number of moves and the board position itself
          // are saved. This solution is push optimal, but it needn't to be the one with
          // best moves, too!
          if (currentBoardPositionLowerBound == 0) {
            numberOfMovesBestSolution = numberOfMovesSoFar;
            numberOfPushesBestSolution = numberOfPushesCurrentBoardPosition;
            solutionBoardPosition = currentBoardPositionWithMoves;
            if (Debug.isDebugModeActivated) {
              System.out.println(
                  "Solution Found "
                      + "Moves/Pushes: "
                      + currentBoardPositionWithMoves.getTotalMovesCount()
                      + "/"
                      + currentBoardPositionWithMoves.getPushesCount());
            }
            continue;
          }

          // Calculate the number of no deadlock board positions reached during the search.
          boardPositionsCount++;

          // Display info about the search (every 5000 board positions and every time the search
          // depths has been increased)
          if (boardPositionsCount % 5000 == 0
              || shortestSolutionPathLengthSoFar != shortestSolutionPathLength) {
            if (shortestSolutionPathLengthSoFar != shortestSolutionPathLength) {
              shortestSolutionPathLengthSoFar = shortestSolutionPathLength;
            }

            publish(
                Texts.getText("numberofpositions")
                    + boardPositionsCount
                    + ", "
                    + Texts.getText("searchdepth")
                    + shortestSolutionPathLength
                    + " "
                    + Texts.getText("moves"));

            // Throw "out of memory" if less than 15MB RAM is free.
            if (Utilities.getMaxUsableRAMinMiB() <= 15) {
              isSolverStoppedDueToOutOfMemory = true;
              cancel(true);
            }
          }

          // Save the board position in the hash table for further searching.
          positionStorage.storeBoardPosition(currentBoardPositionWithMoves);
          storeBoardPosition(currentBoardPositionWithMoves);
        }
      }
    }
  }