/* 这段代码定义了一个名为 Board 的类,用于表示和管理围棋或象棋等棋盘游戏的状态。以下是其功能和结构概述: 初始化棋盘大小、当前玩家角色以及各种缓存。 提供检测游戏是否结束的方法。 提供获取当前获胜者的方法。 提供获取合法走法的方法。 提供执行走子及撤销走子的方法。 提供坐标与位置之间转换的方法。 提供获取有价值走法的方法。 提供显示棋盘的方法。 提供棋盘状态的哈希方法。 提供评估棋盘状态的方法。 提供复制当前棋盘并反转角色的方法。 提供将棋盘状态转换为字符串的方法。 */ // 导入相关模块 import Zobrist from './zobrist'; import Cache from './cache'; // import { evaluate } from './evaluate'; // 这一行已经被注释掉,因为下面使用了新的评估方法 import Evaluate, { FIVE } from './eval'; // 定义棋盘类 class Board { constructor(size = 15, firstRole = 1) { this.size = size; // 棋盘大小,默认为15x15 this.board = Array(this.size).fill().map(() => Array(this.size).fill(0)); // 初始化棋盘,所有位置为空(用0表示) this.firstRole = firstRole; // 第一个玩家的角色,默认为1(通常代表黑棋) this.role = firstRole; // 当前玩家的角色 this.history = []; // 历史记录,用于记录每次走子的位置和角色 this.zobrist = new Zobrist(this.size); // 初始化Zobrist哈希 this.winnerCache = new Cache(); // 获胜者缓存 this.gameoverCache = new Cache(); // 游戏结束缓存 this.evaluateCache = new Cache(); // 评估分数缓存 this.valuableMovesCache = new Cache(); // 有价值走法缓存 this.evaluateTime = 0; // 评估时间 this.evaluator = new Evaluate(this.size); // 初始化评估器 } // 检查游戏是否结束 isGameOver() { const hash = this.hash(); // 获取当前棋盘的哈希值 // 如果游戏结束缓存中有记录,直接返回结果 if (this.gameoverCache.get(hash)) { return this.gameoverCache.get(hash); } // 如果已经有获胜者,游戏结束 if (this.getWinner() !== 0) { this.gameoverCache.put(hash, true); // 缓存结果 return true; } // 如果棋盘上没有空位,则游戏结束,否则游戏继续 for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { if (this.board[i][j] === 0) { this.gameoverCache.put(hash, false); return false; } } } this.gameoverCache.put(hash, true); return true; } // 定义getWinner函数,用于判断当前棋盘上是否有获胜者 getWinner() { // 计算当前棋盘状态的哈希值 const hash = this.hash(); // 如果在缓存中已经存在当前哈希值对应的获胜者信息,则直接返回该信息 if (this.winnerCache.get(hash)) { return this.winnerCache.get(hash); } // 定义四个检查方向:水平、垂直、正对角线、反对角线 let directions = [[1, 0], [0, 1], [1, 1], [1, -1]]; // 遍历棋盘上的所有格子 for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { // 如果当前格子为空,则跳过 if (this.board[i][j] === 0) continue; // 遍历四个方向 for (let direction of directions) { let count = 0; // 在当前方向上连续检查相同棋子的数量 while ( i + direction[0] * count >= 0 && i + direction[0] * count < this.size && j + direction[1] * count >= 0 && j + direction[1] * count < this.size && this.board[i + direction[0] * count][j + direction[1] * count] === this.board[i][j] ) { count++; } // 如果连续相同的棋子数量达到5个或以上,则该玩家获胜 if (count >= 5) { // 将获胜者信息存入缓存 this.winnerCache.put(hash, this.board[i][j]); // 返回获胜者信息 return this.board[i][j]; } } } } // 如果没有获胜者,则在缓存中记录当前哈希值对应的获胜者信息为0(无获胜者) this.winnerCache.put(hash, 0); // 返回0表示当前没有获胜者 return 0; } // 定义getValidMoves函数,用于获取当前棋盘上所有合法的落子位置 getValidMoves() { let moves = []; // 遍历棋盘的每一个格子 for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { // 如果当前格子为空,则可以落子 if (this.board[i][j] === 0) { // 将该位置加入到合法落子位置列表中 moves.push([i, j]); } } } // 返回所有合法的落子位置列表 return moves; } // 定义put函数,用于在棋盘上放置一个棋子 put(i, j, role) { // 如果没有指定角色,则使用当前角色 if (role === undefined) { role = this.role; } // 如果输入的坐标不是数字,则打印错误信息并返回false if (isNaN(i) || isNaN(j)) { console.log("Invalid move:input position is not numbers!", i, j); return false; } // 如果当前坐标已经有棋子,则打印错误信息并返回false if (this.board[i][j] !== 0) { console.log("Invalid move: current position is not empty!", i, j); return false; } // 在指定位置放置棋子 this.board[i][j] = role; // 将此次移动记录到历史记录中 this.history.push({ i, j, role }); // 使用Zobrist散列更新当前棋盘的哈希值 this.zobrist.togglePiece(i, j, role); // 更新评估器中的棋盘状态 this.evaluator.move(i, j, role); // 切换角色,如果当前是1,切换为-1;如果当前是-1,切换为1 this.role *= -1; // Switch role return true; } // 实现撤销操作的函数 undo() { // 如果历史记录为空,说明没有可撤销的步骤 if (this.history.length === 0) { console.log("No moves to undo!"); // 打印提示信息 return false; // 返回false,表示撤销失败 } // 从历史记录中取出最后一步棋的信息 let lastMove = this.history.pop(); // 将棋盘上对应的位置重置为0(假设0代表该位置没有棋子) this.board[lastMove.i][lastMove.j] = 0; // 将当前的玩家角色恢复到前一步的玩家 this.role = lastMove.role; // 切换Zobrist哈希中的棋子,用于快速哈希棋盘状态 this.zobrist.togglePiece(lastMove.i, lastMove.j, lastMove.role); // 调用评估器的undo函数,撤销上一步的评估效果 this.evaluator.undo(lastMove.i, lastMove.j); // 返回true,表示撤销成功 return true; } // 将一维位置索引转换为二维坐标 position2coordinate(position) { // 计算行索引 const row = Math.floor(position / this.size) // 计算列索引 const col = position % this.size // 返回二维坐标数组 return [row, col] } // 将二维坐标转换为一维位置索引 coordinate2position(coordinate) { // 根据行、列索引和棋盘大小计算一维位置索引 return coordinate[0] * this.size + coordinate[1] } // 获取价值较高的可落子点 getValuableMoves(role, depth = 0, onlyThree = false, onlyFour = false) { // 获取当前棋盘的哈希值 const hash = this.hash(); // 尝试从缓存中获取此哈希值对应的价值较高的落子点 const prev = this.valuableMovesCache.get(hash); if (prev) { // 如果缓存中存在,并且各项参数都相同,则直接返回缓存中的落子点 if (prev.role === role && prev.depth === depth && prev.onlyThree === onlyThree && prev.onlyFour === onlyFour) { return prev.moves; } } // 否则,调用评估器获取价值较高的落子点 const moves = this.evaluator.getMoves(role, depth, onlyThree, onlyFour); // 如果不是仅考虑三连或四连的情况,则默认在中心点落子(如果中心点为空) if (!onlyThree && !onlyFour) { const center = Math.floor(this.size / 2); if (this.board[center][center] == 0) moves.push([center, center]); } // 将计算出的落子点存入缓存 this.valuableMovesCache.put(hash, { role, moves, depth, onlyThree, onlyFour }); // 返回价值较高的落子点数组 return moves; } // 用于显示棋盘的函数,可以传入额外的位置列表以显示问号,辅助调试 display(extraPoints = []) { // 将额外的点转换为一维位置索引 const extraPosition = extraPoints.map((point) => this.coordinate2position(point)); let result = ''; // 初始化结果字符串 for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { // 获取当前遍历的点的一维位置索引 const position = this.coordinate2position([i, j]); // 如果当前点在额外的位置列表中,将其显示为问号 if (extraPosition.includes(position)) { result += '? '; continue; } // 根据棋盘上的值,显示不同的字符 switch (this.board[i][j]) { case 1: result += 'O '; // 玩家1的棋子用'O'表示 break; case -1: result += 'X '; // 玩家-1的棋子用'X'表示 break; default: result += '- '; // 空位用'-'表示 break; } } result += '\n'; // 每行结束后添加换行符 } // 返回棋盘的字符串表示 return result; } // 生成当前棋盘状态的哈希值的函数 hash() { // 调用zobrist实例的getHash方法来获取当前棋盘的哈希值 return this.zobrist.getHash(); } // 注释掉的旧的评估函数,可能是用于调试或替换的函数 //evaluate(role) { // const start = + new Date(); // const hash = this.hash(); // const prev = this.evaluateCache.get(hash); // if (prev) { // if (prev.role === role) { // return prev.value; // } // } // const value = evaluate(this.board, role); // this.evaluateTime += +new Date - start; // this.evaluateCache.put(hash, { role, value }); // return value; //} // 新的评估函数,用于评估当前棋盘对指定玩家的得分 evaluate(role) { // 获取当前棋盘的哈希值 const hash = this.hash(); // 从评估缓存中获取之前的评估结果 const prev = this.evaluateCache.get(hash); if (prev) { // 如果缓存中有对应角色的评估结果,直接返回该结果 if (prev.role === role) { return prev.score; } } // 获取当前棋盘的胜者 const winner = this.getWinner(); let score = 0; // 如果已经有胜者,根据胜者和当前角色计算分数 if (winner !== 0) { score = FIVE * winner * role; } else { // 否则通过评估器计算当前角色的得分 score = this.evaluator.evaluate(role); } // 将评估结果存入缓存 this.evaluateCache.put(hash, { role, score }); // 返回评估得分 return score; } // 反转棋盘的函数,反转棋盘上所有棋子的角色 reverse() { // 创建新的Board实例,大小与当前棋盘相同,但是首个落子角色相反 const newBoard = new Board(this.size, -this.firstRole); // 遍历历史记录中的所有落子 for (let i = 0; i < this.history.length; i++) { // 获取落子的坐标和角色 const { i: x, j: y, role } = this.history[i]; // 在新棋盘上落子,但是角色取反 newBoard.put(x, y, -role); } // 返回反转后的棋盘 return newBoard; } // 将棋盘转换为字符串形式的函数 toString() { // 遍历棋盘的每一行,将每个位置的值转换为字符串,并将每行连接起来 return this.board.map(row => row.join('')).join(''); } } // 导出Board类 export default Board;