@idealic/poker-engine
Version:
Professional poker game engine and hand evaluator with built-in iterator utilities
286 lines • 10.2 kB
JavaScript
import { dealStreet as dealStreetAction, showCards as dealerShowCards } from './game/dealer';
/**
* Command constructor/factory function that creates actions from a request object.
* Acts as a unified interface for creating poker actions with validation.
*
* @param props - Command request properties
* @returns Action string that can be applied to the game
*
* @example
* const action = Command({
* type: 'bet',
* game: gameState,
* playerIndex: 1,
* amount: 100
* });
*
* @example
* const action = Command({
* type: 'fold',
* game: gameState,
* playerIndex: 0
* });
*/
export function Command(props) {
const { type, game, playerIndex, amount, cards } = props;
// Basic validation
if (!game || !game.players) {
throw new Error('Command requires valid game state');
}
if (typeof playerIndex !== 'number' || playerIndex < 0 || playerIndex >= game.players.length) {
throw new Error('Command requires valid playerIndex');
}
// Route to appropriate command implementation
switch (type) {
case 'fold':
return fold(game, playerIndex);
case 'call':
return call(game, playerIndex);
case 'check':
return check(game, playerIndex);
case 'bet':
if (typeof amount !== 'number' || amount <= 0) {
throw new Error('bet command requires positive amount');
}
return bet(game, playerIndex, amount);
case 'raise':
if (typeof amount !== 'number' || amount <= 0) {
throw new Error('raise command requires positive amount');
}
return raise(game, playerIndex, amount);
case 'allIn':
return allIn(game, playerIndex);
case 'dealBoard':
if (!Array.isArray(cards) || cards.length === 0) {
throw new Error('dealBoard command requires non-empty cards array');
}
return dealBoard(game, cards);
case 'dealHoleCards':
if (!Array.isArray(cards) || cards.length === 0) {
throw new Error('dealHoleCards command requires non-empty cards array');
}
return dealHoleCards(game, playerIndex, cards);
case 'dealStreet':
return dealStreet(game);
case 'showCards':
return showCards(game);
default:
throw new Error(`Unknown command type: ${type}`);
}
}
/**
* Returns a fold action string without applying it
*/
function fold(_, playerIndex) {
return `p${playerIndex + 1} f`;
}
/**
* Returns a check action string without applying it
*/
function check(game, playerIndex) {
return `p${playerIndex + 1} cc 0`;
}
/**
* Returns a call action string without applying it.
* Available stack includes current bet since it will be returned when calling.
*/
function call(game, playerIndex) {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const amountToCall = game.bet - player.currentBet;
const callAmount = Math.min(amountToCall, availableStack);
return `p${playerIndex + 1} cc ${player.currentBet + callAmount}`;
}
/**
* Returns a bet action string without applying it.
* Available stack includes current bet since it will be returned when betting.
*/
function bet(game, playerIndex, amount) {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const betAmount = Math.min(amount, availableStack);
return `p${playerIndex + 1} cbr ${betAmount}`;
}
/**
* Returns a raise action string without applying it.
* Available stack includes current bet since it will be returned when raising.
*/
function raise(game, playerIndex, amount) {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const raiseAmount = Math.min(amount, availableStack);
return `p${playerIndex + 1} cbr ${raiseAmount}`;
}
/**
* Player bets their entire remaining stack.
* This action automatically determines whether it's a bet or raise based on the current game state.
*
* @param game - The current game state
* @param playerIndex - Index of the player (0-based)
* @returns Action string for going all-in
*/
function allIn(game, playerIndex) {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
// If no bet exists or player hasn't bet yet, it's a bet
if (game.bet === 0 || game.bet === player.currentBet) {
return bet(game, playerIndex, availableStack);
}
// Otherwise it's a raise
return raise(game, playerIndex, availableStack);
}
/**
* Returns a deal board action string without applying it
*/
function dealBoard(_table, cards) {
return `d db ${cards.join('')}`;
}
/**
* Returns a deal hole cards action string without applying it
*/
function dealHoleCards(_table, playerIndex, cards) {
return `d dh p${playerIndex + 1} ${cards.join('')}`;
}
/**
* Advances the game to the next betting round by dealing appropriate cards.
*
* @param game - The current game state
* @returns Action string for dealing street cards
*/
function dealStreet(game) {
return dealStreetAction(game);
}
/**
* Shows cards for showdown or mucking.
*
* @param game - The current game state
* @returns Action string for showing cards
*/
function showCards(game) {
const action = dealerShowCards(game);
if (!action) {
throw new Error('No cards to show');
}
return action;
}
/**
* Command namespace containing all command functions.
* These functions generate action strings that can be applied to the game using Game.applyAction().
*/
(function (Command) {
/**
* Player folds their hand and forfeits the current round
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @returns Action string for folding
*/
Command.fold = (game, playerIndex) => {
return `p${playerIndex + 1} f`;
};
/**
* Player matches the current bet amount
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @returns Action string for calling
*/
Command.call = (game, playerIndex) => {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const amountToCall = game.bet - player.currentBet;
const callAmount = Math.min(amountToCall, availableStack);
return `p${playerIndex + 1} cc ${player.currentBet + callAmount}`;
};
/**
* Player passes when no bet is required
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @returns Action string for checking
*/
Command.check = (game, playerIndex) => {
return `p${playerIndex + 1} cc 0`;
};
/**
* Player makes an initial bet
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @param amount - Total bet amount
* @returns Action string for betting
*/
Command.bet = (game, playerIndex, amount) => {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const betAmount = Math.min(amount, availableStack);
return `p${playerIndex + 1} cbr ${betAmount}`;
};
/**
* Player increases the current bet
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @param amount - Total raise amount
* @returns Action string for raising
*/
Command.raise = (game, playerIndex, amount) => {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
const raiseAmount = Math.min(amount, availableStack);
return `p${playerIndex + 1} cbr ${raiseAmount}`;
};
/**
* Player bets their entire remaining stack
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @returns Action string for going all-in
*/
Command.allIn = (game, playerIndex) => {
const player = game.players[playerIndex];
const availableStack = player.stack + player.currentBet;
// If no bet exists or player hasn't bet yet, it's a bet
if (game.bet === 0 || game.bet === player.currentBet) {
return `p${playerIndex + 1} cbr ${availableStack}`;
}
// Otherwise it's a raise
return `p${playerIndex + 1} cbr ${availableStack}`;
};
/**
* Deals community cards to the board
* @param game - Current game state
* @param cards - Array of card strings to deal
* @returns Action string for dealing board cards
*/
Command.dealBoard = (game, cards) => {
return `d db ${cards.join('')}`;
};
/**
* Deals hole cards to a specific player
* @param game - Current game state
* @param playerIndex - Index of the player (0-based)
* @param cards - Array of card strings to deal
* @returns Action string for dealing hole cards
*/
Command.dealHoleCards = (game, playerIndex, cards) => {
return `d dh p${playerIndex + 1} ${cards.join('')}`;
};
/**
* Advances the game to the next betting round
* @param game - Current game state
* @returns Action string for dealing street cards
*/
Command.dealStreet = (game) => {
return dealStreetAction(game);
};
/**
* Shows cards for showdown or mucking
* @param game - Current game state
* @returns Action string for showing cards
*/
Command.showCards = (game) => {
const action = dealerShowCards(game);
if (!action) {
throw new Error('No cards to show');
}
return action;
};
})(Command || (Command = {}));
// Export individual functions for backward compatibility
export { fold, check, call, bet, raise, dealBoard, dealHoleCards, dealStreet, showCards, allIn };
//# sourceMappingURL=Command.js.map