Sprague–Grundy theorem
TheoremUniqueness of the P-positions of a terminating impartial game#✓fully proved
Write \(x \to y\) when there is a move from position \(x\) to position \(y\), and assume there is no infinite sequence of moves \(x_0 \to x_1 \to x_2 \to \cdots\). Call a set \(P\) of positions stable if no move leads from a position in \(P\) to a position in \(P\), and absorbing if from every position outside \(P\) some move leads to a position in \(P\). Then any two stable and absorbing sets of positions are equal.
In particular the P-positions are determined by stability and absorption, and the N-positions are their complement.
theorem pPositions_unique
{Position : Type*}
(move : Position → Position → Prop)
(hmove : WellFounded (fun next current => move current next))
(P Q : Set Position)
(hP_stable : ∀ x y : Position, x ∈ P → move x y → y ∉ P)
(hP_absorbing : ∀ x : Position, x ∉ P → ∃ y : Position, move x y ∧ y ∈ P)
(hQ_stable : ∀ x y : Position, x ∈ Q → move x y → y ∉ Q)
(hQ_absorbing : ∀ x : Position, x ∉ Q → ∃ y : Position, move x y ∧ y ∈ Q) :
P = Q
Sources
- Antoine Renard and Michel Rigo, "Variants of Wythoff Game with Terminal Positions or Blocking Maneuvers," Journal of Integer Sequences 29 (2026), Article 26.1.6, Proposition (Folklore), lines 438–448, https://cs.uwaterloo.ca/journals/JIS/VOL29/Rigo/rigo5.tex; stability and absorption are defined in lines 430–436. Termination is encoded by the well-foundedness of the move relation on the acyclic game graph.
Used by 1
StructureImpartial game#✓fully proved
An impartial game consists of a type of positions, a starting position, for each position \(p\) a finite set of positions reachable from \(p\) in one move, and a rank function assigning a natural number to each position such that every move strictly decreases the rank: if \(q\) is reachable from \(p\) in one move, then \(\mathrm{rank}(q) < \mathrm{rank}(p)\).
Because the move sets are finite and the rank strictly decreases along moves, every play terminates and only finitely many positions are reachable from any position, even if the type of positions is infinite or contains unreachable positions.
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
Pos : Type
MathlibExt/GameTheory/SpragueGrundy.lean:32
moves : Pos → Finset Pos
MathlibExt/GameTheory/SpragueGrundy.lean:34
rank : Pos → ℕ
MathlibExt/GameTheory/SpragueGrundy.lean:35
Used by 34
DefinitionP-positions#✓fully proved
In an impartial game \(G\), the P-positions are defined by well-founded recursion along moves: a position \(p\) is a P-position if and only if no position reachable from \(p\) in one move is a P-position. Positions that are not P-positions are called N-positions.
Under normal play, where the player unable to move loses, the P-positions are exactly the positions from which the player to move loses.
noncomputable def IsP (G : ImpartialGame) : G.Pos → Prop :=
(wfRel G).fix fun p ih => ∀ q (h : q ∈ G.moves p), ¬ ih q h
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
theorem wfRel (G : ImpartialGame) :
WellFounded (fun a b => a ∈ G.moves b)
MathlibExt/GameTheory/SpragueGrundy.lean:39
Uses 1 · Used by 38
DefinitionSum of impartial games#✓fully proved
The sum \(G + H\) of two impartial games \(G\) and \(H\) is the impartial game whose positions are the pairs \((a, b)\) of a position \(a\) of \(G\) and a position \(b\) of \(H\), and in which a move changes exactly one component: from \((a, b)\) one may move to \((a', b)\) for any move from \(a\) to \(a'\) in \(G\), or to \((a, b')\) for any move from \(b\) to \(b'\) in \(H\). Its starting position is the pair of the starting positions of \(G\) and \(H\), and the rank of \((a, b)\) is the sum of the ranks of \(a\) in \(G\) and of \(b\) in \(H\).
MetaMathlibExt.SpragueGrundy.gameSum · 24 lines
noncomputable def gameSum (G H : ImpartialGame) : ImpartialGame :=
letI := G.eqDec
letI := H.eqDec
{ Pos := G.Pos × H.Pos
eqDec := inferInstance
moves := fun p =>
(G.moves p.1).image (fun a => (a, p.2)) ∪
(H.moves p.2).image (fun b => (p.1, b))
rank := fun p => G.rank p.1 + H.rank p.2
rank_lt := by
intro p q h
simp only [Finset.mem_union, Finset.mem_image] at h
cases h with
| inl h1 =>
obtain ⟨a', ha', heq⟩ := h1
change (a', p.2) = q at heq
subst heq
exact Nat.add_lt_add_right (G.rank_lt p.1 a' ha') _
| inr h2 =>
obtain ⟨b', hb', heq⟩ := h2
change (p.1, b') = q at heq
subst heq
exact Nat.add_lt_add_left (H.rank_lt p.2 b' hb') _
start := (G.start, H.start) }
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
Uses 1 · Used by 3
DefinitionNim heap#✓fully proved
For a natural number \(n\), the Nim heap of size \(n\) is the impartial game whose positions are \(0, 1, \dots, n\), whose starting position is \(n\), and in which a move from \(k\) goes to any \(j < k\); the rank of a position is its value.
def nimHeap (n : ℕ) : ImpartialGame where
Pos := Fin (n + 1)
eqDec := inferInstance
moves := fun k => Finset.univ.filter (fun j => j.val < k.val)
rank := fun k => k.val
rank_lt := by
intro k j h
simpa using h
start := ⟨n, Nat.lt_succ_self n⟩
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
Uses 1 · Used by 15
DefinitionEquivalence of impartial games#✓fully proved
Two impartial games \(G\) and \(H\) are equivalent when no third game tells them apart as summands: for every impartial game \(K\), the starting position of the sum \(G + K\) is a P-position if and only if the starting position of the sum \(H + K\) is a P-position. The starting position of a sum is the pair of the starting positions of its summands.
noncomputable def Equiv (G H : ImpartialGame) : Prop :=
∀ K : ImpartialGame,
IsP (gameSum G K) (G.start, K.start) ↔
IsP (gameSum H K) (H.start, K.start)
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
noncomputable def IsP (G : ImpartialGame) : G.Pos → Prop :=
(wfRel G).fix fun p ih => ∀ q (h : q ∈ G.moves p), ¬ ih q h
MathlibExt/GameTheory/SpragueGrundy.lean:46
noncomputable def gameSum (G H : ImpartialGame) : ImpartialGame :=
letI := G.eqDec
letI := H.eqDec
{ Pos := G.Pos × H.Pos
eqDec := inferInstance
moves := fun p =>
(G.moves p.1).image (fun a => (a, p.2)) ∪
(H.moves p.2).image (fun b => (p.1, b))
rank := fun p => G.rank p.1 + H.rank p.2
rank_lt := by
intro p q h
simp only [Finset.mem_union, Finset.mem_image] at h
⋯
MathlibExt/GameTheory/SpragueGrundy.lean:50
Uses 3 · Used by 19
TheoremRecursive characterization of P-positions#✓fully proved
In an impartial game \(G\), a position \(p\) is a P-position if and only if every position reachable from \(p\) in one move is not a P-position.
theorem isP_iff (G : ImpartialGame) (p : G.Pos) :
IsP G p ↔ ∀ q (_ : q ∈ G.moves p), ¬ IsP G q
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
noncomputable def IsP (G : ImpartialGame) : G.Pos → Prop :=
(wfRel G).fix fun p ih => ∀ q (h : q ∈ G.moves p), ¬ ih q h
MathlibExt/GameTheory/SpragueGrundy.lean:46
Uses 2 · Used by 6
TheoremSprague–Grundy theorem#✓fully proved
Every impartial game \(G\) is equivalent to a Nim heap: there is a natural number \(n\) such that \(G\) is equivalent to the Nim heap of size \(n\).
theorem sprague_grundy (G : ImpartialGame) :
∃ n : ℕ, Equiv G (nimHeap n)
structure ImpartialGame where
Pos : Type
eqDec : DecidableEq Pos
moves : Pos → Finset Pos
rank : Pos → ℕ
rank_lt : ∀ p q, q ∈ moves p → rank q < rank p
start : Pos
MathlibExt/GameTheory/SpragueGrundy.lean:31
noncomputable def Equiv (G H : ImpartialGame) : Prop :=
∀ K : ImpartialGame,
IsP (gameSum G K) (G.start, K.start) ↔
IsP (gameSum H K) (H.start, K.start)
MathlibExt/GameTheory/SpragueGrundy.lean:89
def nimHeap (n : ℕ) : ImpartialGame where
Pos := Fin (n + 1)
eqDec := inferInstance
moves := fun k => Finset.univ.filter (fun j => j.val < k.val)
rank := fun k => k.val
rank_lt := by
intro k j h
simpa using h
start := ⟨n, Nat.lt_succ_self n⟩
MathlibExt/GameTheory/SpragueGrundy.lean:76
Sources
- Tanya Khovanova and Joshua Xiong, "Nim Fractals", Journal of Integer Sequences 17 (2014), lines 101–105, https://cs.uwaterloo.ca/journals/JIS/VOL17/Khovanova/khova6.tex. Its cited original references: Grundy, Eureka 2 (1939), 6–8, reprinted 27 (1964), 9–11; Sprague, Tôhoku Math. Journal 41 (1935–1936), 438–444.
Uses 6
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.