generide is a Python tool that reads, validates, generates, and evolves RollerCoaster Tycoon 2 track designs. It works directly with the game's .td6 file format, and the tracks it produces load and run in OpenRCT2, the open-source reimplementation of the game.
I grew up playing RollerCoaster Tycoon 2, and I started this project because I wanted to know whether I could write a program that designs new rides and gets them running in the game. Answering that meant working through a 1999-era binary file format, three-dimensional track geometry, the game's construction rules, approximate physics, and a genetic algorithm that has to produce something the game will accept.
Why generating a coaster is hard
A coaster has to form a closed circuit, avoid colliding with itself, stay inside its footprint, transition cleanly between slopes and banks, put a chain lift where the train needs one, and preserve enough energy to finish the course. A random list of track pieces fails nearly all of these at once, so a generator has to understand the game's construction rules well enough to satisfy every one of them in the same design.
There is also a file format between the Python code and the game. Before generating anything, I had to prove I understood that format, because a design the game silently refuses to load tells you nothing about whether the design was any good.
The file format problem
A .td6 file is how the game saves and shares individual ride designs. There is no official spec, only a community-maintained document that is mostly accurate and occasionally wrong. The format has two layers. The outer layer is a run-length encoding scheme RCT2 has used since 1999, where a single control byte says whether the next chunk is literal data or one repeated byte. The inner layer is a flat binary structure with values at fixed offsets, some of them bit-packed because in 1999 every byte mattered. A custom checksum protects the file, so one wrong byte means the game rejects the whole design.
Some header fields are still not fully understood. generide preserves those bytes during a round trip instead of discarding them, so a file it reads and rewrites comes back byte for byte identical to the original even where the code does not interpret the contents.
The stack
The project is structured as a stack, and each layer is only useful if the layers below it hold up.
↓
segment genome and construction rules
↓
3D geometry, occupancy, and validation
↓
TD6 serialization, compression, and checksum
↓
OpenRCT2
A high fitness score is not useful if the circuit does not close, and a valid Python object is not useful if the game rejects the file. Construction validation is shared across generation, scoring, and export, so every part of the program uses the same definition of a valid ride. Building bottom-up also made debugging tractable, because when a generated coaster failed to load I could tell whether the bug was in the generator, the encoder, or the geometry math.
How evolution works
Each coaster is represented as a list of TD6 segment IDs. That list is both the genome the algorithm mutates and the literal track written to the final file. The evolutionary loop creates a population, scores each candidate, selects parents through tournament selection, applies crossover and mutation, and carries the strongest candidates into the next generation. Every run is seeded, so an interesting result or an interesting failure can be reproduced.
Fitness is currently a proxy. It rewards coaster-like qualities such as length, elevation change, turn balance, and segment variety, and it applies heavy penalties to designs that cannot be built or completed. It does not yet reproduce the game's own excitement, intensity, and nausea ratings, and connecting candidate evaluation to those ratings is the next major step.
The mutation system is intentionally conservative. Slope and bank transitions are inserted as complete legal sequences, because OpenRCT2 treats them as stateful construction operations. That approach builds working tracks but cannot reach every supported piece, including steep drops. The next mutation design will ask the validator which pieces are legal at a specific point instead of choosing from a small collection of pre-built sequences.
Where it stands
Today generide can evolve a Mine Train layout, export it with a valid checksum, and run it in OpenRCT2, including stations, entrances, and exits. The project has 119 passing tests, including regression tests against real rides exported from the game. Reading and writing TD6 files, reconstructing and validating geometry, and authoring a coaster in Python that runs in-game are complete. Evolution is in progress.
How I build it
I use AI coding tools throughout the project for research, implementation, debugging, and review. They are only useful here because the workflow lets me check their output. I test assumptions against real exported rides, keep binary parsing separate from geometry and evolution, and write a regression test for every failure found in the game. One reference implementation, a Go project called kevinburke/rct, had a file structure worth borrowing and a sine/cosine bug in its geometry. Treating the reference as evidence rather than authority kept that bug out of this implementation.
The code, architecture guide, roadmap, and development log are all in the GitHub repository.