c# - 迷宫墙上的按位逻辑

标签 c# bit-manipulation maze

我有一个枚举,代表迷宫中某个单元格的当前状态,如下所示:

[Flags]
public enum CellInfo : ushort
{
    None = 0,
    NorthWall = 1,
    EastWall = 2,
    SouthWall = 4,
    WestWall = 8,
    AllWalls = NorthWall | EastWall | SouthWall | WestWall
}

有了它,我可以很容易地跟踪每个单元格墙壁的当前状态,例如,如果我想打开东面的墙壁。

var myCell = CellInfo.AllWalls; // initialise a cell with all walls up
myCell ^= CellInfo.EastWall; // east wall now down!

简单!但是,每个单元格都紧挨着另一个单元格,它有自己的墙,所以实际上我总是知道我正在从单元格 a 移动。到细胞 b ,并且为了争论起见,可以说这是向东的运动,我需要拆除 a 的东墙和 b西墙:

var a = CellInfo.AllWalls;
var b = CellInfo.AllWalls;
// assume a->b is eastwards
a ^= CellInfo.EastWall;
b ^= CellInfo.WestWall;

现在,我有一个通用的方法来处理这个移动,但是我不喜欢它,它有代码味!太多if语句,我想知道我是否错过了一些明显的东西 - 一些我可能没有发现的按位逻辑?我已经粘贴了我的 KnockDownWall下面的方法,它采用单元格网格 ( CellInfo[,] ) 以及 a 的网格位置b表示为Tuple<int,int> (x,y 位置)


protected static void KnockDownWall(CellInfo[,] cells, Tuple<int, int> target, Tuple<int, int> neighbour)
{
    var offsetTarget = Tuple.Create(neighbour.Item1 - target.Item1, neighbour.Item2 - target.Item2);
    if(offsetTarget.Item1 == 0)
    {
        if(offsetTarget.Item2 == -1)
        {
            cells[target.Item1, target.Item2] ^= CellInfo.NorthWall;
            cells[neighbour.Item1, neighbour.Item2] ^= CellInfo.SouthWall;
        }
        else
        {
            cells[target.Item1, target.Item2] ^= CellInfo.SouthWall;
            cells[neighbour.Item1, neighbour.Item2] ^= CellInfo.NorthWall;
        }
    }
    else
    {
        if (offsetTarget.Item1 == -1)
        {
            cells[target.Item1, target.Item2] ^= CellInfo.WestWall;
            cells[neighbour.Item1, neighbour.Item2] ^= CellInfo.EastWall;
        }
        else
        {
            cells[target.Item1, target.Item2] ^= CellInfo.EastWall;
            cells[neighbour.Item1, neighbour.Item2] ^= CellInfo.WestWall;
        }
    }
}

更新

到目前为止,答案中有大量有用的信息 - 但我担心我一开始就把问题简单化了。 CellInfo有更多的值存储诸如 N/S/E/W 边界(允许我自由塑造迷宫和围栏区域)、N/S/E/W 解决方案和回溯信息之类的东西——这些都输入到模型中绘制迷宫。将一切都视为细胞而不是墙壁有一些好处。

最佳答案

您对迷宫进行建模的方法存在的问题是每个内墙都被表示两次,需要付出大量努力才能保持内部一致性。更好的方法是通过在南侧额外增加一排并在东侧增加一列来扩展迷宫,并使用两种墙类型而不是四种 - 即单元格北侧的墙和单元东侧的墙:

Walls to be removed

这张图片显示了一个 2 行 3 列的网格,扩展为 3 行 4 列。红线表示要拆除的墙壁。

为了处理来自特定单元格的所有四个方向的墙壁,制定一种将南墙和西墙识别为相邻单元格的北墙和东墙的方法。

最后,您可以制作两个 bool 的二维数组 - 一个代表水平墙,一个代表垂直墙。这样,各个单元根本就不必存储自己的墙,迷宫将始终保持内部一致:

private const int rows = 10;
private const int cols = 15;
private bool northWalls[,] = new bool[rows+1, cols+1];
private bool westWalls[,] = new bool[rows+1, cols+1];
public bool HasWall(int cellRow, int cellCol, CellInfo dir) {
    switch(dir) {
        case CellInfo.NorthWall:
            return northWalls[cellRow,cellCol];
        case CellInfo.WestWall:
            return westWalls[cellRow,cellCol];
        case CellInfo.EasthWall:
            return westWalls[cellRow,cellCol+1];
        case CellInfo.SouthhWall:
            return northWalls[cellRow+1,cellCol];
    }
}

如果您必须使用当前设计,但又想摆脱 KnockDownWall 中的条件,您可以制作两个 3×3 数组,其值为 CellInfo.XyzWall 来自你的实现,并用 offsetTarget.Item1+1, offsetTarget.Item2+1 索引它们:

protected static void KnockDownWall(CellInfo[,] cells, Tuple<int, int> target, Tuple<int, int> neighbour) {
    var offsetTarget = Tuple.Create(neighbour.Item1 - target.Item1, neighbour.Item2 - target.Item2);
    cells[target.Item1, target.Item2] ^= targetLookup[offsetTarget.Item1+1, offsetTarget.Item2+1];
    cells[neighbour.Item1, neighbour.Item2] ^= neighborLookup[offsetTarget.Item1+1, offsetTarget.Item2+1];
}

关于c# - 迷宫墙上的按位逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31326141/

相关文章:

c# - 双工 channel 故障事件不会在第二次连接尝试时出现

java - 移位以获得余数

swift - 如何将颜色 Sprite 调用到某个位置(Swift 3)

java - 大于 lg N 的最小整数

C++ 函数采用整数,但示例显示文本?

algorithm - 具有障碍物覆盖算法的网格

java - 迷宫解算器方法应该检查 8 个方向,却检查了根本不存在的方向

c# - 将对象转换为具有指定区域性的字符串

c# - 不为同步请求调用中间件

c# - AspNet Core Identity,如何设置options.Cookie.SameSite?