【问题标题】:What would be a better way to implement this if else logic?if else 逻辑有什么更好的方法来实现这个?
【发布时间】:2021-11-26 18:54:17
【问题描述】:

老实说,我想不出更好的标题,因为这是一个基于场景的问题:

我们有一个 Battleships 游戏,我们想要求玩家提供一对坐标,以便在网格上设置他们的一艘船(更具体地说,是船的起点和终点)。假设给定了正确的坐标并且船在网格平面的边界内,我们只需要检查船是否与当前在网格平面上的任何其他船发生碰撞。

上下文:Grid 有一个 Content 属性,它可以是 ShipContent 或 EmptyContent。

CollisionChecker() 方法循环遍历先前给定的坐标对之间的空间(请注意,这些坐标不能是对角线,这也被假定为事先检查过的)。

玩家想把他们的战舰放在 A1 和 A4 之间。由于字母相等,我们循环遍历 1 到 4,很简单。但是,玩家可以按照他们的顺序分别输入 A4 和 A1。这两种情况在逻辑上都可以正常工作,但如果不进行相应处理,它们可能会导致 OutOfBound 异常和/或不正确的循环。

最后一点上下文,CoordinateLetter 是一个枚举,其中包含整个英文字母表。

static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter coordinateLetter1, CoordinateLetter coordinateLetter2, int coordinateNumber1, int coordinateNumber2)
    {
        bool lettersEqual = coordinateLetter1 == coordinateLetter2;
        bool cL1Bigger = coordinateLetter1 > coordinateLetter2;
        bool cN1Bigger = coordinateNumber1 > coordinateNumber2;
        if (lettersEqual && cN1Bigger)
            for (int num = coordinateNumber2; num <= coordinateNumber1; num++)
            {
                // Assume that if it collides, it returns false
            }
        else if (lettersEqual && !cN1Bigger)
        {
            for (int num = coordinateNumber1; num <= coordinateNumber2; num++)
            {
                // Assume that if it collides, it returns false
            }
        }
        else if (!lettersEqual && cL1Bigger)
        {
            for (int num = (int)coordinateLetter2; num <= (int)coordinateLetter1; num++)
            {
                // Assume that if it collides, it returns false
            }
        }
        else
        {
            for (int num = (int)coordinateLetter1; num <= (int)coordinateLetter2; num++)
            {
                // Assume that if it collides, it returns false
            }
        }
        return true;
    }

这段代码让我脊背发凉。如果其他情况是这样的,我不喜欢链接。有什么更好的方法来实现这一点?

【问题讨论】:

  • 上次我为战舰编码时,我使用char[][] 表示网格,将大写字符编码为未轰炸的舰船,将小写字符编码为被轰炸的舰船,空格表示海,x 表示被轰炸的海。 . 我有一种方法可以水平或垂直布局,如果它们与现有船或板边缘发生碰撞,则报告真假;通过具有不会修改游戏板但检查碰撞和报告的“假装”标志参数,它可以用于布局和测试游戏中的碰撞。 x 和 y 步长的两个参数和一个循环支持的船布局。我不会..
  • ..麻烦有一个字母枚举,只需使用 char/int 等价:如果你有一个字符,例如 C 和减去 A ,你会得到 2..string coord = "A1"; var x = coord[0] - 'A'; var y = int.Parse(coord.Substring(1)) -1;
  • 我写的大部分似乎不必要的东西都是为了灵活性,未来可能发生的任何变化,主要是为了让它看起来总体上很漂亮:) 最好让局外人查看我的代码和看到一个网格有一个“CoordinateLetter”和一个“CoordinateNumber”,还有一个“GridState”和“GridContent”。通过我的实现,我可以轻松地添加更多船只并更改它们的符号等等,但这不是重点。至于答案,我不太明白那部分。对不起。
  • 我认为您需要决定:“布局是水平的还是垂直的”,然后是“开始大于结束?如果是这样交换它们”。根据方向进行布局
  • @Flydog57 这是解决解决方案的一种方法,也是一种可靠的方法。我觉得这会导致未来有更多的问题需要解决。就个人而言,我会坚持我的 x 和 y。不过感谢答案。

