bpHigh commited on
Commit
4fedd95
·
verified ·
1 Parent(s): b36b193

3D replay: the models' plans only

Browse files
README.md CHANGED
@@ -24,7 +24,7 @@ tasks on other days.
24
  |---|---|
25
  | `/web` | OpenEnv's web UI. **Explore**: the eval explorer, the 3D replay and the train graph. **Playground**: reset, step and the MCP tools by hand |
26
  | `/explorer/` | the leaderboard and every model's plan on every eval task; pick one to watch it |
27
- | `/twin/` | the 3D replay: any plan on the line's real track from OpenStreetMap, signals, level crossings, closures, the train graph and the cost as it accrues |
28
  | `/viewer/` | the train graph (time across, distance down) of any plan, with the plan's holds, short-turns and rule breaks |
29
  | `/reset`, `/step`, `/state`, `/ws`, `/mcp` | OpenEnv |
30
  | `/rail_ops/...` | OpenEnv Task API (`splits`, `num_tasks`, `task`, `task_range`) |
 
24
  |---|---|
25
  | `/web` | OpenEnv's web UI. **Explore**: the eval explorer, the 3D replay and the train graph. **Playground**: reset, step and the MCP tools by hand |
26
  | `/explorer/` | the leaderboard and every model's plan on every eval task; pick one to watch it |
27
+ | `/twin/` | the 3D replay: each model's plan on every eval task, on the line's real track from OpenStreetMap, with signals, level crossings, closures, the train graph and the cost as it accrues |
28
  | `/viewer/` | the train graph (time across, distance down) of any plan, with the plan's holds, short-turns and rule breaks |
29
  | `/reset`, `/step`, `/state`, `/ws`, `/mcp` | OpenEnv |
30
  | `/rail_ops/...` | OpenEnv Task API (`splits`, `num_tasks`, `task`, `task_range`) |
openenv/rail_openenv/web/explorer/index.html CHANGED
@@ -220,7 +220,7 @@ function renderGrid() {
220
  }).join("");
221
  return `<tr><td class="task"><b>${esc(day(r.t.day))}</b> ${esc(r.t.start)}–${esc(r.t.end)} <span class="tier">${esc(TIER[r.t.difficulty] || r.t.difficulty)}</span><br><small>${esc(cause)}</small></td>`
222
  + `<td class="n">${r.t.runs}</td>${cells}<td class="n">${f3(r.mean)}</td>`
223
- + `<td><a href="/twin/?task=${encodeURIComponent(r.id)}&plan=optimal">3D</a> · <a href="/viewer/?task=${encodeURIComponent(r.id)}&plan=optimal">graph</a></td></tr>`;
224
  }).join("");
225
  $("#grid").innerHTML = head + `<tbody>${body}</tbody>`;
226
  for (const h of $("#grid").querySelectorAll("th.sort")) h.onclick = () => {
 
220
  }).join("");
221
  return `<tr><td class="task"><b>${esc(day(r.t.day))}</b> ${esc(r.t.start)}–${esc(r.t.end)} <span class="tier">${esc(TIER[r.t.difficulty] || r.t.difficulty)}</span><br><small>${esc(cause)}</small></td>`
222
  + `<td class="n">${r.t.runs}</td>${cells}<td class="n">${f3(r.mean)}</td>`
223
+ + `<td><a href="/twin/?task=${encodeURIComponent(r.id)}">3D</a> · <a href="/viewer/?task=${encodeURIComponent(r.id)}&plan=optimal">graph</a></td></tr>`;
224
  }).join("");
225
  $("#grid").innerHTML = head + `<tbody>${body}</tbody>`;
