Skip to content

Commit 22ed671

Browse files
committed
Add lightweight replay viewer
1 parent 91023dc commit 22ed671

6 files changed

Lines changed: 530 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ __pycache__/
1111
*.jsonl.gz
1212
!tests/fixtures/*.jsonl
1313
!benchmarks/ccerl-v1/positions/*.jsonl
14+
!viewer/*.jsonl
1415
*.manifest.json
1516
*.npz
1617
*.pt

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ External engines can implement CCP and be run through:
7878
python3 tools/ccbench.py referee-ccp --help
7979
```
8080

81+
## Replay Viewer
82+
83+
A small static replay viewer is included at
84+
[viewer/replay.html](viewer/replay.html). It can load the included sample game or
85+
any JSONL produced by:
86+
87+
```sh
88+
./build/cczero match --rules strict --p0 greedy --p1 random --seed 7 --max-plies 80 --log build/game.jsonl
89+
```
90+
8191
## Model Artifacts
8292

8393
The public repository records exact model labels, paths, sizes, and hashes used
@@ -91,4 +101,3 @@ Release assets and restored to the paths documented in
91101
No open-source license has been selected yet. Until a license is added, this
92102
source is public for inspection and reproducibility but is not broadly licensed
93103
for reuse or redistribution.
94-

viewer/replay.css

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
:root {
2+
color-scheme: light;
3+
--bg: #f7f4ec;
4+
--surface: #fffdfa;
5+
--ink: #211f1a;
6+
--muted: #6f695e;
7+
--line: #d9d0c1;
8+
--red: #c9503d;
9+
--blue: #2f6f9f;
10+
--gold: #d4a72c;
11+
}
12+
13+
* {
14+
box-sizing: border-box;
15+
}
16+
17+
body {
18+
margin: 0;
19+
background:
20+
radial-gradient(circle at 1px 1px, rgba(33, 31, 26, 0.05) 1px, transparent 0) 0 0 / 24px 24px,
21+
var(--bg);
22+
color: var(--ink);
23+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
24+
}
25+
26+
.app {
27+
width: min(1180px, calc(100vw - 32px));
28+
margin: 0 auto;
29+
padding: 28px 0 36px;
30+
}
31+
32+
header {
33+
display: flex;
34+
align-items: end;
35+
justify-content: space-between;
36+
gap: 18px;
37+
margin-bottom: 18px;
38+
}
39+
40+
h1 {
41+
margin: 0;
42+
font-size: clamp(2rem, 5vw, 4rem);
43+
line-height: 0.95;
44+
}
45+
46+
.eyebrow {
47+
margin: 0 0 7px;
48+
color: var(--muted);
49+
font-size: 0.75rem;
50+
font-weight: 800;
51+
letter-spacing: 0.12em;
52+
text-transform: uppercase;
53+
}
54+
55+
.actions,
56+
.controls {
57+
display: flex;
58+
gap: 8px;
59+
flex-wrap: wrap;
60+
}
61+
62+
button,
63+
.file-button {
64+
min-height: 40px;
65+
padding: 0 14px;
66+
border: 1px solid var(--line);
67+
border-radius: 8px;
68+
background: var(--surface);
69+
color: var(--ink);
70+
font: inherit;
71+
font-weight: 700;
72+
cursor: pointer;
73+
}
74+
75+
button:hover,
76+
.file-button:hover {
77+
border-color: color-mix(in srgb, var(--ink) 30%, var(--line));
78+
}
79+
80+
.file-button input {
81+
position: absolute;
82+
inline-size: 1px;
83+
block-size: 1px;
84+
opacity: 0;
85+
pointer-events: none;
86+
}
87+
88+
.workspace {
89+
display: grid;
90+
grid-template-columns: minmax(0, 1fr) 310px;
91+
gap: 18px;
92+
align-items: start;
93+
}
94+
95+
.board-wrap,
96+
.panel {
97+
border: 1px solid var(--line);
98+
border-radius: 8px;
99+
background: color-mix(in srgb, var(--surface) 94%, transparent);
100+
box-shadow: 0 18px 40px -32px rgba(33, 31, 26, 0.45);
101+
}
102+
103+
.board-wrap {
104+
aspect-ratio: 1 / 1;
105+
min-height: 360px;
106+
padding: 14px;
107+
}
108+
109+
#board {
110+
display: block;
111+
width: 100%;
112+
height: 100%;
113+
}
114+
115+
.panel {
116+
display: grid;
117+
gap: 16px;
118+
padding: 16px;
119+
}
120+
121+
.meta {
122+
display: grid;
123+
gap: 10px;
124+
margin: 0;
125+
}
126+
127+
.meta div {
128+
padding-bottom: 10px;
129+
border-bottom: 1px solid var(--line);
130+
}
131+
132+
dt {
133+
color: var(--muted);
134+
font-size: 0.72rem;
135+
font-weight: 800;
136+
letter-spacing: 0.08em;
137+
text-transform: uppercase;
138+
}
139+
140+
dd {
141+
margin: 3px 0 0;
142+
font-weight: 700;
143+
}
144+
145+
.slider-label {
146+
display: flex;
147+
justify-content: space-between;
148+
color: var(--muted);
149+
font-size: 0.85rem;
150+
font-weight: 700;
151+
}
152+
153+
input[type="range"] {
154+
width: 100%;
155+
accent-color: var(--ink);
156+
}
157+
158+
.move-card {
159+
min-height: 86px;
160+
padding: 12px;
161+
border: 1px solid var(--line);
162+
border-radius: 8px;
163+
background: #fff8ea;
164+
}
165+
166+
.move-card p:last-child {
167+
margin: 0;
168+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
169+
font-size: 0.95rem;
170+
line-height: 1.35;
171+
}
172+
173+
.hole {
174+
fill: #ece2d2;
175+
stroke: #c8bcaa;
176+
stroke-width: 0.7;
177+
}
178+
179+
.piece-0 {
180+
fill: var(--red);
181+
stroke: #7f2d23;
182+
stroke-width: 1.1;
183+
}
184+
185+
.piece-1 {
186+
fill: var(--blue);
187+
stroke: #1e425c;
188+
stroke-width: 1.1;
189+
}
190+
191+
.last-from {
192+
fill: color-mix(in srgb, var(--gold) 28%, #ece2d2);
193+
}
194+
195+
.last-to {
196+
stroke: var(--gold);
197+
stroke-width: 2.4;
198+
}
199+
200+
.move-path {
201+
fill: none;
202+
stroke: var(--gold);
203+
stroke-width: 2.2;
204+
stroke-linecap: round;
205+
stroke-linejoin: round;
206+
}
207+
208+
@media (max-width: 820px) {
209+
.app {
210+
width: min(100vw - 20px, 680px);
211+
padding-top: 18px;
212+
}
213+
214+
header,
215+
.workspace {
216+
grid-template-columns: 1fr;
217+
}
218+
219+
header {
220+
align-items: start;
221+
}
222+
223+
.board-wrap {
224+
min-height: 300px;
225+
}
226+
}

viewer/replay.html

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>JumpStar Replay Viewer</title>
7+
<link rel="stylesheet" href="replay.css">
8+
</head>
9+
<body>
10+
<main class="app">
11+
<header>
12+
<div>
13+
<p class="eyebrow">JumpStar / CCERL</p>
14+
<h1>Replay Viewer</h1>
15+
</div>
16+
<div class="actions">
17+
<button id="loadSample" type="button">Load sample</button>
18+
<label class="file-button">
19+
<input id="fileInput" type="file" accept=".jsonl,application/json">
20+
Open JSONL
21+
</label>
22+
</div>
23+
</header>
24+
25+
<section class="workspace" aria-label="Replay board and controls">
26+
<div class="board-wrap">
27+
<svg id="board" role="img" aria-label="Chinese Checkers board"></svg>
28+
</div>
29+
30+
<aside class="panel">
31+
<dl class="meta">
32+
<div><dt>Players</dt><dd id="players">-</dd></div>
33+
<div><dt>Rule profile</dt><dd id="rules">-</dd></div>
34+
<div><dt>Status</dt><dd id="status">No game loaded</dd></div>
35+
</dl>
36+
37+
<div class="controls">
38+
<button id="prev" type="button" aria-label="Previous move">Prev</button>
39+
<button id="play" type="button" aria-label="Play or pause">Play</button>
40+
<button id="next" type="button" aria-label="Next move">Next</button>
41+
</div>
42+
43+
<label class="slider-label" for="plySlider">
44+
Ply <span id="plyText">0 / 0</span>
45+
</label>
46+
<input id="plySlider" type="range" min="0" max="0" value="0">
47+
48+
<div class="move-card">
49+
<p class="eyebrow">Current move</p>
50+
<p id="moveText">-</p>
51+
</div>
52+
</aside>
53+
</section>
54+
</main>
55+
56+
<script src="replay.js"></script>
57+
</body>
58+
</html>

0 commit comments

Comments
 (0)