From 89461b9866914029747d4a7a4a79aeae42747c6e Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Thu, 15 Dec 2022 16:49:19 -0500 Subject: [PATCH] Add aoc 2022 day 15 --- 2022/15/Cargo.lock | 14 +++ 2022/15/Cargo.toml | 9 ++ 2022/15/input | 40 +++++++ 2022/15/src/main.rs | 266 +++++++++++++++++++++++++++++++++++++++++ 2022/15/test_input | 14 +++ 2022/common/src/lib.rs | 6 + 6 files changed, 349 insertions(+) create mode 100644 2022/15/Cargo.lock create mode 100644 2022/15/Cargo.toml create mode 100644 2022/15/input create mode 100644 2022/15/src/main.rs create mode 100644 2022/15/test_input diff --git a/2022/15/Cargo.lock b/2022/15/Cargo.lock new file mode 100644 index 0000000..039f4c5 --- /dev/null +++ b/2022/15/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "common" +version = "0.1.0" + +[[package]] +name = "day15" +version = "0.1.0" +dependencies = [ + "common", +] diff --git a/2022/15/Cargo.toml b/2022/15/Cargo.toml new file mode 100644 index 0000000..33fbc23 --- /dev/null +++ b/2022/15/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day15" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +common = { path = "../common" } \ No newline at end of file diff --git a/2022/15/input b/2022/15/input new file mode 100644 index 0000000..e663ca5 --- /dev/null +++ b/2022/15/input @@ -0,0 +1,40 @@ +Sensor at x=1112863, y=496787: closest beacon is at x=1020600, y=2000000 +Sensor at x=2980210, y=1712427: closest beacon is at x=2946825, y=1712605 +Sensor at x=2799204, y=1425283: closest beacon is at x=2946825, y=1712605 +Sensor at x=3999908, y=2754283: closest beacon is at x=4064129, y=2651511 +Sensor at x=760990, y=1455625: closest beacon is at x=1020600, y=2000000 +Sensor at x=3996490, y=3239979: closest beacon is at x=4064129, y=2651511 +Sensor at x=3347352, y=3603589: closest beacon is at x=3621840, y=3614596 +Sensor at x=2888433, y=2337157: closest beacon is at x=2946825, y=1712605 +Sensor at x=3423261, y=2191958: closest beacon is at x=3153728, y=1862250 +Sensor at x=1160237, y=3999960: closest beacon is at x=109153, y=3585462 +Sensor at x=693519, y=3701289: closest beacon is at x=109153, y=3585462 +Sensor at x=2615270, y=2824808: closest beacon is at x=2554122, y=2935074 +Sensor at x=3046971, y=1755494: closest beacon is at x=2946825, y=1712605 +Sensor at x=139591, y=1186912: closest beacon is at x=1020600, y=2000000 +Sensor at x=2309134, y=47090: closest beacon is at x=3211831, y=-792661 +Sensor at x=1849154, y=1377259: closest beacon is at x=2946825, y=1712605 +Sensor at x=2515971, y=2851853: closest beacon is at x=2554122, y=2935074 +Sensor at x=2524614, y=2738138: closest beacon is at x=2554122, y=2935074 +Sensor at x=3811778, y=1370280: closest beacon is at x=3153728, y=1862250 +Sensor at x=2615590, y=3819371: closest beacon is at x=2554122, y=2935074 +Sensor at x=3996286, y=3719213: closest beacon is at x=3621840, y=3614596 +Sensor at x=3963152, y=2368927: closest beacon is at x=4064129, y=2651511 +Sensor at x=3495504, y=3076982: closest beacon is at x=3621840, y=3614596 +Sensor at x=3725521, y=2560764: closest beacon is at x=4064129, y=2651511 +Sensor at x=952643, y=2385401: closest beacon is at x=1020600, y=2000000 +Sensor at x=3934384, y=2596106: closest beacon is at x=4064129, y=2651511 +Sensor at x=3060628, y=3082730: closest beacon is at x=2554122, y=2935074 +Sensor at x=3468382, y=3916817: closest beacon is at x=3621840, y=3614596 +Sensor at x=3300107, y=469364: closest beacon is at x=3211831, y=-792661 +Sensor at x=2306388, y=1932261: closest beacon is at x=2946825, y=1712605 +Sensor at x=1965, y=3514070: closest beacon is at x=109153, y=3585462 +Sensor at x=3081537, y=1841861: closest beacon is at x=3153728, y=1862250 +Sensor at x=2997643, y=1729779: closest beacon is at x=2946825, y=1712605 +Sensor at x=21714, y=3624181: closest beacon is at x=109153, y=3585462 +Sensor at x=1549467, y=3109269: closest beacon is at x=2554122, y=2935074 +Sensor at x=3722307, y=3839410: closest beacon is at x=3621840, y=3614596 +Sensor at x=3848580, y=3544878: closest beacon is at x=3621840, y=3614596 +Sensor at x=1189516, y=2153239: closest beacon is at x=1020600, y=2000000 +Sensor at x=468190, y=1889204: closest beacon is at x=1020600, y=2000000 +Sensor at x=270403, y=2762568: closest beacon is at x=109153, y=3585462 diff --git a/2022/15/src/main.rs b/2022/15/src/main.rs new file mode 100644 index 0000000..9e70a24 --- /dev/null +++ b/2022/15/src/main.rs @@ -0,0 +1,266 @@ +use std::str::FromStr; + +use common::Point; + +fn main() { + let input_file = common::parse_args_input_file(&mut std::env::args()); + let contents = std::fs::read_to_string(input_file.clone()).expect("Failed to read input file"); + let mut sensors = std::vec::Vec::<(Point, Point)>::new(); + let mut occupied = std::collections::HashMap::::new(); + for line in contents.lines() { + let r = parse_line(line); + if r.is_none() { + continue; + } + sensors.push(r.unwrap().clone()); + } + + let skip_other_than_row_to_count = true; + let row_to_count = if input_file.eq("test_input") { 10 } else { 2_000_000 }; + let coord_min = 0; + let coord_max = if input_file.eq("test_input") { 20 } else { 4_000_000 }; + let mut x_min = i32::MAX; + let mut x_max = i32::MIN; + let mut y_min = i32::MAX; + let mut y_max = i32::MIN; + for (index, (sensor, beacon)) in sensors.iter().enumerate() { + let distance = sensor.manhattan_distance(beacon) as i32; + occupied.insert(*sensor, Thing::Sensor); + occupied.insert(*beacon, Thing::Beacon); + // if sensor.x != 8 && sensor.y != 7 { + // continue; + // } + // println!("{:?}", sensor); + for y in sensor.y-distance..=sensor.y+distance { + if skip_other_than_row_to_count && y != row_to_count { + continue; + } + for x in sensor.x-distance..=sensor.x+distance { + let p = Point { x: x, y: y }; + if sensor.manhattan_distance(&p) <= distance.try_into().unwrap() { + if !occupied.contains_key(&p) { + occupied.insert(p, Thing::LogicallyEmpty); + x_min = std::cmp::min(x_min, p.x); + x_max = std::cmp::max(x_max, p.x); + y_min = std::cmp::min(y_min, p.y); + y_max = std::cmp::max(y_max, p.y); + } + } + } + } + println!("Processed beacon {} of {}", index + 1, sensors.len()); + } + + let mut count = 0; + for x in x_min..=x_max { + match occupied.get(&Point{ x: x, y: row_to_count }) { + Some(v) => { + match v { + Thing::LogicallyEmpty => {count += 1;}, + _ => {}, + }; + }, + None => {}, + }; + } + if input_file.eq("test_input") { + for y in y_min..y_max { + let mut s = String::new(); + let prefix = format!("{:3} ", y); + s.push_str(prefix.as_str()); + for x in x_min..x_max { + let p = Point { x: x, y: y }; + match occupied.get(&p) { + Some(v) => { + match v { + Thing::Beacon => { s.push('B'); }, + Thing::Sensor => { s.push('S'); }, + Thing::LogicallyEmpty => { s.push('#'); }, + }; + }, + None => { s.push('.'); }, + }; + } + println!("{}", s); + } + } + println!("[PART 1] {}", count); + + // Brute force approach by checking each point. + if false { + let mut distress_beacon: Option = None; + for y in coord_min..=coord_max { + if distress_beacon.is_some() { + break; + } + for x in coord_min..=coord_max { + let mut occupiable = true; + let p = Point {x: x, y: y}; + for (sensor, beacon) in sensors.iter() { + let distance = sensor.manhattan_distance(beacon); + if sensor.manhattan_distance(&p) <= distance { + occupiable = false; + break; + } + } + if occupiable { + match occupied.get(&p) { + Some(v) => { + match v { + Thing::Sensor => { continue; }, + Thing::Beacon => { continue; }, + _ => {}, + }; + }, + None => {}, + }; + assert!(distress_beacon.is_none(), "Duplicate distress beacon"); + distress_beacon = Some(p); + } + } + } + println!("[PART 2] {}", distress_beacon.unwrap().x * 4000000 + distress_beacon.unwrap().y); + } + + // One though that I had is that is could be possible to create a polygon representing + // the acceptable area, and then "subtract" the intersecting areas from the other areas + // created by each sensor/beacon combination. Visually this makes a lot of sense. The points + // we have set up until now are integer coordinates, and we might have to switch or work + // around that in some way. + // + // Another approach could be to take a line from A->B, for example, and then see if it + // fits entirely within the bounds of a given sensor/beacon pair. If it does, the entire + // line can be excluded pretty easily. In the case that it doesn't, the line could be + // subdivded in two eg. A1->B1, A2->B2, and each of those smaller lines checked. For + // a given line of size the worst case (checking a line where the length is 1) would + // be slower than simply checking each point individually. + // + // Perhaps a property that could be exploitable is take advantage of the lines produced + // by sensor areas being "diagonal" with respect to the grid. Would checking diagonal lines + // be faster somehow? + // + // After reading some of the comments on reddit: + // * use squares instead of lines + // * calculate perimeter points + 1 for each sensor, and check only those to reduce + // the number of checks + // * there is some sort of geometric solution, but what I don't know + // + + // This is a perimeter bounded solution + let mut potential_beacon_spots = std::vec::Vec::::new(); + let mut sensor_distances = std::vec::Vec::<(Point, u32)>::new(); + for (sensor, beacon) in sensors.iter() { + let d = sensor.manhattan_distance(beacon); + sensor_distances.push((sensor.clone(), d)); + } + + let mut potential_spots = std::collections::HashSet::::new(); + for (sensor, distance) in sensor_distances.iter() { + let d = (distance + 1) as i32; + for i in 0..=d { + let offset = d - i; + // left to top + let left = Point{x: sensor.x - offset, y: sensor.y + i}; + // top to right + let top = Point{x: sensor.x + i, y: sensor.y + offset}; + // right to bottom + let right = Point{x: sensor.x + offset, y: sensor.y - i}; + // bottom to left + let bottom = Point{x: sensor.x - i, y: sensor.y - offset}; + + for p in [left,top,right,bottom] { + if p.x > coord_max || p.x < coord_min || p.y > coord_max || p.y < coord_min { + continue; + } + potential_spots.insert(p); + } + } + } + let mut potential_spots2 = std::collections::HashSet::::new(); + let mut comparisons = 0; + for point in potential_spots.iter() { + let mut ok = true; + comparisons += 1; + for (index, (sensor, distance)) in sensor_distances.iter().enumerate() { + if sensor.manhattan_distance(point) <= *distance { + ok = false; + break; + } + } + if ok { + potential_spots2.insert(*point); + } + } + for point in potential_spots2.iter() { + println!("[PART 2] ({:?}) {} comparisons: {}", point, comparisons, point.x as u64 * 4_000_000 + point.y as u64); + } +} + +struct Square { + top_left: Point, + bottom_right: Point, +} + +impl Square { + fn contains_line(&self, line: &Line) -> bool { + return false; + } +} + +#[derive(Debug)] +struct Line { + start: Point, + end: Point +} + +enum Thing { + Beacon, + Sensor, + LogicallyEmpty +} + +fn parse_line(s: &str) -> Option<(Point, Point)> { + let divider = s.find(":"); + if divider.is_none() { + return None; + } + let sensor: Option = point_from_str(&s[0..=divider.unwrap()]); + let beacon: Option = point_from_str(&s[divider.unwrap()..s.len()]); + if sensor.is_none() || beacon.is_none() { + println!("Failed to find anything in line: '{}'", s); + return None; + } + println!("Sensor at {:?}\nBeacon at {:?}\ndistance: {}\n", sensor, beacon, sensor.unwrap().manhattan_distance(&beacon.unwrap())); + return Some((sensor.unwrap(), beacon.unwrap())); +} + +fn point_from_str(s: &str) -> Option { + let x = value_from_str(s, "x="); + let y = value_from_str(s, "y="); + if x.is_none() || y.is_none() { + return None; + } + return Some(Point { x: x.unwrap(), y: y.unwrap() }); +} + +fn value_from_str(s: &str, delimiter: &str) -> Option { + let mut start = s.find(delimiter); + let mut length = 0; + if start.is_none() { + return None; + } + start = Some(start.unwrap() + 2); + for i in start.unwrap()..s.len() { + let c = s.chars().nth(i).unwrap(); + if c.is_ascii_digit() || c.eq(&'-') { + length += 1; + } + else { + break; + } + } + if length == 0 { + return None; + } + return Some(i32::from_str(&s[start.unwrap()..start.unwrap()+length]).expect("Failed to parse")); +} diff --git a/2022/15/test_input b/2022/15/test_input new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/2022/15/test_input @@ -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 diff --git a/2022/common/src/lib.rs b/2022/common/src/lib.rs index b5079a9..33172a5 100644 --- a/2022/common/src/lib.rs +++ b/2022/common/src/lib.rs @@ -85,6 +85,12 @@ impl Point { self.x = (self.x.abs() as f32 / larger).ceil() as i32; self.y = (self.y.abs() as f32 / larger).ceil() as i32; } + + pub fn manhattan_distance(&self, other: &Point) -> u32 { + let x = (self.x - other.x).abs() as u32; + let y = (self.y - other.y).abs() as u32; + return x + y; + } } #[cfg(test)]