这是我在Java中对Minesweeper的看法。欢迎任何关于可读性、设计或其他方面的反馈。
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;
}
}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;
}
}
}下面是一个命令行客户机:
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);
}
}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!发布于 2019-09-02 13:37:16
...有他们的用途,但不在这里。{}在一般情况下也经常被使用。所以:
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。设计批评:
row和col是多余的。
您还避免了几个陷阱,因此通常代码并不坏。
发布于 2019-09-04 07:45:58
似乎不清楚细胞类应该承担什么责任。您从委员会获得一个单元格,然后查询该单元格是否已翻转的信息,但要翻转一个单元格或获取周围的单元格,则必须向委员会提出请求。
对我来说,对一个细胞所做的操作将通过有关的细胞来完成,这是有意义的。该单元格将提供是否翻转的信息。它是否已被标记。这是翻转后的状态(空的,周围的地雷数量,炸弹)。
董事会将提供有关游戏状态和进入细胞的信息。
即使模型是CLI/Swing UI的内部模型,您仍然应该保持关注点的分离。对对象字段的访问应该通过访问器方法进行,而不是直接进行,而且字段应该是私有的。一旦您开始从其他对象修改对象的内部状态,您就会在一家意大利餐厅介绍自己的食物大战。意大利面会变得很乱。
https://codereview.stackexchange.com/questions/227315
复制相似问题