Skip to content

Commit 18cc3c2

Browse files
committed
Work a bit on Day 11
1 parent 0eb7bd8 commit 18cc3c2

File tree

1 file changed

+200
-35
lines changed

1 file changed

+200
-35
lines changed

src/day11.rs

Lines changed: 200 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
1-
use std::{collections::HashMap, fs, hash::Hash, ops::AddAssign};
1+
use std::{collections::HashMap, fs, hash::Hash, num::ParseIntError, ops::AddAssign, str::FromStr};
22

33
pub fn day11(input_path: String) {
44
let content = fs::read_to_string(input_path).unwrap();
55

6-
let stones: Vec<_> = content.split_whitespace().map(Stone::parse).collect();
6+
let stones: Vec<_> = content
7+
.split_whitespace()
8+
.map(|l| l.parse().unwrap())
9+
.collect();
710

811
println!("{:?}", part1(stones.iter().cloned().collect()));
9-
println!("{:?}", part2(stones));
12+
println!("{:?}", part2(&stones));
13+
14+
let stones: Vec<_> = content
15+
.split_whitespace()
16+
.map(|l| l.parse().unwrap()) // this deduces Stone2 because of the signature of part12
17+
.collect();
18+
19+
println!("{:?}", part12(&stones));
20+
println!("{:?}", part22(&stones));
1021
}
1122

1223
// Most significant digit is last,
@@ -24,12 +35,12 @@ fn part1(stones: Vec<Stone>) -> usize {
2435
.len()
2536
}
2637

