From c9d9dc12753f43a213f08eedf5abeaf6bd5db198 Mon Sep 17 00:00:00 2001 From: Tobias Marschner Date: Sun, 3 Mar 2024 08:26:52 +0100 Subject: [PATCH] Solution for 2022/day15-part2 --- 2022/day15-part2/Cargo.toml | 8 +++ 2022/day15-part2/src/main.rs | 117 +++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 2022/day15-part2/Cargo.toml create mode 100644 2022/day15-part2/src/main.rs diff --git a/2022/day15-part2/Cargo.toml b/2022/day15-part2/Cargo.toml new file mode 100644 index 0000000..c1e72ea --- /dev/null +++ b/2022/day15-part2/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day15-part2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2022/day15-part2/src/main.rs b/2022/day15-part2/src/main.rs new file mode 100644 index 0000000..e3cdb55 --- /dev/null +++ b/2022/day15-part2/src/main.rs @@ -0,0 +1,117 @@ +use std::collections::VecDeque; + +#[derive(Debug, Copy, Clone)] +struct Sensor { + sx: isize, + sy: isize, + bx: isize, + by: isize, +} + +impl Sensor { + // Determine the distance between sensor and beacon, using Manhattan metric. + fn beacon_distance(&self) -> isize { + (self.sx - self.bx).abs() + (self.sy - self.by).abs() + } + + // fn beacon_impossible(&self, x: isize, y: isize) -> bool { + // let d = (self.sx - x).abs() + (self.sy - y).abs(); + // d <= self.beacon_distance() + // } + + // Return a range for a given y-coordinate where no beacon could + // possibly be present for this sensor. + fn covered_in_line(&self, y: isize) -> Option<(isize, isize)> { + let d = self.beacon_distance() - (self.sy - y).abs(); + if d < 0 { + None + } else { + Some(((self.sx - d), (self.sx + d))) + } + } +} + +// Collapse multiple ranges into the minimal set of ranges. +fn collapse_ranges(ranges: &mut VecDeque<(isize, isize)>) { + // First, sort by minimal value.. + ranges + .make_contiguous() + .sort_unstable_by(|a, b| a.0.cmp(&b.0)); + + // Then continually merge pairs throughout the VecDeque. + let mut idx: usize = 0; + while idx + 1 < ranges.len() { + // Grab the current pair. + let a = ranges[idx]; + let b = ranges[idx + 1]; + // Check if the minimal vaue of b is contained in a. + // In that case we can merge the two. + if a.0 <= b.0 && b.0 <= a.1 + 1 { + // Remove the first of the pair. + ranges.remove(idx); + // And update the second element. + // (which just moved from idx+1 to idx) + ranges[idx] = (a.0, a.1.max(b.1)); + } else { + // If no merge took place, move on to the next pair. + idx += 1; + } + } +} + +fn main() { + // Use command line arguments to specify the input filename. + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + panic!("Usage: ./main \nNo input file provided. Exiting."); + } + + // Next, read the contents of the input file into a string for easier processing. + let input = std::fs::read_to_string(&args[1]).expect("Error opening file"); + + // --- TASK BEGIN --- + + // Parse the input. + let mut sensors: Vec = Vec::new(); + for line in input.lines() { + let line = line + .split('=') + .skip(1) + .map(|x| { + x.chars() + .take_while(|c| *c != ',' && *c != ':') + .collect::() + }) + .map(|e| e.parse::().unwrap()) + .collect::>(); + sensors.push(Sensor { + sx: line[0], + sy: line[1], + bx: line[2], + by: line[3], + }) + } + + // Iterate over all possible lines. + for y in 0..4000000 { + // Collect all the ranges in line y where no beacons could be. + let mut ranges: VecDeque<(isize, isize)> = VecDeque::new(); + for s in &sensors { + let r = s.covered_in_line(y); + // Only collect non-empty ranges, ofc. + if let Some(sr) = r { + ranges.push_back(sr); + } + } + // Next up, sort + collapse the ranges. + collapse_ranges(&mut ranges); + // Check if there is a gap in the ranges. + // This is likely the spot we're looking for. + if ranges.len() > 1 { + // dbg!(&ranges); + let x = ranges[0].1 + 1; + println!("Gap spotted: ({},{})", x, y); + println!("Tuning frequency: {}", x * 4000000 + y); + } + } +}