标签: c# console


【解决方案1】:

您可以使代码更易于遵循和维护,也许在命名上更合乎逻辑。

但有时你会做你必须做的。

这只是我在玩弄它......

    static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter gridRow1, CoordinateLetter gridRow2, int gridCol1, int gridCol2)
    {
        bool sameRow = (gridRow1 == gridRow2);
        
        if (!(sameRow || (gridCol1 == gridCol2)))
            throw new ArgumentException("Diagonal not allowed!");
        
        if (sameRow)
        {
            int row = (int)gridRow1;
            int start = Math.Min(gridCol1, gridCol2);
            int end   = Math.Max(gridCol1, gridCol2);

            for (int i = start; i <= end; i++)
            {
                //Check gridPlane[row][i]
            }
        }
        else //Same column
        {
            int col = gridCol1;
            int start = Math.Min((int)gridRow1, (int)gridRow2);
            int end   = Math.Max((int)gridRow1, (int)gridRow2);

            for (int i = start; i <= end; i++)
            {
                //Check gridPlane[i][col]
            }
        }

        return true;
    }

【讨论】:

  • 谢谢。我最终认为使用 Math.Min 和 Math.Max 会有所帮助,但我肯定会使用您建议的名称。它肯定会让它更容易理解。
【解决方案2】:

您在这里真正要做的是在两个向量之间进行插值,因此您无需明确处理每种情况,只需使用它们的归一化差异来逐步遍历网格。让我解释得更好。

首先,让我们将坐标系中的字母替换为数字(只是为了让事情更简单),这样 A1 = (1; 1)、B4 = (2; 4) 等等。想象一下,您正试图从 E1 转到 A1,或者 X = (5; 1) 到 Y = (1; 1)。要一次性完成这个旅程,您需要从 X 的第一个分量中删除 4 个单位,这可以在数学上写为 X + (-4; 0) = Y。重新排列它为我们提供了找到任何之间差异的公式两个向量:Y - X = (-4; 0),也就是你的目标位置(Y)减去你的起始位置(X)。

但您不想一口气完成这段旅程,您想分几步检查是否有另一艘船挡住了您。现在,因为你在一个离散的整数网格中(即你在位置 1 或 2,你不能在位置 1.5),这简化了事情:你可以走的最长的一步,同时不会错过沿线的任何船只way 的长度为 1。因此,如果您的点之间的差异为 (-4; 0),则您需要 (-1; 0) 的步长。如果差为 (0; 5),则需要 (0; 1) 的步长。在保持其方向不变的情况下获取向量并将其减少到长度 1 的过程称为归一化。由于您的向量将始终是轴对齐的(即您不会沿对角线行走),您可以作弊并在每个组件上使用Math.sign,而不是进行“完整”向量归一化。

把它们放在一起会给你这样的东西(考虑一下这个伪代码,我什至不知道它是否会编译):

int diffX = (int)coordinateLetter2 - (int)coordinateLetter1;
int diffY = coordinateNumber2 - coordinateNumber1;

int stepX = Math.sign(diffX);
int stepY = Math.sign(diffY);

CoordinateLetter currentX = coordinateLetter1;
int currentY = coordinateNumber1;
while (currentX != coordinateLetter2 || currentY != coordinateNumber2)
{
    if (HasCollision(currentX, currentY))
        return false;

    currentX += stepX;
    currentY += stepY;
}

return true;

我还建议创建一个结构来保存位置的两个坐标(字母和数字),以便您可以直接对位置进行操作,而不是对其组件进行操作。这使得推理二维码变得更容易。

【讨论】:

  • 解决问题的“科学”方法。我印象深刻!
  • 很难读懂这个答案(添加换行符、标记代码等)。
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 2022-06-19
  • 1970-01-01
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多