kaggle-environments 1.22.6__py3-none-any.whl → 1.24.3__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 (68) hide show
  1. kaggle_environments/envs/connectx/visualizer/default/index.html +13 -0
  2. kaggle_environments/envs/connectx/visualizer/default/package.json +22 -0
  3. kaggle_environments/envs/connectx/visualizer/default/replays/test-replay.json +1129 -0
  4. kaggle_environments/envs/connectx/visualizer/default/src/main.ts +12 -0
  5. kaggle_environments/envs/connectx/visualizer/default/src/renderer.ts +396 -0
  6. kaggle_environments/envs/connectx/visualizer/default/src/style.css +38 -0
  7. kaggle_environments/envs/connectx/visualizer/default/tsconfig.json +4 -0
  8. kaggle_environments/envs/connectx/visualizer/default/vite.config.ts +7 -0
  9. kaggle_environments/envs/open_spiel_env/games/repeated_poker/repeated_poker.js +163 -88
  10. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/index.html +13 -0
  11. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/package.json +23 -0
  12. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/replays/test-replay.json +1 -0
  13. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_first_steps.mjs +202 -0
  14. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_replay.mjs +215 -0
  15. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_steps_with_end_states.mjs +234 -0
  16. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/getRepeatedPokerStateForStep.js +260 -0
  17. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/utils.ts +61 -0
  18. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/debug_repeated_poker_renderer.ts +49 -0
  19. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_1.svg +22 -0
  20. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_10.svg +22 -0
  21. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_100.svg +48 -0
  22. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_25.svg +22 -0
  23. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_5.svg +22 -0
  24. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/main.ts +36 -0
  25. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.ts +573 -0
  26. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/style.css +594 -0
  27. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/tsconfig.json +7 -0
  28. kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/vite.config.ts +6 -0
  29. kaggle_environments/envs/werewolf/README.md +190 -0
  30. kaggle_environments/envs/werewolf/harness/__init__.py +0 -0
  31. kaggle_environments/envs/werewolf/harness/base.py +773 -0
  32. kaggle_environments/envs/werewolf/harness/litellm_models.yaml +51 -0
  33. kaggle_environments/envs/werewolf/harness/main.py +54 -0
  34. kaggle_environments/envs/werewolf/harness/test_base.py +35 -0
  35. kaggle_environments/envs/werewolf/runner.py +146 -0
  36. kaggle_environments/envs/werewolf/scripts/__init__.py +0 -0
  37. kaggle_environments/envs/werewolf/scripts/add_audio.py +425 -0
  38. kaggle_environments/envs/werewolf/scripts/configs/audio/standard.yaml +24 -0
  39. kaggle_environments/envs/werewolf/scripts/configs/run/block_basic.yaml +102 -0
  40. kaggle_environments/envs/werewolf/scripts/configs/run/comprehensive.yaml +100 -0
  41. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_DisableDoctorSelfSave_DisableDoctorConsecutiveSave_large.yaml +104 -0
  42. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_large.yaml +103 -0
  43. kaggle_environments/envs/werewolf/scripts/configs/run/roundrobin_discussion_small.yaml +103 -0
  44. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard.yaml +103 -0
  45. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_DisableDoctorConsecutiveSave.yaml +104 -0
  46. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam.yaml +105 -0
  47. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam_NightEliminationNoReveal_DayExileNoReveal.yaml +105 -0
  48. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_DisableDoctorSelfSave_SeerRevealTeam_NightEliminationRevealTeam_DayExileRevealTeam.yaml +105 -0
  49. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_disable_doctor_self_save.yaml +103 -0
  50. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting.yaml +103 -0
  51. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_no_tie_exile.yaml +103 -0
  52. kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_roundbiddiscussion.yaml +105 -0
  53. kaggle_environments/envs/werewolf/scripts/configs/run/run_config.yaml +58 -0
  54. kaggle_environments/envs/werewolf/scripts/configs/run/vertex_api_example_config.yaml +115 -0
  55. kaggle_environments/envs/werewolf/scripts/measure_cost.py +251 -0
  56. kaggle_environments/envs/werewolf/scripts/plot_existing_trajectories.py +135 -0
  57. kaggle_environments/envs/werewolf/scripts/rerender_html.py +87 -0
  58. kaggle_environments/envs/werewolf/scripts/run.py +93 -0
  59. kaggle_environments/envs/werewolf/scripts/run_block.py +237 -0
  60. kaggle_environments/envs/werewolf/scripts/run_pairwise_matrix.py +222 -0
  61. kaggle_environments/envs/werewolf/scripts/self_play.py +196 -0
  62. kaggle_environments/envs/werewolf/scripts/utils.py +47 -0
  63. kaggle_environments/envs/werewolf/werewolf.json +1 -1
  64. {kaggle_environments-1.22.6.dist-info → kaggle_environments-1.24.3.dist-info}/METADATA +1 -1
  65. {kaggle_environments-1.22.6.dist-info → kaggle_environments-1.24.3.dist-info}/RECORD +68 -7
  66. {kaggle_environments-1.22.6.dist-info → kaggle_environments-1.24.3.dist-info}/WHEEL +0 -0
  67. {kaggle_environments-1.22.6.dist-info → kaggle_environments-1.24.3.dist-info}/entry_points.txt +0 -0
  68. {kaggle_environments-1.22.6.dist-info → kaggle_environments-1.24.3.dist-info}/licenses/LICENSE +0 -0
