Orchestrate the Warehouse

Write JavaScript to coordinate robots, manage batteries, and conquer deadlocks—before your simple rules create emergent gridlock

Connect an AI agent (Claude, Cursor, etc.) to control the simulation
Preset:Assign orders to nearest idle robot using atomic locks. Includes collision resolution.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
Tick
0/3000
Fulfilled
0
Avg Time
0
Pending
0
In Progress
0
Robot Util
0%
Battery
0
Conveyor
0
Stocked
0
Inventory
0
Deadlocked
0
SPEED1.0x
Initializing simulation...
No events yet

Warehouse Simulation Documentation

Learn how to write algorithms that control warehouse robots to fulfill orders efficiently.

🚀 Quick Start

Your algorithm controls robots to pick items from shelves and deliver them to the shipping zone. You can also manage restocking from the conveyor belt.

{
  init: function(robots, warehouse, store) {
    // Called once when simulation starts
    // Set up any initial state
  },

  update: function(robots, orders, warehouse, store, conveyorItems) {
    // Called every tick
    // Assign orders, navigate robots, handle stocking
  }
}

🎲 Determinism & RNG

Simulations are strictly deterministic for a given seed and algorithm. Use rand() or seededMath.random() for randomness.

Real-time APIs like Date,performance, timers, andcrypto, plus network APIs likefetch, and Node globals likeprocess/requireare stubbed or disabled to keep runs reproducible.

Date values are tick-based and normalized to UTC, and performance.now() returns the current tick count for stable timing across machines.

⏱️ Runtime Limits & Reset

init and update are time-boxed to 50ms. If you exceed the limit, the run stops with an error.

Editing code or config marks the simulation as stale. Hit Reset to apply changes.

Blocked APIs (determinism):
setTimeout / setInterval
requestAnimationFrame
queueMicrotask / setImmediate
fetch / XMLHttpRequest / WebSocket
crypto.getRandomValues / randomUUID
Function / eval
process / require
Promise
navigator / document / location
localStorage / sessionStorage / indexedDB
Intl
BroadcastChannel / Worker
postMessage

🏆 Leaderboard Scoring

Algorithms run on 1,000 canonical seeds. Lower scores rank higher.

score_per_seed =
  sum(completedAt - createdAt for each completed order)
  + sum(maxTicks - createdAt for each unfinished order)

final_score = average(score_per_seed over all seeds)

📊 Simulation Metrics

The stats bar updates every tick and highlights throughput, delays, and system health.

Order Metrics

fulfilled- Total completed orders
avgTime- Average ticks to complete an order
pending- Orders waiting to be assigned
inProgress- Orders currently being picked or delivered

System Metrics

robotUtil- Percent of robots that are busy
avgBattery- Average robot battery level
conveyor- Items currently on the conveyor
stocked- Items stocked since start
inventory- Items currently on shelves
deadlocked- Robots stuck for 30+ ticks

🧾 Event Log

The event log is your primary debugging tool. It records significant simulation and algorithm events as they happen.

Orders & Stocking

order- New order created
assign- Order assigned to a robot
pickup- Robot picked item from shelf
deliver- Order delivered to shipping
incoming- Item arrived on conveyor
receive- Robot picked from conveyor
stocked- Robot stocked item on shelf

Robots & System

stuck- Robot blocked for multiple ticks
deadlock- Deadlock detected or run paused
battery- Battery warning or recharge event
message- Direct robot message
broadcast- Robot broadcast message
log- console.log output
error- Algorithm runtime error

🤖 Robot API

Properties

robot.id- Unique identifier (number)
robot.x, robot.y- Current grid position
robot.state- 'idle' | 'picking' | 'delivering' | 'stocking'
robot.carrying- Item type emoji or null
robot.currentOrder- Assigned order object or null
robot.stockingTarget- Shelf spot for stocking or null
robot.battery- Current charge (0-400)
robot.path- Array of waypoints [{x, y}, ...]
robot.pathfindAlgorithm- 'astar' | 'bfs' | 'greedy' | 'custom'
robot.ordersCompleted- Total orders delivered
robot.itemsStocked- Total items stocked

Movement Methods

robot.navigateTo(x, y)
Pathfind to grid coordinates
robot.pathfindVia(algo)
Set pathfinding: 'astar', 'bfs', 'greedy', or function
robot.goToCharging()
Navigate to charging zone
robot.goToReceiving()
Navigate to conveyor pickup area
robot.clearPath()
Cancel current navigation
robot.distanceTo(x, y)
Manhattan distance to point

Order Methods

robot.assignOrder(order)
Assign an order to this robot. Sets state to 'picking'.

Stocking Methods

robot.pickFromConveyor(item)
Pick item from conveyor belt. Must be at receiving zone.
robot.assignStockingTarget(spot)
Set shelf destination. spot = {shelfPos, pickupPos}
robot.inReceivingZone()
Returns true if robot is in conveyor pickup area

