zjowowen's picture
init space
079c32c
raw
history blame
No virus
5.84 kB
/*
这段代码定义了一个React组件Control,它是一个用户界面控制面板,用于启动和结束游戏、悔棋、设置AI的一些参数等。
它使用react-redux的useDispatch和useSelector来与Redux状态管理库交互,允许它派发动作并获取状态。
这个组件有以下几个部分:
按钮组: 提供开始游戏、悔棋和认输的操作。
设置项: 允许用户设置AI是否先手、AI的类型(深度)、是否显示序号。
状态显示: 显示当前的评分、搜索深度、搜索路径和历史步骤。
组件中使用了antd库的Button、Switch和Select组件来创建用户界面。
*/
// 引入React和CSS样式文件
import React from 'react';
import './control.css';
// 引入Redux的钩子函数,用于状态管理和动作派发
import { useDispatch, useSelector } from 'react-redux';
// 引入游戏状态管理的动作
import { startGame, endGame, undoMove, setAiFirst, setDepth, setIndex } from '../store/gameSlice';
// 引入游戏的配置信息,如棋盘大小
import { board_size } from '../config';
// 引入antd库中的组件供我们使用
import { Button, Switch, Select } from 'antd';
// 引入游戏的状态常量
import { STATUS } from '../status';
// 引入React的钩子函数,用于记忆函数,以避免不必要的重新渲染
import { useCallback } from 'react';
// 定义Control组件
function Control() {
// 使用dispatch发送动作到Redux的store
const dispatch = useDispatch();
// 通过useSelector从Redux的store中获取游戏状态数据
const { loading, winner, status, history, aiFirst, depth, index, score, path, currentDepth } =
useSelector((state) => state.game);
// useCallback是React的一个钩子,用于缓存函数,以便在组件的重新渲染之间保持函数的引用不变,除非依赖项发生变化。
// 这里创建了一个start函数,该函数在调用时会调用dispatch函数并发送一个startGame动作,这个动作带有关于棋盘大小、AI是否先手和AI深度的参数。
// useCallback的第二个参数是依赖项数组,只有当这些依赖项发生变化时,函数start才会被重新创建。
const start = useCallback(() => {
dispatch(startGame({board_size, aiFirst, depth}));
}, [dispatch, board_size, aiFirst, depth]);
// 缓存结束游戏的函数
const end = useCallback(() => {
dispatch(endGame());
}, [dispatch]);
// 缓存悔棋的函数
const undo = useCallback(() => {
dispatch(undoMove());
}, [dispatch]);
// 缓存改变AI先手的函数
const onFirstChange = useCallback((checked) => {
dispatch(setAiFirst(checked));
}, [dispatch]);
// 缓存改变AI深度的函数
const onDepthChange = useCallback((value) => {
dispatch(setDepth(value));
}, [dispatch]);
// 缓存改变序号显示的函数
const onIndexChange = useCallback((checked) => {
dispatch(setIndex(checked));
}, [dispatch]);
// 渲染组件
return (
<div className="controle">
<div className="buttons">
{/* 开始按钮,当游戏正在加载或不在空闲状态时禁用*/}
<Button className="button" type="primary" onClick={start} disabled={loading || status !== STATUS.IDLE}>开始</Button>
{/* 悔棋按钮,当游戏正在加载或不在游戏进行状态或没有历史步骤时禁用*/}
<Button className="button" type="primary" onClick={undo} disabled={loading || status !== STATUS.GAMING || history.length === 0}>悔棋</Button>
{/* 认输按钮,当游戏正在加载或不在游戏进行状态时禁用*/}
<Button className="button" type="primary" onClick={end} disabled={loading || status !== STATUS.GAMING}>认输</Button>
</div>
<div className="setting">
{/* AI先手开关*/}
<div className="setting-item">
AI 先手: <Switch defaultChecked={aiFirst} onChange={onFirstChange} disabled={loading} />
</div>
{/* AI类型选择器,包含不同深度的AI选项*/}
<div className="setting-item">
AI 类型:
<Select
defaultValue={String(depth)}
style={{ width: 200 }}
onChange={onDepthChange}
disabled={loading}
options={[
// 选项中的value代表AI的深度label是显示给用户看的类型名称
{ value: '-2', label: 'Random' },
{ value: '-1', label: 'RuleBot' },
{ value: '0', label: 'AlphaZero' },
{ value: '2', label: 'AB-2' },
{ value: '4', label: 'AB-4' },
// 注释掉的是其他可能的AI类型选项这些被替换为更专业的名称了
// { value: '2', label: '弱智' },
// { value: '4', label: '简单' },
// { value: '6', label: '普通' },
// { value: '8', label: '困难' },
]}
/>
</div>
{/* 序号开关,控制是否显示序号*/}
<div className="setting-item">
显示棋子步数: <Switch defaultChecked={index} onChange={onIndexChange} />
</div>
</div>
{/* 状态显示区域,显示当前评分、深度、路径和历史步骤 */}
<div className="status">
<div className="status-item">评分:{score}</div>
<div className="status-item">深度: {currentDepth}</div>
<div className="status-item">路径: {JSON.stringify(path)}</div>
{/* 历史步骤是一个数组,这里将其转换为JSON字符串显示,并且只显示每个步骤的(i, j)坐标 */}
<div className="status-item">历史: {JSON.stringify(history.map(h => [h.i, h.j]))}</div>
</div>
</div>
);
}
// 导出Control组件,使其可以在其他文件中被引入和使用
export default Control;