UNPKG

@idealic/poker-engine

Version:

Professional poker game engine and hand evaluator with built-in iterator utilities

118 lines 3.99 kB
/** * @instructions * Keep dealer actions pure and functional. * Do not introduce state management. * Follow existing action format. */ import { getRemainingPlayers, timestampAction } from '../utils/position'; import { isMultiWayAllIn } from '../utils/validation'; /** * Generates a shuffled deck of cards using a seed for deterministic shuffling */ function generateDeck(seed) { const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']; const suits = ['h', 'd', 'c', 's']; const deck = []; // Create deck for (const suit of suits) { for (const rank of ranks) { deck.push(rank + suit); } } // Shuffle using Fisher-Yates with seeded random let rng = seed; for (let i = deck.length - 1; i > 0; i--) { // Simple LCG for deterministic random numbers rng = (1103515245 * rng + 12345) & 0x7fffffff; const j = rng % (i + 1); [deck[i], deck[j]] = [deck[j], deck[i]]; } return deck; } /** * Gets the next N cards from the deck, using the table's seed and usedCards */ export function getNextCards(game, count) { if (game.seed === undefined) { throw new Error('Table seed is undefined'); } const deck = generateDeck(game.seed); const cards = deck.slice(game.usedCards, game.usedCards + count); return cards; } /** * Deals hole cards to the next player who needs them */ export function dealHoleCardsFromDeck(game) { // Deal to players in order for (let i = 0; i < game.players.length; i++) { if (game.players[i].cards.length === 0 && !game.players[i].isInactive) { const cards = getNextCards(game, 2); const baseAction = `d dh p${i + 1} ${cards.join('')}`; return timestampAction(baseAction); } } return null; } /** * Deals the next street of community cards */ export function dealStreet(game) { const cardsNeeded = game.board.length === 0 ? 3 : 1; const cards = getNextCards(game, cardsNeeded); const baseAction = `d db ${cards.join('')}`; return timestampAction(baseAction); } /** * Shows next player's cards at showdown */ export function showCards(game) { // Find next player who hasn't shown cards yet const nextPlayerIndex = game.players.findIndex((p) => !p.hasFolded && !p.hasShownCards && p.cards.length > 0 && !p.isInactive); if (nextPlayerIndex === -1) return null; const player = game.players[nextPlayerIndex]; const baseAction = `p${nextPlayerIndex + 1} sm ${player.cards.join('')}`; return timestampAction(baseAction); } /** * Determines which dealer action to take based on table state. * Assumes it's dealer's turn (getCurrentPlayerIndex returned -1). * Returns a single action without applying it to the game. * * @instructions * This function ONLY checks: * 1. Missing hole cards - deal to next player * 2. Single player remaining - no more dealing needed * 3. All-in situations - deal all remaining streets * 4. Normal betting - deal next street if all acted * 5. Showdown - show cards * * It does NOT: * - Check bet amounts (only hasActed flags) * - Apply actions to table * - Handle side pots * - Reset flags */ export function deal(game) { const activePlayers = getRemainingPlayers(game); // 1. If some players don't have cards yet, deal next hole card if (activePlayers.some((p) => p.cards.length === 0)) { return dealHoleCardsFromDeck(game); } // 2. If all players but one have folded, no need to deal more cards if (activePlayers.length === 1) { return null; } // 3. Check if we should deal next street const isAllInSituation = isMultiWayAllIn(game); const shouldDeal = isAllInSituation || game.isBettingComplete; if (shouldDeal) { if (game.board.length < 5) { return dealStreet(game); } return showCards(game); } return null; } //# sourceMappingURL=dealer.js.map