@@ -12,7 +12,7 @@ function renderer(options) {
12
12
  playerInfoAreas: [],
13
13
  dealerButton: null,
14
14
  diagnosticHeader: null,
15
- gameMessageArea: null,
15
+ stepCounter: null,
16
16
  };
17
17
 
18
18
  const css = `
@@ -43,7 +43,7 @@ function renderer(options) {
43
43
 
44
44
  .poker-renderer-host {
45
45
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
46
- font-family: 'Zeitung Pro', sans-serif; background-color: #2d3748; color: #fff;
46
+ font-family: 'Zeitung Pro', sans-serif; background-color: #1C1D20; color: #fff;
47
47
  overflow: hidden; padding: 1rem; box-sizing: border-box; position: relative;
48
48
  }
49
49
  .poker-game-layout { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: relative; max-width: 750px; max-height: 750px; }
@@ -135,10 +135,11 @@ function renderer(options) {
135
135
  .pot-display { font-size: 40px; font-weight: bold; color: #ffffff; margin-bottom: 30px; }
136
136
  .bet-display {
137
137
  display: inline-block; padding: 10px 20px; border-radius: 12px;
138
- background-color: #1a202c; color: #ffff;
138
+ background-color: #3C4043; color: #ffff;
139
139
  font-family: 'Inter' sans-serif; font-size: 1.75rem; font-weigth: 600;
140
140
  text-align: center;
141
141
  height: 3rem; line-height: 3rem;
142
+ min-width: 200px;
142
143
  }
143
144
  .blind-indicator { font-size: 0.7rem; color: #a0aec0; margin-top: 3px; }
144
145
  .dealer-button {
@@ -148,10 +149,16 @@ function renderer(options) {
148
149
  }
149
150
  .dealer-button.dealer-player0 { bottom: 110px; }
150
151
  .dealer-button.dealer-player1 { top: 110px; }
151
- #game-message-area { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); background-color: rgba(0,0,0,0.6); padding: 5px 10px; border-radius: 5px; font-size: 0.9rem; z-index: 20;}
152
+ .step-counter {
153
+ position: absolute; top: 12px; right: 12px; z-index: 20;
154
+ background-color: rgba(60, 64, 67, 0.9); color: #ffffff;
155
+ padding: 6px 12px; border-radius: 6px;
156
+ font-size: 14px; font-weight: 600;
157
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
158
+ }
152
159
 
153
160
  @media (max-width: 768px) {
154
- .bet-display { font-size: 1.5rem; height: 2.2rem; line-height: 2.2rem; }
161
+ .bet-display { font-size: 1.5rem; height: 2.2rem; line-height: 2.2rem; min-width: 0;}
155
162
  .card { width: 60px; height: 85px; } .card-rank { font-size: 35px; } .card-suit { width: 35px; height: 35px; }
156
163
  .community-cards-container { gap: 6px; }
157
164
  .player-card-area { min-height: 120px; }
@@ -268,10 +275,6 @@ function renderer(options) {
268
275
  elements.diagnosticHeader.style.cssText = "color: lime; background-color: black; padding: 5px; font-size: 12px; position: absolute; top: 0px; left: 0px; z-index: 10001; display: none;"; // Hidden by default
269
276
  parentElement.appendChild(elements.diagnosticHeader);
270
277
 
271
- elements.gameMessageArea = document.createElement('div');
272
- elements.gameMessageArea.id = 'game-message-area';
273
- parentElement.appendChild(elements.gameMessageArea);
274
-
275
278
  elements.gameLayout = document.createElement('div');
276
279
  elements.gameLayout.className = 'poker-game-layout';
277
280
  parentElement.appendChild(elements.gameLayout);
@@ -352,20 +355,93 @@ function renderer(options) {
352
355
  elements.dealerButton.textContent = 'D';
353
356
  elements.dealerButton.style.display = 'none';
354
357
  elements.playersContainer.appendChild(elements.dealerButton);
358
+
359
+ elements.stepCounter = document.createElement('div');
360
+ elements.stepCounter.className = 'step-counter';
361
+ elements.stepCounter.textContent = 'Standby';
362
+ elements.gameLayout.appendChild(elements.stepCounter);
355
363
  return true;
356
364
  }
357
365
 
358
- function _parseACPCState(acpcState) {
366
+ function _getLastMovesACPC(bettingString, currentPlayer) {
367
+ // We will store all human-readable moves here
368
+ const allMoves = [];
369
+
370
+ // Split the action string by street (e.g., ["r5c", "cr11f"])
371
+ const streets = bettingString.split('/');
372
+
373
+ // Process each street's actions
374
+ for (let streetIndex = 0; streetIndex < streets.length; streetIndex++) {
375
+ const streetAction = streets[streetIndex];
376
+ let i = 0;
377
+
378
+ // Preflop (streetIndex 0), action is "open" due to blinds.
379
+ // Postflop (streetIndex > 0), action is "not open" (first player checks or bets).
380
+ let isAggressiveActionOpen = (streetIndex === 0);
381
+
382
+ // 4. Parse the moves within the street
383
+ while (i < streetAction.length) {
384
+ const char = streetAction[i];
385
+ let move = null;
386
+
387
+ if (char === 'c') {
388
+ // 'c' (call/check)
389
+ if (isAggressiveActionOpen) {
390
+ move = 'call';
391
+ } else {
392
+ move = 'check';
393
+ }
394
+ isAggressiveActionOpen = false; // 'c' never leaves action open
395
+ i++;
396
+ } else if (char === 'f') {
397
+ // 'f' (fold)
398
+ move = 'fold';
399
+ isAggressiveActionOpen = false; // 'f' ends the hand
400
+ i++;
401
+ } else if (char === 'r') {
402
+ // 'r' (raise/bet)
403
+ let amount = '';
404
+ i++;
405
+ // Continue to parse all digits of the raise amount
406
+ while (i < streetAction.length && streetAction[i] >= '0' && streetAction[i] <= '9') {
407
+ amount += streetAction[i];
408
+ i++;
409
+ }
410
+ move = `raise ${amount}`;
411
+ isAggressiveActionOpen = true; // 'r' always leaves action open
412
+ } else {
413
+ // Should not happen with valid input, but good to prevent infinite loops
414
+ i++;
415
+ continue;
416
+ }
417
+
418
+ // 5. Store this move in the history
419
+ if (move) {
420
+ allMoves.push(move);
421
+ }
422
+ }
423
+ }
424
+
425
+ // 6. Get the last two moves from our complete list
426
+ const lastMove = allMoves.length > 0 ? allMoves[allMoves.length - 1] : null;
427
+ const secondLastMove = allMoves.length > 1 ? allMoves[allMoves.length - 2] : null;
428
+
429
+ const lastMoves = currentPlayer === 0 ? [secondLastMove, lastMove] : [lastMove, secondLastMove];
430
+
431
+ return lastMoves;
432
+ }
433
+
434
+ function _parseStepHistoryData(universalPokerJSON) {
359
435
  const result = {
360
- p0cards: '',
361
- p1cards: '',
436
+ cards: [],
362
437
  communityCards: '',
363
- p0bet: 0,
364
- p1bet: 0,
438
+ bets: [],
439
+ lastMoves: ['', ''],
440
+ winOdds: [0, 0],
365
441
  };
366
442
 
367
443
  // Split the string into its main lines
368
- const lines = acpcState.trim().split('\n');
444
+ const lines = universalPokerJSON.acpc_state.trim().split('\n');
369
445
  if (lines.length < 2) {
370
446
  console.error("Invalid state string format.");
371
447
  return result;
@@ -374,10 +450,29 @@ function renderer(options) {
374
450
  const stateLine = lines[0]; // example: "STATE:0:r5c/cr11c/:6cKd|AsJc/7hQh6d/2c"
375
451
  const spentLine = lines[1]; // example: "Spent: [P0: 11 P1: 11 ]"
376
452
 
377
- // --- 1. Parse the State Line ---
453
+ // --- Parse the Spent Line ---
454
+ if (spentLine) {
455
+ const p0BetMatch = spentLine.match(/P0:\s*(\d+)/);
456
+ const p1BetMatch = spentLine.match(/P1:\s*(\d+)/);
457
+
458
+ const bets = [0, 0];
459
+
460
+ if (p0BetMatch) {
461
+ bets[0] = parseInt(p0BetMatch[1], 10);
462
+ }
463
+
464
+ if (p1BetMatch) {
465
+ bets[1] = parseInt(p1BetMatch[1], 10);
466
+ }
467
+
468
+ result.bets = bets;
469
+ }
470
+
471
+ // --- Parse the State Line ---
378
472
  if (stateLine) {
379
473
  const stateParts = stateLine.split(':');
380
474
 
475
+ // --- Parse Cards ---
381
476
  // The card string is always the last part
382
477
  const cardString = stateParts[stateParts.length - 1]; // example: "6cKd|AsJc/7hQh6d/2c"
383
478
 
@@ -389,8 +484,7 @@ function renderer(options) {
389
484
  const playerHands = cardSegments[0].split('|');
390
485
  if (playerHands.length >= 2) {
391
486
  // example: "6cKd"
392
- result.p0cards = playerHands[0];
393
- result.p1cards = playerHands[1];
487
+ result.cards = [playerHands[0], playerHands[1]];
394
488
  }
395
489
  }
396
490
 
@@ -399,22 +493,22 @@ function renderer(options) {
399
493
  .slice(1) // gets all elements AFTER the player hands
400
494
  .filter(Boolean) // removes any empty strings (e.g., from a trailing "/")
401
495
  .join(''); // joins the remaining segments into a single string
402
- }
403
496
 
404
- // --- 2. Parse the Spent Line ---
405
- if (spentLine) {
406
- const p0BetMatch = spentLine.match(/P0:\s*(\d+)/);
407
- const p1BetMatch = spentLine.match(/P1:\s*(\d+)/);
497
+ // --- Parse Betting String --
498
+ // The betting string is everything between the 2nd colon and the last colon.
499
+ // This handles edge cases like "STATE:0:r5c/cr11c/:cards"
500
+ const bettingString = stateParts.slice(2, stateParts.length - 1).join(':');
408
501
 
409
- if (p0BetMatch) {
410
- result.p0bet = parseInt(p0BetMatch[1], 10);
411
- }
412
-
413
- if (p1BetMatch) {
414
- result.p1bet = parseInt(p1BetMatch[1], 10);
502
+ if (bettingString) {
503
+ result.lastMoves = _getLastMovesACPC(bettingString, universalPokerJSON.current_player);
415
504
  }
416
505
  }
417
506
 
507
+ // Parse win odds
508
+ p0WinOdds = Number(universalPokerJSON.odds[0]).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 2 })
509
+ p1WinOdds = Number(universalPokerJSON.odds[1]).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 2 })
510
+ result.winOdds = [p0WinOdds, p1WinOdds];
511
+
418
512
  return result;
419
513
  }
420
514
 
@@ -433,7 +527,7 @@ function renderer(options) {
433
527
  const numPlayers = 2;
434
528
 
435
529
  // --- Default State ---
436
- const defaultUIData = {
530
+ const stateUIData = {
437
531
  players: Array(numPlayers).fill(null).map((_, i) => {
438
532
  const agentName = environment?.info?.TeamNames?.[i] ||
439
533
  `Player ${i}`;
@@ -443,23 +537,24 @@ function renderer(options) {
443
537
  stack: 0,
444
538
  cards: [], // Will be filled with nulls or cards
445
539
  currentBet: 0,
446
- position: i === 0 ? "Small Blind" : "Big Blind",
447
540
  isDealer: i === 0,
448
541
  isTurn: false,
449
- status: "Waiting...",
450
542
  reward: null
451
543
  };
452
544
  }),
453
545
  communityCards: [],
454
546
  pot: 0,
455
547
  isTerminal: false,
456
- gameMessage: "Initializing...",
548
+ blinds: [1, 2],
549
+ lastMoves: [],
457
550
  rawObservation: null, // For debugging
551
+ step: step,
458
552
  };
459
553
 
460
554
  // --- Step Validation ---
461
555
  if (!environment || !environment.steps || !environment.steps[step] || !environment.info) {
462
- return defaultUIData;
556
+ // return default state
557
+ return stateUIData;
463
558
  }
464
559
 
465
560
  currentStateHistory = JSON.parse(environment.info.stateHistory[step]);
@@ -468,45 +563,44 @@ function renderer(options) {
468
563
  // TODO: Handle the flop phase steps (chance steps)
469
564
 
470
565
  currentUniversalPokerJSON = _getCurrentStepUniversalPokerJSON(options);
471
- currentStepFromStateHistory = _parseACPCState(currentUniversalPokerJSON.acpc_state);
566
+ currentStepFromStateHistory = _parseStepHistoryData(currentUniversalPokerJSON);
472
567
 
473
568
  const currentStepAgents = environment.steps[step];
474
569
  if (!currentStepAgents || currentStepAgents.length < numPlayers) {
475
- defaultUIData.gameMessage = "Waiting for agent data...";
476
- return defaultUIData;
570
+ return stateUIData;
477
571
  }
478
572
 
479
-
480
- const pot_size = currentStepFromStateHistory.p0bet + currentStepFromStateHistory.p1bet;
481
- const player_contributions = [currentStepFromStateHistory.p0bet, currentStepFromStateHistory.p1bet];
573
+ const pot_size = currentStepFromStateHistory.bets.reduce((a, b) => a + b, 0);
574
+ const player_contributions = currentStepFromStateHistory.bets;
482
575
  const starting_stacks = currentUniversalPokerJSON.starting_stacks;
483
576
  const player_hands = [
484
- currentStepFromStateHistory.p0cards ? currentStepFromStateHistory.p0cards.match(/.{1,2}/g) : [],
485
- currentStepFromStateHistory.p1cards ? currentStepFromStateHistory.p1cards.match(/.{1,2}/g) : [],
577
+ currentStepFromStateHistory.cards[0]?.match(/.{1,2}/g) || [],
578
+ currentStepFromStateHistory.cards[1]?.match(/.{1,2}/g) || []
486
579
  ];
487
580
  const board_cards = currentStepFromStateHistory.communityCards ? currentStepFromStateHistory.communityCards.match(/.{1,2}/g).reverse() : [];
488
- const current_player = currentStepFromStateHistory.current_player;
489
- const betting_history = currentStepFromStateHistory.betting_history;
490
581
 
491
582
  // TODO: Add odds, best_five_card_hands best_hand_rank_types
492
583
 
493
- const isTerminal = false // TODO: read isTerminal from observation
494
- defaultUIData.isTerminal = isTerminal;
495
- defaultUIData.pot = pot_size || 0;
496
- defaultUIData.communityCards = board_cards || [];
584
+ // TODO: Add current player
497
585
 
586
+ const isTerminal = false // TODO: read isTerminal from observation
587
+ stateUIData.isTerminal = isTerminal;
588
+ stateUIData.pot = pot_size || 0;
589
+ stateUIData.communityCards = board_cards || [];
590
+ stateUIData.lastMoves = currentStepFromStateHistory.lastMoves;
591
+ stateUIData.blinds = currentUniversalPokerState.blinds;
498
592
 
499
- // --- Update Player Pods ---
593
+ // --- Update Players ---
500
594
  for (let i = 0; i < numPlayers; i++) {
501
- const pData = defaultUIData.players[i];
595
+ const pData = stateUIData.players[i];
502
596
  const contribution = player_contributions ? player_contributions[i] : 0;
503
597
  const startStack = starting_stacks ? starting_stacks[i] : 0;
504
598
 
505
599
  pData.currentBet = contribution;
506
600
  pData.stack = startStack - contribution;
507
601
  pData.cards = (player_hands[i] || []).map(c => c === "??" ? null : c);
508
- pData.isTurn = String(i) === String(current_player);
509
- pData.status = pData.position; // Default status
602
+ pData.isTurn = currentUniversalPokerState.current_player === i;
603
+ pData.isDealer = currentUniversalPokerState.blinds[i] === 1; // infer dealer from small blind
510
604
 
511
605
  if (isTerminal) {
512
606
  const reward = environment.rewards ? environment.rewards[i] : null;
@@ -523,52 +617,24 @@ function renderer(options) {
523
617
  }
524
618
  }
525
619
 
526
- // Handle folded player status
527
- if (!isTerminal && betting_history && betting_history.includes('f')) {
528
- // A simple fold check: the player who didn't make the last action and isn't the current player might have folded.
529
- // This is a simplification. A more robust parser would track the betting sequence.
530
- const lastAction = betting_history.slice(-1);
531
- if (lastAction === 'f') {
532
- // Find who is NOT the current player
533
- const nonCurrentPlayerIndex = current_player === '0' ? 1 : 0;
534
- // If they are not all-in, they folded.
535
- if (defaultUIData.players[nonCurrentPlayerIndex].status !== 'All-in') {
536
- defaultUIData.players[nonCurrentPlayerIndex].status = "Folded";
537
- }
538
- }
539
- }
540
-
541
-
542
- // --- Set Game Message ---
543
- if (isTerminal) {
544
- const winnerIndex = environment.rewards ? environment.rewards.findIndex(r => r > 0) : -1;
545
- if (winnerIndex !== -1) {
546
- defaultUIData.gameMessage = `Player ${winnerIndex} wins!`;
547
- } else {
548
- defaultUIData.gameMessage = "Game Over.";
549
- }
550
- } else if (current_player === "chance") {
551
- defaultUIData.gameMessage = `Dealing...`;
552
- } else {
553
- defaultUIData.gameMessage = `Player ${current_player}'s turn.`;
554
- }
555
-
556
- return defaultUIData;
620
+ return stateUIData;
557
621
  }
