NIM paper · interactive edition Interactive edition · paper, explanations and Lean proofs.

Appendix GFull proof of Proposition 5.9: rollout counts, outcome identities, selector, winning, step bound

Checked theorem Lean checks a formal statement corresponding to this claim.

1 Paper text

Appendix G. Additional proofs for the rollout analysis

Generated from paper_jmlr.tex, lines 1627–1627.

2 Explain this

The full proof of Proposition 5.9: count the rollout moves of πj\pi_j in two phases (moves of size 2j2^j, then moves of size 11); read off the parity of each phase from the heaps' bits; recover all bits of σ\sigma from the BB outcomes; choose the first heap with hi⊕σ<hih_i\oplus\sigma<h_i (one exists: a heap with the top bit of σ\sigma); show that this selector wins against any legal opponent (after the selected move every opponent move makes the nim-sum nonzero, stones always decrease, and a nonterminal zero-nim-sum position cannot move straight to the end); and bound the total simulated moves by BN(2B−1)BN(2^B-1).

3 How this is formalised

Each step is checked: Rollout.run_length (move count under any tie-breaking), Rollout.run_parity (its parity is the channel bit), Game.reconstruct_eq (reconstruction), Rollout.exists_decreasing_heap (a cancelling heap exists), Game.agent_wins (the selector wins against every legal opponent) and Game.totalRolloutMoves_le (the step bound).

Coverage record
Paper anchorapp:rollout-proofs (manuscript label)
Displayed asAppendix G
ClaimFull proof of Proposition 5.9: rollout counts, outcome identities, selector, winning, step bound
Coverage categoryChecked theorem
Statement sourcepaper_jmlr.tex lines 1627–1627; SHA-256 of the normalised block source 7c7a6d65cdea3582…
Lean declarations
Depends onProposition 5.9
Audited correspondence rowsapp:rollout-proofs
Explanationsexplain/app:rollout-proofs, formal/app:rollout-proofs
Build evidencefresh isolated rebuild, audit report

4 Lean statement and source

Principal declarations

theorem Rollout.run_length

Module NimRollout (lean/NimRollout.lean, lines 88–94) · file SHA-256 1a99428d71573d31… Axioms reported by the fresh build: propext, Quot.sound

EXECUTION COUNT: every complete run of `pi_j`, under any tie-breaking rule, has exactly `sum_h (h / 2^j + h % 2^j)` moves.

Statement as elaborated and printed by Lean 4.26.0

∀ {j : ℕ} {p : List ℕ} {n : ℕ}, NimAudit.Rollout.Run j p n → n = NimAudit.rolloutCount j p
Exact source text, lines 88–94
88/-- EXECUTION COUNT: every complete run of `pi_j`, under any tie-breaking rule,89    has exactly `sum_h (h / 2^j + h % 2^j)` moves. -/90theorem run_length {j : Nat} {p : List Nat} {n : Nat} (hr : Run j p n) :91    n = rolloutCount j p := by92  induction hr with93  | done p hz => exact (rolloutCount_zero hz).symm94  | step p q n hm _ ih => rw [ih, policyMove_count hm]

Project declarations in the statement: Rollout.Run, rolloutCount

Project declarations used by the proof or definition body

Rollout.PolicyMove, Rollout.Run, Rollout.Run.rec, Rollout.policyMove_count, Rollout.rolloutCount_zero, rolloutCount

theorem Rollout.run_parity

Module NimRollout (lean/NimRollout.lean, lines 96–102) · file SHA-256 1a99428d71573d31… Axioms reported by the fresh build: propext, Quot.sound

PARITY OF EXECUTION: the number of moves of any complete run of `pi_j` has low bit equal to the channel value of the nim-sum (bit 0 for j = 0; bit j XOR bit 0 for j ≥ 1).

Statement as elaborated and printed by Lean 4.26.0

