@idealic/poker-engine
v1.1.0
Published
Poker game engine and hand evaluator
Readme
@idealic/poker-engine
Professional poker game engine with TypeScript support. Provides comprehensive game state management, action processing, and hand evaluation for Texas Hold'em and other poker variants.
Overview
The Poker Engine provides two core abstractions for building poker applications:
Hand: A standardized notation format that stores the complete sequence of actions in a poker game, similar to chess notation. This action-based approach enables deterministic game reconstruction, easy storage, and network transmission.
Game: A rich game state object containing all information needed to render a poker table, including player stacks, community cards, pot size, betting status, and available actions.
Core Concepts
The State (Poker.State) as a Functional Core
The Poker Engine uses a standardized notation for poker games, which is conceptually the State (implemented as Poker.State), as the source of truth. This State is fully self-contained, meaning it holds every piece of information required to play the game. Similar to chess notation, it stores the complete history of actions, which allows for perfect reconstruction of the game at any point.
The engine follows the Functional Core, Imperative Shell architectural pattern:
- Functional Core (
Poker.State): This is the pure, immutable data structure that represents the game state. It is modified only through pure reducer functions that take a state and an action, and return a new state. Immutability is achieved not by deep copying, but by the reducer pattern itself, ensuring a predictable state evolution. - Imperative Shell (
Poker.Game): TheGameacts as a mutable wrapper or "shell" around the functionally pureState. It manages the runtime environment, provides a convenient API for querying the state, and handles side effects, but it delegates the core logic to the pure state reducers.
This approach offers several key advantages:
- Portability: The State notation is a human-readable, portable format that is easily stored, transmitted, and replayed.
- Determinism: The game state can be precisely reconstructed by replaying actions, simplifying debugging and testing.
- Flexibility: The notation supports a wide variety of poker variants and game types.
The Game as a Vision of the Table
While the State is the data, the Game object provides the interface needed to render a poker game, including:
- Players, stacks, and action status
- Community cards and pot size
- Current betting round and next player to act
- Allowed actions (buttons to enable) and timeouts
- Winnings, losses, and rake computations
Standardized Statistics
The engine gathers metrics (stats) for each play action using a standardized API. While the specific statistics (like "VPIP" or "3-Bet") are unique to Poker, the approach allows different engines to plug into the same infrastructure.
Poker.Format Namespace
The Poker.Format namespace provides specialized utilities for:
- Serialization/Deserialization: Efficiently encoding and decoding game states and actions for network transmission or storage.
- Hand History Parsing: Converting raw hand history text into structured
Handobjects, compatible with major formats like PokerStars.
Reliability
The engine is developed using a rigorous spec-driven approach, ensuring that every rule and mechanic is formally defined. It is backed by approximately 3000 tests, covering a vast array of scenarios—from standard play to complex edge cases like multi-way side pots and split pots—guaranteeing rock-solid stability and correctness.
Additionally:
- Fuzzy Testing: The full game lifecycle is continuously validated via fuzzy testing at the Game Service integration level, simulating chaotic real-world conditions.
- Proven Heritage: The engine's logic is derived from a system compatible with the PokerStars hand format, which has been stress-tested against a dataset of over 10 million historical games.
System Architecture
Scope & Responsibilities
The Poker Engine provides the game logic and state management for a single table. It explicitly does not handle:
- Table Management: Creation of tables, matchmaking, or seating logic.
- User Sessions: Authentication, balance management, or persistent connections.
These concerns are handled by the generic Game Service.
Client-Server Model
The Poker Engine runs on both the client and the server.
- Client-Side: Handles optimistic rendering and enables user actions (e.g., showing the bet button) based on the local state.
- Server-Side: Acts as the ultimate authority for action validation, game sanitization (hiding hole cards), and randomness (shuffling/dealing).
Integration
This engine is designed to work seamlessly with:
- Backend: @idealic/game-service — A generic game service that handles session management, persistence, and networking. The Poker Engine registers as a logic provider within this service.
- Frontend: @idealic/poker-ui — A pure functional React UI that renders the
Gamestate produced by this engine.
Game Lifecycle
The Poker Engine operates as a request-response pipeline.
1. Joining and Initialization
Players request to join or create a game. The system finds a matching table or creates a new one. The game waits until at least two active players are seated before starting.
2. Game Creation
Once conditions are met:
- Initialization: The authoritative
Stateis created with a secure seed. - Dealer Actions: Initial shuffling and dealing occur immediately.
- Persist & Broadcast: The state is stored and a sanitized version is sent to players.
3. Player Action Loop
- Receive: Client sends an action.
- Process: The engine applies the action to the current server state.
- Update: The new state is stored, sanitized, and broadcast to all players.
4. Completion and Succession
When a hand finishes, the engine enforces succession rules (moving the button, handling blinds) and automatically prepares the State for the next hand.
5. Player Lifecycle Management
Players can signal their intent to change status at any time, which takes effect starting from the next game:
- Quit: Leave the table after the current hand.
- Pause: Sit out starting next hand.
- Resume / Wait for BB: Unpause ASAP or wait for the Big Blind to rejoin.
6. Waiting State
If there are not enough active players (minimum 2) to start the next game:
- The game loop pauses.
- Players wait at the table.
- The next game starts automatically once new players join or existing players unpause.
7. Timeout Handling
The system (via the Game Service) periodically checks for timeouts and applies default actions (e.g., fold) to keep the game moving.
Usage Examples
Basic Hand Processing
import * as Poker from '@idealic/poker-engine';
// Create a hand object
const hand = Poker.State({
variant: 'NT',
players: ['Alice', 'Bob'],
startingStacks: [1000, 1000],
blindsOrStraddles: [10, 20],
minBet: 20,
actions: [],
});
// Convert to game state
const game = Poker.Game(hand);
// Execute commands
const foldAction = Poker.Command.fold(game, 0);
const updatedHand = Poker.State.applyAction(hand, foldAction);
// Export for display (from player perspective)
const playerPerspectiveGame = Poker.State.personalize(hand, 'Alice');
// Check time elapsed
const elapsed = Poker.State.getTimeLeft(hand);
// Handle timeouts automatically
const timeoutCheckedHand = Poker.State.advance(hand);Documentation
- Engine Reference - System design and principles.
- Game Namespace - Game state management and table processing.
- Hand Namespace - Hand notation format.
- Format Namespace - Tools for serializing and deserializing game data and hand histories.
- Command Namespace - Action generation and execution.
- API Reference - Detailed API documentation.
- Statistics - Analytics capabilities.
License
MIT License - see LICENSE file for details.
