-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsolution.gleam
151 lines (133 loc) · 3.59 KB
/
solution.gleam
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
import gleam/int
import gleam/io
import gleam/list
import gleam/option.{None, Some}
import gleam/result
import gleam/set.{type Set}
import gleam/string
import internal/aoc_utils
import internal/dijkstra
import internal/point.{type Point}
pub fn main() {
let filename = "inputs/day18.txt"
let lines_result = aoc_utils.read_lines(from: filename)
case lines_result {
Ok(lines) -> {
// If the file was converting into a list of lines
// successfully then run each part of the problem
aoc_utils.run_part_and_print("Part 1", fn() { solve_p1(lines, 70, 1024) })
aoc_utils.run_part_and_print("Part 2", fn() { solve_p2(lines, 70, 1024) })
}
Error(_) -> io.println("Error reading file")
}
}
// Part 1
pub fn solve_p1(
lines: List(String),
size: Int,
fallen: Int,
) -> Result(String, String) {
let bytes =
parse(lines)
|> list.take(fallen)
|> set.from_list
let pqueue = dijkstra.push(dijkstra.new(), 0, #(0, 0), None)
use length <- result.map({
find_route_length(bytes, size, pqueue)
|> result.replace_error("Error in search")
})
length
|> int.to_string
}
// Part 2
pub fn solve_p2(
lines: List(String),
size: Int,
min_start: Int,
) -> Result(String, String) {
let bytes = parse(lines)
let blocker = find_blocking_piece(bytes, min_start, list.length(bytes), size)
Ok(int.to_string(blocker.0) <> "," <> int.to_string(blocker.1))
}
fn parse(lines: List(String)) -> List(Point) {
lines
|> list.map(fn(line) {
let vals = string.split(line, ",") |> list.map(int.parse)
case vals {
[Ok(x), Ok(y)] -> Ok(#(x, y))
_ -> Error(Nil)
}
})
|> result.values
}
// Dijkstra search.
fn find_route_length(
bytes: Set(Point),
size: Int,
pqueue: dijkstra.Queue(Point),
) -> Result(Int, Nil) {
use #(newpqueue, score, pos) <- result.try(dijkstra.pop(pqueue))
case pos == #(size, size) {
True -> {
// plot_route(newpqueue, bytes, size)
Ok(score)
}
False -> {
let next_points =
list.map(point.directions, point.add(_, pos))
|> list.filter(fn(p) {
p.0 >= 0 && p.0 <= size && p.1 >= 0 && p.1 <= size
})
|> list.filter(fn(p) { !set.contains(bytes, p) })
|> list.map(fn(p) { #(score + 1, p) })
let newpqueue = dijkstra.push_list(newpqueue, next_points, Some(pos))
find_route_length(bytes, size, newpqueue)
}
}
}
pub fn plot_route(
queue: dijkstra.Queue(Point),
bytes: Set(Point),
size: Int,
) -> Nil {
let path =
dijkstra.get_path(queue, #(size, size))
|> result.unwrap([])
|> set.from_list
list.each(list.range(0, size), fn(y) {
list.each(list.range(0, size), fn(x) {
case set.contains(bytes, #(x, y)), set.contains(path, #(x, y)) {
True, False -> io.print("#")
False, True -> io.print("O")
False, False -> io.print(" ")
True, True -> panic as "stepped in a byte"
}
})
io.println("")
})
}
// Do a binary search. When min pieces fall the path is open,
// when max falls the path is closed.
fn find_blocking_piece(
bytes: List(Point),
min: Int,
max: Int,
size: Int,
) -> Point {
let mid = { max - min } / 2 + min
case max == min + 1 {
True -> {
list.drop(bytes, min)
|> list.first
|> result.unwrap(#(0, 0))
}
False -> {
let byte_set = list.take(bytes, mid) |> set.from_list
let pqueue = dijkstra.push(dijkstra.new(), 0, #(0, 0), None)
case find_route_length(byte_set, size, pqueue) {
Error(_) -> find_blocking_piece(bytes, min, mid, size)
Ok(_) -> find_blocking_piece(bytes, mid, max, size)
}
}
}
}