Spaces:
Sleeping
Sleeping
Demo: per-step action log showing which policy made each move (hanabi/mini-hanabi)
Browse files
benchmark_ui/frontend/src/envs/hanabi/GameLog.tsx
CHANGED
|
@@ -20,9 +20,12 @@ export type LogEntry = {
|
|
| 20 |
type Props = {
|
| 21 |
entries: LogEntry[]
|
| 22 |
state: HanabiState
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
-
export function GameLog({ entries, state }: Props) {
|
| 26 |
if (entries.length === 0) {
|
| 27 |
return (
|
| 28 |
<div className="text-xs text-ink-500 italic">
|
|
@@ -41,7 +44,7 @@ export function GameLog({ entries, state }: Props) {
|
|
| 41 |
const absoluteIdx = total - 1 - revIdx
|
| 42 |
return (
|
| 43 |
<li key={absoluteIdx} className="text-sm animate-fade-in">
|
| 44 |
-
<LogLine entry={entry} state={state} />
|
| 45 |
</li>
|
| 46 |
)
|
| 47 |
})}
|
|
@@ -49,8 +52,9 @@ export function GameLog({ entries, state }: Props) {
|
|
| 49 |
)
|
| 50 |
}
|
| 51 |
|
| 52 |
-
function LogLine({ entry, state }: { entry: LogEntry; state: HanabiState }) {
|
| 53 |
-
const
|
|
|
|
| 54 |
const whoClass = entry.player === 0 ? "text-hanabi-blue" : "text-hanabi-yellow"
|
| 55 |
const base = <span className={`font-medium ${whoClass}`}>{who}</span>
|
| 56 |
|
|
@@ -95,5 +99,5 @@ function LogLine({ entry, state }: { entry: LogEntry; state: HanabiState }) {
|
|
| 95 |
)
|
| 96 |
}
|
| 97 |
|
| 98 |
-
return <div className="text-ink-500">{
|
| 99 |
}
|
|
|
|
| 20 |
type Props = {
|
| 21 |
entries: LogEntry[]
|
| 22 |
state: HanabiState
|
| 23 |
+
// optional override for player labels. Defaults to ["You", "Partner"] for /play mode.
|
| 24 |
+
// /demo mode passes the actual policy names.
|
| 25 |
+
playerNames?: [string, string]
|
| 26 |
}
|
| 27 |
|
| 28 |
+
export function GameLog({ entries, state, playerNames }: Props) {
|
| 29 |
if (entries.length === 0) {
|
| 30 |
return (
|
| 31 |
<div className="text-xs text-ink-500 italic">
|
|
|
|
| 44 |
const absoluteIdx = total - 1 - revIdx
|
| 45 |
return (
|
| 46 |
<li key={absoluteIdx} className="text-sm animate-fade-in">
|
| 47 |
+
<LogLine entry={entry} state={state} playerNames={playerNames} />
|
| 48 |
</li>
|
| 49 |
)
|
| 50 |
})}
|
|
|
|
| 52 |
)
|
| 53 |
}
|
| 54 |
|
| 55 |
+
function LogLine({ entry, state, playerNames }: { entry: LogEntry; state: HanabiState; playerNames?: [string, string] }) {
|
| 56 |
+
const [name0, name1] = playerNames ?? ["You", "Partner"]
|
| 57 |
+
const who = entry.player === 0 ? name0 : name1
|
| 58 |
const whoClass = entry.player === 0 ? "text-hanabi-blue" : "text-hanabi-yellow"
|
| 59 |
const base = <span className={`font-medium ${whoClass}`}>{who}</span>
|
| 60 |
|
|
|
|
| 99 |
)
|
| 100 |
}
|
| 101 |
|
| 102 |
+
return <div className="text-ink-500">{base} passed</div>
|
| 103 |
}
|
benchmark_ui/frontend/src/pages/Demo.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
| 11 |
type PartnerInfo,
|
| 12 |
} from "../api/client"
|
| 13 |
import { getEnvComponents } from "../envs/registry"
|
|
|
|
| 14 |
|
| 15 |
type AgentOption = {
|
| 16 |
id: string
|
|
@@ -264,9 +265,20 @@ export function Demo() {
|
|
| 264 |
/>
|
| 265 |
</div>
|
| 266 |
|
| 267 |
-
{
|
| 268 |
-
<
|
| 269 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
</div>
|
| 271 |
)}
|
| 272 |
</div>
|
|
@@ -282,6 +294,44 @@ export function Demo() {
|
|
| 282 |
)
|
| 283 |
}
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
function AgentSelect({
|
| 286 |
label,
|
| 287 |
options,
|
|
|
|
| 11 |
type PartnerInfo,
|
| 12 |
} from "../api/client"
|
| 13 |
import { getEnvComponents } from "../envs/registry"
|
| 14 |
+
import { GameLog, type LogEntry } from "../envs/hanabi/GameLog"
|
| 15 |
|
| 16 |
type AgentOption = {
|
| 17 |
id: string
|
|
|
|
| 265 |
/>
|
| 266 |
</div>
|
| 267 |
|
| 268 |
+
{(env === "hanabi" || env === "mini-hanabi") && parsedState && (
|
| 269 |
+
<HanabiDemoLog
|
| 270 |
+
frames={frames}
|
| 271 |
+
upToIdx={frameIdx}
|
| 272 |
+
state={parsedState as any}
|
| 273 |
+
playerNames={[labelA, labelB]}
|
| 274 |
+
/>
|
| 275 |
+
)}
|
| 276 |
+
{!(env === "hanabi" || env === "mini-hanabi") && currentFrame.event && (
|
| 277 |
+
<div className="text-xs text-ink-400">
|
| 278 |
+
<span className="font-medium text-ink-200">
|
| 279 |
+
{currentFrame.actor === 0 ? labelA : labelB}
|
| 280 |
+
</span>
|
| 281 |
+
<span className="font-mono"> · {String((currentFrame.event as any).kind ?? JSON.stringify(currentFrame.event))}</span>
|
| 282 |
</div>
|
| 283 |
)}
|
| 284 |
</div>
|
|
|
|
| 294 |
)
|
| 295 |
}
|
| 296 |
|
| 297 |
+
// Per-frame event narration for Hanabi/mini-hanabi demos so the user can see
|
| 298 |
+
// each move attributed to a specific policy.
|
| 299 |
+
function HanabiDemoLog({
|
| 300 |
+
frames, upToIdx, state, playerNames,
|
| 301 |
+
}: {
|
| 302 |
+
frames: { event: Record<string, unknown> | null; actor: number | null }[]
|
| 303 |
+
upToIdx: number
|
| 304 |
+
state: any
|
| 305 |
+
playerNames: [string, string]
|
| 306 |
+
}) {
|
| 307 |
+
const entries: LogEntry[] = []
|
| 308 |
+
for (let i = 0; i <= Math.min(upToIdx, frames.length - 1); i++) {
|
| 309 |
+
const f = frames[i]
|
| 310 |
+
if (!f.event || f.actor == null) continue
|
| 311 |
+
const ev = f.event as any
|
| 312 |
+
const player = (f.actor === 0 ? 0 : 1) as 0 | 1
|
| 313 |
+
if (ev.kind === "play" || ev.kind === "discard" || ev.kind === "hint_color" || ev.kind === "hint_rank" || ev.kind === "noop") {
|
| 314 |
+
entries.push({
|
| 315 |
+
player,
|
| 316 |
+
kind: ev.kind,
|
| 317 |
+
slot: ev.slot,
|
| 318 |
+
color: ev.color,
|
| 319 |
+
rank: ev.rank,
|
| 320 |
+
bombed: ev.bombed,
|
| 321 |
+
scored: ev.scored,
|
| 322 |
+
revealed: ev.revealed,
|
| 323 |
+
})
|
| 324 |
+
}
|
| 325 |
+
}
|
| 326 |
+
return (
|
| 327 |
+
<div className="rounded-lg border border-ink-800 bg-ink-950/40 p-4 max-h-72 overflow-y-auto">
|
| 328 |
+
<div className="text-xs uppercase tracking-wider text-ink-500 mb-2">Action log</div>
|
| 329 |
+
<GameLog entries={entries} state={state} playerNames={playerNames} />
|
| 330 |
+
</div>
|
| 331 |
+
)
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
|
| 335 |
function AgentSelect({
|
| 336 |
label,
|
| 337 |
options,
|