Martin Escardo, Paulo Oliva, 2-27 July 2021, with later additions.
We represent the moves of a history-dependent sequential game by a
dependent-type tree, collected in a type π».  This is either an empty
tree [] or else has a type X of initial moves at the root, and,
inductively, a family Xf of subtrees indexed by elements of X, which
is written X β· Xf. We refer to the family Xf as a forest. We let Xt
range over such trees.
 * Xt ranges over dependent-type trees.
 * Xf ranges over dependent-type forests.
\begin{code}
{-# OPTIONS --safe --without-K #-}
module Games.TypeTrees where
open import MonadOnTypes.Monad hiding (map)
open import MLTT.Spartan
open import UF.FunExt
open import UF.Subsingletons
open import UF.Subsingletons-FunExt
data π» : Typeβ where
  []  : π»
  _β·_ : (X : Type) (Xf : X β π») β π»
\end{code}
The type of full paths in a tree Xt, from the root to a leaf, is
inductively defined as follows:
\begin{code}
Path : π» β Type
Path []       = π
Path (X β· Xf) = Ξ£ x κ X , Path (Xf x)
\end{code}
As discussed above, a play in a game is defined to be such a path.
The idea is that we choose a move x, and then, inductively, a path in
the subtree Xf x.
The variable xs ranges over paths, that is, elements of the type
Path Xt for a dependent-type-tree Xt.
\begin{code}
pattern β¨β©        = β
pattern _::_ x xs = (x , xs)
path-head : {X : Type} {Xf : X β π»} β Path (X β· Xf) β X
path-head (x :: xs) = x
path-tail : {X : Type} {Xf : X β π»} ((x :: xs) : Path (X β· Xf)) β Path (Xf x)
path-tail (x :: xs) = xs
plength : {Xt : π»} β Path Xt β β
plength {[]}     β¨β©        = 0
plength {X β· Xf} (x :: xs) = succ (plength {Xf x} xs)
subpred : {X R : Type} {Xf : X β π»} β ((Ξ£ x κ X , Path (Xf x)) β R) β (x : X) β Path (Xf x) β R
subpred q x xs = q (x :: xs)
\end{code}
NB. An alternative inductive definition of Path is the following,
where, unfortunately, we get a higher type level, and so we won't use
it:
\begin{code}
data Pathβ : π» β Typeβ where
 []  : Pathβ []
 _β·_ : {X : Type} {Xf : X β π»} (x : X) (xs : Pathβ (Xf x)) β Pathβ (X β· Xf)
\end{code}
Equip the internal nodes of a type tree with structure:
\begin{code}
structure : (Type β π€ Μ ) β π» β π€ Μ
structure S []       = π
structure S (X β· Xf) = S X Γ ((x : X) β structure S (Xf x))
\end{code}
NB. An alternative inductive definition of structure is the following,
where, unfortunately, we get a higher type level, and so we won't use
it:
\begin{code}
data structureβ (S : Type β π€ Μ ) : π» β π€ βΊ Μ where
 []  : structureβ S []
 _β·_ : {X : Type} {Xf : X β π»} β S X β ((x : X) β structureβ S (Xf x)) β structureβ S (X β· Xf)
structure-up : (S : Type β π€ Μ ) (Xt : π») β structure S Xt β structureβ S Xt
structure-up S []      β¨β©         = []
structure-up S (X β· Xf) (s :: sf) = s β· (Ξ» x β structure-up S (Xf x) (sf x))
structure-down : (S : Type β π€ Μ ) (Xt : π») β structureβ S Xt β structure S Xt
structure-down S []      []        = β¨β©
structure-down S (X β· Xf) (s β· sf) = s :: (Ξ» x β structure-down S (Xf x) (sf x))
\end{code}
Xt is hereditarily P if all its internal nodes satisfy P:
\begin{code}
_is-hereditarily_ : π» β (Type β Type) β Type
[]       is-hereditarily P = π
(X β· Xf) is-hereditarily P = P X Γ ((x : X) β Xf x is-hereditarily P)
being-hereditary-is-prop : Fun-Ext
                         β (P : Type β Type)
                         β ((X : Type) β is-prop (P X))
                         β (Xt : π») β is-prop (Xt is-hereditarily P)
being-hereditary-is-prop fe P P-is-prop-valued [] = π-is-prop
being-hereditary-is-prop fe P P-is-prop-valued (X β· Xf) =
 Γ-is-prop
  (P-is-prop-valued X)
  (Ξ -is-prop fe Ξ» x β being-hereditary-is-prop fe P P-is-prop-valued (Xf x))
\end{code}
The sequence operator for monads is defined on lists, but here we
consider a version on paths of a tree instead:
\begin{code}
path-sequence : (π£ : Monad) {Xt : π»} β structure (functor π£) Xt β functor π£ (Path Xt)
path-sequence π£ {[]}     β¨β©        = Ξ· π£ β¨β©
path-sequence π£ {X β· Xf} (t :: tf) = t β[ π£ ] (Ξ» x β path-sequence π£ {Xf x} (tf x))
\end{code}
The induction principle for π» is included for the sake of
completeness, but won't be used directly:
\begin{code}
π»-induction : (P : π» β π€ Μ )
            β P []
            β ((X : Type) (Xf : X β π») β ((x : X) β P (Xf x)) β P (X β· Xf))
            β (Xt : π») β P Xt
π»-induction P b f = h
 where
  h : (Xt : π») β P Xt
  h []       = b
  h (X β· Xf) = f X Xf (Ξ» x β h (Xf x))
π»-recursion : (A : π€ Μ )
            β A
            β ((X : Type) β (X β π») β (X β A) β A)
            β π» β A
π»-recursion A = π»-induction (Ξ» _ β A)
π»-iteration : (A : π€ Μ )
            β A
            β ((X : Type) β (X β A) β A)
            β π» β A
π»-iteration A a g = π»-induction (Ξ» _ β A) a (Ξ» X Xf β g X)
\end{code}
Here are some examples for the sake of illustration:
\begin{code}
private
 Path' : π» β Type
 Path' = π»-iteration Type π (Ξ» X F β Ξ£ x κ X , F x)
 Path'-[] : Path' [] οΌ π
 Path'-[] = refl
 Path'-β· : (X : Type) (Xf : X β π»)
         β Path' (X β· Xf) οΌ (Ξ£ x κ X , Path' (Xf x))
 Path'-β· X Xf = refl
 structure' : (S : Type β π€ Μ ) β π» β π€ Μ
 structure' {π€} S = π»-iteration (π€ Μ ) π (Ξ» X F β S X Γ ((x : X) β F x))
 structure'-[] : (S : Type β π€ Μ )
               β structure' S [] οΌ π
 structure'-[] S = refl
 structure'-β· : (S : Type β π€ Μ ) (X : Type) (Xf : X β π»)
              β structure' S (X β· Xf) οΌ S X Γ ((x : X) β structure' S (Xf x))
 structure'-β· S X Xf = refl
