OpenFront spatial autoencoder

Frozen spatial observation encoder for OpenFront.io RL agents: compresses tile ownership + terrain + fallout + static structures into a latent grid (any map size). Structure detection is at precision/recall 1.0 per class for every model below.

Current best: ae_v31_d8.pt — v3.1 architecture (terrain-conditioned nearest-upsample decoder, focal loss, border-dense sampling, cosine LR) with the latent at 1/8 resolution instead of 1/16. Trained 40k steps on a mixed corpus of bot self-play and real archived human games.

Border-tile accuracy (the hard metric — overall tile accuracy saturates at ~99% for all models), uniform 256-crop eval:

checkpoint latent human border bot border human tiles bot tiles
ae_v31_d8.pt 64ch @ 1/8 89.3% 96.1% 99.6% 99.8%
ae_v31.pt 64ch @ 1/16 78.5% 90.7% 99.1% 99.6%
ae_v3.pt (bot-only) 64ch @ 1/16 71.8% 87.5% 99.2% 99.4%

The ablation story: mixing human games into training fixes the human-domain gap, the v3.1 decoder/loss fixes add a few points, and latent resolution (not channel count) is what finally cracks border geometry — borders are high-frequency spatial detail that can't be bought back with more channels.

Full details, training code, results, and roadmap: github.com/djmango/openfront-ai

reconstruction

Trained on djmango/openfront-snapshots (bot) and djmango/openfront-human-games (human).

import torch
from ae.model_v3 import SpatialAE

ckpt = torch.load("ae_v31_d8.pt", map_location="cpu", weights_only=False)
a = ckpt["args"]
model = SpatialAE(
    latent_c=a["latent_c"],
    terrain_cond=a.get("terrain_cond", False),
    upsample_decoder=a.get("upsample_decoder", False),
    latent_down=a.get("latent_down", 16),
)
model.load_state_dict(ckpt["model_state_dict"])
model.eval()
z = model.encode(owner_slots, terrain, static_planes)  # (B, 64, H/8, W/8)

ae.pt (v1 tile-only TileAutoencoder in model.py) is kept for history.

Downloads last month

-

Downloads are not tracked for this model. How to track
Video Preview
loading