首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >另一个Minesweeper实现(用Java)

另一个Minesweeper实现(用Java)
EN

Code Review用户
提问于 2019-09-02 11:31:19
回答 2查看 301关注 0票数 2

这是我在Java中对Minesweeper的看法。欢迎任何关于可读性、设计或其他方面的反馈。

Cell.java

代码语言:javascript
复制
public class Cell {

    public static final int BOMB = -1;

    boolean revealed = false;
    int row, col, val;

    public Cell(int... coordinates) {
        this.row = coordinates[0];
        this.col = coordinates[1];
    }

    boolean isBomb() {
        return val == BOMB;
    }

    boolean isEmpty() {
        return val == 0;
    }

}

Board.java

代码语言:javascript
复制
class Board {

    enum BoardResponse {
        OK, BOMB, ALL_CELLS_REVEALED
    }

    Cell[][] rows;

    // Returns a new Board with a few bombs and the values around the bombs.
    public static Board newBoard() {
        Board board = new Board();
        board.rows = new Cell[7][7];  // todo: Accept parameters instead of 7,7

        for (int i = 0; i < board.rows.length; i++)
            for (int j = 0; j < board.rows[i].length; j++)
                board.rows[i][j] = new Cell(i, j);

        board.rows[1][1].val = Cell.BOMB;  // todo: randomise bomb placement
        board.rows[3][3].val = Cell.BOMB;  // make sure bombs not adjacent
        board.rows[5][5].val = Cell.BOMB;

        // Fill bombs surroundings with values.
        for (Cell[] row : board.rows)
            for (Cell cell : row)
                if (cell.isBomb())
                    board.surroundingCells(cell).forEach(c -> c.val = c.val + 1);

        return board;
    }

    // Accepts a row and a column, modifies the state as needed and returns 
    // whether game is finished, a bomb has been clicked or game continues.
    BoardResponse flipCell(int... coordinates) {
        Cell clicked = rows[coordinates[0]][coordinates[1]];
        clicked.revealed = true;

        // Player lost
        if (clicked.isBomb())
            return BoardResponse.BOMB;

        // Check if game finished, i.e. player won..
        // For the game to finish all cells
        // (except the ones holding bombs) must be revealed.
        boolean allCellsRevealed = true;
        isAllCellsRevealed:
        for (Cell[] row : rows)
            for (Cell cell : row)
                if (!cell.revealed && !cell.isBomb()) {
                    allCellsRevealed = false;
                    break isAllCellsRevealed;
                }

        if (allCellsRevealed)
            return BoardResponse.ALL_CELLS_REVEALED;

        // If the cell clicked on is an empty cell,
        // reveal all surrounding cells recursively until valued cells.
        if (clicked.isEmpty()) {
            Queue<Cell> queue = new LinkedList<>();
            surroundingCells(clicked)
                    .stream().filter(c -> !c.revealed).forEach(queue::add);
            while (!queue.isEmpty()) {
                Cell cell = queue.remove();
                cell.revealed = true;
                if (cell.isEmpty()) {
                    Set<Cell> cells = surroundingCells(cell);
                    cells.stream().filter(c -> !c.revealed).forEach(queue::add);
                }
            }
        }

        return BoardResponse.OK;
    }

    // Given a single cell in the board, returns all surrounding cells in a Set.
    private Set<Cell> surroundingCells(Cell cell) {
        Set<Cell> surroundingCells = new HashSet<>();

        // Cells in upper row
        for (int i = cell.col - 1; i < cell.col + 2; i++)
            if (inBounds(cell.row - 1, i))
                surroundingCells.add(rows[cell.row - 1][i]);

        // Cells in lower row
        for (int i = cell.col - 1; i < cell.col + 2; i++)
            if (inBounds(cell.row + 1, i))
                surroundingCells.add(rows[cell.row + 1][i]);

        // Cell to left
        if (inBounds(cell.row, cell.col - 1))
            surroundingCells.add(rows[cell.row][cell.col - 1]);

        // Cell to right
        if (inBounds(cell.row, cell.col + 1))
            surroundingCells.add(rows[cell.row][cell.col + 1]);

        return surroundingCells;
    }

