@idealic/poker-engine
Version:
Poker game engine and hand evaluator
760 lines (702 loc) • 24.3 kB
text/typescript
import { Street } from '../types';
/**
* Each Stats record represents stats for a single street in a hand.
* For example, one record might capture all actions on the preflop or on the flop.
* These values are recorded per street and must be aggregated to get overall metrics.
*/
export interface StreetStat {
// Meta information about the game/street
/** Unique identifier for each hand */
hand: number;
/** Unique identifier for the venue */
venue: string;
/** Unique identifier for the table */
table: string;
/** Identifier for the player */
player: string;
/** Current street (e.g., 'preflop', 'flop', 'turn', 'river') */
street: Street;
/** Total time spent by the player on decisions on the street*/
decisionDuration: number;
createdAt: number;
startedAt: number;
/** Number of aggressions on this street */
aggressions: number;
/** Count of calls or checks made on this street */
passivities: number;
/** Decisions made on this street */
decisions: number;
// Basic actions
/** Count of bets made on this street */
bets: number;
/** Count of raises made on this street */
raises: number;
/** Count of calls made on this street */
calls: number;
/** Count of checks made on this street */
checks: number;
/** Player folded on this street */
folds: number;
/** 1 if the player went all-in on this street, else 0 */
allIns: number;
/** 1 if the player voluntarily put money in the pot (beyond forced bets), else 0 */
voluntaryPutMoneyInPotTimes: number;
/** 1 if the player made the first aggression on this street, else 0 */
firstAggressions: number;
/** 1 if the player made the last aggression on this street, else 0 */
lastAggressions: number;
/** Number of times the player limped (called without raising) on this street */
limps: number;
/** Number of opportunities for a limp */
limpOpportunities: number;
/**
* ### Preflop Raise (PFR)
* An open-raise before the flop.
*/
preflopRaiseOpportunities: number;
preflopRaises: number;
// --------------------------------------------------------------------------
// --- Positional Maneuver Stats ---
// --------------------------------------------------------------------------
// The following sections group all stats related to a specific poker maneuver.
/**
* ### Three-Bet (3-Bet) Stats
* A 3-bet is the first re-raise before the flop.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** Can 3-bet a wider, merged range to leverage postflop advantage.
* - **OOP:** Must 3-bet a more polarized range (premiums & bluffs) to compensate for postflop disadvantage.
*/
threeBetIpOpportunities: number;
threeBetOopOpportunities: number;
threeBetIpAttempts: number;
threeBetOopAttempts: number;
threeBetIpTakedowns: number;
threeBetOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - **IP:** Can defend (call or 4-bet) wider against a 3-bet due to postflop positional advantage.
* - **OOP:** Must defend with a tighter, stronger range, often preferring to 4-bet or fold rather than call.
*/
threeBetIpChallenges: number;
threeBetOopChallenges: number;
threeBetIpContinues: number;
threeBetOopContinues: number;
threeBetIpFolds: number;
threeBetOopFolds: number;
/**
* ### Squeeze Stats
* A 3-bet made after an initial raise and one or more calls.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** Powerful squeeze with a wider range due to extra "dead money" in the pot.
* - **OOP:** High-risk, polarized play to take the pot down immediately.
*/
squeezeIpOpportunities: number;
squeezeOopOpportunities: number;
squeezeIpAttempts: number;
squeezeOopAttempts: number;
squeezeIpTakedowns: number;
squeezeOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - As Original Raiser:** Defends tighter than vs. a standard 3-bet due to the multi-way threat.
* - **As Caller:** Very tight defense range, as their hand was not strong enough to 3-bet initially.
*/
squeezeIpChallenges: number;
squeezeOopChallenges: number;
squeezeIpContinues: number;
squeezeOopContinues: number;
squeezeIpFolds: number;
squeezeOopFolds: number;
/**
* ### Four-Bet (4-Bet) Stats
* A re-raise against a 3-bet preflop.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** Can 4-bet slightly wider (including bluffs) to pressure the OOP 3-bettor.
* - **OOP:** Typically represents the very top of a player's range; rarely a bluff.
*/
fourBetIpOpportunities: number;
fourBetOopOpportunities: number;
fourBetIpAttempts: number;
fourBetOopAttempts: number;
fourBetIpTakedowns: number;
fourBetOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - A player facing a 4-bet is almost always continuing with an all-in (5-bet shove) or folding, regardless of position. Range is paramount.
*/
fourBetIpChallenges: number;
fourBetOopChallenges: number;
fourBetIpContinues: number;
fourBetOopContinues: number;
fourBetIpFolds: number;
fourBetOopFolds: number;
/**
* ### Five-Bet (5-Bet) Stats
* A re-raise against a 4-bet preflop. This is typically an all-in move.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - A 5-bet almost exclusively represents the absolute strongest premium hands (e.g., AA, KK). Position is less critical than hand strength at this point.
*/
fiveBetIpOpportunities: number;
fiveBetOopOpportunities: number;
fiveBetIpAttempts: number;
fiveBetOopAttempts: number;
fiveBetIpTakedowns: number;
fiveBetOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - Facing a 5-bet requires an extremely strong hand to continue. The decision is based on pot odds against the opponent's perceived premium range.
*/
fiveBetIpChallenges: number;
fiveBetOopChallenges: number;
fiveBetIpContinues: number;
fiveBetOopContinues: number;
fiveBetIpFolds: number;
fiveBetOopFolds: number;
/**
* ### Continuation-Bet (C-Bet) Stats
* A bet on the flop by the preflop aggressor.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** High frequency with smaller sizing, leveraging positional advantage.
* - **OOP:** More selective, often with larger sizing and a more polarized range.
*/
cbetIpOpportunities: number;
cbetOopOpportunities: number;
cbetIpAttempts: number;
cbetOopAttempts: number;
cbetIpTakedowns: number;
cbetOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - **IP:** Can "float" (call) with a wider range of hands, intending to bluff on later streets.
* - **OOP:** Must play more straightforwardly, often preferring to check-raise or fold.
*/
cbetIpChallenges: number;
cbetOopChallenges: number;
cbetIpContinues: number;
cbetOopContinues: number;
cbetIpFolds: number;
cbetOopFolds: number;
/**
* ### Delayed Continuation-Bet Stats
* A bet on the turn or river by the preflop aggressor after they checked the previous street.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** Often a pot-control bet with a medium-strength hand or a bluff on a scare card.
* - **OOP:** Less common; typically indicates a polarized range (a slow-played monster or a bluff).
*/
delayedCbetIpOpportunities: number;
delayedCbetOopOpportunities: number;
delayedCbetIpAttempts: number;
delayedCbetOopAttempts: number;
delayedCbetIpTakedowns: number;
delayedCbetOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - Facing a delayed c-bet often means the aggressor has a capped or polarized range, allowing the defender to call wider or look for bluff-raise spots.
*/
delayedCbetIpChallenges: number;
delayedCbetOopChallenges: number;
delayedCbetIpContinues: number;
delayedCbetOopContinues: number;
delayedCbetIpFolds: number;
delayedCbetOopFolds: number;
/**
* ### Double & Triple Barrel Stats
* Continuing to bet on the turn (double) and river (triple) as the street-by-street aggressor.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP:** Higher frequency, can use smaller sizing, and can barrel with thinner value and more bluffs.
* - **OOP:** Lower frequency, larger sizing required, and range becomes extremely polarized by the river.
*/
doubleBarrelIpOpportunities: number;
doubleBarrelOopOpportunities: number;
doubleBarrelIpAttempts: number;
doubleBarrelOopAttempts: number;
doubleBarrelIpTakedowns: number;
doubleBarrelOopTakedowns: number;
tripleBarrelIpOpportunities: number;
tripleBarrelOopOpportunities: number;
tripleBarrelIpAttempts: number;
tripleBarrelOopAttempts: number;
tripleBarrelIpTakedowns: number;
tripleBarrelOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - Facing multiple barrels requires a strong hand or a specific plan to bluff-catch, as the aggressor's range is very strong or polarized.
*/
doubleBarrelIpChallenges: number;
doubleBarrelOopChallenges: number;
doubleBarrelIpContinues: number;
doubleBarrelOopContinues: number;
doubleBarrelIpFolds: number;
doubleBarrelOopFolds: number;
tripleBarrelIpChallenges: number;
tripleBarrelOopChallenges: number;
tripleBarrelIpContinues: number;
tripleBarrelOopContinues: number;
tripleBarrelIpFolds: number;
tripleBarrelOopFolds: number;
/**
* ### Probe Bet Stats
* An OOP bet on the turn or river after the preflop aggressor checked back.
* - **Aggressor:** Always OOP.
* - **Defender:** Always IP.
*/
probeBetOpportunities: number;
probeBetAttempts: number;
probeBetTakedowns: number;
probeBetChallenges: number;
probeBetContinues: number;
probeBetFolds: number;
/**
* ### Float Bet Stats
* An IP bet after the previous street's aggressor checks.
* - **Aggressor:** Always IP.
* - **Defender:** Always OOP.
*/
floatBetOpportunities: number;
floatBetAttempts: number;
floatBetTakedowns: number;
floatBetChallenges: number;
floatBetContinues: number;
floatBetFolds: number;
// Float
floatOpportunities: number;
floatAttempts: number;
/**
* ### Steal Attempt Stats
* An open-raise from a late position (CO, BTN, or SB).
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **IP (CO/BTN):** Very high-frequency action with a wide range of hands.
* - **OOP (SB):** More polarized action, as the SB is OOP postflop if called.
*/
stealIpOpportunities: number;
stealOopOpportunities: number;
stealIpAttempts: number;
stealOopAttempts: number;
stealIpTakedowns: number;
stealOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - As the BB, the defender is always IP against a steal attempt from the SB, and always OOP against the CO/BTN, heavily influencing their 3-betting and calling range.
*/
stealIpChallenges: number;
stealOopChallenges: number;
stealIpContinues: number;
stealOopContinues: number;
stealIpFolds: number;
stealOopFolds: number;
/**
* ### Donk Bet Stats
* An OOP bet by a player who was not the aggressor on the previous street.
* - **Aggressor:** Always OOP.
* - **Defender:** Always IP.
*/
donkBetOpportunities: number;
donkBetAttempts: number;
donkBetTakedowns: number;
donkBetChallenges: number;
donkBetContinues: number;
donkBetFolds: number;
/**
* ### Check-Raise Stats
* A check followed by a raise on the same street.
* - **Aggressor:** A powerful, exclusively OOP maneuver.
* - **Defender:** Always IP.
*/
checkRaiseOpportunities: number;
checkRaiseAttempts: number;
checkRaiseTakedowns: number;
checkRaiseChallenges: number;
checkRaiseContinues: number;
checkRaiseFolds: number;
/**
* ### Shove Stats
* Committing all remaining chips. Context (Open Shove vs. Re-raise Shove) is key.
*/
/**
* **IP vs. OOP Strategy (Aggressor):**
* - **Open Shove:** Often a polarized range (premiums or bluffs).
* - **Re-raise Shove:** Typically a value-heavy range, narrowing with each successive re-raise.
*/
openShoveIpOpportunities: number;
openShoveOopOpportunities: number;
openShoveIpAttempts: number;
openShoveOopAttempts: number;
openShoveIpTakedowns: number;
openShoveOopTakedowns: number;
shoveIpOpportunities: number;
shoveOopOpportunities: number;
shoveIpAttempts: number;
shoveOopAttempts: number;
shoveIpTakedowns: number;
shoveOopTakedowns: number;
/**
* **IP vs. OOP Strategy (Defender):**
* - Facing an all-in is a purely mathematical decision based on pot odds and equity. Position matters less than the exact ranges and stack sizes.
*/
openShoveIpChallenges: number;
openShoveOopChallenges: number;
openShoveIpContinues: number;
openShoveOopContinues: number;
openShoveIpFolds: number;
openShoveOopFolds: number;
shoveIpChallenges: number;
shoveOopChallenges: number;
shoveIpContinues: number;
shoveOopContinues: number;
shoveIpFolds: number;
shoveOopFolds: number;
// Stats filled retroactively for simpler aggregation
/** Whether the player won subsequently */
success: number;
// Positional flags (used for simple counting, not metric derivation)
/** Count of aggressive actions made in position on this street */
aggressionsInPosition: number;
/** Count of defensive challenges faced in position on this street */
challengesInPosition: number;
// Finance information computed at the end of the game
/** Whether the player went to showdown */
wentToShowdown: number;
/** Whether the player won money at showdown (success) */
wonAtShowdown: number;
/** Whether the player won money at showdown (success) */
wonWithoutShowdown: number;
/** Final pot size for the hand */
pot: number;
/** 1 if the player saw the flop, recorded on the flop street */
sawFlop: number;
/** True if this is the last stat entry for this player in this hand */
isFinalAction: number;
/** Chip stack at the start of the hand */
stackBefore: number;
/** Chip stack at the end of the hand */
stackAfter: number;
/** Big blind value used for normalizing profit (for bb100 calculations) */
bigBlind: number;
/** Times the player won */
won: number;
/** Times the player lost */
lost: number;
/** Currency of the game */
currency: string;
/** Rate of the currency at the time of the game */
currencyRate: number;
/** Amount invested on this street */
investments: number;
/** Amount of uncalled bets returned to the player */
returns: number;
/** Amount of earned money with investment subtracted */
profits: number;
/** Amount of earned money with investment subtracted */
balance: number;
/** Amount won on this street (positive only) */
winnings: number;
/** Amount lost on this street (positive only) */
losses: number;
/** Amount taken by the house from the pot */
rake: number;
}
/**
* The PokerMetrics interface defines aggregated metrics computed over multiple hands.
* Each metric includes its calculation, an explanation of its usefulness (importance),
* and notes on how certain the metric is (certainty), based on the data quality.
*/
export interface PokerMetrics extends StreetStat {
gameIds: Set<string>;
distinctGameCount: number;
limpFrequency: number;
aggressionFactor: number;
aggressionFrequency: number;
vpipFrequency: number;
allInFrequency: number;
// --- Maneuver-Specific Metrics ---
// Preflop Raise (PFR)
preflopRaiseOpportunities: number;
preflopRaises: number;
preflopRaiseFrequency: number;
// 3-Bet
threeBetOpportunities: number;
threeBetAttempts: number;
threeBetChallenges: number;
threeBetContinues: number;
threeBetFolds: number;
threeBetTakedowns: number;
threeBetFrequency: number;
threeBetIpFrequency: number;
threeBetOopFrequency: number;
threeBetFoldFrequency: number;
threeBetIpFoldFrequency: number;
threeBetOopFoldFrequency: number;
threeBetContinueFrequency: number;
threeBetIpContinueFrequency: number;
threeBetOopContinueFrequency: number;
threeBetTakedownFrequency: number;
threeBetIpTakedownFrequency: number;
threeBetOopTakedownFrequency: number;
// Squeeze
squeezeOpportunities: number;
squeezeAttempts: number;
squeezeChallenges: number;
squeezeContinues: number;
squeezeFolds: number;
squeezeTakedowns: number;
squeezeFrequency: number;
squeezeIpFrequency: number;
squeezeOopFrequency: number;
squeezeFoldFrequency: number;
squeezeIpFoldFrequency: number;
squeezeOopFoldFrequency: number;
squeezeContinueFrequency: number;
squeezeIpContinueFrequency: number;
squeezeOopContinueFrequency: number;
squeezeTakedownFrequency: number;
squeezeIpTakedownFrequency: number;
squeezeOopTakedownFrequency: number;
// 4-Bet
fourBetOpportunities: number;
fourBetAttempts: number;
fourBetChallenges: number;
fourBetContinues: number;
fourBetFolds: number;
fourBetTakedowns: number;
fourBetFrequency: number;
fourBetIpFrequency: number;
fourBetOopFrequency: number;
fourBetFoldFrequency: number;
fourBetIpFoldFrequency: number;
fourBetOopFoldFrequency: number;
fourBetContinueFrequency: number;
fourBetIpContinueFrequency: number;
fourBetOopContinueFrequency: number;
fourBetTakedownFrequency: number;
fourBetIpTakedownFrequency: number;
fourBetOopTakedownFrequency: number;
// 5-Bet
fiveBetOpportunities: number;
fiveBetAttempts: number;
fiveBetChallenges: number;
fiveBetContinues: number;
fiveBetFolds: number;
fiveBetTakedowns: number;
fiveBetFrequency: number;
fiveBetIpFrequency: number;
fiveBetOopFrequency: number;
fiveBetFoldFrequency: number;
fiveBetIpFoldFrequency: number;
fiveBetOopFoldFrequency: number;
fiveBetContinueFrequency: number;
fiveBetIpContinueFrequency: number;
fiveBetOopContinueFrequency: number;
fiveBetTakedownFrequency: number;
fiveBetIpTakedownFrequency: number;
fiveBetOopTakedownFrequency: number;
// C-Bet
cbetOpportunities: number;
cbetAttempts: number;
cbetChallenges: number;
cbetContinues: number;
cbetFolds: number;
cbetTakedowns: number;
cbetFrequency: number;
cbetIpFrequency: number;
cbetOopFrequency: number;
cbetFoldFrequency: number;
cbetIpFoldFrequency: number;
cbetOopFoldFrequency: number;
cbetContinueFrequency: number;
cbetIpContinueFrequency: number;
cbetOopContinueFrequency: number;
cbetTakedownFrequency: number;
cbetIpTakedownFrequency: number;
cbetOopTakedownFrequency: number;
// Delayed C-Bet
delayedCbetOpportunities: number;
delayedCbetAttempts: number;
delayedCbetChallenges: number;
delayedCbetContinues: number;
delayedCbetFolds: number;
delayedCbetTakedowns: number;
delayedCbetFrequency: number;
delayedCbetIpFrequency: number;
delayedCbetOopFrequency: number;
delayedCbetFoldFrequency: number;
delayedCbetIpFoldFrequency: number;
delayedCbetOopFoldFrequency: number;
delayedCbetContinueFrequency: number;
delayedCbetIpContinueFrequency: number;
delayedCbetOopContinueFrequency: number;
delayedCbetTakedownFrequency: number;
delayedCbetIpTakedownFrequency: number;
delayedCbetOopTakedownFrequency: number;
// Double Barrel
doubleBarrelOpportunities: number;
doubleBarrelAttempts: number;
doubleBarrelChallenges: number;
doubleBarrelContinues: number;
doubleBarrelFolds: number;
doubleBarrelTakedowns: number;
doubleBarrelFrequency: number;
doubleBarrelIpFrequency: number;
doubleBarrelOopFrequency: number;
doubleBarrelFoldFrequency: number;
doubleBarrelIpFoldFrequency: number;
doubleBarrelOopFoldFrequency: number;
doubleBarrelContinueFrequency: number;
doubleBarrelIpContinueFrequency: number;
doubleBarrelOopContinueFrequency: number;
doubleBarrelTakedownFrequency: number;
doubleBarrelIpTakedownFrequency: number;
doubleBarrelOopTakedownFrequency: number;
// Triple Barrel
tripleBarrelOpportunities: number;
tripleBarrelAttempts: number;
tripleBarrelChallenges: number;
tripleBarrelContinues: number;
tripleBarrelFolds: number;
tripleBarrelTakedowns: number;
tripleBarrelFrequency: number;
tripleBarrelIpFrequency: number;
tripleBarrelOopFrequency: number;
tripleBarrelFoldFrequency: number;
tripleBarrelIpFoldFrequency: number;
tripleBarrelOopFoldFrequency: number;
tripleBarrelContinueFrequency: number;
tripleBarrelIpContinueFrequency: number;
tripleBarrelOopContinueFrequency: number;
tripleBarrelTakedownFrequency: number;
tripleBarrelIpTakedownFrequency: number;
tripleBarrelOopTakedownFrequency: number;
// Probe Bet
probeBetOpportunities: number;
probeBetAttempts: number;
probeBetChallenges: number;
probeBetContinues: number;
probeBetFolds: number;
probeBetTakedowns: number;
probeBetFrequency: number;
probeBetFoldFrequency: number;
probeBetContinueFrequency: number;
probeBetTakedownFrequency: number;
// Float Bet
floatBetOpportunities: number;
floatBetAttempts: number;
floatBetChallenges: number;
floatBetContinues: number;
floatBetFolds: number;
floatBetTakedowns: number;
floatBetFrequency: number;
floatBetFoldFrequency: number;
floatBetContinueFrequency: number;
floatBetTakedownFrequency: number;
// Steal
stealOpportunities: number;
stealAttempts: number;
stealChallenges: number;
stealContinues: number;
stealFolds: number;
stealTakedowns: number;
stealFrequency: number;
stealIpFrequency: number;
stealOopFrequency: number;
stealFoldFrequency: number;
stealIpFoldFrequency: number;
stealOopFoldFrequency: number;
stealContinueFrequency: number;
stealIpContinueFrequency: number;
stealOopContinueFrequency: number;
stealTakedownFrequency: number;
stealIpTakedownFrequency: number;
stealOopTakedownFrequency: number;
// Donk Bet
donkBetOpportunities: number;
donkBetAttempts: number;
donkBetChallenges: number;
donkBetContinues: number;
donkBetFolds: number;
donkBetTakedowns: number;
donkBetFrequency: number;
donkBetFoldFrequency: number;
donkBetContinueFrequency: number;
donkBetTakedownFrequency: number;
// Check-Raise
checkRaiseOpportunities: number;
checkRaiseAttempts: number;
checkRaiseChallenges: number;
checkRaiseContinues: number;
checkRaiseFolds: number;
checkRaiseTakedowns: number;
checkRaiseFrequency: number;
checkRaiseFoldFrequency: number;
checkRaiseContinueFrequency: number;
checkRaiseTakedownFrequency: number;
// Open Shove
openShoveOpportunities: number;
openShoveAttempts: number;
openShoveChallenges: number;
openShoveContinues: number;
openShoveFolds: number;
openShoveTakedowns: number;
openShoveFrequency: number;
openShoveIpFrequency: number;
openShoveOopFrequency: number;
openShoveFoldFrequency: number;
openShoveIpFoldFrequency: number;
openShoveOopFoldFrequency: number;
openShoveContinueFrequency: number;
openShoveIpContinueFrequency: number;
openShoveOopContinueFrequency: number;
openShoveTakedownFrequency: number;
openShoveIpTakedownFrequency: number;
openShoveOopTakedownFrequency: number;
// Shove
shoveOpportunities: number;
shoveAttempts: number;
shoveChallenges: number;
shoveContinues: number;
shoveFolds: number;
shoveTakedowns: number;
shoveFrequency: number;
shoveIpFrequency: number;
shoveOopFrequency: number;
shoveFoldFrequency: number;
shoveIpFoldFrequency: number;
shoveOopFoldFrequency: number;
shoveContinueFrequency: number;
shoveIpContinueFrequency: number;
shoveOopContinueFrequency: number;
shoveTakedownFrequency: number;
shoveIpTakedownFrequency: number;
shoveOopTakedownFrequency: number;
// --- General Metrics ---
wentToShowdownFrequency: number;
wonAtShowdownFrequency: number;
wonWithoutShowdownFrequency: number;
decisionDurationAverage: number;
decisionDuration: number;
winningsAverage: number;
investmentsAverage: number;
profitAverage: number;
lossesAverage: number;
potAverage: number;
sawFlopFrequency: number;
startedAt: number;
bb100: number;
profitFactor: number;
returnOnInvestmentFactor: number;
stackToPotFactor: number;
}