Skip to content

Commit

Permalink
Add day 14 solution
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrofgodinho committed Dec 14, 2023
1 parent f4e8c0f commit d295444
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Solutions to [Advent of Code 2023](https://adventofcode.com/2023) in Rust 🎄
| [11](src/solutions/day11.rs) | ⭐ | ⭐ | Not much to say, it's a fairly easy challenge. Not super interesting, but not a bad challenge either.
| [12](src/solutions/day12.rs) | ⭐ | ⭐ | Maybe it's just because I'm not used to dynamic programming, but I found this challenge *very* hard. Pretty fun challenge though. My implementation still takes 50ms to run, could probably optimize it further.
| [13](src/solutions/day13.rs) | ⭐ | ⭐ | Compared to previous days, it was a very easy challenge. Took it as an opportunity to learn more about ndarray. Enjoyable problem to solve overall
| [14](src/solutions/day14.rs) | ⭐ | ⭐ | Not a very hard problem. Took me a little bit because I tried to do part 1 without mutating, which meant I had to rewrite most code for part 2. Overall, fairly easy challenge.
2 changes: 1 addition & 1 deletion src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ fn run_day(solution: ParserFn, part: Option<i64>, input: String) -> DayResult {
}

fn print_results(results: Vec<CompleteDayResult>, totals: Totals) {
if results.len() == 0 {
if results.is_empty() {
return;
}
let totals = if results.len() > 1 {
Expand Down
114 changes: 114 additions & 0 deletions src/solutions/day14.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use ahash::AHasher;
use ahash::HashMap;
use ahash::HashMapExt;
use ndarray::{Array, Array2, Axis};
use std::hash::Hasher;

use super::Solution;

pub struct Day14 {
rocks: Array2<Option<bool>>,
seen: HashMap<u64, usize>,
seen_vec: Vec<Array2<Option<bool>>>,
}

impl Solution for Day14 {
fn part1(&mut self) -> String {
let rocks = &mut self.rocks;
shift_up(rocks);
total_load(rocks).to_string()
}

fn part2(&mut self) -> String {
let mut rocks = self.rocks.clone();
let mut loop_end = 0;

let loop_start = loop {
rocks = spin_cycle(rocks);
let hash = hash(&rocks);
if let Some(idx) = self.seen.get(&hash) {
break *idx;
}
self.seen.insert(hash, loop_end);
self.seen_vec.push(rocks.clone());
loop_end += 1;
};

let loop_length = loop_end - loop_start;

let target = 1000000000;
let target = (target - loop_start - 1) % loop_length;

total_load(&self.seen_vec[loop_start + target]).to_string()
}

fn parse(input: String) -> Box<dyn Solution> {
let cols = input.find('\n').unwrap();
let rows = input.lines().count();
let rocks = Array::from_iter(input.lines().flat_map(|line| {
line.bytes().map(|byte| match byte {
b'O' => Some(true),
b'#' => Some(false),
_ => None,
})
}))
.into_shape((rows, cols))
.unwrap();
Box::new(Self {
rocks,
seen: HashMap::new(),
seen_vec: Vec::new(),
})
}
}

fn hash(rocks: &Array2<Option<bool>>) -> u64 {
let mut hasher = AHasher::default();
for (pos, rock) in rocks.iter().enumerate() {
if let Some(true) = rock {
hasher.write_usize(pos);
}
}
hasher.finish()
}

fn total_load(rocks: &Array2<Option<bool>>) -> usize {
let mut sum = 0;
let len = rocks.shape()[0];
for col in rocks.columns() {
for (row_idx, row) in col.iter().enumerate() {
if let Some(true) = row {
sum += len - row_idx;
}
}
}
sum
}

fn shift_up(rocks: &mut Array2<Option<bool>>) {
for mut col in rocks.columns_mut() {
let mut next_rock_position = 0;
for row_idx in 0..col.len() {
match col[row_idx] {
Some(true) => {
col[row_idx] = None;
col[next_rock_position] = Some(true);
next_rock_position += 1;
}
Some(false) => {
next_rock_position = row_idx + 1;
}
None => {}
}
}
}
}

fn spin_cycle(mut rocks: Array2<Option<bool>>) -> Array2<Option<bool>> {
for _ in 0..4 {
shift_up(&mut rocks);
rocks = rocks.reversed_axes();
rocks.invert_axis(Axis(1));
}
rocks
}
4 changes: 3 additions & 1 deletion src/solutions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod day10;
mod day11;
mod day12;
mod day13;
mod day14;
mod day2;
mod day3;
mod day4;
Expand All @@ -22,7 +23,7 @@ pub trait Solution: Sync + Send {
fn part2(&mut self) -> String;
}

pub fn get_parser_fns() -> [ParserFn; 13] {
pub fn get_parser_fns() -> [ParserFn; 14] {
[
day1::Day1::parse,
day2::Day2::parse,
Expand All @@ -37,5 +38,6 @@ pub fn get_parser_fns() -> [ParserFn; 13] {
day11::Day11::parse,
day12::Day12::parse,
day13::Day13::parse,
day14::Day14::parse,
]
}
23 changes: 23 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,26 @@ fn day13_part1() {
fn day13_part2() {
test_day(13, DAY13_INPUT.to_string(), "400", Part::Part2);
}

const DAY14_INPUT: &str = indoc! {"
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
"};

#[test]
fn day14_part1() {
test_day(14, DAY14_INPUT.to_string(), "136", Part::Part1);
}

#[test]
fn day14_part2() {
test_day(14, DAY14_INPUT.to_string(), "64", Part::Part2);
}

0 comments on commit d295444

Please sign in to comment.