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
@@ -23,8 +30,8 @@ pub fn part1(input: &[Robot]) -> i32 {
23
30
let mut q4 = 0 ;
24
31
25
32
for [ x, y, dx, dy] in input {
26
- let x = ( x + 100 * dx ) . rem_euclid ( 101 ) ;
27
- let y = ( y + 100 * dy ) . rem_euclid ( 103 ) ;
33
+ let x = ( x + 100 * ( dx + 101 ) ) % 101 ;
34
+ let y = ( y + 100 * ( dy + 103 ) ) % 103 ;
28
35
29
36
if x < 50 {
30
37
if y < 51 {
@@ -47,52 +54,61 @@ 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
+ // and times mod 103 when the tree could possibly exist using y coordinates only.
60
+ let mut rows = Vec :: new ( ) ;
61
+ let mut columns = Vec :: new ( ) ;
55
62
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 ( ) ;
63
+ for time in 0 ..103 {
64
+ let mut xs = [ 0 ; 101 ] ;
65
+ let mut ys = [ 0 ; 103 ] ;
60
66
61
- let mut unique = vec ! [ true ; 10403 ] ;
67
+ for [ x, y, dx, dy] in robots {
68
+ let x = ( x + time * ( dx + 101 ) ) % 101 ;
69
+ xs[ x as usize ] += 1 ;
70
+ let y = ( y + time * ( dy + 103 ) ) % 103 ;
71
+ ys[ y as usize ] += 1 ;
72
+ }
62
73
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 ) ;
74
+ // Tree bounding box is 31x33.
75
+ if time < 101 && xs. iter ( ) . filter ( |& & c| c >= 33 ) . count ( ) >= 2 {
76
+ columns. push ( time) ;
77
+ }
78
+ if ys. iter ( ) . filter ( |& & c| c >= 31 ) . count ( ) >= 2 {
79
+ rows. push ( time) ;
80
+ }
81
+ }
68
82
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 ) ;
83
+ // If there's only one combination then return answer.
84
+ if rows . len ( ) == 1 && columns . len ( ) == 1 {
85
+ let t = columns [ 0 ] ;
86
+ let u = rows [ 0 ] ;
87
+ // Combine indices using the Chinese Remainder Theorem to get index mod 10403.
88
+ return ( 5253 * t + 5151 * u ) % 10403 ;
89
+ }
76
90
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 ;
91
+ // Backup check looking for time when all robot positions are unique.
92
+ let mut grid = Grid :: new ( 101 , 103 , 0 ) ;
93
+
94
+ for & t in & columns {
95
+ ' outer: for & u in & rows {
96
+ let time = ( 5253 * t + 5151 * u) % 10403 ;
97
+
98
+ for & [ x, y, dx, dy] in robots {
99
+ let x = ( x + time * ( dx + 101 ) ) % 101 ;
100
+ let y = ( y + time * ( dy + 103 ) ) % 103 ;
101
+
102
+ let point = Point :: new ( x, y) ;
103
+ if grid[ point] == time {
104
+ continue ' outer;
87
105
}
106
+ grid[ point] = time;
88
107
}
108
+
109
+ return time;
89
110
}
90
111
}
91
112
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
113
+ unreachable ! ( )
98
114
}
0 commit comments