better Pos

This commit is contained in:
Rüdiger Ludwig 2023-08-07 05:50:26 +02:00
parent bcefb1b68f
commit 651ccb9cba
11 changed files with 268 additions and 318 deletions

View file

@ -1,7 +1,7 @@
use super::template::{DayTrait, ResultType};
use crate::common::{
parser::{eol_terminated, extract_result, ignore, trim0},
pos::Pos,
pos::Pos2D,
};
use itertools::Itertools;
use nom::{
@ -44,7 +44,7 @@ impl DayTrait for Day {
.iter()
.tuple_combinations()
.filter_map(|(first, second)| first.cross(second))
.filter(|pos| sensors.iter().all(|sensor| !sensor.contains(pos)))
.filter(|pos| sensors.iter().all(|sensor| !sensor.contains(*pos)))
.dedup()
.collect::<Vec<_>>();
@ -58,7 +58,7 @@ impl DayTrait for Day {
}
impl Day {
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos2D<i64>>), SensorError> {
let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?;
let mut sensors = HashSet::new();
@ -70,7 +70,11 @@ impl Day {
Ok((sensors, beacons))
}
fn count_coverage_at(sensors: &HashSet<Sensor>, beacons: &HashSet<Pos<i64>>, row: i64) -> i64 {
fn count_coverage_at(
sensors: &HashSet<Sensor>,
beacons: &HashSet<Pos2D<i64>>,
row: i64,
) -> i64 {
let ranges = sensors
.iter()
.filter_map(|sensor| sensor.range_at(row))
@ -117,7 +121,7 @@ impl From<Err<Error<&str>>> for SensorError {
#[derive(Debug)]
struct Line {
start: Pos<i64>,
start: Pos2D<i64>,
is_up: bool,
steps: i64,
}
@ -139,16 +143,16 @@ impl Line {
if one.pos.y() < two.pos.y() {
is_up = true;
if one.pos.y() + one.radius <= two.pos.y() {
start = Pos::new(one.pos.x(), one.pos.y() + one.radius + 1);
start = Pos2D::new(one.pos.x(), one.pos.y() + one.radius + 1);
} else {
start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y());
start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y());
}
} else {
is_up = false;
if one.pos.y() - one.radius >= two.pos.y() {
start = Pos::new(one.pos.x(), one.pos.y() - one.radius - 1);
start = Pos2D::new(one.pos.x(), one.pos.y() - one.radius - 1);
} else {
start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y());
start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y());
}
}
let steps = two.pos.x().min(one.pos.x() + one.radius) - start.x();
@ -160,7 +164,7 @@ impl Line {
})
}
fn cross(&self, other: &Line) -> Option<Pos<i64>> {
fn cross(&self, other: &Line) -> Option<Pos2D<i64>> {
if self.is_up == other.is_up {
return None;
}
@ -181,7 +185,7 @@ impl Line {
return None;
}
let pos = top_down.start + Pos::splat(r);
let pos = top_down.start + Pos2D::splat(r);
Some(pos)
}
}
@ -227,7 +231,7 @@ impl Range {
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
struct Sensor {
pos: Pos<i64>,
pos: Pos2D<i64>,
radius: i64,
}
@ -239,21 +243,21 @@ impl Sensor {
}
}
fn parse_pos(input: &str) -> IResult<&str, Pos<i64>> {
fn parse_pos(input: &str) -> IResult<&str, Pos2D<i64>> {
let (input, (x, y)) = separated_pair(
Sensor::component("x"),
trim0(char(',')),
Sensor::component("y"),
)(input)?;
Ok((input, Pos::new(x, y)))
Ok((input, Pos2D::new(x, y)))
}
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos<i64>)> {
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2D<i64>)> {
let input = ignore(tag("Sensor at"))(input)?;
let (input, pos) = trim0(Sensor::parse_pos)(input)?;
let input = ignore(tag(": closest beacon is at"))(input)?;
let (input, beacon) = trim0(Sensor::parse_pos)(input)?;
let radius = pos.taxicab(&beacon);
let radius = pos.taxicab_between(beacon);
Ok((input, (Sensor { pos, radius }, beacon)))
}
@ -268,12 +272,12 @@ impl Sensor {
}
pub fn border_distance(&self, other: &Sensor) -> i64 {
let distance = self.pos.taxicab(&other.pos);
let distance = self.pos.taxicab_between(other.pos);
distance - self.radius - other.radius
}
pub fn contains(&self, pos: &Pos<i64>) -> bool {
self.pos.taxicab(pos) <= self.radius
pub fn contains(&self, pos: Pos2D<i64>) -> bool {
self.pos.taxicab_between(pos) <= self.radius
}
}
@ -287,10 +291,10 @@ mod test {
let input = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15";
let expected = (
Sensor {
pos: Pos::new(2, 18),
pos: Pos2D::new(2, 18),
radius: 7,
},
Pos::new(-2, 15),
Pos2D::new(-2, 15),
);
let result = extract_result(Sensor::parse)(input)?;
assert_eq!(result, expected);
@ -301,7 +305,7 @@ mod test {
#[test]
fn test_width() {
let sensor = Sensor {
pos: Pos::new(8, 7),
pos: Pos2D::new(8, 7),
radius: 9,
};
assert_eq!(sensor.range_at(17), None);