summaryrefslogtreecommitdiff
path: root/Day10.lean
diff options
context:
space:
mode:
Diffstat (limited to 'Day10.lean')
-rw-r--r--Day10.lean112
1 files changed, 111 insertions, 1 deletions
diff --git a/Day10.lean b/Day10.lean
index c3508a2..27c3c14 100644
--- a/Day10.lean
+++ b/Day10.lean
@@ -1,3 +1,113 @@
+import «Common»
+
namespace Day10
--- I'm going to go with a Dijkstra graph search here.
+structure Coordinate (width : Nat) (height : Nat) where
+ x : Fin width
+ y : Fin height
+
+def Coordinate.toIndex {w h : Nat} (c : Coordinate w h) : Fin (w*h) :=
+ Fin.mk (c.x.val * c.y.val) (Nat.mul_lt_mul_of_lt_of_lt c.x.isLt c.y.isLt)
+
+def Coordinate.fromIndex {w h : Nat} (index : Fin (w*h)) : Coordinate w h :=
+ have : w > 0 :=
+ have := index.isLt
+ match w with
+ | Nat.zero => absurd ((Nat.zero_mul h).subst this) (Nat.not_lt_zero index.val)
+ | Nat.succ ww => Nat.succ_pos ww
+ {
+ x := ⟨index.val % w, Nat.mod_lt index.val this⟩
+ y := ⟨index.val / w, Nat.div_lt_of_lt_mul index.isLt⟩
+ }
+
+inductive Pipe
+| NS : Pipe
+| WE : Pipe
+| NE : Pipe
+| ES : Pipe
+| SW : Pipe
+| WN : Pipe
+deriving BEq
+
+inductive Tile
+| pipe : Pipe → Tile
+| ground : Tile
+| start : Tile
+deriving BEq
+
+-- The invariants are maybe overdoing it a bit, but (in the voice of king Leonidas) "This is Lean4!"
+structure Area where
+ width : Nat
+ height : Nat
+ start : Coordinate width height
+ fields : Array Tile
+ size_invariant : fields.size = width * height
+ start_invariant : fields[start.toIndex] = Tile.start
+ start_invariant2 : ∀ (index : Fin (width*height)), (index ≠ start.toIndex) → fields[index] ≠ Tile.start
+
+inductive Area.ParseError
+| NoInput
+| UnexpectedCharacter
+| NoStart
+| MoreThanOneStart
+| NotRectangular
+
+private structure Area.Raw where
+ width : Nat
+ height : Nat
+ start : Nat
+ fields : Array Tile
+
+private def Area.ParseRaw (input : String) : Except Area.ParseError Area.Raw := do
+ let lines := input.splitTrim (· == '\n')
+ -- Treat the first line special. We use it to establish width
+ match lines with
+ | [] => throw Area.ParseError.NoInput
+ | l :: ls =>
+ let mut width := 0
+ let mut arr : Array Tile := Array.empty
+ let mut start : Option Nat := none
+ for c in l.toSubstring do
+ let tile ← parse_character c |> Option.toExcept Area.ParseError.UnexpectedCharacter
+ arr := arr.push tile
+ if tile == Tile.start then
+ if start.isNone then
+ start := some width
+ else
+ throw Area.ParseError.MoreThanOneStart
+ width := width + 1
+ let mut height := 1
+ for line in ls do
+ let mut thisWidth := 0
+ for c in line.toSubstring do
+ let tile ← parse_character c |> Option.toExcept Area.ParseError.UnexpectedCharacter
+ arr := arr.push tile
+ if tile == Tile.start then
+ if start.isNone then
+ start := some (thisWidth * height)
+ else
+ throw Area.ParseError.MoreThanOneStart
+ thisWidth := thisWidth +1
+ height := height + 1
+ if thisWidth != width then
+ throw Area.ParseError.NotRectangular
+ if let some s := start then
+ return {
+ width := width,
+ height := height,
+ start := s,
+ fields := arr
+ }
+ else
+ throw Area.ParseError.NoStart
+where
+ parse_character : Char → Option Tile := λ c ↦ match c with
+ | '|' => some $ Tile.pipe Pipe.NS
+ | '-' => some $ Tile.pipe Pipe.WE
+ | 'L' => some $ Tile.pipe Pipe.NE
+ | 'J' => some $ Tile.pipe Pipe.WN
+ | '7' => some $ Tile.pipe Pipe.SW
+ | 'F' => some $ Tile.pipe Pipe.ES
+ | 'S' => some Tile.start
+ | '.' => some Tile.ground
+ | _ => none