∀ {j : ℕ} {p : List ℕ} {n : ℕ},
  NimAudit.Rollout.Run j p n → n.testBit 0 = NimAudit.channel j (NimAudit.nimSum p)
Exact source text, lines 96–102
96/-- PARITY OF EXECUTION: the number of moves of any complete run of `pi_j` has97    low bit equal to the channel value of the nim-sum98    (bit 0 for j = 0; bit j XOR bit 0 for j ≥ 1). -/99theorem run_parity {j : Nat} {p : List Nat} {n : Nat} (hr : Run j p n) :100    n.testBit 0 = channel j (nimSum p) := by101  rw [run_length hr]102  exact rollout_count_channel j p

Project declarations in the statement: Rollout.Run, channel, nimSum

Project declarations used by the proof or definition body

Rollout.Run, Rollout.run_length, channel, nimSum, rolloutCount, rollout_count_channel

theorem Game.reconstruct_eq

Module NimGame (lean/phase2/NimGame.lean, lines 224–240) · file SHA-256 23df6bb87ecfd344… Axioms reported by the fresh build: propext, Quot.sound

RECONSTRUCTION: on a B-bit board (any N, including the empty and terminal boards), the B rollout outcomes determine the full nim-sum.

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} {p : List ℕ},
  NimAudit.Game.Width B p →
    NimAudit.Game.reconstruct B (NimAudit.Game.outcomes p) = NimAudit.nimSum p
Exact source text, lines 224–240
224/-- RECONSTRUCTION: on a B-bit board (any N, including the empty and terminal225    boards), the B rollout outcomes determine the full nim-sum. -/226theorem reconstruct_eq {B : Nat} {p : List Nat} (hw : Width B p) :227    reconstruct B (outcomes p) = nimSum p := by228  apply Nat.eq_of_testBit_eq229  intro i230  rw [reconstruct, testBit_ofBits]231  by_cases hi : i < B232  · simp only [hi, decide_true, Bool.true_and, recoverBit, outcomes, rolloutWinner_channel]233    by_cases h0 : i = 0234    · subst h0; simp [channel]235    · simp only [h0, if_false, channel, if_true]236      cases (nimSum p).testBit i <;> cases (nimSum p).testBit 0 <;> rfl237  · simp only [hi, decide_false, Bool.false_and]238    symm239    apply Nat.testBit_lt_two_pow240    exact Nat.lt_of_lt_of_le (nimSum_lt hw) (Nat.pow_le_pow_right (by decide) (by omega))

Project declarations in the statement: Game.Width, Game.outcomes, Game.reconstruct, nimSum

Project declarations used by the proof or definition body

Game.Width, Game.nimSum_lt, Game.ofBits, Game.outcomes, Game.reconstruct, Game.recoverBit, Game.rolloutWinner, Game.rolloutWinner_channel, Game.testBit_ofBits, channel, nimSum

theorem Rollout.exists_decreasing_heap

Module NimRollout (lean/NimRollout.lean, lines 281–314) · file SHA-256 1a99428d71573d31… Axioms reported by the fresh build: propext, Quot.sound

LEGAL MOVE TO ZERO: if the nim-sum `s` is nonzero, some heap `x` has `x ^^^ s < x`; replacing it by `x ^^^ s` is a legal Nim move and, by `move_to_zero_algebra`, yields nim-sum zero.

Statement as elaborated and printed by Lean 4.26.0

∀ (p : List ℕ),
  NimAudit.nimSum p ≠ 0 →
    ∃ pre x post,
      p = pre ++ x :: post ∧
        x ^^^ NimAudit.nimSum p < x ∧ NimAudit.nimSum (pre ++ (x ^^^ NimAudit.nimSum p) :: post) = 0
