共计 3531 个字符,预计需要花费 9 分钟才能阅读完成。
游戏规则与基础架构
黑白棋(又称翻转棋)的规则简单但策略丰富:

- 8×8 棋盘,双方使用黑白两色棋子
- 每次落子必须夹住对手的棋子(横 / 竖 / 斜方向)
- 被夹住的棋子会翻转为己方颜色
- 无合法移动时跳过回合,双方都无法移动时游戏结束
- 棋盘填满或双方无棋可下时,棋子多者胜
网页游戏的基础结构可分为三层:
- 表现层:Canvas 渲染棋盘和棋子
- 逻辑层:游戏状态管理、规则验证
- AI 层:决策算法和评估函数
AI 核心算法实现
Minimax 算法基础
经典的博弈树搜索算法,核心思想是:
- 我方回合选择收益最大的走法(max 层)
- 对方回合假设会选择对我最不利的走法(min 层)
- 递归直到终止条件(达到深度或游戏结束)
function minimax(board, depth, isMaximizing) {if (depth === 0 || gameOver(board)) {return evaluate(board);
}
const moves = generateMoves(board, isMaximizing);
if (isMaximizing) {
let maxEval = -Infinity;
for (const move of moves) {const newBoard = makeMove(board, move);
maxEval = Math.max(maxEval, minimax(newBoard, depth-1, false));
}
return maxEval;
} else {
let minEval = Infinity;
for (const move of moves) {const newBoard = makeMove(board, move);
minEval = Math.min(minEval, minimax(newBoard, depth-1, true));
}
return minEval;
}
}
Alpha-Beta 剪枝优化
通过记录当前层的已知最优值,提前终止无效分支的搜索:
- α:max 层当前最小值
- β:min 层当前最大值
- 当 α ≥ β 时可终止当前分支
优化后时间复杂度从 O(b^d) 降至 O(√b^d)(b 为分支因子,d 为深度)
function alphabeta(board, depth, α, β, isMaximizing) {
// 与 minimax 类似,增加剪枝逻辑
if (isMaximizing) {for (const move of moves) {const newBoard = makeMove(board, move);
α = Math.max(α, alphabeta(newBoard, depth-1, α, β, false));
if (β <= α) break; // 剪枝
}
return α;
}
// min 层同理...
}
评估函数设计
好的评估函数应考虑:
- 棋子数量差(终盘时最重要)
- 行动力(可走位置数量)
- 稳定子(不会被翻转的棋子)
- 角点和边线控制
示例加权评估:
function evaluate(board) {
let score = 0;
// 棋子差(权重 50%)score += (countPieces(board, AI_COLOR) - countPieces(board, PLAYER_COLOR)) * 0.5;
// 行动力差(权重 30%)score += (generateMoves(board, true).length - generateMoves(board, false).length) * 0.3;
// 角点控制(权重 20%)score += countCorners(board) * 0.2;
return score;
}
完整代码实现
Canvas 渲染核心
class BoardRenderer {constructor(canvas, size=8) {
this.cellSize = canvas.width / size;
this.ctx = canvas.getContext('2d');
this.drawBoard();}
drawBoard() {
this.ctx.fillStyle = '#2e8b57';
this.ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格线
this.ctx.strokeStyle = '#000';
for (let i = 0; i <= 8; i++) {
const pos = i * this.cellSize;
this.ctx.beginPath();
this.ctx.moveTo(pos, 0);
this.ctx.lineTo(pos, canvas.height);
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.moveTo(0, pos);
this.ctx.lineTo(canvas.width, pos);
this.ctx.stroke();}
}
drawPiece(x, y, color) {
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(
x * this.cellSize + this.cellSize/2,
y * this.cellSize + this.cellSize/2,
this.cellSize/2 - 2,
0, Math.PI * 2
);
this.ctx.fill();}
}
游戏状态管理
class OthelloGame {constructor() {this.board = Array(8).fill().map(() => Array(8).fill(null));
this.board[3][3] = this.board[4][4] = 'white';
this.board[3][4] = this.board[4][3] = 'black';
this.currentPlayer = 'black';
}
isValidMove(x, y) {if (this.board[x][y] !== null) return false;
// 检查 8 个方向是否能夹住对手棋子
for (let dx = -1; dx <= 1; dx++) {for (let dy = -1; dy <= 1; dy++) {if (dx === 0 && dy === 0) continue;
if (this.checkDirection(x, y, dx, dy)) {return true;}
}
}
return false;
}
checkDirection(x, y, dx, dy) {// 方向检查逻辑...}
}
性能优化实践
递归深度控制
// 根据剩余棋子数动态调整搜索深度
function getDynamicDepth(board) {const emptyCells = countEmptyCells(board);
if (emptyCells > 40) return 3; // 开局
if (emptyCells > 20) return 5; // 中局
return 7; // 残局
}
Web Worker 多线程
主线程:
const worker = new Worker('ai-worker.js');
worker.postMessage({board, depth: 5});
worker.onmessage = (e) => {
const bestMove = e.data;
// 更新 UI...
};
worker.js:
self.onmessage = (e) => {const { board, depth} = e.data;
const move = findBestMove(board, depth);
self.postMessage(move);
};
常见问题解决
棋盘表示误区
- 错误:直接用二维数组存储棋子颜色
- 正确:应包含空位、临时翻转状态等信息
避免局部最优
- 增加随机因素:当多个走法评分相近时随机选择
- 开局库:使用预定义的开局策略
- 多阶段评估:不同棋局阶段使用不同权重
移动端适配
-
触摸事件处理:
canvas.addEventListener('touchstart', (e) => {const rect = canvas.getBoundingClientRect(); const x = Math.floor((e.touches[0].clientX - rect.left) / cellSize); // 处理落点... }); -
响应式布局:
canvas { max-width: 100%; height: auto; aspect-ratio: 1/1; }
进阶方向
- 蒙特卡洛树搜索(MCTS):
- 更适合高分支因子的棋类
-
通过随机模拟评估走法
-
机器学习:
- 使用卷积神经网络评估局面
-
通过自我对弈强化学习
-
并行计算:
- 使用 WebAssembly 加速计算
- 多 Worker 并行搜索不同分支
完整项目代码可参考 GitHub 仓库(示例链接),包含详细注释和在线演示。通过调整评估函数参数和搜索深度,你可以轻松创建从初学者到大师级的不同难度 AI。
正文完
发表至: 编程开发
近一天内
