【问题标题】:How to determine if a path is free of obstacles in chess?国际象棋如何判断一条路径是否无障碍?
【发布时间】:2010-11-29 15:32:55
【问题描述】:

我有一个包含所有 Piece 对象的二维数组,每个对象都是 Rook、Bishop、King 等的实例...

我如何知道从 srcX,srcY 到 dstX,dstY 的路径是否被另一块挡住了?

我能想到的唯一事情就是涉及大量繁琐的代码 =/

【问题讨论】:

  • “路径”是什么意思? 2点之间没有多条路径吗?还是您说的是在 1 步内从 src 移动到 dest 的特定作品?
  • @lijie 是的,我说的是一次性将一个特定的片段从 src 移动到 dst

标签: java arrays algorithm


【解决方案1】:

您对“大量繁琐代码”的评论过于夸张。棋盘上的路径不超过八个方格,所有这些方格都可以遵循一个简单的算法 - 递增或递减行和/或列计数器。 (除了马,它只能移动到八个方格,不能被阻挡。)我怀疑任何一块的代码都需要超过二十行。

例如这里是主教的代码:

// check move legality not taking into account blocking
  boolean canMoveBishopTo(int srcx,int srcY,int destX,int destY) {
      if (srcX<0 || srcX>7 ||srcY<0 || srcY>7 || destX<0 || destX>7 ||destY<0 || destY>7) {
        throw new IllegalArgumentException();
      }
      if ((srcX==destX || srcY==destY) {
        return false;
      }

      if (Math.abs(destX-srcX) == Math.abs(srcY-destY) {
        return true;
      }
      return false;
    }

boolean isBishopMoveBlocked(int srcX,int srcY,int destX,int destY) {
  // assume we have already done the tests above
  int dirX = destX > srcX ? 1 : -1;
  int dirY = destY > srcY ? 1 : -1;
  for (int i=1; i < Math.abs(destX - srcX) - 1; ++i) {
    if (pieceOnSquare(srcX + i*dirX, srcY + i*dirY) {
      return false;
    }
  }
  return true;
}
  

【讨论】:

  • 当然,一个具有足够 if 语句的通用算法可以写在几行上。但是,要获得任何类型的性能,可能应该为每个正方形和方向设置一个专门的函数,以避免使用 if 语句。
  • @Daniel:这就是分析器的用途。首先写一些有效的东西。然后,如果性能是一个问题,分析和调整。
  • 我已经去过那里了 ;-) 但你说得对,马特应该先把它弄好。
  • @DJClayworth:我不太同意你的看法。您的声明仅适用于具有直线模式移动规则的棋子(即沿行列、文件和对角线)。它不适用于骑士。
  • @menjaraz 骑士无法被阻挡,因此无需确定是否有障碍物。
【解决方案2】:

我们有起点和终点,并且知道,我们只需要查看水平线、垂直线或对角线。

首先,计算一个方向向量。这是一个 2D 点,其值类似于

Point north = new Point(0,1);
Point northEast = new Point(1,1);
Point east = new Point(1,1);
// ...
Point northWest = new Point(-1,1);

这很简单:

Point start = getStart();
Point dest = getDest();
Point direction = new Point(Math.signum(dest.x-start.x), 
                            Math.signum(dest.y-start.y));

(例如:开始 = (2,2), 目的地 = (7,7) -> (signum(7-2), signum(7-2)) = (1,1))

现在只需将棋盘位置增加方向点,直到您到达目的地并检查每个 2D 点,如果该位置包含一块。

这是一个快速草稿(如果无法编译,则将其视为伪代码;))

Point start = getStart();
Point dest = getDest();
if (start.equals(dest)) return false; // nothing in between by definition

Point direction = new Point(Math.signum(dest.x-start.x), 
                            Math.signum(dest.y-start.y));
Point current = new Point(start.x+direction.x, start.y+direction.y);
while(!current.equals(dest)) {
  if (isOccupied(board[current.x][current.y])) // test to be implemented
     return true; // something in between
  current.x = current.x + direction.x;
  current.y = current.y + direction.y;      
}
return false; // nothing in between

【讨论】:

    【解决方案3】:

    需要满足两个条件:

    1. dst不能被a占用 一块同色的
    2. 所有其他(非骑士)动作都是 对角线、水平线或 垂直的。所以只需检查相邻的行, 您的列或对角线条目 数组在 srcdst 之间没有现有片段

    【讨论】:

    • 是的,这就是我的想法,但我认为这将是混乱的代码。我是否需要为每个非骑士移动都这样做,这取决于它可以去哪里?我是否还必须为 4 个可能的方向中的每一个编写不同的代码?
    • 将有八个可能的方向 - 左、上、右、下和对角线左上、右上、左下、右下。我想你可以为左、上、右、下编写一个写得很好的函数,另一个对角线。
    【解决方案4】:

    与其每次想知道时都计算它,不如随时跟踪每件作品的所有可能性可能更容易。

    【讨论】:

    • 但在某些时候我仍然需要考虑障碍
    【解决方案5】:

    我会看看 A*,它是最流行的路径算法之一: http://en.wikipedia.org/wiki/A*_search_algorithm

    它可能比您想要输入的代码多,但它是您可以在其他地方使用的非常有用的知识

    【讨论】:

      【解决方案6】:

      这完全取决于您选择哪种董事会代表。您选择的一个很容易开始,但您很快就会遇到性能问题。所以是的,您将需要编写一些繁琐的代码。它可以帮助您生成此代码,而不是手动编写。另一方面,您可能对其他表示感兴趣。例如,位板是最先进的,将(正确实施)提供巨大的加速。请注意,代码量可能仍然很大,而且肯定要复杂得多。以下是 Robert Hyatt(Crafty 的作者)的介绍,您可能会觉得有趣:boardrep

      祝你好运!

      【讨论】:

        【解决方案7】:

        由于棋子都是直线移动而不会跳跃(马除外),因此编写一个函数来检查从 src 到 dst 的(直线)线是否穿过棋子就足够了,并且对每个棋子都使用它除了骑士(它总是能够到达目的地)。

        当然,对于所有棋子,目标方格都应该是空的。

        到达目的地实际上应该是一个合法的举动——如果有必要,这也不应该太难检查。

        main 函数只会获取使用的方向(通过 dst-src 解释为类似比率的东西并简化为最简单的术语)并循环检查是否有一块在路上。

        【讨论】:

          【解决方案8】:

          让二维数组代表棋盘、空格、棋子等等。

          您需要检查 8 个方向。

          1. 向上和向右。
          2. 向上。
          3. 向上和向左。
          4. 左。
          5. 向下和向左。
          6. 向下。
          7. 向右下方。
          8. 对。

          现在您只需要知道如何增加/减少您的索引并测试空的位置。

          希望我没有放弃太多。

          【讨论】:

            【解决方案9】:

            这个问题很好地产生了一个数组解决方案(这可能是它被分配为一个数组练习的原因)。似乎您缺少的是多个方向中的每一个都可以使用步骤的符号来完成,因此在代码中您不需要明确地处理每个方向。在伪代码中:

            steps_bishop = [[1,1], [1,-1], [-1, 1], [-1,-1]] # an array of each possible step direction (which are also arrays)
            steps_rook = [[1,0], [-1,0], [0, 1], [0,-1]]
            
            # everything else is agnostic to step direction:
            current_position = get_current_position()
            steps = steps_bishop  # step like a bishop, for example
            
            for step_direction in steps:
                while still_on_board(current_position) and no_collision(current_position):
                    current_position += step_direction
            

            【讨论】:

              【解决方案10】:

              您是否考虑过将您的棋盘表示为graph?然后,您可以简单地将图形从 A 点遍历到 B 点,看看是否有任何“节点”挡在您的路上。

              【讨论】:

              • 这是一个作业,我们必须演示数组的使用
              • @Matt ...所以如果是为了作业(又名“家庭作业”),请展示你到目前为止所做的事情。
              • 您可以使用二维数组来跟踪碎片的位置。但是,当涉及到应用程序的逻辑时,我认为您可以使用图表。目前,这是我想到的最佳选择。或者,您可以将组件移动的方式作为棋子的属性。当你必须移动时,只需根据棋子的移动方式遍历数组即可。
              • @Andreas_D 任务是从零开始做一个国际象棋游戏,而不是写一个路径算法。 @npinti 我会调查一下,但看起来很混乱
              • 如果从正方形到正方形的过渡是固定的、简单的和明确定义的,那么图形似乎有点过头了。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-06-17
              • 1970-01-01
              • 1970-01-01
              • 2014-03-15
              • 2014-03-02
              • 2013-05-24
              • 1970-01-01
              相关资源
              最近更新 更多