558
622
 
559
623
 
560
624
  function _renderPokerTableUI(data, passedOptions) {
561
625
  if (!elements.pokerTable || !data) return;
562
- const { players, communityCards, pot, isTerminal, gameMessage } = data;
626
+ const { players, communityCards, pot, isTerminal, step } = data;
627
+
628
+ // Update step counter
629
+ if (elements.stepCounter && step !== undefined) {
630
+ elements.stepCounter.textContent = `Step: ${step}`;
631
+ }
563
632
 
564
633
  if (elements.diagnosticHeader && data.rawObservation) {
565
634
  // Optional: Show diagnostics for debugging
566
635
  // elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
567
636
  // elements.diagnosticHeader.style.display = 'block';
568
637
  }
569
- if (elements.gameMessageArea) {
570
- elements.gameMessageArea.textContent = gameMessage;
571
- }
572
638
 
573
639
  elements.communityCardsContainer.innerHTML = '';
574
640
  // Always show 5 slots for the river
@@ -630,7 +696,16 @@ function renderer(options) {
630
696
 
631
697
  const betDisplay = playerInfoArea.querySelector('.bet-display');
632
698
  if (playerData.currentBet > 0) {
633
- betDisplay.textContent = `Bet: ${playerData.currentBet}`;
699
+ if (data.lastMoves[index]) {
700
+ betDisplay.textContent = data.lastMoves[index];
701
+ }
702
+ else {
703
+ if (playerData.isDealer) {
704
+ betDisplay.textContent = 'Small Blind';
705
+ } else {
706
+ betDisplay.textContent = 'Big Blind';
707
+ }
708
+ }
634
709
  betDisplay.style.display = 'block';
635
710
  } else {
636
711
  betDisplay.style.display = 'none';
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Repeated Poker Visualizer</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@kaggle-environments/repeated-poker-visualizer",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "dev-with-replay": "cross-env VITE_REPLAY_FILE=./replays/test-replay.json vite",
9
+ "build": "tsc && vite build",
10
+ "preview": "vite preview"
11
+ },
12
+ "devDependencies": {
13
+ "cross-env": "^10.1.0",
14
+ "typescript": "^5.0.0",
15
+ "vite": "^5.0.0"
16
+ },
17
+ "dependencies": {
18
+ "@kaggle-environments/core": "workspace:*",
19
+ "htm": "^3.1.1",
20
+ "json-formatter-js": "^2.5.23",
21
+ "preact": "^10.13.2"
22
+ }
23
+ }