l3lab/ntp-mathlib huggingface dataset:
/-- The Sprague-Grundy theorem which states that every impartial game is equivalent to a game of
 nim, namely the game of nim corresponding to the games Grundy value -/
theorem equiv_nim_grundyValue : ∀ (G : PGame.{u}) [G.Impartial], G ≈ nim (grundyValue G)
  | G => by
    rw [Impartial.equiv_iff_add_equiv_zero, ← Impartial.forall_leftMoves_fuzzy_iff_equiv_zero]
    intro i
    apply leftMoves_add_cases i
    · intro i₁
      rw [add_moveLeft_inl]
      apply
        (fuzzy_congr_left (add_congr_left (Equiv.symm (equiv_nim_grundyValue (G.moveLeft i₁))))).1
      rw [nim_add_fuzzy_zero_iff]
      intro heq
      rw [eq_comm, grundyValue_eq_mex_left G] at heq
      -- Porting note: added universe annotation, argument
      have h := Ordinal.ne_mex.{u, u} (fun i ↦ grundyValue (moveLeft G i))
      rw [heq] at h
      exact (h i₁).irrefl
    · intro i₂
      rw [add_moveLeft_inr, ← Impartial.exists_left_move_equiv_iff_fuzzy_zero]
      revert i₂
      rw [nim_def]
      intro i₂
      have h' :
        ∃ i : G.LeftMoves,
          grundyValue (G.moveLeft i) = Ordinal.typein (Quotient.out (grundyValue G)).r i₂ := by
        revert i₂
        rw [grundyValue_eq_mex_left]
        intro i₂
        have hnotin : _ ∉ _ := fun hin =>
          (le_not_le_of_lt (Ordinal.typein_lt_self i₂)).2 (csInf_le' hin)
        simpa using hnotin
      cases' h' with i hi
      use toLeftMovesAdd (Sum.inl i)
      rw [add_moveLeft_inl, moveLeft_mk]
      apply Equiv.trans (add_congr_left (equiv_nim_grundyValue (G.moveLeft i)))
      simpa only [hi] using Impartial.add_self (nim (grundyValue (G.moveLeft i)))
termination_by equiv_nim_grundyValue G _ => G
decreasing_by 

Proof state:
x✝ : PGame
inst✝ : Impartial x✝
a✝ :
  ∀ (y : (x : PGame) ×' Impartial x),
    (invImage (fun a => PSigma.casesOn a fun G snd => G) instWellFoundedRelationPGame).1 y { fst := x✝, snd := inst✝ } →
      y.1 ≈ nim (grundyValue y.1)
G : PGame := x✝
i₂ :
  LeftMoves
    (mk (Quotient.out (grundyValue x✝)).α (Quotient.out (grundyValue x✝)).α
      (fun o₂ => nim (typein (fun x x_1 => x < x_1) o₂)) fun o₂ => nim (typein (fun x x_1 => x < x_1) o₂))
h' : ∃ i, grundyValue (moveLeft G i) = typein (Quotient.out (grundyValue G)).r i₂
i : LeftMoves G
hi : grundyValue (moveLeft G i) = typein (Quotient.out (grundyValue G)).r i₂
h✝ : h' = (_ : ∃ i, grundyValue (moveLeft G i) = typein (Quotient.out (grundyValue G)).r i₂)
⊢ (invImage (fun a => PSigma.casesOn a fun G snd => G) instWellFoundedRelationPGame).1
    { fst := moveLeft G i, snd := (_ : Impartial (moveLeft G i)) } { fst := x✝, snd := inst✝ }

Next tactic:
pgame_wf_tac
theorem grundyValue_eq_iff_equiv_nim {G : PGame} [G.Impartial] {o : Ordinal} :
    grundyValue G = o ↔ (G ≈ nim o) :=
  ⟨by 

Proof state:
G : PGame
inst✝ : Impartial G
o : Ordinal.{u_1}
⊢ grundyValue G = o → G ≈ nim o

Next tactic:
rintro rfl
theorem grundyValue_eq_iff_equiv_nim {G : PGame} [G.Impartial] {o : Ordinal} :
    grundyValue G = o ↔ (G ≈ nim o) :=
  ⟨by rintro rfl; 

Proof state:
G : PGame
inst✝ : Impartial G
⊢ G ≈ nim (grundyValue G)

Next tactic:
exact equiv_nim_grundyValue G
theorem grundyValue_eq_iff_equiv_nim {G : PGame} [G.Impartial] {o : Ordinal} :
    grundyValue G = o ↔ (G ≈ nim o) :=
  ⟨by rintro rfl; exact equiv_nim_grundyValue G,
   by 

Proof state:
G : PGame
inst✝ : Impartial G
o : Ordinal.{u_1}
⊢ G ≈ nim o → grundyValue G = o

Next tactic:
intro h
theorem grundyValue_eq_iff_equiv_nim {G : PGame} [G.Impartial] {o : Ordinal} :
    grundyValue G = o ↔ (G ≈ nim o) :=
  ⟨by rintro rfl; exact equiv_nim_grundyValue G,
   by intro h; 

Proof state:
G : PGame
inst✝ : Impartial G
o : Ordinal.{u_1}
h : G ≈ nim o
⊢ grundyValue G = o

Next tactic:
rw [← nim_equiv_iff_eq]
theorem grundyValue_eq_iff_equiv_nim {G : PGame} [G.Impartial] {o : Ordinal} :
    grundyValue G = o ↔ (G ≈ nim o) :=
  ⟨by rintro rfl; exact equiv_nim_grundyValue G,
   by intro h; rw [← nim_equiv_iff_eq]; 

Proof state:
G : PGame
inst✝ : Impartial G
o : Ordinal.{u_1}
h : G ≈ nim o
⊢ nim (grundyValue G) ≈ nim o

Next tactic:
exact Equiv.trans (Equiv.symm (equiv_nim_grundyValue G)) h
theorem grundyValue_iff_equiv_zero (G : PGame) [G.Impartial] : grundyValue G = 0 ↔ (G ≈ 0) := by
  

Proof state:
G : PGame
inst✝ : Impartial G
⊢ grundyValue G = 0 ↔ G ≈ 0

Next tactic:
rw [← grundyValue_eq_iff_equiv, grundyValue_zero]
@[simp]
theorem grundyValue_neg (G : PGame) [G.Impartial] : grundyValue (-G) = grundyValue G := by
  

Proof state:
G : PGame
inst✝ : Impartial G
⊢ grundyValue (-G) = grundyValue G

Next tactic:
rw [grundyValue_eq_iff_equiv_nim, neg_equiv_iff, neg_nim, ← grundyValue_eq_iff_equiv_nim]
theorem grundyValue_eq_mex_right :
    ∀ (G : PGame) [G.Impartial],
      grundyValue G = Ordinal.mex.{u, u} fun i => grundyValue (G.moveRight i)
   | ⟨l, r, L, R⟩, _ => by
    

Proof state:
l r : Type u
L : l → PGame
R : r → PGame
x✝ : Impartial (mk l r L R)
⊢ grundyValue (mk l r L R) = mex fun i => grundyValue (moveRight (mk l r L R) i)

Next tactic:
rw [← grundyValue_neg, grundyValue_eq_mex_left]
theorem grundyValue_eq_mex_right :
    ∀ (G : PGame) [G.Impartial],
      grundyValue G = Ordinal.mex.{u, u} fun i => grundyValue (G.moveRight i)
   | ⟨l, r, L, R⟩, _ => by
    rw [← grundyValue_neg, grundyValue_eq_mex_left]
    

Proof state:
l r : Type u
L : l → PGame
R : r → PGame
x✝ : Impartial (mk l r L R)
⊢ (mex fun i => grundyValue (moveLeft (-mk l r L R) i)) = mex fun i => grundyValue (moveRight (mk l r L R) i)

Next tactic:
congr
theorem grundyValue_eq_mex_right :
    ∀ (G : PGame) [G.Impartial],
      grundyValue G = Ordinal.mex.{u, u} fun i => grundyValue (G.moveRight i)
   | ⟨l, r, L, R⟩, _ => by
    rw [← grundyValue_neg, grundyValue_eq_mex_left]
    congr
    

Proof state:
case e_f
l r : Type u
L : l → PGame
R : r → PGame
x✝ : Impartial (mk l r L R)
⊢ (fun i => grundyValue (moveLeft (-mk l r L R) i)) = fun i => grundyValue (moveRight (mk l r L R) i)

Next tactic:
ext i
theorem grundyValue_eq_mex_right :
    ∀ (G : PGame) [G.Impartial],
      grundyValue G = Ordinal.mex.{u, u} fun i => grundyValue (G.moveRight i)
   | ⟨l, r, L, R⟩, _ => by
    rw [← grundyValue_neg, grundyValue_eq_mex_left]
    congr
    ext i
    

Proof state:
case e_f.h
l r : Type u
L : l → PGame
R : r → PGame
x✝ : Impartial (mk l r L R)
i : LeftMoves (-mk l r L R)
⊢ grundyValue (moveLeft (-mk l r L R) i) = grundyValue (moveRight (mk l r L R) i)

Next tactic:
haveI : (R i).Impartial := @Impartial.moveRight_impartial ⟨l, r, L, R⟩ _ i
theorem grundyValue_eq_mex_right :
    ∀ (G : PGame) [G.Impartial],
      grundyValue G = Ordinal.mex.{u, u} fun i => grundyValue (G.moveRight i)
   | ⟨l, r, L, R⟩, _ => by
    rw [← grundyValue_neg, grundyValue_eq_mex_left]
    congr
    ext i
    haveI : (R i).Impartial := @Impartial.moveRight_impartial ⟨l, r, L, R⟩ _ i
    

Proof state:
case e_f.h
l r : Type u
L : l → PGame
R : r → PGame
x✝ : Impartial (mk l r L R)
i : LeftMoves (-mk l r L R)
this : Impartial (R i)
⊢ grundyValue (moveLeft (-mk l r L R) i) = grundyValue (moveRight (mk l r L R) i)

Next tactic:
apply grundyValue_neg
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  

Proof state:
n m : ℕ
⊢ grundyValue (nim ↑n + nim ↑m) = ↑(n ^^^ m)

Next tactic:
induction' n using Nat.strong_induction_on with n hn generalizing m
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  

Proof state:
case h
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
⊢ grundyValue (nim ↑n + nim ↑m) = ↑(n ^^^ m)

Next tactic:
induction' m using Nat.strong_induction_on with m hm
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  

Proof state:
case h.h
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
⊢ grundyValue (nim ↑n + nim ↑m) = ↑(n ^^^ m)

Next tactic:
rw [grundyValue_eq_mex_left]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  

Proof state:
case h.h
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
⊢ (mex fun i => grundyValue (moveLeft (nim ↑n + nim ↑m) i)) = ↑(n ^^^ m)

Next tactic:
refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · 

Proof state:
case h.h.refine_1
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
⊢ grundyValue (moveLeft (nim ↑n + nim ↑m) i) ≠ ↑(n ^^^ m)

Next tactic:
apply leftMoves_add_cases i
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        

Proof state:
case h.h.refine_1.hl
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
⊢ ∀ (i : LeftMoves (nim ↑n)), grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inl i))) ≠ ↑(n ^^^ m)