Exact source text, lines 281–314
281/-- LEGAL MOVE TO ZERO: if the nim-sum `s` is nonzero, some heap `x` has282    `x ^^^ s < x`; replacing it by `x ^^^ s` is a legal Nim move and, by283    `move_to_zero_algebra`, yields nim-sum zero. -/284theorem exists_decreasing_heap (p : List Nat) (hs : nimSum p ≠ 0) :285    ∃ pre x post, p = pre ++ x :: post ∧ x ^^^ nimSum p < x ∧286      nimSum (pre ++ (x ^^^ nimSum p) :: post) = 0 := by287  let s := nimSum p288  let k := s.log2289  have htop : s.testBit k = true := by290    cases hb : s.testBit k with291    | true => rfl292    | false =>293      exfalso294      have hlt : s < 2 ^ k := by295        apply Nat.lt_pow_two_of_testBit296        intro i hi297        by_cases hik : i = k298        · subst hik; exact hb299        · apply Nat.testBit_lt_two_pow300          exact Nat.lt_of_lt_of_le Nat.lt_log2_self (Nat.pow_le_pow_right (by decide) (by omega))301      exact absurd (Nat.log2_self_le hs) (Nat.not_le.mpr hlt)302  have habove : ∀ i, k < i → s.testBit i = false := by303    intro i hi304    apply Nat.testBit_lt_two_pow305    exact Nat.lt_of_lt_of_le Nat.lt_log2_self (Nat.pow_le_pow_right (by decide) (by omega))306  obtain ⟨x, hxmem, hxk⟩ := exists_heap_testBit htop307  have hlt : x ^^^ s < x := by308    apply lt_of_top_testBit (k := k)309    · rw [Nat.testBit_xor, hxk, htop]; rfl310    · exact hxk311    · intro i hi312      rw [Nat.testBit_xor, habove i hi, Bool.xor_false]313  obtain ⟨pre, post, rfl⟩ := List.append_of_mem hxmem314  exact ⟨pre, x, post, rfl, hlt, move_to_zero_algebra pre post x⟩

Project declarations in the statement: nimSum

Project declarations used by the proof or definition body

Rollout.exists_heap_testBit, Rollout.lt_of_top_testBit, move_to_zero_algebra, nimSum

theorem Game.agent_wins

Module NimGame (lean/phase2/NimGame.lean, lines 376–388) · file SHA-256 23df6bb87ecfd344… Axioms reported by the fresh build: propext, Quot.sound

END-TO-END STRATEGY GUARANTEE (Target 1C): on B-bit boards, the agent that queries the B structured rollouts, reconstructs the nim-sum and plays the selected move wins from every nonzero-nim-sum position against every legal opponent.

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} (p : List ℕ),
  NimAudit.Game.Width B p → NimAudit.nimSum p ≠ 0 → NimAudit.Game.Wins (NimAudit.Game.agent B) p
Exact source text, lines 376–388
376/-- END-TO-END STRATEGY GUARANTEE (Target 1C): on B-bit boards, the agent that377    queries the B structured rollouts, reconstructs the nim-sum and plays the378    selected move wins from every nonzero-nim-sum position against every legal379    opponent. -/380theorem agent_wins {B : Nat} (p : List Nat) (hw : Width B p) (h : nimSum p ≠ 0) :381    Wins (agent B) p := by382  obtain ⟨q, hsel, hm, hq⟩ := selectMove_zero h383  refine Wins.intro p q (by rw [agent_eq hw]; exact hsel) hm ?_384  intro r hr385  have h1 := move_sum_lt hm386  have h2 := move_sum_lt hr387  exact agent_wins r (width_move (width_move hw hm) hr) (move_from_zero hr hq)388termination_by p.sum

Project declarations in the statement: Game.Width, Game.Wins, Game.agent, nimSum

Project declarations used by the proof or definition body

Game.Move, Game.Width, Game.Wins, Game.Wins.intro, Game.agent, Game.agent_eq, Game.move_from_zero, Game.move_sum_lt, Game.selectMove, Game.selectMove_zero, Game.width_move, nimSum

theorem Game.totalRolloutMoves_le

