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 day14;
|
||||||
pub mod day15;
|
pub mod day15;
|
||||||
pub mod day16;
|
pub mod day16;
|
||||||
|
pub mod day17;
|
||||||
|
|||||||
Reference in New Issue
Block a user