{"version":3,"sources":["../src/generalUtils.ts","../src/actionListUtils.ts","../src/utilsForStcLand.ts","../src/currencyUtils.ts","../src/stateHelpers.ts","../src/stateValidators.ts","../src/stateMutators.ts","../src/spotStateUtils.ts","../src/applyActionToState.ts","../src/actionValidators.ts","../src/actionUtils.ts","../src/pokerHandMutators.ts","../src/pokerHandUtils.ts","../src/handIterator.ts"],"names":["streetList","streetIsPreFlop","__name","street","streetIsPostFlop","isNot","streetIsShowDown","s","getNextStreet","currentStreet","voluntaryActionList","actionList","_actionsRequringAmount","Set","actionRequiresAmount","actionType","has","actionRequiresAmountAddedToPot","t","actionRequiresRaiseAmount","actionMustNotHaveAmounts","actionIsForcedBet","actionIsPost","actionIsStraddle","actionIsVoluntary","cardList","positionsByNumPlayers","allPositions","positions","numberPlayersSeated","getBetLimitType","pokerVariant","Error","playerPosition","player","position","orderPositionList","want","pipe","filter","p","orderPlayerListByPosition","handPlayerList","byPos","indexBy","map","pos","undefined","getFirstToActPreFlop","handPlayers","forcedBets","length","lastForcedBetPosition","playerPositions","nextIndex","indexOf","orderForcedBetsByPosition","fb","forcedBetPosition","getFinalStreet","playerActions","hasFlop","some","action","hasTurn","hasRiver","hasShowdown","activePlayers","hasRiverActions","hasTwoOrMorePlayers","getFirstFlopActionIndex","findIndex","getFirstTurnActionIndex","getFirstRiverActionIndex","getLastActionIndex","isUndefined","toTest","isNotArray","isArray","isValidNonNegativeInteger","value","isNumber","Number","isSafeInteger","isValidPositiveInteger","isNotValidNonNegativeInteger","isNotValidPositiveInteger","isNotPlainObject","isPlainObject","formatMsg","op","message","context","printableContext","mapValues","v","errLines","push","toJson","join","throwIf","condition","warnIf","hasDuplicates","arr","size","hasDuplicatesBy","key","uniqueBy","arraysHaveSameElements","arr1","arr2","counts1","Map","counts2","item","set","get","count","Array","from","entries","arraysDontHaveSameElements","curryLast","fn","args","last","setPathImpl","keys","obj","first","rest","setPath","setImpl","max","firstBy","identity","min","maxBy","criteria","minBy","predicate","_currencyConfigs","USD","code","minorUnitsPerMajor","symbol","decimalPlaces","EUR","GBP","_getCurrencyConfig","_toMajor","minor","currency","config","$","createMinor","isFinite","asMinor","n","toMinor","major","minorValue","format","amount","display","bbAmount","bbPrecision","formatted","toFixed","isInteger","minorToBB","bbToMinor","bb","bigBlindAmount","countPlayersInHand","players","Object","values","hasFolded","curStackSize","countActivePlayers","getMaxPlayerContribution","maxContribution","amtAddedToPotCurStreet","playerStateSanityCheck","playerState","valid","bettingStateSanityCheck","bettingState","options","potSize","prevStreetPotSize","curLiveBet","curMinRaiseIncrement","curMinBetSize","isPreflopWithAntes","skipPotInvariantCheck","expected","spotContextSanityCheck","spotContext","activeStreet","actionIsOn","numPlayersInHand","numActivePlayers","isNullish","playerActionSanityCheck","playerAction","raiseToAmount","amountAddedToPot","expectedDisplay","amountToDisplay","spotStateSanityCheck","spotState","playersInHandState","val","validateSpotState","basicVal","pendingForcedBets","awaitingCommunityCards","actualPlayersInHand","actualCount","actualActivePlayers","inForcedBetPhase","hasGhostPlayers","ghostPositions","maxPlayerContribution","someoneOverpaid","someoneUnderpaid","handStateSanityCheck","pokerHand","mode","playersInHand","positionParam","playerStatePosition","validatePlayerActionCanBeApplied","byGhostPlayer","hasPlayerState","includes","playerActionPosition","actionPosition","actionStreet","isAllIn","raiseByAmount","toCall","expectedAmount","Math","validateAntes","antes","type","bbPlayer","find","hp","startingStack","anteAmount","bbStack","initSpotState","variant","numPlayersSeated","initPlayersInHandState","detectGhostPositions","forcedBetPositions","bigBlindOrHighestStraddle","initBettingState","firstToAct","numVoluntaryActionsCurStreet","initPlayerState","hasActedCurStreet","liveBetAfterLastAction","minRaiseIncrementAfterLastAction","bbOrHighestStraddle","anteType","totalAntes","initSpotContext","uniquePositions","reduce","acc","handPlayer","updatePlayerState","updates","updatePlayersInHandState","byPosition","updateBettingState","updateSpotContext","resetPlayerStatesForNewStreet","fromEntries","resetBettingStateForNewStreet","previousStreet","nextStreet","newCurMinBetSize","newCurMinRaiseIncrement","resetSpotContextForNewStreet","newStreet","expectedPositions","handPlayerPositions","hasFoldedAtSpot","hasNotFoldedAtSpot","isAllInAtSpot","isNotAllInAtSpot","playerIsActiveAtSpot","playerIsNotActiveAtSpot","numOtherActivePlayers","numOtherPlayersInHand","hasActedOnCurStreet","hasNotActedOnCurStreet","hasRaisingRights","doesNotHaveRaisingRights","isPlayerRequiredToAct","hasBettingRights","amtToAddForFullCall","toAddForFullCall","minToAddForFullRaise","minRaiseTo","minAdd","playerCanCall","canCall","reason","fullCallAmt","callAmount","callType","playerCanRaise","canRaise","minFullRaiseAmtToAdd","toCallAmtToAdd","minFullRaiseAmt","toCallAmt","minRaiseToAmt","maxRaiseToAmt","minAmtToAdd","maxAmtToAdd","raiseType","allInAmt","playerCanCheck","canCheck","playerCanBet","canBet","minFullBet","minBetAmt","maxBetAmt","betType","playerCanFold","canFold","activeStreetPlayersAtSpot","pickBy","isNonNullish","activePositionsAtSpot","playersInHandAtSpot","positionsInHandAtSpot","getPlayersRequiredToAct","activeList","getFirstToActOnNewStreet","getNextToActOnCurStreet","currentActionIsOn","playersRequiredToAct","positionsInHand","needToActSet","searchPositions","currentIndex","tableSize","i","nextPosition","curStreetActionClosed","isClosed","getPlayerActionStatus","availableActions","unavailableActions","checkRes","check","callRes","call","betRes","bet","raiseRes","raise","foldRes","fold","getAnteForPosition","getBigBlindAmount","bigBlind","maxForcedBet","initSpotStateFromPokerHand","antePot","positionAnteAmount","handPlayersArray","replayActionsOnSpotState","actions","initialSpotState","cardsNeeded","getTotalCardsNeededForStreet","communityCards","applyActionToSpotState","createSpotStateFromPokerHand","applyActionToHandState","updatedSpotState","updatedPlayerActions","applyActionToBettingState","newPotSize","newAmtAddedToPotCurStreet","newCurLiveBet","sbAmountForLiveBet","intendedForcedBetAmount","forcedBetAmountForLiveBet","newBettingState","applyActionToPlayerState","updatedBettingState","newCurStackSize","newLiveBetAfterLastAction","newMinRaiseIncrementAfterLastAction","newHasActedCurStreet","newHasFolded","newPlayerState","applyActionToSpotContext","updatedPlayersInHandState","newNumPlayersInHand","newNumActivePlayers","newActiveStreet","newActionIsOn","newNumVoluntaryActionsCurStreet","newPendingForcedBets","newAwaitingCommunityCards","remainingForcedBets","slice","result","determineNextAction","newSpotContext","updatedPlayerState","updatedSpotContext","streetAdvanced","finalPlayersInHandState","finalBettingState","handEnded","finalSpotState","mostRecentActionEffects","currentPosition","nextToAct","isValidAction","isValid","validateForcedBet","playerActionStatus","unavailableResult","callInfo","validateCall","raiseInfo","validateRaise","betInfo","validateBet","validateCheck","validateFold","expectedRaiseToAmount","availableActionsAtSpot","callResult","minAmount","maxAmount","betResResult","raiseResult","addActionToHand","hand","actionToAdd","attemptedAction","makePlayerAction","validateActionToAdd","isForcedBet","betAmount","forcedBetAmount","amountToAdd","playerStackSize","inferBetDisplay","gameType","isForcedAction","isNotForcedAction","getFirstVoluntaryActionIndex","getLastForcedActionIndex","lastForcedIdx","trimCommunityCardsToStreet","cards","targetCardCount","preFlop","flop","turn","river","showDown","getCardsForStreet","existingCards","currentCount","dropLastActions","numToDrop","numActions","remainingActions","streetAfterDrop","communityCardsAfterDrop","updatedPokerHand","recomputedSpotState","finalPokerHand","currentPokerHand","currentSpotState","cardsForStreet","addCommunityCards","actionIsOnGhost","validationPosition","initPokerHand","info","venueType","venue","betDisplay","handTimestamp","dealerSeatNum","heroPosition","initHandPlayersByPosition","forcedBet","updatePokerHand","getFirstPosToActPreflop","orderedForcedBets","lastForcedBetIndex","offset","candidate","extractInitialPokerHandFrom","newCardsRequiredForStreet","boardLengthBeforeStreet","streetName","isRunout","isAwaitingCards","hint","requestedStreet","existingBoard","expectedBoardLength","requiredNewCards","duplicateCheck","card","add","updatedCommunityCards","targetStreet","nextActionIsOn","nextAwaitingCommunityCards","advancedSpotContext","advancedPlayers","advancedBetting","advancedSpotState","makeHandIterator","handStart","_fullHand","_communityCards","_actionList","_numActions","_initialPokerHand","_initialSpotState","_startBoundaryIdx","_startBoundaryPokerHand","_startBoundarySpotState","spotStateForAction","cardCount","getCardCountForStreet","_currentPosition","_currentPokerHand","_currentSpotState","_currentAction","_navigateToStreetStart","iteratorContext","firstActionIndex","hasStreet","streetCap","charAt","toUpperCase","targetPosition","getCurrent","rewind","next","Symbol","iterator","done","previous","basePokerHand","baseSpotState","pokerHandForAction","actionApplied","actionIndex","hasNext","hasPrevious","toLast","toFirstVoluntary","lastForcedIndex","toFlop","toTurn","toRiver"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * General utilities for poker hand processing.\n *\n * Provides core functions for:\n * - Position ordering and mapping\n * - Currency formatting\n * - Poker variant configuration\n * - Player list manipulation\n */\n\nimport {\n  pipe, map, filter, indexBy, isNot\n} from 'remeda'\n\n\nimport type {\n  PokerVariant, Street, Position, Card, BetLimitType,\n  HandPlayer, HandPlayerList, VoluntaryAction,\n  ActionType, ForcedBet,\n} from '@pokerland/types'\n\n/**\n * @public\n *\n * Ordered list of streets in a poker hand (excludes showDown).\n */\nexport const streetList: Street[] = ['preFlop', 'flop', 'turn', 'river']\n\n/**\n * @public\n */\nexport const streetIsPreFlop = (street: Street): boolean => street === 'preFlop'\n\n/**\n * @public\n */\nexport const streetIsPostFlop = isNot(streetIsPreFlop)\n\n/**\n * @public\n */\nexport const streetIsShowDown = (s: Street): s is 'showDown' => s === 'showDown'\n\n/**\n * @internal\n *\n * Returns the next street in the betting sequence.\n *\n * @param currentStreet - The current street\n * @returns The next street (preFlop → flop → turn → river → showDown)\n *\n * @example\n * ```ts\n * getNextStreet('preFlop')  // 'flop'\n * getNextStreet('flop')     // 'turn'\n * getNextStreet('turn')     // 'river'\n * getNextStreet('river')    // 'showDown'\n * getNextStreet('showDown') // 'showDown' (terminal state)\n * ```\n */\nexport const getNextStreet = (currentStreet: Street): Street => {\n  switch (currentStreet) {\n  case 'preFlop':\n    return 'flop'\n  case 'flop':\n    return 'turn'\n  case 'turn':\n    return 'river'\n  case 'river':\n    return 'showDown'\n  case 'showDown':\n    return 'showDown' // Terminal state\n  }\n}\n\n/**\n * @public\n *\n * Complete list of voluntary actions available to players.\n */\nexport const voluntaryActionList: VoluntaryAction[] = [ 'call', 'fold', 'check', 'bet', 'raise' ]\n\n/**\n * @public\n *\n * Complete list of actions available to players, including forced bets.\n */\nexport const actionList : ActionType[] = [...voluntaryActionList, 'smallBlind', 'bigBlind', 'straddle' ]\n\nconst _actionsRequringAmount: Set<ActionType> = new Set<ActionType>([\n  'call',\n  'raise',\n  'bet',\n  'smallBlind',\n  'bigBlind',\n  'straddle',\n])\n\n/**\n * @public\n */\nexport const actionRequiresAmount = (actionType: ActionType): boolean =>\n  _actionsRequringAmount.has(actionType)\n\n/**\n * @internal\n */\nexport const actionRequiresAmountAddedToPot = (t: ActionType): boolean =>\n  _actionsRequringAmount.has(t)\n\n/**\n * @internal\n */\nexport const actionRequiresRaiseAmount = (t: ActionType): boolean =>\n  t === 'raise'\n\n/**\n * @internal\n */\nexport const actionMustNotHaveAmounts = (t: ActionType): boolean =>\n  t === 'check' || t === 'fold'\n\n/**\n * @public\n */\nexport const actionIsForcedBet = (actionType: ActionType): boolean =>\n  actionType === 'smallBlind' ||\n    actionType === 'bigBlind' ||\n    actionType === 'straddle'\n\n/**\n * @public\n */\nexport const actionIsPost = (actionType: ActionType): boolean =>\n  actionType === 'smallBlind' ||\n  actionType === 'bigBlind'\n\n/**\n * @public\n */\nexport const actionIsStraddle = (actionType: ActionType): boolean =>\n  actionType === 'straddle'\n\n/**\n * @public\n */\nexport const actionIsVoluntary = isNot(actionIsForcedBet)\n\n/**\n * @public\n *\n * Complete 52-card deck in suit-ordered format. \\\n * Ordered by suit: diamonds, clubs, hearts, spades (d, c, h, s).\n */\nexport const cardList: Card[]  = [\n  'Ad','2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d', 'Td', 'Jd', 'Qd', 'Kd',\n  'Ac','2c', '3c', '4c', '5c', '6c', '7c', '8c', '9c', 'Tc', 'Jc', 'Qc', 'Kc',\n  'Ah','2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh',\n  'As','2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks',\n]\n\n/**\n * Mapping of player counts to their canonical position lists. \\\n * Positions are ordered from small blind through button.\n *\n * Special cases:\n * - 2 players: uses 'SB/BU' (button posts small blind)\n * - 3-10 players: standard position names compressed as table shrinks\n */\nconst positionsByNumPlayers: Record<number, Position[]> = {\n  10: ['SB', 'BB', 'UTG', 'UTG+1', 'UTG+2', 'UTG+3', 'LJ', 'HJ', 'CO', 'BU', ],\n  9: ['SB', 'BB', 'UTG', 'UTG+1', 'UTG+2', 'LJ', 'HJ', 'CO', 'BU', ],\n  8: ['SB', 'BB', 'UTG', 'UTG+1', 'LJ', 'HJ', 'CO', 'BU', ],\n  7: ['SB', 'BB', 'UTG', 'LJ', 'HJ', 'CO', 'BU', ],\n  6: ['SB', 'BB', 'UTG', 'HJ', 'CO', 'BU', ],\n  5: ['SB', 'BB', 'UTG', 'CO', 'BU', ],\n  4: ['SB', 'BB', 'CO', 'BU', ],\n  3: ['SB', 'BB', 'BU', ],\n  2: ['SB/BU', 'BB'],\n}\n\n/**\n * @public\n *\n * All possible positions in canonical order. \\\n * Includes special case of 'SB/BU' for heads-up play.\n */\nexport const allPositions: Position[] = ['SB/BU', ...positionsByNumPlayers[10]]\n\n/**\n * @public\n *\n * Returns the canonical position list for a given table size.\n *\n * @param numberPlayersSeated - Number of players at the table (2-10)\n * @returns Ordered array of positions for that table size, or empty array if invalid\n *\n * @example\n * ```ts\n * positions(6) // ['SB', 'BB', 'UTG', 'HJ', 'CO', 'BU']\n * positions(2) // ['SB/BU', 'BB']\n * positions(11) // []\n * ```\n */\nexport const positions = (numberPlayersSeated: number): Position[] =>\n  positionsByNumPlayers[numberPlayersSeated] ?? []\n\n/**\n * @public\n *\n * Maps a poker variant to its betting structure.\n *\n * @param pokerVariant - The poker variant to query\n * @returns The bet limit type for the variant\n * @throws Error if the variant is not recognized\n *\n * Variant mappings:\n * - nlhe → 'no-limit'\n * - plo  → 'pot-limit'\n *\n * @example\n * ```ts\n * getBetLimitType('nlhe') // 'no-limit'\n * getBetLimitType('plo')  // 'pot-limit'\n * ```\n */\nexport const getBetLimitType = (pokerVariant: PokerVariant): BetLimitType => {\n  switch(pokerVariant) {\n  case 'nlhe':\n    return 'no-limit'\n  case 'plo':\n    return 'pot-limit'\n  default:\n    throw new Error(`getBetLimitType(): Unknown poker variant: ${pokerVariant}`)\n  }\n}\n\n/**\n * @public\n *\n * Extracts the position from a HandPlayer object.\n *\n * @param player - The player object to query\n * @returns The player's position\n *\n * @example\n * ```ts\n * const player: HandPlayer = { position: 'BU', ... }\n * playerPosition(player) // 'BU'\n * ```\n */\nexport const playerPosition = (player: HandPlayer): Position => player.position\n\n/**\n * @public\n *\n * Orders a list of positions according to canonical position order. \\\n * Removes duplicates and filters out invalid positions.\n *\n * @param positions - Array of positions to order (may contain duplicates)\n * @returns Ordered array of positions in canonical order\n *\n * @example\n * ```ts\n * orderPositionList(['BU', 'BB', 'CO', 'BB']) // ['BB', 'CO', 'BU']\n * orderPositionList(['HJ', 'UTG', 'BU'])      // ['UTG', 'HJ', 'BU']\n * ```\n */\nexport const orderPositionList = (positions: Position[]): Position[] =>\n{\n  const want = new Set(positions ?? []) // for fast lookup and dedup\n  return pipe(allPositions, filter(p => want.has(p)))\n}\n\n/**\n * @public\n *\n * Orders a list of players by canonical position order. \\\n * Useful for displaying players in standard table order.\n *\n * @param handPlayerList - Array of players to order\n * @returns New array with players ordered by position\n *\n * @example\n * ```ts\n * const players: HandPlayerList = [\n *   { position: 'BU', ... },\n *   { position: 'BB', ... },\n *   { position: 'CO', ... }\n * ]\n * orderPlayerListByPosition(players) // Returns ordered: BB, CO, BU\n * ```\n */\nexport const orderPlayerListByPosition = (\n  handPlayerList: HandPlayerList\n): HandPlayerList =>\n{\n  const byPos = indexBy(handPlayerList ?? [], playerPosition)\n  return pipe(\n    allPositions, // this is in order\n    map(pos => byPos[pos]), // map to player or undefined if not in incoming list\n    filter((p): p is HandPlayer => p !== undefined), // remove undefineds\n  )\n}\n\n/**\n * @public\n *\n * Determines the first position to act preflop based on forced bets. \\\n * Returns the next position in canonical order after the last/highest forced bet.\n *\n * @param handPlayers - Array of HandPlayer objects in the hand\n * @param forcedBets - Array of forced bets (blinds, straddles) posted preflop\n * @returns Position of first player to act preflop\n *\n * Logic:\n * - If forcedBets is empty, returns first position in handPlayers\n * - Otherwise, finds position of last forced bet (BB or highest straddle)\n * - Returns next position in canonical order after that\n *\n * @example\n * ```ts\n * // Standard 3-handed (SB, BB, BU)\n * const players = [\n *   { position: 'SB', ... },\n *   { position: 'BB', ... },\n *   { position: 'BU', ... }\n * ]\n * const forcedBets = [\n *   { type: 'smallBlind', position: 'SB', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 }\n * ]\n * getFirstToActPreFlop(players, forcedBets) // 'BU'\n *\n * // With button straddle\n * const forcedBetsWithStraddle = [\n *   { type: 'smallBlind', position: 'SB', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 },\n *   { type: 'straddle', position: 'BU', amount: 4 }\n * ]\n * getFirstToActPreFlop(players, forcedBetsWithStraddle) // 'SB'\n *\n * // No forced bets (edge case)\n * getFirstToActPreFlop(players, []) // 'SB' (first player)\n * ```\n */\nexport const getFirstToActPreFlop = (\n  handPlayers: HandPlayer[],\n  forcedBets: ForcedBet[]\n): Position => {\n  // If no forced bets, return first position\n  if (forcedBets.length === 0) {\n    const orderedPlayers = orderPlayerListByPosition(handPlayers)\n    return orderedPlayers[0].position\n  }\n\n  // Get position of last forced bet\n  const lastForcedBet = forcedBets[forcedBets.length - 1]\n  const lastForcedBetPosition = lastForcedBet.position\n\n  // Get ordered positions for players in hand\n  const orderedPlayers = orderPlayerListByPosition(handPlayers)\n  const playerPositions = orderedPlayers.map(p => p.position)\n\n  // Find index of last forced bet position\n  const lastForcedBetIndex = playerPositions.indexOf(lastForcedBetPosition)\n\n  // Next position to act is the one after the last forced bet (wraps around)\n  const nextIndex = (lastForcedBetIndex + 1) % playerPositions.length\n  return playerPositions[nextIndex]\n}\n","import { pipe, map, filter, indexBy } from 'remeda'\n\nimport type { Position, ForcedBet, PlayerAction, Street } from '@pokerland/types'\n\nimport { allPositions } from './generalUtils'\n\n/**\n * @internal\n */\nexport const orderForcedBetsByPosition = (\n  forcedBets: ForcedBet[]\n): ForcedBet[] =>\n{\n  const forcedBetPosition = (fb: ForcedBet): Position => fb.position\n  const byPos = indexBy(forcedBets ?? [], forcedBetPosition)\n  return pipe(\n    allPositions, // this is in order\n    map(pos => byPos[pos]), // map to forced bet or undefined if not in incoming list\n    filter((fb): fb is ForcedBet => fb !== undefined), // remove undefineds\n  )\n}\n\n/**\n * @public\n *\n * Returns the final street that occurred in a hand based on the list of player actions.\n *\n * Simply returns the street of the last action in the list.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns The street of the last action, or 'preFlop' if actions is empty\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },\n *   { street: 'preFlop', position: 'BB', actionType: 'bigBlind', ... },\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },\n * ]\n * getFinalStreet(actions) // 'flop'\n * ```\n */\nexport const getFinalStreet = (playerActions: PlayerAction[]): Street => {\n  if (!playerActions || playerActions.length === 0) {\n    return 'preFlop'\n  }\n\n  return playerActions[playerActions.length - 1].street\n}\n\n/**\n * @public\n *\n * Checks if a hand reached the flop street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns True if any action has street 'flop', false otherwise\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },\n * ]\n * hasFlop(actions) // true\n * ```\n */\nexport const hasFlop = (playerActions: PlayerAction[]): boolean => {\n  if (!playerActions || playerActions.length === 0) {\n    return false\n  }\n\n  return playerActions.some(action => action.street === 'flop')\n}\n\n/**\n * @public\n *\n * Checks if a hand reached the turn street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns True if any action has street 'turn', false otherwise\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },\n *   { street: 'turn', position: 'BB', actionType: 'bet', ... },\n * ]\n * hasTurn(actions) // true\n * ```\n */\nexport const hasTurn = (playerActions: PlayerAction[]): boolean => {\n  if (!playerActions || playerActions.length === 0) {\n    return false\n  }\n\n  return playerActions.some(action => action.street === 'turn')\n}\n\n/**\n * @public\n *\n * Checks if a hand reached the river street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns True if any action has street 'river', false otherwise\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },\n *   { street: 'turn', position: 'BB', actionType: 'check', ... },\n *   { street: 'river', position: 'SB', actionType: 'bet', ... },\n * ]\n * hasRiver(actions) // true\n * ```\n */\nexport const hasRiver = (playerActions: PlayerAction[]): boolean => {\n  if (!playerActions || playerActions.length === 0) {\n    return false\n  }\n\n  return playerActions.some(action => action.street === 'river')\n}\n\n/**\n * @public\n *\n * Checks if a hand reached showdown.\n *\n * A hand reaches showdown if it has river actions AND at least 2 players remain active.\n * Showdown is not a street with actions - it's the final state after river betting\n * completes with 2+ players remaining who will show their cards.\n *\n * @param playerActions - List of player actions from a poker hand\n * @param activePlayers - Number of active players remaining at the end of the hand\n * @returns True if hand has river actions and 2+ players remain, false otherwise\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },\n *   { street: 'river', position: 'SB', actionType: 'check', ... },\n * ]\n * hasShowdown(actions, 2) // true - has river and 2 players\n * hasShowdown(actions, 1) // false - only 1 player remains\n * ```\n */\nexport const hasShowdown = (playerActions: PlayerAction[], activePlayers: number): boolean => {\n  if (!playerActions || playerActions.length === 0) {\n    return false\n  }\n\n  const hasRiverActions = playerActions.some(action => action.street === 'river')\n  const hasTwoOrMorePlayers = activePlayers >= 2\n\n  return hasRiverActions && hasTwoOrMorePlayers\n}\n\n/**\n * @public\n *\n * Finds the index of the first action on the flop street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns Zero-based index of first flop action, or -1 if hand never reached flop\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },  // index 0\n *   { street: 'preFlop', position: 'BB', actionType: 'bigBlind', ... },     // index 1\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },           // index 2\n * ]\n * getFirstFlopActionIndex(actions) // 2\n * ```\n */\nexport const getFirstFlopActionIndex = (playerActions: PlayerAction[]): number => {\n  if (!playerActions || playerActions.length === 0) {\n    return -1\n  }\n\n  return playerActions.findIndex(action => action.street === 'flop')\n}\n\n/**\n * @public\n *\n * Finds the index of the first action on the turn street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns Zero-based index of first turn action, or -1 if hand never reached turn\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },  // index 0\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },           // index 1\n *   { street: 'turn', position: 'BB', actionType: 'bet', ... },             // index 2\n * ]\n * getFirstTurnActionIndex(actions) // 2\n * ```\n */\nexport const getFirstTurnActionIndex = (playerActions: PlayerAction[]): number => {\n  if (!playerActions || playerActions.length === 0) {\n    return -1\n  }\n\n  return playerActions.findIndex(action => action.street === 'turn')\n}\n\n/**\n * @public\n *\n * Finds the index of the first action on the river street.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns Zero-based index of first river action, or -1 if hand never reached river\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },  // index 0\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },           // index 1\n *   { street: 'turn', position: 'BB', actionType: 'check', ... },           // index 2\n *   { street: 'river', position: 'SB', actionType: 'bet', ... },            // index 3\n * ]\n * getFirstRiverActionIndex(actions) // 3\n * ```\n */\nexport const getFirstRiverActionIndex = (playerActions: PlayerAction[]): number => {\n  if (!playerActions || playerActions.length === 0) {\n    return -1\n  }\n\n  return playerActions.findIndex(action => action.street === 'river')\n}\n\n/**\n * @public\n *\n * Finds the index of the last action in a hand.\n *\n * @param playerActions - List of player actions from a poker hand\n * @returns Zero-based index of last action, or -1 if actions list is empty\n *\n * @example\n * ```ts\n * const actions = [\n *   { street: 'preFlop', position: 'SB', actionType: 'smallBlind', ... },  // index 0\n *   { street: 'flop', position: 'SB', actionType: 'check', ... },           // index 1\n *   { street: 'river', position: 'SB', actionType: 'bet', ... },            // index 2\n * ]\n * getLastActionIndex(actions) // 2\n * ```\n */\nexport const getLastActionIndex = (playerActions: PlayerAction[]): number => {\n  if (!playerActions || playerActions.length === 0) {\n    return -1\n  }\n\n  return playerActions.length - 1\n}\n","/**\n * Utility functions for StcLand poker application.\n *\n * Provides core functions for:\n * - Type checking and validation\n * - Error handling and formatting\n * - Array operations and comparisons\n * - Functional programming utilities (currying, immutable updates)\n * - Min/max operations with criteria\n */\n\nimport {\n  isPlainObject, mapValues,\n  // These are used by functions in this file but not exported from index.ts:\n  uniqueBy, isArray, firstBy, identity,\n  isNot, isNumber\n} from 'remeda'\n\nimport { toJson } from '@stcland/utils'\n\n/**\n * @internal\n *\n * Checks if a value is undefined. \\\n * Type predicate for TypeScript narrowing.\n *\n * @param toTest - Value to test\n * @returns True if value is undefined, false otherwise\n *\n * @example\n * ```ts\n * isUndefined(undefined) // true\n * isUndefined(null)      // false\n * isUndefined(0)         // false\n * ```\n */\nexport const isUndefined = (toTest: unknown): toTest is undefined => toTest === undefined\n\n/**\n * @internal\n *\n * Checks if a value is not an array. \\\n * Useful for filtering or validation.\n *\n * @param toTest - Value to test\n * @returns True if value is not an array, false if it is an array\n *\n * @example\n * ```ts\n * isNotArray([])       // false\n * isNotArray({})       // true\n * isNotArray('string') // true\n * ```\n */\nexport const isNotArray = (toTest: any): boolean => !isArray(toTest)\n\n/**\n * @internal\n *\n * Checks if a value is a valid non-negative integer (>= 0).\n *\n * Returns true if the value is:\n * - A number (not a string or other type)\n * - A safe integer (no decimals, within safe integer range)\n * - Non-negative (>= 0, including zero)\n *\n * Use cases:\n * - $.MinorUnits validation (currency amounts in minor units)\n * - Player count validation (numPlayersInHand, numActivePlayers)\n * - Any domain value that must be a non-negative integer\n *\n * @param value - Value to test\n * @returns True if value is a valid non-negative integer, false otherwise\n *\n * @example\n * ```ts\n * isValidNonNegativeInteger(0)         // true  - zero is valid\n * isValidNonNegativeInteger(5)         // true\n * isValidNonNegativeInteger(100)       // true\n * isValidNonNegativeInteger(-1)        // false - negative\n * isValidNonNegativeInteger(3.14)      // false - has decimals\n * isValidNonNegativeInteger(10.5)      // false - has decimals\n * isValidNonNegativeInteger(NaN)       // false - not finite\n * isValidNonNegativeInteger(Infinity)  // false - not safe integer\n * isValidNonNegativeInteger('5')       // false - not a number\n * isValidNonNegativeInteger(null)      // false - not a number\n * isValidNonNegativeInteger(undefined) // false - not a number\n * ```\n */\nexport const isValidNonNegativeInteger = (value: unknown): value is number =>\n  isNumber(value) && Number.isSafeInteger(value) && value >= 0\n\n/**\n * @internal\n *\n * Checks if a value is a valid positive integer (> 0, excludes zero).\n *\n * Returns true if the value is:\n * - A number (not a string or other type)\n * - A safe integer (no decimals, within safe integer range)\n * - Positive (> 0, excluding zero)\n *\n * Use cases:\n * - Bet/raise amount validation (must be positive)\n * - Minimum bet size validation\n * - Any domain value that must be a positive integer\n *\n * @param value - Value to test\n * @returns True if value is a valid positive integer, false otherwise\n *\n * @example\n * ```ts\n * isValidPositiveInteger(5)         // true\n * isValidPositiveInteger(100)       // true\n * isValidPositiveInteger(1)         // true\n * isValidPositiveInteger(0)         // false - zero not positive\n * isValidPositiveInteger(-1)        // false - negative\n * isValidPositiveInteger(3.14)      // false - has decimals\n * isValidPositiveInteger(10.5)      // false - has decimals\n * isValidPositiveInteger(NaN)       // false - not finite\n * isValidPositiveInteger(Infinity)  // false - not safe integer\n * isValidPositiveInteger('5')       // false - not a number\n * isValidPositiveInteger(null)      // false - not a number\n * isValidPositiveInteger(undefined) // false - not a number\n * ```\n */\nexport const isValidPositiveInteger = (value: unknown): value is number =>\n  isNumber(value) && Number.isSafeInteger(value) && value > 0\n\n/**\n * @internal\n *\n * Checks if a value is NOT a valid non-negative integer.\n * Complement of isValidNonNegativeInteger.\n *\n * Returns true if the value is:\n * - Not a number\n * - Not a safe integer (has decimals or outside safe range)\n * - Negative (< 0)\n *\n * @param value - Value to test\n * @returns True if value is not a valid non-negative integer, false otherwise\n *\n * @example\n * ```ts\n * isNotValidNonNegativeInteger(-1)        // true  - negative\n * isNotValidNonNegativeInteger(3.14)      // true  - has decimals\n * isNotValidNonNegativeInteger(NaN)       // true  - not finite\n * isNotValidNonNegativeInteger(Infinity)  // true  - not safe integer\n * isNotValidNonNegativeInteger('5')       // true  - not a number\n * isNotValidNonNegativeInteger(null)      // true  - not a number\n * isNotValidNonNegativeInteger(undefined) // true  - not a number\n * isNotValidNonNegativeInteger(0)         // false - zero is valid\n * isNotValidNonNegativeInteger(5)         // false - positive integer is valid\n * isNotValidNonNegativeInteger(100)       // false - positive integer is valid\n * ```\n */\nexport const isNotValidNonNegativeInteger = isNot(isValidNonNegativeInteger)\n\n/**\n * @internal\n *\n * Checks if a value is NOT a valid positive integer.\n * Complement of isValidPositiveInteger.\n *\n * Returns true if the value is:\n * - Not a number\n * - Not a safe integer (has decimals or outside safe range)\n * - Not positive (<= 0, including zero)\n *\n * @param value - Value to test\n * @returns True if value is not a valid positive integer, false otherwise\n *\n * @example\n * ```ts\n * isNotValidPositiveInteger(0)         // true  - zero not positive\n * isNotValidPositiveInteger(-1)        // true  - negative\n * isNotValidPositiveInteger(3.14)      // true  - has decimals\n * isNotValidPositiveInteger(NaN)       // true  - not finite\n * isNotValidPositiveInteger(Infinity)  // true  - not safe integer\n * isNotValidPositiveInteger('5')       // true  - not a number\n * isNotValidPositiveInteger(null)      // true  - not a number\n * isNotValidPositiveInteger(undefined) // true  - not a number\n * isNotValidPositiveInteger(5)         // false - positive integer is valid\n * isNotValidPositiveInteger(100)       // false - positive integer is valid\n * isNotValidPositiveInteger(1)         // false - positive integer is valid\n * ```\n */\nexport const isNotValidPositiveInteger = isNot(isValidPositiveInteger)\n\n/**\n * @internal\n *\n * Checks if a value is not a plain object. \\\n * Plain objects are created with `{}` or `new Object()`.\n *\n * @param toTest - Value to test\n * @returns True if value is not a plain object, false if it is\n *\n * @example\n * ```ts\n * isNotPlainObject({})           // false\n * isNotPlainObject([])           // true\n * isNotPlainObject(new Date())   // true\n * isNotPlainObject(new Map())    // true\n * ```\n */\nexport const isNotPlainObject = (toTest: any): boolean => !isPlainObject(toTest)\n\n/**\n * @internal\n *\n * Formats a failure message with operation name, message, and optional context. \\\n * Includes a \"Context:\" line whenever `context` is provided (even if `0`, `false`, or `\"\"`). \\\n * For plain-object contexts, converts any `undefined` property values to the string `\"undefined\"` \\\n * so they don't disappear when serialized.\n *\n * @param op - Name of the operation that failed\n * @param message - Human-readable error message\n * @param context - Optional context data to include (can be any type)\n * @returns Formatted error message string\n *\n * @example\n * ```ts\n * formatMsg('validateUser', 'Invalid email', { email: 'bad@' })\n * // 'validateUser failed: Invalid email\\nContext: {\"email\":\"bad@\"}'\n *\n * formatMsg('checkValue', 'Value was zero', { value: 0 })\n * // 'checkValue failed: Value was zero\\nContext: {\"value\":0}'\n *\n * formatMsg('operation', 'Something went wrong')\n * // 'operation failed: Something went wrong'\n * ```\n */\nexport const formatMsg = (\n  op: string,\n  message: string,\n  context?: any\n): string =>\n{\n  const printableContext = isPlainObject(context)\n    ? mapValues(context, (v:any) => isUndefined(v) ? 'undefined' : v  )\n    : context\n\n  const errLines = [`${op} failed: ${message}`]\n  if (context !== undefined) errLines.push('Context: ' + toJson(printableContext))\n\n  return errLines.join('\\n')\n}\n\n/**\n * @internal\n *\n * Throws an Error with a formatted message if `condition` is true. \\\n * No-op if `condition` is false.\n *\n * @param condition - When true, throws an error\n * @param op - Name of the operation being validated\n * @param message - Error message to display\n * @param context - Optional context data to include in error\n * @throws Error with formatted message when condition is true\n *\n * @example\n * ```ts\n * throwIf(amount < 0, 'validateAmount', 'Amount must be positive', { amount })\n * // Throws: Error: validateAmount failed: Amount must be positive\n * //         Context: {\"amount\":-5}\n *\n * throwIf(false, 'test', 'This will not throw')\n * // Does nothing\n * ```\n */\nexport const throwIf = (\n  condition: boolean,\n  op: string,\n  message: string,\n  context?: any\n): void =>\n{\n  if (!condition) return\n  throw new Error(formatMsg(op, message, context))\n}\n\n\n// at lest for dev always throw\nconst throwIfAlways = true\n\n/**\n * @internal\n *\n * Warns (or throws) if `condition` is true. \\\n * NOTE: With the current configuration (`throwIfAlways === true`), this behaves exactly like `throwIf` \\\n * and will throw instead of logging a warning.\n *\n * @param condition - When true, warns or throws\n * @param op - Name of the operation being validated\n * @param message - Warning/error message to display\n * @param context - Optional context data to include\n * @throws Error when condition is true (in current configuration)\n *\n * Future behavior (when `throwIfAlways === false`):\n * - Logs formatted message to console.error instead of throwing\n *\n * @example\n * ```ts\n * warnIf(cache.size > 1000, 'cacheCheck', 'Cache growing large', { size: cache.size })\n * // Currently throws, but would log warning in non-dev mode\n * ```\n */\nexport const warnIf = throwIfAlways\n  ? throwIf\n  : (\n    condition: boolean,\n    op: string,\n    message: string,\n    context?: any\n  ): void => {\n    if (!condition) return\n    console.error(formatMsg(op, message, context))\n  }\n\n/**\n * @internal\n *\n * Checks if an array has duplicate values using primitive/reference equality. \\\n * Uses Set comparison for O(n) performance.\n *\n * @param arr - Array to check for duplicates\n * @returns True if array contains duplicate values, false otherwise\n *\n * @example\n * ```ts\n * hasDuplicates([1, 2, 3])    // false\n * hasDuplicates([1, 2, 1])    // true\n * hasDuplicates(['a', 'b'])   // false\n * hasDuplicates(['a', 'a'])   // true\n * ```\n */\nexport const hasDuplicates = <T>(arr: readonly T[]): boolean =>\n  new Set(arr).size !== arr.length\n\n/**\n * @internal\n *\n * Checks if an array has duplicate values based on a key extraction function. \\\n * Useful for checking uniqueness of object properties.\n *\n * @param arr - Array to check for duplicates\n * @param key - Function to extract comparison key from each element\n * @returns True if array contains duplicates based on key, false otherwise\n *\n * @example\n * ```ts\n * hasDuplicatesBy([{id: 1}, {id: 2}], x => x.id)  // false\n * hasDuplicatesBy([{id: 1}, {id: 1}], x => x.id)  // true\n *\n * const players = [\n *   { name: 'Alice', position: 'BU' },\n *   { name: 'Bob', position: 'BB' },\n *   { name: 'Charlie', position: 'BU' }\n * ]\n * hasDuplicatesBy(players, p => p.position) // true (BU appears twice)\n * ```\n */\nexport const hasDuplicatesBy = <T, K>(\n  arr: readonly T[],\n  key: (x: T) => K\n): boolean => uniqueBy(arr, key).length !== arr.length\n\n/**\n * @internal\n *\n * Checks if two arrays contain exactly the same elements (using Set equality). \\\n * Order doesn't matter; only checks for same elements with no extras in either array.\n *\n * @param arr1 - First array to compare\n * @param arr2 - Second array to compare\n * @returns True if arrays contain identical elements (ignoring order), false otherwise\n *\n * @example\n * ```ts\n * arraysHaveSameElements([1, 2, 3], [3, 2, 1])      // true\n * arraysHaveSameElements([1, 2], [1, 2, 3])         // false\n * arraysHaveSameElements(['a', 'b'], ['a', 'b'])    // true\n * arraysHaveSameElements(['a', 'b'], ['a', 'c'])    // false\n * arraysHaveSameElements([1, 1, 2], [1, 2])         // false (different counts)\n * ```\n */\nexport const arraysHaveSameElements = <T>(\n  arr1: readonly T[],\n  arr2: readonly T[]\n): boolean => {\n  if (arr1.length !== arr2.length) return false\n\n  // Count occurrences of each element in both arrays\n  const counts1 = new Map<T, number>()\n  const counts2 = new Map<T, number>()\n\n  for (const item of arr1) {\n    counts1.set(item, (counts1.get(item) ?? 0) + 1)\n  }\n\n  for (const item of arr2) {\n    counts2.set(item, (counts2.get(item) ?? 0) + 1)\n  }\n\n  // Check if both maps have the same keys and counts\n  if (counts1.size !== counts2.size) return false\n\n  for (const [item, count] of Array.from(counts1.entries())) {\n    if (counts2.get(item) !== count) return false\n  }\n\n  return true\n}\n\n/**\n * @internal\n *\n * Checks if two arrays do NOT contain exactly the same elements. \\\n * Complement of arraysHaveSameElements.\n *\n * @param arr1 - First array to compare\n * @param arr2 - Second array to compare\n * @returns True if arrays differ in elements, false if they match\n *\n * @example\n * ```ts\n * arraysDontHaveSameElements([1, 2], [1, 2, 3])    // true\n * arraysDontHaveSameElements([1, 2, 3], [3, 2, 1]) // false\n * arraysDontHaveSameElements(['a'], ['b'])         // true\n * ```\n */\nexport const arraysDontHaveSameElements = <T>(\n  arr1: readonly T[],\n  arr2: readonly T[]\n): boolean => !arraysHaveSameElements(arr1, arr2)\n\n/**\n * @internal\n *\n * Curries a function using a data-last convention. \\\n * Supports arities 1–6. Non-generic functions retain full type inference. \\\n * For generic source functions, provide explicit generic-over-generic overloads (see maxBy/minBy pattern).\n *\n * Data-last currying allows partial application from the left while keeping the data parameter last, \\\n * making it ideal for use in pipe chains.\n *\n * @param fn - Function to curry\n * @returns Curried function that can be called with all args at once or progressively\n *\n * @example\n * ```ts\n * const add = (a: number, b: number) => a + b\n * const curriedAdd = curryLast(add)\n *\n * curriedAdd(5, 10)        // 15\n * curriedAdd(5)(10)        // 15\n * pipe(10, curriedAdd(5))  // 15\n *\n * const greet = (greeting: string, name: string) => `${greeting}, ${name}!`\n * const sayHello = curryLast(greet)('Hello')\n * sayHello('Alice')        // 'Hello, Alice!'\n * ```\n */\nexport function curryLast<A, R>(\n  fn: (a: A) => R\n): (a: A) => R;\n\n/** @internal */\nexport function curryLast<A, B, R>(\n  fn: (a: A, b: B) => R\n): ((a: A, b: B) => R) & ((a: A) => (b: B) => R);\n\n/** @internal */\nexport function curryLast<A, B, C, R>(\n  fn: (a: A, b: B, c: C) => R\n): ((a: A, b: B, c: C) => R) & ((a: A, b: B) => (c: C) => R);\n\n/** @internal */\nexport function curryLast<A, B, C, D, R>(\n  fn: (a: A, b: B, c: C, d: D) => R\n): ((a: A, b: B, c: C, d: D) => R) & ((a: A, b: B, c: C) => (d: D) => R);\n\n/** @internal */\nexport function curryLast<A, B, C, D, E, R>(\n  fn: (a: A, b: B, c: C, d: D, e: E) => R\n): ((a: A, b: B, c: C, d: D, e: E) => R) & ((a: A, b: B, c: C, d: D) => (e: E) => R);\n\n/** @internal */\nexport function curryLast<A, B, C, D, E, F, R>(\n  fn: (a: A, b: B, c: C, d: D, e: E, f: F) => R\n): ((a: A, b: B, c: C, d: D, e: E, f: F) => R) & ((a: A, b: B, c: C, d: D, e: E) => (f: F) => R);\n\n/** @internal */\nexport function curryLast(fn: any): any {\n  return ((...args: any[]) =>\n    args.length >= fn.length ? fn(...args) : (last: any) => fn(...args, last))\n}\n\nconst setPathImpl = <T = any>(keys: string[], value: any, obj: any): T => {\n  if (keys.length === 0) return value\n  if (keys.length === 1) return { ...obj, [keys[0]]: value } as T\n\n  const [first, ...rest] = keys\n  return {\n    ...obj,\n    [first]: setPathImpl(rest, value, obj?.[first] ?? {})\n  } as T\n}\n\n/**\n * @internal\n *\n * Sets a value at a nested path in an object (immutably). \\\n * Creates intermediate plain objects as needed. Data-last convention for pipe-friendly usage: \\\n * `setPath(['a', 'b', 'c'], value, obj)`.\n *\n * If empty keys array is provided, returns `value` (i.e., replaces the entire object).\n *\n * Curried for use in pipes:\n * - `setPath(keys, value)(obj)` - partial application\n * - `setPath(keys, value, obj)` - full application\n *\n * @param keys - Path keys to navigate/create\n * @param value - Value to set at the path\n * @param obj - Object to update (not mutated)\n * @returns New object with value set at path\n *\n * @example\n * ```ts\n * setPath(['a', 'b'], 99, { a: { b: 1 } })\n * // { a: { b: 99 } }\n *\n * setPath(['a', 'c'], 99, { a: { b: 1 } })\n * // { a: { b: 1, c: 99 } }\n *\n * setPath(['x', 'y', 'z'], 99, {})\n * // { x: { y: { z: 99 } } }\n *\n * // Pipe usage\n * pipe(\n *   { a: 1 },\n *   setPath(['b'], 2),\n *   setPath(['c'], 3)\n * )\n * // { a: 1, b: 2, c: 3 }\n * ```\n */\nexport const setPath: (<T = any>(keys: string[], value: any, obj: any) => T) &\n  (<T = any>(keys: string[], value: any) => (obj: any) => T) = curryLast(setPathImpl)\n\nconst setImpl = <T = any>(key: string, value: any, obj: any): T => {\n  return { ...obj, [key]: value } as T\n}\n\n/**\n * @internal\n *\n * Sets a single top-level property immutably. \\\n * Data-last convention for pipe-friendly usage: `set(key, value, obj)`.\n *\n * Curried for use in pipes:\n * - `set(key, value)(obj)` - partial application\n * - `set(key, value, obj)` - full application\n *\n * @param key - Property name to set\n * @param value - Value to assign\n * @param obj - Object to update (not mutated)\n * @returns New object with property set\n *\n * @example\n * ```ts\n * set('c', 3, { a: 1, b: 2 })\n * // { a: 1, b: 2, c: 3 }\n *\n * set('a', 99, { a: 1 })\n * // { a: 99 }\n *\n * // Pipe usage\n * pipe(\n *   { a: 1 },\n *   set('b', 2),\n *   set('c', 3)\n * )\n * // { a: 1, b: 2, c: 3 }\n * ```\n */\nexport const set: (<T = any>(key: string, value: any, obj: any) => T) &\n  (<T = any>(key: string, value: any) => (obj: any) => T) = curryLast(setImpl)\n\n/**\n * @internal\n *\n * Returns the maximum value from an array of numbers; `undefined` if the array is empty. \\\n * Usable in data-last style (unary).\n *\n * @param arr - Array of numbers to find maximum from\n * @returns Maximum number in array, or undefined if empty\n *\n * @example\n * ```ts\n * max([1, 5, 3, 2])       // 5\n * max([])                 // undefined\n * max([-10, -5, -20])     // -5\n * pipe([1, 5, 3, 2], max) // 5\n * ```\n */\nexport const max = curryLast((\n  arr: number[]\n): number | undefined =>\n  firstBy(arr, [identity(), 'desc'])\n)\n\n/**\n * @internal\n *\n * Returns the minimum value from an array of numbers; `undefined` if the array is empty. \\\n * Usable in data-last style (unary).\n *\n * @param arr - Array of numbers to find minimum from\n * @returns Minimum number in array, or undefined if empty\n *\n * @example\n * ```ts\n * min([1, 5, 3, 2])       // 1\n * min([])                 // undefined\n * min([-10, -5, -20])     // -20\n * pipe([1, 5, 3, 2], min) // 1\n * ```\n */\nexport const min = curryLast((\n  arr: number[]\n): number | undefined =>\n  firstBy(arr, [identity(), 'asc'])\n)\n\n/**\n * @internal\n *\n * Returns the element with the maximum value per `criteria`, or `undefined` for an empty array. \\\n * Curried, data last - ideal for pipe chains.\n *\n * @param criteria - Function to extract numeric value for comparison\n * @param arr - Array of elements to search\n * @returns Element with maximum criteria value, or undefined if array is empty\n *\n * @example\n * ```ts\n * maxBy(x => x.age, [{age: 20}, {age: 30}, {age: 25}])\n * // {age: 30}\n *\n * pipe(\n *   [{age: 20}, {age: 30}, {age: 25}],\n *   maxBy(x => x.age)\n * )\n * // {age: 30}\n *\n * const players = [\n *   { name: 'Alice', stack: 1500 },\n *   { name: 'Bob', stack: 2000 },\n *   { name: 'Charlie', stack: 1200 }\n * ]\n * maxBy(p => p.stack, players)\n * // { name: 'Bob', stack: 2000 }\n * ```\n */\nexport function maxBy<T>(criteria: (item: T) => number, arr: T[]): T | undefined;\n/** @internal */\nexport function maxBy<T>(criteria: (item: T) => number): (arr: T[]) => T | undefined;\n/** @internal */\nexport function maxBy<T>(criteria: (item: T) => number, arr?: T[]): any {\n  if (arr === undefined) {\n    return (arr: T[]) => firstBy(arr, [criteria, 'desc'])\n  }\n  return firstBy(arr, [criteria, 'desc'])\n}\n\n/**\n * @internal\n *\n * Returns the element with the minimum value per `criteria`, or `undefined` for an empty array. \\\n * Curried, data last - ideal for pipe chains.\n *\n * @param criteria - Function to extract numeric value for comparison\n * @param arr - Array of elements to search\n * @returns Element with minimum criteria value, or undefined if array is empty\n *\n * @example\n * ```ts\n * minBy(x => x.age, [{age: 20}, {age: 30}, {age: 25}])\n * // {age: 20}\n *\n * pipe(\n *   [{age: 20}, {age: 30}, {age: 25}],\n *   minBy(x => x.age)\n * )\n * // {age: 20}\n *\n * const players = [\n *   { name: 'Alice', stack: 1500 },\n *   { name: 'Bob', stack: 2000 },\n *   { name: 'Charlie', stack: 1200 }\n * ]\n * minBy(p => p.stack, players)\n * // { name: 'Charlie', stack: 1200 }\n * ```\n */\nexport function minBy<T>(criteria: (item: T) => number, arr: T[]): T | undefined;\n/** @internal */\nexport function minBy<T>(criteria: (item: T) => number): (arr: T[]) => T | undefined;\n/** @internal */\nexport function minBy<T>(criteria: (item: T) => number, arr?: T[]): any {\n  if (arr === undefined) {\n    return (arr: T[]) => firstBy(arr, [criteria, 'asc'])\n  }\n  return firstBy(arr, [criteria, 'asc'])\n}\n\n/**\n * @internal\n *\n * Checks if at least one element in an array satisfies a predicate. \\\n * Wrapper around Array.prototype.some with predicate-first, data-last convention.\n *\n * @param predicate - Function to test each element\n * @param arr - Array to test\n * @returns True if at least one element satisfies the predicate, false otherwise\n *\n * @example\n * ```ts\n * some(x => x > 5, [1, 2, 3, 4, 5, 6])  // true\n * some(x => x > 10, [1, 2, 3])          // false\n * some(x => x.isActive, [{isActive: false}, {isActive: true}])  // true\n * pipe([1, 2, 3], some(x => x === 2))   // true\n * ```\n */\nexport function some<T>(predicate: (item: T) => boolean, arr: T[]): boolean;\n/** @internal */\nexport function some<T>(predicate: (item: T) => boolean): (arr: T[]) => boolean;\n/** @internal */\nexport function some<T>(predicate: (item: T) => boolean, arr?: T[]): any {\n  if (arr === undefined) {\n    return (arr: T[]) => arr.some(predicate)\n  }\n  return arr.some(predicate)\n}\n","/**\n * Currency utilities implementing the $ namespace.\n *\n * All currency amounts are stored internally as minor units (integers) to avoid\n * floating-point precision errors. This module provides the complete $ API for\n * poker-aware currency handling.\n *\n * ## Import Note\n *\n * This module **merges** the type definitions from `@pokerland/types` with runtime\n * function implementations. When you import `$` from this module, you get both:\n * - Type definitions (`$.MinorUnits`, `$.CurrencyCode`)\n * - Runtime functions (`$.createMinor()`, `$.asMinor()`, etc.)\n *\n * **Recommended usage:**\n * ```ts\n * import { $ } from '@pokerland/utils/currencyUtils'\n *\n * const amount: $.MinorUnits = $.createMinor(1000)  // ✅ Both types and functions work\n * ```\n *\n * See poker.d.ts for full API documentation on the `$` namespace.\n */\n\nimport type { $ as _DollarTypes, BetDisplay } from '@pokerland/types'\nimport { throwIf } from './utilsForStcLand'\n\n// ============================================================================\n// Internal Currency Configurations\n// ============================================================================\n\nconst _currencyConfigs: Record<_DollarTypes.CurrencyCode, CurrencyConfig> = {\n  USD: {\n    code: 'USD',\n    minorUnitsPerMajor: 100,\n    symbol: '$',\n    decimalPlaces: 2,\n  },\n  EUR: {\n    code: 'EUR',\n    minorUnitsPerMajor: 100,\n    symbol: '€',\n    decimalPlaces: 2,\n  },\n  GBP: {\n    code: 'GBP',\n    minorUnitsPerMajor: 100,\n    symbol: '£',\n    decimalPlaces: 2,\n  },\n}\n\ninterface CurrencyConfig {\n  code: _DollarTypes.CurrencyCode\n  minorUnitsPerMajor: number\n  symbol: string\n  decimalPlaces: number\n}\n\nconst _getCurrencyConfig = (code: _DollarTypes.CurrencyCode): CurrencyConfig => {\n  return _currencyConfigs[code]\n}\n\n// Internal helper for formatting (needs to be before namespace to avoid circular reference)\nconst _toMajor = (minor: _DollarTypes.MinorUnits, currency: _DollarTypes.CurrencyCode) => {\n  const config = _getCurrencyConfig(currency)\n  return minor / config.minorUnitsPerMajor\n}\n\n// ============================================================================\n// $ Namespace Implementation - Merges with @pokerland/types\n// ============================================================================\n\n/**\n * @public\n *\n * Currency utilities namespace merging type definitions with runtime implementations.\n *\n * This namespace combines:\n * - Type definitions from `@pokerland/types`\n * - Runtime function implementations from this module\n *\n * **For consumers:** Import this namespace to get both types and functions in a single import.\n */\nexport namespace $ {\n  // ============================================================================\n  // Re-export Types from @pokerland/types\n  // ============================================================================\n\n  /**\n   * @public\n   *\n   * Branded type for currency amounts in minor units (cents).\n   *\n   * Ensures type safety by preventing accidental mixing of raw numbers with currency values.\n   * All currency amounts should be stored using this type.\n   *\n   * @example\n   * ```ts\n   * const stack: $.MinorUnits = $.createMinor(5000)  // $50.00\n   * const bet: $.MinorUnits = $.createMinor(1000)    // $10.00\n   * const newStack = $.asMinor(stack - bet)          // $40.00\n   * ```\n   */\n  export type MinorUnits = _DollarTypes.MinorUnits\n\n  /**\n   * @public\n   *\n   * Supported currency codes.\n   *\n   * Currently supports USD, EUR, and GBP. All use 100 minor units per major unit\n   * and 2 decimal places.\n   *\n   * @example\n   * ```ts\n   * const currency: $.CurrencyCode = 'USD'\n   * const amount = $.toMinor(10.50, currency)  // 1050 minor units\n   * ```\n   */\n  export type CurrencyCode = _DollarTypes.CurrencyCode\n\n  // ============================================================================\n  // Runtime Functions\n  // ============================================================================\n\n  /**\n   * @public\n   *\n   * Creates a validated `$.MinorUnits` value from a raw number.\n   *\n   * Use when creating new currency values from scratch or converting external input.\n   * Validates that the value is a non-negative safe integer.\n   *\n   * @param value - Integer value in minor units\n   * @returns Validated MinorUnits\n   * @throws Error if value is not a finite number\n   * @throws Error if value is not a safe integer\n   * @throws Error if value is negative\n   *\n   * @example\n   * ```ts\n   * const stack = $.createMinor(5000)      // $50.00\n   * const pot = $.createMinor(12500)       // $125.00\n   * const zero = $.createMinor(0)          // $0.00\n   *\n   * // These throw errors:\n   * $.createMinor(10.5)   // Not an integer\n   * $.createMinor(-100)   // Negative\n   * $.createMinor(NaN)    // Not finite\n   * ```\n   */\n  export const createMinor = (value: number): MinorUnits => {\n    throwIf(\n      !Number.isFinite(value),\n      '$.createMinor()',\n      'value must be a finite number',\n      { value }\n    )\n\n    throwIf(\n      !Number.isSafeInteger(value),\n      '$.createMinor()',\n      'value must be a safe integer (no decimals)',\n      { value }\n    )\n\n    throwIf(\n      value < 0,\n      '$.createMinor()',\n      'value must be non-negative',\n      { value }\n    )\n\n    return value as MinorUnits\n  }\n\n  /**\n   * @public\n   *\n   * Recasts a number to `$.MinorUnits` after arithmetic operations.\n   *\n   * Use when arithmetic operations strip the branded type and you need to recast the result.\n   * Performs identical validation to `createMinor()`, but the semantic intent is\n   * \"recasting after arithmetic\" rather than \"creating from scratch\".\n   *\n   * @param n - Number to validate and cast (must be non-negative safe integer)\n   * @returns Validated MinorUnits\n   * @throws Error if value is not a finite number\n   * @throws Error if value is not a safe integer\n   * @throws Error if value is negative\n   *\n   * @example\n   * ```ts\n   * const stack = $.createMinor(5000)\n   * const bet = $.createMinor(1000)\n   *\n   * // Arithmetic strips the branded type\n   * const newStack = $.asMinor(stack - bet)        // $40.00\n   * const total = $.asMinor(stack + bet)           // $60.00\n   * const maxBet = $.asMinor(Math.min(stack, 500)) // $5.00\n   * ```\n   */\n  export const asMinor = (n: number): MinorUnits => {\n    throwIf(\n      !Number.isFinite(n),\n      '$.asMinor()',\n      'value must be a finite number',\n      { value: n }\n    )\n\n    throwIf(\n      !Number.isSafeInteger(n),\n      '$.asMinor()',\n      'value must be a safe integer (no decimals)',\n      { value: n }\n    )\n\n    throwIf(\n      n < 0,\n      '$.asMinor()',\n      'value must be non-negative',\n      { value: n }\n    )\n\n    return n as MinorUnits\n  }\n\n  /**\n   * @public\n   *\n   * Converts major currency units (user input) to minor units for storage.\n   *\n   * Use when converting user-entered amounts (e.g., \"$10.50\") to internal minor units.\n   * Validates that conversion results in an integer (no precision loss).\n   *\n   * @param major - Amount in major units (e.g., 10.50 for $10.50)\n   * @param currency - Currency code\n   * @returns Amount in minor units\n   * @throws Error if major is not a finite number\n   * @throws Error if conversion results in non-integer (precision loss)\n   *\n   * @example\n   * ```ts\n   * // User enters \"$25.50\" in USD\n   * const userInput = 25.50\n   * const amount = $.toMinor(userInput, 'USD')  // 2550 minor units\n   *\n   * // Different currencies\n   * $.toMinor(100.00, 'EUR')  // 10000 minor units (€100.00)\n   * $.toMinor(50.25, 'GBP')   // 5025 minor units (£50.25)\n   *\n   * // This throws (precision loss):\n   * $.toMinor(10.505, 'USD')  // Would be 1050.5 minor units\n   * ```\n   */\n  export const toMinor = (major: number, currency: CurrencyCode): MinorUnits => {\n    throwIf(\n      !Number.isFinite(major),\n      '$.toMinor()',\n      'major must be a finite number',\n      { major, currency }\n    )\n\n    const config = _getCurrencyConfig(currency)\n    const minorValue = major * config.minorUnitsPerMajor\n\n    throwIf(\n      !Number.isSafeInteger(minorValue),\n      '$.toMinor()',\n      'conversion results in non-integer (precision loss)',\n      { major, currency, minorValue }\n    )\n\n    return createMinor(minorValue)\n  }\n\n  /**\n   * @public\n   *\n   * Formats a currency amount for user display.\n   *\n   * Supports two display modes:\n   * - **'currency'**: Standard currency format with symbol (e.g., \"$10.50\")\n   * - **'bigBlinds'**: Big blind relative format (e.g., \"5.25 BB\")\n   *\n   * @param amount - Amount in minor units\n   * @param currency - Currency code\n   * @param display - Display mode: 'currency' or 'bigBlinds'\n   * @param bbAmount - Value of one big blind in minor units (required for 'bigBlinds' mode)\n   * @param bbPrecision - Number of decimal places for BB display (default: 2)\n   * @returns Formatted string\n   * @throws Error if display is 'bigBlinds' but bbAmount not provided\n   * @throws Error if bbAmount is non-positive (when provided)\n   * @throws Error if bbPrecision is not a non-negative integer\n   *\n   * @example\n   * ```ts\n   * const amount = $.createMinor(1050)  // $10.50\n   * const currency = 'USD'\n   * const bbAmount = $.createMinor(200) // BB is $2.00\n   *\n   * // Currency mode\n   * $.format(amount, currency, 'currency')\n   * // \"$10.50\"\n   *\n   * // Big blinds mode (10.50 / 2.00 = 5.25 BB)\n   * $.format(amount, currency, 'bigBlinds', bbAmount)\n   * // \"5.25 BB\"\n   *\n   * $.format(amount, currency, 'bigBlinds', bbAmount, 1)\n   * // \"5.3 BB\"\n   *\n   * // Different currencies\n   * $.format($.createMinor(1050), 'EUR', 'currency')\n   * // \"€10.50\"\n   * ```\n   */\n  export const format = (\n    amount: MinorUnits,\n    currency: CurrencyCode,\n    display: BetDisplay,\n    bbAmount?: MinorUnits,\n    bbPrecision: number = 2\n  ): string => {\n    if (display === 'currency') {\n      const config = _getCurrencyConfig(currency)\n      const major = _toMajor(amount, currency)\n      const formatted = major.toFixed(config.decimalPlaces)\n      return `${config.symbol}${formatted}`\n    }\n\n    // 'bigBlinds' mode\n    throwIf(\n      bbAmount === undefined,\n      '$.format()',\n      'bbAmount is required for bigBlinds display mode',\n      { display, amount, currency }\n    )\n\n    throwIf(\n      bbAmount! <= 0,\n      '$.format()',\n      'bbAmount must be positive',\n      { bbAmount, display, amount, currency }\n    )\n\n    throwIf(\n      !Number.isInteger(bbPrecision) || bbPrecision < 0,\n      '$.format()',\n      'bbPrecision must be a non-negative integer',\n      { bbPrecision, display, amount, currency }\n    )\n\n    const bbValue = minorToBB(amount, bbAmount!)\n    return `${bbValue.toFixed(bbPrecision)} BB`\n  }\n\n  /**\n   * @public\n   *\n   * Converts big blinds to minor currency units.\n   *\n   * Use when converting poker-relative amounts (e.g., \"2.5 BB\") to absolute currency values.\n   * Validates that conversion results in an integer (no fractional minor units).\n   *\n   * @param bb - Amount in big blinds (can be fractional, e.g., 2.5)\n   * @param bigBlindAmount - Value of one big blind in minor units\n   * @returns Amount in minor units\n   * @throws Error if bb is not a finite number\n   * @throws Error if bigBlindAmount is not positive\n   * @throws Error if conversion results in non-integer or unsafe value\n   *\n   * @example\n   * ```ts\n   * const bbAmount = $.createMinor(200)  // BB is $2.00\n   *\n   * $.bbToMinor(2.5, bbAmount)   // 500 minor units ($5.00)\n   * $.bbToMinor(100, bbAmount)   // 20000 minor units ($200.00)\n   * $.bbToMinor(0.5, bbAmount)   // 100 minor units ($1.00)\n   *\n   * // Tournament: 1000 chips = 1 BB\n   * const tourneyBB = $.createMinor(1000)\n   * $.bbToMinor(25, tourneyBB)   // 25000 chips\n   * ```\n   */\n  export const bbToMinor = (bb: number, bigBlindAmount: MinorUnits): MinorUnits => {\n    throwIf(\n      !Number.isFinite(bb),\n      '$.bbToMinor()',\n      'bb must be a finite number',\n      { bb, bigBlindAmount }\n    )\n\n    throwIf(\n      bigBlindAmount <= 0,\n      '$.bbToMinor()',\n      'bigBlindAmount must be positive',\n      { bb, bigBlindAmount }\n    )\n\n    const minorValue = bb * bigBlindAmount\n\n    throwIf(\n      !Number.isSafeInteger(minorValue),\n      '$.bbToMinor()',\n      'conversion results in non-integer or unsafe value',\n      { bb, bigBlindAmount, minorValue }\n    )\n\n    return asMinor(minorValue)\n  }\n\n  /**\n   * @public\n   *\n   * Converts minor currency units to big blinds.\n   *\n   * Use when displaying amounts relative to the big blind (common in poker analysis).\n   * Returns a decimal number (e.g., 2.5 for 2.5 BB).\n   *\n   * @param minor - Amount in minor units\n   * @param bigBlindAmount - Value of one big blind in minor units\n   * @returns Amount in big blinds (decimal)\n   * @throws Error if bigBlindAmount is not positive\n   *\n   * @example\n   * ```ts\n   * const bbAmount = $.createMinor(200)  // BB is $2.00\n   *\n   * $.minorToBB($.createMinor(500), bbAmount)    // 2.5 BB\n   * $.minorToBB($.createMinor(20000), bbAmount)  // 100 BB\n   * $.minorToBB($.createMinor(100), bbAmount)    // 0.5 BB\n   *\n   * // Calculate effective stack in BB\n   * const heroStack = $.createMinor(10000)  // $100\n   * const villainStack = $.createMinor(25000)  // $250\n   * const effectiveBB = Math.min(\n   *   $.minorToBB(heroStack, bbAmount),\n   *   $.minorToBB(villainStack, bbAmount)\n   * )  // 50 BB (min of 50 and 125)\n   * ```\n   */\n  export const minorToBB = (minor: MinorUnits, bigBlindAmount: MinorUnits): number => {\n    throwIf(\n      bigBlindAmount <= 0,\n      '$.minorToBB()',\n      'bigBlindAmount must be positive',\n      { minor, bigBlindAmount }\n    )\n\n    return minor / bigBlindAmount\n  }\n}\n","import type { PlayerStateByPosition } from '@pokerland/types'\n\n/**\n * @internal\n *\n * Counts players still in the hand who can win pots at showdown.\n * Includes all-in players (curStackSize = 0) since they can win pots.\n * Excludes folded players.\n *\n * Used for:\n * - Pot distribution\n * - Showdown eligibility\n * - Determining when hand ends (0-1 players → hand over)\n */\nexport const countPlayersInHand = (players: PlayerStateByPosition): number => {\n  if (!players) return 0\n  let count = 0\n  for (const player of Object.values(players)) {\n    if (player && !player.hasFolded && player.curStackSize >= 0)\n      count += 1\n  }\n  return count\n}\n\n/**\n * @internal\n *\n * Counts players who can still take betting actions.\n * Excludes all-in players (curStackSize = 0) since they cannot act.\n * Excludes folded players.\n *\n * Used for:\n * - Betting closure checks (≤1 active → betting closed)\n * - Raise/bet eligibility\n * - Action sequencing and turn order\n *\n * See also: {@link countPlayersInHand} for pot/showdown-related counting\n */\nexport const countActivePlayers = (players: PlayerStateByPosition): number => {\n  if (!players) return 0\n  let count = 0\n  for (const player of Object.values(players)) {\n    if (player && !player.hasFolded && player.curStackSize > 0)\n      count += 1\n  }\n  return count\n}\n\n/**\n * @internal\n *\n * Gets the maximum amtAddedToPotCurStreet across all players.\n *\n * This represents the current live bet amount - the highest amount any player\n * has contributed on the current street.\n *\n * Used for:\n * - Validating curLiveBet consistency (curLiveBet should equal max contribution)\n * - Determining call amounts (players must match this to call)\n * - Cross-checking state invariants\n *\n * @param players - Player states to check\n * @returns Maximum contribution amount in minor units, or 0 if no players\n *\n * @example\n * ```ts\n * const players = {\n *   SB: { amtAddedToPotCurStreet: 100, ... },\n *   BB: { amtAddedToPotCurStreet: 200, ... },\n *   BU: { amtAddedToPotCurStreet: 150, ... }\n * }\n * getMaxPlayerContribution(players) // Returns 200\n * ```\n */\nexport const getMaxPlayerContribution = (players: PlayerStateByPosition): number => {\n  if (!players) return 0\n  let maxContribution = 0\n  for (const player of Object.values(players)) {\n    if (player && player.amtAddedToPotCurStreet > maxContribution) {\n      maxContribution = player.amtAddedToPotCurStreet\n    }\n  }\n  return maxContribution\n}\n","import { formatMsg, some, isNotValidNonNegativeInteger } from './utilsForStcLand'\n/**\n * State validation functions for poker hand processing.\n *\n * Provides validators that ensure state objects contain valid values.\n * These can be used both before and after state transformations to\n * catch invalid inputs and calculation bugs.\n */\nimport { isNullish } from 'remeda'\n\nimport type {\n  PlayerState, BettingState, SpotContext, PlayerAction,\n  Position, PokerHand, SpotState, Ante, HandPlayer,\n} from '@pokerland/types'\n\nimport { countActivePlayers, countPlayersInHand, getMaxPlayerContribution } from './stateHelpers'\n\n/**\n * Result of a validation check.\n *\n * @property valid - True if validation passed, false otherwise\n * @property message - Optional error message when validation fails (undefined when valid is true)\n */\nexport interface ValidationStatus {\n  valid: boolean\n  message?: string\n}\n\n/**\n * @internal\n *\n * Validates that a PlayerState object has all required fields with valid values.\n *\n * @param playerState - The player state to validate\n * @param op - Operation being performed during state validation\n * @returns ValidationStatus indicating if validation passed\n */\nexport const playerStateSanityCheck = (\n  playerState: PlayerState | undefined | null,\n  op: string = 'validatePlayerState()',\n): ValidationStatus => {\n\n  if (!playerState) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'playerState is null or undefined', { playerState }\n      )\n    }\n  }\n\n  const { curStackSize, amtAddedToPotCurStreet } = playerState\n\n  if (some(isNotValidNonNegativeInteger, [curStackSize, amtAddedToPotCurStreet])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'invalid player state amounts',\n        { curStackSize, amtAddedToPotCurStreet }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates that a BettingState object has all required fields with valid values.\n *\n * @param bettingState - The betting state to validate\n * @param op - Operation being performed during state validation\n * @param options - Optional context: skipPotInvariantCheck to bypass pot tracking invariant validation\n * @returns ValidationStatus indicating if validation passed\n */\nexport const bettingStateSanityCheck = (\n  bettingState: BettingState | undefined | null,\n  op: string = 'validateBettingState()',\n  options?: { skipPotInvariantCheck?: boolean },\n): ValidationStatus => {\n\n  if (!bettingState) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'bettingState is null or undefined',\n        { bettingState }\n      )\n    }\n  }\n\n  const {\n    potSize, prevStreetPotSize, amtAddedToPotCurStreet, curLiveBet, curMinRaiseIncrement, curMinBetSize\n  } = bettingState\n\n  if (some(isNotValidNonNegativeInteger,\n    [potSize, prevStreetPotSize, amtAddedToPotCurStreet, curLiveBet, curMinRaiseIncrement, curMinBetSize]))\n  {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'invalid betting state amounts',\n        { potSize, prevStreetPotSize, amtAddedToPotCurStreet, curLiveBet, curMinRaiseIncrement, curMinBetSize }\n      )\n    }\n  }\n\n  // Semantic check: amtAddedToPotCurStreet cannot exceed potSize\n  if (amtAddedToPotCurStreet > potSize) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'amtAddedToPotCurStreet exceeds potSize',\n        { amtAddedToPotCurStreet, potSize }\n      )\n    }\n  }\n\n  // Semantic check: prevStreetPotSize cannot exceed current potSize (pot cannot shrink)\n  if (prevStreetPotSize > potSize) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'prevStreetPotSize exceeds current potSize (pot cannot shrink)',\n        { prevStreetPotSize, potSize }\n      )\n    }\n  }\n\n  // Semantic check: Core invariant for pot tracking\n  // potSize should equal previous pot + amount added this street\n  //\n  // Exception: On preflop with antes, we have prevStreetPotSize = 0 (new policy) but potSize\n  // includes antes that were posted. This temporarily violates the invariant throughout preflop\n  // until we transition to flop (which bookmarks total pot to prevStreetPotSize).\n  //\n  // We detect this scenario by: prevStreetPotSize = 0 AND potSize > amtAddedToPotCurStreet\n  // (indicating there's money in the pot beyond current street contributions, i.e., from antes)\n  //\n  // OR by the explicit skipPotInvariantCheck option\n  const isPreflopWithAntes = prevStreetPotSize === 0 && potSize > amtAddedToPotCurStreet\n  const skipCheck = options?.skipPotInvariantCheck || isPreflopWithAntes\n\n  if (!skipCheck && potSize !== prevStreetPotSize + amtAddedToPotCurStreet) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'pot tracking invariant violated: potSize !== prevStreetPotSize + amtAddedToPotCurStreet',\n        { potSize, prevStreetPotSize, amtAddedToPotCurStreet, expected: prevStreetPotSize + amtAddedToPotCurStreet }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates that a SpotContext object has all required fields with valid values.\n *\n * @param spotContext - The spot context to validate\n * @param op - Operation being performed during state validation\n * @returns ValidationStatus indicating if validation passed\n */\nexport const spotContextSanityCheck = (\n  spotContext: SpotContext | undefined | null,\n  op: string = 'validateSpotContext()',\n): ValidationStatus => {\n\n  if (!spotContext) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'spotContext is null or undefined', { spotContext })\n    }\n  }\n\n  const {\n    activeStreet,\n    actionIsOn,\n    numPlayersInHand,\n    numActivePlayers,\n  } = spotContext\n\n  if (some(isNullish, [activeStreet, actionIsOn])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'activeStreet or actionIsOn is null or undefined',\n        { activeStreet, actionIsOn }\n      )\n    }\n  }\n\n  if (isNotValidNonNegativeInteger(numPlayersInHand)) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'invalid numPlayersInHand', { numPlayersInHand }\n      )\n    }\n  }\n\n  if (isNotValidNonNegativeInteger(numActivePlayers)) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'invalid numActivePlayers', { numActivePlayers }\n      )\n    }\n  }\n\n  if (numActivePlayers > numPlayersInHand) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        'numActivePlayers exceeds numPlayersInHand',\n        { numActivePlayers, numPlayersInHand }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates that a PlayerAction object has all required fields with valid values.\n *\n * @param playerAction - The player action to validate\n * @param op - Operation being performed during state validation\n * @returns ValidationStatus indicating if validation passed\n */\nexport const playerActionSanityCheck = (\n  playerAction: PlayerAction | undefined | null,\n  op: string = 'validateAction()',\n): ValidationStatus => {\n\n  if (!playerAction) {\n    return {\n      valid: false,\n      message: formatMsg(op, 'playerAction is null or undefined', { playerAction })\n    }\n  }\n\n  const { actionType, raiseToAmount, amountAddedToPot, position } = playerAction\n\n  if (some(isNullish, [actionType, position])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'actionType or position is null or undefined',\n        { actionType, position }\n      )\n    }\n  }\n\n  if (some(isNotValidNonNegativeInteger, [raiseToAmount, amountAddedToPot])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'invalid action amounts',\n        { actionType, raiseToAmount, amountAddedToPot }\n      )\n    }\n  }\n\n  const expectedDisplay = actionType === 'raise'\n    ? raiseToAmount\n    : amountAddedToPot\n  const amountToDisplay = playerAction.amountToDisplay ?? expectedDisplay\n\n  if (amountToDisplay !== expectedDisplay) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        'amountToDisplay inconsistent with action amounts',\n        { actionType, amountToDisplay, expectedDisplay }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates spot state components independently of PokerHand.\n * Performs comprehensive validation of spot state integrity.\n *\n * Checks performed:\n * - All state objects are non-null/non-undefined\n * - Individual state validity (via bettingStateSanityCheck, spotContextSanityCheck, playerStateSanityCheck)\n * - Cross-state consistency: each player's pot contribution doesn't exceed total pot size\n *\n * This validator is PokerHand-independent and can be used by functions that operate\n * purely on SpotState (e.g., applyActionToSpotState, dropLastActions).\n *\n * @param spotState - The spot state to validate\n * @param op - Operation being performed during validation (for error messages)\n * @returns ValidationStatus indicating if validation passed\n */\nexport const spotStateSanityCheck = (\n  spotState: SpotState,\n  op: string = 'spotStateSanityCheck()',\n): ValidationStatus => {\n\n  const { playersInHandState, bettingState, spotContext } = spotState\n  if (some(isNullish, [playersInHandState, bettingState, spotContext])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'one or more spotState objects are null or undefined',\n        { playersInHandState, bettingState, spotContext }\n      )}}\n\n  // Validate individual states first\n  let val = bettingStateSanityCheck(bettingState, op)\n  if (!val.valid) { return val }\n\n  val = spotContextSanityCheck(spotContext, op)\n  if (!val.valid) { return val }\n\n  // Validate each player state\n  for (const [pos, playerState] of Object.entries(playersInHandState)) {\n    if (playerState) {\n      val = playerStateSanityCheck(playerState, op)\n      if (!val.valid) { return val }\n\n      // Cross-state consistency check: player's contribution doesn't exceed pot\n      if (playerState.amtAddedToPotCurStreet > bettingState.potSize) {\n        return {\n          valid: false,\n          message: formatMsg(\n            op,\n            `player's contribution this street (${playerState.amtAddedToPotCurStreet}) exceeds total pot size (${bettingState.potSize})`,\n            { position: pos, amtAddedToPotCurStreet: playerState.amtAddedToPotCurStreet, potSize: bettingState.potSize }\n          )\n        }\n      }\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates spot state with comprehensive cross-checks between components.\n * Throws an error if validation fails.\n *\n * This function:\n * 1. Calls spotStateSanityCheck() for basic validation\n * 2. Performs additional cross-checks:\n *    - actionIsOn position exists in playersInHandState (if not 'hand-ended')\n *    - numPlayersInHand matches count of non-folded players\n *    - numActivePlayers matches count of active (not folded, has chips) players\n *    - curLiveBet equals max amtAddedToPotCurStreet across all players (with exceptions\n *      for forced bet phase, ghost players, button double-post, and forced bet all-in-for-less)\n *\n * Use this when you want validation that throws on failure (cleaner than checking return values).\n *\n * @param spotState - The spot state to validate\n * @param op - Operation being performed during validation (for error messages)\n * @throws Error if validation fails with detailed message\n *\n * @example\n * ```ts\n * // Throws if spotState is invalid\n * validateSpotState(spotState, 'myFunction()')\n *\n * // If we get here, spotState is valid\n * doSomethingWithSpotState(spotState)\n * ```\n */\nexport const validateSpotState = (\n  spotState: SpotState,\n  op: string = 'validateSpotState()',\n): void => {\n  // First run basic sanity checks\n  const basicVal = spotStateSanityCheck(spotState, op)\n  if (!basicVal.valid) {\n    throw new Error(basicVal.message)\n  }\n\n  const { playersInHandState, bettingState, spotContext } = spotState\n\n  // Cross-check 1: actionIsOn position exists in playersInHandState\n  // Skip validation in transitional states:\n  // - During forced bet posting phase: actionIsOn can point to ghost positions (not in playersInHandState)\n  // - When awaiting community cards: actionIsOn points to future actor (can't act until cards dealt)\n  // Only validate actionIsOn exists in playersInHandState during normal voluntary action phase\n  if (spotContext.actionIsOn !== 'hand-ended' &&\n      spotContext.actionIsOn !== 'runout-pending' &&\n      spotContext.actionIsOn !== 'hand-not-started' &&\n      !spotContext.pendingForcedBets &&  // Skip during forced bet posting\n      !spotContext.awaitingCommunityCards) {  // Skip when awaiting cards\n    const actionIsOnPlayer = playersInHandState[spotContext.actionIsOn]\n    if (!actionIsOnPlayer) {\n      throw new Error(\n        formatMsg(\n          op,\n          `actionIsOn position (${spotContext.actionIsOn}) not found in playersInHandState`,\n          { actionIsOn: spotContext.actionIsOn, positions: Object.keys(playersInHandState) }\n        )\n      )\n    }\n  }\n\n  // Cross-check 2: numPlayersInHand matches count of non-folded players\n  const actualPlayersInHand = countPlayersInHand(playersInHandState)\n  if (spotContext.numPlayersInHand !== actualPlayersInHand) {\n    throw new Error(\n      formatMsg(\n        op,\n        `numPlayersInHand (${spotContext.numPlayersInHand}) does not match actual count of non-folded players (${actualPlayersInHand})`,\n        { numPlayersInHand: spotContext.numPlayersInHand, actualCount: actualPlayersInHand }\n      )\n    )\n  }\n\n  // Cross-check 3: numActivePlayers matches count of active players (not folded AND has chips)\n  const actualActivePlayers = countActivePlayers(playersInHandState)\n  if (spotContext.numActivePlayers !== actualActivePlayers) {\n    throw new Error(\n      formatMsg(\n        op,\n        `numActivePlayers (${spotContext.numActivePlayers}) does not match actual count of active players (${actualActivePlayers})`,\n        { numActivePlayers: spotContext.numActivePlayers, actualCount: actualActivePlayers }\n      )\n    )\n  }\n\n  // Cross-check 4: curLiveBet equals max amtAddedToPotCurStreet across all players\n  // Skip this check during forced bet posting phase (pendingForcedBets exists and is non-empty)\n  const inForcedBetPhase = spotContext.pendingForcedBets && spotContext.pendingForcedBets.length > 0\n\n  // Also skip if ghost players exist - they can post forced bets that set curLiveBet\n  // but don't appear in any player's amtAddedToPotCurStreet (ghosts have no playerState)\n  const hasGhostPlayers = spotContext.ghostPositions && spotContext.ghostPositions.length > 0\n\n  if (!inForcedBetPhase && !hasGhostPlayers) {\n    const maxPlayerContribution = getMaxPlayerContribution(playersInHandState)\n\n    // Also skip if someone has overpaid (contributed more than curLiveBet)\n    // This occurs when a single player posts multiple forced bets (button double-post scenario)\n    // Example: SB/BU posts SB ($1) + straddle ($4) = $5 total, but curLiveBet = $4\n    // This temporary mismatch resolves on the first voluntary action\n    const someoneOverpaid = maxPlayerContribution > bettingState.curLiveBet\n\n    // Also skip if someone has underpaid (contributed less than curLiveBet)\n    // This occurs when a player is all-in for less on a forced bet\n    // Example: BB is $200, player has $150 - contribution=$150, curLiveBet=$200\n    // This mismatch is permanent (player is all-in) and is valid\n    const someoneUnderpaid = maxPlayerContribution < bettingState.curLiveBet\n\n    if (!someoneOverpaid && !someoneUnderpaid && bettingState.curLiveBet !== maxPlayerContribution) {\n      throw new Error(\n        formatMsg(\n          op,\n          `curLiveBet (${bettingState.curLiveBet}) does not match max player contribution (${maxPlayerContribution})`,\n          { curLiveBet: bettingState.curLiveBet, maxPlayerContribution }\n        )\n      )\n    }\n  }\n}\n\n/**\n * @internal\n *\n * Validates full hand state including PokerHand and SpotState consistency.\n * Performs comprehensive validation of hand integrity and pre-action requirements.\n *\n * Checks performed:\n * - PokerHand fields are non-null (playersInHand, playerActions)\n * - All SpotState components (via spotStateSanityCheck)\n * - Player exists at the specified position\n * - Position consistency (position parameter matches playerState.position)\n * - Pre-action mode only: hand is active, player hasn't folded, player has chips remaining\n *\n * This validator requires both PokerHand and SpotState and is used when validating\n * actions before they are applied to the hand (e.g., validatePlayerActionCanBeApplied).\n *\n * @param position - Position of the player\n * @param pokerHand - The poker hand to validate\n * @param spotState - The spot state to validate\n * @param mode - Validation mode: 'pre-action' checks if player can act (default), 'post-action' skips action-ability checks\n * @param op - Operation being performed during validation (for error messages)\n * @returns ValidationStatus indicating if validation passed\n */\nexport const handStateSanityCheck = (\n  position: Position,\n  pokerHand: PokerHand,\n  spotState : SpotState,\n  mode: 'pre-action' | 'post-action' = 'pre-action',\n  op: string = 'handStateSanityCheck()',\n): ValidationStatus => {\n\n  // Validate PokerHand fields\n  const { playersInHand, playerActions } = pokerHand\n  if (some(isNullish, [playersInHand, playerActions])) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, 'one or more pokerHand objects are null or undefined',\n        { playersInHand, playerActions }\n      )}}\n\n  // Validate SpotState components (delegates to spotStateSanityCheck)\n  const val = spotStateSanityCheck(spotState, op)\n  if (!val.valid) { return val }\n\n  // Get player state and spot context for additional checks\n  const { playersInHandState, spotContext } = spotState\n  const playerState = playersInHandState[position]\n  if (!playerState) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `player at position ${position} not found in hand`,\n        { position, playersInHandState }\n      )}}\n\n  // Position consistency check: position parameter should match playerState.position\n  if (playerState.position !== position) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `position parameter (${position}) does not match playerState.position (${playerState.position})`,\n        { positionParam: position, playerStatePosition: playerState.position }\n      )\n    }\n  }\n\n  // Pre-action only checks: verify player can take an action\n  if (mode === 'pre-action') {\n    // Check hand is in a state where players can act\n    // Block if: hand ended, runout pending, or awaiting community cards\n    if (spotContext.actionIsOn === 'hand-ended' ||\n        spotContext.actionIsOn === 'runout-pending' ||\n        spotContext.awaitingCommunityCards === true) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          spotContext.awaitingCommunityCards\n            ? `Cannot apply action - community cards must be dealt first for ${spotContext.activeStreet} street. Call addCommunityCards() before adding actions.`\n            : 'hand has ended (actionIsOn is \"hand-ended\" or \"runout-pending\"), cannot apply action',\n          {\n            actionIsOn: spotContext.actionIsOn,\n            awaitingCommunityCards: spotContext.awaitingCommunityCards,\n            activeStreet: spotContext.activeStreet\n          }\n        )\n      }\n    }\n\n    // Check hasFolded is not set for the player at this position\n    if (playerState.hasFolded) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `player at position ${position} has folded and cannot act`,\n          { position, hasFolded: playerState.hasFolded }\n        )\n      }\n    }\n\n    // Check curStackSize is not zero (player should have chips to act)\n    if (playerState.curStackSize === 0) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `player at position ${position} has no chips remaining (all-in)`,\n          { position, curStackSize: playerState.curStackSize }\n        )\n      }\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates that the action being added is consistent with the current poker hand\n * and spot state. Performs comprehensive validation before applying an action.\n *\n * Checks performed:\n * - Spot state validity (via handStateSanityCheck in 'pre-action' mode)\n * - Action object validity (via playerActionSanityCheck)\n * - Position consistency (position parameter matches action.position)\n * - Turn order (action.position matches spotContext.actionIsOn)\n * - Street consistency (action.street matches spotContext.activeStreet)\n * - Stack constraints (amountAddedToPot doesn't exceed player's stack)\n * - All-in consistency (isAllIn flag matches amount vs stack)\n * - Betting rules:\n *   - Raise: raiseToAmount > curLiveBet, respects min raise increment (unless all-in)\n *   - Bet: curLiveBet must be 0, respects min bet size (unless all-in)\n *   - Call: exact call amount (unless all-in for less)\n *   - Check/Fold: amountAddedToPot must be 0\n *\n * @param position - Position of the player taking the action\n * @param playerAction - The action being validated for application to hand state\n * @param pokerHand - Current poker hand state\n * @param spotState - Current spot state\n * @param op - Operation being performed during validation (for error messages)\n * @returns ValidationStatus indicating if validation passed\n */\nexport const validatePlayerActionCanBeApplied = (\n  position: Position,\n  playerAction: PlayerAction,\n  pokerHand: PokerHand,\n  spotState: SpotState,\n  op: string = 'validatePlayerActionCanBeApplied()',\n): ValidationStatus => {\n\n  // Validate playerAction is not null/undefined first\n  let val = playerActionSanityCheck(playerAction, op)\n  if (!val.valid) return val\n\n  // Validate ghost player flag consistency with playerState existence\n  // If byGhostPlayer: true, the position should NOT have a PlayerState\n  if (playerAction.byGhostPlayer && spotState.playersInHandState[position]) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, `action has byGhostPlayer: true but position ${position} has a PlayerState (ghost players cannot have PlayerState)`,\n        { position, byGhostPlayer: playerAction.byGhostPlayer, hasPlayerState: true }\n      )\n    }\n  }\n\n  // Ghost players can ONLY perform forced bets (smallBlind, bigBlind, straddle)\n  if (playerAction.byGhostPlayer) {\n    const validGhostActions = ['smallBlind', 'bigBlind', 'straddle'] as const\n    if (!validGhostActions.includes(playerAction.actionType as any)) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op, `ghost player at position ${position} can only perform forced bets (smallBlind, bigBlind, straddle), not ${playerAction.actionType}`,\n          { position, actionType: playerAction.actionType, byGhostPlayer: playerAction.byGhostPlayer }\n        )\n      }\n    }\n  }\n\n  // Skip hand state validation for ghost players (they won't be in playersInHandState)\n  if (!playerAction.byGhostPlayer) {\n    // Validate the spot state itself (pre-action: player must be able to act)\n    val = handStateSanityCheck(position, pokerHand, spotState, 'pre-action', op)\n    if (!val.valid) return val\n  }\n\n  const { playersInHandState, bettingState, spotContext } = spotState\n  const playerState = playersInHandState[position]\n\n  // Allow ghost player actions (they won't have playerState)\n  if(!playerState && !playerAction.byGhostPlayer) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op, `player at position ${position} not found in hand`,\n        { position, playersInHandState }\n      )\n    }\n  }\n\n  // Check position parameter matches playerAction.position\n  if (position !== playerAction.position) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `position parameter (${position}) does not match playerAction.position (${playerAction.position})`,\n        { positionParam: position, playerActionPosition: playerAction.position }\n      )\n    }\n  }\n\n  // Check playerAction.position matches spotContext.actionIsOn (it's their turn)\n  if (playerAction.position !== spotContext.actionIsOn) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `action position (${playerAction.position}) does not match who should act (${spotContext.actionIsOn})`,\n        { actionPosition: playerAction.position, actionIsOn: spotContext.actionIsOn }\n      )\n    }\n  }\n\n  // Check playerAction.street matches spotContext.activeStreet\n  if (playerAction.street !== spotContext.activeStreet) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `action street (${playerAction.street}) does not match active street (${spotContext.activeStreet})`,\n        { actionStreet: playerAction.street, activeStreet: spotContext.activeStreet }\n      )\n    }\n  }\n\n  // Skip stack validation for ghost players (they have no playerState)\n  if (!playerAction.byGhostPlayer && playerState) {\n    // Check amountAddedToPot doesn't exceed player's stack\n    if (playerAction.amountAddedToPot > playerState.curStackSize) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `action adds ${playerAction.amountAddedToPot} to pot but player only has ${playerState.curStackSize} remaining`,\n          { amountAddedToPot: playerAction.amountAddedToPot, curStackSize: playerState.curStackSize }\n        )\n      }\n    }\n\n    // Check isAllIn consistency: if putting in all chips, should be marked all-in\n    const puttingInAllChips = playerAction.amountAddedToPot === playerState.curStackSize\n    if (puttingInAllChips && !playerAction.isAllIn) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `action adds all player's chips (${playerAction.amountAddedToPot}) but isAllIn is false`,\n          { amountAddedToPot: playerAction.amountAddedToPot, curStackSize: playerState.curStackSize, isAllIn: playerAction.isAllIn }\n        )\n      }\n    }\n\n    // Check isAllIn consistency: if marked all-in, should be putting in all chips OR have no chips\n    if (playerAction.isAllIn && playerAction.amountAddedToPot < playerState.curStackSize && playerState.curStackSize > 0) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `action is marked isAllIn but only adds ${playerAction.amountAddedToPot} of ${playerState.curStackSize} available`,\n          { amountAddedToPot: playerAction.amountAddedToPot, curStackSize: playerState.curStackSize, isAllIn: playerAction.isAllIn }\n        )\n      }\n    }\n  }\n\n  // BettingState consistency checks\n\n  // Check: if action is 'raise', raiseToAmount should be greater than curLiveBet\n  if (playerAction.actionType === 'raise' && playerAction.raiseToAmount <= bettingState.curLiveBet) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `raise action has raiseToAmount (${playerAction.raiseToAmount}) not greater than curLiveBet (${bettingState.curLiveBet})`,\n        { actionType: playerAction.actionType, raiseToAmount: playerAction.raiseToAmount, curLiveBet: bettingState.curLiveBet }\n      )\n    }\n  }\n\n  // Check: raise should respect minimum raise increment (unless all-in for less)\n  if (playerAction.actionType === 'raise') {\n    const raiseByAmount = playerAction.raiseToAmount - bettingState.curLiveBet\n    if (raiseByAmount < bettingState.curMinRaiseIncrement && !playerAction.isAllIn) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `raise by ${raiseByAmount} is less than minimum raise increment (${bettingState.curMinRaiseIncrement}) and not all-in`,\n          { raiseByAmount, curMinRaiseIncrement: bettingState.curMinRaiseIncrement, isAllIn: playerAction.isAllIn }\n        )\n      }\n    }\n  }\n\n  // Check: if action is 'bet', curLiveBet should be 0 (otherwise it should be a raise)\n  if (playerAction.actionType === 'bet' && bettingState.curLiveBet !== 0) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `bet action but curLiveBet is ${bettingState.curLiveBet} (should be 0, otherwise this should be a raise)`,\n        { actionType: playerAction.actionType, curLiveBet: bettingState.curLiveBet }\n      )\n    }\n  }\n\n  // Check: bet should respect minimum bet size (unless all-in for less)\n  if (playerAction.actionType === 'bet') {\n    if (playerAction.amountAddedToPot < bettingState.curMinBetSize && !playerAction.isAllIn) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `bet of ${playerAction.amountAddedToPot} is less than minimum bet size (${bettingState.curMinBetSize}) and not all-in`,\n          { amountAddedToPot: playerAction.amountAddedToPot, curMinBetSize: bettingState.curMinBetSize, isAllIn: playerAction.isAllIn }\n        )\n      }\n    }\n  }\n\n  // Check: call amount should match what's needed to call\n  // Ghost players can't call (only forced bets), so playerState must exist\n  if (playerAction.actionType === 'call' && !playerAction.byGhostPlayer && playerState) {\n    const toCall = bettingState.curLiveBet - playerState.amtAddedToPotCurStreet\n    const expectedAmount = Math.min(toCall, playerState.curStackSize)\n    if (playerAction.amountAddedToPot !== expectedAmount) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `call adds ${playerAction.amountAddedToPot} but should add ${expectedAmount} (toCall: ${toCall}, stack: ${playerState.curStackSize})`,\n          { amountAddedToPot: playerAction.amountAddedToPot, expectedAmount, toCall, curStackSize: playerState.curStackSize }\n        )\n      }\n    }\n  }\n\n  // Check: fold and check should add 0 to pot\n  if ((playerAction.actionType === 'fold' || playerAction.actionType === 'check') &&\n      playerAction.amountAddedToPot !== 0) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `${playerAction.actionType} action should add 0 to pot but adds ${playerAction.amountAddedToPot}`,\n        { actionType: playerAction.actionType, amountAddedToPot: playerAction.amountAddedToPot }\n      )\n    }\n  }\n\n  // Check: raise should have raiseToAmount > 0\n  if (playerAction.actionType === 'raise' && playerAction.raiseToAmount === 0) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        'raise action must have raiseToAmount > 0',\n        { actionType: playerAction.actionType, raiseToAmount: playerAction.raiseToAmount }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n\n/**\n * @internal\n *\n * Validates antes before initializing hand state.\n * Ensures ante has valid amount, type, and doesn't exceed player stacks.\n *\n * Checks performed:\n * - Ante amount is positive integer\n * - Ante type is valid ('bigBlind' | 'allPlayers')\n * - For 'bigBlind': BB position exists in handPlayers with sufficient stack\n * - For 'allPlayers': all handPlayers have sufficient stacks for the ante\n *\n * @param antes - Optional single Ante object to validate\n * @param handPlayers - Array of players in the hand\n * @param op - Operation being performed during validation (for error messages)\n * @returns ValidationStatus indicating if validation passed\n *\n * @example\n * ```ts\n * const handPlayers = [\n *   { position: 'SB', startingStack: 1000, ... },\n *   { position: 'BB', startingStack: 2000, ... },\n *   { position: 'BU', startingStack: 1500, ... }\n * ]\n *\n * // Valid: big blind ante\n * const antes1 = { type: 'bigBlind', amount: 2 }\n * validateAntes(antes1, handPlayers) // { valid: true }\n *\n * // Valid: all players ante\n * const antes2 = { type: 'allPlayers', amount: 5 }\n * validateAntes(antes2, handPlayers) // { valid: true }\n *\n * // Invalid: ante exceeds BB stack\n * const antes3 = { type: 'bigBlind', amount: 3000 }\n * validateAntes(antes3, handPlayers) // { valid: false, message: '...' }\n *\n * // Invalid: ante exceeds smallest stack (SB has 1000)\n * const antes4 = { type: 'allPlayers', amount: 1500 }\n * validateAntes(antes4, handPlayers) // { valid: false, message: '...' }\n * ```\n */\nexport const validateAntes = (\n  antes: Ante | undefined,\n  handPlayers: HandPlayer[],\n  op: string = 'validateAntes()',\n): ValidationStatus => {\n  // No antes is valid\n  if (!antes) {\n    return { valid: true }\n  }\n\n  // Check ante amount is positive first (for finite negative numbers and zero)\n  if (Number.isFinite(antes.amount) && antes.amount <= 0) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        'Ante amount must be positive',\n        { type: antes.type, amount: antes.amount }\n      )\n    }\n  }\n\n  // Check ante amount is valid integer (catches NaN, Infinity, -Infinity, non-integers)\n  if (isNotValidNonNegativeInteger(antes.amount)) {\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        'Ante amount is not a valid non-negative integer',\n        { type: antes.type, amount: antes.amount }\n      )\n    }\n  }\n\n  // Validate based on ante type\n  if (antes.type === 'bigBlind') {\n    // Find BB player in handPlayers (may not exist if BB is a ghost player)\n    const bbPlayer = handPlayers.find(hp => hp.position === 'BB')\n\n    // Only validate stack if BB is present (not a ghost)\n    // Ghost BB players are valid: they post the ante but open-fold before voluntary action\n    if (bbPlayer && antes.amount > bbPlayer.startingStack) {\n      return {\n        valid: false,\n        message: formatMsg(\n          op,\n          `Big blind ante amount ${antes.amount} exceeds BB starting stack ${bbPlayer.startingStack}`,\n          { type: antes.type, anteAmount: antes.amount, bbStack: bbPlayer.startingStack }\n        )\n      }\n    }\n\n    // If BB is not in handPlayers, it's a ghost player scenario - this is valid\n    // Ghost detection and validation happens later during hand initialization\n  } else if (antes.type === 'allPlayers') {\n    // Check that all players have sufficient stacks for the ante\n    for (const hp of handPlayers) {\n      if (antes.amount > hp.startingStack) {\n        return {\n          valid: false,\n          message: formatMsg(\n            op,\n            `All players ante amount ${antes.amount} exceeds ${hp.position} starting stack ${hp.startingStack}`,\n            { type: antes.type, anteAmount: antes.amount, position: hp.position, startingStack: hp.startingStack }\n          )\n        }\n      }\n    }\n  } else {\n    // Invalid ante type (should never happen with TypeScript, but be defensive)\n    return {\n      valid: false,\n      message: formatMsg(\n        op,\n        `Invalid ante type: ${(antes as any).type}. Must be 'bigBlind' or 'allPlayers'`,\n        { type: (antes as any).type }\n      )\n    }\n  }\n\n  return { valid: true }\n}\n","/**\n * Pure state initialization and update utilities for poker hands.\n *\n * Provides immutable functions for:\n * - Initializing player state, betting state, and spot state\n * - Updating player and betting state without mutation\n * - Managing state transitions throughout a hand\n *\n * All functions return new objects and never mutate inputs.\n */\n\nimport { throwIf, set } from './utilsForStcLand'\n\nimport type {\n  PokerVariant, Position, Street,\n  PlayerState, BettingState,  PlayerStateByPosition,  SpotContext , SpotState,\n  HandPlayer, ForcedBet, Ante,\n} from '@pokerland/types'\n\nimport { countActivePlayers, countPlayersInHand } from './stateHelpers'\nimport { orderPlayerListByPosition, getFirstToActPreFlop, positions } from './generalUtils'\nimport { $ } from './currencyUtils'\n\n//\n// Initializers\n//\n\n/**\n * @internal\n *\n * Initializes a fresh spot state at the beginning of a hand (preflop), BEFORE any actions occur. \\\n * Represents \"hand-not-started\" state with antes posted but no forced bets or player actions yet.\n *\n * @param variant - Poker variant being played (nlhe, plo, etc.)\n * @param numPlayersSeated - Total number of players at the table (2-10)\n * @param handPlayers - Array of HandPlayer objects containing position and starting stack info\n * @param forcedBets - Array of forced bets (blinds, straddles) that will be posted\n * @param antes - Optional ante configuration (table-wide config: { type: 'allPlayers' | 'bigBlind', amount: number })\n * @returns New SpotState initialized to preflop hand-not-started state\n *\n * Initial state semantics:\n * - actionIsOn: First position that needs to post a forced bet (or first voluntary actor if no forced bets)\n * - pendingForcedBets: Queue of positions that need to post forced bets (present if forcedBets.length > 0)\n * - ghostPositions: Positions that were seated but open-folded (not in handPlayers)\n * - bettingState: Created internally with potSize = sum(antes), curLiveBet = 0, curMinBetSize = 0\n * - playersInHandState: Initialized with antes applied, stack sizes reduced accordingly\n *\n * This function creates the INITIAL state. To apply forced bets and transition to voluntary action phase,\n * use addActionToHand() with each forced bet action.\n *\n * @example\n * ```ts\n * // Initialize 3-handed preflop spot BEFORE forced bets posted\n * const handPlayers: HandPlayer[] = [\n *   { position: 'SB', startingStack: 1000, ... },\n *   { position: 'BB', startingStack: 2000, ... },\n *   { position: 'BU', startingStack: 1500, ... }\n * ]\n * const forcedBets = [\n *   { type: 'smallBlind', position: 'SB', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 }\n * ]\n *\n * const spot = initSpotState('nlhe', 3, handPlayers, forcedBets)\n * // {\n * //   spotContext: {\n * //     variant: 'nlhe',\n * //     activeStreet: 'preFlop',\n * //     actionIsOn: 'SB',  // first forced bet poster\n * //     numPlayersInHand: 3,\n * //     numActivePlayers: 3,\n * //     numVoluntaryActionsCurStreet: 0,\n * //     pendingForcedBets: ['SB', 'BB']  // queue of forced bets to post\n * //   },\n * //   playersInHandState: { 'SB': {...}, 'BB': {...}, 'BU': {...} },\n * //   bettingState: { potSize: 0, curLiveBet: 0, curMinBetSize: 0, curMinRaiseIncrement: 2 }\n * // }\n *\n * // With big blind ante\n * const bbAnte = { type: 'bigBlind', amount: 2 }\n * const spotWithBBAnte = initSpotState('nlhe', 3, handPlayers, forcedBets, bbAnte)\n * // playersInHandState['BB'].curStackSize will be 1998 (2000 - 2)\n * // playersInHandState['BB'].amtAddedToPotCurStreet will be 0 (antes don't count as street contribution)\n * // bettingState.potSize will be 2 (ante seeded into pot)\n *\n * // With ghost players (BB open-folded)\n * const handPlayers2 = [\n *   { position: 'SB', startingStack: 1000, ... },\n *   { position: 'BU', startingStack: 1500, ... }\n * ]\n * const spot2 = initSpotState('nlhe', 3, handPlayers2, forcedBets)\n * // spotContext.ghostPositions will be ['BB']\n * ```\n */\nexport const initSpotState = (\n  variant: PokerVariant,\n  numPlayersSeated: number,\n  handPlayers: HandPlayer[],\n  forcedBets: ForcedBet[],\n  antes?: Ante\n): SpotState => {\n  const playerState = initPlayersInHandState(handPlayers, antes)\n\n  // Detect ghost positions (seated but not in handPlayers)\n  const ghostPositions = detectGhostPositions(numPlayersSeated, handPlayers)\n\n  // Extract posting order from forcedBets array\n  const forcedBetPositions = forcedBets.map(fb => fb.position)\n\n  // Create betting state internally - need BB amount for raise increment seed\n  const bigBlindOrHighestStraddle = forcedBets.length > 0\n    ? Math.max(...forcedBets.map(fb => fb.amount))\n    : 0\n  const bettingState = initBettingState(bigBlindOrHighestStraddle, antes, numPlayersSeated)\n\n  // For \"hand-not-started\" state: actionIsOn is first forced bet poster\n  // (or first voluntary actor if no forced bets)\n  const firstToAct = forcedBetPositions.length > 0\n    ? forcedBetPositions[0]\n    : getFirstToActPreFlop(handPlayers, [])\n\n  return {\n    spotContext: {\n      variant,\n      activeStreet: 'preFlop',\n      actionIsOn: firstToAct,\n      numPlayersInHand: countPlayersInHand(playerState),\n      numActivePlayers: countActivePlayers(playerState),\n      numVoluntaryActionsCurStreet: 0,\n      pendingForcedBets: forcedBetPositions.length > 0 ? forcedBetPositions : undefined,\n      ghostPositions: ghostPositions.length > 0 ? ghostPositions : undefined,\n      awaitingCommunityCards: false,\n    },\n    playersInHandState: playerState,\n    bettingState,\n  }\n}\n\n/**\n * @internal\n *\n * Initializes a fresh player state prior to hand start. \\\n * Creates clean state with zero contributions and no actions taken.\n *\n * @param position - The player's position at the table\n * @param startingStack - The player's starting stack size for this hand\n * @param anteAmount - Optional ante amount posted by this player (defaults to 0)\n * @returns New PlayerState initialized to hand start\n * @throws Error if startingStack is not finite or is negative\n * @throws Error if anteAmount is not finite or is negative\n * @throws Error if anteAmount exceeds startingStack\n *\n * Initial state values:\n * - curStackSize: startingStack - anteAmount (ante reduces stack immediately)\n * - amtAddedToPotCurStreet: 0 (antes go to pot but don't count as street contribution)\n * - hasActedCurStreet: false\n * - liveBetAfterLastAction: 0 (remains 0 until first voluntary action)\n * - minRaiseIncrementAfterLastAction: 0 (remains 0 until first voluntary action)\n * - hasFolded: false\n *\n * @example\n * ```ts\n * // Initialize player in BU position with 1500 chips, no ante\n * const playerState = initPlayerState('BU', 1500)\n * // {\n * //   position: 'BU',\n * //   curStackSize: 1500,\n * //   amtAddedToPotCurStreet: 0,\n * //   hasActedCurStreet: false,\n * //   liveBetAfterLastAction: 0,\n * //   minRaiseIncrementAfterLastAction: 0,\n * //   hasFolded: false\n * // }\n *\n * // Initialize player with button ante of 2\n * const playerStateWithAnte = initPlayerState('BU', 100, 2)\n * // {\n * //   position: 'BU',\n * //   curStackSize: 98,  // reduced by ante\n * //   amtAddedToPotCurStreet: 0,  // antes don't count as street contribution\n * //   hasActedCurStreet: false,\n * //   ...\n * // }\n *\n * // Invalid: negative stack\n * initPlayerState('BB', -100)\n * // Throws: Error: startingStack must be a finite, non-negative number\n *\n * // Invalid: ante exceeds stack\n * initPlayerState('BU', 100, 150)\n * // Throws: Error: anteAmount cannot exceed startingStack\n * ```\n */\nexport const initPlayerState = (\n  position: Position,\n  startingStack: number,\n  anteAmount: number = 0\n): PlayerState => {\n\n  throwIf(!Number.isFinite(startingStack) || startingStack < 0,\n    'initPlayerState()',\n    'startingStack must be a finite, non-negative number',\n    { position, startingStack }\n  )\n\n  throwIf(!Number.isInteger(startingStack),\n    'initPlayerState()',\n    'startingStack must be an integer ($.MinorUnits represents cents)',\n    { position, startingStack }\n  )\n\n  throwIf(!Number.isFinite(anteAmount) || anteAmount < 0,\n    'initPlayerState()',\n    'anteAmount must be a finite, non-negative number',\n    { position, anteAmount }\n  )\n\n  throwIf(!Number.isInteger(anteAmount),\n    'initPlayerState()',\n    'anteAmount must be an integer ($.MinorUnits represents cents)',\n    { position, anteAmount }\n  )\n\n  throwIf(anteAmount > startingStack,\n    'initPlayerState()',\n    'anteAmount cannot exceed startingStack',\n    { position, anteAmount, startingStack }\n  )\n\n  return {\n    position,\n    curStackSize: $.asMinor(startingStack - anteAmount),\n    amtAddedToPotCurStreet: $.asMinor(0), // Antes go to pot but don't count as street contribution\n    hasActedCurStreet: false,\n    liveBetAfterLastAction: $.asMinor(0),            // 0 until first voluntary action (can remain 0 if check)\n    minRaiseIncrementAfterLastAction: $.asMinor(0),  // 0 until first voluntary action\n    hasFolded: false\n  }\n}\n\n/**\n * @internal\n *\n * Initializes betting state prior to hand start (preflop). \\\n * Sets up preflop raise increment. Per poker rules, curMinBetSize is always 0 preflop\n * because blinds define the live bet.\n *\n * @param bbOrHighestStraddle - Big blind amount, or highest straddle if straddles present\n * @param antes - Optional ante configuration to seed the pot (table-wide config: { type: 'allPlayers' | 'bigBlind', amount: number })\n * @returns New BettingState initialized to preflop\n * @throws Error if bbOrHighestStraddle is not finite or is negative\n *\n * Initial state values:\n * - potSize: sum of all antes (0 if no antes)\n * - curLiveBet: 0\n * - curMinBetSize: 0 (preflop only - blinds define live bet per poker.d.ts:840)\n * - curMinRaiseIncrement: bbOrHighestStraddle (preflop raise-increment seed)\n *\n * BettingState evolution throughout a hand:\n * ```\n * Phase                                  | potSize              | curLiveBet        | curMinBetSize | curMinRaiseIncrement\n * ---------------------------------------|----------------------|-------------------|---------------|---------------------\n * (1) Prior to hand start                | 0                    | 0                 | 0             | BB or highest straddle\n * (2) After antes                        | sum(antes)           | 0                 | 0             | BB or highest straddle\n * (3) After small blind                  | SB amount            | 0                 | 0             | BB or highest straddle\n * (4) After big blind                    | SB + BB              | BB amount         | 0             | BB amount\n * (5) After straddles                    | SB + BB + straddles  | highest straddle  | 0             | highest straddle\n * (6) First to act preflop               | (same as 5)          | (same as 5)       | 0             | (same as 5)\n * (7) Postflop (flop/turn/river)         | (accumulated)        | 0                 | BB amount     | BB amount\n * ```\n *\n * Note: curMinBetSize updates from 0 to BB amount when advancing from preflop to postflop streets.\n * See resetBettingStateForNewStreet() for street transition logic.\n *\n * @example\n * ```ts\n * // Standard 1/2 game (BB = 2), no antes\n * const bettingState = initBettingState(2)\n * // {\n * //   potSize: 0,\n * //   curLiveBet: 0,\n * //   curMinBetSize: 0,  // preflop: 0 per poker rules\n * //   curMinRaiseIncrement: 2\n * // }\n *\n * // Game with button straddle to 4\n * const bettingStateWithStraddle = initBettingState(4)\n * // {\n * //   potSize: 0,\n * //   curLiveBet: 0,\n * //   curMinBetSize: 0,  // preflop: 0 per poker rules\n * //   curMinRaiseIncrement: 4  // seeded by straddle amount\n * // }\n *\n * // Game with big blind ante of 2 (BB posts for whole table)\n * const bbAnte = { type: 'bigBlind', amount: 2 }\n * const bettingStateWithBBAnte = initBettingState(2, bbAnte)\n * // {\n * //   potSize: 2,  // seeded with BB ante\n * //   curLiveBet: 0,\n * //   curMinBetSize: 0,  // preflop: 0 per poker rules\n * //   curMinRaiseIncrement: 2\n * // }\n *\n * // Game with all players ante of 5, 9-handed\n * const allPlayersAnte = { type: 'allPlayers', amount: 5 }\n * const bettingStateWithAllPlayersAnte = initBettingState(2, allPlayersAnte, 9)\n * // {\n * //   potSize: 45,  // 9 players × 5 ante = 45\n * //   curLiveBet: 0,\n * //   curMinBetSize: 0,  // preflop: 0 per poker rules\n * //   curMinRaiseIncrement: 2\n * // }\n * ```\n */\nexport const initBettingState = (\n  bbOrHighestStraddle: number, // BB or largest straddle\n  antes?: Ante,\n  numPlayersSeated?: number  // Required for allPlayers ante calculation\n): BettingState => {\n\n  throwIf(!Number.isFinite(bbOrHighestStraddle) || bbOrHighestStraddle < 0,\n    'initBettingState()',\n    'bbOrHighestStraddle must be a finite, non-negative number',\n    { bbOrHighestStraddle }\n  )\n\n  throwIf(!Number.isInteger(bbOrHighestStraddle),\n    'initBettingState()',\n    'bbOrHighestStraddle must be an integer ($.MinorUnits represents cents)',\n    { bbOrHighestStraddle }\n  )\n\n  // Validate ante if provided\n  if (antes) {\n    throwIf(!Number.isInteger(antes.amount),\n      'initBettingState()',\n      'ante amount must be an integer ($.MinorUnits represents cents)',\n      { anteType: antes.type, amount: antes.amount }\n    )\n\n    throwIf(antes.type === 'allPlayers' && !numPlayersSeated,\n      'initBettingState()',\n      'numPlayersSeated is required when antes.type is allPlayers',\n      { anteType: antes.type }\n    )\n  }\n\n  // Calculate total antes based on type\n  const totalAntes = !antes ? 0 :\n    antes.type === 'bigBlind' ? antes.amount :\n    antes.type === 'allPlayers' ? antes.amount * (numPlayersSeated ?? 0) :\n    0\n\n  return {\n    potSize: $.asMinor(totalAntes),\n    prevStreetPotSize: $.createMinor(0), // Always 0 on preflop (no previous street)\n    amtAddedToPotCurStreet: $.asMinor(0), // Antes go to pot but don't count as street contributions\n    curLiveBet: $.asMinor(0),\n    curMinBetSize: $.asMinor(0), // preflop: 0 per poker.d.ts:840 (blinds define live bet)\n    curMinRaiseIncrement: $.asMinor(bbOrHighestStraddle), // preflop raise-increment seed\n  }\n}\n\n/**\n * @internal\n *\n * Initializes spot context at the beginning of a hand (preflop). \\\n * Creates context metadata independent of player/betting state.\n *\n * @param variant - Poker variant being played (nlhe, plo, etc.)\n * @param actionIsOn - Position to act, or special marker ('hand-not-started', 'runout-pending', 'hand-ended')\n * @param numPlayersInHand - Number of players in hand (can win pots, includes all-in)\n * @param numActivePlayers - Number of active players (can take actions, excludes all-in)\n * @param pendingForcedBets - Optional queue of positions that need to post forced bets\n * @returns New SpotContext initialized to preflop\n *\n * @example\n * ```ts\n * // Initialize preflop context with 3 players (all with chips)\n * const context = initSpotContext('nlhe', 'BU', 3, 3)\n * // {\n * //   variant: 'nlhe',\n * //   activeStreet: 'preFlop',\n * //   actionIsOn: 'BU',\n * //   numPlayersInHand: 3,\n * //   numActivePlayers: 3,\n * //   numVoluntaryActionsCurStreet: 0\n * // }\n *\n * // With forced bet queue\n * const contextWithForcedBets = initSpotContext('nlhe', 'SB', 3, 3, ['SB', 'BB', 'BU'])\n * // {\n * //   variant: 'nlhe',\n * //   activeStreet: 'preFlop',\n * //   actionIsOn: 'SB',\n * //   numPlayersInHand: 3,\n * //   numActivePlayers: 3,\n * //   numVoluntaryActionsCurStreet: 0,\n * //   pendingForcedBets: ['SB', 'BB', 'BU']\n * // }\n * ```\n */\nexport const initSpotContext = (\n  variant: PokerVariant,\n  actionIsOn: SpotContext['actionIsOn'],\n  numPlayersInHand: number,\n  numActivePlayers: number,\n  pendingForcedBets?: Position[]\n): SpotContext => ({\n  variant,\n  activeStreet: 'preFlop',\n  actionIsOn,\n  numPlayersInHand,\n  numActivePlayers,\n  numVoluntaryActionsCurStreet: 0,\n  pendingForcedBets,\n  awaitingCommunityCards: false, // Initially false (no cards needed on preFlop)\n})\n\n/**\n * @internal\n *\n * Initializes player states for all players in the hand. \\\n * Creates a PlayerStateByPosition mapping from an array of HandPlayer objects.\n *\n * @param handPlayers - Array of HandPlayer objects containing position and starting stack info\n * @param antes - Optional ante configuration (table-wide config: { type: 'allPlayers' | 'bigBlind', amount: number })\n * @returns New PlayerStateByPosition mapping with initialized states\n * @throws Error if duplicate positions found in the input array\n *\n * Players are automatically ordered by canonical position before building the mapping.\n * Antes are applied during initialization, reducing stack size and counting as contributions.\n *\n * @example\n * ```ts\n * const handPlayers: HandPlayer[] = [\n *   { position: 'BU', startingStack: 1500, ... },\n *   { position: 'BB', startingStack: 2000, ... },\n *   { position: 'SB', startingStack: 1000, ... }\n * ]\n *\n * // Without antes\n * const playerStates = initPlayersInHandState(handPlayers)\n * // {\n * //   'SB': { position: 'SB', curStackSize: 1000, amtAddedToPotCurStreet: 0, ... },\n * //   'BB': { position: 'BB', curStackSize: 2000, amtAddedToPotCurStreet: 0, ... },\n * //   'BU': { position: 'BU', curStackSize: 1500, amtAddedToPotCurStreet: 0, ... }\n * // }\n *\n * // With big blind ante\n * const bbAnte = { type: 'bigBlind', amount: 2 }\n * const playerStatesWithBBAnte = initPlayersInHandState(handPlayers, bbAnte)\n * // {\n * //   'SB': { position: 'SB', curStackSize: 1000, amtAddedToPotCurStreet: 0, ... },\n * //   'BB': { position: 'BB', curStackSize: 1998, amtAddedToPotCurStreet: 0, ... },  // 2000 - 2 ante\n * //   'BU': { position: 'BU', curStackSize: 1500, amtAddedToPotCurStreet: 0, ... }\n * // }\n *\n * // With all players ante\n * const allPlayersAnte = { type: 'allPlayers', amount: 5 }\n * const playerStatesWithAllAnte = initPlayersInHandState(handPlayers, allPlayersAnte)\n * // {\n * //   'SB': { position: 'SB', curStackSize: 995, amtAddedToPotCurStreet: 0, ... },   // 1000 - 5 ante\n * //   'BB': { position: 'BB', curStackSize: 1995, amtAddedToPotCurStreet: 0, ... },  // 2000 - 5 ante\n * //   'BU': { position: 'BU', curStackSize: 1495, amtAddedToPotCurStreet: 0, ... }   // 1500 - 5 ante\n * // }\n * ```\n */\nexport const initPlayersInHandState = (\n  handPlayers: HandPlayer[],\n  antes?: Ante\n): PlayerStateByPosition => {\n  // Check for duplicate positions\n  const positions = handPlayers.map(hp => hp.position)\n  const uniquePositions = new Set(positions)\n\n  throwIf(positions.length !== uniquePositions.size,\n    'initPlayersInHandState()',\n    'Duplicate positions found in handPlayers',\n    { positions }\n  )\n\n  // No ante validation needed - ghost detection is handled elsewhere\n  // Ante structure validated in initBettingState\n\n  // Order players by position first\n  const orderedPlayers = orderPlayerListByPosition(handPlayers)\n\n  // Build PlayerStateByPosition object\n  return orderedPlayers.reduce((acc, handPlayer) => {\n    // Calculate ante for this player based on ante type\n    const anteAmount = !antes ? 0 :\n      antes.type === 'bigBlind' && handPlayer.position === 'BB' ? antes.amount :\n      antes.type === 'allPlayers' ? antes.amount :\n      0\n\n    acc[handPlayer.position] = initPlayerState(\n      handPlayer.position,\n      handPlayer.startingStack,\n      anteAmount\n    )\n    return acc\n  }, {} as PlayerStateByPosition)\n}\n\n//\n// Updaters\n//\n\n/**\n * @internal\n *\n * Updates a single player state immutably. \\\n * Merges partial updates with existing player state.\n *\n * @param playerState - Current player state\n * @param updates - Partial player state updates to apply\n * @returns New PlayerState with updates applied\n *\n * @example\n * ```ts\n * const player = initPlayerState('BU', 1500)\n *\n * // Player calls, reducing stack by 50\n * const updated = updatePlayerState(player, {\n *   curStackSize: 1450,\n *   amtAddedToPotCurStreet: 50,\n *   hasActedCurStreet: true\n * })\n * // { position: 'BU', curStackSize: 1450, amtAddedToPotCurStreet: 50, hasActedCurStreet: true, ... }\n *\n * // Player folds\n * const folded = updatePlayerState(player, {\n *   hasFolded: true,\n *   hasActedCurStreet: true\n * })\n * // { position: 'BU', curStackSize: 1500, hasFolded: true, hasActedCurStreet: true, ... }\n * ```\n */\nexport const updatePlayerState = (\n  playerState: PlayerState,\n  updates: Partial<PlayerState>\n): PlayerState => ({ ...playerState, ...updates })\n\n/**\n * @internal\n *\n * Updates player state at a position immutably. \\\n * Merges partial updates with existing player state in the collection.\n *\n * @param byPosition - Current mapping of positions to player states\n * @param position - Position of player to update\n * @param updates - Partial player state updates to apply\n * @returns New PlayerStateByPosition with updates applied\n * @throws Error if player at position not found\n *\n * @example\n * ```ts\n * const players: PlayerStateByPosition = {\n *   'BU': initPlayerState('BU', 1500),\n *   'BB': initPlayerState('BB', 2000)\n * }\n *\n * // Player calls, reducing stack by 50\n * const updated = updatePlayersInHandState(players, 'BU', {\n *   curStackSize: 1450,\n *   amtAddedToPotCurStreet: 50,\n *   hasActedCurStreet: true\n * })\n * // Returns new object with BU's state updated\n *\n * // Player folds\n * const folded = updatePlayersInHandState(players, 'BB', {\n *   hasFolded: true,\n *   hasActedCurStreet: true\n * })\n *\n * // Error: position not in hand\n * updatePlayersInHandState(players, 'CO', { hasFolded: true })\n * // Throws: Error: Player at position CO not found\n * ```\n */\nexport const updatePlayersInHandState = (\n  byPosition: PlayerStateByPosition,\n  position: Position,\n  updates: Partial<PlayerState>\n): PlayerStateByPosition => {\n  const playerState = byPosition?.[position]\n  throwIf(!playerState, 'updatePlayersInHandState()',\n    `Player at position ${position} not found`,\n    { position }\n  )\n  return set(position, updatePlayerState(playerState!, updates), byPosition)\n}\n\n/**\n * @internal\n *\n * Updates betting state immutably. \\\n * Merges partial updates with existing betting state.\n *\n * @param bettingState - Current betting state\n * @param updates - Partial betting state updates to apply\n * @returns New BettingState with updates applied\n *\n * @example\n * ```ts\n * const betting = initBettingState(2)\n *\n * // Player bets 50\n * const afterBet = updateBettingState(betting, {\n *   potSize: 50,\n *   curLiveBet: 50,\n *   curMinBetSize: 2\n * })\n * // { potSize: 50, curLiveBet: 50, curMinBetSize: 2, curMinRaiseIncrement: 2 }\n *\n * // Player raises to 150 (raise-by 100)\n * const afterRaise = updateBettingState(afterBet, {\n *   potSize: 150,\n *   curLiveBet: 150,\n *   curMinRaiseIncrement: 100  // new raise increment\n * })\n * // { potSize: 150, curLiveBet: 150, curMinBetSize: 2, curMinRaiseIncrement: 100 }\n * ```\n */\nexport const updateBettingState = (\n  bettingState: BettingState,\n  updates: Partial<BettingState>\n): BettingState => ({ ...bettingState, ...updates })\n\n/**\n * @internal\n *\n * Updates spot context immutably. \\\n * Merges partial updates with existing spot context.\n *\n * @param spotContext - Current spot context\n * @param updates - Partial spot context updates to apply\n * @returns New SpotContext with updates applied\n *\n * @example\n * ```ts\n * const context = initSpotContext('nlhe', 'flop', 'SB', 3)\n *\n * // Player acts, increment counter\n * const afterAction = updateSpotContext(context, {\n *   actionIsOn: 'BB',\n *   numVoluntaryActionsCurStreet: 1\n * })\n * // { variant: 'nlhe', activeStreet: 'flop', actionIsOn: 'BB', numActivePlayers: 3, numVoluntaryActionsCurStreet: 1 }\n *\n * // Player folds, update active count\n * const afterFold = updateSpotContext(afterAction, {\n *   numActivePlayers: 2\n * })\n * // { variant: 'nlhe', activeStreet: 'flop', actionIsOn: 'BB', numActivePlayers: 2, numVoluntaryActionsCurStreet: 1 }\n * ```\n */\nexport const updateSpotContext = (\n  spotContext: SpotContext,\n  updates: Partial<SpotContext>\n): SpotContext => ({ ...spotContext, ...updates })\n\n/**\n * @internal\n *\n * Resets per-street state for all players when advancing to a new street. \\\n * Clears contributions, action flags, and betting context while preserving cross-street state.\n *\n * @param byPosition - Current player states to reset\n * @returns New PlayerStateByPosition with per-street fields reset\n *\n * Resets:\n * - `amtAddedToPotCurStreet` → 0 (fresh contribution tracking)\n * - `hasActedCurStreet` → false (no one has acted on new street)\n * - `liveBetAfterLastAction` → 0 (cleared for new street, will update on first action)\n * - `minRaiseIncrementAfterLastAction` → 0 (cleared for new street, will update on first action)\n *\n * Preserves:\n * - `curStackSize` (accumulated across streets)\n * - `hasFolded` (permanent for the hand)\n *\n * @example\n * ```ts\n * // Flop just closed, advancing to turn\n * const players = {\n *   'SB': { ..., amtAddedToPotCurStreet: 10, hasActedCurStreet: true, liveBetAfterLastAction: 20 },\n *   'BB': { ..., amtAddedToPotCurStreet: 10, hasActedCurStreet: true, liveBetAfterLastAction: 20 }\n * }\n * const resetPlayers = resetPlayerStatesForNewStreet(players)\n * // All players now have:\n * //   amtAddedToPotCurStreet: 0\n * //   hasActedCurStreet: false\n * //   liveBetAfterLastAction: 0\n * //   minRaiseIncrementAfterLastAction: 0\n * ```\n */\nexport const resetPlayerStatesForNewStreet = (\n  byPosition: PlayerStateByPosition\n): PlayerStateByPosition =>\n  Object.fromEntries(\n    Object.entries(byPosition).map(([position, playerState]) => [\n      position,\n      {\n        ...playerState,\n        amtAddedToPotCurStreet: 0,\n        hasActedCurStreet: false,\n        liveBetAfterLastAction: 0,\n        minRaiseIncrementAfterLastAction: 0\n      }\n    ])\n  ) as PlayerStateByPosition\n\n/**\n * @internal\n *\n * Resets per-street betting state when advancing to a new street. \\\n * Clears live bet, updates min bet size for postflop, and resets minimum raise increment.\n *\n * @param bettingState - Current betting state to reset\n * @param previousStreet - The street we're transitioning FROM\n * @param bigBlindAmount - Big blind amount for the hand (used to set postflop min bet size)\n * @returns New BettingState with per-street fields reset\n *\n * Updates:\n * - `curLiveBet` → 0 (no one has bet on new street)\n * - `curMinBetSize` → bigBlindAmount if transitioning from preflop to postflop (otherwise unchanged)\n * - `curMinRaiseIncrement` → curMinBetSize (reset to min bet size, per poker rules)\n *\n * Preserves:\n * - `potSize` (accumulated across streets)\n *\n * BettingState transitions across streets:\n * ```\n * Transition              | potSize      | curLiveBet | curMinBetSize             | curMinRaiseIncrement\n * ------------------------|--------------|------------|---------------------------|---------------------\n * preFlop → flop          | preserved    | 0 (reset)  | 0 → BB (UPDATED)          | BB (reset to curMinBetSize)\n * flop → turn             | preserved    | 0 (reset)  | BB (preserved)            | BB (reset to curMinBetSize)\n * turn → river            | preserved    | 0 (reset)  | BB (preserved)            | BB (reset to curMinBetSize)\n * ```\n *\n * CRITICAL: In straddle games, postflop curMinBetSize = BB amount (NOT straddle amount).\n * Straddles only affect preflop betting; postflop minimum bet is always 1 BB per poker rules.\n *\n * Per poker rules:\n * - Preflop: curMinBetSize = 0 (blinds define betting structure)\n * - Postflop: curMinBetSize = BB amount (minimum opening bet)\n * - The minimum raise increment resets to curMinBetSize at the start of each new street\n *\n * @example\n * ```ts\n * // Preflop closing, advancing to flop (BB = 2)\n * const preflopState = {\n *   potSize: 10,\n *   curLiveBet: 2,\n *   curMinBetSize: 0,  // preflop always 0\n *   curMinRaiseIncrement: 2\n * }\n * const flopState = resetBettingStateForNewStreet(preflopState, 'preFlop', 'flop', 2)\n * // {\n * //   potSize: 10,          // preserved\n * //   curLiveBet: 0,        // RESET\n * //   curMinBetSize: 2,     // UPDATED to BB (preflop→postflop transition)\n * //   curMinRaiseIncrement: 2  // RESET to new curMinBetSize\n * // }\n *\n * // Flop closing, advancing to turn (BB = 2)\n * const turnState = {\n *   potSize: 60,\n *   curLiveBet: 20,\n *   curMinBetSize: 2,\n *   curMinRaiseIncrement: 20  // from large flop raise\n * }\n * const riverState = resetBettingStateForNewStreet(turnState, 'flop', 'turn', 2)\n * // {\n * //   potSize: 60,          // preserved\n * //   curLiveBet: 0,        // RESET\n * //   curMinBetSize: 2,     // UNCHANGED (postflop→postflop)\n * //   curMinRaiseIncrement: 2  // RESET to curMinBetSize\n * // }\n *\n * // STRADDLE GAME: Preflop with straddle, advancing to flop (BB = 2, straddle = 4)\n * const straddlePreflopState = {\n *   potSize: 7,              // SB (1) + BB (2) + straddle (4)\n *   curLiveBet: 4,           // straddle amount\n *   curMinBetSize: 0,        // preflop always 0\n *   curMinRaiseIncrement: 4  // straddle amount\n * }\n * const straddleFlopState = resetBettingStateForNewStreet(straddlePreflopState, 'preFlop', 'flop', 2)\n * // {\n * //   potSize: 7,           // preserved\n * //   curLiveBet: 0,        // RESET\n * //   curMinBetSize: 2,     // UPDATED to BB (NOT 4!)\n * //   curMinRaiseIncrement: 2  // RESET to BB (NOT straddle!)\n * // }\n * ```\n */\nexport const resetBettingStateForNewStreet = (\n  bettingState: BettingState,\n  previousStreet: Street,\n  nextStreet: Street,\n  bigBlindAmount: number\n): BettingState => {\n  // Determine new curMinBetSize: update from 0 to BB when transitioning from preflop to postflop\n  const newCurMinBetSize = previousStreet === 'preFlop'\n    ? $.asMinor(bigBlindAmount)\n    : bettingState.curMinBetSize\n\n  // Determine new curMinRaiseIncrement per poker.d.ts:849\n  // - showdown: reset to 0 (no more betting possible)\n  // - postflop: reset to curMinBetSize\n  // - preflop: keep at 0\n  const newCurMinRaiseIncrement = nextStreet === 'showDown'\n    ? $.asMinor(0)\n    : newCurMinBetSize\n\n  return {\n    ...bettingState,\n    prevStreetPotSize: bettingState.potSize, // Bookmark current pot for new street\n    amtAddedToPotCurStreet: $.asMinor(0), // reset for new street\n    curLiveBet: $.asMinor(0),\n    curMinBetSize: newCurMinBetSize,\n    curMinRaiseIncrement: newCurMinRaiseIncrement\n  }\n}\n\n/**\n * @internal\n *\n * Resets per-street spot context when advancing to a new street. \\\n * Advances street and clears voluntary action counter.\n *\n * @param spotContext - Current spot context\n * @param newStreet - The new street to advance to\n * @returns New SpotContext with street advanced and counter reset\n *\n * Updates:\n * - `activeStreet` → newStreet\n * - `numVoluntaryActionsCurStreet` → 0 (fresh counter for new street)\n *\n * Preserves:\n * - `variant` (fixed for the hand)\n * - `actionIsOn` (must be updated separately by caller)\n * - `numActivePlayers` (must be updated separately if players folded)\n *\n * @example\n * ```ts\n * // Flop just closed, advancing to turn\n * const context = {\n *   variant: 'nlhe',\n *   activeStreet: 'flop',\n *   actionIsOn: 'SB',\n *   numActivePlayers: 3,\n *   numVoluntaryActionsCurStreet: 4\n * }\n * const resetContext = resetSpotContextForNewStreet(context, 'turn')\n * // {\n * //   variant: 'nlhe',\n * //   activeStreet: 'turn',               // UPDATED\n * //   actionIsOn: 'SB',                   // preserved (caller updates)\n * //   numActivePlayers: 3,                // preserved\n * //   numVoluntaryActionsCurStreet: 0     // RESET\n * // }\n * ```\n */\nexport const resetSpotContextForNewStreet = (\n  spotContext: SpotContext,\n  newStreet: Street\n): SpotContext => ({\n  ...spotContext,\n  activeStreet: newStreet,\n  numVoluntaryActionsCurStreet: 0\n})\n\n//\n// Ghost Player Detection\n//\n\n/**\n * @internal\n *\n * Detects \"ghost\" positions: players who were seated but open-folded before the hand started. \\\n * These players are not in handPlayers and did not participate in the hand.\n *\n * @param numPlayersSeated - Total number of players at the table (2-10)\n * @param handPlayers - Array of HandPlayer objects who participated in the hand\n * @returns Array of positions that were seated but NOT in handPlayers, in canonical order\n *\n * Detection logic:\n * - Uses numPlayersSeated to get expected position list from positionsByNumPlayers\n * - Returns positions NOT in handPlayers (simple set difference)\n * - Returns empty array if all seated players are in handPlayers\n *\n * Note: Ghosts may or may not have posted antes/forced bets. Any seated position not in\n * handPlayers is considered a ghost, regardless of contributions.\n *\n * @example\n * ```ts\n * // 6-handed: UTG and CO open-fold (no antes, no forced bets posted)\n * const handPlayers = [\n *   { position: 'SB', ... },\n *   { position: 'BB', ... },\n *   { position: 'HJ', ... },\n *   { position: 'BU', ... }  // UTG, CO missing\n * ]\n *\n * detectGhostPositions(6, handPlayers)\n * // ['UTG', 'CO'] - seated but not in handPlayers\n *\n * // 3-handed: BB posts blind then folds preflop\n * const handPlayers2 = [\n *   { position: 'SB', ... },\n *   { position: 'BU', ... }  // BB missing\n * ]\n *\n * detectGhostPositions(3, handPlayers2)\n * // ['BB'] - seated but not in handPlayers\n *\n * // No ghosts: all players in hand\n * const allPlayers = [\n *   { position: 'SB', ... },\n *   { position: 'BB', ... },\n *   { position: 'BU', ... }\n * ]\n * detectGhostPositions(3, allPlayers)\n * // [] - all seated players are in handPlayers\n * ```\n */\nexport const detectGhostPositions = (\n  numPlayersSeated: number,\n  handPlayers: HandPlayer[]\n): Position[] => {\n  // Get all expected positions for this table size\n  const expectedPositions = positions(numPlayersSeated)\n\n  // Get set of positions actually in handPlayers\n  const handPlayerPositions = new Set(handPlayers.map(hp => hp.position))\n\n  // Return positions that are expected but NOT in handPlayers\n  return expectedPositions.filter(pos => !handPlayerPositions.has(pos))\n}\n","import {\n  isNot, pipe, pickBy, values, keys, isNonNullish, map, filter\n} from 'remeda'\n\nimport { throwIf, isNotValidNonNegativeInteger } from './utilsForStcLand'\n\nimport {\n  orderPositionList,\n  streetIsShowDown,\n} from './generalUtils'\n\nimport { $ } from './currencyUtils'\nimport { applyActionToSpotState } from './applyActionToState'\nimport { detectGhostPositions } from './stateMutators'\nimport { getTotalCardsNeededForStreet } from './pokerHandUtils'\n\nimport type {\n  Position, Street,\n  PlayerState, SpotState, SpotContext, BettingState, PlayerStateByPosition,\n  AvailablePlayerActions, UnavailablePlayerActions, PlayerActionStatus,\n  CanCallResult, CanRaiseResult, CanCheckResult, CanBetResult, CanFoldResult,\n  AbleToCallResult, UnableToCallResult,\n  AbleToRaiseResult, UnableToRaiseResult,\n  AbleToCheckResult, UnableToCheckResult,\n  AbleToBetResult, UnableToBetResult,\n  AbleToFoldResult, UnableToFoldResult,\n  StreetClosedReason, StreetOpenReason,\n  PokerHand, PlayerAction, Ante,\n} from '@pokerland/types'\n\n/**\n * @internal\n *\n * Returns true if the player is no longer contesting the hand because they folded.\n */\nexport const hasFoldedAtSpot = (\n  playerState: PlayerState\n): boolean => playerState.hasFolded\n\n/**\n * @internal\n *\n * Returns true when the player remains in the hand (i.e., has not folded).\n */\nexport const hasNotFoldedAtSpot = isNot(hasFoldedAtSpot)\n\n/**\n * @internal\n *\n * Returns true when the player has zero or fewer chips behind at the spot (all-in).\n */\nexport const isAllInAtSpot = (\n  playerState: PlayerState\n): boolean => playerState.curStackSize <= 0\n\n/**\n * @internal\n *\n * Returns true when the player still has chips behind (not all-in).\n */\nexport const isNotAllInAtSpot = isNot(isAllInAtSpot)\n\n/**\n * @internal\n *\n * Returns true when the player is active, meaning they have not folded and still have chips behind.\n */\nexport const playerIsActiveAtSpot = (\n  playerState: PlayerState\n): boolean =>\n  hasNotFoldedAtSpot(playerState) && isNotAllInAtSpot(playerState)\n\n/**\n * @internal\n *\n * Returns true when the player is either folded or out of chips.\n */\nexport const playerIsNotActiveAtSpot = isNot(playerIsActiveAtSpot)\n\n/**\n * @internal\n *\n * Computes how many opponents remain active excluding the player in question.\n */\nexport const numOtherActivePlayers = (\n  playerState: PlayerState,\n  numActivePlayers: number,\n): number =>\n  Math.max(\n    0, numActivePlayers - (playerIsActiveAtSpot(playerState) ? 1 : 0)\n  )\n\n/**\n * @internal\n *\n * Computes how many other players remain in the hand (can win pot) excluding the player in question.\n * Includes all-in opponents since they can win pots at showdown.\n *\n * Used for:\n * - Pot odds calculations\n * - Determining showdown scenarios\n * - Hand strength relative to field size\n */\nexport const numOtherPlayersInHand = (\n  playerState: PlayerState,\n  numPlayersInHand: number,\n): number =>\n  Math.max(\n    0, numPlayersInHand - (hasNotFoldedAtSpot(playerState) ? 1 : 0)\n  )\n\n/**\n * @internal\n *\n * Returns true when the player has taken a voluntary action on the current street.\n */\nexport const hasActedOnCurStreet = (playerState: PlayerState): boolean =>\n  playerState.hasActedCurStreet\n\n/**\n * @internal\n *\n * Returns true when the player has not yet taken a voluntary action on the current street.\n */\nexport const hasNotActedOnCurStreet = isNot(hasActedOnCurStreet)\n\n/**\n * @internal\n *\n * Determines whether the player currently holds procedural raising rights.\n *\n * - Requires a live bet (`curLiveBet > 0`).\n * - Players who have not acted this street always retain raising rights.\n * - Players who already acted regain rights once the live bet has advanced by at least\n *   the raise increment they faced at their previous action (multiple partial raises can\n *   accumulate to satisfy this).\n *\n * Stack depth, opponent state, and turn order validation occur in higher level helpers.\n */\nexport const hasRaisingRights = (\n  playerState: PlayerState,\n  bettingState: BettingState\n): boolean => {\n\n  if (hasFoldedAtSpot(playerState)) return false\n  if (bettingState.curLiveBet === 0) return false\n  if (!playerState.hasActedCurStreet) return true\n\n  const minRaiseIncrementAfterLastAction =\n    playerState.minRaiseIncrementAfterLastAction ??\n    bettingState.curMinRaiseIncrement // safe fallback, but should never hit\n\n  const reopenDelta =\n    bettingState.curLiveBet - playerState.liveBetAfterLastAction\n\n  return reopenDelta >= minRaiseIncrementAfterLastAction\n}\n\n/**\n * @internal\n *\n * Negated convenience helper for {@link hasRaisingRights}.\n */\nexport const doesNotHaveRaisingRights = (\n  playerState : PlayerState,\n  bettingState: BettingState\n): boolean =>\n  !hasRaisingRights(playerState, bettingState)\n\n/**\n * @internal\n *\n * Determines if a player is still required to act on the current street.\n *\n * Algorithm:\n * Step 0 - Immediate exits (player-level):\n *   - Player has folded → Not required\n *   - Player is all-in → Not required\n *\n * Step 1 - Unopened street (curLiveBet === 0):\n *   - Player has already acted (checked/bet) → Not required\n *   - Player hasn't acted yet → Required (must check or bet)\n *\n * Step 2 - Opened street (curLiveBet > 0):\n *   - Player behind on bet (amtAdded < curLiveBet) → Required (must call/fold/raise)\n *   - Player matched bet but hasn't acted voluntarily → Required (BB option case)\n *   - Player matched bet and has acted → Not required\n *\n * Note: This is a player-specific check. Table-level checks (betting closed due to\n * insufficient active players) should be handled separately by curStreetActionClosed().\n */\nexport const isPlayerRequiredToAct = (\n  playerState: PlayerState,\n  bettingState: BettingState\n): boolean => {\n  // Step 0: Immediate exits (player-level only)\n  if (hasFoldedAtSpot(playerState)) return false\n  if (isAllInAtSpot(playerState)) return false\n\n  const { curLiveBet } = bettingState\n  const { amtAddedToPotCurStreet, hasActedCurStreet } = playerState\n\n  // Step 1: Unopened street (curLiveBet === 0)\n  if (curLiveBet === 0) {\n    // Player needs to act if they haven't checked/bet yet\n    return !hasActedCurStreet\n  }\n\n  // Step 2: Opened street (curLiveBet > 0)\n  // Player is behind the current bet - must act\n  if (amtAddedToPotCurStreet < curLiveBet) return true\n\n  // Player has matched the bet amount, but needs to act if they haven't voluntarily acted yet\n  // This handles the BB option case: BB posted forced bet, matched amount, but hasn't acted voluntarily\n  return !hasActedCurStreet\n}\n\n/**\n * @internal\n *\n * Returns true when the player holds betting rights on the street.\n *\n * Betting rights require an unopened street (`curLiveBet === 0`) and an active player.\n * Preflop never qualifies because blinds/straddles seed a live bet.\n *\n * Turn sequencing is enforced by the surrounding state machine; this helper only evaluates eligibility.\n */\nexport const hasBettingRights = (\n  playerState: PlayerState,\n  bettingState: BettingState\n): boolean => {\n  return playerIsActiveAtSpot(playerState) && bettingState.curLiveBet === 0\n}\n\n/**\n * @internal\n *\n * Computes the chips the player must add to complete a full call on the current street.\n *\n * Returns `0` when the street is unopened or the player already matches the live bet\n * (e.g. big blind option scenarios). Throws if the derived amount is not a finite,\n * non-negative number to surface corrupted game state.\n */\nexport const amtToAddForFullCall = (\n  playerState : PlayerState,\n  bettingState: BettingState\n): number => {\n\n  const toAddForFullCall =\n    Math.max(0, bettingState.curLiveBet - playerState.amtAddedToPotCurStreet)\n\n  throwIf(isNotValidNonNegativeInteger(toAddForFullCall),\n    'amtToAddForFullCall()',\n    'calculated amtToAddForFullCall is invalid',\n    {\n      position: playerState.position,\n      curLiveBet: bettingState.curLiveBet,\n      amtAddedToPotCurStreet: playerState.amtAddedToPotCurStreet,\n      amtToAddForFullCall: toAddForFullCall,\n    }\n  )\n  return toAddForFullCall\n}\n\n/**\n * @internal\n *\n * Returns the minimum chips the player must add this street to execute a full raise.\n *\n * When the street is unopened (no live bet) or the player has already committed at least the\n * minimum raise-to amount, the helper returns `0` to indicate a non-raising spot. Throws when\n * the table-level minimum raise increment is invalid.\n *\n * Turn order, rights validation, and stack comparisons are handled by higher-level helpers.\n */\nexport const minToAddForFullRaise = (\n  playerState: PlayerState,\n  bettingState: BettingState\n): number => {\n  const { amtAddedToPotCurStreet } = playerState\n  const { curLiveBet, curMinRaiseIncrement } = bettingState\n\n  throwIf(!Number.isFinite(curMinRaiseIncrement) || curMinRaiseIncrement <= 0,\n    'minToAddForFullRaise()',\n    'curMinRaiseIncrement must be a finite positive number',\n    { position: playerState.position, curMinRaiseIncrement }\n  )\n\n  if (curLiveBet === 0) return 0\n\n  const minRaiseTo = curLiveBet + curMinRaiseIncrement\n  const minToAddForFullRaise = Math.max(0, minRaiseTo - amtAddedToPotCurStreet)\n\n  throwIf(!Number.isFinite(minToAddForFullRaise),\n    'minToAddForFullRaise()',\n    'calculated minToAddForFullRaise is invalid',\n    {\n      position: playerState.position,\n      curLiveBet,\n      curMinRaiseIncrement,\n      amtAddedToPotCurStreet,\n      minRaiseTo,\n      minAdd: minToAddForFullRaise,\n    }\n  )\n\n  return minToAddForFullRaise\n}\n\n/**\n * @public\n *\n * Evaluates whether the player may call the current live bet and returns validation metadata.\n *\n * Guards against situations where the hand already ended, the player folded, is all-in,\n * or it is not their turn. The helper reports partial-call scenarios when the player lacks\n * sufficient chips to match the live bet.\n */\nexport const playerCanCall = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext\n): CanCallResult => {\n  const { activeStreet, actionIsOn } = spotContext\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { canCall: false, reason: 'hand-ended' }\n\n  if (hasFoldedAtSpot(playerState))\n    return { canCall: false, reason: 'player-has-folded' }\n\n  if (isAllInAtSpot(playerState))\n    return { canCall: false, reason: 'player-all-in' }\n\n  if (actionIsOn !== playerState.position)\n    return { canCall: false, reason: 'not-players-turn' }\n\n  // Invariants on inputs we'll use\n  throwIf(isNotValidNonNegativeInteger(bettingState.curLiveBet),\n    'playerCanCall()',\n    'curLiveBet must be a finite non-negative number',\n    {\n      street: activeStreet,\n      position: playerState.position,\n      curLiveBet: bettingState.curLiveBet,\n      amtAddedToPotCurStreet: playerState.amtAddedToPotCurStreet,\n    }\n  )\n  throwIf(isNotValidNonNegativeInteger(playerState.amtAddedToPotCurStreet),\n    'playerCanCall()',\n    'amtAddedToPotCurStreet must be a finite non-negative number',\n    {\n      street: activeStreet,\n      position: playerState.position,\n      amtAddedToPotCurStreet: playerState.amtAddedToPotCurStreet,\n    }\n  )\n\n  if (bettingState.curLiveBet === 0)\n    return { canCall: false, reason: 'no-live-bet' }\n\n  const fullCallAmt = amtToAddForFullCall(playerState, bettingState)\n\n  if (fullCallAmt === 0)\n    return { canCall: false, reason: 'already-equal' }\n\n  if (playerState.curStackSize >= fullCallAmt)\n    return { canCall: true, callAmount: $.asMinor(fullCallAmt), callType: 'full' }\n\n  return { canCall: true, callAmount: $.asMinor(playerState.curStackSize), callType: 'partial' }\n}\n\n/**\n * @public\n *\n * Determines whether the player can raise given the current betting state, returning\n * sizing boundaries and failure reasons that align with NLHE rules. Currently only NLHE\n * is supported; other variants trigger a guard.\n *\n * Validates turn order, player status, remaining opponents, and raising rights before\n * computing min/max raise sizing. Partial raises are surfaced when the player can exceed\n * the call amount but cannot meet the minimum raise increment.\n */\nexport const playerCanRaise = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext\n): CanRaiseResult => {\n\n  const { activeStreet, actionIsOn, variant, numActivePlayers } = spotContext\n\n  // Explicitly out of scope (anything but NLHE)\n  throwIf(variant !== 'nlhe',\n    'playerCanRaise()',\n    'only NLHE max-raise logic is implemented for now',\n    { pokerVariant: variant }\n  )\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { canRaise: false, reason: 'hand-ended' }\n\n  if (actionIsOn !== playerState.position)\n    return { canRaise: false, reason: 'not-players-turn' }\n\n  if (hasFoldedAtSpot(playerState))\n    return { canRaise: false, reason: 'player-has-folded' }\n\n  if (isAllInAtSpot(playerState))\n    return { canRaise: false, reason: 'player-all-in' }\n\n  // Invariants relevant to a raising spot\n  throwIf(isNotValidNonNegativeInteger(bettingState.curLiveBet),\n    'playerCanRaise()',\n    'curLiveBet must be a finite non-negative number',\n    {\n      street: activeStreet,\n      position: playerState.position,\n      curLiveBet: bettingState.curLiveBet,\n      curMinRaiseIncrement: bettingState.curMinRaiseIncrement,\n    }\n  )\n\n  if (bettingState.curLiveBet === 0)\n    return { canRaise: false, reason: 'not-raising-spot' }\n\n  const otherActive = numOtherActivePlayers(playerState, numActivePlayers)\n  if (otherActive === 0)\n    return { canRaise: false, reason: 'betting-closed' }\n\n  if (!hasRaisingRights(playerState, bettingState))\n    return { canRaise: false, reason: 'no-raising-rights' }\n\n  // In a valid raising spot the table min-raise-increment must be > 0\n  throwIf(!Number.isFinite(bettingState.curMinRaiseIncrement) || bettingState.curMinRaiseIncrement <= 0,\n    'playerCanRaise()',\n    'curMinRaiseIncrement must be a finite positive number in a raising spot',\n    {\n      street: activeStreet,\n      position: playerState.position,\n      curMinRaiseIncrement: bettingState.curMinRaiseIncrement,\n    }\n  )\n\n  const minFullRaiseAmtToAdd = minToAddForFullRaise(playerState, bettingState)\n  const toCallAmtToAdd = amtToAddForFullCall(playerState, bettingState)\n\n  // Guard against broken helpers\n  throwIf(isNotValidNonNegativeInteger(minFullRaiseAmtToAdd),\n    'playerCanRaise()',\n    'minToAddForFullRaise returned an invalid amount',\n    { street: activeStreet, position: playerState.position, minFullRaiseAmt: minFullRaiseAmtToAdd }\n  )\n\n  throwIf(isNotValidNonNegativeInteger(toCallAmtToAdd),\n    'playerCanRaise()',\n    'amtToAddForFullCall returned an invalid amount',\n    { street: activeStreet, position: playerState.position, toCallAmt: toCallAmtToAdd }\n  )\n\n  if (minFullRaiseAmtToAdd <= 0)\n    return { canRaise: false, reason: 'not-raising-spot' }\n\n  if (playerState.curStackSize >= minFullRaiseAmtToAdd)\n    return {\n      canRaise: true,\n      minRaiseToAmt: $.asMinor(playerState.amtAddedToPotCurStreet + minFullRaiseAmtToAdd),\n      maxRaiseToAmt: $.asMinor(playerState.amtAddedToPotCurStreet + playerState.curStackSize),\n      minAmtToAdd: $.asMinor(minFullRaiseAmtToAdd),\n      maxAmtToAdd: $.asMinor(playerState.curStackSize),\n      raiseType: 'full',\n    }\n\n  if (playerState.curStackSize > toCallAmtToAdd) {\n    const allInAmt = playerState.curStackSize\n    return {\n      canRaise: true,\n      minRaiseToAmt: $.asMinor(playerState.amtAddedToPotCurStreet + allInAmt),\n      maxRaiseToAmt: $.asMinor(playerState.amtAddedToPotCurStreet + allInAmt),\n      minAmtToAdd: $.asMinor(allInAmt),\n      maxAmtToAdd: $.asMinor(allInAmt),\n      raiseType: 'partial',\n    }\n  }\n\n  // If we get here, player does not enough chips to raise\n  return { canRaise: false, reason: 'insufficient-chips' }\n}\n\n/**\n * @public\n *\n * Determines whether the player may check and returns the corresponding result envelope.\n *\n * Checking is only legal when the player is active, it is their turn, and no additional chips\n * are required to match the live bet.\n */\nexport const playerCanCheck = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext\n): CanCheckResult => {\n\n  const { activeStreet, actionIsOn } = spotContext\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { canCheck: false, reason: 'hand-ended' }\n\n  if (hasFoldedAtSpot(playerState))\n    return { canCheck: false, reason: 'player-has-folded' }\n\n  if (isAllInAtSpot(playerState))\n    return { canCheck: false, reason: 'player-all-in' }\n\n  if (actionIsOn !== playerState.position)\n    return { canCheck: false, reason: 'not-players-turn' }\n\n  // If there’s any amount to add, checking is not allowed\n  if (amtToAddForFullCall(playerState, bettingState) !== 0)\n    return { canCheck: false, reason: 'need-to-call' }\n\n  return { canCheck: true }\n}\n\n/**\n * @public\n *\n * Determines whether the player may open the betting on an unopened street.\n *\n * Requires an active player, their turn to act, at least two active players remaining,\n * and `curLiveBet === 0`. Returns partial bet metadata when the player cannot satisfy\n * the minimum bet size but can still move all-in.\n */\nexport const playerCanBet = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext\n): CanBetResult => {\n\n  const {\n    activeStreet, actionIsOn, numActivePlayers,\n  } = spotContext\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { canBet: false, reason: 'hand-ended' }\n\n  if (hasFoldedAtSpot(playerState))\n    return { canBet: false, reason: 'player-has-folded' }\n\n  if (isAllInAtSpot(playerState))\n    return { canBet: false, reason: 'player-all-in' }\n\n  if (actionIsOn !== playerState.position)\n    return { canBet: false, reason: 'not-players-turn' }\n\n  if (bettingState.curLiveBet !== 0)\n    return { canBet: false, reason: 'not-betting-spot' }\n\n  if (numActivePlayers <= 1)\n    return { canBet: false, reason: 'betting-closed' }\n\n  const minFullBet = bettingState.curMinBetSize\n\n  throwIf(!Number.isFinite(minFullBet) || minFullBet <= 0,\n    'playerCanBet()',\n    'curMinBetSize must be a finite positive number in an unopened betting spot',\n    {\n      street: activeStreet,\n      position: playerState.position,\n      curMinBetSize: minFullBet,\n      curLiveBet: bettingState.curLiveBet,\n      numActivePlayers,\n    }\n  )\n\n  if (playerState.curStackSize >= minFullBet)\n    return {\n      canBet: true,\n      minBetAmt: minFullBet,\n      maxBetAmt: playerState.curStackSize,\n      betType: 'full',\n    }\n\n  // partial bet (all-in < min bet)\n  return {\n    canBet: true,\n    minBetAmt: playerState.curStackSize,\n    maxBetAmt: playerState.curStackSize,\n    betType: 'partial',\n  }\n}\n\n/**\n * @public\n *\n * Determines whether the player may fold and returns the annotated result.\n *\n * Folding is unavailable once the hand has ended, the player already folded, the player is all-in,\n * or it is not their turn to act.\n */\nexport const playerCanFold = (\n  playerState: PlayerState,\n  spotContext: SpotContext\n): CanFoldResult => {\n\n  const { activeStreet, actionIsOn } = spotContext\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { canFold: false, reason: 'hand-ended' }\n\n  if (hasFoldedAtSpot(playerState))\n    return { canFold: false, reason: 'player-has-folded' }\n\n  if (isAllInAtSpot(playerState))\n    return { canFold: false, reason: 'player-all-in' }\n\n  if (actionIsOn !== playerState.position)\n    return { canFold: false, reason: 'not-players-turn' }\n\n  return { canFold: true }\n}\n\n/**\n * @internal\n *\n * Filters the per-position player state down to active (not folded, chips behind) players.\n */\nexport const activeStreetPlayersAtSpot = (\n  playersInHandState: PlayerStateByPosition\n): PlayerStateByPosition =>\n  pipe(\n    playersInHandState ?? {},\n    pickBy((p): p is PlayerState => isNonNullish(p) && playerIsActiveAtSpot(p))\n  ) as PlayerStateByPosition\n\n/**\n * @internal\n *\n * Returns the ordered list of active player positions at the spot.\n */\nexport const activePositionsAtSpot = (\n  playersInHandState: PlayerStateByPosition\n): Position[] => pipe(\n  playersInHandState,\n  activeStreetPlayersAtSpot,\n  keys(),\n  orderPositionList\n)\n\n/**\n * @internal\n *\n * Filters the per-position player state to everyone still in the hand (includes all-ins).\n */\nexport const playersInHandAtSpot = (\n  playersInHandState: PlayerStateByPosition\n): PlayerStateByPosition =>\n  pipe(\n    playersInHandState ?? {},\n    pickBy((p): p is PlayerState => isNonNullish(p) && hasNotFoldedAtSpot(p))\n  ) as PlayerStateByPosition\n\n/**\n * @internal\n *\n * Returns the ordered list of positions for everyone still in the hand (includes all-ins).\n */\nexport const positionsInHandAtSpot = (\n  playersInHandState: PlayerStateByPosition\n): Position[] => pipe(\n  playersInHandState,\n  playersInHandAtSpot,\n  keys(),\n  orderPositionList\n)\n\nexport type StreetClosureResult =\n  | { isClosed: true; reason: StreetClosedReason }\n  | { isClosed: false; reason: StreetOpenReason }\n\n/**\n * @internal\n *\n * Returns an array of positions for players who are still required to act on the current street.\n * Only includes active players (not folded, not all-in) who need to act.\n *\n * Uses isPlayerRequiredToAct() as the source of truth for determining if each player needs to act.\n *\n * @param playersInHandState - Map of all player states by position\n * @param bettingState - Current betting state (curLiveBet, etc.)\n * @returns Array of positions for players who still need to act, in canonical order\n */\nexport const getPlayersRequiredToAct = (\n  playersInHandState: PlayerStateByPosition,\n  bettingState: BettingState\n): Position[] => {\n  const activeList = values(activeStreetPlayersAtSpot(playersInHandState))\n\n  return pipe(\n    activeList,\n    filter(p => isPlayerRequiredToAct(p, bettingState)),\n    map(p => p.position),\n    orderPositionList\n  )\n}\n\n/**\n * @public\n *\n * Finds the first active player to act at the start of a new postflop street.\n * Returns players in canonical order (SB first if active, then BB, etc.).\n *\n * Returns 'hand-ended' if 0 or 1 active players remain.\n *\n * IMPORTANT: This function assumes it's being called for a newly opened street.\n * It will throw an error if any voluntary actions have already occurred on the current street.\n *\n * @param street - The street being initialized (flop, turn, river)\n * @param spotContext - Spot context to validate we're on a new street\n * @param playersInHandState - Map of all player states by position\n * @returns Position of first active player, or 'hand-ended' if ≤1 active players\n * @throws Error if voluntary actions have already occurred on the current street\n *\n * @example\n * ```ts\n * // Flop begins with SB, BB, CO active\n * getFirstToActOnNewStreet('flop', spotContext, playersInHandState)\n * // Returns: 'SB'\n *\n * // Only one player left after others folded\n * getFirstToActOnNewStreet('turn', spotContext, playersInHandState)\n * // Returns: 'hand-ended'\n *\n * // Error if called mid-street\n * // After SB checks on flop:\n * getFirstToActOnNewStreet('flop', spotContext, playersInHandState)\n * // Throws: \"Cannot determine first to act - voluntary actions have already occurred\"\n * ```\n */\nexport const getFirstToActOnNewStreet = (\n  street: Street,\n  numVoluntaryActionsCurStreet: number,\n  playersInHandState: PlayerStateByPosition\n): Position | 'hand-ended' => {\n  // Validate we're on a new street with no voluntary actions yet\n  throwIf(numVoluntaryActionsCurStreet !== 0,\n    'getFirstToActOnNewStreet()',\n    'Cannot determine first to act - voluntary actions have already occurred on current street',\n    { street, numVoluntaryActionsCurStreet }\n  )\n\n  const activePlayers = activePositionsAtSpot(playersInHandState)\n\n  // 0 or 1 active players means hand is over\n  if (activePlayers.length <= 1) {\n    return 'hand-ended'\n  }\n\n  // Return first active player in canonical order\n  return activePlayers[0]\n}\n\n/**\n * @public\n *\n * Determines the next player to act on the current street based on current action position\n * and players who need to act.\n *\n * Searches clockwise (in canonical position order) from the current position to find the next\n * player who is in the playersRequiredToAct list.\n *\n * @param currentActionIsOn - Current position that has action (or 'hand-ended'/'hand-not-started'/'runout-pending')\n * @param playersRequiredToAct - Array of positions that still need to act on current street\n * @param positionsInHand - All positions at the table in canonical order\n * @returns Next position to act on current street, or 'street-closed' if no one needs to act\n *\n * @example\n * ```ts\n * // After CO acts, find next player who needs to act\n * getNextToActOnCurStreet('CO', ['SB', 'BB'], ['SB', 'BB', 'UTG', 'CO', 'BU'])\n * // Returns: 'BU' (first clockwise from CO who needs to act)\n *\n * // Everyone has acted\n * getNextToActOnCurStreet('BB', [], ['SB', 'BB', 'CO'])\n * // Returns: 'street-closed'\n * ```\n */\nexport const getNextToActOnCurStreet = (\n  currentActionIsOn: Position | 'hand-ended' | 'hand-not-started' | 'runout-pending',\n  playersRequiredToAct: Position[],\n  positionsInHand: Position[]\n): Position | 'street-closed' => {\n\n  // If hand is already ended or not started, street is closed\n  if (currentActionIsOn === 'hand-ended'\n    || currentActionIsOn === 'hand-not-started'\n    || currentActionIsOn === 'runout-pending') {\n    return 'street-closed'\n  }\n\n  // If no players need to act, street is closed\n  if (playersRequiredToAct.length === 0) {\n    return 'street-closed'\n  }\n\n  // If only one player needs to act, return them\n  if (playersRequiredToAct.length === 1) {\n    return playersRequiredToAct[0]\n  }\n\n  // Create a set for fast lookup\n  const needToActSet = new Set(playersRequiredToAct)\n\n  const actionPosition = currentActionIsOn\n  const searchPositions = positionsInHand.includes(actionPosition)\n    ? positionsInHand\n    : orderPositionList([...positionsInHand, actionPosition])\n\n  // Find current position index\n  const currentIndex = searchPositions.indexOf(actionPosition)\n\n  // If current position not found in positionsInHand, something is wrong\n  // Return first player who needs to act as fallback\n  if (currentIndex === -1) {\n    return playersRequiredToAct[0]\n  }\n\n  // Search clockwise (forward in canonical order) for next player who needs to act\n  const tableSize = searchPositions.length\n  for (let i = 1; i <= tableSize; i++) {\n    const nextIndex = (currentIndex + i) % tableSize\n    const nextPosition = searchPositions[nextIndex]\n\n    if (needToActSet.has(nextPosition)) {\n      return nextPosition\n    }\n  }\n\n  // Completed full circle without finding anyone - street is closed\n  return 'street-closed'\n}\n\n/**\n * @public\n *\n * Determines whether action on the current street is closed and, if not, explains why.\n *\n * Considers showdown sentinels, active player counts, and which players still owe action.\n */\nexport const curStreetActionClosed = (\n  spotState: SpotState\n): StreetClosureResult => {\n\n  const { spotContext, bettingState, playersInHandState } = spotState\n  const {\n    activeStreet, actionIsOn, numActivePlayers,\n  } = spotContext\n\n  if (streetIsShowDown(activeStreet)\n    || actionIsOn === 'hand-ended'\n    || actionIsOn === 'runout-pending')\n    return { isClosed: true, reason: 'hand-ended' }\n\n  const { curLiveBet } = bettingState\n\n  throwIf(isNotValidNonNegativeInteger(curLiveBet),\n    'curStreetActionClosed()',\n    'curLiveBet must be a finite non-negative number',\n    { street: activeStreet, curLiveBet }\n  )\n\n  const activeCount = numActivePlayers\n  if (activeCount <= 1)\n    return { isClosed: true, reason: 'insufficient-active-players' }\n\n  // Use getPlayersRequiredToAct as our source of truth\n  const playersRequired = getPlayersRequiredToAct(playersInHandState, bettingState)\n  const anyoneNeedsToAct = playersRequired.length > 0\n\n  // If anyone needs to act, street is open; otherwise closed\n  if (anyoneNeedsToAct) {\n    return curLiveBet === 0\n      ? { isClosed: false, reason: 'unopened-pending-checks' }\n      : { isClosed: false, reason: 'opened-pending-match-or-action' }\n  }\n\n  return curLiveBet === 0\n    ? { isClosed: true, reason: 'unopened-all-checked' }\n    : { isClosed: true, reason: 'opened-all-acted-and-matched' }\n}\n\n/**\n * @public\n *\n * Aggregates the availability status for each voluntary action at the spot, separating\n * executable actions from those blocked with a reason.\n */\nexport const getPlayerActionStatus = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext,\n): PlayerActionStatus => {\n\n  const availableActions: AvailablePlayerActions = {}\n  const unavailableActions: UnavailablePlayerActions = {}\n\n  const checkRes = playerCanCheck(playerState, bettingState, spotContext)\n  if (checkRes.canCheck) availableActions.check = checkRes as AbleToCheckResult\n  else unavailableActions.check = checkRes as UnableToCheckResult\n\n  const callRes = playerCanCall(playerState, bettingState, spotContext)\n  if (callRes.canCall) availableActions.call = callRes as AbleToCallResult\n  else unavailableActions.call = callRes as UnableToCallResult\n\n  const betRes = playerCanBet(playerState, bettingState, spotContext)\n  if (betRes.canBet) availableActions.bet = betRes as AbleToBetResult\n  else unavailableActions.bet = betRes as UnableToBetResult\n\n  const raiseRes = playerCanRaise(playerState, bettingState, spotContext)\n  if (raiseRes.canRaise) availableActions.raise = raiseRes as AbleToRaiseResult\n  else unavailableActions.raise = raiseRes as UnableToRaiseResult\n\n  const foldRes = playerCanFold(playerState, spotContext)\n  if (foldRes.canFold) availableActions.fold = foldRes as AbleToFoldResult\n  else unavailableActions.fold = foldRes as UnableToFoldResult\n\n  return { availableActions, unavailableActions }\n}\n\n//======================================================================================\n// SPOT STATE CREATION - Event Sourcing Pattern\n//======================================================================================\n\n/**\n * @internal\n *\n * Gets the ante amount for a specific position based on ante type.\n *\n * @param antes - Ante configuration from PokerHand\n * @param position - Position to check\n * @returns Ante amount in minor units, or 0 if no ante for this position\n */\nconst getAnteForPosition = (\n  antes: Ante | undefined,\n  position: Position\n): number => {\n  if (!antes) return 0\n\n  // Big blind ante: only BB position posts\n  if (antes.type === 'bigBlind') {\n    return position === 'BB' ? antes.amount : 0\n  }\n\n  // All players ante: every position posts same amount\n  if (antes.type === 'allPlayers') {\n    return antes.amount\n  }\n\n  return 0\n}\n\n/**\n * @internal\n *\n * Gets the big blind amount from the hand's forced bets.\n *\n * @param pokerHand - The poker hand containing forced bets\n * @returns Big blind amount in minor units\n */\nconst getBigBlindAmount = (\n  pokerHand: PokerHand\n): $.MinorUnits => {\n  const bigBlind = pokerHand.forcedBets.find(fb => fb.type === 'bigBlind')\n  if (bigBlind) return bigBlind.amount\n\n  // Fallback: use the maximum forced bet amount\n  const maxForcedBet = Math.max(0, ...pokerHand.forcedBets.map(fb => fb.amount))\n  return $.asMinor(maxForcedBet)\n}\n\n/**\n * @internal\n *\n * Based on a POkerHand, this function generates the initial spot state before\n * any actions are applied.\n * - Player states from starting stacks (minus antes)\n * - Betting state with ante pot (if any)\n * - Spot context for preflop start with forced bet posting phase\n *\n * Used by {@link createSpotStateFromPokerHand} as the starting point for event replay.\n *\n * @param pokerHand - The poker hand containing initial conditions\n * @returns Initial SpotState for this poker hand before any actions are applied\n *\n * @example\n * ```ts\n * const hand: PokerHand = { ... }\n * const initialState = initSpotStateFromPokerHand(hand)\n * // initialState has all players at starting stacks minus antes\n * // pot contains only antes\n * // actionIsOn is first forced bet position\n * ```\n */\nexport const initSpotStateFromPokerHand = (\n  pokerHand: PokerHand\n): SpotState => {\n  const { variant, playersInHand, forcedBets, antes, numPlayersSeated } = pokerHand\n\n  // Validate forcedBets is not empty (required to determine initial action order)\n  throwIf(!forcedBets || forcedBets.length === 0,\n    'initSpotStateFromPokerHand()',\n    'forcedBets must contain at least one forced bet (SB, BB, or straddle)',\n    { forcedBets }\n  )\n\n  // Calculate total ante pot contribution based on ante type\n  const antePot: number = !antes ? 0 :\n    antes.type === 'bigBlind' ? antes.amount :\n    antes.type === 'allPlayers' ? antes.amount * numPlayersSeated :\n    0\n\n  // Build initial player states from starting stacks\n  // Subtract antes from starting stacks since antes are posted before actions\n  const playersInHandState: PlayerStateByPosition = Object.fromEntries(\n    Object.entries(playersInHand).map(([pos, handPlayer]) => {\n      const positionAnteAmount = getAnteForPosition(antes, pos as Position)\n      return [\n        pos,\n        {\n          position: pos as Position,\n          curStackSize: $.asMinor(handPlayer.startingStack - positionAnteAmount),\n          amtAddedToPotCurStreet: $.createMinor(0),\n          hasActedCurStreet: false,\n          liveBetAfterLastAction: $.createMinor(0),\n          minRaiseIncrementAfterLastAction: $.createMinor(0),\n          hasFolded: false,\n        } satisfies PlayerState\n      ]\n    })\n  ) as PlayerStateByPosition\n\n  // Build initial betting state\n  // Pot starts with antes, all other fields start at 0\n  const bigBlindAmount = getBigBlindAmount(pokerHand)\n  const bettingState: BettingState = {\n    potSize: $.asMinor(antePot),\n    prevStreetPotSize: $.createMinor(0), // Always 0 on preflop (no previous street)\n    amtAddedToPotCurStreet: $.createMinor(0),\n    curLiveBet: $.createMinor(0),\n    curMinBetSize: $.createMinor(0), // Preflop starts at 0 - forced bets will set it\n    curMinRaiseIncrement: $.asMinor(bigBlindAmount),\n  }\n\n  // Detect ghost positions (seated players who open-folded before hand started)\n  // Convert playersInHand object to array for ghost detection\n  const handPlayersArray = Object.values(playersInHand)\n  const ghostPositions = detectGhostPositions(numPlayersSeated, handPlayersArray)\n\n  // Build initial spot context\n  // Start on preFlop with forced bet posting phase\n  const spotContext: SpotContext = {\n    variant,\n    activeStreet: 'preFlop',\n    actionIsOn: forcedBets[0].position, // First forced bet to be posted\n    numPlayersInHand: Object.keys(playersInHand).length,\n    numActivePlayers: Object.keys(playersInHand).length,\n    numVoluntaryActionsCurStreet: 0,\n    pendingForcedBets: forcedBets.map(fb => fb.position), // Queue of forced bets\n    ghostPositions: ghostPositions.length > 0 ? ghostPositions : undefined,\n    awaitingCommunityCards: false,\n  }\n\n  return { spotContext, playersInHandState, bettingState }\n}\n\n/**\n * @internal\n *\n * Replays a sequence of actions and updates the SpotState accordingly.\n * Iteratively applies each action to build up the final state.\n *\n * **awaitingCommunityCards Handling:**\n * In the runtime flow, when a street advances, `awaitingCommunityCards` is set to `true`,\n * then `addCommunityCards()` is called (which clears the flag), and then player actions\n * continue. During replay, we simulate this by automatically clearing the flag before\n * applying each action, as if the cards were dealt between the street advancement and\n * the action.\n *\n * @param actions - Array of actions to replay\n * @param initialSpotState - Starting spot state (typically from {@link initSpotStateFromPokerHand})\n * @param pokerHand - The poker hand (needed for bigBlindAmount)\n * @returns Final SpotState after all actions are applied\n *\n * @example\n * ```ts\n * const hand: PokerHand = { ... }\n * const initialState = initSpotStateFromPokerHand(hand)\n * const finalState = replayActionsOnSpotState(\n *   hand.playerActions,\n *   initialState,\n *   hand\n * )\n * // finalState reflects the state after all actions in the hand\n * ```\n */\nexport const replayActionsOnSpotState = (\n  actions: PlayerAction[],\n  initialSpotState: SpotState,\n  pokerHand: PokerHand,\n): SpotState => {\n  const bigBlindAmount = getBigBlindAmount(pokerHand)\n  let spotState = initialSpotState\n\n  for (const action of actions) {\n    // Before applying each action, clear awaitingCommunityCards flag if cards are present\n    // This simulates the card dealing that would happen in runtime between\n    // street advancement and the next action. However, only clear if cards were actually dealt\n    // (present in communityCards array), not just assumed to be dealt.\n    if (spotState.spotContext.awaitingCommunityCards === true) {\n      const cardsNeeded = getTotalCardsNeededForStreet(spotState.spotContext.activeStreet)\n      const cardsPresent = (pokerHand.communityCards ?? []).length\n\n      if (cardsPresent >= cardsNeeded) {\n        // Cards are present, clear the flag\n        spotState = {\n          ...spotState,\n          spotContext: {\n            ...spotState.spotContext,\n            awaitingCommunityCards: false,\n          }\n        }\n      }\n    }\n\n    spotState = applyActionToSpotState(action, spotState, bigBlindAmount)\n  }\n\n  return spotState\n}\n\n/**\n * @public\n *\n * Generates the current spot state for a poker hand.  Remember, the hand\n * may represent a completed hand, or one in progress.\n *\n * Uses event-sourcing pattern: `PokerHand.playerActions` is the source of truth,\n * and SpotState is computed/derived from it.\n *\n * **Event Sourcing Benefits:**\n * - Single source of truth: SpotState cannot diverge from action history\n * - Guaranteed consistency: State always matches the recorded actions\n *\n * **awaitingCommunityCards Handling:**\n * The `awaitingCommunityCards` flag is a transient state indicator used in the runtime\n * to enforce that `addCommunityCards()` must be called after a street advances and before\n * any player actions. However, when replaying actions to reconstruct state, this flag\n * should never remain `true` in the final state - it represents a momentary pause in the\n * action flow, not a resting state. This function clears the flag after replay to ensure\n * consistency with the expected final state semantics.\n *\n * @param pokerHand - The poker hand containing action history\n * @returns SpotState representing the current state after all actions\n *\n * @example\n * ```ts\n * const hand: PokerHand = {\n *   playersInHand: { BU: { ... }, BB: { ... } },\n *   playerActions: [\n *     { position: 'BU', actionType: 'call', ... },\n *     { position: 'BB', actionType: 'check', ... }\n *   ],\n *   ...\n * }\n * const currentState = createSpotStateFromPokerHand(hand)\n * console.log(currentState.spotContext.activeStreet) // 'flop'\n * console.log(currentState.bettingState.potSize) // 150\n * ```\n */\nexport const createSpotStateFromPokerHand = (\n  pokerHand: PokerHand\n): SpotState => {\n  const initialSpotState = initSpotStateFromPokerHand(pokerHand)\n  const replayedState = replayActionsOnSpotState(pokerHand.playerActions, initialSpotState, pokerHand)\n\n  return replayedState\n}\n","import { actionIsVoluntary, getNextStreet } from './generalUtils'\n\nimport type {\n  PokerHand, PlayerAction, Position, Street, PokerVariant,\n  SpotState, BettingState, PlayerState, SpotContext,\n  PlayerStateByPosition,\n} from '@pokerland/types'\n\nimport {\n  bettingStateSanityCheck,\n  playerStateSanityCheck,\n  validatePlayerActionCanBeApplied,\n  spotContextSanityCheck,\n  spotStateSanityCheck,\n  validateSpotState,\n} from './stateValidators'\n\nimport { $ } from './currencyUtils'\nimport { throwIf } from './utilsForStcLand'\n\nimport {\n  getPlayersRequiredToAct,\n  getNextToActOnCurStreet,\n  getFirstToActOnNewStreet,\n  positionsInHandAtSpot\n} from './spotStateUtils'\n\nimport {\n  updatePlayersInHandState,\n  updateBettingState,\n  updateSpotContext,\n  resetPlayerStatesForNewStreet,\n  resetBettingStateForNewStreet\n} from './stateMutators'\n\nimport { countActivePlayers, countPlayersInHand } from './stateHelpers'\n\n/**\n * @internal\n *\n * Result of applying a player action to a poker hand.\n *\n * @property pokerHand - Updated poker hand with action appended to playerActions\n * @property spotState - Updated spot state reflecting the action and any street transitions.\n *   Access spotState.mostRecentActionEffects for handEnded/streetAdvanced info.\n */\nexport interface ApplyActionToHandStateResult {\n  pokerHand: PokerHand\n  spotState: SpotState\n}\n\n/**\n * @internal\n *\n * Applies a player action to a poker hand, updating all related state immutably.\n * This is the main orchestration function that:\n * 1. Validates the action can be applied (via actionToAddSanityCheck)\n * 2. Updates betting state, player states, and spot context\n * 3. Handles street transitions and per-street state resets\n * 4. Appends action to playerActions list\n * 5. Validates the resulting state (via spotStateSanityCheck)\n *\n * Action effects (handEnded, streetAdvanced) are embedded in the returned\n * spotState.mostRecentActionEffects field.\n *\n * @param position - Position of the player taking the action\n * @param playerAction - The action to apply\n * @param pokerHand - Poker hand state before the action\n * @param spotState - Spot state before the action\n * @returns Object containing updated pokerHand and spotState (with mostRecentActionEffects embedded)\n * @throws Error if validation fails (either pre-action or post-action)\n */\nexport const applyActionToHandState = (\n  position: Position,\n  playerAction: PlayerAction,\n  pokerHand: PokerHand,\n  spotState: SpotState,\n): ApplyActionToHandStateResult => {\n\n  const { playerActions } = pokerHand\n\n  // Validate the action before applying\n  const val = validatePlayerActionCanBeApplied(\n    position, playerAction, pokerHand, spotState, 'applyActionToHandState()'\n  )\n  if (!val.valid) { throw new Error(val.message) }\n\n  // Get BB amount for postflop min bet size update (needed for street transitions)\n  const bigBlindAmount = pokerHand.forcedBets.find(fb => fb.type === 'bigBlind')?.amount\n    ?? $.asMinor(Math.max(0, ...pokerHand.forcedBets.map(fb => fb.amount)))\n\n  // Apply action to spot state (handles all three components + street transitions + validation)\n  const updatedSpotState = applyActionToSpotState(playerAction, spotState, bigBlindAmount)\n\n  // Append action to playerActions list\n  const updatedPlayerActions = [...playerActions, playerAction]\n\n  // Build updated PokerHand\n  const updatedPokerHand: PokerHand = {\n    ...pokerHand,\n    playerActions: updatedPlayerActions\n  }\n\n  // Action effects are now embedded in spotState.mostRecentActionEffects\n  // No need to return separate meta object\n  return {\n    pokerHand: updatedPokerHand,\n    spotState: updatedSpotState\n  }\n}\n\n/**\n * @internal\n *\n * Applies a player action to the betting state immutably.\n * Updates pot size, live bet, and min raise increment based on action type.\n *\n * Handles special cases:\n * - Partial bets/raises (all-in for less): min raise increment logic\n * - Forced bets: blind/straddle initialization of betting state\n * - Forced bet all-in-for-less: Uses intendedForcedBetAmount for curLiveBet calculation\n *   while pot calculations use the actual amountAddedToPot (capped at player's stack)\n * - curMinBetSize is preserved throughout the hand (never changes)\n *\n * @param playerAction - The action being applied (assumed to be valid)\n * @param bettingState - Betting state before the action (assumed to be valid)\n * @returns Updated betting state after the action\n * @throws Error if the resulting state is invalid (via bettingStateSanityCheck)\n */\nexport const applyActionToBettingState = (\n  playerAction: PlayerAction,\n  bettingState: BettingState,\n): BettingState => {\n\n  const { potSize, amtAddedToPotCurStreet, curLiveBet, curMinRaiseIncrement, curMinBetSize } = bettingState\n  let newPotSize = potSize\n  let newAmtAddedToPotCurStreet = amtAddedToPotCurStreet\n  let newCurLiveBet = curLiveBet\n  let newCurMinRaiseIncrement = curMinRaiseIncrement\n\n  const raiseByAmount = playerAction.actionType === 'raise'\n    ? (playerAction.raiseToAmount ?? 0) - curLiveBet\n    : 0\n\n  switch (playerAction.actionType) {\n  case 'smallBlind': {\n    newPotSize = $.asMinor(potSize + playerAction.amountAddedToPot)\n    newAmtAddedToPotCurStreet = $.asMinor(newAmtAddedToPotCurStreet + playerAction.amountAddedToPot)\n\n    // Use intended amount if available (short forced bet), otherwise use actual amount\n    const sbAmountForLiveBet = playerAction.intendedForcedBetAmount ?? playerAction.amountAddedToPot\n    newCurLiveBet = $.asMinor(sbAmountForLiveBet)\n    newCurMinRaiseIncrement = $.asMinor(sbAmountForLiveBet)\n    break\n  }\n\n  case 'bigBlind':\n  case 'straddle': {\n    newPotSize = $.asMinor(potSize + playerAction.amountAddedToPot)\n    newAmtAddedToPotCurStreet = $.asMinor(newAmtAddedToPotCurStreet + playerAction.amountAddedToPot)\n\n    // Use intended amount if available (short forced bet), otherwise use actual amount\n    const forcedBetAmountForLiveBet = playerAction.intendedForcedBetAmount ?? playerAction.amountAddedToPot\n    newCurLiveBet = $.asMinor(forcedBetAmountForLiveBet)\n    newCurMinRaiseIncrement = $.asMinor(forcedBetAmountForLiveBet)\n    break\n  }\n\n  case 'check':\n  case 'fold':\n    break\n\n  case 'call':\n    newPotSize = $.asMinor(potSize + playerAction.amountAddedToPot)\n    newAmtAddedToPotCurStreet = $.asMinor(newAmtAddedToPotCurStreet + playerAction.amountAddedToPot)\n    break\n\n  case 'bet':\n    newPotSize = $.asMinor(potSize + playerAction.amountAddedToPot)\n    newAmtAddedToPotCurStreet = $.asMinor(newAmtAddedToPotCurStreet + playerAction.amountAddedToPot)\n    newCurLiveBet = $.asMinor(playerAction.amountAddedToPot)\n    // Partial bet: all-in for less than min bet size -> min raise increment stays as min bet size\n    // Full bet: bet >= min bet size -> min raise increment updates to the bet amount\n    if (playerAction.isAllIn && playerAction.amountAddedToPot < curMinBetSize) {\n      // Partial bet: min raise increment remains min bet size\n      newCurMinRaiseIncrement = $.asMinor(curMinBetSize)\n    } else {\n      // Full bet: min raise increment becomes the bet amount\n      newCurMinRaiseIncrement = $.asMinor(playerAction.amountAddedToPot)\n    }\n    // curMinBetSize never changes during a hand\n    break\n\n  case 'raise':\n    newPotSize = $.asMinor(potSize + playerAction.amountAddedToPot)\n    newAmtAddedToPotCurStreet = $.asMinor(newAmtAddedToPotCurStreet + playerAction.amountAddedToPot)\n    newCurLiveBet = $.asMinor(playerAction.raiseToAmount)\n    if (raiseByAmount >= curMinRaiseIncrement) {\n      newCurMinRaiseIncrement = $.asMinor(raiseByAmount)\n    }\n    break\n  }\n\n  // Use updateBettingState to preserve curMinBetSize while updating other fields\n  const newBettingState = updateBettingState(bettingState, {\n    potSize: newPotSize,\n    amtAddedToPotCurStreet: newAmtAddedToPotCurStreet,\n    curLiveBet: newCurLiveBet,\n    curMinRaiseIncrement: newCurMinRaiseIncrement,\n  })\n\n  // validate the result\n  const val = bettingStateSanityCheck(\n    newBettingState, 'applyActionToBettingState() updated bettingState'\n  )\n  if (!val.valid) { throw new Error(val?.message) }\n\n  return newBettingState\n}\n\n/**\n * @internal\n *\n * Applies a player action to player state immutably.\n * Updates stack size, pot contribution, and action flags based on action type.\n *\n * State updates:\n * - curStackSize: reduced by amountAddedToPot (clamped to 0 for all-in)\n * - amtAddedToPotCurStreet: increased by amountAddedToPot\n * - hasActedCurStreet: set to true for voluntary actions only\n * - hasFolded: set to true for fold actions\n * - liveBetAfterLastAction: updated for voluntary wagers (call/bet/raise)\n * - minRaiseIncrementAfterLastAction: updated for voluntary wagers (call/bet/raise)\n *\n * @param playerAction - The action being applied (assumed to be valid)\n * @param playerState - Player state before the action (assumed to be valid)\n * @param updatedBettingState - Betting state after the action is applied (used to capture current betting levels)\n * @returns Updated player state after the action\n * @throws Error if the resulting state is invalid (via playerStateSanityCheck)\n */\nexport const applyActionToPlayerState = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n  updatedBettingState: BettingState,\n): PlayerState => {\n\n  const { position, curStackSize, amtAddedToPotCurStreet } = playerState\n\n  // Update stack and pot contribution (applies to all actions)\n  // Clamp stack to 0 to handle all-in for less (e.g., short stack posting blinds)\n  const newCurStackSize = $.asMinor(Math.max(0, curStackSize - playerAction.amountAddedToPot))\n  const newAmtAddedToPotCurStreet = $.asMinor(amtAddedToPotCurStreet + playerAction.amountAddedToPot)\n\n  // Initialize state tracking (only updated for voluntary wagers)\n  let newLiveBetAfterLastAction = playerState.liveBetAfterLastAction\n  let newMinRaiseIncrementAfterLastAction = playerState.minRaiseIncrementAfterLastAction\n  let newHasActedCurStreet = playerState.hasActedCurStreet\n  let newHasFolded = playerState.hasFolded\n\n  switch (playerAction.actionType) {\n  case 'smallBlind':\n  case 'bigBlind':\n  case 'straddle':\n    // Forced bets don't count as voluntary action\n    // liveBetAfterLastAction and minRaiseIncrementAfterLastAction remain unchanged\n    newHasActedCurStreet = false\n    break\n\n  case 'check':\n    // Voluntary action but not a wager (no chips added)\n    // liveBetAfterLastAction and minRaiseIncrementAfterLastAction remain unchanged\n    newHasActedCurStreet = actionIsVoluntary(playerAction.actionType)\n    break\n\n  case 'fold':\n    // Voluntary action but not a wager (player exits hand)\n    // liveBetAfterLastAction and minRaiseIncrementAfterLastAction remain unchanged\n    newHasActedCurStreet = actionIsVoluntary(playerAction.actionType)\n    newHasFolded = true\n    break\n\n  case 'call':\n  case 'bet':\n  case 'raise':\n    // Voluntary wagers: update all tracking fields\n    newHasActedCurStreet = actionIsVoluntary(playerAction.actionType)\n    newLiveBetAfterLastAction = updatedBettingState.curLiveBet\n    newMinRaiseIncrementAfterLastAction = updatedBettingState.curMinRaiseIncrement\n    break\n  }\n\n  const newPlayerState: PlayerState = {\n    position,\n    curStackSize: newCurStackSize,\n    amtAddedToPotCurStreet: newAmtAddedToPotCurStreet,\n    hasActedCurStreet: newHasActedCurStreet,\n    liveBetAfterLastAction: newLiveBetAfterLastAction,\n    minRaiseIncrementAfterLastAction: newMinRaiseIncrementAfterLastAction,\n    hasFolded: newHasFolded,\n  }\n\n  // validate the result\n  const val = playerStateSanityCheck(\n    newPlayerState, 'applyActionToPlayerState() updated playerState'\n  )\n  if (!val.valid) { throw new Error(val?.message) }\n\n  return newPlayerState\n}\n\n/**\n * @internal\n *\n * Applies a player action to spot context immutably.\n * Determines next-to-act, street transitions, and hand completion.\n *\n * Logic flow:\n * 1. Update numPlayersInHand (counts players not folded, includes all-ins)\n * 2. Update numActivePlayers (counts players not folded and with chips to act)\n * 3. Update numVoluntaryActionsCurStreet (increments for voluntary actions)\n * 4. Determine next action state:\n *    - If ≤1 player remains in hand: hand ends\n *    - Check if street closes (all players acted and matched bet)\n *      - If street closes: advance to next street, reset counter, find first to act\n *      - If river closes: transition to showDown and end hand\n *    - Otherwise: find next to act on current street\n *\n * Ghost Player Handling:\n * - Ghost players ARE processed in the forced bet queue (they post their forced bets)\n * - After forced bet queue empties, ghosts are excluded from voluntary action\n *   (they're not in updatedPlayersInHandState, so determineNextAction ignores them)\n * - Ghost forced bets advance actionIsOn normally within the queue\n *\n * @param playerAction - The action being applied (assumed to be valid)\n * @param spotContext - Spot context before the action (assumed to be valid)\n * @param updatedPlayersInHandState - Player states after applying the action (excludes ghosts)\n * @param updatedBettingState - Betting state after applying the action\n * @returns Updated spot context after the action\n * @throws Error if the resulting state is invalid (via spotContextSanityCheck)\n */\nexport const applyActionToSpotContext = (\n  playerAction: PlayerAction,\n  spotContext: SpotContext,\n  updatedPlayersInHandState: PlayerStateByPosition,\n  updatedBettingState: BettingState,\n): SpotContext => {\n\n  const {\n    variant, activeStreet, numVoluntaryActionsCurStreet, pendingForcedBets, awaitingCommunityCards\n  } = spotContext\n\n  // Compute new state values (declared at top for clarity)\n  const newNumPlayersInHand = countPlayersInHand(updatedPlayersInHandState)\n  const newNumActivePlayers = countActivePlayers(updatedPlayersInHandState)\n  let newActiveStreet = activeStreet\n  let newActionIsOn: SpotContext['actionIsOn'] = 'hand-ended'\n  let newNumVoluntaryActionsCurStreet = actionIsVoluntary(playerAction.actionType)\n    ? numVoluntaryActionsCurStreet + 1\n    : numVoluntaryActionsCurStreet\n  let newPendingForcedBets = pendingForcedBets\n  // PRESERVE existing value instead of defaulting to undefined\n  let newAwaitingCommunityCards: boolean | undefined = awaitingCommunityCards\n\n  // Determine next action state\n  // Hand ends only if players folded out (≤1 player in hand)\n  // If everyone is all-in (numActivePlayers=0) but multiple players remain (numPlayersInHand>1),\n  // streets still advance to showDown per poker rules\n  if (newNumPlayersInHand <= 1) {\n    newActionIsOn = 'hand-ended'\n    newPendingForcedBets = undefined // Clear queue if hand ends\n    // Note: awaitingCommunityCards is NOT cleared here - preserve historical state\n    // (whether cards were dealt before hand ended). All checks use === true, so\n    // false and undefined are treated identically. Preserving false maintains\n    // event-sourcing principle of retaining state history.\n  } else if (pendingForcedBets && pendingForcedBets.length > 0) {\n    // FORCED BET POSTING PHASE: Process queue of positions that need to post forced bets\n    // Validate that the head of queue matches the action (fail fast on incorrect order)\n    if (pendingForcedBets[0] !== playerAction.position) {\n      throw new Error(\n        `applyActionToSpotContext(): Expected ${pendingForcedBets[0]} to post forced bet, got ${playerAction.position}`\n      )\n    }\n\n    // Remove only the first element from queue (handles multiple forced bets from same position)\n    const remainingForcedBets = pendingForcedBets.slice(1)\n\n    if (remainingForcedBets.length > 0) {\n      // More forced bets remaining - next position is head of queue\n      newActionIsOn = remainingForcedBets[0]\n      newPendingForcedBets = remainingForcedBets\n    } else {\n      // All forced bets posted - transition to voluntary action phase\n      newPendingForcedBets = undefined // Clear queue marker\n      const result = determineNextAction(\n        playerAction.position,\n        activeStreet,\n        variant,\n        newNumPlayersInHand,\n        newNumActivePlayers,\n        updatedPlayersInHandState,\n        updatedBettingState\n      )\n      newActionIsOn = result.actionIsOn\n      newActiveStreet = result.activeStreet\n      // Only update awaitingCommunityCards if street advanced AND result includes it\n      // If street didn't advance, preserve the current value\n      // If result doesn't include awaitingCommunityCards (e.g., showDown), preserve current value\n      if (result.activeStreet !== activeStreet) {\n        if (result.awaitingCommunityCards !== undefined) {\n          newAwaitingCommunityCards = result.awaitingCommunityCards\n        }\n        newNumVoluntaryActionsCurStreet = 0\n      }\n    }\n  } else {\n    // VOLUNTARY ACTION PHASE: Use standard turn order logic\n    const result = determineNextAction(\n      playerAction.position,\n      activeStreet,\n      variant,\n      newNumPlayersInHand,\n      newNumActivePlayers,\n      updatedPlayersInHandState,\n      updatedBettingState\n    )\n    newActionIsOn = result.actionIsOn\n    newActiveStreet = result.activeStreet\n    // Only update awaitingCommunityCards if street advanced AND result includes it\n    // If street didn't advance, preserve the current value\n    // If result doesn't include awaitingCommunityCards (e.g., showDown), preserve current value\n    if (result.activeStreet !== activeStreet) {\n      if (result.awaitingCommunityCards !== undefined) {\n        newAwaitingCommunityCards = result.awaitingCommunityCards\n      }\n      newNumVoluntaryActionsCurStreet = 0\n    }\n  }\n\n  const newSpotContext = updateSpotContext(\n    spotContext,\n    {\n      activeStreet: newActiveStreet,\n      actionIsOn: newActionIsOn,\n      numPlayersInHand: newNumPlayersInHand,\n      numActivePlayers: newNumActivePlayers,\n      numVoluntaryActionsCurStreet: newNumVoluntaryActionsCurStreet,\n      pendingForcedBets: newPendingForcedBets,\n      awaitingCommunityCards: newAwaitingCommunityCards,\n    })\n\n  const val = spotContextSanityCheck(\n    newSpotContext, 'applyActionToSpotContext() updated spotContext'\n  )\n  if (!val.valid) { throw new Error(val?.message) }\n\n  return newSpotContext\n}\n\n/**\n * @internal\n *\n * Applies a player action to spot state, updating all three components immutably.\n *\n * This is a consolidated orchestration function that:\n * 1. Applies action to betting state\n * 2. Applies action to player state (using updated betting state)\n * 3. Applies action to spot context (using updated player states and betting state)\n * 4. Handles street transitions and per-street state resets\n * 5. Validates the resulting spot state\n *\n * This function is independent of PokerHand and operates purely on SpotState.\n * This function does NOT validate the action itself - it assumes the action is valid.\n *\n * Ghost Player Handling:\n * - Ghost forced bets (byGhostPlayer=true) update betting state but skip player state updates\n * - Ghost players have no playerState (they open-folded before voluntary action)\n * - Ghost forced bets contribute to pot and advance action in the forced bet queue\n * - After forced bet queue empties, ghosts are excluded from voluntary action turn order\n *\n * @param playerAction - The action to apply (assumed to be valid)\n * @param spotState - Current spot state before the action\n * @param bigBlindAmount - Big blind amount for the hand (needed for postflop min bet size on street transitions)\n * @returns Updated spot state after applying the action\n * @throws Error if the resulting spot state is invalid (via spotStateSanityCheck)\n * @throws Error if non-ghost player not found in playersInHandState\n */\nexport const applyActionToSpotState = (\n  playerAction: PlayerAction,\n  spotState: SpotState,\n  bigBlindAmount: $.MinorUnits,\n): SpotState => {\n  // Validate input spotState before processing\n  validateSpotState(spotState, 'applyActionToSpotState() input validation')\n\n  const { playersInHandState, bettingState, spotContext } = spotState\n  const { position, byGhostPlayer } = playerAction\n\n  // Step 1: Apply action to betting state\n  const updatedBettingState = applyActionToBettingState(playerAction, bettingState)\n\n  // Step 2: Apply action to player state (using updated betting state)\n  // Ghost players skip player state updates (they have no playerState)\n  let updatedPlayersInHandState = playersInHandState\n\n  if (byGhostPlayer) {\n    // Ghost forced bet: contributes to pot but has no playerState to update\n    // updatedPlayersInHandState remains unchanged\n  } else {\n    // Regular player: apply state updates\n    const playerState = playersInHandState[position]\n    throwIf(\n      !playerState,\n      'applyActionToSpotState()',\n      `Player at position ${position} not found in playersInHandState`\n    )\n\n    const updatedPlayerState = applyActionToPlayerState(\n      playerAction,\n      playerState!,\n      updatedBettingState\n    )\n    updatedPlayersInHandState = updatePlayersInHandState(\n      playersInHandState,\n      position,\n      updatedPlayerState\n    )\n  }\n\n  // Step 3: Apply action to spot context (using updated player states and betting state)\n  const updatedSpotContext = applyActionToSpotContext(\n    playerAction,\n    spotContext,\n    updatedPlayersInHandState,\n    updatedBettingState\n  )\n\n  // Step 4: Handle street transitions and per-street resets\n  const streetAdvanced = spotContext.activeStreet !== updatedSpotContext.activeStreet\n\n  const finalPlayersInHandState = streetAdvanced\n    ? resetPlayerStatesForNewStreet(updatedPlayersInHandState)\n    : updatedPlayersInHandState\n\n  const finalBettingState = streetAdvanced\n    ? resetBettingStateForNewStreet(\n      updatedBettingState,\n      spotContext.activeStreet,\n      updatedSpotContext.activeStreet,\n      bigBlindAmount\n    )\n    : updatedBettingState\n\n  // Step 5: Compute action effects metadata\n  const handEnded = updatedSpotContext.actionIsOn === 'hand-ended'\n\n  // Build final SpotState\n  const finalSpotState: SpotState = {\n    spotContext: updatedSpotContext,\n    playersInHandState: finalPlayersInHandState,\n    bettingState: finalBettingState,\n    mostRecentActionEffects: {\n      handEnded,\n      streetAdvanced,\n    },\n  }\n\n  // Step 5: Validate the resulting spot state\n  const val = spotStateSanityCheck(\n    finalSpotState,\n    'applyActionToSpotState() final spot state'\n  )\n  if (!val.valid) { throw new Error(val.message) }\n\n  return finalSpotState\n}\n\n/**\n * Result of determining next action and street.\n *\n * @property actionIsOn - Position of next player to act, or 'hand-ended' if hand is over\n * @property activeStreet - Current or next street\n * @property awaitingCommunityCards - Optional flag indicating community cards must be dealt before players can act\n */\ninterface DetermineNextActionResult {\n  actionIsOn: SpotContext['actionIsOn']\n  activeStreet: Street\n  awaitingCommunityCards?: boolean\n}\n\n/**\n * Determines next action and street after a player acts.\n * Shared logic for both forced bet completion and voluntary action phases.\n *\n * Checks if the street should advance and determines who acts next.\n * Does NOT handle the voluntary action counter - caller is responsible for resetting it when street advances.\n *\n * @param currentPosition - Position that just acted\n * @param currentStreet - Current active street\n * @param variant - Poker variant\n * @param newNumPlayersInHand - Updated count of players in hand\n * @param newNumActivePlayers - Updated count of active players\n * @param updatedPlayersInHandState - Player states after the action\n * @param updatedBettingState - Betting state after the action\n * @returns DetermineNextActionResult containing new actionIsOn and activeStreet\n */\nconst determineNextAction = (\n  currentPosition: Position,\n  currentStreet: Street,\n  variant: PokerVariant,\n  newNumPlayersInHand: number,\n  newNumActivePlayers: number,\n  updatedPlayersInHandState: PlayerStateByPosition,\n  updatedBettingState: BettingState\n): DetermineNextActionResult => {\n\n  const nextToAct = getNextToActOnCurStreet(\n    currentPosition,\n    getPlayersRequiredToAct(updatedPlayersInHandState, updatedBettingState),\n    positionsInHandAtSpot(updatedPlayersInHandState)\n  )\n\n  // Case 1: Street not closed - continue on current street\n  if (nextToAct !== 'street-closed') {\n    return {\n      actionIsOn: nextToAct,\n      activeStreet: currentStreet\n    }\n  }\n\n  // Street is closed - advance to next street\n  const nextStreet = getNextStreet(currentStreet)\n\n  // All players are either all-in or folded, but more than one remains in hand.\n  // Betting is closed for the rest of the hand; advance street and mark runout pending.\n  // Trigger runout when no further betting is possible. numActivePlayers counts the players\n  // with chips behind; a value <= 1 while more than one player remains in the hand means the\n  // remaining opponents are all-in (or the lone active player has no one left who can\n  // continue betting against them). In this case we advance the street but mark the\n  // spot as awaiting runout instead of ending the hand immediately.\n  // Set awaitingCommunityCards=true because cards must be dealt (via runout flow).\n  if (newNumPlayersInHand > 1 && newNumActivePlayers <= 1 && nextStreet !== 'showDown') {\n    return {\n      actionIsOn: 'runout-pending',\n      activeStreet: nextStreet,\n      awaitingCommunityCards: true\n    }\n  }\n\n  // Case 2: Advancing to showdown - hand ends\n  if (nextStreet === 'showDown') {\n    return {\n      actionIsOn: 'hand-ended',\n      activeStreet: nextStreet\n    }\n  }\n\n  // Case 3: Advancing to new betting street (normal flow with active players)\n  // Set awaitingCommunityCards flag to require card dealing before next actions\n  return {\n    actionIsOn: getFirstToActOnNewStreet(\n      nextStreet,\n      0, // we have a new street, so no voluntary actions have occurred yet\n      updatedPlayersInHandState\n    ),\n    activeStreet: nextStreet,\n    awaitingCommunityCards: true\n  }\n}\n","/**\n * Action validation for poker hands.\n *\n * Provides the main isValidAction function and individual validators\n * for each action type, checking amounts, betting context, and all-in consistency.\n */\n\nimport type {\n  PlayerAction, PlayerState, BettingState, SpotContext,\n  ActionType, CantActReason, InvalidActionAmountReason,\n  AbleToCallResult, AbleToRaiseResult, AbleToBetResult,\n} from '@pokerland/types'\n\nimport { throwIf, some } from './utilsForStcLand'\nimport { actionList } from './generalUtils'\nimport { getPlayerActionStatus } from './spotStateUtils'\nimport { isNullish } from 'remeda'\n\nexport type IsValidActionResults =\n  | { actionType: ActionType; isValid: true }\n  | { actionType: ActionType; isValid: false; reason: CantActReason | InvalidActionAmountReason }\n\n/**\n * @internal\n *\n * Assumes positions have been validated already.\n */\nexport const isValidAction = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext,\n): IsValidActionResults => {\n\n  const { actionType } = playerAction\n\n  // Validate action type is recognized\n  if (!actionList.includes(actionType))\n    return { actionType, isValid: false, reason: 'unknown-action-type' }\n\n  // Validate amounts are within expected ranges\n  const { raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n\n  throwIf(some(isNullish, [raiseToAmount, amountAddedToPot, isAllIn]),\n    'isValidAction()',\n    'undefined action amounts',\n    { raiseToAmount, amountAddedToPot, isAllIn },\n  )\n\n  // Handle forced bets separately, as getPlayerActionStatus only returns voluntary action info\n  if (actionType === 'smallBlind' || actionType === 'bigBlind' || actionType === 'straddle') {\n    return validateForcedBet(playerAction, playerState)\n  }\n\n  // For voluntary actions, check eligibility\n  const playerActionStatus = getPlayerActionStatus(playerState, bettingState, spotContext)\n  const { availableActions, unavailableActions } = playerActionStatus\n\n  // If action is unavailable, return the reason\n  const unavailableResult = unavailableActions[actionType]\n  if (unavailableResult)\n    return { actionType, isValid: false, reason: unavailableResult.reason }\n\n  // If action is not in available list, something went wrong\n  if (!availableActions[actionType])\n    return { actionType, isValid: false, reason: 'unknown-action-type' }\n\n  // Validate action amounts are in bounds\n  switch (actionType) {\n  case 'call': {\n    const callInfo = availableActions.call as AbleToCallResult\n    return validateCall(playerAction, playerState, bettingState, callInfo)\n  }\n\n  case 'raise': {\n    const raiseInfo = availableActions.raise as AbleToRaiseResult\n    return validateRaise(playerAction, playerState, bettingState, raiseInfo)\n  }\n\n  case 'bet': {\n    const betInfo = availableActions.bet as AbleToBetResult\n    return validateBet(playerAction, playerState, bettingState, betInfo)\n  }\n\n  case 'check':\n    return validateCheck(playerAction)\n\n  case 'fold':\n    return validateFold(playerAction)\n  }\n}\n\n/**\n * @internal\n */\nexport const validateCall = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n  bettingState: BettingState,\n  callInfo: AbleToCallResult,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n  const { callAmount, callType } = callInfo\n\n  // Validate betting context - calls require a live bet\n  if (bettingState.curLiveBet <= 0)\n    return { actionType, isValid: false, reason: 'call-requires-live-bet' }\n\n  if (raiseToAmount > 0)\n    return { actionType, isValid: false, reason: 'raise-to-amount-specified-on-non-raise' }\n\n  if (amountAddedToPot !== callAmount)\n    return { actionType, isValid: false, reason: 'invalid-call-amount' }\n\n  // Validate cannot add more than stack\n  if (amountAddedToPot > playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'amount-exceeds-stack' }\n\n  // Validate partial calls are all-in\n  if (callType === 'partial' && !isAllIn)\n    return { actionType, isValid: false, reason: 'partial-call-must-be-all-in' }\n\n  // Validate full calls that are all-in use exact stack\n  if (callType === 'full' && isAllIn && amountAddedToPot !== playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'full-call-all-in-stack-mismatch' }\n\n  // Validate that using exact stack must be marked all-in\n  if (callType === 'full' && amountAddedToPot === playerState.curStackSize && !isAllIn)\n    return { actionType, isValid: false, reason: 'exact-stack-must-be-all-in' }\n\n  return { actionType, isValid: true }\n}\n\n/**\n * @internal\n */\nexport const validateRaise = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n  bettingState: BettingState,\n  raiseInfo: AbleToRaiseResult,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n  const { minAmtToAdd, maxAmtToAdd, raiseType } = raiseInfo\n\n  // Validate betting context - raises require a live bet\n  if (bettingState.curLiveBet <= 0)\n    return { actionType, isValid: false, reason: 'raise-requires-live-bet' }\n\n  if (raiseToAmount <= 0)\n    return { actionType, isValid: false, reason: 'no-raise-to-amount-specified-on-raise' }\n\n  // Validate raise actually increases the bet\n  if (raiseToAmount <= bettingState.curLiveBet)\n    return { actionType, isValid: false, reason: 'raise-to-amount-must-exceed-live-bet' }\n\n  // Validate amount is in valid range\n  if (amountAddedToPot < minAmtToAdd || amountAddedToPot > maxAmtToAdd)\n    return { actionType, isValid: false, reason: 'invalid-raise-amount' }\n\n  // Validate consistency between raiseToAmount and amountAddedToPot\n  const expectedRaiseToAmount = playerState.amtAddedToPotCurStreet + amountAddedToPot\n  if (raiseToAmount !== expectedRaiseToAmount)\n    return { actionType, isValid: false, reason: 'raise-to-amount-inconsistent-with-amount-added' }\n\n  // Validate cannot add more than stack\n  if (amountAddedToPot > playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'amount-exceeds-stack' }\n\n  // Validate partial raises are all-in\n  if (raiseType === 'partial' && !isAllIn)\n    return { actionType, isValid: false, reason: 'partial-raise-must-be-all-in' }\n\n  // Validate full raises that are all-in use exact stack\n  if (raiseType === 'full' && isAllIn && amountAddedToPot !== playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'full-raise-all-in-stack-mismatch' }\n\n  // Validate that using exact stack must be marked all-in\n  if (raiseType === 'full' && amountAddedToPot === playerState.curStackSize && !isAllIn)\n    return { actionType, isValid: false, reason: 'exact-stack-must-be-all-in' }\n\n  return { actionType, isValid: true }\n}\n\n/**\n * @internal\n */\nexport const validateBet = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n  bettingState: BettingState,\n  betInfo: AbleToBetResult,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n  const { minBetAmt, maxBetAmt, betType } = betInfo\n\n  // Validate betting context - bets require no live bet\n  if (bettingState.curLiveBet !== 0)\n    return { actionType, isValid: false, reason: 'bet-requires-no-live-bet' }\n\n  if (raiseToAmount > 0)\n    return { actionType, isValid: false, reason: 'raise-to-amount-specified-on-non-raise' }\n\n  // Validate amount is in valid range\n  if (amountAddedToPot < minBetAmt || amountAddedToPot > maxBetAmt)\n    return { actionType, isValid: false, reason: 'invalid-bet-amount' }\n\n  // Validate cannot add more than stack\n  if (amountAddedToPot > playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'amount-exceeds-stack' }\n\n  // Validate partial bets are all-in\n  if (betType === 'partial' && !isAllIn)\n    return { actionType, isValid: false, reason: 'partial-bet-must-be-all-in' }\n\n  // Validate full bets that are all-in use exact stack\n  if (betType === 'full' && isAllIn && amountAddedToPot !== playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'full-bet-all-in-stack-mismatch' }\n\n  // Validate that using exact stack must be marked all-in\n  if (betType === 'full' && amountAddedToPot === playerState.curStackSize && !isAllIn)\n    return { actionType, isValid: false, reason: 'exact-stack-must-be-all-in' }\n\n  return { actionType, isValid: true }\n}\n\n/**\n * @internal\n */\nexport const validateCheck = (\n  playerAction: PlayerAction,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n\n  if (raiseToAmount > 0)\n    return { actionType, isValid: false, reason: 'raise-to-amount-specified-on-non-raise' }\n\n  if (amountAddedToPot > 0)\n    return { actionType, isValid: false, reason: 'check-must-not-add-to-pot' }\n\n  // Validate checks cannot be all-in (they add 0 chips)\n  if (isAllIn)\n    return { actionType, isValid: false, reason: 'check-cannot-be-all-in' }\n\n  return { actionType, isValid: true }\n}\n\n/**\n * @internal\n */\nexport const validateFold = (\n  playerAction: PlayerAction,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n\n  if (raiseToAmount > 0)\n    return { actionType, isValid: false, reason: 'raise-to-amount-specified-on-non-raise' }\n\n  if (amountAddedToPot > 0)\n    return { actionType, isValid: false, reason: 'fold-must-not-add-to-pot' }\n\n  // Validate folds cannot be all-in (they add 0 chips)\n  if (isAllIn)\n    return { actionType, isValid: false, reason: 'fold-cannot-be-all-in' }\n\n  return { actionType, isValid: true }\n}\n\n/**\n * @internal\n */\nexport const validateForcedBet = (\n  playerAction: PlayerAction,\n  playerState: PlayerState,\n): IsValidActionResults => {\n  const { actionType, raiseToAmount, amountAddedToPot, isAllIn } = playerAction\n\n  if (raiseToAmount > 0)\n    return { actionType, isValid: false, reason: 'raise-to-amount-specified-on-non-raise' }\n\n  if (amountAddedToPot <= 0)\n    return { actionType, isValid: false, reason: 'forced-bet-must-add-to-pot' }\n\n  // Validate cannot add more than stack\n  if (amountAddedToPot > playerState.curStackSize)\n    return { actionType, isValid: false, reason: 'amount-exceeds-stack' }\n\n  // Validate isAllIn consistency: if putting in all chips, should be marked all-in\n  const puttingInAllChips = amountAddedToPot === playerState.curStackSize\n  if (puttingInAllChips && !isAllIn)\n    return { actionType, isValid: false, reason: 'exact-stack-must-be-all-in' }\n\n  // Validate isAllIn consistency: if marked all-in, should be putting in all chips OR have no chips\n  if (isAllIn && amountAddedToPot < playerState.curStackSize && playerState.curStackSize > 0)\n    return { actionType, isValid: false, reason: 'not-all-in-but-marked-as-all-in' }\n\n  return { actionType, isValid: true }\n}\n","import {\n  PlayerAction,\n  SpotState,\n  PokerHand,\n  PlayerState, BettingState, SpotContext,\n  PlayerOption,\n  ActionToAdd,\n  GameType,\n  BetDisplay,\n  Position,\n  Street,\n  Card,\n} from '@pokerland/types'\n\nimport { isNot } from 'remeda'\n\nimport {\n  throwIf, isNotValidNonNegativeInteger, isNotValidPositiveInteger\n} from './utilsForStcLand'\n\nimport { $ } from './currencyUtils'\nimport { actionRequiresAmount } from './generalUtils'\nimport { applyActionToHandState, } from './applyActionToState'\nimport {\n  playerCanCheck, playerCanCall, playerCanBet, playerCanRaise, playerCanFold,\n  createSpotStateFromPokerHand,\n} from './spotStateUtils'\nimport { addCommunityCards } from './pokerHandUtils'\n\nimport { isValidAction, type IsValidActionResults } from './actionValidators'\nimport { handStateSanityCheck } from './stateValidators'\n\nexport type { IsValidActionResults }\n\n// Import and re-export result type from internal implementation\nimport type { ApplyActionToHandStateResult } from './applyActionToState'\n\n/**\n * @public\n *\n * Result of applying a player action to a poker hand.\n *\n * This is the public API type that wraps the internal ApplyActionToHandStateResult.\n * Action effects (handEnded, streetAdvanced) are embedded in\n * spotState.mostRecentActionEffects.\n */\nexport type AddActionToHandResult = ApplyActionToHandStateResult\n\n/**\n * @public\n *\n * Returns the available actions for a player at the current spot.\n *\n * Queries player capability functions to determine which actions are legal\n * and includes min/max amounts for actions that require amounts.\n *\n * @param playerState - Player's state at the current spot\n * @param bettingState - Betting state at the current spot\n * @param spotContext - Context of the current spot\n * @returns Array of available player options with action types and amounts\n */\nexport const availableActionsAtSpot = (\n  playerState: PlayerState,\n  bettingState: BettingState,\n  spotContext: SpotContext,\n): PlayerOption[] => {\n\n  const options: PlayerOption[] = []\n\n  const checkResult = playerCanCheck(playerState, bettingState, spotContext)\n  if (checkResult.canCheck)\n    options.push({ actionType: 'check' })\n\n  const callResult = playerCanCall(playerState, bettingState, spotContext)\n  if (callResult.canCall) {\n    options.push({\n      actionType: 'call',\n      minAmount: callResult.callAmount,\n      maxAmount: callResult.callAmount,\n    })\n  }\n\n  const betResResult = playerCanBet(playerState, bettingState, spotContext)\n  if (betResResult.canBet) {\n    options.push({\n      actionType: 'bet',\n      minAmount: betResResult.minBetAmt,\n      maxAmount: betResResult.maxBetAmt,\n    })\n  }\n\n  const raiseResult = playerCanRaise(playerState, bettingState, spotContext)\n  if (raiseResult.canRaise) {\n    options.push({\n      actionType: 'raise',\n      minAmount: raiseResult.minRaiseToAmt,\n      maxAmount: raiseResult.maxRaiseToAmt,\n    })\n  }\n\n  const foldResult = playerCanFold(playerState, spotContext)\n  if (foldResult.canFold) {\n    options.push({ actionType: 'fold' })\n  }\n\n  return options\n}\n\n/**\n * @public\n *\n * Adds a player action to a poker hand, updating both hand and spot state.\n *\n * This is the primary API for applying actions to a hand. It creates a PlayerAction\n * from the input parameters and applies it to both the hand history and spot state.\n *\n * @param hand - Current poker hand state\n * @param spotState - Current spot state (player states, betting state, context)\n * @param actionToAdd - Action to add with position and actionType. Amount field varies by type:\n *   - check/fold: no amount field\n *   - call: optional callAmount (omit to auto-calculate)\n *   - bet: betAmount (chips to add to pot)\n *   - raise: raiseToAmount (what live bet will be after raise)\n *   - forced bets: forcedBetAmount (chips posted)\n * @returns Object containing updated hand, spotState, and metadata about state changes\n * @throws Error if action is invalid (player not found, invalid amounts, etc.)\n *\n * @example\n * ```ts\n * // Bet 20 chips\n * addActionToHand(hand, spotState, { position: 'BU', actionType: 'bet', betAmount: 20 })\n *\n * // Raise to 50\n * addActionToHand(hand, spotState, { position: 'CO', actionType: 'raise', raiseToAmount: 50 })\n *\n * // Call (auto-calculated)\n * addActionToHand(hand, spotState, { position: 'SB', actionType: 'call' })\n * ```\n */\nexport const addActionToHand = (\n  hand: PokerHand,\n  spotState: SpotState,\n  actionToAdd: ActionToAdd,\n): AddActionToHandResult => {\n\n  // Guard: Reject player actions if community cards need to be dealt first\n  throwIf(\n    spotState.spotContext.awaitingCommunityCards === true,\n    'addActionToHand()',\n    `Cannot add player action - community cards must be dealt first for ${spotState.spotContext.activeStreet} street. ` +\n    `Call addCommunityCards('${spotState.spotContext.activeStreet}', cards, hand, spotState) before adding actions.`,\n    {\n      activeStreet: spotState.spotContext.activeStreet,\n      awaitingCommunityCards: spotState.spotContext.awaitingCommunityCards,\n      attemptedAction: actionToAdd\n    }\n  )\n\n  const playerAction = makePlayerAction(actionToAdd, spotState)\n  const actionResult = applyActionToHandState(\n    actionToAdd.position, playerAction, hand, spotState\n  )\n  return actionResult\n}\n\n/**\n * @public\n *\n * Validates an action-to-add against the current spot state.\n *\n * This is a convenience function that combines makePlayerAction and isValidAction\n * into a single call, making it easier to validate user input before applying actions.\n *\n * @param actionToAdd - Action input with position and actionType. Amount field varies by type:\n *   - check/fold: no amount field\n *   - call: optional callAmount (omit to auto-calculate)\n *   - bet: betAmount (chips to add to pot)\n *   - raise: raiseToAmount (what live bet will be after raise)\n *   - forced bets: forcedBetAmount (chips posted)\n * @param spotState - Current spot state (player states, betting state, context)\n * @returns Validation result indicating if action is valid and reason if invalid\n * @throws Error if player not found at position or invalid amounts provided\n *\n * @example\n * ```ts\n * // Validate a bet\n * const result = validateActionToAdd(\n *   { position: 'BU', actionType: 'bet', betAmount: 20 },\n *   spotState\n * )\n * if (!result.isValid) {\n *   console.log('Invalid action:', result.reason)\n * }\n *\n * // Validate a raise\n * const result = validateActionToAdd(\n *   { position: 'CO', actionType: 'raise', raiseToAmount: 50 },\n *   spotState\n * )\n * ```\n */\nexport const validateActionToAdd = (\n  actionToAdd: ActionToAdd,\n  spotState: SpotState,\n): IsValidActionResults => {\n  // Generate ephemeral PlayerAction from ActionToAdd\n  const playerAction = makePlayerAction(actionToAdd, spotState)\n\n  // Extract state components\n  const playerState = spotState.playersInHandState[actionToAdd.position]!\n  const { bettingState, spotContext } = spotState\n\n  // Validate using existing function\n  return isValidAction(playerAction, playerState, bettingState, spotContext)\n}\n\n/**\n * @internal\n *\n * Creates a PlayerAction object from player input, computing all required fields.\n *\n * Handles forced bet all-in-for-less scenarios where a player must post a blind/straddle\n * but has insufficient chips. In these cases:\n * - amountAddedToPot is capped at the player's stack (actual chips posted)\n * - intendedForcedBetAmount stores the full blind amount (for curLiveBet calculation)\n * - isAllIn is set to true\n *\n * @param actionToAdd - Simplified action input with position and actionType. Amount field varies by type:\n *   - check/fold: no amount field\n *   - call: optional callAmount (omit to auto-calculate)\n *   - bet: betAmount (chips to add to pot)\n *   - raise: raiseToAmount (what live bet will be after raise)\n *   - forced bets: forcedBetAmount (intended blind amount, may exceed stack for all-in-for-less)\n * @param spotState - Current spot state (player states, betting state, context)\n * @returns PlayerAction object ready for validation and application\n * @throws Error on invalid input (e.g., player not found, missing/invalid amount for action type)\n *\n * @example\n * ```ts\n * // Bet 20 chips\n * makePlayerAction({ position: 'BU', actionType: 'bet', betAmount: 20 }, spotState)\n *\n * // Raise to 50\n * makePlayerAction({ position: 'CO', actionType: 'raise', raiseToAmount: 50 }, spotState)\n *\n * // Call (auto-calculated)\n * makePlayerAction({ position: 'SB', actionType: 'call' }, spotState)\n * ```\n */\nexport const makePlayerAction = (\n  actionToAdd: ActionToAdd,\n  spotState: SpotState,\n): PlayerAction => {\n  const { position, actionType } = actionToAdd\n\n  // Auto-detect ghost player for forced bets\n  // Ghost = position not in playersInHandState (only for forced bets, not voluntary actions)\n  const isForcedBet = actionType === 'smallBlind' ||\n                      actionType === 'bigBlind' ||\n                      actionType === 'straddle'\n\n  const { spotContext, playersInHandState, bettingState } = spotState\n  const { activeStreet: street } = spotContext\n\n  const byGhostPlayer = isForcedBet && !playersInHandState[position]\n    ? true\n    : undefined  // Preserve undefined for non-ghost actions (cleaner serialization)\n\n  // Extract amount based on action type (discriminated union)\n  let amount = 0\n  if (actionType === 'call') {\n    amount = actionToAdd.callAmount ?? 0\n  } else if (actionType === 'bet') {\n    amount = actionToAdd.betAmount\n  } else if (actionType === 'raise') {\n    amount = actionToAdd.raiseToAmount\n  } else if (actionType === 'smallBlind' || actionType === 'bigBlind' || actionType === 'straddle') {\n    amount = actionToAdd.forcedBetAmount\n  }\n  // check/fold: amount stays 0\n\n  const playerState = playersInHandState[position]\n\n  // For ghost forced bets, playerState won't exist - this is expected\n  throwIf(!playerState && !byGhostPlayer,\n    'makePlayerAction()',\n    `Player at position ${position} is not in the hand and is not a ghost player`\n  )\n\n  throwIf(isNotValidNonNegativeInteger(amount),\n    'makePlayerAction()',\n    'amount must be a non-negative number',\n    { actionType, amount }\n  )\n\n  // All actions except call and check/fold require positive amounts\n  // Call allows amount=0 for auto-calculation\n  throwIf(\n    actionRequiresAmount(actionType) &&\n    actionType !== 'call' &&\n    isNotValidPositiveInteger(amount),\n    'makePlayerAction()',\n    `action type \"${actionType}\" requires a positive amount`,\n    { actionType, amount }\n  )\n\n  // Validate amount doesn't exceed player stack (action-specific validation)\n  // Skip stack validations for ghost forced bets (they have no playerState)\n  if (!byGhostPlayer && actionType === 'raise') {\n    // For raise: amount is raise-to, so check that (amount - amtAddedToPotCurStreet) <= stack\n    const amountToAdd = amount - playerState!.amtAddedToPotCurStreet\n    throwIf(isNotValidPositiveInteger(amountToAdd) || amountToAdd > playerState!.curStackSize,\n      'makePlayerAction()',\n      'raiseToAmount exceeds player stack size',\n      { actionType, raiseToAmount: amount, amountToAdd, playerStackSize: playerState!.curStackSize }\n    )\n  } else if (!byGhostPlayer && actionRequiresAmount(actionType)) {\n    // For other actions: amount is the amount being added\n    const isForcedBet = actionType === 'smallBlind' || actionType === 'bigBlind' || actionType === 'straddle'\n\n    // Forced bets can exceed stack (all-in for less) - skip validation\n    // Voluntary actions (bet/call) must not exceed stack\n    throwIf(!isForcedBet && amount > playerState!.curStackSize,\n      'makePlayerAction()',\n      'action amount exceeds player stack size',\n      { actionType, amount, playerStackSize: playerState!.curStackSize }\n    )\n  }\n\n  // Default amounts\n  let amountAddedToPot = 0\n  let raiseToAmount = 0\n  let isAllIn = false\n  let amountToDisplay = 0\n  let intendedForcedBetAmount: number | undefined = undefined\n\n  switch (actionType) {\n\n  // No amounts calcs\n  case 'check':\n  case 'fold':\n    break\n\n  // forced bets\n  case 'smallBlind':\n  case 'bigBlind':\n  case 'straddle': {\n    // amount = intended forced bet amount (for curLiveBet)\n    // amountAddedToPot = actual chips posted (capped at stack)\n    amountAddedToPot = byGhostPlayer\n      ? amount  // Ghosts post full amount (no stack constraint)\n      : Math.min(amount, playerState!.curStackSize)  // Real players capped at stack\n\n    isAllIn = byGhostPlayer ? false : amountAddedToPot >= playerState!.curStackSize\n    amountToDisplay = amountAddedToPot  // Display actual amount posted\n\n    // Store intended amount if player is short-stacked\n    const isShortForcedBet = !byGhostPlayer && amount > playerState!.curStackSize\n    intendedForcedBetAmount = isShortForcedBet ? amount : undefined\n    break\n  }\n\n  case 'bet':\n    amountAddedToPot = amount\n    isAllIn = byGhostPlayer ? false : amountAddedToPot >= playerState!.curStackSize\n    amountToDisplay = amountAddedToPot\n    break\n\n  case 'call': {\n    // Ghost players cannot call (only forced bets) - enforce at runtime\n    throwIf(byGhostPlayer === true,\n      'makePlayerAction()',\n      'Ghost players cannot take voluntary actions (call)',\n      { position, actionType }\n    )\n    // Auto-calculate call amount if not provided (amount === 0)\n    if (amount > 0) {\n      amountAddedToPot = amount\n    } else {\n      const toCall = bettingState.curLiveBet - playerState!.amtAddedToPotCurStreet\n      amountAddedToPot = Math.min(toCall, playerState!.curStackSize)\n    }\n    isAllIn = amountAddedToPot >= playerState!.curStackSize\n    amountToDisplay = amountAddedToPot\n    break\n  }\n\n  case 'raise':\n    // Ghost players cannot raise (only forced bets) - enforce at runtime\n    throwIf(byGhostPlayer === true,\n      'makePlayerAction()',\n      'Ghost players cannot take voluntary actions (raise)',\n      { position, actionType }\n    )\n    raiseToAmount = amount\n    amountAddedToPot = raiseToAmount - playerState!.amtAddedToPotCurStreet\n    isAllIn = amountAddedToPot >= playerState!.curStackSize\n    amountToDisplay = raiseToAmount\n    break\n\n  default:\n    throwIf(\n      true, 'makePlayerAction()', 'unsupported action type',  { actionType }\n    )\n  }\n\n  return {\n    position,\n    actionType,\n    street,\n    amountAddedToPot: $.asMinor(amountAddedToPot),\n    raiseToAmount: $.asMinor(raiseToAmount),\n    amountToDisplay: $.asMinor(amountToDisplay),\n    isAllIn,\n    ...(byGhostPlayer && { byGhostPlayer: true }),  // Only include if true (preserve optional field contract)\n    ...(intendedForcedBetAmount !== undefined && { intendedForcedBetAmount: $.asMinor(intendedForcedBetAmount) }),\n  }\n}\n\n\n/**\n * @public\n *\n * Infers the appropriate betDisplay mode from gameType.\n *\n * Tournament games typically display amounts in big blinds,\n * while cash games display in currency units.\n *\n * @param gameType - The type of poker game (cash or tournament)\n * @returns 'bigBlinds' for tournaments, 'currency' for cash games\n *\n * @example\n * ```ts\n * inferBetDisplay('cash')       // 'currency'\n * inferBetDisplay('tournament') // 'bigBlinds'\n * ```\n */\nexport const inferBetDisplay = (gameType: GameType): BetDisplay => {\n  return gameType === 'tournament' ? 'bigBlinds' : 'currency'\n}\n\n/**\n * @public\n *\n * Type guard to check if an action is a forced bet.\n *\n * Forced bets include small blinds, big blinds, and straddles -\n * mandatory preflop contributions that occur before voluntary action.\n *\n * @param action - The action to check (PlayerAction or ActionToAdd)\n * @returns True if the action is a forced bet, false otherwise\n *\n * @example\n * ```ts\n * const action = { actionType: 'smallBlind', ... }\n * if (isForcedAction(action)) {\n *   // Handle forced bet\n * }\n * ```\n */\nexport const isForcedAction = (action: PlayerAction | ActionToAdd): boolean => {\n  return action.actionType === 'smallBlind'\n    || action.actionType === 'bigBlind'\n    || action.actionType === 'straddle'\n}\n\n/**\n * @public\n *\n * Type guard to check if an action is NOT a forced bet.\n *\n * Returns true for voluntary actions (check, call, bet, raise, fold).\n * Returns false for forced bets (small blind, big blind, straddle).\n *\n * @returns True if the action is NOT a forced bet, false otherwise\n *\n * @example\n * ```ts\n * // Filter to only voluntary actions\n * const voluntaryActions = actions.filter(isNotForcedAction)\n * ```\n */\nexport const isNotForcedAction = isNot(isForcedAction)\n\n/**\n * @public\n *\n * Find the index of the first voluntary action in a list of actions.\n *\n * Assumes forced actions (blinds, straddles, antes) are contiguous at the start\n * of the action list. Returns -1 if no voluntary actions are found.\n *\n * @param actions - Array of player actions to search\n * @returns Index of first voluntary action, or -1 if none found\n *\n * @example\n * ```ts\n * const actions = [smallBlind, bigBlind, raise, call, fold]\n * const firstVoluntaryIdx = getFirstVoluntaryActionIndex(actions)\n * // Returns 2 (the raise action)\n * ```\n */\nexport function getFirstVoluntaryActionIndex(\n  actions: readonly (PlayerAction | ActionToAdd)[]\n): number {\n  for (let i = 0; i < actions.length; i++) {\n    if (!isForcedAction(actions[i])) {\n      return i\n    }\n  }\n  return -1\n}\n\n/**\n * @public\n *\n * Find the index of the last forced action in a list of actions.\n *\n * Assumes forced actions (blinds, straddles, antes) are contiguous at the start\n * of the action list. Returns -1 if no forced actions are found.\n *\n * @param actions - Array of player actions to search\n * @returns Index of last forced action, or -1 if none found\n *\n * @example\n * ```ts\n * const actions = [smallBlind, bigBlind, raise, call, fold]\n * const lastForcedIdx = getLastForcedActionIndex(actions)\n * // Returns 1 (the bigBlind action)\n * ```\n */\nexport function getLastForcedActionIndex(\n  actions: readonly (PlayerAction | ActionToAdd)[]\n): number {\n  let lastForcedIdx = -1\n  for (let i = 0; i < actions.length; i++) {\n    if (isForcedAction(actions[i])) {\n      lastForcedIdx = i\n    } else {\n      // Stop at first voluntary action (assumes forced actions are contiguous at start)\n      break\n    }\n  }\n  return lastForcedIdx\n}\n\n/**\n * @internal\n *\n * Trims community cards array to match the specified street.\n *\n * Poker streets have specific numbers of community cards:\n * - preFlop: 0 cards\n * - flop: 3 cards\n * - turn: 4 cards\n * - river: 5 cards\n * - showDown: 5 cards (same as river)\n *\n * @param cards - Current community cards array\n * @param street - Target street to trim to\n * @returns Trimmed community cards array\n *\n * @example\n * ```ts\n * const riverCards = [card1, card2, card3, card4, card5]\n * trimCommunityCardsToStreet(riverCards, 'flop') // [card1, card2, card3]\n * trimCommunityCardsToStreet(riverCards, 'preFlop') // []\n * ```\n */\nconst trimCommunityCardsToStreet = (cards: Card[], street: Street): Card[] => {\n  const cardsPerStreet: Record<Street, number> = {\n    preFlop: 0,\n    flop: 3,\n    turn: 4,\n    river: 5,\n    showDown: 5,\n  }\n\n  const targetCardCount = cardsPerStreet[street]\n  return cards.slice(0, targetCardCount)\n}\n\n/**\n * Helper function to extract cards for a specific street from the community cards array.\n * Returns the new cards that should be dealt for the given street.\n *\n * @param street - The street to get cards for\n * @param existingCards - Current community cards on the board\n * @returns Array of cards to deal for this street\n */\nconst getCardsForStreet = (street: Street, existingCards: Card[]): Card[] => {\n  const currentCount = existingCards.length\n\n  switch (street) {\n  case 'preFlop':\n    return []\n  case 'flop':\n      // Flop needs 3 cards, deal cards at indices 0-2\n    return currentCount >= 3 ? existingCards.slice(0, 3) : []\n  case 'turn':\n      // Turn needs 1 card at index 3\n    return currentCount >= 4 ? [existingCards[3]] : []\n  case 'river':\n      // River needs 1 card at index 4\n    return currentCount >= 5 ? [existingCards[4]] : []\n  case 'showDown':\n    return []\n  }\n}\n\n/**\n * @public\n *\n * Removes the last N actions from a poker hand and recomputes state.\n * Returns updated pokerHand and recomputed spotState\n *\n * Use cases:\n * - Undo/redo functionality in hand replayers\n * - Correcting mistaken actions in hand history editors\n *\n * @param numToDrop - Number of actions to remove from the end (must be >= 0)\n * @param pokerHand - Current poker hand with action history\n * @returns Object containing updated pokerHand and recomputed spotState\n * @throws Error if numToDrop is invalid (negative or exceeds action count)\n * @throws Error if resulting state is invalid\n *\n * @example\n * ```ts\n * // Remove last 2 actions and recompute state\n * const hand: PokerHand = { ... } // has 10 actions\n * const result = dropLastActions(2, hand)\n * // result.pokerHand.playerActions.length === 8\n * // result.spotState reflects state after first 8 actions\n *\n * // Remove all actions (back to initial state)\n * const initialResult = dropLastActions(hand.playerActions.length, hand)\n * // initialResult.spotState is initial state (post-antes, pre-forced-bets)\n * ```\n */\nexport const dropLastActions = (\n  numToDrop: number,\n  pokerHand: PokerHand,\n): { pokerHand: PokerHand; spotState: SpotState } => {\n\n  // Validate input\n  throwIf(\n    isNotValidNonNegativeInteger(numToDrop),\n    'dropLastActions()',\n    'numToDrop must be a non-negative integer',\n    { numToDrop }\n  )\n\n  throwIf(\n    numToDrop > pokerHand.playerActions.length,\n    'dropLastActions()',\n    'numToDrop exceeds number of actions in hand',\n    { numToDrop, numActions: pokerHand.playerActions.length }\n  )\n\n  // Remove last N actions from the hand\n  // When numToDrop is 0, this keeps all actions\n  const remainingActions = numToDrop === 0\n    ? pokerHand.playerActions\n    : pokerHand.playerActions.slice(0, -numToDrop)\n\n  // Determine what street we're on after dropping actions\n  // This will tell us how many community cards should be present\n  const lastRemainingAction = remainingActions[remainingActions.length - 1]\n  const streetAfterDrop = lastRemainingAction?.street ?? 'preFlop'\n\n  // Trim community cards based on the street we're reverting to\n  const communityCardsAfterDrop = trimCommunityCardsToStreet(pokerHand.communityCards, streetAfterDrop)\n\n  const updatedPokerHand: PokerHand = {\n    ...pokerHand,\n    playerActions: remainingActions,\n    communityCards: communityCardsAfterDrop,\n  }\n\n  // Recompute spot state from scratch by replaying remaining actions\n  let recomputedSpotState = createSpotStateFromPokerHand(updatedPokerHand)\n\n  // Track the final poker hand - either updated or with runout cards added\n  let finalPokerHand = updatedPokerHand\n\n  // Apply runout progression if hand is in runout mode\n  // When all players are all-in, the hand enters runout-pending state\n  // We need to progress through remaining streets by dealing community cards\n  if (recomputedSpotState.spotContext.actionIsOn === 'runout-pending') {\n    let currentPokerHand = updatedPokerHand\n    let currentSpotState = recomputedSpotState\n\n    // Continue dealing cards for each remaining street until showdown\n    while (currentSpotState.spotContext.actionIsOn === 'runout-pending' &&\n           currentSpotState.spotContext.activeStreet !== 'showDown') {\n\n      const currentStreet = currentSpotState.spotContext.activeStreet\n\n      // Runout can only happen on flop, turn, or river (not preFlop)\n      // showDown is already excluded by the while condition above\n      if (currentStreet === 'preFlop') {\n        break\n      }\n\n      // Determine how many cards to deal for the CURRENT street\n      // When in runout mode with awaitingCommunityCards=true, we need to deal cards\n      // for the current street before advancing further\n      // IMPORTANT: Use the ORIGINAL pokerHand.communityCards which has all cards,\n      // not currentPokerHand.communityCards which may have been trimmed\n      const cardsForStreet = getCardsForStreet(currentStreet, pokerHand.communityCards ?? [])\n\n      // If we don't have cards for this street in the original hand, stop\n      // (This handles incomplete runout scenarios)\n      if (cardsForStreet.length === 0) {\n        break\n      }\n\n      // Deal the cards and advance the state\n      // addCommunityCards will advance to the next street and keep actionIsOn='runout-pending'\n      const result = addCommunityCards(\n        currentStreet,\n        cardsForStreet,\n        currentPokerHand,\n        currentSpotState\n      )\n\n      currentPokerHand = result.pokerHand\n      currentSpotState = result.spotState\n    }\n\n    recomputedSpotState = currentSpotState\n    finalPokerHand = currentPokerHand  // Use the hand with runout cards added\n  }\n\n  // Validate the final state\n  // Use any valid position from the hand for validation when hand is ended/not-started\n  // Also use a real player position when actionIsOn is a ghost position (during forced bet phase)\n  const actionIsOnGhost = recomputedSpotState.spotContext.ghostPositions?.includes(\n    recomputedSpotState.spotContext.actionIsOn as Position\n  ) ?? false\n\n  const validationPosition = recomputedSpotState.spotContext.actionIsOn === 'hand-ended'\n    || recomputedSpotState.spotContext.actionIsOn === 'runout-pending'\n    || recomputedSpotState.spotContext.actionIsOn === 'hand-not-started'\n    || actionIsOnGhost\n    ? Object.keys(updatedPokerHand.playersInHand)[0] as Position\n    : recomputedSpotState.spotContext.actionIsOn\n\n  const val = handStateSanityCheck(\n    validationPosition,\n    finalPokerHand,\n    recomputedSpotState,\n    'post-action',\n    'dropLastActions() result'\n  )\n  if (!val.valid) throw new Error(val.message)\n\n  return {\n    pokerHand: finalPokerHand,\n    spotState: recomputedSpotState\n  }\n}\n","/**\n * Pure initialization and update utilities for poker hands.\n *\n * Provides immutable functions for:\n * - Initializing complete poker hand records\n * - Managing hand state without mutation\n *\n * All functions return new objects and never mutate inputs.\n */\n\nimport { throwIf } from './utilsForStcLand'\n\nimport type {\n  PokerVariant, VenueType, GameType, BetDisplay, $, Position,\n  ForcedBet, Ante, HandPlayer, HandPlayersByPosition, PokerHand, SpotState,\n} from '@pokerland/types'\n\nimport { orderPlayerListByPosition } from './generalUtils'\nimport { addActionToHand } from './actionUtils'\nimport { initSpotState } from './stateMutators'\n\n//\n// Initializers\n//\n\n/**\n * Configuration object for initializing a poker hand.\n * Contains all metadata and player information needed to start a hand.\n */\nexport interface PokerHandInfo {\n  /** Poker variant being played (e.g., 'nlhe', 'plo', 'plo5') */\n  variant: PokerVariant\n\n  /** Environment where hand takes place ('live' or 'online') */\n  venueType: VenueType\n\n  /** Name or identifier of the venue (e.g., 'The Lodge', 'PokerStars') */\n  venue: string\n\n  /** Game format ('cash' or 'tournament') */\n  gameType: GameType\n\n  /** Display mode for betting amounts ('currency' or 'bigBlinds') */\n  betDisplay: BetDisplay\n\n  /** ISO 8601 timestamp when the hand occurred (e.g., '2024-01-15T20:30:00Z') */\n  handTimestamp: string\n\n  /** Number of players seated at the table (2-10) */\n  numPlayersSeated: number\n\n  /** Currency code used for the hand (e.g., 'USD', 'EUR', 'GBP') */\n  currency: $.CurrencyCode\n\n  /** Dealer seat number (0-indexed or 1-indexed per venue convention) */\n  dealerSeatNum: number\n\n  /** Array of HandPlayer objects for players who volunarily participated in the hand (i.e. did not open fold pre-flop) */\n  handPlayers: HandPlayer[]\n\n  /** Forced bets posted before voluntary action (blinds, straddles) */\n  forcedBets: ForcedBet[]\n\n  /** Name or identifier of the hand (e.g., 'Bluffed My Rent Money'*/\n  handName?: string\n\n  /** Position of the hero player, if known (optional) */\n  heroPosition?: Position\n\n  /** Ante configuration if table plays with them (optional) */\n  antes?: Ante\n}\n\n/**\n * @public\n *\n * Initializes a fresh poker hand record at hand start, with forced bets applied. \\\n * Returns both the complete hand record and spot state ready for voluntary action.\n *\n * @param info - Configuration object containing all hand initialization parameters\n * @returns Object containing initialized PokerHand and SpotState (voluntary-action-ready)\n * @throws Error if numPlayersSeated is not between 2 and 10\n * @throws Error if handPlayers is empty\n *\n * Process:\n * 1. Creates initial spot state via initSpotState() (hand-not-started state)\n * 2. Applies each forced bet through addActionToHand(), updating state after each\n * 3. Returns both pokerHand (with forced bet actions applied) and spotState (voluntary-action-ready)\n *\n * NOTE: The returned spotState is \"voluntary-action-ready\", meaning:\n * - pendingForcedBets: undefined (all forced bets applied)\n * - actionIsOn: first voluntary actor (e.g., 'BU' in standard 3-handed)\n * - bettingState: potSize includes forced bets, curLiveBet = highest forced bet\n * - Player stacks reduced by forced bet amounts\n *\n * State values in returned pokerHand:\n * - communityCards: [] (no cards dealt yet)\n * - playerActions: forced bets applied as actions\n *\n * Returned spotState is voluntary-action-ready:\n * - pendingForcedBets: undefined (cleared)\n * - actionIsOn: first voluntary actor position\n * - bettingState: potSize = sum(forced bets + antes), curLiveBet = highest forced bet, curMinBetSize = 0\n *\n * @example\n * ```ts\n * // Initialize 3-handed cash game hand\n * const handPlayers: HandPlayer[] = [\n *   { position: 'SB', startingStack: 1000, player: {...}, holeCards: ['?', '?'] },\n *   { position: 'BB', startingStack: 2000, player: {...}, holeCards: ['?', '?'] },\n *   { position: 'BU', startingStack: 1500, player: {...}, holeCards: ['As', 'Kh'] }\n * ]\n *\n * const { pokerHand, spotState } = initPokerHand({\n *   variant: 'nlhe',\n *   venueType: 'live',\n *   venue: 'The Lodge',\n *   gameType: 'cash',\n *   betDisplay: 'currency',\n *   handTimestamp: '2024-01-15T20:30:00Z',\n *   numPlayersSeated: 3,\n *   currency: 'USD',\n *   dealerSeatNum: 0,\n *   handPlayers,\n *   forcedBets: [\n *     { type: 'smallBlind', position: 'SB', amount: 1 },\n *     { type: 'bigBlind', position: 'BB', amount: 2 }\n *   ],\n *   heroPosition: 'BU'\n * })\n * // pokerHand: {\n * //   variant: 'nlhe',\n * //   venueType: 'live',\n * //   venue: 'The Lodge',\n * //   gameType: 'cash',\n * //   betDisplay: 'currency',\n * //   communityCards: [],\n * //   playerActions: [<SB forced bet>, <BB forced bet>],\n * //   playersInHand: { 'SB': {...}, 'BB': {...}, 'BU': {...} },\n * //   ...\n * // }\n * // spotState: {\n * //   spotContext: {\n * //     actionIsOn: 'BU',  // first voluntary actor (voluntary-action-ready)\n * //     pendingForcedBets: undefined,  // all forced bets posted\n * //     ...\n * //   },\n * //   bettingState: { potSize: 3, curLiveBet: 2, curMinBetSize: 0, ... },\n * //   playersInHandState: {\n * //     'SB': { curStackSize: 999, amtAddedToPotCurStreet: 1, ... },\n * //     'BB': { curStackSize: 1998, amtAddedToPotCurStreet: 2, ... },\n * //     'BU': { curStackSize: 1500, amtAddedToPotCurStreet: 0, ... }\n * //   }\n * // }\n * ```\n */\nexport const initPokerHand = (\n  info: PokerHandInfo\n): { pokerHand: PokerHand; spotState: SpotState } => {\n  const {\n    variant,\n    venueType,\n    venue,\n    gameType,\n    betDisplay,\n    handTimestamp,\n    numPlayersSeated,\n    currency,\n    dealerSeatNum,\n    handPlayers,\n    forcedBets,\n    heroPosition,\n    antes,\n  } = info\n\n  throwIf(numPlayersSeated < 2 || numPlayersSeated > 10,\n    'initPokerHand()',\n    'numPlayersSeated must be between 2 and 10',\n    { numPlayersSeated }\n  )\n\n  throwIf(handPlayers.length === 0,\n    'initPokerHand()',\n    'handPlayers cannot be empty',\n    { handPlayers }\n  )\n\n  const playersInHand = initHandPlayersByPosition(handPlayers)\n\n\n\n  // Initialize spot state BEFORE any actions (hand-not-started state)\n  let currentSpotState = initSpotState(\n    variant, numPlayersSeated, handPlayers, forcedBets, antes\n  )\n\n  // Apply forced bets one by one, updating spotState as we go\n  // Start with minimal hand structure for addActionToHand validation\n  let currentPokerHand: PokerHand = {\n    variant,\n    venueType,\n    venue,\n    gameType,\n    betDisplay,\n    handTimestamp,\n    numPlayersSeated,\n    currency,\n    communityCards: [],\n    dealerSeatNum,\n    heroPosition,\n    forcedBets,\n    ...(antes && { antes }),  // Only include antes if defined\n    playersInHand,\n    playerActions: [],\n  }\n\n  // Apply each forced bet through addActionToHand to properly update state\n  for (const forcedBet of forcedBets) {\n\n    const result = addActionToHand(currentPokerHand, currentSpotState, {\n      position: forcedBet.position,\n      actionType: forcedBet.type,\n      forcedBetAmount: forcedBet.amount,\n    })\n\n    // Update to the new hand and state\n    currentPokerHand = result.pokerHand\n    currentSpotState = result.spotState\n  }\n\n  // Now spotState is voluntary-action-ready:\n  // - pendingForcedBets cleared\n  // - actionIsOn points to first voluntary actor\n  // - pot contains forced bets\n  // - player stacks reduced by forced bet amounts\n\n  return { pokerHand: currentPokerHand, spotState: currentSpotState }\n}\n\n/**\n * @internal\n *\n * Initializes HandPlayersByPosition mapping from an array of HandPlayer objects. \\\n * Orders players by canonical position and validates for duplicates.\n *\n * @param handPlayers - Array of HandPlayer objects\n * @returns HandPlayersByPosition mapping ordered by canonical position\n * @throws Error if duplicate positions found in the input array\n *\n * @example\n * ```ts\n * const handPlayers: HandPlayer[] = [\n *   { position: 'BU', startingStack: 1500, ... },\n *   { position: 'BB', startingStack: 2000, ... },\n *   { position: 'SB', startingStack: 1000, ... }\n * ]\n * const playersByPos = initHandPlayersByPosition(handPlayers)\n * // {\n * //   'SB': { position: 'SB', startingStack: 1000, ... },\n * //   'BB': { position: 'BB', startingStack: 2000, ... },\n * //   'BU': { position: 'BU', startingStack: 1500, ... }\n * // }\n * ```\n */\nexport const initHandPlayersByPosition = (\n  handPlayers: HandPlayer[]\n): HandPlayersByPosition => {\n  // Check for duplicate positions\n  const positions = handPlayers.map(hp => hp.position)\n  const uniquePositions = new Set(positions)\n\n  throwIf(positions.length !== uniquePositions.size,\n    'initHandPlayersByPosition()',\n    'Duplicate positions found in handPlayers',\n    { positions }\n  )\n\n  // Order players by position first\n  const orderedPlayers = orderPlayerListByPosition(handPlayers)\n\n  // Build HandPlayersByPosition object\n  return orderedPlayers.reduce((acc, handPlayer) => {\n    acc[handPlayer.position] = handPlayer\n    return acc\n  }, {} as HandPlayersByPosition)\n}\n\n//\n// Updaters\n//\n\n/**\n * @public\n *\n * Updates a poker hand immutably. \\\n * Merges partial updates with existing poker hand.\n *\n * @param pokerHand - Current poker hand\n * @param updates - Partial poker hand updates to apply\n * @returns New PokerHand with updates applied\n *\n * @example\n * ```ts\n * const hand = initPokerHand(...)\n *\n * // Add community cards\n * const withFlop = updatePokerHand(hand, {\n *   communityCards: ['As', 'Kh', 'Qd']\n * })\n *\n * // Append player action\n * const withAction = updatePokerHand(hand, {\n *   playerActions: [...hand.playerActions, newAction]\n * })\n *\n * // Update multiple fields\n * const updated = updatePokerHand(hand, {\n *   communityCards: ['As', 'Kh', 'Qd', 'Jc'],\n *   playerActions: [...hand.playerActions, newAction]\n * })\n * ```\n */\nexport const updatePokerHand = (\n  pokerHand: PokerHand,\n  updates: Partial<PokerHand>\n): PokerHand => ({ ...pokerHand, ...updates })\n","/**\n * Utilities for poker hand analysis and player ordering.\n *\n * Provides core functions for:\n * - Extracting player positions from hand data\n * - Determining action order based on forced bets\n * - Preflop action sequencing\n */\n\nimport { orderForcedBetsByPosition } from './actionListUtils'\nimport { orderPositionList, getNextStreet } from './generalUtils'\nimport { throwIf } from './utilsForStcLand'\nimport { updatePokerHand } from './pokerHandMutators'\nimport {\n  resetBettingStateForNewStreet,\n  resetPlayerStatesForNewStreet,\n  resetSpotContextForNewStreet,\n  updateSpotContext,\n} from './stateMutators'\nimport { $ } from './currencyUtils'\nimport { keys } from 'remeda'\n\nimport type {\n  HandPlayersByPosition,\n  ForcedBet,\n  Position,\n  PokerHand,\n  SpotState,\n  SpotContext,\n  Street,\n  Card,\n} from '@pokerland/types'\n\n/**\n * @internal\n *\n * Extracts and orders all positions that have players in the hand. \\\n * Returns positions in canonical order (SB → BB → UTG → ... → BU).\n *\n * @param playersInHand - Mapping of positions to players\n * @returns Ordered array of positions with seated players\n *\n * @example\n * ```ts\n * const players: HandPlayersByPosition = {\n *   'BU': { player: {...}, position: 'BU', ... },\n *   'BB': { player: {...}, position: 'BB', ... },\n *   'CO': { player: {...}, position: 'CO', ... }\n * }\n * positionsInHand(players)\n * // ['BB', 'CO', 'BU'] (canonical order)\n * ```\n */\nexport const positionsInHand = (\n  playersInHand: HandPlayersByPosition,\n): Position[] => orderPositionList(keys(playersInHand ?? {}) as Position[])\n\n/**\n * @public\n *\n * Determines the first position to act preflop based on forced bets. \\\n * The first to act is the player immediately after the last forced bet (BB/straddle).\n *\n * Action order preflop:\n * - With no straddles: UTG (or first position after BB)\n * - With straddles: first position after the last straddle\n * - Heads-up: non-BB player acts first\n *\n * @param playersInHand - Mapping of positions to players\n * @param forcedBets - Array of forced bets (SB, BB, straddles)\n * @returns Position of first player to act preflop\n * @throws Error if no forced bets provided\n * @throws Error if no players in hand\n * @throws Error if last forced bet position not found among players\n * @throws Error if unable to determine next acting position\n *\n * @example\n * ```ts\n * // 6-handed game, no straddles\n * const players: HandPlayersByPosition = {\n *   'SB': {...}, 'BB': {...}, 'UTG': {...}, 'HJ': {...}, 'CO': {...}, 'BU': {...}\n * }\n * const forcedBets: ForcedBet[] = [\n *   { type: 'smallBlind', position: 'SB', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 }\n * ]\n * getFirstPosToActPreflop(players, forcedBets)\n * // 'UTG' (first position after BB)\n *\n * // With a button straddle\n * const forcedBetsWithStraddle: ForcedBet[] = [\n *   { type: 'smallBlind', position: 'SB', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 },\n *   { type: 'straddle', position: 'BU', amount: 4 }\n * ]\n * getFirstPosToActPreflop(players, forcedBetsWithStraddle)\n * // 'SB' (first position after BU straddle)\n *\n * // Heads-up\n * const headsUpPlayers: HandPlayersByPosition = {\n *   'SB/BU': {...}, 'BB': {...}\n * }\n * const headsUpBlinds: ForcedBet[] = [\n *   { type: 'smallBlind', position: 'SB/BU', amount: 1 },\n *   { type: 'bigBlind', position: 'BB', amount: 2 }\n * ]\n * getFirstPosToActPreflop(headsUpPlayers, headsUpBlinds)\n * // 'SB/BU' (button acts first preflop heads-up)\n * ```\n */\nexport const getFirstPosToActPreflop = (\n  playersInHand: HandPlayersByPosition,\n  forcedBets: ForcedBet[]\n): Position => {\n  const orderedForcedBets = orderForcedBetsByPosition(forcedBets ?? [])\n\n  throwIf(orderedForcedBets.length === 0,\n    'getFirstPosToActPreflop()',\n    'At least one forced bet (SB/BB/straddle) is required to determine action order',\n    { forcedBets }\n  )\n\n  const playerPositions = positionsInHand(playersInHand)\n\n  throwIf(playerPositions.length === 0,\n    'getFirstPosToActPreflop()',\n    'playersInHand must include at least one seated player',\n    { playersInHand }\n  )\n\n  const lastForcedBetPosition = orderedForcedBets[orderedForcedBets.length - 1]!.position\n  const lastForcedBetIndex = playerPositions.indexOf(lastForcedBetPosition)\n\n  throwIf(lastForcedBetIndex === -1,\n    'getFirstPosToActPreflop()',\n    'Last forced bet position not found among players in hand',\n    { lastForcedBetPosition, playerPositions }\n  )\n\n  for (let offset = 1; offset <= playerPositions.length; offset++) {\n    const nextIndex = (lastForcedBetIndex + offset) % playerPositions.length\n    const candidate = playerPositions[nextIndex]\n    if (playersInHand[candidate]) return candidate\n  }\n\n  throwIf(true,\n    'getFirstPosToActPreflop()',\n    'Unable to determine next acting position after forced bets',\n    { playersInHand, forcedBets }\n  )\n\n  // Unreachable - throwIf above will always throw but TS doesn't know that\n  return playerPositions[0]\n}\n\n/**\n * @public\n *\n * Extracts an initial poker hand state from a complete poker hand. \\\n * Returns a hand with all metadata preserved but community cards and player actions cleared.\n *\n * This is useful when you need to:\n * - Reset a hand to its pre-action state\n * - Create an iterator that processes actions step-by-step\n * - Initialize a hand for replay/analysis\n *\n * The returned hand preserves:\n * - All game metadata (variant, venue, timestamps, etc.)\n * - Player information (playersInHand)\n * - Forced bet configuration (forcedBets)\n * - Ante configuration (antes, if present)\n *\n * The returned hand clears:\n * - communityCards (set to empty array)\n * - playerActions (set to empty array)\n *\n * @param hand - Complete poker hand with all actions and community cards\n * @returns Initial poker hand state ready for action processing\n *\n * @example\n * ```ts\n * const completeHand: PokerHand = {\n *   variant: 'nlhe',\n *   // ... other metadata\n *   communityCards: ['Ah', 'Kd', 'Qs'],\n *   playerActions: [\n *     { actionType: 'smallBlind', position: 'SB', ... },\n *     { actionType: 'bigBlind', position: 'BB', ... },\n *     { actionType: 'raise', position: 'BU', ... }\n *   ]\n * }\n *\n * const initialHand = extractInitialPokerHandFrom(completeHand)\n * // initialHand.communityCards === []\n * // initialHand.playerActions === []\n * // All other fields preserved from completeHand\n * ```\n */\nexport const extractInitialPokerHandFrom = (hand: PokerHand): PokerHand => {\n  return updatePokerHand(hand, {\n    communityCards: [],\n    playerActions: [],\n  })\n}\n\ntype RunoutStreet = Extract<Street, 'flop' | 'turn' | 'river'>\n\nconst newCardsRequiredForStreet: Record<RunoutStreet, number> = {\n  flop: 3,\n  turn: 1,\n  river: 1,\n}\n\nconst boardLengthBeforeStreet: Record<RunoutStreet, number> = {\n  flop: 0,\n  turn: 3,\n  river: 4,\n}\n\n/**\n * Returns the total number of community cards that should be present\n * on the board for a given street, assuming cards have been dealt.\n *\n * @param street - The street to check\n * @returns Number of community cards needed (0 for preFlop, 3 for flop, 4 for turn, 5 for river/showDown)\n *\n * @internal\n */\nexport const getTotalCardsNeededForStreet = (street: Street): number => {\n  switch (street) {\n  case 'preFlop':\n    return 0\n  case 'flop':\n    return 3\n  case 'turn':\n    return 4\n  case 'river':\n  case 'showDown':\n    return 5\n  }\n}\n\nconst streetName = (street: RunoutStreet): string => street\n\n/**\n * @public\n *\n * Adds community cards for a postflop street. Used in two scenarios:\n * 1. Normal flow: After a street closes with active players\n * 2. Runout flow: When all players are all-in (no more betting possible)\n *\n * In both cases, the state machine sets `awaitingCommunityCards: true` to signal that\n * cards must be dealt before any further actions can occur.\n *\n * Expected usage:\n * - Call once per street (flop/turn/river) after the state machine reports\n *   `spotState.spotContext.awaitingCommunityCards === true`.\n * - Cards must be provided in natural order and must not duplicate existing board cards.\n *\n * @param street - Street being revealed (`flop`, `turn`, or `river`). Must match the\n *   current `spotState.spotContext.activeStreet`.\n * @param cards - Community cards to add for the street. Flop expects three cards; turn\n *   and river expect exactly one.\n * @param pokerHand - Current poker hand (immutable update).\n * @param spotState - Current spot state (immutable update).\n * @returns Updated poker hand and spot state reflecting the new board.\n *\n * @throws Error when awaitingCommunityCards is not true, when card counts are invalid,\n * or when the street ordering is violated.\n */\nexport const addCommunityCards = (\n  street: RunoutStreet,\n  cards: Card[],\n  pokerHand: PokerHand,\n  spotState: SpotState,\n): { pokerHand: PokerHand; spotState: SpotState } => {\n\n  const { spotContext, playersInHandState, bettingState } = spotState\n\n  // Community cards can be added when awaitingCommunityCards flag is set.\n  // This flag is set to true in two scenarios:\n  // 1. Normal flow: after a street closes with active players\n  // 2. Runout flow: when everyone is all-in (actionIsOn === 'runout-pending')\n  const isRunout = spotContext.actionIsOn === 'runout-pending'\n  const isAwaitingCards = spotContext.awaitingCommunityCards === true\n\n  throwIf(\n    !isAwaitingCards,\n    'addCommunityCards()',\n    'Community cards can only be added when awaitingCommunityCards is true',\n    {\n      awaitingCommunityCards: spotContext.awaitingCommunityCards,\n      actionIsOn: spotContext.actionIsOn,\n      activeStreet: spotContext.activeStreet,\n      hint: 'The awaitingCommunityCards flag is set when advancing to a new postflop street (normal flow) or entering runout mode (all-in scenario)'\n    }\n  )\n\n  throwIf(\n    spotContext.activeStreet !== street,\n    'addCommunityCards()',\n    `Street mismatch: expected to add community cards for ${spotContext.activeStreet}, received ${streetName(street)}`,\n    { activeStreet: spotContext.activeStreet, requestedStreet: street }\n  )\n\n  const existingBoard = pokerHand.communityCards ?? []\n  const expectedBoardLength = boardLengthBeforeStreet[street]\n\n  throwIf(\n    existingBoard.length !== expectedBoardLength,\n    'addCommunityCards()',\n    `Board length mismatch for ${streetName(street)}. Expected ${expectedBoardLength} cards before dealing, found ${existingBoard.length}.`,\n    { street, existingBoard }\n  )\n\n  const requiredNewCards = newCardsRequiredForStreet[street]\n\n  throwIf(\n    cards.length !== requiredNewCards,\n    'addCommunityCards()',\n    `Incorrect number of cards provided for ${streetName(street)}. Expected ${requiredNewCards}, received ${cards.length}.`,\n    { street, cards }\n  )\n\n  const duplicateCheck = new Set(existingBoard)\n  for (const card of cards) {\n    throwIf(\n      duplicateCheck.has(card),\n      'addCommunityCards()',\n      `Card ${card} already exists on the board`,\n      { card, existingBoard }\n    )\n    duplicateCheck.add(card)\n  }\n\n  const updatedCommunityCards = [...existingBoard, ...cards]\n\n  const nextStreet = getNextStreet(street)\n  const targetStreet = isRunout ? nextStreet : street\n\n  const bigBlindAmount = pokerHand.forcedBets?.find(fb => fb.type === 'bigBlind')?.amount\n    ?? $.asMinor(Math.max(0, ...((pokerHand.forcedBets ?? []).map(fb => Number(fb.amount)))))\n\n  // Determine next actionIsOn and awaitingCommunityCards based on mode and destination street:\n  // - If advancing to showdown: always 'hand-ended', awaitingCommunityCards=false (no more cards)\n  // - If in runout mode and NOT showdown: stay in 'runout-pending', awaitingCommunityCards=true (more cards needed)\n  // - If in normal mode: keep actionIsOn from determineNextAction, awaitingCommunityCards=false (cards just dealt)\n  let nextActionIsOn: SpotContext['actionIsOn']\n  let nextAwaitingCommunityCards: boolean\n\n  if (targetStreet === 'showDown') {\n    nextActionIsOn = 'hand-ended'\n    nextAwaitingCommunityCards = false\n  } else if (isRunout) {\n    nextActionIsOn = 'runout-pending'\n    nextAwaitingCommunityCards = true  // More cards will be dealt in runout\n  } else {\n    // Normal flow: actionIsOn already set by determineNextAction, keep it\n    nextActionIsOn = spotContext.actionIsOn\n    nextAwaitingCommunityCards = false  // Cards just dealt for this street\n  }\n\n  const advancedSpotContext = updateSpotContext(\n    resetSpotContextForNewStreet(spotContext, targetStreet),\n    {\n      actionIsOn: nextActionIsOn,\n      awaitingCommunityCards: nextAwaitingCommunityCards,\n    }\n  )\n\n  const advancedPlayers = resetPlayerStatesForNewStreet(playersInHandState)\n  const advancedBetting = resetBettingStateForNewStreet(\n    bettingState,\n    street,\n    targetStreet,\n    bigBlindAmount\n  )\n\n  const advancedSpotState: SpotState = {\n    spotContext: advancedSpotContext,\n    playersInHandState: advancedPlayers,\n    bettingState: advancedBetting,\n    mostRecentActionEffects: {\n      streetAdvanced: isRunout, // Only true in runout mode when street actually advances\n      handEnded: advancedSpotContext.actionIsOn === 'hand-ended',\n    },\n  }\n\n  const updatedPokerHand = updatePokerHand(pokerHand, {\n    communityCards: updatedCommunityCards,\n  })\n\n  return {\n    pokerHand: updatedPokerHand,\n    spotState: advancedSpotState,\n  }\n}\n","//  add toFlop, toTurn, toRiver, toShowdown\n\nimport type { PokerHand, SpotState, PlayerAction, Street } from '@pokerland/types'\nimport { dropLastActions, getLastForcedActionIndex } from './actionUtils'\nimport { hasFlop, getFirstFlopActionIndex, hasTurn, getFirstTurnActionIndex, hasRiver, getFirstRiverActionIndex } from './actionListUtils'\nimport { extractInitialPokerHandFrom, getTotalCardsNeededForStreet } from './pokerHandUtils'\nimport { initSpotStateFromPokerHand } from './spotStateUtils'\nimport { applyActionToHandState } from './applyActionToState'\nimport { updatePokerHand } from './pokerHandMutators'\nimport { throwIf } from './utilsForStcLand'\n\n/*\n  Poker Hand Iterator\n\n  Example Timeline:\n\n    Legend: fa=forced action, va=voluntary action, S=state after action\n\n    BEFORE                                                                                                AFTER\n    start       -(fa1)->    S1        -(fa2)->    S2        -(va1)->    S3        -(va2)->    S4  ----->  end\n                            |                     |                     |                     |           |\n    rewind()     next()     |          next()     |          next()     |          next()     |   next()  |\n    |                       |                     |                     |                     |           |\n    |           /───────────┘         /───────────┘         /───────────┘         /───────────┘           no state\n    no state    state after           state after           state after           state after\n                fa1 applied           fa2 applied           va1 applied           va2 applied\n                                          |                                            |\n                toFirstVoluntary() ───────┘                                            |\n                toLast() ──────────────────────────────────────────────────────────────┘\n\n*/\n\n/**\n * @public\n *\n * State returned at each iteration step.\n */\nexport interface HandIteratorState {\n  /**\n   * Updated poker hand after applying the action\n   */\n  pokerHand: PokerHand\n  /**\n   * Updated spot state after applying the action\n   */\n  spotState: SpotState\n  /**\n   * The action that was applied to reach the current spot\n   * (undefined when at BEFORE start position)\n   */\n  actionApplied?: PlayerAction\n  /**\n   * Zero-based index of this action in the original playerActions array\n   * (-1 when at BEFORE start position)\n   */\n  actionIndex: number\n}\n\n/**\n * @public\n *\n * Iterator object returned by makeHandIterator.\n * Combines standard JavaScript iterator protocol with bidirectional navigation.\n */\nexport interface HandIterator {\n  /**\n   * Standard iterator protocol - allows for...of loops\n   */\n  [Symbol.iterator](): HandIterator\n  /**\n   * Apply next action, return new state\n   */\n  next(): IteratorResult<HandIteratorState>\n  /**\n   * Undo previous action, return prior state\n   */\n  previous(): IteratorResult<HandIteratorState>\n  /**\n   * Get current hand/spot state without advancing\n   */\n  getCurrent(): IteratorResult<HandIteratorState>\n  /**\n   * Check if more actions exist ahead\n   */\n  hasNext(): boolean\n  /**\n   * Check if can step backward\n   */\n  hasPrevious(): boolean\n  /**\n   * Jumps to start boundary\n   *\n   * 'preHand' mode:\n   *   - Position -1, no actions applied, pot is 0\n   *   - Next action queued: first action in hand (typically SB)\n   * 'firstVoluntaryAction' mode:\n   *   - Position after last forced bet applied\n   *   - Next action queued: first voluntary action\n   */\n  rewind(): IteratorResult<HandIteratorState>\n  /**\n   * Jump to last valid action (after all actions applied)\n   *\n   * - Position to state after last action index (N-1)\n   * - Last action has been applied to hand/spot state\n   */\n  toLast(): IteratorResult<HandIteratorState>\n  /**\n   * Jump to position where first voluntary action can be taken\n   *\n   * - Position to state after last forced bet applied\n   * - Next action queued: first voluntary action (NOT yet applied)\n   * - Calling next() will apply the first voluntary action\n   * - 'firstVoluntaryAction' mode: Equivalent to rewind() - returns to start boundary\n   */\n  toFirstVoluntary(): IteratorResult<HandIteratorState>\n  /**\n   * Jump to position immediately BEFORE the first flop action\n   *\n   * - Position to state immediately before first flop action\n   * - Next action queued: first flop action\n   *\n   * @throws Error if hand never reached the flop (no actions with street: 'flop')\n   */\n  toFlop(): IteratorResult<HandIteratorState>\n  /**\n   * Jump to position immediately BEFORE the first turn action\n   *\n   * - Position to state immediately before first turn action\n   * - All pre-flop and flop actions applied\n   * - Turn card dealt\n   * - Next action queued: first turn action\n   *\n   * @throws Error if hand never reached the turn (no actions with street: 'turn')\n   */\n  toTurn(): IteratorResult<HandIteratorState>\n  /**\n   * Jump to position immediately BEFORE the first river action\n   *\n   * - Position to state immediately before first river action\n   * - All pre-flop, flop, and turn actions applied\n   * - River card dealt\n   * - Next action queued: first river action\n   * - Throws if hand never reached the river\n   *\n   * @throws Error if hand never reached the river (no actions with street: 'river')\n   */\n  toRiver(): IteratorResult<HandIteratorState>\n}\n\n/**\n * @public\n *\n * Start boundary mode for hand iterator\n */\nexport type HandStartBoundary = 'preHand' | 'firstVoluntaryAction'\n\n/**\n * @public\n *\n * Poker Hand Iterator\n *\n * Iterator\n * - iterates over all spots (hand states) in a poker hand\n * - each next() applies next action and returns updated hand/spot state\n *\n * Interface:\n * - next()              → apply next queued action, return new state\n * - previous()          → undo previous action, return prior state\n * - getCurrent()        → get current hand/spot state without advancing\n * - hasNext()           → check if more actions exist ahead\n * - hasPrevious()       → check if can step backward\n * - rewind()            → jump to start boundary (queues first action to be applied)\n * - toLast()            → jump to end state (after last action applied, no action queued)\n * - toFirstVoluntary()  → jump to state after forced bets (first voluntary action queued, ready to be applied)\n * - toFlop()            → jump to state before first flop action (flop cards dealt, first flop action queued)\n * - toTurn()            → jump to state before first turn action (turn card dealt, first turn action queued)\n * - toRiver()           → jump to state before first river action (river card dealt, first river action queued)\n *\n * Position Semantics:\n * - Iterator position represents actions APPLIED, not queued\n * - Position -1: no actions applied (action 0 is queued for next())\n * - Position 0: action 0 applied (action 1 is queued for next())\n * - Position N-1: last action applied (no action queued, hasNext() = false)\n * - Position N: exhausted state (done: true)\n * - Iterator starts BEFORE first action (standard iterator convention)\n * - First next() call applies first action and returns resulting state\n * - Iterator ends AFTER last action (standard iterator convention)\n * - for...of compatible\n *\n * Start Boundary Modes:\n * - 'preHand' (default): Start boundary is before any actions applied\n *   - rewind() goes to position -1 (before first action)\n *   - previous() cannot go before position -1\n *   - 'firstVoluntaryAction': Start boundary is after all forced bets applied\n *   - rewind() goes to state after last forced bet\n *   - previous() cannot go before last forced bet\n *   - first next() applies first voluntary action\n *\n * Boundary Behavior:\n * - next() at end                  → returns { done: true, value: undefined }\n * - previous() at start boundary   → returns { done: true, value: undefined }\n * - getCurrent() after end         → returns { done: true, value: undefined }\n * - getCurrent() at start boundary → returns { done: false, value: HandIteratorState }\n *\n * @param hand - Complete poker hand with all actions\n * @param handStart - Start boundary mode ('preHand' | 'firstVoluntaryAction')\n * @returns Bidirectional iterator object\n *\n * @example\n *\n * ```ts\n *    // Iterate over all actions\n *    const iterator = makeHandIterator(pokerHand)\n *    for (const state of iterator) {\n *      console.log(state.actionApplied.actionType, state.actionApplied.position)\n *      console.log('Pot:', state.spotState.bettingState.potSize)\n *    }\n * ```\n *\n * @example\n *\n *    ```ts\n *    // Bidirectional navigation\n *    const iterator = makeHandIterator(pokerHand)\n *    iterator.next()     // Forward\n *    iterator.next()     // Forward\n *    iterator.previous() // Backward\n *    iterator.toLast()   // Jump to end\n *    ```\n *\n * @example\n *\n *    ```ts\n *    // Start from first voluntary action\n *    const iterator = makeHandIterator(pokerHand, 'firstVoluntaryAction')\n *\n *    // Iterator starts after forced bets, rewind() goes back to after forced bets\n *    iterator.next() // First voluntary action\n *    iterator.rewind() // Back to after forced bets (not before all actions)\n *    ```\n */\nexport function makeHandIterator(\n  hand: PokerHand,\n  handStart: HandStartBoundary = 'preHand'\n): HandIterator {\n\n  // lets remember hand info\n  const _fullHand: PokerHand = hand\n  const _communityCards = hand.communityCards || []\n  const _actionList = hand.playerActions || []\n  const _numActions = _actionList.length\n\n  // Validate preconditions for NL Hold'em and PLO (always require forced bets)\n  throwIf(\n    !hand.forcedBets || hand.forcedBets.length === 0,\n    'makeHandIterator()',\n    'PokerHand must have at least one forced bet (SB/BB). NL Hold\\'em and PLO always require forced bets.',\n    { forcedBets: hand.forcedBets }\n  )\n\n  // Capture initial state (BEFORE any actions applied)\n  const _initialPokerHand: PokerHand = extractInitialPokerHandFrom(hand)\n  const _initialSpotState: SpotState = initSpotStateFromPokerHand(hand)\n\n  // Compute start boundary index and state based on handStart mode\n  let _startBoundaryIdx: number\n  let _startBoundaryPokerHand: PokerHand\n  let _startBoundarySpotState: SpotState\n\n  if (handStart === 'firstVoluntaryAction') {\n    // Find last forced action index\n    const lastForcedIdx = getLastForcedActionIndex(_actionList)\n\n    _startBoundaryIdx = lastForcedIdx\n\n    // Compute state after last forced bet applied\n    if (lastForcedIdx >= 0) {\n      // Apply all forced bets to get boundary state\n      let pokerHand = _initialPokerHand\n      let spotState = _initialSpotState\n\n      for (let i = 0; i <= lastForcedIdx; i++) {\n        const action = _actionList[i]\n\n        // Clear awaitingCommunityCards flag if cards are present (simulates card dealing between street advance and action)\n        let spotStateForAction = spotState\n        if (spotStateForAction.spotContext.awaitingCommunityCards === true) {\n          const cardsNeeded = getTotalCardsNeededForStreet(spotStateForAction.spotContext.activeStreet)\n          const cardsPresent = (pokerHand.communityCards ?? []).length\n\n          if (cardsPresent >= cardsNeeded) {\n            // Cards are present, clear the flag\n            spotStateForAction = {\n              ...spotStateForAction,\n              spotContext: {\n                ...spotStateForAction.spotContext,\n                awaitingCommunityCards: false,\n              }\n            }\n          }\n        }\n\n        const result = applyActionToHandState(action.position, action, pokerHand, spotStateForAction)\n        pokerHand = result.pokerHand\n        spotState = result.spotState\n\n        // Handle community cards if street advanced\n        if (result.spotState.mostRecentActionEffects?.streetAdvanced) {\n          const newStreet = result.spotState.spotContext.activeStreet\n          const cardCount = getCardCountForStreet(newStreet)\n          const cardsForStreet = _communityCards.slice(0, cardCount)\n          pokerHand = updatePokerHand(pokerHand, { communityCards: cardsForStreet })\n        }\n      }\n\n      _startBoundaryPokerHand = pokerHand\n      _startBoundarySpotState = spotState\n    } else {\n      // No forced actions - boundary is same as initial\n      _startBoundaryPokerHand = _initialPokerHand\n      _startBoundarySpotState = _initialSpotState\n    }\n  } else {\n    // 'preHand' mode - start boundary is before any actions\n    _startBoundaryIdx = -1\n    _startBoundaryPokerHand = _initialPokerHand\n    _startBoundarySpotState = _initialSpotState\n  }\n\n  // -1 = before start, 0 = after first action, _numActions = after last action\n  // Note: for 'firstVoluntaryAction' mode, _startBoundaryIdx will be >= 0\n  let _currentPosition = _startBoundaryIdx\n\n  // Track current state (starts at boundary, updated as we iterate)\n  let _currentPokerHand: PokerHand = _startBoundaryPokerHand\n  let _currentSpotState: SpotState = _startBoundarySpotState\n  let _currentAction: PlayerAction | undefined = undefined\n\n  /**\n   * Helper function to navigate to position before first action of a given street.\n   * Used by toFlop(), toTurn(), and toRiver().\n   *\n   * This is a closure function that has access to iterator state variables.\n   */\n  const _navigateToStreetStart = (\n    iteratorContext: HandIterator,\n    streetName: 'flop' | 'turn' | 'river',\n    firstActionIndex: number,\n    hasStreet: boolean\n  ): IteratorResult<HandIteratorState> => {\n    const streetCap = streetName.charAt(0).toUpperCase() + streetName.slice(1)\n\n    throwIf(\n      firstActionIndex <= 0 || !hasStreet,\n      `to${streetCap}()`,\n      `Hand never reached the ${streetName} or has invalid structure`\n    )\n\n    // Target position is immediately BEFORE first action on street\n    const targetPosition = firstActionIndex - 1\n\n    // If already at target position, return current state\n    if (_currentPosition === targetPosition) {\n      return iteratorContext.getCurrent()\n    }\n\n    // If target is before current position, need to navigate backwards\n    if (targetPosition < _currentPosition) {\n      // Rebuild state by rewinding and moving forward\n      iteratorContext.rewind()\n\n      // Move forward to target position\n      for (let i = _startBoundaryIdx + 1; i <= targetPosition; i++) {\n        iteratorContext.next()\n      }\n    } else {\n      // Target is ahead, move forward from current position\n      while (_currentPosition < targetPosition) {\n        iteratorContext.next()\n      }\n    }\n\n    // Rehydrate community cards if we're at a street boundary awaiting them\n    // The navigation may have positioned us after a street-advancing action,\n    // leaving awaitingCommunityCards: true without cards in the poker hand.\n    // We need to add cards from the original hand to deliver a complete state.\n    if (_currentSpotState.spotContext.awaitingCommunityCards === true) {\n      const currentStreet = _currentSpotState.spotContext.activeStreet\n      const cardsNeeded = getCardCountForStreet(currentStreet)\n      const cardsAvailable = _communityCards.length\n\n      if (cardsAvailable >= cardsNeeded) {\n        // Cards exist in original hand - add them to current poker hand\n        const cardsForStreet = _communityCards.slice(0, cardsNeeded)\n        _currentPokerHand = updatePokerHand(_currentPokerHand, { communityCards: cardsForStreet })\n\n        // Clear the awaiting flag since we just dealt the cards\n        _currentSpotState = {\n          ..._currentSpotState,\n          spotContext: {\n            ..._currentSpotState.spotContext,\n            awaitingCommunityCards: false,\n          }\n        }\n      }\n    }\n\n    return iteratorContext.getCurrent()\n  }\n\n  return {\n    [Symbol.iterator](): HandIterator { return this },\n\n    next(): IteratorResult<HandIteratorState> {\n\n      // Boundary: already at or past end\n      if (_currentPosition >= _numActions - 1) {\n        _currentPosition = _numActions\n        return { done: true, value: undefined }\n      }\n\n      // Move to next position\n      _currentPosition++\n      const action = _actionList[_currentPosition]\n\n      // Clear awaitingCommunityCards flag if cards are present (simulates card dealing between street advance and action)\n      let spotStateForAction = _currentSpotState\n      if (spotStateForAction.spotContext.awaitingCommunityCards === true) {\n        const cardsNeeded = getTotalCardsNeededForStreet(spotStateForAction.spotContext.activeStreet)\n        const cardsPresent = (_currentPokerHand.communityCards ?? []).length\n\n        if (cardsPresent >= cardsNeeded) {\n          // Cards are present, clear the flag\n          spotStateForAction = {\n            ...spotStateForAction,\n            spotContext: {\n              ...spotStateForAction.spotContext,\n              awaitingCommunityCards: false,\n            }\n          }\n        }\n      }\n\n      // Apply action to get new state\n      const result = applyActionToHandState(\n        action.position, action, _currentPokerHand, spotStateForAction\n      )\n\n      // Update current state\n      _currentPokerHand = result.pokerHand\n      _currentSpotState = result.spotState\n      _currentAction = action\n\n      // If street advanced, copy community cards from source hand for the new street\n      if (result.spotState.mostRecentActionEffects?.streetAdvanced) {\n        const newStreet = result.spotState.spotContext.activeStreet\n        const cardCount = getCardCountForStreet(newStreet)\n        const cardsForStreet = _communityCards.slice(0, cardCount)\n        _currentPokerHand = updatePokerHand(_currentPokerHand, { communityCards: cardsForStreet })\n      }\n\n      return this.getCurrent()\n    },\n\n    previous(): IteratorResult<HandIteratorState> {\n\n      // Boundary: already at or before start boundary\n      if (_currentPosition <= _startBoundaryIdx) {\n        return { done: true, value: undefined }\n      }\n\n      // Move backward one position\n      const targetPosition = _currentPosition - 1\n\n      // Special case: Moving to start boundary position\n      if (targetPosition === _startBoundaryIdx) {\n        _currentPosition = _startBoundaryIdx\n        _currentPokerHand = _startBoundaryPokerHand\n        _currentSpotState = _startBoundarySpotState\n        _currentAction = undefined\n\n        return this.getCurrent()\n      }\n\n      // General case: rebuild state at targetPosition\n      // Step 1: Get base state (state before target action)\n      let basePokerHand: PokerHand\n      let baseSpotState: SpotState\n\n      if (targetPosition === 0) {\n        // Target is first action - use initial state as base\n        basePokerHand = _initialPokerHand\n        baseSpotState = _initialSpotState\n      } else {\n        // Drop to targetPosition - 1 (one before target)\n        const numToDrop = _numActions - (targetPosition - 1) - 1\n        const result = dropLastActions(numToDrop, _fullHand)\n        basePokerHand = result.pokerHand\n        baseSpotState = result.spotState\n      }\n\n      // Step 2: Re-apply the target action to compute accurate metadata\n      const action = _actionList[targetPosition]\n\n      // Rehydrate community cards if we're awaiting them\n      // When dropLastActions rebuilds state at a street boundary, the cards are trimmed\n      // but the state shows awaitingCommunityCards: true. We need to add cards from the\n      // original hand before applying the action (mirrors what next() does after applying).\n      let pokerHandForAction = basePokerHand\n      let spotStateForAction = baseSpotState\n      if (spotStateForAction.spotContext.awaitingCommunityCards === true) {\n        const currentStreet = spotStateForAction.spotContext.activeStreet\n        const cardsNeeded = getCardCountForStreet(currentStreet)\n        const cardsAvailable = _communityCards.length\n\n        if (cardsAvailable >= cardsNeeded) {\n          // Cards exist in original hand - add them to the poker hand\n          const cardsForStreet = _communityCards.slice(0, cardsNeeded)\n          pokerHandForAction = updatePokerHand(pokerHandForAction, { communityCards: cardsForStreet })\n\n          // Clear the awaiting flag since we just dealt the cards\n          spotStateForAction = {\n            ...spotStateForAction,\n            spotContext: {\n              ...spotStateForAction.spotContext,\n              awaitingCommunityCards: false,\n            }\n          }\n        }\n      }\n\n      const result = applyActionToHandState(\n        action.position, action, pokerHandForAction, spotStateForAction\n      )\n\n      // Step 3: Handle community cards if street advanced\n      let finalPokerHand = result.pokerHand\n      if (result.spotState.mostRecentActionEffects?.streetAdvanced) {\n        const newStreet = result.spotState.spotContext.activeStreet\n        const cardCount = getCardCountForStreet(newStreet)\n        const cardsForStreet = _communityCards.slice(0, cardCount)\n        finalPokerHand = updatePokerHand(finalPokerHand, { communityCards: cardsForStreet })\n      }\n\n      // Update cached state\n      _currentPosition = targetPosition\n      _currentPokerHand = finalPokerHand\n      _currentSpotState = result.spotState\n      _currentAction = action\n\n      return this.getCurrent()\n    },\n\n    getCurrent(): IteratorResult<HandIteratorState> {\n      // At start boundary position\n      if (_currentPosition === _startBoundaryIdx) {\n        return {\n          done: false,\n          value: {\n            pokerHand: _startBoundaryPokerHand,\n            spotState: _startBoundarySpotState,\n            actionApplied: undefined,\n            actionIndex: _startBoundaryIdx,\n          },\n        }\n      }\n\n      // At AFTER end position (exhausted)\n      if (_currentPosition >= _numActions) {\n        return { done: true, value: undefined }\n      }\n\n      // At a valid position with state available\n      return {\n        done: false,\n        value: {\n          pokerHand: _currentPokerHand,\n          spotState: _currentSpotState,\n          actionApplied: _currentAction,\n          actionIndex: _currentPosition,\n        },\n      }\n    },\n\n    hasNext(): boolean {\n      return _currentPosition < _numActions - 1\n    },\n\n    hasPrevious(): boolean {\n      return _currentPosition > _startBoundaryIdx\n    },\n\n    rewind(): IteratorResult<HandIteratorState> {\n      // Reset iterator to start boundary position\n      _currentPosition = _startBoundaryIdx\n      _currentPokerHand = _startBoundaryPokerHand\n      _currentSpotState = _startBoundarySpotState\n      _currentAction = undefined\n\n      return this.getCurrent()\n    },\n\n    toLast(): IteratorResult<HandIteratorState> {\n      // Already at the last valid action position\n      if (_currentPosition === _numActions - 1) {\n        return this.getCurrent()\n      }\n\n      // If exhausted, rewind first then fast-forward to last\n      if (_currentPosition >= _numActions) {\n        this.rewind()\n      }\n\n      // Fast-forward to last action position (N-1) by iterating forward\n      while (_currentPosition < _numActions - 1) {\n        this.next()\n      }\n\n      // Now at position N-1 (last valid action), return current state\n      return this.getCurrent()\n    },\n\n    toFirstVoluntary(): IteratorResult<HandIteratorState> {\n      // Navigate to position AFTER last forced bet (BEFORE first voluntary action applied)\n      // PRECONDITION: Assumes all forced actions (blinds, straddles, antes) are\n      // contiguous at the start of playerActions.\n      const lastForcedIndex = getLastForcedActionIndex(_actionList)\n\n      // If no forced actions, rewind to BEFORE START (position -1)\n      if (lastForcedIndex === -1) {\n        return this.rewind()\n      }\n\n      // Target position is lastForcedIndex (after forced bets, before first voluntary)\n      const targetPosition = lastForcedIndex\n\n      // If already at target position, return current state\n      if (_currentPosition === targetPosition) {\n        return this.getCurrent()\n      }\n\n      // Navigate to target position\n      this.rewind()\n      // Call next() (targetPosition - _startBoundaryIdx) times to reach target\n      // Loop variable i is just a counter (not an index into actions)\n      for (let i = _startBoundaryIdx + 1; i <= targetPosition; i++) {\n        this.next()\n      }\n\n      return this.getCurrent()\n    },\n\n    toFlop(): IteratorResult<HandIteratorState> {\n      return _navigateToStreetStart(\n        this,\n        'flop',\n        getFirstFlopActionIndex(_actionList),\n        hasFlop(_actionList)\n      )\n    },\n\n    toTurn(): IteratorResult<HandIteratorState> {\n      return _navigateToStreetStart(\n        this,\n        'turn',\n        getFirstTurnActionIndex(_actionList),\n        hasTurn(_actionList)\n      )\n    },\n\n    toRiver(): IteratorResult<HandIteratorState> {\n      return _navigateToStreetStart(\n        this,\n        'river',\n        getFirstRiverActionIndex(_actionList),\n        hasRiver(_actionList)\n      )\n    },\n  }\n}\n\n/**\n * Helper to determine community card count for a given street\n */\nfunction getCardCountForStreet(street: Street): number {\n  switch (street) {\n  case 'preFlop':\n    return 0\n  case 'flop':\n    return 3\n  case 'turn':\n    return 4\n  case 'river':\n  case 'showDown':\n    return 5\n  default:\n    return 0\n  }\n}\n"]}