\end{code}
Moved here 22 Oct 2025 from code from 8th October 2025 in the file
OptimalPlays, with a simplification on 29th October.
\begin{code}
open import MLTT.List
private
 Ξ½ : {X : Type}
     {Xf : X β π»}
   β ((x : X) β List (Path (Xf x)))
   β (X β List (Path (X β· Xf)))
 Ξ½ f x = map (x ::_) (f x)
\end{code}
Notice that the above function Ξ½ transforms a dependent function into
a non-dependent one.
\begin{code}
list-of-all-paths : (Xt : π»)
                    (lt : structure listed Xt)
                  β List (Path Xt)
list-of-all-paths [] β¨β© = [ β¨β© ]
list-of-all-paths (X β· Xf) ((xs , m) , lf) = List-ext (Ξ½ f) xs
 where
  f : (x : X) β List (Path (Xf x))
  f x = list-of-all-paths (Xf x) (lf x)
path-is-member-of-list-of-all-paths : (Xt : π»)
                                      (lt : structure listed Xt)
                                      (xs : Path Xt)
                                    β member xs (list-of-all-paths Xt lt)
path-is-member-of-list-of-all-paths [] β¨β© β¨β© = in-head
path-is-member-of-list-of-all-paths (X β· Xf) ((ys , m) , lf) (x :: xs) = III
 where
  f : (x : X) β List (Path (Xf x))
  f x = list-of-all-paths (Xf x) (lf x)
  IH : member xs (f x)
  IH = path-is-member-of-list-of-all-paths (Xf x) (lf x) xs
  I : member  (x :: xs) (Ξ½ f x)
  I = member-of-mapβ (x ::_) (f x) xs IH
  II : member (Ξ½ f x) (map (Ξ½ f) ys)
  II = member-of-mapβ (Ξ½ f) ys x (m x)
  III : member (x :: xs) (List-ext (Ξ½ f) ys)
  III = member-of-concatβ (x :: xs) (map (Ξ½ f) ys) (Ξ½ f x) II I
\end{code}
We are not currently using pmap, but we keep it for the record:
\begin{code}
pmap  : {X : Type}
        {Xf : X β π»}
      β ((x : X) β Path (Xf x))
      β List X
      β List (Path (X β· Xf))
pmap = dmap
\end{code}