如果猜测行或列0,则无法正确打印扫雷

时间:2017-11-08 20:41:59

标签: java arrays printing minesweeper

嘿所有我对我的扫雷舰代码都有一些问题:



public class MineSweeper {

  /**
   * This is the main method for Mine Sweeper game!
   * This method contains the within game and play again loops and calls
   * the various supporting methods.
   *  
   * @param args (unused)
   */
  public static void main(String[] args) {
    //Variable Declaration
    String promptWidth = "What width of map would you like (3 - 20): ";
    String promptHeight = "What height of map would you like (3 - 20): ";
    String promptRow = "row: ";
    String promptColumn = "column: ";
    int mapWidth = 0;
    int mapHeight = 0;
    int userRow = 0;
    int userColumn = 0;
    int numberOfMines = 0;
    int adjacentMines = 0;
    String endGame = " ";
    boolean continueGame = false;
    char adjacentMinesChar = 0;
    boolean NoSafeLocations = false;


    Scanner scnr = new Scanner(System.in); //initialize scanner
    Random randGen = new Random(Config.SEED); //initialize random generator


    System.out.println("Welcome to Mine Sweeper!"); //Welcome message 

    do {
      boolean userWin = false;
      boolean userLoss = false;
      //user prompts for map sizing
      mapWidth = promptUser(scnr, promptWidth, Config.MIN_SIZE, Config.MAX_SIZE);
      mapHeight = promptUser(scnr, promptHeight, Config.MIN_SIZE, Config.MAX_SIZE);
      System.out.println();

      //creation of the board
      char[][] map = new char[mapHeight][mapWidth]; //initializing the map array with the user input width and height
      eraseMap(map); //map creation
      boolean[][] mines = new boolean[mapHeight][mapWidth];
      numberOfMines = placeMines(mines, randGen); //initializes the mines and brings back number of them

      while (userWin == false && userLoss == false) { //game loop begins and continues until player either wins or loses
        System.out.println("Mines: " + numberOfMines); //prints out number of mines
        printMap(map);

        //User play begins
        //user selects a field to sweep
        userRow = promptUser(scnr, promptRow, 1, (mapHeight)) - 1;
        userColumn = promptUser(scnr, promptColumn, 1, (mapWidth)) - 1;
        if (mines[userRow][userColumn] == true) { //if user selects mine he loses end of the game
          map[userRow][userColumn] = Config.SWEPT_MINE;
          showMines(map, mines);
          printMap(map);
          System.out.println("Sorry, you lost.");
          userLoss = true;
        } else { //if user selects a mine free field the play continues 
          adjacentMines = sweepLocation(map, mines, userRow, userColumn);
          if (adjacentMines == -2) { //if user selects a previously selected field just reprinting the map
            printMap(map);
          } else if (adjacentMines == -1) { //if user selects a field with a mine all mines shown
            showMines(map, mines);
          } else {
            if (adjacentMines == 0) { //if a user selects a field with no nearby mines
              sweepAllNeighbors(map, mines, userRow, userColumn);
            } else { //if a user selects a field with some nearby mines
              printMap(map);
            }
            //Checks if all locations without mines have been swept if so user wins and the loop is broken
            NoSafeLocations = allSafeLocationsSwept(map, mines);
            if (NoSafeLocations == false) {
              System.out.println();
            }
            if (NoSafeLocations == true) {
              showMines(map, mines);
              printMap(map);
              System.out.println("You Win!");
              userWin = true;
            }
          }
        }
      }

      //Ending the game
      System.out.print("Would you like to play again (y/n)? ");
      endGame = scnr.next().trim().toLowerCase();

      if (endGame.charAt(0) == 'y') { //if user inputs a word starting with y - the game continues
        continueGame = true;
      } else { //if user enters a character other than y the game ends
        continueGame = false;
      }
    } while (continueGame == true);
    System.out.println("Thank you for playing Mine Sweeper!"); //goodbye statement
  }


