Modes & rules

Ways to play

Solo, vs Bot and Online rooms (race, draft), with the Classic or the Balanced rules. Solo and vs Bot need no sign-in; online rooms need an account.

ContestThe contest is judged with the classic rules in Solo. The other modes and the Balanced rules are for playing and experimenting.

Multiplayer modes #

The browser game (index.html) has a Mode switch in its top bar: Solo (the default, the contest's game), vs Bot and Online. Solo and vs Bot run entirely in the page (no server, no network; the browser remembers your game). Online play happens in rooms on the contest website's server: each player on their own device, signed in to their own account.

Every match is set up with one seed and one ruleset (Classic or Balanced), shared by all players: the same draw orders and the same shop queues. Each player has their own independent run (a PokerRun.Game), so each player's move list is a valid output file for that seed, and the results screen shows them all with their checker verdicts. Highest total wins; on equal totals, more rounds cleared; otherwise it's a shared place.

(Earlier versions also had same-device modes, Pass & play and a local Draft. They are gone: old links to them open the Online screen, a draft link with Draft preselected.)

vs Bot #

You against a computer player on the same deal. The bot plays its whole run on its own at the start (it never sees your moves); its rounds are revealed one at a time: when you finish round r (your last play of the round), the bot's round r appears in the Race panel with its score, its plays, what it bought and its moves. The panel also compares your totals round for round. When your run ends, the rest of the bot's run is revealed and the results screen shows the winner, both scores, both move lists, Rematch (same seed) and New seed.

Bot levels (bot.js, and the same moves from poker_run/bot.py on the server):

LevelWhat it doesMean score, seeds 1-20 (classic / balanced)
Easyplays its best hand every time, never discards, buys at most one modifier per shop, never levels2,346 / 2,351
Normalan exact port of poker_run/baseline.py: the same moves, move for move9,069 / 10,702
Hardthe baseline, plus a look-ahead when it discards (the draw order is known, so it tries ~10 discards and keeps the one that leaves the best play) and a modifier wish list in the shop19,496 / 24,145

On seeds 21-120: Easy 2,738 / 3,044, Normal 8,435 / 10,426, Hard 21,474 / 21,704. The example solvers in poker_run/solvers/ score 80,000-95,000.

Online rooms #

Pick Online, sign in (the game sends you to the contest page and back), then create a room or join one with its code. A room has a mode (Race, Draft or Rounds), a ruleset, a seed (random unless you pick one) and 2 to 4 seats; empty seats can hold bots (Easy, Normal or Hard), which play on the server. The room code is 5 characters from ABCDEFGHJKLMNPQRSTUVWXYZ23456789 (no 0/O or 1/I), and the share link is /play/#room=CODE.

Lobby. Everyone appears under their username with a colour, a ready tick and online/away. The host can change the mode, rules, seat count and seed (that asks everyone to confirm "ready" again), add or remove bots, and kick a player (who can't come back to that room). The host starts the game once everyone else is ready. Anyone can leave; if the host leaves, the next player becomes host; an empty lobby is deleted.

Reconnecting. The room belongs to your account, not the tab: reload, or open the link on another device and sign in, and you are back in your seat. If the connection drops, a "Connection lost, retrying" banner shows and the page keeps trying (1 s, 2 s, ... up to every 10 s).

What you see. Your own game in full (hand, draw pile, shop, deck). For the others: their scores, the hand types of their plays, what they bought and sold, round results, and whether they are online. Their move lists appear at the end. Toasts tell you when someone clears a round, buys a modifier, leaves or seems away, and a soft chime says when it's your turn.

Away players. A player is away when their page hasn't checked in for 30 seconds. If an away player keeps the game waiting for 90 seconds (in Draft and Rounds: it's their turn; in Race: they made no move), every other player gets a Remove button. A removed player, like one who leaves, stops: their run scores what it has, and the game goes on without them.

Idle rooms. A lobby with no activity for 30 minutes is deleted. A game with no move for 2 hours ends: it is saved like any finished game ("ended after a long pause"), everyone scoring what they have. A finished room stays open for 30 minutes (results, rematch) and is then deleted; the match itself stays in your history.

Results. When every run is over (or left), the server saves the match to every player's account: the players, final scores, rounds cleared, places, each player's move list, the mode, rules, seed and times. Your matches on the Online screen lists them with your record (played, won), the details and a Replay button that opens your run in the Replay tab. Rematch makes a new room with the same settings and bots (a new seed, or the same one); the other players get a button to join it. Online matches never count for the contest leaderboard.

Race #

Everyone plays the whole run at the same time, each on their own (like vs Bot, with people). You see the others' progress live (which round they are on); each of their rounds, with its score, plays and purchases, is revealed when you have finished the same round yourself. The game ends when every run is over.

Draft: the shared shop #

Turns go round by round, and the players share one shop. The exact rules:

  1. Two stages per round. Play: every player still in the run plays round r, in seat order. Shop: then the shop after round r is visited by one player at a time. A player's turn ends with the round's last play (play stage) or with "Done shopping" (next, shop stage).
  2. Visit order: lowest total first. The players who cleared round r visit the shop in increasing order of their total after round r. Ties go in seat order starting from seat r mod N (seat 1 first after round 1, seat 2 first after round 2, ...), so nobody is always first on a tie. Lowest first is a catch-up rule: whoever is behind gets the first pick, which keeps the match close and makes being ahead cost something.
  3. One pool of modifiers. A modifier that another player owns cannot be bought. It shows as "Drafted by <name>" in every shop where it is offered: for the players after the buyer in that round, and for everyone in later rounds (later rounds have their own queues; the name is blocked wherever it appears in them).
  4. Queue, window and reroll are as in solo, per player. Each player has their own position in the queue: the window shows 3 slots, a reroll ($2, +$1 each time) moves your window to the next 3 slots, and a slot you bought from stays empty. A drafted offer keeps its place in your window (it is not replaced by the next one), so a later pick may see fewer than 3 offers and has to decide whether a reroll is worth it.
  5. Selling returns a modifier to the pool. From that moment anyone may buy it again where it is offered. A player who goes out, leaves or is removed keeps their modifiers (they stay drafted).
  6. Levels, deck edits (remove, paint, promote), +discards and +hand size are private. Bots take part: they buy greedily from what is left.

Because the draft only forbids some purchases and never changes the window, every player's moves are legal in a solo game too: each move list is a valid output for the seed, checked by PokerRun.check and by python3 -m poker_run check.

Rounds #

Turns round by round without a shared shop: player 1 plays round 1 and the shop after it, then player 2 plays round 1 and its shop, and so on; then round

  1. A player who goes out (or leaves) is skipped.

The balanced ruleset #

An optional variant of the rules in statement.md. The contest is judged with the classic rules; balanced is an extra choice for playing and experimenting. Inputs are the same files for both rulesets (same generator, same targets); only the scoring and shop rules change.

Every difference from classic #

ClassicBalancedWhy
Full House45 base × 4 mult, +25 / +2 per level35 × 4, +20 / +2Full House made 63-69% of the example solvers' points: with known draws and discards it is available almost every round, and it got tricolor and twin_engine for free.
Flush35 × 4, +25 / +240 × 5, +30 / +2A flush can never use tricolor or twin_engine; now it is a real alternative (and painting cards, which is cheaper, feeds it).
Three of a Kind25 × 3, +20 / +230 × 3, +25 / +2Makes the 3-card hand worth playing, with short_hand.
tricolor$6, x2 mult when the scored cards have 3 or more suits$7, x2 mult when they have exactly 3 suitsA three of a kind always has 3 suits, so tricolor doubled every Full House: it was bought in ~80% of runs and ~40% of all points depended on it. Now a Full House qualifies only when its pair's suits are among its three of a kind's (about half the time); four suits never do. Choosing the cards matters.
short_hand$7, x2 mult when you play 3 or fewer cards$7, x3Never bought: x2 did not close the gap between 3-card and 5-card hands.
collector$6, +10 base per Straight / Straight Flush played, for good$5, +20Never bought. +20 makes a straight build worth it (forcing the tuned solver to buy it: +4% mean score; +25 was +27%, too strong).
momentum$6, +2 mult per play in a row of the same hand type$5, +3Never bought.
thin_deck$6, +1 mult per card your deck is below 52$6, +2Never bought; together with $2 removals a thin deck is a real build.
ace_dividend$4, +$2 per scored ace$3, +$3Never bought.
spare_hands$4, +$2 per unused discard when you clear a round$3, +$3Never bought; now a real trade-off between discarding and money.
savings$5, interest cap +$5 (only pays if you hold $30+)$4, interest $1 per $3 held (not per $5), cap +$5Holding $15 already pays the full $5, $30 pays $10: a banking strategy.
remove / paint / promote$3 a card$2 a cardDeck editing was rarely used (under one edit per run).
endallowed in any round once the target is metnot allowed in the last roundMoney is worthless after the last round, so ending it early can only throw points away; the last round is always played out.

Everything else is unchanged: the other hand types, the other modifiers (rose_tint, coal_seam, low_road, high_court, twin_engine, full_spread, last_call, mirror, odd_job, even_split), level prices, reroll, +discard and +hand size prices, slots, the shop window, round rewards and interest without savings.

Using it #

python3 -m poker_run check in.txt out.txt --rules balanced
python3 -m poker_run batch --seeds 1-20 --solver "python3 my_solver.py" --rules balanced
python3 -m poker_run versus --solvers "python3 a.py" "python3 b.py" --rules balanced

check, batch and versus take --rules classic|balanced (default classic). batch and versus also pass it to the solver in the environment variable POKER_RULES; the baseline and the example solvers in solvers/ read it (tuned.py then loads tuned_weights_balanced.json). A verdict scored with balanced rules has "rules": "balanced".

In code: Game(inst, rules="balanced") (Python, poker_run.sim) or new PokerRun.Game(inst, "balanced") / new PokerRun.Game(inst, { rules: "balanced" }) (JavaScript, web/engine.js); rules.get_ruleset(name) / PokerRun.getRuleset(name) return the tables, and game.rules is the ruleset a game plays by. The browser game has a Rules: Classic / Balanced switch in the top bar (remembered by the browser, and in the page's URL as &rules=balanced).

Measured effect #

python3 -m poker_run.tools.balance_stats runs solvers on many seeds and reports where the points come from. On the held-out seeds 2001-2100 (100 runs per cell; "beam (no prior)" is beam_search without the shop's built-in bonus for tricolor and last_call):

SolverMean scoreRounds clearedWonFull House share of points
baseline7,799 → 9,3963.02 → 3.240 → 026% → 15%
tuned86,244 → 69,5196.73 → 6.482 → 069% → 43%
beam_search94,803 → 82,8237.01 → 6.8314 → 765% → 36%
monte_carlo94,065 → 80,6907.06 → 6.7922 → 163% → 35%
beam (no prior)90,555 → 81,4606.96 → 6.8912 → 965% → 36%
  • Hand types (beam_search): Full House 65% → 36%, Straight 21% → 34%, Flush 5% → 14%, Two Pair 6% → 9%, Three of a Kind 0% → 4%.
  • Modifiers: 17 of the 18 are bought by tuned, beam_search or monte_carlo on some seeds (classic: 14 on these seeds). savings is the exception: these solvers never hold money long enough for it to pay. tricolor's share of all points drops from ~40% to ~32%.
  • Deck edits per run: beam_search 0.62 → 1.29, monte_carlo 0.73 → 1.59, tuned 0 → 2.67.
  • Slightly harder: about 0.2 fewer rounds cleared and 10-20% lower mean scores for the search solvers (they were built and tuned for classic).