From 3a878122d9a9c087bba2e13ded9edf7d7b2ebbe1 Mon Sep 17 00:00:00 2001 From: Songyi Huang Date: Sun, 25 Apr 2021 20:48:34 -0700 Subject: [PATCH] add prediction area & control area --- src/assets/gameview.scss | 6 + src/components/GameBoard/DoudizhuGameBoard.js | 79 +++---- src/view/PvEView/PvEDoudizhuDemoView.js | 202 +++++++++++++++++- 3 files changed, 240 insertions(+), 47 deletions(-) diff --git a/src/assets/gameview.scss b/src/assets/gameview.scss index c62fef6..c660bb9 100644 --- a/src/assets/gameview.scss +++ b/src/assets/gameview.scss @@ -101,6 +101,12 @@ box-sizing: border-box; width: 80px; } + + .MuiFormControlLabel-label { + display: table-cell; + line-height: 51px; + vertical-align: middle; + } } .doudizhu-view-container { diff --git a/src/components/GameBoard/DoudizhuGameBoard.js b/src/components/GameBoard/DoudizhuGameBoard.js index 76ac762..a227952 100644 --- a/src/components/GameBoard/DoudizhuGameBoard.js +++ b/src/components/GameBoard/DoudizhuGameBoard.js @@ -143,43 +143,41 @@ class DoudizhuGameBoard extends React.Component {
{millisecond2Second(this.props.considerationTime)}
- {this.props.gamePlayable ? - (<> - - - - ) - : - undefined} - + {this.props.gamePlayable ? ( + <> + + + + + ) : undefined} ); } else { @@ -195,7 +193,12 @@ class DoudizhuGameBoard extends React.Component { } componentDidUpdate(prevProps, prevState, snapshot) { - if (prevProps.turn !== this.props.turn && this.props.turn !== 0 && this.props.gameStatus === 'playing') { + if ( + this.props.runNewTurn && + prevProps.turn !== this.props.turn && + this.props.turn !== 0 && + this.props.gameStatus === 'playing' + ) { // new turn starts this.props.runNewTurn(prevProps); } diff --git a/src/view/PvEView/PvEDoudizhuDemoView.js b/src/view/PvEView/PvEDoudizhuDemoView.js index eccb8bd..ca5a6f9 100644 --- a/src/view/PvEView/PvEDoudizhuDemoView.js +++ b/src/view/PvEView/PvEDoudizhuDemoView.js @@ -4,7 +4,12 @@ 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 FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormGroup from '@material-ui/core/FormGroup'; import Paper from '@material-ui/core/Paper'; +import Switch from '@material-ui/core/Switch'; +import NotInterestedIcon from '@material-ui/icons/NotInterested'; import axios from 'axios'; import { Layout, Message } from 'element-react'; import qs from 'query-string'; @@ -13,11 +18,13 @@ import '../../assets/doudizhu.scss'; import { DoudizhuGameBoard } from '../../components/GameBoard'; import { card2SuiteAndRank, + computeHandCardsWidth, deepCopy, fullDoudizhuDeck, isDoudizhuBomb, shuffleArray, sortDoudizhuCards, + translateCardData, } from '../../utils'; import { douzeroDemoUrl } from '../../utils/config'; @@ -66,6 +73,9 @@ function PvEDoudizhuDemoView() { }); const [selectedCards, setSelectedCards] = useState([]); // user selected hand card const [isPassDisabled, setIsPassDisabled] = useState(true); + const [predictionRes, setPredictionRes] = useState({ prediction: [], hands: [] }); + const [hideRivalHand, setHideRivalHand] = useState(false); + const [hidePredictionArea, setHidePredictionArea] = useState(false); const cardArr2DouzeroFormat = (cards) => { return cards @@ -272,20 +282,41 @@ function PvEDoudizhuDemoView() { rival_move, }; const apiRes = await axios.post(`${douzeroDemoUrl}/legal`, qs.stringify(requestBody)); - if (apiRes.data.legal_action === '') proceedNextTurn([]); - else if (apiRes.data.legal_action.split(',').length === 1) + if (apiRes.data.legal_action === '') { + proceedNextTurn([]); + setPredictionRes({ + prediction: [['', 'Only Choice']], + hands: gameState.hands[gameState.currentPlayer].slice(), + }); + } else if (apiRes.data.legal_action.split(',').length === 1) { proceedNextTurn(apiRes.data.legal_action.split('')); - else { + setPredictionRes({ + prediction: [[apiRes.data.legal_action, 'Only Choice']], + hands: gameState.hands[gameState.currentPlayer].slice(), + }); + } else { Message({ message: 'Error receiving prediction result, please try refresh the page', type: 'error', showClose: true, }); } + } else { + Message({ + message: `Error: ${apiRes.data.message}`, + type: 'error', + showClose: true, + }); } } else { let bestAction = ''; if (data.result && Object.keys(data.result).length > 0) { + setPredictionRes({ + prediction: Object.entries(data.result).sort((a, b) => { + return Number(b[1]) - Number(a[1]); + }), + hands: gameState.hands[gameState.currentPlayer].slice(), + }); if (Object.keys(data.result).length === 1) bestAction = Object.keys(data.result)[0]; else { bestAction = Object.keys(data.result)[0]; @@ -309,6 +340,14 @@ function PvEDoudizhuDemoView() { } }; + const toggleHideRivalHand = () => { + setHideRivalHand(!hideRivalHand); + }; + + const toggleHidePredictionArea = () => { + setHidePredictionArea(!hidePredictionArea); + }; + const handleSelectedCards = (cards) => { let newSelectedCards = selectedCards.slice(); cards.forEach((card) => { @@ -416,14 +455,16 @@ function PvEDoudizhuDemoView() { }; useEffect(() => { - gameStateTimer(); + if (gameStatus === 'playing') gameStateTimer(); }, [considerationTime]); useEffect(() => { - if (gameState.currentPlayer && gameStatus === 'playing') { + if (gameState.currentPlayer !== null && gameStatus === 'playing') { // if current player is not user, request for API player if (gameState.currentPlayer !== mainPlayerId) { requestApiPlay(); + } else { + setPredictionRes({ prediction: [], hands: [] }); } } }, [gameState.currentPlayer]); @@ -432,8 +473,6 @@ function PvEDoudizhuDemoView() { if (gameStatus === 'playing') startGame(); }, [gameStatus]); - const runNewTurn = () => {}; - const handleMainPlayerAct = (type) => { switch (type) { case 'play': { @@ -472,6 +511,82 @@ function PvEDoudizhuDemoView() { } }; + const computePredictionCards = (cards, hands) => { + let computedCards = []; + if (cards.length > 0) { + hands.forEach((card) => { + const { rank } = card2SuiteAndRank(card); + const idx = cards.indexOf(rank); + if (idx >= 0) { + cards.splice(idx, 1); + computedCards.push(card); + } + }); + } else { + computedCards = 'pass'; + } + + if (computedCards === 'pass') { + return ( +
+ Pass +
+ ); + } else { + return ( +
+ +
+ ); + } + }; + + const computeProbabilityItem = (idx) => { + if (gameStatus !== 'ready') { + if (hidePredictionArea) { + return ( +
+
+ {'Hidden'} +
+
+ ); + } + return ( +
+
+ {predictionRes.prediction.length > idx ? ( + computePredictionCards(predictionRes.prediction[idx][0].split(''), predictionRes.hands) + ) : ( + + )} +
+ {predictionRes.prediction.length > idx ? ( +
+ {`Expected Score: ${predictionRes.prediction[idx][1]}`} +
+ ) : ( + '' + )} +
+ ); + } else { + return Waiting...; + } + }; + return (
runNewTurn(prevTurn)} toggleFade={toggleFade} gameStatus={gameStatus} handleMainPlayerAct={handleMainPlayerAct} @@ -520,7 +634,77 @@ function PvEDoudizhuDemoView() {
+ + +
+ {playerInfo.length > 0 && gameState.currentPlayer !== null ? ( + + Current Player: {gameState.currentPlayer} +
+ {playerInfo[gameState.currentPlayer].role} +
+ ) : ( + Waiting... + )} +
+ +
+
{computeProbabilityItem(0)}
+
{computeProbabilityItem(1)}
+
{computeProbabilityItem(2)}
+
+
+
+
+ + + + + } + label="Show Rival Cards" + /> + + + + + + + + + } + label="Show Prediction Area" + /> + + + + + + +
{`Turn ${gameState.turn}`}
+
+ + + + +
{`Game Status: ${gameStatus}`}
+
+
+
+
);