Skip to content
On this page

井字棋里的算法小问题

2023 年 3 月下旬,React 团队更新了官方网站,并正式为官网启用了新域名:https://react.dev/。 其中在井字棋教程中,有几个有趣的小问题可以思考。

1、计算赢棋线

在官网教程中,直接给出了赢棋的所有可能性,见下方代码中的lines变量:

javascript
function calculateWinner(squares) {
  // 列举所有可能赢棋的坐标
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ]
  // 遍历所有赢棋的可能性:当某条赢棋线上的旗子全部属于同一个玩家,则该玩家胜出
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i]
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a]
    }
  }
  return null
}

如何通过计算得到这个值呢?转换成题目:

javascript
// 棋盘的大小
const size = 3
function generateWinningLines(size) {
  // 所有可能赢棋的坐标
  const lines = [][]
  // ... 计算lines
  return lines
}

参考代码如下:

javascript
/**
 * @param size 棋盘的大小(例如,对于 3x3 的棋盘,size 为 3)
 */
function generateWinningLines(size: number): number[][] {
  // 所有可能的获胜的坐标
  const lines: number[][] = []

  // 计算棋子连成一行获胜的坐标
  for (let i = 0; i < size; i++) {
    const row: number[] = []
    for (let j = 0; j < size; j++) {
      row.push(i * size + j)
    }
    lines.push(row)
  }

  // 计算棋子连成一列获胜的坐标
  for (let i = 0; i < size; i++) {
    const col: number[] = []
    for (let j = 0; j < size; j++) {
      col.push(j * size + i)
    }
    lines.push(col)
  }

  // 计算棋子连成对角线获胜的坐标
  const diag1: number[] = []
  const diag2: number[] = []
  for (let i = 0; i < size; i++) {
    diag1.push(i * size + i)
    diag2.push(i * size + (size - 1 - i))
  }
  lines.push(diag1)
  lines.push(diag2)

  return lines
}

// 使用函数生成 3x3 井字棋的赢棋线路
const lines = generateWinningLines(3)
console.log(lines)

2、一定有胜出者吗?

在 3x3 的棋盘下棋有几率出现没有胜者的情况,如何证明呢?如果换到 4x4 的棋盘下棋,一定能保证有人胜出吗?

在 3x3 的棋盘平局示例:

mathematica
X | O | X
O | X | O
O | X | O

在 4x4 的棋盘平局示例:

mathematica
X | O | X | X
O | X | O | O
O | X | O | X
X | O | X | O