diff --git a/samples/day14.txt b/samples/day14.txt new file mode 100644 index 0000000..5a24dce --- /dev/null +++ b/samples/day14.txt @@ -0,0 +1,10 @@ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... diff --git a/src/bin/day14.rs b/src/bin/day14.rs new file mode 100644 index 0000000..75335f0 --- /dev/null +++ b/src/bin/day14.rs @@ -0,0 +1,142 @@ +use std::collections::HashMap; + +use aoc_2023::*; +use indicatif::ProgressIterator; +use memoize::memoize; + +type Output1 = usize; +type Output2 = Output1; + +#[memoize] +fn slide_rocks(map: Vec>) -> Vec> { + let mut tilted: Vec> = map.to_vec(); + + for x in 0..tilted[0].len() { + let mut next = 0; + for y in 0..tilted.len() { + match tilted[y][x] { + '#' | 'O' => { + next = y + 1; + } + '.' => { + while next < tilted.len() && tilted[next][x] == '.' { + next += 1; + } + + if next < tilted.len() && tilted[next][x] == 'O' { + tilted[y][x] = 'O'; + tilted[next][x] = '.'; + } + } + _ => unreachable!(), + } + } + } + + tilted +} + +#[memoize] +fn rotate_east(map: Vec>) -> Vec> { + let mut r = map.clone(); + + for y in 0..map.len() { + for (x, row) in r.iter_mut().enumerate().take(map[y].len()) { + row[map.len() - y - 1] = map[y][x]; + } + } + + r +} + +#[memoize] +fn run_cycle(map: Vec>) -> Vec> { + let mut map = map; + for _ in 0..4 { + map = slide_rocks(map); + map = rotate_east(map); + } + + map +} + +fn total_load(m: &[Vec]) -> usize { + let rows = m.len(); + + m.iter() + .enumerate() + .map(|(y, l)| (rows - y) * l.iter().filter(|&&c| c == 'O').count()) + .sum() +} + +struct Day14 { + map: Vec>, +} + +impl Solution for Day14 { + fn new>(pathname: P) -> Self { + let lines: Vec = file_to_lines(pathname); + + Self { + map: lines.iter().map(|l| l.chars().collect()).collect(), + } + } + + fn part_1(&mut self) -> Output1 { + let tilted = slide_rocks(self.map.clone()); + total_load(&tilted) + } + + fn part_2(&mut self) -> Output2 { + let mut map = self.map.clone(); + + let iterations = 1000000000; + let mut remaining = 0; + + let mut seen: HashMap>, usize> = HashMap::new(); + for i in (0..iterations).progress() { + seen.insert(map.clone(), i); + map = run_cycle(map); + + if let Some(&old_i) = seen.get(&map) { + remaining = (iterations - i - 1) % (i - old_i + 1); + break; + } + } + + for _ in 0..remaining { + map = run_cycle(map); + } + + total_load(&map) + } +} + +fn main() -> Result<()> { + Day14::main() +} + +test_sample!(day_14, Day14, 136, 64); + +#[cfg(test)] +mod day_14_extended { + use super::*; + + #[test] + fn test_rotate_east() { + let m = vec![ + vec!['#', 'O', 'O'], + vec!['.', '#', 'O'], + vec!['.', '.', 'O'], + ]; + + assert_eq!( + rotate_east(m), + vec![ + vec!['.', '.', '#'], + vec!['.', '#', 'O'], + vec!['O', 'O', 'O'], + ] + ) + } +}