  /**
   * This method prompts the user for a number, verifies that it is between min
   * and max, inclusive, before returning the number.  
   * 
   * If the number entered is not between min and max then the user is shown 
   * an error message and given another opportunity to enter a number.
   * If min is 1 and max is 5 the error message is:
   *      Expected a number from 1 to 5.  
   * 
   * If the user enters characters, words or anything other than a valid int then 
   * the user is shown the same message.  The entering of characters other
   * than a valid int is detected using Scanner's methods (hasNextInt) and
   * does not use exception handling.
   * 
   * Do not use constants in this method, only use the min and max passed
   * in parameters for all comparisons and messages.
   * Do not create an instance of Scanner in this method, pass the reference
   * to the Scanner in main, to this method.
   * The entire prompt should be passed in and printed out.
   *
   * @param in  The reference to the instance of Scanner created in main.
   * @param prompt  The text prompt that is shown once to the user.
   * @param min  The minimum value that the user must enter.
   * @param max  The maximum value that the user must enter.
   * @return The integer that the user entered that is between min and max, 
   *          inclusive.
   */
  public static int promptUser(Scanner in , String prompt, int min, int max) {
    //initialize variables
    Integer userInput = 0;
    boolean userInteger = false;

    System.out.print(prompt); //prompts the user for input
    userInteger = in .hasNextInt();

    while (userInteger == false) { //checks if user input is an integer if not prints out an error
      System.out.println("Expected a number from " + min + " to " + max + "."); in .nextLine();
      userInteger = in .hasNextInt();
    }
    while (userInteger == true) { //if user in put is an integer checks that it fits between allowed min and max
      userInput = in .nextInt();
      while (userInput > max || userInput < min) {
        System.out.println("Expected a number from " + min + " to " + max + "."); in .nextLine();
        userInteger = in .hasNextInt();

        while (userInteger == false) { //if user didn't fit between min and max checks again for integer input
          System.out.println("Expected a number from " + min + " to " + max + "."); in .nextLine();
          userInteger = in .hasNextInt();

        }
        userInput = in .nextInt(); in .nextLine();
      }
      userInteger = false;
    }

    return userInput; //returns user input
  }

  /**
   * This initializes the map char array passed in such that all
   * elements have the Config.UNSWEPT character.
   * Within this method only use the actual size of the array. Don't
   * assume the size of the array.
   * This method does not print out anything. T	his method does not
   * return anything.
   * 
   * @param map An allocated array. After this method call all elements
   *      in the array have the same character, Config.UNSWEPT. 
   */
  public static void eraseMap(char[][] map) {
    for (int i = 0; i < map.length; i++) {
      for (int j = 0; j < map[0].length; j++) {
        map[i][j] = Config.UNSWEPT;
      }
    }
    return; //sets up a clean map array
  }

  /**
   * This prints out a version of the map without the row and column numbers.
   * A map with width 4 and height 6 would look like the following: 
   *  . . . .
   *  . . . .
   *  . . . .
   *  . . . .
   *  . . . .
   *  . . . .
   * For each location in the map a space is printed followed by the 
   * character in the map location.
   * @param map The map to print out.
   */
  public static void simplePrintMap(char[][] map) {
    for (int i = 0; i < map.length; i++) {
      for (int j = 0; j < map[0].length; j++) {
        System.out.print(" " + map[i][j]);
      }
      System.out.println();
    }
    return; //Prints out a map without labels
  }

  /**
   * This prints out the map. This shows numbers of the columns
   * and rows on the top and left side, respectively. 
   * map[0][0] is row 1, column 1 when shown to the user.
   * The first column, last column and every multiple of 5 are shown.
   * 
   * To print out a 2 digit number with a leading space if the number
   * is less than 10, you may use:
   *     System.out.printf("%2d", 1); 
   * 
   * @param map The map to print out.
   */
  public static void printMap(char[][] map) {
    for (int j = 0; j < map[0].length; j++) { //prints out the labels for columns)
      if (j == 0) {
        System.out.print("   1");
      } else if (j == map.length - 1) {
        System.out.printf("%2d", (j + 1));
      } else if ((j + 1) % 5 == 0) {
        System.out.printf("%2d", (j + 1));
      } else {
        System.out.print("--");
      }


    }
    System.out.println();
    for (int i = 0; i < map.length; i++) { //prints out labels for rows and the map
      if (i == 0) {
        System.out.print(" 1");
      } else if (i == map.length - 1) {
        System.out.printf("%2d", (i + 1));
      } else if ((i + 1) % 5 == 0) {
        System.out.printf("%2d", (i + 1));
      } else {
        System.out.print(" |");
      }
      for (int j = 0; j < map[0].length; j++) {
        if (map[i][j] == Config.NO_NEARBY_MINE) {
          System.out.print(" T " + (map[i][j]));
        } else {
          System.out.print(" " + (map[i][j]));
        }
      }
      System.out.println();
    }

    return; //prints out a map with row and column labels
  }

