Skip to content

Contingency: plans as staged sessions

You plan; Bo, a scripted commander, builds live. Every plan you draw is a staged session over the board Bo is building on, so you watch it go red the moment Bo builds where it wanted to. Everything runs in this tab: two clients, one server and a simulation, on the in-memory engine, with no backend. Play it full window, which is the better way on a phone.

Under the game are its documents, as one client holds them. Switch between your client and Bo’s, raise a latency, and watch his writes sit unconfirmed and the two views disagree until the server catches up. A plan’s card lists its staged operations, each write behind the test that guards it, and the guards that no longer hold turn red.

  • Start. Nothing moves until you press Start, and you can plan before you do: close the intro card and Start waits in the wave forecast at the top. The first wave comes 30 seconds after.
  • Plan. Click or tap a cell and pick what to build there, or what to do with the tower on it. The first change starts a plan. Its towers show as blue holograms, and every other plan’s cells carry that plan’s badge.
  • Deploy. “Deploy” puts the whole plan on the board at once, for one of your two command points.
  • Watch it go red. Bo builds live and doesn’t look at your plans. When he builds on a cell your plan needs, the plan turns red. “Rebase on the board” takes the board as it is now and keeps the rest of the plan.
  • Spend your own gold. You and Bo each have a wallet, and every kill pays into both. Bo spends his on live towers; your plans spend yours.
  • Break the network. Your latency slider and unplug switch sit at the foot of the plan panel; Bo’s are in his view. Unplugged, you can keep planning, but you can’t deploy.

The castle has 20 lives; hold it for eight waves to win.

A plan is a staged session with a host document. Each plan is a plan:<id> document that holds its session’s changeset. Because the changeset lives in a document, every pane can open every plan, and a plan survives an unplugged pane. The holograms are the session’s staged view: the live board with the plan laid over it, which keeps moving as Bo builds.

A red plan is a derived conflict. Every buildable cell exists from the start as { tower: null }, so a staged placement is a replace guarded by a test that the cell is still empty. When Bo builds on that cell, the guard no longer holds and the plan’s stage is blocked. Plans use the default autoMerge policy: Bo building elsewhere leaves them clean. Rebase is the resolution verb that accepts the live board.

A deploy is one atomic commit. Deploying stages a ledger entry for the plan’s cost on your wallet, the bank:planner document, then commits: the towers on board and the cost in your wallet land together or not at all.

Live building is a guarded write. Bo’s towers go straight to the board as guarded patches, applied optimistically and confirmed or rejected by the server. Raise his latency and watch his towers appear late in your view.

Gold is a ledger, not a counter. You and Bo each have a wallet, a bank:planner and a bank:commander document. Every purchase, kill and deploy adds an entry under a fresh key, so spending never conflicts with anything; a balance is the sum. Every kill pays into both wallets. datadata’s authorization doesn’t read document contents, so nothing in the engine stops two deploys from overspending. The game allows debt instead: while a wallet is negative, the towers it paid for fire at half rate.

Pointers and enemies are presence. Each player’s pointer, and the plan they’re viewing, are presence cells. So are the enemies: the simulation publishes their positions ten times a second, and they never enter the event log.

  • The second player is a bot, in the same tab. Two people in two browsers would need a server; the demo shows the engine, not a deployment.
  • One changeset per host document. Each plan needs its own document, because a host holds one changeset. Multiplexed changesets are an open question.
  • No overspend guard in the engine. Debt is a game rule standing in for an integer decrement the engine doesn’t have.
  • Unmeasured on phones. The game lowers its resolution and shadow detail on touch devices, but its frame rate on a real phone hasn’t been measured.