kaggle-environments 1.23.2__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 +586 -361
  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.2.dist-info → kaggle_environments-1.23.4.dist-info}/METADATA +1 -1
  43. {kaggle_environments-1.23.2.dist-info → kaggle_environments-1.23.4.dist-info}/RECORD +46 -8
  44. {kaggle_environments-1.23.2.dist-info → kaggle_environments-1.23.4.dist-info}/WHEEL +0 -0
  45. {kaggle_environments-1.23.2.dist-info → kaggle_environments-1.23.4.dist-info}/entry_points.txt +0 -0
  46. {kaggle_environments-1.23.2.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,16 +59,49 @@ 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;
48
- overflow: hidden; padding: 1rem; box-sizing: border-box; position: relative;
62
+ font-family: 'Zeitung Pro', sans-serif; background-color: #28303F; color: #fff;
63
+ overflow: hidden; box-sizing: border-box; position: relative;
64
+ }
65
+ .poker-game-layout {
66
+ width: 1000px;
67
+ height: 900px;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ position: relative;
72
+ transform-origin: center center;
73
+ }
74
+ .poker-table-container {
75
+ width: 100%;
76
+ height: 400px;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
49
80
  }
50
- .poker-game-layout { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: relative; max-width: 750px; max-height: 750px; }
51
- .poker-table-container { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; max-width: 750px; max-height: 275px; }
52
81
  .poker-table {
53
- width: clamp(400px, 85vw, 750px); height: clamp(220px, 48vw, 275px);
54
- background-color: #197631; border-radius: 24px; position: relative;
55
- display: flex; align-items: center; justify-content: center;
56
- margin: 0 60px;
82
+ width: 900px;
83
+ height: 400px;
84
+ background: radial-gradient(43.33% 50% at 50% 50%, #20BD48 0%, #0A4018 99.99%);
85
+ border-radius: 300px;
86
+ position: relative;
87
+ border: 20px solid #5C3A21;
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: center;
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;
57
105
  }
58
106
  .players-container {
59
107
  position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10;
@@ -65,50 +113,87 @@ export function renderer(options) {
65
113
  display: flex;
66
114
  flex-direction: column;
67
115
  }
68
- .player-container-0 { bottom: 0; flex-direction: column-reverse; }
69
- .player-container-1 { top: 0; }
70
- .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 {
71
125
  display: flex;
72
- justify-content: space-between;
126
+ flex-direction: row;
73
127
  align-items: center;
74
- }
75
- .player-card-area {
76
- margin: 20px 60px; color: white; text-align: center;
77
- display: flex; flex-direction: column; justify-content: center; align-items: center;
78
- min-height: 100px; pointer-events: auto;
128
+ justify-content: center;
129
+ gap: 16px;
130
+ margin: 12px;
131
+ width: 100%;
79
132
  }
80
133
  .player-info-area {
81
134
  color: white;
82
- min-width: 180px;
135
+ width: auto;
136
+ min-width: 200px;
83
137
  pointer-events: auto;
84
138
  display: flex;
85
139
  flex-direction: column;
86
- justify-content: left;
87
- align-items: left;
88
- 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);
89
153
  }
90
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
+ }
91
172
  .player-name {
92
- font-size: 32px; font-weight: 600;
173
+ font-size: 24px; font-weight: 600;
93
174
  white-space: nowrap;
94
175
  overflow: hidden;
95
176
  text-overflow: ellipsis;
96
177
  color: white;
97
- text-align: left;
98
- padding: 10px 0;
99
- margin: 0 60px;
178
+ text-align: center;
100
179
  }
