2d-array with "wrapped edges" in C#

本文关键字:quot in edges wrapped 2d-array with | 更新日期: 2023-09-27 18:14:48

警告:我是c#新手。除了回答我的问题,如果你看了我的代码后有什么建议,我也欢迎。

假设我在c#中定义了一个大小为10x10的二维数组:

var arr = new int[10,10];

访问索引不在0-9范围内的元素是错误的。在某些应用中(游戏邦注:例如某些2d数组代表一个世界的游戏),有必要"包裹"数组的边缘。例如

arr[-1, 0]

实际上指的是arr[9,0]处的元素。

我一直使用的一种方法是下面的类。我没有子类化系统。数组,因为c#显然禁止这样做。

使用例子:

var grid = new Grid(10,10);
grid.set(-1, 0, 100); // Set element at (-1,0) to value 100.
grid.at(-1,0); // retrieve element at (-1,0)

类本身:

class Grid
{
    public int[,] state;
    public int width { get { return state.GetLength(0); } }
    public int height { get { return state.GetLength(1); } }
    public Grid(int width_init, int height_init)
    {
        state = new int[width_init, height_init];
    }
    int mod(int a, int b)
    {
        if (a >= 0)
            return a % b;
        else
            return (b + a % b) % b;
    }
    int wrap_x(int x) { return mod(x, width); }
    int wrap_y(int y) { return mod(y, height); }
    public int at(int x, int y)
    {
        return state[wrap_x(x), wrap_y(y)];
    }
    public void set(int x, int y, int val)
    {
        state[wrap_x(x), wrap_y(y)] = val;
    }    
    // more stuff here...
}

问题:是否有游戏/创意编码框架提供这种类?

问题:你能想到一个更简单的mod我可以在上面使用吗?

为了处理每个元素以及相应的"x"answers"y",我使用以下方法:

public void each(Action<int, int, int> proc)
{
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++)
            proc(x, y, state[x, y]);
}

问题:我四处寻找在System上定义的类似方法。数组,但是我没有找到。我错过了吗?

问题:在上面,for(int x = 0; x < width; x++)是表示"从0到N乘1"的常用习语。在c#中是否有一种机制来表达这一点?也就是说,我想把上面的内容写成:

width.up_to((x) =>
    height.up_to((y) =>
        proc(x, y, state[x, y]);

,其中up_to是对整数的方法。像up_to这样的东西已经定义了吗?

与Scheme中的map类似,这里有一个map方法,它对每个元素及其相应的索引应用Func。它返回一个新的Grid

public Grid map(Func<int, int, int, int> proc)
{
    var grid = new Grid(width, height);
    each((x, y, val) => grid.state[x, y] = proc(x, y, val));
    return grid;
}

问题:假设我设置了一个子类class World : Grid,它添加了额外的实例变量。上面的map的问题是,当在World的实例上调用时,您得到的是Grid,而不是World。我该如何解决这个问题?这是完全错误的做法吗?也许一个更好的设计是不子类化Grid,而是保持它作为World的实例变量。

很抱歉这么长时间的提交。: -)

更新:我单独问了关于upto的问题,得到了一些很好的答案

2d-array with "wrapped edges" in C#

你可以做的一件事是方便引用你的网格是重载[,]:

public int this[int x, int y]
{
    get { return state[wrap_x(x), wrap_y(y)]; }
    set { state[wrap_x(x), wrap_y(y)] = value; }
}

如果你觉得语法更合适,那就去吧。

关于你的mod函数,我能做的最好的建议是使这些变量(a和b)有意义。indexmaxSize应该做。


其他东西:

  • 你的state变量应该是私有的。
  • 除非你绝对、专门地需要整型,否则考虑使用state数组类型的泛型。你的Grid类变成了Grid<T>
  • 用括号[,]过载,你可以摆脱你的atset函数。
  • 对于您的World class,使用简化的Grid,要问的问题是这个经典的问题:is还是has?你的世界是网格还是有网格?只有你能回答这个问题,但我倾向于HAS。
  • 考虑一个网格构造函数,它接受一个现成的2d数组作为参数:

的例子:

public Grid(int[,] state)
{
    this.state = state;
}

  • mod可以对任何值(多个绕行)进行轻微修改。

的例子:

int mod(int index, int maxSize)
{
    while (index < 0) index += maxSize;
    return index % maxSize;
}

结果:

  • mod(0,10) => 0
  • mod(1,10) => 1
  • mod(-1,10) => 9
  • mod(10,10) => 0
  • mod(-10,10) => 0
  • mod(11,10) => 1
  • mod(-11,10) => 9

使用模%函数访问数组。对于N by M数组,使用如下命令:

int x = A[i % N, j % M];

它会做你所需要的。在您的示例中,使用arr[-1 % 10, 0 % 10]代替arr[-1,0]。不需要包装器函数或额外的代码!