    // Helper method to surroundingCells.
    // Tries to access the cell in given coordinates handling OutOfBoundException.
    private boolean inBounds(int... coordinates) {
        try {
            Cell cell = rows[coordinates[0]][coordinates[1]];
            return true;
        } catch (ArrayIndexOutOfBoundsException e) {
            return false;
        }
    }

}

下面是一个命令行客户机:

代码语言:javascript
复制
public class MinesweeperCli {
    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);

        Board board = Board.newBoard();
        Board.BoardResponse boardResponse;
        do {
            int row = in.nextInt();
            int col = in.nextInt();
            boardResponse = board.flipCell(row, col);
            printBoard(board);
            if (boardResponse == Board.BoardResponse.BOMB) {
                System.out.println("Bomb!");
                break;
            }
        } while (boardResponse != Board.BoardResponse.ALL_CELLS_REVEALED);

        in.close();
    }

    public static void printBoard(Board board) {
        StringBuilder sb = new StringBuilder();

        for (Cell[] row : board.rows) {
            for (Cell cell : row)
                sb.append(cellRep(cell)).append(" ");
            sb.append("\n");
        }

        System.out.println(sb.toString());
    }

    public static String cellRep(Cell cell) {
        if (!cell.revealed)
            return "-";

        if (cell.isBomb())
            return "*";

        if (cell.isEmpty())
            return ".";

        return valueOf(cell.val);
    }

}

示例运行

代码语言:javascript
复制
0
0
1 - - - - - - 
- - - - - - - 
- - - - - - - 
- - - - - - - 
- - - - - - - 
- - - - - - - 
- - - - - - - 

6
0
1 - - - - - - 
- - - - - - - 
1 1 2 - - - - 
. . 1 - - - - 
. . 1 1 2 - - 
. . . . 1 - - 
. . . . 1 - - 

1
1
1 - - - - - - 
- * - - - - - 
1 1 2 - - - - 
. . 1 - - - - 
. . 1 1 2 - - 
. . . . 1 - - 
. . . . 1 - -

Bomb!
EN

回答 2

Code Review用户

回答已采纳

发布于 2019-09-02 13:37:16

  • 没有缩写的名字会更好。
  • 不可变属性(行、列)应成为最终属性。
  • 包私有直接访问-特别是可变-字段是不喜欢的。
  • ...有他们的用途,但不在这里。
  • {}在一般情况下也经常被使用。

所以:

代码语言:javascript
复制
public class Cell {

    public static final int BOMB = -1;

    final int row;
    final col;

    boolean revealed;
    int value;

    public Cell(int row, int col) {
        this.row = row;
        this.col = col;
    }

批评者:

  • cellRep更属于Cell类。您可以使它成为一个char方法,以便只有一个字符,用于董事会表示。
  • printBoard更属于Board
  • 您也可以将板的大小考虑为构造函数参数。

设计批评:

rowcol是多余的。

您还避免了几个陷阱,因此通常代码并不坏。

票数 5
EN

Code Review用户

发布于 2019-09-04 07:45:58

似乎不清楚细胞类应该承担什么责任。您从委员会获得一个单元格,然后查询该单元格是否已翻转的信息,但要翻转一个单元格或获取周围的单元格,则必须向委员会提出请求。

对我来说,对一个细胞所做的操作将通过有关的细胞来完成,这是有意义的。该单元格将提供是否翻转的信息。它是否已被标记。这是翻转后的状态(空的,周围的地雷数量,炸弹)。

董事会将提供有关游戏状态和进入细胞的信息。

即使模型是CLI/Swing UI的内部模型,您仍然应该保持关注点的分离。对对象字段的访问应该通过访问器方法进行,而不是直接进行,而且字段应该是私有的。一旦您开始从其他对象修改对象的内部状态,您就会在一家意大利餐厅介绍自己的食物大战。意大利面会变得很乱。

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

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

复制
相关文章

相似问题

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