Skip to content

Add calling/defense ranges (facing a shove & facing an open) - #4

Merged
testtest126 merged 2 commits into
mainfrom
feature/calling-ranges
Jul 21, 2026
Merged

Add calling/defense ranges (facing a shove & facing an open)#4
testtest126 merged 2 commits into
mainfrom
feature/calling-ranges

Conversation

@testtest126

Copy link
Copy Markdown
Owner

Summary

Adds the biggest gap flagged in the last repo audit: hero defending rather than acting first. Two new decision types, both built on the existing PushFoldRange/OpeningRange/ChenScore pipeline — no new hand-ranking system, no new range-model architecture.

  • Facing a shove (CallingRange.decideVsShove) — short stacks (~1-20bb), call-or-fold against someone else's all-in.
  • Facing an open (CallingRange.decideVsOpen) — standard stacks (~20-100bb), fold/call/3-bet against someone else's open-raise.

New DefendingPosition enum (UTG...SB + BB) models hero's defending seat — Position deliberately excludes BB (it can't open or shove into an unopened pot), but BB is exactly who most often calls one. Both enums share action-order semantics so CallingRange/PreflopGrid can reject nonsensical pairings (e.g. UTG can never be "facing" anyone's shove) by returning nil instead of a fabricated decision.

PreflopGrid gains callingDecisions/openDefenseDecisions; PreflopRangeView gets a 4-way mode picker (Push/Fold, Opening, Facing Shove, Facing Open) with an opponent+hero position-picker pair for the two new modes, a third grid color for 3-bet vs. call vs. fold, and a persistent on-screen caveat pointing at the docs.

Range assumptions & sources — read before trusting a number

Full writeup in ai-docs/RANGES.md (new "Facing a Shove" / "Facing an Open" sections). Short version:

  • Big blind facing a shove is the model's best-grounded case — it's the one situation with a published Nash equivalent (heads-up SB-shoves/BB-calls). Cross-checked an external heads-up SB-shove figure (~50% at 10bb) against this project's own PushFoldRange SB figure (58% at 10bb) — close enough to treat as the same phenomenon.
  • Everything else in "Facing a shove" (small blind or non-blind callers) — no public multi-way calling chart exists for these (confirmed via search: published Nash calling charts are explicitly heads-up-only). Modeled as a disclosed discount off the shover's own PushFoldRange percentage, with per-position discount factors that are hand-tuned placeholders, not sourced.
  • Big blind facing an open — anchored on one external figure (~84% combined call+3-bet vs. a button open, from MTT blind-defense strategy material), then scaled by the ratio of the opener's own OpeningRange percentage to the button's — reuses OpeningRange's numbers rather than inventing a second opinion.
  • Small blind facing an open — set to 65% of what big blind would defend (every source agrees SB defends narrower, none gives a ratio). Hand-tuned.
  • Non-blind defenders facing an open (e.g. cutoff facing a UTG open) — flat 50% of big blind's number. This is the single least-confident number in the whole model — no source at all distinguishes different non-blind defending spots.
  • The 3-bet/call split within total defense — hand-tuned percentages (BB 25% 3-bet, SB 45%, others 35%), explicitly disclosed as capturing only the value side of what should be a polarized (value + bluffs) 3-betting range.

None of this is solver output. Every model in this codebase says so already; these two say it more, because they're the least certain ones here.

Testing

  • swift test: 116/116 passing (94 before this PR + 22 new).
  • New tests: nil-guards for invalid position pairings, threshold monotonicity (tighter vs. earlier shover/opener, wider as stack shortens, BB wider than SB), suited-never-loses-to-offsuit, grid/model consistency, reasoning-text checks.
  • App: built and ran clean on iOS Simulator (Xcode 27 beta toolchain); all 3 PreflopRangeUITests pass, including two new tests exercising both defense modes end-to-end (mode switch → position pickers filter correctly → grid renders → summary reacts to position change).

Housekeeping

Not merging — flagging for review on the honesty/soundness of the range assumptions per instructions.

New CallingRange module covers the two situations PushFoldRange and
OpeningRange don't: hero reacting to someone else's action rather than
acting first. DefendingPosition extends the six-position scheme with the
big blind, since a defending hero can be exactly the position Position
deliberately excludes.

Both defense models reuse PushFoldRange/OpeningRange's own numbers rather
than inventing a second opinion on how wide any position shoves or opens —
big-blind-facing-a-shove is the one case with a real Nash equivalent
(heads-up SB-shove/BB-call), everything else is a disclosed, hand-tuned
extrapolation off that. See ai-docs/RANGES.md for the full source basis,
including exactly which numbers are sourced vs. hand-tuned placeholders.

PreflopGrid gains callingDecisions/openDefenseDecisions, returning nil for
position pairings that can't happen at an unopened table (e.g. UTG can't be
facing anyone's shove). 22 new tests: coverage, nil-guards, threshold
monotonicity by stack and position, suited-never-loses-to-offsuit, and
grid/model consistency.
Extends the four-way mode picker (Push/Fold, Opening, Facing Shove, Facing
Open) with an opponent-position + hero-position picker pair for the two new
defense modes, replacing the single position picker used by the aggressor
modes. The hero picker filters to only positions that could plausibly be
facing the chosen opponent (DefendingPosition.actionOrderIndex >
Position.actionOrderIndex), auto-resetting to the big blind — always valid —
if the opponent choice would otherwise strand it on an impossible pairing.

Facing Open renders a third grid color (3-bet vs. call vs. fold) since it's
the one mode with three distinct actions. Both defense modes show a
persistent caveat line pointing at RANGES.md's confidence notes, rather than
presenting these numbers with the same certainty as push/fold or opening.
@testtest126
testtest126 merged commit 2364b04 into main Jul 21, 2026
2 checks passed
@testtest126
testtest126 deleted the feature/calling-ranges branch July 21, 2026 13:47
testtest126 added a commit that referenced this pull request Jul 21, 2026
Resolves conflicts in PreflopRangeView.swift and PreflopRangeUITests.swift
by keeping both features: the four-way mode picker (Push/Fold, Opening,
Facing Shove, Facing Open) from the calling-ranges PR, and the PKO bounty
overlay (toggle, bounty slider, "you cover villain" toggle, bounty-only
grid color) from this branch — the overlay only ever applies in Push/Fold
mode, so the two never actually compete for the same UI state.

CellStyle gains a fourth case (.bountyOnly) alongside the existing
.primary/.secondary/.fold from the calling-ranges merge. summaryText and
the caveat line both switch on mode plus the bounty-active flag rather than
mode alone. ai-docs/RANGES.md and PreflopGrid.swift merged cleanly with no
conflicts — the two PRs touched non-overlapping sections/functions there.

swift test: 135/135 passing (116 post-#4 baseline + 19 bounty tests, none
lost). App builds clean; all 4 PreflopRangeUITests pass (base grid, bounty
overlay, facing shove, facing open).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant