-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday15.roc
286 lines (247 loc) · 8.6 KB
/
day15.roc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
app [part1, part2] {
pf: platform "https://github.com/ostcar/roc-aoc-platform/releases/download/v0.0.8/lhFfiil7mQXDOB6wN-jduJQImoT8qRmoiNHDB4DVF9s.tar.br",
}
Position : { row : U64, col : U64 }
Direction : [Top, Right, Bottom, Left]
Map : Dict Position [Wall, Box]
part1 = \input ->
input
|> parse?
|> walk
|> mapToNumber
|> Num.toStr
|> Ok
walk = \{ map, start, moves } ->
moves
|> List.walk { map, cur: start } walkStep
|> \{ map: endMap } -> endMap
walkStep : { map : Map, cur : Position }, Direction -> { map : Map, cur : Position }
walkStep = \{ map, cur }, direction ->
nextPos = nextPosition cur direction
when Dict.get map nextPos is
Err KeyNotFound -> { map, cur: nextPos }
Ok Wall -> { map, cur }
Ok Box ->
when walkBoxes map nextPos direction is
Err Blocked -> { map, cur }
Ok freePos ->
map
|> Dict.remove nextPos
|> Dict.insert freePos Box
|> \newMap -> { map: newMap, cur: nextPos }
nextPosition = \{ col, row }, direction ->
when direction is
Top -> { row: row - 1, col }
Right -> { col: col + 1, row }
Bottom -> { row: row + 1, col }
Left -> { col: col - 1, row }
walkBoxes : Map, Position, Direction -> Result Position [Blocked]
walkBoxes = \map, cur, direction ->
when Dict.get map cur is
Err KeyNotFound -> Ok cur
Ok Wall -> Err Blocked
Ok Box ->
walkBoxes map (nextPosition cur direction) direction
mapToNumber = \map ->
map
|> Dict.walk 0 \acc, { row, col }, elem ->
when elem is
Box -> acc + row * 100 + col
Wall -> acc
part2 = \input ->
input
|> parse?
|> modifyForPart2
|> walkPart2
|> mapToNumber
|> Num.toStr
|> Ok
modifyForPart2 = \{ map, start: { row: startRow, col: startCol }, moves } ->
newMap =
map
|> Dict.walk (Dict.withCapacity (Dict.len map)) \newDict, { row, col: oldCol }, value ->
newDict
|> Dict.insert { row, col: oldCol * 2 } value
|> \d ->
when value is
Wall -> d |> Dict.insert { row, col: oldCol * 2 + 1 } value
Box -> d
newStart = { row: startRow, col: startCol * 2 }
{ map: newMap, start: newStart, moves }
walkPart2 = \{ map, start, moves } ->
moves
|> List.walk { map, cur: start } walkStepPart2
|> \{ map: endMap } -> endMap
walkStepPart2 : { map : Map, cur : Position }, Direction -> { map : Map, cur : Position }
walkStepPart2 = \{ map, cur }, direction ->
nextPos = nextPosition cur direction
when Dict.get map nextPos is
Ok Wall -> { map, cur }
Ok Box ->
when walkAndUpdateBoxes map nextPos direction is
Ok newMap ->
{ map: newMap, cur: nextPos }
Err Blocked ->
{ map, cur }
Err KeyNotFound ->
nextPosLeft = nextPosition nextPos Left
if direction != Right && Dict.get map nextPosLeft == Ok Box then
when walkAndUpdateBoxes map nextPosLeft direction is
Ok newMap ->
{ map: newMap, cur: nextPos }
Err Blocked ->
{ map, cur }
else
{ map, cur: nextPos }
walkAndUpdateBoxes = \map, cur, direction ->
mayBoxIndexList =
when direction is
Left | Right ->
findBoxesLeftRight map cur direction []
Top | Bottom ->
findBoxesTopBottom map [cur] direction [cur]
mayBoxIndexList
|> Result.map \boxIndexList ->
nextMap =
boxIndexList
|> List.walk map \acc, index ->
acc |> Dict.remove index
boxIndexList
|> List.walk nextMap \acc, index ->
acc |> Dict.insert (index |> nextPosition direction) Box
findBoxesLeftRight : Map, Position, Direction, List Position -> Result (List Position) _
findBoxesLeftRight = \map, cur, direction, result ->
when Dict.get map cur is
Ok Box ->
nextPos = cur |> nextPosition direction
if direction == Left then
nextPosLeft = cur |> nextPosition direction
nextPosVal = Dict.get map nextPosLeft
if nextPosVal == Ok Wall then
Err Blocked
else
findBoxesLeftRight map (nextPos |> nextPosition direction) direction (result |> List.append cur)
else
findBoxesLeftRight map (nextPos |> nextPosition direction) direction (result |> List.append cur)
Err KeyNotFound ->
Ok result
Ok Wall ->
if direction == Left then
Ok result
else
Err Blocked
findBoxesTopBottom = \map, indexList, direction, result ->
when indexList is
[] -> Ok result
[first, .. as rest] ->
nextPos = nextPosition first direction
nextPosLeft = nextPosition nextPos Left
nextPosRight = nextPosition nextPos Right
val = Dict.get map nextPos
valLeft = Dict.get map nextPosLeft
valRight = Dict.get map nextPosRight
if val == Ok Wall || valRight == Ok Wall then
Err Blocked
else
newBoxes =
[(nextPosLeft, valLeft), (nextPos, val), (nextPosRight, valRight)]
|> List.keepIf \(_, v) -> v == Ok Box
|> List.map \(p, _) -> p
findBoxesTopBottom map (rest |> List.concat newBoxes) direction (result |> List.concat newBoxes)
parse : Str -> Result { map : Map, start : Position, moves : List Direction } _
parse = \input ->
when input |> Str.splitOn "\n\n" is
[inputMap, inputMoves] ->
(map, start) = parseMap? inputMap
moves = parseMoves? inputMoves
Ok { map, start, moves }
_ ->
Err InvalidInput
parseMap = \input ->
input
|> Str.walkUtf8 { start: Err NoStart, row: 0, col: 0, map: Dict.empty {} } \acc, elem ->
row = acc.row
col = acc.col
when elem is
'#' -> { acc & map: Dict.insert acc.map { row, col } Wall, col: col + 1 }
'O' -> { acc & map: Dict.insert acc.map { row, col } Box, col: col + 1 }
'@' -> { acc & start: Ok { row, col }, col: col + 1 }
'\n' -> { acc & row: row + 1, col: 0 }
_ -> { acc & col: col + 1 }
|> \{ start, map } ->
start
|> Result.map \pos -> (map, pos)
parseMoves : Str -> Result (List Direction) _
parseMoves = \input ->
input
|> Str.toUtf8
|> List.walkTry (List.withCapacity (Str.countUtf8Bytes input)) \acc, elem ->
when elem is
'^' -> acc |> List.append Top |> Ok
'>' -> acc |> List.append Right |> Ok
'v' -> acc |> List.append Bottom |> Ok
'<' -> acc |> List.append Left |> Ok
'\n' -> acc |> Ok
_ -> Err InvalidMove
exampleLarge =
"""
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#..O@..O.#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
"""
exampleSmall =
"""
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
<^^>>>vv<v>>v<<
"""
expect
got = part1 exampleLarge
expected = Ok "10092"
got == expected
expect
got = part1 exampleSmall
expected = Ok "2028"
got == expected
expect
example =
"""
#######
#...#.#
#.....#
#..OO@#
#..O..#
#.....#
#######
<vv<<^^<<^^
"""
got = part2 example
expected = Ok "618"
got == expected
expect
got = part2 exampleLarge
expected = Ok "9021"
got == expected