-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday14.rs
121 lines (109 loc) · 2.96 KB
/
day14.rs
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
use std::collections::{hash_map::Entry, HashMap};
fn parse(data: &str) -> (Vec<u8>, usize, usize) {
let lines = data.lines().map(|line| line.as_bytes()).collect::<Vec<_>>();
let width = lines
.iter()
.map(|line| line.len())
.max()
.unwrap_or_default();
(
(0..width)
.flat_map(|x| {
lines
.iter()
.map(move |line| if x < line.len() { line[x] } else { b'.' })
})
.collect(),
width,
lines.len(),
)
}
fn tilt(data: &mut [u8], height: usize) {
for col in data.chunks_mut(height) {
for group in col.split_mut(|&c| c == b'#') {
let n = group.iter().filter(|&&c| c == b'O').count();
group[..n].fill(b'O');
group[n..].fill(b'.');
}
}
}
fn rotate(src: &[u8], dst: &mut [u8], width: usize, height: usize) {
for (i, c) in (0..height)
.rev()
.flat_map(|y| (0..width).map(move |x| src[x * height + y]))
.enumerate()
{
dst[i] = c;
}
}
fn spin(data: &mut [u8], temp: &mut [u8], width: usize, height: usize) {
tilt(data, height);
rotate(data, temp, width, height);
tilt(temp, width);
rotate(temp, data, height, width);
tilt(data, height);
rotate(data, temp, width, height);
tilt(temp, width);
rotate(temp, data, height, width);
}
fn load(data: &[u8], height: usize) -> usize {
data.chunks(height)
.flat_map(|col| {
col.iter()
.enumerate()
.filter_map(|(y, &c)| if c == b'O' { Some(height - y) } else { None })
})
.sum()
}
pub fn part1(data: &str) -> usize {
let (mut data, _, height) = parse(data);
tilt(&mut data, height);
load(&data, height)
}
const N: usize = 1000000000;
pub fn part2(data: &str) -> usize {
let (mut data, width, height) = parse(data);
let mut temp = vec![0; data.len()];
let mut cache = [(data.clone(), 0)].into_iter().collect::<HashMap<_, _>>();
for i in 1..=N {
spin(&mut data, &mut temp, width, height);
match cache.entry(data.clone()) {
Entry::Vacant(entry) => {
entry.insert(i);
}
Entry::Occupied(entry) => {
for _ in 0..(N - i) % (i - *entry.get()) {
spin(&mut data, &mut temp, width, height);
}
break;
}
}
}
load(&data, height)
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
static EXAMPLE: &str = indoc! {"
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
"};
#[test]
fn part1_examples() {
assert_eq!(136, part1(EXAMPLE));
}
#[test]
fn part2_examples() {
assert_eq!(64, part2(EXAMPLE));
}
}