zjowowen's picture
init space
079c32c
raw
history blame
5.88 kB
/*
这段代码是一个用于游戏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://zjowowen-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(); // 返回更新后的游戏状态
}