Ровный робот 853 points / 58 solvers misc сыграй в нарды против Искусственного Интеллекта©®℗! тебе нужно выиграть его 15 раз подряд. чтобы всё было честно, игра проходит на блокчейне, чтобы точно никто не смог никого обмануть. тут все ребята ровные, ничего не сломается
заходит как то клод кодекс и дипсик на борду а там таска на нарды...
flygammon-handout.tgz 184 KiB ctfcup{...} submit destroy instance extended https://zkls7g1p1hosw2f0.instances.ctfcup.online expires: 31.10.2026 21:42:58(1796 seconds)
Instance is live. Key facts: N=15, tokens=80, fresh game, G=2, H=g^s mod p. The title "Ровный" (smooth/even) hints at a smooth prime — let me factor p-1.
p−1 is fully smooth (2 × 13 primes of ~20 bits) — Pohlig–Hellman recovers the oracle's secret s trivially. Then we can compute every dice roll (present and future) ourselves. Let me recover s:
s recovered and verified. Now I can compute any future dice roll. Next: replicate the oracle bot exactly (model weights + engine) to simulate games before committing. Let me parse model.fly:
The model file layout is unclear — rather than guess float formats, I'll compile the handout's own Rust crate to get a bit-exact oracle policy. Let me check the Cargo setup:
No ASCII keys — need burn 0.21's exact CompactRecorder format. Let me fetch its source:
The stats (31% zero / 31% denormal / 38% normal, uniformly) smell like a periodic byte pattern. Let me check for structural periodicity:
Let me test decodings systematically — I'll port the feature encoder + forward pass and score candidate decodings against sanity benchmarks (opening ≈ 0.5 win-prob, near-win ≈ 1.0):
The denormals are likely decay-killed weights of rarely-active input features (unary slots that are almost always 0) — plain f32 may be right after all. Let me sweep layouts using a strong signal: outputs must vary across random-play positions and correlate with race progress:
tr=True (weights stored [in,out]) stands out. Let me refine — per-layer transpose combos + a cleaner benchmark (mover with k checkers off vs untouched opponent):
I need certainty on the format — installing Rust to test-load model.fly with burn 0.21's actual recorders:
All burn formats ruled out — it's a custom dump. Let me fix the sweep crash and complete the grid, plus check u16 pair relations:
Let me re-read train.rs carefully — it may reveal how weights were produced/dumped:
The zero-density spike in the l2 region suggests a real trained net with dead weights. Let me build a mirror-symmetry test (a trained evaluator must satisfy r0(board) + r0(flip(board)) ≈ 1) across all layouts:
Time is limited (instance expires soon). New approach: use the blockchain itself as ground truth — the bot's observed moves will identify the correct model decode. Let me build the full player framework:
Now the bot-replica (exact port of choose_2ply) and the observation-based decode tester:
Dice predictions confirmed live — turn 14 predicted (5,4) = actual (5,4). The sacrifice game is mid-flight; let me resume it to collect more observations, then run decode tests:
No matches — but I spotted a bug in my recorder: I recorded the bot's input board as flip(pre-my-move) instead of the board during the bot's turn. Let me fix and also sanity-check that observed ends are reachable per my engine:
My recorder captured the wrong board (pre-my-move instead of during-bot-turn). The on-chain boardPacked() read while it's the bot's turn is already the bot's perspective. Fixing the collector:
RPC is heavily rate-limited (~2.5s/read → a full state() costs 23s). I need batched JSON-RPC — one HTTP round trip per state read:
Batched reads: 0.82s. Now collect observations properly:
Rather than burn tokens, let me extract structural evidence: in a row-major weight matrix, dead hidden units create contiguous runs of near-zero weights whose length reveals the orientation (runs≈203 → [out,in], runs≈80 → [in,out]):
What if it's a byte-plane dump (all 1st bytes, then all 2nd bytes, ...)? Testing plane permutations:
Let me collect clean observations efficiently (deriving the bot's input board offline from my own move):
Engine verified (6/6 reachable). Testing decodes against clean observations:
Two decodes replicate the bot 6/6: W1=[in,out], W3=[in,out], W2 either orientation (square). Let me quantify whether the W2 ambiguity even matters, and check chain state:
Let me check chain state and build the winner engine — policy: my 1-ply greedy with known future dice vs exact bot replica, full-game simulation before committing, skip otherwise:
Simulation works — 3/4 wins with greedy play. With skip-filtering (only commit winning sims), nearly every committed game should win. Let me check win margins, then write the live winner:
Found a critical bug in my simulator: after each move I forgot to flip() the board for the next player's perspective. Fixing and re-measuring:
Realistic now: 6/8 wins, games ~24-34 plies. Time to write the live winner loop:
[stopped: the account is out of credit]
