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)