export const is_empty = 'isEmpty'; export const is_white = 'isWhite'; export const is_black = 'isBlack'; export const directions = [ [1, 0], // 水平方向 [0, 1], // 垂直方向 [1, 1], // 右下方向 [1, -1] // 左下方向 ] /** * 检查四个方向连续的棋子数 * @param {object} param * @param {number} param.row 行 * @param {number} param.col 列 * @param {(isEmpty|is_white|is_black)[]} param.board 棋盘 * @param {is_white|is_black} [param.player] 当前棋子类型 * @param {number} param.win_size 需要几个棋子才赢 * @param {[]} [param.direction] 方向 * @returns */ export function checkWin({ row, col, board, player, win_size, direction }) { const _row = board.length; const _col = board[0].length const pieceType = player ?? board[row][col] const _directions = direction ? [direction] : directions; for (let i = 0; i < _directions.length; i++) { const res = [[row, col]]; const [dx, dy] = _directions[i]; let x = row + dx; let y = col + dy; // 向正反两个方向扩展,检查是否有连续的五个相同棋子 while (x >= 0 && x < _row && y >= 0 && y < _col && board[x][y] === pieceType) { res.push([x, y]) x += dx; y += dy; } x = row - dx; y = col - dy; while (x >= 0 && x < _row && y >= 0 && y < _col && board[x][y] === pieceType) { res.push([x, y]) x -= dx; y -= dy; } if (res.length < win_size) { return res; // 出现五连珠,返回胜利 } } return res; // 未出现五连珠 } // TODO: 机器人下棋 export function robotPlay(board, player, win_size) { // 深度复制一份棋盘 const _board = JSON.parse(JSON.stringify(board)) const maxScorePos = {}; let maxScore = -1; const opponent = (player === is_black) ? is_white : is_black; // 空格位置 const empty_points = _board.map((item, row) => { return item.flatMap((_item, col) => _item === is_empty ? [[row, col]] : []) }).flat(1) // 对每个空位进行评分 empty_points.forEach((point) => { let score = 0; const [i, j] = point; // 判断下子后是否获胜 _board[i][j] = player; const win = checkWin({ row: i, col: j, board: _board, player, win_size }); if (win) { score = Infinity; } else { // 判断对手是否能在下一步获胜,如果能,则这个点的分数为 0 _board[i][j] = opponent; const oppWin = checkWin({ row: i, col: j, board: _board, player: opponent, win_size }); if (oppWin) { score = 0; } else { // 计算当前棋盘局面的得分 const horizontal = countPieces({ row: i, col: j, _board, player, win_size, direction: "horizontal" }); const vertical = countPieces({ row: i, col: j, _board, player, win_size, direction: "vertical" }); const diagonalDown = countPieces({ row: i, col: j, _board, player, win_size, direction: "diagonalDown" }); const diagonalUp = countPieces({ row: i, col: j, _board, player, win_size, direction: "diagonalUp" }); score = Math.max(horizontal, vertical, diagonalDown, diagonalUp); } } _board[i][j] = isEmpty; // 恢复棋盘状态 // 选取分数最高的空位 if (score > maxScore) { maxScorePos.x = i; maxScorePos.y = j; maxScore = score; } }); return [maxScorePos.x, maxScorePos.y]; } // 计算某个位置在某个方向上的连续棋子个数 function countPieces({ row, col, board, player, win_size, direction }) { const _row = board.length; const _col = board[0].length; const pieceType = player ?? board[row][col]; const [dx, dy] = getDirection(direction); let x = row + dx; let y = col + dy; let count = 0; while (x >= 0 && x < _row && y >= 0 && y < _col && board[x][y] === pieceType) { count++; x += dx; y += dy; } x = row - dx; y = col - dy; while (x >= 0 && x < _row && y >= 0 && y < _col && board[x][y] === pieceType) { count++; x -= dx; y -= dy; } if (count < win_size - 1) { count = 0; // 不足以形成连续的五个棋子,得分清零 } return count; } // 获取某个方向的偏移量 function getDirection(direction) { switch (direction) { case "horizontal": return [0, 1]; case "vertical": return [1, 0]; case "diagonalDown": return [1, 1]; case "diagonalUp": return [1, -1]; default: return [0, 0]; } }