Monte Carlo simulations of two drinking games, written in JAX so that thousands of games can be played in parallel on a GPU. Every run is fully deterministic given its random seed.
The repo contains two independent studies:
| Study | Entry point | Question it answers |
|---|---|---|
| Kwf-vierkant | vierkant/vierkant.py |
Which cell-picking strategy finishes the card square fastest, and how much does card counting help? |
| Beer pong | bierpong/simulate.py |
Do cup re-rack strategies (rhombus / triangle / never) actually change your win rate? (Yes — re-racking aggressively wins.) |
The project is managed with uv and requires Python 3.14 (pinned in .python-version). Dependencies are declared in pyproject.toml. If your GPU is capable, switch from jax[cuda12] to jax[cuda13]. JAX falls back to CPU if GPU is not available, but the simulations will be much slower.
uv syncA simulator for a card game played on a 7×7 grid. The middle row and column (a 13-card "plus sign") are dealt face up; the remaining 36 cells are filled one at a time by picking a cell adjacent to a placed card and predicting something about the next drawn card. What you predict depends on how many orthogonal neighbours are already filled: higher/lower (1 neighbour), between/outside (2), suit present/absent (3), or the exact rank (4). A wrong guess wipes the entire row and column of that cell back into the deck, and you drink a sip per removed card — which makes finishing the square brutally hard. The full rules are documented in the docstring at the top of vierkant/vierkant.py.
The script runs a Monte Carlo study along three axes:
- Strategies — six cell-picking policies (greedy expected progress, safest cell, max success probability, min wipe cost, random, …) defined in a small registry; adding a strategy is one function plus one registry entry.
- Deck models — reshuffle (a wiped card goes back into a fully shuffled deck) vs. ordered (wiped cards go to the bottom as a known group, so a card-counting player gets much better odds once they resurface).
- Starting positions — does a suit-clustered opening plus-sign correlate with finishing faster?
Run it from the vierkant/ directory (figures and game dumps are written to the current working directory):
cd vierkant
uv run python vierkant.pyThis prints per-configuration summary tables and writes a strategy × deck-model comparison figure, per-run histograms, the fastest/slowest finished games as ASCII boards, and the starting-position correlation plots. Tune the run by editing the block under if __name__ == "__main__": (STRATEGIES_TO_RUN, DECK_MODELS_TO_RUN, N_GAMES, SEED).
A vectorized beer pong simulator that compares three cup-rearrangement strategies (request a re-rack of the opponent's cups at 4 cups, at 3 cups, or never) under three aiming strategies (centre of mass, a random cup, or the nearest cup). Re-racking is modelled the way it actually works: you re-rack the cups you shoot at, and the opponent places the compact rack in the hardest legal spot (shoved to the back edge). Throws are sampled with radial and angular Gaussian noise so accuracy degrades with distance, plus human factors — target lock (tight clusters are easier to aim at), momentum (streaks tighten your aim), and rim bounce-ins between touching cups — on top of bonus throws, double-hit penalties, and low-probability trick shots. The complete rule set and throw model live in bierpong/simulate.md.
The headline result is about timing: re-racking helps, and re-racking earliest wins. Rhombus (re-racks the opponent at 4 cups) beats Triangle (at 3 cups) under every aim mode, and both beat never re-racking (Baseline), because you spend more of the game shooting at an easy, consolidated target. The payoff grows for noisier, more casual throwers.
uv run python bierpong/simulate.py [--seed 0] [--games 100000] [--no-gif]Results go to bierpong/results/ by default (--out to change): a win-rate matrix with confidence intervals, game-length distributions, layout/throw-scatter galleries, a re-rack effect plot, a skill-sweep plot (Rhombus's edge vs thrower skill), an analytic-vs-simulated validation figure, and an animated GIF of a single game (skip with --no-gif).
vierkant/ Kwf-vierkant simulator + generated figures/dumps
bierpong/ Beer pong simulator, rules spec, and results
main.py uv-init placeholder, not part of either study