fix bugs
This commit is contained in:
parent
9da0633270
commit
a4d95e943a
|
@ -7,7 +7,7 @@ import '../../assets/doudizhu.scss';
|
||||||
import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png';
|
import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png';
|
||||||
import Peasant_wName from '../../assets/images/Portrait/Peasant_wName.png';
|
import Peasant_wName from '../../assets/images/Portrait/Peasant_wName.png';
|
||||||
import PlaceHolderPlayer from '../../assets/images/Portrait/Player.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 {
|
class DoudizhuGameBoard extends React.Component {
|
||||||
computePlayerPortrait(playerId, playerIdx) {
|
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') {
|
if (cards === 'pass') {
|
||||||
return (
|
return (
|
||||||
<div className="non-card">
|
<div className="non-card">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import Slider from '@material-ui/core/Slider';
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||||
|
@ -28,14 +29,13 @@ import {
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { douzeroDemoUrl } from '../../utils/config';
|
import { douzeroDemoUrl } from '../../utils/config';
|
||||||
|
|
||||||
const shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
let shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
||||||
|
|
||||||
let threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
|
let threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
|
||||||
const originalThreeLandlordCards = threeLandlordCards.slice();
|
let originalThreeLandlordCards = threeLandlordCards.slice();
|
||||||
|
|
||||||
const initConsiderationTime = 30000;
|
const initConsiderationTime = 30000;
|
||||||
const considerationTimeDeduction = 1000;
|
const considerationTimeDeduction = 1000;
|
||||||
const apiPlayDelay = 3000;
|
|
||||||
const mainPlayerId = 0; // index of main player (for the sake of simplify code logic)
|
const mainPlayerId = 0; // index of main player (for the sake of simplify code logic)
|
||||||
let playerInfo = [];
|
let playerInfo = [];
|
||||||
|
|
||||||
|
@ -60,8 +60,10 @@ let playedCardsLandlordDown = [];
|
||||||
let playedCardsLandlordUp = [];
|
let playedCardsLandlordUp = [];
|
||||||
let legalActions = { turn: -1, actions: [] };
|
let legalActions = { turn: -1, actions: [] };
|
||||||
let gameEndDialogText = '';
|
let gameEndDialogText = '';
|
||||||
|
let syncGameStatus = 'ready';
|
||||||
|
|
||||||
function PvEDoudizhuDemoView() {
|
function PvEDoudizhuDemoView() {
|
||||||
|
const [apiPlayDelay, setApiPlayDelay] = useState(3000);
|
||||||
const [isGameEndDialogOpen, setIsGameEndDialogOpen] = useState(false);
|
const [isGameEndDialogOpen, setIsGameEndDialogOpen] = useState(false);
|
||||||
const [considerationTime, setConsiderationTime] = useState(initConsiderationTime);
|
const [considerationTime, setConsiderationTime] = useState(initConsiderationTime);
|
||||||
const [toggleFade, setToggleFade] = useState('');
|
const [toggleFade, setToggleFade] = useState('');
|
||||||
|
@ -203,6 +205,10 @@ function PvEDoudizhuDemoView() {
|
||||||
newGameState.hands[gameState.currentPlayer] = newHand;
|
newGameState.hands[gameState.currentPlayer] = newHand;
|
||||||
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3;
|
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3;
|
||||||
newGameState.turn++;
|
newGameState.turn++;
|
||||||
|
if (newHand.length === 0) {
|
||||||
|
setGameStatus('over');
|
||||||
|
syncGameStatus = 'over';
|
||||||
|
}
|
||||||
setGameState(newGameState);
|
setGameState(newGameState);
|
||||||
setToggleFade('fade-in');
|
setToggleFade('fade-in');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -215,7 +221,6 @@ function PvEDoudizhuDemoView() {
|
||||||
|
|
||||||
if (newHand.length === 0) {
|
if (newHand.length === 0) {
|
||||||
const winner = playerInfo[gameState.currentPlayer];
|
const winner = playerInfo[gameState.currentPlayer];
|
||||||
setGameStatus('over');
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
gameEndDialogText = winner.role + ' wins!';
|
gameEndDialogText = winner.role + ' wins!';
|
||||||
setIsGameEndDialogOpen(true);
|
setIsGameEndDialogOpen(true);
|
||||||
|
@ -331,9 +336,12 @@ function PvEDoudizhuDemoView() {
|
||||||
} else {
|
} else {
|
||||||
let bestAction = '';
|
let bestAction = '';
|
||||||
if (data.result && Object.keys(data.result).length > 0) {
|
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({
|
setPredictionRes({
|
||||||
prediction: Object.entries(data.result).sort((a, b) => {
|
prediction: sortedResult.map((result) => {
|
||||||
return Number(b[1]) - Number(a[1]);
|
return [result[0], data.win_rates[result[0]]];
|
||||||
}),
|
}),
|
||||||
hands: gameState.hands[gameState.currentPlayer].slice(),
|
hands: gameState.hands[gameState.currentPlayer].slice(),
|
||||||
});
|
});
|
||||||
|
@ -360,11 +368,8 @@ function PvEDoudizhuDemoView() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleHideRivalHand = () => {
|
|
||||||
setHideRivalHand(!hideRivalHand);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleHidePredictionArea = () => {
|
const toggleHidePredictionArea = () => {
|
||||||
|
setHideRivalHand(!hideRivalHand);
|
||||||
setHidePredictionArea(!hidePredictionArea);
|
setHidePredictionArea(!hidePredictionArea);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -423,6 +428,7 @@ function PvEDoudizhuDemoView() {
|
||||||
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
|
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
|
||||||
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
||||||
setGameStatus('playing');
|
setGameStatus('playing');
|
||||||
|
syncGameStatus = 'playing';
|
||||||
};
|
};
|
||||||
|
|
||||||
const gameStateTimer = () => {
|
const gameStateTimer = () => {
|
||||||
|
@ -441,14 +447,53 @@ function PvEDoudizhuDemoView() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseGameEndDialog = () => {
|
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);
|
setIsGameEndDialogOpen(false);
|
||||||
// todo: proceed next game option
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const startGame = async () => {
|
const startGame = async () => {
|
||||||
// start game
|
// start game
|
||||||
setGameStatus('playing');
|
setGameStatus('playing');
|
||||||
|
syncGameStatus = 'playing';
|
||||||
const newGameState = deepCopy(gameState);
|
const newGameState = deepCopy(gameState);
|
||||||
// find landord to be the first player
|
// find landord to be the first player
|
||||||
newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index;
|
newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index;
|
||||||
|
@ -475,11 +520,11 @@ function PvEDoudizhuDemoView() {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gameStatus === 'playing') gameStateTimer();
|
if (syncGameStatus === 'playing') gameStateTimer();
|
||||||
}, [considerationTime]);
|
}, [considerationTime]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gameState.currentPlayer !== null && gameStatus === 'playing') {
|
if (gameState.currentPlayer !== null && syncGameStatus === 'playing') {
|
||||||
// if current player is not user, request for API player
|
// if current player is not user, request for API player
|
||||||
if (gameState.currentPlayer !== mainPlayerId) {
|
if (gameState.currentPlayer !== mainPlayerId) {
|
||||||
requestApiPlay();
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -625,7 +734,7 @@ function PvEDoudizhuDemoView() {
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleCloseGameEndDialog()} color="primary" autoFocus>
|
<Button onClick={() => handleCloseGameEndDialog()} color="primary" autoFocus>
|
||||||
OK
|
Play Again
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -668,6 +777,7 @@ function PvEDoudizhuDemoView() {
|
||||||
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={'probability-cards-' + rankText + '-' + suitText}
|
||||||
style={{ fontSize: '1.2em' }}
|
style={{ fontSize: '1.2em' }}
|
||||||
className={`card ${rankClass} full-content ${suitClass}`}
|
className={`card ${rankClass} full-content ${suitClass}`}
|
||||||
>
|
>
|
||||||
|
@ -692,11 +802,10 @@ function PvEDoudizhuDemoView() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
<div className={'probability-player'} style={{ height: '19px' }}>
|
<div className={'probability-player'} style={{ height: '19px', textAlign: 'center' }}>
|
||||||
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
||||||
<span>
|
<span>
|
||||||
Current Player: {gameState.currentPlayer} |{' '}
|
{['Landlord', 'Landlord Down', 'Landlord Up'][playerInfo[gameState.currentPlayer].douzeroPlayerPosition]}
|
||||||
{playerInfo[gameState.currentPlayer].role}
|
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span>Waiting...</span>
|
<span>Waiting...</span>
|
||||||
|
@ -716,19 +825,6 @@ function PvEDoudizhuDemoView() {
|
||||||
<Layout.Row style={{ height: '51px' }}>
|
<Layout.Row style={{ height: '51px' }}>
|
||||||
<Layout.Col span="6" style={{ height: '51px', lineHeight: '48px' }}>
|
<Layout.Col span="6" style={{ height: '51px', lineHeight: '48px' }}>
|
||||||
<FormGroup style={{ height: '100%' }}>
|
<FormGroup style={{ height: '100%' }}>
|
||||||
<FormControlLabel
|
|
||||||
style={{ textAlign: 'center', height: '100%', display: 'inline-block' }}
|
|
||||||
className="switch-control"
|
|
||||||
control={<Switch checked={!hideRivalHand} onChange={toggleHideRivalHand} />}
|
|
||||||
label="Show Rival Cards"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Layout.Col>
|
|
||||||
<Layout.Col span="1" style={{ height: '100%', width: '1px' }}>
|
|
||||||
<Divider orientation="vertical" />
|
|
||||||
</Layout.Col>
|
|
||||||
<Layout.Col span="6" style={{ height: '51px', lineHeight: '48px' }}>
|
|
||||||
<FormGroup sty282718 le={{ height: '100%' }}>
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
style={{ textAlign: 'center', height: '100%', display: 'inline-block' }}
|
style={{ textAlign: 'center', height: '100%', display: 'inline-block' }}
|
||||||
className="switch-control"
|
className="switch-control"
|
||||||
|
@ -743,7 +839,7 @@ function PvEDoudizhuDemoView() {
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col
|
<Layout.Col
|
||||||
span="6"
|
span="3"
|
||||||
style={{ height: '51px', lineHeight: '51px', marginLeft: '-1px', marginRight: '-1px' }}
|
style={{ height: '51px', lineHeight: '51px', marginLeft: '-1px', marginRight: '-1px' }}
|
||||||
>
|
>
|
||||||
<div style={{ textAlign: 'center' }}>{`Turn ${gameState.turn}`}</div>
|
<div style={{ textAlign: 'center' }}>{`Turn ${gameState.turn}`}</div>
|
||||||
|
@ -751,11 +847,24 @@ function PvEDoudizhuDemoView() {
|
||||||
<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
|
<Layout.Col span="15">
|
||||||
span="6"
|
<div>
|
||||||
style={{ height: '51px', lineHeight: '51px', marginLeft: '-1px', marginRight: '-1px' }}
|
<label className={"form-label-left"} style={{width: '140px', lineHeight: '28px', fontSize: '15px'}}>AI Player Delay</label>
|
||||||
>
|
<div style={{"marginLeft": "160px", "marginRight": "10px"}}>
|
||||||
<div style={{ textAlign: 'center' }}>{`Game Status: ${gameStatus}`}</div>
|
<Slider
|
||||||
|
value={gameSpeedMap.find(element => 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}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
</Layout.Row>
|
</Layout.Row>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
Loading…
Reference in New Issue