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 7 of AoC 2025
jonas.tngl.sh
3 months ago
76174d2c
9f72a8be
1/1
rust.yml
success
3m 43s
+247
-3
3 changed files
expand all
collapse all
unified
split
aoc_2025
src
day07.rs
main.rs
aoc_utils
src
iter.rs
+135
aoc_2025/src/day07.rs
···
1
1
+
use std::collections::HashMap;
2
2
+
3
3
+
use aoc_companion::prelude::*;
4
4
+
use aoc_utils::iter::AtMostTwo;
5
5
+
use itertools::Itertools;
6
6
+
7
7
+
pub(crate) struct Door {
8
8
+
splitters: Vec<Vec<i32>>,
9
9
+
}
10
10
+
11
11
+
impl<'input> Solution<'input> for Door {
12
12
+
fn parse(input: &'input str) -> Result<Self> {
13
13
+
parse_splitters(input).map(|splitters| Door { splitters })
14
14
+
}
15
15
+
16
16
+
fn part1(&self) -> usize {
17
17
+
split_beam(&self.splitters).splits
18
18
+
}
19
19
+
20
20
+
fn part2(&self) -> usize {
21
21
+
split_beam(&self.splitters).quantum_worlds()
22
22
+
}
23
23
+
}
24
24
+
25
25
+
fn parse_splitters(input: &str) -> Result<Vec<Vec<i32>>> {
26
26
+
let mut lines = input.lines().step_by(2);
27
27
+
let start_col = lines
28
28
+
.next()
29
29
+
.unwrap()
30
30
+
.bytes()
31
31
+
.position(|b| b == b'S')
32
32
+
.unwrap() as i32;
33
33
+
Ok(lines
34
34
+
.map(|line| {
35
35
+
line.bytes()
36
36
+
.enumerate()
37
37
+
.filter(|&(_, b)| b == b'^')
38
38
+
.map(|(i, _)| i as i32 - start_col)
39
39
+
.collect()
40
40
+
})
41
41
+
.collect())
42
42
+
}
43
43
+
44
44
+
#[derive(Debug, PartialEq, Eq)]
45
45
+
struct State {
46
46
+
beams: HashMap<i32, usize>,
47
47
+
splits: usize,
48
48
+
}
49
49
+
50
50
+
impl Default for State {
51
51
+
fn default() -> Self {
52
52
+
Self {
53
53
+
beams: HashMap::from([(0, 1)]),
54
54
+
splits: 0,
55
55
+
}
56
56
+
}
57
57
+
}
58
58
+
59
59
+
impl State {
60
60
+
fn quantum_worlds(&self) -> usize {
61
61
+
self.beams.values().sum()
62
62
+
}
63
63
+
}
64
64
+
65
65
+
fn split_beam(splitters: &[impl AsRef<[i32]>]) -> State {
66
66
+
splitters.iter().fold(
67
67
+
State::default(),
68
68
+
|State { beams, mut splits }, splitters| {
69
69
+
let beams = beams
70
70
+
.into_iter()
71
71
+
.flat_map(|(b, c)| {
72
72
+
if splitters.as_ref().iter().contains(&b) {
73
73
+
splits += 1;
74
74
+
AtMostTwo::two((b - 1, c), (b + 1, c))
75
75
+
} else {
76
76
+
AtMostTwo::one((b, c))
77
77
+
}
78
78
+
})
79
79
+
.into_group_map()
80
80
+
.into_iter()
81
81
+
.map(|(b, cs)| (b, cs.into_iter().sum()))
82
82
+
.collect();
83
83
+
84
84
+
State { beams, splits }
85
85
+
},
86
86
+
)
87
87
+
}
88
88
+
89
89
+
#[cfg(test)]
90
90
+
mod tests {
91
91
+
use super::*;
92
92
+
93
93
+
const EXAMPLE_INPUT: &str = "\
94
94
+
.......S.......
95
95
+
...............
96
96
+
.......^.......
97
97
+
...............
98
98
+
......^.^......
99
99
+
...............
100
100
+
.....^.^.^.....
101
101
+
...............
102
102
+
....^.^...^....
103
103
+
...............
104
104
+
...^.^...^.^...
105
105
+
...............
106
106
+
..^...^.....^..
107
107
+
...............
108
108
+
.^.^.^.^.^...^.
109
109
+
...............";
110
110
+
111
111
+
const EXAMPLE_SPLITTERS: &[&[i32]] = &[
112
112
+
&[0],
113
113
+
&[-1, 1],
114
114
+
&[-2, 0, 2],
115
115
+
&[-3, -1, 3],
116
116
+
&[-4, -2, 2, 4],
117
117
+
&[-5, -1, 5],
118
118
+
&[-6, -4, -2, 0, 2, 6],
119
119
+
];
120
120
+
121
121
+
#[test]
122
122
+
fn parse_example_splitters() {
123
123
+
assert_eq!(parse_splitters(EXAMPLE_INPUT).unwrap(), EXAMPLE_SPLITTERS);
124
124
+
}
125
125
+
126
126
+
#[test]
127
127
+
fn example_beam_split_count() {
128
128
+
assert_eq!(split_beam(EXAMPLE_SPLITTERS).splits, 21);
129
129
+
}
130
130
+
131
131
+
#[test]
132
132
+
fn example_quantum_worlds() {
133
133
+
assert_eq!(split_beam(EXAMPLE_SPLITTERS).quantum_worlds(), 40);
134
134
+
}
135
135
+
}
+2
-2
aoc_2025/src/main.rs
···
6
6
mod day04;
7
7
mod day05;
8
8
mod day06;
9
9
-
// mod day07;
9
9
+
mod day07;
10
10
// mod day08;
11
11
// mod day09;
12
12
// mod day10;
···
24
24
door!(2025-12-04 ~> day04),
25
25
door!(2025-12-05 ~> day05),
26
26
door!(2025-12-06 ~> day06),
27
27
-
// door!(2025-12-07 ~> day07),
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),
+110
-1
aoc_utils/src/iter.rs
···
1
1
-
use std::rc::Rc;
1
1
+
use std::{iter::FusedIterator, rc::Rc};
2
2
3
3
pub trait IterUtils: Iterator {
4
4
fn try_unzip<A, B, E, FromA, FromB>(mut self) -> Result<(FromA, FromB), E>
···
73
73
})
74
74
}
75
75
}
76
76
+
77
77
+
#[derive(Debug, Clone, Copy, PartialEq)]
78
78
+
pub struct Few<T, const N: usize>([Option<T>; N]);
79
79
+
80
80
+
pub type AtMostTwo<T> = Few<T, 2>;
81
81
+
82
82
+
impl<T, const N: usize> Few<T, N> {
83
83
+
pub fn new<const M: usize>(items: [T; M]) -> Self {
84
84
+
assert!(M <= N);
85
85
+
Few(crate::array::from_iter(
86
86
+
items
87
87
+
.into_iter()
88
88
+
.map(Some)
89
89
+
.chain(std::iter::repeat_with(|| None)),
90
90
+
)
91
91
+
.ok()
92
92
+
.unwrap())
93
93
+
}
94
94
+
95
95
+
pub fn none() -> Self {
96
96
+
Few(std::array::from_fn(|_| None))
97
97
+
}
98
98
+
99
99
+
pub fn one(item: T) -> Self {
100
100
+
Few::new([item])
101
101
+
}
102
102
+
103
103
+
pub fn two(item1: T, item2: T) -> Self {
104
104
+
Few::new([item1, item2])
105
105
+
}
106
106
+
}
107
107
+
108
108
+
impl<T, const N: usize> Default for Few<T, N> {
109
109
+
fn default() -> Self {
110
110
+
Few::none()
111
111
+
}
112
112
+
}
113
113
+
114
114
+
impl<T, const N: usize> Iterator for Few<T, N> {
115
115
+
type Item = T;
116
116
+
117
117
+
fn next(&mut self) -> Option<Self::Item> {
118
118
+
if N > 0 {
119
119
+
let item = self.0[0].take();
120
120
+
self.0.rotate_left(1);
121
121
+
item
122
122
+
} else {
123
123
+
None
124
124
+
}
125
125
+
}
126
126
+
}
127
127
+
128
128
+
impl<T, const N: usize> FusedIterator for Few<T, N> {}
129
129
+
130
130
+
#[cfg(test)]
131
131
+
mod tests {
132
132
+
use super::*;
133
133
+
134
134
+
#[test]
135
135
+
#[should_panic]
136
136
+
fn few_insufficient_capacity_one() {
137
137
+
Few::<i32, 0>::one(42);
138
138
+
}
139
139
+
140
140
+
#[test]
141
141
+
#[should_panic]
142
142
+
fn few_insufficient_capacity_two() {
143
143
+
Few::<i32, 1>::two(42, 17);
144
144
+
}
145
145
+
146
146
+
#[test]
147
147
+
#[should_panic]
148
148
+
fn few_insufficient_capacity_new() {
149
149
+
Few::<i32, 2>::new([42, 17, -1]);
150
150
+
}
151
151
+
152
152
+
#[test]
153
153
+
fn few_iterator() {
154
154
+
itertools::assert_equal(Few::<i32, 0>::none(), []);
155
155
+
itertools::assert_equal(Few::<i32, 1>::none(), []);
156
156
+
itertools::assert_equal(Few::<i32, 2>::none(), []);
157
157
+
itertools::assert_equal(Few::<i32, 1>::one(42), [42]);
158
158
+
itertools::assert_equal(Few::<i32, 2>::one(42), [42]);
159
159
+
itertools::assert_equal(Few::<i32, 2>::two(42, 17), [42, 17]);
160
160
+
itertools::assert_equal(Few::<i32, 3>::two(42, 17), [42, 17]);
161
161
+
itertools::assert_equal(Few::<i32, 3>::new([42, 17]), [42, 17]);
162
162
+
itertools::assert_equal(Few::<i32, 3>::new([42, 17, -1]), [42, 17, -1]);
163
163
+
}
164
164
+
165
165
+
#[test]
166
166
+
fn few_iterator_fused() {
167
167
+
let mut few = AtMostTwo::two(42, 17);
168
168
+
assert!(few.next().is_some());
169
169
+
assert!(few.next().is_some());
170
170
+
assert!(few.next().is_none());
171
171
+
assert!(few.next().is_none());
172
172
+
assert!(few.next().is_none());
173
173
+
assert!(few.next().is_none());
174
174
+
assert!(few.next().is_none());
175
175
+
}
176
176
+
177
177
+
#[test]
178
178
+
fn few_default_empty() {
179
179
+
itertools::assert_equal(Few::<i32, 0>::default(), []);
180
180
+
itertools::assert_equal(Few::<i32, 1>::default(), []);
181
181
+
itertools::assert_equal(Few::<i32, 2>::default(), []);
182
182
+
itertools::assert_equal(Few::<i32, 3>::default(), []);
183
183
+
}
184
184
+
}