Loopy games #
The standard notion of a game studied in combinatorial game theory is that of a terminating game,
meaning that there exists no infinite sequence of moves. Loopy games relax this condition by
allowing "self-refential" games, with the basic examples being on = {on | }, off = { | off}, and
dud = {dud | dud}.
In the literature, loopy games are defined as rooted directed graphs up to isomorphism. However,
it's simpler to define LGame as the coinductive type for the single constructor:
| ofSets (s t : Set LGame.{u}) [Small.{u} s] [Small.{u} t] : LGame.{u}
(The inductive type for this same constructor would be IGame.) This gives us a powerful
corecursion principle LGame.corec, which can be interpreted as saying "for any graph we can draw
on a type α, as long as the amount of branches per node is u-small, there's an LGame
isomorphic to it".
Although extensionality still holds, it's not always sufficient to prove two games equal. For
instance, if x = !{x | x} and y = !{y | y}, then x = y, but trying to use extensionality to
prove this just leads to a cyclic argument. Instead, we can use LGame.eq_of_bisim, which can
roughly be interpreted as saying "if two games have the same shape, they're equal". In this case,
the relation r a b ↔ a = x ∧ b = y is a bisimulation between both games, which proves their
equality.
For Mathlib #
Game moves #
The type of loopy games.
Most games studied within game theory are terminating, which means that the IsOption relation is
well-founded. A loopy game relaxes this constraint. Thus, LGame contains all normal IGames, but
it also contains games such as on = {on | }, off = { | off}, and dud = {dud | dud}.
See the module docstring for guidance on how to make use of this type.
Equations
Instances For
Equations
The set of left moves of the game.
Equations
- LGame.«term_ᴸ» = Lean.ParserDescr.trailingNode `LGame.«term_ᴸ» 1024 1024 (Lean.ParserDescr.symbol "ᴸ")
Instances For
The set of right moves of the game.
Equations
- LGame.«term_ᴿ» = Lean.ParserDescr.trailingNode `LGame.«term_ᴿ» 1024 1024 (Lean.ParserDescr.symbol "ᴿ")
Instances For
A (proper) subposition is any game in the transitive closure of IsOption.
Instances For
Two loopy games are equal when there exists a bisimulation between them.
A way to think about this is that r defines a pairing between nodes of the game trees, which then
shows that the trees are isomorphic.
Two LGames are equal when their move sets are.
This is not always sufficient to prove that two games are equal. For instance, if x = !{x | x} and
y = !{y | y}, then x = y, but trying to use extensionality to prove this just leads to a cyclic
argument. For these situations, you can use eq_of_bisim instead.
The corecursor on LGame.
You can use this in order to define an arbitrary LGame by "drawing" its move graph on some other
type. As an example, on = !{on | } is defined as corec (Player.cases ⊤ ⊥) ().
Equations
- LGame.corec mov init = LGame.corec'✝ mov init ⟨init, ⋯⟩
Instances For
Construct an LGame from its left and right sets.
It's not possible to create a non-well-founded game through this constructor alone. For that,
see LGame.corec.
Equations
- One or more equations did not get rendered due to their size.
Basic games #
The game 0 = !{∅ | ∅}.
Negation #
The negative of a game is defined by -!{s | t} = !{-t | -s}.
Equations
- LGame.instNeg = { neg := LGame.corec fun (p : Player) => LGame.moves (-p) }
Equations
- LGame.instInvolutiveNeg = { toNeg := LGame.instNeg, neg_neg := LGame.instInvolutiveNeg._proof_1 }
Equations
- LGame.instNegZeroClass = { toZero := LGame.instZero, toNeg := LGame.instNeg, neg_zero := LGame.instNegZeroClass._proof_1 }
Addition #
The sum of x = !{s₁ | t₁} and y = !{s₂ | t₂} is !{s₁ + y, x + s₂ | t₁ + y, x + t₂}.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
The subtraction of x and y is defined as x + (-y).
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Multiplication #
Given two game graphs drawn on types α and β, the graph for the product can be drawn on the
type Multiset (Player × α × β). Each term corresponds to a sum Σ ±aᵢ * bᵢ, where aᵢ and bᵢ
are terms of α and β respectively, and the attached player represents each term's sign.
Instances For
Inverts the sign of all entries.
Equations
- LGame.MulTy.instInvolutiveNeg = { neg := fun (x : LGame.MulTy α β) => Multiset.map (fun (y : Player × α × β) => (-y.1, y.2)) x, neg_neg := ⋯ }
The general form of an option of x * y is a * y + x * b - a * b.
If the player argument is left, all signs are flipped.
Instances For
The left or right moves of ±x * y are left or right moves of x * y if the sign is positive,
or the negatives of right or left moves of x * y if the sign is negative.
Equations
- LGame.MulTy.movesSingle p movα movβ x = LGame.MulTy.mulOption x.1 x.2 '' LGame.MulTy.movesSet p movα movβ x
Instances For
The set of left or right moves of Σ ±xᵢ * yᵢ are zᵢ + Σ ±xⱼ * yⱼ for all i, where cᵢ is
a left or right move of ±xᵢ * yᵢ, and the summation is taken over indices j ≠ i.
Equations
- LGame.MulTy.moves p movα movβ x = ⋃ y ∈ x, (fun (x_1 : LGame.MulTy α β) => Multiset.erase x y + x_1) '' LGame.MulTy.movesSingle p movα movβ y
Instances For
Map MulTy α₁ β₁ to MulTy α₂ β₂ using f : α₁ → α₂ and g : β₁ → β₂ in the natural way.
Equations
- LGame.MulTy.map f g = Multiset.map (Prod.map id (Prod.map f g))
Instances For
The game ±xᵢ * yᵢ.
Equations
- LGame.MulTy.toLGame movα movβ x = LGame.corec (fun (x : Player) => LGame.MulTy.moves x movα movβ) {x}
Instances For
The product of x = !{s₁ | t₁} and y = !{s₂ | t₂} is
!{a₁ * y + x * b₁ - a₁ * b₁ | a₂ * y + x * b₂ - a₂ * b₂}, where (a₁, b₁) ∈ s₁ ×ˢ s₂ ∪ t₁ ×ˢ t₂
and (a₂, b₂) ∈ s₁ ×ˢ t₂ ∪ t₁ ×ˢ s₂.
Using LGame.mulOption, this can alternatively be written as
x * y = !{mulOption x y a₁ b₁ | mulOption x y a₂ b₂}.
Equations
- LGame.instMul = { mul := fun (x y : LGame) => LGame.MulTy.toLGame LGame.moves LGame.moves (Player.right, x, y) }
Equations
- LGame.instCommMagma = { toMul := LGame.instMul, mul_comm := LGame.instCommMagma._proof_3 }
Equations
- LGame.instMulZeroClass = { toMul := LGame.instMul, toZero := LGame.instZero, zero_mul := LGame.instMulZeroClass._proof_1, mul_zero := LGame.instMulZeroClass._proof_2 }
Equations
- LGame.instMulOneClass = { toOne := LGame.instOne, toMul := LGame.instMul, one_mul := LGame.one_mul'✝, mul_one := LGame.instMulOneClass._proof_1 }
Equations
- LGame.instHasDistribNeg = { toInvolutiveNeg := LGame.instInvolutiveNeg, neg_mul := LGame.neg_mul'✝, mul_neg := LGame.instHasDistribNeg._proof_1 }
Stoppers #
A game is a stopper for player p when an alternating sequence of moves starting with said
player always terminates.
- mk {p : Player} {x : LGame} : (∀ y ∈ moves p x, StopperFor (-p) y) → StopperFor p x
Instances For
A game is a stopper when it's StopperFor for both players.
Equations
- x.Stopper = ∀ (p : Player), LGame.StopperFor p x