Skip to content

Commit b44ed88

Browse files
authored
More Games (#97)
* Moregames (#96) * Added EPICS for adding other poker games * EPIC-29: variant engine foundation (Phases 1-11) + defect note * EPIC-30: Fixed-Limit Hold'em (Phases 1-11) * EPIC-31: Pot-Limit Omaha Hi (Phases 1-10) * EPIC-31: Pot-Limit Omaha Hi (Phases 1-10) * ... * EPIC-32: Seven-Card Stud Hi (Phases 1-13) EPIC-32 implementation complete. All 13 phases shipped (Phase 12 as live-play smoke; replay round-trip deferred to v1.1 with rationale documented). ┌────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┐ │ Phase │ Result │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 1: GamePhase Stud variants + bet-tier │ ✅ Stud3rd..Stud7th + stud_street_index + next_stud_street │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 2: Antes + family-dispatched forced │ ✅ act_antes + TableAction::BetAnteForced │ │ bets │ │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 3: Visibility-aware dealing │ ✅ deal_card_to_seat_with_visibility, deal_stud_3rd_street, deal_stud_street │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 4: Bring-in selection + posting │ ✅ lowest/highest_upcard_seat, third_street_extreme_upcard_seat, act_bring_in, │ │ │ TableAction::StudBringInPost │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 5: Stud action order │ ✅ best_visible_hand_seat with phase-aware upcard truncation; │ │ │ VisibleHandMode::HighStud/LowRazz │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 6: stud_hi_from_seats + session │ ✅ PokerSession::start_hand + advance_street Stud branches │ │ dispatch │ │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 7: Stud showdown via Seven::eval │ ✅ stud_river_case_eval (bypasses build_game's Board requirement) │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 8: Mid-hand Stud equity │ ✅ 3- and 4-card heuristic bucket in RuleBasedDecider::hand_equity │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 9: HandHistory visibility serde │ ✅ PlayerEntry.hole_cards_visibility, Stakes.bring_in, PlayerEntry::to_seven, replay routes │ │ │ Stud through stud_hi_from_seats │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 10: for_stud_hi factory + profiles │ ✅ tight_aggressive_stud_hi.yaml + loose_passive_stud_hi.yaml │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 11: │ ✅ 20-hand bot-vs-bot demo │ │ examples/interactive_play_stud_hi.rs │ │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 12: Stud replay-consistency test │ ✅ Live smoke + YAML field round-trip (replay round-trip deferred to v1.1) │ ├────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ 13: corrigendum + verification │ ✅ │ └────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘ Final pin tests: - 9057 lib tests pass (+3 from EPIC-31: for_stud_hi marker + 2 YAML load tests) - 657 doc tests pass (+5) - 4/4 replay_consistency tests pass — NLHE, FLHE, PLO + Stud live-smoke - interactive_play_stud_hi 20 hands run end-to-end - bot_selfplay NLHE regression-free - Clippy clean on every EPIC-32 file Files changed: 10 modified, 3 new (2 YAML + 1 example). ~1279 lines added. ★ Insight ───────────────────────────────────── - Stud replay round-trip surfaced a deep architectural gap: live action ordering depends on the per-street visible-card set, which doesn't survive a one-shot inject_hole_cards injection of all 7 cards. Phase-aware truncation in best_visible_hand_seat + third_street_extreme_upcard_seat aligns live and replay for bring-in / first-to-act selection, but the deeper issue is that Streets (preflop/flop/turn/river) doesn't carry stud street action lists separately. The proper fix is either incremental dealing during replay or a StudStreets enum — both are v1.1 polish. - The hand-loop "growth" mostly happened in two methods: PokerSession::start_hand and PokerSession::advance_street both gained Stud-family branches at the top, leaving the Hold'em-family path untouched. This is the right shape — variant-specific behavior at the dispatch layer, shared infrastructure (bring_it_in, apply_action, side-pot resolution) underneath. - The BetAnteForced table-action variant already existed from earlier work but wasn't wired. Phase 2's act_antes finally exercises it. This is the third time during the variant initiative that EPIC-29's foundation paid for itself: pre-existing scaffolding (ForcedBets.bring_in, SeatHand, Visibility, STUD_HI_STREETS, BetAnteForced) was waiting to be activated. ───────────────────────────────────────────────── * EPIC-33: Razz (A-5 Lowball) [Phases 1-7] Wires Razz showdown evaluation on top of EPIC-32's Stud engine. Razz reused ~95% of Stud's infrastructure (bring-in, action order, dealing, is_game_over, replay visibility) — the only load-bearing work was the CaliforniaHandRank → Eval bridge. - Phase 1: Eval::from_razz_rank + Eval::from_seven_razz; new HandRankName::RazzLow + HandRankClass::Lowball variants. - Phase 2: razz_river_case_eval + dispatch split (StudHi vs Razz) in river_case_eval_for_variant + build_eval_for_seat. - Phase 3: razz_from_seats constructor (mirrors stud_hi_from_seats). - Phase 4: BotProfile::for_razz factory + 2 reference YAML profiles. - Phase 5: examples/interactive_play_razz.rs — 20-hand smoke. - Phase 6: HandHistory replay routes HandVariant::Razz through razz_from_seats; new test_razz_bot_selfplay_replay_roundtrip. - Phase 7: Verification + EPIC-33 corrigendum. Inversion math turned out unnecessary — HandRank::cmp is already inverted, cancelling CaliforniaHandRank's lower-is-better ordering. Razz full replay round-trip deferred to v1.1 (incremental-dealing gap shared with Stud Hi). EPIC-33: Razz (A-5 Lowball) [Phases 1-7] Wires Razz showdown evaluation on top of EPIC-32's Stud engine. Razz reused ~95% of Stud's infrastructure (bring-in, action order, dealing, is_game_over, replay visibility) — the only load-bearing work was the CaliforniaHandRank → Eval bridge. - Phase 1: Eval::from_razz_rank + Eval::from_seven_razz; new HandRankName::RazzLow + HandRankClass::Lowball variants. - Phase 2: razz_river_case_eval + dispatch split (StudHi vs Razz) in river_case_eval_for_variant + build_eval_for_seat. - Phase 3: razz_from_seats constructor (mirrors stud_hi_from_seats). - Phase 4: BotProfile::for_razz factory + 2 reference YAML profiles. - Phase 5: examples/interactive_play_razz.rs — 20-hand smoke. - Phase 6: HandHistory replay routes HandVariant::Razz through razz_from_seats; new test_razz_bot_selfplay_replay_roundtrip. - Phase 7: Verification + EPIC-33 corrigendum. Inversion math turned out unnecessary — HandRank::cmp is already inverted, cancelling CaliforniaHandRank's lower-is-better ordering. Razz full replay round-trip deferred to v1.1 (incremental-dealing gap shared with Stud Hi). * fix: accept Axs/Axo/Ax as aliases for A2s+/A2o+/A2+ The combo parser at src/analysis/gto/combo.rs only knew explicit anchor tokens (A2s+, A3s+, etc.), so any-suited-Ace shorthand silently fell through to "Unable to process Axs" stdout noise and dropped the range entry. loose_passive.yaml's open_raise field tripped this. Two-part fix: - loose_passive.yaml: Axs -> A2s+ (canonical form). - combo.rs: also accept "axs" / "axo" / "ax" as aliases that expand to the same Combo constants. Both notations now produce identical parsed ranges; YAMLs can use whichever reads better. Adds 6 new parametrized rstest cases (both casings). * docs: spec for position indicators in interactive_play examples * feat(interactive_play): add position_tag helper with HU collapse * feat(interactive_play): show position tags on Stacks line * feat(interactive_play): add position tag column to action lines * feat(interactive_play): show Position field in Your turn box * docs: spec scope trim + implementation plan * needs work * fix(bot): gate YAML-loading tests behind bot-profiles feature The PLO/Stud Hi/Razz/FLHE/NLHE *_yaml_loads tests call BotProfile::from_yaml_str, which is gated behind feature = \"bot-profiles\". Without the matching cfg on the tests, --no-default-features breaks the test build in CI. * bump
1 parent 7a1064a commit b44ed88

49 files changed

Lines changed: 8415 additions & 86 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"Skill(release-notes)",
5151
"Bash([ -d \"$BASE/$repo\" ])",
5252
"Bash(cargo fmt *)",
53-
"WebSearch"
53+
"WebSearch",
54+
"Bash(git symbolic-ref *)"
5455
]
5556
}
5657
}

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "pkcore"
33
description = "Prototype core poker library."
4-
version = "0.0.56"
4+
version = "0.0.57"
55
rust-version = "1.94.1"
66
edition = "2024"
77
authors = ["electronicpanopticon <gaoler@electronicpanopticon.com>"]

