Deriving a Two-Glider Synthesis From Scratch: a Block and a Blinker

Our own pattern-synthesis guide claims two gliders can collide into a block or a blinker, but never shows one. Here's both, derived by brute-force search and verified live against our MCP eval sandbox.

angen.ai
August 24, 2026
4 min read
mcp
glider synthesis
algorithm
cellular automata
how to

Deriving a Two-Glider Synthesis From Scratch: a Block and a Blinker

Our own guide to pattern synthesis makes a specific claim: "the simplest syntheses use just two gliders" — a block from a head-on collision, a blinker from "a specific angle collision." It never shows either one. No starting positions, no cell counts, nothing you could paste into a simulator and watch happen. That's the kind of claim worth actually building rather than repeating, so this piece does exactly that: a brute-force search over glider offsets, two collisions that genuinely work, and both verified live against our MCP eval sandbox rather than asserted from memory.

A glider is 5 cells, and it has four possible directions of travel (rotations of the same shape). Two gliders approaching each other have two free parameters once you fix their directions: the perpendicular offset and the phase offset between them — everything else is determined. That's a small enough search space to brute-force directly: place one glider at a fixed position, slide the second one across a range of relative offsets, simulate every combination forward, and check what's left once things settle.

We wrote a plain Python life-stepper for this (verified first against the glider's own well-known period-4 diagonal drift, so the stepper itself wasn't the thing being trusted on faith) and searched two collision angles:

Head-on — one glider moving southeast, the other northwest, aimed at each other on the same diagonal. Sweeping the perpendicular offset from −6 to 6 found exactly three offsets that don't just make the gliders pass through or annihilate completely — they collapse into a block. The tightest of the three:

.o......
..o.....
ooo.....
........
........
........
........
.....ooo
.....o..
......o.

RLE: bo$2bo$3o$$$$$5b3o$5bo$6bo!

Angled — one glider moving southeast, the other moving in a direction 90° from that. A similar sweep found an offset that settles into a blinker instead:

o....
o.o..
oo.o.
....o
..ooo

RLE: o$obo$2obo$4bo$2b3o!

Checking both live, not just locally

A local Python simulation proves the search logic is internally consistent, but it doesn't prove our production simulator agrees — and the whole point of citing a result is that someone else can reproduce it. Both starting patterns above went through run_generations against the real endpoint (https://life.angen.ai/api/mcp/mcp):

curl -X POST "https://life.angen.ai/api/mcp/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
    "name":"run_generations",
    "arguments":{"rle":"bo$2bo$3o$$$$$5b3o$5bo$6bo!","generations":12}
  }}'

Response: {"rle":"2o$2o!","generationsRun":12,"population":4,"extinct":false,"aborted":false,"offset":{"dx":3,"dy":4}} — a clean 2×2 block, population 4, by generation 12. verify_prediction confirms the same thing with an explicit diff (match: true, zero missing or extra cells), which is worth doing separately from run_generations even when you already have the answer: it's the tool that actually checks a claimed result against the real one, rather than just producing a result to eyeball.

The angled collision is slower to resolve — the two gliders interact through several generations of debris (population climbs to 14 before it collapses) before settling. Feeding the same starting RLE to run_generations for 44 generations returns {"rle":"3o!","population":3,"extinct":false,"aborted":false} — a blinker, exactly as the local search predicted, and stable for at least several generations past that (checked by re-running with higher generation counts and confirming the population stays at 3).

What this confirms, and what it doesn't

This validates the two specific claims our existing synthesis article makes in passing — two gliders really can produce a block or a blinker, not just in principle but with an actual, reproducible starting configuration for each. It doesn't validate the article's larger claims (three-glider syntheses, "massive syntheses" needing hundreds of gliders for patterns like Gemini) — those are real and well-documented in the wider Life community, but outside what a same-day brute-force search over a 2-glider offset space can independently check. Worth being explicit about the boundary: verifying a specific, small, checkable claim is not the same as verifying every claim in the same paragraph.

Try it yourself

Both RLE strings above are small enough to paste directly into the simulator via the pattern library's Custom RLE tab, or to run through your own brute-force search — the search loop here is under 40 lines of Python with no dependencies beyond a Life step function. If you'd rather drive the search itself through an agent, the MCP tutorial and the terminal recipe cover connecting Claude to the same run_generations/verify_prediction tools used here, so the checking step doesn't have to be a separate local script at all.