Icy Tower - Game & AI
A game written from scratch, then a network evolved to play it
- 179
- floors reached by the AI
- 2,000
- generations trained
- 0
- game engines or ML libraries used
What it does
A browser remake of Icy Tower built without a game engine, and a neural network taught to play it. The game has hand-written physics, a webcam hand-tracking control mode, ghost replays of previous runs and a coin economy with unlockable characters. The AI side is a neural network evolved by a genetic algorithm, which reached floor 179 after 2,000 generations.
How it works
Game
The clock runs at a fixed rate
The game always takes the same size step, whatever the frame rate, so a fast machine and a slow one play the same. If the tab goes to the background the step is capped, otherwise coming back would replay ten seconds at once.
Movement written by hand
Gravity, friction, and how the player lands on a platform are all written directly. The rule that matters: the faster you are running, the higher you jump. Everything else in the game is built on that one line.
Platforms get reused
A platform that scrolls off the bottom is moved back to the top instead of being thrown away, so the tower can go on forever without the memory growing.
Three ways to play
Keyboard, touch, or webcam. In webcam mode your hand steers, and a quick flick up or a pinch makes you jump. The hand position is smoothed first, or every tremor would read as a move.
AI
The game again, with nothing to look at
Training needs to play thousands of games a minute, which no browser will do. So the game was rebuilt in Python and run headless: no window, no drawing, just the numbers.
Evolve, do not train
Population of 200, top 10% carried forward untouched, layer-wise crossover that swaps whole kernel and bias pairs, and a mutation sigma that doubles after five generations without improvement.
One bridge file
Evolved weights serialise to JSON that both runtimes read, so the network trained in Python is the same one that plays in the browser.
The shape of it
game.js
the brain of the game
- ported to:
icy_tower.py
same brain, but headless
- 2,000 generations:
genetic_algorithm.py
200 pop, 10% elites
neural_net.py
the player's decision-maker
- writes:
best_weights.json
the bridge
- read back by:
Browser AI mode
same weights, live
Tech choices
The decisions that shaped it, and why they went that way.
- No engine, deliberately
- Phaser or Matter.js would have abstracted away exactly the part that was interesting: the momentum-to-jump-height coupling and the collision handling.
- Neuroevolution rather than reinforcement learning
- No gradients and no RL library. The genetic algorithm is written by hand, which made every design choice something I had to configure.
- Taught one skill at a time
- Training all of it at once went nowhere, so it was split into stages. Roughly the first 500 generations only rewarded jumping. The next 500 added moving left and right, which is what makes a jump go anywhere. Only then did the score start counting floors climbed. Each stage starts from the winners of the last one, so nothing is relearned from scratch.
- Fitness shaped to give early signal
- Height times 0.5 plus floor times 1000. Generation zero contains nobody who can land on a platform, so a floor-only score gives every agent zero and evolution has nothing to select on.
- One seed per generation
- Every agent in a generation plays an identical tower. Otherwise the fittest agent is just the one that got easy platforms, and the run selects for luck.
- Layer-wise crossover
- Children inherit whole layers from each parent instead of a random mix of individual weights. Splicing mid-layer destroys whatever that layer had learned.
Built with
- Game
- Vanilla JavaScript (ES6+)HTML5 CanvasWeb Audio API
- Input
- MediaPipe HandsKeyboardTouch
- AI
- PythonKeras 3 on JAXNumPypygame
- Training UI
- FastAPIServer-Sent EventsGradio
What it produces
How the repo is laid out
Roughly the order you'd read it in: entry point first, then the parts doing the work.
Game
index.htmlCanvas page; loads the game and hand trackinggame.jsPhysics, sprites, combos, coins, ghost replaysAI
icy_tower.pyPython port of the game, headless for trainingneural_net.pyThe 14-16-8-3 network and weight serialisinggenetic_algorithm.pyElites, layer-wise crossover, adaptive mutationtrain_headless.pyThe 2,000-generation curriculum runbest_weights.jsonThe bridge: Python writes it, the browser reads it
Where it falls short
The whole game lives in a single 4,400-line game.js file. Splitting it into modules is the top item on its own roadmap and I have not done it.