迷宫墙上的按位逻辑

时间:2015-07-09 19:05:29

标签: c# bit-manipulation maze

我有一个enum,它代表迷宫中一个细胞的当前状态,如下所示:

[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[,])与ab的网格位置一起表达作为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解决方案和回溯信息 - 这一切进入吸引迷宫的模型。把所有东西当作细胞而不是墙壁来处理都有一些好处。

2 个答案:

答案 0 :(得分:2)

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

Walls to be removed

此图显示了2行乘3列的网格,扩展为3行4列。红线显示要拆除的墙壁。

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

最后,你可以制作两个Car MyCar = new Car(); MyCar.GetCarByID(5); s的2D阵列 - 一个代表水平墙,一个代表垂直墙。通过这种方式,单个细胞根本不必存储自己的墙壁,迷宫始终在内部保持一致:

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

答案 1 :(得分:2)

你的模型说每个细胞都有4个墙,都是自己的。但这并不准确,因为这些墙可能与其他细胞共享。你看到了模型问题的痛苦点。此外,您的单元格没有任何其他信息(从您向我们展示的内容):您真的只有墙壁。他们只有一个属性:无论是上升还是被击倒。

我建议你的模型应该基于墙,而不是单元格。

这是你的细胞和墙壁的ASCII表示,正如我想象的那样。

 ─ ─ ─ ─ ─ ─  // wall row 0, walls 0-5
│ │ │ │ │ │ │ // wall row 1, walls 0-6; cell row 0, cells 0-5
 ─ ─ ─ ─ ─ ─  // wall row 2, walls 0-5
│ │ │ │ │ │ │ // wall row 3, walls 0-6; cell row 1, cells 0-5
 ─ ─ ─ ─ ─ ─  // wall row 4, walls 0-5

请注意,交替行的墙数不同。解决这个问题的一种方法是使用锯齿状数组而不是多维数组。

bool[][] walls = // whatever

所以要击倒第1行的最后一个垂直墙,只需:

walls[1][6] = false;

或切换它:

walls[1][6] = !walls[1][6];

如果您需要计算walls相对于单元格的索引(例如,从单元格1,2向东的墙),您可以制作一个简单的方法来执行此操作。

如果你有关于每个单元格和墙壁存储的属性,你可以这样:

Wall[][] walls;
Cell[,] cells;

Wall可能有ActiveStanding属性,您可以切换。如果WallCell个对象知道自己的位置,您甚至可以拥有Cell.WallsCell.NorthWall(等)或Wall.Cells属性周围的元素。