From 2ae5cce33d37a38ad3e66df200b3c8d7859603bb Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 14 Dec 2022 23:37:58 +0800 Subject: [PATCH] day 14 this is the part where the challenges start to get hard. implementing the naive solution for part 1 did not take much time. used a hashset since it's easier than using arrays, but slightly less performant. I expected the naive solution would not work for part 2 due to the size of the problem but it still worked, although it did take a few seconds to finish. the solution i would have used if part 2 took too long would be to trace some 45* rays from the end points of horizontal beams and subtract the area inside from the largest sand triangle. --- day_14/src/main.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/day_14/src/main.rs b/day_14/src/main.rs index e7a11a9..eb2d2c6 100644 --- a/day_14/src/main.rs +++ b/day_14/src/main.rs @@ -1,3 +1,112 @@ -fn main() { - println!("Hello, world!"); +use std::{collections::HashSet, fs::read_to_string, ops::RangeInclusive}; + +const PATH: &str = "src/input"; + +fn parse_pt(p_str: &str) -> Vec { + p_str + .split(',') + .map(|x| x.parse::().unwrap()) + .collect::>() +} + +fn any_range(a: i32, b: i32) -> RangeInclusive { + if a < b { + return a..=b; + } + return b..=a; +} + +const SAND_SOURCE: (i32, i32) = (500, 0); + +fn main() { + let data = read_to_string(PATH).expect("Error reading file"); + assert!(data.is_ascii()); + println!("PART 1 {}", sand_sim(data, true)); + let data = read_to_string(PATH).expect("Error reading file"); + assert!(data.is_ascii()); + println!("PART 2 {}", sand_sim(data, false)); +} + +fn sand_sim(data: String, part_1: bool) -> u32 { + // TODO: DETERMINE MAX DEPTH AUTOMATICALLY. + let (mut walls, max_depth) = scan_walls(data); + + let mut stop_sand = false; + let mut sand_units = 0; + while !stop_sand && !walls.contains(&SAND_SOURCE) { + let mut sand_pos = SAND_SOURCE; + + loop { + // If the tile immediately below is blocked (by rock or sand), ... + if walls.contains(&(sand_pos.0, sand_pos.1 + 1)) { + // ... the unit of sand attempts to instead move diagonally one step down and to the left + if !walls.contains(&(sand_pos.0 - 1, sand_pos.1 + 1)) { + sand_pos.0 -= 1; + // If that tile is blocked, the unit of sand attempts to instead move diagonally one step down and to the right + } else if !walls.contains(&(sand_pos.0 + 1, sand_pos.1 + 1)) { + sand_pos.0 += 1; + // If all three possible destinations are blocked, the unit of sand comes to rest and no longer moves + } else { + walls.insert(sand_pos); + sand_units += 1; + break; + } + } + if sand_pos.1 > max_depth { + walls.insert(sand_pos); + if part_1 { + stop_sand = true; + } + sand_units += 1; + break; + } + // A unit of sand always falls down one step if possible. + sand_pos.1 += 1; + } + } + // visualize_sand(walls, max_depth); + return sand_units; +} + +fn scan_walls(data: String) -> (HashSet<(i32, i32)>, i32) { + let paths = data.split('\n'); + let mut walls = HashSet::<(i32, i32)>::new(); + let mut max_depth = 0; + for path in paths { + let mut points = path.split(" -> ").into_iter(); + let mut previous_point = parse_pt(points.next().unwrap()); + while let Some(next_point) = points.next() { + let next_point = parse_pt(next_point); + if previous_point[1] > max_depth { + max_depth = previous_point[1]; + } + if next_point[1] > max_depth { + max_depth = next_point[1]; + } + // println!( + // "{} {} -> {} {}", + // previous_point[0], previous_point[1], next_point[0], next_point[1] + // ); + for x in any_range(previous_point[0], next_point[0]) { + for y in any_range(previous_point[1], next_point[1]) { + walls.insert((x, y)); + } + } + previous_point = next_point; + } + } + return (walls, max_depth); +} + +fn visualize_sand(walls: HashSet<(i32, i32)>, max_depth: i32) { + for j in 0..=max_depth + 2 { + for i in 480..550 { + if walls.contains(&(i, j)) { + print!("#"); + } else { + print!("."); + } + } + println!(); + } }