首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >初学者Java扫雷游戏

初学者Java扫雷游戏
EN

Code Review用户
提问于 2017-08-08 02:48:07
回答 1查看 7.3K关注 0票数 1

我在Java 8中做了一个扫雷游戏,但我不确定代码是否好。注释和问题都在代码中。

下面是Github上的代码(但没有注释)。

Game.java:

游戏的主类,处理输入和输出以及其他一些事情(我不确定我是否应该删除这个类)。

代码语言:javascript
复制
import java.util.*;

public class Game {

Board board;

public Game(int length, int width,int numOfMines) {

    board = new Board(length, width, numOfMines);

    help();

    // First choice isn't guaranteed to be safe
    // TODO: generate the board when the user makes his first choice ( to make sure it's safe )

    System.out.println("Generating Board");

    start();

}

public void start() {
    while (true) {
        userInput();
    }
}

public static void main(String[] args) {

    // currently doesn't check if  numOfMines > (length*width)

    Scanner scan = new Scanner(System.in);
    System.out.print("Length: ");
    int length = scan.nextInt();

    System.out.print("Width: ");
   int width =  scan.nextInt();

    System.out.print("Number Of Mines: ");
    int numOfMines = scan.nextInt();

    new Game(length,width,numOfMines);
}


public void help() {
    System.out.println();
    System.out.println("Commands:");
    System.out.println("           \"help\" opens the help menu");
    System.out.println("           \"choose\" specify which tile you want to check");
    System.out.println("           \"flag\" specify which tile you want to flag");
    System.out.println("           \"restart\" start a new game");
    System.out.println("           \"quit\" to quit the game");
    System.out.println();
}

// handles user input (not sure if this is the best way to handle it)
// there is a lot of exceptions that can be caught here but I'm leaving them for now

void userInput() {
    Scanner scan = new Scanner(System.in);

    System.out.print("$ ");
    String userInput;

    userInput = scan.nextLine();
    userInput = userInput.trim().toLowerCase();

    int row;
    int column;

    switch (userInput)
    {
        case "help":

            help();
            break;

        case "choose":

            System.out.print("Row: ");
            row = scan.nextInt() - 1;

            System.out.print("Column: ");
            column = scan.nextInt() - 1;

            choose(row, column);
            board.printBoard();
            break;

        case "restart":

            // currently doesn't check if  numOfMines > (length*width)
            System.out.print("Length: ");
            int length = scan.nextInt();

            System.out.print("Width: ");
            int width =  scan.nextInt();

            System.out.print("Number Of Mines: ");
            int numOfMines = scan.nextInt();

            restart(length, width, numOfMines);
            break;

        case "flag":

            System.out.print("Row: ");
            row = scan.nextInt()-1;

            System.out.print("Column: ");
            column =  scan.nextInt()-1;

            flag(row, column);
            board.printBoard();
            break;

        case "quit":

            scan.close();
            System.exit(0);

        case "":
            break;
        default:
            System.out.println("Invalid input");
            break;
    }
}

// checks Input:
// if the cell is  a mine then the user losses( not implemented yet)
// if it has a value( value>0 ) then it reveals the cell then prints the board
// if the cell doesn't have a value it (value == 0)  then it calls reveal()

void choose(int row, int column) {
    Cell cell = board.getBoard()[row][column];
    if (cell.isMine()) {
        board.printBoard();
        System.out.println("Lose");
    } else if (cell.hasValue()) {
        cell.show();
        board.printBoard();
    } else if (!cell.hasValue()) {
        reveal(cell, new ArrayList<>(), new ArrayList<>(),0);
    }
}

// queue contains empty cells that hasn't been processed ( processing means checking its surroundingCells )
// adds the Cells to the queue(DOESN'T ADD THEM IF THEY'RE IN THE "PROCESSED" ARRAYLIST) (and reveals it) if their value is equal to "0"(Empty)
// if their value > 0 (and not a mine) then it just reveals them
// if the cell is a mine it doesn't reveal them
// should returns if queue.isEmpty
// TODO: Bug: returns in a weird way.

public void reveal(Cell cell, ArrayList<Cell> queue, ArrayList<Cell> processed,Integer i) {

    // debugging( number of recursions )
    i++;

    cell.show();

    if (queue.isEmpty()) {
        ArrayList<Cell> surroudingCells = cell.getSurroundingCells();
        for (Cell cell2 : surroudingCells) {
            if (!cell2.hasValue() && !cell.isMine()) {
                queue.add(cell2);
            } else if (cell2.hasValue()) {
                cell2.show();
            }
        }

        if (queue.isEmpty()) {

        } else {
            reveal(queue.get(0), queue, processed, i);
        }
    } else {
        for (Cell cell2 : cell.getSurroundingCells()) {
            if (queue.contains(cell2)) {

            } else if (processed.contains(cell2)) {

            } else if (!cell2.hasValue() && !cell2.isMine()) {
                queue.add(cell2);
            } else if (cell2.hasValue()) {
                cell2.show();
            } else if (cell2.isShown()) {

            } else if (cell2.isMine()) {

            }
        }

        processed.add(cell);
        queue.remove(cell);

        if (queue.isEmpty()) {
            return;
        } else {
            reveal(queue.get(0), queue, processed,i);
        }
    }
    // Debugging
    board.printBoard();
    System.out.println(i);
}

// sets isFlagged to true/false depending on its state

void flag(int row, int column) {

    Cell cell = board.getBoard()[row][column];
    cell.setFlagged(!cell.isFlagged());

}


// creates a new board

void restart(int length, int width, int numOfMines) {
    System.out.println("Generating new Board");
    board = new Board(length, width, numOfMines);
}
}

