aoc2019/day10/src/main.rs

107 lines
2.4 KiB
Rust

use itertools::Itertools;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Position {
pub x: usize,
pub y: usize,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Field {
rows: usize,
columns: usize,
astroids: Vec<bool>,
}
fn gcd(mut x: isize, mut y: isize) -> isize {
loop {
if y == 0 { return x; }
x = x % y;
if x == 0 { return y; }
y = y % x;
}
}
impl Field {
pub fn parse(input: &str) -> Self {
let lines = input.lines().collect::<Vec<_>>();
let rows = lines.len();
let columns = lines[0].len();
let mut astroids = Vec::new();
for line in lines {
assert_eq!(columns, line.len());
for cell in line.chars() {
astroids.push(match cell {
'#' => true,
'.' => false,
_ => panic!("Unexpected cell: {:?}", cell),
})
}
}
Self {
rows,
columns,
astroids,
}
}
pub fn sees(&self, a: Position, b: Position) -> bool {
if a == b { return true; }
let diff_x = b.x as isize - a.x as isize;
let diff_y = b.y as isize - a.y as isize;
let d = gcd(diff_x, diff_y).abs();
let diff_x = diff_x / d;
let diff_y = diff_y / d;
let mut pos = a;
loop {
pos.x = (pos.x as isize + diff_x) as usize;
pos.y = (pos.y as isize + diff_y) as usize;
if pos == b { return true; }
if self[pos] { return false; }
}
}
pub fn all_astroids(&self) -> impl Iterator<Item = Position> + '_ {
(0..self.columns).cartesian_product(0..self.rows).map(|(x, y)| Position { x, y }).filter(move |pos| self[*pos])
}
pub fn best_monitoring_station(&self) -> (Position, usize) {
self.all_astroids().map(|monitor| {
// can see itself, but only count "others"
(monitor, self.all_astroids().filter(|astroid| self.sees(monitor, *astroid)).count() - 1)
}).max_by_key(|(_, n)| *n).unwrap()
}
}
impl std::ops::Index<Position> for Field {
type Output = bool;
fn index(&self, index: Position) -> &Self::Output {
assert!(index.x < self.columns);
assert!(index.y < self.rows);
&self.astroids[index.y * self.columns + index.x]
}
}
fn main() {
let input = include_str!("input.txt");
println!("Best monitoring station sees astroids: {}", Field::parse(input).best_monitoring_station().1);
}
#[cfg(test)]
mod day10test {
use super::*;
#[test]
fn examples1() {
assert_eq!(
Field::parse(".#..#
.....
#####
....#
...##").best_monitoring_station(),
(Position { x: 3, y: 4 }, 8),
);
}
}