pokertools-arena 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <rect width="64" height="64" rx="15" fill="#0b0d10"/>
3
+ <path d="M32 10c-5.3 8.6-16.2 13.2-16.2 23.2 0 5.1 3.8 9 8.7 9 2.3 0 4.6-.9 6.2-2.7-.4 5.8-2.2 9.3-7.1 12.8h16.8c-4.9-3.5-6.7-7-7.1-12.8 1.6 1.8 3.9 2.7 6.2 2.7 4.9 0 8.7-3.9 8.7-9C48.2 23.2 37.3 18.6 32 10Z" fill="#79b98e"/>
4
+ </svg>
Binary file
@@ -0,0 +1,190 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-label="pokertools-arena — AI poker benchmark where frontier models play Texas Hold'em">
2
+ <title>pokertools-arena — AI Poker Benchmark</title>
3
+ <desc>Frontier language models play no-limit Texas Hold'em at the same table. Every seat gets the same information and legal actions; one model wins.</desc>
4
+
5
+ <defs>
6
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
7
+ <stop offset="0" stop-color="#0d1a14"/>
8
+ <stop offset="0.48" stop-color="#070b0d"/>
9
+ <stop offset="1" stop-color="#040607"/>
10
+ </linearGradient>
11
+ <radialGradient id="glow" cx="0.75" cy="0.44" r="0.66">
12
+ <stop offset="0" stop-color="#2f8159" stop-opacity="0.46"/>
13
+ <stop offset="0.5" stop-color="#16432f" stop-opacity="0.16"/>
14
+ <stop offset="1" stop-color="#0a1a13" stop-opacity="0"/>
15
+ </radialGradient>
16
+ <radialGradient id="glow2" cx="0.08" cy="0.92" r="0.5">
17
+ <stop offset="0" stop-color="#1c6d4d" stop-opacity="0.22"/>
18
+ <stop offset="1" stop-color="#0a1a13" stop-opacity="0"/>
19
+ </radialGradient>
20
+ <radialGradient id="felt" cx="0.5" cy="0.34" r="0.8">
21
+ <stop offset="0" stop-color="#1e7150"/>
22
+ <stop offset="0.58" stop-color="#125039"/>
23
+ <stop offset="1" stop-color="#092a1e"/>
24
+ </radialGradient>
25
+ <linearGradient id="rail" x1="0" y1="0" x2="0" y2="1">
26
+ <stop offset="0" stop-color="#2a4a3a"/>
27
+ <stop offset="0.5" stop-color="#152a20"/>
28
+ <stop offset="1" stop-color="#0b1712"/>
29
+ </linearGradient>
30
+ <linearGradient id="card" x1="0" y1="0" x2="0" y2="1">
31
+ <stop offset="0" stop-color="#fdfefd"/>
32
+ <stop offset="1" stop-color="#d9e3dc"/>
33
+ </linearGradient>
34
+ <linearGradient id="mark" x1="0" y1="0" x2="0" y2="1">
35
+ <stop offset="0" stop-color="#193026"/>
36
+ <stop offset="1" stop-color="#0b1511"/>
37
+ </linearGradient>
38
+ <linearGradient id="rule" x1="0" y1="0" x2="1" y2="0">
39
+ <stop offset="0" stop-color="#8fd6a8" stop-opacity="0.5"/>
40
+ <stop offset="0.7" stop-color="#8fd6a8" stop-opacity="0.08"/>
41
+ <stop offset="1" stop-color="#8fd6a8" stop-opacity="0"/>
42
+ </linearGradient>
43
+ <pattern id="grid" width="46" height="46" patternUnits="userSpaceOnUse">
44
+ <path d="M46 0H0V46" fill="none" stroke="#9fd8b6" stroke-opacity="0.045" stroke-width="1"/>
45
+ </pattern>
46
+ </defs>
47
+
48
+ <!-- Background -->
49
+ <rect width="1200" height="630" fill="url(#bg)"/>
50
+ <rect width="1200" height="630" fill="url(#grid)"/>
51
+ <rect width="1200" height="630" fill="url(#glow)"/>
52
+ <rect width="1200" height="630" fill="url(#glow2)"/>
53
+
54
+ <!-- Accent bar -->
55
+ <rect x="0" y="0" width="1200" height="6" fill="url(#rule)"/>
56
+
57
+ <!-- ===================== Right: table illustration ===================== -->
58
+ <g>
59
+ <!-- table shadow -->
60
+ <ellipse cx="902" cy="352" rx="258" ry="178" fill="#000000" opacity="0.34"/>
61
+ <!-- rail -->
62
+ <ellipse cx="900" cy="322" rx="258" ry="176" fill="url(#rail)"/>
63
+ <ellipse cx="900" cy="322" rx="246" ry="166" fill="#0a1611" stroke="#264434" stroke-width="1.5"/>
64
+ <!-- felt -->
65
+ <ellipse cx="900" cy="322" rx="232" ry="152" fill="url(#felt)"/>
66
+ <ellipse cx="900" cy="322" rx="232" ry="152" fill="none" stroke="#000000" stroke-opacity="0.28" stroke-width="3"/>
67
+ <ellipse cx="900" cy="322" rx="206" ry="128" fill="none" stroke="#ffffff" stroke-opacity="0.09" stroke-width="1.5" stroke-dasharray="3 9"/>
68
+ <ellipse cx="900" cy="322" rx="176" ry="104" fill="none" stroke="#ffffff" stroke-opacity="0.045" stroke-width="1"/>
69
+
70
+ <!-- felt wordmark -->
71
+ <text x="900" y="394" text-anchor="middle" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="9.5" font-weight="800" letter-spacing="4.6" fill="#a7d9bd" fill-opacity="0.4">MODEL BENCHMARK TABLE</text>
72
+
73
+ <!-- pot -->
74
+ <g>
75
+ <rect x="829" y="236" width="142" height="36" rx="18" fill="#04100b" fill-opacity="0.62" stroke="#8fd6a8" stroke-opacity="0.26"/>
76
+ <text x="900" y="259" text-anchor="middle" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="800" letter-spacing="1.6" fill="#dff3e7">POT 24,400</text>
77
+ </g>
78
+
79
+ <!-- community cards: A K Q J 10 -->
80
+ <g>
81
+ <!-- A spade -->
82
+ <rect x="764" y="290" width="50" height="70" rx="7" fill="url(#card)" stroke="#b9c7be" stroke-width="1"/>
83
+ <text x="773" y="310" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="16" font-weight="800" fill="#1b2730">A</text>
84
+ <text x="789" y="345" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="26" fill="#1b2730">&#9824;</text>
85
+ <!-- K heart -->
86
+ <rect x="820" y="290" width="50" height="70" rx="7" fill="url(#card)" stroke="#b9c7be" stroke-width="1"/>
87
+ <text x="829" y="310" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="16" font-weight="800" fill="#b23a3a">K</text>
88
+ <text x="845" y="345" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="26" fill="#b23a3a">&#9829;</text>
89
+ <!-- Q diamond -->
90
+ <rect x="876" y="290" width="50" height="70" rx="7" fill="url(#card)" stroke="#b9c7be" stroke-width="1"/>
91
+ <text x="885" y="310" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="16" font-weight="800" fill="#b23a3a">Q</text>
92
+ <text x="901" y="345" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="24" fill="#b23a3a">&#9830;</text>
93
+ <!-- J club -->
94
+ <rect x="932" y="290" width="50" height="70" rx="7" fill="url(#card)" stroke="#b9c7be" stroke-width="1"/>
95
+ <text x="941" y="310" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="16" font-weight="800" fill="#1b2730">J</text>
96
+ <text x="957" y="345" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="25" fill="#1b2730">&#9827;</text>
97
+ <!-- 10 spade -->
98
+ <rect x="988" y="290" width="50" height="70" rx="7" fill="url(#card)" stroke="#b9c7be" stroke-width="1"/>
99
+ <text x="994" y="310" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="15" font-weight="800" fill="#1b2730">10</text>
100
+ <text x="1013" y="345" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="25" fill="#1b2730">&#9824;</text>
101
+ </g>
102
+
103
+ <!-- seats -->
104
+ <g font-family="Inter, Helvetica Neue, Arial, sans-serif">
105
+ <!-- top -->
106
+ <g>
107
+ <rect x="830" y="140" width="140" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.42"/>
108
+ <circle cx="848" cy="157" r="4" fill="#c6ff57"/>
109
+ <text x="908" y="162" text-anchor="middle" font-size="13" font-weight="700" fill="#dcefe3">GPT-5</text>
110
+ </g>
111
+ <!-- upper-left -->
112
+ <g>
113
+ <rect x="612" y="236" width="124" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.24"/>
114
+ <text x="674" y="258" text-anchor="middle" font-size="13" font-weight="700" fill="#bcd6c6">Claude</text>
115
+ </g>
116
+ <!-- upper-right -->
117
+ <g>
118
+ <rect x="1064" y="236" width="124" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.24"/>
119
+ <text x="1126" y="258" text-anchor="middle" font-size="13" font-weight="700" fill="#bcd6c6">Gemini</text>
120
+ </g>
121
+ <!-- lower-left -->
122
+ <g>
123
+ <rect x="624" y="402" width="112" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.24"/>
124
+ <text x="680" y="424" text-anchor="middle" font-size="13" font-weight="700" fill="#bcd6c6">Qwen</text>
125
+ </g>
126
+ <!-- lower-right -->
127
+ <g>
128
+ <rect x="1064" y="402" width="112" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.24"/>
129
+ <text x="1120" y="424" text-anchor="middle" font-size="13" font-weight="700" fill="#bcd6c6">Grok</text>
130
+ </g>
131
+ <!-- bottom -->
132
+ <g>
133
+ <rect x="838" y="472" width="124" height="34" rx="17" fill="#0c1813" stroke="#8fd6a8" stroke-opacity="0.42"/>
134
+ <circle cx="856" cy="489" r="4" fill="#c6ff57"/>
135
+ <text x="908" y="494" text-anchor="middle" font-size="13" font-weight="700" fill="#dcefe3">Jev 1.13</text>
136
+ </g>
137
+ </g>
138
+ </g>
139
+
140
+ <!-- ===================== Left: copy ===================== -->
141
+ <g font-family="Inter, Helvetica Neue, Arial, sans-serif">
142
+ <!-- brand mark -->
143
+ <rect x="72" y="62" width="66" height="66" rx="17" fill="url(#mark)" stroke="#8fd6a8" stroke-opacity="0.4" stroke-width="1.2"/>
144
+ <g transform="translate(73,63)">
145
+ <path d="M32 10c-5.3 8.6-16.2 13.2-16.2 23.2 0 5.1 3.8 9 8.7 9 2.3 0 4.6-.9 6.2-2.7-.4 5.8-2.2 9.3-7.1 12.8h16.8c-4.9-3.5-6.7-7-7.1-12.8 1.6 1.8 3.9 2.7 6.2 2.7 4.9 0 8.7-3.9 8.7-9C48.2 23.2 37.3 18.6 32 10Z" fill="#8fd6a8"/>
146
+ </g>
147
+ <!-- brand text -->
148
+ <text x="156" y="96" font-size="27" font-weight="800" letter-spacing="-0.6" fill="#f0f6f1">pokertools-arena</text>
149
+ <text x="157" y="120" font-size="11" font-weight="800" letter-spacing="3.6" fill="#7fc59b">AI POKER BENCHMARK</text>
150
+
151
+ <!-- headline -->
152
+ <text x="72" y="252" font-size="55" font-weight="800" letter-spacing="-1.6" fill="#f6faf7">Watch frontier models</text>
153
+ <text x="72" y="314" font-size="55" font-weight="800" letter-spacing="-1.6" fill="#f6faf7">play Texas Hold&#8217;em.</text>
154
+
155
+ <!-- supporting line -->
156
+ <text x="73" y="362" font-size="20" font-weight="500" fill="#9fb0a7">Same cards. Same legal actions. One model wins.</text>
157
+
158
+ <!-- chips -->
159
+ <g>
160
+ <rect x="72" y="404" width="185" height="42" rx="21" fill="#8fd6a8" fill-opacity="0.06" stroke="#8fd6a8" stroke-opacity="0.24"/>
161
+ <text x="164" y="430" text-anchor="middle" font-size="14" font-weight="700" fill="#bfe6cd">OpenAI-compatible</text>
162
+
163
+ <rect x="271" y="404" width="128" height="42" rx="21" fill="#8fd6a8" fill-opacity="0.06" stroke="#8fd6a8" stroke-opacity="0.24"/>
164
+ <text x="335" y="430" text-anchor="middle" font-size="14" font-weight="700" fill="#bfe6cd">OpenRouter</text>
165
+
166
+ <rect x="413" y="404" width="152" height="42" rx="21" fill="#8fd6a8" fill-opacity="0.06" stroke="#8fd6a8" stroke-opacity="0.24"/>
167
+ <text x="489" y="430" text-anchor="middle" font-size="14" font-weight="700" fill="#bfe6cd">Jev Decisions</text>
168
+ </g>
169
+
170
+ <!-- divider -->
171
+ <rect x="72" y="494" width="520" height="1" fill="#ffffff" fill-opacity="0.1"/>
172
+
173
+ <!-- GitHub repository badge -->
174
+ <g>
175
+ <rect x="72" y="512" width="318" height="46" rx="23" fill="#8fd6a8" fill-opacity="0.07" stroke="#8fd6a8" stroke-opacity="0.32"/>
176
+ <g transform="translate(90,522) scale(1.625)">
177
+ <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" fill="#cdeedb"/>
178
+ </g>
179
+ <text x="128" y="541" font-size="16.5" font-weight="700" fill="#e2f3e9">github.com/pokertools-arena</text>
180
+ </g>
181
+
182
+ <!-- Website URL -->
183
+ <g>
184
+ <circle cx="87" cy="579" r="10.5" fill="none" stroke="#8fd6a8" stroke-width="1.9"/>
185
+ <ellipse cx="87" cy="579" rx="4.6" ry="10.5" fill="none" stroke="#8fd6a8" stroke-width="1.4"/>
186
+ <path d="M76.5 579h21" stroke="#8fd6a8" stroke-width="1.6"/>
187
+ <text x="108" y="586" font-size="20" font-weight="800" letter-spacing="-0.3" fill="#f2f8f4">pokertools-arena.github.io</text>
188
+ </g>
189
+ </g>
190
+ </svg>
@@ -0,0 +1,108 @@
1
+ const POLICY = Object.freeze({
2
+ private: 'hero hole cards only',
3
+ public: 'board, pot, blinds, stacks, positions, current-hand actions, recent public hand history, public player statistics, and legal actions',
4
+ excluded: 'opponent hole cards, other agents reasoning, model outputs, API/provider metadata, hidden deck state, and future cards',
5
+ });
6
+
7
+ const OBJECTIVE = 'Choose exactly one legal action that best maximizes tournament chip EV from the supplied state. Use only the information in this state and only an actionId present in legalActions.';
8
+
9
+ function action(id, type, description, amount = null) { return { id, type, amount, description }; }
10
+ // Production serializeForAgent always includes this derived object. The legacy
11
+ // fixtures omitted it, which is a real state-serialization confound: models that
12
+ // rely on betting.toCall can misread an otherwise free check. Derive it from the
13
+ // merged hero/opponent state so every sanity scenario is production-shaped.
14
+ function withBetting(state) {
15
+ const heroBet = Number(state.hero?.currentBet) || 0;
16
+ const highestBet = Math.max(heroBet, ...(state.opponents ?? []).map(o => Number(o.currentBet) || 0));
17
+ const toCall = Math.max(0, highestBet - heroBet);
18
+ const stack = Number(state.hero?.stack) || 0;
19
+ return {
20
+ ...state,
21
+ betting: {
22
+ highestBet,
23
+ heroCurrentBet: heroBet,
24
+ toCall,
25
+ effectiveCall: Math.min(stack, toCall),
26
+ stackBehind: stack,
27
+ facingAllInCall: stack > 0 && stack <= toCall,
28
+ },
29
+ };
30
+ }
31
+ function baseState(overrides = {}) {
32
+ return withBetting({
33
+ contextVersion: 3,
34
+ informationPolicy: POLICY,
35
+ objective: OBJECTIVE,
36
+ game: 'No-Limit Texas Holdem tournament',
37
+ memoryPolicy: {
38
+ currentHand: 'all public model actions in the current hand before this decision',
39
+ recentHands: 'last 8 completed public hands',
40
+ publicPlayerStats: 'deterministic aggregates from completed hands before the current hand; identical public dataset for every seat',
41
+ },
42
+ tournament: { handNumber: 50, blindLevel: 4, playersRemaining: 2, startingPlayers: 2 },
43
+ blinds: { smallBlind: 50, bigBlind: 100, ante: 0 },
44
+ hero: { id: 'benchmark-hero', name: 'Benchmark Hero', seat: 1, position: 'BTN/SB', stack: 10000, stackBB: 100, cards: ['As', 'Kd'], currentBet: 0 },
45
+ board: [], street: 'PREFLOP', pot: 150, buttonSeat: 1,
46
+ actionHistory: [], recentHands: [],
47
+ publicPlayerStats: [
48
+ { playerId: 'benchmark-hero', playerName: 'Benchmark Hero', sampleHands: 40, vpipPct: 0.30, pfrPct: 0.22, aggressionPct: 0.41, foldPct: 0.28, callPct: 0.24, checkPct: 0.18, wins: 10 },
49
+ { playerId: 'benchmark-villain', playerName: 'Benchmark Villain', sampleHands: 40, vpipPct: 0.31, pfrPct: 0.21, aggressionPct: 0.40, foldPct: 0.29, callPct: 0.24, checkPct: 0.17, wins: 9 },
50
+ ],
51
+ opponents: [{ seat: 2, id: 'benchmark-villain', name: 'Benchmark Villain', stack: 10000, stackBB: 100, currentBet: 100, status: 'ACTIVE', cards: [] }],
52
+ legalActions: [],
53
+ ...overrides,
54
+ });
55
+ }
56
+
57
+ export const DECISION_SANITY_SCENARIOS = Object.freeze([
58
+ {
59
+ id: 'free-check-dominates-fold', title: 'Free check dominates folding', category: 'Dominance',
60
+ expectedTypes: ['CHECK'], note: 'With no bet to call, checking preserves every future outcome while folding gives up immediately.',
61
+ state: baseState({ street: 'FLOP', hero: { id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:5000,stackBB:50,cards:['7c','2d'],currentBet:0 }, board:['As','Kd','Qh'], pot:300, opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:5000,stackBB:50,currentBet:0,status:'ACTIVE',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CHECK','Check')] }),
62
+ },
63
+ {
64
+ id: 'board-royal-call', title: 'Board royal flush: call a small bet', category: 'Guaranteed tie',
65
+ expectedTypes: ['CALL'], note: 'The board itself is a royal flush, so folding gives up a guaranteed share of the pot.',
66
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:4900,stackBB:49,cards:['2c','3d'],currentBet:0}, board:['As','Ks','Qs','Js','Ts'], pot:1100, actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:100,description:'Bet 100 (1 BB)'},potBefore:1000}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:4900,stackBB:49,currentBet:100,status:'ACTIVE',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 100')] }),
67
+ },
68
+ {
69
+ id: 'royal-nuts-call', title: 'Royal flush facing a shove', category: 'Nuts',
70
+ expectedTypes: ['CALL'], note: 'Hero holds the unbeatable royal flush and must not fold to an all-in bet.',
71
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:4000,stackBB:40,cards:['As','Ks'],currentBet:0}, board:['Qs','Js','Ts','2d','3c'], pot:6000, actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:4000,description:'All-in 4000 (40 BB)'},potBefore:2000}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:0,stackBB:0,currentBet:4000,status:'ALL_IN',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 4000')] }),
72
+ },
73
+ {
74
+ id: 'straight-flush-call', title: 'Straight flush facing a river bet', category: 'Nuts',
75
+ expectedTypes: ['CALL'], note: 'Hero has a nine-high straight flush; folding to the available call is an obvious sanity failure.',
76
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:7000,stackBB:70,cards:['9s','8s'],currentBet:0}, board:['7s','6s','5s','Kd','2c'], pot:3500, opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:5500,stackBB:55,currentBet:1500,status:'ACTIVE',cards:[]}], actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:1500,description:'Bet 1500 (15 BB)'},potBefore:2000}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 1500')] }),
77
+ },
78
+ {
79
+ id: 'aces-vs-short-shove', title: 'Pocket aces versus a short-stack shove', category: 'Preflop',
80
+ expectedTypes: ['CALL'], note: 'With only fold or call available, folding AA to a short all-in is a basic strategic failure.',
81
+ state: baseState({ hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:9600,stackBB:96,cards:['As','Ah'],currentBet:100}, pot:2550, actionHistory:[{street:'PREFLOP',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'RAISE',amount:2500,description:'All-in to 2500 (25 BB)'},potBefore:150}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:0,stackBB:0,currentBet:2500,status:'ALL_IN',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 2400')] }),
82
+ },
83
+ {
84
+ id: 'seven-deuce-vs-shove', title: 'Seven-deuce offsuit versus a 100 BB shove', category: 'Preflop',
85
+ expectedTypes: ['FOLD'], note: 'The weakest starting hand facing an enormous shove with little dead money should fold.',
86
+ state: baseState({ hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:9900,stackBB:99,cards:['7c','2d'],currentBet:100}, pot:10150, actionHistory:[{street:'PREFLOP',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'RAISE',amount:10000,description:'All-in to 10000 (100 BB)'},potBefore:150}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:0,stackBB:0,currentBet:10000,status:'ALL_IN',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 9900')] }),
87
+ },
88
+ {
89
+ id: 'river-air-overbet', title: 'Seven-high versus a 10× pot river shove', category: 'River discipline',
90
+ expectedTypes: ['FOLD'], note: 'Hero has seven-high with no meaningful showdown value and faces an extreme overbet.',
91
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:10000,stackBB:100,cards:['7d','2d'],currentBet:0}, board:['Ac','Kh','Qh','9s','3c'], pot:1100, actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:10000,description:'All-in 10000 (100 BB)'},potBefore:1000}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:0,stackBB:0,currentBet:10000,status:'ALL_IN',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 10000')] }),
92
+ },
93
+ {
94
+ id: 'nut-flush-tiny-bet', title: 'Nut flush versus a tiny river bet', category: 'Pot odds',
95
+ expectedTypes: ['CALL'], note: 'Hero has the nut flush and faces a tiny bet; folding is an obvious sanity failure.',
96
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:9900,stackBB:99,cards:['As','8s'],currentBet:0}, board:['Ks','Qs','4s','2h','3d'], pot:1100, actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:100,description:'Bet 100 (1 BB)'},potBefore:1000}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:9900,stackBB:99,currentBet:100,status:'ACTIVE',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 100')] }),
97
+ },
98
+ {
99
+ id: 'kings-vs-open', title: 'Pocket kings versus a normal open', category: 'Preflop',
100
+ expectedTypes: ['CALL','RAISE'], note: 'Continuing with KK is expected; the suite accepts either calling or raising and rejects folding.',
101
+ state: baseState({ hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:9900,stackBB:99,cards:['Kc','Kd'],currentBet:100}, pot:450, actionHistory:[{street:'PREFLOP',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'RAISE',amount:300,description:'Raise to 300 (3 BB)'},potBefore:150}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:9700,stackBB:97,currentBet:300,status:'ACTIVE',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 200'),action('A2','RAISE','Raise to 900 (9 BB)',900),action('A3','RAISE','Raise to 1200 (12 BB)',1200)] }),
102
+ },
103
+ {
104
+ id: 'top-pair-tiny-river-bet', title: 'Top pair versus a 5% pot river bet', category: 'Pot odds',
105
+ expectedTypes: ['CALL'], note: 'Top pair facing a tiny river bet gets overwhelming pot odds; folding is a basic sanity failure.',
106
+ state: baseState({ street:'RIVER', hero:{id:'benchmark-hero',name:'Benchmark Hero',seat:1,position:'BB',stack:9950,stackBB:99.5,cards:['Ah','Jd'],currentBet:0}, board:['As','8c','6d','4h','2c'], pot:1050, actionHistory:[{street:'RIVER',playerName:'Benchmark Villain',position:'BTN/SB',action:{type:'BET',amount:50,description:'Bet 50 (0.5 BB)'},potBefore:1000}], opponents:[{seat:2,id:'benchmark-villain',name:'Benchmark Villain',stack:9950,stackBB:99.5,currentBet:50,status:'ACTIVE',cards:[]}], legalActions:[action('A0','FOLD','Fold'),action('A1','CALL','Call 50')] }),
107
+ },
108
+ ]);
@@ -0,0 +1,2 @@
1
+ // Overridden dynamically by the local launcher when a supported .env file is present.
2
+ window.__POKERTOOLS_ENV__ = window.__POKERTOOLS_ENV__ || null;