@@ -12,6 +12,7 @@ private def blocksToString (blocks : Blocks) : String :=
12
12
| none => "."
13
13
| some id => toString id
14
14
b.foldl (· ++ ·) ""
15
+
15
16
#guard blocksToString #[none, none, some 1 , none, some 2 ] == "..1.2"
16
17
17
18
inductive ExpandMode where
@@ -31,7 +32,7 @@ private def expandDiskMap (diskMap : List Nat) : Blocks :=
31
32
private def nextFreeBlockOnOrAfter (i : Nat) (blocks : Blocks) : Nat :=
32
33
let rec loop (i : Nat) : Nat :=
33
34
if i < blocks.size then
34
- match blocks.get! i with
35
+ match blocks.getD i none with
35
36
| none => i
36
37
| some _ => loop (i + 1 )
37
38
else
@@ -43,7 +44,7 @@ private def nextFreeBlockOnOrAfter (i : Nat) (blocks : Blocks) : Nat :=
43
44
private def nextFileBlockOnOrBefore (i : Nat) (blocks : Blocks) : Nat :=
44
45
let rec loop (i : Nat) : Nat :=
45
46
if i > 0 then
46
- match blocks.get! i with
47
+ match blocks.getD i none with
47
48
| none => loop (i - 1 )
48
49
| some _ => i
49
50
else
@@ -62,14 +63,14 @@ private def swapBlocks (blocks : Blocks) (index1 index2 : Nat) : Blocks :=
62
63
#[some 0 , some 2 , none, none, none, some 1 ]
63
64
64
65
private def checksum (blocks : Blocks) : Int :=
65
- blocks.toList.enum.sumBy fun
66
+ blocks.toList.enum.sumBy λ
66
67
| (_, none) => 0
67
68
| (i, some id) => id * i
68
69
69
70
private def solvePart1 (diskMap : List Nat) : Int := Id.run do
70
71
let mut blocks := expandDiskMap diskMap
71
72
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
73
74
while nextFree < nextFile do
74
75
blocks := swapBlocks blocks nextFree nextFile
75
76
nextFree := nextFreeBlockOnOrAfter (nextFree + 1 ) blocks
@@ -82,8 +83,68 @@ def parseAndSolvePart1 (s : String): Except String Int := parseDiskMap s |>.map
82
83
83
84
-- Part 2
84
85
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
86
147
87
148
def parseAndSolvePart2 (s : String): Except String Int := parseDiskMap s |>.map solvePart2
88
149
89
- -- #guard parseAndSolvePart2 exampleInput == Except.ok -1
150
+ #guard parseAndSolvePart2 exampleInput == Except.ok 2858
0 commit comments