UNPKG

@idealic/poker-engine

Version:

Poker game engine and hand evaluator

263 lines (222 loc) 8.57 kB
import { describe, expect, it } from 'vitest'; import * as Poker from '../../../index'; import type { Action } from '../../../types'; import { BASE_HAND, COMPLETED_HAND, MINIMAL_HAND, SHOWDOWN_HAND } from './fixtures/baseGame'; describe('Game API - Construction', () => { describe('Basic Construction', () => { it('should create Game from Hand object', () => { const game = Poker.Game(BASE_HAND); expect(game).toBeDefined(); expect(game.players).toHaveLength(4); expect(game.players[0].name).toBe('Alice'); expect(game.players[1].name).toBe('Bob'); expect(game.players[2].name).toBe('Charlie'); expect(game.players[3].name).toBe('David'); expect(game.street).toBe('flop'); expect(game.board).toEqual(['Ah', 'Kh', 'Qd']); expect(game.pot).toBe(150); expect(game.isShowdown).toBe(false); expect(game.isComplete).toBe(false); expect(game.nextPlayerIndex).toBe(2); expect(game.lastAction).toBe('d db AhKhQd'); expect(game.variant).toBe('NT'); }); it('should replay actions to build current state', () => { const game = Poker.Game(BASE_HAND); // After replaying actions, game state should reflect: // - Alice and Bob folded // - Charlie and David in the pot // - Flop has been dealt expect(game.players[0].hasFolded).toBe(true); // Alice folded expect(game.players[1].hasFolded).toBe(true); // Bob folded expect(game.players[2].hasFolded).toBe(false); // Charlie active expect(game.players[3].hasFolded).toBe(false); // David active expect(game.board).toEqual(['Ah', 'Kh', 'Qd']); expect(game.street).toBe('flop'); }); it('should handle optional actions parameter', () => { const handWithoutActions = { ...BASE_HAND, actions: ['d dh p1 AsKs', 'd dh p2 7h2d', 'd dh p3 QhQc', 'd dh p4 JdTd'], }; const customActions: Action[] = [ 'd dh p1 AsKs', 'd dh p2 7h2d', 'd dh p3 ThTc', 'd dh p4 JdTd', 'p3 cbr 100', ] as Action[]; const game = Poker.Game(handWithoutActions, customActions); // Should apply custom actions instead of Hand's actions expect(game.players[2].roundBet).toBe(100); expect(game.players[2].cards).toEqual(['Th', 'Tc']); }); it('should handle empty optional actions array', () => { const emptyActions: Action[] = []; const game = Poker.Game(BASE_HAND, emptyActions); // Should still create game with Hand's actions expect(game).toBeDefined(); expect(game.board).toEqual([]); }); }); describe('Variant Support', () => { it("should support No-Limit Texas Hold'em", () => { const hand: Poker.Hand = { ...BASE_HAND, variant: 'NT', minBet: 20, } as Poker.Hand; const game = Poker.Game(hand); expect(game.variant).toBe('NT'); expect(game.bigBlind).toBe(20); }); it("should support Fixed-Limit Texas Hold'em", () => { // SCENARIO: Fixed-Limit Texas Hold'em game // INPUT: FT variant with smallBet: 10 (BB), bigBet: 20, blindsOrStraddles: [5, 10, 0, 0] // EXPECTED: Game is created with FT variant const hand: Poker.Hand = { ...BASE_HAND, variant: 'FT', smallBet: 10, bigBet: 20, minBet: undefined, blindsOrStraddles: [5, 10, 0, 0], // Fixed limit: BB = smallBet (10), SB = 5 } as Poker.Hand; const game = Poker.Game(hand); expect(game.variant).toBe('FT'); // Game should have betting structure info expect(game).toBeDefined(); }); }); describe('State Initialization', () => { it('should initialize pot correctly', () => { const game = Poker.Game(BASE_HAND); // Pot should include blinds and bets // SB: 10, BB: 20, Charlie: 60, David: 60 // Total: 10 + 20 + 60 + 60 = 150 expect(game.pot).toBeGreaterThan(0); expect(game.pot).toBe(150); }); it('should set correct initial positions', () => { const freshHand: Poker.Hand = { ...BASE_HAND, actions: [], }; const game = Poker.Game(freshHand); // With no actions, should be waiting for dealer action or first player expect(game.nextPlayerIndex).toBeDefined(); expect(game.nextPlayerIndex).toBeGreaterThanOrEqual(-1); // -1 for dealer }); it('should initialize button and blind positions', () => { const game = Poker.Game(MINIMAL_HAND); expect(game).toHaveProperty('buttonIndex'); expect(game).toHaveProperty('smallBlindIndex'); expect(game).toHaveProperty('bigBlindIndex'); // In a 2-player game, button is small blind if (game.players.length === 2) { expect(game.buttonIndex).toBe(game.smallBlindIndex); } }); it('should handle completed hands', () => { const game = Poker.Game(COMPLETED_HAND); expect(game.isComplete).toBe(true); // Game should have winner information expect(game.players.some(p => p.winnings && p.winnings > 0)).toBe(true); }); it('should set seed for deterministic dealing', () => { const game = Poker.Game(BASE_HAND); expect(game.seed).toBe(BASE_HAND.seed); }); it('should initialize timing properties', () => { const game = Poker.Game(BASE_HAND); expect(game.timeLimit).toBe(BASE_HAND.timeLimit); expect(game).toHaveProperty('lastTimestamp'); }); }); describe('Edge Cases', () => { it('should handle minimal Hand data', () => { const minimalHand: Poker.Hand = { variant: 'NT', players: ['Alice', 'Bob'], startingStacks: [1000, 1000], blindsOrStraddles: [10, 20], minBet: 20, antes: [0, 0], actions: [], }; const game = Poker.Game(minimalHand); expect(game.players).toHaveLength(2); expect(game.variant).toBe('NT'); }); it('should handle empty actions array', () => { const hand: Poker.Hand = { ...BASE_HAND, actions: [], }; const game = Poker.Game(hand); expect(game).toBeDefined(); expect(game.board).toEqual([]); expect(game.street).toBe('preflop'); }); it('should handle hands with only dealer actions', () => { const hand: Poker.Hand = { ...BASE_HAND, actions: ['d dh p1 AsKs', 'd dh p2 7h2d', 'd dh p3 QhQc', 'd dh p4 JdTd'], }; const game = Poker.Game(hand); expect(game).toBeDefined(); expect(game.players[0].cards).toEqual(['As', 'Ks']); expect(game.players[1].cards).toEqual(['7h', '2d']); }); it('should handle different player counts', () => { const twoPlayerHand: Poker.Hand = { ...MINIMAL_HAND, }; const ninePlayerHand: Poker.Hand = { variant: 'NT', players: ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'P9'], startingStacks: [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], blindsOrStraddles: [0, 10, 20, 0, 0, 0, 0, 0, 0], antes: [0, 0, 0, 0, 0, 0, 0, 0, 0], minBet: 20, actions: [], }; const game2 = Poker.Game(twoPlayerHand); const game9 = Poker.Game(ninePlayerHand); expect(game2.players).toHaveLength(2); expect(game9.players).toHaveLength(9); }); }); describe('Action Replay', () => { it('should correctly replay all actions in sequence', () => { const hand: Poker.Hand = { ...BASE_HAND, actions: [ 'd dh p1 AsKs', 'd dh p2 7h2d', 'd dh p3 QhQc', 'd dh p4 JdTd', 'p3 cbr 60', // Charlie raises to 60 'p4 cc 60', // David calls 60 ], }; const game = Poker.Game(hand); expect(game.players[0].cards).toEqual(['As', 'Ks']); expect(game.players[1].cards).toEqual(['7h', '2d']); expect(game.players[2].cards).toEqual(['Qh', 'Qc']); expect(game.players[3].cards).toEqual(['Jd', 'Td']); expect(game.players[0].roundBet).toBe(10); expect(game.players[1].roundBet).toBe(20); expect(game.players[2].roundBet).toBe(60); expect(game.players[3].roundBet).toBe(60); expect(game.pot).toBe(150); }); it('should handle complex action sequences', () => { const game = Poker.Game(SHOWDOWN_HAND); // Should be at river after all actions expect(game.street).toBe('river'); expect(game.board).toHaveLength(5); expect(game.isComplete).toBe(true); }); }); });