From 41a6478127089ee73d9548bbec762e34f04b0a44 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Sat, 24 Dec 2022 14:30:29 -0800 Subject: [PATCH] 2022 Day 23 Part 1 --- 2022/src/bin/day23.rs | 227 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 2022/src/bin/day23.rs diff --git a/2022/src/bin/day23.rs b/2022/src/bin/day23.rs new file mode 100644 index 0000000..7adc1f6 --- /dev/null +++ b/2022/src/bin/day23.rs @@ -0,0 +1,227 @@ +use std::collections::{HashMap, HashSet}; + +const INPUT: &str = include_str!("../../input/day23.txt"); + +fn main() { + let grove = Grove::parse(INPUT); + + println!("{}", part1(grove)); +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +struct RC { + r: i64, + c: i64, +} + +impl RC { + const ORIGIN: Self = Self { r: 0, c: 0 }; + + fn new(r: i64, c: i64) -> Self { + Self { r, c } + } + + fn unsigned(r: usize, c: usize) -> Self { + Self::new(r.try_into().unwrap(), c.try_into().unwrap()) + } + + + #[rustfmt::skip] + fn neighbors() -> Vec<(i64, i64)> { + vec![ + (-1, -1), (-1, 0), (-1, 1), + ( 0, -1), ( 0, 1), + ( 1, -1), ( 1, 0), ( 1, 1), + ] + } + + fn north() -> Vec<(i64, i64)> { + vec![(-1, -1), (-1, 0), (-1, 1)] + } + + fn south() -> Vec<(i64, i64)> { + vec![(1, -1), (1, 0), (1, 1)] + } + + fn west() -> Vec<(i64, i64)> { + vec![(-1, -1), (0, -1), (1, -1)] + } + + fn east() -> Vec<(i64, i64)> { + vec![(-1, 1), (0, 1), (1, 1)] + } + + fn toward_north(&self) -> RC { + RC::new(self.r - 1, self.c) + } + + fn toward_south(&self) -> RC { + RC::new(self.r + 1, self.c) + } + + fn toward_east(&self) -> RC { + RC::new(self.r, self.c + 1) + } + + fn toward_west(&self) -> RC { + RC::new(self.r, self.c - 1) + } +} + +#[derive(Debug, Clone)] +struct Grove { + grid: HashSet, +} + +impl Grove { + fn parse(input: &str) -> Self { + let mut grid = HashSet::new(); + + for (r, line) in input.lines().enumerate() { + for (c, fill) in line.chars().enumerate() { + match fill { + '.' => continue, + '#' => grid.insert(RC::unsigned(r, c)), + other => panic!("{:?}", other), + }; + } + } + + Self { grid } + } + + fn propose(&self, from: RC, round: usize) -> Option { + if !self.has_elves(&from, &RC::neighbors()) { + return None; + } + + let mut props: Vec<(RC, Vec<(i64, i64)>)> = vec![ + (from.toward_north(), RC::north()), + (from.toward_south(), RC::south()), + (from.toward_west(), RC::west()), + (from.toward_east(), RC::east()), + ]; + + props.rotate_left(round % 4); + + for (dest, neighbors) in props { + if !self.has_elves(&from, &neighbors) { + return Some(dest); + } + } + + None + } + + fn has_elf(&self, loc: &RC) -> bool { + self.grid.contains(loc) + } + + fn has_elves(&self, RC { r, c }: &RC, deltas: &[(i64, i64)]) -> bool { + deltas + .iter() + .any(|&(dr, dc)| self.has_elf(&RC::new(r + dr, c + dc))) + } + + fn apply(&self, moves: Vec) -> Self { + let mut grid = self.grid.clone(); + + for Move { from, to } in moves { + let was_present = grid.remove(&from); + assert!(was_present); + + let was_empty = grid.insert(to); + assert!(was_empty); + } + + Self { grid } + } +} + +#[derive(Debug, Copy, Clone)] +struct Move { + from: RC, + to: RC, +} + +fn part1(mut grove: Grove) -> usize { + println!("== Initial State ==\n{}", grove); + for round in 0..10 { + let mut proposals: HashMap> = HashMap::new(); + for &src in &grove.grid { + let Some(dest) = grove.propose(src, round) else { continue }; + proposals + .entry(dest) + .and_modify(|v| v.push(src)) + .or_insert_with(|| vec![src]); + } + + let mut moves: Vec = vec![]; + for (dest, sources) in proposals { + if sources.len() != 1 { + continue; + } + + let src = sources[0]; + moves.push(Move { + from: src, + to: dest, + }); + } + + grove = grove.apply(moves); + + println!("== End of Round {} ==\n{}", round + 1, grove); + } + + grove.empty_tiles() +} + +impl Grove { + fn empty_tiles(&self) -> usize { + let (lo, hi) = self.bounds(); + + let rows: usize = (1 + hi.r - lo.r).try_into().unwrap(); + let cols: usize = (1 + hi.c - lo.c).try_into().unwrap(); + + (rows * cols) - self.grid.len() + } + + fn bounds(&self) -> (RC, RC) { + let (lo, hi) = self + .grid + .iter() + .fold((RC::ORIGIN, RC::ORIGIN), |(lo, hi), RC { r, c }| { + ( + RC::new(lo.r.min(*r), lo.c.min(*c)), + RC::new(hi.r.max(*r), hi.c.max(*c)), + ) + }); + (lo, hi) + } +} + +impl std::fmt::Display for Grove { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (lo, hi) = self.bounds(); + + for r in lo.r..=hi.r { + for c in lo.c..=hi.c { + let is_origin = (r, c) == (0, 0); + let is_elf = self.has_elf(&RC { r, c }); + + let chr = match (is_origin, is_elf) { + (true, true) => 'X', + (true, false) => 'O', + (false, true) => '#', + (false, false) => '.', + }; + + write!(f, "{}", chr)?; + } + writeln!(f)?; + } + + Ok(()) + } +}