在2d阵列中计数相邻的-1,而不检查越界的位置(扫雷器)



我正在编写一个类似扫雷器的程序。这是在一个10x10的二维数组中完成的,由于我使用的是约束,所以我只能使用二维数组来编写。但是,我的count()方法出现了一个逻辑错误。count()方法返回在输入位置周围的网格中找到的-1个数(输入位置是我在main()中创建的rowcolumn,例如(5, 5)。它也不能检查2d数组边界之外的位置。以下是count()输出的外观。

我还必须使用count()setCounts()setCounts()遍历整个2d数组,跳过-1的任何位置,并调用count()方法,将当前位置设置为count()方法返回的值。

public int count(int row, int col)
{
int value = 0;
for(int r = -1; r < 2; r++)
{
for(int c = -1; c < 2; c++)
{
if(c == 0 && r == 0)
continue;

int newR = row + c;
int newC = col + c;
if(newR < 0 || newR >= array.length && newC < 0 || newC >= array[0].length)
continue;

if(array[newR][newC] == -1)
value++;
}
}
return value;
}
public void setCounts()
{
for(int r = 0; r < array.length; r++)
{
for(int c = 0; c < array[r].length; c++)
{
if(array[r][c] != -1)
array[r][c] = count(r, c);
String formatted = String.format("%2d", array[r][c]);
System.out.print(formatted + " ");
}
System.out.println();
}
}   

问题是:

  1. count()方法错误地计算了我在main()中放置的任何位置周围的相邻-1
  2. 打印随机数行后setCounts()越界

我确信这与以下代码块有关:

if(newR < 0 || newR >= array.length && newC < 0 || newC >= array[0].length)
continue;

if(array[newR][newC] == -1)
value++;

当我在continue之后的循环中打印newRnewC时,当在setCounts():中调用count()时,循环正在向行/列组合随机添加更多数字,而整个输出没有直接模式

00
11
11
00
11
0 01
12
12
01
12
0 02
13
13
02
13
0 03
14
14
03
14
3 04
15
15
04
15
0 -1 -1 -1 -1 09
09
0 
-1 00
...

因此,把打印报表拿出来,我得到了一个输出:

0 -1 -1 0 -1 -1 -1 0 0 0 
0 0 0 0 0 0 -1 0 0 0 
0 -1 0 -1 0 0 0 0 0 0 
0 0 -1 0 0 0 0 0 -1 0 
-1 0 0 0 -1 0 0 0 0 0 
0 0 0 -1 0 0 0 -1 0 0 
0 0 0 0 0 0 -1 0 -1 0 
0 0 0 0 -1 -1 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
there are 0 -1s
-----------
3  3  0 -1  3  3  0 -1  0  0 
/* Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 10
at Grid3.count(Grid3.java:44)
at Grid3.setCounts(Grid3.java:58)
at Grid3.main(Grid3.java:86) */

第一个数组是我用Grid的构造函数创建的数组。当调用setCounts()时,程序试图打印的第二个数组正在完成。

我想把它改成:

if(newR < 0 || newR >= array.length)
continue;

if(newC < 0 || newC >= array[0].length)
continue;

if(array[newR][newC] == -1)
value++;

会起作用:确实如此,但逻辑上不可行。它消除了ArrayIndexOutOfBoundsException错误,但逻辑上不起作用,因为它不计算相邻-1的右边。它似乎还将更多的数字随机添加到任何行/列组合中。我在main()中放了(5, 5)的位置,有一次我运行代码时,它计数了6-1s,但我放的位置只有3-1s:

-1 0 0 0 0 -1 0 -1 0 0 
0 0 0 0 -1 -1 0 0 0 0 
0 -1 0 0 0 0 0 0 0 0 
0 -1 0 -1 -1 0 0 -1 -1 0 
0 0 0 -1 -1 -1 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-1 0 0 0 0 0 -1 0 0 0 
0 -1 0 0 0 -1 0 0 0 0 
0 -1 0 0 -1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
there are 6 -1s

对于setCounts(),它打印了一个完整的10x10 2d阵列,但也不能正确计数-1s。对于位置(0, 0)(在这种情况下,位于输出左上角的3,如下所示(,该位置实际上应该具有值2,因为只有2个相邻的-1,但它实际上计数为3。

3  0  0  0  0  0 -1  0  0  0 
-1 -1  0  3  0  0  0 -1  0  0 
-1  3  6  3 -1  0  3  0  3  0 
0  3  0 -1 -1 -1  0 -1  0  0 
3  0  0  0 -1  3  6  3  3  0 
3 -1  0  0  0  3  0 -1 -1  0 
-1 -1  3  0  0  0  0  3  3  3 
0  6  6  0  3  0  0  0 -1  0 
0  0 -1 -1  0 -1  0  0  0  3 
0  0  0  3  3  0  3  0  0  0 

以下是完整的输出:

0 0 0 0 0 0 -1 0 0 0 
0 -1 0 0 -1 0 0 0 0 0 
0 -1 0 0 0 -1 0 0 0 0 
0 0 -1 -1 -1 0 0 -1 0 0 
0 0 0 0 0 -1 -1 0 -1 0 
-1 0 0 -1 0 -1 0 0 0 0 
0 0 0 0 0 0 0 0 -1 0 
0 -1 0 0 0 0 0 0 -1 0 
0 0 0 0 0 0 -1 0 -1 0 
0 0 0 0 0 0 0 0 0 0 
there are 2 -1's
-----------
3  0  0  3  0  0 -1  0  0  0 
3 -1  0  0 -1  0  0  3  0  0 
0 -1  6  3  0 -1  3  0  0  0 
0  0 -1 -1 -1  3  3 -1  0  0 
0  0  3  3  6 -1 -1  0 -1  0 
-1  0  0 -1  0 -1  3  6  0  3 
3  3  0  0  3  0  3  3 -1  0 
0 -1  0  0  0  3  0  3 -1  3 
0  0  3  0  0  0 -1  0 -1  3 
0  0  0  0  0  0  0  3  0  3 

我不明白自己做错了什么。我需要count()来正确计算给定位置的相邻-1。如果它没有正确地计算相邻的-1,那么setCounts()将不会在逻辑上工作。我应该在其中一种或两种方法中更改什么,以使其正确且合乎逻辑地工作?这是我迄今为止的代码。

public class Grid
{
private int [][] array;
private int max;
public Grid(int max)
{
array = new int[10][10];
this.max = max;
setRandom();
}
public void setRandom()
{
int i = 0;
while(i < max)
{
int r = (int)(Math.random() * 9) + 0;
int c = (int)(Math.random() * 9) + 0;
if(array[r][c] != -1)
{
array[r][c] = -1;
i++;
}
}
}
public int count(int row, int col)
{
int value = 0;
for(int r = -1; r < 2; r++)
{
for(int c = -1; c < 2; c++)
{
if(c == 0 && r == 0)
continue;

int newR = row + c;
int newC = col + c;
if(newR < 0 || newR >= array.length && newC < 0 || newC >= array[0].length)
continue;

if(array[newR][newC] == -1)
value++;
}
}
return value;
}
public void setCounts()
{
for(int r = 0; r < array.length; r++)
{
for(int c = 0; c < array[r].length; c++)
{
if(array[r][c] != -1)
array[r][c] = count(r, c);
String formatted = String.format("%2d", array[r][c]);
System.out.print(formatted + " ");
}
System.out.println();
}
}   
public void print()
{
for(int r = 0; r < array.length; r++)
{
for(int c = 0; c < array[r].length; c++)
{
System.out.print(array[r][c] + " ");
}
System.out.println();
}
}

public static void main(String[] args)  // printing grid
{
Grid first = new Grid(20);
int count = first.count(5, 5);
first.print();
System.out.println("there are " + count + " -1s");
System.out.println("-----------");
first.setCounts();
}
}

不使用2d数组可能更容易。或者至少,您可能希望存储一个表示每个单元格的对象,而不仅仅是一个int。这样,您就可以实现在cell类中为特定单元格计算相邻单元格的逻辑。

下面是一个例子(使用映射而不是数组(:

public class Grid {
public final int width, height;
private final Map<Coordinate, Cell> cells;
public Grid(int width, int height) {
this.width = width;
this.height = height;
this.cells = IntStream.range(0, width).boxed()
.flatMap(column ->
IntStream.range(0, height).boxed()
.map(row -> new Coordinate(row, column))
)
.map(Cell::new)
.collect(Collectors.toMap(Cell::getCoordinate, Function.identity()));
}
public Cell get(int row, int col) {
return this.cells.get(new Coordinate(row, col));
}
public class Cell {
private final Coordinate coordinate;
private final boolean isMine;
public Cell(Coordinate coordinate, boolean isMine) {
this.coordinate = coordinate;
this.isMine = isMine;
}
public Cell(Coordinate coordinate) {
this(coordinate, new Random().nextBoolean());
}
public Coordinate getCoordinate() {
return coordinate;
}
public List<Cell> getNeighbours() {
int leftNeighbourColumnIdx = coordinate.column - 1;
int topNeighbourRowIdx = coordinate.row - 1;
return IntStream.range(leftNeighbourColumnIdx, leftNeighbourColumnIdx + 3).boxed()
.flatMap(column -> IntStream.range(topNeighbourRowIdx, topNeighbourRowIdx + 3).boxed().map(row -> new Coordinate(row, column)))
.map(Grid.this.cells::get)
.filter(Objects::nonNull)
.filter(c -> !c.equals(this))
.collect(Collectors.toList());
}
public int countNeighbouringMines() {
return (int) getNeighbours().stream()
.filter(cell -> cell.isMine)
.count();
}
}
public static class Coordinate {
private final int row, column;
public Coordinate(int row, int column) {
this.row = row;
this.column = column;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coordinate that = (Coordinate) o;
return row == that.row && column == that.column;
}
@Override
public int hashCode() {
return Objects.hash(row, column);
}
}

public static void main(String[] args) {
final var grid = new Grid(10, 10);
for (int row = 0; row < grid.height; row++) {
for (int col = 0; col < grid.width; col++) {
final var cell = grid.get(row, col);
System.out.print(cell.isMine ? "x" : "o");
System.out.print(" ");
}
System.out.println();
}
System.out.printf("mines around (5,5): %d%n", grid.get(5, 5).countNeighbouringMines());
}

}

编辑:与上面的示例相同,但使用2d阵列而不是地图

public class Grid {
public final int width, height;
private final Cell[][] cells;
public Grid(int width, int height) {
this.width = width;
this.height = height;
cells = new Cell[width][height];
IntStream.range(0, width)
.forEach(column -> IntStream.range(0, height)
.forEach(row -> cells[column][row] = new Cell(column, row))
);
}
public Cell get(int row, int col) {
final var column = col < 0 || col >= this.cells.length ? null : this.cells[col];
return row < 0 || column == null || row >= column.length ? null : column[row];
}
public class Cell {
private final int column, row;
private final boolean isMine;
public Cell(int column, int row, boolean isMine) {
this.column = column;
this.row = row;
this.isMine = isMine;
}
public Cell(int column, int row) {
this(column, row, new Random().nextBoolean());
}
public List<Cell> getNeighbours() {
int leftNeighbourColumnIdx = column - 1;
int topNeighbourRowIdx = row - 1;
return IntStream.range(leftNeighbourColumnIdx, leftNeighbourColumnIdx + 3).boxed()
.flatMap(column -> IntStream.range(topNeighbourRowIdx, topNeighbourRowIdx + 3).boxed()
.map(row -> Grid.this.get(row, column)))
.filter(Objects::nonNull)
.filter(c -> !c.equals(this))
.collect(Collectors.toList());
}
public int countNeighbouringMines() {
return (int) getNeighbours().stream()
.filter(cell -> cell.isMine)
.count();
}
}

public static void main(String[] args) {
final var grid = new Grid(10, 10);
for (int row = 0; row < grid.height; row++) {
for (int col = 0; col < grid.width; col++) {
final var cell = grid.get(row, col);
System.out.print(cell.isMine ? "x" : "o");
System.out.print(" ");
}
System.out.println();
}
System.out.printf("mines around (5,5): %d%n", grid.get(5, 5).countNeighbouringMines());
}
}

其思想是,如果rowcell无效,则Grid中的get(int row, int col)方法返回Cell对象或null,但它从未失败(引发异常(。然后,每个小区都可以使用这种方法来尝试获取其所有邻居(getNeighbours()(。单元格不需要担心,如果它请求的行/列是否有效,这将由get(int row, int col)处理。它只需要过滤掉所有null值(这些坐标无效(和它自己。

使用getNeighbours(),您将获得一个包含所有相邻单元格的列表。如果你过滤掉所有没有地雷的细胞——你会得到一个有地雷的邻居的列表——然后你只需要计算它们。

最新更新