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;
|