Board.java:代表董事会。

创建一个由Cells填充的数组,处理每个单元格的属性,由Game.java用于与单元格通信。

代码语言:javascript
复制
import java.util.Random;
import java.util.Arrays;

public class Board {

private final int width;
private final int length;
private final int numOfMines;

private Cell[][] board;


public Board(int length, int width, int numOfMines) {

    this.width = width;
    this.length = length;
    this.numOfMines = numOfMines;

    board = new Cell[length][width];
    generate();
}

private void generate() {
    generateMines();
    generateNumbers();
    printSolvedBoard();
}

// creates a new cell that has a chance of being a mine (0.00001) ( The chance is low because I'm afraid that only the first few Cells are going to become mines)
// doesn't stop until there are the amount of mines specified by Game.java ( user )
// This is inefficient(loops the array 20000-30000 times when the number of mines = 20), I'm sure there is a better way to do this.


private void generateMines() {

    int currentNumOfMines = 0;
    Random random = new Random();


    while (currentNumOfMines < numOfMines) {

        for (int i = 0; i < length; i++) {
            for (int j = 0; j < width; j++) {

                double probability = random.nextDouble();

                if (board[i][j] == null) {
                    board[i][j] = new Cell(i, j, false, board.clone());
                } else if (board[i][j].isMine()) {

                } else if (probability > 0.99999 && currentNumOfMines < numOfMines) {
                    board[i][j] = new Cell(i, j, true, board.clone());
                    currentNumOfMines++;
                }
            }
        }
    }
}

// Calls Cell.setValue() method on every Cell in the board

private void generateNumbers() {
    for (int i = 0; i < length; i++) {
        for (int j = 0; j < width; j++) {
            if (board[i][j].isMine()) {

            } else {
                board[i][j].setValue();
            }
        }
    }
}
public void printBoard() {

    for (int i = 0; i < length; i++) {
        for (int j = 0; j < width; j++) {
            if (board[i][j].isMine()) {

            }
            System.out.print(" " + board[i][j].getSymbol());
        }
        System.out.println();
    }
}

public void printSolvedBoard() {

    for (int i = 0; i < length; i++) {
        for (int j = 0; j < width; j++) {
            if (board[i][j].isMine()) {

            }
            System.out.print(" " + board[i][j].getValue());
        }
        System.out.println();
    }
}

// I am not sure if I am overriding these correctly (hashCode(),equals())

@Override
public int hashCode() {
    return width * length * numOfMines + 13;
}

@Override
public boolean equals(Object obj) {
    Board boardObj = (Board) obj;
    return Arrays.deepEquals(boardObj.getBoard(), board);
}

public int getWidth() {
    return width;
}

public int getLength() {
    return length;
}

public int getNumOfMines() {
    return numOfMines;
}

public Cell[][] getBoard() {
    return board;
}
}

Cell.java:

表示Cell。知道它在数组中的位置,它是否是一个地雷,它是否被标记/显示,它也有它在其中的数组的克隆(我不确定这是否是一个好主意)。

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Arrays;

