先尝试用iFlow生成的,但是在棋盘布局这里走了一些弯路,很多时候棋盘都不规整,也就是棋子无法保证放在交叉点。很长时间才调试好,结果一改动,又全乱了。于是转而用Trae来处理。
使用的prompt
学习wgo的知识,写一个围棋程序。要求能选择5-19大小的棋盘。wgo的手册参考: https://wgo.waltheri.net/documentation
一轮就出来基本实现了,后面又微调了一下,基本就ok了。
网页主文件index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>围棋对弈程序</title>
<link rel="stylesheet" href="styles.css?v=1.1">
</head>
<body>
<div class="container">
<h1>围棋对弈程序</h1>
<div class="game-info">
<div class="status">当前回合:<span id="current-player">黑棋</span></div>
<div class="captured">
<span class="black-captured">黑棋提子:<span id="black-captured-count">0</span></span>
<span class="white-captured">白棋提子:<span id="white-captured-count">0</span></span>
</div>
</div>
<div class="board-controls">
<label for="board-size">棋盘大小:</label>
<select id="board-size">
<option value="5">5×5</option>
<option value="9">9×9</option>
<option value="13">13×13</option>
<option value="19" selected>19×19</option>
</select>
<button id="apply-size">应用</button>
</div>
<div id="board"></div>
<div class="controls">
<button id="pass-btn">虚手</button>
<button id="undo-btn">悔棋</button>
<button id="reset-btn">重新开始</button>
<button id="hint-btn">智能提示</button>
</div>
</div>
<script src="game.js?v=1.1"></script>
</body>
</html>
围棋实现主文件game.js
javascript
class GoGame {
constructor() {
// WGO标准:支持5-19的棋盘大小,默认19x19
this.boardSize = 19;
// 0 表示空位,1 表示黑棋,-1 表示白棋
this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
this.currentPlayer = 1; // 1 代表黑棋,-1 代表白棋
this.gameHistory = [];
this.capturedStones = { black: 0, white: 0 };
this.komi = 6.5; // WGO标准贴目(黑棋贴白棋6.5目)
this.passCount = 0; // 连续虚手次数
this.gameOver = false;
this.initializeBoard();
this.bindEvents();
}
initializeBoard() {
const boardElement = document.getElementById('board');
boardElement.innerHTML = '';
// WGO标准:根据棋盘大小自适应设置尺寸
let boardSizePx, cellSize, offset;
// 根据棋盘大小优化显示效果
if (this.boardSize <= 9) {
boardSizePx = this.boardSize * 50 + 40; // 小棋盘使用更大的格子
cellSize = 50;
offset = 20;
} else if (this.boardSize <= 13) {
boardSizePx = this.boardSize * 40 + 40;
cellSize = 40;
offset = 20;
} else if (this.boardSize <= 17) {
boardSizePx = this.boardSize * 35 + 35;
cellSize = 35;
offset = 17;
} else {
boardSizePx = this.boardSize * 30 + 30;
cellSize = 30;
offset = 15;
}
boardElement.style.width = `${boardSizePx}px`;
boardElement.style.height = `${boardSizePx}px`;
// 绘制棋盘网格
for (let i = 0; i < this.boardSize; i++) {
// 横线
const horizontalLine = document.createElement('div');
horizontalLine.className = 'line horizontal';
horizontalLine.style.top = `${i * cellSize + offset}px`;
horizontalLine.style.left = `${offset}px`;
horizontalLine.style.width = `${(this.boardSize - 1) * cellSize}px`;
boardElement.appendChild(horizontalLine);
// 竖线
const verticalLine = document.createElement('div');
verticalLine.className = 'line vertical';
verticalLine.style.left = `${i * cellSize + offset}px`;
verticalLine.style.top = `${offset}px`;
verticalLine.style.height = `${(this.boardSize - 1) * cellSize}px`;
boardElement.appendChild(verticalLine);
}
// 绘制星位点(根据WGO标准)
this.drawStarPoints(cellSize, offset);
this.updateStatus();
}
// 绘制星位点(根据WGO标准)
drawStarPoints(cellSize, offset) {
const boardElement = document.getElementById('board');
const starPoints = this.getStarPoints();
// 创建星位点标记
for (const [row, col] of starPoints) {
// 确保星位点在棋盘范围内
if (row < this.boardSize && col < this.boardSize) {
const starPoint = document.createElement('div');
starPoint.className = 'star-point';
starPoint.style.left = `${col * cellSize + offset}px`;
starPoint.style.top = `${row * cellSize + offset}px`;
boardElement.appendChild(starPoint);
}
}
}
// 获取星位点位置(根据不同棋盘大小调整,符合WGO标准)
getStarPoints() {
if (this.boardSize === 5) {
// 5x5棋盘只有中心点
return [[2, 2]];
} else if (this.boardSize === 9) {
// 9x9棋盘星位点:四角和中心
const hoshi = [[2, 2], [2, 6], [6, 2], [6, 6], [4, 4]];
return hoshi;
} else if (this.boardSize === 13) {
// 13x13棋盘星位点:四角、边中心和中心
const hoshi = [
[3, 3], [3, 9], [9, 3], [9, 9],
[6, 6]
];
return hoshi;
} else if (this.boardSize === 19) {
// 19x19棋盘星位点:标准9个星位点
const hoshi = [
[3, 3], [3, 9], [3, 15],
[9, 3], [9, 9], [9, 15],
[15, 3], [15, 9], [15, 15]
];
return hoshi;
} else {
// 其他大小的棋盘,根据WGO标准显示星位点
const center = Math.floor(this.boardSize / 2);
const quarter = Math.floor(this.boardSize / 4);
if (this.boardSize >= 15) {
// 大棋盘显示9个星位点
return [
[quarter, quarter], [quarter, center], [quarter, this.boardSize - quarter - 1],
[center, quarter], [center, center], [center, this.boardSize - quarter - 1],
[this.boardSize - quarter - 1, quarter], [this.boardSize - quarter - 1, center], [this.boardSize - quarter - 1, this.boardSize - quarter - 1]
];
} else {
// 小棋盘显示中心点
return [[center, center]];
}
}
}
bindEvents() {
const boardElement = document.getElementById('board');
const passButton = document.getElementById('pass-btn');
const undoButton = document.getElementById('undo-btn');
const resetButton = document.getElementById('reset-btn');
const applySizeButton = document.getElementById('apply-size');
boardElement.addEventListener('click', (e) => {
// 获取点击位置相对于棋盘的坐标
const rect = boardElement.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 根据当前棋盘大小计算格子大小
let cellSize, offset;
if (this.boardSize <= 9) {
cellSize = 50;
offset = 20;
} else if (this.boardSize <= 13) {
cellSize = 40;
offset = 20;
} else if (this.boardSize <= 17) {
cellSize = 35;
offset = 17;
} else {
cellSize = 30;
offset = 15;
}
// 计算点击的行列
const col = Math.round((x - offset) / cellSize);
const row = Math.round((y - offset) / cellSize);
// 检查是否在有效范围内
if (row >= 0 && row < this.boardSize && col >= 0 && col < this.boardSize) {
this.makeMove(row, col);
}
});
passButton.addEventListener('click', () => {
this.pass();
});
undoButton.addEventListener('click', () => {
this.undo();
});
resetButton.addEventListener('click', () => {
this.resetGame();
});
applySizeButton.addEventListener('click', () => {
this.changeBoardSize();
});
const hintButton = document.getElementById('hint-btn');
hintButton.addEventListener('click', () => {
this.showHint();
});
}
makeMove(row, col) {
// 检查位置是否为空
if (this.board[row][col] !== 0) {
return false;
}
// 保存当前状态用于悔棋
this.saveState();
// 在内部棋盘数组中放置棋子
this.board[row][col] = this.currentPlayer;
// 检查并处理吃子
const captured = this.checkAndCapture(row, col);
// 检查是否自杀(没有气且没有提子)
if (this.getLiberties(row, col) === 0 && captured === 0) {
// 自杀是非法的,恢复棋盘状态
this.restoreState();
return false;
}
// 在UI上显示棋子
this.renderStone(row, col);
// 更新提子计数
if (this.currentPlayer === 1) {
this.capturedStones.white += captured;
} else {
this.capturedStones.black += captured;
}
// 切换玩家
this.currentPlayer *= -1;
this.updateStatus();
return true;
}
renderStone(row, col) {
const board = document.getElementById('board');
// 移除已存在的同位置棋子
const existingStone = board.querySelector(`.stone[data-row="${row}"][data-col="${col}"]`);
if (existingStone) {
board.removeChild(existingStone);
}
const stone = document.createElement('div');
stone.className = `stone ${this.board[row][col] === 1 ? 'black' : 'white'}`;
stone.dataset.row = row;
stone.dataset.col = col;
// 根据当前棋盘大小计算棋子位置(与initializeBoard保持一致)
let cellSize, offset;
if (this.boardSize <= 9) {
cellSize = 50;
offset = 20;
} else if (this.boardSize <= 13) {
cellSize = 40;
offset = 20;
} else if (this.boardSize <= 17) {
cellSize = 35;
offset = 17;
} else {
cellSize = 30;
offset = 15;
}
const x = col * cellSize + offset;
const y = row * cellSize + offset;
stone.style.left = `${x}px`;
stone.style.top = `${y}px`;
// 设置棋子大小(WGO标准:棋子大小约为格子大小的80%)
const stoneSize = cellSize * 0.8;
stone.style.width = `${stoneSize}px`;
stone.style.height = `${stoneSize}px`;
board.appendChild(stone);
}
updateStatus() {
const statusElement = document.getElementById('current-player');
if (this.gameOver) {
statusElement.textContent = '游戏结束';
statusElement.style.color = '#f00';
} else {
statusElement.textContent = this.currentPlayer === 1 ? '黑棋' : '白棋';
statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
}
document.getElementById('black-captured-count').textContent = this.capturedStones.black;
document.getElementById('white-captured-count').textContent = this.capturedStones.white;
// 显示贴目信息
const komiInfo = document.getElementById('komi-info');
if (!komiInfo) {
const gameInfo = document.querySelector('.game-info');
const komiElement = document.createElement('div');
komiElement.id = 'komi-info';
komiElement.className = 'komi';
komiElement.textContent = `贴目:${this.komi}`;
gameInfo.appendChild(komiElement);
}
}
// 获取棋块(相连的同色棋子)
getGroup(row, col) {
const color = this.board[row][col];
if (color === 0) return [];
const group = [];
const visited = new Set();
const queue = [{r: row, c: col}];
visited.add(`${row},${col}`);
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
while (queue.length > 0) {
const {r, c} = queue.shift();
group.push({r, c});
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
// 检查边界
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
// 检查是否是同色棋子且未访问过
if (this.board[nr][nc] === color && !visited.has(`${nr},${nc}`)) {
visited.add(`${nr},${nc}`);
queue.push({r: nr, c: nc});
}
}
}
}
return group;
}
// 获取棋子或棋块的气
getLiberties(row, col) {
const color = this.board[row][col];
if (color === 0) return 0;
// 找到整个棋块
const group = this.getGroup(row, col);
// 计算气的数量
const liberties = new Set();
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const {r, c} of group) {
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
// 检查边界
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
// 如果是空点,增加气
if (this.board[nr][nc] === 0) {
liberties.add(`${nr},${nc}`);
}
}
}
}
return liberties.size;
}
// 检查并处理吃子
checkAndCapture(row, col) {
let capturedCount = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
const opponent = -this.currentPlayer;
// 检查周围的敌方棋子
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
// 检查边界
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
// 如果是敌方棋子
if (this.board[nr][nc] === opponent) {
// 检查该棋子或棋块是否没气
if (this.getLiberties(nr, nc) === 0) {
// 提子
const captured = this.captureGroup(nr, nc);
capturedCount += captured;
}
}
}
}
return capturedCount;
}
// 提子(移除棋块)
captureGroup(row, col) {
// 提子(移除棋块)
const group = this.getGroup(row, col);
const color = this.board[row][col];
// 从棋盘上移除棋子
for (const {r, c} of group) {
this.board[r][c] = 0;
}
// 从UI上移除棋子
this.removeStones(group);
return group.length; // 返回被提子的数量
}
// 从UI上移除棋子
removeStones(group) {
const board = document.getElementById('board');
// 从UI上移除棋子
for (const {r, c} of group) {
const stone = board.querySelector(`.stone[data-row="${r}"][data-col="${c}"]`);
if (stone) {
board.removeChild(stone);
}
}
}
// 保存当前状态用于悔棋
saveState() {
this.gameHistory.push({
board: JSON.parse(JSON.stringify(this.board)),
currentPlayer: this.currentPlayer,
capturedStones: {...this.capturedStones}
});
}
// 恢复上一个状态
restoreState() {
if (this.gameHistory.length > 0) {
const prevState = this.gameHistory.pop();
this.board = prevState.board;
this.currentPlayer = prevState.currentPlayer;
this.capturedStones = prevState.capturedStones;
this.redrawBoard();
this.updateStatus();
}
}
// 重新绘制棋盘
redrawBoard() {
const board = document.getElementById('board');
// 移除所有棋子
const stones = board.querySelectorAll('.stone');
stones.forEach(stone => board.removeChild(stone));
// 重新绘制所有棋子
for (let row = 0; row < this.boardSize; row++) {
for (let col = 0; col < this.boardSize; col++) {
if (this.board[row][col] !== 0) {
this.renderStone(row, col);
}
}
}
}
// 虚手(根据WGO标准)
pass() {
this.saveState();
this.passCount++;
// 检查游戏是否结束(连续两次虚手)
if (this.passCount >= 2) {
this.endGame();
} else {
this.currentPlayer *= -1;
this.updateStatus();
}
}
// 悔棋
undo() {
this.restoreState();
}
// 显示智能提示
showHint() {
// 如果游戏已经结束,不显示提示
if (this.gameOver) {
alert('游戏已经结束,无法提供提示');
return;
}
// 清除之前的提示
this.clearHint();
// 使用MCTS算法获取更智能的建议
const bestMove = this.getSmartHintMove ? this.getSmartHintMove() : null;
if (bestMove) {
// 显示提示标记
this.displayHint(bestMove.row, bestMove.col);
// 显示提示信息
const statusElement = document.getElementById('current-player');
const originalText = statusElement.textContent;
statusElement.textContent = `智能提示:建议在 ${String.fromCharCode(65 + bestMove.col)}${bestMove.row + 1} 落子`;
statusElement.style.color = '#007bff';
// 3秒后恢复原状态
setTimeout(() => {
statusElement.textContent = originalText;
statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
}, 3000);
} else {
// 如果MCTS没有找到合适位置,使用启发式算法作为备选
const fallbackMove = this.getBestHintMove();
if (fallbackMove) {
this.displayHint(fallbackMove.row, fallbackMove.col);
const statusElement = document.getElementById('current-player');
const originalText = statusElement.textContent;
statusElement.textContent = `智能提示:建议在 ${String.fromCharCode(65 + fallbackMove.col)}${fallbackMove.row + 1} 落子`;
statusElement.style.color = '#007bff';
setTimeout(() => {
statusElement.textContent = originalText;
statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
}, 3000);
} else {
alert('没有有效的落子位置');
}
}
}
// 清除提示标记
clearHint() {
const board = document.getElementById('board');
const existingHint = board.querySelector('.hint-marker');
if (existingHint) {
board.removeChild(existingHint);
}
}
// 显示提示标记
displayHint(row, col) {
const board = document.getElementById('board');
// 根据当前棋盘大小计算位置
let cellSize, offset;
if (this.boardSize <= 9) {
cellSize = 50;
offset = 20;
} else if (this.boardSize <= 13) {
cellSize = 40;
offset = 20;
} else if (this.boardSize <= 17) {
cellSize = 35;
offset = 17;
} else {
cellSize = 30;
offset = 15;
}
const x = col * cellSize + offset;
const y = row * cellSize + offset;
const hintMarker = document.createElement('div');
hintMarker.className = 'hint-marker';
hintMarker.style.left = `${x}px`;
hintMarker.style.top = `${y}px`;
// 设置提示标记样式
const markerSize = cellSize * 0.3;
hintMarker.style.width = `${markerSize}px`;
hintMarker.style.height = `${markerSize}px`;
board.appendChild(hintMarker);
}
// 获取最佳提示落子位置(简单启发式算法)
getBestHintMove() {
const moves = [];
// 收集所有合法落子位置
for (let row = 0; row < this.boardSize; row++) {
for (let col = 0; col < this.boardSize; col++) {
if (this.board[row][col] === 0) {
// 检查是否为合法落子
if (this.isValidMove(row, col)) {
const score = this.evaluateMove(row, col);
moves.push({ row, col, score });
}
}
}
}
// 如果没有合法落子,返回null
if (moves.length === 0) {
return null;
}
// 按得分排序,选择得分最高的位置
moves.sort((a, b) => b.score - a.score);
return moves[0];
}
// 获取智能落子位置 - 集成MCTS算法
getSmartHintMove() {
try {
// 创建当前游戏状态的副本
const gameState = {
board: JSON.parse(JSON.stringify(this.board)),
currentPlayer: this.currentPlayer,
boardSize: this.boardSize
};
// 根据棋盘大小调整MCTS参数
let iterations = 200;
if (this.boardSize <= 9) {
// 小棋盘:减少迭代次数,增加速度
iterations = 100;
} else if (this.boardSize >= 13) {
// 大棋盘:增加迭代次数,提高准确性
iterations = 300;
}
// 使用MCTS算法进行深度分析
const mcts = new MCTS(gameState, iterations);
const bestMove = mcts.getBestMove();
if (bestMove && this.isValidMove(bestMove.row, bestMove.col)) {
return bestMove;
}
} catch (error) {
console.warn('MCTS算法执行失败,使用启发式算法:', error);
}
return null;
}
// 评估落子位置的得分
evaluateMove(row, col) {
let score = 0;
// 1. 检查是否能吃子(最高优先级)
const captureScore = this.simulateCapture(row, col);
score += captureScore * 100;
// 2. 检查是否能连接己方棋子
const connectionScore = this.checkConnection(row, col);
score += connectionScore * 50;
// 3. 检查是否靠近星位点
const starScore = this.checkStarPointProximity(row, col);
score += starScore * 30;
// 4. 检查是否靠近边角
const cornerScore = this.checkCornerProximity(row, col);
score += cornerScore * 20;
// 5. 检查气数
const libertyScore = this.checkLiberties(row, col);
score += libertyScore * 10;
// 6. 小棋盘专用策略
if (this.boardSize <= 9) {
score += this.evaluateSmallBoardStrategy(row, col);
}
return score;
}
// 检查落子是否合法
isValidMove(row, col) {
if (row < 0 || row >= this.boardSize || col < 0 || col >= this.boardSize) {
return false;
}
if (this.board[row][col] !== 0) {
return false;
}
// 检查自杀规则
const tempBoard = JSON.parse(JSON.stringify(this.board));
tempBoard[row][col] = this.currentPlayer;
// 检查是否有气
const hasLiberty = this.checkLiberty(tempBoard, row, col);
// 检查是否提子
const captures = this.checkCaptures(tempBoard, row, col);
// 如果有气或者能提子,就是合法走法
return hasLiberty || captures > 0;
}
// 检查棋子是否有气
checkLiberty(board, row, col) {
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (board[nr][nc] === 0) {
return true; // 有空点,有气
}
}
}
return false;
}
// 检查是否能提子
checkCaptures(board, row, col) {
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
const opponent = -this.currentPlayer;
let captures = 0;
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (board[nr][nc] === opponent) {
// 检查该棋子是否没气
if (!this.checkGroupLiberty(board, nr, nc)) {
captures++;
}
}
}
}
return captures;
}
// 检查棋块是否有气
checkGroupLiberty(board, row, col) {
const visited = new Set();
const queue = [{r: row, c: col}];
visited.add(`${row},${col}`);
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
while (queue.length > 0) {
const {r, c} = queue.shift();
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (board[nr][nc] === 0) {
return true; // 找到气
}
if (board[nr][nc] === board[row][col] && !visited.has(`${nr},${nc}`)) {
visited.add(`${nr},${nc}`);
queue.push({r: nr, c: nc});
}
}
}
}
return false;
}
// 小棋盘专用策略评估
evaluateSmallBoardStrategy(row, col) {
let score = 0;
const center = Math.floor(this.boardSize / 2);
// 1. 中心控制(小棋盘中心更重要)
const distanceToCenter = Math.abs(row - center) + Math.abs(col - center);
score += (this.boardSize - distanceToCenter) * 15;
// 2. 快速扩张(小棋盘需要快速占领空间)
const expansionScore = this.evaluateExpansion(row, col);
score += expansionScore * 20;
// 3. 分割对手(小棋盘分割对手更有效)
const splitScore = this.evaluateOpponentSplit(row, col);
score += splitScore * 25;
// 4. 眼位形成(小棋盘眼位更重要)
const eyeScore = this.evaluateEyeFormation(row, col);
score += eyeScore * 30;
return score;
}
// 评估扩张潜力
evaluateExpansion(row, col) {
let expansion = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]];
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (this.board[nr][nc] === 0) {
expansion++;
}
}
}
return expansion;
}
// 评估分割对手的效果
evaluateOpponentSplit(row, col) {
let splitScore = 0;
const opponent = -this.currentPlayer;
// 检查是否能够分割对手的棋块
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
let opponentGroups = 0;
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (this.board[nr][nc] === opponent) {
opponentGroups++;
}
}
}
// 如果能够分割对手的多个棋块,得分更高
if (opponentGroups >= 2) {
splitScore = opponentGroups * 10;
}
return splitScore;
}
// 评估眼位形成潜力
evaluateEyeFormation(row, col) {
let eyeScore = 0;
// 检查是否能够形成眼位
const tempBoard = JSON.parse(JSON.stringify(this.board));
tempBoard[row][col] = this.currentPlayer;
// 检查周围是否能够形成眼位
const eyePatterns = this.getEyePatterns(tempBoard, row, col);
eyeScore += eyePatterns * 15;
return eyeScore;
}
// 获取眼位模式
getEyePatterns(board, row, col) {
let patterns = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
// 检查是否能够形成基本眼位
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (board[nr][nc] === this.currentPlayer) {
// 检查这个棋子周围是否有形成眼位的潜力
const surrounding = this.getSurroundingEmptyPoints(board, nr, nc);
if (surrounding >= 2) {
patterns++;
}
}
}
}
return patterns;
}
// 获取周围空点数
getSurroundingEmptyPoints(board, row, col) {
let emptyPoints = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (board[nr][nc] === 0) {
emptyPoints++;
}
}
}
return emptyPoints;
}
// 模拟吃子
simulateCapture(row, col) {
// 临时放置棋子
this.board[row][col] = this.currentPlayer;
let captured = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
const opponent = -this.currentPlayer;
// 检查周围的敌方棋子
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (this.board[nr][nc] === opponent) {
// 检查敌方棋子是否被提
if (this.getLiberties(nr, nc) === 0) {
captured++;
}
}
}
}
// 恢复棋盘状态
this.board[row][col] = 0;
return captured;
}
// 检查连接己方棋子
checkConnection(row, col) {
let connections = 0;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (this.board[nr][nc] === this.currentPlayer) {
connections++;
}
}
}
return connections;
}
// 检查是否靠近星位点
checkStarPointProximity(row, col) {
const starPoints = this.getStarPoints();
let proximity = 0;
for (const [sr, sc] of starPoints) {
const distance = Math.abs(row - sr) + Math.abs(col - sc);
if (distance <= 2) {
proximity += (3 - distance);
}
}
return proximity;
}
// 检查是否靠近边角
checkCornerProximity(row, col) {
const center = Math.floor(this.boardSize / 2);
const distanceFromCenter = Math.abs(row - center) + Math.abs(col - center);
// 距离中心越远,得分越低(鼓励开局时靠近边角)
return Math.max(0, 5 - distanceFromCenter / 2);
}
// 检查气数
checkLiberties(row, col) {
// 临时放置棋子
this.board[row][col] = this.currentPlayer;
const liberties = this.getLiberties(row, col);
// 恢复棋盘状态
this.board[row][col] = 0;
return Math.min(liberties, 4); // 最大4分
}
// 检查是否为合法落子
isValidMove(row, col) {
// 检查位置是否为空
if (this.board[row][col] !== 0) {
return false;
}
// 临时放置棋子
this.board[row][col] = this.currentPlayer;
// 检查是否有气
const hasLiberties = this.getLiberties(row, col) > 0;
// 检查是否提子
let captures = false;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
const opponent = -this.currentPlayer;
for (const [dr, dc] of directions) {
const nr = row + dr;
const nc = col + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
if (this.board[nr][nc] === opponent && this.getLiberties(nr, nc) === 0) {
captures = true;
break;
}
}
}
// 恢复棋盘状态
this.board[row][col] = 0;
// 合法落子条件:有气或者能提子
return hasLiberties || captures;
}
// 游戏结束(根据WGO标准计算胜负)
endGame() {
this.gameOver = true;
// 计算领地(空点和己方棋子)
const territory = this.calculateTerritory();
// 计算最终得分
const blackScore = territory.black + this.capturedStones.white;
const whiteScore = territory.white + this.capturedStones.black + this.komi;
// 显示结果
let resultMessage;
if (blackScore > whiteScore) {
resultMessage = `黑棋胜!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}\n黑棋领先:${blackScore - whiteScore}目`;
} else if (whiteScore > blackScore) {
resultMessage = `白棋胜!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}\n白棋领先:${whiteScore - blackScore}目`;
} else {
resultMessage = `平局!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}`;
}
alert(resultMessage);
// 更新状态显示
this.updateStatus();
}
// 计算领地(WGO标准)
calculateTerritory() {
const territory = { black: 0, white: 0 };
const visited = new Set();
for (let row = 0; row < this.boardSize; row++) {
for (let col = 0; col < this.boardSize; col++) {
const key = `${row},${col}`;
if (!visited.has(key) && this.board[row][col] === 0) {
// 找到空点区域
const area = this.getEmptyArea(row, col);
const owner = this.determineAreaOwner(area);
if (owner === 1) {
territory.black += area.length;
} else if (owner === -1) {
territory.white += area.length;
}
// 标记为已访问
area.forEach(({r, c}) => visited.add(`${r},${c}`));
}
}
}
return territory;
}
// 获取空点区域
getEmptyArea(row, col) {
const area = [];
const queue = [{r: row, c: col}];
const visited = new Set();
visited.add(`${row},${col}`);
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
while (queue.length > 0) {
const {r, c} = queue.shift();
area.push({r, c});
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
const key = `${nr},${nc}`;
if (!visited.has(key) && this.board[nr][nc] === 0) {
visited.add(key);
queue.push({r: nr, c: nc});
}
}
}
}
return area;
}
// 确定区域归属
determineAreaOwner(area) {
let blackBorder = false;
let whiteBorder = false;
for (const {r, c} of area) {
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
const stone = this.board[nr][nc];
if (stone === 1) blackBorder = true;
if (stone === -1) whiteBorder = true;
}
}
}
if (blackBorder && !whiteBorder) return 1; // 黑棋领地
if (whiteBorder && !blackBorder) return -1; // 白棋领地
return 0; // 中立或争议区域
}
// 重新开始游戏
resetGame() {
if (confirm('确定要重新开始游戏吗?')) {
this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
this.currentPlayer = 1;
this.gameHistory = [];
this.capturedStones = { black: 0, white: 0 };
this.passCount = 0;
this.gameOver = false;
this.initializeBoard();
this.redrawBoard();
}
}
// 改变棋盘大小
changeBoardSize() {
const sizeSelect = document.getElementById('board-size');
const newSize = parseInt(sizeSelect.value);
if (newSize >= 5 && newSize <= 19) {
if (confirm(`确定要将棋盘大小改为 ${newSize}×${newSize} 吗?这将重新开始游戏。`)) {
this.boardSize = newSize;
this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
this.currentPlayer = 1;
this.gameHistory = [];
this.capturedStones = { black: 0, white: 0 };
this.initializeBoard();
this.redrawBoard();
}
}
}
}
// 初始化游戏
document.addEventListener('DOMContentLoaded', () => {
new GoGame();
});
效果如图

当前实现的功能
5*5 19*19等棋盘的选择,吃子判断以及提子,悔棋以及智能提示等。
当前的问题:
智能提示还不太智能,机器AI棋力低。
打劫的设定还不对。也就是打劫吃了子,当前对方可以直接吃回去,这与规则不符合。