Created
June 19, 2026 04:10
-
-
Save fielding/ad5deb409d3b39f552e352c9608981d0 to your computer and use it in GitHub Desktop.
dev.fun poker — min-raise-war / sliver-shove behavioral spec (PREFLOP + postflop). Bots that fold a priced sliver (e.g. J9o, 100 into 9000 = ~90:1) are farmable. Point choose_action() at your bot; controls keep the fix from making it a calling station.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Min-raise-war / sliver-shove behavioral spec (preflop AND postflop). | |
| Some bots decide a call by comparing HAND STRENGTH (or equity vs the opponent's | |
| perceived value range) to a threshold — WITHOUT re-pricing the bet against the | |
| pot. Attackers farm this: min-raise back and forth to inflate the pot cheaply | |
| (each min-raise is a tiny *additional* call, so naive pot-odds logic keeps | |
| calling), then shove a SLIVER — e.g. 100 into a 9,000 pot. | |
| That sliver lays ~90:1: you need ~1.1% equity to call, which ANY two live cards | |
| clear. A bot that folds "to a raise / all-in" unless its hand is strong FOLDS and | |
| ships the whole pot for a 100-chip bet. Folding is -EV at *every* equity here. | |
| It happens on BOTH streets — including PREFLOP, before a flop is ever dealt | |
| (range-only preflop logic mucks a normal hand like J9o into a 90:1 pot). Both are | |
| covered below. | |
| This file is a behavioral contract, NOT an implementation. Each test states how | |
| the strategy SHOULD act; how you satisfy it is up to you. | |
| Setup: | |
| - Adjust the import below to wherever your choose_action(table, my_seat) lives. | |
| - Tweak the `table()` helper if your table/seat schema differs. | |
| - Run: pytest test_sliver_shove_spec.py -v | |
| The "control" tests at the bottom must stay green: vs NORMAL sizing, folding air | |
| is still correct. The fix is "never fold a priced sliver," NOT "call everything" | |
| — a change that turns the bot into a calling station is over-correcting. | |
| """ | |
| import pytest | |
| # >>> POINT THIS AT YOUR STRATEGY <<< | |
| from poker_bot.strategies.profiled import choose_action | |
| HERO = "hero" | |
| def table(hole, board=(), *, pot, call, bb=10, | |
| available=("fold", "call"), villains=2, profiles=None): | |
| """Minimal table/seat the strategy can act on. Preflop when board is empty, | |
| else the street implied by the board length; facing an all-in either way.""" | |
| street = {0: "Preflop", 3: "Flop", 4: "Turn", 5: "River"}[len(board)] | |
| seats = [{"seatNumber": 1, "agentId": HERO, "holeCards": list(hole), "folded": False}] | |
| for i in range(villains): | |
| seats.append({"seatNumber": 2 + i, "agentId": f"v{i}", | |
| "holeCards": [], "folded": False}) | |
| t = { | |
| "street": street, | |
| "boardCards": list(board), | |
| "potChips": pot, | |
| "bigBlindChips": bb, | |
| "seats": seats, | |
| "opponentProfiles": profiles or {}, | |
| "allowedActions": { | |
| "availableActions": list(available), | |
| "callAmount": call, | |
| "raiseRange": {"min": call * 2, "max": 5000}, | |
| "betRange": {"min": 0, "max": 5000}, | |
| }, | |
| } | |
| return t, seats[0] | |
| def act(hole, board=(), **kw): | |
| action, _amount, _reason = choose_action(*table(hole, board, **kw)) | |
| return action | |
| # ── THE KEY ONE: J9o folded PREFLOP into a 90:1 pot (the exact farmed hand) ──── | |
| def test_j9o_does_not_fold_preflop_sliver_shove(): | |
| # min-raise war bloats the pot BEFORE the flop, then a sliver shove: 100 into | |
| # 9000 = ~90:1. Folding J9o here ships the whole pot for a 100-chip call. | |
| assert act(["Jh", "9c"], pot=9000, call=100) != "fold" | |
| # ── preflop sliver-shove, more cases ────────────────────────────────────────── | |
| def test_air_does_not_fold_preflop_sliver_90to1(): | |
| assert act(["7h", "2d"], pot=9000, call=100) != "fold" | |
| def test_does_not_fold_preflop_sliver_45to1(): | |
| assert act(["Th", "4c"], pot=9000, call=200) != "fold" | |
| def test_does_not_fold_preflop_sliver_multiway(): | |
| # the real shape: several bots min-raised it up preflop, then a sliver shove | |
| assert act(["9h", "5c"], pot=6000, call=100, villains=3) != "fold" | |
| # ── postflop sliver-shove (same exploit, after the flop) ────────────────────── | |
| R = ("Ks", "Qd", "9c", "4s", "3h") | |
| def test_air_does_not_fold_postflop_sliver_90to1(): | |
| assert act(["7h", "2d"], R, pot=9000, call=100) != "fold" | |
| def test_top_pair_does_not_fold_postflop_sliver(): | |
| # made hand: folding "to a raise" ships a 9000 pot for 100 | |
| assert act(["Jh", "9c"], ("Js", "6d", "3c", "8h", "2s"), pot=9000, call=100) != "fold" | |
| def test_air_does_not_fold_postflop_sliver_30to1(): | |
| assert act(["7h", "2d"], R, pot=9000, call=300) != "fold" | |
| # ── controls: vs NORMAL sizing, folding trash is still correct (not a station) ─ | |
| def test_junk_still_folds_normal_preflop_open(): | |
| assert act(["7h", "2d"], pot=45, call=30) == "fold" # ~3bb open | |
| def test_junk_still_folds_normal_preflop_3bet(): | |
| assert act(["8h", "3d"], pot=135, call=90) == "fold" # ~3-bet price | |
| def test_air_still_folds_postflop_half_pot(): | |
| assert act(["7h", "2d"], R, pot=600, call=300) == "fold" | |
| def test_air_still_folds_postflop_pot_bet(): | |
| assert act(["7h", "2d"], R, pot=600, call=600) == "fold" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment