Alice Laroche, the 18th of September 2023,
using ideas and notations from Ayberk Tosun.

This file is a formalisation of Kirby-Paris Hydra game.  We show that
every battle eventually ends, and use this fact to compute the first
terms of Hydra(n).

[1] https://en.wikipedia.org/wiki/Hydra_game

[2] Kirby, Laurie; Paris, Jeff. Bull. Accessible independence results
    for Peano arithmetic.  London Math. Soc, 14 (1982), 285-293.
    http://logic.amu.edu.pl/images/3/3c/Kirbyparis.pdf

\begin{code}

{-# OPTIONS --safe --without-K #-}

module Various.Hydra where

open import MLTT.Spartan
open import MLTT.List
open import Ordinals.Notions

\end{code}

The type of Hydra is defined inductively: It is a list of subhydras
where empty list represent heads.

\begin{code}

data Hydra : 𝓤₀ ̇ where
 Node : List Hydra  Hydra

pattern Head = Node []

example₁ : Hydra
example₁ = Node (Node []  [])

example₂ : Hydra
example₂ = Node (Node []  Node []  [])

defeated : Hydra
defeated = Node []

\end{code}

We define the type of head's locations using two inductive type.  This
is to know if the head is a child or a grandchild, to help
implementing hydra regeneration mechanism.

\begin{code}

data HeadLocation₀ : List Hydra  𝓤₀ ̇ where
 here : {hs : List Hydra}
       HeadLocation₀ (Head  hs)
 next : {h : Hydra} {hs : List Hydra}
       HeadLocation₀ hs  HeadLocation₀ (h  hs)

data HeadLocation₁ : List Hydra  𝓤₀ ̇ where
 here₀ : {hs hs' : List Hydra}
        HeadLocation₀ hs  HeadLocation₁ (Node hs  hs')
 here₁ : {hs hs' : List Hydra}
        HeadLocation₁ hs  HeadLocation₁ (Node hs  hs')
 next  : {h : Hydra} {hs : List Hydra}
        HeadLocation₁ hs  HeadLocation₁ (h  hs)

HeadLocation : Hydra  𝓤₀ ̇
HeadLocation (Node hs) = HeadLocation₀ hs + HeadLocation₁ hs

\end{code}

Similarly, we define two cutting functions, one for cutting child head
and for cutting a grand-child head and regrowing subheads.

\begin{code}

cons-mult : {X : 𝓤 ̇ }  X    List X  List X
cons-mult x 0 xs = xs
cons-mult x (succ n) xs = x  (cons-mult x n xs)

cut₀ : (hs : List Hydra)  HeadLocation₀ hs  List Hydra
cut₀ (h  hs) (here)    = hs
cut₀ (h  hs) (next l)  = h  (cut₀ hs l)

cut₁ :   (hs : List Hydra)  HeadLocation₁ hs  List Hydra
cut₁ n (h  hs')         (next  l) = h  (cut₁ n hs' l)
cut₁ n ((Node hs)  hs') (here₀ l) = cons-mult (Node (cut₀ hs l)) (succ n) hs'
cut₁ n ((Node hs)  hs') (here₁ l) = Node (cut₁ n hs l)  hs'

cut :   (h : Hydra)  HeadLocation h  Hydra
cut n (Node hs) (inl l) = Node (cut₀ hs l)
cut n (Node hs) (inr l) = Node (cut₁ n hs l)

\end{code}

To prove that the process of cutting hydra's heads always end, we
define a relation between hydras and prove it's well founded.

\begin{code}

_⊲_ : Hydra  Hydra  𝓤₀ ̇
h  h' = Σ n   , Σ l  HeadLocation h' , h  cut n h' l

pattern step n l eq = (n , l , eq)

_⊲'_ : List Hydra  List Hydra  𝓤₀ ̇
hs ⊲' hs' = Σ n   , ( ( Σ l  HeadLocation₀ hs' , hs  cut₀ hs' l)
                      + ( Σ l  HeadLocation₁ hs' , hs  cut₁ n hs' l))

pattern step₀ n l eq = (n , inl (l , eq))
pattern step₁ n l eq = (n , inr (l , eq))

⊲⇒⊲' : {hs hs' : List Hydra}  Node hs  Node hs'  hs ⊲' hs'
⊲⇒⊲' (step n (inl l) refl) = step₀ n l refl
⊲⇒⊲' (step n (inr l) refl) = step₁ n l refl

[]-is-minimum : (hs : List Hydra)  ¬(hs ⊲' [])
[]-is-minimum h (step₀ _ () _)
[]-is-minimum h (step₁ _ () _)

⊲-is-well-founded : is-well-founded _⊲_
⊲'-is-well-founded : is-well-founded _⊲'_

⊲-is-well-founded (Node hs) = acc (recurs (⊲'-is-well-founded hs))
 where
  recurs : {hs : List Hydra}
          is-accessible (_⊲'_) hs
          (h' : Hydra)  h'  Node hs
          is-accessible (_⊲_) h'
  recurs (acc rec₁) (Node hs') r = acc (recurs (rec₁ hs' (⊲⇒⊲' r)))

⊲'-is-well-founded [] = acc  h r  𝟘-elim ([]-is-minimum h r))
⊲'-is-well-founded (h  hs) =
 acc (recurs (⊲-is-well-founded h) (⊲'-is-well-founded hs))
 where
  recurs : {h : Hydra} {hs : List Hydra}
          is-accessible _⊲_ h
          is-accessible _⊲'_ hs
          (hs' : List Hydra)  hs' ⊲' (h  hs)
          is-accessible _⊲'_ hs'
  recurs (acc rec₁) (acc rec₂) hs' (step₀ n here refl)      = acc rec₂
  recurs (acc rec₁) (acc rec₂) hs' (step₀ n (next l) refl)  =
   acc (recurs (acc rec₁) (rec₂ _ (step₀ n l refl)))
  recurs (acc rec₁) (acc rec₂) hs' (step₁ n (next l) refl)  =
   acc (recurs (acc rec₁) (rec₂ _ (step₁ n l refl)))
  recurs (acc rec₁) (acc rec₂) hs' (step₁ n (here₁ l) refl) =
   acc (recurs (rec₁ _ (step n (inr l) refl)) (acc rec₂))
  recurs (acc rec₁) (acc rec₂) hs' (step₁ n (here₀ l) refl) = recurs' n
   where
    recurs' : (n : )  is-accessible _⊲'_ (cons-mult _ (succ n) _)
    recurs' 0        = acc (recurs (rec₁ _ (step 0 (inl l) refl)) (acc rec₂))
    recurs' (succ n) = acc (recurs (rec₁ _ (step 0 (inl l) refl)) (recurs' n))

\end{code}

We can now define Hydra(n) and compute its four first terms.

\begin{code}

leftmost-head₀ : (hs : List Hydra)  hs  []
                HeadLocation₀ hs + HeadLocation₁ hs
leftmost-head₀ []                    neq = 𝟘-elim (neq refl)
leftmost-head₀ (Head  _)            neq = inl here
leftmost-head₀ ((Node (h  hs))  _) neq =
 leftmost-head₀' (leftmost-head₀ (h  hs)  ()))
 where
  leftmost-head₀' : HeadLocation₀ (h  hs) + HeadLocation₁ (h  hs)
                   HeadLocation₀ _ + HeadLocation₁ _
  leftmost-head₀' (inl l) = inr (here₀ l)
  leftmost-head₀' (inr l) = inr (here₁ l)

leftmost-head : (h : Hydra)  h  Head  HeadLocation h
leftmost-head Head            neq = 𝟘-elim (neq refl)
leftmost-head (Node (h  hs)) neq = leftmost-head₀ (h  hs)  ())

f-Hydra : (n : )  
f-Hydra n = battle 1 (tall-hydra n) (⊲-is-well-founded _)
 where
  tall-hydra : (n : )  Hydra
  tall-hydra 0 = Node []
  tall-hydra (succ n) = Node (tall-hydra n  [])

  battle :   (h : Hydra)  is-accessible _⊲_ h  
  battle turn Head            (acc rec₁ ) = 0
  battle turn (Node (h  hs)) (acc rec₁ ) =
   succ (battle (succ turn) cut-hydra (rec₁ cut-hydra (turn , cut-head , refl)))
   where
    cut-head : HeadLocation (Node (h  hs))
    cut-head = leftmost-head _  ())

    cut-hydra : Hydra
    cut-hydra = cut turn (Node (h  hs)) cut-head

f-Hydra0 : f-Hydra 0  0
f-Hydra0 = refl

f-Hydra1 : f-Hydra 1  1
f-Hydra1 = refl

f-Hydra2 : f-Hydra 2  3
f-Hydra2 = refl

f-Hydra3 : f-Hydra 3  37
f-Hydra3 = refl

\end{code}