diff --git a/src/bin/d17p2.rs b/src/bin/d17p2.rs new file mode 100644 index 0000000..3a4acd5 --- /dev/null +++ b/src/bin/d17p2.rs @@ -0,0 +1,13 @@ +use aoc22::{day17, util}; + +const BLOCKS: usize = 1_000_000_000_000; + +pub fn main() { + let jets = day17::parse_jets(&util::parse_input()); + + println!( + "Height after {} blocks: {}", + BLOCKS, + day17::do_moves(&jets, BLOCKS) + ); +} diff --git a/src/day17.rs b/src/day17.rs index dca034b..1d9fd0d 100644 --- a/src/day17.rs +++ b/src/day17.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use once_cell::sync::OnceCell; use crate::util; @@ -148,14 +150,45 @@ impl Block { type VentRow = u8; +const CYCLE_SIZE: usize = 5; + pub fn do_moves(jets: &Vec, n_blocks: usize) -> usize { let mut block_i = 0; let mut current_block: Option = None; let mut vent = vec![0; 4]; + // {jet_i: {round: (height, block_i)}} + let mut block_cycles: HashMap> = HashMap::new(); + let mut skipped_height: Option = None; + let mut round = 0; while block_i <= n_blocks { + let jet_i = round % jets.len(); + if current_block.is_none() { + if block_i % N_BLOCK_TYPES == 0 { + if skipped_height.is_none() { + let entry = block_cycles.entry(jet_i).or_insert_with(HashMap::new); + entry.insert(round, (vent.len() - empty_rows(&vent), block_i)); + if let Some((cycle_height, cycle_blocks)) = find_cycle(entry, CYCLE_SIZE) { + println!( + "Found cycle ({} height per {} blocks) after {} rounds!", + cycle_height, cycle_blocks, round + ); + + let remaining_blocks = n_blocks - block_i; + let cycles = remaining_blocks / cycle_blocks; + block_i += cycles * cycle_blocks; + skipped_height = Some(cycles * cycle_height); + + println!( + "Skipping {} blocks, {} height", + cycles * cycle_blocks, + cycles * cycle_height + ); + } + } + } current_block = Some(spawn_block(&mut vent, block_i % N_BLOCK_TYPES)); // println!("Vent as block {} begins falling:", block_i); // print_vent(&vent, current_block.as_ref()); @@ -165,7 +198,7 @@ pub fn do_moves(jets: &Vec, n_blocks: usize) -> usize { let block = current_block.as_mut().unwrap(); // Push left/right - let jet = jets[round % jets.len()]; + let jet = jets[jet_i]; if block.can_push(&vent, jet) { let (_, ref mut col) = block.pos; *col = ((*col as isize) - jet.shift()) as usize; @@ -182,7 +215,7 @@ pub fn do_moves(jets: &Vec, n_blocks: usize) -> usize { round += 1; } - vent.len() - empty_rows(&vent) + vent.len() - empty_rows(&vent) + skipped_height.unwrap_or(0) } const SPAWN_OFFSET_X: usize = 2; @@ -244,3 +277,41 @@ pub fn print_vent(vent: &Vec, block: Option<&Block>) { } println!("+-------+"); } + +fn find_cycle( + jet_heights: &HashMap, + n_cycles: usize, +) -> Option<(usize, usize)> { + if jet_heights.len() < n_cycles { + return None; + } + + for (init_round, (init_height, init_blocks)) in jet_heights { + 'next_loop: for (next_round, (next_height, next_blocks)) in jet_heights { + if init_round == next_round || *next_height < *init_height { + continue; + } + + let cycle = next_round - init_round; + let cycle_height = next_height - init_height; + let cycle_blocks = next_blocks - init_blocks; + for n in 2..=n_cycles { + let round = init_round + n * cycle; + let entry = jet_heights.get(&round); + if let Some((height, blocks)) = entry { + let expected_height = init_height + n * cycle_height; + let expected_blocks = init_blocks + n * cycle_blocks; + if *height != expected_height || *blocks != expected_blocks { + continue 'next_loop; + } + } else { + continue 'next_loop; + } + } + + return Some((cycle_height, cycle_blocks)); + } + } + + None +}