mathieu.dev
All projects
Personal Project — Genetic Algorithms & Neural Networks

Snake Genetics

A neural network learns to play Snake with no training data and no backpropagation — just generations of selection, crossover, and mutation.

C++ built in 2023 · rewritten in Rust in 2025

Overview

No labels, no gradients — just survival.

Snake Genetics evolves a population of snakes, each controlled by a small feed-forward neural network, to play the classic game on its own. Every snake is born with a brain full of random weights, and nothing decides which weights survive except how well that snake actually plays.

Each generation, hundreds of snakes play a full game at once. The strongest performers are picked through fitness-weighted roulette selection, paired up, and crossed over to produce the next generation's brains, with a small chance of random mutation along the way. After a few hundred generations, snakes that once wandered straight into walls are reliably chasing food instead.

768
snakes competing every generation
5%
of each generation survives to breed
1.5%
chance per weight to randomly mutate
0
hidden layers by default — eyes wired straight to muscles
01 — Seeing the board

Three ways to look at a snake game

A vision module turns raw game state into the network's input vector. The simplest encoding just checks for danger directly left, ahead and right of the head and the angle toward the food; wider encodings look further down each direction and add distance to the walls and to the snake's own body, all normalized so the network never has to guess how big the board is.

02 — Thinking

A hand-rolled feed-forward network

Both versions implement matrices and a feed-forward network from scratch — no ML library involved. Every layer multiplies by a weight matrix, adds a bias, and squashes the result through a sigmoid. By default there are zero hidden layers, so vision feeds straight into the three outputs and the whole "brain" is a single matrix multiplication.

Hidden layers are fully configurable — the network takes an array of layer sizes — but the plain, layer-less version turned out to already be enough to learn to play well.

03 — Selection & crossover

Breeding the next generation

Once every snake has finished its game, the top slice of the population — 5% by default — is kept as potential parents. Two parents are drawn per child through fitness-weighted roulette selection, and the child's weight matrices are assembled by taking each individual weight from one parent or the other at random: uniform crossover applied directly to the brain.

04 — Mutation & fighting the spin

Random resets, and a fitness function that hates loops

Every weight has a small independent chance of being thrown out and replaced with a fresh random value, which is what keeps the population from settling too early. The fitness function needed its own trick too: early populations discovered that spinning in tight loops was an easy way to survive without ever finding food, so any snake whose moves are overwhelmingly one-directional gets its fitness docked for it.

Try it yourself

Three ways to look at the board

Same snake, same moment — switch encodings to see exactly what it can and can't perceive. All three agree on one thing: turning right right now would be fatal.

Checks the single cell to its left, ahead, and to its right: wall or body, or free? Fast, but blind beyond the very next step.

Input vector
  • Leftfree1
  • Frontfree1
  • Rightown body0
  • Food angle-0.51

food is slightly left of forward

The story

Same snake, two languages, two years apart

2023

C++ — learning genetic algorithms

The genetic algorithm came first. The goal in 2023 was to actually understand evolution-based learning — fitness, selection, crossover, mutation — built entirely by hand, with no neural network library and no shortcuts. It renders through SFML, splits a population of hundreds of snakes across every CPU core with std::thread for each generation, and plots the average and best score per generation with matplotlib-cpp.

2025

Rust — learning a language

Two years later, wanting to actually learn Rust rather than just read about it — and well aware of how much potential the language has — I rebuilt the exact same project from scratch instead of picking something new. A familiar problem meant the borrow checker, not the algorithm, was the thing I had to fight. It swaps SFML for macroquad, trades the manual threading for a plain sequential loop, and lets you replay the best snake's run afterward, speeding up or slowing down playback with the arrow keys.

Built with
  • C++17
  • Rust
  • SFML
  • macroquad
  • Genetic Algorithm
  • Neural Network