@idealic/poker-engine
Version:
Professional poker game engine and hand evaluator with built-in iterator utilities
118 lines • 3.99 kB
JavaScript
/**
* @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