# Reproducible bunching-effect experiment

This bundle measures conditional card removal after observed upstream folds. It is deliberately narrower than a strategy study: every non-control fold policy is synthetic, and the only conclusion available is about the rank composition of unseen cards under the stated probability rule.

## Question and estimand

All seats receive two cards from a standard 52-card deck. We observe every seat before the Button fold and ask how the conditional distribution of the Button's already-dealt hole cards differs from an unconditional two-card hand.

The table-size sensitivity is:

| Table | Observed upstream folds |
| --- | ---: |
| 6-max | 3 |
| 8-max | 5 |
| 9-max | 6 |

The code generates upstream cards before Button cards only as exchangeable sampling bookkeeping. It does not imply that cards are physically dealt after actions.

The primary outcomes are the Button probabilities of at least one ace, any pocket pair, TT+, AK, and two cards ranked T or higher. The bundle also publishes the expected remaining share of every rank and all 169 canonical Button hand-class probabilities.

## Fold-likelihood policies

Let `high` and `low` be rank values from 2 through 14, `pair` be 1 for a pocket pair, and `suited` be 1 when two distinct-rank cards share a suit. Each formula first produces a base weight. A single model-wide scale factor then makes the physical-combo-weighted average over all 1,326 starting hands exactly 0.60.

| Model | Base rule | Scale |
| --- | --- | ---: |
| `random_fold_control` | `p = 0.60` | 1.000000000000 |
| `rank_gradient` | `p = clamp(0.86 - 0.0275 × ((high + low) - 4), 0.18, 0.86)` | 1.132075471698 |
| `pair_suited_gradient` | `score = high + low + 5×pair + 1×suited`; `p = 0.05 + 0.90/(1 + exp((score - 20)/2.4))` | 0.887448750856 |
| `bucketed_top_band` | Base `p=0.08` for 88+, AT+, suited A8+/KT+/QT+; `p=0.38` for remaining pairs, remaining Ax, and remaining suited hands with `high≥J, low≥8`; `p=0.86` otherwise | 0.839665653495 |
| `inverted_rank_diagnostic` | `p = clamp(0.20 + 0.0275 × ((high + low) - 4), 0.20, 0.86)` | 1.132075471698 |

The actual probability is `base × scale`. [`fold_likelihoods.csv`](fold_likelihoods.csv) is the authoritative expanded table for all 169 classes. Pairs, suited hands, and offsuit hands represent 6, 4, and 12 physical combos respectively; the checker expands these to all 1,326 concrete card pairs before blockers.

Matching the 0.60 marginal rate removes a simple rate confound. Multiplying every likelihood in one policy by the same positive constant also leaves the conditional posterior unchanged, provided no value exceeds 1. The matched figure is the marginal likelihood for one hand from a fresh deck. The joint chance that all 3, 5, or 6 seats fold can differ slightly because the dealt hands share a deck.

The inverted policy is an intentionally non-behavioral direction check. Its purpose is to show that the posterior can reverse when the composition of folded hands reverses—not to characterize poker players.

## Primary estimator

For one complete deal `d`, let `w(d)` be the product of the fold probabilities for all observed upstream hands. The generator proposes complete deals uniformly without replacement and accepts a deal when `U < w(d)` for `U ~ Uniform(0,1)`. Therefore accepted deals are iid draws from

`P(d | all observed folds) ∝ P(d) × ∏ p(fold_i | hand_i)`.

This conditions on all actions jointly. A seat-by-seat sampler that normalizes after each fold would be subtly wrong because later folds also carry information about cards assigned to earlier seats.

The published run used 200,000 accepted deals for each of 15 model/table-size scenarios and NumPy's `PCG64DXSM`; every scenario seed and attempted-deal count is frozen in [`results.json`](results.json). Binary outcomes and each of the 169 hand-class proportions use 95% Wilson intervals. Mean rank shares use a normal Monte Carlo interval. Split-half diagnostics are recorded for the five headline metrics.

