예제 #1
0
  /**
   * Saves the passed collection to the passed file.
   *
   * @param levelCollection the level collection to be saved
   * @param fileName the file the level is to be saved to
   * @throws IOException thrown when the level couldn't be saved
   */
  public final void saveCollection(LevelCollection levelCollection, String fileName)
      throws IOException {

    // Create a PrintWriter for writing the data to hard disk.
    PrintWriter collectionFile = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));

    // Write the collection data
    if (!levelCollection.getTitle().isEmpty()) {
      collectionFile.println("Title: " + levelCollection.getTitle());
    }

    Author collectionAuthor = levelCollection.getAuthor();
    if (!collectionAuthor.getName().equals(Texts.getText("unknown"))) {
      collectionFile.println("Author: " + collectionAuthor.getName());
    }
    if (!collectionAuthor.getEmail().isEmpty()) {
      collectionFile.println("Email: " + collectionAuthor.getEmail());
    }
    if (!collectionAuthor.getWebsiteURL().isEmpty()) {
      collectionFile.println("Homepage: " + collectionAuthor.getWebsiteURL());
    }
    if (!collectionAuthor.getComment().isEmpty()) {
      collectionFile.println("Author comment: " + collectionAuthor.getComment());
    }
    if (!levelCollection.getComment().isEmpty()) {
      collectionFile.println(levelCollection.getComment());
    }

    // Loop over all levels of the collection and write their data to the file.
    // If the author of the level is identical to the collection author
    // then don't write the author data for such levels.
    for (Level level : levelCollection) {
      Author levelAuthor = level.getAuthor();
      if (levelAuthor.equals(collectionAuthor)) {
        level.setAuthor(new Author());
      }
      writeLevelToFile(level, collectionFile);
      level.setAuthor(levelAuthor);
    }

    // Check the error status.
    boolean isFileSavingFailed = collectionFile.checkError();

    // Close the file.
    collectionFile.close();

    // Throw exception in the case of an error.
    if (isFileSavingFailed) {
      throw new IOException(Texts.getText("errorBySaving"));
    }
  }
예제 #2
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;
  }
예제 #3
0
  /**
   * Saves the passed level using the passed file name.
   *
   * @param level the <code>Level</code> to save
   * @param fileName the file the level is to be saved to
   * @throws IOException thrown when the level couldn't be saved
   */
  public final void saveLevel(Level level, String fileName) throws IOException {

    // Create a PrintWriter for writing the data to hard disk.
    PrintWriter levelFile = new PrintWriter(fileName);

    // Write the level data to the file.
    writeLevelToFile(level, levelFile);

    // Check the error status.
    boolean isFileSavingFailed = levelFile.checkError();

    // Close the file.
    levelFile.close();

    // Throw exception in the case of an error.
    if (isFileSavingFailed) {
      throw new IOException(Texts.getText("errorBySaving"));
    }
  }
예제 #4
0
  /**
   * Writes the passed level data into the passed file.
   *
   * @param level the level to be saved
   * @param PrintWriter the file to write to
   */
  private final void writeLevelToFile(Level level, PrintWriter file) {

    // Stores the board data of the level.
    List<String> boardData;

    file.println();
    file.println();
    file.println(level.getTitle());
    file.println();

    // Get the board data of the level.
    boardData = level.getBoardData();

    // Write the board to the file.
    for (String boardRow : boardData) {
      file.println(boardRow);
    }

    // Empty line between board and transformation data.
    file.println();

    // Save the transformation.
    if (!Transformation.getTransformationAsString().isEmpty()) {

      // Write the transformation string.
      file.write(Transformation.getTransformationAsString());

      // Write empty lines.
      file.println();
      file.println();
    }

    // Write the additional level data.
    if (level.getComment().length() > 0) {
      file.println(level.getComment());
    }

    Author author = level.getAuthor();
    if (!author.getName().equals(Texts.getText("unknown"))) {
      file.println("Author: " + author.getName());
    }
    if (author.getEmail().length() > 0) {
      file.println("Email: " + author.getEmail());
    }
    if (author.getWebsiteURL().length() > 0) {
      file.println("Homepage: " + author.getWebsiteURL());
    }
    if (author.getComment().length() > 0) {
      file.println("Author comment: " + author.getComment());
    }
    if (level.getDifficulty().length() > 0) {
      file.println("Difficulty: " + level.getDifficulty());
    }

    // Save the solution information.
    SolutionsManager solutions = level.getSolutionsManager();

    for (int solutionNo = 0; solutionNo < solutions.getSolutionCount(); solutionNo++) {
      Solution solution = solutions.getSolution(solutionNo);

      file.println();
      file.println("Solution " + solution.movesCount + "/" + solution.pushesCount);
      // FFS/hm: also write minor metrics?
      file.println(solution.lurd);
      if (solution.name.length() > 0) {
        file.println("Solution name: " + solution.name);
      }
      if (solution.isOwnSolution) {
        file.println("Own solution: yes");
      }
      if (solution.comment.length() > 0) {
        file.println("Solution comment: " + solution.comment);
        file.println("Solution comment end:");
      }
    }

    // Get the LURD-representation of the history.
    String historyLURD = level.getHistory().getHistoryAsSaveGame();

    // Save the history string if there is any
    if (!historyLURD.isEmpty()) {
      file.println();
      file.println("Savegame:");
      file.println(historyLURD);
    }
  }
  /**
   * 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);
        }
      }
    }
  }