🛸 LLM Dogfight
A terminal UFO dogfight between two local LLMs. Team ZORB and team KRELL are each commanded by a different small open model running on your own machine. About once a second every commander reads a text description of the field and answers with one order per saucer: attack, hunt, flee, or abduct a cow. Losses make them shout, and every loss becomes a lesson in their prompt.
GitHub repository Latest release
Install and play
# the tool: Rust toolchain from https://rustup.rs, or a binary from Releases
cargo install --git https://github.com/AlveeeRahman/llm-dogfight
# a model runtime, one of:
pip install torch transformers # NVIDIA GPU, 8 GB or more (a CUDA build of torch)
pip install mlx-lm # Apple silicon Mac
dogfight # first run downloads the two default models (2.2 GB)
dogfight models # the catalogue, and the strongest pair your machine can hold
dogfight evolve # a genetic algorithm also evolves each team's tactics
dogfight ufo # no GPU? the built-in pilots, no models at all
The binary is 0.8 MB of Rust with one dependency. The models live in a Python sidecar and run on CUDA, Apple MLX (dogfight mlx) or the CPU. Everything a model says is filtered before it reaches your terminal, any key gives the terminal back exactly as it was, and dogfight remove deletes everything the tool put on the machine, itself included.
How a battle works
| Rule | What happens |
|---|---|
| Fleet | 4 saucers per team on screen; a destroyed one is replaced 4 s later from 20 reinforcements per game, then it is a fight to the death. |
| Game over | A team with nothing in the air and nothing left to send loses. The next game starts 10 s later and the loser fields one extra saucer. |
| Score | One point per enemy saucer destroyed and one per cow abducted; games won are remembered per model pair. |
| Orders | attack E4, hunt, flee, abduct C1. No idle order; anything a model invents becomes hunt. |
| Cries and lessons | Losing a saucer earns the team a battle cry in the HUD and a post-mortem the commander turns into one rule for the future. Lessons persist per model. |
| Doctrine | Six bounded parameters (when to flee, when to be brave, when a cow is worth it, target focus, courage) keep a small model from drifting. dogfight evolve breeds them with a genetic algorithm, both teams in parallel, with zero extra model calls. |
The doctrine bounds follow the guardrail idea of AutoSafe: the model chooses freely inside the bounds, and the bounds themselves are what evolves. The README has the full rules, the doctrine table and the algorithm.
Models
A catalogue of 23 ungated Hugging Face models from 0.6B to 14B parameters, or any model id you name with --zorb and --krell. The default pair, Qwen3-0.6B against SmolLM2-360M, is a 2.2 GB download and fits any CUDA card; dogfight models measures your GPU or unified memory and names the strongest pair it can hold. Matchups played through many full games:
| Pair | Memory | Character |
|---|---|---|
qwen3-0.6b vs smollm2-1.7b | 4.6 GB | Qwen's aggression against SmolLM2's caution |
qwen3-0.6b vs smollm2-360m | 2.2 GB | the default: decisions in a tenth of a second, fast and messy |
granite3.3-2b vs qwen3-1.7b | 9.2 GB | the smartest small pair; a card above 8 GB, or 8-bit on 8 GB |
qwen3-0.6b vs gemma3-1b | 3.5 GB | the most even of the small pairs |
Refusal-free ("abliterated") community builds have more to say in the HUD; the README explains how to run them and when a Hugging Face token helps.
What touches the network
The Rust binary opens no connection at all. The Python sidecar downloads the two chosen models from huggingface.co once, into ~/.cache/huggingface/hub, and then runs offline. No API key is needed; a Hugging Face token is optional and only raises the download rate limit.
| Component | Reaches | Why |
|---|---|---|
agents/arena.py | huggingface.co, cdn-lfs.huggingface.co | Fetch model weights on first run. |
Nothing sends your terminal contents, prompts or scores anywhere. Model output is reduced to plain ASCII before it is drawn, so a model cannot emit escape sequences into your terminal.
Why terminal text, and why such small models?
The renderer is plain truecolor text (half-blocks and braille), so it runs in the default Ubuntu terminal, GNOME Terminal, Ptyxis, kitty, iTerm2 and Ghostty with no graphics protocol, and it stays under 45 KB per frame. The models are small on purpose: two of them have to share an 8 GB card and answer about once a second, and the game is more interesting when its commanders are fallible and learn than when they are perfect.
Project resources
- Source repository
- Releases: Linux x86_64 and macOS arm64 binaries with checksums
- Security policy and threat model
- Architecture notes
By the author of Skill Vision · Agent Oracle · Research Hound.