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 (
{isFinished &&
}
); } function OverviewSidebar({ scenarios, scenarioId, onScenarioChange, modeId, onModeChange, onOpenPlayground, onAutoplayDemo, }) { return ( ); } 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.
); } function PlaygroundHeader({ onBack, scenario, modeId }) { const mode = MODES.find((item) => item.id === modeId) || MODES[0]; return (
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();