From 69b85f0f610e087604380f96e256305d16e4485f Mon Sep 17 00:00:00 2001 From: Arsalan Date: Sun, 13 Oct 2024 21:13:30 +0330 Subject: [PATCH] show down --- apps/web/src/app/games/[id]/page.tsx | 2 +- .../games/[id]/state/actions/gameActions.ts | 2 + .../[id]/state/actions/gameEventHandlers.ts | 36 ++++++++- apps/web/src/app/games/[id]/state/state.ts | 4 +- apps/web/src/utils/seat.ts | 10 +-- biome.json | 3 + packages/ts-sdk/src/Jeton/Jeton.ts | 75 +++++++++++++++---- packages/ts-sdk/src/Jeton/helpers.ts | 7 +- .../AptosOnChainDataSource.ts | 16 +++- .../OnChainDataSource/onChainEvents.types.ts | 10 ++- packages/ts-sdk/src/contracts/contractData.ts | 23 +++--- .../src/contracts/contractInteractions.ts | 25 ++++++- packages/ts-sdk/src/types/GameEvents.ts | 10 +++ 13 files changed, 180 insertions(+), 43 deletions(-) diff --git a/apps/web/src/app/games/[id]/page.tsx b/apps/web/src/app/games/[id]/page.tsx index 725cc09..6cd09bb 100644 --- a/apps/web/src/app/games/[id]/page.tsx +++ b/apps/web/src/app/games/[id]/page.tsx @@ -84,7 +84,7 @@ export default function PlayPage({ params }: { params: { id: string } }) { const privateCards = reorderedPlayers.reduce( (acc, player, seat) => { if (player) { - acc[seat + 1] = getRandomCards(); + acc[seat + 1] = player.cards ?? []; } return acc; }, diff --git a/apps/web/src/app/games/[id]/state/actions/gameActions.ts b/apps/web/src/app/games/[id]/state/actions/gameActions.ts index 1798418..cfb7521 100644 --- a/apps/web/src/app/games/[id]/state/actions/gameActions.ts +++ b/apps/web/src/app/games/[id]/state/actions/gameActions.ts @@ -12,6 +12,7 @@ import { privateCardsDecryptionHandler, receivedPrivateCardHandler, receivedPublicCardsHandler, + receivedShowDown, } from "./gameEventHandlers"; import { decryptCardShareZkey, shuffleEncryptDeckZkey } from "@jeton/zk-deck"; @@ -66,6 +67,7 @@ function setGameEventListeners(game: Jeton) { game.addListener?.(GameEventTypes.AWAITING_BET, awaitingPlayerBetHandler); game.addListener?.(GameEventTypes.PLAYER_PLACED_BET, playerPlacedBetHandler); game.addListener?.(GameEventTypes.RECEIVED_PUBLIC_CARDS, receivedPublicCardsHandler); + game.addListener?.(GameEventTypes.SHOW_DOWN, receivedShowDown); console.log("set game event listeners end"); } diff --git a/apps/web/src/app/games/[id]/state/actions/gameEventHandlers.ts b/apps/web/src/app/games/[id]/state/actions/gameEventHandlers.ts index 8e46eea..e03313f 100644 --- a/apps/web/src/app/games/[id]/state/actions/gameEventHandlers.ts +++ b/apps/web/src/app/games/[id]/state/actions/gameEventHandlers.ts @@ -1,4 +1,3 @@ -import { stat } from "fs"; import { type AwaitingBetEvent, GameStatus, @@ -9,6 +8,7 @@ import { getGameStatus, type playerShufflingEvent, } from "@jeton/ts-sdk"; +import type { ShowDownEvent } from "@jeton/ts-sdk"; import type { ReceivedPublicCardsEvent } from "@jeton/ts-sdk"; import { PublicCardRounds } from "@jeton/ts-sdk"; import { state$ } from "../state"; @@ -21,12 +21,26 @@ export function newPlayerCheckedInHandler(player: Player) { } export function handStartedHandler({ dealer }: HandStartedEvent) { + console.log("hand started handler", dealer); const gameState$ = state$.gameState; gameState$.dealer.set(dealer); gameState$.status.set(GameStatus.Shuffle); + gameState$.players.forEach((player) => { + player.cards.set(undefined); + player.bet.set(0); + player.winAmount.set(undefined); + player.roundAction.set(undefined); + }); + gameState$.myCards.set(undefined); + gameState$.flopCards.set(undefined); + gameState$.riverCard.set(undefined); + gameState$.turnCard.set(undefined); + gameState$.pot.set(0); + gameState$.betState.set(undefined); } export function playerShufflingHandler(player: playerShufflingEvent) { + console.log("player shuffling handler"); state$.gameState.shufflingPlayer.set(player); state$.gameState.status.set(GameStatus.Shuffle); } @@ -83,8 +97,15 @@ export function playerPlacedBetHandler({ state$.gameState.betState.availableActions.set(availableActions); } +export function clearPlayersRoundAction() { + state$.gameState.players.forEach((player) => { + player.roundAction.set(undefined); + }); +} + export function receivedPublicCardsHandler({ cards, round }: ReceivedPublicCardsEvent) { console.log("received public cards", round, cards); + clearPlayersRoundAction(); if (round === PublicCardRounds.FLOP) { state$.gameState.flopCards.set(cards); } else if (round === PublicCardRounds.RIVER) { @@ -93,3 +114,16 @@ export function receivedPublicCardsHandler({ cards, round }: ReceivedPublicCards state$.gameState.turnCard.set(cards); } } + +export function receivedShowDown(data: ShowDownEvent) { + console.log("received showdown event", data); + state$.gameState.players.forEach((player) => { + const eventPlayer = data[player.id.get()!]; + if (eventPlayer) { + player.cards.set(eventPlayer.cards); + player.balance.set(eventPlayer.player.balance); + player.bet.set(0); + player.winAmount.set(eventPlayer.winAmount); + } + }); +} diff --git a/apps/web/src/app/games/[id]/state/state.ts b/apps/web/src/app/games/[id]/state/state.ts index 0ef1146..d333c58 100644 --- a/apps/web/src/app/games/[id]/state/state.ts +++ b/apps/web/src/app/games/[id]/state/state.ts @@ -8,11 +8,13 @@ import { } from "@jeton/ts-sdk"; import { type Observable, observable } from "@legendapp/state"; -type UIPlayer = Player & { +export type UIPlayer = Player & { roundAction?: { action: BettingActions; amount: number; }; + cards?: number[]; + winAmount?: number; }; type GameState = { diff --git a/apps/web/src/utils/seat.ts b/apps/web/src/utils/seat.ts index 07171fc..e0827e4 100644 --- a/apps/web/src/utils/seat.ts +++ b/apps/web/src/utils/seat.ts @@ -1,9 +1,9 @@ -import type { Player } from "@jeton/ts-sdk"; +import type { UIPlayer } from "@src/app/games/[id]/state/state"; export const orderPlayersSeats = ( - players: (Player | null)[], + players: (UIPlayer | null)[], mainPlayerId: string, -): (Player | null)[] => { +): (UIPlayer | null)[] => { const mainPlayerIndex = players.findIndex((player) => player?.id === mainPlayerId); const mainPlayer = players[mainPlayerIndex]; @@ -11,7 +11,7 @@ export const orderPlayersSeats = ( return players; } - const playersAfterMainPlayer: (Player | null)[] = players.slice(mainPlayerIndex + 1); - const playersBeforeMainPlayer: (Player | null)[] = players.slice(0, mainPlayerIndex); + const playersAfterMainPlayer: (UIPlayer | null)[] = players.slice(mainPlayerIndex + 1); + const playersBeforeMainPlayer: (UIPlayer | null)[] = players.slice(0, mainPlayerIndex); return [mainPlayer, ...playersAfterMainPlayer, ...playersBeforeMainPlayer]; }; diff --git a/biome.json b/biome.json index b0b3128..d7a2c7b 100644 --- a/biome.json +++ b/biome.json @@ -19,6 +19,9 @@ "useNodejsImportProtocol": "off", "useEnumInitializers": "off", "noNonNullAssertion": "off" + }, + "complexity": { + "noForEach": "off" } } }, diff --git a/packages/ts-sdk/src/Jeton/Jeton.ts b/packages/ts-sdk/src/Jeton/Jeton.ts index 4617fd0..85d5180 100644 --- a/packages/ts-sdk/src/Jeton/Jeton.ts +++ b/packages/ts-sdk/src/Jeton/Jeton.ts @@ -6,6 +6,7 @@ import { OnChainEventTypes, type OnChainPlayerCheckedInData, type OnChainPlayerPlacedBetData, + type OnChainShowDownData, type OnChainShuffledDeckData, type OnChainTableObject, } from "@src/OnChainDataSource"; @@ -14,6 +15,7 @@ import onChainDataMapper, { covertTableInfo } from "@src/OnChainDataSource/onCha import { BettingActions, BettingRounds, + type CardShareAndProof, type ChipUnits, type GameEventMap, GameEventTypes, @@ -23,6 +25,7 @@ import { PlayerStatus, type PublicCardRounds, type ReceivedPublicCardsEvent, + type ShowDownEvent, type TableInfo, } from "@src/types"; import { type PendingMemo, createPendingMemo } from "@src/utils/PendingMemo"; @@ -37,7 +40,6 @@ import { getDeck, getNumberOfRaisesLeft, getPlayerByAddress, - getPlayerByIndex, getPot, getPrivateCardsIndexes, getShufflingPlayer, @@ -68,7 +70,7 @@ export class Jeton extends EventEmitter { private secretKey: bigint; private publicKey: Uint8Array; public gameState?: JGameState; - private myPrivateCardsShares?: [DecryptionCardShare, DecryptionCardShare]; + private myPrivateCardsShares?: [CardShareAndProof, CardShareAndProof]; private pendingMemo: PendingMemo; constructor(config: JetonConfigs) { @@ -107,9 +109,31 @@ export class Jeton extends EventEmitter { this.onChainDataSource.on(OnChainEventTypes.SHUFFLED_DECK, this.playerShuffledDeck); this.onChainDataSource.on(OnChainEventTypes.CARDS_SHARES_RECEIVED, this.receivedCardsShares); this.onChainDataSource.on(OnChainEventTypes.PLAYER_PLACED_BET, this.receivedPlayerBet); + this.onChainDataSource.on(OnChainEventTypes.SHOW_DOWN, this.receivedShowDown); this.onChainDataSource.listenToTableEvents(this.tableInfo.id); } + private receivedShowDown = async (data: OnChainShowDownData) => { + if (!this.gameState) throw new Error(" must exist"); + const players = this.gameState.players; + const eventData: ShowDownEvent = {}; + for (const [index, player] of players.entries()) { + eventData[player.id] = { + player, + winAmount: data.winningAmounts[index]!, + cards: [data.privateCards[index * 2]!, data.privateCards[index * 2 + 1]!], + }; + } + this.emit(GameEventTypes.SHOW_DOWN, eventData); + + const onChainTableObject = await this.pendingMemo.memoize(this.queryGameState); + this.gameState = onChainDataMapper.convertJetonState(onChainTableObject); + // TODO: timeout is not a good solution + setTimeout(() => { + this.gameStarted(onChainTableObject); + }, 3000); + }; + private receivedPlayerBet = async (data: OnChainPlayerPlacedBetData) => { console.log("received Player bet", data); const onChainTableObject = await this.pendingMemo.memoize(this.queryGameState); @@ -147,7 +171,7 @@ export class Jeton extends EventEmitter { return; } if (nextPlayer === null) { - //TODO: showdown + this.createAndShareSelfPrivateKeyShares(onChainTableObject); return; } // don't send awaiting bet to ui for small and big blind @@ -199,20 +223,22 @@ export class Jeton extends EventEmitter { // if the new player is active player then the game has just started if (newPlayer.status !== PlayerStatus.sittingOut) { - const dealerIndex = newJState.dealerIndex; - this.emit(GameEventTypes.HAND_STARTED, { dealer: newJState.players[dealerIndex]! }); - const shufflingPlayer = getShufflingPlayer(onChainTableObject); - if (shufflingPlayer) - this.emit( - GameEventTypes.PLAYER_SHUFFLING, - onChainDataMapper.convertPlayer(shufflingPlayer), - ); - this.checkForActions(onChainTableObject); + this.gameStarted(onChainTableObject); } this.gameState = newJState; }; + private gameStarted(state: OnChainTableObject) { + const newJState = onChainDataMapper.convertJetonState(state); + const dealerIndex = newJState.dealerIndex; + this.emit(GameEventTypes.HAND_STARTED, { dealer: newJState.players[dealerIndex]! }); + const shufflingPlayer = getShufflingPlayer(state); + if (shufflingPlayer) + this.emit(GameEventTypes.PLAYER_SHUFFLING, onChainDataMapper.convertPlayer(shufflingPlayer)); + this.checkForActions(state); + } + private playerShuffledDeck = async (data: OnChainShuffledDeckData) => { console.log("player shuffled deck"); const onChainTableObject = await this.pendingMemo.memoize(this.queryGameState); @@ -260,7 +286,20 @@ export class Jeton extends EventEmitter { this.myPrivateCardsShares = proofsAndShares .filter((pas) => myPrivateCardsIndexes.includes(pas.cardIndex)) .sort((a, b) => a.cardIndex - b.cardIndex) - .map((pas) => pas.decryptionCardShare) as [DecryptionCardShare, DecryptionCardShare]; + .map((pas) => pas) as [CardShareAndProof, CardShareAndProof]; + this.onChainDataSource.cardsDecryptionShares(this.tableInfo.id, sharesToSend, proofsToSend); + } + + private async createAndShareSelfPrivateKeyShares(state: OnChainTableObject) { + if (!this.myPrivateCardsShares) { + this.myPrivateCardsShares = (await this.createDecryptionShareProofsFor( + getPrivateCardsIndexes(state, this.playerId), + state, + )) as [CardShareAndProof, CardShareAndProof]; + } + + const proofsToSend = this.myPrivateCardsShares.map((pas) => pas.proof); + const sharesToSend = this.myPrivateCardsShares.map((pas) => pas.decryptionCardShare); this.onChainDataSource.cardsDecryptionShares(this.tableInfo.id, sharesToSend, proofsToSend); } @@ -303,8 +342,14 @@ export class Jeton extends EventEmitter { if (!this.myPrivateCardsShares) throw new Error("must be available"); const [firstCardIndex, secondCardIndex] = getPrivateCardsIndexes(state, this.playerId); - const firstCardShares = [getCardShares(state, firstCardIndex), this.myPrivateCardsShares[0]]; - const secondCardShares = [getCardShares(state, secondCardIndex), this.myPrivateCardsShares[1]]; + const firstCardShares = [ + getCardShares(state, firstCardIndex), + this.myPrivateCardsShares[0].decryptionCardShare, + ]; + const secondCardShares = [ + getCardShares(state, secondCardIndex), + this.myPrivateCardsShares[1].decryptionCardShare, + ]; const firstPrivateCard = this.zkDeck.decryptCard(firstCardIndex, deck, firstCardShares); const secondPrivateCard = this.zkDeck.decryptCard(secondCardIndex, deck, secondCardShares); diff --git a/packages/ts-sdk/src/Jeton/helpers.ts b/packages/ts-sdk/src/Jeton/helpers.ts index 3b42a8f..d541ae2 100644 --- a/packages/ts-sdk/src/Jeton/helpers.ts +++ b/packages/ts-sdk/src/Jeton/helpers.ts @@ -20,10 +20,13 @@ export function isActivePlayer(tableObject: OnChainTableObject, playerId: string return !!tableObject.roster.players.find((p) => p.addr === playerId); } -export function getPrivateCardsIndexes(tableObject: OnChainTableObject, playerId: string) { +export function getPrivateCardsIndexes( + tableObject: OnChainTableObject, + playerId: string, +): [number, number] { const playerPosition = tableObject.roster.players.findIndex((p) => p.addr === playerId); if (playerPosition === -1) throw new Error("player does not exist"); - return [playerPosition * 2, playerPosition * 2 + 1] as const; + return [playerPosition * 2, playerPosition * 2 + 1]; } export function getDeck(tableObject: OnChainTableObject) { diff --git a/packages/ts-sdk/src/OnChainDataSource/AptosOnChainDataSource.ts b/packages/ts-sdk/src/OnChainDataSource/AptosOnChainDataSource.ts index 396939a..cbd2ec5 100644 --- a/packages/ts-sdk/src/OnChainDataSource/AptosOnChainDataSource.ts +++ b/packages/ts-sdk/src/OnChainDataSource/AptosOnChainDataSource.ts @@ -12,11 +12,14 @@ import { contractCheckedInEventType, contractFoldEventType, contractRaiseEventType, + contractShowDownEventType, contractShuffleEventType, contractSmallBlindEventType, } from "@src/contracts/contractData"; import { createTableInfo } from "@src/contracts/contractDataMapper"; import { + type ChainEvents, + type ShowDownEvent, callCallContract, callCheckInContract, callDecryptCardShares, @@ -49,7 +52,7 @@ export class AptosOnChainDataSource this.address = address; } - private publishEvent(event: { data: { sender_addr: string }; indexed_type: string }) { + private publishEvent(event: ChainEvents) { console.log("publish event", event); switch (event.indexed_type) { case contractCheckedInEventType: @@ -99,6 +102,15 @@ export class AptosOnChainDataSource address: event.data.sender_addr, }); break; + case contractShowDownEventType: { + console.log("publishing", OnChainEventTypes.SHOW_DOWN); + const data = (event as ShowDownEvent).data; + this.emit(OnChainEventTypes.SHOW_DOWN, { + publicCards: data.public_cards.map((p) => Number(p)), + privateCards: data.private_cards.map((p) => Number(p)), + winningAmounts: data.winning_amounts.map((a) => Number(a)), + }); + } } } @@ -117,7 +129,7 @@ export class AptosOnChainDataSource // TODO: parse events and emit const pollingTable = this.pollingTables[tableId]; if (pollingTable) { - pollingTable.lastEventBlockHeight = events[0].transaction_block_height; + pollingTable.lastEventBlockHeight = events[0]!.transaction_block_height; pollingTable.timerId = setTimeout(this.pollTableEvents.bind(this, tableId), POLLING_INTERVAL); } }; diff --git a/packages/ts-sdk/src/OnChainDataSource/onChainEvents.types.ts b/packages/ts-sdk/src/OnChainDataSource/onChainEvents.types.ts index 7f69776..4035376 100644 --- a/packages/ts-sdk/src/OnChainDataSource/onChainEvents.types.ts +++ b/packages/ts-sdk/src/OnChainDataSource/onChainEvents.types.ts @@ -3,8 +3,9 @@ import type { BettingActions, BettingRounds, PublicCardRounds } from "../types"; export enum OnChainEventTypes { PLAYER_CHECKED_IN = "player-checked-in", SHUFFLED_DECK = "shuffled-deck", - CARDS_SHARES_RECEIVED = "private-cards-shares", + CARDS_SHARES_RECEIVED = "cards-shares", PLAYER_PLACED_BET = "player-placed-bet", + SHOW_DOWN = "show-down", } export type OnChainPlayerCheckedInData = { @@ -24,9 +25,16 @@ export type OnChainPlayerPlacedBetData = { address?: string; }; +export type OnChainShowDownData = { + privateCards: number[]; + publicCards: number[]; + winningAmounts: number[]; +}; + export type OnChainEventMap = { [OnChainEventTypes.PLAYER_CHECKED_IN]: [OnChainPlayerCheckedInData]; [OnChainEventTypes.SHUFFLED_DECK]: [OnChainShuffledDeckData]; [OnChainEventTypes.CARDS_SHARES_RECEIVED]: [OnChainCardsSharesData]; [OnChainEventTypes.PLAYER_PLACED_BET]: [OnChainPlayerPlacedBetData]; + [OnChainEventTypes.SHOW_DOWN]: [OnChainShowDownData]; }; diff --git a/packages/ts-sdk/src/contracts/contractData.ts b/packages/ts-sdk/src/contracts/contractData.ts index 2f7e5fd..e397873 100644 --- a/packages/ts-sdk/src/contracts/contractData.ts +++ b/packages/ts-sdk/src/contracts/contractData.ts @@ -5,26 +5,25 @@ const appName = "holdem_table"; const tableType = "Table"; export const contractTableCreatedEventType = - `${contractAddress}::${appName}::TableCreatedEvent` as MoveStructId; -export const contractCheckedInEventType = - `${contractAddress}::${appName}::CheckedInEvent` as MoveStructId; + `${contractAddress}::${appName}::TableCreatedEvent` as const; +export const contractCheckedInEventType = `${contractAddress}::${appName}::CheckedInEvent` as const; export const contractShuffleEventType = - `${contractAddress}::${appName}::DeckShuffleEncryptedEvent` as MoveStructId; + `${contractAddress}::${appName}::DeckShuffleEncryptedEvent` as const; export const contractCardDecryptionShareEventType = - `${contractAddress}::${appName}::CardDecryptedShareEvent` as MoveStructId; + `${contractAddress}::${appName}::CardDecryptedShareEvent` as const; export const contractSmallBlindEventType = - `${contractAddress}::${appName}::SmallBlindEvent` as MoveStructId; -export const contractBigBlindEventType = - `${contractAddress}::${appName}::BigBlindEvent` as MoveStructId; -export const contractFoldEventType = `${contractAddress}::${appName}::FoldedEvent` as MoveStructId; -export const contractRaiseEventType = `${contractAddress}::${appName}::RaisedEvent` as MoveStructId; -export const contractCallEventType = `${contractAddress}::${appName}::CalledEvent` as MoveStructId; + `${contractAddress}::${appName}::SmallBlindEvent` as const; +export const contractBigBlindEventType = `${contractAddress}::${appName}::BigBlindEvent` as const; +export const contractFoldEventType = `${contractAddress}::${appName}::FoldedEvent` as const; +export const contractRaiseEventType = `${contractAddress}::${appName}::RaisedEvent` as const; +export const contractCallEventType = `${contractAddress}::${appName}::CalledEvent` as const; +export const contractShowDownEventType = `${contractAddress}::${appName}::ShowdownEvent` as const; export const contractTableType = `${contractAddress}::${appName}::${tableType}` as MoveStructId; const createTable = "create_table"; export const contractCreateTableFunctionName = - `${contractAddress}::${appName}::${createTable}` as MoveStructId; + `${contractAddress}::${appName}::${createTable}` as const; export const contractCheckInFunctionName = `${contractAddress}::${appName}::check_in` as MoveStructId; export const contractShuffleEncryptDeckFunctionName = diff --git a/packages/ts-sdk/src/contracts/contractInteractions.ts b/packages/ts-sdk/src/contracts/contractInteractions.ts index 8b508b4..e757343 100644 --- a/packages/ts-sdk/src/contracts/contractInteractions.ts +++ b/packages/ts-sdk/src/contracts/contractInteractions.ts @@ -17,6 +17,7 @@ import { contractFoldFunctionName, contractRaiseEventType, contractRaiseFunctionName, + contractShowDownEventType, contractShuffleEncryptDeckFunctionName, contractShuffleEventType, contractSmallBlindEventType, @@ -24,6 +25,23 @@ import { contractTableType, } from "./contractData"; +export interface BaseEvent { + data: unknown; + indexed_type: string; + transaction_block_height: number; +} +export interface GeneralEvent extends BaseEvent { + data: { sender_addr: string }; +} + +export interface ShowDownEvent extends BaseEvent { + data: { private_cards: number[]; public_cards: number[]; winning_amounts: number[] }; + indexed_type: typeof contractShowDownEventType; + transaction_block_height: number; +} + +export type ChainEvents = GeneralEvent | ShowDownEvent; + //TODO apply pagination for getModuleEventsByEventType export const getTableObjectAddresses = async (): Promise => { return aptos.getModuleEventsByEventType({ @@ -237,6 +255,7 @@ export async function queryEvents(tableId: string) { "${contractFoldEventType}", "${contractRaiseEventType}", "${contractCallEventType}", + "${contractShowDownEventType}" ]}, data: {_cast: {String: {_like: "%${tableId}%"}}}}, order_by: {transaction_block_height: desc} @@ -246,8 +265,8 @@ export async function queryEvents(tableId: string) { transaction_block_height } }`; - //TODO: typing - // biome-ignore lint/suspicious/noExplicitAny: - const res = await request<{ events: any[] }>(NODIT_GQL_ADDRESS, document); + const res = await request<{ + events: ChainEvents[]; + }>(NODIT_GQL_ADDRESS, document); return res.events; } diff --git a/packages/ts-sdk/src/types/GameEvents.ts b/packages/ts-sdk/src/types/GameEvents.ts index 17071a0..639304d 100644 --- a/packages/ts-sdk/src/types/GameEvents.ts +++ b/packages/ts-sdk/src/types/GameEvents.ts @@ -13,6 +13,7 @@ export enum GameEventTypes { AWAITING_BET = "awaiting-bet", PLAYER_PLACED_BET = "player-placed-bet", RECEIVED_PUBLIC_CARDS = "received-public-cards", + SHOW_DOWN = "show-down", } export type DownloadProgressEvent = { @@ -62,6 +63,14 @@ export type PlayerPlacedBetEvent = { availableActions: PlacingBettingActions[]; }; +export type ShowDownEvent = { + [playerId: string]: { + winAmount: number; + cards: number[]; + player: Player; + }; +}; + export type GameEvents = | DownloadProgressEvent | CheckInEvent @@ -85,4 +94,5 @@ export type GameEventMap = { [GameEventTypes.AWAITING_BET]: [AwaitingBetEvent]; [GameEventTypes.PLAYER_PLACED_BET]: [PlayerPlacedBetEvent]; [GameEventTypes.RECEIVED_PUBLIC_CARDS]: [ReceivedPublicCardsEvent]; + [GameEventTypes.SHOW_DOWN]: [ShowDownEvent]; };