SNG Strategy Architecture Design

> **Status**: Design — not yet implemented
> **Scope**: Add Sit-and-Go tournament strategy chain to `super_marvin`

---

## 1. Overview

Architecture for porting the Java SNG strategy chain (`JointNoLimitSnGStrategy`) into the Rust `holdem_bots` crate. The SNG bot uses `FirstApplicable` voting to select among prioritized sub-strategies.

Introduces a new `holdem_bots/src/sng/` module with five new strategies, reuses two existing cash strategies, and adds one new helper method to `StrategyContext`.

```
SNG Bot Assembly (FirstApplicable Voting)
├── 1. FoldMicroStack
├── 2. StealFromInactive
├── 3. PushOrFold
├── 4. BlindSteal
├── 5. BlindReSteal
├── 6. BigStackPreFlop (reuse)
└── 7. PostFlopStrategy (reuse)
```

---

## 2. Strategy Comparison Table

| # | Java Strategy | Rust Name | Status | Module |
|---|--------------|-----------|--------|--------|
| 1 | `FoldMicroStackStrategyV1` | `FoldMicroStack` | **New** | `sng::fold_micro_stack` |
| 2 | `StealFromInactiveStrategyV1` | `StealFromInactive` | **New** | `sng::steal_from_inactive` |
| 3 | `ModularNashICM` | — | **Skipped** | Using `PushOrFold` instead |
| 4 | `PushOrFoldStrategyV3` | `PushOrFold` | **New** | `sng::push_or_fold` |
| 5 | `BlindStealStrategyV1` | `BlindSteal` | **New** | `sng::blind_steal` |
| 6 | `BlindReStealStrategyV1` | `BlindReSteal` | **New** | `sng::blind_re_steal` |
| 7 | `ShortStackPreFlopStrategyV2` | `ShortStackPreFlop` | **Replaced** | Cash version NOT used in SNG |
| 8 | `BigStackPreFlopStrategyV2` | `BigStackPreFlop` | **Reuse** | `cash::big_stack_preflop` |
| 9 | `PostFlopStrategyV1` | `PostFlopStrategy` | **Reuse** | `cash::postflop` |

**Key design decision**: SNG chain does NOT include `ShortStackPreFlop` from cash module. Instead, `PushOrFold` (≤14 BB), `BlindSteal` (13–24 BB), and `BlindReSteal` (13–24 BB) cover short-stack SNG-specific play.

---

## 3. Strategy Designs

### 3.1 FoldMicroStack (Low complexity)
- **Applicable when:** Preflop, stack < 0.5 × SB, 4-5 active players
- **Action:** AA/KK/QQ → AllIn, otherwise Fold
- `consider_fold_as_not_applicable()` = `false`

### 3.2 StealFromInactive (Low-Medium complexity)
- **Applicable when:** Preflop, stack ≤ 50 BB, no raises, all opponents sitting out
- **Action:** Min-raise (2× BB), or AllIn if raise > stack
- Needs `all_opponents_inactive()` helper on StrategyContext

### 3.3 PushOrFold (High complexity)
- **Applicable when:** Preflop, stack ≤ 14 BB
- **Action:** Chart-based push/fold decision
- Uses position-based hand range charts
- Stack ≤ 5 BB: wider ranges; ≤ 10 BB: medium; ≤ 14 BB: tighter
- `consider_fold_as_not_applicable()` = `false` (owns the decision when ≤14 BB)

**Push range chart (simplified):**
| Position | ≤ 5 BB | ≤ 10 BB | ≤ 14 BB |
|----------|--------|---------|---------|
| SB | Any ace, K7+, Q9+, J9+, T9+, 22+ | A8+, KJ+, QJ+, 55+ | AT+, KQ+, 77+ |
| BTN | Any ace, K8+, Q9+, J9+, 22+ | A9+, KJ+, QJ+, 55+ | AJ+, KQ+, 88+ |
| CO | A5+, K9+, Q9+, 33+ | AT+, KJ+, 66+ | AQ+, 99+ |
| Early | A8+, KJ+, QJ+, 66+ | AJ+, KQ+, 88+ | AK+, TT+ |

### 3.4 BlindSteal (Medium complexity)
- **Applicable when:** Preflop, 13-24 BB, no raises/callers, position in [CO, BTN, SB]
- **Action:** Classify hand (Premium/Strong/Steal-worthy/Weak) → Raise or Fold
- `consider_fold_as_not_applicable()` = `true`

### 3.5 BlindReSteal (Medium complexity)
- **Applicable when:** Preflop, 13-24 BB, someone raised, we are BB, raiser in late position, heads-up
- **Action:** Premium/Strong → AllIn, Good → Call if pot odds, Weak → Fold
- `consider_fold_as_not_applicable()` = `true`

---

## 4. SNG Bot Assembly Config

```toml
# configs/bots/sng_nl.toml
[bot]
name = "sng_nl"
voting_mode = "first_applicable"

[[strategies]]
type = "fold_micro_stack"

[[strategies]]
type = "steal_from_inactive"

[[strategies]]
type = "push_or_fold"
config = { max_stack_bb = 14.0 }

[[strategies]]
type = "blind_steal"
config = { min_stack_bb = 13.0, max_stack_bb = 24.0, raise_size_bb = 2.5 }

[[strategies]]
type = "blind_re_steal"
config = { min_stack_bb = 13.0, max_stack_bb = 24.0 }

[[strategies]]
type = "big_stack_preflop"

[[strategies]]
type = "postflop_static"
```

---

## 5. StrategyContext Extensions Needed

1. **`num_opponents_sitting_out()`** — count inactive opponents
2. **`all_opponents_inactive()`** — check if all opponents sitting out
3. **`position_for_seat(seat)`** — get position of player at given seat

---

## 6. Implementation Order

1. StrategyContext extensions (context.rs)
2. FoldMicroStack — simplest, validates module structure
3. StealFromInactive
4. BlindSteal
5. BlindReSteal
6. PushOrFold — most complex, hand range charts
7. sng/mod.rs — module declarations
8. registrations.rs — register all SNG strategies
9. lib.rs — add `pub mod sng`
10. Config files — create all TOML configs
11. Integration test — verify full chain assembles and plays

---

## 7. Open Questions

1. **PlayerState for sitting-out**: Does `holdem_core` have a `SittingOut` variant, or infer from `!is_in_hand() && stack > 0`?
2. **Tournament simulation support**: Does `holdem_sim` support `game_type = "Tournament"` with blind levels and payouts?
3. **PushOrFold chart data**: Exact ranges should be ported verbatim from Java source.
4. **BlindReSteal BB position**: Is `our_position == 1` always correct for BB?

id: 1568bbe9e7fc4e5a8d4335bdd3cc5988
parent_id: 2c8da247905946c3aa19eb4936e16323
created_time: 2026-05-31T10:37:47.610Z
updated_time: 2026-05-31T10:37:47.610Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1780223867610
user_created_time: 2026-05-31T10:37:47.610Z
user_updated_time: 2026-05-31T10:37:47.610Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1