changed agent leaderboard table columns; fixed bugs in leduc holdem game replay; load agents lists from backend api

This commit is contained in:
Songyi Huang 2020-09-04 23:08:02 -07:00
parent b7b722761e
commit 379edbb4f3
4 changed files with 58 additions and 26 deletions

View File

@ -20,3 +20,7 @@ code {
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
.el-message {
z-index: 9999;
}

View File

@ -9,6 +9,7 @@ import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore'; import ExpandMore from '@material-ui/icons/ExpandMore';
import {makeStyles} from "@material-ui/core/styles"; import {makeStyles} from "@material-ui/core/styles";
import qs from 'query-string'; import qs from 'query-string';
import ListSubheader from "@material-ui/core/ListSubheader";
const drawerWidth = 250; const drawerWidth = 250;
@ -75,13 +76,15 @@ function MenuBar (props) {
</List> </List>
}); });
const agentMenu = props.modelList.map(model => { const generateAgentMenu = (modelList) => {
return <List component="div" disablePadding key={"game-menu-"+model.model}> return modelList.map((model) => {
<ListItem button className={classes.nested} onClick={() => {handleAgentJump(model.model)}}> return <List component="div" disablePadding key={"game-menu-"+model}>
<ListItemText primary={model.dispName} className={`${classes.menuLayer2} ${(type === 'agent' && name === model.model) ? classes.active : classes.inactive}`} /> <ListItem button className={classes.nested} onClick={() => {handleAgentJump(model)}}>
<ListItemText primary={model} className={`${classes.menuLayer2} ${(type === 'agent' && name === model) ? classes.active : classes.inactive}`} />
</ListItem> </ListItem>
</List> </List>
}); })
};
return ( return (
<Drawer <Drawer
@ -108,7 +111,15 @@ function MenuBar (props) {
{open.game ? <ExpandLess /> : <ExpandMore />} {open.game ? <ExpandLess /> : <ExpandMore />}
</ListItem> </ListItem>
<Collapse in={open.game} timeout="auto" unmountOnExit> <Collapse in={open.game} timeout="auto" unmountOnExit>
{agentMenu} {Object.keys(props.modelList).map(gameName => {
return (
<div key={`agentMenu-sublist-${gameName}`}>
<ListSubheader className={classes.nested}>{gameName}</ListSubheader>
{generateAgentMenu(props.modelList[gameName])}
</div>
)
})}
</Collapse> </Collapse>
</List> </List>
</Drawer> </Drawer>

View File

@ -7,7 +7,8 @@ import Navbar from '../../components/Navbar';
import {deepCopy} from "../../utils"; import {deepCopy} from "../../utils";
import { apiUrl } from "../../utils/config"; import { apiUrl } from "../../utils/config";
import { Layout, Message, Loading } from 'element-react'; import { Layout, Loading } from 'element-react';
import { Message } 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';
@ -87,7 +88,7 @@ class LeducHoldemGameView extends React.Component {
case "check": case "check":
break; break;
case "raise": case "raise":
gameInfo.pot[gameInfo.currentPlayer] += (gameInfo.round+1) * 2; gameInfo.pot[gameInfo.currentPlayer] = (gameInfo.pot[(gameInfo.currentPlayer+2-1)%2] + (gameInfo.round+1) * 2);
break; break;
case "call": case "call":
// the upstream player must have bet more // the upstream player must have bet more
@ -197,12 +198,22 @@ class LeducHoldemGameView extends React.Component {
startReplay() { startReplay() {
const { name, agent0, agent1, index } = qs.parse(window.location.search); const { name, agent0, agent1, index } = qs.parse(window.location.search);
const requestUrl = `${apiUrl}/tournament/replay?name=${name}&agent0=${agent0}&agent1=${agent1}&index=${index}`; const requestUrl = `${apiUrl}/tournament/replay?name=${name}&agent0=${agent0}&agent1=${agent1}&index=${index}`;
// start full screen loading // start full screen loading
this.setState({fullScreenLoading: true}); this.setState({fullScreenLoading: true});
axios.get(requestUrl) axios.get(requestUrl)
.then(res => { .then(res => {
res = res.data; res = res.data;
if (res.moveHistory.length === 0) {
Message({
message: "Empty move history",
type: "error",
showClose: true,
duration: 0
});
this.setState({fullScreenLoading: false});
return false;
}
// init replay info // init replay info
this.moveHistory = res.moveHistory; this.moveHistory = res.moveHistory;
this.moveHistoryTotalLength = this.moveHistory.reduce((count, round) => count + round.length, 0) - 1; this.moveHistoryTotalLength = this.moveHistory.reduce((count, round) => count + round.length, 0) - 1;
@ -211,6 +222,8 @@ class LeducHoldemGameView extends React.Component {
gameInfo.playerInfo = res.playerInfo; gameInfo.playerInfo = res.playerInfo;
gameInfo.hands = res.initHands; gameInfo.hands = res.initHands;
gameInfo.currentPlayer = res.moveHistory[0][0].playerIdx; gameInfo.currentPlayer = res.moveHistory[0][0].playerIdx;
// the other player is big blind, should have 2 unit in pot
gameInfo.pot[(res.moveHistory[0][0].playerIdx + 1) % 2] = 2;
gameInfo.publicCard = res.publicCard; gameInfo.publicCard = res.publicCard;
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);
@ -290,9 +303,12 @@ class LeducHoldemGameView extends React.Component {
{currentMove !== null ? {currentMove !== null ?
(<div className={"non-card"}> (<div className={"non-card"}>
{ {
currentMove.probabilities[idx].probability < 0 ? currentMove.probabilities[idx].probability === -1 ?
<span>Illegal</span> <span>Illegal</span>
: :
currentMove.probabilities[idx].probability === -2 ?
<span>Rule Based</span>
:
<span>{`Probability ${(currentMove.probabilities[idx].probability * 100).toFixed(2)}%`}</span> <span>{`Probability ${(currentMove.probabilities[idx].probability * 100).toFixed(2)}%`}</span>
} }
</div>) : ""} </div>) : ""}

View File

@ -25,13 +25,8 @@ const gameList = [
{game: 'doudizhu', dispName: 'Dou Dizhu'}, {game: 'doudizhu', dispName: 'Dou Dizhu'},
]; ];
const modelList = [ // {'doudizhu': ['agent1', 'agent2', 'agent3']}
{model: 'leduc-holdem-random', dispName: 'Leduc Hold\'em Random'}, const modelList = {};
{model: 'leduc-holdem-cfr', dispName: 'Leduc Hold\'em CFR'},
{model: 'leduc-holdem-rule-v1', dispName: 'Leduc Hold\'em Rule V1'},
{model: 'doudizhu-random', dispName: 'Dou Dizhu Random'},
{model: 'doudizhu-rule-v1', dispName: 'Dou Dizhu Rule V1'}
];
function LeaderBoard () { function LeaderBoard () {
const initRowsPerPage = 10; const initRowsPerPage = 10;
@ -40,6 +35,17 @@ function LeaderBoard () {
const [rowsTotal, setRowsTotal] = React.useState(0); const [rowsTotal, setRowsTotal] = React.useState(0);
const [rows, setRows] = React.useState([]); const [rows, setRows] = React.useState([]);
// passing an empty array as second argument triggers the callback in useEffect
// only after the initial render thus replicating `componentDidMount` lifecycle behaviour
useEffect(() => {
gameList.forEach((game) => {
axios.get(`${apiUrl}/tournament/list_baseline_agents?game=${game.game}`)
.then(res => {
modelList[game.game] = res.data.data;
});
});
}, []);
const { type, name } = qs.parse(window.location.search); const { type, name } = qs.parse(window.location.search);
let requestUrl = `${apiUrl}/tournament/`; let requestUrl = `${apiUrl}/tournament/`;
if (type === 'game') { if (type === 'game') {
@ -112,8 +118,7 @@ function createLeaderBoardData(resData, rank) {
const agentHeadCells = [ const agentHeadCells = [
{ id: 'id', numeric: false, disablePadding: false, label: 'ID' }, { id: 'id', numeric: false, disablePadding: false, label: 'ID' },
{ id: 'game', numeric: false, disablePadding: false, label: 'Game' }, { id: 'game', numeric: false, disablePadding: false, label: 'Game' },
{ id: 'agent0', numeric: false, disablePadding: false, label: 'Agent 0' }, { id: 'agent1', numeric: false, disablePadding: false, label: 'Opponent Agent' },
{ id: 'agent1', numeric: false, disablePadding: false, label: 'Agent 1' },
{ id: 'win', numeric: false, disablePadding: false, label: 'Result' }, { id: 'win', numeric: false, disablePadding: false, label: 'Result' },
{ id: 'payoff', numeric: false, disablePadding: false, label: 'Payoff' }, { id: 'payoff', numeric: false, disablePadding: false, label: 'Payoff' },
{ id: 'replay', numeric: false, disablePadding: false, label: 'Replay' } { id: 'replay', numeric: false, disablePadding: false, label: 'Replay' }
@ -188,10 +193,7 @@ const EnhancedTableToolbar = (props) => {
}); });
name = foundItem.dispName; name = foundItem.dispName;
} else if (routeInfo.type === 'agent') { } else if (routeInfo.type === 'agent') {
const foundItem = modelList.find(model => { name = routeInfo.name;
return model.model === routeInfo.name;
});
name = foundItem.dispName;
} }
return ( return (
@ -308,7 +310,6 @@ const AgentTableContent = (props) => {
{row.id} {row.id}
</TableCell> </TableCell>
<TableCell>{row.game}</TableCell> <TableCell>{row.game}</TableCell>
<TableCell>{row.agent0}</TableCell>
<TableCell>{row.agent1}</TableCell> <TableCell>{row.agent1}</TableCell>
<TableCell>{row.win}</TableCell> <TableCell>{row.win}</TableCell>
<TableCell>{row.payoff}</TableCell> <TableCell>{row.payoff}</TableCell>