56,000 lines of DOOM, in a language I made up.
bet is a small, real programming language: slang keywords on the surface, a serious compiler underneath. It compiles to native code through LLVM, manages its own memory, self-hosts, and runs DOOM. I built it as a contained experiment, and ran it to completion.
I built bet on a break from trying to make something that sells. I wanted a
project with a clear edge to it, one I could actually finish, and building a
compiler had been on my list for a long time. A joke language gave me the excuse:
keywords that read like a group chat, wrapped around a compiler that had to
genuinely work. I set one rule at the start: no research. I never checked whether
anyone had built this before, because I didn’t want the internet to kill the idea
before I’d written a line. As it turned out, Geoffrey Huntley
already had, and I didn’t learn that until later.
A language that only works at the level of syntax is really just a costume, so I
gave it one real job. Games allocate and release objects constantly, and both
manual bookkeeping and garbage collection cost you frames you can’t spare. bet
handles that with arenas: allocate into a scope, use it for a frame, drop the
whole scope at once. The pattern itself isn’t new. Zig ships an arena allocator
that frees everything in one call, and Odin bakes the same arena and
temp-allocator idea in for game code. I wanted bet to carry it as a first-class
feature with its own keywords, so the language would read as a design of its own
rather than a slang reskin of something that already existed.
It’s a joke. I still wanted it to actually do something.
The other reason I built it was to run an experiment. I wanted to see how far an
AI coding agent could get on a hard, well-specified project if I stayed out of
the implementation. My job was to be the architect: I set the problem and the
acceptance criteria, then held the line on them. I did zero code review. A change
shipped only if it passed its tests, met the acceptance criteria, and cleared the
corpus. That was the only gate.
The proof that it works is DOOM, ported in full. id Software’s original C source
for the renderer, the game logic, the WAD loading, and the audio was rewritten in
bet: more than 56,000 lines, compiled to a native binary that runs the real
shareware game in a window.
Launch it and DOOM plays itself in attract mode. Those clips are the game’s
original recorded inputs, replayed through the simulation. Fixed-point math is
what makes that a real test: drift by a single step and the player starts walking
into walls. bet replays the inputs in lockstep with the reference C build, frame
for frame, so the whole simulation comes out bit-for-bit identical. Getting that
far with no shortcuts is what separates a toy language from one that can carry
real software, and it’s the result I trust most on a build I never code-reviewed.
So it finished, end to end: the compiler, self-hosting, documentation, and this
site. And that is where it stops. If a feature request comes in I’ll review it,
but there’s no roadmap and no version two on the way. bet was a joke and an
experiment and a way to learn what a build like this
actually takes, and it did all of that. If there’s one thing I want it to say
about me, it’s that I finish what I start.
The memory model is the real work.
Manual free() bookkeeping and GC pauses both wreck a frame budget. In bet you allocate into a crib, an arena scoped to the frame, and release the whole thing with evict in O(1). No per-object tracking, no stall in the middle of a frame.
That is the experiment. A joke on the surface, one hard problem solved properly underneath, and a build gated by nothing but its tests. It compiles to native code, it self-hosts, and it runs DOOM. It was never meant to become a product, and it doesn't need to be one. It's finished.
Where the time actually went.
Real numbers from a commit-tracked timelog, counting active effort with idle time clamped out. That clamping has one gap: three of the agents porting DOOM stalled on usage limits while the clock kept running, so the porting-games row runs about 12 hours high. Take those off and the real total is nearer 22.
I didn't know cursed existed until bet was already built. Geoffrey Huntley reached the same idea first: a slang-keyword language, compiled through LLVM, written by directing an AI, the same way I built bet. He did it well, and he got there before me. The credit is his.