Spaces:
Sleeping
Sleeping
| /* | |
| 这段代码是一个用于游戏AI的Web Worker脚本,用于在一个单独的线程中运行游戏AI的计算,以避免阻塞UI线程。 | |
| 它实现了简单的消息处理,根据不同的动作(如开始游戏、移动棋子、撤销操作和结束游戏)对游戏状态进行更新,并计算AI的最佳移动。 | |
| 代码使用了minimax算法来计算这些移动,这是一种常见的算法,用于在对弈游戏中找到最优策略。 | |
| */ | |
| // 导入Board类和minmax函数 | |
| import Board from './ai/board'; | |
| import { minmax } from './ai/minmax'; | |
| import { board_size } from './config'; | |
| // 监听worker的消息事件 | |
| onmessage = async function (event) { // 注意这里也添加了async关键字 | |
| const { action, payload } = event.data; // 解构传入的动作和负载数据 | |
| let res = null; | |
| switch (action) { // 根据动作类型决定要执行的代码块 | |
| case 'start': // 开始游戏 | |
| res = await start(payload.board_size, payload.aiFirst, payload.depth); // 注意这里使用了await关键字 | |
| break; | |
| case 'move': // 执行移动 | |
| res = move(payload.position, payload.depth); | |
| break; | |
| case 'undo': // 撤销操作 | |
| res = undo(); | |
| break; | |
| case 'end': // 结束游戏 | |
| res = end(); | |
| break; | |
| default: | |
| break; | |
| } | |
| postMessage({ // 发送处理结果回主线程 | |
| action, | |
| payload: res, | |
| }); | |
| }; | |
| // 初始化棋盘 | |
| let board = new Board(board_size); | |
| let score = 0, bestPath = [], currentDepth = 0; | |
| // 获取棋盘数据的函数 | |
| const getBoardData = () => { | |
| return { | |
| board: JSON.parse(JSON.stringify(board.board)), // 获取当前棋盘状态的深拷贝 | |
| winner: board.getWinner(), // 获取获胜者信息 | |
| current_player: board.role, // 获取当前玩家 | |
| history: JSON.parse(JSON.stringify(board.history)), // 获取历史记录的深拷贝 | |
| size: board.size, // 获取棋盘大小 | |
| score, // 当前得分 | |
| bestPath, // 最佳路径 | |
| currentDepth, // 当前搜索深度 | |
| } | |
| } | |
| // 开始游戏的函数 | |
| export const start = async (board_size, aiFirst = true, depth = 4) => { // 注意这里使用了async关键字,表示这是一个异步函数 | |
| console.log('start', board_size, aiFirst, depth); | |
| board = new Board(board_size); // 创建新的棋盘实例 | |
| try { | |
| if (aiFirst) { // 如果AI先手 | |
| let score, move, bestPath, currentDepth; | |
| if (depth <= 0) { //NOTE: depth <= 0 表示 AI 使用后端传来的 LightZero Agent action | |
| // 当搜索深度为0时,直接放置在中心位置, 这种情况下 cmd 只有 "step" 起作用,即如果 AI 先手,则执行预置的(7,7)动作 | |
| // move = [7, 7]; // 这里假设棋盘足够大,中心位置是[7, 7] | |
| // 当搜索深度为0时,通过HTTP请求获取移动 | |
| const response = await fetch('https://opendilabcommunity-gomoku.hf.space/gomoku_server_ui/', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| command: 'reset', | |
| argument: depth, // 这里需要根据服务器要求调整数据格式 | |
| uid: ':1' // 如果需要的话,这里应填入玩家的唯一标识符 | |
| })}); | |
| if (!response.ok) { | |
| throw new Error('Network response was not ok'); | |
| } | |
| const data = await response.json(); // 解析JSON响应 | |
| // 服务器响应的数据,这里假设服务器返回的 Agent 动作格式为 {'i': x, 'j': y } | |
| const agentAction = data.result.action; | |
| move = [agentAction.i, agentAction.j]; | |
| console.log('env reset agent action:', move); | |
| } else { | |
| // 当搜索深度不为0时,使用minimax算法计算最佳移动 | |
| const res = minmax(board, board.role, depth); | |
| [score, move, bestPath, currentDepth] = res; | |
| } | |
| // 执行移动 | |
| board.put(move[0], move[1]); | |
| } | |
| } catch (e) { | |
| console.log(e); | |
| // 在这里处理错误,比如发送错误消息回主线程 | |
| postMessage({ | |
| action: 'error', | |
| payload: { message: e.message } | |
| }); | |
| } | |
| return getBoardData(); // 返回游戏状态 | |
| }; | |
| // 执行移动的函数 | |
| export const move = (position, depth) => { | |
| console.log('move now', 'board_size:', board_size, 'depth:', depth); | |
| // try { | |
| // board.put(position[0], position[1]); // 在棋盘上放置棋子 | |
| // } catch (e) { | |
| // console.log(e); | |
| // } | |
| if (!board.isGameOver()) { // 如果游戏没有结束 | |
| let score, move, bestPath, currentDepth; | |
| if (depth <= 0) { //NOTE: depth <= 0 表示 AI 使用后端传来的 LightZero Agent action | |
| // debugger; // TODO: 调试代码,应该在实际使用中移除 | |
| try { | |
| board.put(position[0], position[1]); // 更新棋盘状态 | |
| } catch (e) { | |
| console.log(e); | |
| } | |
| // 如果搜索深度为0,直接使用输入的position作为结果 | |
| move = [position[2], position[3]]; // 假设这里position是一个数组,如[2, 3, 4, 5] | |
| } else { | |
| try { | |
| board.put(position[0], position[1]); // 将玩家的移动更新到棋盘上 | |
| } catch (e) { | |
| console.log(e); | |
| } | |
| // 当搜索深度不为0时,使用minimax算法计算最佳移动 | |
| const res = minmax(board, board.role, depth); | |
| [score, move, bestPath, currentDepth] = res; | |
| } | |
| // 执行AI的移动 | |
| board.put(move[0], move[1]); | |
| } | |
| return getBoardData(); // 返回更新后的游戏状态 | |
| }; | |
| // 结束游戏的函数,实际上并不进行任何操作 | |
| export const end = () => { | |
| // do nothing | |
| return getBoardData(); // 返回当前游戏状态 | |
| }; | |
| // 撤销操作的函数 | |
| export const undo = () => { | |
| board.undo(); // 撤销一步 | |
| board.undo(); // 再撤销一步,总共撤销两步,即玩家和AI各撤销一步 | |
| return getBoardData(); // 返回更新后的游戏状态 | |
| } |