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