NG Preflop Port — 169-Hand Ranking Strategy

# NG Preflop Port — 169-Hand Ranking Strategy

## Summary

Ported the Java `PreFlopStrategyV1` (ringnl/supersonic) to Rust as `ng_preflop`.
This replaces the boolean hand-grouping approach (`g1_big_stack_preflop`) with
a continuous 169-hand ranking table approach for Gen 2 and Gen 3.

Gen 3 is now the production bot (`cash_nl.toml`).

## New Files
- `holdem_bots/src/common/hand_rankings.rs` — Three 169-hand ranking tables
  (general, HU, profitability) + blended strength functions
- `holdem_bots/src/cash/ng_preflop.rs` — `NgPreFlop` strategy ported from Java

## Architecture: Old vs New

### Old: Boolean Hand Grouping (`g1_big_stack_preflop`)
- Discrete categories with gaps (J9s falls through)
- No continuous strength measure, no HU/full-ring interpolation

### New: 169-Hand Ranking Tables (`ng_preflop`)
- Three orderings: general (Sklansky), HU (jazbo.com), profit (tightpoker.com)
- Blended strength by active players: `mixed_strength_half` and `mixed_strength`
- Position-aware thresholds: SB steal 17.7%, early 7.2%, mid 8.4%, late 14.2%
- Implied odds for limping: requires `potentialWin >= 15 × to_call`
- Re-raise thresholds: standard 5.9% (JJ+/AK), huge raise 1.2%

## Configs

### Production (`cash_nl.toml` — Gen 3)
```toml
[[strategies]]
type = "g1_short_stack_preflop"
[[strategies]]
type = "ng_preflop"
[[strategies]]
type = "g3_postflop_rollout"
```

### Comparison (`cash_nl_gen2.toml` — Gen 2)
```toml
[[strategies]]
type = "g1_short_stack_preflop"
[[strategies]]
type = "g1_big_stack_preflop"
[[strategies]]
type = "g2_postflop_rollout_v4"
```

### Comparison (`cash_gen1.toml` — Gen 1)
```toml
[[strategies]]
type = "g1_short_stack_preflop"
[[strategies]]
type = "g1_big_stack_preflop"
[[strategies]]
type = "g1_postflop_rules"
```

## What Was NOT Ported (from NGPreFlopStrategyV2)

- **PlayerModelV2** — opponent statistics database
- **ICM all-in profitability** — tournament equity calculations
- **Win probability tables** (13×13 vs top 5%/10% ranges) — could be ported later
- **Squeeze plays, limp-reraise** — future enhancements
- **Blind level modifier** — SNG-specific

## Key Fixes Applied During Port

- **f64::MAX sentinel inversion** — `determine_limp_cards_threshold` returned MAX (always-limp) → fixed to 0.0
- **action_commits_me cost** — now passes `to_call + raise_amount` (true out-of-pocket)
- **safe_raise** — caps raise totals at effective stack, saturating arithmetic
- **Redundant percentile scans** — eliminated (4→2 per decision via `_from` variants)
- **sweep_mode** — production config sets `sweep_mode = false` for live entropy

id: f9c8cc6ea0e743b1a7997fbb0f8ea792
parent_id: 1246bbc3bb4948fc8329079b84b4ae3d
created_time: 2026-06-22T11:45:19.938Z
updated_time: 2026-06-22T15:53:10.156Z
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: 1782128719938
user_created_time: 2026-06-22T11:45:19.938Z
user_updated_time: 2026-06-22T15:53:10.156Z
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