Battery & Status

robot.needsCharging()
Returns true if battery < 100
robot.inChargingZone()
Returns true if in charging zone
robot.getBatteryPercent()
Battery percent (0-100)
robot.isStuck()
Returns true if hasn't moved for 5+ ticks

Communication

robot.sendMessage(robotId, data)
Send message to another robot
robot.broadcast(data)
Send message to all robots
robot.getMessages()
Get received messages [{from, data, tick}, ...]
robot.peekMessages()
Peek messages without clearing the queue

Path & Collision Helpers

robot.getPath()
Current path as waypoints
robot.getDestination()
Final waypoint or null
robot.getBlocker()
Robot blocking next step or null
robot.repath()
Recalculate path to destination
robot.getPathLengthTo(x, y)
Path length to target, or -1
robot.yield()
Move aside to unblock traffic

🗄️ Store API

The store is persistent key-value storage that persists across ticks. It also provides atomic locks for coordination.

Key-Value Storage

store.get(key)
Get value for key (or undefined)
store.set(key, value)
Store a value
store.has(key)
Check if key exists (true/false)
store.delete(key)
Remove a key
store.clear()
Remove all keys

Atomic Locks

store.locks.acquire(resourceId, robotId)
Try to lock resource. Returns true if acquired.
store.locks.release(resourceId, robotId)
Release lock. Only succeeds if robot owns it.
store.locks.isLocked(resourceId)
Check if resource is locked
store.locks.owner(resourceId)
Get robotId that owns lock, or null
store.locks.releaseAll(robotId)
Release all locks held by a robot

📦 Data Structures

Order Object

{
  id: "order-42",
  item: "📦",           // Item type to pick
  status: "pending",    // "pending" | "in-progress" | "completed"
  priority: 1,          // 0 (low) .. 2 (high)
  createdAt: 150,       // Tick when created
  assignedRobot: null,  // Robot id or null
  completedAt: null     // Tick when delivered
}

Warehouse Object

{
  width: 20, height: 14,
  grid: [[0,0,1,...], ...], // 0=walkable, 1=shelf
  items: [{                  // Items on shelves
    id, type, shelfPos, pickupPos, quantity
  }, ...],
  emptyShelfSpots: [{        // Available for stocking
    shelfPos: {x, y}, pickupPos: {x, y}
  }, ...],
  shippingZone: {x, y, width, height},
  chargingZone: {x, y, width, height},
  conveyorZone: {x, y, width, height, pickupY}
}

Conveyor Item

{
  id: "incoming-150-42",
  type: "📱",           // Item emoji
  x: 20,                // X position on conveyor
  y: 8.5,               // Y position (flows north)
  createdAt: 150        // Tick when spawned
}

🧭 Navigation Algorithms

Each robot can use a different pathfinding algorithm. By default, all robots use A*. You can change a robot's pathfinding strategy using robot.pathfindVia().

Setting Pathfinding Per Robot

// In your init or update function:

// Use a built-in algorithm (default is 'astar')
robot.pathfindVia('astar');   // A* - optimal path
robot.pathfindVia('bfs');     // Breadth-first search
robot.pathfindVia('greedy');  // Greedy best-first

// Or use a custom function
robot.pathfindVia(function(grid, start, end, robots, selfId) {
  // Your custom pathfinding logic
  return [{x: 1, y: 2}, {x: 2, y: 2}, ...]; // or null
});

// Check current algorithm
console.log(robot.pathfindAlgorithm); // 'astar', 'bfs', 'greedy', or 'custom'

Different robots can use different algorithms - try A* for precision tasks and Greedy for fast responses!

Custom Pathfinding Function Signature

function customNav(grid, start, end, robots, selfId) {
  // grid: 2D array (0=walkable, 1=shelf)
  // start: {x, y} - robot's current position
  // end: {x, y} - destination
  // robots: array of all robots (for collision avoidance)
  // selfId: this robot's id

  // Return: array of {x, y} waypoints, or null if no path
  return [{x: 1, y: 2}, {x: 2, y: 2}, ...];
}

💡 Tips & Strategies

Order Assignment

  • Consider robot battery level before assigning
  • Account for travel distance to both item AND shipping
  • Don't assign orders for items that don't exist
  • Track which orders are already assigned

Deadlock Prevention

  • Use isStuck() to detect blocked robots
  • Implement yielding: lower-priority robots move aside
  • Use message passing to coordinate
  • Consider zone-based assignment to reduce conflicts

Battery Management

  • Send robots to charge when battery < 150
  • Don't assign new orders to low-battery robots
  • Charging zone is at top-right
  • Robots charge +5 per tick when in zone

Restocking

  • Balance order fulfillment with restocking
  • Don't let the conveyor back up (max 6 items)
  • Check emptyShelfSpots for available slots
  • Conveyor items flow north (y decreases)
Built for learning algorithms through simulation