A soccer value function you can touch.

pitchperfect ports a trained neural-network value function for football — V(s), the estimate of which team scores next — into a tiny numpy package and an interactive browser demo. Drag 22 players around a real pitch and watch the model reason about danger in real time.

Open the interactive demo → View on GitHub

How it works

One network, three runtimes. The model is trained in PyTorch in the value repo. We export its weights once; pitchperfect reimplements the forward pass in numpy (the pip package) and in JavaScript (the demo). A parity test proves all three agree to ~1e-6.
MettleNet — a small demo network. A Deep-Sets value net: a per-player encoder summed over each team (permutation invariant), feeding a value head. It generalizes naturally beyond soccer to any spatial team sport.
Enforced symmetry. The raw value is antisymmetrized over the x-flip + team-swap symmetry: V(s) = (raw(s) − raw(swap s))/2. This removes any residual red/blue bias and guarantees a mirror-balanced position scores exactly 0 — the principled fix for an asymmetric trained model.
Probe what it knows. The demo computes interpretable formation metrics live — stretch (spread; low = compact), width, depth, block area, line height — so you can connect a shape change to a value change, and pick any player to sweep their marginal value surface across the pitch.

Install

pip install pitchperfect

from pitchperfect import ValueNet
net = ValueNet.load()
v = net.value(
    ball=[0, 0],
    blue=[[-46,0],[-30,-18], ...],   # 11 [x,y] in sim coords (x∈[-50,50], y∈[-30,30])
    red=[[46,0],[30,18], ...],
)
print(v)   # antisymmetrized value in [-1, +1]; +1 favors blue

Status & limits

A battery of diagnostics (tools/study.py) checks the value surface against common sense, and the model passes the clear-cut tests: an open goal reads ±0.66 to the attacking side, a balanced shape reads exactly 0, numerical overloads favour the overloading team, and every mirror position is the precise negative of its twin.

Known limits, since this is a research preview:

Follow along on GitHub.

marginal value surface for a blue attacker

Above: the marginal value as a blue attacker (with the ball) is moved over the pitch, others fixed — bluer where the model rates that position higher for blue.

Why now

The World Cup is on — so here's something to play with at half-time. Recreate a famous shape, drag a striker into the channel, and see what a value function thinks.