From 19fb53535bf1029bc981b6c2cc2b6994c4ec384d Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 2 Jul 2019 17:04:28 -0400 Subject: [PATCH 1/3] Adds call clock Functionality and showdown --- src/common/game/game-play.ts | 11 +++- src/common/game/game.ts | 87 +++++++++++++++++++++++++++++-- src/common/game/solve-hands.ts | 6 --- src/common/player/player.ts | 4 ++ src/common/types.ts | 3 ++ src/components/action.ts | 95 ++++++++++++++++++++++------------ src/data/connection.ts | 4 ++ src/socket.ts | 32 +++++++++++- test/test.ts | 15 ++++++ 9 files changed, 211 insertions(+), 46 deletions(-) diff --git a/src/common/game/game-play.ts b/src/common/game/game-play.ts index d87e598..ba9dc3f 100644 --- a/src/common/game/game-play.ts +++ b/src/common/game/game-play.ts @@ -23,6 +23,7 @@ export class GamePlay implements GamePlayType { public isGameOver: boolean = false; public isStarted: boolean = false; public isOpen: boolean = false; + public timer?: number; get dealerIndex(): number { return this.players.findIndex((player) => player.dealer === true); @@ -115,6 +116,8 @@ export class GamePlay implements GamePlayType { player.subtractBet(this.currentBet); this.currentPot += this.currentBet - (parseFloat(player.bet) || 0); player.bet = data.toFixed(2); + this.clearLastAggresor(); + player.isLastAggressor = true; break; case "rebuy": player.stackAmount += data || 0; @@ -158,8 +161,8 @@ export class GamePlay implements GamePlayType { this.board.push(this.deck!.pop()!); break; default: - this.solveHands(); - return; + this.isGameOver = true; + break; } updatePots(this); @@ -298,4 +301,8 @@ export class GamePlay implements GamePlayType { ) .map((player) => (player.isSittingOut = false)); } + + private clearLastAggresor(): void { + this.players.map((player) => (player.isLastAggressor = false)); + } } diff --git a/src/common/game/game.ts b/src/common/game/game.ts index 2454222..c38ae21 100644 --- a/src/common/game/game.ts +++ b/src/common/game/game.ts @@ -15,10 +15,9 @@ export class Game extends GamePlay { return { board: this.board, gameID: this.gameID, - players: - this.isGameOver || this.isOpen - ? this.players - : this.getGameStatePlayers(currentPlayerID), + players: this.isOpen + ? this.players + : this.getGameStatePlayers(currentPlayerID), pot: this.pot, round: this.round, bigBlind: this.bigBlind, @@ -33,6 +32,7 @@ export class Game extends GamePlay { isStarted: this.isStarted, isOpen: this.isOpen, sittingInPlayers: this.sittingInPlayers, + timer: this.timer, }; } @@ -45,7 +45,9 @@ export class Game extends GamePlay { player.playerID !== currentPlayerID && player.cards.length > 0 ) .map((player) => { - player.cards = [this.cardBack, this.cardBack]; + player.cards = player.showCards + ? player.cards + : [this.cardBack, this.cardBack]; }); return copyPlayers; @@ -90,4 +92,79 @@ export class Game extends GamePlay { setTimeout(() => this.OpenGame(callback), 3000); } + + public showdown(callback: () => void): void { + this.solveHands(); + + const livePlayers: PlayerType[] = this.activePlayers.concat(); + const winnerFound: boolean = false; + const lastAggressorIndex = livePlayers.findIndex( + (player) => player.isLastAggressor === true + ); + const resultTemp: number[] = []; + this.activePlayers.map((activePlayer) => + resultTemp.push(activePlayer.result) + ); + + setTimeout( + () => + this.showdownHelper(lastAggressorIndex, winnerFound, resultTemp, () => + callback() + ), + 1500 + ); + } + + private showdownHelper( + lastAggressorIndex: number, + winnerFound: boolean, + resultTemp: number[], + callback: () => void + ): void { + if ( + this.activePlayers.filter((activePlayer) => activePlayer.result > 0) + .length === 0 + ) { + this.activePlayers.map( + (activePlayer, i) => (activePlayer.stackAmount += resultTemp[i]) + ); + callback(); + return; + } + + const player = this.activePlayers[lastAggressorIndex]; + + if (!player) { + lastAggressorIndex = 0; + + this.showdownHelper(lastAggressorIndex, winnerFound, resultTemp, () => + callback() + ); + return; + } + + lastAggressorIndex++; + + if (winnerFound && player.result === 0) { + this.showdownHelper(lastAggressorIndex, winnerFound, resultTemp, () => + callback() + ); + return; + } + + if (player.result > 0) { + winnerFound = true; + player.result = 0; + } + + player.showCards = true; + callback(); + setTimeout( + () => + this.showdownHelper(lastAggressorIndex, winnerFound, resultTemp, () => + callback() + ), + 1500 + ); + } } diff --git a/src/common/game/solve-hands.ts b/src/common/game/solve-hands.ts index 7f7b469..6c81371 100644 --- a/src/common/game/solve-hands.ts +++ b/src/common/game/solve-hands.ts @@ -84,10 +84,4 @@ export function solveHands(game: GamePlayType): void { winners = []; boardWinner = true; } - - game.players - .filter((player) => player.result > 0) - .map((player) => (player.stackAmount += player.result)); - - game.isGameOver = true; } diff --git a/src/common/player/player.ts b/src/common/player/player.ts index 5363e2a..6a80db4 100644 --- a/src/common/player/player.ts +++ b/src/common/player/player.ts @@ -16,6 +16,8 @@ export class Player implements PlayerType { public result: number = 0; public pendingSitOut: boolean = false; public isSittingOut: boolean = false; + public isLastAggressor: boolean = false; + public showCards: boolean = false; get isActive(): boolean { return this.cards.length === 2 && this.status !== "fold"; @@ -62,6 +64,8 @@ export class Player implements PlayerType { this.isTurn = false; this.isBigBlind = false; this.isLittleBlind = false; + this.isLastAggressor = false; + this.showCards = false; this.invested = 0; this.result = 0; } diff --git a/src/common/types.ts b/src/common/types.ts index 093bacc..524522f 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -14,6 +14,8 @@ export interface PlayerState { solvedHand?: Hand; isSittingOut: boolean; pendingSitOut: boolean; + isLastAggressor: boolean; + showCards: boolean; readonly isActive: boolean; readonly isCheck: boolean; readonly numBet: number; @@ -43,6 +45,7 @@ export interface GameState { isStarted: boolean; isGameOver: boolean; isOpen: boolean; + timer?: number; readonly sittingInPlayers: PlayerType[]; } diff --git a/src/components/action.ts b/src/components/action.ts index ad329fc..11ff99a 100644 --- a/src/components/action.ts +++ b/src/components/action.ts @@ -10,7 +10,12 @@ import { import { classMap } from "lit-html/directives/class-map"; import { GameState, PlayerState } from "../common/types"; import { roundToPrecision } from "../common/round-to-precision"; -import { sendPlayerAction, startGame, leaveGame } from "../data/connection"; +import { + sendPlayerAction, + startGame, + leaveGame, + callClock, +} from "../data/connection"; interface BetTarget extends EventTarget { multiplier: number; @@ -45,8 +50,8 @@ export class Action extends LitElement { if (this.player.bet === "" && this.player.isTurn && !this.game.isGameOver) { this.player.bet = (this.game.currentBet !== 0 - ? this.game.currentBet - : this.game.bigBlind + ? this.game.currentBet + this.game.bigBlind + : this.game.bigBlind + this.game.littleBlind ).toFixed(2); } @@ -124,35 +129,38 @@ export class Action extends LitElement { type="number" /> -
- - - - +
+
+ + + + +
+
${this.game.timer || ""}
@@ -215,6 +223,16 @@ export class Action extends LitElement {
`} + ${this.game.timer !== undefined || + !this.game.isStarted || + this.player.isSittingOut || + this.game.isGameOver + ? "" + : html` + + `} `; } @@ -317,6 +335,15 @@ export class Action extends LitElement { .rebuy-button { font-size: 12px; } + .bottom-main-box { + display: flex; + justify-content: space-between; + align-items: center; + } + .timer { + color: white; + padding-right: 4px; + } `; } @@ -373,4 +400,8 @@ export class Action extends LitElement { private _leaveGame(): void { leaveGame(this.socket, this.game.gameID); } + + private _callClock(): void { + callClock(this.socket, this.game.gameID); + } } diff --git a/src/data/connection.ts b/src/data/connection.ts index b564b3d..325e737 100644 --- a/src/data/connection.ts +++ b/src/data/connection.ts @@ -49,3 +49,7 @@ export function leaveGame(socket: SocketIOClient.Socket, gameID: string) { localStorage.clear(); document.location.reload(); } + +export function callClock(socket: SocketIOClient.Socket, gameID: string) { + socket.emit("callClock", gameID); +} diff --git a/src/socket.ts b/src/socket.ts index af2b4c5..e3b1a45 100644 --- a/src/socket.ts +++ b/src/socket.ts @@ -73,6 +73,7 @@ export function createSocket(server: Server) { return; } + game.timer = undefined; game.playerAction(player, action, data); sendGameState(io, game); @@ -82,7 +83,9 @@ export function createSocket(server: Server) { } // Game has ended, show last cards and winning desc then wait 5 secs and start a new game - if (game.winDesc !== "") { + if (game.isGameOver) { + game.showdown(() => sendGameState(io, game)); + setTimeout(() => { game.start(); sendGameState(io, game); @@ -101,6 +104,33 @@ export function createSocket(server: Server) { sendGameState(io, game); }); + + socket.on("callClock", (gameID: string) => { + const game = getGame(gameID); + game.timer = 15; + + sendGameState(io, game); + const interval = setInterval(() => { + if (game.timer === undefined) { + clearInterval(interval); + return; + } + + if (game.timer === 0) { + const player = game.players[game.playerTurnIndex]; + player.pendingSitOut = true; + game.timer = undefined; + + game.playerAction(player, "fold", ""); + sendGameState(io, game); + clearInterval(interval); + return; + } + + game.timer!--; + sendGameState(io, game); + }, 1000); + }); }); } diff --git a/test/test.ts b/test/test.ts index 41a0205..250fd03 100644 --- a/test/test.ts +++ b/test/test.ts @@ -115,6 +115,21 @@ describe("Evaluates Hand Results", () => { currentGame.start(); }); + it("should be a Two Pair, K's & J's WIN, Player 0 Wins", () => { + currentGame.players[0].cards = ["AC", "5S"]; + currentGame.players[0].invested = 50; + currentGame.players[1].cards = ["QH", "3S"]; + currentGame.players[1].invested = 50; + currentGame.players[2].cards = ["8D", "9C"]; + currentGame.players[2].invested = 50; + currentGame.board = ["TS", "JD", "JH", "KD", "KH"]; + + currentGame.solveHands(); + + assert.strictEqual(currentGame.winDesc, "Two Pair, K's & J's"); + assert.strictEqual(currentGame.players[0].result, 150); + }); + it("should be a High Card Win, Player 0 Wins", () => { currentGame.players[0].cards = ["AS", "4S"]; currentGame.players[0].invested = 50; From bc42f5de0941f9d9a3206a8124ac62566bff13ad Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 2 Jul 2019 19:00:09 -0400 Subject: [PATCH 2/3] small updates --- src/common/game/game-play.ts | 16 +++++++++++++--- src/common/game/game.ts | 9 ++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/common/game/game-play.ts b/src/common/game/game-play.ts index ba9dc3f..8b12d43 100644 --- a/src/common/game/game-play.ts +++ b/src/common/game/game-play.ts @@ -106,9 +106,13 @@ export class GamePlay implements GamePlayType { case "check": break; case "call": - player.subtractBet(this.currentBet); - this.currentPot += this.currentBet - (parseFloat(player.bet) || 0); - player.bet = this.currentBet.toFixed(2); + const bet = + this.currentBet > player.stackAmount + ? player.stackAmount + : this.currentBet; + player.subtractBet(bet); + this.currentPot += bet - (parseFloat(player.bet) || 0); + player.bet = bet.toFixed(2); break; case "bet": case "raise": @@ -168,6 +172,12 @@ export class GamePlay implements GamePlayType { updatePots(this); } + public updatePlayerStacks(): void { + this.activePlayers.map( + (activePlayer, i) => (activePlayer.stackAmount += activePlayer.result) + ); + } + private nextTurn(): void { if (this.activePlayers.length === 1) { this.activePlayers.map((player) => { diff --git a/src/common/game/game.ts b/src/common/game/game.ts index c38ae21..676c1dc 100644 --- a/src/common/game/game.ts +++ b/src/common/game/game.ts @@ -83,6 +83,9 @@ export class Game extends GamePlay { callback(); if (this.isGameOver) { + this.solveHands(); + this.updatePlayerStacks(); + setTimeout(() => { this.start(); callback(); @@ -96,9 +99,8 @@ export class Game extends GamePlay { public showdown(callback: () => void): void { this.solveHands(); - const livePlayers: PlayerType[] = this.activePlayers.concat(); const winnerFound: boolean = false; - const lastAggressorIndex = livePlayers.findIndex( + const lastAggressorIndex = this.activePlayers.findIndex( (player) => player.isLastAggressor === true ); const resultTemp: number[] = []; @@ -126,8 +128,9 @@ export class Game extends GamePlay { .length === 0 ) { this.activePlayers.map( - (activePlayer, i) => (activePlayer.stackAmount += resultTemp[i]) + (activePlayer, i) => (activePlayer.result = resultTemp[i]) ); + this.updatePlayerStacks(); callback(); return; } From 11d86949e51ce21fcdd9d02a2f264713a4f46d99 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 2 Jul 2019 19:24:29 -0400 Subject: [PATCH 3/3] Small updates to patch bugs --- src/common/game/game-play.ts | 2 ++ src/common/game/game.ts | 7 +++++++ src/components/action.ts | 3 ++- src/socket.ts | 5 ----- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/game/game-play.ts b/src/common/game/game-play.ts index 8b12d43..b9e019f 100644 --- a/src/common/game/game-play.ts +++ b/src/common/game/game-play.ts @@ -118,8 +118,10 @@ export class GamePlay implements GamePlayType { case "raise": this.currentBet = data; player.subtractBet(this.currentBet); + this.currentPot += this.currentBet - (parseFloat(player.bet) || 0); player.bet = data.toFixed(2); + this.clearLastAggresor(); player.isLastAggressor = true; break; diff --git a/src/common/game/game.ts b/src/common/game/game.ts index 676c1dc..d8e9ffd 100644 --- a/src/common/game/game.ts +++ b/src/common/game/game.ts @@ -130,8 +130,15 @@ export class Game extends GamePlay { this.activePlayers.map( (activePlayer, i) => (activePlayer.result = resultTemp[i]) ); + this.updatePlayerStacks(); callback(); + + setTimeout(() => { + this.start(); + callback(); + }, 5000); + return; } diff --git a/src/components/action.ts b/src/components/action.ts index 11ff99a..cbfde8b 100644 --- a/src/components/action.ts +++ b/src/components/action.ts @@ -226,7 +226,8 @@ export class Action extends LitElement { ${this.game.timer !== undefined || !this.game.isStarted || this.player.isSittingOut || - this.game.isGameOver + this.game.isGameOver || + this.game.isOpen ? "" : html`