+192
src/aoc_2025/day_10.gleam
+192
src/aoc_2025/day_10.gleam
···
1
+
import crew
2
+
import gleam/erlang/process
3
+
import gleam/int
4
+
import gleam/io
5
+
import gleam/list
6
+
import gleam/result
7
+
import gleam/set.{type Set}
8
+
import gleam/string
9
+
10
+
pub type Joltages {
11
+
Joltages(joltages: List(Int))
12
+
}
13
+
14
+
pub type State {
15
+
State(lamps: List(Bool))
16
+
}
17
+
18
+
pub type Button {
19
+
Button(toggles: List(Int))
20
+
}
21
+
22
+
pub type Machine {
23
+
Machine(target: State, buttons: List(Button), joltages: Joltages)
24
+
}
25
+
26
+
pub fn parse(input: String) -> List(Machine) {
27
+
input
28
+
|> string.split("\n")
29
+
|> list.map(fn(line) {
30
+
line
31
+
|> string.split(" ")
32
+
|> list.fold(Machine(State([]), [], Joltages([])), fn(acc, item) {
33
+
let Machine(state, buttons, joltages) = acc
34
+
35
+
case item {
36
+
"[" <> state ->
37
+
state
38
+
|> string.drop_end(1)
39
+
|> parse_state([])
40
+
|> Machine(buttons, joltages)
41
+
42
+
"(" <> button ->
43
+
button
44
+
|> string.drop_end(1)
45
+
|> parse_int_list
46
+
|> Button
47
+
|> fn(button) { Machine(state, [button, ..buttons], joltages) }
48
+
49
+
"{" <> joltages ->
50
+
joltages
51
+
|> string.drop_end(1)
52
+
|> parse_int_list
53
+
|> Joltages
54
+
|> fn(joltages) { Machine(state, buttons, joltages) }
55
+
56
+
"" -> acc
57
+
58
+
other -> {
59
+
echo other
60
+
panic as "huh"
61
+
}
62
+
}
63
+
})
64
+
})
65
+
}
66
+
67
+
fn parse_state(state_string: String, acc) {
68
+
case state_string {
69
+
"." <> rest -> parse_state(rest, [False, ..acc])
70
+
"#" <> rest -> parse_state(rest, [True, ..acc])
71
+
_ -> State(acc |> list.reverse)
72
+
}
73
+
}
74
+
75
+
fn parse_int_list(button_string: String) {
76
+
button_string
77
+
|> string.split(",")
78
+
|> list.map(int.parse)
79
+
|> result.values
80
+
}
81
+
82
+
pub fn pt_1(input: List(Machine)) {
83
+
input
84
+
|> list.fold(0, fn(sum, machine) {
85
+
sum + pt_1_loop(machine, [#(machine.target, 0)], set.new())
86
+
})
87
+
}
88
+
89
+
fn pt_1_loop(
90
+
machine: Machine,
91
+
last_states: List(#(State, Int)),
92
+
next_states: Set(#(State, Int)),
93
+
) {
94
+
case last_states {
95
+
[] -> pt_1_loop(machine, next_states |> set.to_list, set.new())
96
+
[#(state, num), ..rest] -> {
97
+
// all off?
98
+
case state.lamps |> list.all(fn(lamp) { !lamp }) {
99
+
True -> num
100
+
False -> {
101
+
// click all buttons on this state
102
+
machine.buttons
103
+
|> list.fold(next_states, fn(next_states, button) {
104
+
next_states
105
+
|> set.insert(#(click_lamp_button(button, state), num + 1))
106
+
})
107
+
|> pt_1_loop(machine, rest, _)
108
+
}
109
+
}
110
+
}
111
+
}
112
+
}
113
+
114
+
fn click_lamp_button(button: Button, state: State) {
115
+
state.lamps
116
+
|> list.index_map(fn(lamp, index) {
117
+
case button.toggles |> list.contains(index) {
118
+
True -> !lamp
119
+
False -> lamp
120
+
}
121
+
})
122
+
|> State
123
+
}
124
+
125
+
pub fn pt_2(input: List(Machine)) {
126
+
let pool_name = process.new_name("worker_pool")
127
+
let assert Ok(_) =
128
+
crew.new(pool_name, run_pt_2_loop_for_machine)
129
+
|> crew.fixed_size(input |> list.length)
130
+
|> crew.start()
131
+
132
+
input
133
+
|> crew.call_parallel(pool_name, 120_000, _)
134
+
|> list.fold(0, int.add)
135
+
}
136
+
137
+
fn run_pt_2_loop_for_machine(machine: Machine) {
138
+
pt_2_loop(
139
+
machine,
140
+
[
141
+
#(
142
+
list.repeat(0, times: list.length(machine.joltages.joltages))
143
+
|> Joltages,
144
+
0,
145
+
),
146
+
],
147
+
set.new(),
148
+
)
149
+
|> echo
150
+
}
151
+
152
+
fn pt_2_loop(
153
+
machine: Machine,
154
+
last_joltages: List(#(Joltages, Int)),
155
+
next_joltages: Set(#(Joltages, Int)),
156
+
) {
157
+
case last_joltages {
158
+
[] -> pt_2_loop(machine, next_joltages |> set.to_list, set.new())
159
+
160
+
[#(joltages, num), ..rest] if num > 100 -> {
161
+
echo machine
162
+
163
+
num
164
+
}
165
+
[#(joltages, num), ..rest] -> {
166
+
// all correct?
167
+
case joltages.joltages == machine.joltages.joltages {
168
+
True -> num
169
+
False -> {
170
+
// click all buttons on this state
171
+
machine.buttons
172
+
|> list.fold(next_joltages, fn(next_joltages, button) {
173
+
next_joltages
174
+
|> set.insert(#(click_joltage_button(button, joltages), num + 1))
175
+
})
176
+
|> pt_2_loop(machine, rest, _)
177
+
}
178
+
}
179
+
}
180
+
}
181
+
}
182
+
183
+
fn click_joltage_button(button: Button, joltages: Joltages) -> Joltages {
184
+
joltages.joltages
185
+
|> list.index_map(fn(joltage, index) {
186
+
case button.toggles |> list.contains(index) {
187
+
True -> joltage + 1
188
+
False -> joltage
189
+
}
190
+
})
191
+
|> Joltages
192
+
}
+195
src/aoc_2025/day_8.gleam
+195
src/aoc_2025/day_8.gleam
···
1
+
import gleam/bool
2
+
import gleam/dict
3
+
import gleam/float
4
+
import gleam/int
5
+
import gleam/list
6
+
import gleam/option.{None, Some}
7
+
import gleam/pair
8
+
import gleam/string
9
+
10
+
pub type Position {
11
+
Position(x: Int, y: Int, z: Int)
12
+
}
13
+
14
+
type Edge {
15
+
Edge(length: Int, a: Position, b: Position)
16
+
}
17
+
18
+
fn equal_edge(edge_a, edge_b) {
19
+
let Edge(_, a1, b1) = edge_a
20
+
let Edge(_, a2, b2) = edge_b
21
+
22
+
a1 == a2 && b1 == b2 || a1 == b2 && a2 == b1
23
+
}
24
+
25
+
fn connecting_edge(edge_a, edge_b) {
26
+
let Edge(_, a1, b1) = edge_a
27
+
let Edge(_, a2, b2) = edge_b
28
+
29
+
a1 == a2 || b1 == b2 || a1 == b2 || b1 == a2
30
+
}
31
+
32
+
pub fn parse(input: String) {
33
+
input
34
+
|> string.split("\n")
35
+
|> list.map(string.split(_, ","))
36
+
|> list.map(fn(coords) {
37
+
case coords {
38
+
[x, y, z] -> {
39
+
let assert Ok(x) = int.parse(x)
40
+
let assert Ok(y) = int.parse(y)
41
+
let assert Ok(z) = int.parse(z)
42
+
Position(x:, y:, z:)
43
+
}
44
+
_ -> panic as "Invalid input"
45
+
}
46
+
})
47
+
}
48
+
49
+
pub fn pt_1(input: List(Position)) {
50
+
let distances =
51
+
input
52
+
|> list.fold(dict.new(), fn(distances, point) {
53
+
list.fold(input, distances, fn(distances, npoint) {
54
+
let dist = distance_3d(point, npoint)
55
+
dict.upsert(distances, point, fn(item) {
56
+
case item {
57
+
Some(item) -> [Edge(dist, point, npoint), ..item]
58
+
_ -> [Edge(dist, point, npoint)]
59
+
}
60
+
})
61
+
})
62
+
})
63
+
|> dict.map_values(fn(key, value) {
64
+
list.filter(value, fn(item) { item.length != 0 })
65
+
})
66
+
67
+
let circuits = dict.new()
68
+
pt_1_loop(distances, circuits, 1)
69
+
// |> echo
70
+
|> dict.values()
71
+
|> list.map(fn(circuit) {
72
+
list.fold(circuit, [], fn(acc, edge) {
73
+
let is_in_acc =
74
+
acc
75
+
|> list.any(fn(in_acc) { equal_edge(in_acc, edge) })
76
+
case is_in_acc {
77
+
True -> acc
78
+
False -> [edge, ..acc]
79
+
}
80
+
})
81
+
})
82
+
|> list.map(list.length)
83
+
|> list.sort(int.compare)
84
+
|> echo
85
+
|> list.take(3)
86
+
|> echo
87
+
|> list.reduce(fn(a, b) { a * b })
88
+
// todo as "part 1 not implemented"
89
+
}
90
+
91
+
fn pt_1_loop(
92
+
distances: dict.Dict(Position, List(Edge)),
93
+
circuits: dict.Dict(Int, List(Edge)),
94
+
index_multi: Int,
95
+
) -> dict.Dict(Int, List(Edge)) {
96
+
let distances =
97
+
distances
98
+
// filter out already assigned edges
99
+
|> dict.map_values(fn(_, edges) {
100
+
let circuits =
101
+
circuits
102
+
|> dict.values
103
+
|> list.flatten
104
+
105
+
edges
106
+
|> list.filter(fn(edge) {
107
+
circuits
108
+
|> list.any(fn(in_circuit) { equal_edge(edge, in_circuit) })
109
+
|> bool.negate()
110
+
})
111
+
})
112
+
113
+
let remaining = distances |> dict.values() |> list.flatten() |> list.length()
114
+
115
+
case remaining == 0 {
116
+
True -> circuits
117
+
False -> {
118
+
pt_1_loop(
119
+
distances,
120
+
pt_1_loop_inner(distances, circuits, index_multi),
121
+
index_multi * 10,
122
+
)
123
+
}
124
+
}
125
+
}
126
+
127
+
fn pt_1_loop_inner(distances, circuits, index_multi) {
128
+
distances
129
+
|> dict.fold([], fn(smallest, _, distances) {
130
+
let closest = closest(distances)
131
+
[closest, ..smallest]
132
+
})
133
+
|> list.filter_map(fn(item) { item })
134
+
// get shortest first
135
+
|> list.sort(fn(a, b) { int.compare(a.length, b.length) })
136
+
// make sure there are no self referencing edges
137
+
|> list.filter(fn(a) { a.length != 0 })
138
+
|> list.index_fold(circuits, fn(circuits, edge, index) {
139
+
// are there any already existing circuits?
140
+
let connected =
141
+
dict.filter(circuits, fn(key, value) {
142
+
list.any(value, fn(item) { connecting_edge(item, edge) })
143
+
})
144
+
145
+
let index = case connected |> dict.keys() {
146
+
// yes? add to it
147
+
[index, ..] -> index
148
+
// no? add to current index
149
+
_ -> index * index_multi
150
+
}
151
+
dict_append(circuits, index, edge)
152
+
})
153
+
}
154
+
155
+
fn dict_append(dict, key, item) {
156
+
dict.upsert(dict, key, fn(in_dict) {
157
+
case in_dict {
158
+
Some(in_dict) -> [item, ..in_dict] |> list.unique()
159
+
None -> [item]
160
+
}
161
+
})
162
+
}
163
+
164
+
fn closest(distances: List(Edge)) {
165
+
list.fold(distances, [], fn(smallest, item) {
166
+
case smallest {
167
+
[Edge(0, _, _)] | [] -> [item]
168
+
[Edge(distance, _, _)] if distance > item.length -> [item]
169
+
[Edge(distance, _, _)] if distance <= item.length -> smallest
170
+
_ -> todo as "huh"
171
+
}
172
+
})
173
+
|> list.first()
174
+
}
175
+
176
+
fn distance_3d(point_a, point_b) {
177
+
let Position(x1, y1, z1) = point_a
178
+
let Position(x2, y2, z2) = point_b
179
+
180
+
let xdistance = dist_1d(x1, x2)
181
+
let ydistance = dist_1d(y1, y2)
182
+
let zdistance = dist_1d(z1, z2)
183
+
184
+
xdistance + ydistance + zdistance
185
+
}
186
+
187
+
fn dist_1d(c1, c2) {
188
+
let diff = c2 - c1
189
+
let dist = diff * diff
190
+
dist
191
+
}
192
+
193
+
pub fn pt_2(input: List(Position)) {
194
+
todo as "part 2 not implemented"
195
+
}
+242
src/aoc_2025/day_9.gleam
+242
src/aoc_2025/day_9.gleam
···
1
+
import gleam/float
2
+
import gleam/int
3
+
import gleam/list
4
+
import gleam/set
5
+
import gleam/string
6
+
7
+
pub fn parse(input: String) {
8
+
input
9
+
|> string.split("\n")
10
+
|> list.map(fn(line) {
11
+
let assert [x, y] = string.split(line, ",")
12
+
let assert Ok(x) = int.parse(x)
13
+
let assert Ok(y) = int.parse(y)
14
+
Point(x:, y:)
15
+
})
16
+
}
17
+
18
+
pub type Point {
19
+
Point(x: Int, y: Int)
20
+
}
21
+
22
+
pub type FloatPoint {
23
+
FloatPoint(x: Float, y: Float)
24
+
}
25
+
26
+
pub type Area {
27
+
Area(a: Point, b: Point, area: Int)
28
+
}
29
+
30
+
pub type Area4Point {
31
+
Area4Point(
32
+
a: FloatPoint,
33
+
b: FloatPoint,
34
+
c: FloatPoint,
35
+
d: FloatPoint,
36
+
area: Float,
37
+
)
38
+
}
39
+
40
+
pub fn pt_1(input: List(Point)) {
41
+
input
42
+
|> list.fold(set.new(), fn(areas, point_a) {
43
+
input
44
+
|> list.fold(areas, fn(areas, point_b) {
45
+
let area =
46
+
{ int.absolute_value(point_a.x - point_b.x) + 1 }
47
+
* { int.absolute_value(point_a.y - point_b.y) + 1 }
48
+
49
+
areas
50
+
|> set.insert(Area(point_a, point_b, area))
51
+
})
52
+
})
53
+
|> set.fold(0, fn(acc, area) {
54
+
case area {
55
+
Area(a:, b:, area:) if area > acc -> area
56
+
_ -> acc
57
+
}
58
+
})
59
+
}
60
+
61
+
pub fn pt_2(input: List(Point)) {
62
+
// echo intersect(#(Point(0, 0), Point(10, 10)), #(Point(0, 5), Point(0, 2)))
63
+
64
+
// let mid_x = { largest_x - smallest_x } / 2
65
+
// |> echo
66
+
// let mid_y = { largest_y - smallest_y } / 2
67
+
// |> echo
68
+
69
+
let input =
70
+
input
71
+
|> list.map(fn(point) {
72
+
FloatPoint(int.to_float(point.x), int.to_float(point.y))
73
+
})
74
+
75
+
input
76
+
|> list.fold(set.new(), fn(acc, point_a) {
77
+
input
78
+
|> list.fold(acc, fn(acc, point_b) {
79
+
case point_a, point_b {
80
+
point_a, point_b if point_a == point_b -> acc
81
+
82
+
_, _ -> acc |> set.insert(other_corners(point_a, point_b))
83
+
}
84
+
})
85
+
})
86
+
|> set.filter(fn(area) {
87
+
let Area4Point(_, _, c, d, _) = area
88
+
let assert Ok(first) = list.first(input)
89
+
90
+
let input = list.append(input, [first])
91
+
92
+
// case a {
93
+
// FloatPoint(x:, y:) if x == 11.0 && y == 1.0 && area.area == 50.0 -> {
94
+
// echo area
95
+
// echo count_intersections(of: c, in: input, acc: 0) as "c"
96
+
// echo count_intersections(of: d, in: input, acc: 0) as "d"
97
+
// Nil
98
+
// }
99
+
// _ -> Nil
100
+
// }
101
+
102
+
count_intersections(of: c, in: input, acc: 0) |> int.is_odd()
103
+
&& count_intersections(of: d, in: input, acc: 0) |> int.is_odd()
104
+
// #(
105
+
// area,
106
+
// count_intersections(of: c, in: input, acc: 0),
107
+
// count_intersections(of: d, in: input, acc: 0),
108
+
// )
109
+
// False
110
+
})
111
+
|> echo
112
+
|> set.fold(0.0, fn(acc, area) {
113
+
case area {
114
+
Area4Point(a: _, b: _, c: _, d: _, area:) if area >. acc -> area
115
+
_ -> acc
116
+
}
117
+
})
118
+
// |> echo
119
+
}
120
+
121
+
fn count_intersections(
122
+
of intersecting_point: FloatPoint,
123
+
in all_points: List(FloatPoint),
124
+
acc sum: Int,
125
+
) {
126
+
case all_points {
127
+
[] | [_] -> sum
128
+
[a, b, ..rest] -> {
129
+
case a {
130
+
a if a == intersecting_point -> 1
131
+
_ -> {
132
+
case
133
+
intersect(
134
+
line: #(FloatPoint(0.0, 0.0), intersecting_point),
135
+
on_line: #(a, b),
136
+
)
137
+
{
138
+
True -> {
139
+
// echo string.inspect(a) <> " | " <> string.inspect(b) as "a-b, true"
140
+
count_intersections(intersecting_point, [b, ..rest], sum + 1)
141
+
}
142
+
False -> count_intersections(intersecting_point, [b, ..rest], sum)
143
+
}
144
+
}
145
+
}
146
+
}
147
+
148
+
s -> {
149
+
echo s
150
+
panic as "huh"
151
+
}
152
+
}
153
+
}
154
+
155
+
fn other_corners(point_a: FloatPoint, point_b: FloatPoint) {
156
+
let area =
157
+
{ float.absolute_value(point_a.x -. point_b.x) +. 1.0 }
158
+
*. { float.absolute_value(point_a.y -. point_b.y) +. 1.0 }
159
+
160
+
Area4Point(
161
+
point_a,
162
+
point_b,
163
+
FloatPoint(point_a.x, point_b.y),
164
+
FloatPoint(point_b.x, point_a.y),
165
+
area,
166
+
)
167
+
}
168
+
169
+
fn intersect(
170
+
line line_a: #(FloatPoint, FloatPoint),
171
+
on_line line_b: #(FloatPoint, FloatPoint),
172
+
) {
173
+
let #(FloatPoint(x1, y1), FloatPoint(x2, y2)) = line_a
174
+
let #(FloatPoint(x3, y3), FloatPoint(x4, y4)) = line_b
175
+
176
+
let denominator = { x1 -. x2 } *. { y3 -. y4 } -. { y1 -. y2 } *. { x3 -. x4 }
177
+
178
+
case denominator {
179
+
0.0 -> False
180
+
_ -> {
181
+
let px =
182
+
{
183
+
{ x1 *. y2 -. y1 *. x2 }
184
+
*. { x3 -. x4 }
185
+
-. { x1 -. x2 }
186
+
*. { x3 *. y4 -. y3 *. x4 }
187
+
}
188
+
/. denominator
189
+
190
+
let py =
191
+
{
192
+
{ x1 *. y2 -. y1 *. x2 }
193
+
*. { y3 -. y4 }
194
+
-. { y1 -. y2 }
195
+
*. { x3 *. y4 -. y3 *. x4 }
196
+
}
197
+
/. denominator
198
+
199
+
echo string.inspect(line_a)
200
+
<> " intersects "
201
+
<> string.inspect(line_b)
202
+
<> " at "
203
+
<> string.inspect(FloatPoint(px, py))
204
+
<> " on line_b?: "
205
+
<> string.inspect(on_line(line_b, FloatPoint(px, py)))
206
+
207
+
on_line(line_b, FloatPoint(px, py))
208
+
}
209
+
}
210
+
}
211
+
212
+
fn on_line(line: #(FloatPoint, FloatPoint), point: FloatPoint) {
213
+
case line {
214
+
#(a, b)
215
+
if {
216
+
a.x >=. point.x
217
+
&& b.x <=. point.x
218
+
&& a.y >=. point.y
219
+
&& b.y <=. point.y
220
+
}
221
+
|| {
222
+
a.x >=. point.x
223
+
&& b.x <=. point.x
224
+
&& a.y <=. point.y
225
+
&& b.y >=. point.y
226
+
}
227
+
|| {
228
+
a.x <=. point.x
229
+
&& b.x >=. point.x
230
+
&& a.y >=. point.y
231
+
&& b.y <=. point.y
232
+
}
233
+
|| {
234
+
a.x <=. point.x
235
+
&& b.x >=. point.x
236
+
&& a.y <=. point.y
237
+
&& b.y >=. point.y
238
+
}
239
+
-> True
240
+
_ -> False
241
+
}
242
+
}