For the first couple of months, each player effectively had their own world. There were two coordinate systems — one per side — stitched together at a seam down the middle of the board. It worked when units mostly stayed on their own half.
That worked until it didn't
The trouble started the moment things had to cross the middle. Pathfinding had to translate positions between the two coordinate systems. Combat across the seam had to do the same. Working out which player owned a unit standing near the middle was genuinely ambiguous. Every new system had to learn about the seam and pay a tax to deal with it, and the bugs all clustered in the same place.
So we stopped patching it and rewrote the core.
One canonical board
Now there's a single board — one coordinate system, one list of every unit on the field, everything measured in the same space. The seam isn't a real thing in the simulation anymore; it's just where we split the screen. Combat and movement compute distances directly, with no translating back and forth. It was a big rewrite, but almost every system got simpler on the other side of it.
The two-seats problem
Then the rendering problem: both players sit on opposite sides of one horizontal screen, so each one needs the board to read with their side at the bottom. If we just drew it once, one player would be looking at everything upside down.
The fix is to draw Player 2's view into an off-screen image and flip it when we paint it to their half of the screen. We also rotate the far territory's terrain 180° so it isn't upside down from their seat. Both players' cameras still agree on where everything actually is, so input doesn't need any per-player flipping — only the final picture does.
What's still rough
Sprites that cross the middle still need care so they face the right way during the handoff, and there are edge cases we're still finding. But the foundation is finally solid, and we're not scared to touch movement code anymore.