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(); // 返回更新后的游戏状态 | |
} |