Next tactic:
refine' fun a => leftMovesNimRecOn a fun ok hk => _
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        

Proof state:
case h.h.refine_1.hl
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
ok : Ordinal.{u}
hk : ok < ↑n
⊢ grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inl (toLeftMovesNim { val := ok, property := hk })))) ≠
    ↑(n ^^^ m)

Next tactic:
obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : ↑k < ↑n
⊢ grundyValue (moveLeft (nim ↑n + nim ↑m) (toLeftMovesAdd (Sum.inl (toLeftMovesNim { val := ↑k, property := hk })))) ≠
    ↑(n ^^^ m)

Next tactic:
simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : ↑k < ↑n
⊢ grundyValue (nim ↑k + nim ↑m) ≠ ↑(n ^^^ m)

Next tactic:
rw [nat_cast_lt] at hk
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
⊢ grundyValue (nim ↑k + nim ↑m) ≠ ↑(n ^^^ m)

Next tactic:
first
        | rw [hn _ hk]
        | rw [hm _ hk]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | 

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
⊢ grundyValue (nim ↑k + nim ↑m) ≠ ↑(n ^^^ m)

Next tactic:
rw [hn _ hk]
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
⊢ ↑(k ^^^ m) ≠ ↑(n ^^^ m)

Next tactic:
refine' fun h => hk.ne _
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
h : ↑(k ^^^ m) = ↑(n ^^^ m)
⊢ k = n

