From bece6b708b5a72b6b79afaad47c5c947f3880e7e Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Thu, 14 Dec 2023 15:26:10 -0500 Subject: [PATCH] day14 part2 --- src/day14.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 11 deletions(-) diff --git a/src/day14.rs b/src/day14.rs index b994dac..62da2f1 100644 --- a/src/day14.rs +++ b/src/day14.rs @@ -1,6 +1,8 @@ +use std::collections::HashMap; + use aoc_runner_derive::{aoc, aoc_generator}; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] enum Rock { Round, Square, @@ -43,10 +45,8 @@ fn part1(input: &Vec>) -> usize { Rock::Round => { cur_weight -= 1; load += cur_weight; - //println!("{}", cur_weight); }, Rock::Square => { - //println!("{}", input[0].len() - i); cur_weight = input[0].len() - i; }, Rock::None => continue @@ -56,10 +56,80 @@ fn part1(input: &Vec>) -> usize { load } -// #[aoc(day14, part2)] -// fn part2(input: &Vec>) -> String { -// todo!() -// } +#[aoc(day14, part2)] +fn part2(input: &Vec>) -> usize { + let mut seen_at = HashMap::new(); + let mut next_y = Vec::::new(); + let mut map = input.to_vec(); + + // Functions to get a Rock using a rotated coordinate space + let cycle_parts: &[(Box>, usize, usize) -> &mut Rock>, _, _); 4] = &[ + (Box::new(|map: &mut Vec>, x: usize, y: usize| &mut map[y][x]), + map[0].len(), map.len()), + (Box::new(|map: &mut Vec>, x: usize, y: usize| &mut map[x][y]), + map.len(), map[0].len()), + (Box::new(|map: &mut Vec>, x: usize, y: usize| { let h = map.len(); &mut map[h - 1 - y][x] }), + map[0].len(), map.len()), + (Box::new(|map: &mut Vec>, x: usize, y: usize| { let w = map[0].len(); &mut map[x][w - 1 - y] }), + map.len(), map[0].len()), + ]; + + let mut cycle = 0; + const END: u32 = 1000000000; + while cycle < END { + // Handle tilts in each direction + for (getter, width, height) in cycle_parts { + next_y.clear(); + next_y.resize(*width, 0); + for y in 0..*height { + for x in 0..*width { + let item = getter(&mut map, x, y); + match *item { + Rock::None => {} + Rock::Square => { + next_y[x] = y + 1; + } + Rock::Round => { + *item = Rock::None; + *getter(&mut map, x, next_y[x]) = Rock::Round; + next_y[x] += 1; + } + } + } + } + } + + // More compact representation of the current state, for saving in hashmap + let key = map.iter().enumerate().flat_map(|(y, line)| { + line.iter().enumerate().filter_map(move |(x, &ref rock)| { + if rock == &Rock::Round { + Some((x as u8, y as u8)) + } else { + None + } + }) + }).collect::>(); + + cycle += 1; + if let Some(seen_at_cycle) = seen_at.insert(key, cycle) { + // Current state was identical to one we'd already seen, we can skip forward + let diff = cycle - seen_at_cycle; + let remaining = END - cycle; + let skipped = remaining / diff * diff; + cycle += skipped; + } + } + + let height = map.len(); + + map.into_iter().enumerate().flat_map(|(y, line)| line.into_iter().filter_map(move |rock| { + if rock == Rock::Round { + Some(height - y) + } else { + None + } + })).sum() +} #[cfg(test)] @@ -82,8 +152,8 @@ O.#..O.#.# assert_eq!(part1(&parse(EX)), 136); } - // #[test] - // fn part2_example() { - // assert_eq!(part2(&parse(EX)), ""); - // } + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 64); + } } \ No newline at end of file