diff --git a/src/components/GameBoard/DoudizhuGameBoard.js b/src/components/GameBoard/DoudizhuGameBoard.js index a227952..e1eeb44 100644 --- a/src/components/GameBoard/DoudizhuGameBoard.js +++ b/src/components/GameBoard/DoudizhuGameBoard.js @@ -7,7 +7,7 @@ import '../../assets/doudizhu.scss'; import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png'; import Peasant_wName from '../../assets/images/Portrait/Peasant_wName.png'; import PlaceHolderPlayer from '../../assets/images/Portrait/Player.png'; -import { computeHandCardsWidth, millisecond2Second, translateCardData } from '../../utils'; +import { computeHandCardsWidth, millisecond2Second, translateCardData, sortDoudizhuCards } from '../../utils'; class DoudizhuGameBoard extends React.Component { computePlayerPortrait(playerId, playerIdx) { @@ -32,7 +32,8 @@ class DoudizhuGameBoard extends React.Component { ); } - computeSingleLineHand(cards, fadeClassName = '', cardSelectable = false) { + computeSingleLineHand(inputCards, fadeClassName = '', cardSelectable = false) { + const cards = inputCards === 'pass' ? inputCards : sortDoudizhuCards(inputCards); if (cards === 'pass') { return (
diff --git a/src/view/PvEView/PvEDoudizhuDemoView.js b/src/view/PvEView/PvEDoudizhuDemoView.js index 7ff02c0..de5d034 100644 --- a/src/view/PvEView/PvEDoudizhuDemoView.js +++ b/src/view/PvEView/PvEDoudizhuDemoView.js @@ -1,5 +1,6 @@ import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; +import Slider from '@material-ui/core/Slider'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; @@ -28,14 +29,13 @@ import { } from '../../utils'; import { douzeroDemoUrl } from '../../utils/config'; -const shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice()); +let shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice()); let threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3))); -const originalThreeLandlordCards = threeLandlordCards.slice(); +let originalThreeLandlordCards = threeLandlordCards.slice(); const initConsiderationTime = 30000; const considerationTimeDeduction = 1000; -const apiPlayDelay = 3000; const mainPlayerId = 0; // index of main player (for the sake of simplify code logic) let playerInfo = []; @@ -60,8 +60,10 @@ let playedCardsLandlordDown = []; let playedCardsLandlordUp = []; let legalActions = { turn: -1, actions: [] }; let gameEndDialogText = ''; +let syncGameStatus = 'ready'; function PvEDoudizhuDemoView() { + const [apiPlayDelay, setApiPlayDelay] = useState(3000); const [isGameEndDialogOpen, setIsGameEndDialogOpen] = useState(false); const [considerationTime, setConsiderationTime] = useState(initConsiderationTime); const [toggleFade, setToggleFade] = useState(''); @@ -203,6 +205,10 @@ function PvEDoudizhuDemoView() { newGameState.hands[gameState.currentPlayer] = newHand; newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3; newGameState.turn++; + if (newHand.length === 0) { + setGameStatus('over'); + syncGameStatus = 'over'; + } setGameState(newGameState); setToggleFade('fade-in'); setTimeout(() => { @@ -215,7 +221,6 @@ function PvEDoudizhuDemoView() { if (newHand.length === 0) { const winner = playerInfo[gameState.currentPlayer]; - setGameStatus('over'); setTimeout(() => { gameEndDialogText = winner.role + ' wins!'; setIsGameEndDialogOpen(true); @@ -331,9 +336,12 @@ function PvEDoudizhuDemoView() { } else { let bestAction = ''; if (data.result && Object.keys(data.result).length > 0) { + const sortedResult = Object.entries(data.result).sort((a, b) => { + return Number(b[1]) - Number(a[1]); + }); setPredictionRes({ - prediction: Object.entries(data.result).sort((a, b) => { - return Number(b[1]) - Number(a[1]); + prediction: sortedResult.map((result) => { + return [result[0], data.win_rates[result[0]]]; }), hands: gameState.hands[gameState.currentPlayer].slice(), }); @@ -360,11 +368,8 @@ function PvEDoudizhuDemoView() { } }; - const toggleHideRivalHand = () => { - setHideRivalHand(!hideRivalHand); - }; - const toggleHidePredictionArea = () => { + setHideRivalHand(!hideRivalHand); setHidePredictionArea(!hidePredictionArea); }; @@ -423,6 +428,7 @@ function PvEDoudizhuDemoView() { playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2; initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice()); setGameStatus('playing'); + syncGameStatus = 'playing'; }; const gameStateTimer = () => { @@ -441,14 +447,53 @@ function PvEDoudizhuDemoView() { }; const handleCloseGameEndDialog = () => { + // reset all game state for new game + shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice()); + + threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3))); + originalThreeLandlordCards = threeLandlordCards.slice(); + + initHands = [ + shuffledDoudizhuDeck.slice(3, 20), + shuffledDoudizhuDeck.slice(20, 37), + shuffledDoudizhuDeck.slice(37, 54), + ]; + + playerInfo = []; + + gameStateTimeout = null; + gameHistory = []; + bombNum = 0; + lastMoveLandlord = []; + lastMoveLandlordDown = []; + lastMoveLandlordUp = []; + playedCardsLandlord = []; + playedCardsLandlordDown = []; + playedCardsLandlordUp = []; + legalActions = { turn: -1, actions: [] }; + gameEndDialogText = ''; + + setConsiderationTime(initConsiderationTime); + setToggleFade(''); + setGameState({ + hands: [[], [], []], + latestAction: [[], [], []], + currentPlayer: null, // index of current player + turn: 0, + }); + setSelectedCards([]); // user selected hand card + setPredictionRes({ prediction: [], hands: [] }); + + setGameStatus('ready'); + syncGameStatus = 'ready'; setIsGameEndDialogOpen(false); - // todo: proceed next game option + }; const startGame = async () => { // start game setGameStatus('playing'); - + syncGameStatus = 'playing'; const newGameState = deepCopy(gameState); // find landord to be the first player newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index; @@ -475,11 +520,11 @@ function PvEDoudizhuDemoView() { }; useEffect(() => { - if (gameStatus === 'playing') gameStateTimer(); + if (syncGameStatus === 'playing') gameStateTimer(); }, [considerationTime]); useEffect(() => { - if (gameState.currentPlayer !== null && gameStatus === 'playing') { + if (gameState.currentPlayer !== null && syncGameStatus === 'playing') { // if current player is not user, request for API player if (gameState.currentPlayer !== mainPlayerId) { requestApiPlay(); @@ -609,6 +654,70 @@ function PvEDoudizhuDemoView() { } }; + const gameSpeedMarks = [ + { + value: 0, + label: '0s', + }, + { + value: 1, + label: '1s', + }, + { + value: 2, + label: '3s', + }, + { + value: 3, + label: '5s', + }, + { + value: 4, + label: '10s', + }, + { + value: 5, + label: '30s', + } + ]; + + const gameSpeedMap = [ + { + value: 0, + delay: 0, + }, + { + value: 1, + delay: 1000, + }, + { + value: 2, + delay: 3000, + }, + { + value: 3, + delay: 5000, + }, + { + value: 4, + delay: 10000, + }, + { + value: 5, + delay: 30000, + } + ]; + + const changeApiPlayerDelay = (newVal) => { + const found = gameSpeedMap.find(element => element.value === newVal); + if (found) + setApiPlayDelay(found.delay); + } + + const sliderValueText = (value) => { + return value; + }; + return (
@@ -668,6 +777,7 @@ function PvEDoudizhuDemoView() { const [rankClass, suitClass, rankText, suitText] = translateCardData(card); return (
@@ -692,11 +802,10 @@ function PvEDoudizhuDemoView() {
)} -
+
{playerInfo.length > 0 && gameState.currentPlayer !== null ? ( - Current Player: {gameState.currentPlayer} |{' '} - {playerInfo[gameState.currentPlayer].role} + {['Landlord', 'Landlord Down', 'Landlord Up'][playerInfo[gameState.currentPlayer].douzeroPlayerPosition]} ) : ( Waiting... @@ -716,19 +825,6 @@ function PvEDoudizhuDemoView() { - } - label="Show Rival Cards" - /> - - - - - - -
{`Turn ${gameState.turn}`}
@@ -751,11 +847,24 @@ function PvEDoudizhuDemoView() { - -
{`Game Status: ${gameStatus}`}
+ +
+ +
+ element.delay === apiPlayDelay).value} + getAriaValueText={sliderValueText} + onChange={(e, newVal)=>{changeApiPlayerDelay(newVal)}} + aria-labelledby="discrete-slider-custom" + step={1} + min={0} + max={5} + track={false} + valueLabelDisplay="off" + marks={gameSpeedMarks} + /> +
+