Module NimGame (lean/phase2/NimGame.lean, lines 454–467) · file SHA-256 23df6bb87ecfd344… Axioms reported by the fresh build: propext, Quot.sound

Statement as elaborated and printed by Lean 4.26.0

∀ {B : ℕ} {p : List ℕ},
  NimAudit.Game.Width B p → NimAudit.Game.totalRolloutMoves B p ≤ B * (p.length * (2 ^ B - 1))
Exact source text, lines 454–467
454theorem totalRolloutMoves_le {B : Nat} {p : List Nat} (hw : Width B p) :455    totalRolloutMoves B p ≤ B * (p.length * (2 ^ B - 1)) := by456  unfold totalRolloutMoves457  have key : ∀ k, ((List.range k).map (fun j => rolloutMoves j p)).sum ≤458      k * (p.length * (2 ^ B - 1)) := by459    intro k460    induction k with461    | zero => simp462    | succ k ih =>463      rw [List.range_succ, List.map_append, List.sum_append_nat, Nat.succ_mul]464      simp only [List.map_cons, List.map_nil, List.sum_cons, List.sum_nil, Nat.add_zero]465      have := rolloutMoves_le (j := k) hw466      omega467  exact key B

Project declarations in the statement: Game.Width, Game.totalRolloutMoves

Project declarations used by the proof or definition body

Game.Width, Game.rolloutMoves_le, Game.totalRolloutMoves, Rollout.rolloutMoves

Definitions these statements use

inductive Rollout.Run

Module NimRollout (lean/NimRollout.lean, lines 34–38) · file SHA-256 1a99428d71573d31…

`Run j p n`: following `pi_j` from board `p` reaches the all-zero board after exactly `n` policy moves.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ → Prop
Exact source text, lines 34–38
34/-- `Run j p n`: following `pi_j` from board `p` reaches the all-zero board after35    exactly `n` policy moves. -/36inductive Run (j : Nat) : List Nat → Nat → Prop37  | done (p : List Nat) : (∀ x ∈ p, x = 0) → Run j p 038  | step (p q : List Nat) (n : Nat) : PolicyMove j p q → Run j q n → Run j p (n + 1)

Project declarations in the statement: none

Project declarations used by the proof or definition body

Rollout.PolicyMove

def rolloutCount

Module NimCore (lean/NimCore.lean, lines 82–82) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ
Exact source text, lines 82–82
82def rolloutCount (j : Nat) (rows : List Nat) : Nat := (rows.map (heapCount j)).sum

Project declarations in the statement: none

Project declarations used by the proof or definition body

heapCount

def channel

Module NimCore (lean/NimCore.lean, lines 77–78) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

ℕ → ℕ → Bool
Exact source text, lines 77–78
77def channel (j sigma : Nat) : Bool :=78  if j = 0 then sigma.testBit 0 else xor (sigma.testBit j) (sigma.testBit 0)

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

def nimSum

Module NimCore (lean/NimCore.lean, lines 7–9) · file SHA-256 b07db6c505210d5a…

Statement as elaborated and printed by Lean 4.26.0

List ℕ → ℕ
Exact source text, lines 7–9
7def nimSum : List Nat → Nat8  | [] => 09  | h :: t => h ^^^ nimSum t

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

def Game.Width

Module NimGame (lean/phase2/NimGame.lean, lines 31–32) · file SHA-256 23df6bb87ecfd344…

B-bit board: every heap is below 2^B.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → Prop
Exact source text, lines 31–32
31/-- B-bit board: every heap is below 2^B. -/32def Width (B : Nat) (p : List Nat) : Prop := ∀ x ∈ p, x < 2 ^ B

Project declarations in the statement: none

Project declarations used by the proof or definition body

none

def Game.outcomes

Module NimGame (lean/phase2/NimGame.lean, lines 214–215) · file SHA-256 23df6bb87ecfd344…

The B actual rollout measurements of board p.

