Cleanup in 2019 day 10: Introduced a proper data type for angles
This commit is contained in:
parent
1895197c49
commit
947d843b7b
1 changed files with 25 additions and 64 deletions
|
@ -3,19 +3,25 @@ use std::collections::BTreeSet;
|
|||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct RationalAngle {
|
||||
upper_half: bool,
|
||||
quadrant: u8,
|
||||
divident: isize,
|
||||
divisor: usize,
|
||||
divisor: isize,
|
||||
}
|
||||
|
||||
impl From<(isize, isize)> for RationalAngle {
|
||||
fn from((x, y): (isize, isize)) -> Self {
|
||||
let upper_half = x < 0;
|
||||
let divident = y.signum() * x / gcd(x, y);
|
||||
let divisor = (y / gcd(x, y)).unsigned_abs();
|
||||
let quadrant = match (x.signum(), y.signum()) {
|
||||
(0, -1) | (1, -1) => 1,
|
||||
(1, 0) | (1, 1) => 2,
|
||||
(0, 1) | (-1, 1) => 3,
|
||||
(-1, 0) | (-1, -1) => 4,
|
||||
_ => panic!("Unexpected combination of signs: {x}, {y}"),
|
||||
};
|
||||
let divident = x / gcd(x, y);
|
||||
let divisor = y / gcd(x, y);
|
||||
|
||||
Self {
|
||||
upper_half,
|
||||
quadrant,
|
||||
divident,
|
||||
divisor,
|
||||
}
|
||||
|
@ -24,14 +30,9 @@ impl From<(isize, isize)> for RationalAngle {
|
|||
|
||||
impl PartialOrd for RationalAngle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self.upper_half, other.upper_half) {
|
||||
(true, false) => Some(Ordering::Greater),
|
||||
(false, true) => Some(Ordering::Less),
|
||||
_ => match (self. divident as f64 / self.divisor as f64) - (other.divident as f64 / other.divisor as f64) {
|
||||
n if n < 0.0 => Some(Ordering::Greater),
|
||||
p if p > 0.0 => Some(Ordering::Less),
|
||||
_ => Some(Ordering::Equal),
|
||||
}
|
||||
match self.quadrant.cmp(&(other.quadrant)) {
|
||||
Ordering::Equal => Some((other.divident * self.divisor).cmp(&(self.divident * other.divisor))),
|
||||
diff => Some(diff),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,49 +53,15 @@ impl RationalAngle {
|
|||
|
||||
pub fn run(input: &str) -> (usize, usize) {
|
||||
let asteroids: Vec<(usize, usize)> = input.lines().enumerate().flat_map(|(y, line)| line.chars().enumerate().filter(|(_x, c)| *c == '#').map(|(x, _c)| (x, y)).collect::<Vec<_>>()).collect();
|
||||
// let mut angles: Vec<Vec<(isize, isize)>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| reduced_angle(*a, *other)).collect()).collect();
|
||||
let mut angles: Vec<BTreeSet<RationalAngle>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| RationalAngle::from_vector(*a, *other)).collect()).collect();
|
||||
// for asteroid in angles.iter_mut() {
|
||||
// asteroid.sort_by(rotation_sort);
|
||||
// asteroid.dedup();
|
||||
// }
|
||||
let angles: Vec<BTreeSet<RationalAngle>> = asteroids.iter().map(|a| asteroids.iter().filter(|other| *other != a).map(|other| RationalAngle::from_vector(*a, *other)).collect()).collect();
|
||||
let (idx, first) = angles.iter().enumerate().max_by_key(|(_idx, a)| a.len()).unwrap();
|
||||
let angle = first.iter().nth(199).unwrap();
|
||||
let second = (0, 0);
|
||||
// let second = (1..).map(|i| ((asteroids[idx].0 as isize + i * angle.divident) as usize, (asteroids[idx].1 as isize + i * angle.divisor as isize) as usize))
|
||||
// .find(|a| asteroids.contains(a))
|
||||
// .unwrap();
|
||||
let second = (1..).map(|i| ((asteroids[idx].0 as isize + i * angle.divident) as usize, (asteroids[idx].1 as isize + i * angle.divisor) as usize))
|
||||
.find(|a| asteroids.contains(a))
|
||||
.unwrap();
|
||||
(first.len(), (second.0*100 + second.1))
|
||||
}
|
||||
|
||||
fn rotation_sort(lhs: &(isize, isize), rhs: &(isize, isize)) -> Ordering {
|
||||
if lhs == rhs {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
match (lhs.0.signum(), rhs.0.signum()) {
|
||||
(-1, 1) => Ordering::Greater,
|
||||
(1, -1) => Ordering::Less,
|
||||
(0, -1) =>Ordering::Less,
|
||||
(-1, 0) => Ordering::Greater,
|
||||
(0, 1) => if lhs.1.signum() == 1 { Ordering::Less } else { Ordering::Greater },
|
||||
(1, 0) => if rhs.1.signum() == 1 { Ordering::Greater } else { Ordering::Less },
|
||||
(0, 0) => match (lhs.1.signum(), rhs.1.signum()) {
|
||||
(1, 1) | (-1, -1) => Ordering::Equal,
|
||||
(1, -1) => Ordering::Less,
|
||||
(-1, 1) => Ordering::Greater,
|
||||
_ => panic!("Unable to sort {lhs:?} and {rhs:?}"),
|
||||
},
|
||||
_ => match (lhs.1.signum(), rhs.1.signum()) {
|
||||
(1, 1) | (-1, -1) => match (lhs.0 as f64 / lhs.1 as f64) - (rhs.0 as f64 / rhs.1 as f64) {
|
||||
n if n < 0.0 => Ordering::Greater,
|
||||
p if p > 0.0 => Ordering::Less,
|
||||
_ => panic!("Unexpected sorting of {lhs:?} and {rhs:?}: Equal"),
|
||||
},
|
||||
_ => (lhs.0.signum()*lhs.1.signum()).cmp(&(lhs.0.signum()*rhs.1.signum()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(lhs: isize, rhs: isize) -> isize {
|
||||
if lhs == 0 {
|
||||
return rhs.abs();
|
||||
|
@ -108,13 +75,6 @@ fn gcd(lhs: isize, rhs: isize) -> isize {
|
|||
(1..=a.min(b)).rev().find(|i| a%i == 0 && b%i == 0).unwrap()
|
||||
}
|
||||
|
||||
fn reduced_angle(lhs: (usize, usize), rhs: (usize, usize)) -> (isize, isize) {
|
||||
let x = rhs.0 as isize - lhs.0 as isize;
|
||||
let y = rhs.1 as isize - lhs.1 as isize;
|
||||
|
||||
(x/gcd(x,y), y/gcd(x,y))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -124,11 +84,12 @@ mod tests {
|
|||
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_sample() {
|
||||
// let sample_input = read_file("tests/sample_input");
|
||||
// assert_eq!(run(&sample_input), (33, 0));
|
||||
// }
|
||||
#[test]
|
||||
#[ignore = "The sample doesn't have enough asteroids to run part 2."]
|
||||
fn test_sample() {
|
||||
let sample_input = read_file("tests/sample_input");
|
||||
assert_eq!(run(&sample_input), (33, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue