-
-
Showdown{this.props.gameName === '' ? '' : '/ ' + this.props.gameName}
-
-
{window.location.href = 'https://github.com/datamllab/rlcard'}}>
-
-
Github
{this.state.stars} stars
-
+ return (
+
+
+
+
Showdown{subtitle ? '/ ' + subtitle : ''}
+
+
{window.location.href = 'https://github.com/datamllab/rlcard'}}>
+
+
Github
{stars} stars
-
+
+
)
- }
+
}
export default Navbar;
diff --git a/src/index.js b/src/index.js
index 8f9c359..1d01335 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
+
import './assets/index.scss';
import App from './App';
diff --git a/src/utils/config.js b/src/utils/config.js
index 40d5cf3..117b76b 100644
--- a/src/utils/config.js
+++ b/src/utils/config.js
@@ -1,3 +1,4 @@
const apiUrl = 'http://127.0.0.1:8000';
+const douzeroDemoUrl = 'http://127.0.0.1:5000';
-export {apiUrl};
+export { apiUrl, douzeroDemoUrl };
diff --git a/src/utils/index.js b/src/utils/index.js
index 16ab065..8494cc4 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -1,66 +1,73 @@
-const suitMap = new Map(
- [["H", "hearts"], ["D", "diams"], ["S", "spades"], ["C", "clubs"]]
-);
+const suitMap = new Map([
+ ['H', 'hearts'],
+ ['D', 'diams'],
+ ['S', 'spades'],
+ ['C', 'clubs'],
+]);
-const suitMapSymbol = new Map(
- [["H", "\u2665"], ["D", "\u2666"], ["S", "\u2660"], ["C", "\u2663"]]
-);
+const suitMapSymbol = new Map([
+ ['H', '\u2665'],
+ ['D', '\u2666'],
+ ['S', '\u2660'],
+ ['C', '\u2663'],
+]);
-export function removeCards(cards, hands){ // remove cards from hands, return the remained hands
+export function removeCards(cards, hands) {
+ // remove cards from hands, return the remained hands
let remainedHands = deepCopy(hands);
// if the player's action is pass then return the copy of original hands
- if(cards === "pass"){
+ if (cards === 'pass') {
return remainedHands;
}
let misMatch = false;
- cards.forEach(card => {
- let foundIdx = remainedHands.findIndex(element => {return element === card;});
- if(foundIdx > -1){
+ cards.forEach((card) => {
+ let foundIdx = remainedHands.findIndex((element) => {
+ return element === card;
+ });
+ if (foundIdx > -1) {
remainedHands.splice(foundIdx, 1);
- }else {
+ } else {
misMatch = true;
}
});
- if(misMatch)
- return false;
- else
- return remainedHands;
+ if (misMatch) return false;
+ else return remainedHands;
}
-export function doubleRaf(callback){
+export function doubleRaf(callback) {
// secure all the animation got rendered before callback function gets executed
requestAnimationFrame(() => {
- requestAnimationFrame(callback)
- })
+ requestAnimationFrame(callback);
+ });
}
-export function deepCopy(toCopy){
+export function deepCopy(toCopy) {
return JSON.parse(JSON.stringify(toCopy));
}
export function translateCardData(card) {
let rankClass;
- let suitClass = "";
+ let suitClass = '';
let rankText;
- let suitText = "";
+ let suitText = '';
// translate rank
- if(card === "RJ"){
- rankClass = "big";
- rankText = "+";
- suitClass = "joker";
- suitText = "Joker";
- }else if(card === "BJ"){
- rankClass = "little";
- rankText = "-";
- suitClass = "joker";
- suitText = "Joker";
- }else{
- rankClass = card.charAt(1) === "T" ? `10` : card.charAt(1).toLowerCase();
+ if (card === 'RJ') {
+ rankClass = 'big';
+ rankText = '+';
+ suitClass = 'joker';
+ suitText = 'Joker';
+ } else if (card === 'BJ') {
+ rankClass = 'little';
+ rankText = '-';
+ suitClass = 'joker';
+ suitText = 'Joker';
+ } else {
+ rankClass = card.charAt(1) === 'T' ? `10` : card.charAt(1).toLowerCase();
rankClass = `rank-${rankClass}`;
- rankText = card.charAt(1) === "T" ? `10` : card.charAt(1);
+ rankText = card.charAt(1) === 'T' ? `10` : card.charAt(1);
}
// translate suitClass
- if(card !== "RJ" && card !== "BJ"){
+ if (card !== 'RJ' && card !== 'BJ') {
suitClass = suitMap.get(card.charAt(0));
suitText = suitMapSymbol.get(card.charAt(0));
}
@@ -68,14 +75,15 @@ export function translateCardData(card) {
return [rankClass, suitClass, rankText, suitText];
}
-export function millisecond2Second(t){
- return Math.ceil(t/1000);
+export function millisecond2Second(t) {
+ return Math.ceil(t / 1000);
}
export function debounce(func, wait, immediate) {
let timeout;
- return function() {
- const context = this, args = arguments;
+ return function () {
+ const context = this,
+ args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
@@ -88,7 +96,157 @@ export function debounce(func, wait, immediate) {
}
export function computeHandCardsWidth(num, emWidth) {
- if(num === 0)
- return 0;
- return (num-1)*1.1*emWidth + 4.3*emWidth*1.2 + 2;
+ if (num === 0) return 0;
+ return (num - 1) * 1.1 * emWidth + 4.3 * emWidth * 1.2 + 2;
+}
+
+export function card2SuiteAndRank(card) {
+ if (card === 'BJ') {
+ return { suite: null, rank: 'X' };
+ } else if (card === 'RJ') {
+ return { suite: null, rank: 'D' };
+ } else {
+ return { suite: card[0], rank: card[1] };
+ }
+}
+
+export const fullDoudizhuDeck = [
+ 'RJ',
+ 'BJ',
+ 'S2',
+ 'C2',
+ 'H2',
+ 'D2',
+ 'SA',
+ 'CA',
+ 'HA',
+ 'DA',
+ 'SK',
+ 'CK',
+ 'HK',
+ 'DK',
+ 'SQ',
+ 'CQ',
+ 'HQ',
+ 'DQ',
+ 'SJ',
+ 'CJ',
+ 'HJ',
+ 'DJ',
+ 'ST',
+ 'CT',
+ 'HT',
+ 'DT',
+ 'S9',
+ 'C9',
+ 'H9',
+ 'D9',
+ 'S8',
+ 'C8',
+ 'H8',
+ 'D8',
+ 'S7',
+ 'C7',
+ 'H7',
+ 'D7',
+ 'S6',
+ 'C6',
+ 'H6',
+ 'D6',
+ 'S5',
+ 'C5',
+ 'H5',
+ 'D5',
+ 'S4',
+ 'C4',
+ 'H4',
+ 'D4',
+ 'S3',
+ 'C3',
+ 'H3',
+ 'D3',
+];
+
+export const fullDoudizhuDeckIndex = {
+ RJ: 54,
+ BJ: 53,
+ S2: 52,
+ C2: 51,
+ H2: 50,
+ D2: 49,
+ SA: 48,
+ CA: 47,
+ HA: 46,
+ DA: 45,
+ SK: 44,
+ CK: 43,
+ HK: 42,
+ DK: 41,
+ SQ: 40,
+ CQ: 39,
+ HQ: 38,
+ DQ: 37,
+ SJ: 36,
+ CJ: 35,
+ HJ: 34,
+ DJ: 33,
+ ST: 32,
+ CT: 31,
+ HT: 30,
+ DT: 29,
+ S9: 28,
+ C9: 27,
+ H9: 26,
+ D9: 25,
+ S8: 24,
+ C8: 23,
+ H8: 22,
+ D8: 21,
+ S7: 20,
+ C7: 19,
+ H7: 18,
+ D7: 17,
+ S6: 16,
+ C6: 15,
+ H6: 14,
+ D6: 13,
+ S5: 12,
+ C5: 11,
+ H5: 10,
+ D5: 9,
+ S4: 8,
+ C4: 7,
+ H4: 6,
+ D4: 5,
+ S3: 4,
+ C3: 3,
+ H3: 2,
+ D3: 1,
+};
+
+export function sortDoudizhuCards(cards, ascending = false) {
+ const cardsCopy = cards.slice();
+ return cardsCopy.sort((a, b) => {
+ return ascending
+ ? fullDoudizhuDeckIndex[a] - fullDoudizhuDeckIndex[b]
+ : fullDoudizhuDeckIndex[b] - fullDoudizhuDeckIndex[a];
+ });
+}
+
+export function isDoudizhuBomb(cards) {
+ if (cards.length === 2) return (cards[0] === 'RJ' && cards[1] === 'BJ') || (cards[0] === 'BJ' && cards[1] === 'RJ');
+ if (cards.length === 4)
+ return cards[0][1] === cards[1][1] && cards[0][1] === cards[2][1] && cards[0][1] === cards[3][1];
+ return false;
+}
+
+export function shuffleArray(inputArray) {
+ let array = inputArray.slice();
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ const temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+ return array;
}
diff --git a/src/view/GameView/index.js b/src/view/GameView/index.js
deleted file mode 100644
index a64e709..0000000
--- a/src/view/GameView/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import DoudizhuGameView from "./DoudizhuGameView";
-import LeducHoldemGameView from "./LeducHoldemGameView";
-
-export {DoudizhuGameView, LeducHoldemGameView};
diff --git a/src/view/LeaderBoard.js b/src/view/LeaderBoard.js
index 5556f99..3caa184 100644
--- a/src/view/LeaderBoard.js
+++ b/src/view/LeaderBoard.js
@@ -62,7 +62,14 @@ function LeaderBoard () {
fetchModelData();
}, [reloadMenu]);
- const { type, name } = qs.parse(window.location.search);
+ let { type, name } = qs.parse(window.location.search);
+ // default value
+ if (!type) {
+ type = "game";
+ }
+ if (!name) {
+ name = "leduc-holdem";
+ }
let requestUrl = `${apiUrl}/tournament/`;
if (type === 'game') {
requestUrl += `query_agent_payoff?name=${name}&elements_every_page=${rowsPerPage}&page_index=${page}`
diff --git a/src/view/PvEView/PvEDoudizhuDemoView.js b/src/view/PvEView/PvEDoudizhuDemoView.js
new file mode 100644
index 0000000..f5d7b14
--- /dev/null
+++ b/src/view/PvEView/PvEDoudizhuDemoView.js
@@ -0,0 +1,440 @@
+import Paper from '@material-ui/core/Paper';
+import axios from 'axios';
+import { Layout, Message } from 'element-react';
+import qs from 'query-string';
+import React, { useEffect, useState } from 'react';
+import { DoudizhuGameBoard } from '../../components/GameBoard';
+import {
+ card2SuiteAndRank,
+ deepCopy,
+ fullDoudizhuDeck,
+ isDoudizhuBomb,
+ shuffleArray,
+ sortDoudizhuCards,
+} from '../../utils';
+import { douzeroDemoUrl } from '../../utils/config';
+
+const shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
+
+const threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
+
+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 initHands = [
+ shuffledDoudizhuDeck.slice(3, 20),
+ shuffledDoudizhuDeck.slice(20, 37),
+ shuffledDoudizhuDeck.slice(37, 54),
+];
+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;
+
+let gameHistory = [];
+let bombNum = 0;
+let lastMoveLandlord = [];
+let lastMoveLandlordDown = [];
+let lastMoveLandlordUp = [];
+let playedCardsLandlord = [];
+let playedCardsLandlordDown = [];
+let playedCardsLandlordUp = [];
+let legalActions = { turn: -1, actions: [] };
+
+function PvEDoudizhuDemoView() {
+ const [considerationTime, setConsiderationTime] = useState(initConsiderationTime);
+ const [toggleFade, setToggleFade] = useState('');
+ const [gameStatus, setGameStatus] = useState('ready'); // "ready", "playing", "paused", "over"
+ const [gameState, setGameState] = useState({
+ hands: [[], [], []],
+ latestAction: [[], [], []],
+ currentPlayer: null, // index of current player
+ turn: 0,
+ });
+ 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) => {
+ if (card === 'RJ') return 'D';
+ if (card === 'BJ') return 'X';
+ return card[1];
+ })
+ .join('');
+ };
+
+ // todo: generate inital player / hand states
+ // for test use
+
+ function timeout(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ const proceedNextTurn = async (playingCard, rankOnly = true) => {
+ // if next player is user, get legal actions
+ if ((gameState.currentPlayer + 1) % 3 === mainPlayerId) {
+ const player_hand_cards = cardArr2DouzeroFormat(gameState.hands[mainPlayerId].slice().reverse());
+ let rival_move = '';
+ if (playingCard.length === 0) {
+ rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 1], true));
+ } else {
+ rival_move = rankOnly ? playingCard.join('') : cardArr2DouzeroFormat(playingCard);
+ }
+ const requestBody = {
+ player_hand_cards,
+ rival_move,
+ };
+ const apiRes = await axios.post(`${douzeroDemoUrl}/legal`, qs.stringify(requestBody));
+ const data = apiRes.data;
+ legalActions = {
+ turn: gameState.turn + 1,
+ actions: data.legal_action.split(','),
+ };
+
+ setIsPassDisabled(playingCard.length === 0 && gameHistory[gameHistory.length - 1].length === 0);
+ }
+
+ // delay play for api player
+ if (gameState.currentPlayer !== mainPlayerId && considerationTime > apiPlayDelay) {
+ await timeout(apiPlayDelay);
+ }
+
+ setToggleFade('fade-out');
+
+ let newGameState = deepCopy(gameState);
+
+ // take played card out from hand, and generate playing cards with suite
+ const currentHand = newGameState.hands[gameState.currentPlayer];
+ let newHand;
+ let newLatestAction = [];
+ if (playingCard.length === 0) {
+ newHand = currentHand;
+ newLatestAction = 'pass';
+ } else if (rankOnly) {
+ newHand = currentHand.filter((card) => {
+ if (playingCard.length === 0) return true;
+
+ const { rank } = card2SuiteAndRank(card);
+ const idx = playingCard.indexOf(rank);
+ if (idx >= 0) {
+ playingCard.splice(idx, 1);
+ newLatestAction.push(card);
+ return false;
+ }
+ return true;
+ });
+ } else {
+ newLatestAction = playingCard.slice();
+ newHand = currentHand.filter((card) => {
+ if (playingCard.length === 0) return true;
+
+ const idx = playingCard.indexOf(card);
+ if (idx >= 0) {
+ playingCard.splice(idx, 1);
+ return false;
+ }
+ return true;
+ });
+ }
+
+ // update value records for douzero
+ const newHistoryRecord = sortDoudizhuCards(newLatestAction === 'pass' ? [] : newLatestAction, true);
+ switch (playerInfo[gameState.currentPlayer].douzeroPlayerPosition) {
+ case 0:
+ lastMoveLandlord = newHistoryRecord;
+ playedCardsLandlord = playedCardsLandlord.concat(newHistoryRecord);
+ break;
+ case 1:
+ lastMoveLandlordDown = newHistoryRecord;
+ playedCardsLandlordDown = playedCardsLandlordDown.concat(newHistoryRecord);
+ break;
+ case 2:
+ lastMoveLandlordUp = newHistoryRecord;
+ playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
+ break;
+ }
+ gameHistory.push(newHistoryRecord);
+ if (isDoudizhuBomb(newHistoryRecord)) bombNum++;
+
+ newGameState.latestAction[gameState.currentPlayer] = newLatestAction;
+ newGameState.hands[gameState.currentPlayer] = newHand;
+ newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3;
+ newGameState.turn++;
+ setGameState(newGameState);
+ setToggleFade('fade-in');
+ setTimeout(() => {
+ setToggleFade('');
+ }, 200);
+ if (gameStateTimeout) {
+ clearTimeout(gameStateTimeout);
+ }
+ setConsiderationTime(initConsiderationTime);
+ };
+
+ const requestApiPlay = async () => {
+ // gather information for api request
+ const player_position = playerInfo[gameState.currentPlayer].douzeroPlayerPosition;
+ const player_hand_cards = cardArr2DouzeroFormat(gameState.hands[gameState.currentPlayer].slice().reverse());
+ const num_cards_left_landlord =
+ gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 0).index].length;
+ const num_cards_left_landlord_down =
+ gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 1).index].length;
+ const num_cards_left_landlord_up =
+ gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 2).index].length;
+ const three_landlord_cards = cardArr2DouzeroFormat(threeLandlordCards.slice().reverse());
+ const card_play_action_seq = gameHistory
+ .map((cards) => {
+ return cardArr2DouzeroFormat(cards);
+ })
+ .join(',');
+ const other_hand_cards = cardArr2DouzeroFormat(
+ sortDoudizhuCards(
+ gameState.hands[(gameState.currentPlayer + 1) % 3].concat(
+ gameState.hands[(gameState.currentPlayer + 2) % 3],
+ ),
+ true,
+ ),
+ );
+ const last_move_landlord = cardArr2DouzeroFormat(lastMoveLandlord.slice().reverse());
+ const last_move_landlord_down = cardArr2DouzeroFormat(lastMoveLandlordDown.slice().reverse());
+ const last_move_landlord_up = cardArr2DouzeroFormat(lastMoveLandlordUp.slice().reverse());
+ const bomb_num = bombNum;
+ const played_cards_landlord = cardArr2DouzeroFormat(playedCardsLandlord);
+ const played_cards_landlord_down = cardArr2DouzeroFormat(playedCardsLandlordDown);
+ const played_cards_landlord_up = cardArr2DouzeroFormat(playedCardsLandlordUp);
+
+ const requestBody = {
+ player_position,
+ player_hand_cards,
+ num_cards_left_landlord,
+ num_cards_left_landlord_down,
+ num_cards_left_landlord_up,
+ three_landlord_cards,
+ card_play_action_seq,
+ other_hand_cards,
+ last_move_landlord,
+ last_move_landlord_down,
+ last_move_landlord_up,
+ bomb_num,
+ played_cards_landlord,
+ played_cards_landlord_down,
+ played_cards_landlord_up,
+ };
+
+ try {
+ const apiRes = await axios.post(`${douzeroDemoUrl}/predict`, qs.stringify(requestBody));
+ const data = apiRes.data;
+
+ if (data.status !== 0) {
+ if (data.status === -1) {
+ // check if no legal action can be made
+ const player_hand_cards = cardArr2DouzeroFormat(
+ gameState.hands[gameState.currentPlayer].slice().reverse(),
+ );
+ let rival_move = '';
+ if (gameHistory[gameHistory.length - 1].length > 0) {
+ rival_move = cardArr2DouzeroFormat(
+ sortDoudizhuCards(gameHistory[gameHistory.length - 1], true),
+ );
+ } else if (gameHistory.length >= 2 && gameHistory[gameHistory.length - 2].length > 0) {
+ rival_move = cardArr2DouzeroFormat(
+ sortDoudizhuCards(gameHistory[gameHistory.length - 2], true),
+ );
+ }
+ const requestBody = {
+ player_hand_cards,
+ rival_move,
+ };
+ const apiRes = await axios.post(`${douzeroDemoUrl}/legal`, qs.stringify(requestBody));
+ if (apiRes.data.legal_action === '') proceedNextTurn([]);
+ else {
+ Message({
+ message: 'Error receiving prediction result, please try refresh the page',
+ type: 'error',
+ showClose: true,
+ });
+ }
+ }
+ } else {
+ let bestAction = '';
+ if (data.result && Object.keys(data.result).length > 0) {
+ if (Object.keys(data.result).length === 1) bestAction = Object.keys(data.result)[0];
+ else {
+ bestAction = Object.keys(data.result)[0];
+ let bestConfidence = Number(data.result[Object.keys(data.result)[0]]);
+ for (let i = 1; i < Object.keys(data.result).length; i++) {
+ if (Number(data.result[Object.keys(data.result)[i]]) > bestConfidence) {
+ bestAction = Object.keys(data.result)[i];
+ bestConfidence = Number(data.result[Object.keys(data.result)[i]]);
+ }
+ }
+ }
+ }
+ proceedNextTurn(bestAction.split(''));
+ }
+ } catch (err) {
+ Message({
+ message: 'Error receiving prediction result, please try refresh the page',
+ type: 'error',
+ showClose: true,
+ });
+ }
+ };
+
+ const handleSelectedCards = (cards) => {
+ let newSelectedCards = selectedCards.slice();
+ cards.forEach((card) => {
+ if (newSelectedCards.indexOf(card) >= 0) {
+ newSelectedCards.splice(newSelectedCards.indexOf(card), 1);
+ } else {
+ newSelectedCards.push(card);
+ }
+ });
+ setSelectedCards(newSelectedCards);
+ };
+
+ const gameStateTimer = () => {
+ gameStateTimeout = setTimeout(() => {
+ let currentConsiderationTime = considerationTime;
+ if (currentConsiderationTime > 0) {
+ currentConsiderationTime -= considerationTimeDeduction;
+ currentConsiderationTime = Math.max(currentConsiderationTime, 0);
+ setConsiderationTime(currentConsiderationTime);
+ } else {
+ // consideration time used up for current player
+ // if current player is controlled by user, play a random card
+ // todo
+ }
+ }, considerationTimeDeduction);
+ };
+
+ useEffect(() => {
+ gameStateTimer();
+ }, [considerationTime]);
+
+ // set init game state
+ useEffect(() => {
+ // start game
+ setGameStatus('playing');
+
+ const newGameState = deepCopy(gameState);
+ // find landord to be the first player
+ newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index;
+ newGameState.hands = initHands.map((element) => sortDoudizhuCards(element));
+ setGameState(newGameState);
+ gameStateTimer();
+ }, []);
+
+ useEffect(() => {
+ if (gameState.currentPlayer) {
+ // if current player is not user, request for API player
+ if (gameState.currentPlayer !== mainPlayerId) {
+ requestApiPlay();
+ }
+ }
+ }, [gameState.currentPlayer]);
+
+ const runNewTurn = () => {};
+
+ const handleMainPlayerAct = (type) => {
+ switch (type) {
+ case 'play': {
+ // check if cards to play is in legal action list
+ if (gameState.turn === legalActions.turn) {
+ if (
+ legalActions.actions.indexOf(cardArr2DouzeroFormat(sortDoudizhuCards(selectedCards, true))) >= 0
+ ) {
+ proceedNextTurn(selectedCards, false);
+ } else {
+ Message({
+ message: 'Selected cards are not legal action',
+ type: 'warning',
+ showClose: true,
+ });
+ setSelectedCards([]);
+ }
+ } else {
+ Message({
+ message: 'Legal Action not received or turn info inconsistant',
+ type: 'error',
+ showClose: true,
+ });
+ }
+ break;
+ }
+ case 'pass': {
+ proceedNextTurn([], false);
+ setSelectedCards([]);
+ break;
+ }
+ case 'deselect': {
+ setSelectedCards([]);
+ break;
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ runNewTurn(prevTurn)}
+ toggleFade={toggleFade}
+ gameStatus={gameStatus}
+ handleMainPlayerAct={handleMainPlayerAct}
+ />
+
+
+
+
+
+
+ );
+}
+
+export default PvEDoudizhuDemoView;
diff --git a/src/view/PvEView/index.js b/src/view/PvEView/index.js
new file mode 100644
index 0000000..5d89759
--- /dev/null
+++ b/src/view/PvEView/index.js
@@ -0,0 +1,3 @@
+import PvEDoudizhuDemoView from './PvEDoudizhuDemoView';
+
+export {PvEDoudizhuDemoView};
\ No newline at end of file
diff --git a/src/view/GameView/DoudizhuGameView.js b/src/view/ReplayView/DoudizhuReplayView.js
similarity index 99%
rename from src/view/GameView/DoudizhuGameView.js
rename to src/view/ReplayView/DoudizhuReplayView.js
index 13b15a6..00ed55c 100644
--- a/src/view/GameView/DoudizhuGameView.js
+++ b/src/view/ReplayView/DoudizhuReplayView.js
@@ -2,7 +2,6 @@ import React from 'react';
import axios from 'axios';
import '../../assets/gameview.scss';
import { DoudizhuGameBoard } from '../../components/GameBoard';
-import Navbar from "../../components/Navbar";
import {removeCards, doubleRaf, deepCopy, computeHandCardsWidth, translateCardData} from "../../utils";
import { apiUrl } from "../../utils/config";
@@ -25,7 +24,7 @@ import DialogActions from "@material-ui/core/DialogActions";
import Dialog from "@material-ui/core/Dialog";
import qs from "query-string";
-class DoudizhuGameView extends React.Component {
+class DoudizhuReplayView extends React.Component {
constructor(props) {
super(props);
@@ -381,7 +380,6 @@ class DoudizhuGameView extends React.Component {
-
@@ -493,4 +491,4 @@ class DoudizhuGameView extends React.Component {
}
}
-export default DoudizhuGameView;
+export default DoudizhuReplayView;
diff --git a/src/view/GameView/LeducHoldemGameView.js b/src/view/ReplayView/LeducHoldemReplayView.js
similarity index 99%
rename from src/view/GameView/LeducHoldemGameView.js
rename to src/view/ReplayView/LeducHoldemReplayView.js
index cf77741..fb60116 100644
--- a/src/view/GameView/LeducHoldemGameView.js
+++ b/src/view/ReplayView/LeducHoldemReplayView.js
@@ -3,7 +3,6 @@ import axios from 'axios';
import qs from 'query-string';
import '../../assets/gameview.scss';
import {LeducHoldemGameBoard} from '../../components/GameBoard';
-import Navbar from '../../components/Navbar';
import {deepCopy} from "../../utils";
import { apiUrl } from "../../utils/config";
@@ -26,7 +25,7 @@ import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
-class LeducHoldemGameView extends React.Component {
+class LeducHoldemReplayView extends React.Component {
constructor(props) {
super(props);
@@ -398,7 +397,6 @@ class LeducHoldemGameView extends React.Component {
-
@@ -513,4 +511,4 @@ class LeducHoldemGameView extends React.Component {
}
}
-export default LeducHoldemGameView;
+export default LeducHoldemReplayView;
diff --git a/src/view/ReplayView/index.js b/src/view/ReplayView/index.js
new file mode 100644
index 0000000..0cd02f6
--- /dev/null
+++ b/src/view/ReplayView/index.js
@@ -0,0 +1,4 @@
+import DoudizhuReplayView from "./DoudizhuReplayView";
+import LeducHoldemReplayView from "./LeducHoldemReplayView";
+
+export {DoudizhuReplayView, LeducHoldemReplayView};