zjowowen's picture
init space
079c32c
raw
history blame
5.7 kB
/*
此代码是一个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;