adding select role interface for pve demo; fix bugs

This commit is contained in:
Songyi Huang 2021-04-25 16:08:50 -07:00
parent b6f0503c9e
commit edc060c00a
3 changed files with 205 additions and 85 deletions

View File

@ -3,12 +3,21 @@
.doudizhu-wrapper {
width: 100%;
height: 100%;
//background-color: #C3CDFF;
position: relative;
#gameboard-background {
width: 100%;
height: 100%;
background-image: url("./images/gameboard.png");
background-repeat: no-repeat;
background-size: 100% 125%;
background-position: bottom;
position: relative;
&.blur-background {
filter: blur(3px);
pointer-events: none;
}
}
.played-card-area {
font-size: 12px;

View File

@ -1,6 +1,7 @@
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import Chip from '@material-ui/core/Chip';
import { Layout } from 'element-react';
import React from 'react';
import '../../assets/doudizhu.scss';
import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png';
@ -14,19 +15,19 @@ class DoudizhuGameBoard extends React.Component {
return this.props.playerInfo[playerIdx].role === 'landlord' ? (
<div>
<img src={Landlord_wName} alt={'Landlord'} height="70%" width="70%" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
</div>
) : (
<div>
<img src={Peasant_wName} alt={'Peasant'} height="70%" width="70%" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
</div>
);
} else
return (
<div>
<img src={PlaceHolderPlayer} alt={'Player'} height="70%" width="70%" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
</div>
);
}
@ -90,7 +91,11 @@ class DoudizhuGameBoard extends React.Component {
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
return (
<li key={`handCard-${card}`}>
<label className={`card ${rankClass} ${suitClass}`}>
<label
className={`card ${
this.props.showCardBack ? 'back' : '' + rankClass + ' ' + suitClass
}`}
>
<span className="rank">{rankText}</span>
<span className="suit">{suitText}</span>
</label>
@ -107,7 +112,11 @@ class DoudizhuGameBoard extends React.Component {
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
return (
<li key={`handCard-${card}`}>
<label className={`card ${rankClass} ${suitClass}`}>
<label
className={`card ${
this.props.showCardBack ? 'back' : '' + rankClass + ' ' + suitClass
}`}
>
<span className="rank">{rankText}</span>
<span className="suit">{suitText}</span>
</label>
@ -209,6 +218,12 @@ class DoudizhuGameBoard extends React.Component {
}
return (
<div className="doudizhu-wrapper" style={{}}>
<div
id={'gameboard-background'}
className={
this.props.gameStatus === 'ready' && this.props.gamePlayable ? 'blur-background' : undefined
}
>
<div id={'left-player'}>
<div className="player-main-area">
<div className="player-info">{this.computePlayerPortrait(leftId, leftIdx)}</div>
@ -236,7 +251,9 @@ class DoudizhuGameBoard extends React.Component {
<div className="played-card-area">{rightIdx >= 0 ? this.playerDecisionArea(rightIdx) : ''}</div>
</div>
<div id={'bottom-player'}>
<div className="played-card-area">{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}</div>
<div className="played-card-area">
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}
</div>
<div className="player-main-area">
<div className="player-info">{this.computePlayerPortrait(bottomId, bottomIdx)}</div>
{bottomIdx >= 0 ? (
@ -251,6 +268,53 @@ class DoudizhuGameBoard extends React.Component {
</div>
</div>
</div>
{this.props.gamePlayable && this.props.gameStatus === 'ready' ? (
<Layout.Row
type="flex"
style={{
position: 'absolute',
top: 0,
height: '100%',
width: '100%',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Button
onClick={() => this.props.handleSelectRole('landlord_up')}
style={{ width: '220px' }}
variant="contained"
color="primary"
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" />}
>
Play as Peasant
<br />
(Early Hand)
</Button>
<Button
onClick={() => this.props.handleSelectRole('landlord')}
style={{ width: '220px', marginTop: '20px', marginBottom: '20px' }}
variant="contained"
color="primary"
startIcon={<img src={Landlord_wName} alt="Peasant" width="48px" />}
>
Play as Landlord
</Button>
<Button
onClick={() => this.props.handleSelectRole('landlord_down')}
style={{ width: '220px' }}
variant="contained"
color="primary"
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" />}
>
Play as Peasant
<br />
(Late Hand)
</Button>
</Layout.Row>
) : undefined}
</div>
);
}
}

View File

@ -9,6 +9,7 @@ import axios from 'axios';
import { Layout, Message } from 'element-react';
import qs from 'query-string';
import React, { useEffect, useState } from 'react';
import '../../assets/doudizhu.scss';
import { DoudizhuGameBoard } from '../../components/GameBoard';
import {
card2SuiteAndRank,
@ -28,26 +29,7 @@ const initConsiderationTime = 30000;
const considerationTimeDeduction = 1000;
const apiPlayDelay = 3000;
const mainPlayerId = 0; // index of main player (for the sake of simplify code logic)
const playerInfo = [
{
id: 0,
index: 0,
role: 'peasant',
douzeroPlayerPosition: 1,
},
{
id: 1,
index: 1,
role: 'peasant',
douzeroPlayerPosition: 2,
},
{
id: 2,
index: 2,
role: 'landlord',
douzeroPlayerPosition: 0,
},
];
let playerInfo = [];
let initHands = [
shuffledDoudizhuDeck.slice(3, 20),
@ -57,8 +39,6 @@ let initHands = [
console.log('init hands', initHands);
console.log('three landlord card', threeLandlordCards);
console.log('player info', playerInfo);
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
let gameStateTimeout = null;
@ -87,10 +67,6 @@ function PvEDoudizhuDemoView() {
const [selectedCards, setSelectedCards] = useState([]); // user selected hand card
const [isPassDisabled, setIsPassDisabled] = useState(true);
const cardStr2Arr = (cardStr) => {
return cardStr === 'pass' || cardStr === '' ? 'pass' : cardStr.split(' ');
};
const cardArr2DouzeroFormat = (cards) => {
return cards
.map((card) => {
@ -190,6 +166,8 @@ function PvEDoudizhuDemoView() {
lastMoveLandlordUp = newHistoryRecord;
playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
break;
default:
break;
}
gameHistory.push(newHistoryRecord);
if (isDoudizhuBomb(newHistoryRecord)) bombNum++;
@ -215,10 +193,11 @@ function PvEDoudizhuDemoView() {
gameEndDialogText = winner.role + ' wins!';
setIsGameEndDialogOpen(true);
}, 300);
return;
}
} else {
setConsiderationTime(initConsiderationTime);
// manually trigger timer if consideration time equals initConsiderationTime
if (initConsiderationTime === considerationTime) gameStateTimer();
}
};
const requestApiPlay = async () => {
@ -345,6 +324,51 @@ function PvEDoudizhuDemoView() {
setSelectedCards(newSelectedCards);
};
const handleSelectRole = (role) => {
const playerInfoTemplate = [
{
id: 0,
index: 0,
role: 'peasant',
douzeroPlayerPosition: -1,
},
{
id: 1,
index: 1,
role: 'peasant',
douzeroPlayerPosition: -1,
},
{
id: 2,
index: 2,
role: 'peasant',
douzeroPlayerPosition: -1,
},
];
switch (role) {
case 'landlord_up':
playerInfo = deepCopy(playerInfoTemplate);
playerInfo[1].role = 'landlord';
break;
case 'landlord':
playerInfo = deepCopy(playerInfoTemplate);
playerInfo[0].role = 'landlord';
break;
case 'landlord_down':
playerInfo = deepCopy(playerInfoTemplate);
playerInfo[2].role = 'landlord';
break;
default:
break;
}
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
playerInfo[landlordIdx].douzeroPlayerPosition = 0;
playerInfo[(landlordIdx + 1) % 3].douzeroPlayerPosition = 1;
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
setGameStatus('playing');
};
const gameStateTimer = () => {
gameStateTimeout = setTimeout(() => {
let currentConsiderationTime = considerationTime;
@ -365,12 +389,7 @@ function PvEDoudizhuDemoView() {
// todo: proceed next game option
};
useEffect(() => {
gameStateTimer();
}, [considerationTime]);
// set init game state
useEffect(() => {
const startGame = async () => {
// start game
setGameStatus('playing');
@ -378,12 +397,33 @@ function PvEDoudizhuDemoView() {
// find landord to be the first player
newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index;
newGameState.hands = initHands.map((element) => sortDoudizhuCards(element));
// if first player is user, fetch legal actions
if (newGameState.currentPlayer === mainPlayerId) {
const player_hand_cards = cardArr2DouzeroFormat(newGameState.hands[mainPlayerId].slice().reverse());
let rival_move = '';
const requestBody = {
player_hand_cards,
rival_move,
};
const apiRes = await axios.post(`${douzeroDemoUrl}/legal`, qs.stringify(requestBody));
const data = apiRes.data;
legalActions = {
turn: 0,
actions: data.legal_action.split(','),
};
}
setGameState(newGameState);
gameStateTimer();
}, []);
};
useEffect(() => {
if (gameState.currentPlayer) {
gameStateTimer();
}, [considerationTime]);
useEffect(() => {
if (gameState.currentPlayer && gameStatus === 'playing') {
// if current player is not user, request for API player
if (gameState.currentPlayer !== mainPlayerId) {
requestApiPlay();
@ -391,6 +431,10 @@ function PvEDoudizhuDemoView() {
}
}, [gameState.currentPlayer]);
useEffect(() => {
if (gameStatus === 'playing') startGame();
}, [gameStatus]);
const runNewTurn = () => {};
const handleMainPlayerAct = (type) => {
@ -446,7 +490,7 @@ function PvEDoudizhuDemoView() {
<DialogContentText id="alert-dialog-description">{gameEndDialogText}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseGameEndDialog} color="primary" autoFocus>
<Button onClick={() => handleCloseGameEndDialog()} color="primary" autoFocus>
OK
</Button>
</DialogActions>
@ -457,6 +501,8 @@ function PvEDoudizhuDemoView() {
<div style={{ height: '100%' }}>
<Paper className={'doudizhu-gameboard-paper'} elevation={3}>
<DoudizhuGameBoard
showCardBack={true}
handleSelectRole={handleSelectRole}
isPassDisabled={isPassDisabled}
gamePlayable={true}
playerInfo={playerInfo}
@ -473,6 +519,7 @@ function PvEDoudizhuDemoView() {
gameStatus={gameStatus}
handleMainPlayerAct={handleMainPlayerAct}
/>
{/* )} */}
</Paper>
</div>
</Layout.Col>