Watch
1
0
Fork
You've already forked advent-of-code
0

2022 Day 17 Part 2

This commit is contained in:
Jeremy Kaplan 2022-12-16 23:46:49 -08:00
commit 1d8bb838d6

View file

@ -1,17 +1,17 @@
use std::{convert::TryInto, fmt::Display, str::FromStr}; use std::{collections::HashSet, convert::TryInto, fmt::Display, str::FromStr};
use serde::{ use serde::{
de::{value, IntoDeserializer}, de::{value, IntoDeserializer},
Deserialize, Deserialize,
}; };
// const INPUT: (&str, usize) = (include_str!("../../input/day17-ex.txt"), 10); // const INPUT: &str = include_str!("../../input/day17-ex.txt");
const INPUT: (&str, usize) = (include_str!("../../input/day17.txt"), 2022); const INPUT: &str = include_str!("../../input/day17.txt");
fn main() { fn main() {
let (text, rock_count) = INPUT; let jets = parse_input(INPUT);
let jets = parse_input(text); println!("Part 1: {}", simulate(jets.clone(), 2022));
println!("{}", part1(jets, rock_count)); println!("Part 2: {}", simulate(jets, 1_000_000_000_000));
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize)]
@ -47,6 +47,9 @@ enum Piece {
} }
impl Piece { impl Piece {
// TODO(variant_count): std::mem::variant_count::<Piece>()
const NUM_SHAPES: usize = 5;
fn generator() -> impl Iterator<Item = Self> { fn generator() -> impl Iterator<Item = Self> {
use Piece::*; use Piece::*;
[Dash, Plus, Corner, Line, Square].into_iter().cycle() [Dash, Plus, Corner, Line, Square].into_iter().cycle()
@ -75,7 +78,6 @@ impl Piece {
"..#....", "..#....",
], ],
Piece::Square => vec![ Piece::Square => vec![
".......",
"..##...", "..##...",
"..##...", "..##...",
], ],
@ -83,10 +85,7 @@ impl Piece {
rows.iter() rows.iter()
.rev() // Bottom-up! .rev() // Bottom-up!
.map(|row| { .map(|s| Row::parse(s))
let bools: Vec<bool> = row.chars().map(|c| c == '#').collect();
TryInto::<Row>::try_into(bools).unwrap()
})
.collect() .collect()
} }
} }
@ -106,16 +105,25 @@ impl Block {
} }
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Row([bool; 7]); struct Row([bool; 7]);
impl Row { impl Row {
const EMPTY: Self = Self([false; 7]); const EMPTY: Self = Self([false; 7]);
fn parse(s: &str) -> Self {
let bools: Vec<bool> = s.chars().map(|c| c == '#').collect();
bools.try_into().unwrap()
}
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.0.iter().all(|&v| !v) self.0.iter().all(|&v| !v)
} }
fn is_full(&self) -> bool {
self.0.iter().all(|&v| v)
}
fn intersects(&self, other: &Self) -> bool { fn intersects(&self, other: &Self) -> bool {
self.0.iter().zip(other.0.iter()).any(|(&t, &p)| t && p) self.0.iter().zip(other.0.iter()).any(|(&t, &p)| t && p)
} }
@ -173,27 +181,34 @@ impl From<Vec<bool>> for Row {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Tower { struct Tower {
grid: Vec<Row>, grid: Vec<Row>,
rows_removed: usize,
pieces: usize,
} }
impl Tower { impl Tower {
fn new() -> Self { fn new() -> Self {
Self { grid: vec![] } Self {
grid: vec![],
rows_removed: 0,
pieces: 0,
}
} }
fn height(&self) -> usize { fn height(&self) -> usize {
let empty_rows = self.grid.iter().rev().take_while(|r| r.is_empty()).count(); self.grid.len() + self.rows_removed
self.grid.len() - empty_rows
} }
fn spawn(&mut self, piece: Piece, jets: &mut impl Iterator<Item = Direction>) { fn spawn(&mut self, piece: Piece, jets: &mut impl Iterator<Item = Direction>) -> usize {
let mut block = Block::new(piece);
let mut y = self.grid.len() + 3;
self.grid.extend([Row::EMPTY; 4]); self.grid.extend([Row::EMPTY; 4]);
let mut block = Block::new(piece); let mut dj = 0;
let mut y = self.height() + 3;
loop { loop {
// Push! // Push!
let dir = jets.next().unwrap(); let dir = jets.next().unwrap();
dj += 1;
if let Some(new_block) = block.push(dir) { if let Some(new_block) = block.push(dir) {
if self.can_place(y, &new_block) { if self.can_place(y, &new_block) {
block = new_block; block = new_block;
@ -212,8 +227,26 @@ impl Tower {
} }
self.place_at(y, block); self.place_at(y, block);
self.clean();
self.grid.truncate(self.height()); self.pieces += 1;
dj
}
fn clean(&mut self) {
// Garbage-collect empty rows.
let saved = self.grid.len();
let empty = self.grid.iter().rev().take_while(|r| r.is_empty()).count();
self.grid.truncate(saved - empty);
}
fn top(&self) -> Vec<Row> {
self.grid
.iter()
.cloned()
.rev()
.take_while(|r| !r.is_full())
.collect()
} }
fn can_place(&self, y: usize, block: &Block) -> bool { fn can_place(&self, y: usize, block: &Block) -> bool {
@ -229,24 +262,127 @@ impl Tower {
} }
} }
fn part1(jets: Vec<Direction>, rock_count: usize) -> usize { fn simulate(jets: Vec<Direction>, rock_count: usize) -> usize {
let pieces = Piece::generator().take(rock_count); let (base_tower, loop_state) = find_loop(jets.clone());
let mut jets = jets.into_iter().cycle(); let template = run_loop(&base_tower, jets.clone(), &loop_state);
let mut tower = Tower::new(); let mut pieces_used = base_tower.pieces;
let mut jet_idx = loop_state.jet;
let mut tower_height = base_tower.height();
for piece in pieces { while pieces_used + template.pieces < rock_count {
tower.spawn(piece, &mut jets); pieces_used += template.pieces;
// tower.render(); tower_height += template.height;
jet_idx += template.jets % jets.len();
} }
tower.height() let remaining_rocks = rock_count - pieces_used;
let leftover_pieces = Piece::generator()
.skip(pieces_used % Piece::NUM_SHAPES)
.take(remaining_rocks);
let leftover_jets = jets.iter().cloned().skip(jet_idx);
let leftover_height = run(&base_tower, leftover_pieces, leftover_jets);
tower_height + leftover_height
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct State {
piece: Piece,
jet: usize,
top: Vec<Row>,
}
fn find_loop(jets: Vec<Direction>) -> (Tower, State) {
let mut pieces = Piece::generator();
let mut jets_iter = jets.iter().cloned().cycle();
let mut tower = Tower::new();
let mut states: HashSet<State> = HashSet::new();
let mut jets_used = 0;
let mut loop_start: Option<(State, usize)> = None;
for piece in &mut pieces {
let state = State {
piece,
jet: jets_used % jets.len(),
top: tower.top(),
};
let looped = !states.insert(state.clone());
if looped {
loop_start = Some((state, tower.height()));
break;
}
let dj = tower.spawn(piece, &mut jets_iter);
jets_used += dj;
}
(tower, loop_start.unwrap().0)
}
#[derive(Debug, Copy, Clone)]
struct LoopInfo {
pieces: usize,
jets: usize,
height: usize,
}
fn run_loop(base_tower: &Tower, jets: Vec<Direction>, loop_start: &State) -> LoopInfo {
let mut tower = base_tower.clone();
let pieces = Piece::generator().skip_while(|&p| p != loop_start.piece);
let num_jets = jets.len();
let mut jets = jets.iter().cloned().cycle().skip(loop_start.jet); // off-by-one?
let mut jets_used = loop_start.jet;
let mut seen = 0;
for (pieces_used, piece) in pieces.enumerate() {
let state = State {
piece,
jet: jets_used % num_jets,
top: tower.top(),
};
seen += i32::from(state == *loop_start);
if seen == 2 {
return LoopInfo {
pieces: pieces_used,
jets: jets_used - loop_start.jet,
height: tower.height() - base_tower.height(),
};
}
let dj = tower.spawn(piece, &mut jets);
jets_used += dj;
}
unreachable!();
}
fn run(
base_tower: &Tower,
pieces: impl Iterator<Item = Piece>,
mut jets: impl Iterator<Item = Direction>,
) -> usize {
let base_height = base_tower.height();
let mut tower: Tower = base_tower.clone();
for piece in pieces {
tower.spawn(piece, &mut jets);
}
tower.height() - base_height
} }
impl Tower { impl Tower {
#[allow(dead_code)]
fn render(&self) { fn render(&self) {
// TODO(int_log): self.0.checked_log10().unwrap_or(0) + 1
let width = format!("{}", self.height()).chars().count();
for (i, row) in self.grid.iter().enumerate().rev() { for (i, row) in self.grid.iter().enumerate().rev() {
println!("{} {}", i, row); println!("{: >width$} {}", i + self.rows_removed, row);
} }
println!(" -------"); println!(" -------");
} }