From 31c9a768ec950eb82a3bbf86222afbce68d38146 Mon Sep 17 00:00:00 2001 From: sepia Date: Thu, 19 Dec 2024 14:32:51 -0600 Subject: [PATCH] Day 19 --- src/day18.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/day19.rs | 48 +++++++++++++++++++++++++++++++++++ src/main.rs | 4 ++- 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/day18.rs create mode 100644 src/day19.rs diff --git a/src/day18.rs b/src/day18.rs new file mode 100644 index 0000000..563a43e --- /dev/null +++ b/src/day18.rs @@ -0,0 +1,71 @@ +use itertools::Itertools; + +use crate::util::maps::World; + +pub fn day18() { + let bytes: Vec<(usize, usize)> = crate::input(18) + .lines() + .map(|line| { + line.split(",") + .map(|b| b.parse::().unwrap()) + .collect_tuple() + .unwrap() + }) + .collect(); + + let world_with_bytes = |b| { + let mut world = World::from_2d_vec(vec![vec![false; 71]; 71]); + for &(x, y) in bytes.iter().take(b) { + world.set(x, y, true); + } + world + }; + + let mut low_bound = 0; + let mut high_bound = bytes.len(); + loop { + if high_bound - low_bound <= 1 { + break; + } + let i = low_bound + (high_bound - low_bound) / 2; + + let world = (world_with_bytes)(i); + let path_length = path_costs(&world, 0, 0).get(70, 70).unwrap(); + + if path_length == u64::MAX { + high_bound = i; + } else { + low_bound = i; + } + } + + let (last_byte_x, last_byte_y) = bytes[high_bound - 1]; + println!("After a byte falls at ({last_byte_x}, {last_byte_y}), there is no way out! The researchers perish!"); +} + +fn path_costs(world: &World, x: usize, y: usize) -> World { + fn calculate_path_costs( + world: &World, + x: usize, + y: usize, + cost_so_far: u64, + costs: &mut World, + ) { + let adjacents = world + .adjacent_locations(x, y) + .into_iter() + .filter(|&(nx, ny)| !world.get(nx, ny).unwrap()); + for (nx, ny) in adjacents { + let cost = cost_so_far + 1; + let old_cost = costs.get(nx, ny).unwrap(); + if cost < old_cost { + costs.set(nx, ny, cost); + calculate_path_costs(world, nx, ny, cost, costs) + } + } + } + + let mut costs = World::from_dimensions(world.width(), world.height(), u64::MAX); + calculate_path_costs(world, x, y, 0, &mut costs); + costs +} diff --git a/src/day19.rs b/src/day19.rs new file mode 100644 index 0000000..6f384cf --- /dev/null +++ b/src/day19.rs @@ -0,0 +1,48 @@ +use itertools::Itertools; +use rayon::prelude::*; +use std::collections::HashMap; + +pub fn day19() { + let input = crate::input(19); + let (towels_input, designs_input) = input.split("\n\n").collect_tuple().unwrap(); + let towels: Vec<&str> = towels_input.split(", ").collect(); + let designs: Vec<&str> = designs_input.lines().collect(); + + let ways_to_make_designs: Vec = designs + .par_iter() + .map(|design| make_pattern_from_towels(design, &towels)) + .collect(); + + let possible_designs_count = ways_to_make_designs + .iter() + .filter(|&&ways| ways != 0) + .count(); + println!("{possible_designs_count} designs are possible to make."); + + let total_ways: u64 = ways_to_make_designs.iter().sum(); + println!("{total_ways} ways to make a design are possible."); +} + +fn make_pattern_from_towels(design: &str, towels: &Vec<&str>) -> u64 { + fn make_pattern(design: &str, towels: &Vec<&str>, memo: &mut HashMap) -> u64 { + if let Some(&ways) = memo.get(design) { + return ways; + } + if design.is_empty() { + return 1; + } + + let ways = towels + .iter() + .map(|towel| match design.strip_prefix(towel) { + Some(subdesign) => make_pattern(subdesign, towels, memo), + None => 0, + }) + .sum(); + memo.insert(design.into(), ways); + ways + } + + let mut memo: HashMap = HashMap::new(); + make_pattern(design, towels, &mut memo) +} diff --git a/src/main.rs b/src/main.rs index 4cf489c..ebf949c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,10 +34,12 @@ mod day15; mod day16; #[allow(dead_code)] mod day17; +#[allow(dead_code)] mod day18; +mod day19; fn main() { - day18::day18(); + day19::day19(); } pub fn input(day: u8) -> String {