-
Notifications
You must be signed in to change notification settings - Fork 0
/
day_03.gleam
135 lines (117 loc) · 3.15 KB
/
day_03.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
import gleam/int
import gleam/list
import gleam/option.{Some}
import gleam/pair
import gleam/regex.{Match}
import gleam/result
import gleam/string
type Number {
Number(x1: Int, x2: Int, y: Int, value: Int)
}
type Symbol {
Symbol(x: Int, y: Int, value: String)
}
pub fn part_1(input: String) -> Int {
let numbers = get_numbers(input)
let symbols = get_symbols(input)
numbers
|> list.filter_map(fn(number) {
let neighbor_coords =
get_neighbor_coords(x1: number.x1, x2: number.x2, y: number.y)
let has_any_symbol_neighbors =
symbols
|> list.any(fn(symbol) {
neighbor_coords |> list.contains(#(symbol.x, symbol.y))
})
case has_any_symbol_neighbors {
True -> Ok(number.value)
False -> Error(Nil)
}
})
|> int.sum
}
pub fn part_2(input: String) -> Int {
let numbers = get_numbers(input)
let symbols = get_symbols(input)
symbols
|> list.filter(fn(symbol) { symbol.value == "*" })
|> list.filter_map(fn(gear) {
let number_neighbors =
numbers
|> list.filter_map(fn(number) {
let is_neighbor =
True
&& gear.x >= number.x1 - 1
&& gear.x <= number.x2 + 1
&& gear.y >= number.y - 1
&& gear.y <= number.y + 1
case is_neighbor {
True -> Ok(number.value)
False -> Error(Nil)
}
})
case number_neighbors {
[a, b] -> Ok(a * b)
_ -> Error(Nil)
}
})
|> int.sum
}
fn get_numbers(schematic: String) -> List(Number) {
schematic
|> string.trim
|> string.split("\n")
|> list.index_map(fn(row, y) {
let assert Ok(re) = regex.from_string("([^\\d]*)(\\d+)")
regex.scan(re, row)
|> list.map_fold(0, fn(prev_match_length, match) {
let assert Match(submatches: [prefix, Some(number_string)], ..) = match
let prefix_length = prefix |> option.unwrap("") |> string.length
let number_length = number_string |> string.length
let x1 = prev_match_length + prefix_length
let x2 = x1 + number_length - 1
let assert Ok(number_value) =
number_string
|> string.to_graphemes
|> list.filter_map(int.parse)
|> int.undigits(10)
let number = Number(x1:, x2:, y:, value: number_value)
#(prev_match_length + prefix_length + number_length, number)
})
|> pair.second
})
|> list.flatten
}
fn get_symbols(schematic: String) -> List(Symbol) {
schematic
|> string.trim
|> string.split("\n")
|> list.index_map(fn(row, y) {
row
|> string.to_graphemes
|> list.index_map(fn(char, x) {
case char != "." && char |> int.parse |> result.is_error {
True -> Ok(Symbol(x:, y:, value: char))
False -> Error(Nil)
}
})
|> result.values
})
|> list.flatten
}
/// Returns `List(#(x, y))`
fn get_neighbor_coords(x1 x1: Int, x2 x2: Int, y y: Int) -> List(#(Int, Int)) {
[
// Top-left, left and bottom-left:
#(x1 - 1, y - 1),
#(x1 - 1, y),
#(x1 - 1, y + 1),
// Top-right, right and bottom-right:
#(x2 + 1, y - 1),
#(x2 + 1, y),
#(x2 + 1, y + 1),
// Ups and downs:
..list.range(x1, x2)
|> list.flat_map(fn(x) { [#(x, y - 1), #(x, y + 1)] })
]
}