2022 Day 23 Part 1
This commit is contained in:
parent
397cfff63e
commit
41a6478127
1 changed files with 227 additions and 0 deletions
227
2022/src/bin/day23.rs
Normal file
227
2022/src/bin/day23.rs
Normal file
|
|
@ -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<RC>,
|
||||
}
|
||||
|
||||
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<RC> {
|
||||
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<Move>) -> 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<RC, Vec<RC>> = 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<Move> = 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(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue