Compare commits
6 commits
1b59efa250
...
6fb08930bc
Author | SHA1 | Date | |
---|---|---|---|
6fb08930bc | |||
b9b46aab5b | |||
146e0d594c | |||
633591acf8 | |||
3d47372967 | |||
0b4b1d4f07 |
10 changed files with 236 additions and 39 deletions
1
2022/data/day15.expected
Normal file
1
2022/data/day15.expected
Normal file
|
@ -0,0 +1 @@
|
||||||
|
5335787
|
25
2022/data/day15.input
Normal file
25
2022/data/day15.input
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
Sensor at x=1259754, y=1927417: closest beacon is at x=1174860, y=2000000
|
||||||
|
Sensor at x=698360, y=2921616: closest beacon is at x=1174860, y=2000000
|
||||||
|
Sensor at x=2800141, y=2204995: closest beacon is at x=3151616, y=2593677
|
||||||
|
Sensor at x=3257632, y=2621890: closest beacon is at x=3336432, y=2638865
|
||||||
|
Sensor at x=3162013, y=3094407: closest beacon is at x=3151616, y=2593677
|
||||||
|
Sensor at x=748228, y=577603: closest beacon is at x=849414, y=-938539
|
||||||
|
Sensor at x=3624150, y=2952930: closest beacon is at x=3336432, y=2638865
|
||||||
|
Sensor at x=2961687, y=2430611: closest beacon is at x=3151616, y=2593677
|
||||||
|
Sensor at x=142293, y=3387807: closest beacon is at x=45169, y=4226343
|
||||||
|
Sensor at x=3309479, y=1598941: closest beacon is at x=3336432, y=2638865
|
||||||
|
Sensor at x=1978235, y=3427616: closest beacon is at x=2381454, y=3683743
|
||||||
|
Sensor at x=23389, y=1732536: closest beacon is at x=1174860, y=2000000
|
||||||
|
Sensor at x=1223696, y=3954547: closest beacon is at x=2381454, y=3683743
|
||||||
|
Sensor at x=3827517, y=3561118: closest beacon is at x=4094575, y=3915146
|
||||||
|
Sensor at x=3027894, y=3644321: closest beacon is at x=2381454, y=3683743
|
||||||
|
Sensor at x=3523333, y=3939956: closest beacon is at x=4094575, y=3915146
|
||||||
|
Sensor at x=2661743, y=3988507: closest beacon is at x=2381454, y=3683743
|
||||||
|
Sensor at x=2352285, y=2877820: closest beacon is at x=2381454, y=3683743
|
||||||
|
Sensor at x=3214853, y=2572272: closest beacon is at x=3151616, y=2593677
|
||||||
|
Sensor at x=3956852, y=2504216: closest beacon is at x=3336432, y=2638865
|
||||||
|
Sensor at x=219724, y=3957089: closest beacon is at x=45169, y=4226343
|
||||||
|
Sensor at x=1258233, y=2697879: closest beacon is at x=1174860, y=2000000
|
||||||
|
Sensor at x=3091374, y=215069: closest beacon is at x=4240570, y=610698
|
||||||
|
Sensor at x=3861053, y=889064: closest beacon is at x=4240570, y=610698
|
||||||
|
Sensor at x=2085035, y=1733247: closest beacon is at x=1174860, y=2000000
|
9
2022/day15/rust/Cargo.toml
Normal file
9
2022/day15/rust/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "rust_2022_15"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aoc = { path = "../../../common/rust" }
|
100
2022/day15/rust/src/main.rs
Normal file
100
2022/day15/rust/src/main.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
#![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) -> usize {
|
||||||
|
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);
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ fn main() {
|
||||||
|
|
||||||
let pairs: Vec<_> = data
|
let pairs: Vec<_> = data
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| -> (SectionRange, SectionRange) {
|
.map(|line| -> (SectionRange<u64>, SectionRange<u64>) {
|
||||||
let (left, right) = line.split_once(',').unwrap();
|
let (left, right) = line.split_once(',').unwrap();
|
||||||
let left = left.parse().unwrap();
|
let left = left.parse().unwrap();
|
||||||
let right = right.parse().unwrap();
|
let right = right.parse().unwrap();
|
||||||
|
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -14,6 +14,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc"
|
name = "aoc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
|
@ -601,6 +604,13 @@ dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust_2022_15"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
|
|
@ -24,4 +24,5 @@ members = [
|
||||||
"2022/day12/rust",
|
"2022/day12/rust",
|
||||||
"2022/day13/rust",
|
"2022/day13/rust",
|
||||||
"2022/day14/rust",
|
"2022/day14/rust",
|
||||||
|
"2022/day15/rust",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
num-traits = "0.2.15"
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt,
|
fmt,
|
||||||
num::NonZeroU64,
|
num::NonZeroUsize,
|
||||||
ops::{BitAnd, BitOr, RangeInclusive, Sub},
|
ops::{Add, BitAnd, BitOr, RangeInclusive, Sub},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use num_traits::{one, One};
|
||||||
|
|
||||||
use crate::UpToTwo;
|
use crate::UpToTwo;
|
||||||
|
|
||||||
/// Error returned when an attempt is made to construct an empty `SectionRange`.
|
/// Error returned when an attempt is made to construct an empty `SectionRange`.
|
||||||
|
@ -33,13 +35,13 @@ impl fmt::Display for InvalidSectionString {
|
||||||
impl Error for InvalidSectionString {}
|
impl Error for InvalidSectionString {}
|
||||||
|
|
||||||
/// A range of sections. Always contains at least one element.
|
/// A range of sections. Always contains at least one element.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct SectionRange {
|
pub struct SectionRange<T> {
|
||||||
start: u64,
|
start: T,
|
||||||
end: u64,
|
end: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SectionRange {
|
impl<T: Ord + Copy> SectionRange<T> {
|
||||||
/// Constructs a new section range from a start section and an end section, both inclusive.
|
/// Constructs a new section range from a start section and an end section, both inclusive.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -56,7 +58,7 @@ impl SectionRange {
|
||||||
/// let range = SectionRange::try_new(3, 2);
|
/// let range = SectionRange::try_new(3, 2);
|
||||||
/// assert_eq!(range, Err(EmptyRange));
|
/// assert_eq!(range, Err(EmptyRange));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_new(start: u64, end: u64) -> Result<Self, EmptyRange> {
|
pub fn try_new(start: T, end: T) -> Result<Self, EmptyRange> {
|
||||||
if start <= end {
|
if start <= end {
|
||||||
Ok(Self { start, end })
|
Ok(Self { start, end })
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,25 +66,16 @@ impl SectionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of sections contained by the range. Since the range always contains at
|
|
||||||
/// least one element, the length is never zero.
|
|
||||||
#[allow(clippy::missing_panics_doc)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn len(&self) -> NonZeroU64 {
|
|
||||||
debug_assert!(self.start <= self.end);
|
|
||||||
NonZeroU64::new(self.end - self.start + 1).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if and only if the range contains the given section.
|
/// Returns true if and only if the range contains the given section.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains(&self, section: u64) -> bool {
|
pub fn contains(&self, section: T) -> bool {
|
||||||
debug_assert!(self.start <= self.end);
|
debug_assert!(self.start <= self.end);
|
||||||
section >= self.start && section <= self.end
|
section >= self.start && section <= self.end
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if and only if the range contains the entirety of `other`.
|
/// Returns true if and only if the range contains the entirety of `other`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn encompasses(&self, other: &SectionRange) -> bool {
|
pub fn encompasses(&self, other: &Self) -> bool {
|
||||||
let Some(intersection) = *self & *other else {
|
let Some(intersection) = *self & *other else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -90,12 +83,23 @@ impl SectionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for SectionRange {
|
impl<T: Ord + Copy + Sub<Output = L>, L: TryInto<usize>> SectionRange<T> {
|
||||||
|
/// Returns the number of sections contained by the range. Since the range always contains at
|
||||||
|
/// least one element, the length is never zero.
|
||||||
|
#[allow(clippy::missing_panics_doc)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn len(&self) -> NonZeroUsize {
|
||||||
|
debug_assert!(self.start <= self.end);
|
||||||
|
NonZeroUsize::new((self.end - self.start).try_into().ok().unwrap() + 1).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord + Copy + FromStr> FromStr for SectionRange<T> {
|
||||||
type Err = InvalidSectionString;
|
type Err = InvalidSectionString;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
// poor man's try block
|
// poor man's try block
|
||||||
fn inner(s: &str) -> Option<SectionRange> {
|
fn inner<T: Ord + Copy + FromStr>(s: &str) -> Option<SectionRange<T>> {
|
||||||
let (start, end) = s.split_once('-')?;
|
let (start, end) = s.split_once('-')?;
|
||||||
|
|
||||||
let start = start.parse().ok()?;
|
let start = start.parse().ok()?;
|
||||||
|
@ -108,21 +112,21 @@ impl FromStr for SectionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SectionRange> for RangeInclusive<u64> {
|
impl<T> From<SectionRange<T>> for RangeInclusive<T> {
|
||||||
fn from(r: SectionRange) -> Self {
|
fn from(r: SectionRange<T>) -> Self {
|
||||||
r.start..=r.end
|
r.start..=r.end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RangeInclusive<u64>> for SectionRange {
|
impl<T: Ord + Copy> TryFrom<RangeInclusive<T>> for SectionRange<T> {
|
||||||
type Error = EmptyRange;
|
type Error = EmptyRange;
|
||||||
|
|
||||||
fn try_from(range: RangeInclusive<u64>) -> Result<Self, Self::Error> {
|
fn try_from(range: RangeInclusive<T>) -> Result<Self, Self::Error> {
|
||||||
Self::try_new(*range.start(), *range.end())
|
Self::try_new(*range.start(), *range.end())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitAnd for SectionRange {
|
impl<T: Ord + Copy> BitAnd for SectionRange<T> {
|
||||||
type Output = Option<Self>;
|
type Output = Option<Self>;
|
||||||
|
|
||||||
fn bitand(self, other: Self) -> Self::Output {
|
fn bitand(self, other: Self) -> Self::Output {
|
||||||
|
@ -133,15 +137,15 @@ impl BitAnd for SectionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitOr for SectionRange {
|
impl<T: Ord + Copy> BitOr for SectionRange<T> {
|
||||||
type Output = UpToTwo<Self>;
|
type Output = UpToTwo<Self>;
|
||||||
|
|
||||||
fn bitor(self, other: Self) -> Self::Output {
|
fn bitor(self, other: Self) -> Self::Output {
|
||||||
let first_start = u64::min(self.start, other.start);
|
let first_start = T::min(self.start, other.start);
|
||||||
let first_end = u64::min(self.end, other.end);
|
let first_end = T::min(self.end, other.end);
|
||||||
|
|
||||||
let second_start = u64::max(self.start, other.start);
|
let second_start = T::max(self.start, other.start);
|
||||||
let second_end = u64::max(self.end, other.end);
|
let second_end = T::max(self.end, other.end);
|
||||||
|
|
||||||
if first_end < second_start {
|
if first_end < second_start {
|
||||||
let first = Self {
|
let first = Self {
|
||||||
|
@ -162,7 +166,7 @@ impl BitOr for SectionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for SectionRange {
|
impl<T: Ord + Copy + Sub<Output = T> + Add<Output = T> + One> Sub for SectionRange<T> {
|
||||||
type Output = UpToTwo<Self>;
|
type Output = UpToTwo<Self>;
|
||||||
|
|
||||||
fn sub(self, other: Self) -> Self::Output {
|
fn sub(self, other: Self) -> Self::Output {
|
||||||
|
@ -173,10 +177,10 @@ impl Sub for SectionRange {
|
||||||
// Closures to prevent integer overflow
|
// Closures to prevent integer overflow
|
||||||
let first = || Self {
|
let first = || Self {
|
||||||
start: self.start,
|
start: self.start,
|
||||||
end: u64::min(self.end, other.start - 1),
|
end: T::min(self.end, other.start - one()),
|
||||||
};
|
};
|
||||||
let second = || Self {
|
let second = || Self {
|
||||||
start: u64::max(self.start, other.end + 1),
|
start: T::max(self.start, other.end + one()),
|
||||||
end: self.end,
|
end: self.end,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,7 +217,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn section_range_intersection() {
|
fn section_range_intersection() {
|
||||||
fn check_intersection(a: SectionRange, b: SectionRange, expected: Option<SectionRange>) {
|
fn check_intersection(
|
||||||
|
a: SectionRange<u64>,
|
||||||
|
b: SectionRange<u64>,
|
||||||
|
expected: Option<SectionRange<u64>>,
|
||||||
|
) {
|
||||||
let x = a & b;
|
let x = a & b;
|
||||||
let y = b & a;
|
let y = b & a;
|
||||||
|
|
||||||
|
@ -247,11 +255,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn section_range_operations() {
|
fn section_range_operations() {
|
||||||
fn elements(r: SectionRange) -> Vec<u64> {
|
fn elements(r: SectionRange<u64>) -> Vec<u64> {
|
||||||
RangeInclusive::<_>::from(r).collect()
|
RangeInclusive::<_>::from(r).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elements_multi(r: UpToTwo<SectionRange>) -> Vec<u64> {
|
fn elements_multi(r: UpToTwo<SectionRange<u64>>) -> Vec<u64> {
|
||||||
let is_empty = r.is_empty();
|
let is_empty = r.is_empty();
|
||||||
let sections: Vec<_> = r.into_iter().flat_map(elements).collect();
|
let sections: Vec<_> = r.into_iter().flat_map(elements).collect();
|
||||||
|
|
||||||
|
@ -288,7 +296,7 @@ mod tests {
|
||||||
// len()
|
// len()
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
left.len().get(),
|
left.len().get(),
|
||||||
*range_left.end() - *range_left.start() + 1
|
usize::try_from(*range_left.end() - *range_left.start() + 1).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
// contains()
|
// contains()
|
||||||
|
|
|
@ -28,6 +28,48 @@ impl Vec2 {
|
||||||
pub fn len(self) -> f64 {
|
pub fn len(self) -> f64 {
|
||||||
(f64::from(self.x).powi(2) + f64::from(self.y).powi(2)).sqrt()
|
(f64::from(self.x).powi(2) + f64::from(self.y).powi(2)).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn manhattan_dist(self, other: Vec2) -> usize {
|
||||||
|
let Vec2 { x, y } = (other - self).map(i32::abs);
|
||||||
|
let x = usize::try_from(x).unwrap();
|
||||||
|
let y = usize::try_from(y).unwrap();
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_x_with_manhattan_dist(self, x: i32, dist: usize) -> Option<(Vec2, Vec2)> {
|
||||||
|
let dx = usize::try_from((x - self.x).abs()).ok()?;
|
||||||
|
|
||||||
|
let dy = dist.checked_sub(dx)?;
|
||||||
|
let dy = i32::try_from(dy).ok()?;
|
||||||
|
|
||||||
|
let p1 = Vec2::new(x, self.y - dy);
|
||||||
|
let p2 = Vec2::new(x, self.y + dy);
|
||||||
|
|
||||||
|
debug_assert!(p1 <= p2);
|
||||||
|
debug_assert_eq!(self.manhattan_dist(p1), dist);
|
||||||
|
debug_assert_eq!(self.manhattan_dist(p2), dist);
|
||||||
|
|
||||||
|
Some((p1, p2))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_y_with_manhattan_dist(self, y: i32, dist: usize) -> Option<(Vec2, Vec2)> {
|
||||||
|
let dy = usize::try_from((y - self.y).abs()).ok()?;
|
||||||
|
|
||||||
|
let dx = dist.checked_sub(dy)?;
|
||||||
|
let dx = i32::try_from(dx).ok()?;
|
||||||
|
|
||||||
|
let p1 = Vec2::new(self.x - dx, y);
|
||||||
|
let p2 = Vec2::new(self.x + dx, y);
|
||||||
|
|
||||||
|
debug_assert!(p1 <= p2);
|
||||||
|
debug_assert_eq!(self.manhattan_dist(p1), dist);
|
||||||
|
debug_assert_eq!(self.manhattan_dist(p2), dist);
|
||||||
|
|
||||||
|
Some((p1, p2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Vec2 {
|
impl fmt::Display for Vec2 {
|
||||||
|
|
Loading…
Reference in a new issue