diff --git a/Cargo.lock b/Cargo.lock index bdbf4e5..0730a53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "nohash-hasher", + "num", "regex", "rustc-hash", ] @@ -21,6 +22,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "lazy_static" version = "1.4.0" @@ -39,6 +46,82 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "regex" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index d9d4cd0..9619a6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ edition = "2021" regex = "1" lazy_static = "1.4.0" rustc-hash = "1.1.0" -nohash-hasher = "0.2.0" \ No newline at end of file +nohash-hasher = "0.2.0" +num = "0.4.0" diff --git a/input/day15.txt b/input/day15.txt new file mode 100644 index 0000000..26fe777 --- /dev/null +++ b/input/day15.txt @@ -0,0 +1,31 @@ +Sensor at x=3923513, y=2770279: closest beacon is at x=3866712, y=2438950 +Sensor at x=675683, y=3223762: closest beacon is at x=-224297, y=2997209 +Sensor at x=129453, y=2652332: closest beacon is at x=92656, y=2629486 +Sensor at x=3906125, y=2154618: closest beacon is at x=3866712, y=2438950 +Sensor at x=65723, y=902062: closest beacon is at x=92656, y=2629486 +Sensor at x=3137156, y=2876347: closest beacon is at x=2907507, y=3100765 +Sensor at x=32848, y=2676435: closest beacon is at x=92656, y=2629486 +Sensor at x=3272472, y=3445147: closest beacon is at x=2907507, y=3100765 +Sensor at x=2926008, y=128948: closest beacon is at x=3089364, y=-501737 +Sensor at x=2975, y=2769838: closest beacon is at x=92656, y=2629486 +Sensor at x=3540455, y=2469135: closest beacon is at x=3866712, y=2438950 +Sensor at x=3674809, y=2062166: closest beacon is at x=3719980, y=2000000 +Sensor at x=3693706, y=2027384: closest beacon is at x=3719980, y=2000000 +Sensor at x=3869683, y=2291983: closest beacon is at x=3866712, y=2438950 +Sensor at x=2666499, y=2796436: closest beacon is at x=2650643, y=2489479 +Sensor at x=492, y=2601991: closest beacon is at x=92656, y=2629486 +Sensor at x=2710282, y=3892347: closest beacon is at x=2907507, y=3100765 +Sensor at x=28974, y=3971342: closest beacon is at x=-224297, y=2997209 +Sensor at x=3990214, y=2399722: closest beacon is at x=3866712, y=2438950 +Sensor at x=3853352, y=1009020: closest beacon is at x=3719980, y=2000000 +Sensor at x=1231833, y=3999338: closest beacon is at x=1313797, y=4674300 +Sensor at x=2083669, y=875035: closest beacon is at x=1369276, y=-160751 +Sensor at x=1317274, y=2146819: closest beacon is at x=2650643, y=2489479 +Sensor at x=3712875, y=2018770: closest beacon is at x=3719980, y=2000000 +Sensor at x=963055, y=23644: closest beacon is at x=1369276, y=-160751 +Sensor at x=3671967, y=64054: closest beacon is at x=3089364, y=-501737 +Sensor at x=3109065, y=2222392: closest beacon is at x=2650643, y=2489479 +Sensor at x=3218890, y=1517419: closest beacon is at x=3719980, y=2000000 +Sensor at x=3856777, y=3987650: closest beacon is at x=4166706, y=3171774 +Sensor at x=1912696, y=3392788: closest beacon is at x=2907507, y=3100765 +Sensor at x=3597620, y=3100104: closest beacon is at x=4166706, y=3171774 \ No newline at end of file diff --git a/input/day15_example.txt b/input/day15_example.txt new file mode 100644 index 0000000..652e631 --- /dev/null +++ b/input/day15_example.txt @@ -0,0 +1,14 @@ +Sensor at x=2, y=18: closest beacon is at x=-2, y=15 +Sensor at x=9, y=16: closest beacon is at x=10, y=16 +Sensor at x=13, y=2: closest beacon is at x=15, y=3 +Sensor at x=12, y=14: closest beacon is at x=10, y=16 +Sensor at x=10, y=20: closest beacon is at x=10, y=16 +Sensor at x=14, y=17: closest beacon is at x=10, y=16 +Sensor at x=8, y=7: closest beacon is at x=2, y=10 +Sensor at x=2, y=0: closest beacon is at x=2, y=10 +Sensor at x=0, y=11: closest beacon is at x=2, y=10 +Sensor at x=20, y=14: closest beacon is at x=25, y=17 +Sensor at x=17, y=20: closest beacon is at x=21, y=22 +Sensor at x=16, y=7: closest beacon is at x=15, y=3 +Sensor at x=14, y=3: closest beacon is at x=15, y=3 +Sensor at x=20, y=1: closest beacon is at x=15, y=3 \ No newline at end of file diff --git a/src/day15.rs b/src/day15.rs new file mode 100644 index 0000000..cd3f4a6 --- /dev/null +++ b/src/day15.rs @@ -0,0 +1,126 @@ +use std::collections::HashSet; + +use regex::Regex; + +use crate::{day_solver::DaySolver, util::Coord}; + +use super::util; + +pub struct Day15 { + beacon_scanners: Vec +} + +impl Day15 { + + pub fn create() -> Self { + // let lines = util::read_file("input/day15_example.txt"); + let lines = util::read_file("input/day15.txt"); + + // Lines look like: + // "Sensor at x=2, y=18: closest beacon is at x=-2, y=15" + let re = Regex::new(r"Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)").unwrap(); + // Put the input into the day struct + 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() } + } + }).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", + // 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{ + min: bs.sensor.x - coverage_width, + max: bs.sensor.x + coverage_width + }) + } + }).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 + ranges_covered.sort_by_key(|r| r.min); + + // println!("Ranges covered: {:?}", ranges_covered); + + let mut merged_ranges: Vec = Vec::new(); + let mut ranges_covered_iter = ranges_covered.iter(); + let mut current_range = ranges_covered_iter.next().unwrap().clone(); + while let Some(next_range) = ranges_covered_iter.next() { + if current_range.overlaps(next_range) { + current_range.max = next_range.max.max(current_range.max); + } else { + // New range! + merged_ranges.push(current_range); + current_range = next_range.clone(); + } + } + merged_ranges.push(current_range); + + let beacons_on_check_y = self.beacon_scanners.iter() + .filter(|bs| bs.closest_beacon.y == check_y) + .map(|bs| bs.closest_beacon.x) + .filter(|x| merged_ranges.iter().any(|r| r.in_range(&x))) + .collect::>(); + + // println!("Merged ranges: {:?}", merged_ranges); + + return (merged_ranges.iter().map(|r| r.max - r.min + 1).sum::() - beacons_on_check_y.len() as i32).to_string(); + + } + + fn solve_part2(&mut self) -> String { + return 0.to_string(); + } +} + +#[derive(Debug, Clone)] +struct BeaconScanner { + sensor: Coord, + closest_beacon: Coord +} + +#[derive(Debug, Clone)] +struct Range { + min: i32, + max: i32 +} + +impl Range { + pub fn overlaps(&self, other: &Range) -> bool { + self.min <= other.max && self.max >= other.min + } + + pub fn in_range(&self, x: &i32) -> bool { + self.min.le(&x) && self.max.ge(&x) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 81a5052..a2cda99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use crate::day11::Day11; use crate::day12::Day12; use crate::day13::Day13; use crate::day14::Day14; +use crate::day15::Day15; use crate::day_solver::DaySolver; mod util; @@ -31,8 +32,9 @@ mod day11; mod day12; mod day13; mod day14; +mod day15; -const MAX_DAY: u8 = 14; +const MAX_DAY: u8 = 15; const DEFAULT_BENCHMARK_AMOUNT: u32 = 100; fn main() { @@ -110,6 +112,7 @@ fn build_day_solver(day: u8) -> Option> { 12 => Some(Box::new(Day12::create())), 13 => Some(Box::new(Day13::create())), 14 => Some(Box::new(Day14::create())), + 15 => Some(Box::new(Day15::create())), _ => None } } diff --git a/src/util.rs b/src/util.rs index c3bdbdd..53a0aab 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,8 @@ use std::fmt::Display; use std::fs; +use num::Integer; + pub fn read_file(filename: &str) -> Vec { let contents = fs::read_to_string(filename) @@ -82,3 +84,9 @@ pub struct Coord where T: Sized { pub y: T } +impl Coord where T: Integer + Copy { + pub fn manhattan_dist(&self, other: &Coord) -> T { + self.x.max(other.x).sub(self.x.min(other.x)) + self.y.max(other.y).sub(self.y.min(other.y)) + } +} +