Skip to content

Commit a4bac37

Browse files
authored
Merge pull request #531 from Kaggle/repeated-poker-moar-tweaks
[Repeated Poker] Minor style improvements, better labels, and hand tracking
2 parents fa3ec9d + 2cde4b4 commit a4bac37

File tree

7 files changed

+119
-90
lines changed

7 files changed

+119
-90
lines changed

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/default/src/main.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Player, GameAdapter, ReplayData, ChessStep } from '@kaggle-environments/core';
1+
import { createReplayVisualizer, GameAdapter, ReplayData, ChessStep } from '@kaggle-environments/core';
22
import { renderer } from './chess_renderer';
33
import { render } from 'preact';
44

@@ -48,5 +48,10 @@ class LegacyAdapter implements GameAdapter {
4848

4949
const app = document.getElementById('app');
5050
if (app) {
51-
new Player(app, new LegacyAdapter());
51+
// Set up an HMR boundary for development
52+
if (import.meta.env?.DEV && import.meta.hot) {
53+
import.meta.hot.accept();
54+
}
55+
56+
createReplayVisualizer(app, new LegacyAdapter());
5257
}

kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.ts

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -344,23 +344,10 @@ export function renderer(options: RendererOptions): void {
344344
function _renderPokerTableUI(data: RepeatedPokerStep): void {
345345
if (!elements.pokerTable || !data || !elements.legend) return;
346346

347-
// TODO: [TYPE_MISMATCH] The 'RepeatedPokerStep' type is missing many properties
348-
// that the original JS code expects.
349-
const {
350-
players, // This exists in BaseGameStep
351-
communityCards, // This is a string in RepeatedPokerStep, but JS expects string[]
352-
stepType,
353-
pot,
354-
winOdds,
355-
bestFiveCardHands,
356-
bestHandRankTypes,
357-
} = data;
347+
const { players, communityCards, stepType, pot, winOdds, bestFiveCardHands, bestHandRankTypes, currentHandIndex } =
348+
data;
358349

359350
// TODO: [TYPE_MISMATCH] Manually defining missing properties from the type.
360-
const isTerminal = false; // 'isTerminal' is not in RepeatedPokerStep
361-
const handCount = 0; // 'handCount' is not in RepeatedPokerStep
362-
const winProb = winOdds; // 'winProb' is not in type, mapping 'winOdds'
363-
const handRank = bestHandRankTypes;
364351
const leaderInfo: any = null; // 'leaderInfo' is not in type. Using 'any' to allow compilation.
365352

366353
// Update legend
@@ -372,7 +359,7 @@ export function renderer(options: RendererOptions): void {
372359
legendTitle.innerHTML = ''; // Clear existing content
373360

374361
const handSpan = document.createElement('span');
375-
handSpan.textContent = `Hand: ${handCount !== undefined && handCount !== null ? handCount + 1 : 'Standby'}`;
362+
handSpan.textContent = `Hand: ${currentHandIndex != null ? currentHandIndex + 1 : 'Standby'}`;
376363
legendTitle.appendChild(handSpan);
377364

378365
if (leaderInfo) {
@@ -481,7 +468,7 @@ export function renderer(options: RendererOptions): void {
481468
playerCardsContainer.innerHTML = '';
482469

483470
// In heads-up, we show both hands at the end.
484-
const showCards = isTerminal || (playerData.cards && !playerData.cards.includes(null!));
471+
const showCards = playerData.cards && !playerData.cards.includes(null!);
485472

486473
// TODO: [TYPE_MISMATCH] 'playerData.cards' is a string, but code expects an array - move this to the transformer
487474
const playerCardsArray = playerData.cards ? playerData.cards.match(/.{1,2}/g) : [null, null];
@@ -543,27 +530,28 @@ export function renderer(options: RendererOptions): void {
543530
}
544531
}
545532

546-
// TODO: swap this out bestHandRankType
547533
const handRankElement = playerInfoArea.querySelector('.player-hand-rank') as HTMLElement;
548-
if (handRankElement && handRank && handRank[index]) {
549-
handRankElement.textContent = handRank[index];
534+
if (handRankElement && bestHandRankTypes && bestHandRankTypes[index]) {
535+
handRankElement.textContent = bestHandRankTypes[index];
550536
} else if (handRankElement) {
551537
handRankElement.textContent = '';
552538
}
553539

554540
const playerOddsElement = playerInfoArea.querySelector('.player-odds') as HTMLElement;
555-
if (playerOddsElement && winProb && winProb[index] && !isTerminal) {
556-
let oddsString = `WIN: ${winProb[index].toLocaleString(undefined, {
541+
// WinOdds is an array like [P0WinOdds, P0TieOdds, P1WinOdds, P1TieOdds] - our index is either 0 or 1, so
542+
// for the P1 case just set the index to 2
543+
const winOddsIndex = index === 0 ? 0 : 2;
544+
if (playerOddsElement && winOdds && winOdds[winOddsIndex] != undefined) {
545+
const winOddsStringForPlayer = `WIN: ${winOdds[winOddsIndex].toLocaleString(undefined, {
546+
style: 'percent',
547+
minimumFractionDigits: 2,
548+
})}`;
549+
const tieOddsStringForPlayer = `TIE: ${winOdds[winOddsIndex + 1].toLocaleString(undefined, {
557550
style: 'percent',
558551
minimumFractionDigits: 2,
559552
})}`;
560553

561-
if (winProb[index + 2]) {
562-
oddsString = `${oddsString} · TIE: ${winProb[index + 1].toLocaleString(undefined, {
563-
style: 'percent',
564-
minimumFractionDigits: 2,
565-
})}`;
566-
}
554+
const oddsString = `${winOddsStringForPlayer} · ${tieOddsStringForPlayer}`;
567555

568556
playerOddsElement.textContent = oddsString;
569557
} else if (playerOddsElement) {

pnpm-lock.yaml

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/core/src/transformers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const defaultGetGameStepDescription = (gameStep: BaseGameStep) => {
3333
export const processEpisodeData = (environment: any, gameName: string): BaseGameStep[] => {
3434
switch (gameName) {
3535
case 'repeated_poker':
36+
console.log('trasnsforming the entire thing');
3637
return repeatedPokerTransformerV2(environment);
3738
case 'chess':
3839
return chessTransformer(environment);

web/core/src/transformers/repeated_poker/v1/repeatedPokerTransformer.ts

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { RepeatedPokerStep } from '../v2/poker-steps-types';
1+
import { RepeatedPokerStep, RepeatedPokerStepPlayer } from '../v2/poker-steps-types';
22
import { getActionStringsFromACPC } from './buildTimeline';
33

44
// const _parseRoundState = (currentStateHistory: string) => {
55
// const currentState = JSON.parse(JSON.parse(currentStateHistory).current_universal_poker_json).acpc_state;
6-
//
6+
//
77
// /**
88
// * Example lines:
99
// * STATE:0:r5c/cr9c/:Ks4s|5hAs/2dJs7s/Qh
@@ -14,13 +14,13 @@ import { getActionStringsFromACPC } from './buildTimeline';
1414
// return '';
1515
// }
1616
// const stateParts = lines[0].split(':');
17-
//
17+
//
1818
// const currentCardString = stateParts[stateParts.length - 1]; // example: "6cKd|AsJc/7hQh6d/2c"
1919
// // Grab the hand and board blocks
2020
// const currentCardSegments = currentCardString.split('|');
2121
// // Split card string by '/' to separate hand and board blocks
2222
// const currentCommunitySegments = currentCardSegments.length > 1 ? currentCardSegments[1].split('/') : [];
23-
//
23+
//
2424
// if (currentCommunitySegments.length === 2) {
2525
// return '### Flop';
2626
// } else if (currentCommunitySegments.length === 3) {
@@ -32,7 +32,6 @@ import { getActionStringsFromACPC } from './buildTimeline';
3232
// }
3333
// };
3434

35-
3635
const _isStateHistoryAgentAction = (stateHistoryEntry: string): boolean =>
3736
JSON.parse(JSON.parse(stateHistoryEntry).current_universal_poker_json).current_player !== -1;
3837
const _isStateHistoryEntryInitial = (stateHistoryEntry: string): boolean => {
@@ -192,13 +191,13 @@ const _getEndCondition = (
192191
// for now, fold + tie = impossible state
193192
handConclusion: 'fold',
194193
winner: -1,
195-
bestFiveCardHands: []
194+
bestFiveCardHands: [],
196195
};
197196
}
198197
let next_prev_universal_poker_json = {
199198
acpc_state: '',
200199
best_five_card_hands: ['', ''],
201-
best_hand_rank_types: ['', '']
200+
best_hand_rank_types: ['', ''],
202201
};
203202
// since the current_universal_poker_json does not contain the end move in it's history,
204203
// we need to go to the prev_universal_poker_json of the next one
@@ -216,70 +215,75 @@ const _getEndCondition = (
216215
if (moves.pop() === 'f') {
217216
return {
218217
handConclusion: 'fold',
219-
winner: current_player === 0 ? 1 : 0
218+
winner: current_player === 0 ? 1 : 0,
220219
};
221220
}
222221
// Showdown case
223222
return {
224223
handConclusion: 'showdown',
225224
winner: current_player === 0 ? 1 : 0,
226225
bestFiveCardHands: next_prev_universal_poker_json.best_five_card_hands,
227-
bestHandRankType: next_prev_universal_poker_json.best_hand_rank_types
226+
bestHandRankType: next_prev_universal_poker_json.best_hand_rank_types,
228227
};
229228
};
230229

231230
export const getPokerStepLabel = (gameStep: RepeatedPokerStep) => {
232-
let pokerStepLabel = `TODO - label ${gameStep.stepType}`;
233-
if(gameStep.stepType === 'player-action') {
234-
pokerStepLabel = gameStep.players[gameStep.currentPlayer].actionDisplayText ?? "";
231+
const currentHandNumber = gameStep.currentHandIndex + 1;
232+
switch (gameStep.stepType) {
233+
case 'player-action':
234+
return `**Decision**: ${gameStep.players[gameStep.currentPlayer].actionDisplayText ?? ''}`;
235+
case 'deal-player-hands':
236+
return `**Hand ${currentHandNumber}**: Dealing...`;
237+
case 'deal-flop':
238+
return `**Hand ${currentHandNumber}**: Flop`;
239+
case 'deal-turn':
240+
return `**Hand ${currentHandNumber}**: Turn`;
241+
case 'deal-river':
242+
return `**Hand ${currentHandNumber}**: River`;
243+
case 'big-blind-post':
244+
return `**Hand ${currentHandNumber}**: Post Big Blind`;
245+
case 'small-blind-post':
246+
return `**Hand ${currentHandNumber}**: Post Small Blind`;
247+
case 'final': {
248+
const winningPlayer = (gameStep.players as RepeatedPokerStepPlayer[]).find((p) => p.isWinner);
249+
return `**Hand ${currentHandNumber}**: 🎉 ${winningPlayer?.name} wins ${winningPlayer?.reward}! 🎉`;
250+
}
251+
default: {
252+
// If you missed a case, TypeScript will complain here because
253+
// it cannot assign the missed type to 'never'.
254+
const _exhaustiveCheck: never = gameStep.stepType;
255+
return `Unknown step: ${_exhaustiveCheck}`;
256+
}
235257
}
236-
237-
return pokerStepLabel;
238258
};
239259

240260
export const getPokerStepDescription = (gameStep: RepeatedPokerStep) => {
241-
let pokerStepDescription = `TODO - description ${gameStep.stepType}`;
242-
if(gameStep.stepType === 'player-action') {
243-
pokerStepDescription = gameStep.players[gameStep.currentPlayer].thoughts ?? "";
244-
}
245-
246-
return pokerStepDescription;
247-
248-
/* if (gameStep.step?.action?.thoughts) {
249-
return gameStep.step.action.thoughts;
250-
} else if (gameStep.isEndState && gameStep.winner !== undefined) {
251-
if (gameStep.winner === -1) {
252-
return `
253-
### ${playerNames.join(' and ')} tie
254-
`;
255-
}
256-
257-
const loser = gameStep.winner.toString() === '0' ? 1 : 0;
258-
if (gameStep.handConclusion === 'showdown') {
259-
return `
260-
###🎉 ${playerNames[gameStep.winner]} wins round ${gameStep.hand + 1}
261-
#### ${gameStep.bestHandRankType?.join(' and ')}
262-
`;
263-
} else {
264-
return `
265-
###🎉 ${playerNames[gameStep.winner]} wins round ${gameStep.hand + 1}
266-
#### ${playerNames[loser]} folds
267-
`;
261+
switch (gameStep.stepType) {
262+
case 'player-action':
263+
return gameStep.players[gameStep.currentPlayer].thoughts ?? '';
264+
case 'deal-player-hands':
265+
return '';
266+
case 'deal-flop':
267+
return '';
268+
case 'deal-turn':
269+
return '';
270+
case 'deal-river':
271+
return '';
272+
case 'big-blind-post':
273+
return '';
274+
case 'small-blind-post':
275+
return '';
276+
case 'final':
277+
return '';
278+
default: {
279+
// If you missed a case, TypeScript will complain here because
280+
// it cannot assign the missed type to 'never'.
281+
const _exhaustiveCheck: never = gameStep.stepType;
282+
return `Unknown step: ${_exhaustiveCheck}`;
268283
}
269284
}
270-
271-
return _parseRoundState(gameStep.stateHistory); */
272-
return 'TODO - get Step Description if needed'
273285
};
274286

275-
/* interface TimelineEvent {
276-
stateIndex: number | undefined;
277-
highlightPlayer: number | null;
278-
actionText: string;
279-
hideHoleCards: boolean;
280-
hideCommunity: boolean;
281-
} */
282-
283287
export const getPokerStepsWithEndStates = (environment: any): any[] => {
284288
const stepsWithEndStates: any[] = [];
285289
let handCount = 0;
@@ -379,7 +383,7 @@ export const getPokerStepsWithEndStates = (environment: any): any[] => {
379383
actingPlayer,
380384
actingPlayerName: getPlayerName(actingPlayer),
381385
currentPlayer,
382-
currentPlayerName: getPlayerName(currentPlayer)
386+
currentPlayerName: getPlayerName(currentPlayer),
383387
});
384388

385389
lastActionPointer = preActionPointer;
@@ -403,7 +407,7 @@ export const getPokerStepsWithEndStates = (environment: any): any[] => {
403407
actingPlayer,
404408
actingPlayerName: getPlayerName(actingPlayer),
405409
currentPlayer: postActionCurrentPlayer,
406-
currentPlayerName: getPlayerName(postActionCurrentPlayer)
410+
currentPlayerName: getPlayerName(postActionCurrentPlayer),
407411
});
408412
}
409413

@@ -437,7 +441,7 @@ export const getPokerStepsWithEndStates = (environment: any): any[] => {
437441
stateHistoryIndex: lastActionPointer,
438442
currentPlayer: endStateCurrentPlayer,
439443
currentPlayerName: getPlayerName(endStateCurrentPlayer),
440-
...endState
444+
...endState,
441445
});
442446

443447
handCount++;
@@ -470,7 +474,7 @@ export const getPokerStepsWithEndStates = (environment: any): any[] => {
470474
// Return a new step with action strings added
471475
return {
472476
...step,
473-
actionText: nextPlayerIndex ? playerActionStrings[nextPlayerIndex] : undefined
477+
actionText: nextPlayerIndex ? playerActionStrings[nextPlayerIndex] : undefined,
474478
};
475479
} catch (error) {
476480
console.error('Error adding action strings to step:', error);
@@ -539,5 +543,5 @@ export const getPokerStepsWithEndStates = (environment: any): any[] => {
539543

540544
export const __testing = {
541545
_getMovesFromBettingStringACPC,
542-
_getReadableMovesFromBettingStringACPC
546+
_getReadableMovesFromBettingStringACPC,
543547
};

web/core/src/transformers/repeated_poker/v2/poker-steps-types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BaseGamePlayer, BaseGameStep } from "../../../types";
1+
import { BaseGamePlayer, BaseGameStep } from '../../../types';
22

33
export type RepeatedPokerStepType =
44
| 'small-blind-post'
@@ -18,6 +18,7 @@ export interface RepeatedPokerStep extends BaseGameStep {
1818
bestFiveCardHands: string[];
1919
bestHandRankTypes: string[];
2020
currentPlayer: number;
21+
currentHandIndex: number;
2122
}
2223

2324
export interface RepeatedPokerStepPlayer extends BaseGamePlayer {

0 commit comments

Comments
 (0)