public class Cell {

// the Cells Position

private final int xPos;
private final int yPos;

// the Cells Properties
private final boolean isMine;
private boolean isFlagged;
private boolean isShown;

private String symbol;
private String value;

// the Cell's surroundings

private Cell[][] myBoard;
private ArrayList<Cell> surrondingCells;

public Cell(int x, int y, boolean isMine, Cell[][] board) {

    this.isMine = isMine;
    this.isFlagged = false;
    this.isShown = false;

    this.myBoard = board;
    surrondingCells = new ArrayList<>();

    this.xPos = x;
    this.yPos = y;
    symbol = "*";

    if (this.isMine) {
        value = "#";
    }
}

public boolean isMine() {
    return isMine;
}

public int getxPos() {
    return xPos;
}

public int getyPos() {
    return yPos;
}

public String getSymbol() {
    return symbol;
}

public void show() {
    symbol = value;
    isShown = true;
}

public boolean isShown() {
    return isShown;
}

public boolean hasValue() {

    if (isMine == true) return false;

    return Integer.parseInt(value) > 0;
}

public ArrayList<Cell> getSurroundingCells() {

    if (surrondingCells.isEmpty()) {
        setSurroundingCells();
    }

    return surrondingCells;
}

private void setSurroundingCells() {
    for (int i = xPos - 1; i <= xPos + 1; i++) {
        for (int j = yPos - 1; j <= yPos + 1; j++) {

            if (i == xPos && j == yPos) {

            } else {
                try {
                    surrondingCells.add(myBoard[i][j]);
                } catch (ArrayIndexOutOfBoundsException e) {
                    continue;
                }
            }
        }
    }
}

public String getValue() {
    return value;
}


// gets the surroundingCells from getSurroundingCells() then checks the number of mines in the ArrayList

public void setValue() {

    if (isMine) {
        return;
    }

    // if the board contains null then the method will exist (to avoid errors)

    if (Arrays.asList(myBoard).contains(null)) {
        return;
    }

    int surroundingMines = 0;

    for (Cell cell : getSurroundingCells()) {
        if (cell.isMine()) {
            surroundingMines++;
        }
    }

    value = Integer.toString(surroundingMines);
}

public boolean isFlagged() {
    return isFlagged;
}

public void setFlagged(boolean flagged) {
    isFlagged = flagged;

    if (isShown) {
        return;
    } else if (isFlagged) {
        symbol = "F";
    } else {
        if (isMine) {
            symbol = "#";
        } else if (isShown) {
            symbol = value;
        } else {
            symbol = "*";
        }
    }
}

// I am not sure if I am overriding these correctly (hashCode(),equals())

@Override
public int hashCode() {
    return xPos * yPos * symbol.hashCode() * value.hashCode() * 29 + 6;
}

@Override
public boolean equals(Object obj) {
    Cell cell = (Cell) obj;
    return cell.getyPos() == yPos && cell.getxPos() == xPos && cell.isMine() == isMine && cell.getValue().equals(value);
}

@Override
public String toString() {
    return "X: " + xPos + " Y: " + yPos + " Value:" + value;
}

}
EN

回答 1

Code Review用户

发布于 2017-08-08 05:33:39

冗余字段

私有最终整数宽度;

你不需要这些。

this.width = width; this.length = length;

也不在这里。

公共int getWidth() {返回宽度;} public int getLength() {返回长度;}

这些可能是

代码语言:javascript
复制
public int getWidth() {
    return board[0].length;
}

public int getLength() {
    return board.length;
}

现在,您可以保证与board的一致性。

for (int i = 0; i < length; i++) { for (int j = 0; j < width; j++) {

会变成

代码语言:javascript
复制
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {

同样,这可以确保您永远不会超出board维度。

hashCode/equals

//我不确定是否正确地覆盖了这些( hashCode(),等于())@重写公共int hashCode(){返回宽度*长度* numOfMines + 13;}@覆盖公共布尔值等于(Object obj) { Board boardObj = (Board) obj;返回Arrays.deepEquals(boardObj.getBoard(),board);}

这些都不一致。经验法则是,如果两个对象相等,那么两者的hashCode值应该相等。如果hashCode值相等,那么对象通常应该相等。

来源:哈希码方法的最佳实现

如果使用Java 5或更高版本,最简单的实现是:

代码语言:javascript
复制
public int hashCode() {
    return Arrays.deepHashCode(board);
}

这使得这两种方法一致。两者都基于board的值。

但是,如果obj in equals不是Board呢?

代码语言:javascript
复制
public boolean equals(Object obj) {
    // if obj is the same object as this, no need to process more
    if (obj == this) {
        return true;
    }

    // if obj is null, then it can't equal this
    // if obj is a different class than this, then they can't be equal
    if (obj == null || obj.getClass() != getClass()) {
        return false;
    }

    Board boardObj = (Board) obj;
    return Arrays.deepEquals(boardObj.getBoard(), board);
}

如果我们谈论的是同一个对象,而不仅仅是两个潜在的等效对象,就不需要浪费时间。

现在,我们不会有任何异常,因为有人试图检查BoardCell的相等性。

来源:如何在java中重写等于方法

请注意,该源提供了一些更复杂的示例。例如,它处理可能有其他类扩展Board的情况。我在这里没有这样做,因为你现在的例子不需要它。

公共int hashCode() {返回xPos * yPos * symbol.hashCode() * value.hashCode() * 29 + 6;}

所以如果你使用Java 7或更高版本,你可以说

代码语言:javascript
复制
public int hashCode() {
    return Objects.hash(yPos, xPos, isMine, value);
}

现在,它将与您的equals实现保持一致(这与Board的问题相同,我将不再重复)。

如果使用旧Java进行编译,则可以执行以下自定义实现

代码语言:javascript
复制
public int hashCode() {
    int result = 6;

    result = 29 * result + yPos;
    result = 29 * result + xPos;
    result = 29 * result + (isMine ? 0 : 1);
    result = 29 * result + ((value == null) ? 0 : value.hashCode());

    return result;
}

同样,请注意,这将使用与equals方法相同的字段。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/172338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档