add new example model download links; implementing new doudizhu replay probability view
This commit is contained in:
parent
983a9c8b43
commit
31723396ea
|
@ -115,6 +115,7 @@ function MenuBar(props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const [uploadDialogOpen, setUploadDialogOpen] = React.useState(false);
|
const [uploadDialogOpen, setUploadDialogOpen] = React.useState(false);
|
||||||
|
const [downloadChannelDialogOpen, setDownloadChannelDialogOpen] = React.useState(false);
|
||||||
|
|
||||||
const openUploadDialog = () => {
|
const openUploadDialog = () => {
|
||||||
setUploadDialogOpen(true);
|
setUploadDialogOpen(true);
|
||||||
|
@ -133,6 +134,10 @@ function MenuBar(props) {
|
||||||
setUploadDialogOpen(false);
|
setUploadDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDownloadChannelDialogClose = () => {
|
||||||
|
setDownloadChannelDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
let uploadRef = React.createRef();
|
let uploadRef = React.createRef();
|
||||||
const handleSubmitUpload = () => {
|
const handleSubmitUpload = () => {
|
||||||
// check if data to upload is legal
|
// check if data to upload is legal
|
||||||
|
@ -326,12 +331,14 @@ function MenuBar(props) {
|
||||||
DQN model
|
DQN model
|
||||||
</Link>{' '}
|
</Link>{' '}
|
||||||
for Leduc Holdem or{' '}
|
for Leduc Holdem or{' '}
|
||||||
<Link
|
<a
|
||||||
href={apiUrl + '/tournament/download_examples?name=example_luduc_rule_model'}
|
href="#"
|
||||||
download
|
onClick={() => {
|
||||||
|
setDownloadChannelDialogOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
DMC model
|
DMC model
|
||||||
</Link>{' '}
|
</a>{' '}
|
||||||
for Doudizhu to test and learn about model upload functionality.
|
for Doudizhu to test and learn about model upload functionality.
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
@ -408,16 +415,46 @@ function MenuBar(props) {
|
||||||
</Loading>
|
</Loading>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog
|
<Dialog
|
||||||
open={uploadDialogOpen}
|
open={downloadChannelDialogOpen}
|
||||||
onClose={handleUploadDialogClose}
|
onClose={handleDownloadChannelDialogClose}
|
||||||
aria-labelledby="form-dialog-title"
|
aria-labelledby="form-dialog-title"
|
||||||
disableBackdropClick={true}
|
|
||||||
>
|
>
|
||||||
<DialogTitle id="form-dialog-title">Choose Download Channel</DialogTitle>
|
<DialogTitle id="form-dialog-title">Choose Download Channel</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Button>Google Drive</Button>
|
<div>
|
||||||
<Button>百度网盘</Button>
|
<Button
|
||||||
|
href="https://drive.google.com/file/d/127XEKfEJrYyPtobT4u7j4_KBqk19FUej/view?usp=sharing"
|
||||||
|
target="_blank"
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
Google Drive
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
href="https://pan.baidu.com/s/1fHH86DBpGRnN58q9ctAt6A"
|
||||||
|
target="_blank"
|
||||||
|
style={{ marginTop: '20px', marginBottom: '10px' }}
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
百度网盘
|
||||||
|
</Button>
|
||||||
|
<div style={{ width: '100%', textAlign: 'center', marginBottom: '20px' }}>(提取码: s54s)</div>
|
||||||
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setDownloadChannelDialogOpen(false);
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
disableElevation
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,47 +1,46 @@
|
||||||
import React from 'react';
|
|
||||||
import axios from 'axios';
|
|
||||||
import '../../assets/gameview.scss';
|
|
||||||
import { DoudizhuGameBoard } from '../../components/GameBoard';
|
|
||||||
import {removeCards, doubleRaf, deepCopy, computeHandCardsWidth, translateCardData} from "../../utils";
|
|
||||||
import { apiUrl } from "../../utils/config";
|
|
||||||
|
|
||||||
import { Layout, Message, Loading } from 'element-react';
|
|
||||||
import Slider from '@material-ui/core/Slider';
|
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
|
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||||
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider';
|
||||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||||
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import Slider from '@material-ui/core/Slider';
|
||||||
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
|
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
|
||||||
|
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
|
||||||
import ReplayRoundedIcon from '@material-ui/icons/ReplayRounded';
|
import ReplayRoundedIcon from '@material-ui/icons/ReplayRounded';
|
||||||
// import NotInterestedIcon from '@material-ui/icons/NotInterested';
|
// import NotInterestedIcon from '@material-ui/icons/NotInterested';
|
||||||
import SkipNextIcon from '@material-ui/icons/SkipNext';
|
import SkipNextIcon from '@material-ui/icons/SkipNext';
|
||||||
import SkipPreviousIcon from '@material-ui/icons/SkipPrevious';
|
import SkipPreviousIcon from '@material-ui/icons/SkipPrevious';
|
||||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
import axios from 'axios';
|
||||||
import DialogContent from "@material-ui/core/DialogContent";
|
import { Layout, Loading, Message } from 'element-react';
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import qs from 'query-string';
|
||||||
import DialogActions from "@material-ui/core/DialogActions";
|
import React from 'react';
|
||||||
import Dialog from "@material-ui/core/Dialog";
|
import '../../assets/gameview.scss';
|
||||||
import qs from "query-string";
|
import { DoudizhuGameBoard } from '../../components/GameBoard';
|
||||||
|
import { computeHandCardsWidth, deepCopy, doubleRaf, removeCards, translateCardData } from '../../utils';
|
||||||
|
import { apiUrl } from '../../utils/config';
|
||||||
|
|
||||||
class DoudizhuReplayView extends React.Component {
|
class DoudizhuReplayView extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const mainViewerId = 0; // Id of the player at the bottom of screen
|
const mainViewerId = 0; // Id of the player at the bottom of screen
|
||||||
this.initConsiderationTime = 2000;
|
this.initConsiderationTime = 2000;
|
||||||
this.considerationTimeDeduction = 200;
|
this.considerationTimeDeduction = 200;
|
||||||
this.gameStateTimeout = null;
|
this.gameStateTimeout = null;
|
||||||
this.moveHistory = [];
|
this.moveHistory = [];
|
||||||
this.gameStateHistory = [];
|
this.gameStateHistory = [];
|
||||||
this.initGameState = {
|
this.initGameState = {
|
||||||
gameStatus: "ready", // "ready", "playing", "paused", "over"
|
gameStatus: 'ready', // "ready", "playing", "paused", "over"
|
||||||
playerInfo: [],
|
playerInfo: [],
|
||||||
hands: [],
|
hands: [],
|
||||||
latestAction: [[], [], []],
|
latestAction: [[], [], []],
|
||||||
mainViewerId: mainViewerId,
|
mainViewerId: mainViewerId,
|
||||||
turn: 0,
|
turn: 0,
|
||||||
toggleFade: "",
|
toggleFade: '',
|
||||||
|
|
||||||
currentPlayer: null,
|
currentPlayer: null,
|
||||||
considerationTime: this.initConsiderationTime,
|
considerationTime: this.initConsiderationTime,
|
||||||
|
@ -53,62 +52,67 @@ class DoudizhuReplayView extends React.Component {
|
||||||
gameStateLoop: null,
|
gameStateLoop: null,
|
||||||
gameSpeed: 0,
|
gameSpeed: 0,
|
||||||
gameEndDialog: false,
|
gameEndDialog: false,
|
||||||
gameEndDialogText: "",
|
gameEndDialogText: '',
|
||||||
fullScreenLoading: false
|
fullScreenLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
cardStr2Arr(cardStr){
|
cardStr2Arr(cardStr) {
|
||||||
return cardStr === "pass" || cardStr === "" ? "pass" : cardStr.split(" ");
|
return cardStr === 'pass' || cardStr === '' ? 'pass' : cardStr.split(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
generateNewState(){
|
generateNewState() {
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
if(this.state.gameInfo.turn === this.moveHistory.length) return gameInfo;
|
if (this.state.gameInfo.turn === this.moveHistory.length) return gameInfo;
|
||||||
// check if the game state of next turn is already in game state history
|
// check if the game state of next turn is already in game state history
|
||||||
if(this.state.gameInfo.turn+1 < this.gameStateHistory.length){
|
if (this.state.gameInfo.turn + 1 < this.gameStateHistory.length) {
|
||||||
gameInfo = deepCopy(this.gameStateHistory[this.state.gameInfo.turn+1]);
|
gameInfo = deepCopy(this.gameStateHistory[this.state.gameInfo.turn + 1]);
|
||||||
}else{
|
} else {
|
||||||
let newMove = this.moveHistory[this.state.gameInfo.turn];
|
let newMove = this.moveHistory[this.state.gameInfo.turn];
|
||||||
if(newMove.playerIdx === this.state.gameInfo.currentPlayer) {
|
if (newMove.playerIdx === this.state.gameInfo.currentPlayer) {
|
||||||
gameInfo.latestAction[newMove.playerIdx] = this.cardStr2Arr(Array.isArray(newMove.move) ? newMove.move.join(" ") : newMove.move);
|
gameInfo.latestAction[newMove.playerIdx] = this.cardStr2Arr(
|
||||||
|
Array.isArray(newMove.move) ? newMove.move.join(' ') : newMove.move,
|
||||||
|
);
|
||||||
gameInfo.turn++;
|
gameInfo.turn++;
|
||||||
gameInfo.currentPlayer = (gameInfo.currentPlayer + 1) % 3;
|
gameInfo.currentPlayer = (gameInfo.currentPlayer + 1) % 3;
|
||||||
// take away played cards from player's hands
|
// take away played cards from player's hands
|
||||||
const remainedCards = removeCards(gameInfo.latestAction[newMove.playerIdx], gameInfo.hands[newMove.playerIdx]);
|
const remainedCards = removeCards(
|
||||||
|
gameInfo.latestAction[newMove.playerIdx],
|
||||||
|
gameInfo.hands[newMove.playerIdx],
|
||||||
|
);
|
||||||
if (remainedCards !== false) {
|
if (remainedCards !== false) {
|
||||||
gameInfo.hands[newMove.playerIdx] = remainedCards;
|
gameInfo.hands[newMove.playerIdx] = remainedCards;
|
||||||
} else {
|
} else {
|
||||||
Message({
|
Message({
|
||||||
message: "Cannot find cards in move from player's hand",
|
message: "Cannot find cards in move from player's hand",
|
||||||
type: "error",
|
type: 'error',
|
||||||
showClose: true
|
showClose: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// check if game ends
|
// check if game ends
|
||||||
if(remainedCards.length === 0){
|
if (remainedCards.length === 0) {
|
||||||
doubleRaf(()=>{
|
doubleRaf(() => {
|
||||||
const winner = this.state.gameInfo.playerInfo.find(element => {
|
const winner = this.state.gameInfo.playerInfo.find((element) => {
|
||||||
return element.index === newMove.playerIdx;
|
return element.index === newMove.playerIdx;
|
||||||
});
|
});
|
||||||
if(winner){
|
if (winner) {
|
||||||
gameInfo.gameStatus = "over";
|
gameInfo.gameStatus = 'over';
|
||||||
this.setState({ gameInfo: gameInfo });
|
this.setState({ gameInfo: gameInfo });
|
||||||
if(winner.role === "landlord")
|
if (winner.role === 'landlord')
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
const mes = "Landlord Wins";
|
const mes = 'Landlord Wins';
|
||||||
this.setState({gameEndDialog: true, gameEndDialogText: mes});
|
this.setState({ gameEndDialog: true, gameEndDialogText: mes });
|
||||||
}, 200);
|
}, 200);
|
||||||
else
|
else
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
const mes = "Peasants Win";
|
const mes = 'Peasants Win';
|
||||||
this.setState({gameEndDialog: true, gameEndDialogText: mes});
|
this.setState({ gameEndDialog: true, gameEndDialogText: mes });
|
||||||
}, 200);
|
}, 200);
|
||||||
}else{
|
} else {
|
||||||
Message({
|
Message({
|
||||||
message: "Error in finding winner",
|
message: 'Error in finding winner',
|
||||||
type: "error",
|
type: 'error',
|
||||||
showClose: true
|
showClose: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -116,21 +120,21 @@ class DoudizhuReplayView extends React.Component {
|
||||||
}
|
}
|
||||||
gameInfo.considerationTime = this.initConsiderationTime;
|
gameInfo.considerationTime = this.initConsiderationTime;
|
||||||
gameInfo.completedPercent += 100.0 / (this.moveHistory.length - 1);
|
gameInfo.completedPercent += 100.0 / (this.moveHistory.length - 1);
|
||||||
}else {
|
} else {
|
||||||
Message({
|
Message({
|
||||||
message: "Mismatched current player index",
|
message: 'Mismatched current player index',
|
||||||
type: "error",
|
type: 'error',
|
||||||
showClose: true
|
showClose: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// if current state is new to game state history, push it to the game state history array
|
// if current state is new to game state history, push it to the game state history array
|
||||||
if(gameInfo.turn === this.gameStateHistory.length){
|
if (gameInfo.turn === this.gameStateHistory.length) {
|
||||||
this.gameStateHistory.push(gameInfo);
|
this.gameStateHistory.push(gameInfo);
|
||||||
}else{
|
} else {
|
||||||
Message({
|
Message({
|
||||||
message: "inconsistent game state history length and turn number",
|
message: 'inconsistent game state history length and turn number',
|
||||||
type: "error",
|
type: 'error',
|
||||||
showClose: true
|
showClose: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,34 +142,34 @@ class DoudizhuReplayView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
gameStateTimer() {
|
gameStateTimer() {
|
||||||
this.gameStateTimeout = setTimeout(()=>{
|
this.gameStateTimeout = setTimeout(() => {
|
||||||
let currentConsiderationTime = this.state.gameInfo.considerationTime;
|
let currentConsiderationTime = this.state.gameInfo.considerationTime;
|
||||||
if(currentConsiderationTime > 0) {
|
if (currentConsiderationTime > 0) {
|
||||||
currentConsiderationTime -= this.considerationTimeDeduction * Math.pow(2, this.state.gameSpeed);
|
currentConsiderationTime -= this.considerationTimeDeduction * Math.pow(2, this.state.gameSpeed);
|
||||||
currentConsiderationTime = currentConsiderationTime < 0 ? 0 : currentConsiderationTime;
|
currentConsiderationTime = currentConsiderationTime < 0 ? 0 : currentConsiderationTime;
|
||||||
if(currentConsiderationTime === 0 && this.state.gameSpeed < 2){
|
if (currentConsiderationTime === 0 && this.state.gameSpeed < 2) {
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
gameInfo.toggleFade = "fade-out";
|
gameInfo.toggleFade = 'fade-out';
|
||||||
this.setState({gameInfo: gameInfo});
|
this.setState({ gameInfo: gameInfo });
|
||||||
}
|
}
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
gameInfo.considerationTime = currentConsiderationTime;
|
gameInfo.considerationTime = currentConsiderationTime;
|
||||||
this.setState({gameInfo: gameInfo});
|
this.setState({ gameInfo: gameInfo });
|
||||||
this.gameStateTimer();
|
this.gameStateTimer();
|
||||||
}else{
|
} else {
|
||||||
let gameInfo = this.generateNewState();
|
let gameInfo = this.generateNewState();
|
||||||
if(gameInfo.gameStatus === "over") return;
|
if (gameInfo.gameStatus === 'over') return;
|
||||||
gameInfo.gameStatus = "playing";
|
gameInfo.gameStatus = 'playing';
|
||||||
if(this.state.gameInfo.toggleFade === "fade-out") {
|
if (this.state.gameInfo.toggleFade === 'fade-out') {
|
||||||
gameInfo.toggleFade = "fade-in";
|
gameInfo.toggleFade = 'fade-in';
|
||||||
}
|
}
|
||||||
this.setState({gameInfo: gameInfo}, ()=>{
|
this.setState({ gameInfo: gameInfo }, () => {
|
||||||
// toggle fade in
|
// toggle fade in
|
||||||
if(this.state.gameInfo.toggleFade !== ""){
|
if (this.state.gameInfo.toggleFade !== '') {
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
gameInfo.toggleFade = "";
|
gameInfo.toggleFade = '';
|
||||||
this.setState({gameInfo: gameInfo});
|
this.setState({ gameInfo: gameInfo });
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -178,25 +182,34 @@ class DoudizhuReplayView extends React.Component {
|
||||||
const requestUrl = `${apiUrl}/tournament/replay?name=${name}&agent0=${agent0}&agent1=${agent1}&index=${index}`;
|
const requestUrl = `${apiUrl}/tournament/replay?name=${name}&agent0=${agent0}&agent1=${agent1}&index=${index}`;
|
||||||
|
|
||||||
// start full screen loading
|
// start full screen loading
|
||||||
this.setState({fullScreenLoading: true});
|
this.setState({ fullScreenLoading: true });
|
||||||
axios.get(requestUrl)
|
axios
|
||||||
.then(res => {
|
.get(requestUrl)
|
||||||
|
.then((res) => {
|
||||||
res = res.data;
|
res = res.data;
|
||||||
|
|
||||||
|
// for test use
|
||||||
|
if (typeof res === 'string') res = JSON.parse(res.replaceAll("'", '"').replaceAll('None', 'null'));
|
||||||
|
console.log(res);
|
||||||
|
|
||||||
// init replay info
|
// init replay info
|
||||||
this.moveHistory = res.moveHistory;
|
this.moveHistory = res.moveHistory;
|
||||||
let gameInfo = deepCopy(this.initGameState);
|
let gameInfo = deepCopy(this.initGameState);
|
||||||
gameInfo.gameStatus = "playing";
|
gameInfo.gameStatus = 'playing';
|
||||||
gameInfo.playerInfo = res.playerInfo;
|
gameInfo.playerInfo = res.playerInfo;
|
||||||
gameInfo.hands = res.initHands.map(element => {
|
gameInfo.hands = res.initHands.map((element) => {
|
||||||
return this.cardStr2Arr(element);
|
return this.cardStr2Arr(element);
|
||||||
});
|
});
|
||||||
// the first player should be landlord
|
// the first player should be landlord
|
||||||
gameInfo.currentPlayer = res.playerInfo.find(element=>{return element.role === "landlord"}).index;
|
gameInfo.currentPlayer = res.playerInfo.find((element) => {
|
||||||
if(this.gameStateHistory.length === 0){ // fix replay bug
|
return element.role === 'landlord';
|
||||||
|
}).index;
|
||||||
|
if (this.gameStateHistory.length === 0) {
|
||||||
|
// fix replay bug
|
||||||
this.gameStateHistory.push(gameInfo);
|
this.gameStateHistory.push(gameInfo);
|
||||||
}
|
}
|
||||||
this.setState({gameInfo: gameInfo, fullScreenLoading: false}, ()=>{
|
this.setState({ gameInfo: gameInfo, fullScreenLoading: false }, () => {
|
||||||
if(this.gameStateTimeout){
|
if (this.gameStateTimeout) {
|
||||||
window.clearTimeout(this.gameStateTimeout);
|
window.clearTimeout(this.gameStateTimeout);
|
||||||
this.gameStateTimeout = null;
|
this.gameStateTimeout = null;
|
||||||
}
|
}
|
||||||
|
@ -204,64 +217,116 @@ class DoudizhuReplayView extends React.Component {
|
||||||
this.gameStateTimer();
|
this.gameStateTimer();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(()=>{
|
.catch(() => {
|
||||||
this.setState({fullScreenLoading: false});
|
this.setState({ fullScreenLoading: false });
|
||||||
Message({
|
Message({
|
||||||
message: "Error in getting replay data",
|
message: 'Error in getting replay data',
|
||||||
type: "error",
|
type: 'error',
|
||||||
showClose: true
|
showClose: true,
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
runNewTurn(){
|
runNewTurn() {
|
||||||
this.gameStateTimer();
|
this.gameStateTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseReplay(){
|
pauseReplay() {
|
||||||
if(this.gameStateTimeout){
|
if (this.gameStateTimeout) {
|
||||||
window.clearTimeout(this.gameStateTimeout);
|
window.clearTimeout(this.gameStateTimeout);
|
||||||
this.gameStateTimeout = null;
|
this.gameStateTimeout = null;
|
||||||
}
|
}
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
gameInfo.gameStatus = "paused";
|
gameInfo.gameStatus = 'paused';
|
||||||
this.setState({ gameInfo: gameInfo });
|
this.setState({ gameInfo: gameInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
resumeReplay(){
|
resumeReplay() {
|
||||||
this.gameStateTimer();
|
this.gameStateTimer();
|
||||||
let gameInfo = deepCopy(this.state.gameInfo);
|
let gameInfo = deepCopy(this.state.gameInfo);
|
||||||
gameInfo.gameStatus = "playing";
|
gameInfo.gameStatus = 'playing';
|
||||||
this.setState({ gameInfo: gameInfo });
|
this.setState({ gameInfo: gameInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
changeGameSpeed(newVal){
|
changeGameSpeed(newVal) {
|
||||||
this.setState({gameSpeed: newVal});
|
this.setState({ gameSpeed: newVal });
|
||||||
}
|
}
|
||||||
|
|
||||||
gameStatusButton(status){
|
gameStatusButton(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "ready":
|
case 'ready':
|
||||||
return <Button className={"status-button"} variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Start</Button>;
|
return (
|
||||||
case "playing":
|
<Button
|
||||||
return <Button className={"status-button"} variant={"contained"} startIcon={<PauseCircleOutlineRoundedIcon />} color="secondary" onClick={()=>{this.pauseReplay()}}>Pause</Button>;
|
className={'status-button'}
|
||||||
case "paused":
|
variant={'contained'}
|
||||||
return <Button className={"status-button"} variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.resumeReplay()}}>Resume</Button>;
|
startIcon={<PlayArrowRoundedIcon />}
|
||||||
case "over":
|
color="primary"
|
||||||
return <Button className={"status-button"} variant={"contained"} startIcon={<ReplayRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Restart</Button>;
|
onClick={() => {
|
||||||
|
this.startReplay();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
case 'playing':
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={'status-button'}
|
||||||
|
variant={'contained'}
|
||||||
|
startIcon={<PauseCircleOutlineRoundedIcon />}
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
this.pauseReplay();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pause
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
case 'paused':
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={'status-button'}
|
||||||
|
variant={'contained'}
|
||||||
|
startIcon={<PlayArrowRoundedIcon />}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
this.resumeReplay();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resume
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
case 'over':
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={'status-button'}
|
||||||
|
variant={'contained'}
|
||||||
|
startIcon={<ReplayRoundedIcon />}
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
this.startReplay();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Restart
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
alert(`undefined game status: ${status}`);
|
alert(`undefined game status: ${status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
computeSingleLineHand(cards) {
|
computeSingleLineHand(cards) {
|
||||||
if(cards === "pass"){
|
if (cards === 'pass') {
|
||||||
return <div className={"non-card "+this.state.gameInfo.toggleFade}><span>Pass</span></div>
|
|
||||||
}else{
|
|
||||||
return (
|
return (
|
||||||
<div className={"unselectable playingCards loose "+this.state.gameInfo.toggleFade}>
|
<div className={'non-card ' + this.state.gameInfo.toggleFade}>
|
||||||
<ul className="hand" style={{width: computeHandCardsWidth(cards.length, 10)}}>
|
<span>Pass</span>
|
||||||
{cards.map(card=>{
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={'unselectable playingCards loose ' + this.state.gameInfo.toggleFade}>
|
||||||
|
<ul className="hand" style={{ width: computeHandCardsWidth(cards.length, 10) }}>
|
||||||
|
{cards.map((card) => {
|
||||||
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
||||||
return (
|
return (
|
||||||
<li key={`handCard-${card}`}>
|
<li key={`handCard-${card}`}>
|
||||||
|
@ -274,58 +339,68 @@ class DoudizhuReplayView extends React.Component {
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
computeProbabilityItem(idx){
|
computeProbabilityItem(idx) {
|
||||||
return <span className={"waiting"}>Currently Unavailable...</span>
|
// return <span className={'waiting'}>Currently Unavailable...</span>;
|
||||||
// if(this.state.gameInfo.gameStatus !== "ready" && this.state.gameInfo.turn < this.moveHistory.length){
|
if (this.state.gameInfo.gameStatus !== 'ready' && this.state.gameInfo.turn < this.moveHistory.length) {
|
||||||
// let style = {};
|
let style = {};
|
||||||
// style["backgroundColor"] = this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? `rgba(63, 81, 181, ${this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability})` : "#bdbdbd";
|
// style["backgroundColor"] = this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? `rgba(63, 81, 181, ${this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability})` : "#bdbdbd";
|
||||||
// return (
|
// let probabilities
|
||||||
// <div className={"playing"} style={style}>
|
return (
|
||||||
// <div className="probability-move">
|
<div className={'playing'} style={style}>
|
||||||
// {this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ?
|
<div className="probability-move">
|
||||||
// this.computeSingleLineHand(this.cardStr2Arr(this.moveHistory[this.state.gameInfo.turn].probabilities[idx].move))
|
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? (
|
||||||
// :
|
this.computeSingleLineHand(
|
||||||
// <NotInterestedIcon fontSize="large" />
|
this.cardStr2Arr(this.moveHistory[this.state.gameInfo.turn].probabilities[idx].move),
|
||||||
// }
|
)
|
||||||
// </div>
|
) : (
|
||||||
// {this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ?
|
<NotInterestedIcon fontSize="large" />
|
||||||
// <div className={"non-card"}>
|
)}
|
||||||
// <span>{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? `Probability ${(this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability * 100).toFixed(2)}%` : ""}</span>
|
</div>
|
||||||
// </div>
|
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? (
|
||||||
// :
|
<div className={'non-card'}>
|
||||||
// ""
|
<span>
|
||||||
// }
|
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx
|
||||||
// </div>
|
? `Probability ${(
|
||||||
// )
|
this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability *
|
||||||
// }else {
|
100
|
||||||
// return <span className={"waiting"}>Waiting...</span>
|
).toFixed(2)}%`
|
||||||
// }
|
: ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <span className={'waiting'}>Waiting...</span>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go2PrevGameState() {
|
go2PrevGameState() {
|
||||||
let gameInfo = deepCopy(this.gameStateHistory[this.state.gameInfo.turn - 1]);
|
let gameInfo = deepCopy(this.gameStateHistory[this.state.gameInfo.turn - 1]);
|
||||||
gameInfo.gameStatus = "paused";
|
gameInfo.gameStatus = 'paused';
|
||||||
gameInfo.toggleFade = "";
|
gameInfo.toggleFade = '';
|
||||||
this.setState({gameInfo: gameInfo});
|
this.setState({ gameInfo: gameInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
go2NextGameState() {
|
go2NextGameState() {
|
||||||
let gameInfo = this.generateNewState();
|
let gameInfo = this.generateNewState();
|
||||||
if(gameInfo.gameStatus === "over") return;
|
if (gameInfo.gameStatus === 'over') return;
|
||||||
gameInfo.gameStatus = "paused";
|
gameInfo.gameStatus = 'paused';
|
||||||
gameInfo.toggleFade = "";
|
gameInfo.toggleFade = '';
|
||||||
this.setState({gameInfo: gameInfo});
|
this.setState({ gameInfo: gameInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseGameEndDialog() {
|
handleCloseGameEndDialog() {
|
||||||
this.setState({gameEndDialog: false, gameEndDialogText: ""});
|
this.setState({ gameEndDialog: false, gameEndDialogText: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
let sliderValueText = (value) => {
|
let sliderValueText = (value) => {
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
@ -357,34 +432,44 @@ class DoudizhuReplayView extends React.Component {
|
||||||
{
|
{
|
||||||
value: 3,
|
value: 3,
|
||||||
label: 'x8',
|
label: 'x8',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.gameEndDialog}
|
open={this.state.gameEndDialog}
|
||||||
onClose={()=>{this.handleCloseGameEndDialog()}}
|
onClose={() => {
|
||||||
|
this.handleCloseGameEndDialog();
|
||||||
|
}}
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title" style={{"width": "200px"}}>{"Game Ends!"}</DialogTitle>
|
<DialogTitle id="alert-dialog-title" style={{ width: '200px' }}>
|
||||||
|
{'Game Ends!'}
|
||||||
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
{this.state.gameEndDialogText}
|
{this.state.gameEndDialogText}
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={()=>{this.handleCloseGameEndDialog()}} color="primary" autoFocus>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.handleCloseGameEndDialog();
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<div className={"doudizhu-view-container"}>
|
<div className={'doudizhu-view-container'}>
|
||||||
<Layout.Row style={{"height": "540px"}}>
|
<Layout.Row style={{ height: '540px' }}>
|
||||||
<Layout.Col style={{"height": "100%"}} span="17">
|
<Layout.Col style={{ height: '100%' }} span="17">
|
||||||
<div style={{"height": "100%"}}>
|
<div style={{ height: '100%' }}>
|
||||||
<Paper className={"doudizhu-gameboard-paper"} elevation={3}>
|
<Paper className={'doudizhu-gameboard-paper'} elevation={3}>
|
||||||
<DoudizhuGameBoard
|
<DoudizhuGameBoard
|
||||||
playerInfo={this.state.gameInfo.playerInfo}
|
playerInfo={this.state.gameInfo.playerInfo}
|
||||||
hands={this.state.gameInfo.hands}
|
hands={this.state.gameInfo.hands}
|
||||||
|
@ -393,34 +478,32 @@ class DoudizhuReplayView extends React.Component {
|
||||||
currentPlayer={this.state.gameInfo.currentPlayer}
|
currentPlayer={this.state.gameInfo.currentPlayer}
|
||||||
considerationTime={this.state.gameInfo.considerationTime}
|
considerationTime={this.state.gameInfo.considerationTime}
|
||||||
turn={this.state.gameInfo.turn}
|
turn={this.state.gameInfo.turn}
|
||||||
runNewTurn={(prevTurn)=>this.runNewTurn(prevTurn)}
|
runNewTurn={(prevTurn) => this.runNewTurn(prevTurn)}
|
||||||
toggleFade={this.state.gameInfo.toggleFade}
|
toggleFade={this.state.gameInfo.toggleFade}
|
||||||
gameStatus={this.state.gameInfo.gameStatus}
|
gameStatus={this.state.gameInfo.gameStatus}
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="7" style={{"height": "100%"}}>
|
<Layout.Col span="7" style={{ height: '100%' }}>
|
||||||
<Paper className={"doudizhu-probability-paper"} elevation={3}>
|
<Paper className={'doudizhu-probability-paper'} elevation={3}>
|
||||||
<div className={"probability-player"}>
|
<div className={'probability-player'}>
|
||||||
{
|
{this.state.gameInfo.playerInfo.length > 0 ? (
|
||||||
this.state.gameInfo.playerInfo.length > 0 ?
|
<span>
|
||||||
<span>Current Player: {this.state.gameInfo.currentPlayer}<br/>Role: {this.state.gameInfo.playerInfo[this.state.gameInfo.currentPlayer].role}</span>
|
Current Player: {this.state.gameInfo.currentPlayer}
|
||||||
:
|
<br />
|
||||||
|
Role:{' '}
|
||||||
|
{this.state.gameInfo.playerInfo[this.state.gameInfo.currentPlayer].role}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
<span>Waiting...</span>
|
<span>Waiting...</span>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div className={"probability-table"}>
|
<div className={'probability-table'}>
|
||||||
<div className={"probability-item"}>
|
<div className={'probability-item'}>{this.computeProbabilityItem(0)}</div>
|
||||||
{this.computeProbabilityItem(0)}
|
<div className={'probability-item'}>{this.computeProbabilityItem(1)}</div>
|
||||||
</div>
|
<div className={'probability-item'}>{this.computeProbabilityItem(2)}</div>
|
||||||
<div className={"probability-item"}>
|
|
||||||
{this.computeProbabilityItem(1)}
|
|
||||||
</div>
|
|
||||||
<div className={"probability-item"}>
|
|
||||||
{this.computeProbabilityItem(2)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
|
@ -430,46 +513,63 @@ class DoudizhuReplayView extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<Loading loading={this.state.fullScreenLoading}>
|
<Loading loading={this.state.fullScreenLoading}>
|
||||||
<div className="game-controller">
|
<div className="game-controller">
|
||||||
<Paper className={"game-controller-paper"} elevation={3}>
|
<Paper className={'game-controller-paper'} elevation={3}>
|
||||||
<Layout.Row style={{"height": "51px"}}>
|
<Layout.Row style={{ height: '51px' }}>
|
||||||
<Layout.Col span="7" style={{"height": "51px", "lineHeight": "48px"}}>
|
<Layout.Col span="7" style={{ height: '51px', lineHeight: '48px' }}>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={this.state.gameInfo.gameStatus !== "paused" || this.state.gameInfo.turn === 0}
|
disabled={
|
||||||
onClick={()=>{this.go2PrevGameState()}}
|
this.state.gameInfo.gameStatus !== 'paused' ||
|
||||||
>
|
this.state.gameInfo.turn === 0
|
||||||
<SkipPreviousIcon />
|
}
|
||||||
</Button>
|
onClick={() => {
|
||||||
{ this.gameStatusButton(this.state.gameInfo.gameStatus) }
|
this.go2PrevGameState();
|
||||||
<Button
|
}}
|
||||||
variant="contained"
|
>
|
||||||
color="primary"
|
<SkipPreviousIcon />
|
||||||
disabled={this.state.gameInfo.gameStatus !== "paused"}
|
</Button>
|
||||||
onClick={()=>{this.go2NextGameState()}}
|
{this.gameStatusButton(this.state.gameInfo.gameStatus)}
|
||||||
>
|
<Button
|
||||||
<SkipNextIcon />
|
variant="contained"
|
||||||
</Button>
|
color="primary"
|
||||||
</div>
|
disabled={this.state.gameInfo.gameStatus !== 'paused'}
|
||||||
|
onClick={() => {
|
||||||
|
this.go2NextGameState();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SkipNextIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
|
<Layout.Col span="1" style={{ height: '100%', width: '1px' }}>
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="3" style={{"height": "51px", "lineHeight": "51px", "marginLeft": "-1px", "marginRight": "-1px"}}>
|
<Layout.Col
|
||||||
<div style={{"textAlign": "center"}}>{`Turn ${this.state.gameInfo.turn}`}</div>
|
span="3"
|
||||||
|
style={{
|
||||||
|
height: '51px',
|
||||||
|
lineHeight: '51px',
|
||||||
|
marginLeft: '-1px',
|
||||||
|
marginRight: '-1px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: 'center' }}>{`Turn ${this.state.gameInfo.turn}`}</div>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
|
<Layout.Col span="1" style={{ height: '100%', width: '1px' }}>
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="14">
|
<Layout.Col span="14">
|
||||||
<div>
|
<div>
|
||||||
<label className={"form-label-left"}>Game Speed</label>
|
<label className={'form-label-left'}>Game Speed</label>
|
||||||
<div style={{"marginLeft": "100px", "marginRight": "10px"}}>
|
<div style={{ marginLeft: '100px', marginRight: '10px' }}>
|
||||||
<Slider
|
<Slider
|
||||||
value={this.state.gameSpeed}
|
value={this.state.gameSpeed}
|
||||||
getAriaValueText={sliderValueText}
|
getAriaValueText={sliderValueText}
|
||||||
onChange={(e, newVal)=>{this.changeGameSpeed(newVal)}}
|
onChange={(e, newVal) => {
|
||||||
|
this.changeGameSpeed(newVal);
|
||||||
|
}}
|
||||||
aria-labelledby="discrete-slider-custom"
|
aria-labelledby="discrete-slider-custom"
|
||||||
step={1}
|
step={1}
|
||||||
min={-3}
|
min={-3}
|
||||||
|
@ -487,7 +587,7 @@ class DoudizhuReplayView extends React.Component {
|
||||||
</Loading>
|
</Loading>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue