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 #"),
}
}