Pong AI

Pong AI trains a Pong player in your browser. Its controller is a multilayer perceptron (MLP), a small neural network that evolves over repeated games.

The debug overlay above each paddle shows the 21 neurons in its network.

Press N to hide the debug overlay.
Press P to cycle between three control modes: AI vs AI, AI vs human, and human vs human.
Control the left paddle with the W and S keys.
Control the right paddle with the ↑ and ↓ arrow keys.

See if you can beat your trained AI!

The code is available on GitHub.

Technical details
  • The game is drawn with Macroquad through the browser's WebGL interface.
  • The AI is written in Rust and compiled to WebAssembly.
  • The network has 8 inputs, hidden layers of 16 and 4 neurons, and 1 output (8-16-4-1).
  • Each genetic-algorithm generation contains 128 players. Browser training runs for 100 generations, with every player competing in a full round-robin tournament.
  • New players come from random Gaussian mutation, with no crossover.
  • Hidden layer activations are rectified linear units (ReLU).
  • The output layer activation is a hyperbolic tangent (tanh).
  • Each player's fitness is its zero-sum score across the tournament.
  • Pre-trained champion
    • The initial population includes a locally trained champion to reduce the work on your processor. The generations shown on screen, including the final result, are still trained in your browser through WebAssembly with a small JavaScript loader.
    • The champion came from several thousand generations of local multithreaded processor training.
Acknowledgements

The original version was a C++ genetic algorithm with a neural network that learned Pong, and it was largely the work of a former university peer. He brought a competitive programmer's rigour, and at the time I couldn't fully follow it.

Rebuilding it in Rust has been my way of finally understanding it, and of learning more about compilation and systems design along the way. It relies on ordinary compiler optimisation, including automatic vectorisation where the compiler finds suitable loops.

This rebuild is an early step in my roadmap to catch up with the field, working from classical neural networks towards transformers. I'll share further work on over|yonder.