101 lines
2.9 KiB
Rust
101 lines
2.9 KiB
Rust
#![warn(clippy::pedantic)]
|
|
|
|
use std::io::stdin;
|
|
|
|
use aoc::{vec2::Vec2, SectionRange, UpToTwo};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
struct Sensor {
|
|
position: Vec2,
|
|
closest_beacon: Vec2,
|
|
}
|
|
|
|
impl Sensor {
|
|
fn beacon_distance(self) -> u32 {
|
|
self.closest_beacon.manhattan_dist(self.position)
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let sensors: Vec<_> = stdin()
|
|
.lines()
|
|
.map(|line| {
|
|
let line = line.unwrap();
|
|
let (sensor, beacon) = line.split_once(": ").unwrap();
|
|
let point = |s: &str| {
|
|
let (x, y) = s.split_once(", ").unwrap();
|
|
let x = x.strip_prefix("x=").unwrap().parse().unwrap();
|
|
let y = y.strip_prefix("y=").unwrap().parse().unwrap();
|
|
(x, y).into()
|
|
};
|
|
let sensor = sensor.strip_prefix("Sensor at ").unwrap();
|
|
let beacon = beacon.strip_prefix("closest beacon is at ").unwrap();
|
|
|
|
Sensor {
|
|
position: point(sensor),
|
|
closest_beacon: point(beacon),
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
for sensor in &sensors {
|
|
let dist = sensor.beacon_distance();
|
|
let candidates_x = sensor
|
|
.position
|
|
.on_x_with_manhattan_dist(sensor.closest_beacon.x, dist)
|
|
.unwrap();
|
|
let candidates_y = sensor
|
|
.position
|
|
.on_y_with_manhattan_dist(sensor.closest_beacon.y, dist)
|
|
.unwrap();
|
|
let candidates = [
|
|
candidates_x.0,
|
|
candidates_x.1,
|
|
candidates_y.0,
|
|
candidates_y.1,
|
|
];
|
|
assert!(candidates.contains(&sensor.closest_beacon));
|
|
}
|
|
|
|
let mut ranges: Vec<_> = sensors
|
|
.iter()
|
|
.flat_map(|sensor| {
|
|
let y = 2_000_000;
|
|
let Some((left, right)) = sensor
|
|
.position
|
|
.on_y_with_manhattan_dist(y, sensor.beacon_distance())
|
|
else {
|
|
return UpToTwo::Zero;
|
|
};
|
|
|
|
let range = SectionRange::try_new(left.x, right.x).unwrap();
|
|
if sensor.closest_beacon.y == y {
|
|
let beacon_pos = sensor.closest_beacon.y;
|
|
let beacon_pos = SectionRange::try_new(beacon_pos, beacon_pos).unwrap();
|
|
|
|
range - beacon_pos
|
|
} else {
|
|
UpToTwo::One(range)
|
|
}
|
|
})
|
|
.collect();
|
|
ranges.sort_unstable();
|
|
let mut covered = ranges.into_iter();
|
|
|
|
let mut count = 0;
|
|
let mut current_range = covered.next().unwrap();
|
|
for range in covered {
|
|
match current_range | range {
|
|
UpToTwo::Zero => unreachable!(),
|
|
UpToTwo::One(new) => current_range = new,
|
|
UpToTwo::Two(_old, new) => {
|
|
count += current_range.len().get();
|
|
current_range = new;
|
|
}
|
|
}
|
|
}
|
|
count += current_range.len().get();
|
|
|
|
println!("{:?}", count);
|
|
}
|