Skip to content

Commit e6a8c58

Browse files
committed
day 9 but very yikes
1 parent 8b9c4dd commit e6a8c58

File tree

2 files changed

+68
-7
lines changed

2 files changed

+68
-7
lines changed

Aoc2024/Day09/Main.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ def main : IO Unit := do
1616
IO.println s!"Example: {<- parseAndSolvePart2 exampleInput}"
1717
let answerPart2 <- parseAndSolvePart2 puzzleInput
1818
IO.println s!"Puzzle: {answerPart2}"
19-
assert! (answerPart2 == -1)
19+
assert! (answerPart2 == 6272188244509)

Aoc2024/Day09/Solve.lean

+67-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private def blocksToString (blocks : Blocks) : String :=
1212
| none => "."
1313
| some id => toString id
1414
b.foldl (· ++ ·) ""
15+
1516
#guard blocksToString #[none, none, some 1, none, some 2] == "..1.2"
1617

1718
inductive ExpandMode where
@@ -31,7 +32,7 @@ private def expandDiskMap (diskMap : List Nat) : Blocks :=
3132
private def nextFreeBlockOnOrAfter (i : Nat) (blocks : Blocks) : Nat :=
3233
let rec loop (i : Nat) : Nat :=
3334
if i < blocks.size then
34-
match blocks.get! i with
35+
match blocks.getD i none with
3536
| none => i
3637
| some _ => loop (i + 1)
3738
else
@@ -43,7 +44,7 @@ private def nextFreeBlockOnOrAfter (i : Nat) (blocks : Blocks) : Nat :=
4344
private def nextFileBlockOnOrBefore (i : Nat) (blocks : Blocks) : Nat :=
4445
let rec loop (i : Nat) : Nat :=
4546
if i > 0 then
46-
match blocks.get! i with
47+
match blocks.getD i none with
4748
| none => loop (i - 1)
4849
| some _ => i
4950
else
@@ -62,14 +63,14 @@ private def swapBlocks (blocks : Blocks) (index1 index2 : Nat) : Blocks :=
6263
#[some 0, some 2, none, none, none, some 1]
6364

6465
private def checksum (blocks : Blocks) : Int :=
65-
blocks.toList.enum.sumBy fun
66+
blocks.toList.enum.sumBy λ
6667
| (_, none) => 0
6768
| (i, some id) => id * i
6869

6970
private def solvePart1 (diskMap : List Nat) : Int := Id.run do
7071
let mut blocks := expandDiskMap diskMap
7172
let mut nextFree := nextFreeBlockOnOrAfter 0 blocks
72-
let mut nextFile := blocks.size - 1 -- assumption on input: ends with file blocks
73+
let mut nextFile := nextFileBlockOnOrBefore (blocks.size - 1) blocks
7374
while nextFree < nextFile do
7475
blocks := swapBlocks blocks nextFree nextFile
7576
nextFree := nextFreeBlockOnOrAfter (nextFree + 1) blocks
@@ -82,8 +83,68 @@ def parseAndSolvePart1 (s : String): Except String Int := parseDiskMap s |>.map
8283

8384
-- Part 2
8485

85-
private def solvePart2 (diskMap : List Nat) : Int := sorry
86+
private def swapBlockRange (blocks : Blocks) (index1 index2 length : Nat) : Blocks := Id.run do
87+
let mut blocks := blocks
88+
for i in [0:length] do
89+
blocks := swapBlocks blocks (index1 + i) (index2 + i)
90+
blocks
91+
92+
#guard swapBlockRange #[some 0, none, none, none, some 9, some 9, none] (index1 := 1) (index2 := 4) (length := 2) ==
93+
#[some 0, some 9, some 9, none, none, none, none]
94+
95+
private def getHighestId (blocks : Blocks) : IdNum :=
96+
blocks.foldl (fun max block => match block with
97+
| none => max
98+
| some id => Nat.max max id) 0
99+
100+
structure Range where
101+
index: Nat
102+
length: Nat
103+
deriving Repr, BEq
104+
105+
private def locateFile (blocks : Blocks) (id : IdNum) : Range :=
106+
let index := blocks.findIdx? (λ block => block == some id) |>.getD 0
107+
let length: Nat := Id.run do
108+
let mut length := 1
109+
while blocks.getD (index + length) none == some id do
110+
length := length + 1
111+
length
112+
{ index, length }
113+
114+
#guard locateFile #[none, some 0, some 0, none, none, none, some 1] 0 == { index := 1, length := 2 }
115+
116+
private def leftmostFreeSpace (blocks : Blocks) (length : Nat): Option Nat :=
117+
let rec loop (n : Nat) : Option Nat :=
118+
match n with
119+
| 0 => none
120+
| n' + 1 =>
121+
let i := blocks.size - n
122+
if blocks.toSubarray i (i + length) |>.all (· == none) then
123+
some i
124+
else
125+
loop n'
126+
loop blocks.size
127+
128+
#guard leftmostFreeSpace #[some 0, none, none, some 1, none, none, none, some 2] 2 == some 1
129+
#guard leftmostFreeSpace #[some 0, none, none, some 1, none, none, none, some 2] 3 == some 4
130+
131+
private def solvePart2 (diskMap : List Nat) : Int := Id.run do
132+
let mut blocks := expandDiskMap diskMap
133+
let mut currentId := getHighestId blocks
134+
while true do
135+
-- dbg_trace blocksToString blocks
136+
let range := locateFile blocks currentId
137+
let maybeSpace := leftmostFreeSpace blocks range.length
138+
match maybeSpace with
139+
| none => blocks := blocks
140+
| some spaceIndex =>
141+
if spaceIndex < range.index then
142+
blocks := swapBlockRange blocks spaceIndex range.index range.length
143+
if currentId == 0 then
144+
break
145+
currentId := currentId - 1
146+
checksum blocks
86147

87148
def parseAndSolvePart2 (s : String): Except String Int := parseDiskMap s |>.map solvePart2
88149

89-
-- #guard parseAndSolvePart2 exampleInput == Except.ok -1
150+
#guard parseAndSolvePart2 exampleInput == Except.ok 2858

0 commit comments

Comments
 (0)