44//! The image appears in part two when the positions of all robots are unique.
55//!
66//! 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:: * ;
916use crate :: util:: iter:: * ;
10- use crate :: util:: math:: * ;
1117use crate :: util:: parse:: * ;
18+ use crate :: util:: point:: * ;
1219
1320type Robot = [ i32 ; 4 ] ;
1421
@@ -23,8 +30,8 @@ pub fn part1(input: &[Robot]) -> i32 {
2330 let mut q4 = 0 ;
2431
2532 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 ;
2835
2936 if x < 50 {
3037 if y < 51 {
@@ -47,52 +54,61 @@ pub fn part1(input: &[Robot]) -> i32 {
4754 q1 * q2 * q3 * q4
4855}
4956
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 ( ) ;
5562
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 ] ;
6066
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+ }
6273
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+ }
6882
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+ }
7690
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;
87105 }
106+ grid[ point] = time;
88107 }
108+
109+ return time;
89110 }
90111 }
91112
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 ! ( )
98114}
0 commit comments