Zhiyuan Song
← Home

VPCT – VPIP Controller

A real-time poker behavior analysis app: during a live session it tracks VPIP (Voluntarily Put $ In Pot) and uses event-driven behavior analysis to detect tilt patterns. If behavior does not self-correct, it escalates to Cooldown mode. It also provides session and lifetime statistics to help you understand your playing style.

This page summarizes the MVP architecture and implementation. For tilt subsystem details, see TILT_DETECTION.md in the repository.

Pages & navigation (4 tabs)

├── VT (Home)       — Home: VPIP overview + recent sessions
├── Live (Session)  — In session: live logging + tilt detection
├── Stats           — Analytics (signed-in users only)
└── Me (Settings)   — Settings, account, language, appearance

Tech stack

Layer Technology
UI SwiftUI
Architecture @Observable + DataService (centralized data service)
Local storage SwiftData
Auth Sign in with Apple (planned)
State @Observable
Localization In-app L10n enum (not system .strings)

Account model

Guest mode (current default)

  • All session-related features work without signing in.
  • Guest data is not persisted: ended sessions are cleared after an app restart.
  • Guest mode does not accumulate lifetime statistics.
  • Prompts to sign in at key moments (session end, home banner, etc.).

Signed-in mode (planned)

  • Sign in with Apple.
  • Persisted data; lifetime stats accumulate.
  • Full Stats tab access.

Data models

PlayerData

Field Type Description
id UUID Unique identifier
createdAt Date Account creation time
lifetimeHands Int Total hands
lifetimeVPIPHands Int Total VPIP hands

SessionData

Field Type Description
id UUID Unique identifier
startTime Date Start time
endTime Date? End time (nil = in progress)
totalHands Int Hands in this session
vpipHands Int VPIP hands in this session
totalBBResult Double? BB P&L (optional)
handRecords [HandRecordData] Cascade delete

HandRecordData

Field Type Description
id UUID Unique identifier
timestamp Date Hand timestamp
didVPIP Bool Whether hero put money in voluntarily (VPIP)
card1Rank / card2Rank String? Card ranks A–2
isSuited Bool? Suited / offsuit
resultRaw String? WIN / NOT_WIN
bbResult Double? BB result (optional)
positionRaw / actionTypeRaw String? Reserved for Pro
session SessionData? Parent session

Hand entry flow

VPIP hands (≤ 4 taps target)

  1. Tap first card rank (A–2)
  2. Tap second card rank (same rank = pocket pair)
  3. (Optional) Toggle suited / offsuit (disabled for pocket pairs)
  4. (Optional) Enter BB amount
  5. Tap WIN or LOSS → auto collapse

Fold

Single tap FOLD: medium haptic + stat bar flash; totalHands += 1, no further input.

Data management

  • Hands in a session: long-press delete (context menu).
  • Home and all-sessions list: long-press delete entire session.
  • When deleting a session: subtract back from lifetime stats and recompute related aggregates.

Live session screen

State UI
Warm-up (< 10 hands) Progress ring countdown + hand count
Normal Session VPIP (hero) + 30min / lifetime / hands strips
Warning 30min VPIP (hero, amber) + session / lifetime strips
Danger 30min VPIP (hero, red) + shake animation
Observing Amber progress ring (X/5 hands) + observe behavior
Cooldown Red countdown ring (remaining / total) + tighten range

Player types (VPIP bands)

VPIP Type Description
< 15% Nit Extremely tight range only
15–19% Tight Selective pots
20–24% Standard Balanced
25–29% Loose On the wide side
≥ 30% Very Loose Too many pots

Tilt detection (core)

Five independent detectors arranged in three priority layers.

Three layers

Layer 1 Emotion-driven (highest)
  Loss Chase danger → Style Drift danger

Layer 2 Events + emotion
  Big Pot danger → Loss Chase warning → Win Tilt → Big Pot warning

Layer 3 Behavioral drift (lowest)
  Style Drift warning → VPIP Drift danger → VPIP Drift warning

Design principles

  • Danger is conservative: high bar, rare fires; when it fires, it is serious.
  • Warning is sensitive: early nudge; does not enter cooldown.
  • Only danger enters cooldown; warnings surface messages only.
  • VPIP Drift: quantity drift (overall higher VPIP).
  • Style Drift: quality drift (hand strength profile worsens).

Five detectors

# Detector Layer Trigger (summary) Level
1 Loss Chase Emotion Loss rate ≥ 60% and VPIP spike ≥ 8% warning / danger
2 Style Drift Behavior ≥ 2 of last 4 hands are abnormal hand types warning / danger
3 Big Pot Event Single-hand |BB| ≥ 100; four scenarios by strength and outcome warning / danger
4 Win Tilt Emotion Win rate ≥ 60% and VPIP widens ≥ 8% warning only
5 VPIP Drift Behavior 30min VPIP ≥ lifetime + 10% / 18% (tiered) warning / danger

