summaryrefslogtreecommitdiff
path: root/Day2.lean
diff options
context:
space:
mode:
Diffstat (limited to 'Day2.lean')
-rw-r--r--Day2.lean97
1 files changed, 97 insertions, 0 deletions
diff --git a/Day2.lean b/Day2.lean
new file mode 100644
index 0000000..f0eff56
--- /dev/null
+++ b/Day2.lean
@@ -0,0 +1,97 @@
+import Common
+
+structure Draw (α : Type) where
+ red : α
+ green : α
+ blue : α
+ deriving Repr
+
+structure Game where
+ id : Nat
+ draw : List $ Draw Nat
+ deriving Repr
+
+def parse (input : String) : Option $ List Game :=
+ let lines := input.splitTrim (. == '\n')
+ let lines := lines.filter (not ∘ String.isEmpty)
+ let parse_single_line : (String → Option Game):= λ (l : String) ↦ do
+ let parse_id := λ (s : String) ↦ do
+ let rest ← if s.startsWith "Game " then some (s.drop 5) else none
+ rest.toNat?
+ let parse_draw := λ (s : String) ↦ do
+ let s := s.splitTrim (· == ',')
+ let findAndRemoveTail := λ (s : String) (t : String) ↦
+ if s.endsWith t then
+ some $ String.dropRight s (t.length)
+ else
+ none
+ let update_draw_parse := λ (pd : Option (Draw (Option String))) (c : String) ↦ do
+ let old ← pd
+ let red := findAndRemoveTail c " red"
+ let green := findAndRemoveTail c " green"
+ let blue := findAndRemoveTail c " blue"
+ match red, green, blue with
+ | some red, none, none => match old.red with
+ | none => some $ {old with red := some red}
+ | some _ => none
+ | none, some green, none => match old.green with
+ | none => some $ {old with green := some green}
+ | some _ => none
+ | none, none, some blue => match old.blue with
+ | none => some $ {old with blue := some blue}
+ | some _ => none
+ | _, _, _ => none
+ let parsed_draw ← s.foldl update_draw_parse (some $ Draw.mk none none none)
+ let parsed_draw := {
+ red := String.toNat? <$> parsed_draw.red,
+ green := String.toNat? <$> parsed_draw.green,
+ blue := String.toNat? <$> parsed_draw.blue : Draw _}
+ let extractOrFail := λ (s : Option $ Option Nat) ↦ match s with
+ | none => some 0
+ | some none => none
+ | some $ some x => some x
+ let red ← extractOrFail parsed_draw.red
+ let green ← extractOrFail parsed_draw.green
+ let blue ← extractOrFail parsed_draw.blue
+ pure { red := red, blue := blue, green := green : Draw _}
+ let parse_draws := λ s ↦ List.mapM parse_draw $ s.splitTrim (· == ';')
+ let (id, draw) ← match l.splitTrim (· == ':') with
+ | [l, r] => Option.zip (parse_id l) (parse_draws r)
+ | _ => none
+ pure { id := id, draw := draw}
+ lines.mapM parse_single_line
+
+def part1 (games : List Game) : Nat :=
+ let draw_possible := λ g ↦ g.red ≤ 12 && g.green ≤ 13 && g.blue ≤ 14
+ let possible := flip List.all draw_possible
+ let possible_games := games.filter (possible ∘ Game.draw)
+ possible_games.map Game.id |> List.foldl Nat.add 0
+
+def part2 (games : List Game) : Nat :=
+ let powerOfGame := λ (g : Game) ↦
+ let minReq := λ (c : Draw Nat → Nat) ↦
+ g.draw.map c |> List.maximum? |> flip Option.getD 0
+ minReq Draw.red * minReq Draw.green * minReq Draw.blue
+ let powers := games.map powerOfGame
+ powers.foldl Nat.add 0
+
+open DayPart
+instance : Parse ⟨2, by simp⟩ (ι := List Game) where
+ parse := parse
+
+instance : Part ⟨2,_⟩ Parts.One (ι := List Game) (ρ := Nat) where
+ run := some ∘ part1
+
+instance : Part ⟨2,_⟩ Parts.Two (ι := List Game) (ρ := Nat) where
+ run := some ∘ part2
+
+def testInput : String :=
+"Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
+Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
+Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
+Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
+Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"
+
+ -- #eval part1 <$> parse testInput
+
+ -- #eval part2 <$> parse testInput