diff --git a/.run/run-day-15.run.xml b/.run/run-day-15.run.xml new file mode 100644 index 0000000..6564ac8 --- /dev/null +++ b/.run/run-day-15.run.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/src/day15.rs b/src/day15.rs index cd3f4a6..6c56600 100644 --- a/src/day15.rs +++ b/src/day15.rs @@ -7,7 +7,7 @@ use crate::{day_solver::DaySolver, util::Coord}; use super::util; pub struct Day15 { - beacon_scanners: Vec + beacon_scanners: Vec } impl Day15 { @@ -23,51 +23,36 @@ impl Day15 { return Day15 { beacon_scanners: lines.iter().map(|s| { let cap = re.captures_iter(s).next().unwrap(); - BeaconScanner { - sensor: Coord { x: cap[1].parse::().unwrap(), y: cap[2].parse::().unwrap() }, - closest_beacon: Coord { x: cap[3].parse::().unwrap(), y: cap[4].parse::().unwrap() } - } + BeaconSensor::new( + Coord { x: cap[1].parse::().unwrap(), y: cap[2].parse::().unwrap() }, + Coord { x: cap[3].parse::().unwrap(), y: cap[4].parse::().unwrap() } + ) }).collect() } } -} - -impl DaySolver for Day15 { - - - fn solve_part1(&mut self) -> String { - - // print!("{:?}", self.beacon_scanners); - - let check_y = if self.beacon_scanners.len() < 20 { 10 } else { 2_000_000 }; - - let mut ranges_covered: Vec = self.beacon_scanners.iter().filter_map(|bs| { - let sensor_dist = bs.sensor.manhattan_dist(&bs.closest_beacon); - let dist_to_check_y = bs.sensor.y.abs_diff(check_y) as i32; - let coverage_width = sensor_dist - dist_to_check_y; - // println!("Sensor at {},{} sees beacon {},{} at distance {}, and distance {} to check_y. So it covers {} cells", + fn find_ranges_without_beacons_at_y(&mut self, y: i32) -> Vec { + self.beacon_scanners.iter().filter_map(|bs| { + let dist_to_check_y = bs.sensor.y.abs_diff(y) as i32; + let coverage_width = bs.distance - dist_to_check_y; + // println!("Sensor at {},{} sees beacon {},{} at distance {}, and distance {} to check_y. So it covers {} cells", // bs.sensor.x, bs.sensor.y, bs.closest_beacon.x, bs.closest_beacon.y, sensor_dist, dist_to_check_y, coverage_width); if coverage_width < 0 { None } else { // println!("Adding"); - Some(Range{ + Some(Range { min: bs.sensor.x - coverage_width, - max: bs.sensor.x + coverage_width + max: bs.sensor.x + coverage_width }) } - }).collect(); + }).collect() + } - if ranges_covered.is_empty() { - return 0.to_string(); - } - - // println!("Ranges covered: {:?}", ranges_covered); - - // Now we need to merge all overlapping rangen into one, so we don't count any double ones: - // Note: there can be no beacons inside the sensor ranges, because if there were, the sensor - // would pick up that one and have a smaller range + fn merge_overlapping_ranges(ranges_covered: &mut Vec) -> Vec { + // Now we need to merge all overlapping ranges into one, so we don't count any double ones: + // Note: there can be no beacons inside the sensor ranges, because if there were, the sensor + // would pick up that one and have a smaller range ranges_covered.sort_by_key(|r| r.min); // println!("Ranges covered: {:?}", ranges_covered); @@ -85,6 +70,31 @@ impl DaySolver for Day15 { } } merged_ranges.push(current_range); + merged_ranges + } +} + +impl DaySolver for Day15 { + + + fn solve_part1(&mut self) -> String { + + // print!("{:?}", self.beacon_scanners); + + // If we sort the beacon scanner by their x-coordinate, range-merging will be faster later on in the process + self.beacon_scanners.sort_by_key(|bs| bs.sensor.x); + + let check_y = if self.beacon_scanners.len() < 20 { 10 } else { 2_000_000 }; + + let mut ranges_covered = self.find_ranges_without_beacons_at_y(check_y); + + if ranges_covered.is_empty() { + return 0.to_string(); + } + + // println!("Ranges covered: {:?}", ranges_covered); + + let merged_ranges = Self::merge_overlapping_ranges(&mut ranges_covered); let beacons_on_check_y = self.beacon_scanners.iter() .filter(|bs| bs.closest_beacon.y == check_y) @@ -99,14 +109,41 @@ impl DaySolver for Day15 { } fn solve_part2(&mut self) -> String { - return 0.to_string(); + + // println!("{:?}", self.beacon_scanners.iter().filter(|bs| bs.distance > 2_000_000).collect::>()); + let check_until = if self.beacon_scanners.len() < 20 { 20 } else { 4_000_000 }; + let target_range = Range { min: 0, max: check_until }; + for y in 0..(check_until+1) { + let ranges_covered = Self::merge_overlapping_ranges(&mut self.find_ranges_without_beacons_at_y(y)); + if !ranges_covered.iter().any(|r| r.covers(&target_range)) { + // println!("Found solution at y={}", y); + let x = ranges_covered.iter() + .filter(|r| r.max < check_until && r.max >= 0) + .next() + .unwrap().max + 1; + return (4000000i64 * x as i64 + y as i64).to_string(); + } + + } + + panic!("Couldn't find the answer :(") } } #[derive(Debug, Clone)] -struct BeaconScanner { +struct BeaconSensor { sensor: Coord, - closest_beacon: Coord + closest_beacon: Coord, + distance: i32 +} + +impl BeaconSensor { + fn new(sensor: Coord, closest_beacon: Coord) -> Self { + BeaconSensor { + sensor, closest_beacon, + distance: sensor.manhattan_dist(&closest_beacon) + } + } } #[derive(Debug, Clone)] @@ -119,6 +156,9 @@ impl Range { pub fn overlaps(&self, other: &Range) -> bool { self.min <= other.max && self.max >= other.min } + pub fn covers(&self, other: &Range) -> bool { + self.min <= other.min && self.max >= other.max + } pub fn in_range(&self, x: &i32) -> bool { self.min.le(&x) && self.max.ge(&x)