Skip to content

Self-Play & Elo Ratings

Absolute scores depend on which opponent an agent faced. To rate policies on a common ladder, NetForge ships a population tournament that plays every red policy against every blue policy and rates all of them with Elo — a population-based evaluation in the spirit of AlphaStar's league.

It lives in benchmarks/self_play.py.

The shared axis

Red and blue are rated on one ladder because they compete over a single quantity: SLA uptime. Blue wants it high; red wants it low. For each match, Blue's Elo score is the mean SLA uptime and Red's is 1 - SLA. Fractional Elo scores are standard, so a red that suppresses SLA and a blue that protects it move on the same rating scale.

Running a tournament

from benchmarks.self_play import population_tournament

result = population_tournament(
    scenarios=['ransomware', 'apt_espionage', 'cloud_hybrid'],
    seeds=list(range(5)),
    max_ticks=150,
)
for rank, entry in enumerate(result['ladder'], 1):
    print(rank, entry['policy'], entry['rating'])

or from the CLI:

python -m benchmarks.self_play --seeds 5 --max-ticks 150
# -> benchmarks/results/self_play_elo.json

The default pools are the built-in policies (random, heuristic, killchain-red); pass your own red_pool / blue_pool dicts of name -> factory to add trained agents. Because every policy is rated against a whole population rather than a single opponent, the ladder is a robust relative ranking — the natural place to drop a newly trained agent and see where it lands.

Example ladder

 1. heuristic-blue    1041.7
 2. random-blue       1022.1
 3. killchain-red      980.4
 4. random-red         978.7
 5. heuristic-red      977.1