Hacking Roller Coaster Tycoon
12 Year Old Me
Goal - Build cool coasters via computer program
Might be tractable!
What is a cool coaster?
High Excitement/Low Sickness
How do you interface with the game?
How do you interface with the game?
Tracks are stored separately
TD6 format
Plan of action:
- Generate tracks outside of the game
- Save to the RCT track format
- Load them in the game
- ...
- Profit
How to represent the track pieces?
Track Data (cont'd)
CPU registers + instructions
RCT Source Code
- 4,862,557 instructions
- ~1,289,635 bytes of data
- No labels/symbols!
- Poor program flow (GOTOs, etc)
How do you find what you are looking for?
OpenRCT2
- Cross platform support! (via SDL2)
- Better game AI!
- Cheats!
- Twitch integration!
- Languages (CN, FR, NL, ES, PO, HU, more)
Janitor unionization
Coming to a Platform Near You
NB: you still need the game assets
How do you test as you go?
How can you test as you go?
- CPU starts execution at an entry point specified by ABI
- Compile game, C into shared DLL
- Modify game's rct2.exe to point to the C entry point
- Distribute new .exe
Finding Game Data - 2 Objectives
- Find the track data
- Find the excitement/nausea ratings
IDA Pro
IDA (cont'd)
Where Is Anything
Track Data Has Consistent Order
Good enough to make guesses
Guessing Track Data (cont'd)
Guessing Track Data (cont'd)
A Good Guess
A Good Guess (cont'd)
A Good Guess (cont'd)
Rating Data
Rating Data
Okay! Time to build an algorithm.
Track needs to be a complete circuit!
Track needs to be a complete circuit!
Track can't collide with itself
Hard Math
- 100 track pieces on average
- ~13 average branch factor
- ~13^100 possible track designs
- roughly 2.47e+111
- Chess: 1e+120
Trim the problem space!
- No Loops
- No Diagonal Pieces
- No Brakes/Boosters
Using a genetic algorithm
Reproduction Strategies
- Selection
- Mutation
- Crossover
Genetic algorithm cont'd
Goal: "Hello"
fitness("Lr2Kl")
How many characters would have to change?
fitness("HerKo")
How many characters would have to change?
Smaller distance from a "good" solution == better fitness
The fitness function is the most expensive part!
Algorithm
- Generate a random pool of strings
- Evaluate fitness
- Let the best ones "reproduce" & form next generation
- Repeat at (1)
Use track pieces instead of strings
Book - David Goldberg
Mutation
- Generate random number for each track piece
- If random # <
MUTATION_PROBABILITY
- Replace the piece with a different piece
Mutation (2)
- Generate random number for each track piece
- If random # <
MUTATION_PROBABILITY
- Insert a new piece
Crossover
- Choose 2 parents via
Select()
- Choose crossover point at random
- Return
TrackA[0] + Track B[1]
- Return
Track B[0] + Track A[1]
Crossover (2)
Occasionally, let the parents survive
Generated Coaster 1
Generated Coaster 1
Generated Coaster 1
Partial coasters don't load
Data inconsistencies will kill you
Observance: Table driven tests
Table Driven Tests (cont'd)
Lessons Learned
- Data inconsistencies will kill you
- Write tests for 2D code
- Fitness function complexity is hard to predict
- Make the problem as small as possible
- Do the dumbest thing
Future Work
- Excitement/Intensity/Nausea
- Genetic Algorithm for fitness costs
- Dynamically weight mutated/chosen track piece
- CGo - call into OpenRCT2
- Integrate with given landscape/layout
- In-browser coaster viewer
Thanks!
Kevin Burke
These slides are available at:
←
→
/
#