2026-10-01 · ATOMIC ACRES

The bot that found the sealed graveyards

A bot that plays both players found graveyards nobody could walk into, a leak that took 186 loaded geometries to 1,089 over one night, and a diagnostic that was wrong about the game it was watching. Then the "hazy graphics" turned out to be the resolution scaler misreading fast-forward.

LEAVE US ALONE! is a two-player co-op run-and-gun set in a 1950s suburb. On this day it was still called Atomic Wake, and the aliens had not arrived yet. The first thing built was a way to watch the game test itself.

A bot that plays like a person

AI Watch plays both heroes through the same input path a keyboard or gamepad uses, one step at a time at 60 Hz. It picks goals, steers around threats, rolls through telegraphed attacks and reports what it finds. It runs on a sandboxed copy of the save, so a watched run can never touch real progress, and it can be sped up to 8x.

Its job was not to win. Its job was to make the game's own mistakes visible, and in its first hour it did.

Graveyards with no way in

Every grave in a graveyard lot was unreachable. Two causes stacked:

  • The lot picked which side its fence gap faced at random, so on edge lots the gap could face the edge of the map.
  • The nav grid keeps about 0.8 m clear around every fence post, so a 2.4 m gap left less than a metre of free ground. That band could fall between two 1 m cells and seal the whole yard, for the bot and for enemy pathing alike.

The gap now faces the centre of the valley, it is 3.4 m wide, and anything placed on ground that cannot be reached from the start is filtered out at generation. Across 78 generated maps (3 districts, 6 Watches each) every grave, spawn, pickup, hideout and gate is reachable.

The same check found enemies being dropped into pockets a walker can never leave. The nav grid now stores each cell's distance from the start, and waves, summons and boss adds only land on reachable ground.

186 geometries to 1,089

One full night of loading stages took resident geometries from 186 to 1,089.

Isolation tests (build, render, dispose, count) showed the maps and hubs were clean, and that each humanoid leaked about four geometries. placed() clones a cached geometry, and the clone inherited the flag that marks a geometry as shared. Disposal skips anything marked shared, so every clone was skipped forever. The fix is one line: delete the flag on the clone.

A second leak was hiding behind it. Every stage built a new 256x256 noise texture for the mist, plus its buffers, and never released any of them.

beforeafter
resident geometries over repeated stage loads186 climbing to 1,089flat, 106 to 110
mist texturerebuilt every stageone cached, reused

Stage loads also got faster, because a 65,000-sample noise loop no longer runs each time.

When the instrument is wrong

A good share of what the bot reported was the bot or the analyser being wrong:

  • A hit one tick after a telegraph was logged as an untelegraphed attack. The checker was racing the game.
  • The bot reported "no route" to the same unreachable grave 441 times. Delivering to a grave is now a bonus, and an unreachable one is blacklisted.
  • Steering froze in one-cell corridors, because wall penalties looked 3.8 m ahead even when the target was 2 m away. Look-ahead is now capped at the distance to the target.
  • The leak check used to sample on a timer. It now samples at every stage load and looks for a rising trend, so a one-off spike does not read as a leak.

The "hazy graphics" were the scaler

A playtest said the picture had gone soft after looking good. The cause was ours. The dynamic-resolution scaler measures whole-frame time, and AI Watch fast-forward runs several simulation steps inside each frame. The scaler read that as a slow GPU and dropped render resolution toward its 55% floor.

It now skips adapting while the bot is fast-forwarding and restores full scale on return to 1x. At 8x the scale holds at 0.9.

That fixed the bot's view, but not the player's. The picture was still soft, with animated film grain on top, and the frame was genuinely expensive. Measured on the integrated GPU the game is being tuned against:

frame cost
full resolution, medium37 ms
half resolution19 ms
post-processing passes alone, at full resolutionabout 19 ms
scene, at full resolutionabout 18 ms

Mist, light shafts, particles, shadows and grass were not the cost. Fill rate was, and the scaler was sitting near 58%, so every frame was a soft upscale.

The fix was to stop fighting for resolution. The scene now renders small, at a size chosen so each internal pixel is an exact k by k block of screen pixels, and is blitted up with nearest-neighbour sampling. The whole grade (bloom, vignette, tone map) runs at that small size too. No blur, no shimmer, no grain.

frame cost
pixel mode, 634x457 at x25 to 7 ms, in every quality preset
native 1:1about 23 ms

The roof see-through fade used a noisy stipple that swam against the new pixel grid. It is a fixed 4x4 ordered pattern now.

One screen, and a leash

The split-screen compositor was removed in favour of a single camera that follows the midpoint of the pair and zooms out as they spread. To keep both heroes in view, only the player moving away is stopped once the two are further apart than the widest zoom can frame. The bot plans around that leash, and the same seed that took a test Watch 570 seconds now takes 147.

What the bot said about difficulty

With the bugs out of its way, the bot cleared every Watch in 56 to 138 seconds while taking between 0 and 30 damage in total. The Dread system, which is meant to apply pressure, peaked between 0.02 and 0.40 and never bit. The party also split 20 to 30 m apart with no penalty.

First pass at the numbers: the enemy budget went up about 30%, Dread now climbs about 2.2 times faster (1.7 times faster while the pair is split), and rescuing a neighbor relieves 0.11 Dread instead of 0.17.

Gamepads

Xbox and PlayStation pads are detected and normalised, including the non-standard PlayStation layouts with a hat d-pad and trigger axes. Two double-binds were fixed on the way: Square used to fire and interact, and Triangle used to use an item and interact. Menus auto-repeat when a direction is held, and a join press no longer also clicks Begin.