|
4 | 4 | //! The image appears in part two when the positions of all robots are unique.
|
5 | 5 | //!
|
6 | 6 | //! The x coordinates repeat every 101 seconds and the y coordinates repeat every 103 seconds.
|
7 |
| -//! Calculating each axis independently then looking it up is twice as fast |
8 |
| -//! as calculating as needed. |
| 7 | +//! First we check for times when the robot x coordinates could form the left and right columns |
| 8 | +//! of the tree's bounding box. This gives a time `t` mod 101. |
| 9 | +//! |
| 10 | +//! Then we check the y coordinates looking for the top and bottom rows of the bounding box, |
| 11 | +//! giving a time `u` mod 103. |
| 12 | +//! |
| 13 | +//! Using the [Chinese Remainder Theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem) |
| 14 | +//! we combine the two times into a single time mod 10403 that is the answer. |
| 15 | +use crate::util::grid::*; |
9 | 16 | use crate::util::iter::*;
|
10 |
| -use crate::util::math::*; |
11 | 17 | use crate::util::parse::*;
|
| 18 | +use crate::util::point::*; |
12 | 19 |
|
13 | 20 | type Robot = [i32; 4];
|
14 | 21 |
|
@@ -47,52 +54,69 @@ pub fn part1(input: &[Robot]) -> i32 {
|
47 | 54 | q1 * q2 * q3 * q4
|
48 | 55 | }
|
49 | 56 |
|
50 |
| -pub fn part2(input: &[Robot]) -> usize { |
51 |
| - let robots: Vec<_> = input |
52 |
| - .iter() |
53 |
| - .map(|&[x, y, h, v]| [x, y, h.rem_euclid(101), v.rem_euclid(103)]) |
54 |
| - .collect(); |
| 57 | +pub fn part2(robots: &[Robot]) -> i32 { |
| 58 | + // Search for times mod 101 when the tree could possibly exist using x coordinates only. |
| 59 | + let mut columns = Vec::new(); |
55 | 60 |
|
56 |
| - let coefficient1 = 103 * 103.mod_inv(101).unwrap(); |
57 |
| - let coefficient2 = 101 * 101.mod_inv(103).unwrap(); |
58 |
| - let horizontal: Vec<_> = (0..101).map(|n| n.mod_inv(101)).collect(); |
59 |
| - let vertical: Vec<_> = (0..103).map(|n| n.mod_inv(103)).collect(); |
| 61 | + for time in 0..101 { |
| 62 | + let mut xs = [0; 101]; |
60 | 63 |
|
61 |
| - let mut unique = vec![true; 10403]; |
| 64 | + for [x, _, dx, _] in robots { |
| 65 | + let index = (x + dx * time).rem_euclid(101); |
| 66 | + xs[index as usize] += 1; |
| 67 | + } |
62 | 68 |
|
63 |
| - for (i, &[x1, y1, h1, v1]) in robots.iter().enumerate().skip(1) { |
64 |
| - for &[x2, y2, h2, v2] in robots.iter().take(i) { |
65 |
| - if x1 == x2 && h1 == h2 { |
66 |
| - if let Some(b) = vertical[to_index(v2 - v1, 103)] { |
67 |
| - let u = to_index((y1 - y2) * b, 103); |
| 69 | + // Tree bounding box columns are 33 high. |
| 70 | + if xs.iter().filter(|&&c| c >= 33).count() >= 2 { |
| 71 | + columns.push(time); |
| 72 | + } |
| 73 | + } |
68 | 74 |
|
69 |
| - for n in (0..10403).step_by(103) { |
70 |
| - unique[n + u] = false; |
71 |
| - } |
72 |
| - } |
73 |
| - } else if y1 == y2 && v1 == v2 { |
74 |
| - if let Some(a) = horizontal[to_index(h2 - h1, 101)] { |
75 |
| - let t = to_index((x1 - x2) * a, 101); |
| 75 | + // Search for times mod 103 when the tree could possibly exist using y coordinates only. |
| 76 | + let mut rows = Vec::new(); |
76 | 77 |
|
77 |
| - for n in (0..10403).step_by(101) { |
78 |
| - unique[n + t] = false; |
79 |
| - } |
80 |
| - } |
81 |
| - } else if let Some(a) = horizontal[to_index(h2 - h1, 101)] { |
82 |
| - if let Some(b) = vertical[to_index(v2 - v1, 103)] { |
83 |
| - let t = (x1 - x2) * a; |
84 |
| - let u = (y1 - y2) * b; |
85 |
| - let crt = to_index(t * coefficient1 + u * coefficient2, 10403); |
86 |
| - unique[crt] = false; |
| 78 | + for time in 0..103 { |
| 79 | + let mut ys = [0; 103]; |
| 80 | + |
| 81 | + for [_, y, _, dy] in robots { |
| 82 | + let index = (y + dy * time).rem_euclid(103); |
| 83 | + ys[index as usize] += 1; |
| 84 | + } |
| 85 | + |
| 86 | + if ys.iter().filter(|&&c| c >= 31).count() >= 2 { |
| 87 | + rows.push(time); |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + // If there's only one combination then return answer. |
| 92 | + if rows.len() == 1 && columns.len() == 1 { |
| 93 | + let t = columns[0]; |
| 94 | + let u = rows[0]; |
| 95 | + // Combine indices using the Chinese Remainder Theorem to get index mod 10403. |
| 96 | + return (5253 * t + 5151 * u) % 10403; |
| 97 | + } |
| 98 | + |
| 99 | + // Backup check for time when all robot positions are unique. |
| 100 | + let mut grid = Grid::new(101, 103, 0); |
| 101 | + |
| 102 | + for &t in &columns { |
| 103 | + 'outer: for &u in &rows { |
| 104 | + let time = (5253 * t + 5151 * u) % 10403; |
| 105 | + |
| 106 | + for &[x, y, dx, dy] in robots { |
| 107 | + let x = (x + dx * time).rem_euclid(101); |
| 108 | + let y = (y + dy * time).rem_euclid(103); |
| 109 | + |
| 110 | + let point = Point::new(x, y); |
| 111 | + if grid[point] == time { |
| 112 | + continue 'outer; |
87 | 113 | }
|
| 114 | + grid[point] = time; |
88 | 115 | }
|
| 116 | + |
| 117 | + return time; |
89 | 118 | }
|
90 | 119 | }
|
91 | 120 |
|
92 |
| - unique.iter().position(|&u| u).unwrap() |
93 |
| -} |
94 |
| - |
95 |
| -#[inline] |
96 |
| -fn to_index(a: i32, m: i32) -> usize { |
97 |
| - a.rem_euclid(m) as usize |
| 121 | + unreachable!() |
98 | 122 | }
|
0 commit comments