PERSLIS DEFENSE · DEMO · VDSG · THE COMMANDED RUNTIME
VDSG. Give it orders. It plays by them.
Before you trust a system near your squad, you want to see three things. It does what it is told. It tells you when it can’t. It never does what you forbade. This is a recording of VDSG doing all three in id Software’s DOOM (1993, the shareware IWAD) and Wolfenstein 3D (1992). On E1M1, Hurt Me Plenty, it reaches the exit switch in 52 seconds. Say don’t fire and the trigger is gone — and no amount of learning brings it back.
How it works: the game engine’s state becomes a situation report. The rules pick a goal from the list of moves it is allowed to make right now. A memory of what went wrong ranks what is left. An actuator presses the buttons. There is no neural network anywhere in the loop. VDSG is the military Peel, the Fail-First Model: it learns from its failures, and it cannot learn its way past its limits. Every decision on the page can be explained from the situation report beside it.
The operator gives orders in words — don’t fire, shotgun only, hold position — and an order can only narrow what the pilot may do. It never invents a move. It binds the learning as well as the rules. When the situation cannot satisfy it, the order is refused with a reason, every decision, until it can be.
What you see is a recorded run, replayed panel by panel from the snapshots the loop published; the live console runs on the operator’s machine (run it yourself). Nothing here is animated by hand.
The console
V-Cards read off the frame above, by colour. The eye is taught by the engine while it plays — the map says a door is at that bearing, the depth says how far — and then labels and ranges frames on its own. agree is how often it matched the map; the time on a card is the distance at the pace the odometer measured the pilot actually making.
Every goal the rules could pursue here. An order removes goals from this set; the evidence memory ranks only inside what is left, so learning can never take an action that was forbidden.
waiting for the recording…
This is the whole input, built from engine state. The only thing that reads pixels is the eye, and its cards are a second witness, never the map.
- nothing widened yet — a rule needs enough harmful encounters to stand clear of the base rate
● VDSG ViZDoom on DOOM1.WAD v1.8 · tapped ECWolf on the WL6 data · research lane, nothing qualified
What you are watching
engine state → situation report → RULES (a goal from the admissible set) → ORDERS → evidence → actuator
▲ │
└────── the goal, the reason, the receipts, the frame ─────┘
Seven goals — HEAL · DODGE · RETREAT · ATTACK · SEARCH · RESUPPLY · EXPLORE — in a fixed priority. Each is allowed only when its object is actually there: it cannot ATTACK what is not in view, and it cannot HEAL without a medikit. The rules are about 300 lines of plain Python, with no weights and no hidden state; the same report gives the same goal every time. The memory of what went wrong is the mechanism from chapter 2. It ranks inside the allowed moves, never outside them.
The navigator plans on the map’s own geometry: an occupancy grid with doors, lifts, one-way ledges (Doom climbs 24 units and drops any) and keyed doors, read from the game data. The exit switch comes from the map’s line specials, checked against the engine’s spawn. Each decision on the page is one sentence — to the exit switch, at a yellow door without the yellow key, picked up the red key.
Giving it orders
An order only narrows
don’t fire takes the trigger away. just dodge removes every goal but DODGE. shotgun only locks the weapon. The order parser is a fixed list of phrases with explicit negation — not a model — so anything it does not recognise is refused, never guessed. An order can take buttons away; the only thing it ever sets is the weapon it named.
Refused, with a reason
shotgun only with no shotgun carried is refused — no shotgun carried, every decision, on the receipts panel above, and takes effect the moment one is picked up. The order binds the learning too: learning can only rank inside what the order left, so it cannot take a move you forbade. Enforced at five places in the code, one regression test each.
In the DOOM recording the operator gives six orders — don’t fire, as you were, shotgun only, hold position — and the page shows each one taken, refused or withdrawn as it happened. Press one in the console to jump to it.
How it sees, and how well
The smallest vision system that can label a frame and judge range. The frame is shrunk to 64×40, with nine-bit colour weighted to the dark end (two bits per channel put 80% of real DOOM in one bin). There is one colour histogram per class — ceiling, floor, wall, door, enemy, pickup — and each column is whatever class explains its pixels best. Nothing is trained ahead of time: it is taught by the engine while it plays. On Doom the label buffer names sprites, the grid says where doors are and the depth buffer says how far; on Wolfenstein a ray per column over the tile map does the same. Then it labels frames on its own, and the page shows the histograms filling, how often it agrees with the teacher, and the range error.
Range works the way the game draws it: height on screen × distance is one constant, so distance = k / height. Measured over 120 s runs: DOOM E1M1 agreement 78–92%; Wolfenstein MAP01 79%, k = 33 against a theoretical 32, range error ±7%. The eye’s own ranging on Doom is off by ±25–65% — the dark palette makes floor and wall the same colours at range — so where the engine has a depth buffer, the pilot trusts that first and shows the eye’s estimate beside it (eye says 3.8 m). The depth buffer’s scale was measured, not assumed: linear in the perpendicular distance, 7.64 units per step for walls and 7.35 for sprites, over 1,336 + 367 credible samples with sub-unit residuals.
How far, how fast
The odometer
It measures how fast it really moves and turns from what each command produced — the 80th percentile of clean full-forward samples, so bumping a wall does not drag the number down. DOOM: 47 units a decision, 12.8 m/s, calibrated within two seconds; Wolfenstein: 0.7 tiles. Every card then carries a time: door · grey · 12° right · 4.2 m · 0.4 s away.
The stuck rule, measured
One core rule binds every goal that moves: not moving forward → turn; the same movement recurring → stuck, break out, never replan into the same loop. Not moving is judged against the measured pace — under 30% of what the command should have made. The escape turns toward the deepest open space the eye sees (45–150°, in decisions at the engine’s turn rate) and walks clear for half that depth at the measured pace. Open space ahead but still not moving means something the eye cannot see is in the way — a ledge, a body — and it alternates sides as before.
Calibration caught a bug the way it should: the Wolfenstein lane was running the stuck rule in DOOM units — five tiles a decision — 150 stuck events in 1,014 decisions, one every eight. In tiles: 9.
Measured
| What | Measured | Where |
|---|---|---|
| Random / rules / rules + memory | 3.2 / 17.9 / 20.3 return on 32 seeds — even, not a win (paired t = 0.78) | Freedoom arena, chapter 2 |
| Kill credit | KILLCOUNT is the map’s counter: a pilot ordered never to fire was still credited 55 kills. Damage dealt is the honest number. | defect 8, published |
| E1M1 (Hurt Me Plenty) | cleared: exit switch at 52 s, 4 kills | real DOOM1.WAD |
| E1M1 (Nightmare) | not cleared in 8 tries, dies at 23–37 s | real DOOM1.WAD |
| E1M2 (Hurt Me Plenty) | shotgun taken at 10–20 s, red key held; dies at 167–207 s with 17–18 kills. Before weapon seeking it passed within 8.8 m of the chaingun and 16.7 m of the shotgun, never in view, and died with the pistol. | real DOOM1.WAD |
| The eye | agreement 78–92% (Doom), 79% (Wolf); range ±7% (Wolf), ±25–65% (Doom, own ranging) | 120 s runs |
| Odometer | 47 units/decision = 12.8 m/s (Doom), 0.7 tiles/decision (Wolf) | calibrated in ~2 s |
| Stuck rule in the wrong units | 150 → 9 stuck events per 1,014 decisions | Wolfenstein MAP01 |
| Wolfenstein doors | 583 of 1,275 decisions spent at doors; USE re-pressed every 0.7 s reverses an opening door in ECWolf — now pressed once, when shut | MAP01 |
Every figure measured in-house on the machine that recorded the run above; negatives included. Nothing here is qualified.
Run it live
The live console serves the same page from a local engine and takes orders as you type them. It needs the game data: DOOM1.WAD is rebuilt from the 1995 shareware installer, the Wolfenstein WL6 files from the 1992 release; both are in the archive, and neither ships in the repository.
python -m doom_floor.console --port 8099 --game doom-e1m1 --skill 5 # DOOM, Nightmare, 78 levels switchable python -m wolf_floor.console --port 8098 --level MAP01 --skill 3 # Wolfenstein 3D, 60 floors
Then open http://127.0.0.1:8099: pick a game, a level and a difficulty in the navigator, press play through to advance on every exit and retry on every death, and type an order.
What this does not show
In the runs measured for this page it clears E1M1 on Hurt Me Plenty and not on Nightmare, and it does not clear E1M2. Its own ranging on Doom is far worse than on Wolfenstein, for the reason given above. The replay is a recording: the orders shown were given while it was recorded, and the page does not take new ones. The eye labels by colour alone and says so — the agreement number on the panel is the whole claim. And a game is not a battlefield: the monsters in it do not think.
The open problem from chapter 6 stands here too: when the dangerous move is the mission, avoiding failure alone does not learn the mission. Doom is where that is being measured.
Next
Chapter 2 — what we have actually measured, including where it fails →