【问题标题】:Check adjacent indices in a multidimensional array having into account the borders在考虑边界的情况下检查多维数组中的相邻索引
【发布时间】:2022-01-23 03:07:30
【问题描述】:

我这样做是为了知道有多少个星号出现在相邻的方格中。

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 == '*');
}

这里的事情显然是它返回了一个异常,因为它正在检查不会被检查的索引。跳过这些索引的最佳方法是什么?使用 try/catch 可能有点棘手?谢谢!


编辑:

矩阵类

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):

主类中的 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);
}

由于我知道这些测试的主要课程是放置 2 个随机地雷以及我自己使用 SetMine() 方法放置的地雷,因此我多次执行这些测试以确保它失败了。结论是 "2...\n....\n....\n....\n"; 出于某种原因始终是 0 而不是 2 或 1。

【问题讨论】:

  • “检查”是一个非常笼统的术语。您到底想对相邻元素做什么?另外,_matrix 是什么?它来自哪里?
  • matrix 是一个具有多维单元格数组的类,基本上使用方法 At 我可以在某个坐标及其属性值处访问单元格,我需要知道相邻元素的值所以我可以计算其中有多少有星号作为值,无论如何我都会用 Matrix 类编辑问题

标签: c# multidimensional-array


【解决方案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 窗口”(中心单元 +/- 1),描述了最多包含 9 个单元的块,其边缘受 Math.MinMath.Max 调用的限制。然后,if 检查会跳过中心单元格本身,从而只返回相邻的单元格。

然后,要计算星号,您只需利用以下方法:

_matrix.NeighborsOf(coordinate).Count(cell => cell.Value == '*');

依靠异常来忽略越界坐标将被认为是一种不好的做法(因为它会使用“异常作为控制流”反模式)并且在性能方面也相当糟糕。

邻居的想法还允许您参数化邻居区域的“大小”,如果这对您有用的话。

【讨论】:

  • 根据测试,int numOfMines = _matrix.NeighborsOf(coordinate).Count(cell =&gt; cell.Value == '*'); 返回 0 也许我做错了什么?
  • @Omar 随时分享您的测试代码,以便我们了解问题所在。
  • 后期编辑和测试添加,谢谢!
  • @Omar 编辑了嵌套循环思想背后的解释。它基本上遍历最多 3 x 3 个单元的块:每个 for 循环用于一个维度。如果这是一个 3D 矩阵,我们将有 3 个 for 循环而不是 2 个(例如)。数学运算确保遵守边界。
  • @Omar 至于如何找到模式,尝试概括问题可能会有所帮助。例如,如果我想在相邻单元格的 5x5 窗口而不是 3x3 窗口上导航怎么办?这迫使您停止对值进行硬编码,并提出一种为您生成“区域”的方法。
【解决方案2】:

您可以向Matrix 类添加一个方法,如果x 和y 都在其允许范围内(0 true 对于给定的@987654323 @:

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 == '*');

此外,在接受Coordinate 作为参数的Matrix 类的所有其他公共方法中使用此方法进行验证是有意义的。

【讨论】:

  • 效果很好,谢谢!我想知道是否存在另一种方法来检查相邻的每个坐标而不对坐标本身进行硬编码。有 8 个坐标看起来很糟糕,但对我来说,在那里找到赞助人有点困难。
猜你喜欢
  • 1970-01-01
  • 2013-08-22
  • 2019-06-12
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 2018-06-10
  • 2018-04-17
  • 1970-01-01
相关资源
最近更新 更多