ROADMAP.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ workspace)
124124
| [EPIC-26](docs/EPIC-26_Player_Stats.md) | Player Action Tracking & Opponent Insights — `PlayerStats` / `StatsRegistry` keyed by `Uuid`, derived ratios (VPIP/PFR/AF/WTSD/c-bet/...), exposed to `BotDecider` (no behavior change), optional persistence | Complete |
125125
| [EPIC-27](docs/EPIC-27_Exploitative_Decider.md) | Adaptive Bot Framework — `ExploitativeDecider<D>` wrapper that converts opponent stats into runtime profile deviations; `ExploitConfig` with 8 deviation rules; `SimTable::new_with_registry`; demo + smoke tests | Complete |
126126
| [EPIC-28](docs/EPIC-28_Profile_Training.md) | Cross-Session Profile Training — `ExploitTrainer` (1+λ)-ES loop tunes `ExploitConfig` parameters against a static field; `bot-training` feature; YAML serialisation for trained configs; `train_exploit_config` example | Complete |
127+
| [EPIC-29](docs/EPIC-29_Variant_Engine_Foundation.md) | Variant Engine Foundation — `BettingStructure` and `GameFamily` enums; data-driven street descriptors; per-card visibility; optional board; `ForcedBets::AnteAndBringIn`; existing NLHE behavior unchanged | Planned |
128+
| [EPIC-30](docs/EPIC-30_Limit_Holdem.md) | Fixed-Limit Hold'em — `GameType::LimitHoldem`; small-bet/big-bet street tiers; raise cap; `limit_holdem_from_seats` constructor; FLHE-tuned bot profiles | Planned |
129+
| [EPIC-31](docs/EPIC-31_Pot_Limit_Omaha.md) | Pot-Limit Omaha (Hi) — wire `OmahaHigh` (from EPIC-09) into showdown; 4-card hole; pot-limit sizing; fix `cards_on_board` for PLO; `plo_from_seats` constructor | Planned |
130+
| [EPIC-32](docs/EPIC-32_Stud_Hi.md) | Seven-Card Stud Hi — no community board; ante + bring-in (lowest upcard); 5 streets with upcards; action-by-best-visible-hand; fixed-limit small/big-bet tiers; `stud_hi_from_seats` constructor | Planned |
131+
| [EPIC-33](docs/EPIC-33_Razz.md) | Razz — A-5 lowball on the Stud engine; bring-in by highest upcard; action by worst visible hand; finishes the integration EPIC-10 left open; `razz_from_seats` constructor | Planned |
132+
| [EPIC-34](docs/EPIC-34_Variant_Web_Selection.md) | pkarena0-web Variant Selection — surface GameType selector in the web app; per-variant table rendering (no-community for Stud/Razz, 4-card hole for PLO, per-seat upcard reveal); per-variant `BotProfile` bundles | Planned |
127133
| [FEATURE: Activate Bluff Fields](docs/FEATURE_BotProfile_ActivateBluffFields.md) | Wire `bluff_frequency`, `check_raise_frequency`, `postflop_cbet_frequency` into `RuleBasedDecider` | Complete |
128134
| [FEATURE: Position-Aware Decisions](docs/FEATURE_BotProfile_PositionAwareDecisions.md) | Route decisions through `Playbook` position-specific `BettingStrategy` | Complete |
129135
| [FEATURE: BotProfile Type Safety](docs/FEATURE_BotProfile_TypeSafety.md) | `PlayStyle` enum, `Percentage` newtype for frequency fields | Complete |
@@ -132,6 +138,49 @@ workspace)
132138