Next tactic:
rw [Ordinal.nat_cast_inj] at h
/-- The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise
xor. -/
@[simp]
theorem grundyValue_nim_add_nim (n m : ℕ) :
    grundyValue (nim.{u} n + nim.{u} m) = n ^^^ m := by
  -- We do strong induction on both variables.
  induction' n using Nat.strong_induction_on with n hn generalizing m
  induction' m using Nat.strong_induction_on with m hm
  rw [grundyValue_eq_mex_left]
  refine (Ordinal.mex_le_of_ne.{u, u} fun i => ?_).antisymm
    (Ordinal.le_mex_of_forall fun ou hu => ?_)
  -- The Grundy value `n ^^^ m` can't be reached by left moves.
  · apply leftMoves_add_cases i <;>
      · -- A left move leaves us with a Grundy value of `k ^^^ m` for `k < n`, or
        -- `n ^^^ k` for `k < m`.
        refine' fun a => leftMovesNimRecOn a fun ok hk => _
        obtain ⟨k, rfl⟩ := Ordinal.lt_omega.1 (hk.trans (Ordinal.nat_lt_omega _))
        simp only [add_moveLeft_inl, add_moveLeft_inr, moveLeft_nim', Equiv.symm_apply_apply]
        -- The inequality follows from injectivity.
        rw [nat_cast_lt] at hk
        first
        | rw [hn _ hk]
        | rw [hm _ hk]
        refine' fun h => hk.ne _
        rw [Ordinal.nat_cast_inj] at h
        

Proof state:
case h.h.refine_1.hl.intro
n : ℕ
hn : ∀ m < n, ∀ (m_1 : ℕ), grundyValue (nim ↑m + nim ↑m_1) = ↑(m ^^^ m_1)
m : ℕ
hm : ∀ m_1 < m, grundyValue (nim ↑n + nim ↑m_1) = ↑(n ^^^ m_1)
i : LeftMoves (nim ↑n + nim ↑m)
a : LeftMoves (nim ↑n)
k : ℕ
hk : k < n
h : k ^^^ m = n ^^^ m
⊢ k = n

Next tactic:
first
        | rwa [Nat.xor_left_inj] at h
        | rwa [Nat.xor_right_inj] at h