226
  for (const h of $("#grid").querySelectorAll("th.sort")) h.onclick = () => {
openenv/rail_openenv/web/twin/app.js CHANGED
@@ -162,11 +162,14 @@ function seek(t) {
162
 
163
  // --- loading a task and a plan ----------------------------------------------------------------------------
164
 
 
 
165
  async function loadPlans(taskId, want) {
166
- const plans = await json(`/api/tasks/${encodeURIComponent(taskId)}/plans`);
 
 
167
  $("#plan").innerHTML = plans.map((p) => `<option value="${p.id}">${p.label}${p.reward !== null && p.reward !== undefined ? ` · ${(+p.reward).toFixed(3)}` : ""}</option>`).join("");
168
- const model = plans.find((p) => p.id.startsWith("run:"));
169
- $("#plan").value = plans.some((p) => p.id === want) ? want : model ? model.id : "optimal";
170
  }
171
 
172
  async function loadReplay(keepTime) {
@@ -239,12 +242,13 @@ async function main() {
239
  $("#loadmsg").textContent = "OpenStreetMap: track, stations, buildings…";
240
  const [twin, tasks] = await Promise.all([loadTwin(), json("/api/tasks")]);
241
  TWIN = twin;
242
- const label = (t) => `${t.day} ${t.start}–${t.end} · ${TIER[t.difficulty] || t.difficulty} · ${t.runs} trains${t.model_plans ? " · ★" : ""}`;
243
- const group = (split) => tasks.filter((t) => t.split === split).sort((a, b) => (b.model_plans - a.model_plans) || a.task_id.localeCompare(b.task_id));
244
- $("#task").innerHTML = ["eval", "train"].map((sp) => `<optgroup label="${sp === "eval" ? "Eval (★ has a model's plan)" : "Train"}">`
245
- + group(sp).map((t) => `<option value="${t.task_id}">${label(t)}</option>`).join("") + "</optgroup>").join("");
 
246
  const want = params.get("task");
247
- $("#task").value = tasks.some((t) => t.task_id === want) ? want : tasks.some((t) => t.task_id === FEATURED) ? FEATURED : tasks[0].task_id;
248
  $("#station").innerHTML += twin.stations.map((s) => `<option value="${s.code}">${s.name}</option>`).join("");
249
  graph = new TrainGraph($("#graph"), $("#gtip"), (t) => seek(t), (u) => select(u, false));
250
  cost = new CostChart($("#cost"), $("#ctip"), (t) => seek(t));
 
162
 
163
  // --- loading a task and a plan ----------------------------------------------------------------------------
164
 
165
+ // the replay shows the models' plans (the eval runs) and live episodes on this server, not the reference methods
166
+ // (the CP-SAT optimum, the greedy dispatchers, change nothing); a server without model runs offers everything
167
  async function loadPlans(taskId, want) {
168
+ const all = await json(`/api/tasks/${encodeURIComponent(taskId)}/plans`);
169
+ const models = all.filter((p) => p.id.startsWith("run:") || p.id.startsWith("episode:"));
170
+ const plans = models.length ? models : all;
171
  $("#plan").innerHTML = plans.map((p) => `<option value="${p.id}">${p.label}${p.reward !== null && p.reward !== undefined ? ` · ${(+p.reward).toFixed(3)}` : ""}</option>`).join("");
172
+ $("#plan").value = plans.some((p) => p.id === want) ? want : plans[0].id;
 
173
  }
174
 
175
  async function loadReplay(keepTime) {
 
242
  $("#loadmsg").textContent = "OpenStreetMap: track, stations, buildings…";
243
  const [twin, tasks] = await Promise.all([loadTwin(), json("/api/tasks")]);
244
  TWIN = twin;
245
+ const label = (t) => `${t.day} ${t.start}–${t.end} · ${TIER[t.difficulty] || t.difficulty} · ${t.runs} trains`;
246
+ // the eval tasks the models were run on; every task when this server has no model runs
247
+ const evals = tasks.filter((t) => t.split === "eval" && t.model_plans);
248
+ const shown = (evals.length ? evals : tasks).sort((a, b) => a.task_id.localeCompare(b.task_id));
249
+ $("#task").innerHTML = shown.map((t) => `<option value="${t.task_id}">${label(t)}</option>`).join("");
250
  const want = params.get("task");
251
+ $("#task").value = shown.some((t) => t.task_id === want) ? want : shown.some((t) => t.task_id === FEATURED) ? FEATURED : shown[0].task_id;
252
  $("#station").innerHTML += twin.stations.map((s) => `<option value="${s.code}">${s.name}</option>`).join("");
253
  graph = new TrainGraph($("#graph"), $("#gtip"), (t) => seek(t), (u) => select(u, false));
254
  cost = new CostChart($("#cost"), $("#ctip"), (t) => seek(t));