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.
9
15
use crate :: util:: iter:: * ;
10
- use crate :: util:: math:: * ;
11
16
use crate :: util:: parse:: * ;
12
17
13
- type Robot = [ i32 ; 4 ] ;
18
+ type Robot = [ usize ; 4 ] ;
14
19
15
20
pub fn parse ( input : & str ) -> Vec < Robot > {
16
- input. iter_signed ( ) . chunk :: < 4 > ( ) . collect ( )
21
+ input
22
+ . iter_signed :: < i32 > ( )
23
+ . chunk :: < 4 > ( )
24
+ . map ( |[ x, y, dx, dy] | {
25
+ [ x as usize , y as usize , dx. rem_euclid ( 101 ) as usize , dy. rem_euclid ( 103 ) as usize ]
26
+ } )
27
+ . collect ( )
17
28
}
18
29
19
30
pub fn part1 ( input : & [ Robot ] ) -> i32 {
@@ -23,8 +34,8 @@ pub fn part1(input: &[Robot]) -> i32 {
23
34
let mut q4 = 0 ;
24
35
25
36
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 ) ;
37
+ let x = ( x + 100 * dx) % 101 ;
38
+ let y = ( y + 100 * dy) % 103 ;
28
39
29
40
if x < 50 {
30
41
if y < 51 {
@@ -47,52 +58,61 @@ pub fn part1(input: &[Robot]) -> i32 {
47
58
q1 * q2 * q3 * q4
48
59
}
49
60
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 ( ) ;
61
+ pub fn part2 ( robots : & [ Robot ] ) -> usize {
62
+ // Search for times mod 101 when the tree could possibly exist using x coordinates only.
63
+ // and times mod 103 when the tree could possibly exist using y coordinates only.
64
+ let mut rows = Vec :: new ( ) ;
65
+ let mut columns = Vec :: new ( ) ;
55
66
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 ( ) ;
67
+ for time in 0 ..103 {
68
+ let mut xs = [ 0 ; 101 ] ;
69
+ let mut ys = [ 0 ; 103 ] ;
60
70
61
- let mut unique = vec ! [ true ; 10403 ] ;
71
+ for [ x, y, dx, dy] in robots {
72
+ let x = ( x + time * dx) % 101 ;
73
+ xs[ x] += 1 ;
74
+ let y = ( y + time * dy) % 103 ;
75
+ ys[ y] += 1 ;
76
+ }
62
77
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 ) ;
78
+ // Tree bounding box is 31x33.
79
+ if time < 101 && xs. iter ( ) . filter ( |& & c| c >= 33 ) . count ( ) >= 2 {
80
+ columns. push ( time) ;
81
+ }
82
+ if ys. iter ( ) . filter ( |& & c| c >= 31 ) . count ( ) >= 2 {
83
+ rows. push ( time) ;
84
+ }
85
+ }
68
86
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 ) ;
87
+ // If there's only one combination then return answer.
88
+ if rows . len ( ) == 1 && columns . len ( ) == 1 {
89
+ let t = columns [ 0 ] ;
90
+ let u = rows [ 0 ] ;
91
+ // Combine indices using the Chinese Remainder Theorem to get index mod 10403.
92
+ return ( 5253 * t + 5151 * u ) % 10403 ;
93
+ }
76
94
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 ;
95
+ // Backup check looking for time when all robot positions are unique.
96
+ let mut floor = vec ! [ 0 ; 10403 ] ;
97
+
98
+ for & t in & columns {
99
+ ' outer: for & u in & rows {
100
+ let time = ( 5253 * t + 5151 * u) % 10403 ;
101
+
102
+ for & [ x, y, dx, dy] in robots {
103
+ let x = ( x + time * dx) % 101 ;
104
+ let y = ( y + time * dy) % 103 ;
105
+
106
+ let index = 101 * y + x;
107
+ if floor[ index] == time {
108
+ continue ' outer;
87
109
}
110
+ floor[ index] = time;
88
111
}
112
+
113
+ return time;
89
114
}
90
115
}
91
116
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
117
+ unreachable ! ( )
98
118
}
0 commit comments