Day 17, part 1

This commit is contained in:
jazzpi 2022-12-20 14:31:23 +01:00
parent 06786817f3
commit 1e7eb12da9
5 changed files with 268 additions and 0 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -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"

13
src/bin/d17p1.rs Normal file
View File

@ -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)
);
}

246
src/day17.rs Normal file
View File

@ -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<Jet> {
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<VentRow> {
static ROWS: OnceCell<Vec<Vec<VentRow>>> = 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<VentRow>) {
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<usize> {
let row = self.pos.0;
row..(row + self.kind.height())
}
pub fn can_push(&self, vent: &Vec<VentRow>, 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<VentRow>) -> 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<Jet>, n_blocks: usize) -> usize {
let mut block_i = 0;
let mut current_block: Option<Block> = 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<VentRow>, 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<VentRow>) -> usize {
vent.iter()
.rev()
.position(|r| *r != 0)
.unwrap_or_else(|| vent.len())
}
pub fn print_vent(vent: &Vec<VentRow>, 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!("+-------+");
}

View File

@ -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;