UNPKG

@idealic/poker-engine

Version:

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

203 lines 7.81 kB
import { getCurrentPlayerIndex } from '../actions/processor'; import { getRemainingPlayers } from './position'; /** * Determines if a player can act (not folded, not all-in) */ export function isPlayerEligibleToAct(player) { return !player.hasFolded && !player.isAllIn && !player.isInactive; } /** * Checks if we're in a multi-way all-in situation where no more betting is possible. * This happens when: * 1. At least one player is all-in * 2. All remaining players have matched the all-in amount * * @example * - One player all-in, others matched -> true * - One player all-in, others still betting -> false * - All players all-in -> true * - No all-in players -> false */ export function isMultiWayAllIn(game) { const activePlayers = getRemainingPlayers(game); const allInPlayers = activePlayers.filter((p) => p.isAllIn); const activeNotAllInPlayers = activePlayers.filter((p) => !p.isAllIn); // No all-in players, not a multi-way all-in if (allInPlayers.length < 2) { return false; } // Get the highest all-in amount const maxAllInBet = Math.max(...allInPlayers.map(p => p.totalBet)); // Check if all non-all-in players have matched the highest all-in bet return activeNotAllInPlayers.every((p) => p.totalBet >= maxAllInBet && activeNotAllInPlayers.length == 1); } /** * Determines if dealer intervention is needed */ export function isAwaitingDealer(game) { // Get active players (not folded) const activePlayers = getRemainingPlayers(game); // Condition 1: Missing hole cards // We need to deal if any player is missing cards, regardless of initial deal phase if (activePlayers.some((p) => p.cards.length === 0)) { return true; } // Condition 2: Single player remaining (need to award pot) if (activePlayers.length === 1) { return true; } // Condition 3: All-in situations (need to deal remaining streets) if (isMultiWayAllIn(game)) { return true; } // Check if betting round is complete const allPlayersActed = activePlayers.every((p) => p.hasActed || p.isAllIn); const allPlayersBetsMatch = activePlayers.every((p) => p.currentBet === game.bet || p.hasFolded || p.isAllIn); // Condition 4: Betting round completion (need to deal next street) if (allPlayersActed && allPlayersBetsMatch) { // If there was no betting (everyone checked), or last bet was called const lastBetWasCalled = game.lastBetAction && activePlayers.every((p) => p.hasFolded || p.isAllIn || p.currentBet === game.bet); if (game.isBettingComplete || !game.lastBetAction || lastBetWasCalled) { return true; } } return false; } /** * Finds the next player who hasn't acted or folded in the current betting round. * Used to determine whose turn it is to act. * @param table Current table state * @returns The next player to act, or undefined if it's dealer's turn */ export function getActivePlayer(game) { const index = getCurrentPlayerIndex(game); return index === -1 ? undefined : index; } /** * Determines if it's a specific player's turn to act. * A player can act if they are the first non-acted, non-folded player in position. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if it's the specified player's turn */ export function isPlayerTurn(game, playerIndex) { const activePlayer = getActivePlayer(game); return activePlayer === playerIndex; } /** * Determines if a player can check. * A player can check if they've matched the highest bet and haven't acted yet. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if the player can check */ export function canCheck(game, position) { const player = game.players[position]; if (player.hasFolded) return false; // On new streets (bet = 0), ignore hasActed flag if current bet is also 0 const isNewStreetAction = game.bet === 0 && player.currentBet === 0; if (!isNewStreetAction && player.hasActed) return false; // Can only check if we've matched the current bet return player.currentBet === game.bet; } /** * Determines if a player can call. * A player can call if they face a bet higher than their current bet and have enough chips. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if the player can call */ export function canCall(game, position) { const player = game.players[position]; if (player.hasFolded) return false; // On new streets (bet = 0), ignore hasActed flag if current bet is also 0 const isNewStreetAction = game.bet === 0 && player.currentBet === 0; if (!isNewStreetAction && player.hasActed) return false; // Can only call if there are existing bets if (game.bet === 0) return false; // Can only call if we haven't matched the current bet if (player.currentBet >= game.bet) return false; // Can't call if we don't have enough chips const amountToAdd = game.bet - player.currentBet; if (amountToAdd >= player.stack) return false; return true; } /** * Determines if a player can bet. * A player can bet if there are no existing bets and they have chips. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if the player can bet */ export function canBet(game, position) { const player = game.players[position]; if (player.hasFolded) return false; // On new streets (bet = 0), ignore hasActed flag if current bet is also 0 const isNewStreetAction = game.bet === 0 && player.currentBet === 0; if (!isNewStreetAction && player.hasActed) return false; // Can only bet if there are no existing bets if (game.bet > 0) return false; // Can't bet if we don't have any chips if (player.stack <= 0) return false; return true; } /** * Determines if a player can raise. * A player can raise if they face a bet and have enough chips. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if the player can raise */ export function canRaise(game, position) { const player = game.players[position]; if (player.hasFolded) return false; // On new streets (bet = 0), ignore hasActed flag if current bet is also 0 const isNewStreetAction = game.bet === 0 && player.currentBet === 0; if (!isNewStreetAction && player.hasActed) return false; // Can only raise if there are existing bets if (game.bet === 0) return false; // Can't raise if we don't have enough chips for min raise const minRaiseAmount = game.bet * 2 - player.currentBet; if (minRaiseAmount > player.stack) return false; return true; } /** * Determines if a player can fold. * A player can fold if they face a bet higher than their current bet. * @param table Current table state * @param playerIndex Index of the player to check * @returns True if the player can fold */ export function canFold(game, position) { const player = game.players[position]; if (player.hasFolded) return false; // On new streets (bet = 0), ignore hasActed flag if current bet is also 0 const isNewStreetAction = game.bet === 0 && player.currentBet === 0; if (!isNewStreetAction && player.hasActed) return false; // Can only fold if there are existing bets to call if (game.bet === 0) return false; // Can only fold if we haven't matched the current bet if (player.currentBet >= game.bet) return false; return true; } //# sourceMappingURL=validation.js.map