|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React from 'react'; |
|
import './control.css'; |
|
|
|
|
|
import { useDispatch, useSelector } from 'react-redux'; |
|
|
|
|
|
import { startGame, endGame, undoMove, setAiFirst, setDepth, setIndex } from '../store/gameSlice'; |
|
|
|
|
|
import { board_size } from '../config'; |
|
|
|
|
|
import { Button, Switch, Select } from 'antd'; |
|
|
|
|
|
import { STATUS } from '../status'; |
|
|
|
|
|
import { useCallback } from 'react'; |
|
|
|
|
|
function Control() { |
|
|
|
const dispatch = useDispatch(); |
|
|
|
|
|
const { loading, winner, status, history, aiFirst, depth, index, score, path, currentDepth } = |
|
useSelector((state) => state.game); |
|
|
|
|
|
|
|
|
|
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]); |
|
|
|
|
|
const onFirstChange = useCallback((checked) => { |
|
dispatch(setAiFirst(checked)); |
|
}, [dispatch]); |
|
|
|
|
|
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> |
|
); |
|
} |
|
|
|
|
|
export default Control; |
|
|