Martin Escardo, Paulo Oliva, 27th October 2022 Implementation of tic-tac-toe using a general definition of finite history dependent game. \begin{code} {-# OPTIONS --safe --without-K #-} module Games.TicTacToe0 where open import Fin.ArgMinMax open import Fin.Topology open import Fin.Type open import Games.J open import Games.K open import Games.TypeTrees open import MLTT.Athenian open import MLTT.Spartan hiding (J) open import TypeTopology.CompactTypes open import TypeTopology.SigmaDiscreteAndTotallySeparated open import UF.DiscreteAndSeparated \end{code} The type of outcomes: \begin{code} R : Type R = Fin 3 open import Games.FiniteHistoryDependent R open import Games.JK \end{code} The meaning of the outcomes: \begin{code} X-wins draw O-wins : R X-wins = π draw = π O-wins = π \end{code} In our conception of game, it is not necessary to specify the players, but in this case it is convenient to do so: \begin{code} data Player : Type where X O : Player opponent : Player β Player opponent X = O opponent O = X value : Player β R value X = X-wins value O = O-wins \end{code} It is also convenient to have a type of boards: \begin{code} Grid = Fin 3 Γ Fin 3 Matrix = Grid β Maybe Player Board = Player Γ Matrix boardβ : Board boardβ = X , (Ξ» _ β Nothing) \end{code} Convention: in a board (p , A), the player p plays next. \begin{code} wins : Player β Matrix β Bool wins p A = line || col || diag where _is_ : Maybe Player β Player β Bool Nothing is _ = false Just X is X = true Just O is X = false Just X is O = false Just O is O = true infix 30 _is_ lβ = A (π , π) is p && A (π , π) is p && A (π , π) is p lβ = A (π , π) is p && A (π , π) is p && A (π , π) is p lβ = A (π , π) is p && A (π , π) is p && A (π , π) is p cβ = A (π , π) is p && A (π , π) is p && A (π , π) is p cβ = A (π , π) is p && A (π , π) is p && A (π , π) is p cβ = A (π , π) is p && A (π , π) is p && A (π , π) is p dβ = A (π , π) is p && A (π , π) is p && A (π , π) is p dβ = A (π , π) is p && A (π , π) is p && A (π , π) is p line = lβ || lβ || lβ col = cβ || cβ || cβ diag = dβ || dβ \end{code} The type of moves in a board: \begin{code} Move : Board β Type Move (_ , A) = Ξ£ g κ Grid , A g οΌ Nothing \end{code} The type of grids has decidable equality (it is discrete) and decidable quantification (it is compact). The type of moves in a board is decidable (either empty or pointed) and also has decidable quantification. \begin{code} Grid-is-discrete : is-discrete Grid Grid-is-discrete = Γ-is-discrete Fin-is-discrete Fin-is-discrete Grid-compact : is-Compact Grid {π€β} Grid-compact = Γ-is-Compact Fin-Compact Fin-Compact Move-decidable : (b : Board) β is-decidable (Move b) Move-decidable (_ , A) = Grid-compact (Ξ» g β A g οΌ Nothing) (Ξ» g β Nothing-is-isolated' (A g)) Move-compact : (b : Board) β is-Compact (Move b) Move-compact (x , A) = complemented-subset-of-compact-type Grid-compact (Ξ» g β Nothing-is-isolated' (A g)) (Ξ» g β Nothing-is-h-isolated' (A g)) \end{code} Update a matrix by playing a move: \begin{code} update : (p : Player) (A : Matrix) β Move (p , A) β Matrix update p A (g , _) g' with (Grid-is-discrete g g') ... | inl _ = Just p ... | inr _ = A g' \end{code} Update a board by playing a move: \begin{code} play : (b : Board) β Move b β Board play (p , A) m = opponent p , update p A m \end{code} The game tree, with a bound on which we perform induction: \begin{code} tree : Board β β β π» tree b 0 = [] tree b@(p , A) (succ k) with wins (opponent p) A | Move-decidable b ... | true | _ = [] ... | false | inl _ = Move b β· (Ξ» (m : Move b) β tree (play b m) k) ... | false | inr _ = [] \end{code} The outcome function: \begin{code} outcome : (b : Board) (k : β) β Path (tree b k) β R outcome b 0 β¨β© = draw outcome b@(p , A) (succ k) ms with wins (opponent p) A | Move-decidable b outcome b@(p , A) (succ k) β¨β© | true | _ = value (opponent p) outcome b@(p , A) (succ k) (m :: ms) | false | inl _ = outcome (play b m) k ms outcome b@(p , A) (succ k) β¨β© | false | inr _ = draw \end{code} Selection functions for players, namely argmin for X and argmax for O: \begin{code} open J-definitions R selection : (p : Player) {M : Type} β M β is-Compact M {π€β} β J M selection X m ΞΊ p = prβ (compact-argmin p ΞΊ m) selection O m ΞΊ p = prβ (compact-argmax p ΞΊ m) \end{code} And their derived quantifiers: \begin{code} open K-definitions R open JK R quantifier : Player β {M : Type} β is-Compact M β is-decidable M β K M quantifier p ΞΊ (inl m) = overline (selection p m ΞΊ) quantifier p ΞΊ (inr _) = Ξ» _ β draw \end{code} The quantifier tree for the game: \begin{code} quantifiers : (b : Board) (k : β) β π (tree b k) quantifiers b 0 = β¨β© quantifiers b@(p , A) (succ k) with wins (opponent p) A | Move-decidable b ... | true | _ = β¨β© ... | false | inl _ = quantifier p (Move-compact b) (Move-decidable b) :: (Ξ» (m : Move b) β quantifiers (play b m) k) ... | false | inr _ = β¨β© \end{code} And finally the game by putting the above together: \begin{code} tic-tac-toe : Game tic-tac-toe = game (tree boardβ 9) (outcome boardβ 9) (quantifiers boardβ 9) r : R r = optimal-outcome tic-tac-toe \end{code} The above computation takes too long, due to the use of brute-force search in the definition of the game (the compactness conditions). A more efficient one is in another file. \begin{code} selections : (b : Board) (k : β) β π (tree b k) selections b 0 = β¨β© selections b@(p , A) (succ k) with wins (opponent p) A | Move-decidable b ... | true | _ = β¨β© ... | false | inl mβ = selection p mβ (Move-compact b) :: (Ξ» m β selections (play b m) k) ... | false | inr _ = β¨β© p : Path (Xt tic-tac-toe) p = sequenceα΄Ά (selections boardβ 9) (q tic-tac-toe) \end{code}