use nom::{ branch::alt, bytes::complete::tag, combinator::{map, value}, multi::many1, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_17.txt")?; let nudges = Inputs::parser(&input).unwrap().1; let pieces = Piece::all(); let board = GameBoard::new(pieces, nudges.nudges); { let mut part_1_board = board.clone(); for _ in 0..2022 { part_1_board.drop_next_piece(); } dbg!(part_1_board.full_height()); } { let mut part_2_board = board.clone(); for i in 0..1_000_000_000_000u64 { if i % 1_000_000_000 == 0 { dbg!(i / 1_000_000_000, part_2_board.truncated); } part_2_board.drop_next_piece(); } dbg!(part_2_board.full_height()); } Ok(()) } #[derive(Debug, Clone)] struct Inputs { nudges: Vec, } #[derive(Debug, Clone)] enum Nudge { Left, Right, } #[derive(Debug, Clone)] struct Piece { data_at_x: Vec>, } impl Inputs { fn parser(input: &str) -> IResult<&str, Self> { map( many1(alt(( value(Nudge::Left, tag("<")), value(Nudge::Right, tag(">")), ))), |nudges| Inputs { nudges }, )(input) } } impl Piece { fn new(data_at_x_0: Vec) -> Piece { let mut shifting_data = data_at_x_0.clone(); let mut data_at_x = vec![data_at_x_0]; while shifting_data.iter().all(|b| b & 0b00000010 == 0) { for row in &mut shifting_data { *row >>= 1; } data_at_x.push(shifting_data.clone()); } Piece { data_at_x } } fn all() -> Vec { vec![ Piece::new(vec![0b11110000]), Piece::new(vec![0b01000000, 0b11100000, 0b01000000]), Piece::new(vec![0b11100000, 0b00100000, 0b00100000]), Piece::new(vec![0b10000000, 0b10000000, 0b10000000, 0b10000000]), Piece::new(vec![0b11000000, 0b11000000]), ] } } #[derive(Debug, Clone)] struct GameBoard { pieces: Vec, piece_index: usize, nudges: Vec, compound_nudges: Vec>, nudge_index: usize, solidified: Vec, truncated: u64, } impl GameBoard { fn new(pieces: Vec, nudges: Vec) -> GameBoard { GameBoard { compound_nudges: pieces .iter() .map(|piece| { (0..nudges.len()) .map(|nudge_start| { let mut piece_x = 2; for i in 0..4 { let nudge_i = (nudge_start + i) % nudges.len(); match nudges[nudge_i] { Nudge::Left => { if piece_x > 0 { piece_x -= 1; } } Nudge::Right => { if piece_x < piece.data_at_x.len() - 1 { piece_x += 1; } } } } piece_x }) .collect() }) .collect(), pieces, piece_index: 0, nudges, nudge_index: 0, solidified: Vec::new(), truncated: 0, } } fn drop_next_piece(&mut self) { let piece = &self.pieces[self.piece_index]; // precomputes the effect of the first 4 nudges let mut piece_x = self.compound_nudges[self.piece_index][self.nudge_index]; self.piece_index += 1; if self.piece_index >= self.pieces.len() { self.piece_index -= self.pieces.len(); } self.nudge_index += 4; if self.nudge_index >= self.nudges.len() { self.nudge_index -= self.nudges.len(); } let mut piece_y = self.first_open_row(); loop { if piece_y > 0 && self.piece_can_be_at(piece_y - 1, &piece.data_at_x[piece_x]) { piece_y -= 1; } else { break; } let nudge = &self.nudges[self.nudge_index]; self.nudge_index += 1; if self.nudge_index >= self.nudges.len() { self.nudge_index -= self.nudges.len(); } match nudge { Nudge::Left => { if piece_x > 0 && self.piece_can_be_at(piece_y, &piece.data_at_x[piece_x - 1]) { piece_x -= 1; } } Nudge::Right => { if piece_x < piece.data_at_x.len() - 1 && self.piece_can_be_at(piece_y, &piece.data_at_x[piece_x + 1]) { piece_x += 1; } } } } let mut to_truncate = None; for (i, piece_row) in piece.data_at_x[piece_x].iter().enumerate() { let full_index = piece_y + i; if full_index >= self.solidified.len() { self.solidified.push(piece_row.clone()); } else { self.solidified[full_index] |= piece_row; if self.solidified[full_index] == 0b11111110 { to_truncate = Some(full_index); } } } if let Some(to_truncate) = to_truncate { self.truncated += to_truncate as u64; let truncated_len = self.solidified.len() - to_truncate; for move_i in 0..truncated_len { self.solidified[move_i] = self.solidified[move_i + to_truncate]; } self.solidified.truncate(truncated_len); } } fn piece_can_be_at(&self, piece_y: usize, piece: &[u8]) -> bool { for (i, row) in piece.iter().enumerate() { let full_i = piece_y + i; if full_i >= self.solidified.len() { return true; } if self.solidified[full_i] & row != 0 { return false; } } return true; } fn first_open_row(&self) -> usize { self.solidified.len() } fn full_height(&self) -> u64 { self.first_open_row() as u64 + self.truncated } fn debug(&self) { println!("Clearerd: {}", self.truncated); for row in self.solidified.iter().rev() { println!("{:08b}", row); } } }