2023 - Day 14

This commit is contained in:
2023-12-14 15:34:54 +01:00
parent a9fe2fa9be
commit 6416c98c73
3 changed files with 371 additions and 0 deletions

261
2023/src/bin/day14.rs Normal file
View File

@@ -0,0 +1,261 @@
#![feature(test)]
use std::{fmt::Display, collections::{hash_map::DefaultHasher, HashMap}, hash::{Hash, Hasher}};
use anyhow::Result;
use aoc::Solver;
// -- Runners --
fn main() -> Result<()> {
Day::solve()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_test1() -> Result<()> {
Day::test(Day::part1, "test-1", 136)
}
#[test]
fn part1_solution() -> Result<()> {
Day::test(Day::part1, "input", 110407)
}
#[test]
fn part2_test1() -> Result<()> {
Day::test(Day::part2, "test-1", 64)
}
#[test]
fn part2_solution() -> Result<()> {
Day::test(Day::part2, "input", 87273)
}
// Benchmarks
extern crate test;
#[bench]
#[ignore]
fn part1_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part1, b)
}
#[bench]
#[ignore]
fn part2_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part2, b)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum Space {
Empty,
Cube,
Round,
}
impl Display for Space {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Space::Empty => write!(f, "."),
Space::Cube => write!(f, "#"),
Space::Round => write!(f, "O"),
}
}
}
impl From<char> for Space {
fn from(c: char) -> Self {
match c {
'.' => Self::Empty,
'#' => Self::Cube,
'O' => Self::Round,
_ => unreachable!("Invalid input"),
}
}
}
fn print_grid(grid: &[Vec<Space>]) {
for line in grid {
for space in line {
print!("{space}");
}
println!();
}
}
fn tilt_north(grid: &mut Vec<Vec<Space>>) {
let width = grid[0].len();
let height = grid.len();
for y in 0..height {
for x in 0..width {
if grid[y][x] == Space::Round {
let mut new_y = y;
for yy in (0..y).rev() {
if grid[yy][x] == Space::Empty {
new_y = yy
} else {
break;
}
}
if new_y < y {
grid[y][x] = Space::Empty;
grid[new_y][x] = Space::Round;
}
}
}
}
}
fn tilt_east(grid: &mut [Vec<Space>]) {
let width = grid[0].len();
for x in (0..width).rev() {
for line in grid.iter_mut() {
if line[x] == Space::Round {
let mut new_x = x;
for (xx, space) in line.iter().enumerate().skip(x+1) {
if space == &Space::Empty {
new_x = xx
} else {
break;
}
}
if new_x > x {
line[x] = Space::Empty;
line[new_x] = Space::Round;
}
}
}
}
}
fn tilt_south(grid: &mut Vec<Vec<Space>>) {
let width = grid[0].len();
let height = grid.len();
for y in (0..height).rev() {
for x in 0..width {
if grid[y][x] == Space::Round {
let mut new_y = y;
for (yy, line) in grid.iter().enumerate().skip(y+1) {
if line[x] == Space::Empty {
new_y = yy
} else {
break;
}
}
if new_y > y {
grid[y][x] = Space::Empty;
grid[new_y][x] = Space::Round;
}
}
}
}
}
fn tilt_west(grid: &mut [Vec<Space>]) {
let width = grid[0].len();
for x in 0..width {
for line in grid.iter_mut() {
if line[x] == Space::Round {
let mut new_x = x;
for xx in (0..x).rev() {
if line[xx] == Space::Empty {
new_x = xx
} else {
break;
}
}
if new_x < x {
line[x] = Space::Empty;
line[new_x] = Space::Round;
}
}
}
}
}
fn calculate_load(grid: &Vec<Vec<Space>>) -> usize {
grid.iter()
.enumerate()
.map(|(y, line)| {
line.iter()
.filter_map(|space| {
if space == &Space::Round {
Some(grid.len() - y)
} else {
None
}
})
.sum::<usize>()
})
.sum()
}
fn cycle(grid: &mut Vec<Vec<Space>>) {
tilt_north(grid);
tilt_west(grid);
tilt_south(grid);
tilt_east(grid);
}
// -- Solution --
pub struct Day;
impl aoc::Solver for Day {
type Output1 = usize;
type Output2 = usize;
fn day() -> u8 {
14
}
fn part1(input: &str) -> Self::Output1 {
let mut grid: Vec<Vec<Space>> = input
.lines()
.map(|line| line.chars().map(|c| c.into()).collect())
.collect();
tilt_north(&mut grid);
calculate_load(&grid)
}
fn part2(input: &str) -> Self::Output2 {
let mut grid: Vec<Vec<Space>> = input
.lines()
.map(|line| line.chars().map(|c| c.into()).collect())
.collect();
let mut cache = HashMap::new();
let mut cycles = 0;
let loop_length = loop {
cycle(&mut grid);
cycles += 1;
// Calculate the hash of the current state
let mut hash = DefaultHasher::new();
grid.hash(&mut hash);
let hash = hash.finish();
// Check if we have encountered this state before
if let Some(c) = cache.get(&hash) {
break cycles - c;
}
// Insert the state in the map
cache.insert(hash, cycles);
};
let remaining = (1000000000 - cycles) % loop_length;
for _ in 0..remaining {
cycle(&mut grid);
}
calculate_load(&grid)
}
}