133139
---
134140

141+
## Variant Initiative (EPIC-29 – EPIC-34)
142+
143+
**Goal:** make four additional poker variants — Fixed-Limit Hold'em,
144+
Pot-Limit Omaha (Hi), Seven-Card Stud Hi, and Razz — fully playable through
145+
the `pkcore` engine and through the interactive `pkarena0-web` UI, with
146+
variant-aware bot profiles that don't blunder.
147+
148+
The initiative is structured foundation-first:
149+
150+
- [**EPIC-29 — Variant Engine Foundation**](docs/EPIC-29_Variant_Engine_Foundation.md):
151+
introduces `BettingStructure` (no-limit / pot-limit / fixed-limit) as
152+
**orthogonal** to `GameFamily` (Hold'em / Omaha / Stud / Razz); replaces
153+
the hardcoded preflop/flop/turn/river `GamePhase` with data-driven street
154+
descriptors; adds per-card visibility and an optional board model;
155+
extends `ForcedBets` to cover ante + bring-in. Existing NLHE behavior
156+
must remain identical after this epic ships.
157+
- [**EPIC-30 — Fixed-Limit Hold'em**](docs/EPIC-30_Limit_Holdem.md):
158+
first variant exercising `BettingStructure`. Same dealing and showdown
159+
as NLHE; only bet sizes and raise cap differ.
160+
- [**EPIC-31 — Pot-Limit Omaha (Hi)**](docs/EPIC-31_Pot_Limit_Omaha.md):
161+
wires `OmahaHigh` (the must-use-2 + must-use-3 evaluator from EPIC-09)
162+
into showdown; 4-card hole; pot-limit bet sizing; fixes the
163+
`cards_on_board` bug for PLO.
164+
- [**EPIC-32 — Stud Hi**](docs/EPIC-32_Stud_Hi.md): the structurally
165+
distinct variant — no community board, ante + bring-in, 5 streets with
166+
upcards, action by best visible hand, fixed-limit small/big-bet tiers.
167+
Showdown reuses the existing `Seven::eval` evaluator unchanged.
168+
- [**EPIC-33 — Razz**](docs/EPIC-33_Razz.md): A-5 lowball on the Stud
169+
engine. Bring-in by highest upcard; action by worst visible hand;
170+
finishes the evaluator integration that EPIC-10 left open.
171+
- [**EPIC-34 — pkarena0-web Variant Selection**](docs/EPIC-34_Variant_Web_Selection.md):
172+
exposes all four new variants through the web app — per-`GameType`
173+
selector, per-family table renderer (no-community for stud-family,
174+
4-card hole for PLO, per-seat upcard reveal for Stud/Razz), and
175+
per-variant `BotProfile` bundles.
176+
177+
**Deferred to a follow-on epic** (not in v1): split-pot / 8-or-better
178+
machinery for Omaha Hi-Lo (O8) and Stud Hi-Lo (Stud8). That work would
179+
become a "Hi-Lo & HORSE" epic (likely EPIC-35) and unlock full HORSE
180+
coverage when combined with the v1 variants.
181+
182+
---
183+
135184
## EPIC-19: Bot Self-Play Simulation
136185

137186
**Goal:** Run a full table of bots against each other *inside pkcore*, using
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: loose_passive_flhe
2+
description: FLHE calling-station counter — wide cold-calling range, infrequent raises, lots of postflop chasing because the small bets price it in.
3+
style: loose_passive
4+
range_strategy:
5+
open_raise: 77+, ATs+, KQs, AJo+
6+
three_bet: QQ+, AKs
7+
call_three_bet: 22+, A2s+, KTs+, QTs+, J9s+, T8s+, 98s, ATo+, KJo+, QJo
8+
postflop_cbet_frequency: 20
9+
betting_strategy:
10+
aggression_factor: 20
11+
bluff_frequency: 4
12+
check_raise_frequency: 3
13+
preferred_bet_sizes:
14+
- 1/1
15+
betting_structure:
16+
kind: fixed_limit
17+
small_bet: 0
18+
big_bet: 0
19+
raise_cap: 3
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: tight_aggressive_flhe
2+
description: Classic Fixed-Limit Hold'em TAG — tight value-driven preflop selection, cap-friendly value betting on later streets, low bluff frequency since there's no all-in pressure.
3+
style: tight_aggressive
4+
range_strategy:
5+
open_raise: TT+, AJs+, KQs, AQo+
6+
three_bet: JJ+, AKs, AKo
7+
call_three_bet: 99+, AQs+, AKo
8+
postflop_cbet_frequency: 70
9+
betting_strategy:
10+
aggression_factor: 75
11+
bluff_frequency: 10
12+
check_raise_frequency: 18
13+
preferred_bet_sizes:
14+
- 1/1
15+
betting_structure:
16+
kind: fixed_limit
17+
small_bet: 0
18+
big_bet: 0
19+
raise_cap: 3

data/bots/loose_passive.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: loose_passive
22
description: Wide hand selection with passive betting — the classic calling station archetype.
33
style: loose_passive
44
range_strategy:
5-
open_raise: 22+, Axs, KTs+, QTs+, J9s+, T8s+, 98s, ATo+, KTo+
5+
open_raise: 22+, A2s+, KTs+, QTs+, J9s+, T8s+, 98s, ATo+, KTo+
66
three_bet: QQ+, AKs
77
call_three_bet: TT+, AJs+
88
postflop_cbet_frequency: 15
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: loose_aggressive_plo
2+
description: Classic PLO loose-aggressive — wide preflop selection, frequent pot-sized aggression, looking for big multiway pots. Range notation is NLHE-style; the engine clamps raise sizes to the pot-limit ceiling at runtime.
3+
style: loose_aggressive
4+
range_strategy:
5+
open_raise: 22+, A2s+, K9s+, Q9s+, J9s+, T8s+, 97s+, 86s+, 75s+, 65s, 54s, A9o+, KTo+, QTo+, JTo
6+
three_bet: TT+, AJs+, KQs, AQo+
7+
call_three_bet: 22+, A2s+, KTs+, QTs+, JTs, T9s, ATo+, KQo
8+
postflop_cbet_frequency: 55
9+
betting_strategy:
10+
aggression_factor: 65
11+
bluff_frequency: 25
12+
check_raise_frequency: 18
13+
preferred_bet_sizes:
14+
- 2/3
15+
- 1/1
16+
betting_structure:
17+
kind: pot_limit
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: tight_aggressive_plo
2+
description: Tight-aggressive PLO archetype — premium hand selection, pot-control on dry boards, big bets on coordinated boards. NLHE-style range placeholders; runtime sizing uses pot-limit ceiling.
3+
style: tight_aggressive
4+
range_strategy:
5+
open_raise: TT+, AJs+, KQs, QJs, JTs, T9s, AQo+
6+
three_bet: JJ+, AKs, AKo
7+
call_three_bet: 99+, AQs+, KQs, AKo
8+
postflop_cbet_frequency: 60
9+
betting_strategy:
10+
aggression_factor: 70
11+
bluff_frequency: 15
12+
check_raise_frequency: 12
13+
preferred_bet_sizes:
14+
- 2/3
15+
- 1/1
16+
betting_structure:
17+
kind: pot_limit
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: loose_passive_razz
2+
description: Razz calling-station — wide 3rd-street range that includes 9-low draws and even some paired starts, frequent calls down with marginal lows, rarely raises until rivered.
3+
style: loose_passive
4+
range_strategy:
5+
open_raise: 99+, ATs+, KQs, AJo+
6+
three_bet: QQ+, AKs
7+
call_three_bet: 22+, A8s+, KTs+, QTs+, J9s+, T9s, ATo+, KJo+, QJo
8+
postflop_cbet_frequency: 20
9+
betting_strategy:
10+
aggression_factor: 18
11+
bluff_frequency: 4
12+
check_raise_frequency: 3
13+
preferred_bet_sizes:
14+
- 1/1
15+
betting_structure:
16+
kind: fixed_limit
17+
small_bet: 0
18+
big_bet: 0
19+
raise_cap: 3
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: tight_aggressive_razz
2+
description: Classic Razz TAG — opens only with three pair-free cards 8-or-lower (premium low draws), folds anything paired or T+ on 3rd street, presses aggressively when an opponent's upcard pairs or shows high.
3+
style: tight_aggressive
4+
range_strategy:
5+
open_raise: TT+, AJs+, KQs, AQo+
6+
three_bet: JJ+, AKs, AKo
7+
call_three_bet: 99+, AQs+, AKo
8+
postflop_cbet_frequency: 65
9+
betting_strategy:
10+
aggression_factor: 70
11+
bluff_frequency: 12
12+
check_raise_frequency: 15
13+
preferred_bet_sizes:
14+
- 1/1
15+
betting_structure:
16+
kind: fixed_limit
17+
small_bet: 0
18+
big_bet: 0
19+
raise_cap: 3

0 commit comments

Comments
 (0)