从一个点出发,向四个方向延伸——五子棋胜负判定的核心算法

截图如下:

在这里插入图片描述

checkWin算法详解

checkWin(row: number, col: number, player: number): boolean {
  const directions: number[][] = [
    [0, 1],   // 水平 →
    [1, 0],   // 垂直 ↓
    [1, 1],   // 主对角线 ↘
    [1, -1]   // 副对角线 ↙
  ];

  for (const dir of directions) {
    let count = 1;  // 包含当前落子
    
    // 正方向计数
    for (let i = 1; i < 5; i++) {
      const r = row + dir[0] * i;
      const c = col + dir[1] * i;
      if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
      if (this.board[r][c] === player) count++;
      else break;
    }
    
    // 反方向计数
    for (let i = 1; i < 5; i++) {
      const r = row - dir[0] * i;
      const c = col - dir[1] * i;
      if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
      if (this.board[r][c] === player) count++;
      else break;
    }
    
    if (count >= 5) return true;
  }
  return false;
}

算法思路

五子棋的获胜条件是:同一颜色的棋子在水平、垂直或对角线方向上连续排列5个或以上。

判定算法的核心思想:

  1. 以最新落子位置为中心
  2. 沿4个方向(水平、垂直、两条对角线)分别检查
  3. 每个方向向正反两个方向延伸,统计连续同色棋子数
  4. 如果任一方向的总数≥5,则获胜

四个方向的向量

方向1: [0, 1]  →  水平方向(行不变,列递增)
方向2: [1, 0]  ↓  垂直方向(行递增,列不变)
方向3: [1, 1]  ↘  主对角线(行列同时递增)
方向4: [1, -1] ↙  副对角线(行递增,列递减)

用向量表示方向的好处是统一的计算公式:

  • 正方向:r = row + dir[0] * ic = col + dir[1] * i
  • 反方向:r = row - dir[0] * ic = col - dir[1] * i

图解计数过程

假设黑棋在(7,7)落子,向左有2个黑棋,向右有2个黑棋:

水平方向 [0,1]:
←反方向               正方向→
  ○ ○ ● ←(7,7)→ ● ○ ○
        count=1
  ←延伸           延伸→
  count=3         count=5  → 获胜!

为什么只需要检查4个方向

五子棋有8个方向(上下左右+4个对角线),但每对相反方向是同一条线。检查4个方向的正反两侧就覆盖了所有8个方向:

      ↖ ↑ ↗
       \ | /
    ← — ● — →
       / | \
      ↙ ↓ ↘

4个方向 × 2侧 = 8个方向

为什么只需检查最近一手

这是算法的关键优化——只检查最新落子位置的胜负

原因:之前的状态没有产生五连(否则游戏已结束),新落子只可能在其所在的4条线上形成五连。因此不需要全盘扫描。

时间复杂度:O(4 × 4 × 2) = O(32) ≈ O(1),常数时间!

边界处理

if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;

每次延伸都要检查是否越界。这是防止数组越界的标准做法。

AIPlayer中的checkWin

AIPlayer中有一个独立的checkWin实现(因为AI不持有GomokuEngine实例):

private checkWin(board: number[][], row: number, col: number, player: number): boolean {
  const directions: number[][] = [[0, 1], [1, 0], [1, 1], [1, -1]];
  for (const dir of directions) {
    let count = 1;
    for (let i = 1; i < 5; i++) {
      const r = row + dir[0] * i;
      const c = col + dir[1] * i;
      if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
      if (board[r][c] === player) count++;
      else break;
    }
    for (let i = 1; i < 5; i++) {
      const r = row - dir[0] * i;
      const c = col - dir[1] * i;
      if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
      if (board[r][c] === player) count++;
      else break;
    }
    if (count >= 5) return true;
  }
  return false;
}

逻辑完全一致,区别在于接收board参数而非使用this.board

总结

五子连珠判定算法是游戏引擎中最精巧的部分之一:

  1. 以点带面:只检查最新落子,O(1)复杂度
  2. 向量方向:4个方向向量统一处理
  3. 正反延伸:每个方向检查正反两侧
  4. 边界安全:每次延伸都验证越界

这个算法是棋类游戏胜负判定的经典范式,可以推广到四子棋、六子棋等变体。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