|
46 | 46 | //! sorting a second time in this order. |
47 | 47 | //! |
48 | 48 | //! [`atan2`]: f64::atan2 |
| 49 | +use crate::util::grid::*; |
49 | 50 | use crate::util::math::*; |
50 | 51 | use crate::util::point::*; |
51 | 52 | use std::cmp::Ordering; |
52 | 53 |
|
53 | 54 | type Input = (i32, i32); |
54 | 55 |
|
55 | 56 | pub fn parse(input: &str) -> Input { |
56 | | - let raw: Vec<_> = input.lines().map(str::as_bytes).collect(); |
57 | | - let width = raw[0].len() as i32; |
58 | | - let height = raw.len() as i32; |
| 57 | + let grid = Grid::parse(input); |
| 58 | + let width = grid.width; |
| 59 | + let height = grid.height; |
59 | 60 |
|
60 | 61 | // Convert asteroids to `Point` objects. |
61 | 62 | let mut points = Vec::new(); |
62 | 63 |
|
63 | | - for (y, row) in raw.iter().enumerate() { |
64 | | - for (x, &col) in row.iter().enumerate() { |
65 | | - if col == b'#' { |
66 | | - points.push(Point::new(x as i32, y as i32)); |
| 64 | + for y in 0..height { |
| 65 | + for x in 0..width { |
| 66 | + let point = Point::new(x, y); |
| 67 | + if grid[point] == b'#' { |
| 68 | + points.push(point); |
67 | 69 | } |
68 | 70 | } |
69 | 71 | } |
70 | 72 |
|
71 | 73 | // Find asteroid with the highest visibility. |
| 74 | + let mut seen = Grid::new(2 * width, 2 * height, 0); |
| 75 | + let mut cache = grid.same_size_with(0); |
72 | 76 | let mut visible = vec![0; points.len()]; |
73 | | - let mut seen = vec![0; (4 * width * height) as usize]; |
74 | 77 | let mut max_visible = 0; |
75 | 78 | let mut max_index = 0; |
76 | 79 |
|
77 | 80 | for i in 0..(points.len() - 1) { |
78 | 81 | for j in (i + 1)..points.len() { |
79 | | - let mut delta = points[j] - points[i]; |
| 82 | + let delta = points[j] - points[i]; |
| 83 | + let key = Point::new(delta.x.abs(), delta.y.abs()); |
| 84 | + |
| 85 | + let gcd = if cache[key] > 0 { |
| 86 | + cache[key] |
| 87 | + } else { |
| 88 | + cache[key] = key.x.gcd(key.y); |
| 89 | + cache[key] |
| 90 | + }; |
80 | 91 |
|
81 | 92 | // Key insight is that points on the same line are integer multiples of each other. |
82 | | - let factor = delta.x.gcd(delta.y).abs(); |
83 | | - delta.x /= factor; |
84 | | - delta.y /= factor; |
| 93 | + let adjusted = Point::new(delta.x / gcd, delta.y / gcd); |
85 | 94 |
|
86 | 95 | // This works as the points are in order from left to right and top to bottom, |
87 | 96 | // so we process points from nearest to furthest. |
88 | | - let index = (2 * width) * (height + delta.y) + (width + delta.x); |
| 97 | + let index = Point::new(width + adjusted.x, height + adjusted.y); |
89 | 98 |
|
90 | | - if seen[index as usize] <= i { |
91 | | - seen[index as usize] = i + 1; |
| 99 | + if seen[index] <= i { |
| 100 | + seen[index] = i + 1; |
92 | 101 | visible[i] += 1; |
93 | 102 | visible[j] += 1; |
94 | 103 | } |
|
0 commit comments