File size: 5,700 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
/*
此代码是一个React组件,用于实现一个五子棋游戏的棋盘。该棋盘能够响应用户点击,将点击位置转换为棋子坐标,并根据游戏状态更新棋盘上的棋子。
它使用了React的hooks,Redux来管理状态,以及axios来与服务器通信。该组件还负责展现棋子的历史记录以及高亮最后一个落子位置。棋盘和棋子的样式通过CSS实现。
*/

// 引入React的核心功能
import React, { useState, useEffect } from "react";
// 引入Redux的hook,用于派发action和选择器
import { useDispatch, useSelector } from 'react-redux';
// 引入Redux store中的动作
import { movePiece, tempMove } from '../store/gameSlice';
// 引入CSS样式文件
import './board.css';
// 引入背景图片
import bg from '../assets/bg.jpg';
// 引入棋盘大小的配置
import { board_size } from '../config';
// 引入游戏状态的常量
import { STATUS } from '../status';
// 引入axios库用于发起HTTP请求
import axios from 'axios';

// 定义Board组件
const Board = () => {
  // 使用Redux的hook来获取dispatch方法
  const dispatch = useDispatch();
  // 使用Redux的hook来从store中选取需要的游戏数据
  const { board, currentPlayer, history, status, size, loading, winner, depth, index } = useSelector((state) => state.game);

  // 定义点击棋盘的事件处理函数
  const handleClick = async (i, j) => {
    // 如果游戏正在加载或者不在游戏中,则不处理点击
    if (loading || status !== STATUS.GAMING) {
      console.log(loading, status);
      console.log(' loading || status !== STATUS.GAMING ');
      return;
    }
    // 如果点击的位置没有棋子
    if (board[i][j] === 0) {
      // 如果深度小于等于0,需要与服务器通信
      if (depth <= 0) {
        try {
          // 先在本地进行临时移动
          dispatch(tempMove([i, j]))
          // 向服务器发送步骤信息,并等待响应
          const response = await axios.post('https://zjowowen-gomoku.hf.space/gomoku_server_ui/', {
            command: 'step',
            argument: [i, j, depth], // [i,j] 表示玩家点击的动作,当depth<=0时,根据 depth 确定 Agent Type
            uid: ':1' // 如果需要的话,这里应填入玩家的唯一标识符
          });
          // 服务器响应的数据,这里假设服务器返回的 Agent 动作格式为 {'i': x, 'j': y }
          const agentAction = response.data.result.action;
          // 使用服务器返回的动作更新Redux store
          dispatch(movePiece({ position: [i, j, agentAction.i, agentAction.j], depth: depth }));
        } catch (error) {
          // 如果通信失败,则打印错误信息
          console.error('Error communicating with the server: ', error.response || error);
        }
      } else {
        // 如果不需要与服务器通信,直接在本地执行移动
        dispatch(tempMove([i, j]))
        dispatch(movePiece({ position: [i, j], depth }));
        console.log('depth > 0 ');
      }
    }
  };

  // 使用effect hook来处理胜利者的出现
  useEffect(() => {
    // 如果有胜利者出现,弹出提示框
    if (winner === 1 || winner === -1) {
      window.alert(winner === 1 ? '黑棋获胜' : '白棋获胜')
    }
  }, [winner]);

  // 计算单个棋盘格子的样式
  const cellStyle = {
    width: `${375 / board_size}px`,
    height: `${375 / board_size}px`,
  };

  // 渲染棋盘组件
  return (
    <div className="board" style={{ backgroundImage: `url(${bg})` }}>
      {/* 遍历棋盘数组,渲染每一行 */}
      {board.map((row, i) => (
        <div key={i} className="board-row">
          {/* 遍历棋盘的每一列,渲染每一个格子 */}
          {row.map((cell, j) => {
            // 根据格子位置给格子添加不同的边界样式
            let cellClassName = 'cell';
            if (i === 0) {
              cellClassName += ' top';
            }
            if (i === board_size - 1) {
              cellClassName += ' bottom';
            }
            if (j === 0) {
              cellClassName += ' left';
            }
            if (j === board_size - 1) {
              cellClassName += ' right';
            }
            // 根据格子内棋子的状态给棋子添加不同的样式
            let pieceClassname = 'piece';
            if (cell === 1) {
              pieceClassname += ' black';
            } else if (cell === -1) {
              pieceClassname += ' white';
            }
            // 判断当前格子是否为最后落子的格子
            let isLastCell = false;
            const lastMove = history[history.length - 1];
            if (lastMove && (lastMove.i === i && lastMove.j === j)) {
              isLastCell = true;
            }
            // 如果显示历史记录索引,则计算当前棋子的序号
            let number = 0;
            if (index) {
              for(let x = 0; x < history.length; x++) {
                if (history[x].i === i && history[x].j === j) {
                  number = x + 1;
                  break;
                }
              }
            }
            // 渲染每一个格子,包括棋子和最后落子的标记
            return (
              <div key={j} className={cellClassName} style={cellStyle} onClick={() => handleClick(i, j)}>
                {cell == 0 ? '' : <div className={pieceClassname}>{ number === 0 ? '' : number}</div>}
                {isLastCell && <div className="last" />}
              </div>
            )
          })}
        </div>
      ))}
    </div>
  );
};

// 导出Board组件以便在其他文件中使用
export default Board;