Skip to content

Commit 0646add

Browse files
committed
day 10
1 parent 9033825 commit 0646add

File tree

9 files changed

+142
-25
lines changed

9 files changed

+142
-25
lines changed

Aoc2024/Day09/Parser.lean

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import Std
55
open Std.Internal.Parsec.String
66
open Std.Internal.Parsec
77

8-
private def digitToNat (b : Char) : Nat := b.toNat - '0'.toNat
9-
10-
private def diskMapParser : Parser (List Nat) := Array.toList <$> many (digitToNat <$> digit)
8+
private def diskMapParser : Parser (List Nat) := Array.toList <$> many (charDigitToNat <$> digit)
119

1210
def parseDiskMap : String -> Except String (List Nat) := diskMapParser.run
1311

Aoc2024/Day10/Examples.lean

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
def exampleInput :=
2-
"..."
2+
"89010123
3+
78121874
4+
87430965
5+
96549874
6+
45678903
7+
32019012
8+
01329801
9+
10456732"

Aoc2024/Day10/Main.lean

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ def main : IO Unit := do
77
IO.println "Part 1"
88
let exampleInput <- IO.FS.readFile "Aoc2024/Day10/inputs/example.txt"
99
let puzzleInput <- IO.FS.readFile "Aoc2024/Day10/inputs/input.txt"
10-
IO.println s!"Example: {<- parseAndSolvePart1 exampleInput}"
11-
let answerPart1 <- parseAndSolvePart1 puzzleInput
10+
IO.println s!"Example: {parseAndSolvePart1 exampleInput}"
11+
let answerPart1 := parseAndSolvePart1 puzzleInput
1212
IO.println s!"Puzzle: {answerPart1}"
13-
assert! (answerPart1 == -1)
13+
assert! (answerPart1 == 461)
1414
IO.println ""
1515
IO.println "Part 2"
16-
IO.println s!"Example: {<- parseAndSolvePart2 exampleInput}"
17-
let answerPart2 <- parseAndSolvePart2 puzzleInput
16+
IO.println s!"Example: {parseAndSolvePart2 exampleInput}"
17+
let answerPart2 := parseAndSolvePart2 puzzleInput
1818
IO.println s!"Puzzle: {answerPart2}"
19-
assert! (answerPart2 == -1)
19+
assert! (answerPart2 == 875)

Aoc2024/Day10/Parser.lean

+14-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ import Aoc2024.Day10.Examples
22
import Aoc2024.Day10.Types
33
import Aoc2024.Utils
44
import Std
5-
open Std.Internal.Parsec.String
6-
open Std.Internal.Parsec
5+
open Std (HashMap)
76

8-
private def inputParser : Parser (List Int) := sorry
7+
private def decorateWithCoordinates (s: String): List (Point × Char) := do
8+
let (y, line) <- s.splitOn "\n" |> .enum
9+
let (x, c) <- line.toList.enum
10+
pure ⟨⟨x, y⟩, c⟩
911

10-
def parseInput : String -> Except String (List Int) := inputParser.run
11-
12-
-- #guard parseInput exampleInput == Except.ok 42
12+
def parseHeightMap (s : String): PuzzleInput :=
13+
let decoratedChars := decorateWithCoordinates s
14+
let (xs, ys) := decoratedChars.map (·.1.toPair) |>.unzip
15+
let width := xs.max?.map (· + 1 |>.toNat) |>.getD 0
16+
let height := ys.max?.map (· + 1 |>.toNat) |>.getD 0
17+
let bounds: Rectangle := { topLeft := Point.origin, width, height }
18+
let heights: HashMap Point Height :=
19+
decoratedChars.foldl (init := HashMap.empty) λ m ⟨p, c⟩ => m.insert p (charDigitToNat c)
20+
{ bounds, heights }

Aoc2024/Day10/Solve.lean

+39-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,51 @@ import Aoc2024.Utils
22
import Aoc2024.Day10.Examples
33
import Aoc2024.Day10.Parser
44
import Aoc2024.Day10.Types
5+
open Std (HashSet HashMap)
56

67
-- Part 1
78

8-
private def solvePart1 (input : List Int) : Int := sorry
9+
private def Point.orthogonalNeighbours (p : Point): List Point :=
10+
let ⟨x, y⟩ := p
11+
[⟨x, y - 1⟩, ⟨x + 1, y⟩, ⟨x, y + 1⟩, ⟨x - 1, y⟩]
12+
13+
private def solvePart1 (input : PuzzleInput) : Int :=
14+
let heights := input.heights
15+
let trailheads := heights.invert.getD 0 []
16+
let getNeighbours (p : Point) (height : Height): List Point :=
17+
p.orthogonalNeighbours.filter (λ neighbour => heights.get? neighbour == some height)
18+
let score (trailhead : Point): Int :=
19+
let rec findReachablePeaks (p : Point) : Nat -> List Point
20+
| 0 => [p]
21+
| n + 1 =>
22+
let targetHeight := 9 - n
23+
let neighbours := getNeighbours p targetHeight
24+
neighbours.flatMap (findReachablePeaks · n)
25+
findReachablePeaks trailhead 9 |>.toSet.size
26+
trailheads.sumBy score
27+
28+
def parseAndSolvePart1 (s : String): Int := parseHeightMap s |> solvePart1
29+
30+
#guard parseAndSolvePart1 exampleInput == 36
931

10-
def parseAndSolvePart1 (s : String): Except String Int := parseInput s |>.map solvePart1
32+
-- Part 2
1133

