import React, { useEffect, useMemo, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { SCENARIOS } from './fixtures.js';
import {
ScenarioMap, OrdersPanel, AgentFeed, RewardPanel,
ControlBar, TickScrubber, FinalCard, ActionBanner,
} from './components.jsx';
import './styles.css';
const MODES = [
{ id: 'trained', label: 'AI agent (trained LoRA)', shortLabel: 'Trained policy', tone: 'trained' },
{ id: 'heuristic', label: 'Heuristic dispatcher', shortLabel: 'Heuristic baseline', tone: 'heuristic' },
{ id: 'prompt_only', label: 'Prompt-only agent', shortLabel: 'Untrained baseline', tone: 'baseline' },
];
const THINKING_MS = 1800;
const ACTION_MS = 1200;
function getTrajectory(scenario, modeId) {
return scenario.trajectories[modeId] || scenario.trajectories.trained;
}
function summarizeScenario(scenario, modeId) {
const trajectory = getTrajectory(scenario, modeId);
const first = trajectory[0];
const last = trajectory[trajectory.length - 1];
const delivered = last.orders.filter((o) => o.status === 'delivered').length;
const expired = last.orders.filter((o) => o.status === 'expired').length;
const onTime = last.orders.filter(
(o) => o.status === 'delivered' && (o.delivered_tick ?? 999) <= o.deadline_tick,
).length;
const successRate = last.orders.length ? (delivered / last.orders.length) * 100 : 0;
return {
trajectory,
previewTick: Math.min(2, trajectory.length - 1),
delivered,
expired,
onTime,
successRate,
totalReward: last.cumulative_reward ?? 0,
activeOrdersAtStart: first.orders.length,
couriersAtStart: first.couriers.length,
maxTicks: scenario.metadata.max_ticks,
};
}
function ScenarioSummary({ scenario }) {
const m = scenario.metadata;
return (
{m.difficulty}
{m.num_couriers} couriers · {m.num_orders} orders · {m.max_ticks} ticks
{m.skill_focus.map((s) => {s.replace(/_/g, ' ')} )}
{m.description}
);
}
function PlaybackPanel({ scenario, modeId, currentTick, onJumpToTick, showRewards }) {
const trajectory = getTrajectory(scenario, modeId);
const snapshot = trajectory[Math.min(currentTick, trajectory.length - 1)];
const isFinished = currentTick >= trajectory.length - 1;
return (
);
}
function OverviewSidebar({
scenarios,
scenarioId,
onScenarioChange,
modeId,
onModeChange,
onOpenPlayground,
onAutoplayDemo,
}) {
return (
Dispatch Control Room
delivery intelligence unit
Simulation difficulty
onScenarioChange(e.target.value)}>
{Object.entries(scenarios).map(([id, s]) => (
{s.metadata.theme}
))}
Active intelligence
{MODES.map((mode) => (
onModeChange(mode.id)}
>
{mode.shortLabel}
))}
onOpenPlayground(false)}>Run playground
Autoplay demo
Choose a scenario, pick the policy to inspect, and jump into the replay playground for step-by-step dispatch simulation.
);
}
function HeroPanel({ scenario, modeId }) {
const mode = MODES.find((item) => item.id === modeId) || MODES[0];
return (
dispatch arena // live replay interface
Dispatch intelligence for food and grocery delivery
Explore how different dispatch policies behave under hidden prep times, deadline pressure,
rolling arrivals, and traffic noise. The environment is packaged for OpenEnv, GRPO training,
and public interactive demos on Hugging Face Spaces.
{scenario.metadata.difficulty}
{scenario.metadata.theme}
{mode.shortLabel}
);
}
function MetricCard({ title, eyebrow, children, tall = false }) {
return (
{title}
{eyebrow}
{children}
);
}
function MetricBar({ label, value, max = 100, tone = 'accent' }) {
const width = Math.max(6, Math.min(100, (value / max) * 100));
return (
{label}
{typeof value === 'number' ? value.toFixed(0) : value}
);
}
function OverviewPage({ scenario, modeId, onOpenPlayground }) {
const summary = summarizeScenario(scenario, modeId);
const previewSnapshot = summary.trajectory[summary.previewTick];
return (
Tick preview
t{previewSnapshot.tick}
Orders visible
{summary.activeOrdersAtStart}
Couriers online
{summary.couriersAtStart}
Total replay reward
{summary.totalReward >= 0 ? '+' : ''}{summary.totalReward.toFixed(2)}
Hidden restaurant prep times and delayed readiness.
Overlapping orders with shared courier capacity.
Deadline pressure and lateness penalties.
Traffic-sensitive travel times and route churn cost.
Fairness pressure across the active courier fleet.
{['/reset', '/step', '/state', '/summary', '/api/sessions', '/ready'].map((endpoint) => (
{endpoint}
))}
The same environment powers the public frontend, the replay interface, and GRPO training loops through the OpenEnv contract.
onOpenPlayground(false)}>Open interactive playground
);
}
function PlaygroundHeader({ onBack, scenario, modeId }) {
const mode = MODES.find((item) => item.id === modeId) || MODES[0];
return (
← Back to overview
Simulation playground
{scenario.metadata.theme} · {mode.shortLabel}
);
}
function App() {
const scenarioIds = Object.keys(SCENARIOS);
const [scenarioId, setScenarioId] = useState(scenarioIds[0]);
const [modeId, setModeId] = useState('trained');
const [currentTick, setCurrentTick] = useState(0);
const [hasReplayInteraction, setHasReplayInteraction] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
const [playbackPhase, setPlaybackPhase] = useState('idle');
const [view, setView] = useState('overview');
const scenario = SCENARIOS[scenarioId];
const trajectory = getTrajectory(scenario, modeId);
const trajectoryLen = useMemo(() => trajectory.length, [trajectory]);
useEffect(() => {
setCurrentTick(0);
setHasReplayInteraction(false);
setIsPlaying(false);
setPlaybackPhase('idle');
}, [scenarioId, modeId]);
useEffect(() => {
if (currentTick > 0) {
setHasReplayInteraction(true);
}
}, [currentTick]);
const timeoutRef = useRef(null);
useEffect(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
if (!isPlaying) {
setPlaybackPhase('idle');
return;
}
if (currentTick >= trajectoryLen - 1) {
setIsPlaying(false);
setPlaybackPhase('idle');
return;
}
setPlaybackPhase('thinking');
timeoutRef.current = setTimeout(() => {
setPlaybackPhase('acting');
timeoutRef.current = setTimeout(() => {
setCurrentTick((prev) => {
if (prev >= trajectoryLen - 1) {
setIsPlaying(false);
return prev;
}
return prev + 1;
});
}, ACTION_MS);
}, THINKING_MS);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};
}, [isPlaying, currentTick, trajectoryLen]);
const jumpToTick = (tick) => {
setIsPlaying(false);
setPlaybackPhase('idle');
setHasReplayInteraction(true);
setCurrentTick(tick);
};
const stepBack = () => {
setIsPlaying(false);
setPlaybackPhase('idle');
setHasReplayInteraction(true);
setCurrentTick((t) => Math.max(0, t - 1));
};
const stepForward = () => {
setIsPlaying(false);
setPlaybackPhase('idle');
setHasReplayInteraction(true);
setCurrentTick((t) => Math.min(trajectoryLen - 1, t + 1));
};
const resetPlayback = () => {
setCurrentTick(0);
setHasReplayInteraction(true);
setIsPlaying(false);
setPlaybackPhase('idle');
};
const openPlayground = (autoplay = false) => {
setView('playground');
setCurrentTick(0);
setHasReplayInteraction(false);
setPlaybackPhase('idle');
setIsPlaying(autoplay);
};
return (
{view === 'overview' ? (
openPlayground(true)}
/>
) : (
<>
{ setView('overview'); setIsPlaying(false); }} scenario={scenario} modeId={modeId} />
setIsPlaying((p) => !p)}
onStepBack={stepBack}
onStepForward={stepForward}
onReset={resetPlayback}
/>
>
)}
);
}
createRoot(document.getElementById('root')).render( );