在考虑边界的情况下检查多维数组中的相邻索引
Check adjacent indices in a multidimensional array having into account the borders
我这样做是为了知道相邻方块中出现了多少个星号。
private int CheckAdjacents(Coordinate cord)
{
List<Coordinate> coordinates = new List<Coordinate>()
{
new Coordinate(cord.X - 1, cord.Y - 1),
new Coordinate(cord.X, cord.Y-1),
new Coordinate(cord.X + 1, cord.Y -1),
new Coordinate(cord.X + 1, cord.Y),
new Coordinate(cord.X + 1, cord.Y + 1),
new Coordinate(cord.X, cord.Y + 1),
new Coordinate(cord.X - 1, cord.Y + 1),
new Coordinate(cord.X - 1, cord.Y)
};
return coordinates.Count(x => _matrix.At(x).Value == '*');
}
这里的问题是,显然它 returns 是一个异常,因为正在检查不会被检查的索引。跳过这类索引的最佳方法是什么?使用 try/catch 可能有点棘手?谢谢!
编辑:
矩阵class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace MineSweeper
{
public record Coordinate (int X, int Y);
public record Size(int M, int N);
public class Matrix
{
private readonly Size _size;
private readonly Cell[,] _matrix;
private const char InitValue = '.';
public Matrix(Size size)
{
_size = size;
_matrix = new Cell[size.M, size.N];
Initialize();
}
private void Initialize()
{
for (int m = 0; m < _size.M; m++)
for (int n = 0; n < _size.N; n++)
_matrix[m, n] = new Cell(InitValue);
}
public Size GetSize()
=> _size;
public Cell At(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y];
public void SetMine(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y] = new Cell('*');
public void ChangeValue(Coordinate coordinate, char value)
=> _matrix[coordinate.X, coordinate.Y] = new Cell(value);
public Cell Open(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y];
public IEnumerable ToList()
=> _matrix.Cast<Cell>().ToList();
private string CellsAsString()
=> string.Concat(_matrix.OfType<Cell>().Select(c => c.Value));
public override bool Equals(object other)
=> this.CellsAsString().Equals((other as Matrix)?.CellsAsString());
public override int GetHashCode()
=> this.CellsAsString().GetHashCode();
}
}
编辑(2):
来自主要 class.
的 PrintMatrix 和 Open 方法
public void Open(Coordinate coordinate)
{
if (_matrix.At(coordinate).Value == '*')
HasLose = true;
int numOfMines = _matrix.NeighborsOf(coordinate).Count(cell => cell.Value == '*');
_showedMatrix.ChangeValue(coordinate, char.Parse(numOfMines.ToString()));
HasWin = PlayerHasWin();
}
public String PrintMatrix()
{
string temp = "";
for (int x = 0; x < _size.M; x++)
{
for (int y = 0; y < _size.N; y++)
{
temp += _showedMatrix.At(new Coordinate(x, y)).Value;
}
temp += '\n';
}
return temp;
}
请注意,我使用的是 showedMatrix
,这是另一个包含单元格的矩阵,每个单元格的值都是一个简单的 .
。我正在使用这个新矩阵,所以我可以更改它的值并打印它。
这是两个失败的测试。
[Fact]
public void CellIsOpenWithoutAMineButWithOneMineAdjacent()
{
string printExpected = "1...\n....\n....\n....\n";
Matrix matrix = new Matrix(new(4, 4));
matrix.SetMine(new(0,1));
MineSweeper mineSweeper = new(matrix, 2);
mineSweeper.Open(new(0,0));
mineSweeper.PrintMatrix().Should().Be(printExpected);
}
[Fact]
public void CellIsOpenWithoutAMineButWithTwoMineAdjacent()
{
string printExpected = "2...\n....\n....\n....\n";
Matrix matrix = new Matrix(new(4, 4));
matrix.SetMine(new(0,1));
matrix.SetMine(new(1,0));
MineSweeper mineSweeper = new(matrix, 2);
mineSweeper.Open(new(0,0));
mineSweeper.PrintMatrix().Should().Be(printExpected);
}
因为我知道这些测试的主要 class 是放置 2 个随机地雷加上我自己用 SetMine()
方法放置的地雷,所以我执行了几次这些测试以确保它失败了。结论是 "2...\n....\n....\n....\n";
由于某种原因总是 0 而不是 2 或 1。
我建议在矩阵本身上使用迭代器方法,它负责在返回单元格之前检查边界,如下所示:
IEnumerable<Cell> NeighborsOf(Coordinate coord)
{
// if coord is not in bounds of the matrix, throw an argument exception
for (int x = Math.Max(coord.X - 1, 0); x <= Math.Min(coord.X + 1, _size.M - 1); x++)
{
for (int y = Math.Max(coord.Y - 1, 0); y <= Math.Min(coord.Y + 1, _size.N - 1); y++)
{
if ((x,y) == (coord.X, coord.Y)) continue;
yield return At(new Coordinate(x, y));
}
}
}
for
循环定义了坐标的“3x3 window”(中心单元格 +/- 1),描述了 9 个单元格的最大块,其边缘受 Math.Min
和 Math.Max
电话。然后,中央单元格本身被 if
检查跳过,结果只返回相邻的单元格。
然后,要计算星号,您只需利用此方法:
_matrix.NeighborsOf(coordinate).Count(cell => cell.Value == '*');
依靠异常来忽略越界坐标将被认为是一种不好的做法(因为它会使用“异常作为控制流”反模式)并且在性能方面也相当差。
邻居的想法还允许您参数化邻居区域的“大小”,如果这对您有用的话。
您可以向 Matrix
class 添加一个方法,即 returns true
如果 x 和 y 都在允许的范围内(0 <= x < M, 0 <= y < N) 对于给定的 Coordinate
:
public bool IsValid(Coordinate coordinate) =>
0 <= coordinate.X && coordinate.X < _size.M &&
0 <= coordinate.Y && coordinate.Y < _size.N;
那么你可以只计算有效的单元格:
return coordinates.Count(x => _matrix.IsValid(x) && _matrix.At(x).Value == '*');
此外,在 Matrix
class 的所有其他 public 方法中将此方法用于验证目的是有意义的,接受 Coordinate
作为一个论点。
我这样做是为了知道相邻方块中出现了多少个星号。
private int CheckAdjacents(Coordinate cord)
{
List<Coordinate> coordinates = new List<Coordinate>()
{
new Coordinate(cord.X - 1, cord.Y - 1),
new Coordinate(cord.X, cord.Y-1),
new Coordinate(cord.X + 1, cord.Y -1),
new Coordinate(cord.X + 1, cord.Y),
new Coordinate(cord.X + 1, cord.Y + 1),
new Coordinate(cord.X, cord.Y + 1),
new Coordinate(cord.X - 1, cord.Y + 1),
new Coordinate(cord.X - 1, cord.Y)
};
return coordinates.Count(x => _matrix.At(x).Value == '*');
}
这里的问题是,显然它 returns 是一个异常,因为正在检查不会被检查的索引。跳过这类索引的最佳方法是什么?使用 try/catch 可能有点棘手?谢谢!
编辑:
矩阵class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace MineSweeper
{
public record Coordinate (int X, int Y);
public record Size(int M, int N);
public class Matrix
{
private readonly Size _size;
private readonly Cell[,] _matrix;
private const char InitValue = '.';
public Matrix(Size size)
{
_size = size;
_matrix = new Cell[size.M, size.N];
Initialize();
}
private void Initialize()
{
for (int m = 0; m < _size.M; m++)
for (int n = 0; n < _size.N; n++)
_matrix[m, n] = new Cell(InitValue);
}
public Size GetSize()
=> _size;
public Cell At(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y];
public void SetMine(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y] = new Cell('*');
public void ChangeValue(Coordinate coordinate, char value)
=> _matrix[coordinate.X, coordinate.Y] = new Cell(value);
public Cell Open(Coordinate coordinate)
=> _matrix[coordinate.X, coordinate.Y];
public IEnumerable ToList()
=> _matrix.Cast<Cell>().ToList();
private string CellsAsString()
=> string.Concat(_matrix.OfType<Cell>().Select(c => c.Value));
public override bool Equals(object other)
=> this.CellsAsString().Equals((other as Matrix)?.CellsAsString());
public override int GetHashCode()
=> this.CellsAsString().GetHashCode();
}
}
编辑(2):
来自主要 class.
的 PrintMatrix 和 Open 方法public void Open(Coordinate coordinate)
{
if (_matrix.At(coordinate).Value == '*')
HasLose = true;
int numOfMines = _matrix.NeighborsOf(coordinate).Count(cell => cell.Value == '*');
_showedMatrix.ChangeValue(coordinate, char.Parse(numOfMines.ToString()));
HasWin = PlayerHasWin();
}
public String PrintMatrix()
{
string temp = "";
for (int x = 0; x < _size.M; x++)
{
for (int y = 0; y < _size.N; y++)
{
temp += _showedMatrix.At(new Coordinate(x, y)).Value;
}
temp += '\n';
}
return temp;
}
请注意,我使用的是 showedMatrix
,这是另一个包含单元格的矩阵,每个单元格的值都是一个简单的 .
。我正在使用这个新矩阵,所以我可以更改它的值并打印它。
这是两个失败的测试。
[Fact]
public void CellIsOpenWithoutAMineButWithOneMineAdjacent()
{
string printExpected = "1...\n....\n....\n....\n";
Matrix matrix = new Matrix(new(4, 4));
matrix.SetMine(new(0,1));
MineSweeper mineSweeper = new(matrix, 2);
mineSweeper.Open(new(0,0));
mineSweeper.PrintMatrix().Should().Be(printExpected);
}
[Fact]
public void CellIsOpenWithoutAMineButWithTwoMineAdjacent()
{
string printExpected = "2...\n....\n....\n....\n";
Matrix matrix = new Matrix(new(4, 4));
matrix.SetMine(new(0,1));
matrix.SetMine(new(1,0));
MineSweeper mineSweeper = new(matrix, 2);
mineSweeper.Open(new(0,0));
mineSweeper.PrintMatrix().Should().Be(printExpected);
}
因为我知道这些测试的主要 class 是放置 2 个随机地雷加上我自己用 SetMine()
方法放置的地雷,所以我执行了几次这些测试以确保它失败了。结论是 "2...\n....\n....\n....\n";
由于某种原因总是 0 而不是 2 或 1。
我建议在矩阵本身上使用迭代器方法,它负责在返回单元格之前检查边界,如下所示:
IEnumerable<Cell> NeighborsOf(Coordinate coord)
{
// if coord is not in bounds of the matrix, throw an argument exception
for (int x = Math.Max(coord.X - 1, 0); x <= Math.Min(coord.X + 1, _size.M - 1); x++)
{
for (int y = Math.Max(coord.Y - 1, 0); y <= Math.Min(coord.Y + 1, _size.N - 1); y++)
{
if ((x,y) == (coord.X, coord.Y)) continue;
yield return At(new Coordinate(x, y));
}
}
}
for
循环定义了坐标的“3x3 window”(中心单元格 +/- 1),描述了 9 个单元格的最大块,其边缘受 Math.Min
和 Math.Max
电话。然后,中央单元格本身被 if
检查跳过,结果只返回相邻的单元格。
然后,要计算星号,您只需利用此方法:
_matrix.NeighborsOf(coordinate).Count(cell => cell.Value == '*');
依靠异常来忽略越界坐标将被认为是一种不好的做法(因为它会使用“异常作为控制流”反模式)并且在性能方面也相当差。
邻居的想法还允许您参数化邻居区域的“大小”,如果这对您有用的话。
您可以向 Matrix
class 添加一个方法,即 returns true
如果 x 和 y 都在允许的范围内(0 <= x < M, 0 <= y < N) 对于给定的 Coordinate
:
public bool IsValid(Coordinate coordinate) =>
0 <= coordinate.X && coordinate.X < _size.M &&
0 <= coordinate.Y && coordinate.Y < _size.N;
那么你可以只计算有效的单元格:
return coordinates.Count(x => _matrix.IsValid(x) && _matrix.At(x).Value == '*');
此外,在 Matrix
class 的所有其他 public 方法中将此方法用于验证目的是有意义的,接受 Coordinate
作为一个论点。