27-
fn part2(stones: Vec<Stone>) -> usize {
28-
let mut stone_map: HashMap<Stone, usize> =
29-
accumulate_into_hashmap(stones.into_iter().map(|s| (s, 1)));
38+
fn part2(stones: &[Stone]) -> usize {
39+
// order does not matter, so we can compress the stones into a hashmap, and do every operation just once
40+
let mut stone_map: HashMap<Stone, usize> = accumulate(stones.iter().map(|s| (s.clone(), 1)));
3041

3142
for _ in 0..75 {
32-
stone_map = accumulate_into_hashmap(
43+
stone_map = accumulate(
3344
stone_map
3445
.into_iter()
3546
.flat_map(|(stone, count)| blink_at(stone).into_iter().map(move |s| (s, count))),
@@ -38,20 +49,6 @@ fn part2(stones: Vec<Stone>) -> usize {
3849
stone_map.iter().map(|(_, count)| count).sum()
3950
}
4051

41-
fn accumulate_into_hashmap<T, N, I>(values: I) -> HashMap<T, N>
42-
where
43-
T: Eq + Hash,
44-
N: Default + AddAssign,
45-
I: IntoIterator<Item = (T, N)>,
46-
{
47-
values
48-
.into_iter()
49-
.fold(HashMap::new(), |mut acc, (key, value)| {
50-
*acc.entry(key).or_default() += value;
51-
acc
52-
})
53-
}
54-
5552
fn blink_at(stone: Stone) -> Vec<Stone> {
5653
match stone {
5754
zero if zero.0.is_empty() => vec![Stone::from([1])],
@@ -61,17 +58,6 @@ fn blink_at(stone: Stone) -> Vec<Stone> {
6158
}
6259

6360
impl Stone {
64-
// should actually be proper parse
65-
fn parse(string: &str) -> Stone {
66-
Stone::new(
67-
string
68-
.chars()
69-
.map(|c| c.to_digit(10).unwrap() as u8)
70-
.rev()
71-
.collect(),
72-
)
73-
}
74-
7561
fn new(digits: Vec<u8>) -> Stone {
7662
Stone(digits).trimmed()
7763
}
@@ -108,29 +94,208 @@ impl Stone {
10894
}
10995
}
11096

97+
#[derive(Debug, PartialEq, Eq)]
98+
struct ParseStoneError;
99+
100+
impl FromStr for Stone {
101+
type Err = ParseStoneError;
102+
103+
// this is just for illustrating
104+
// fn from_str(s: &str) -> Result<Self, Self::Err> {
105+
// let digits: Result<Vec<u8>, ParseStoneError> = s
106+
// .chars()
107+
// .map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
108+
// .rev()
109+
// // this collect flips and creates a vector,
110+
// // i.e. it does
111+
// // Iter<Result<u8, Error>> -> Result<Vec<u8>, Error>
112+
// // i.e. it does "early return with Result"
113+
// .collect();
114+
// Ok(Stone::new(digits?))
115+
// }
116+
117+
fn from_str(s: &str) -> Result<Self, Self::Err> {
118+
s.chars()
119+
.map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
120+
.rev()
121+
.collect::<Result<_, _>>()
122+
.map(Stone::new)
123+
}
124+
}
125+
126+
fn accumulate<T, N, I>(values: I) -> HashMap<T, N>
127+
where
128+
T: Eq + Hash,
129+
N: Default + AddAssign,
130+
I: IntoIterator<Item = (T, N)>,
131+
{
132+
values
133+
.into_iter()
134+
.fold(HashMap::new(), |mut acc, (key, value)| {
135+
*acc.entry(key).or_default() += value;
136+
acc
137+
})
138+
}
139+
140+
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
141+
struct Stone2 {
142+
number: usize,
143+
digits: u32,
144+
}
145+
146+
fn digits(number: usize) -> u32 {
147+
if number == 0 {
148+
return 1;
149+
}
150+
let mut temp = number;
151+
let mut digit_count = 0;
152+
while temp > 0 {
153+
temp /= 10;
154+
digit_count += 1;
155+
}
156+
digit_count
157+
}
158+
159+
impl Stone2 {
160+
fn new(number: usize) -> Self {
161+
Stone2 {
162+
number,
163+
digits: digits(number),
164+
}
165+
}
166+
167+
fn split(self) -> [Self; 2] {
168+
// calling this with odd number of digits is ub (I'm lazy)
169+
let divisor = 10usize.pow(self.digits / 2);
170+
let left = Stone2 {
171+
number: self.number / divisor,
172+
digits: self.digits / 2,
173+
};
174+
// right could have leading zeros, recompute digits
175+
let right = Stone2::new(self.number % divisor);
176+
[left, right]
177+
}
178+
179+
fn muled_by(self, n: usize) -> Self {
180+
Stone2::new(self.number * n)
181+
}
182+
}
183+
184+
// here, returning impl Iterator<Item = Stone2>
185+
// does not work, because all match arms would need to have the same concrete type
186+
// (which they cannot have, except when we'd implement a bespoke OneOrTwo-struct)
187+
fn blink_at2(stone: Stone2) -> Vec<Stone2> {
188+
match stone {
189+
Stone2 {
190+
number: 0,
191+
digits: 1,
192+
} => vec![Stone2 {
193+
number: 1,
194+
digits: 1,
195+
}],
196+
even if even.digits % 2 == 0 => Vec::from(even.split()),
197+
other => vec![other.muled_by(2024)],
198+
}
199+
}
200+
201+
fn do_rounds(stones: &[Stone2], rounds: usize) -> usize {
202+
let mut stone_map = accumulate(stones.iter().map(|s| (s.clone(), 1)));
203+
204+
for _ in 0..rounds {
205+
stone_map = accumulate(
206+
stone_map
207+
.into_iter()
208+
.flat_map(|(stone, count)| blink_at2(stone).into_iter().map(move |s| (s, count))),
209+
);
210+
}
211+
stone_map.iter().map(|(_, count)| count).sum()
212+
}
213+
214+
fn part12(stones: &[Stone2]) -> usize {
215+
do_rounds(stones, 25)
216+
}
217+
218+
fn part22(stones: &[Stone2]) -> usize {
219+
do_rounds(stones, 75)
220+
}
221+
222+
impl FromStr for Stone2 {
223+
type Err = ParseIntError;
224+
225+
fn from_str(s: &str) -> Result<Self, Self::Err> {
226+
s.parse().map(|ds| Stone2::new(ds))
227+
}
228+
}
229+
111230
#[cfg(test)]
112231
mod tests {
113232
use super::*;
114233

115234
#[test]
116235
fn test_muled_by_zero() {
117-
let stone = Stone::parse("123");
236+
let stone: Stone = "123".parse().unwrap();
118237
let result = stone.muled_by(0);
119238
assert_eq!(result.0, vec![0, 0, 0]); // 123 * 0 = 0
120239
}
121240

122241
#[test]
123242
fn test_muled_by_one() {
124-
let stone = Stone::parse("456");
243+
let stone: Stone = "456".parse().unwrap();
125244
let result = stone.muled_by(1);
126245
assert_eq!(result.0, vec![6, 5, 4]); // 456 * 1 = 456
127246
}
128247

129248
#[test]
130249
fn test_muled_by_large_number() {
131-
let stone = Stone::parse("999");
250+
let stone: Stone = "999".parse().unwrap();
132251
let result = stone.muled_by(999);
133252
// 999 * 999 = 998001
134253
assert_eq!(result.0, vec![1, 0, 0, 8, 9, 9]);
135254
}
255+
256+
#[test]
257+
fn test_muled_by_zero2() {
258+
let stone: Stone2 = "123".parse().unwrap();
259+
let result = stone.muled_by(0);
260+
assert_eq!(
261+
result,
262+
Stone2 {
263+
number: 0,
264+
digits: 1
265+
}
266+
); // 123 * 0 = 0
267+
}
268+
269+
#[test]
270+
fn test_muled_by_one2() {
271+
let stone: Stone2 = "456".parse().unwrap();
272+
let result = stone.muled_by(1);
273+
assert_eq!(
274+
result,
275+
Stone2 {
276+
number: 456,
277+
digits: 3
278+
}
279+
); // 456 * 1 = 456
280+
}
281+
282+
#[test]
283+
fn test_muled_by_large_number2() {
284+
let stone: Stone2 = "999".parse().unwrap();
285+
let result = stone.muled_by(999);
286+
assert_eq!(
287+
result,
288+
Stone2 {
289+
number: 998001,
290+
digits: 6
291+
}
292+
); // 999 * 999 = 998001
293+
}
294+
295+
#[test]
296+
fn test_split() {
297+
let stone: Stone2 = "1001".parse().unwrap();
298+
let result = stone.split();
299+
assert_eq!(result, [Stone2::new(10), Stone2::new(1)]);
300+
}
136301
}

0 commit comments

Comments
 (0)