Statement as elaborated and printed by Lean 4.26.0

List ℕ → ℕ → Bool
Exact source text, lines 214–215
214/-- The B actual rollout measurements of board p. -/215def outcomes (p : List Nat) : Nat → Bool := fun j => rolloutWinner j p

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.rolloutWinner

def Game.reconstruct

Module NimGame (lean/phase2/NimGame.lean, lines 211–212) · file SHA-256 23df6bb87ecfd344…

The nim-sum reconstructed from the B rollout outcomes o 0, ..., o (B-1).

Statement as elaborated and printed by Lean 4.26.0

ℕ → (ℕ → Bool) → ℕ
Exact source text, lines 211–212
211/-- The nim-sum reconstructed from the B rollout outcomes o 0, ..., o (B-1). -/212def reconstruct (B : Nat) (o : Nat → Bool) : Nat := ofBits B (recoverBit o)

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.ofBits, Game.recoverBit

inductive Game.Wins

Module NimGame (lean/phase2/NimGame.lean, lines 125–132) · file SHA-256 23df6bb87ecfd344…

`Wins sel p`: the player to move at `p`, always playing the successor chosen by `sel`, wins against EVERY legal opponent. The selected move is legal, and after every legal opponent reply the player is again in a `Wins` position. Because the predicate is inductive, every such play is finite and ends with the opponent facing a terminal board (normal play: the opponent loses).

Statement as elaborated and printed by Lean 4.26.0

(List ℕ → Option (List ℕ)) → List ℕ → Prop
Exact source text, lines 125–132
125/-- `Wins sel p`: the player to move at `p`, always playing the successor chosen by126    `sel`, wins against EVERY legal opponent.  The selected move is legal, and after127    every legal opponent reply the player is again in a `Wins` position.  Because128    the predicate is inductive, every such play is finite and ends with the129    opponent facing a terminal board (normal play: the opponent loses). -/130inductive Wins (sel : List Nat → Option (List Nat)) : List Nat → Prop131  | intro (p q : List Nat) : sel p = some q → Move p q →132      (∀ r, Move q r → Wins sel r) → Wins sel p

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.Move

def Game.agent

Module NimGame (lean/phase2/NimGame.lean, lines 257–259) · file SHA-256 23df6bb87ecfd344…

The agent: reconstruct the nim-sum from the B root rollouts, then select.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → Option (List ℕ)
Exact source text, lines 257–259
257/-- The agent: reconstruct the nim-sum from the B root rollouts, then select. -/258def agent (B : Nat) (p : List Nat) : Option (List Nat) :=259  selectMove (reconstruct B (outcomes p)) p

Project declarations in the statement: none

Project declarations used by the proof or definition body

Game.outcomes, Game.reconstruct, Game.selectMove

def Game.totalRolloutMoves

Module NimGame (lean/phase2/NimGame.lean, lines 450–452) · file SHA-256 23df6bb87ecfd344…

Total simulated moves of the B root rollouts.

Statement as elaborated and printed by Lean 4.26.0

ℕ → List ℕ → ℕ
Exact source text, lines 450–452
450/-- Total simulated moves of the B root rollouts. -/451def totalRolloutMoves (B : Nat) (p : List Nat) : Nat :=452  ((List.range B).map (fun j => rolloutMoves j p)).sum

Project declarations in the statement: none

Project declarations used by the proof or definition body

Rollout.rolloutMoves

Full modules

ModuleFileLinesImports
NimCorelean/NimCore.lean139Std
NimGamelean/phase2/NimGame.lean634NimRollout
NimRolloutlean/NimRollout.lean349NimCore

Build evidence: fresh isolated rebuild of all 38 bundled modules with Lean (version 4.26.0, started 2026-10-10T15:21:56Z, finished 2026-10-10T15:27:00Z: 38/38 modules compiled, 1588 axiom reports, no errors. See Build for the commands and the independent audit of 2026-10-10 (dated evidence).