NeuroDino

A small neural network that plays Chrome's T-Rex game. Watch it play live, with its network drawn next to it: NeuroDino Space. The code that trained it: github.com/ahm3texe/NeuroDino. The paper describes all three models and how they were tested: paper/neurodino-en.pdf (Turkish original: paper/neurodino.pdf).

This is v3. The earlier models are in v2/ and v1/.

Model

A multilayer perceptron written with NumPy, no ML framework:

  • 12 inputs β†’ 56 hidden neurons (tanh) β†’ 3 outputs (softmax): jump, duck, run
  • 899 parameters: weights 12Γ—56 + 56Γ—3, biases 56 + 3
  • hidden neurons 48–55 feed only duck; their weights to jump and run are 0
  • every frame the obstacles move, the game computes the inputs and the dino takes the likeliest action

Inputs

Missing obstacles give 0. Distances are in game pixels, the speed in pixels per frame at 60 fps, and "the next obstacle" is the nearest one ahead of the dino.

# Name Value
0 Obj1 Dist 1 βˆ’ min(1, frames to reach the next obstacle / 50): 1 is about to hit
1 Obj1 Act 1 if the next obstacle is the highest bird (y < 75), which flies over a running dino
2 Obj1 W min(1, its width / 75)
3–5 Obj2 Dist, Act, W The same for the obstacle after it
6 Speed Game speed / 13, the top speed
7 Gap 1 βˆ’ min(1, frames between the two obstacles / 15)
8 DinoY min(1, height above the ground / 30) during a jump
9 DinoVel Vertical velocity / 10 within [βˆ’1, 1] during a jump, negative while rising
10 Air 1 while jumping
11 Duck 1 while ducking

The reference implementation is neurodino/sensors.py.

Usage

import numpy as np
from huggingface_hub import hf_hub_download
from safetensors.numpy import load_file

w = load_file(hf_hub_download("ahm3texe/neurodino", "model.safetensors"))

def act(inputs):
    """The 12 inputs above in, 'jump', 'duck' or 'run' out."""
    hidden = np.tanh(w["weights_ih"] @ inputs + w["bias_h"])
    logits = w["weights_ho"] @ hidden + w["bias_o"]
    return ["jump", "duck", "run"][int(np.argmax(logits))]  # = argmax of the softmax

Compute the inputs after the obstacles have moved for the frame, as the website and watch_model.py do. Duck in the air is a fast drop. To watch it play, run watch_model.py from the GitHub repository.

Training

  • v1: a genetic algorithm with 50 networks per generation (the best two carry over unchanged, the rest are bred by tournament selection, uniform crossover and Gaussian mutation), 83 generations from random weights. Each generation played a single game. Its runner let the networks decide before the obstacles moved each frame, so v1 acts on what it saw a frame earlier.
  • v2: the same algorithm for 40 more generations starting from v1, in the website's loop, with every network playing the same 10 games each generation, and picked on validation games. 12-48-3; it never ducks.
  • v3: v2 crashed about once in a hundred hours at top speed, nearly always still in the air when the next obstacle needed a jump; a fast drop would have saved it. v3 adds 8 hidden neurons that drive only duck. Frames in the air were labelled by playing on from them twice, with a duck and without, and the new neurons and duck's output row learned from those labels by gradient descent, with duck's bias then lowered by 1. Everything else is v2's, so every jump is exactly as v2 makes it.

The logs are training_log.csv (v1), training_log_v2.csv and validation_v2.csv; v3's pipeline is in the repository's training/.

Results

On the website's engine, four-hour games:

Games Ended before top speed Lasted 4 hours Crashes at top speed
v1 200 (test) 113 85 2
v2 200 (test) 0 190 10
v3 200 (test) 0 198 2
v2 1,000 new 0 960 40 (1 every ~100 h)
v3 1,000 new 0 997 3 (1 every ~1,300 h)

Neither v2 nor v3 had played the test games (seeds 1–200) or the new ones before. Every game is in the repository's evaluation/.

Limitations

  • It was taught to duck only to drop fast in the air, not under a bird: Obj Act marks only the highest bird, which needs no action, so a bird it would have to duck under looks the same as a low one it can jump.
  • Its timing is tuned to about a frame; played in another game loop, the results change completely (see v1 in the repository's evaluation).
  • It sees at most two obstacles and tells distances apart only up to 50 frames ahead; Gap never rises above 0 with Chrome's normal obstacle gaps.

Files

File Contents
model.safetensors weights_ih (56Γ—12), bias_h (56), weights_ho (3Γ—56), bias_o (3), float64
best-brain.json The same weights in the format the website loads
config.json Sizes, activations, action and input names, input constants
training_log.csv v1's training: best, average and record score of every generation
training_log_v2.csv, validation_v2.csv v2's training and validation
v2/, v1/ The earlier models' weights, in the same two formats

License

MIT. The game it learned on is Chromium's T-Rex runner (BSD-3-Clause), ported to Python; not affiliated with Google.

Downloads last month
44
Safetensors
Model size
899 params
Tensor type
F64
Β·
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Space using ahm3texe/neurodino 1