12-
-- #guard parseAndSolvePart1 exampleInput == Except.ok -1
34+
private def getNeighbours (heights : HashMap Point Height) (p : Point) (height : Height): List Point :=
35+
p.orthogonalNeighbours.filter (λ neighbour => heights.get? neighbour == some height)
1336

14-
-- Part 2
37+
private partial def countPaths (heights : HashMap Point Height) (p : Point) (currentHeight : Height): Int :=
38+
match currentHeight with
39+
| 9 => 1
40+
| _ =>
41+
let nextHeight := currentHeight + 1
42+
getNeighbours heights p nextHeight |>.sumBy (countPaths heights · nextHeight)
1543

16-
private def solvePart2 (input : List Int) : Int := sorry
44+
private def solvePart2 (input : PuzzleInput) : Int :=
45+
let heights := input.heights
46+
let trailheads := heights.invert.getD 0 []
47+
let score (trailhead : Point): Int := countPaths heights trailhead 0
48+
trailheads.sumBy score
1749

18-
def parseAndSolvePart2 (s : String): Except String Int := parseInput s |>.map solvePart2
50+
def parseAndSolvePart2 (s : String): Int := parseHeightMap s |> solvePart2
1951

20-
-- #guard parseAndSolvePart2 exampleInput == Except.ok -1
52+
#guard parseAndSolvePart2 exampleInput == 81

Aoc2024/Day10/Types.lean

+9
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
11
import Aoc2024.Utils
2+
import Std
3+
open Std (HashMap)
4+
5+
abbrev Height := Nat
6+
7+
structure PuzzleInput where
8+
bounds : Rectangle
9+
heights : HashMap Point Height
10+
deriving Repr, Inhabited

Aoc2024/Day10/inputs/example.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
89010123
2+
78121874
3+
87430965
4+
96549874
5+
45678903
6+
32019012
7+
01329801
8+
10456732

Aoc2024/Day10/inputs/input.txt

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
6541001098012789610347890107654656710323
2+
7832102127643898701256521218323465891410
3+
8996543034556789650987434309012534892565
4+
3887689678965876501874345892105621763676
5+
4305678563456903416765676756898760654980
6+
5214107852107812321254382347872108901221
7+
6543236943056921010341291078963457654338
8+
7896545987045430010980012569454968983549
9+
3217830656189899121676101430356879892678
10+
2106921043210778234585232321267898761432
11+
3478854430345665056798743410456901050501
12+
4569763521012552143895654501345012347670
13+
3654012678903443212104309690432167898981
14+
2783656987654874908765218781201254012567
15+
1092347897893965889034765670387063013498
16+
1001298756102456776121874989496122110901
17+
2310891043201307655430923876565434325892
18+
3456780103011218967649810189410145456743
19+
2561078212320989858236702107320236787654
20+
1232569343423874749145893678741199899873
21+
0343454358514565632098704569632087684562
22+
0456789969609034501347612189323456893001
23+
1499876878798123101256543079012548762110
24+
2387905462687678871212344568187659450223
25+
3456012301056549960305650127691098321054
26+
3456732102345832154454781034540107650169
27+
2369847898738981023763692321121256743278
28+
1078456654567670119832103400012349894361
29+
0012387763456543208041076510123412765010
30+
7650196892565454589107889623296503854321
31+
8943256781074303673236908774387654983432
32+
8912965890985210984365219985345015676541
33+
7607834187866789875434308776236723498650
34+
6506543045679012766923105698109894567743
35+
5410432134988703457810014567056210754892
36+
0322345028767845893456723459847349889701
37+
1201276719454936712679801210738256776545
38+
2450989805103221604589752345629145480230
39+
2347823456012120113298943238710076591121
40+
1056910147893012320107654109656789432012

Aoc2024/Utils.lean

+17-2
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,23 @@ namespace Std.HashSet
169169
def isSubsetOf [Hashable α] [BEq α] (xs: HashSet α) (ys: HashSet α) : Bool :=
170170
xs.all ys.contains
171171

172+
#guard [1, 3].toSet.isSubsetOf [1, 2, 3].toSet == true
173+
#guard [1, 2, 3].toSet.isSubsetOf [1, 3].toSet == false
174+
172175
end Std.HashSet
173176

174-
#guard [1, 3].toSet.isSubsetOf [1, 2, 3].toSet == true
175-
#guard [1, 2, 3].toSet.isSubsetOf [1, 3].toSet == false
177+
namespace Std.HashMap
178+
179+
def foldl [BEq α] [Hashable α] (m : HashMap α β) (init : γ) (f : γ -> α -> β -> γ) : γ :=
180+
m.toList.foldl (init := init) λ acc (k, v) => f acc k v
181+
182+
def invert [BEq β] [Hashable β] [BEq α] [Hashable α] (m : HashMap α β) : HashMap β (List α) :=
183+
m.foldl (init := HashMap.empty) λ acc k v =>
184+
match acc.get? v with
185+
| some ks => acc.insert v (k :: ks)
186+
| none => acc.insert v [k]
187+
188+
end Std.HashMap
176189

177190
namespace String
178191
def lines (s : String) : List String := s.splitOn "\n"
@@ -252,3 +265,5 @@ def Rectangle.allPoints (r : Rectangle) : List Point := do
252265
return { x := x, y := y }
253266
#guard Rectangle.allPoints { topLeft := Point.origin, width := 2, height := 2 } ==
254267
[{ x := 0, y := 0 }, { x := 0, y := 1 }, { x := 1, y := 0 }, { x := 1, y := 1 }]
268+
269+
def charDigitToNat (c : Char) : Nat := c.toNat - '0'.toNat

0 commit comments

Comments
 (0)