summaryrefslogtreecommitdiff
path: root/Day10.lean
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2024-09-08 18:03:32 +0200
committerAndreas Grois <andi@grois.info>2024-09-08 18:03:32 +0200
commitfd1b33bf92ef82eca77fdd1f66a732a0093642cf (patch)
tree957596d00f5c208da58126aeb2ceb28a85a4e533 /Day10.lean
parenta6e5f7c0c830308c3912a043d4e741a13d3ce6cd (diff)
Day 10 Part 1
Diffstat (limited to 'Day10.lean')
-rw-r--r--Day10.lean91
1 files changed, 57 insertions, 34 deletions
diff --git a/Day10.lean b/Day10.lean
index 84df93e..17b864f 100644
--- a/Day10.lean
+++ b/Day10.lean
@@ -5,6 +5,7 @@ namespace Day10
structure Coordinate (width : Nat) (height : Nat) where
x : Fin width
y : Fin height
+deriving DecidableEq
def Coordinate.toIndex {w h : Nat} (c : Coordinate w h) : Fin (w*h) :=
Fin.mk (c.x.val + w * c.y.val) (
@@ -223,7 +224,7 @@ private def Area.parseLines (previous : Area.Raw) (input : List String) : Except
throw Area.ParseError.NotRectangular
private def Area.parseRaw (input : String) : Except Area.ParseError Area.Raw :=
- let lines := input.splitTrim (· == '\n')
+ let lines := input.trim.splitTrim (· == '\n')
if h₁ : lines.isEmpty then
throw Area.ParseError.NoInput
else
@@ -629,8 +630,8 @@ private theorem Area.ParseRaw_start_position_aux :
case isFalse => rw[←hr] at h₁; contradiction
case isTrue =>
rename_i hs _
- have : 0 < (String.splitTrim (fun x => x == '\n') input).length := by let hs := List.isEmpty_eq_false.mpr hs; unfold List.isEmpty at hs; split at hs; contradiction; rename_i hx; simp[hx]
- have := Area.ParseLines_start_position { width := (String.splitTrim (fun x => x == '\n') input)[0].length, height := 0, start := none, fields := #[] } (String.splitTrim (fun x => x == '\n') input) (hr.substr h₁)
+ have : 0 < (String.splitTrim (fun x => x == '\n') input.trim).length := by let hs := List.isEmpty_eq_false.mpr hs; unfold List.isEmpty at hs; split at hs; contradiction; rename_i hx; simp[hx]
+ have := Area.ParseLines_start_position { width := (String.splitTrim (fun x => x == '\n') input.trim)[0].length, height := 0, start := none, fields := #[] } (String.splitTrim (fun x => x == '\n') input.trim) (hr.substr h₁)
simp at this
subst r
split at this
@@ -842,42 +843,41 @@ theorem Coordinate.go_south_is_south_of (area : Area) (c o : Coordinate area.wid
def Area.are_connected (area : Area) (current : Coordinate area.width area.height) (previous : Coordinate area.width area.height) : Prop :=
area.can_connect_to current previous ∧ area.can_connect_to previous current
-structure Area.PathHead where
- area : Area
+structure Area.PathHead (area : Area) where
current : Coordinate area.width area.height
previous : Coordinate area.width area.height
current_can_connect_to_previous : area.can_connect_to current previous
-structure Area.BidirPathHead extends Area.PathHead where
+structure Area.BidirPathHead (area : Area) extends Area.PathHead area where
previous_can_connect_to_current : area.can_connect_to previous current
-private theorem Area.PathHead.current_not_start_not_ground (pathHead : Area.PathHead) : pathHead.area[pathHead.current] ≠ Tile.ground ∧ pathHead.area[pathHead.current] ≠ Tile.start :=
- have h₁ : pathHead.area[pathHead.current] ≠ Tile.ground := by
- cases ht : (pathHead.area[pathHead.current] == Tile.ground) <;> simp at ht
+private theorem Area.PathHead.current_not_start_not_ground (pathHead : Area.PathHead area) : area[pathHead.current] ≠ Tile.ground ∧ area[pathHead.current] ≠ Tile.start :=
+ have h₁ : area[pathHead.current] ≠ Tile.ground := by
+ cases ht : (area[pathHead.current] == Tile.ground) <;> simp at ht
case false => assumption
case true =>
cases pathHead.current_can_connect_to_previous <;> simp_all
- have h₂ : pathHead.area[pathHead.current] ≠ Tile.start := by
- cases ht : (pathHead.area[pathHead.current] == Tile.start) <;> simp at ht
+ have h₂ : area[pathHead.current] ≠ Tile.start := by
+ cases ht : (area[pathHead.current] == Tile.start) <;> simp at ht
case false => assumption
case true =>
cases pathHead.current_can_connect_to_previous <;> simp_all
And.intro h₁ h₂
section
-variable (last_step : Area.PathHead)
-local infixl:55 "is_west_of" => last_step.area.is_west_of
-local infixl:55 "is_north_of" => last_step.area.is_north_of
-local infixl:55 "is_east_of" => last_step.area.is_east_of
-local infixl:55 "is_south_of" => last_step.area.is_south_of
-
-private def Area.nextPathStep : Option (Area.BidirPathHead) :=
- let can_connect_to_current := (last_step.area.can_connect_to · last_step.current)
- let currentTile := last_step.area[last_step.current]
- have h₀ : last_step.area[last_step.current] = currentTile := rfl
+variable (area : Area)
+local infixl:55 "is_west_of" => area.is_west_of
+local infixl:55 "is_north_of" => area.is_north_of
+local infixl:55 "is_east_of" => area.is_east_of
+local infixl:55 "is_south_of" => area.is_south_of
+
+private def Area.nextPathStep (last_step: Area.PathHead area) : Option (Area.BidirPathHead area) :=
+ let can_connect_to_current := (area.can_connect_to · last_step.current)
+ let currentTile := area[last_step.current]'(And.intro rfl rfl)
+ have h₀ : area[last_step.current] = currentTile := rfl
have ⟨(_h₁ : currentTile ≠ Tile.ground), (_h₂ : currentTile ≠ Tile.start)⟩ := Area.PathHead.current_not_start_not_ground last_step
- let next : Option $ (next : Coordinate last_step.area.width last_step.area.height) ×' (last_step.area.can_connect_to last_step.current next) := match h₃ : currentTile with
+ let next : Option $ (next : Coordinate area.width area.height) ×' (area.can_connect_to last_step.current next) := match h₃ : currentTile with
| .pipe .NE =>
if h₄ : last_step.previous is_north_of last_step.current then
last_step.current.goEast.mapWithProof λ next h₅ ↦
@@ -937,7 +937,6 @@ private def Area.nextPathStep : Option (Area.BidirPathHead) :=
next.bind λ (PSigma.mk next h₇) ↦
if h₆ : can_connect_to_current next then
some {
- area := last_step.area
current := next
previous := last_step.current
current_can_connect_to_previous := h₆
@@ -948,12 +947,11 @@ private def Area.nextPathStep : Option (Area.BidirPathHead) :=
end
def part1 (area : Area) : Option Nat := do
- let starts : List (Area.PathHead) :=
+ let mut paths : List (Area.PathHead area) :=
[area.start.goNorth, area.start.goEast, area.start.goSouth, area.start.goWest]
|> .filterMap λ c ↦ c.bind λc ↦
if h: area.can_connect_to c area.start then
some {
- area := area
current := c
previous := area.start
current_can_connect_to_previous := h
@@ -961,27 +959,52 @@ def part1 (area : Area) : Option Nat := do
else
none
- let result : Option Nat := none
let mut steps := 1
- while steps * 2 ≤ area.width * area.height do --check is actually redundant, but I'm too lazy to prove
-
- sorry
- result
+ -- The condition in the while-loop is not needed. The program always terminates, as the
+ -- search space (Coordinate width height) is finite, paths cannot cross, and all closed
+ -- paths have an even number of steps. But I'm too lazy to prove this now, so, while-loop.
+ -- It's ≤ instead of < here, because there might still be a solution for
+ -- steps * 2 = area.width * area.height. Take this simple grid, for instance:
+ --
+ -- S7 01
+ -- LJ 12
+ while steps * 2 ≤ area.width * area.height do
+ if noSolution paths then
+ none
+ if pathsMet paths then
+ return steps
+ steps := steps + 1
+ paths := paths.filterMap (Functor.map Area.BidirPathHead.toPathHead ∘ area.nextPathStep)
+ none
where
- pathsMet := λ (ps : List Area.PathHead) ↦ match ps with
+ pathsMet := λ (ps : List $ Area.PathHead area) ↦ match ps with
| [] => false
- | p :: ps => ps.any λh ↦ sorry --h.current = p.current
+ | p :: ps => ps.any λh ↦ h.current = p.current
+ noSolution := λ (ps : List $ Area.PathHead area) ↦ match ps with
+ | [] => true
+ | _ :: [] => true
+ | _ => false
------------------------------------------------------------------------------------------------------------
+open DayPart
+instance : Parse ⟨10, by simp⟩ (ι := Area) where
+ parse := Except.mapError toString ∘ Area.parse
-section Test
+instance : Part ⟨10,_⟩ Parts.One (ι := Area) (ρ := Nat) where
+ run := part1
+section Test
+------------------------------------------------------------------------------------------------------------
def testData : String := "7-F7-
.FJ|7
SJLL7
|F--J
LJ.LJ"
-#eval (Area.parse testData)
+#eval
+ let area := (Area.parse testData)
+ match area with
+ | .ok a => part1 a
+ | .error e => none
end Test