101
180
  .player-name.winner { color: #FFEB70; }
102
- .player-stack { font-size: 32px; font-weight: 600; color: #ffffff; margin: 16px 0; display: flex; justify-content: space-between; align-items: center; }
103
- .player-cards-container { min-height: 70px; display: flex; justify-content: flex-start; align-items:center; gap: 12px; }
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;}
104
184
  .card {
105
185
  display: flex; flex-direction: column; justify-content: space-between; align-items: center;
106
- width: 80px; height: 112px; border: 2px solid #202124; border-radius: 8px;
186
+ width: 44px; height: 70px; border: 2px solid #202124; border-radius: 8px;
107
187
  background-color: white; color: black; font-weight: bold; text-align: center; overflow: hidden; position: relative;
108
188
  padding: 6px;
189
+ box-shadow: 0 6px 10px 4px rgba(0, 0, 0, 0.15), 0 2px 3px 0 rgba(0, 0, 0, 0.30);
109
190
  }
110
- .card-rank { font-family: 'Inter' sans-serif; font-size: 50px; line-height: 1; display: block; align-self: flex-start; }
111
- .card-suit { width: 50px; height: 50px; 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; }
112
197
  .card-suit svg { width: 100%; height: 100%; }
113
198
  .card-red .card-rank { color: #B3261E; }
114
199
  .card-red .card-suit svg { fill: #B3261E; }
@@ -126,30 +211,36 @@ export function renderer(options) {
126
211
  }
127
212
  .card-back .card-rank, .card-back .card-suit { display: none; }
128
213
  .card-empty {
129
- background-color: rgba(255, 255, 255, 0.1);
130
- border: 2px solid rgba(32, 33, 36, 0.5);
131
- background-image: none;
214
+ background-color: rgba(232, 234, 237, 0.1);
215
+ border: 2px solid rgba(154, 160, 166, 0.5);
216
+ box-shadow: none
132
217
  }
133
218
  .card-empty .card-rank, .card-empty .card-suit { display: none; }
134
219
  .community-cards-area { text-align: center; z-index: 10; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
135
- .community-cards-container { min-height: 75px; display: flex; justify-content: center; align-items:center; margin-bottom: 0.5rem; gap: 12px; }
136
- .pot-display { font-size: 40px; font-weight: bold; color: #ffffff; margin-bottom: 30px; }
220
+ .community-cards-container { min-height: 75px; display: flex; justify-content: center; align-items:center; margin-bottom: 0.5rem; gap: 8px; }
221
+ .pot-display { font-size: 30px; font-weight: bold; color: #ffffff; margin-bottom: 10px; }
137
222
  .bet-display {
138
- display: inline-block; padding: 10px 20px; border-radius: 12px;
139
- background-color: #3C4043; color: #ffff;
140
- 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;
141
226
  text-align: center;
142
- height: 3rem; line-height: 3rem;
143
- min-width: 200px;
227
+ height: 20pxrem; line-height: 20px;
228
+ width: 150px;
229
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
144
230
  }
145
231
  .blind-indicator { font-size: 0.7rem; color: #a0aec0; margin-top: 3px; }
146
232
  .dealer-button {
147
233
  width: 36px; height: 36px; background-color: #f0f0f0; color: #333; border-radius: 50%;
148
- text-align: center; line-height: 36px; font-weight: bold; font-size: 1.5rem; position: absolute;
149
- 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
150
241
  }
151
- .dealer-button.dealer-player0 { bottom: 110px; }
152
- .dealer-button.dealer-player1 { top: 110px; }
242
+ .dealer-button.dealer-player0 { top: 170px; }
243
+ .dealer-button.dealer-player1 { bottom: 170px; }
153
244
  .step-counter {
154
245
  position: absolute; top: 12px; right: 12px; z-index: 20;
155
246
  background-color: rgba(60, 64, 67, 0.9); color: #ffffff;
@@ -157,337 +248,471 @@ export function renderer(options) {
157
248
  font-size: 14px; font-weight: 600;
158
249
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
159
250
  }
160
-
161
- @media (max-width: 768px) {
162
- .bet-display { font-size: 1.5rem; height: 2.2rem; line-height: 2.2rem; min-width: 0;}
163
- .card { width: 60px; height: 85px; } .card-rank { font-size: 35px; } .card-suit { width: 35px; height: 35px; }
164
- .community-cards-container { gap: 6px; }
165
- .player-card-area { min-height: 120px; }
166
- .player-cards-container { gap: 6px; }
167
- .player-info-area { min-width: 160px; }
168
- .poker-game-layout { max-height: 700px; }
169
- .pot-display { font-size: 35px; margin-bottom: 20px; }
170
- }
171
- @media (max-width: 600px) {
172
- .bet-display { font-size: 20px; height: 40px; line-height: 40px; }
173
- .card { width: 50px; height: 70px; padding: 2px; } .card-rank { font-size: 32px; } .card-suit { width: 32px; height: 32px; }
174
- .community-cards-container { gap: 2px; }
175
- .dealer-button { font-size: 20px; height: 24px; line-height: 24px; width: 24px; }
176
- .dealer-button.dealer-player0 { bottom: 95px; }
177
- .dealer-button.dealer-player1 { top: 95px; }
178
- .player-card-area { min-height: 110px; margin: 0 0 0 40px;}
179
- .player-cards-container { gap: 2px; }
180
- .player-info-area { margin-right: 20px; }
181
- .player-name { font-size: 30px; margin: 0 20px; }
182
- .player-stack { font-size: 30px; }
183
- .poker-game-layout { max-height: 600px; }
184
- .poker-table { width: clamp(300px, 90vw, 600px); height: clamp(160px, 50vw, 200px); margin: 20px; }
185
- .pot-display { font-size: 30px; margin-bottom: 20px; }
186
- }
187
- @media (max-width: 400px) {
188
- .bet-display { font-size: 15px; height: 30px; line-height: 30px; }
189
- .card { width: 40px; height: 56px; margin: 0 2px; padding: 2px; } .card-rank { font-size: 25px; } .card-suit { width: 25px; height: 25px; }
190
- .community-cards-container { gap: 2px; }
191
- .dealer-button { font-size: 15px; height: 20px; line-height: 20px; width: 20px; }
192
- .dealer-button.dealer-player0 { bottom: 85px; }
193
- .dealer-button.dealer-player1 { top: 85px; }
194
- .player-card-area { margin: 0 0 0 30px;}
195
- .player-cards-container { gap: 2px; }
196
- .player-info-area { min-width: 100px; margin-right: 0; }
197
- .player-name { font-size: 25px; }
198
- .player-stack { font-size: 15px; }
199
- .poker-game-layout { max-height: 500px; }
200
- .poker-table { width: clamp(280px, 95vw, 380px); height: clamp(150px, 55vw, 150px); margin: 0;}
201
- .pot-display { font-size: 25px; margin-bottom: 15px; }
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;
202
302
  }
203
303
  `;
204
304
 
205
- function _injectStyles(passedOptions) {
206
- if (typeof document === 'undefined' || window.__poker_styles_injected) {
207
- return;
208
- }
209
- const style = document.createElement('style');
210
- style.textContent = css;
211
- const parentForStyles =
212
- passedOptions && passedOptions.parent ? passedOptions.parent.ownerDocument.head : document.head;
213
- if (parentForStyles && !parentForStyles.querySelector('style[data-poker-renderer-styles]')) {
214
- style.setAttribute('data-poker-renderer-styles', 'true');
215
- parentForStyles.appendChild(style);
216
- }
217
- window.__poker_styles_injected = true;
305
+ function _injectStyles(passedOptions) {
306
+ if (typeof document === 'undefined' || window.__poker_styles_injected) {
307
+ return;
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');
218
345
  }
346
+ return cardDiv;
347
+ }
219
348
 
220
- function createCardElement(cardStr, isHidden = false) {
221
- const cardDiv = document.createElement('div');
222
- cardDiv.classList.add('card');
223
- if (isHidden || !cardStr || cardStr === '?' || cardStr === '??') {
224
- cardDiv.classList.add('card-back');
225
- } else {
226
- const { rank, suit } = acpcCardToDisplay(cardStr);
227
- const rankSpan = document.createElement('span');
228
- rankSpan.classList.add('card-rank');
229
- rankSpan.textContent = rank;
230
- cardDiv.appendChild(rankSpan);
349
+ function updateChipStack(chipStackElement, betAmount) {
350
+ if (betAmount <= 0) {
351
+ chipStackElement.style.display = 'none';
352
+ return;
353
+ }
231
354
 
232
- const suitSpan = document.createElement('span');
233
- suitSpan.classList.add('card-suit');
355
+ chipStackElement.style.display = 'flex';
356
+ const chipsContainer = chipStackElement.querySelector('.chip-stack-chips');
357
+ const labelElement = chipStackElement.querySelector('.chip-stack-label');
234
358
 
235
- if (suitSVGs[suit]) {
236
- suitSpan.innerHTML = suitSVGs[suit];
237
- }
359
+ chipsContainer.innerHTML = '';
360
+ labelElement.textContent = betAmount;
238
361
 
239
- cardDiv.appendChild(suitSpan);
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 = [];
240
366
 
241
- if (suit === 'hearts') cardDiv.classList.add('card-red');
242
- else if (suit === 'spades') cardDiv.classList.add('card-black');
243
- else if (suit === 'diamonds') cardDiv.classList.add('card-blue');
244
- else if (suit === 'clubs') cardDiv.classList.add('card-green');
245
- }
246
- return cardDiv;
247
- }
248
-
249
- // --- Board Parsing and Rendering ---
250
- function _ensurePokerTableElements(parentElement, passedOptions) {
251
- if (!parentElement) return false;
252
- parentElement.innerHTML = '';
253
- parentElement.classList.add('poker-renderer-host');
254
-
255
- elements.diagnosticHeader = document.createElement('h1');
256
- elements.diagnosticHeader.id = 'poker-renderer-diagnostic-header';
257
- elements.diagnosticHeader.textContent = 'Poker Table Initialized (Live Data)';
258
- elements.diagnosticHeader.style.cssText =
259
- 'color: lime; background-color: black; padding: 5px; font-size: 12px; position: absolute; top: 0px; left: 0px; z-index: 10001; display: none;'; // Hidden by default
260
- parentElement.appendChild(elements.diagnosticHeader);
261
-
262
- elements.gameLayout = document.createElement('div');
263
- elements.gameLayout.className = 'poker-game-layout';
264
- parentElement.appendChild(elements.gameLayout);
265
-
266
- elements.pokerTableContainer = document.createElement('div');
267
- elements.pokerTableContainer.className = 'poker-table-container';
268
- elements.gameLayout.appendChild(elements.pokerTableContainer);
269
-
270
- elements.playersContainer = document.createElement('div');
271
- elements.playersContainer.className = 'players-container';
272
- elements.gameLayout.appendChild(elements.playersContainer);
273
-
274
- elements.pokerTable = document.createElement('div');
275
- elements.pokerTable.className = 'poker-table';
276
- elements.pokerTableContainer.appendChild(elements.pokerTable);
277
-
278
- const communityArea = document.createElement('div');
279
- communityArea.className = 'community-cards-area';
280
- elements.pokerTable.appendChild(communityArea);
281
-
282
- elements.potDisplay = document.createElement('div');
283
- elements.potDisplay.className = 'pot-display';
284
- communityArea.appendChild(elements.potDisplay);
285
-
286
- elements.communityCardsContainer = document.createElement('div');
287
- elements.communityCardsContainer.className = 'community-cards-container';
288
- communityArea.appendChild(elements.communityCardsContainer);
289
-
290
- elements.playerContainers = [];
291
- elements.playerCardAreas = [];
292
- elements.playerInfoAreas = [];
293
- elements.playerNames = [];
294
-
295
- for (let i = 0; i < 2; i++) {
296
- // Create player container that groups all player elements
297
- const playerContainer = document.createElement('div');
298
- playerContainer.className = `player-container player-container-${i}`;
299
- elements.playersContainer.appendChild(playerContainer);
300
- elements.playerContainers.push(playerContainer);
301
-
302
- // Player name
303
- const playerName = document.createElement('div');
304
- playerName.className = `player-name`;
305
- playerName.textContent = `Player ${i}`;
306
- playerContainer.appendChild(playerName);
307
- elements.playerNames.push(playerName);
308
-
309
- // Create wrapper for card and info areas
310
- const playerAreaWrapper = document.createElement('div');
311
- playerAreaWrapper.className = 'player-area-wrapper';
312
- playerContainer.appendChild(playerAreaWrapper);
313
-
314
- // Card area (left side)
315
- const playerCardArea = document.createElement('div');
316
- playerCardArea.className = `player-card-area`;
317
- playerCardArea.innerHTML = `
318
- <div class="player-cards-container"></div>
319
- `;
320
- playerAreaWrapper.appendChild(playerCardArea);
321
- elements.playerCardAreas.push(playerCardArea);
322
-
323
- // TODO: Render chip stack
324
- // Info area (right side)
325
- const playerInfoArea = document.createElement('div');
326
- playerInfoArea.className = `player-info-area`;
327
- playerInfoArea.innerHTML = `
328
- <div class="player-stack">
329
- <span class="player-stack-value">0</span>
330
- </div>
331
- <div class="bet-display" style="display:none;">Bet : 0</div>
332
- `;
333
- playerAreaWrapper.appendChild(playerInfoArea);
334
- elements.playerInfoAreas.push(playerInfoArea);
335
- }
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
+ }
373
+ }
336
374
 
337
- elements.dealerButton = document.createElement('div');
338
- elements.dealerButton.className = 'dealer-button';
339
- elements.dealerButton.textContent = 'D';
340
- elements.dealerButton.style.display = 'none';
341
- elements.playersContainer.appendChild(elements.dealerButton);
342
-
343
- elements.stepCounter = document.createElement('div');
344
- elements.stepCounter.className = 'step-counter';
345
- elements.stepCounter.textContent = 'Standby';
346
- elements.gameLayout.appendChild(elements.stepCounter);
347
- return true;
348
- }
349
-
350
- // --- State Parsing ---
351
- function _parseKagglePokerState(options) {
352
- const { environment, step } = options;
353
- const numPlayers = 2;
354
-
355
- // --- Default State ---
356
- const defaultStateUiData = {
357
- players: [],
358
- communityCards: [],
359
- pot: 0,
360
- isTerminal: false,
361
- };
362
-
363
- // --- Step Validation ---
364
- if (!environment || !environment.steps || !environment.steps[step] || !environment.info?.stateHistory) {
365
- return defaultStateUiData;
366
- }
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
+ }
367
440
 
368
- return getPokerStateForStep(environment, step);
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);
369
505
  }
370
506
 
371
- function _renderPokerTableUI(data, passedOptions) {
372
- if (!elements.pokerTable || !data) return;
373
- const { players, communityCards, pot, isTerminal, step } = data;
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
+ };
374
532
 
375
- // Update step counter
376
- if (elements.stepCounter && step !== undefined) {
377
- elements.stepCounter.textContent = `Step: ${step}`;
378
- }
533
+ // --- Step Validation ---
534
+ if (!environment || !environment.steps || !environment.steps[step] || !environment.info?.stateHistory) {
535
+ return defaultStateUiData;
536
+ }
379
537
 
380
- if (elements.diagnosticHeader && data.rawObservation) {
381
- // Optional: Show diagnostics for debugging
382
- // elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
383
- // elements.diagnosticHeader.style.display = 'block';
384
- }
538
+ return getPokerStateForStep(environment, step);
539
+ }
385
540
 
386
- elements.communityCardsContainer.innerHTML = '';
387
- // Always show 5 slots for the river
388
- // Display cards left to right, with empty slots at the end
389
- const numCommunityCards = 5;
390
- const numCards = communityCards ? communityCards.length : 0;
541
+ function _applyScale(parentElement) {
542
+ if (!parentElement || !elements.gameLayout) return;
391
543
 
392
- // Since the 4th and 5th street cards are appended to the communityCards array, we need to
393
- // reverse it so that the added cards are put at the end of the display area on the board.
394
- if (communityCards) communityCards.reverse();
544
+ const parentWidth = parentElement.clientWidth;
545
+ const parentHeight = parentElement.clientHeight;
395
546
 
396
- // Add actual cards
397
- for (let i = 0; i < numCards; i++) {
398
- elements.communityCardsContainer.appendChild(createCardElement(communityCards[i]));
399
- }
547
+ const baseWidth = 1000;
548
+ const baseHeight = 1000;
400
549
 
401
- // Fill remaining slots with empty cards
402
- for (let i = numCards; i < numCommunityCards; i++) {
403
- const emptyCard = document.createElement('div');
404
- emptyCard.classList.add('card', 'card-empty');
405
- elements.communityCardsContainer.appendChild(emptyCard);
406
- }
550
+ const scaleX = parentWidth / baseWidth;
551
+ const scaleY = parentHeight / baseHeight;
552
+ const scale = Math.min(scaleX, scaleY);
407
553
 
408
- elements.potDisplay.textContent = `Pot : ${pot}`;
409
-
410
- players.forEach((playerData, index) => {
411
- const playerNameElement = elements.playerNames[index];
412
- if (playerNameElement) {
413
- const playerNameText =
414
- playerData.isTurn && !isTerminal ? `${playerData.name} responding...` : playerData.name;
415
- playerNameElement.textContent = playerNameText;
416
-
417
- // Add winner class if player won
418
- if (playerData.isWinner) {
419
- playerNameElement.classList.add('winner');
420
- } else {
421
- playerNameElement.classList.remove('winner');
422
- }
423
- }
554
+ elements.gameLayout.style.transform = `scale(${scale})`;
555
+ }
424
556
 
425
- // Update card area (left side)
426
- const playerCardArea = elements.playerCardAreas[index];
427
- if (playerCardArea) {
428
- const playerCardsContainer = playerCardArea.querySelector('.player-cards-container');
429
- playerCardsContainer.innerHTML = '';
557
+ function _renderPokerTableUI(data, passedOptions) {
558
+ if (!elements.pokerTable || !data) return;
559
+ const { players, communityCards, pot, isTerminal, step } = data;
430
560
 
431
- // In heads-up, we show both hands at the end.
432
- const showCards = isTerminal || (playerData.cards && !playerData.cards.includes(null));
561
+ // Update step counter
562
+ if (elements.stepCounter && step !== undefined) {
563
+ elements.stepCounter.textContent = `Step: ${step}`;
564
+ }
433
565
 
434
- (playerData.cards || [null, null]).forEach((cardStr) => {
435
- playerCardsContainer.appendChild(createCardElement(cardStr, !showCards && cardStr !== null));
436
- });
437
- }
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
+ }
438
571
 
439
- // Update info area (right side)
440
- const playerInfoArea = elements.playerInfoAreas[index];
441
- if (playerInfoArea) {
442
- playerInfoArea.querySelector('.player-stack-value').textContent = `${playerData.stack}`;
443
-
444
- const betDisplay = playerInfoArea.querySelector('.bet-display');
445
- if (playerData.currentBet > 0) {
446
- if (data.lastMoves[index]) {
447
- betDisplay.textContent = data.lastMoves[index];
448
- } else {
449
- if (playerData.isDealer) {
450
- betDisplay.textContent = 'small blind';
451
- } else {
452
- betDisplay.textContent = 'big blind';
453
- }
454
- }
455
- betDisplay.style.display = 'block';
456
- } else {
457
- betDisplay.style.display = 'none';
458
- }
459
- }
460
- });
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;
461
577
 
462
- const dealerPlayerIndex = players.findIndex((p) => p.isDealer);
463
- if (elements.dealerButton) {
464
- if (dealerPlayerIndex !== -1) {
465
- elements.dealerButton.style.display = 'block';
466
- // Remove previous dealer class
467
- elements.dealerButton.classList.remove('dealer-player0', 'dealer-player1');
468
- // Add new dealer class based on player index
469
- elements.dealerButton.classList.add(`dealer-player${dealerPlayerIndex}`);
470
- } else {
471
- elements.dealerButton.style.display = 'none';
472
- }
473
- }
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();
581
+
582
+ // Add actual cards
583
+ for (let i = 0; i < numCards; i++) {
584
+ elements.communityCardsContainer.appendChild(createCardElement(communityCards[i]));
474
585
  }
475
586
 
476
- // --- MAIN EXECUTION LOGIC ---
477
- const { parent } = options;
478
- if (!parent) {
479
- console.error('Renderer: Parent element not provided.');
480
- 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);
481
592
  }
482
593
 
483
- _injectStyles(options);
594
+ elements.potDisplay.textContent = `Total Pot : ${pot}`;
484
595
 
485
- if (!_ensurePokerTableElements(parent, options)) {
486
- console.error('Renderer: Failed to ensure poker table elements.');
487
- parent.innerHTML = '<p style="color:red;">Error: Could not create poker table structure.</p>';
488
- return;
489
- }
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;
602
+
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
+ }
490
609
 
491
- const uiData = _parseKagglePokerState(options);
492
- _renderPokerTableUI(uiData, options);
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));
638
+ });
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
+ }
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
+ }
493
718
  }