added pause game function in doudizhu, optimize changing game speed
This commit is contained in:
parent
29c709d749
commit
a9f234fc43
|
@ -3,6 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/core": "^4.9.0",
|
||||
"element-react": "^1.4.34",
|
||||
"element-theme-default": "^1.4.13",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"nodemon": "^2.0.2",
|
||||
|
|
|
@ -8,143 +8,469 @@
|
|||
{
|
||||
"id": 0,
|
||||
"index": 0,
|
||||
"role": "peasant"
|
||||
"role": "peasant",
|
||||
"agentInfo": {
|
||||
"name": "random",
|
||||
"winRate": 0.333
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"index": 1,
|
||||
"role": "peasant"
|
||||
"role": "peasant",
|
||||
"agentInfo": {
|
||||
"name": "DQN",
|
||||
"winRate": 0.555
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"index": 2,
|
||||
"role": "landlord"
|
||||
"role": "landlord",
|
||||
"agentInfo": {
|
||||
"name": "CFR",
|
||||
"winRate": 0.666
|
||||
}
|
||||
}
|
||||
],
|
||||
"moveHistory": [
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "H3 S3 D3 D5"
|
||||
"move": "H3 S3 D3 D5",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "H3 S3 D3 D5",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "H3",
|
||||
"probability": 0.05
|
||||
},
|
||||
{
|
||||
"move": "D5",
|
||||
"probability": 0.02
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "S9 H9 D9 S3"
|
||||
"move": "S9 H9 D9 S3",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "S9 H9 D9 S3",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "HQ CQ DQ S3",
|
||||
"probability": 0.05
|
||||
},
|
||||
{
|
||||
"move": "HQ CQ DQ C3",
|
||||
"probability": 0.02
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "SJ HJ DJ D7"
|
||||
"move": "SJ HJ DJ D7",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "SJ HJ DJ D7",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "SJ HJ DJ C9",
|
||||
"probability": 0.05
|
||||
},
|
||||
{
|
||||
"move": "BJ RJ",
|
||||
"probability": 0.02
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "HQ CQ DQ C7"
|
||||
"move": "HQ CQ DQ C7",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "HQ CQ DQ C7",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "HQ CQ DQ CJ",
|
||||
"probability": 0.05
|
||||
},
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.02
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "C4 D4"
|
||||
"move": "C4 D4",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "HK DK",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "C4 D4",
|
||||
"probability": 0.2
|
||||
},
|
||||
{
|
||||
"move": "S2 H2",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "ST HT"
|
||||
"move": "ST HT",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "S6 D6",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "ST HT",
|
||||
"probability": 0.2
|
||||
},
|
||||
{
|
||||
"move": "HA CA",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "SK CK"
|
||||
"move": "SK CK",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "RJ BJ",
|
||||
"probability": 0.3
|
||||
},
|
||||
{
|
||||
"move": "SK CK",
|
||||
"probability": 0.2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "S2 H2"
|
||||
"move": "S2 H2",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "HK DK",
|
||||
"probability": 0.5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "S6 H6"
|
||||
"move": "S6 H6",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "S6 H6",
|
||||
"probability": 0.6
|
||||
},
|
||||
{
|
||||
"move": "HK DK",
|
||||
"probability": 0.2
|
||||
},
|
||||
{
|
||||
"move": "CJ",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.8
|
||||
},
|
||||
{
|
||||
"move": "S7 H7",
|
||||
"probability": 0.1
|
||||
},
|
||||
{
|
||||
"move": "HA CA",
|
||||
"probability": 0.05
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "CT DT"
|
||||
"move": "CT DT",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "S7 H7",
|
||||
"probability": 0.1
|
||||
},
|
||||
{
|
||||
"move": "HA CA",
|
||||
"probability": 0.05
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "HK DK"
|
||||
"move": "HK DK",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "HK DK",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "HA CA",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "RJ BJ"
|
||||
"move": "RJ BJ",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "RJ BJ",
|
||||
"probability": 0.9
|
||||
},
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "S8 H8 C8 C9"
|
||||
"move": "S8 H8 C8 C9",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "S8 H8 C8 C9",
|
||||
"probability": 0.7
|
||||
},
|
||||
{
|
||||
"move": "SA",
|
||||
"probability": 0.1
|
||||
},
|
||||
{
|
||||
"move": "D2",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "HA CA DA H5"
|
||||
"move": "HA CA DA H5",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "HA CA DA H5",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.1
|
||||
},
|
||||
{
|
||||
"move": "HA CA DA D6",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "SQ"
|
||||
"move": "SQ",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "SQ",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "S4 H4",
|
||||
"probability": 0.2
|
||||
},
|
||||
{
|
||||
"move": "D6",
|
||||
"probability": 0.1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "D2"
|
||||
"move": "D2",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "D2",
|
||||
"probability": 0.5
|
||||
},
|
||||
{
|
||||
"move": "SA",
|
||||
"probability": 0.3
|
||||
},
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 0.2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 0,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 1,
|
||||
"move": "P"
|
||||
"move": "P",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "P",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"playerIdx": 2,
|
||||
"move": "SA"
|
||||
"move": "SA",
|
||||
"probabilities": [
|
||||
{
|
||||
"move": "SA",
|
||||
"probability": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -74,4 +74,17 @@ export function millisecond2Second(t){
|
|||
return Math.ceil(t/1000);
|
||||
}
|
||||
|
||||
export { suitMap, suitMapSymbol };
|
||||
export function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this, args = arguments;
|
||||
const later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
|
@ -2,21 +2,26 @@ import React from 'react';
|
|||
import '../assets/gameview.scss';
|
||||
import { DoudizhuGameBoard } from '../components/GameBoard';
|
||||
import webSocket from "socket.io-client";
|
||||
import { removeCards, doubleRaf, deepCopy } from "../utils";
|
||||
import { removeCards, doubleRaf, deepCopy, debounce } from "../utils";
|
||||
|
||||
import { Button, Layout } from 'element-react';
|
||||
import { Layout } from 'element-react';
|
||||
import Slider from '@material-ui/core/Slider';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
|
||||
import ReplayRoundedIcon from '@material-ui/icons/ReplayRounded';
|
||||
|
||||
class DoudizhuGameView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const mainViewerId = 0; // Id of the player at the bottom of screen
|
||||
this.initConsiderationTime = 1000;
|
||||
this.initConsiderationTime = 2000;
|
||||
this.considerationTimeDeduction = 100;
|
||||
this.gameStateTimeout = null;
|
||||
|
||||
this.initGameState = {
|
||||
gameStatus: "ready", // "ready", "playing", "paused", "over"
|
||||
playerInfo: [],
|
||||
hands: [],
|
||||
latestAction: [[], [], []],
|
||||
|
@ -30,15 +35,21 @@ class DoudizhuGameView extends React.Component {
|
|||
ws: null,
|
||||
gameInfo: this.initGameState,
|
||||
gameStateLoop: null,
|
||||
considerationTimeSetting: this.initConsiderationTime
|
||||
gameSpeed: 0
|
||||
};
|
||||
}
|
||||
|
||||
gameStateTimer() {
|
||||
this.gameStateTimeout = setTimeout(()=>{
|
||||
let currentConsiderationTime = this.state.gameInfo.considerationTime;
|
||||
// for test use
|
||||
// console.log(currentConsiderationTime);
|
||||
// if(currentConsiderationTime === 1000){
|
||||
// debugger;
|
||||
// }
|
||||
if(currentConsiderationTime > 0) {
|
||||
currentConsiderationTime -= this.considerationTimeDeduction;
|
||||
currentConsiderationTime -= this.considerationTimeDeduction * Math.pow(2, this.state.gameSpeed);
|
||||
currentConsiderationTime = currentConsiderationTime < 0 ? 0 : currentConsiderationTime;
|
||||
let gameInfo = deepCopy(this.state.gameInfo);
|
||||
gameInfo.considerationTime = currentConsiderationTime;
|
||||
this.setState({gameInfo: gameInfo});
|
||||
|
@ -53,7 +64,7 @@ class DoudizhuGameView extends React.Component {
|
|||
this.setState({gameInfo: gameInfo});
|
||||
this.state.ws.emit("getMessage", gameStateReq);
|
||||
}
|
||||
}, this.considerationTimeDeduction);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
startReplay() {
|
||||
|
@ -61,7 +72,11 @@ class DoudizhuGameView extends React.Component {
|
|||
const replayReq = {type: 0};
|
||||
this.state.ws.emit("getMessage", replayReq);
|
||||
// init game state
|
||||
this.setState({gameInfo: this.initGameState});
|
||||
let initGameState = deepCopy(this.initGameState);
|
||||
// set game status to playing
|
||||
initGameState.gameStatus = "playing";
|
||||
this.setState({gameInfo: initGameState});
|
||||
|
||||
if(this.gameStateTimeout){
|
||||
window.clearTimeout(this.gameStateTimeout);
|
||||
this.gameStateTimeout = null;
|
||||
|
@ -104,7 +119,7 @@ class DoudizhuGameView extends React.Component {
|
|||
}else{
|
||||
console.log("Cannot find cards in move from player's hand");
|
||||
}
|
||||
gameInfo.considerationTime = this.state.considerationTimeSetting;
|
||||
gameInfo.considerationTime = this.initConsiderationTime;
|
||||
this.setState({gameInfo: gameInfo});
|
||||
}else{
|
||||
console.log("Mismatched game turn or current player index", message);
|
||||
|
@ -127,6 +142,9 @@ class DoudizhuGameView extends React.Component {
|
|||
return element.index === prevTurn.currentPlayer;
|
||||
});
|
||||
if(winner){
|
||||
let gameInfo = deepCopy(this.state.gameInfo);
|
||||
gameInfo.gameStatus = "over";
|
||||
this.setState({ gameInfo: gameInfo });
|
||||
if(winner.role === "landlord")
|
||||
alert("Landlord Wins");
|
||||
else
|
||||
|
@ -139,8 +157,79 @@ class DoudizhuGameView extends React.Component {
|
|||
this.gameStateTimer();
|
||||
}
|
||||
|
||||
pauseReplay(){
|
||||
if(this.gameStateTimeout){
|
||||
window.clearTimeout(this.gameStateTimeout);
|
||||
this.gameStateTimeout = null;
|
||||
}
|
||||
let gameInfo = deepCopy(this.state.gameInfo);
|
||||
gameInfo.gameStatus = "paused";
|
||||
this.setState({ gameInfo: gameInfo });
|
||||
}
|
||||
|
||||
resumeReplay(){
|
||||
this.gameStateTimer();
|
||||
let gameInfo = deepCopy(this.state.gameInfo);
|
||||
gameInfo.gameStatus = "playing";
|
||||
this.setState({ gameInfo: gameInfo });
|
||||
}
|
||||
|
||||
changeGameSpeed(newVal){
|
||||
console.log('wdnmd');
|
||||
this.setState({gameSpeed: newVal});
|
||||
}
|
||||
|
||||
gameStatusButton(status){
|
||||
switch (status) {
|
||||
case "ready":
|
||||
return <Button variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Start Replay</Button>;
|
||||
case "playing":
|
||||
return <Button variant={"contained"} startIcon={<PauseCircleOutlineRoundedIcon />} color="secondary" onClick={()=>{this.pauseReplay()}}>Pause</Button>;
|
||||
case "paused":
|
||||
return <Button variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.resumeReplay()}}>Resume</Button>;
|
||||
case "over":
|
||||
return <Button variant={"contained"} startIcon={<ReplayRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Resume</Button>;
|
||||
default:
|
||||
alert(`undefined game status: ${status}`);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
render(){
|
||||
// todo: reset game state timer when considerationTimeSetting changes
|
||||
let sliderValueText = (value) => {
|
||||
return `${value}°C`;
|
||||
};
|
||||
const gameSpeedMarks = [
|
||||
{
|
||||
value: -3,
|
||||
label: 'x0.125',
|
||||
},
|
||||
{
|
||||
value: -2,
|
||||
label: 'x0.25',
|
||||
},
|
||||
{
|
||||
value: -1,
|
||||
label: 'x0.5',
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: 'x1',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'x2',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'x4',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'x8',
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{width: "960px", height: "540px"}}>
|
||||
|
@ -158,8 +247,8 @@ class DoudizhuGameView extends React.Component {
|
|||
<div className="game-controller">
|
||||
<Layout.Row>
|
||||
<Layout.Col span="24">
|
||||
<Button type="primary" onClick={()=>{this.connectWebSocket()}}>Connect</Button>
|
||||
<Button type="primary" onClick={()=>{this.startReplay()}}>Start Replay</Button>
|
||||
<Button variant={"contained"} color="primary" onClick={()=>{this.connectWebSocket()}}>Connect</Button>
|
||||
{ this.gameStatusButton(this.state.gameInfo.gameStatus) }
|
||||
</Layout.Col>
|
||||
</Layout.Row>
|
||||
<Layout.Row style={{height: "31px"}}>
|
||||
|
@ -170,14 +259,16 @@ class DoudizhuGameView extends React.Component {
|
|||
</Layout.Col>
|
||||
<Layout.Col span="16">
|
||||
<Slider
|
||||
value={this.state.considerationTimeSetting}
|
||||
onChange={(e, newVal)=>{console.log('slider val', newVal);this.setState({considerationTimeSetting: newVal})}}
|
||||
aria-labelledby="discrete-slider"
|
||||
valueLabelDisplay="auto"
|
||||
step={1000}
|
||||
marks
|
||||
min={0}
|
||||
max={10000}
|
||||
value={this.state.gameSpeed}
|
||||
getAriaValueText={sliderValueText}
|
||||
onChange={(e, newVal)=>{this.changeGameSpeed(newVal)}}
|
||||
aria-labelledby="discrete-slider-custom"
|
||||
step={1}
|
||||
min={-3}
|
||||
max={3}
|
||||
track={false}
|
||||
valueLabelDisplay="off"
|
||||
marks={gameSpeedMarks}
|
||||
/>
|
||||
</Layout.Col>
|
||||
</Layout.Row>
|
||||
|
|
Loading…
Reference in New Issue