LaCam for Multi-Agent Path Finding
A from-scratch LaCam solver for CS440 — agents searching a space of joint configurations, each expanded by a constraint tree that rules out collisions and swaps.
- Type
- School
- Year
- 2026
- Built with
- Python · Search · Multi-agent systems
Every frame above is real: a trace captured by instrumenting my actual solver as it runs on a small two-agent instance with a single-cell doorway — every push, backtrack, branch, accept and reject shown is a genuine event from the code, not a re-enactment.
For the last assignment in CS440 (Artificial Intelligence), we had to solve “hard” multi-agent path finding (MAPF) instances: get every agent from its start cell to its goal cell on a shared grid, with no two agents ever occupying the same cell at the same time and no two agents swapping places along an edge. Optimality wasn’t graded — completeness and speed were. That combination is exactly what LaCam (Okumura, 2023) is built for, so I implemented it.
Why not just run A* per agent?
Because the moment you plan agents independently, their paths collide, and patching collisions one at a time (à la CBS) means re-planning individual agents over and over as new constraints pile up. LaCam sidesteps that by searching a completely different space: instead of nodes being single-agent positions, nodes are joint configurations — one position per agent, all at once. An edge in this space is a fully collision-free simultaneous move for every agent. Find a path from the start configuration to the goal configuration in that space, and you’ve solved the whole team in one search.
The catch is that the branching factor of “every agent moves at once” is
enormous — up to 5 choices per agent (4 directions + wait), so 5^n for n
agents. LaCam’s second idea is what makes that tractable: at each
configuration node, don’t enumerate all 5^n successors. Grow a small
constraint tree instead — a breadth-first tree where each node forces one
additional agent to a specific next cell, and a greedy assignment (nearest
neighbor to that agent’s goal, checked against everyone already assigned)
fills in the rest. Pop one constraint tree node, try to build a full
successor; if it collides, that branch dies and the next constraint tree node
gets a turn. The two searches interleave: depth-first over configurations,
breadth-first over constraints at each one.
Three spaces, one algorithm
That gives you three different spaces to keep straight, which is the part that’s genuinely hard to hold in your head from the pseudocode alone:
- The problem space — the actual grid: walls, start cells, goal cells.
- The search space — the DFS over joint configurations. Each node here is a full grid state for every agent simultaneously.
- The constraint space — the BFS tree grown at whichever configuration node is currently on top of the DFS stack, used only to generate that node’s next valid configuration.
Watch the loop at the top of the page with that in mind: the constraint tree (right panel) grows and gets pruned every time the two agents contend for the doorway, and that failure or success feeds directly into whether the search tree (middle panel) gains a new node or backtracks.
What I’d change
The greedy assignment inside each constraint tree node has no lookahead, so
on this doorway instance it backtracks a fair amount before it finds the
crossing order that works — you can see it in the trace. The published LaCam
paper adds a PIBT-style priority scheme to cut that down substantially. I
kept mine simple on purpose: the assignment above is exactly what’s in
hard_mapf.py, and it’s fast enough to pass every instance in the course’s
test suite well under the time limit, which was the actual assignment.
Route the robots yourself
The solver below is a small JavaScript port of the same idea, with the PIBT-style configuration generator from the paper in place of my greedy assignment. Pick a puzzle or draw your own, watch it route every robot at once, then try to beat it by hand. LaCam is complete but not optimal, so on most boards a careful human can shave a few steps off its plan.
Try it · Route the robots
Draw walls, drop up to six robots and their goals, then let a JavaScript LaCAM solver route them — or try to beat it by hand.
Loading puzzle…
- Solver makespan
- —
- Solver sum of costs
- —
- Configs explored
- —
- Your steps
- —
- Your sum of costs
- —
- Your moves
- —
Keyboard and rules
- Editing: arrow keys move the cursor, Enter or Space applies the tool, W / S / G / X pick a tool, 1–6 pick a robot.
- Playing: 1–6 pick a robot, arrow keys plan its move (. to wait), Enter steps everyone at once, U undoes.
- Watching: Space plays or pauses, left and right arrows step through time.
- Every robot moves at the same time each step. Two robots can't end up on the same cell, and two neighbours can't swap places. Makespan is when the last robot arrives; sum of costs adds up each robot's arrival time.