From 688e5d00ef013c737aa84660a00f9e21045b9a77 Mon Sep 17 00:00:00 2001 From: Dylan Thies Date: Fri, 15 Dec 2023 22:22:58 -0500 Subject: [PATCH] day-14 all done and some format to day 13 --- 2023/day-13/src/part1.rs | 10 +- 2023/day-14/src/part2.rs | 233 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 233 insertions(+), 10 deletions(-) diff --git a/2023/day-13/src/part1.rs b/2023/day-13/src/part1.rs index 036d073..4ff76de 100644 --- a/2023/day-13/src/part1.rs +++ b/2023/day-13/src/part1.rs @@ -17,10 +17,9 @@ impl Drawing { let (max_col, max_row) = self.size.into(); let col_score = (1..max_col) .filter(|reflect_col| { - let reflect_col = * reflect_col; + let reflect_col = *reflect_col; let span = reflect_col.min(max_col - reflect_col); - self - .mounds + self.mounds .iter() .filter(|mound| mound.x + span >= reflect_col && mound.x < reflect_col) .map(|mound| (2 * reflect_col - mound.x - 1, mound.y).into()) @@ -35,10 +34,9 @@ impl Drawing { .sum::(); let row_score = (1..max_row) .filter(|reflect_row| { - let reflect_row = * reflect_row; + let reflect_row = *reflect_row; let span = reflect_row.min(max_row - reflect_row); - self - .mounds + self.mounds .iter() .filter(|mound| mound.y + span >= reflect_row && mound.y < reflect_row) .map(|mound| (mound.x, 2 * reflect_row - mound.y - 1).into()) diff --git a/2023/day-14/src/part2.rs b/2023/day-14/src/part2.rs index d8e6695..9d06d66 100644 --- a/2023/day-14/src/part2.rs +++ b/2023/day-14/src/part2.rs @@ -1,19 +1,244 @@ #![warn(clippy::all, clippy::pedantic)] +use std::collections::HashMap; + +use glam::IVec2; +use nom::{bytes::complete::is_a, character::complete, multi::separated_list1, IResult}; + +#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] +enum Boulder { + Round, + Static, +} +impl From for Boulder { + fn from(value: char) -> Self { + match value { + 'O' => Self::Round, + '#' => Self::Static, + x => unimplemented!("there is no boulder type for this charachter {x}"), + } + } +} + +/// day 14 part 2 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than #[must_use] -pub fn part2(_input: &str) -> String { - "Not Finished".to_string() +#[allow(clippy::cast_sign_loss)] +pub fn part2(input: &str) -> String { + let (_, (maxes, mut map)) = parse_input(input).expect("stuff"); + + let cycles = 1_000_000_000; + let mut cur_cycle = 0; + let mut cache = HashMap::new(); + + let (start_of_cycle, end_of_cycle) = loop { + cur_cycle += 1; + if cur_cycle >= cycles { + break (0, cur_cycle); + } + let grid_hash = hash_grid(&map, maxes); + if let Some((_, cycle)) = cache.get(&grid_hash) { + break (*cycle, cur_cycle); //reached steady state? + } + let next = tilt_north(maxes, &map); + let next = tilt_west(maxes, &next); + let next = tilt_south(maxes, &next); + let next = tilt_east(maxes, &next); + cache.insert(grid_hash, (next.clone(), cur_cycle)); + map = next; + }; + + let len_of_cyle = end_of_cycle - start_of_cycle; + + let pos_in_cycle = start_of_cycle + (cycles - end_of_cycle) % len_of_cyle; + + map = cache + .values() + .find_map(|(look_at_map, pos)| (*pos == pos_in_cycle).then_some(look_at_map)) + .unwrap() + .clone(); + + let mut total = 0_usize; + for col in 0..maxes.x { + for row in 0..maxes.y { + total += match map.get(&(col, row).into()) { + Some(Boulder::Round) => (maxes.y - row) as usize, + _ => 0, + } + } + } + + total.to_string() +} + +fn hash_grid(map: &HashMap, maxes: IVec2) -> String { + (0..maxes.y) + .flat_map(|y| { + (0..maxes.x).map(move |x| match map.get(&IVec2::from((x, y))) { + Some(Boulder::Static) => "#", + Some(Boulder::Round) => "O", + _ => ".", + }) + }) + .collect::() +} + +fn _print_grid(map: &HashMap, maxes: IVec2) { + (0..maxes.y).for_each(|y| { + println!( + "{}", + (0..maxes.x) + .map(move |x| match map.get(&IVec2::from((x, y))) { + Some(Boulder::Static) => "#", + Some(Boulder::Round) => "O", + _ => ".", + }) + .collect::() + ); + }); +} + +fn tilt_north(maxes: IVec2, in_map: &HashMap) -> HashMap { + let mut out_map = HashMap::new(); + + for col in 0..maxes.x { + let mut last = 0; + for row in 0..maxes.y { + match in_map.get(&IVec2::from((col, row))) { + Some(Boulder::Static) => { + last = row + 1; + out_map.insert(IVec2::from((col, row)), Boulder::Static); + } + Some(Boulder::Round) => { + out_map.insert(IVec2::from((col, last)), Boulder::Round); + last += 1; + } + _ => {} + } + } + } + + out_map +} + +fn tilt_south(maxes: IVec2, in_map: &HashMap) -> HashMap { + let mut out_map = HashMap::new(); + + for col in 0..maxes.x { + let mut last = maxes.y - 1; + for row in (0..maxes.y).rev() { + match in_map.get(&IVec2::from((col, row))) { + Some(Boulder::Static) => { + last = row - 1; + out_map.insert(IVec2::from((col, row)), Boulder::Static); + } + Some(Boulder::Round) => { + out_map.insert(IVec2::from((col, last)), Boulder::Round); + last -= 1; + } + _ => {} + } + } + } + + out_map +} + +fn tilt_west(maxes: IVec2, in_map: &HashMap) -> HashMap { + let mut out_map = HashMap::new(); + + for row in 0..maxes.y { + let mut last = 0; + for col in 0..maxes.x { + match in_map.get(&IVec2::from((col, row))) { + Some(Boulder::Static) => { + last = col + 1; + out_map.insert(IVec2::from((col, row)), Boulder::Static); + } + Some(Boulder::Round) => { + out_map.insert(IVec2::from((last, row)), Boulder::Round); + last += 1; + } + _ => {} + } + } + } + + out_map +} + +fn tilt_east(maxes: IVec2, in_map: &HashMap) -> HashMap { + let mut out_map = HashMap::new(); + + for row in 0..maxes.y { + let mut last = maxes.x - 1; + for col in (0..maxes.x).rev() { + match in_map.get(&IVec2::from((col, row))) { + Some(Boulder::Static) => { + last = col - 1; + out_map.insert(IVec2::from((col, row)), Boulder::Static); + } + Some(Boulder::Round) => { + out_map.insert(IVec2::from((last, row)), Boulder::Round); + last -= 1; + } + _ => {} + } + } + } + + out_map +} +fn parse_input(input: &str) -> IResult<&str, (IVec2, HashMap)> { + let (input, rows) = separated_list1(complete::line_ending, is_a(".O#"))(input)?; + let max_rows = i32::try_from(rows.len()).expect("stuff and things"); + let max_cols = i32::try_from(rows[0].len()).expect("things and stuff?"); + let maxs = IVec2::from((max_cols, max_rows)); + let hash = rows + .iter() + .enumerate() + .flat_map(|(line_no, chars)| { + chars + .chars() + .enumerate() + .filter_map(move |(col_no, c)| { + (c != '.').then_some(( + IVec2::from(( + i32::try_from(col_no).expect("hopefully not to small"), + i32::try_from(line_no).expect("this shouldn't be too big"), + )), + c, + )) + }) + .map(|(pos, c)| (pos, Boulder::from(c))) + }) + .collect(); + Ok((input, (maxs, hash))) } #[cfg(test)] mod test { use super::*; - const INPUT: &str = ""; + const INPUT: &str = "O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#...."; #[test] fn part2_works() { let result = part2(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "64".to_string()); } }