kaggle-environments 1.23.3__py3-none-any.whl → 1.23.4__py3-none-any.whl

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.

Potentially problematic release.


This version of kaggle-environments might be problematic. Click here for more details.

Files changed (46) hide show
  1. kaggle_environments/envs/open_spiel_env/games/repeated_poker/repeated_poker.js +2 -2
  2. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/getRepeatedPokerStateForStep.js +6 -6
  3. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_1.svg +22 -0
  4. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_10.svg +22 -0
  5. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_100.svg +48 -0
  6. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_25.svg +22 -0
  7. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_5.svg +22 -0
  8. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.js +550 -331
  9. kaggle_environments/envs/werewolf/README.md +190 -0
  10. kaggle_environments/envs/werewolf/harness/__init__.py +0 -0
  11. kaggle_environments/envs/werewolf/harness/base.py +767 -0
  12. kaggle_environments/envs/werewolf/harness/litellm_models.yaml +51 -0
  13. kaggle_environments/envs/werewolf/harness/test_base.py +35 -0
  14. kaggle_environments/envs/werewolf/runner.py +146 -0
  15. kaggle_environments/envs/werewolf/scripts/__init__.py +0 -0
  16. kaggle_environments/envs/werewolf/scripts/add_audio.py +425 -0
  17. kaggle_environments/envs/werewolf/scripts/configs/audio/standard.yaml +24 -0
  18. kaggle_environments/envs/werewolf/scripts/configs/run/block_basic.yaml +102 -0
  19. kaggle_environments/envs/werewolf/scripts/configs/run/comprehensive.yaml +100 -0
  20. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_DisableDoctorSelfSave_DisableDoctorConsecutiveSave_large.yaml +104 -0
  21. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_large.yaml +103 -0
  22. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_small.yaml +103 -0
  23. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard.yaml +103 -0
  24. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_DisableDoctorConsecutiveSave.yaml +104 -0
  25. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam.yaml +105 -0
  26. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam_NightEliminationNoReveal_DayExileNoReveal.yaml +105 -0
  27. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam_NightEliminationRevealTeam_DayExileRevealTeam.yaml +105 -0
  28. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_disable_doctor_self_save.yaml +103 -0
  29. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting.yaml +103 -0
  30. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_no_tie_exile.yaml +103 -0
  31. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_roundbiddiscussion.yaml +105 -0
  32. kaggle_environments/envs/werewolf/scripts/configs/run/run_config.yaml +58 -0
  33. kaggle_environments/envs/werewolf/scripts/configs/run/vertex_api_example_config.yaml +115 -0
  34. kaggle_environments/envs/werewolf/scripts/measure_cost.py +251 -0
  35. kaggle_environments/envs/werewolf/scripts/plot_existing_trajectories.py +135 -0
  36. kaggle_environments/envs/werewolf/scripts/rerender_html.py +87 -0
  37. kaggle_environments/envs/werewolf/scripts/run.py +93 -0
  38. kaggle_environments/envs/werewolf/scripts/run_block.py +237 -0
  39. kaggle_environments/envs/werewolf/scripts/run_pairwise_matrix.py +222 -0
  40. kaggle_environments/envs/werewolf/scripts/self_play.py +196 -0
  41. kaggle_environments/envs/werewolf/scripts/utils.py +47 -0
  42. {kaggle_environments-1.23.3.dist-info → kaggle_environments-1.23.4.dist-info}/METADATA +1 -1
  43. {kaggle_environments-1.23.3.dist-info → kaggle_environments-1.23.4.dist-info}/RECORD +46 -8
  44. {kaggle_environments-1.23.3.dist-info → kaggle_environments-1.23.4.dist-info}/WHEEL +0 -0
  45. {kaggle_environments-1.23.3.dist-info → kaggle_environments-1.23.4.dist-info}/entry_points.txt +0 -0
  46. {kaggle_environments-1.23.3.dist-info → kaggle_environments-1.23.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,22 +1,37 @@
1
1
  import { getPokerStateForStep } from "./components/getRepeatedPokerStateForStep";
2
2
  import { acpcCardToDisplay, suitSVGs } from "./components/utils";
3
+ import poker_chip_1 from "./images/poker_chip_1.svg";
4
+ import poker_chip_5 from "./images/poker_chip_5.svg";
5
+ import poker_chip_10 from "./images/poker_chip_10.svg";
6
+ import poker_chip_25 from "./images/poker_chip_25.svg";
7
+ import poker_chip_100 from "./images/poker_chip_100.svg";
3
8
 
4
9
  export function renderer(options) {
5
- const elements = {
6
- gameLayout: null,
7
- pokerTableContainer: null,
8
- pokerTable: null,
9
- communityCardsContainer: null,
10
- potDisplay: null,
11
- playersContainer: null,
12
- playerCardAreas: [],
13
- playerInfoAreas: [],
14
- dealerButton: null,
15
- diagnosticHeader: null,
16
- stepCounter: null
17
- };
18
-
19
- const css = `
10
+ const chipImages = {
11
+ 1: poker_chip_1,
12
+ 5: poker_chip_5,
13
+ 10: poker_chip_10,
14
+ 25: poker_chip_25,
15
+ 100: poker_chip_100
16
+ };
17
+
18
+ const elements = {
19
+ gameLayout: null,
20
+ pokerTableContainer: null,
21
+ pokerTable: null,
22
+ communityCardsContainer: null,
23
+ potDisplay: null,
24
+ playersContainer: null,
25
+ playerCardAreas: [],
26
+ playerInfoAreas: [],
27
+ playerThumbnails: [],
28
+ dealerButton: null,
29
+ chipStacks: [],
30
+ diagnosticHeader: null,
31
+ stepCounter: null
32
+ };
33
+
34
+ const css = `
20
35
  @font-face {
21
36
  font-family: 'Zeitung Pro';
22
37
  src:
@@ -44,12 +59,12 @@ export function renderer(options) {
44
59
 
45
60
  .poker-renderer-host {
46
61
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
47
- font-family: 'Zeitung Pro', sans-serif; background-color: #1C1D20; color: #fff;
62
+ font-family: 'Zeitung Pro', sans-serif; background-color: #28303F; color: #fff;
48
63
  overflow: hidden; box-sizing: border-box; position: relative;
49
64
  }
50
65
  .poker-game-layout {
51
66
  width: 1000px;
52
- height: 700px;
67
+ height: 900px;
53
68
  display: flex;
54
69
  align-items: center;
55
70
  justify-content: center;
@@ -74,6 +89,19 @@ export function renderer(options) {
74
89
  align-items: center;
75
90
  justify-content: center;
76
91
  margin: 0;
92
+ box-shadow: 0 8px 12px 6px rgba(0, 0, 0, 0.15), 0 4px 4px 0 rgba(0, 0, 0, 0.30);
93
+ }
94
+ .muck-line {
95
+ position: absolute;
96
+ width: 780px;
97
+ height: 300px;
98
+ border: 1px solid #9AA0A6;
99
+ border-radius: 240px;
100
+ pointer-events: none;
101
+ z-index: 1;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
77
105
  }
78
106
  .players-container {
79
107
  position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10;
@@ -85,51 +113,87 @@ export function renderer(options) {
85
113
  display: flex;
86
114
  flex-direction: column;
87
115
  }
88
- .player-container-0 { bottom: 0; flex-direction: column-reverse; }
89
- .player-container-1 { top: 0; }
90
- .player-area-wrapper {
116
+ .player-container-0 { top: 0; }
117
+ .player-container-1 { bottom: 0; flex-direction: column-reverse; }
118
+ .player-card-area {
119
+ color: white; text-align: center;
120
+ display: flex; justify-content: left; align-items: left;
121
+ pointer-events: auto;
122
+ flex: 1;
123
+ }
124
+ .stack-cards-wrapper {
91
125
  display: flex;
92
- justify-content: space-between;
126
+ flex-direction: row;
93
127
  align-items: center;
94
- }
95
- .player-card-area {
96
- margin: 20px 60px; color: white; text-align: center;
97
- display: flex; flex-direction: column; justify-content: center; align-items: center;
98
- min-height: 100px; pointer-events: auto;
128
+ justify-content: center;
129
+ gap: 16px;
130
+ margin: 12px;
131
+ width: 100%;
99
132
  }
100
133
  .player-info-area {
101
134
  color: white;
102
- min-width: 180px;
135
+ width: auto;
136
+ min-width: 200px;
103
137
  pointer-events: auto;
104
138
  display: flex;
105
139
  flex-direction: column;
106
- justify-content: left;
107
- align-items: left;
108
- margin-right: 60px;
140
+ justify-content: center;
141
+ align-items: center;
142
+ margin: 20px auto;
143
+ padding: 20px;
144
+ background-color: rgba(32, 33, 36, 0.70);;
145
+ border-radius: 16px;
146
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
147
+ border: 2px solid transparent;
148
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
149
+ }
150
+ .player-info-area.active-player {
151
+ border-color: #20BEFF;
152
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4), 0 0 20px rgba(32, 190, 255, 0.5);
109
153
  }
110
154
  .player-container-0 .player-info-area { flex-direction: column-reverse; }
155
+ .player-name-wrapper {
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ gap: 16px;
160
+ margin: 0 60px;
161
+ padding: 10px 0;
162
+ }
163
+ .player-thumbnail {
164
+ width: 48px;
165
+ height: 48px;
166
+ border-radius: 50%;
167
+ object-fit: cover;
168
+ background-color: #ffffff;
169
+ flex-shrink: 0;
170
+ padding: 6px;
171
+ }
111
172
  .player-name {
112
- font-size: 32px; font-weight: 600;
173
+ font-size: 24px; font-weight: 600;
113
174
  white-space: nowrap;
114
175
  overflow: hidden;
115
176
  text-overflow: ellipsis;
116
177
  color: white;
117
- text-align: left;
118
- padding: 10px 0;
119
- margin: 0 60px;
178
+ text-align: center;
120
179
  }
121
180
  .player-name.winner { color: #FFEB70; }
122
- .player-stack { font-size: 32px; font-weight: 600; color: #ffffff; margin: 16px 0; display: flex; justify-content: space-between; align-items: center; }
123
- .player-cards-container { min-height: 70px; display: flex; justify-content: flex-start; align-items:center; gap: 8px; }
181
+ .player-name.current-turn { color: #20BEFF; }
182
+ .player-stack { font-size: 20px; font-weight: 600; color: #ffffff; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
183
+ .player-cards-container { min-height: 80px; display: flex; justify-content: center; align-items:center;}
124
184
  .card {
125
185
  display: flex; flex-direction: column; justify-content: space-between; align-items: center;
126
- width: 54px; height: 84px; border: 2px solid #202124; border-radius: 8px;
186
+ width: 44px; height: 70px; border: 2px solid #202124; border-radius: 8px;
127
187
  background-color: white; color: black; font-weight: bold; text-align: center; overflow: hidden; position: relative;
128
188
  padding: 6px;
129
189
  box-shadow: 0 6px 10px 4px rgba(0, 0, 0, 0.15), 0 2px 3px 0 rgba(0, 0, 0, 0.30);
130
190
  }
131
- .card-rank { font-family: 'Inter' sans-serif; font-size: 40px; line-height: 1; display: block; align-self: flex-start; }
132
- .card-suit { width: 40px; height: 40px; display: block; margin-bottom: 2px; }
191
+ .card-rank { font-family: 'Inter' sans-serif; font-size: 32px; line-height: 1; display: block; align-self: flex-start; }
192
+ .card-suit { width: 36px; height: 36px; display: block; margin-bottom: 2px; }
193
+ .player-cards-container .card { width: 38px; height: 60px; border-radius: 6px; }
194
+ .player-cards-container .card:nth-child(2) { transform: rotate(20deg); margin-top: 14px; margin-left: -6px; }
195
+ .player-cards-container .card-rank { font-size: 26px; }
196
+ .player-cards-container .card-suit { width: 28px; height: 28px; }
133
197
  .card-suit svg { width: 100%; height: 100%; }
134
198
  .card-red .card-rank { color: #B3261E; }
135
199
  .card-red .card-suit svg { fill: #B3261E; }
@@ -154,24 +218,29 @@ export function renderer(options) {
154
218
  .card-empty .card-rank, .card-empty .card-suit { display: none; }
155
219
  .community-cards-area { text-align: center; z-index: 10; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
156
220
  .community-cards-container { min-height: 75px; display: flex; justify-content: center; align-items:center; margin-bottom: 0.5rem; gap: 8px; }
157
- .pot-display { font-size: 30px; font-weight: bold; color: #ffffff; margin-bottom: 30px; }
221
+ .pot-display { font-size: 30px; font-weight: bold; color: #ffffff; margin-bottom: 10px; }
158
222
  .bet-display {
159
- display: inline-block; padding: 10px 20px; border-radius: 12px;
160
- background-color: #3C4043; color: #ffff;
161
- font-family: 'Inter' sans-serif; font-size: 1.75rem; font-weigth: 600;
223
+ display: inline-block; padding: 10px 20px; border-radius: 30px;
224
+ background-color: #ffffff; color: black;
225
+ font-family: 'Inter' sans-serif; font-size: 20px; font-weight: 600;
162
226
  text-align: center;
163
- height: 3rem; line-height: 3rem;
164
- min-width: 200px;
227
+ height: 20pxrem; line-height: 20px;
228
+ width: 150px;
165
229
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
166
230
  }
167
231
  .blind-indicator { font-size: 0.7rem; color: #a0aec0; margin-top: 3px; }
168
232
  .dealer-button {
169
233
  width: 36px; height: 36px; background-color: #f0f0f0; color: #333; border-radius: 50%;
170
- text-align: center; line-height: 36px; font-weight: bold; font-size: 1.5rem; position: absolute;
171
- border: 3px solid #1EBEFF; box-shadow: 0 1px 3px rgba(0,0,0,0.3); z-index: 15; pointer-events: auto;
234
+ text-align: center; font-weight: bold; font-size: 28px; position: absolute;
235
+ padding-left: 1px;
236
+ line-height: 33px;
237
+ box-shadow: 0 1px 3px rgba(0,0,0,0.3); z-index: 15; pointer-events: auto;
238
+ border: 2px solid black;
239
+ outline: 2px solid #20BEFF;
240
+ left: 320px
172
241
  }
173
- .dealer-button.dealer-player0 { bottom: 110px; }
174
- .dealer-button.dealer-player1 { top: 110px; }
242
+ .dealer-button.dealer-player0 { top: 170px; }
243
+ .dealer-button.dealer-player1 { bottom: 170px; }
175
244
  .step-counter {
176
245
  position: absolute; top: 12px; right: 12px; z-index: 20;
177
246
  background-color: rgba(60, 64, 67, 0.9); color: #ffffff;
@@ -179,321 +248,471 @@ export function renderer(options) {
179
248
  font-size: 14px; font-weight: 600;
180
249
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
181
250
  }
251
+ .chip-stack {
252
+ position: absolute;
253
+ display: flex;
254
+ flex-direction: row;
255
+ align-items: center;
256
+ gap: 12px;
257
+ z-index: 12;
258
+ pointer-events: none;
259
+ left: 50%;
260
+ transform: translateX(-50%);
261
+ }
262
+ .chip-stack.chip-stack-player0 {
263
+ top: 60px;
264
+ }
265
+ .chip-stack.chip-stack-player1 {
266
+ bottom: 60px;
267
+ }
268
+ .chip-stack-chips {
269
+ display: flex;
270
+ flex-direction: row-reverse;
271
+ align-items: flex-end;
272
+ justify-content: center;
273
+ gap: 8px;
274
+ position: relative;
275
+ }
276
+ .chip-denomination-stack {
277
+ display: flex;
278
+ flex-direction: column-reverse;
279
+ align-items: center;
280
+ position: relative;
281
+ }
282
+ .chip {
283
+ width: 40px;
284
+ height: 40px;
285
+ position: relative;
286
+ margin-bottom: -34px;
287
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
288
+ }
289
+ .chip:first-child {
290
+ margin-bottom: 0;
291
+ }
292
+ .chip img {
293
+ width: 100%;
294
+ height: 100%;
295
+ display: block;
296
+ }
297
+ .chip-stack-label {
298
+ color: #FFFFFF;
299
+ font-size: 18px;
300
+ font-weight: bold;
301
+ white-space: nowrap;
302
+ }
182
303
  `;
183
304
 
184
- function _injectStyles(passedOptions) {
185
- if (typeof document === 'undefined' || window.__poker_styles_injected) {
186
- return;
187
- }
188
- const style = document.createElement('style');
189
- style.textContent = css;
190
- const parentForStyles =
191
- passedOptions && passedOptions.parent ? passedOptions.parent.ownerDocument.head : document.head;
192
- if (parentForStyles && !parentForStyles.querySelector('style[data-poker-renderer-styles]')) {
193
- style.setAttribute('data-poker-renderer-styles', 'true');
194
- parentForStyles.appendChild(style);
195
- }
196
- window.__poker_styles_injected = true;
305
+ function _injectStyles(passedOptions) {
306
+ if (typeof document === 'undefined' || window.__poker_styles_injected) {
307
+ return;
197
308
  }
309
+ const style = document.createElement('style');
310
+ style.textContent = css;
311
+ const parentForStyles =
312
+ passedOptions && passedOptions.parent ? passedOptions.parent.ownerDocument.head : document.head;
313
+ if (parentForStyles && !parentForStyles.querySelector('style[data-poker-renderer-styles]')) {
314
+ style.setAttribute('data-poker-renderer-styles', 'true');
315
+ parentForStyles.appendChild(style);
316
+ }
317
+ window.__poker_styles_injected = true;
318
+ }
319
+
320
+ function createCardElement(cardStr, isHidden = false) {
321
+ const cardDiv = document.createElement('div');
322
+ cardDiv.classList.add('card');
323
+ if (isHidden || !cardStr || cardStr === '?' || cardStr === '??') {
324
+ cardDiv.classList.add('card-back');
325
+ } else {
326
+ const { rank, suit } = acpcCardToDisplay(cardStr);
327
+ const rankSpan = document.createElement('span');
328
+ rankSpan.classList.add('card-rank');
329
+ rankSpan.textContent = rank;
330
+ cardDiv.appendChild(rankSpan);
331
+
332
+ const suitSpan = document.createElement('span');
333
+ suitSpan.classList.add('card-suit');
334
+
335
+ if (suitSVGs[suit]) {
336
+ suitSpan.innerHTML = suitSVGs[suit];
337
+ }
338
+
339
+ cardDiv.appendChild(suitSpan);
340
+
341
+ if (suit === 'hearts') cardDiv.classList.add('card-red');
342
+ else if (suit === 'spades') cardDiv.classList.add('card-black');
343
+ else if (suit === 'diamonds') cardDiv.classList.add('card-blue');
344
+ else if (suit === 'clubs') cardDiv.classList.add('card-green');
345
+ }
346
+ return cardDiv;
347
+ }
198
348
 
199
- function createCardElement(cardStr, isHidden = false) {
200
- const cardDiv = document.createElement('div');
201
- cardDiv.classList.add('card');
202
- if (isHidden || !cardStr || cardStr === '?' || cardStr === '??') {
203
- cardDiv.classList.add('card-back');
204
- } else {
205
- const { rank, suit } = acpcCardToDisplay(cardStr);
206
- const rankSpan = document.createElement('span');
207
- rankSpan.classList.add('card-rank');
208
- rankSpan.textContent = rank;
209
- cardDiv.appendChild(rankSpan);
210
-
211
- const suitSpan = document.createElement('span');
212
- suitSpan.classList.add('card-suit');
213
-
214
- if (suitSVGs[suit]) {
215
- suitSpan.innerHTML = suitSVGs[suit];
216
- }
349
+ function updateChipStack(chipStackElement, betAmount) {
350
+ if (betAmount <= 0) {
351
+ chipStackElement.style.display = 'none';
352
+ return;
353
+ }
217
354
 
218
- cardDiv.appendChild(suitSpan);
355
+ chipStackElement.style.display = 'flex';
356
+ const chipsContainer = chipStackElement.querySelector('.chip-stack-chips');
357
+ const labelElement = chipStackElement.querySelector('.chip-stack-label');
219
358
 
220
- if (suit === 'hearts') cardDiv.classList.add('card-red');
221
- else if (suit === 'spades') cardDiv.classList.add('card-black');
222
- else if (suit === 'diamonds') cardDiv.classList.add('card-blue');
223
- else if (suit === 'clubs') cardDiv.classList.add('card-green');
224
- }
225
- return cardDiv;
226
- }
227
-
228
- // --- Board Parsing and Rendering ---
229
- function _ensurePokerTableElements(parentElement, passedOptions) {
230
- if (!parentElement) return false;
231
- parentElement.innerHTML = '';
232
- parentElement.classList.add('poker-renderer-host');
233
-
234
- elements.diagnosticHeader = document.createElement('h1');
235
- elements.diagnosticHeader.id = 'poker-renderer-diagnostic-header';
236
- elements.diagnosticHeader.textContent = 'Poker Table Initialized (Live Data)';
237
- elements.diagnosticHeader.style.cssText =
238
- 'color: lime; background-color: black; padding: 5px; font-size: 12px; position: absolute; top: 0px; left: 0px; z-index: 10001; display: none;'; // Hidden by default
239
- parentElement.appendChild(elements.diagnosticHeader);
240
-
241
- elements.gameLayout = document.createElement('div');
242
- elements.gameLayout.className = 'poker-game-layout';
243
- parentElement.appendChild(elements.gameLayout);
244
-
245
- elements.pokerTableContainer = document.createElement('div');
246
- elements.pokerTableContainer.className = 'poker-table-container';
247
- elements.gameLayout.appendChild(elements.pokerTableContainer);
248
-
249
- elements.playersContainer = document.createElement('div');
250
- elements.playersContainer.className = 'players-container';
251
- elements.gameLayout.appendChild(elements.playersContainer);
252
-
253
- elements.pokerTable = document.createElement('div');
254
- elements.pokerTable.className = 'poker-table';
255
- elements.pokerTableContainer.appendChild(elements.pokerTable);
256
-
257
- const communityArea = document.createElement('div');
258
- communityArea.className = 'community-cards-area';
259
- elements.pokerTable.appendChild(communityArea);
260
-
261
- elements.potDisplay = document.createElement('div');
262
- elements.potDisplay.className = 'pot-display';
263
- communityArea.appendChild(elements.potDisplay);
264
-
265
- elements.communityCardsContainer = document.createElement('div');
266
- elements.communityCardsContainer.className = 'community-cards-container';
267
- communityArea.appendChild(elements.communityCardsContainer);
268
-
269
- elements.playerContainers = [];
270
- elements.playerCardAreas = [];
271
- elements.playerInfoAreas = [];
272
- elements.playerNames = [];
273
-
274
- for (let i = 0; i < 2; i++) {
275
- // Create player container that groups all player elements
276
- const playerContainer = document.createElement('div');
277
- playerContainer.className = `player-container player-container-${i}`;
278
- elements.playersContainer.appendChild(playerContainer);
279
- elements.playerContainers.push(playerContainer);
280
-
281
- // Player name
282
- const playerName = document.createElement('div');
283
- playerName.className = `player-name`;
284
- playerName.textContent = `Player ${i}`;
285
- playerContainer.appendChild(playerName);
286
- elements.playerNames.push(playerName);
287
-
288
- // Create wrapper for card and info areas
289
- const playerAreaWrapper = document.createElement('div');
290
- playerAreaWrapper.className = 'player-area-wrapper';
291
- playerContainer.appendChild(playerAreaWrapper);
292
-
293
- // Card area (left side)
294
- const playerCardArea = document.createElement('div');
295
- playerCardArea.className = `player-card-area`;
296
- playerCardArea.innerHTML = `
297
- <div class="player-cards-container"></div>
298
- `;
299
- playerAreaWrapper.appendChild(playerCardArea);
300
- elements.playerCardAreas.push(playerCardArea);
301
-
302
- // TODO: Render chip stack
303
- // Info area (right side)
304
- const playerInfoArea = document.createElement('div');
305
- playerInfoArea.className = `player-info-area`;
306
- playerInfoArea.innerHTML = `
307
- <div class="player-stack">
308
- <span class="player-stack-value">0</span>
309
- </div>
310
- <div class="bet-display" style="display:none;">Bet : 0</div>
311
- `;
312
- playerAreaWrapper.appendChild(playerInfoArea);
313
- elements.playerInfoAreas.push(playerInfoArea);
314
- }
359
+ chipsContainer.innerHTML = '';
360
+ labelElement.textContent = betAmount;
315
361
 
316
- elements.dealerButton = document.createElement('div');
317
- elements.dealerButton.className = 'dealer-button';
318
- elements.dealerButton.textContent = 'D';
319
- elements.dealerButton.style.display = 'none';
320
- elements.playersContainer.appendChild(elements.dealerButton);
321
-
322
- elements.stepCounter = document.createElement('div');
323
- elements.stepCounter.className = 'step-counter';
324
- elements.stepCounter.textContent = 'Standby';
325
- elements.gameLayout.appendChild(elements.stepCounter);
326
- return true;
327
- }
328
-
329
- // --- State Parsing ---
330
- function _parseKagglePokerState(options) {
331
- const { environment, step } = options;
332
- const numPlayers = 2;
333
-
334
- // --- Default State ---
335
- const defaultStateUiData = {
336
- players: [],
337
- communityCards: [],
338
- pot: 0,
339
- isTerminal: false,
340
- };
341
-
342
- // --- Step Validation ---
343
- if (!environment || !environment.steps || !environment.steps[step] || !environment.info?.stateHistory) {
344
- return defaultStateUiData;
345
- }
362
+ // Break down bet into denominations (100, 25, 10, 5, 1)
363
+ const denominations = [100, 25, 10, 5, 1];
364
+ let remaining = betAmount;
365
+ const chipCounts = [];
346
366
 
347
- return getPokerStateForStep(environment, step);
367
+ for (const denom of denominations) {
368
+ const count = Math.floor(remaining / denom);
369
+ if (count > 0) {
370
+ chipCounts.push({ denom, count: Math.min(count, 5) }); // Max 5 of each denomination
371
+ remaining -= count * denom;
372
+ }
348
373
  }
349
374
 
350
- function _applyScale(parentElement) {
351
- if (!parentElement || !elements.gameLayout) return;
352
-
353
- const parentWidth = parentElement.clientWidth;
354
- const parentHeight = parentElement.clientHeight;
375
+ // Render chips separated by denomination (highest to lowest, left to right)
376
+ chipCounts.forEach(({ denom, count }) => {
377
+ const denomStack = document.createElement('div');
378
+ denomStack.className = 'chip-denomination-stack';
379
+
380
+ for (let i = 0; i < count; i++) {
381
+ const chip = document.createElement('div');
382
+ chip.className = 'chip';
383
+ const img = document.createElement('img');
384
+ img.src = chipImages[denom];
385
+ img.alt = `${denom} chip`;
386
+ chip.appendChild(img);
387
+ denomStack.appendChild(chip);
388
+ }
389
+
390
+ chipsContainer.appendChild(denomStack);
391
+ });
392
+ }
393
+
394
+ // --- Board Parsing and Rendering ---
395
+ function _ensurePokerTableElements(parentElement, passedOptions) {
396
+ if (!parentElement) return false;
397
+ parentElement.innerHTML = '';
398
+ parentElement.classList.add('poker-renderer-host');
399
+
400
+ elements.diagnosticHeader = document.createElement('h1');
401
+ elements.diagnosticHeader.id = 'poker-renderer-diagnostic-header';
402
+ elements.diagnosticHeader.textContent = 'Poker Table Initialized (Live Data)';
403
+ elements.diagnosticHeader.style.cssText =
404
+ 'color: lime; background-color: black; padding: 5px; font-size: 12px; position: absolute; top: 0px; left: 0px; z-index: 10001; display: none;'; // Hidden by default
405
+ parentElement.appendChild(elements.diagnosticHeader);
406
+
407
+ elements.gameLayout = document.createElement('div');
408
+ elements.gameLayout.className = 'poker-game-layout';
409
+ parentElement.appendChild(elements.gameLayout);
410
+
411
+ elements.pokerTableContainer = document.createElement('div');
412
+ elements.pokerTableContainer.className = 'poker-table-container';
413
+ elements.gameLayout.appendChild(elements.pokerTableContainer);
414
+
415
+ elements.playersContainer = document.createElement('div');
416
+ elements.playersContainer.className = 'players-container';
417
+ elements.gameLayout.appendChild(elements.playersContainer);
418
+
419
+ elements.pokerTable = document.createElement('div');
420
+ elements.pokerTable.className = 'poker-table';
421
+ elements.pokerTableContainer.appendChild(elements.pokerTable);
422
+
423
+ const muckLine = document.createElement('div');
424
+ muckLine.className = 'muck-line';
425
+ elements.pokerTable.appendChild(muckLine);
426
+
427
+ // Create chip stacks for each player inside the table
428
+ elements.chipStacks = [];
429
+ for (let i = 0; i < 2; i++) {
430
+ const chipStack = document.createElement('div');
431
+ chipStack.className = `chip-stack chip-stack-player${i}`;
432
+ chipStack.style.display = 'none';
433
+ chipStack.innerHTML = `
434
+ <div class="chip-stack-chips"></div>
435
+ <div class="chip-stack-label">0</div>
436
+ `;
437
+ elements.pokerTable.appendChild(chipStack);
438
+ elements.chipStacks.push(chipStack);
439
+ }
355
440
 
356
- const baseWidth = 1000;
357
- const baseHeight = 700;
441
+ const communityArea = document.createElement('div');
442
+ communityArea.className = 'community-cards-area';
443
+ elements.pokerTable.appendChild(communityArea);
444
+
445
+ elements.potDisplay = document.createElement('div');
446
+ elements.potDisplay.className = 'pot-display';
447
+ communityArea.appendChild(elements.potDisplay);
448
+
449
+ elements.communityCardsContainer = document.createElement('div');
450
+ elements.communityCardsContainer.className = 'community-cards-container';
451
+ communityArea.appendChild(elements.communityCardsContainer);
452
+
453
+ elements.playerContainers = [];
454
+ elements.playerCardAreas = [];
455
+ elements.playerInfoAreas = [];
456
+ elements.playerNames = [];
457
+ elements.playerThumbnails = [];
458
+
459
+ for (let i = 0; i < 2; i++) {
460
+ // Create player container that groups all player elements
461
+ const playerContainer = document.createElement('div');
462
+ playerContainer.className = `player-container player-container-${i}`;
463
+ elements.playersContainer.appendChild(playerContainer);
464
+ elements.playerContainers.push(playerContainer);
465
+
466
+ // Player name wrapper with thumbnail
467
+ const playerNameWrapper = document.createElement('div');
468
+ playerNameWrapper.className = `player-name-wrapper`;
469
+ playerContainer.appendChild(playerNameWrapper);
470
+
471
+ // Player thumbnail
472
+ const playerThumbnail = document.createElement('img');
473
+ playerThumbnail.className = `player-thumbnail`;
474
+ playerThumbnail.style.display = 'none'; // Hidden by default
475
+ playerNameWrapper.appendChild(playerThumbnail);
476
+ elements.playerThumbnails.push(playerThumbnail);
477
+
478
+ // Player name
479
+ const playerName = document.createElement('div');
480
+ playerName.className = `player-name`;
481
+ playerName.textContent = `Player ${i}`;
482
+ playerNameWrapper.appendChild(playerName);
483
+ elements.playerNames.push(playerName);
484
+
485
+ // Info area containing bet, cards, and stack
486
+ const playerInfoArea = document.createElement('div');
487
+ playerInfoArea.className = `player-info-area`;
488
+ playerInfoArea.innerHTML = `
489
+ <div class="bet-display">Standby</div>
490
+ <div class="stack-cards-wrapper">
491
+ <div class="player-card-area">
492
+ <div class="player-cards-container"></div>
493
+ </div>
494
+ <div class="player-stack">
495
+ <span class="player-stack-value">0</span>
496
+ </div>
497
+ </div>
498
+ `;
499
+ playerContainer.appendChild(playerInfoArea);
500
+ elements.playerInfoAreas.push(playerInfoArea);
501
+
502
+ // Get reference to card area (already in DOM)
503
+ const playerCardArea = playerInfoArea.querySelector('.player-card-area');
504
+ elements.playerCardAreas.push(playerCardArea);
505
+ }
358
506
 
359
- const scaleX = parentWidth / baseWidth;
360
- const scaleY = parentHeight / baseHeight;
361
- const scale = Math.min(scaleX, scaleY);
507
+ elements.dealerButton = document.createElement('div');
508
+ elements.dealerButton.className = 'dealer-button';
509
+ elements.dealerButton.textContent = 'D';
510
+ elements.dealerButton.style.display = 'none';
511
+ elements.playersContainer.appendChild(elements.dealerButton);
512
+
513
+ elements.stepCounter = document.createElement('div');
514
+ elements.stepCounter.className = 'step-counter';
515
+ elements.stepCounter.textContent = 'Standby';
516
+ elements.gameLayout.appendChild(elements.stepCounter);
517
+ return true;
518
+ }
519
+
520
+ // --- State Parsing ---
521
+ function _parseKagglePokerState(options) {
522
+ const { environment, step } = options;
523
+ const numPlayers = 2;
524
+
525
+ // --- Default State ---
526
+ const defaultStateUiData = {
527
+ players: [],
528
+ communityCards: [],
529
+ pot: 0,
530
+ isTerminal: false,
531
+ };
362
532
 
363
- elements.gameLayout.style.transform = `scale(${scale})`;
533
+ // --- Step Validation ---
534
+ if (!environment || !environment.steps || !environment.steps[step] || !environment.info?.stateHistory) {
535
+ return defaultStateUiData;
364
536
  }
365
537
 
366
- function _renderPokerTableUI(data, passedOptions) {
367
- if (!elements.pokerTable || !data) return;
368
- const { players, communityCards, pot, isTerminal, step } = data;
538
+ return getPokerStateForStep(environment, step);
539
+ }
369
540
 
370
- // Update step counter
371
- if (elements.stepCounter && step !== undefined) {
372
- elements.stepCounter.textContent = `Step: ${step}`;
373
- }
541
+ function _applyScale(parentElement) {
542
+ if (!parentElement || !elements.gameLayout) return;
374
543
 
375
- if (elements.diagnosticHeader && data.rawObservation) {
376
- // Optional: Show diagnostics for debugging
377
- // elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
378
- // elements.diagnosticHeader.style.display = 'block';
379
- }
544
+ const parentWidth = parentElement.clientWidth;
545
+ const parentHeight = parentElement.clientHeight;
380
546
 
381
- elements.communityCardsContainer.innerHTML = '';
382
- // Always show 5 slots for the river
383
- // Display cards left to right, with empty slots at the end
384
- const numCommunityCards = 5;
385
- const numCards = communityCards ? communityCards.length : 0;
547
+ const baseWidth = 1000;
548
+ const baseHeight = 1000;
386
549
 
387
- // Since the 4th and 5th street cards are appended to the communityCards array, we need to
388
- // reverse it so that the added cards are put at the end of the display area on the board.
389
- if (communityCards) communityCards.reverse();
550
+ const scaleX = parentWidth / baseWidth;
551
+ const scaleY = parentHeight / baseHeight;
552
+ const scale = Math.min(scaleX, scaleY);
390
553
 
391
- // Add actual cards
392
- for (let i = 0; i < numCards; i++) {
393
- elements.communityCardsContainer.appendChild(createCardElement(communityCards[i]));
394
- }
554
+ elements.gameLayout.style.transform = `scale(${scale})`;
555
+ }
395
556
 
396
- // Fill remaining slots with empty cards
397
- for (let i = numCards; i < numCommunityCards; i++) {
398
- const emptyCard = document.createElement('div');
399
- emptyCard.classList.add('card', 'card-empty');
400
- elements.communityCardsContainer.appendChild(emptyCard);
401
- }
557
+ function _renderPokerTableUI(data, passedOptions) {
558
+ if (!elements.pokerTable || !data) return;
559
+ const { players, communityCards, pot, isTerminal, step } = data;
402
560
 
403
- elements.potDisplay.textContent = `Total Pot : ${pot}`;
404
-
405
- players.forEach((playerData, index) => {
406
- const playerNameElement = elements.playerNames[index];
407
- if (playerNameElement) {
408
- const playerNameText =
409
- playerData.isTurn && !isTerminal ? `${playerData.name} responding...` : playerData.name;
410
- playerNameElement.textContent = playerNameText;
411
-
412
- // Add winner class if player won
413
- if (playerData.isWinner) {
414
- playerNameElement.classList.add('winner');
415
- } else {
416
- playerNameElement.classList.remove('winner');
417
- }
418
- }
561
+ // Update step counter
562
+ if (elements.stepCounter && step !== undefined) {
563
+ elements.stepCounter.textContent = `Step: ${step}`;
564
+ }
419
565
 
420
- // Update card area (left side)
421
- const playerCardArea = elements.playerCardAreas[index];
422
- if (playerCardArea) {
423
- const playerCardsContainer = playerCardArea.querySelector('.player-cards-container');
424
- playerCardsContainer.innerHTML = '';
566
+ if (elements.diagnosticHeader && data.rawObservation) {
567
+ // Optional: Show diagnostics for debugging
568
+ // elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
569
+ // elements.diagnosticHeader.style.display = 'block';
570
+ }
425
571
 
426
- // In heads-up, we show both hands at the end.
427
- const showCards = isTerminal || (playerData.cards && !playerData.cards.includes(null));
572
+ elements.communityCardsContainer.innerHTML = '';
573
+ // Always show 5 slots for the river
574
+ // Display cards left to right, with empty slots at the end
575
+ const numCommunityCards = 5;
576
+ const numCards = communityCards ? communityCards.length : 0;
428
577
 
429
- (playerData.cards || [null, null]).forEach((cardStr) => {
430
- playerCardsContainer.appendChild(createCardElement(cardStr, !showCards && cardStr !== null));
431
- });
432
- }
433
-
434
- // Update info area (right side)
435
- const playerInfoArea = elements.playerInfoAreas[index];
436
- if (playerInfoArea) {
437
- playerInfoArea.querySelector('.player-stack-value').textContent = `${playerData.stack}`;
438
-
439
- const betDisplay = playerInfoArea.querySelector('.bet-display');
440
- if (playerData.currentBet > 0) {
441
- if (data.lastMoves[index]) {
442
- betDisplay.textContent = data.lastMoves[index];
443
- } else {
444
- if (playerData.isDealer) {
445
- betDisplay.textContent = 'small blind';
446
- } else {
447
- betDisplay.textContent = 'big blind';
448
- }
449
- }
450
- betDisplay.style.display = 'block';
451
- } else {
452
- betDisplay.style.display = 'none';
453
- }
454
- }
455
- });
578
+ // Since the 4th and 5th street cards are appended to the communityCards array, we need to
579
+ // reverse it so that the added cards are put at the end of the display area on the board.
580
+ if (communityCards) communityCards.reverse();
456
581
 
457
- const dealerPlayerIndex = players.findIndex((p) => p.isDealer);
458
- if (elements.dealerButton) {
459
- if (dealerPlayerIndex !== -1) {
460
- elements.dealerButton.style.display = 'block';
461
- // Remove previous dealer class
462
- elements.dealerButton.classList.remove('dealer-player0', 'dealer-player1');
463
- // Add new dealer class based on player index
464
- elements.dealerButton.classList.add(`dealer-player${dealerPlayerIndex}`);
465
- } else {
466
- elements.dealerButton.style.display = 'none';
467
- }
468
- }
582
+ // Add actual cards
583
+ for (let i = 0; i < numCards; i++) {
584
+ elements.communityCardsContainer.appendChild(createCardElement(communityCards[i]));
469
585
  }
470
586
 
471
- // --- MAIN EXECUTION LOGIC ---
472
- const { parent } = options;
473
- if (!parent) {
474
- console.error('Renderer: Parent element not provided.');
475
- return;
587
+ // Fill remaining slots with empty cards
588
+ for (let i = numCards; i < numCommunityCards; i++) {
589
+ const emptyCard = document.createElement('div');
590
+ emptyCard.classList.add('card', 'card-empty');
591
+ elements.communityCardsContainer.appendChild(emptyCard);
476
592
  }
477
593
 
478
- _injectStyles(options);
594
+ elements.potDisplay.textContent = `Total Pot : ${pot}`;
479
595
 
480
- if (!_ensurePokerTableElements(parent, options)) {
481
- console.error('Renderer: Failed to ensure poker table elements.');
482
- parent.innerHTML = '<p style="color:red;">Error: Could not create poker table structure.</p>';
483
- return;
484
- }
485
-
486
- const uiData = _parseKagglePokerState(options);
487
- _renderPokerTableUI(uiData, options);
596
+ players.forEach((playerData, index) => {
597
+ const playerNameElement = elements.playerNames[index];
598
+ if (playerNameElement) {
599
+ const playerNameText =
600
+ !playerData.isTurn && !isTerminal ? `${playerData.name} responding...` : playerData.name;
601
+ playerNameElement.textContent = playerNameText;
488
602
 
489
- // Apply initial scale
490
- _applyScale(parent);
603
+ // Highlight current player's turn
604
+ if (playerData.isTurn && !isTerminal) {
605
+ playerNameElement.classList.add('current-turn');
606
+ } else {
607
+ playerNameElement.classList.remove('current-turn');
608
+ }
491
609
 
492
- // Watch for container size changes and reapply scale
493
- if (typeof ResizeObserver !== 'undefined') {
494
- const resizeObserver = new ResizeObserver(() => {
495
- _applyScale(parent);
610
+ // Add winner class if player won
611
+ if (playerData.isWinner) {
612
+ playerNameElement.classList.add('winner');
613
+ } else {
614
+ playerNameElement.classList.remove('winner');
615
+ }
616
+ }
617
+
618
+ // Update thumbnail
619
+ const playerThumbnailElement = elements.playerThumbnails[index];
620
+ if (playerThumbnailElement && playerData.thumbnail) {
621
+ playerThumbnailElement.src = playerData.thumbnail;
622
+ playerThumbnailElement.style.display = 'block';
623
+ } else if (playerThumbnailElement) {
624
+ playerThumbnailElement.style.display = 'none';
625
+ }
626
+
627
+ // Update card area (left side)
628
+ const playerCardArea = elements.playerCardAreas[index];
629
+ if (playerCardArea) {
630
+ const playerCardsContainer = playerCardArea.querySelector('.player-cards-container');
631
+ playerCardsContainer.innerHTML = '';
632
+
633
+ // In heads-up, we show both hands at the end.
634
+ const showCards = isTerminal || (playerData.cards && !playerData.cards.includes(null));
635
+
636
+ (playerData.cards || [null, null]).forEach((cardStr) => {
637
+ playerCardsContainer.appendChild(createCardElement(cardStr, !showCards && cardStr !== null));
496
638
  });
497
- resizeObserver.observe(parent);
639
+ }
640
+
641
+ // Update chip stacks on the table
642
+ if (elements.chipStacks[index]) {
643
+ updateChipStack(elements.chipStacks[index], playerData.currentBet);
644
+ }
645
+
646
+ // Update info area (right side)
647
+ const playerInfoArea = elements.playerInfoAreas[index];
648
+ if (playerInfoArea) {
649
+ // Highlight active player's pod
650
+ if (playerData.isTurn && !isTerminal) {
651
+ playerInfoArea.classList.add('active-player');
652
+ } else {
653
+ playerInfoArea.classList.remove('active-player');
654
+ }
655
+
656
+ playerInfoArea.querySelector('.player-stack-value').textContent = `${playerData.stack}`;
657
+
658
+ const betDisplay = playerInfoArea.querySelector('.bet-display');
659
+ if (playerData.currentBet > 0) {
660
+ if (data.lastMoves[index]) {
661
+ betDisplay.textContent = data.lastMoves[index];
662
+ } else {
663
+ if (playerData.isDealer) {
664
+ betDisplay.textContent = 'Small Blind';
665
+ } else {
666
+ betDisplay.textContent = 'Big Blind';
667
+ }
668
+ }
669
+ betDisplay.style.display = 'block';
670
+ } else {
671
+ betDisplay.style.display = 'none';
672
+ }
673
+ }
674
+ });
675
+
676
+ const dealerPlayerIndex = players.findIndex((p) => p.isDealer);
677
+ if (elements.dealerButton) {
678
+ if (dealerPlayerIndex !== -1) {
679
+ elements.dealerButton.style.display = 'block';
680
+ // Remove previous dealer class
681
+ elements.dealerButton.classList.remove('dealer-player0', 'dealer-player1');
682
+ // Add new dealer class based on player index
683
+ elements.dealerButton.classList.add(`dealer-player${dealerPlayerIndex}`);
684
+ } else {
685
+ elements.dealerButton.style.display = 'none';
686
+ }
498
687
  }
688
+ }
689
+
690
+ // --- MAIN EXECUTION LOGIC ---
691
+ const { parent } = options;
692
+ if (!parent) {
693
+ console.error('Renderer: Parent element not provided.');
694
+ return;
695
+ }
696
+
697
+ _injectStyles(options);
698
+
699
+ if (!_ensurePokerTableElements(parent, options)) {
700
+ console.error('Renderer: Failed to ensure poker table elements.');
701
+ parent.innerHTML = '<p style="color:red;">Error: Could not create poker table structure.</p>';
702
+ return;
703
+ }
704
+
705
+ const uiData = _parseKagglePokerState(options);
706
+ _renderPokerTableUI(uiData, options);
707
+
708
+ // Apply initial scale
709
+ _applyScale(parent);
710
+
711
+ // Watch for container size changes and reapply scale
712
+ if (typeof ResizeObserver !== 'undefined') {
713
+ const resizeObserver = new ResizeObserver(() => {
714
+ _applyScale(parent);
715
+ });
716
+ resizeObserver.observe(parent);
717
+ }
499
718
  }