Severity levels

  • Normal: no alert
  • Warning: coach card (flat border)
  • Danger: coach card + shake animation

Cooldown escalation

Core rule: only danger triggers cooldown; warnings do not escalate.

State machine

normal
  → danger fires → observing (5 hands)
       → no improvement → cooldown (10 hands) → normal
       → improvement   → normal

Cooldown type by detector

Detector Level Cooldown type
Loss Chase danger lossBased (+5 hands extension)
Style Drift danger driftBased (+5 hands extension)
Big Pot (weak hand lost) danger lossBased (+5 hands extension)
VPIP Drift danger driftBased (+5 hands extension)
Win Tilt warning Does not trigger
Big Pot (other) warning Does not trigger

Deviation check (isStillDeviating)

  • Signal A: last-5-hand VPIP rate still ≥ baseline + 10%
  • Signal B: ≥ 2 weak hands / GTO deviation in last 5 hands
  • Signal C: (lossBased / driftBased only) ≥ 2 losing outcomes and ≥ 3 VPIP entries

If any signal holds → still deviating → extend cooldown (max 25 hands).

UI copy notes

  • Observing: amber ring X/5, “Observing”
  • Cooldown: red countdown ring, “Cooldown mode” + “Tighten range” badge

Settings & feature toggles

Notifications / tilt master

Toggle Key Default Effect
Tilt Alert vt_tilt_enabled ON Master switch; OFF disables all detectors
Cooldown Mode vt_cooldown_enabled ON Cooldown escalation system
GTO Advice vt_gto_advice_enabled ON Preflop strategy hints while picking cards

Per-detector toggles (shown only when Tilt Alert is ON)

Toggle Key Default
Big Pot vt_bigpot_enabled ON
Loss Chase vt_losschase_enabled ON
Win Tilt vt_wintilt_enabled ON
Style Drift vt_styledrift_enabled ON
VPIP Drift vt_vpipdrift_enabled ON

Other

  • Appearance: System / Light / Dark
  • Language: English (default) / Chinese
  • About: version, privacy policy, terms of use

Localization

  • In-app L10n enum, not iOS .strings files.
  • LanguageManager (@Observable) stores language preference in UserDefaults.
  • All views use L10n.s(.key, lang); 80+ keys (docs cite 90+).
  • App name “VPCT” / “VPIP CONTROLLER” is not translated.
Language Code Label
English .english English
Chinese .chinese Chinese

Stats tab

Access control

User state Stats
Guest Locked; sign-in prompt
Signed in, < 10 hands Empty state: “Play more hands”
Signed in, ≥ 10 hands Full analytics

Signed-in content (summary)

  • Header: player-type badge, lifetime VPIP, total hands
  • BB: total BB, BB/100
  • GTO compliance: in-range share, common deviations, suggestions
  • Tilt: tilt session count, share, average duration

Implementation status

Done

  • SwiftData models (Player / Session / HandRecord)
  • Session system (start, end, in-progress tracking)
  • Hand logging (Fold + VPIP picker)
  • VPIP math (session, 30-minute rolling, lifetime)
  • Player-type classification
  • Five-detector tilt stack (three layers) + danger-only cooldown and trigger types
  • Big Pot (single hand ≥ 100BB, four scenarios)
  • Per-detector toggles in Settings
  • In-app EN/ZH localization (90+ keys)
  • Guest architecture, welcome / onboarding, Sign in with Apple UI
  • Custom flat TabBar (4 tabs), iOS 18 flat visuals
  • Appearance, language, feature switches; session/hand delete with stat recompute
  • Haptics system

Backlog

  • Sign in with Apple backend auth
  • Cloud data sync
  • Privacy policy & terms (store copy)
  • App Store submission (current: in review)
  • TestFlight public beta

Strong / weak hand reference (tilt / Big Pot)

Weak hands (Style Drift tracking)

Category Examples
Weak Broadway KTo, QTo, JTo
Weak Suited K9s, Q9s, J8s
Gap Hands K7s, Q8s, J7s
Marginal A9o, K8o, Q7o
Connectors T9o, 98o, 87o

If a weak hand is historically profitable (≥ 2 records and total BB > 0), it is not treated as weak.

Strong hands (Big Pot alert)

Category Examples
Premium Pairs AA–99
Premium Suited AKs, AQs, AJs, ATs, KQs, KJs
Premium Offsuit AKo, AQo

Document version

Site digest from MVP spec v3.0 · source note updated 2026-03-22