day17
This commit is contained in:
1
inputs/day17.txt
Normal file
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
8
src/bin/day17_1.rs
Normal 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
8
src/bin/day17_2.rs
Normal 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
224
src/day17.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -17,3 +17,4 @@ pub mod day13;
|
||||
pub mod day14;
|
||||
pub mod day15;
|
||||
pub mod day16;
|
||||
pub mod day17;
|
||||
|
||||
Reference in New Issue
Block a user