dynamicsystemsarchitecture.org

Poker Tilt Detection — A Real Synthesis

Four separate tilt-detection implementations existed in the library, none of them a clean match for "the best poker tilt detection." This combines the genuinely complementary parts of three of them into one detector — not by picking a favorite, but by checking what each one actually contributed.

Bug found and fixed in the library itself

While testing an unrelated kernel version (STATE_ESTIMATION_KERNEL_v8_REAL_DEPS.py), a real bug surfaced in both near-duplicate detector files this synthesis drew design ideas from — not code, design ideas, so this synthesis was never affected — but the library files themselves were broken: self._aggregate_severity([s[1] for _, s in active_signals]) tried to index an already-unpacked dict with an integer key, crashing on every real call. Fixed in both applications/TiltDetector.py and pipeline_v2/detectors_tilt_detector.py, each independently re-verified with a real end-to-end call afterward.

What went in, and why

Snapshot signal, adapted from the better of two near-duplicate detectors in the library (pipeline_v2/detectors_tilt_detector.py, which has a real check — confidence misalignment — that its near-duplicate in applications/ is missing entirely). Answers: does this one instant's reading diverge sharply from an established baseline?

Temporal signal, new here, but the underlying insight is real and comes directly from poker_decision_state_estimation.py's own regime data: the TILTED regime's aggression range (1.8–7.5) is far wider than any other regime's, including MANIAC (5.5–9.0). Tilt isn't an extreme setting — it's inconsistency. That's a rolling-window variance signal, genuinely different from a snapshot check, and no existing detector in the library actually measured it.

State machine, structurally borrowed from a fourth file (COMPONENT_A_TILT_SPECTRUM_FIXED.py's FractalBrain) — a CLEAR/TILT/RECOVERY cycle with real hysteresis, the same structural pattern already verified in this codebase's circuit-breaker work. That file's actual tilt logic is text-keyword matching for a different domain and didn't transfer — the state machine shape did.

Verified against real regime data, not just run once

Tested against the same regime-generation logic used elsewhere in the library — 40 hands per regime:

TIGHT_AGGRESSIVE   0/40 hands flagged   final state: CLEAR
MANIAC              0/40 hands flagged   final state: CLEAR
GTO                 0/40 hands flagged   final state: CLEAR
TILTED              28/40 hands flagged  final state: TILT

The important result is MANIAC scoring 0. A naive detector conflates "extreme" with "tilted" and would flag it. This one doesn't, because MANIAC is extreme but consistent — narrow range relative to its own baseline — while TILTED is specifically inconsistent. That's the actual poker-native insight, doing real work instead of just being described.

The JavaScript version below was checked against the exact same Python logic on a fixed, non-random sequence — matched hand-by-hand, state-by-state, to three decimal places, before being trusted.

Feed it a sequence yourself and watch the state transitions →

Source

Real Python source