kaggle-environments 1.23.11__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 +75 -13
- kaggle_environments/envs/open_spiel_env/games/repeated_poker/visualizer/default/src/repeated_poker_renderer.js +238 -28
- {kaggle_environments-1.23.11.dist-info → kaggle_environments-1.23.12.dist-info}/METADATA +1 -1
- {kaggle_environments-1.23.11.dist-info → kaggle_environments-1.23.12.dist-info}/RECORD +10 -7
- {kaggle_environments-1.23.11.dist-info → kaggle_environments-1.23.12.dist-info}/WHEEL +0 -0
- {kaggle_environments-1.23.11.dist-info → kaggle_environments-1.23.12.dist-info}/entry_points.txt +0 -0
- {kaggle_environments-1.23.11.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
|
+
});
|
|
@@ -61,15 +61,22 @@ function _parseStepHistoryData(universalPokerJSON, nextPlayerIndex, numPlayers =
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const odds = universalPokerJSON.odds || [];
|
|
64
|
-
|
|
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, {
|
|
65
75
|
style: 'percent',
|
|
66
|
-
minimumFractionDigits:
|
|
76
|
+
minimumFractionDigits: 1,
|
|
77
|
+
maximumFractionDigits: 1
|
|
67
78
|
});
|
|
68
|
-
|
|
69
|
-
style: 'percent',
|
|
70
|
-
minimumFractionDigits: 2
|
|
71
|
-
});
|
|
72
|
-
result.winOdds = [p0WinOdds, p1WinOdds];
|
|
79
|
+
result.handRank = fiveCardBestHands;
|
|
73
80
|
|
|
74
81
|
return result;
|
|
75
82
|
}
|
|
@@ -140,7 +147,7 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
140
147
|
|
|
141
148
|
const parsedStateHistory = _parseStepHistoryData(
|
|
142
149
|
stateInfo.universal,
|
|
143
|
-
|
|
150
|
+
event.actingPlayer,
|
|
144
151
|
numPlayers
|
|
145
152
|
);
|
|
146
153
|
|
|
@@ -163,11 +170,12 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
163
170
|
cards: [],
|
|
164
171
|
currentBet: contributions[i] || 0,
|
|
165
172
|
isDealer: stateInfo.outer?.dealer === i,
|
|
166
|
-
isTurn:
|
|
173
|
+
isTurn: event.actingPlayer === i,
|
|
167
174
|
isLastActor: event.highlightPlayer === i,
|
|
168
175
|
reward: rewards[0]?.[i] ?? null,
|
|
169
|
-
actionDisplayText: event.
|
|
170
|
-
|
|
176
|
+
actionDisplayText: event.winner === i ? "Winner" : parsedStateHistory.playerActionStrings[i],
|
|
177
|
+
isWinner: event.winner === i,
|
|
178
|
+
handCount: 0,
|
|
171
179
|
};
|
|
172
180
|
});
|
|
173
181
|
|
|
@@ -182,6 +190,57 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
182
190
|
|
|
183
191
|
const displayCommunity = event.hideCommunity ? [] : communityCards;
|
|
184
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
|
+
|
|
185
244
|
return {
|
|
186
245
|
players,
|
|
187
246
|
communityCards: displayCommunity,
|
|
@@ -189,10 +248,13 @@ export const getPokerStateForStep = (environment, step) => {
|
|
|
189
248
|
isTerminal: false,
|
|
190
249
|
rawObservation: stateInfo.universal,
|
|
191
250
|
step,
|
|
192
|
-
|
|
193
|
-
|
|
251
|
+
winProb: parsedStateHistory.winProb,
|
|
252
|
+
tieProb: parsedStateHistory.tieProb,
|
|
253
|
+
handRank: parsedStateHistory.handRank,
|
|
194
254
|
currentPlayer: stateInfo.universal?.current_player ?? -1,
|
|
195
255
|
winner: -1,
|
|
196
256
|
handCount: event.hand,
|
|
257
|
+
previousHands,
|
|
258
|
+
leaderInfo
|
|
197
259
|
};
|
|
198
260
|
};
|
|
@@ -29,7 +29,7 @@ export function renderer(options) {
|
|
|
29
29
|
chipStacks: [],
|
|
30
30
|
diagnosticHeader: null,
|
|
31
31
|
stepCounter: null,
|
|
32
|
-
|
|
32
|
+
legend: null
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const css = `
|
|
@@ -134,14 +134,14 @@ export function renderer(options) {
|
|
|
134
134
|
.player-info-area {
|
|
135
135
|
color: white;
|
|
136
136
|
width: auto;
|
|
137
|
-
min-width:
|
|
137
|
+
min-width: 280px;
|
|
138
138
|
pointer-events: auto;
|
|
139
139
|
display: flex;
|
|
140
140
|
flex-direction: column;
|
|
141
141
|
justify-content: center;
|
|
142
142
|
align-items: center;
|
|
143
|
-
margin:
|
|
144
|
-
padding:
|
|
143
|
+
margin: 10px auto;
|
|
144
|
+
padding: 10px;
|
|
145
145
|
background-color: rgba(32, 33, 36, 0.70);;
|
|
146
146
|
border-radius: 16px;
|
|
147
147
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
@@ -163,7 +163,7 @@ export function renderer(options) {
|
|
|
163
163
|
justify-content: center;
|
|
164
164
|
gap: 16px;
|
|
165
165
|
margin: 0 60px;
|
|
166
|
-
padding:
|
|
166
|
+
padding: 5px 0;
|
|
167
167
|
}
|
|
168
168
|
.player-thumbnail {
|
|
169
169
|
width: 48px;
|
|
@@ -185,6 +185,20 @@ export function renderer(options) {
|
|
|
185
185
|
.player-name.winner { color: #FFEB70; }
|
|
186
186
|
.player-name.current-turn { color: #20BEFF; }
|
|
187
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
|
+
}
|
|
188
202
|
.player-cards-container { min-height: 80px; display: flex; justify-content: center; align-items:center;}
|
|
189
203
|
.card {
|
|
190
204
|
display: flex; flex-direction: column; justify-content: space-between; align-items: center;
|
|
@@ -243,7 +257,6 @@ export function renderer(options) {
|
|
|
243
257
|
box-shadow: 0 1px 3px rgba(0,0,0,0.3); z-index: 15; pointer-events: auto;
|
|
244
258
|
border: 2px solid black;
|
|
245
259
|
outline: 2px solid #20BEFF;
|
|
246
|
-
left: 320px
|
|
247
260
|
}
|
|
248
261
|
.dealer-button.dealer-player0 { top: 170px; }
|
|
249
262
|
.dealer-button.dealer-player1 { bottom: 170px; }
|
|
@@ -312,6 +325,70 @@ export function renderer(options) {
|
|
|
312
325
|
font-weight: bold;
|
|
313
326
|
white-space: nowrap;
|
|
314
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
|
+
}
|
|
315
392
|
`;
|
|
316
393
|
|
|
317
394
|
function _injectStyles(passedOptions) {
|
|
@@ -503,6 +580,11 @@ export function renderer(options) {
|
|
|
503
580
|
<div class="player-card-area">
|
|
504
581
|
<div class="player-cards-container"></div>
|
|
505
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>
|
|
506
588
|
<div class="player-stack">
|
|
507
589
|
<span class="player-stack-value">0</span>
|
|
508
590
|
</div>
|
|
@@ -522,20 +604,16 @@ export function renderer(options) {
|
|
|
522
604
|
elements.dealerButton.style.display = 'none';
|
|
523
605
|
elements.playersContainer.appendChild(elements.dealerButton);
|
|
524
606
|
|
|
525
|
-
elements.
|
|
526
|
-
elements.
|
|
527
|
-
elements.
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
elements.
|
|
532
|
-
elements.handCounter.textContent = '';
|
|
533
|
-
elements.gameLayout.appendChild(elements.handCounter);
|
|
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);
|
|
534
614
|
|
|
535
615
|
return true;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// --- State Parsing ---
|
|
616
|
+
} // --- State Parsing ---
|
|
539
617
|
function _parseKagglePokerState(options) {
|
|
540
618
|
const { environment, step } = options;
|
|
541
619
|
|
|
@@ -573,18 +651,123 @@ export function renderer(options) {
|
|
|
573
651
|
|
|
574
652
|
function _renderPokerTableUI(data) {
|
|
575
653
|
if (!elements.pokerTable || !data) return;
|
|
576
|
-
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
|
+
}
|
|
577
687
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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);
|
|
582
748
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
749
|
+
winnerCell.appendChild(winnerCellContainer);
|
|
750
|
+
row.appendChild(winnerCell);
|
|
751
|
+
|
|
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);
|
|
586
767
|
}
|
|
587
768
|
|
|
769
|
+
legendBody.appendChild(table);
|
|
770
|
+
|
|
588
771
|
if (elements.diagnosticHeader && data.rawObservation) {
|
|
589
772
|
// Optional: Show diagnostics for debugging
|
|
590
773
|
// elements.diagnosticHeader.textContent = `[${passedOptions.step}] P_TURN:${data.rawObservation.current_player} POT:${data.pot}`;
|
|
@@ -689,6 +872,27 @@ export function renderer(options) {
|
|
|
689
872
|
} else {
|
|
690
873
|
betDisplay.style.display = 'none';
|
|
691
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
|
+
}
|
|
692
896
|
}
|
|
693
897
|
});
|
|
694
898
|
|
|
@@ -696,10 +900,16 @@ export function renderer(options) {
|
|
|
696
900
|
if (elements.dealerButton) {
|
|
697
901
|
if (dealerPlayerIndex !== -1) {
|
|
698
902
|
elements.dealerButton.style.display = 'block';
|
|
699
|
-
// Remove previous dealer class
|
|
700
903
|
elements.dealerButton.classList.remove('dealer-player0', 'dealer-player1');
|
|
701
|
-
// Add new dealer class based on player index
|
|
702
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
|
+
}
|
|
703
913
|
} else {
|
|
704
914
|
elements.dealerButton.style.display = 'none';
|
|
705
915
|
}
|
|
@@ -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.11.dist-info → kaggle_environments-1.23.12.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{kaggle_environments-1.23.11.dist-info → kaggle_environments-1.23.12.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|