diff --git a/Cargo.lock b/Cargo.lock index 7d5f963..be7672f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "lazy_static", "log", "num", + "once_cell", "regex", ] @@ -250,6 +251,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + [[package]] name = "rand" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index dcc47f0..43b7e0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ log = "^0.4" env_logger = "^0.10" num = "^0.1" lazy_static = "^1.4" +once_cell = "^1.16" [[bin]] name = "d1p1" diff --git a/src/bin/d17p1.rs b/src/bin/d17p1.rs new file mode 100644 index 0000000..bc59bfb --- /dev/null +++ b/src/bin/d17p1.rs @@ -0,0 +1,13 @@ +use aoc22::{day17, util}; + +const BLOCKS: usize = 2022; + +pub fn main() { + let jets = day17::parse_jets(&util::parse_input()); + + println!( + "Height after {} blocks: {}", + BLOCKS, + day17::do_moves(&jets, BLOCKS) + ); +} diff --git a/src/day17.rs b/src/day17.rs new file mode 100644 index 0000000..dca034b --- /dev/null +++ b/src/day17.rs @@ -0,0 +1,246 @@ +use once_cell::sync::OnceCell; + +use crate::util; + +const VENT_WIDTH: usize = 7; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Jet { + Left, + Right, +} + +impl Jet { + pub fn shift(&self) -> isize { + match &self { + Jet::Left => 1, + Jet::Right => -1, + } + } +} + +pub fn parse_jets(input: &String) -> Vec { + assert_eq!(input.lines().count(), 1); + input + .chars() + .filter_map(|c| match c { + '<' => Some(Jet::Left), + '>' => Some(Jet::Right), + '\n' => None, + _ => panic!("Unknown jet {}", c), + }) + .collect() +} + +#[derive(Clone, Copy, Debug)] +pub enum BlockKind { + Dash = 0, + Plus, + L, + Pipe, + Square, +} + +impl BlockKind { + pub fn from_num(n: usize) -> BlockKind { + match n { + 0 => BlockKind::Dash, + 1 => BlockKind::Plus, + 2 => BlockKind::L, + 3 => BlockKind::Pipe, + 4 => BlockKind::Square, + _ => panic!("Unknown block kind {}", n), + } + } + + pub fn height(&self) -> usize { + match self { + BlockKind::Dash => 1, + BlockKind::Plus => 3, + BlockKind::L => 3, + BlockKind::Pipe => 4, + BlockKind::Square => 2, + } + } + + pub fn width(&self) -> usize { + match self { + BlockKind::Dash => 4, + BlockKind::Plus => 3, + BlockKind::L => 3, + BlockKind::Pipe => 1, + BlockKind::Square => 2, + } + } + + pub fn vent_rows(&self) -> &'static Vec { + static ROWS: OnceCell>> = OnceCell::new(); + let rows = ROWS.get_or_init(|| { + vec![ + vec![0b1111], // Dash + vec![0b010, 0b111, 0b010], // Plus + vec![0b001, 0b001, 0b111], // L + vec![0b1; 4], // Pipe + vec![0b11, 0b11], // Square + ] + }); + &rows + .get(*self as usize) + .unwrap_or_else(|| panic!("Unknown BlockKind: {:?}", self)) + } +} + +const N_BLOCK_TYPES: usize = 5; + +pub struct Block { + kind: BlockKind, + pos: util::Coord, +} + +impl Block { + pub fn spawn(&self, vent: &mut Vec) { + for (i, row) in self.kind.vent_rows().iter().rev().enumerate() { + vent[self.pos.0 + i] |= *row << self.shift_dist(); + } + } + + pub fn row_range(&self) -> std::ops::Range { + let row = self.pos.0; + row..(row + self.kind.height()) + } + + pub fn can_push(&self, vent: &Vec, jet: Jet) -> bool { + if (jet == Jet::Left && self.pos.1 == 0) + || (jet == Jet::Right && self.pos.1 + self.kind.width() >= VENT_WIDTH) + { + return false; + } + + let shift = self.shift_dist() as isize + jet.shift(); + for (i, row) in self.row_range().rev().enumerate() { + let bitmask = self.kind.vent_rows()[i] << shift; + if vent[row] & bitmask != 0 { + return false; + } + } + true + } + + pub fn can_fall(&self, vent: &Vec) -> bool { + if self.pos.0 == 0 { + return false; + } + + for (i, row) in self.row_range().rev().enumerate() { + let bitmask = self.kind.vent_rows()[i] << self.shift_dist(); + if vent[row - 1] & bitmask != 0 { + return false; + } + } + + true + } + + pub fn shift_dist(&self) -> usize { + VENT_WIDTH - self.pos.1 - self.kind.width() + } +} + +type VentRow = u8; + +pub fn do_moves(jets: &Vec, n_blocks: usize) -> usize { + let mut block_i = 0; + let mut current_block: Option = None; + let mut vent = vec![0; 4]; + + let mut round = 0; + while block_i <= n_blocks { + if current_block.is_none() { + current_block = Some(spawn_block(&mut vent, block_i % N_BLOCK_TYPES)); + // println!("Vent as block {} begins falling:", block_i); + // print_vent(&vent, current_block.as_ref()); + // println!(""); + block_i += 1; + } + let block = current_block.as_mut().unwrap(); + + // Push left/right + let jet = jets[round % jets.len()]; + if block.can_push(&vent, jet) { + let (_, ref mut col) = block.pos; + *col = ((*col as isize) - jet.shift()) as usize; + } + + // Fall + if block.can_fall(&vent) { + let (ref mut row, _) = block.pos; + *row -= 1; + } else { + block.spawn(&mut vent); + current_block = None; + } + round += 1; + } + + vent.len() - empty_rows(&vent) +} + +const SPAWN_OFFSET_X: usize = 2; +const SPAWN_OFFSET_Y: usize = 3; + +fn spawn_block(vent: &mut Vec, block: usize) -> Block { + let block = BlockKind::from_num(block); + + let mut empty_rows = empty_rows(vent); + let diff = (block.height() as isize + 3) - (empty_rows as isize); + if diff > 0 { + vent.append(&mut vec![0; diff as usize]); + empty_rows += diff as usize; + } + + let row = vent.len() - empty_rows + SPAWN_OFFSET_Y; + let col = SPAWN_OFFSET_X; + + assert!(row + block.height() - 1 < vent.len()); + + let block = Block { + kind: block, + pos: (row, col), + }; + + block +} + +fn empty_rows(vent: &Vec) -> usize { + vent.iter() + .rev() + .position(|r| *r != 0) + .unwrap_or_else(|| vent.len()) +} + +pub fn print_vent(vent: &Vec, block: Option<&Block>) { + let block_rows = block.map(|b| b.kind.vent_rows()); + let block_range = block.map(|b| b.row_range()); + let block_range = block_range.as_ref(); + for (r, vent_row) in vent.iter().enumerate().rev() { + print!("|"); + let block_collision = if block.is_some() && block_range.unwrap().contains(&r) { + block_rows.unwrap()[block_range.unwrap().end - r - 1] << block.unwrap().shift_dist() + } else { + 0 + }; + for i in (0..VENT_WIDTH).rev() { + let col = 1 << i; + let c = if (block_collision & col) != 0 { + '@' + } else if (vent_row & col) == 0 { + '.' + } else { + '#' + }; + print!("{}", c); + } + print!("|\n"); + } + println!("+-------+"); +} diff --git a/src/lib.rs b/src/lib.rs index f75f5f3..ff93375 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod day13; pub mod day14; pub mod day15; pub mod day16; +pub mod day17; pub mod day2; pub mod day3; pub mod day4;