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