This commit is contained in:
2022-12-17 17:57:02 +01:00
parent 628e556063
commit 8b1c3ff175
5 changed files with 242 additions and 0 deletions

1
inputs/day17.txt Normal file

File diff suppressed because one or more lines are too long

8
src/bin/day17_1.rs Normal file
View File

@@ -0,0 +1,8 @@
use std::fs;
use aoc2022::day17::process_part_1;
fn main() {
let file = fs::read_to_string("./inputs/day17.txt").unwrap();
println!("{}", process_part_1(&file));
}

8
src/bin/day17_2.rs Normal file
View File

@@ -0,0 +1,8 @@
use std::fs;
use aoc2022::day17::process_part_2;
fn main() {
let file = fs::read_to_string("./inputs/day17.txt").unwrap();
println!("{}", process_part_2(&file));
}

224
src/day17.rs Normal file
View File

@@ -0,0 +1,224 @@
use std::collections::HashMap;
use nom::branch::alt;
use nom::character::complete;
use nom::multi::many1;
use nom::{IResult, Parser};
pub fn process_part_1(input: &str) -> usize {
let mut gusts = parse_input(input).unwrap().1.into_iter().cycle();
let mut templates = all_pieces().into_iter().cycle();
// Leftmost bit is kept as a wall in the grid, right is done in the right() of piece
let mut grid = [0b1000_0000_u8; 10_000];
grid[0] = 0b1111_1111;
(0..2022).fold(0, |z, _| {
let mut piece = Piece {
data: templates.next().unwrap(),
z: z + 4,
};
loop {
match gusts.next() {
Some(Gust::Left) => piece.left(&grid),
Some(Gust::Right) => piece.right(&grid),
None => unreachable!(),
}
if !piece.can_move_down(&grid) {
break;
}
piece.down();
}
piece.paste(&mut grid);
z.max(piece.max_z())
})
}
pub fn process_part_2(input: &str) -> usize {
let mut gusts = parse_input(input)
.unwrap()
.1
.into_iter()
.enumerate()
.cycle();
let mut templates = all_pieces().into_iter().enumerate().cycle();
let mut grid = [0b1000_0000_u8; 10_000];
grid[0] = 0b1111_1111;
let mut cache = HashMap::new();
let mut i = 0;
let mut skipped = 0;
let mut z = 0;
let iterations = 1_000_000_000_000usize;
while i < iterations {
let (piece_id, template) = templates.next().unwrap();
let mut piece = Piece {
data: template,
z: z + 4,
};
let mut gust_id;
loop {
let (i, gust) = gusts.next().unwrap();
gust_id = i;
match gust {
Gust::Left => piece.left(&grid),
Gust::Right => piece.right(&grid),
}
if !piece.can_move_down(&grid) {
break;
}
piece.down();
}
piece.paste(&mut grid);
z = z.max(piece.max_z());
if z > 64 && skipped == 0 {
let key = HashKey {
piece_id,
gust_id,
grid: grid[z - 63..=z].try_into().unwrap(),
};
if let Some((previous_z, previous_i)) = cache.insert(key, (z, i)) {
// We got a cache hit! Let's compare the two states to know the period.
let height_diff = z - previous_z;
let iterations_diff = i - previous_i;
// Now we know how many periods (cycles) we can skip without affecting the state/repetition
let skip_repeats = (iterations - i) / iterations_diff;
let skip_iterations = skip_repeats * iterations_diff;
let skip_height = skip_repeats * height_diff;
// We fast-forward our piece counter
i += skip_iterations;
// We record how much height we skipped, which we will add to the final result to get the real value
skipped = skip_height;
}
}
i += 1;
}
z + skipped
}
#[derive(Hash, PartialEq, Eq)]
struct HashKey {
piece_id: usize,
gust_id: usize,
grid: [u8; 64],
}
#[derive(Debug, Copy, Clone)]
enum Gust {
Left,
Right,
}
fn parse_input(input: &str) -> IResult<&str, Vec<Gust>> {
many1(
alt((complete::char('<'), complete::char('>'))).map(|v| match v {
'<' => Gust::Left,
'>' => Gust::Right,
_ => unreachable!(),
}),
)(input)
}
struct Piece {
data: [u8; 4],
z: usize,
}
impl Piece {
fn left(&mut self, grid: &[u8]) {
// Only the rows we occupy are interesting
let grid = &grid[self.z..self.z + 4];
let collision = grid
.iter()
.zip(self.data)
.any(|(grid, piece)| (piece << 1 & grid) > 0);
if collision {
return;
}
for row in &mut self.data {
*row <<= 1;
}
}
fn right(&mut self, grid: &[u8]) {
// Only the rows we occupy are interesting
let grid = &grid[self.z..self.z + 4];
let collision = grid
.iter()
.zip(self.data)
.any(|(grid, piece)| (piece.trailing_ones() as u8 | (piece >> 1 & grid)) > 0);
if collision {
return;
}
for row in &mut self.data {
*row >>= 1;
}
}
fn can_move_down(&self, grid: &[u8]) -> bool {
let grid = &grid[self.z - 1..=self.z];
!grid
.iter()
.zip(self.data)
.any(|(grid, piece)| (piece & grid) > 0)
}
fn down(&mut self) {
self.z -= 1;
}
fn paste(&self, grid: &mut [u8]) {
for (grid, piece) in grid[self.z..self.z + 4].iter_mut().zip(self.data) {
*grid |= piece
}
}
fn height(&self) -> usize {
self.data
.iter()
.take_while(|row| row.count_ones() > 0)
.count()
}
fn max_z(&self) -> usize {
self.z + self.height() - 1
}
}
fn all_pieces() -> [[u8; 4]; 5] {
[
[0b11110, 0b0, 0b0, 0b0],
[0b1000, 0b11100, 0b1000, 0b0],
[0b11100, 0b100, 0b100, 0b0],
[0b10000; 4],
[0b11000, 0b11000, 0b0, 0b0],
]
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>";
#[test]
fn day1() {
assert_eq!(process_part_1(INPUT), 3068);
}
#[test]
fn day2() {
assert_eq!(process_part_2(INPUT), 1_514_285_714_288);
}
}

View File

@@ -17,3 +17,4 @@ pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day17;