kaggle-environments 1.23.10__py3-none-any.whl → 1.23.12__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.
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_first_steps.mjs +202 -0
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_replay.mjs +215 -0
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_steps_with_end_states.mjs +234 -0
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/getRepeatedPokerStateForStep.js +83 -30
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.js +255 -33
- {kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/METADATA +1 -1
- {kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/RECORD +10 -7
- {kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/WHEEL +0 -0
- {kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/entry_points.txt +0 -0
- {kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
|
|
7
|
+
function parseArgs(defaultReplayPath, defaultStepLimit) {
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
let replayPath = defaultReplayPath;
|
|
10
|
+
let stepLimit = defaultStepLimit;
|
|
11
|
+
|
|
12
|
+
const usage = `Usage: node ${path.basename(
|
|
13
|
+
process.argv[1]
|
|
14
|
+
)} [options]\n\nOptions:\n -r, --replay <path> Replay JSON to inspect (default: ${defaultReplayPath})\n -s, --steps <count> Number of steps to print (default: ${defaultStepLimit})\n -h, --help Show this help message\n`;
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
17
|
+
const arg = args[i];
|
|
18
|
+
if (arg === "-h" || arg === "--help") {
|
|
19
|
+
console.log(usage);
|
|
20
|
+
process.exit(0);
|
|
21
|
+
} else if ((arg === "-r" || arg === "--replay") && args[i + 1]) {
|
|
22
|
+
replayPath = path.resolve(args[i + 1]);
|
|
23
|
+
i += 1;
|
|
24
|
+
} else if ((arg === "-s" || arg === "--steps") && args[i + 1]) {
|
|
25
|
+
const value = Number(args[i + 1]);
|
|
26
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
27
|
+
console.error("Steps limit must be a positive integer.");
|
|
28
|
+
console.log(usage);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
stepLimit = value;
|
|
32
|
+
i += 1;
|
|
33
|
+
} else {
|
|
34
|
+
console.error(`Unknown argument: ${arg}`);
|
|
35
|
+
console.log(usage);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { replayPath, stepLimit };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function truncate(text, maxLength = 120) {
|
|
44
|
+
if (typeof text !== "string") {
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
if (text.length <= maxLength) {
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function summarizeAction(action) {
|
|
54
|
+
if (action == null) {
|
|
55
|
+
return "-";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof action !== "object") {
|
|
59
|
+
return String(action);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const parts = [];
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(action, "actionString")) {
|
|
64
|
+
parts.push(`actionString=${action.actionString}`);
|
|
65
|
+
}
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(action, "submission")) {
|
|
67
|
+
parts.push(`submission=${action.submission}`);
|
|
68
|
+
}
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(action, "status") && action.status) {
|
|
70
|
+
parts.push(`status=${truncate(action.status, 60)}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (parts.length > 0) {
|
|
74
|
+
return parts.join(" | ");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return truncate(JSON.stringify(action));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function summarizeObservation(observation) {
|
|
81
|
+
if (observation == null) {
|
|
82
|
+
return "-";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof observation !== "object") {
|
|
86
|
+
return String(observation);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const parts = [];
|
|
90
|
+
if (Object.prototype.hasOwnProperty.call(observation, "step")) {
|
|
91
|
+
parts.push(`step=${observation.step}`);
|
|
92
|
+
}
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(observation, "currentPlayer")) {
|
|
94
|
+
parts.push(`currentPlayer=${observation.currentPlayer}`);
|
|
95
|
+
}
|
|
96
|
+
if (Object.prototype.hasOwnProperty.call(observation, "isTerminal")) {
|
|
97
|
+
parts.push(`isTerminal=${observation.isTerminal}`);
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
Object.prototype.hasOwnProperty.call(observation, "observationString") &&
|
|
101
|
+
observation.observationString
|
|
102
|
+
) {
|
|
103
|
+
parts.push(
|
|
104
|
+
`observationString="${truncate(observation.observationString, 80)}"`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (
|
|
108
|
+
Object.prototype.hasOwnProperty.call(
|
|
109
|
+
observation,
|
|
110
|
+
"serializedGameAndState"
|
|
111
|
+
) &&
|
|
112
|
+
observation.serializedGameAndState
|
|
113
|
+
) {
|
|
114
|
+
parts.push("serializedGameAndState=<omitted>");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (parts.length > 0) {
|
|
118
|
+
return parts.join(" | ");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return truncate(JSON.stringify(observation));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
126
|
+
const __dirname = path.dirname(__filename);
|
|
127
|
+
const defaultReplayPath = path.resolve(
|
|
128
|
+
__dirname,
|
|
129
|
+
"../replays/test-replay.json"
|
|
130
|
+
);
|
|
131
|
+
const defaultStepLimit = 5;
|
|
132
|
+
|
|
133
|
+
const { replayPath, stepLimit } = parseArgs(
|
|
134
|
+
defaultReplayPath,
|
|
135
|
+
defaultStepLimit
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
let replayRaw;
|
|
139
|
+
try {
|
|
140
|
+
replayRaw = await readFile(replayPath, "utf-8");
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(`Failed to read replay at ${replayPath}: ${error.message}`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let replay;
|
|
147
|
+
try {
|
|
148
|
+
replay = JSON.parse(replayRaw);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(
|
|
151
|
+
`Replay file ${replayPath} is not valid JSON: ${error.message}`
|
|
152
|
+
);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const steps = Array.isArray(replay?.steps) ? replay.steps : [];
|
|
157
|
+
if (steps.length === 0) {
|
|
158
|
+
console.error("Replay does not contain any steps.");
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const limit = Math.min(stepLimit, steps.length);
|
|
163
|
+
console.log(`Loaded replay: ${replayPath}`);
|
|
164
|
+
console.log(`Printing first ${limit} of ${steps.length} steps\n`);
|
|
165
|
+
|
|
166
|
+
for (let stepIndex = 0; stepIndex < limit; stepIndex += 1) {
|
|
167
|
+
const step = steps[stepIndex];
|
|
168
|
+
console.log(`Step ${stepIndex}`);
|
|
169
|
+
|
|
170
|
+
if (!Array.isArray(step)) {
|
|
171
|
+
console.log(" (step is not an array)\n");
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
step.forEach((playerStep, playerIndex) => {
|
|
176
|
+
if (!playerStep || typeof playerStep !== "object") {
|
|
177
|
+
console.log(` Player ${playerIndex}: <invalid step payload>`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const { action, observation, reward, status } = playerStep;
|
|
182
|
+
const actionSummary = summarizeAction(action);
|
|
183
|
+
const observationSummary = summarizeObservation(observation);
|
|
184
|
+
const rewardSummary =
|
|
185
|
+
reward == null ? "-" : JSON.stringify(reward, null, 0);
|
|
186
|
+
const statusSummary = status ?? "-";
|
|
187
|
+
|
|
188
|
+
console.log(
|
|
189
|
+
` Player ${playerIndex}: status=${statusSummary} | reward=${rewardSummary}`
|
|
190
|
+
);
|
|
191
|
+
console.log(` action: ${actionSummary}`);
|
|
192
|
+
console.log(` observation: ${observationSummary}`);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
console.log("");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
main().catch((error) => {
|
|
200
|
+
console.error("Failed to print steps:", error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
|
|
8
|
+
async function loadCoreModules(repoRoot) {
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const corePath = path.resolve(repoRoot, "web/core/dist/index.umd.cjs");
|
|
11
|
+
const timelinePath = path.resolve(
|
|
12
|
+
repoRoot,
|
|
13
|
+
"web/core/dist/transformers/buildTimeline.js"
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const { processEpisodeData } = require(corePath);
|
|
17
|
+
const { buildTimeline, getPokerStateForStep } = await import(
|
|
18
|
+
pathToFileURL(timelinePath).href
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return { processEpisodeData, buildTimeline, getPokerStateForStep };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(defaultReplayPath, defaultStepLimit) {
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
let replayPath = defaultReplayPath;
|
|
27
|
+
let stepLimit = defaultStepLimit;
|
|
28
|
+
|
|
29
|
+
const usage = `Usage: node ${path.basename(
|
|
30
|
+
process.argv[1]
|
|
31
|
+
)} [options]\n\nOptions:\n -r, --replay <path> Replay JSON to inspect (default: ${defaultReplayPath})\n -s, --steps <count> Number of timeline steps to print (default: ${defaultStepLimit})\n -h, --help Show this help message\n`;
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
34
|
+
const arg = args[i];
|
|
35
|
+
if (arg === "-h" || arg === "--help") {
|
|
36
|
+
console.log(usage);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
} else if ((arg === "-r" || arg === "--replay") && args[i + 1]) {
|
|
39
|
+
replayPath = path.resolve(args[i + 1]);
|
|
40
|
+
i += 1;
|
|
41
|
+
} else if ((arg === "-s" || arg === "--steps") && args[i + 1]) {
|
|
42
|
+
const value = Number(args[i + 1]);
|
|
43
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
44
|
+
console.error("Steps limit must be a positive integer.");
|
|
45
|
+
console.log(usage);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
stepLimit = value;
|
|
49
|
+
i += 1;
|
|
50
|
+
} else {
|
|
51
|
+
console.error(`Unknown argument: ${arg}`);
|
|
52
|
+
console.log(usage);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { replayPath, stepLimit };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function extractStateHistory(replay) {
|
|
61
|
+
return (
|
|
62
|
+
replay?.info?.stateHistory ??
|
|
63
|
+
replay?.info?.state_history ??
|
|
64
|
+
replay?.stateHistory ??
|
|
65
|
+
replay?.state_history ??
|
|
66
|
+
[]
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function formatCommunityCards(cards) {
|
|
71
|
+
if (!Array.isArray(cards) || cards.length === 0) {
|
|
72
|
+
return "--";
|
|
73
|
+
}
|
|
74
|
+
return cards.join(" ");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatWinOdds(winOdds) {
|
|
78
|
+
if (!Array.isArray(winOdds) || winOdds.length === 0) {
|
|
79
|
+
return "--";
|
|
80
|
+
}
|
|
81
|
+
return winOdds.join(" vs ");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function main() {
|
|
85
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
86
|
+
const __dirname = path.dirname(__filename);
|
|
87
|
+
const repoRoot = path.resolve(__dirname, "../../../../../../../..");
|
|
88
|
+
const defaultReplayPath = path.resolve(
|
|
89
|
+
__dirname,
|
|
90
|
+
"../replays/test-replay.json"
|
|
91
|
+
);
|
|
92
|
+
const defaultStepLimit = 5;
|
|
93
|
+
|
|
94
|
+
const { replayPath, stepLimit } = parseArgs(
|
|
95
|
+
defaultReplayPath,
|
|
96
|
+
defaultStepLimit
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const { processEpisodeData, buildTimeline, getPokerStateForStep } =
|
|
100
|
+
await loadCoreModules(repoRoot);
|
|
101
|
+
|
|
102
|
+
let replayRaw;
|
|
103
|
+
try {
|
|
104
|
+
replayRaw = await readFile(replayPath, "utf-8");
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`Failed to read replay at ${replayPath}: ${error.message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let replay;
|
|
111
|
+
try {
|
|
112
|
+
replay = JSON.parse(replayRaw);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(
|
|
115
|
+
`Replay file ${replayPath} is not valid JSON: ${error.message}`
|
|
116
|
+
);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const stateHistory = extractStateHistory(replay);
|
|
121
|
+
if (!Array.isArray(stateHistory) || stateHistory.length === 0) {
|
|
122
|
+
console.error("Replay does not contain a state history.");
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const processedSteps = processEpisodeData(
|
|
127
|
+
{
|
|
128
|
+
steps: replay.steps,
|
|
129
|
+
state_history: stateHistory,
|
|
130
|
+
info: replay.info,
|
|
131
|
+
configuration: replay.configuration,
|
|
132
|
+
},
|
|
133
|
+
"repeated_poker"
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const environment = {
|
|
137
|
+
configuration: replay.configuration,
|
|
138
|
+
info: {
|
|
139
|
+
...replay.info,
|
|
140
|
+
stateHistory,
|
|
141
|
+
},
|
|
142
|
+
steps: processedSteps,
|
|
143
|
+
__processedSteps: processedSteps,
|
|
144
|
+
__rawSteps: replay.steps,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const timeline = buildTimeline(environment, 2);
|
|
148
|
+
environment.__timeline = timeline;
|
|
149
|
+
|
|
150
|
+
console.log(`Loaded replay: ${replayPath}`);
|
|
151
|
+
console.log(
|
|
152
|
+
`Printing first ${Math.min(stepLimit, timeline.length)} of ${
|
|
153
|
+
timeline.length
|
|
154
|
+
} timeline steps\n`
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
for (let stepIndex = 0; stepIndex < timeline.length; stepIndex += 1) {
|
|
158
|
+
if (stepIndex >= stepLimit) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const event = timeline[stepIndex];
|
|
163
|
+
const uiState = getPokerStateForStep(environment, stepIndex);
|
|
164
|
+
|
|
165
|
+
const headerParts = [
|
|
166
|
+
`Step ${stepIndex}`,
|
|
167
|
+
`stateIndex=${event?.stateIndex ?? "N/A"}`,
|
|
168
|
+
`highlight=${event?.highlightPlayer ?? "-"}`,
|
|
169
|
+
`action='${event?.actionText ?? ""}'`,
|
|
170
|
+
];
|
|
171
|
+
if (event?.hideHoleCards) {
|
|
172
|
+
headerParts.push("hideHoleCards");
|
|
173
|
+
}
|
|
174
|
+
if (event?.hideCommunity) {
|
|
175
|
+
headerParts.push("hideCommunity");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log(headerParts.join(" | "));
|
|
179
|
+
|
|
180
|
+
if (!uiState) {
|
|
181
|
+
console.log(" (UI state unavailable)\n");
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(
|
|
186
|
+
` pot=${uiState.pot} | community=${formatCommunityCards(
|
|
187
|
+
uiState.communityCards
|
|
188
|
+
)} | winOdds=${formatWinOdds(uiState.winOdds)}`
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
uiState.players.forEach((player, seat) => {
|
|
192
|
+
const cards =
|
|
193
|
+
Array.isArray(player.cards) && player.cards.length > 0
|
|
194
|
+
? player.cards.join(" ")
|
|
195
|
+
: "--";
|
|
196
|
+
const flags = [
|
|
197
|
+
player.isDealer ? "D" : "",
|
|
198
|
+
player.isTurn ? "T" : "",
|
|
199
|
+
player.isLastActor ? "LA" : "",
|
|
200
|
+
]
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
.join(",") || "-";
|
|
203
|
+
console.log(
|
|
204
|
+
` P${seat}: stack=${player.stack} bet=${player.currentBet} cards=${cards} flags=${flags} action='${player.actionDisplayText || ""}'`
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
console.log("");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
main().catch((error) => {
|
|
213
|
+
console.error("Failed to print replay steps:", error);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
});
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
|
|
8
|
+
async function loadCoreModules(repoRoot) {
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const corePath = path.resolve(repoRoot, "web/core/dist/index.umd.cjs");
|
|
11
|
+
const transformerPath = path.resolve(
|
|
12
|
+
repoRoot,
|
|
13
|
+
"web/core/dist/transformers/repeatedPokerTransformer.js"
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const { processEpisodeData } = require(corePath);
|
|
17
|
+
const { getPokerStepsWithEndStates } = await import(
|
|
18
|
+
pathToFileURL(transformerPath).href
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return { processEpisodeData, getPokerStepsWithEndStates };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(defaultReplayPath, defaultLimit, defaultJsonOutput) {
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
let replayPath = defaultReplayPath;
|
|
27
|
+
let limit = defaultLimit;
|
|
28
|
+
let asJson = defaultJsonOutput;
|
|
29
|
+
|
|
30
|
+
const usage = `Usage: node ${path.basename(
|
|
31
|
+
process.argv[1]
|
|
32
|
+
)} [options]\n\nOptions:\n -r, --replay <path> Replay JSON to inspect (default: ${defaultReplayPath})\n -l, --limit <count> Number of entries to print (0 = all) (default: ${defaultLimit})\n --json Print raw JSON instead of formatted text\n -h, --help Show this help message\n`;
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
35
|
+
const arg = args[i];
|
|
36
|
+
if (arg === "-h" || arg === "--help") {
|
|
37
|
+
console.log(usage);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
} else if ((arg === "-r" || arg === "--replay") && args[i + 1]) {
|
|
40
|
+
replayPath = path.resolve(args[i + 1]);
|
|
41
|
+
i += 1;
|
|
42
|
+
} else if ((arg === "-l" || arg === "--limit") && args[i + 1]) {
|
|
43
|
+
const value = Number(args[i + 1]);
|
|
44
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
45
|
+
console.error("Limit must be a non-negative integer.");
|
|
46
|
+
console.log(usage);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
limit = value;
|
|
50
|
+
i += 1;
|
|
51
|
+
} else if (arg === "--json") {
|
|
52
|
+
asJson = true;
|
|
53
|
+
} else {
|
|
54
|
+
console.error(`Unknown argument: ${arg}`);
|
|
55
|
+
console.log(usage);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { replayPath, limit, asJson };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function extractStateHistory(replay) {
|
|
64
|
+
return (
|
|
65
|
+
replay?.info?.stateHistory ??
|
|
66
|
+
replay?.info?.state_history ??
|
|
67
|
+
replay?.stateHistory ??
|
|
68
|
+
replay?.state_history ??
|
|
69
|
+
[]
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildEnvironment(
|
|
74
|
+
replay,
|
|
75
|
+
processedSteps,
|
|
76
|
+
processedInfo,
|
|
77
|
+
processedConfig,
|
|
78
|
+
stateHistory
|
|
79
|
+
) {
|
|
80
|
+
return {
|
|
81
|
+
configuration: processedConfig ?? replay.configuration ?? null,
|
|
82
|
+
info: {
|
|
83
|
+
...(processedInfo || replay.info || {}),
|
|
84
|
+
stateHistory,
|
|
85
|
+
},
|
|
86
|
+
steps: processedSteps,
|
|
87
|
+
__processedSteps: processedSteps,
|
|
88
|
+
__rawSteps: replay.steps,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function describeStep(step, index) {
|
|
93
|
+
const baseParts = [
|
|
94
|
+
`Index ${index}`,
|
|
95
|
+
`hand=${step.hand}`,
|
|
96
|
+
`stateHistoryIndex=${step.stateHistoryIndex ?? "?"}`,
|
|
97
|
+
`isEndState=${step.isEndState}`,
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
if (step.postActionOf != null) {
|
|
101
|
+
baseParts.push(`postActionOf=${step.postActionOf}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (step.actingPlayer != null) {
|
|
105
|
+
const label =
|
|
106
|
+
step.actingPlayerName && step.actingPlayerName.length > 0
|
|
107
|
+
? `${step.actingPlayer}(${step.actingPlayerName})`
|
|
108
|
+
: `${step.actingPlayer}`;
|
|
109
|
+
baseParts.push(`actingPlayer=${label}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (step.currentPlayer != null) {
|
|
113
|
+
const label =
|
|
114
|
+
step.currentPlayerName && step.currentPlayerName.length > 0
|
|
115
|
+
? `${step.currentPlayer}(${step.currentPlayerName})`
|
|
116
|
+
: `${step.currentPlayer}`;
|
|
117
|
+
baseParts.push(`currentPlayer=${label}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (step.isEndState) {
|
|
121
|
+
baseParts.push(`conclusion=${step.handConclusion ?? "-"}`);
|
|
122
|
+
baseParts.push(`winner=${step.winner ?? "-"}`);
|
|
123
|
+
} else if (step.step?.action?.actionString) {
|
|
124
|
+
baseParts.push(`action=${step.step.action.actionString}`);
|
|
125
|
+
} else if (step.actionText) {
|
|
126
|
+
baseParts.push(`actionText=${step.actionText}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return baseParts.join(" | ");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function main() {
|
|
133
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
134
|
+
const __dirname = path.dirname(__filename);
|
|
135
|
+
const repoRoot = path.resolve(__dirname, "../../../../../../../..");
|
|
136
|
+
const defaultReplayPath = path.resolve(
|
|
137
|
+
__dirname,
|
|
138
|
+
"../replays/test-replay.json"
|
|
139
|
+
);
|
|
140
|
+
const defaultLimit = 10;
|
|
141
|
+
const defaultJsonOutput = false;
|
|
142
|
+
|
|
143
|
+
const { replayPath, limit, asJson } = parseArgs(
|
|
144
|
+
defaultReplayPath,
|
|
145
|
+
defaultLimit,
|
|
146
|
+
defaultJsonOutput
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const { processEpisodeData, getPokerStepsWithEndStates } =
|
|
150
|
+
await loadCoreModules(repoRoot);
|
|
151
|
+
|
|
152
|
+
let replayRaw;
|
|
153
|
+
try {
|
|
154
|
+
replayRaw = await readFile(replayPath, "utf-8");
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(`Failed to read replay at ${replayPath}: ${error.message}`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let replay;
|
|
161
|
+
try {
|
|
162
|
+
replay = JSON.parse(replayRaw);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(
|
|
165
|
+
`Replay file ${replayPath} is not valid JSON: ${error.message}`
|
|
166
|
+
);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const stateHistory = extractStateHistory(replay);
|
|
171
|
+
if (!Array.isArray(stateHistory) || stateHistory.length === 0) {
|
|
172
|
+
console.error("Replay does not contain a state history.");
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const processedResult = processEpisodeData(
|
|
177
|
+
{
|
|
178
|
+
steps: replay.steps,
|
|
179
|
+
state_history: stateHistory,
|
|
180
|
+
info: replay.info,
|
|
181
|
+
configuration: replay.configuration,
|
|
182
|
+
},
|
|
183
|
+
"repeated_poker"
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const processedSteps = Array.isArray(processedResult?.steps)
|
|
187
|
+
? processedResult.steps
|
|
188
|
+
: [];
|
|
189
|
+
|
|
190
|
+
if (processedSteps.length === 0) {
|
|
191
|
+
console.error("Processed episode contains no steps.");
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const environment = buildEnvironment(
|
|
196
|
+
replay,
|
|
197
|
+
processedSteps,
|
|
198
|
+
processedResult?.info,
|
|
199
|
+
processedResult?.configuration,
|
|
200
|
+
stateHistory
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const stepsWithEndStates = getPokerStepsWithEndStates(environment);
|
|
204
|
+
|
|
205
|
+
console.log(`Loaded replay: ${replayPath}`);
|
|
206
|
+
console.log(
|
|
207
|
+
`Derived ${stepsWithEndStates.length} entries via getPokerStepsWithEndStates`
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (asJson) {
|
|
211
|
+
const subset =
|
|
212
|
+
limit === 0 ? stepsWithEndStates : stepsWithEndStates.slice(0, limit);
|
|
213
|
+
console.log(JSON.stringify(subset, null, 2));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const entriesToPrint =
|
|
218
|
+
limit === 0 ? stepsWithEndStates : stepsWithEndStates.slice(0, limit);
|
|
219
|
+
|
|
220
|
+
entriesToPrint.forEach((entry, index) => {
|
|
221
|
+
console.log(describeStep(entry, index));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (limit > 0 && stepsWithEndStates.length > limit) {
|
|
225
|
+
console.log(
|
|
226
|
+
`\n(${stepsWithEndStates.length - limit} additional entries not shown; rerun with -l 0 to display all or --json for raw output.)`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
main().catch((error) => {
|
|
232
|
+
console.error("Failed to print steps:", error);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getActionStringsFromACPC } from
|
|
1
|
+
import { getActionStringsFromACPC } from '@kaggle-environments/core';
|
|
2
2
|
|
|
3
3
|
const PLACEHOLDER_CARD = '2c';
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ function _parseStepHistoryData(universalPokerJSON, nextPlayerIndex, numPlayers =
|
|
|
8
8
|
communityCards: '',
|
|
9
9
|
bets: [],
|
|
10
10
|
playerActionStrings: Array(numPlayers).fill(''),
|
|
11
|
-
winOdds: [0, 0]
|
|
11
|
+
winOdds: [0, 0]
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
if (!universalPokerJSON) {
|
|
@@ -52,31 +52,31 @@ function _parseStepHistoryData(universalPokerJSON, nextPlayerIndex, numPlayers =
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
result.communityCards = cardSegments
|
|
56
|
-
.slice(1)
|
|
57
|
-
.filter(Boolean)
|
|
58
|
-
.join('');
|
|
55
|
+
result.communityCards = cardSegments.slice(1).filter(Boolean).join('');
|
|
59
56
|
|
|
60
57
|
const bettingString = stateParts.slice(2, stateParts.length - 1).join(':');
|
|
61
58
|
if (bettingString) {
|
|
62
|
-
result.playerActionStrings = getActionStringsFromACPC(
|
|
63
|
-
bettingString,
|
|
64
|
-
nextPlayerIndex,
|
|
65
|
-
numPlayers
|
|
66
|
-
);
|
|
59
|
+
result.playerActionStrings = getActionStringsFromACPC(bettingString, nextPlayerIndex, numPlayers);
|
|
67
60
|
}
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
const odds = universalPokerJSON.odds || [];
|
|
71
|
-
|
|
64
|
+
// The odds array is structured as [Player1_Win_Prob, Tie_Prob, Player2_Win_Prob, Tie_Prob_Repeated]
|
|
65
|
+
const p0WinProb = Number(odds[0] ?? 0);
|
|
66
|
+
const tieProb = Number(odds[1] ?? 0);
|
|
67
|
+
const p1WinProb = Number(odds[2] ?? 0);
|
|
68
|
+
const fiveCardBestHands = universalPokerJSON.best_hand_rank_types || [];
|
|
69
|
+
|
|
70
|
+
result.winProb = [
|
|
71
|
+
p0WinProb.toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 }),
|
|
72
|
+
p1WinProb.toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1 })
|
|
73
|
+
];
|
|
74
|
+
result.tieProb = tieProb.toLocaleString(undefined, {
|
|
72
75
|
style: 'percent',
|
|
73
|
-
minimumFractionDigits:
|
|
76
|
+
minimumFractionDigits: 1,
|
|
77
|
+
maximumFractionDigits: 1
|
|
74
78
|
});
|
|
75
|
-
|
|
76
|
-
style: 'percent',
|
|
77
|
-
minimumFractionDigits: 2
|
|
78
|
-
});
|
|
79
|
-
result.winOdds = [p0WinOdds, p1WinOdds];
|
|
79
|
+
result.handRank = fiveCardBestHands;
|
|
80
80
|
|
|
81
81
|
return result;
|
|
82
82
|
}
|
|
@@ -96,8 +96,6 @@ function sanitizeCardList(cards) {
|
|
|
96
96
|
return (cards || []).filter((card) => card);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
99
|
function getCommunityCardsFromUniversal(universal, numPlayers) {
|
|
102
100
|
const parsed = _parseStepHistoryData(universal, null, numPlayers);
|
|
103
101
|
const cards = splitCards(parsed.communityCards);
|
|
@@ -118,9 +116,6 @@ function getHandCardsFromUniversal(universal, numPlayers) {
|
|
|
118
116
|
});
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
119
|
function getUniversalState(environment, index) {
|
|
125
120
|
const entry = environment?.info?.stateHistory?.[index];
|
|
126
121
|
if (!entry) {
|
|
@@ -152,12 +147,13 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
152
147
|
|
|
153
148
|
const parsedStateHistory = _parseStepHistoryData(
|
|
154
149
|
stateInfo.universal,
|
|
155
|
-
|
|
150
|
+
event.actingPlayer,
|
|
156
151
|
numPlayers
|
|
157
152
|
);
|
|
158
153
|
|
|
159
154
|
const startingStacks = stateInfo.universal?.starting_stacks || Array(numPlayers).fill(0);
|
|
160
|
-
const contributions =
|
|
155
|
+
const contributions =
|
|
156
|
+
stateInfo.universal?.player_contributions || parsedStateHistory.bets || Array(numPlayers).fill(0);
|
|
161
157
|
const rewards = stateInfo.outer?.hand_returns || [];
|
|
162
158
|
const communityCards = getCommunityCardsFromUniversal(stateInfo.universal, numPlayers);
|
|
163
159
|
|
|
@@ -174,10 +170,12 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
174
170
|
cards: [],
|
|
175
171
|
currentBet: contributions[i] || 0,
|
|
176
172
|
isDealer: stateInfo.outer?.dealer === i,
|
|
177
|
-
isTurn:
|
|
173
|
+
isTurn: event.actingPlayer === i,
|
|
178
174
|
isLastActor: event.highlightPlayer === i,
|
|
179
175
|
reward: rewards[0]?.[i] ?? null,
|
|
180
|
-
actionDisplayText: event.
|
|
176
|
+
actionDisplayText: event.winner === i ? "Winner" : parsedStateHistory.playerActionStrings[i],
|
|
177
|
+
isWinner: event.winner === i,
|
|
178
|
+
handCount: 0,
|
|
181
179
|
};
|
|
182
180
|
});
|
|
183
181
|
|
|
@@ -192,6 +190,57 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
192
190
|
|
|
193
191
|
const displayCommunity = event.hideCommunity ? [] : communityCards;
|
|
194
192
|
|
|
193
|
+
const hand_returns = stateInfo.outer?.hand_returns || [];
|
|
194
|
+
const cumulativeWinnings = Array(numPlayers).fill(0);
|
|
195
|
+
const previousHands = [];
|
|
196
|
+
|
|
197
|
+
for (let i = 0; i < hand_returns.length; i++) {
|
|
198
|
+
const handReturn = hand_returns[i];
|
|
199
|
+
if (!handReturn) continue;
|
|
200
|
+
|
|
201
|
+
let winner = -1;
|
|
202
|
+
let winAmount = 0;
|
|
203
|
+
if (handReturn[0] > handReturn[1]) {
|
|
204
|
+
winner = 0;
|
|
205
|
+
winAmount = handReturn[0];
|
|
206
|
+
} else if (handReturn[1] > handReturn[0]) {
|
|
207
|
+
winner = 1;
|
|
208
|
+
winAmount = handReturn[1];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (winner !== -1) {
|
|
212
|
+
previousHands.push({
|
|
213
|
+
handNum: i + 1,
|
|
214
|
+
winnerIndex: winner,
|
|
215
|
+
winnerName: environment?.info?.TeamNames?.[winner] || `Player ${winner}`,
|
|
216
|
+
winnerThumbnail: environment?.info?.Agents?.[winner]?.ThumbnailUrl,
|
|
217
|
+
amount: winAmount
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
cumulativeWinnings[0] += handReturn[0] || 0;
|
|
222
|
+
cumulativeWinnings[1] += handReturn[1] || 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let leadingPlayer = -1;
|
|
226
|
+
let leadingWinnings = 0;
|
|
227
|
+
if (cumulativeWinnings[0] > cumulativeWinnings[1]) {
|
|
228
|
+
leadingPlayer = 0;
|
|
229
|
+
leadingWinnings = cumulativeWinnings[0];
|
|
230
|
+
} else if (cumulativeWinnings[1] > cumulativeWinnings[0]) {
|
|
231
|
+
leadingPlayer = 1;
|
|
232
|
+
leadingWinnings = cumulativeWinnings[1];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const leaderInfo =
|
|
236
|
+
leadingPlayer !== -1
|
|
237
|
+
? {
|
|
238
|
+
name: environment?.info?.TeamNames?.[leadingPlayer] || `Player ${leadingPlayer}`,
|
|
239
|
+
thumbnail: environment?.info?.Agents?.[leadingPlayer]?.ThumbnailUrl,
|
|
240
|
+
winnings: leadingWinnings
|
|
241
|
+
}
|
|
242
|
+
: null;
|
|
243
|
+
|
|
195
244
|
return {
|
|
196
245
|
players,
|
|
197
246
|
communityCards: displayCommunity,
|
|
@@ -199,9 +248,13 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
199
248
|
isTerminal: false,
|
|
200
249
|
rawObservation: stateInfo.universal,
|
|
201
250
|
step,
|
|
202
|
-
|
|
203
|
-
|
|
251
|
+
winProb: parsedStateHistory.winProb,
|
|
252
|
+
tieProb: parsedStateHistory.tieProb,
|
|
253
|
+
handRank: parsedStateHistory.handRank,
|
|
204
254
|
currentPlayer: stateInfo.universal?.current_player ?? -1,
|
|
205
|
-
winner: -1
|
|
255
|
+
winner: -1,
|
|
256
|
+
handCount: event.hand,
|
|
257
|
+
previousHands,
|
|
258
|
+
leaderInfo
|
|
206
259
|
};
|
|
207
260
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { getPokerStateForStep } from
|
|
2
|
-
import { acpcCardToDisplay, suitSVGs } from
|
|
3
|
-
import poker_chip_1 from
|
|
4
|
-
import poker_chip_5 from
|
|
5
|
-
import poker_chip_10 from
|
|
6
|
-
import poker_chip_25 from
|
|
7
|
-
import poker_chip_100 from
|
|
1
|
+
import { getPokerStateForStep } from './components/getRepeatedPokerStateForStep';
|
|
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';
|
|
8
8
|
|
|
9
9
|
export function renderer(options) {
|
|
10
10
|
const chipImages = {
|
|
@@ -28,7 +28,8 @@ export function renderer(options) {
|
|
|
28
28
|
dealerButton: null,
|
|
29
29
|
chipStacks: [],
|
|
30
30
|
diagnosticHeader: null,
|
|
31
|
-
stepCounter: null
|
|
31
|
+
stepCounter: null,
|
|
32
|
+
legend: null
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const css = `
|
|
@@ -133,14 +134,14 @@ export function renderer(options) {
|
|
|
133
134
|
.player-info-area {
|
|
134
135
|
color: white;
|
|
135
136
|
width: auto;
|
|
136
|
-
min-width:
|
|
137
|
+
min-width: 280px;
|
|
137
138
|
pointer-events: auto;
|
|
138
139
|
display: flex;
|
|
139
140
|
flex-direction: column;
|
|
140
141
|
justify-content: center;
|
|
141
142
|
align-items: center;
|
|
142
|
-
margin:
|
|
143
|
-
padding:
|
|
143
|
+
margin: 10px auto;
|
|
144
|
+
padding: 10px;
|
|
144
145
|
background-color: rgba(32, 33, 36, 0.70);;
|
|
145
146
|
border-radius: 16px;
|
|
146
147
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
@@ -162,7 +163,7 @@ export function renderer(options) {
|
|
|
162
163
|
justify-content: center;
|
|
163
164
|
gap: 16px;
|
|
164
165
|
margin: 0 60px;
|
|
165
|
-
padding:
|
|
166
|
+
padding: 5px 0;
|
|
166
167
|
}
|
|
167
168
|
.player-thumbnail {
|
|
168
169
|
width: 48px;
|
|
@@ -184,6 +185,20 @@ export function renderer(options) {
|
|
|
184
185
|
.player-name.winner { color: #FFEB70; }
|
|
185
186
|
.player-name.current-turn { color: #20BEFF; }
|
|
186
187
|
.player-stack { font-size: 20px; font-weight: 600; color: #ffffff; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
|
|
188
|
+
.player-stats-container {
|
|
189
|
+
display: flex;
|
|
190
|
+
flex-direction: column;
|
|
191
|
+
justify-content: center;
|
|
192
|
+
align-items: center;
|
|
193
|
+
margin: 0 12px;
|
|
194
|
+
}
|
|
195
|
+
.player-hand-rank, .player-win-prob, .player-tie-prob {
|
|
196
|
+
font-size: 16px;
|
|
197
|
+
font-weight: 600;
|
|
198
|
+
color: #e0e0e0;
|
|
199
|
+
height: 20px;
|
|
200
|
+
align-self: flex-start;
|
|
201
|
+
}
|
|
187
202
|
.player-cards-container { min-height: 80px; display: flex; justify-content: center; align-items:center;}
|
|
188
203
|
.card {
|
|
189
204
|
display: flex; flex-direction: column; justify-content: space-between; align-items: center;
|
|
@@ -242,7 +257,6 @@ export function renderer(options) {
|
|
|
242
257
|
box-shadow: 0 1px 3px rgba(0,0,0,0.3); z-index: 15; pointer-events: auto;
|
|
243
258
|
border: 2px solid black;
|
|
244
259
|
outline: 2px solid #20BEFF;
|
|
245
|
-
left: 320px
|
|
246
260
|
}
|
|
247
261
|
.dealer-button.dealer-player0 { top: 170px; }
|
|
248
262
|
.dealer-button.dealer-player1 { bottom: 170px; }
|
|
@@ -253,6 +267,12 @@ export function renderer(options) {
|
|
|
253
267
|
font-size: 14px; font-weight: 600;
|
|
254
268
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
255
269
|
}
|
|
270
|
+
.hand-counter {
|
|
271
|
+
position: absolute; bottom: 12px; right: 12px; z-index: 20;
|
|
272
|
+
color: #535965;
|
|
273
|
+
padding: 6px 12px; border-radius: 6px;
|
|
274
|
+
font-size: 40px; font-weight: 600;
|
|
275
|
+
}
|
|
256
276
|
.chip-stack {
|
|
257
277
|
position: absolute;
|
|
258
278
|
display: flex;
|
|
@@ -305,6 +325,70 @@ export function renderer(options) {
|
|
|
305
325
|
font-weight: bold;
|
|
306
326
|
white-space: nowrap;
|
|
307
327
|
}
|
|
328
|
+
.legend {
|
|
329
|
+
position: absolute;
|
|
330
|
+
top: 0px;
|
|
331
|
+
right: 0px;
|
|
332
|
+
width: 280px;
|
|
333
|
+
background-color: rgba(32, 33, 36, 0.70);
|
|
334
|
+
border-radius: 8px;
|
|
335
|
+
color: white;
|
|
336
|
+
z-index: 100;
|
|
337
|
+
display: flex;
|
|
338
|
+
flex-direction: column;
|
|
339
|
+
max-height: 212px;
|
|
340
|
+
}
|
|
341
|
+
.legend-title {
|
|
342
|
+
padding: 10px;
|
|
343
|
+
font-weight: bold;
|
|
344
|
+
border-bottom: 1px solid #555;
|
|
345
|
+
display: flex;
|
|
346
|
+
align-items: center;
|
|
347
|
+
justify-content: space-between;
|
|
348
|
+
}
|
|
349
|
+
.legend-leader-info {
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
}
|
|
353
|
+
.legend-title-avatar {
|
|
354
|
+
width: 32px;
|
|
355
|
+
height: 32px;
|
|
356
|
+
border-radius: 50%;
|
|
357
|
+
margin-right: 8px;
|
|
358
|
+
}
|
|
359
|
+
.legend-body {
|
|
360
|
+
padding: 10px;
|
|
361
|
+
max-height: 200px;
|
|
362
|
+
overflow-y: auto;
|
|
363
|
+
}
|
|
364
|
+
.legend-table {
|
|
365
|
+
display: table;
|
|
366
|
+
width: 100%;
|
|
367
|
+
}
|
|
368
|
+
.legend-row {
|
|
369
|
+
display: table-row;
|
|
370
|
+
}
|
|
371
|
+
.legend-header .legend-cell {
|
|
372
|
+
font-weight: bold;
|
|
373
|
+
}
|
|
374
|
+
.legend-cell {
|
|
375
|
+
display: table-cell;
|
|
376
|
+
padding: 2px 5px;
|
|
377
|
+
vertical-align: middle;
|
|
378
|
+
}
|
|
379
|
+
.legend-cell:nth-child(1) { width: 20%; }
|
|
380
|
+
.legend-cell:nth-child(2) { width: 50%; }
|
|
381
|
+
.legend-cell:nth-child(3) { width: 30%; text-align: right; }
|
|
382
|
+
.legend-winner-cell {
|
|
383
|
+
display: flex;
|
|
384
|
+
align-items: center;
|
|
385
|
+
}
|
|
386
|
+
.legend-avatar {
|
|
387
|
+
width: 24px;
|
|
388
|
+
height: 24px;
|
|
389
|
+
border-radius: 50%;
|
|
390
|
+
margin-right: 5px;
|
|
391
|
+
}
|
|
308
392
|
`;
|
|
309
393
|
|
|
310
394
|
function _injectStyles(passedOptions) {
|
|
@@ -496,6 +580,11 @@ export function renderer(options) {
|
|
|
496
580
|
<div class="player-card-area">
|
|
497
581
|
<div class="player-cards-container"></div>
|
|
498
582
|
</div>
|
|
583
|
+
<div class="player-stats-container">
|
|
584
|
+
<div class="player-hand-rank"></div>
|
|
585
|
+
<div class="player-win-prob"></div>
|
|
586
|
+
<div class="player-tie-prob"></div>
|
|
587
|
+
</div>
|
|
499
588
|
<div class="player-stack">
|
|
500
589
|
<span class="player-stack-value">0</span>
|
|
501
590
|
</div>
|
|
@@ -515,14 +604,16 @@ export function renderer(options) {
|
|
|
515
604
|
elements.dealerButton.style.display = 'none';
|
|
516
605
|
elements.playersContainer.appendChild(elements.dealerButton);
|
|
517
606
|
|
|
518
|
-
elements.
|
|
519
|
-
elements.
|
|
520
|
-
elements.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
607
|
+
elements.legend = document.createElement('div');
|
|
608
|
+
elements.legend.className = 'legend';
|
|
609
|
+
elements.legend.innerHTML = `
|
|
610
|
+
<div class="legend-title"></div>
|
|
611
|
+
<div class="legend-body"></div>
|
|
612
|
+
`;
|
|
613
|
+
elements.gameLayout.appendChild(elements.legend);
|
|
524
614
|
|
|
525
|
-
|
|
615
|
+
return true;
|
|
616
|
+
} // --- State Parsing ---
|
|
526
617
|
function _parseKagglePokerState(options) {
|
|
527
618
|
const { environment, step } = options;
|
|
528
619
|
|
|
@@ -531,7 +622,7 @@ export function renderer(options) {
|
|
|
531
622
|
players: [],
|
|
532
623
|
communityCards: [],
|
|
533
624
|
pot: 0,
|
|
534
|
-
isTerminal: false
|
|
625
|
+
isTerminal: false
|
|
535
626
|
};
|
|
536
627
|
|
|
537
628
|
// --- Step Validation ---
|
|
@@ -539,7 +630,6 @@ export function renderer(options) {
|
|
|
539
630
|
return defaultStateUiData;
|
|
540
631
|
}
|
|
541
632
|
|
|
542
|
-
|
|
543
633
|
return getPokerStateForStep(environment, step);
|
|
544
634
|
}
|
|
545
635
|
|
|
@@ -561,13 +651,123 @@ export function renderer(options) {
|
|
|
561
651
|
|
|
562
652
|
function _renderPokerTableUI(data) {
|
|
563
653
|
if (!elements.pokerTable || !data) return;
|
|
564
|
-
const {
|
|
654
|
+
const {
|
|
655
|
+
players,
|
|
656
|
+
communityCards,
|
|
657
|
+
pot,
|
|
658
|
+
isTerminal,
|
|
659
|
+
handCount,
|
|
660
|
+
winProb,
|
|
661
|
+
tieProb,
|
|
662
|
+
handRank,
|
|
663
|
+
previousHands,
|
|
664
|
+
leaderInfo
|
|
665
|
+
} = data;
|
|
666
|
+
|
|
667
|
+
// Update legend
|
|
668
|
+
const legendTitle = elements.legend.querySelector('.legend-title');
|
|
669
|
+
const legendBody = elements.legend.querySelector('.legend-body');
|
|
670
|
+
|
|
671
|
+
legendTitle.innerHTML = ''; // Clear existing content
|
|
672
|
+
|
|
673
|
+
const handSpan = document.createElement('span');
|
|
674
|
+
handSpan.textContent = `Hand: ${handCount !== undefined && handCount !== null ? handCount + 1 : 'Standby'}`;
|
|
675
|
+
legendTitle.appendChild(handSpan);
|
|
676
|
+
|
|
677
|
+
if (leaderInfo) {
|
|
678
|
+
const leaderInfoDiv = document.createElement('div');
|
|
679
|
+
leaderInfoDiv.className = 'legend-leader-info';
|
|
680
|
+
|
|
681
|
+
if (leaderInfo.thumbnail) {
|
|
682
|
+
const leaderThumbnail = document.createElement('img');
|
|
683
|
+
leaderThumbnail.src = leaderInfo.thumbnail;
|
|
684
|
+
leaderThumbnail.className = 'legend-title-avatar';
|
|
685
|
+
leaderInfoDiv.appendChild(leaderThumbnail);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const leaderNameSpan = document.createElement('span');
|
|
689
|
+
const leaderName = leaderInfo.name.split(' ')[0];
|
|
690
|
+
leaderNameSpan.textContent = `${leaderName} is up ${leaderInfo.winnings}`;
|
|
691
|
+
leaderInfoDiv.appendChild(leaderNameSpan);
|
|
692
|
+
legendTitle.appendChild(leaderInfoDiv);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
legendBody.innerHTML = ''; // Clear existing content
|
|
696
|
+
|
|
697
|
+
const table = document.createElement('div');
|
|
698
|
+
table.className = 'legend-table';
|
|
699
|
+
|
|
700
|
+
const headerRow = document.createElement('div');
|
|
701
|
+
headerRow.className = 'legend-row legend-header';
|
|
702
|
+
|
|
703
|
+
const handHeader = document.createElement('div');
|
|
704
|
+
handHeader.className = 'legend-cell';
|
|
705
|
+
handHeader.textContent = 'Hand';
|
|
706
|
+
headerRow.appendChild(handHeader);
|
|
707
|
+
|
|
708
|
+
const winnerHeader = document.createElement('div');
|
|
709
|
+
winnerHeader.className = 'legend-cell';
|
|
710
|
+
winnerHeader.textContent = 'Winner';
|
|
711
|
+
headerRow.appendChild(winnerHeader);
|
|
712
|
+
|
|
713
|
+
const amountHeader = document.createElement('div');
|
|
714
|
+
amountHeader.className = 'legend-cell';
|
|
715
|
+
amountHeader.textContent = 'Amount';
|
|
716
|
+
headerRow.appendChild(amountHeader);
|
|
717
|
+
|
|
718
|
+
table.appendChild(headerRow);
|
|
719
|
+
|
|
720
|
+
if (previousHands && previousHands.length > 0) {
|
|
721
|
+
previousHands
|
|
722
|
+
.slice()
|
|
723
|
+
.reverse()
|
|
724
|
+
.forEach((hand) => {
|
|
725
|
+
const row = document.createElement('div');
|
|
726
|
+
row.className = 'legend-row';
|
|
727
|
+
|
|
728
|
+
const handCell = document.createElement('div');
|
|
729
|
+
handCell.className = 'legend-cell';
|
|
730
|
+
handCell.textContent = hand.handNum;
|
|
731
|
+
row.appendChild(handCell);
|
|
732
|
+
|
|
733
|
+
const winnerCell = document.createElement('div');
|
|
734
|
+
winnerCell.className = 'legend-cell';
|
|
735
|
+
const winnerCellContainer = document.createElement('div');
|
|
736
|
+
winnerCellContainer.className = 'legend-winner-cell';
|
|
737
|
+
|
|
738
|
+
if (hand.winnerThumbnail) {
|
|
739
|
+
const winnerThumbnail = document.createElement('img');
|
|
740
|
+
winnerThumbnail.src = hand.winnerThumbnail;
|
|
741
|
+
winnerThumbnail.className = 'legend-avatar';
|
|
742
|
+
winnerCellContainer.appendChild(winnerThumbnail);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const winnerNameSpan = document.createElement('span');
|
|
746
|
+
winnerNameSpan.textContent = hand.winnerName.split(' ')[0];
|
|
747
|
+
winnerCellContainer.appendChild(winnerNameSpan);
|
|
748
|
+
|
|
749
|
+
winnerCell.appendChild(winnerCellContainer);
|
|
750
|
+
row.appendChild(winnerCell);
|
|
565
751
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
752
|
+
const amountCell = document.createElement('div');
|
|
753
|
+
amountCell.className = 'legend-cell';
|
|
754
|
+
amountCell.textContent = hand.amount;
|
|
755
|
+
row.appendChild(amountCell);
|
|
756
|
+
|
|
757
|
+
table.appendChild(row);
|
|
758
|
+
});
|
|
759
|
+
} else {
|
|
760
|
+
const emptyRow = document.createElement('div');
|
|
761
|
+
emptyRow.className = 'legend-row';
|
|
762
|
+
const emptyCell = document.createElement('div');
|
|
763
|
+
emptyCell.className = 'legend-cell';
|
|
764
|
+
emptyCell.setAttribute('colspan', '3');
|
|
765
|
+
emptyRow.appendChild(emptyCell);
|
|
766
|
+
table.appendChild(emptyRow);
|
|
569
767
|
}
|
|
570
768
|
|
|
769
|
+
legendBody.appendChild(table);
|
|
770
|
+
|
|
571
771
|
if (elements.diagnosticHeader && data.rawObservation) {
|
|
572
772
|
// Optional: Show diagnostics for debugging
|
|
573
773
|
// elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
|
|
@@ -580,10 +780,6 @@ export function renderer(options) {
|
|
|
580
780
|
const numCommunityCards = 5;
|
|
581
781
|
const numCards = communityCards ? communityCards.length : 0;
|
|
582
782
|
|
|
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();
|
|
586
|
-
|
|
587
783
|
// Add actual cards
|
|
588
784
|
for (let i = 0; i < numCards; i++) {
|
|
589
785
|
elements.communityCardsContainer.appendChild(createCardElement(communityCards[i]));
|
|
@@ -676,6 +872,27 @@ export function renderer(options) {
|
|
|
676
872
|
} else {
|
|
677
873
|
betDisplay.style.display = 'none';
|
|
678
874
|
}
|
|
875
|
+
|
|
876
|
+
const handRankElement = playerInfoArea.querySelector('.player-hand-rank');
|
|
877
|
+
if (handRankElement && handRank && handRank[index]) {
|
|
878
|
+
handRankElement.textContent = handRank[index];
|
|
879
|
+
} else if (handRankElement) {
|
|
880
|
+
handRankElement.textContent = '';
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const winProbElement = playerInfoArea.querySelector('.player-win-prob');
|
|
884
|
+
if (winProbElement && winProb && winProb[index] && !isTerminal) {
|
|
885
|
+
winProbElement.textContent = `Win: ${winProb[index]}`;
|
|
886
|
+
} else if (winProbElement) {
|
|
887
|
+
winProbElement.textContent = '';
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const tieProbElement = playerInfoArea.querySelector('.player-tie-prob');
|
|
891
|
+
if (tieProbElement && tieProb && !isTerminal) {
|
|
892
|
+
tieProbElement.textContent = `Tie: ${tieProb}`;
|
|
893
|
+
} else if (tieProbElement) {
|
|
894
|
+
tieProbElement.textContent = '';
|
|
895
|
+
}
|
|
679
896
|
}
|
|
680
897
|
});
|
|
681
898
|
|
|
@@ -683,10 +900,16 @@ export function renderer(options) {
|
|
|
683
900
|
if (elements.dealerButton) {
|
|
684
901
|
if (dealerPlayerIndex !== -1) {
|
|
685
902
|
elements.dealerButton.style.display = 'block';
|
|
686
|
-
// Remove previous dealer class
|
|
687
903
|
elements.dealerButton.classList.remove('dealer-player0', 'dealer-player1');
|
|
688
|
-
// Add new dealer class based on player index
|
|
689
904
|
elements.dealerButton.classList.add(`dealer-player${dealerPlayerIndex}`);
|
|
905
|
+
|
|
906
|
+
const playerInfoArea = elements.playerInfoAreas[dealerPlayerIndex];
|
|
907
|
+
if (playerInfoArea) {
|
|
908
|
+
const boxRect = playerInfoArea.getBoundingClientRect();
|
|
909
|
+
const containerRect = elements.playersContainer.getBoundingClientRect();
|
|
910
|
+
const left = boxRect.left - containerRect.left - elements.dealerButton.offsetWidth - 20;
|
|
911
|
+
elements.dealerButton.style.left = `${left}px`;
|
|
912
|
+
}
|
|
690
913
|
} else {
|
|
691
914
|
elements.dealerButton.style.display = 'none';
|
|
692
915
|
}
|
|
@@ -711,7 +934,6 @@ export function renderer(options) {
|
|
|
711
934
|
const uiData = _parseKagglePokerState(options);
|
|
712
935
|
_renderPokerTableUI(uiData, options);
|
|
713
936
|
|
|
714
|
-
|
|
715
937
|
// Apply initial scale
|
|
716
938
|
_applyScale(parent);
|
|
717
939
|
|
|
@@ -194,9 +194,12 @@ kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/
|
|
|
194
194
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/tsconfig.json,sha256=3X9dsQOgFw_cZ_uXByZIsGrQ5jhFfAZZL7QC6ApJWak,133
|
|
195
195
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/vite.config.ts,sha256=KhIjUn0WWhaoQzQ5YKuWjNndimRF0kFlYDgEnZ0cg7U,208
|
|
196
196
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/replays/test-replay.json,sha256=jf4ilR6SmOYPNohkIGJvmKP5Gju5FY6sfSStX1qtFZg,28919900
|
|
197
|
+
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_first_steps.mjs,sha256=dveXJxSDRPS78s2nWo3pN-fektaVWSCZ4j8k6qInv_c,5613
|
|
198
|
+
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_replay.mjs,sha256=JvveM8NqRu_ujjwhRj9EbuoQhBdvmdVcLWgOWySqnU8,5879
|
|
199
|
+
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/scripts/print_steps_with_end_states.mjs,sha256=BQ7erkBJTEXmBcMqjUYmxr20ovc0GyRNhHktzXtWgqk,6590
|
|
197
200
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/main.ts,sha256=Its2m61qv7RHlHUzIW6JkmySaHWq1h2bJ5sWY9woots,1000
|
|
198
|
-
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.js,sha256=
|
|
199
|
-
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/getRepeatedPokerStateForStep.js,sha256=
|
|
201
|
+
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.js,sha256=HXv1F3_C4ntrW4IIZ4BrMwAEKLJBbWzRohaETdmzg_A,33502
|
|
202
|
+
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/getRepeatedPokerStateForStep.js,sha256=4JfDFe4-NV8JrY20Zp_eNe-ks4fquGRlAq3NNhNu67s,7557
|
|
200
203
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/components/utils.js,sha256=pXDAu4V2OppRCvMdJKQ56q1uFTJReMPIvBL6gwxIJoI,5734
|
|
201
204
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_1.svg,sha256=v9yCvpnaQAg8OSUJdJ5PhuTHm9_zXnww-9_7oR_DJpc,22160
|
|
202
205
|
kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/images/poker_chip_10.svg,sha256=z3CP2h5eUGlgBdqNoWGcioekAyPgiuzhyRNr-nbutOE,22160
|
|
@@ -279,8 +282,8 @@ kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_p
|
|
|
279
282
|
kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_no_tie_exile.yaml,sha256=sfSFlFU4F7doZ-wXUWBl-JgJtmpjrLR-SpCAqKnUYeQ,3662
|
|
280
283
|
kaggle_environments/envs/werewolf/scripts/configs/run/rule_experiment/standard_parallel_voting_roundbiddiscussion.yaml,sha256=UGSLfOhmC-4pRqWsJvOtZRU0YLUuOMAGeEHtxTf3wf8,3710
|
|
281
284
|
kaggle_environments/static/player.html,sha256=Icl5yYscPe4BRoWt0HLOSRJWnznQq2MdTHHCaC2OrQQ,27753
|
|
282
|
-
kaggle_environments-1.23.
|
|
283
|
-
kaggle_environments-1.23.
|
|
284
|
-
kaggle_environments-1.23.
|
|
285
|
-
kaggle_environments-1.23.
|
|
286
|
-
kaggle_environments-1.23.
|
|
285
|
+
kaggle_environments-1.23.12.dist-info/entry_points.txt,sha256=h03sq76TdcHvXKcsre1Qm3lIni9dkWehu61xJqI-p8k,69
|
|
286
|
+
kaggle_environments-1.23.12.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
287
|
+
kaggle_environments-1.23.12.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
288
|
+
kaggle_environments-1.23.12.dist-info/METADATA,sha256=DnCtDRHWmVXDwo27KBl8wgu7HV2U8iJEQqOeYb5ebko,917
|
|
289
|
+
kaggle_environments-1.23.12.dist-info/RECORD,,
|
|
File without changes
|
{kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{kaggle_environments-1.23.10.dist-info → kaggle_environments-1.23.12.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|