File size: 5,882 Bytes
079c32c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
这段代码是一个用于游戏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(); // 返回更新后的游戏状态
}