  /**
   * This method initializes the boolean mines array passed in. A true value for
   * an element in the mines array means that location has a mine, false means
   * the location does not have a mine. The MINE_PROBABILITY is used to determine
   * whether a particular location has a mine. The randGen parameter contains the
   * reference to the instance of Random created in the main method.
   * 
   * Access the elements in the mines array with row then column (e.g., mines[row][col]).
   * 
   * Access the elements in the array solely using the actual size of the mines
   * array passed in, do not use constants. 
   * 
   * A MINE_PROBABILITY of 0.3 indicates that a particular location has a
   * 30% chance of having a mine.  For each location the result of
   *      randGen.nextFloat() < Config.MINE_PROBABILITY 
   * determines whether that location has a mine.
   * 
   * This method does not print out anything.
   *  
   * @param mines  The array of boolean that tracks the locations of the mines.
   * @param randGen The reference to the instance of the Random number generator
   *      created in the main method.
   * @return The number of mines in the mines array.
   */
  public static int placeMines(boolean[][] mines, Random randGen) {
    int mineCount = 0;
    for (int i = 0; i < mines.length; i++) {
      for (int j = 0; j < mines[0].length; j++) {
        if (randGen.nextFloat() < Config.MINE_PROBABILITY) {
          mines[i][j] = true;
          ++mineCount;
        }
      }
    }
    return mineCount; //returns the total count of mines on the map
  }