The intuitive `one_event_per_target_hands` translation is only emitted when the exact baseline lies outside the 95% interval. It equals `1 / |conditional estimate - baseline|`; it is a frequency translation, not a waiting-time prediction.

## Exact oracle and independent checker

[`one_fold_exact_oracle.json`](one_fold_exact_oracle.json) enumerates all 1,326 possible folded hands and all 1,225 possible Button hands remaining after each fold. It is exact apart from floating-point representation.

[`check_bunching.py`](check_bunching.py) is intentionally independent of the primary module. It:

1. reads the CSV rather than importing model functions;
2. independently expands all 169 classes to concrete combos;
3. recomputes the exact one-fold oracle;
4. draws complete deals with a different random-key algorithm and NumPy `Philox`;
5. uses self-normalized importance sampling with delta-method ratio-estimator standard errors; and
6. compares headline metrics, all 169 Button classes, and all 13 remaining-rank shares with the acceptance/rejection output.

The published 300,000-raw-deal checker run passes: model means differ from 0.60 by at most `1e-11`, exact-oracle discrepancy is below `1e-12`, the random one-fold control matches combinatorial baselines below `1e-12`, and every stochastic estimate is within five combined Monte Carlo standard errors. See [`CHECKS.md`](CHECKS.md) and [`checker_results.json`](checker_results.json).

## Exact no-information baselines

With no card-informative folds, exchangeability leaves the Button's hand unconditional:

- at least one ace: `1 - C(48,2)/C(52,2) = 14.9321%`;
- any pair: `13×C(4,2)/C(52,2) = 5.8824%`;
- TT+: `5×C(4,2)/C(52,2) = 2.2624%`;
- AK: `16/C(52,2) = 1.2066%`; and
- both cards T+: `C(20,2)/C(52,2) = 14.3288%`.

The constant-fold control should fluctuate around these figures regardless of how many random folds are observed.

## Reproduce

Python 3.11 and NumPy 2.2.3 were used for the frozen run.

```powershell
python simulate_bunching.py --samples 200000 --batch-size 100000 --seed 2026090406
python check_bunching.py --raw-samples 300000 --batch-size 50000 --seed 902026090406
```

Files:

- [`simulate_bunching.py`](simulate_bunching.py): primary estimator and exact oracle;
- [`check_bunching.py`](check_bunching.py): independent implementation and acceptance checks;
- [`fold_likelihoods.csv`](fold_likelihoods.csv): actual fold probabilities for all models/classes;
- [`results.json`](results.json): full machine-readable results, diagnostics, seeds, and 169-class posteriors;
- [`headline_metric_results.csv`](headline_metric_results.csv): compact headline table;
- [`button_hand_class_results.csv`](button_hand_class_results.csv): flat 15×169 posterior table;
- [`one_fold_exact_oracle.json`](one_fold_exact_oracle.json): exact one-fold result; and
- [`RESULTS.md`](RESULTS.md), [`CHECKS.md`](CHECKS.md): readable summaries; and
- [`MANIFEST.sha256`](MANIFEST.sha256): SHA-256 identities for the other 12 public files, calculated over the Git/deployment LF bytes. The generator and repository attributes both fix these text files to LF, so the focused test compares the raw bytes on every platform.

## Limits that must travel with any article

- The action policies are transparent synthetic examples, not observed population ranges and not solver outputs.
- Applying the same policy to every folding seat is an isolation device. Actual positions need not share a policy.
- Fold actions are assumed conditionally independent once each seat's private hand is fixed; shared cards are the only modeled dependence.
- No stack depth, raise size, rake, ante, straddle, tournament pressure, player identity, or later action is represented.
- The study conditions on folded private cards only. It does not model muck visibility or infer any particular real hand.
- Monte Carlo intervals describe sampling error conditional on a policy; they do not describe model uncertainty.
- Rank enrichment is not universal. The inverted diagnostic reverses it, while the constant policy produces zero exact effect.
- Card composition alone cannot establish GTO action frequencies, exploitability, expected value, product behavior, or profit.
