changed style for leduc

This commit is contained in:
lpan 2020-02-24 10:47:33 -08:00
parent 7e53368ead
commit bf3546922d
13 changed files with 248 additions and 138 deletions

View File

@ -8,7 +8,6 @@
margin-right: auto; margin-right: auto;
padding: 5px; padding: 5px;
display: flex; display: flex;
.stretch { .stretch {
flex: 1; flex: 1;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -1,5 +1,13 @@
@import "cards.css"; @import "cards.css";
.leduc-holdem-wrapper { .leduc-holdem-wrapper {
width: 100%;
height: 100%;
background-color: #C3CDFF;
// background-image: url("./images/table.png");
// background-repeat: no-repeat;
// background-size: 100% 70%;
// background-position: bottom;
position: relative;
.played-card-area { .played-card-area {
font-size: 12px; font-size: 12px;
@ -8,17 +16,41 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
.timer.fade-in {
visibility: hidden;
opacity: 0;
}
.timer.fade-out {
visibility: hidden;
transition: visibility 0.1s, opacity 0.05s;
opacity: 0;
}
.timer {
visibility: visible;
transition: visibility 0s, opacity 0.2s, transform 0.3s;
opacity: 1;
width: 51px;
height: 60px;
.timer-text {
color: #303133;
margin-top: 3px;
font-size: 23px;
font-weight: bold;
text-shadow: 0 2px 2px #909399;
line-height: 57px;
}
text-align: center;
background-image: url("./images/timer.png");
background-repeat: no-repeat;
background-size: 100% 100%;
}
.non-card { .non-card {
display: table; display: table;
width: 280px; width: 80px;
height: 100px; height: 40px;
span {
display: table-cell;
vertical-align: middle;
text-align: center;
font-size: 16px;
}
} }
} }
@ -37,14 +69,11 @@
font-size: 16px; font-size: 16px;
width: 130px; width: 130px;
height: 130px; height: 130px;
border-radius: 25px; div {
border: 2px solid #73AD21; text-align: center;
display: table; }
span { span {
white-space: pre; white-space: pre;
display: table-cell;
vertical-align: middle;
text-align: center; text-align: center;
} }
} }
@ -96,16 +125,17 @@
margin-top: -75px; margin-top: -75px;
width: 130px; width: 130px;
height: 150px; height: 150px;
border-radius: 25px;
border: 2px solid blue;
display: table; display: table;
.info-area { .info-area {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
text-align: center; text-align: center;
font-size: 20px;
font-weight: bold;
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.87);
color: #F2F6FC;
} }
.playingCards { .playingCards {
transform: translate(3px, 10px); transform: translate(3px, 10px);
} }

View File

@ -2,17 +2,65 @@ import React from 'react';
import { translateCardData, millisecond2Second } from '../../utils' import { translateCardData, millisecond2Second } from '../../utils'
import '../../assets/leducholdem.scss'; import '../../assets/leducholdem.scss';
import Player1 from '../../assets/images/Portrait/Player1.png'
import Player2 from '../../assets/images/Portrait/Player2.png'
import PlaceHolderPlayer from '../../assets/images/Portrait/Player.png';
import Chip from '@material-ui/core/Chip';
import Avatar from '@material-ui/core/Avatar';
class LeducHoldemGameBoard extends React.Component { class LeducHoldemGameBoard extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
computePlayerPortrait(playerId, playerIdx){
if(this.props.playerInfo.length > 0){
return this.props.playerInfo[playerIdx].id === 0 ?
<div>
<img src={Player1} alt={"Player 1"} height="70%" width="70%" />
<Chip
avatar={<Avatar>ID</Avatar>}
label={playerId}
clickable
color="primary"
/>
</div>
:
<div>
<img src={Player2} alt={"Player 2"} height="70%" width="70%" />
<Chip
avatar={<Avatar>ID</Avatar>}
label={playerId}
clickable
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"
/>
</div>
)
}
computeActionImage(action) {
if (action.length > 0) {
return <img src={require('../../assets/images/Actions/' + action + '.png')} alt={action} height="80%" width="100%" />
}
}
computeHand(card) { computeHand(card) {
const [rankClass, suitClass, rankText, suitText] = translateCardData(card); const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
return ( return (
<div className="playingCards faceImages"> <div className="playingCards faceImages unselectable">
<div className={`card ${rankClass} ${suitClass}`}> <div className={`card ${rankClass} full-content ${suitClass}`}>
<span className="rank">{rankText}</span> <span className="rank">{rankText}</span>
<span className="suit">{suitText}</span> <span className="suit">{suitText}</span>
</div> </div>
@ -22,9 +70,13 @@ class LeducHoldemGameBoard extends React.Component {
playerDecisionArea(playerIdx){ playerDecisionArea(playerIdx){
if(this.props.currentPlayer === playerIdx){ if(this.props.currentPlayer === playerIdx){
return <div className="non-card"><span>{`Consideration Time: ${millisecond2Second(this.props.considerationTime)}s`}</span></div> return (
<div className={"timer"}>
<div className="timer-text">{millisecond2Second(this.props.considerationTime)}</div>
</div>
)
}else{ }else{
return <div className="non-card"><span>{this.props.latestAction[playerIdx]}</span></div> return <div className="non-card">{this.computeActionImage(this.props.latestAction[playerIdx])}</div>
} }
} }
@ -39,7 +91,7 @@ class LeducHoldemGameBoard extends React.Component {
const [rankClass, suitClass, rankText, suitText] = translateCardData(this.props.publicCard); const [rankClass, suitClass, rankText, suitText] = translateCardData(this.props.publicCard);
return ( return (
<div className="playingCards faceImages"> <div className="playingCards faceImages">
<div className={`card ${rankClass} ${suitClass}`}> <div className={`card ${rankClass} full-content ${suitClass}`}>
<span className="rank">{rankText}</span> <span className="rank">{rankText}</span>
<span className="suit">{suitText}</span> <span className="suit">{suitText}</span>
</div> </div>
@ -65,14 +117,15 @@ class LeducHoldemGameBoard extends React.Component {
topId = found.id; topId = found.id;
} }
return ( return (
<div className="leduc-holdem-wrapper" style={{width: "100%", height: "100%", backgroundColor: "#ffcc99", position: "relative"}}> <div className="leduc-holdem-wrapper">
<div id={"bottom-player"}> <div id={"bottom-player"}>
<div className="played-card-area"> <div className="played-card-area">
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ""} {bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ""}
</div> </div>
<div className="player-main-area"> <div className="player-main-area">
<div className="player-info"> <div className="player-info">
<span>{`Player Id: ${bottomId}\nBet: ${this.props.pot[bottomIdx]}`}</span> {this.computePlayerPortrait(bottomId, bottomIdx)}
<span>{`Bet: ${this.props.pot[bottomIdx]}`}</span>
</div> </div>
{bottomIdx >= 0 ? <div className="player-hand">{this.computeHand(this.props.hands[bottomIdx])}</div> : <div className="player-hand-placeholder"><span>Waiting...</span></div>} {bottomIdx >= 0 ? <div className="player-hand">{this.computeHand(this.props.hands[bottomIdx])}</div> : <div className="player-hand-placeholder"><span>Waiting...</span></div>}
</div> </div>
@ -80,7 +133,8 @@ class LeducHoldemGameBoard extends React.Component {
<div id={"top-player"}> <div id={"top-player"}>
<div className="player-main-area"> <div className="player-main-area">
<div className="player-info"> <div className="player-info">
<span>{`Player Id: ${topId}\nBet: ${this.props.pot[topIdx]}`}</span> {this.computePlayerPortrait(topId, topIdx)}
<span>{`Bet: ${this.props.pot[topIdx]}`}</span>
</div> </div>
{topIdx >= 0 ? <div className="player-hand">{this.computeHand(this.props.hands[topIdx])}</div> : <div className="player-hand-placeholder"><span>Waiting...</span></div>} {topIdx >= 0 ? <div className="player-hand">{this.computeHand(this.props.hands[topIdx])}</div> : <div className="player-hand-placeholder"><span>Waiting...</span></div>}
</div> </div>

View File

@ -2,9 +2,10 @@ import React from 'react';
import axios from 'axios'; import axios from 'axios';
import '../assets/gameview.scss'; import '../assets/gameview.scss';
import {LeducHoldemGameBoard} from '../components/GameBoard'; import {LeducHoldemGameBoard} from '../components/GameBoard';
import Navbar from '../components/Navbar';
import {deepCopy} from "../utils"; import {deepCopy} from "../utils";
import { Layout } from 'element-react'; import { Layout, Message, Loading } from 'element-react';
import Slider from '@material-ui/core/Slider'; import Slider from '@material-ui/core/Slider';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
@ -46,12 +47,12 @@ class LeducHoldemGameView extends React.Component {
this.state = { this.state = {
gameInfo: this.initGameState, gameInfo: this.initGameState,
gameStateLoop: null, gameStateLoop: null,
gameSpeed: 0 gameSpeed: 0,
fullScreenLoading: false
} }
} }
generateNewState(){ generateNewState(){
// console.log(this.state.gameInfo.latestAction);
let gameInfo = deepCopy(this.state.gameInfo); let gameInfo = deepCopy(this.state.gameInfo);
const turn = this.state.gameInfo.turn; const turn = this.state.gameInfo.turn;
if(turn >= this.moveHistory[this.state.gameInfo.round].length){ if(turn >= this.moveHistory[this.state.gameInfo.round].length){
@ -85,7 +86,11 @@ class LeducHoldemGameView extends React.Component {
if(gameInfo.pot[(gameInfo.currentPlayer+2-1)%2] > gameInfo.pot[gameInfo.currentPlayer]){ if(gameInfo.pot[(gameInfo.currentPlayer+2-1)%2] > gameInfo.pot[gameInfo.currentPlayer]){
gameInfo.pot[gameInfo.currentPlayer] = gameInfo.pot[(gameInfo.currentPlayer+2-1)%2]; gameInfo.pot[gameInfo.currentPlayer] = gameInfo.pot[(gameInfo.currentPlayer+2-1)%2];
}else{ }else{
console.log("Current player choose call but has bet more or equal to the upstream player"); Message({
message: "Current player choose call but has bet more or equal to the upstream player",
type: "error",
showClose: true
});
} }
break; break;
case "Fold": case "Fold":
@ -101,7 +106,11 @@ class LeducHoldemGameView extends React.Component {
}, 200); }, 200);
return gameInfo; return gameInfo;
default: default:
console.log("Error in player's latest action"); Message({
message: "Error in player's latest action",
type: "error",
showClose: true
});
} }
gameInfo.turn++; gameInfo.turn++;
if(gameInfo.round !== 0 && gameInfo.turn === this.moveHistory[gameInfo.round].length){ if(gameInfo.round !== 0 && gameInfo.turn === this.moveHistory[gameInfo.round].length){
@ -119,14 +128,22 @@ class LeducHoldemGameView extends React.Component {
gameInfo.gameStatus = "playing"; gameInfo.gameStatus = "playing";
this.setState({ gameInfo: gameInfo }); this.setState({ gameInfo: gameInfo });
}else{ }else{
console.log("Mismatch in current player & move history"); Message({
message: "Mismatch in current player & move history",
type: "error",
showClose: true
});
} }
} }
// if current state is new to game state history, push it to the game state history array // if current state is new to game state history, push it to the game state history array
if(gameInfo.turn === this.gameStateHistory[gameInfo.round].length){ if(gameInfo.turn === this.gameStateHistory[gameInfo.round].length){
this.gameStateHistory[gameInfo.round].push(gameInfo); this.gameStateHistory[gameInfo.round].push(gameInfo);
}else{ }else{
console.log("inconsistent game state history length and turn number"); Message({
message: "Inconsistent game state history length and turn number",
type: "error",
showClose: true
});
} }
return gameInfo; return gameInfo;
} }
@ -172,6 +189,8 @@ class LeducHoldemGameView extends React.Component {
// for test use // for test use
const replayId = 0; const replayId = 0;
// start full screen loading
this.setState({fullScreenLoading: true});
axios.get(`${this.apiUrl}/replay/leduc_holdem/${replayId}`) axios.get(`${this.apiUrl}/replay/leduc_holdem/${replayId}`)
.then(res => { .then(res => {
res = res.data; res = res.data;
@ -187,7 +206,7 @@ class LeducHoldemGameView extends React.Component {
if(this.gameStateHistory.length !== 0 && this.gameStateHistory[0].length === 0){ if(this.gameStateHistory.length !== 0 && this.gameStateHistory[0].length === 0){
this.gameStateHistory[gameInfo.round].push(gameInfo); this.gameStateHistory[gameInfo.round].push(gameInfo);
} }
this.setState({gameInfo: gameInfo}, ()=>{ this.setState({gameInfo: gameInfo, fullScreenLoading: false}, ()=>{
if(this.gameStateTimeout){ if(this.gameStateTimeout){
window.clearTimeout(this.gameStateTimeout); window.clearTimeout(this.gameStateTimeout);
this.gameStateTimeout = null; this.gameStateTimeout = null;
@ -242,11 +261,14 @@ class LeducHoldemGameView extends React.Component {
currentMove = this.moveHistory[this.state.gameInfo.round][this.state.gameInfo.turn]; currentMove = this.moveHistory[this.state.gameInfo.round][this.state.gameInfo.turn];
} }
let style = {}; let style = {};
style["backgroundColor"] = currentMove !== null ? `rgba(189,183,107,${currentMove.probabilities[idx].probability})` : "#bdbdbd"; style["backgroundColor"] = currentMove !== null ? `rgba(130, 151, 255, ${currentMove.probabilities[idx].probability})` : "#bdbdbd";
return ( return (
<div className={"playing"} style={style}> <div className={"playing"} style={style}>
<div className="probability-move"> <div className="probability-move">
{currentMove !== null ? currentMove.probabilities[idx].move : <NotInterestedIcon fontSize="large" />} {currentMove !== null ?
<img src={require('../assets/images/Actions/' + currentMove.probabilities[idx].move + '.png')} alt={currentMove.probabilities[idx].move} height="30%" width="30%" />
:
<NotInterestedIcon fontSize="large" />}
</div> </div>
{currentMove !== null ? {currentMove !== null ?
(<div className={"non-card"}> (<div className={"non-card"}>
@ -316,111 +338,116 @@ class LeducHoldemGameView extends React.Component {
]; ];
return ( return (
<div className={"leduc-view-container"}> <div>
<Layout.Row style={{"height": "540px"}}> <Navbar gameName="Leduc Hold'em" />
<Layout.Col style={{"height": "100%"}} span="17"> <div className={"leduc-view-container"}>
<div style={{"height": "100%"}}> <Layout.Row style={{"height": "540px"}}>
<Paper className={"leduc-gameboard-paper"} elevation={3}> <Layout.Col style={{"height": "100%"}} span="17">
<LeducHoldemGameBoard <div style={{"height": "100%"}}>
playerInfo={this.state.gameInfo.playerInfo} <Paper className={"leduc-gameboard-paper"} elevation={3}>
hands={this.state.gameInfo.hands} <LeducHoldemGameBoard
latestAction={this.state.gameInfo.latestAction} playerInfo={this.state.gameInfo.playerInfo}
mainPlayerId={this.state.gameInfo.mainViewerId} hands={this.state.gameInfo.hands}
currentPlayer={this.state.gameInfo.currentPlayer} latestAction={this.state.gameInfo.latestAction}
considerationTime={this.state.gameInfo.considerationTime} mainPlayerId={this.state.gameInfo.mainViewerId}
round={this.state.gameInfo.round} currentPlayer={this.state.gameInfo.currentPlayer}
turn={this.state.gameInfo.turn} considerationTime={this.state.gameInfo.considerationTime}
pot={this.state.gameInfo.pot} round={this.state.gameInfo.round}
publicCard={this.state.gameInfo.publicCard} turn={this.state.gameInfo.turn}
/> pot={this.state.gameInfo.pot}
</Paper> publicCard={this.state.gameInfo.publicCard}
</div> />
</Layout.Col> </Paper>
<Layout.Col span="7" style={{"height": "100%"}}>
<Paper className={"leduc-probability-paper"} elevation={3}>
<div className={"probability-player"}>
{
this.state.gameInfo.playerInfo.length > 0 ?
<span>Current Player: {this.state.gameInfo.currentPlayer}</span>
:
<span>Waiting...</span>
}
</div> </div>
<Divider /> </Layout.Col>
<div className={"probability-table"}> <Layout.Col span="7" style={{"height": "100%"}}>
<div className={"probability-item"}> <Paper className={"leduc-probability-paper"} elevation={3}>
{this.computeProbabilityItem(0)} <div className={"probability-player"}>
{
this.state.gameInfo.playerInfo.length > 0 ?
<span>Current Player: {this.state.gameInfo.currentPlayer}</span>
:
<span>Waiting...</span>
}
</div> </div>
<div className={"probability-item"}> <Divider />
{this.computeProbabilityItem(1)} <div className={"probability-table"}>
</div> <div className={"probability-item"}>
<div className={"probability-item"}> {this.computeProbabilityItem(0)}
{this.computeProbabilityItem(2)} </div>
</div> <div className={"probability-item"}>
<div className={"probability-item"}> {this.computeProbabilityItem(1)}
{this.computeProbabilityItem(3)} </div>
</div> <div className={"probability-item"}>
</div> {this.computeProbabilityItem(2)}
</Paper> </div>
</Layout.Col> <div className={"probability-item"}>
</Layout.Row> {this.computeProbabilityItem(3)}
<div className="progress-bar">
<LinearProgress variant="determinate" value={this.state.gameInfo.completedPercent} />
</div>
<div className="game-controller">
<Paper className={"game-controller-paper"} elevation={3}>
<Layout.Row style={{"height": "51px"}}>
<Layout.Col span="7" style={{"height": "51px", "lineHeight": "48px"}}>
<div>
<Button
variant="contained"
color="primary"
disabled={this.state.gameInfo.gameStatus !== "paused" || (this.state.gameInfo.round === 0 && this.state.gameInfo.turn === 0)}
onClick={()=>{this.go2PrevGameState()}}
>
<SkipPreviousIcon />
</Button>
{ this.gameStatusButton(this.state.gameInfo.gameStatus) }
<Button
variant="contained"
color="primary"
disabled={this.state.gameInfo.gameStatus !== "paused"}
onClick={()=>{this.go2NextGameState()}}
>
<SkipNextIcon />
</Button>
</div>
</Layout.Col>
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
<Divider orientation="vertical" />
</Layout.Col>
<Layout.Col span="3" style={{"height": "51px", "lineHeight": "51px", "marginLeft": "-1px", "marginRight": "-1px"}}>
<div style={{"textAlign": "center"}}>{`Turn: ${this.state.gameInfo.turn}`}</div>
</Layout.Col>
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
<Divider orientation="vertical" />
</Layout.Col>
<Layout.Col span="14">
<div>
<label className={"form-label-left"}>Game Speed</label>
<div style={{"marginLeft": "100px", "marginRight": "10px"}}>
<Slider
value={this.state.gameSpeed}
getAriaValueText={sliderValueText}
onChange={(e, newVal)=>{this.changeGameSpeed(newVal)}}
aria-labelledby="discrete-slider-custom"
step={1}
min={-3}
max={3}
track={false}
valueLabelDisplay="off"
marks={gameSpeedMarks}
/>
</div> </div>
</div> </div>
</Layout.Col> </Paper>
</Layout.Row> </Layout.Col>
</Paper> </Layout.Row>
<div className="progress-bar">
<LinearProgress variant="determinate" value={this.state.gameInfo.completedPercent} />
</div>
<Loading loading={this.state.fullScreenLoading}>
<div className="game-controller">
<Paper className={"game-controller-paper"} elevation={3}>
<Layout.Row style={{"height": "51px"}}>
<Layout.Col span="7" style={{"height": "51px", "lineHeight": "48px"}}>
<div>
<Button
variant="contained"
color="primary"
disabled={this.state.gameInfo.gameStatus !== "paused" || (this.state.gameInfo.round === 0 && this.state.gameInfo.turn === 0)}
onClick={()=>{this.go2PrevGameState()}}
>
<SkipPreviousIcon />
</Button>
{ this.gameStatusButton(this.state.gameInfo.gameStatus) }
<Button
variant="contained"
color="primary"
disabled={this.state.gameInfo.gameStatus !== "paused"}
onClick={()=>{this.go2NextGameState()}}
>
<SkipNextIcon />
</Button>
</div>
</Layout.Col>
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
<Divider orientation="vertical" />
</Layout.Col>
<Layout.Col span="3" style={{"height": "51px", "lineHeight": "51px", "marginLeft": "-1px", "marginRight": "-1px"}}>
<div style={{"textAlign": "center"}}>{`Turn: ${this.state.gameInfo.turn}`}</div>
</Layout.Col>
<Layout.Col span="1" style={{"height": "100%", "width": "1px"}}>
<Divider orientation="vertical" />
</Layout.Col>
<Layout.Col span="14">
<div>
<label className={"form-label-left"}>Game Speed</label>
<div style={{"marginLeft": "100px", "marginRight": "10px"}}>
<Slider
value={this.state.gameSpeed}
getAriaValueText={sliderValueText}
onChange={(e, newVal)=>{this.changeGameSpeed(newVal)}}
aria-labelledby="discrete-slider-custom"
step={1}
min={-3}
max={3}
track={false}
valueLabelDisplay="off"
marks={gameSpeedMarks}
/>
</div>
</div>
</Layout.Col>
</Layout.Row>
</Paper>
</div>
</Loading>
</div> </div>
</div> </div>
); );