1- use std:: sync:: atomic:: { AtomicI32 , Ordering } ;
2- use utils:: multithreading:: worker_pool;
1+ use utils:: number:: chinese_remainder;
32use utils:: point:: Point2D ;
43use utils:: prelude:: * ;
54
65/// Finding when robots arrange themselves into a picture.
7- ///
8- /// Assumes that the picture of the Christmas tree will involve a horizontal line of at least 10
9- /// robots, and that doesn't happen in any prior iterations.
106#[ derive( Clone , Debug ) ]
117pub struct Day14 {
128 robots : Vec < Robot > ,
@@ -18,7 +14,6 @@ struct Robot {
1814 velocity : Point2D < i32 > ,
1915}
2016
21- // WIDTH must be less than 128
2217const WIDTH : i32 = 101 ;
2318const HEIGHT : i32 = 103 ;
2419
@@ -64,39 +59,38 @@ impl Day14 {
6459
6560 #[ must_use]
6661 pub fn part2 ( & self ) -> u32 {
67- let counter = AtomicI32 :: new ( 0 ) ;
68- let result = AtomicI32 :: new ( i32:: MAX ) ;
62+ // The only time there are more than 30 robots in a single row and more than 30 robots in a
63+ // single column is when the robots are arranged into a Christmas Tree. Additionally, each
64+ // robot's x position repeats every 101 seconds, and y position repeats every 103 seconds.
65+ // This allows separately finding the time mod 101 where there are enough robots in a single
66+ // column, and then finding the time mod 103 where there are enough robots in a single row.
67+ // This then gives us two equations which can be solved using the Chinese reminder theorem.
68+ // result % 101 == A
69+ // result % 103 == B
6970
70- worker_pool ( || {
71- while result. load ( Ordering :: Acquire ) == i32:: MAX {
72- let i = counter. fetch_add ( 1 , Ordering :: AcqRel ) ;
73- let mut grid = [ 0u128 ; HEIGHT as usize ] ;
71+ let a = ( 0 ..WIDTH )
72+ . find ( |& t| {
73+ let mut columns = [ 0u8 ; WIDTH as usize ] ;
7474 for r in & self . robots {
75- let pos = r. position + ( r. velocity * i ) ;
76- grid [ pos . y . rem_euclid ( HEIGHT ) as usize ] | = 1 << pos . x . rem_euclid ( WIDTH ) ;
75+ let col = ( r. position . x + r. velocity . x * t ) . rem_euclid ( WIDTH ) ;
76+ columns [ col as usize ] + = 1 ;
7777 }
78+ columns. iter ( ) . any ( |& b| b >= 30 )
79+ } )
80+ . expect ( "expected a time to have more than 30 robots in a column" ) ;
7881
79- if grid. iter ( ) . any ( |& b| Self :: has_ten_consecutive_bits ( b) ) {
80- result. fetch_min ( i, Ordering :: AcqRel ) ;
81- return ;
82+ let b = ( 0 ..HEIGHT )
83+ . find ( |& t| {
84+ let mut rows = [ 0u8 ; HEIGHT as usize ] ;
85+ for r in & self . robots {
86+ let row = ( r. position . y + r. velocity . y * t) . rem_euclid ( HEIGHT ) ;
87+ rows[ row as usize ] += 1 ;
8288 }
83- }
84- } ) ;
85-
86- result. into_inner ( ) as u32
87- }
89+ rows. iter ( ) . any ( |& b| b >= 30 )
90+ } )
91+ . expect ( "expected a time to have more than 30 robots in a row" ) ;
8892
89- fn has_ten_consecutive_bits ( b : u128 ) -> bool {
90- b & ( b << 1 )
91- & ( b << 2 )
92- & ( b << 3 )
93- & ( b << 4 )
94- & ( b << 5 )
95- & ( b << 6 )
96- & ( b << 7 )
97- & ( b << 8 )
98- & ( b << 9 )
99- != 0
93+ chinese_remainder ( [ a, b] , [ WIDTH , HEIGHT ] ) . unwrap ( ) as u32
10094 }
10195}
10296
0 commit comments