add probability view to doudizhu replay
This commit is contained in:
parent
68eba9a37b
commit
7171fe4e82
|
@ -101,9 +101,9 @@ export function computeHandCardsWidth(num, emWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function card2SuiteAndRank(card) {
|
export function card2SuiteAndRank(card) {
|
||||||
if (card === 'BJ') {
|
if (card === 'BJ' || card === 'B') {
|
||||||
return { suite: null, rank: 'X' };
|
return { suite: null, rank: 'X' };
|
||||||
} else if (card === 'RJ') {
|
} else if (card === 'RJ' || card === 'R') {
|
||||||
return { suite: null, rank: 'D' };
|
return { suite: null, rank: 'D' };
|
||||||
} else {
|
} else {
|
||||||
return { suite: card[0], rank: card[1] };
|
return { suite: card[0], rank: card[1] };
|
||||||
|
|
|
@ -8,10 +8,10 @@ import Divider from '@material-ui/core/Divider';
|
||||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import Slider from '@material-ui/core/Slider';
|
import Slider from '@material-ui/core/Slider';
|
||||||
|
import NotInterestedIcon from '@material-ui/icons/NotInterested';
|
||||||
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
|
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
|
||||||
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
|
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 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 axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -20,7 +20,14 @@ import qs from 'query-string';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import '../../assets/gameview.scss';
|
import '../../assets/gameview.scss';
|
||||||
import { DoudizhuGameBoard } from '../../components/GameBoard';
|
import { DoudizhuGameBoard } from '../../components/GameBoard';
|
||||||
import { computeHandCardsWidth, deepCopy, doubleRaf, removeCards, translateCardData } from '../../utils';
|
import {
|
||||||
|
card2SuiteAndRank,
|
||||||
|
computeHandCardsWidth,
|
||||||
|
deepCopy,
|
||||||
|
doubleRaf,
|
||||||
|
removeCards,
|
||||||
|
translateCardData,
|
||||||
|
} from '../../utils';
|
||||||
import { apiUrl } from '../../utils/config';
|
import { apiUrl } from '../../utils/config';
|
||||||
|
|
||||||
class DoudizhuReplayView extends React.Component {
|
class DoudizhuReplayView extends React.Component {
|
||||||
|
@ -190,10 +197,25 @@ class DoudizhuReplayView extends React.Component {
|
||||||
|
|
||||||
// for test use
|
// for test use
|
||||||
if (typeof res === 'string') res = JSON.parse(res.replaceAll("'", '"').replaceAll('None', 'null'));
|
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;
|
||||||
|
// pre-process move history
|
||||||
|
for (const historyItem of this.moveHistory) {
|
||||||
|
if (historyItem.info && !Array.isArray(historyItem.info)) {
|
||||||
|
if ('probs' in historyItem.info) {
|
||||||
|
historyItem.info.probs = Object.entries(historyItem.info.probs).sort(
|
||||||
|
(a, b) => Number(b[1]) - Number(a[1]),
|
||||||
|
);
|
||||||
|
} else if ('values' in historyItem.info) {
|
||||||
|
historyItem.info.values = Object.entries(historyItem.info.values).sort(
|
||||||
|
(a, b) => Number(b[1]) - Number(a[1]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('pre-processed move history', this.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;
|
||||||
|
@ -343,32 +365,103 @@ class DoudizhuReplayView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computePredictionCards(cards, hands) {
|
||||||
|
let computedCards = [];
|
||||||
|
if (cards.length > 0) {
|
||||||
|
hands.forEach((card) => {
|
||||||
|
let { rank } = card2SuiteAndRank(card);
|
||||||
|
|
||||||
|
// X is B, D is R
|
||||||
|
if (rank === 'X') rank = 'B';
|
||||||
|
else if (rank === 'D') rank = 'R';
|
||||||
|
const idx = cards.indexOf(rank);
|
||||||
|
if (idx >= 0) {
|
||||||
|
cards.splice(idx, 1);
|
||||||
|
computedCards.push(card);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
computedCards = 'pass';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computedCards === 'pass') {
|
||||||
|
return (
|
||||||
|
<div className={'non-card ' + this.state.gameInfo.toggleFade}>
|
||||||
|
<span>{'PASS'}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={'unselectable playingCards loose ' + this.state.gameInfo.toggleFade}>
|
||||||
|
<ul className="hand" style={{ width: computeHandCardsWidth(computedCards.length, 10) }}>
|
||||||
|
{computedCards.map((card) => {
|
||||||
|
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
||||||
|
return (
|
||||||
|
<li key={`handCard-${card}`}>
|
||||||
|
<label className={`card ${rankClass} ${suitClass}`} href="/#">
|
||||||
|
<span className="rank">{rankText}</span>
|
||||||
|
<span className="suit">{suitText}</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</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 currentMove = null;
|
||||||
|
if (this.state.gameInfo.turn !== this.moveHistory.length) {
|
||||||
|
currentMove = this.moveHistory[this.state.gameInfo.turn];
|
||||||
|
}
|
||||||
|
|
||||||
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";
|
||||||
// let probabilities
|
let probabilities = null;
|
||||||
|
let probabilityItemType = null;
|
||||||
|
|
||||||
|
if (currentMove) {
|
||||||
|
if (Array.isArray(currentMove.info)) {
|
||||||
|
probabilityItemType = 'Rule';
|
||||||
|
} else {
|
||||||
|
if ('probs' in currentMove.info) {
|
||||||
|
probabilityItemType = 'Probability';
|
||||||
|
probabilities = idx < currentMove.info.probs.length ? currentMove.info.probs[idx] : null;
|
||||||
|
} else if ('values' in currentMove.info) {
|
||||||
|
probabilityItemType = 'Expected payoff';
|
||||||
|
probabilities = idx < currentMove.info.values.length ? currentMove.info.values[idx] : null;
|
||||||
|
} else {
|
||||||
|
probabilityItemType = 'Rule';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
style['backgroundColor'] = currentMove !== null ? '#fff' : '#bdbdbd';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'playing'} style={style}>
|
<div className={'playing'} style={style}>
|
||||||
<div className="probability-move">
|
<div className="probability-move">
|
||||||
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? (
|
{probabilities ? (
|
||||||
this.computeSingleLineHand(
|
this.computePredictionCards(
|
||||||
this.cardStr2Arr(this.moveHistory[this.state.gameInfo.turn].probabilities[idx].move),
|
probabilities[0] === 'pass' ? [] : probabilities[0].split(''),
|
||||||
|
this.state.gameInfo.hands[currentMove.playerIdx],
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<NotInterestedIcon fontSize="large" />
|
<NotInterestedIcon fontSize="large" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? (
|
{probabilities ? (
|
||||||
<div className={'non-card'}>
|
<div className={'non-card ' + this.state.gameInfo.toggleFade}>
|
||||||
<span>
|
<span>
|
||||||
{this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx
|
{probabilityItemType === 'Rule'
|
||||||
? `Probability ${(
|
? 'Rule Based'
|
||||||
this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability *
|
: probabilityItemType === 'Probability'
|
||||||
100
|
? `Probability ${(probabilities[1] * 100).toFixed(2)}%`
|
||||||
).toFixed(2)}%`
|
: `Expected payoff: ${probabilities[1].toFixed(4)}`}
|
||||||
: ''}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
Loading…
Reference in New Issue