  /**
   * This method returns the number of mines in the 8 neighboring locations.
   * For locations along an edge of the array, neighboring locations outside of 
   * the mines array do not contain mines. This method does not print out anything.
   * 
   * If the row or col arguments are outside the mines array, then return -1.
   * This method (or any part of this program) should not use exception handling.
   * 
   * @param mines The array showing where the mines are located.
   * @param row The row, 0-based, of a location.
   * @param col The col, 0-based, of a location.
   * @return The number of mines in the 8 surrounding locations or -1 if row or col
   *      are invalid.
   */
  public static int numNearbyMines(boolean[][] mines, int row, int col) {
    int mineCount = 0;
    if (row == 0) { //checks all the fields for a field in the first row
      if (col == 0) {
        if (mines[row + 1][col] == true) {
          mineCount++;
        }
        if (mines[row + 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }
      } else if (col == mines[0].length - 1) {
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col] == true) {
          mineCount++;
        }

        if (mines[row + 1][col - 1]) {
          mineCount++;
        }
      } else {
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col] == true) {
          mineCount++;
        }
        if (mines[row + 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col - 1]) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }
      }
    } else if (row == mines.length - 1) { //checks all the fields for a field in the last row
      if (col == 0) {
        if (mines[row - 1][col] == true) {
          mineCount++;
        }
        if (mines[row - 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }
      } else if (col == mines[0].length - 1) {
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row - 1][col - 1] == true) {
          mineCount++;
        }
        if (mines[row - 1][col] == true) {
          mineCount++;
        }
      } else {
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row - 1][col - 1] == true) {
          mineCount++;
        }
        if (mines[row - 1][col] == true) {
          mineCount++;
        }
        if (mines[row - 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }
      }
    } else { //check all the fields for a field not in the first or the last row
      if (col == 0) {
        if (mines[row - 1][col] == true) {
          mineCount++;
        }
        if (mines[row - 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col] == true) {
          mineCount++;
        }
      } else if (col == mines[0].length - 1) {
        if (mines[row - 1][col] == true) {
          mineCount++;
        }
        if (mines[row - 1][col - 1] == true) {
          mineCount++;
        }
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col - 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col] == true) {
          mineCount++;
        }
      } else {
        if (mines[row - 1][col] == true) {
          ++mineCount;
        }
        if (mines[row - 1][col - 1] == true) {
          mineCount++;
        }
        if (mines[row - 1][col + 1]) {
          mineCount++;
        }
        if (mines[row][col - 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col] == true) {
          mineCount++;
        }
        if (mines[row + 1][col + 1] == true) {
          mineCount++;
        }
        if (mines[row + 1][col - 1]) {
          mineCount++;
        }
        if (mines[row][col + 1] == true) {
          mineCount++;
        }

      }
    }
    return mineCount; //returns the number of mines in the spots next to user picked spot
  }

  /**
   * This updates the map with each unswept mine shown with the Config.HIDDEN_MINE
   * character. Swept mines will already be mapped and so should not be changed.
   * This method does not print out anything.
   * 
   * @param map  An array containing the map. On return the map shows unswept mines.
   * @param mines An array indicating which locations have mines.  No changes
   *      are made to the mines array.
   */
  public static void showMines(char[][] map, boolean[][] mines) {
    for (int i = 0; i < mines.length; i++) {
      for (int j = 0; j < mines[0].length; j++) {
        if (map[i][j] == Config.SWEPT_MINE) {
          map[i][j] = Config.SWEPT_MINE;
        } else if (mines[i][j] == true) {
          map[i][j] = Config.HIDDEN_MINE;
        }
      }
    }
    return; //updates the map array to show uncovered mines
  }

  /**
   * Returns whether all the safe (non-mine) locations have been swept. In 
   * other words, whether all unswept locations have mines. 
   * This method does not print out anything.
   * 
   * @param map The map showing touched locations that is unchanged by this method.
   * @param mines The mines array that is unchanged by this method.
   * @return whether all non-mine locations are swept.
   */
  public static boolean allSafeLocationsSwept(char[][] map, boolean[][] mines) {
    boolean allSafeSwept = true;
    for (int i = 0; i < mines.length; i++) {
      for (int j = 0; j < mines[0].length; j++) {
        if (mines[i][j] == false && map[i][j] == Config.UNSWEPT) {
          allSafeSwept = false;
        }
      }
    }
    return allSafeSwept; //returns true if all safe locations have been swept

  }

  /**
   * This method sweeps the specified row and col.
   *   - If the row and col specify a location outside the map array then 
   *     return -3 without changing the map.***
   *   - If the location has already been swept then return -2 without changing
   *     the map.
   *   - If there is a mine in the location then the map for the corresponding
   *     location is updated with Config.SWEPT_MINE and return -1.
   *   - If there is not a mine then the number of nearby mines is determined 
   *     by calling the numNearbyMines method. 
   *        - If there are 1 to 8 nearby mines then the map is updated with 
   *          the characters '1'..'8' indicating the number of nearby mines.
   *        - If the location has 0 nearby mines then the map is updated with
   *          the Config.NO_NEARBY_MINE character.
   *        - Return the number of nearbyMines.
   *        
   * @param map The map showing swept locations.
   * @param mines The array showing where the mines are located.
   * @param row The row, 0-based, of a location.
   * @param col The col, 0-based, of a location.
   * @return the number of nearby mines, -1 if the location is a mine, -2 if 
   * the location has already been swept, -3 if the location is off the map.
   */
  public static int sweepLocation(char[][] map, boolean[][] mines, int row, int col) {
    int nearbyMines = 0;
    char nearbyMinesChar = 'x'; //returns -3 if the user spot is out of range of the array
    if (row < 0 || row >= map.length || col < 0 || col >= map[0].length) {
      return -3;
    } else if (map[row][col] != Config.UNSWEPT) { //returns -2 if user spot has been previously swept
      return -2;
    } else if (mines[row][col] == true) { //returns -1 if there is a mine in the user spot
      map[row][col] = Config.SWEPT_MINE;
      return -1;
    } else if (mines[row][col] == false) { //checks for number of mines if the user spot has not been previously swept
      nearbyMines = numNearbyMines(mines, row, col);
      if (nearbyMines >= 1 || nearbyMines <= 8) { //if there is at least one mine nearby returns number of mines
        nearbyMinesChar = Character.forDigit(nearbyMines, nearbyMines + 1);
        map[row][col] = nearbyMinesChar;
      } else if (nearbyMines == 0) { //if there are no nearby mines sets map to NO_NEARBY_MINE
        map[row][col] = Config.NO_NEARBY_MINE;
      }
      return nearbyMines;
    }
    return 0;
  }

  /**
   * This method iterates through all 8 neighboring locations and calls sweepLocation
   * for each. It does not call sweepLocation for its own location, just the neighboring
   * locations.
   * @param map The map showing touched locations.
   * @param mines The array showing where the mines are located.
   * @param row The row, 0-based, of a location.
   * @param col The col, 0-based, of a location.
   */
  public static void sweepAllNeighbors(char[][] map, boolean[][] mines, int row, int col) {
    if (row == 0) { //Calls sweep location for all the locations in the first row
      if (col == 0) {
        sweepLocation(map, mines, row + 1, col);
        sweepLocation(map, mines, row + 1, col + 1);
        sweepLocation(map, mines, row, col + 1);
      } else if (col == mines[0].length - 1) {
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row + 1, col);
        sweepLocation(map, mines, row + 1, col - 1);
      } else {
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row + 1, col);
        sweepLocation(map, mines, row + 1, col + 1);
        sweepLocation(map, mines, row + 1, col - 1);
        sweepLocation(map, mines, row, col + 1);
      }
    } else if (row == mines.length - 1) { //Calls sweep location for all the locations in the last row
      if (col == 0) {
        sweepLocation(map, mines, row - 1, col);
        sweepLocation(map, mines, row - 1, col + 1);
        sweepLocation(map, mines, row, col + 1);
      } else if (col == mines[0].length - 1) {
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row - 1, col - 1);
        sweepLocation(map, mines, row - 1, col);
      } else {
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row - 1, col - 1);
        sweepLocation(map, mines, row - 1, col);
        sweepLocation(map, mines, row - 1, col + 1);
        sweepLocation(map, mines, row, col + 1);
      }
    } else { //Calls sweep location for all the locations in not in the first or last row
      if (col == 0) {
        sweepLocation(map, mines, row - 1, col);
        sweepLocation(map, mines, row - 1, col + 1);
        sweepLocation(map, mines, row, col + 1);
        sweepLocation(map, mines, row + 1, col + 1);
        sweepLocation(map, mines, row + 1, col);
      } else if (col == mines[0].length - 1) {
        sweepLocation(map, mines, row - 1, col);
        sweepLocation(map, mines, row - 1, col - 1);
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row + 1, col - 1);
        sweepLocation(map, mines, row + 1, col);
      } else {
        sweepLocation(map, mines, row - 1, col);
        sweepLocation(map, mines, row - 1, col - 1);
        sweepLocation(map, mines, row - 1, col + 1);
        sweepLocation(map, mines, row, col - 1);
        sweepLocation(map, mines, row + 1, col);
        sweepLocation(map, mines, row + 1, col + 1);
        sweepLocation(map, mines, row + 1, col - 1);
        sweepLocation(map, mines, row, col + 1);
      }
    }
    return;
  }
}
&#13;
&#13;
&#13;

此代码的行为如下:

code

然而,当我跑它时,它会改变我的线条。

it shifts my lines

我一直试图将间距改变几个小时,而且我正在撞墙。我想也许添加一个if语句会有所帮助。保持最低限度,插入的是一个常量(这只是一个空格) - 我知道代码中存在一些缺陷并且可以改进但是我专注于尝试使其正常工作 - 任何帮助将深表感谢。

1 个答案:

答案 0 :(得分:0)

sweepLocation中查看此行:

if (nearbyMines >= 1 || nearbyMines <= 8) { //if there is at least one mine nearby returns number of mines

if语句中的条件何时为真?

答案是它永远是真的!任何数字至少为1 最多为8(或两者)。如果你不相信我,请检查一下!

您可能希望检查nearbyMines至少为1 最多为8.您可以将||替换为&&,即:

if (nearbyMines >= 1 && nearbyMines <= 8) { //if there is at least one mine nearby returns number of mines

网格中的线条被移动的原因是由于这一行:

    nearbyMinesChar = Character.forDigit(nearbyMines, nearbyMines + 1);

Character.forDigit的第二个参数是数字的'基数'或数字基数。如果基数超出范围(并且范围恰好是2到36),则此方法将返回空字符。如果nearbyMines为零,则最终为基数传递1,该基数超出范围,因此您返回空字符。这些行最终被移动,因为打印空字符什么都不做。

我真的不明白为什么你在这里传递nearbyMines + 1为基数;使用10会更简单,因为所有数字都在基数10中。

在对代码进行这些更改之后,它看起来像我预期的那样工作得更多,因为换行的问题已经消失了。 (我无法完全重现你的网格,但那是因为我不知道你设置Config.SEED的价值。对我来说,无论如何,将种子设置为零给了我一个没有地雷的网格,这是足以重现你的问题。)