Add aoc 2022 day 15

This commit is contained in:
Kienan Stewart 2022-12-15 16:49:19 -05:00
parent e55389d3bb
commit 89461b9866
6 changed files with 349 additions and 0 deletions

14
2022/15/Cargo.lock generated Normal file
View File

@ -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",
]

9
2022/15/Cargo.toml Normal file
View File

@ -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" }

40
2022/15/input Normal file
View File

@ -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

266
2022/15/src/main.rs Normal file
View File

@ -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::<Point, Thing>::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<Point> = 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::<Point>::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::<Point>::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::<Point>::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> = point_from_str(&s[0..=divider.unwrap()]);
let beacon: Option<Point> = 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<Point> {
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<i32> {
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"));
}

14
2022/15/test_input Normal file
View File

@ -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

View File

@ -85,6 +85,12 @@ impl Point {
self.x = (self.x.abs() as f32 / larger).ceil() as i32; self.x = (self.x.abs() as f32 / larger).ceil() as i32;
self.y = (self.y.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)] #[cfg(test)]