tangled
alpha
login
or
join now
jonas.tngl.sh
/
advent-of-code
0
fork
atom
Advent of Code solutions in Rust
0
fork
atom
overview
issues
pulls
pipelines
feat: day 10 of AoC 2025
jonas.tngl.sh
3 months ago
1fb71d3e
fda00996
+225
-2
2 changed files
expand all
collapse all
unified
split
aoc_2025
src
day10.rs
main.rs
+223
aoc_2025/src/day10.rs
···
1
1
+
use std::ops::BitXor;
2
2
+
3
3
+
use anyhow::{Context, anyhow, bail};
4
4
+
use aoc_companion::prelude::*;
5
5
+
use itertools::Itertools as _;
6
6
+
use rayon::iter::{ParallelBridge as _, ParallelIterator as _};
7
7
+
8
8
+
pub(crate) struct Door {
9
9
+
machines: Vec<Machine>,
10
10
+
}
11
11
+
12
12
+
impl<'input> Solution<'input> for Door {
13
13
+
fn parse(input: &'input str) -> Result<Self> {
14
14
+
input
15
15
+
.lines()
16
16
+
.map(str::parse)
17
17
+
.try_collect()
18
18
+
.map(|machines| Door { machines })
19
19
+
}
20
20
+
21
21
+
fn part1(&self) -> Result<usize> {
22
22
+
self.machines
23
23
+
.iter()
24
24
+
.map(|m| {
25
25
+
m.fewest_button_presses_for_lights().ok_or_else(|| {
26
26
+
anyhow!("no combination of button presses achieved desired indicator lights")
27
27
+
})
28
28
+
})
29
29
+
.try_fold(0, |acc, b| Ok(acc + b?))
30
30
+
}
31
31
+
32
32
+
fn part2(&self) -> Result<usize> {
33
33
+
self.machines
34
34
+
.iter()
35
35
+
.map(|m| {
36
36
+
m.fewest_button_presses_for_joltage().ok_or_else(|| {
37
37
+
anyhow!(
38
38
+
"no combination of button presses achieved desired joltage requirements"
39
39
+
)
40
40
+
})
41
41
+
})
42
42
+
.try_fold(0, |acc, b| Ok(acc + b?))
43
43
+
}
44
44
+
}
45
45
+
46
46
+
#[derive(Debug, Clone, PartialEq, Eq)]
47
47
+
struct Machine {
48
48
+
desired_indicator_lights: u32,
49
49
+
buttons: Vec<u32>,
50
50
+
joltage_requirements: Vec<usize>,
51
51
+
}
52
52
+
53
53
+
impl std::str::FromStr for Machine {
54
54
+
type Err = anyhow::Error;
55
55
+
56
56
+
fn from_str(s: &str) -> Result<Self> {
57
57
+
let mut words = s.split_ascii_whitespace();
58
58
+
let Some(light_pattern) = words.next() else {
59
59
+
bail!("expected indicator light pattern");
60
60
+
};
61
61
+
let Some(light_pattern) = light_pattern
62
62
+
.strip_prefix('[')
63
63
+
.and_then(|rest| rest.strip_suffix(']'))
64
64
+
else {
65
65
+
bail!("indicator light pattern needs to be surrounded by square brackets");
66
66
+
};
67
67
+
let desired_indicator_lights = light_pattern
68
68
+
.bytes()
69
69
+
.rev()
70
70
+
.fold(0, |acc, b| (acc << 1) | (b == b'#') as u32);
71
71
+
let Some(joltage_seq) = words.next_back() else {
72
72
+
bail!("expected joltage requirements");
73
73
+
};
74
74
+
let Some(joltage_seq) = joltage_seq
75
75
+
.strip_prefix('{')
76
76
+
.and_then(|rest| rest.strip_suffix('}'))
77
77
+
else {
78
78
+
bail!("joltage requiements need to be surrounded by curly brackets");
79
79
+
};
80
80
+
let joltage_requirements = joltage_seq
81
81
+
.split(',')
82
82
+
.map(|s| {
83
83
+
s.parse()
84
84
+
.with_context(|| anyhow!("parsing joltage requirement {s:?} failed"))
85
85
+
})
86
86
+
.try_collect()?;
87
87
+
let buttons = words
88
88
+
.map(|button_word| {
89
89
+
let Some(button_word) = button_word
90
90
+
.strip_prefix('(')
91
91
+
.and_then(|rest| rest.strip_suffix(')'))
92
92
+
else {
93
93
+
bail!("button wiring needs to be surrounded by parentheses");
94
94
+
};
95
95
+
button_word
96
96
+
.split(',')
97
97
+
.map(|s| {
98
98
+
s.parse()
99
99
+
.with_context(|| anyhow!("parsing button wiring {s:?} failed"))
100
100
+
})
101
101
+
.try_fold(0, |acc, i: Result<usize, _>| Ok(acc | (1 << i?)))
102
102
+
})
103
103
+
.try_collect()?;
104
104
+
105
105
+
Ok(Machine {
106
106
+
desired_indicator_lights,
107
107
+
buttons,
108
108
+
joltage_requirements,
109
109
+
})
110
110
+
}
111
111
+
}
112
112
+
113
113
+
fn button_presses_for_lights(
114
114
+
buttons: &[u32],
115
115
+
desired_indicator_lights: u32,
116
116
+
) -> impl Iterator<Item = Vec<&u32>> {
117
117
+
(0..=buttons.len())
118
118
+
.flat_map(|k| buttons.iter().combinations(k))
119
119
+
.filter(move |combo| {
120
120
+
combo.iter().copied().fold(0, BitXor::bitxor) == desired_indicator_lights
121
121
+
})
122
122
+
}
123
123
+
124
124
+
impl Machine {
125
125
+
fn fewest_button_presses_for_lights(&self) -> Option<usize> {
126
126
+
button_presses_for_lights(&self.buttons, self.desired_indicator_lights)
127
127
+
.map(|combo| combo.len())
128
128
+
.next()
129
129
+
}
130
130
+
131
131
+
fn fewest_button_presses_for_joltage(&self) -> Option<usize> {
132
132
+
fn inner(buttons: &[u32], required_joltage: &[usize]) -> Option<usize> {
133
133
+
let equiv_lights = required_joltage
134
134
+
.iter()
135
135
+
.rev()
136
136
+
.fold(0, |acc, n| (acc << 1) | (n % 2) as u32);
137
137
+
138
138
+
button_presses_for_lights(buttons, equiv_lights)
139
139
+
.par_bridge()
140
140
+
.filter_map(|combo| {
141
141
+
let remaining_joltage: Vec<_> = required_joltage
142
142
+
.iter()
143
143
+
.enumerate()
144
144
+
.map(|(i, j)| {
145
145
+
j.checked_sub(
146
146
+
combo.iter().copied().filter(|&b| b & (1 << i) != 0).count(),
147
147
+
)
148
148
+
.map(|j| j / 2)
149
149
+
.ok_or(())
150
150
+
})
151
151
+
.try_collect()
152
152
+
.ok()?;
153
153
+
154
154
+
if remaining_joltage.iter().all(|&j| j == 0) {
155
155
+
Some(combo.len())
156
156
+
} else {
157
157
+
Some(combo.len() + 2 * inner(buttons, &remaining_joltage)?)
158
158
+
}
159
159
+
})
160
160
+
.min()
161
161
+
}
162
162
+
163
163
+
inner(&self.buttons, &self.joltage_requirements)
164
164
+
}
165
165
+
}
166
166
+
167
167
+
#[cfg(test)]
168
168
+
mod tests {
169
169
+
use super::*;
170
170
+
171
171
+
const EXAMPLE_INPUT: &str = "\
172
172
+
[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
173
173
+
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
174
174
+
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}";
175
175
+
176
176
+
fn example_machines() -> Vec<Machine> {
177
177
+
vec![
178
178
+
Machine {
179
179
+
desired_indicator_lights: 0b0110,
180
180
+
buttons: vec![0b1000, 0b1010, 0b0100, 0b1100, 0b0101, 0b0011],
181
181
+
joltage_requirements: vec![3, 5, 4, 7],
182
182
+
},
183
183
+
Machine {
184
184
+
desired_indicator_lights: 0b01000,
185
185
+
buttons: vec![0b11101, 0b01100, 0b10001, 0b00111, 0b11110],
186
186
+
joltage_requirements: vec![7, 5, 12, 7, 2],
187
187
+
},
188
188
+
Machine {
189
189
+
desired_indicator_lights: 0b101110,
190
190
+
buttons: vec![0b011111, 0b011001, 0b110111, 0b000110],
191
191
+
joltage_requirements: vec![10, 11, 11, 5, 10, 5],
192
192
+
},
193
193
+
]
194
194
+
}
195
195
+
196
196
+
#[test]
197
197
+
fn parse_example_input() {
198
198
+
itertools::assert_equal(
199
199
+
Door::parse(EXAMPLE_INPUT).unwrap().machines,
200
200
+
example_machines(),
201
201
+
);
202
202
+
}
203
203
+
204
204
+
#[test]
205
205
+
fn fewest_button_presses_for_lights_in_example() {
206
206
+
itertools::assert_equal(
207
207
+
example_machines()
208
208
+
.iter()
209
209
+
.map(Machine::fewest_button_presses_for_lights),
210
210
+
[Some(2), Some(3), Some(2)],
211
211
+
);
212
212
+
}
213
213
+
214
214
+
#[test]
215
215
+
fn fewest_button_presses_for_joltage_in_example() {
216
216
+
itertools::assert_equal(
217
217
+
example_machines()
218
218
+
.iter()
219
219
+
.map(Machine::fewest_button_presses_for_joltage),
220
220
+
[Some(10), Some(12), Some(11)],
221
221
+
);
222
222
+
}
223
223
+
}
+2
-2
aoc_2025/src/main.rs
···
9
9
mod day07;
10
10
mod day08;
11
11
mod day09;
12
12
-
// mod day10;
12
12
+
mod day10;
13
13
// mod day11;
14
14
// mod day12;
15
15
···
27
27
door!(2025-12-07 ~> day07),
28
28
door!(2025-12-08 ~> day08),
29
29
door!(2025-12-09 ~> day09),
30
30
-
// door!(2025-12-10 ~> day10),
30
30
+
door!(2025-12-10 ~> day10),
31
31
// door!(2025-12-11 ~> day11),
32
32
// door!(2025-12-12 ~> day12),
33
33
])