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 Naturals.Properties
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}
