Documentation

Mathlib.SetTheory.Game.Nim

Nim and the Sprague-Grundy theorem #

This file contains the definition for nim for any ordinal o. In the game of nim o₁ both players may move to nim o₂ for any o₂ < o₁. We also define a Grundy value for an impartial game G and prove the Sprague-Grundy theorem, that G is equivalent to nim (grundyValue G). Finally, we compute the sum of finite Grundy numbers: if G and H have Grundy values n and m, where n and m are natural numbers, then G + H has the Grundy value n xor m.

Implementation details #

The pen-and-paper definition of nim defines the possible moves of nim o to be Set.Iio o. However, this definition does not work for us because it would make the type of nim Ordinal.{u} → SetTheory.PGame.{u + 1}, which would make it impossible for us to state the Sprague-Grundy theorem, since that requires the type of nim to be Ordinal.{u} → SetTheory.PGame.{u}. For this reason, we instead use o.out.α for the possible moves. You can use to_left_moves_nim and to_right_moves_nim to convert an ordinal less than o into a left or right move of nim o, and vice versa.

@[irreducible]

The definition of single-heap nim, which can be viewed as a pile of stones where each player can take a positive number of stones from it on their turn.

Equations
  • One or more equations did not get rendered due to their size.
Instances For
    theorem SetTheory.PGame.nim_def (o : Ordinal.{u_1}) :
    let_fun this := ; SetTheory.PGame.nim o = SetTheory.PGame.mk (Quotient.out o) (Quotient.out o) (fun (o₂ : (Quotient.out o)) => SetTheory.PGame.nim (Ordinal.typein (fun (x x_1 : (Quotient.out o)) => x < x_1) o₂)) fun (o₂ : (Quotient.out o)) => SetTheory.PGame.nim (Ordinal.typein (fun (x x_1 : (Quotient.out o)) => x < x_1) o₂)
    theorem SetTheory.PGame.moveLeft_nim_hEq (o : Ordinal.{u_1}) :
    let_fun this := ; HEq (SetTheory.PGame.nim o).moveLeft fun (i : (Quotient.out o)) => SetTheory.PGame.nim (Ordinal.typein (fun (x x_1 : (Quotient.out o)) => x < x_1) i)
    theorem SetTheory.PGame.moveRight_nim_hEq (o : Ordinal.{u_1}) :
    let_fun this := ; HEq (SetTheory.PGame.nim o).moveRight fun (i : (Quotient.out o)) => SetTheory.PGame.nim (Ordinal.typein (fun (x x_1 : (Quotient.out o)) => x < x_1) i)
    noncomputable def SetTheory.PGame.toLeftMovesNim {o : Ordinal.{u_1}} :
    (Set.Iio o) (SetTheory.PGame.nim o).LeftMoves

    Turns an ordinal less than o into a left move for nim o and viceversa.

    Equations
    • SetTheory.PGame.toLeftMovesNim = o.enumIsoOut.trans (Equiv.cast )
    Instances For
      noncomputable def SetTheory.PGame.toRightMovesNim {o : Ordinal.{u_1}} :
      (Set.Iio o) (SetTheory.PGame.nim o).RightMoves

      Turns an ordinal less than o into a right move for nim o and viceversa.

      Equations
      • SetTheory.PGame.toRightMovesNim = o.enumIsoOut.trans (Equiv.cast )
      Instances For
        @[simp]
        theorem SetTheory.PGame.toLeftMovesNim_symm_lt {o : Ordinal.{u_1}} (i : (SetTheory.PGame.nim o).LeftMoves) :
        (SetTheory.PGame.toLeftMovesNim.symm i) < o
        @[simp]
        theorem SetTheory.PGame.toRightMovesNim_symm_lt {o : Ordinal.{u_1}} (i : (SetTheory.PGame.nim o).RightMoves) :
        (SetTheory.PGame.toRightMovesNim.symm i) < o
        @[simp]
        theorem SetTheory.PGame.moveLeft_nim' {o : Ordinal.{u}} (i : (SetTheory.PGame.nim o).LeftMoves) :
        (SetTheory.PGame.nim o).moveLeft i = SetTheory.PGame.nim (SetTheory.PGame.toLeftMovesNim.symm i)
        theorem SetTheory.PGame.moveLeft_nim {o : Ordinal.{u_1}} (i : (Set.Iio o)) :
        (SetTheory.PGame.nim o).moveLeft (SetTheory.PGame.toLeftMovesNim i) = SetTheory.PGame.nim i
        @[simp]
        theorem SetTheory.PGame.moveRight_nim' {o : Ordinal.{u_1}} (i : (SetTheory.PGame.nim o).RightMoves) :
        (SetTheory.PGame.nim o).moveRight i = SetTheory.PGame.nim (SetTheory.PGame.toRightMovesNim.symm i)
        theorem SetTheory.PGame.moveRight_nim {o : Ordinal.{u_1}} (i : (Set.Iio o)) :
        (SetTheory.PGame.nim o).moveRight (SetTheory.PGame.toRightMovesNim i) = SetTheory.PGame.nim i
        def SetTheory.PGame.leftMovesNimRecOn {o : Ordinal.{u_2}} {P : (SetTheory.PGame.nim o).LeftMovesSort u_1} (i : (SetTheory.PGame.nim o).LeftMoves) (H : (a : Ordinal.{u_2}) → (H : a < o) → P (SetTheory.PGame.toLeftMovesNim a, H)) :
        P i

        A recursion principle for left moves of a nim game.

        Equations
        Instances For
          def SetTheory.PGame.rightMovesNimRecOn {o : Ordinal.{u_2}} {P : (SetTheory.PGame.nim o).RightMovesSort u_1} (i : (SetTheory.PGame.nim o).RightMoves) (H : (a : Ordinal.{u_2}) → (H : a < o) → P (SetTheory.PGame.toRightMovesNim a, H)) :
          P i

          A recursion principle for right moves of a nim game.

          Equations
          Instances For
            @[simp]
            theorem SetTheory.PGame.default_nim_one_leftMoves_eq :
            default = SetTheory.PGame.toLeftMovesNim 0,
            @[simp]
            theorem SetTheory.PGame.default_nim_one_rightMoves_eq :
            default = SetTheory.PGame.toRightMovesNim 0,
            @[simp]
            theorem SetTheory.PGame.toLeftMovesNim_one_symm (i : (SetTheory.PGame.nim 1).LeftMoves) :
            SetTheory.PGame.toLeftMovesNim.symm i = 0,
            @[simp]
            theorem SetTheory.PGame.toRightMovesNim_one_symm (i : (SetTheory.PGame.nim 1).RightMoves) :
            SetTheory.PGame.toRightMovesNim.symm i = 0,

            nim 1 has exactly the same moves as star.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              Equations
              • =
              @[irreducible]

              The Grundy value of an impartial game, the ordinal which corresponds to the game of nim that the game is equivalent to

              Equations
              • x.grundyValue = Ordinal.mex fun (i : x.LeftMoves) => (x.moveLeft i).grundyValue
              Instances For
                theorem SetTheory.PGame.grundyValue_eq_mex_left (G : SetTheory.PGame) :
                G.grundyValue = Ordinal.mex fun (i : G.LeftMoves) => (G.moveLeft i).grundyValue
                @[irreducible]

                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 SetTheory.PGame.grundyValue_eq_iff_equiv (G : SetTheory.PGame) (H : SetTheory.PGame) [G.Impartial] [H.Impartial] :
                G.grundyValue = H.grundyValue G H
                theorem SetTheory.PGame.grundyValue_iff_equiv_zero (G : SetTheory.PGame) [G.Impartial] :
                G.grundyValue = 0 G 0
                @[simp]
                theorem SetTheory.PGame.grundyValue_neg (G : SetTheory.PGame) [G.Impartial] :
                (-G).grundyValue = G.grundyValue
                theorem SetTheory.PGame.grundyValue_eq_mex_right (G : SetTheory.PGame) [G.Impartial] :
                G.grundyValue = Ordinal.mex fun (i : G.RightMoves) => (G.moveRight i).grundyValue
                @[simp]
                theorem SetTheory.PGame.grundyValue_nim_add_nim (n : ) (m : ) :
                (SetTheory.PGame.nim n + SetTheory.PGame.nim m).grundyValue = (n ^^^ m)

                The Grundy value of the sum of two nim games with natural numbers of piles equals their bitwise xor.

                theorem SetTheory.PGame.grundyValue_add (G : SetTheory.PGame) (H : SetTheory.PGame) [G.Impartial] [H.Impartial] {n : } {m : } (hG : G.grundyValue = n) (hH : H.grundyValue = m) :
                (G + H).grundyValue = (n ^^^ m)