djmango's picture
Upload model.py with huggingface_hub
7e3d211 verified
Raw
History Blame Contribute Delete
2.55 kB
"""Fully-convolutional autoencoder over OpenFront tile states.
Input encoding per tile:
- owner slot -> learned embedding (static slot per player for a whole game,
slot 0 = unowned, slots 1..MAX_SLOTS-1 assigned by smallID order at spawn)
- terrain scalars: land flag, normalized magnitude, fallout flag
The encoder downsamples by 16x into a spatial latent grid (LATENT_C channels
per 16x16 tile region), so any map size divisible by 16 works. The decoder
reconstructs per-tile owner-slot logits.
"""
import torch
import torch.nn as nn
MAX_SLOTS = 128 # owner classes: 0 = unowned, 1..127 player slots
OWNER_EMB_DIM = 8
TERRAIN_CHANNELS = 3 # land, magnitude, fallout
def conv_block(c_in: int, c_out: int, stride: int) -> nn.Sequential:
return nn.Sequential(
nn.Conv2d(c_in, c_out, kernel_size=3, stride=stride, padding=1),
nn.GroupNorm(8, c_out),
nn.SiLU(),
)
def deconv_block(c_in: int, c_out: int) -> nn.Sequential:
return nn.Sequential(
nn.ConvTranspose2d(c_in, c_out, kernel_size=4, stride=2, padding=1),
nn.GroupNorm(8, c_out),
nn.SiLU(),
)
class TileAutoencoder(nn.Module):
def __init__(self, latent_c: int = 64):
super().__init__()
self.owner_emb = nn.Embedding(MAX_SLOTS, OWNER_EMB_DIM)
c_in = OWNER_EMB_DIM + TERRAIN_CHANNELS
self.encoder = nn.Sequential(
conv_block(c_in, 32, stride=1),
conv_block(32, 64, stride=2), # /2
conv_block(64, 96, stride=2), # /4
conv_block(96, 128, stride=2), # /8
conv_block(128, 128, stride=2), # /16
nn.Conv2d(128, latent_c, kernel_size=1),
)
self.decoder = nn.Sequential(
conv_block(latent_c, 128, stride=1),
deconv_block(128, 128), # /8
deconv_block(128, 96), # /4
deconv_block(96, 64), # /2
deconv_block(64, 32), # /1
nn.Conv2d(32, MAX_SLOTS, kernel_size=1),
)
def encode(self, owners: torch.Tensor, terrain: torch.Tensor) -> torch.Tensor:
"""owners: (B, H, W) int64 slots; terrain: (B, 3, H, W) float."""
emb = self.owner_emb(owners).permute(0, 3, 1, 2) # (B, E, H, W)
x = torch.cat([emb, terrain], dim=1)
return self.encoder(x)
def forward(
self, owners: torch.Tensor, terrain: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor]:
z = self.encode(owners, terrain)
logits = self.decoder(z) # (B, MAX_SLOTS, H, W)
return logits, z