hltv-tick-encoder β self-supervised encoder over raw CS2/CS:GO tick state
Encoder trained on raw per-tick demo state only (no aggregate features), for predicting short-horizon game events from a 10-player joint state sequence.
Input
x : float32 [B, W, 10, 9], 8 Hz (players_loc is sampled 1 row / 8 frames β this is the resolution
ceiling of the source data, not 64 tick).
channels per player per tick:
0 x/32768, 1 y/32768, 2 z/32768, 3 sin(yaw), 4 cos(yaw), 5 sin(pitch), 6 cos(pitch),
7 alive, 8 side (0 = CT, 1 = T).
Model (t1_model.py, 0.67M params, d=192)
9ch per-player MLP -> 2x full spatial attention across the 10 players -> 3x causal dilated Conv1D over
time -> z of width 192 per (tick, player).
Self-supervised objectives
next view direction (unit vector), next displacement, next alive flag, masked-other-player state.
Trained on 364 maps / ~55k steps of raw tick data (two checkpoints released: ck14k.pt, ck30k.pt;
they are numerically equivalent β the loss is flat after ~10k).
What it is good for (Stage 2, honest numbers)
Probe = small MLP on [z at last observed tick] (or [z ; hand features]), predicting whether an event
happens in the next 1 s, evaluated on 71 matches never seen in encoder pretraining or probe fitting
(29,284 train / 11,121 test rows, 3 seeds, 400-rep cluster bootstrap over windows, paired bootstrap for
deltas). AUC:
| target | base rate | hand feats | random-weight encoder + hand | encoder + hand |
|---|---|---|---|---|
| enemy sees me | 1.9% | .613 | .753 | .761 |
| I kill or die | 1.9% | .619 | .761 | .783 |
| I take damage | 2.9% | .663 | .728 | .762 |
| I fire | 8.8% | .607 | .653 | .668 |
Two things to read carefully:
- Most of the gain over hand features comes from jointly encoding all 10 players' positions and views, not from self-supervision: the same architecture with random weights already gets +12-14 AUC. SSL on top of that is +0.85 / +2.07 / +3.33 / +1.55 AUC, and the paired 95% CI excludes zero only for damaged and fired.
- A flatten-the-raw-window baseline (16/32/48 ticks + hand feats) reaches only .61-.69, so the gain is not simply "more raw signal".
What it is NOT good for
Per-player embeddings averaged over a map do not improve the shipped pre-match kill-pair model
(88-feature XGBoost, 61.1% pairwise). Adding a causal per-player profile of the 64-d embedding changed
non-tie accuracy by -0.50pp [-1.02, +0.01] on the 17,556 pairs where tick data exists. See STAGE3.md.
The encoder's information is within-round state, not stable cross-match player skill.
Files
ck14k.pt, ck30k.pt (torch checkpoints, dict with model state_dict and args),
t1_model.py (architecture), t2_probe2.py (probe protocol), results/*.json (probe outputs),
STAGE2.md, STAGE3.md, TICKS_STAGE0.md (extraction + validation), fig_stage2.png, fig_stage3.png.