AdventOfCode/Rust/2023/14.rs

157 lines
3.5 KiB
Rust

#![feature(test)]
use std::{
hash::Hash,
ops::{Index, IndexMut},
};
use itertools::Itertools;
use rustc_hash::FxHashMap;
type Input = Grid;
#[derive(Debug, Clone, Copy)]
enum Cell {
Empty,
Cube,
Rounded,
}
#[derive(Debug, Clone)]
struct Grid {
grid: Vec<Vec<Cell>>,
rotation: u8,
}
fn setup(input: &str) -> Input {
let grid = input
.lines()
.map(|line| {
line.bytes()
.map(|b| match b {
b'.' => Cell::Empty,
b'#' => Cell::Cube,
b'O' => Cell::Rounded,
_ => panic!(),
})
.collect()
})
.collect();
Grid { grid, rotation: 0 }
}
impl Grid {
fn width(&self) -> usize {
if self.rotation % 2 == 0 {
self.grid[0].len()
} else {
self.grid.len()
}
}
fn height(&self) -> usize {
if self.rotation % 2 == 0 {
self.grid.len()
} else {
self.grid[0].len()
}
}
fn index(&self, i: usize, j: usize) -> (usize, usize) {
let w = self.grid[0].len();
let h = self.grid.len();
match self.rotation % 4 {
0 => (i, j),
1 => (w - j - 1, i),
2 => (h - i - 1, w - j - 1),
3 => (j, h - i - 1),
_ => unreachable!(),
}
}
fn tilt_north(&mut self) {
for i in 1..self.height() {
for j in 0..self.width() {
if !matches!(self[(i, j)], Cell::Rounded) {
continue;
}
let mut k = i;
while k > 0 && matches!(self[(k - 1, j)], Cell::Empty) {
self[(k, j)] = Cell::Empty;
self[(k - 1, j)] = Cell::Rounded;
k -= 1;
}
}
}
}
fn total_load(&self) -> usize {
self.grid
.iter()
.rev()
.enumerate()
.map(|(i, row)| row.iter().filter(|x| matches!(x, Cell::Rounded)).count() * (i + 1))
.sum()
}
fn cycle(&mut self) {
for i in 0..4 {
self.rotation = i;
self.tilt_north();
}
}
fn hash(&self) -> impl Hash + Eq {
type Chunk = u128;
self.grid
.iter()
.flatten()
.chunks(Chunk::BITS as _)
.into_iter()
.map(|c| c.fold(0, |acc, x| acc << 1 | matches!(x, Cell::Rounded) as Chunk))
.fold(0, |a, b| a ^ b)
}
}
impl Index<(usize, usize)> for Grid {
type Output = Cell;
fn index(&self, (i, j): (usize, usize)) -> &Self::Output {
let (i, j) = self.index(i, j);
&self.grid[i][j]
}
}
impl IndexMut<(usize, usize)> for Grid {
fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut Self::Output {
let (i, j) = self.index(i, j);
&mut self.grid[i][j]
}
}
fn part1(input: &Input) -> usize {
let mut grid = input.clone();
grid.tilt_north();
grid.total_load()
}
fn part2(input: &Input) -> usize {
let mut grid = input.clone();
let mut n = 1000000000u32;
let mut hist = FxHashMap::default();
while n > 0 {
if let Some(k) = hist.insert(grid.hash(), n) {
let cycle_length = k - n;
n %= cycle_length;
break;
}
grid.cycle();
n -= 1;
}
for _ in 0..n {
grid.cycle();
}
grid.total_load()
}
aoc::main!(2023, 14, ex: 1);