From c585c139a576f41d6989bbc5a670d717c5c60c9e Mon Sep 17 00:00:00 2001 From: Ty Date: Thu, 14 Dec 2023 01:16:38 -0700 Subject: [PATCH] Day 14 DP is cool but actually wtf --- lib/src/day14/mod.rs | 145 +++++++++++++++++++++++++++++++++++++++++++ lib/src/lib.rs | 3 + 2 files changed, 148 insertions(+) create mode 100644 lib/src/day14/mod.rs diff --git a/lib/src/day14/mod.rs b/lib/src/day14/mod.rs new file mode 100644 index 0000000..5359a63 --- /dev/null +++ b/lib/src/day14/mod.rs @@ -0,0 +1,145 @@ +use std::{collections::HashMap, num::NonZeroUsize}; + +use clru::CLruCache; +use itertools::Itertools; + +use crate::utils::Day; + +#[derive(Debug, Default)] +pub struct Day14 { + pub input: String, +} + +const SIZE: usize = 100; // More efficient to hardcode the input size, for less allocations + +impl Day14 { + fn rotate(left: bool, array: &mut [[char; SIZE]; SIZE]) { + let mut rot = [['.'; SIZE]; SIZE]; + match left { + false => for i in 0..SIZE { + for j in 0..SIZE { + rot[i][j] = array[SIZE - j - 1][i]; + } + }, + true => for i in 0..SIZE { + for j in 0..SIZE { + rot[i][j] = array[j][SIZE - i - 1]; + } + } + } + *array = rot; + } +} + +impl Day for Day14 { + fn part1(&mut self) -> String { + let mut square_rocks = HashMap::::new(); + let mut new_dish = vec![vec![false; self.input.lines().next().unwrap().len()]; self.input.lines().count()]; + for (i, row) in self.input.lines().enumerate() { + for (j, char) in row.chars().enumerate() { + match char { + '.' => continue, + '#' => { square_rocks.insert(j, i); }, + 'O' => { + let mut k = square_rocks.get(&j).map(|n| n + 1).unwrap_or(0); + loop { + if new_dish[k][j] != true { new_dish[k][j] = true; break } + else { k += 1; } + } + }, + _ => panic!() + } + } + } + + // Dbg print lines + // println!( + // "{}", + // new_dish + // .iter() + // .map(|l| l.iter().map(|b| if *b { 'O' } else { '.' }).collect::()) + // .collect::>() + // .join("\n") + // ); + + new_dish.into_iter().enumerate().map(|(i, line)| line.into_iter().filter_map(|rock| if rock { Some(SIZE - i) } else { None }).sum::()).sum::().to_string() + } + + fn part2(&mut self) -> String { + let mut direction = 0u8; // 0 -> North, 1 -> East, 2 -> South, 3 -> West + let mut new_dish: [[char; SIZE]; SIZE] = self.input.lines().map(|line| line.chars().collect_vec().try_into().unwrap()).collect_vec().try_into().unwrap(); + let mut cache = CLruCache::<(u8, [[char; SIZE]; SIZE]), [[char; SIZE]; SIZE]>::new(NonZeroUsize::new(100).unwrap()); + + for i in 0..390625u128 { + if i % 100000 == 0 { println!("{:.02}%, {}", i as f64 / 4000000000f64 * 100f64, cache.len()); } + + // Cache check + let cache_ref = new_dish.clone(); + if let Some(result) = cache.get(&(direction, cache_ref)) { + new_dish = *result; + continue; + } + + for _ in 0..10240 { + // Based on direction, flip the input + match direction { + 0 => (), + 1 => Self::rotate(true, &mut new_dish), + 2 => { + for row in new_dish.iter_mut() { + row.reverse(); + } + new_dish.reverse(); + }, + 3 => Self::rotate(false, &mut new_dish), + _ => panic!() + } + + // Init tracking + let mut square_rocks = HashMap::::new(); + let clone = new_dish.clone(); + + for (i, row) in clone.iter().enumerate() { + for (j, char) in row.iter().enumerate() { + match char { + '.' => continue, + '#' => { square_rocks.insert(j, i); }, + 'O' => { + new_dish[i][j] = '.'; + let mut k = square_rocks.get(&j).map(|n| n + 1).unwrap_or(0); + loop { + if new_dish[k][j] != 'O' { new_dish[k][j] = 'O'; break } + else { k += 1; } + } + }, + _ => panic!() + } + } + } + + // Based on direction, tilt the direction again + // Based on direction, flip the input + match direction { + 0 => (), + 1 => Self::rotate(false, &mut new_dish), + 2 => { + for row in new_dish.iter_mut() { + row.reverse(); + } + new_dish.reverse(); + }, + 3 => Self::rotate(true, &mut new_dish), + _ => panic!() + } + + // Loop direction + if direction != 0 { direction -= 1; } else { direction = 3; } + } + + // Cache store + cache.put((direction, cache_ref), new_dish); + } + + new_dish.into_iter().enumerate().map(|(i, line)| line.into_iter().filter_map(|rock| if rock == 'O' { Some(SIZE - i) } else { None }).sum::()).sum::().to_string() + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index acd902d..ef46b8f 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -6,6 +6,7 @@ use day10::Day10; use day11::Day11; use day12::Day12; use day13::Day13; +use day14::Day14; use day2::Day2; use day3::Day3; use day4::Day4; @@ -24,6 +25,7 @@ pub mod day10; pub mod day11; pub mod day12; pub mod day13; +pub mod day14; pub mod day2; pub mod day3; pub mod day4; @@ -68,6 +70,7 @@ pub fn get_day(day: u8, input: String) -> Box { cache: CLruCache::new(NonZeroUsize::new(10000).unwrap()), }), 13 => Box::new(Day13 { input }), + 14 => Box::new(Day14 { input }), _ => panic!("Invalid day #"), } }