2019-12-12 22:23:26 +01:00
use std ::cmp ::Ordering ;
use std ::io ::{ self , BufRead } ;
2019-12-12 14:56:49 +01:00
#[ macro_use ]
extern crate scan_fmt ;
2019-12-12 22:23:26 +01:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
2019-12-12 14:56:49 +01:00
struct Moon {
2019-12-12 22:23:26 +01:00
x : i64 ,
y : i64 ,
z : i64 ,
x_vel : i64 ,
y_vel : i64 ,
z_vel : i64 ,
}
impl std ::fmt ::Display for Moon {
fn fmt ( & self , fmt : & mut std ::fmt ::Formatter ) -> Result < ( ) , std ::fmt ::Error > {
write! (
fmt ,
" pos=<x={}, y={}, z={}>, vel=<x={}, y={}, z={}> " ,
self . x , self . y , self . z , self . x_vel , self . y_vel , self . z_vel
)
}
}
impl std ::fmt ::Display for System {
fn fmt ( & self , fmt : & mut std ::fmt ::Formatter ) -> Result < ( ) , std ::fmt ::Error > {
write! (
fmt ,
" {} \n {} \n {} \n {} " ,
self . moons [ 0 ] , self . moons [ 1 ] , self . moons [ 2 ] , self . moons [ 3 ] ,
)
}
}
impl Moon {
fn mv ( & mut self ) {
self . x + = self . x_vel ;
self . y + = self . y_vel ;
self . z + = self . z_vel ;
}
fn calculate_gravity ( & mut self , others : & [ Moon ] ) {
for moon in others {
self . x_vel + = int ( self . x . cmp ( & moon . x ) ) ;
self . y_vel + = int ( self . y . cmp ( & moon . y ) ) ;
self . z_vel + = int ( self . z . cmp ( & moon . z ) ) ;
}
}
fn energy ( & self ) -> i64 {
( self . x . abs ( ) + self . y . abs ( ) + self . z . abs ( ) )
* ( self . x_vel . abs ( ) + self . y_vel . abs ( ) + self . z_vel . abs ( ) )
}
}
#[ derive(Clone) ]
struct System {
moons : Vec < Moon > ,
}
impl System {
// TODO: don’t take ownership
fn step ( self ) -> Self {
let old_moons = self . moons . clone ( ) ;
System {
moons : self
. moons
. into_iter ( )
. map ( | mut moon | {
moon . calculate_gravity ( & old_moons ) ;
moon . mv ( ) ;
moon
} )
. collect ( ) ,
}
}
2019-12-12 14:56:49 +01:00
}
fn main ( ) {
2019-12-12 22:52:13 +01:00
let mut system = System {
2019-12-12 22:23:26 +01:00
moons : io ::stdin ( )
. lock ( )
. lines ( )
. map ( | l | parse ( & l . unwrap ( ) ) )
. collect ( ) ,
} ;
let mut part1_system = system . clone ( ) ;
for _ in 0 .. 1000 {
part1_system = part1_system . step ( ) ;
}
let energy : i64 = part1_system . moons . into_iter ( ) . map ( | m | m . energy ( ) ) . sum ( ) ;
println! ( " Part 1: {} " , energy ) ;
2019-12-12 22:52:13 +01:00
let initial_x = extract_attribute ( & system , | m | ( m . x , m . x_vel ) ) ;
let initial_y = extract_attribute ( & system , | m | ( m . y , m . y_vel ) ) ;
let initial_z = extract_attribute ( & system , | m | ( m . z , m . z_vel ) ) ;
2019-12-12 22:23:26 +01:00
let ( mut x_found , mut y_found , mut z_found ) = ( 0 u64 , 0 , 0 ) ;
for i in 1 .. {
2019-12-12 22:52:13 +01:00
system = system . step ( ) ;
if x_found = = 0 {
let xs = extract_attribute ( & system , | m | ( m . x , m . x_vel ) ) ;
if xs = = initial_x {
x_found = i ;
}
2019-12-12 22:23:26 +01:00
}
2019-12-12 22:52:13 +01:00
if y_found = = 0 {
let ys = extract_attribute ( & system , | m | ( m . y , m . y_vel ) ) ;
if ys = = initial_y {
y_found = i ;
}
2019-12-12 22:23:26 +01:00
}
2019-12-12 22:52:13 +01:00
if z_found = = 0 {
let zs = extract_attribute ( & system , | m | ( m . z , m . z_vel ) ) ;
if zs = = initial_z {
z_found = i ;
}
2019-12-12 22:23:26 +01:00
}
if x_found ! = 0 & & y_found ! = 0 & & z_found ! = 0 {
break ;
}
}
println! ( " Part 2: {} " , lcm ( lcm ( x_found , y_found ) , z_found ) ) ;
}
2019-12-12 22:52:13 +01:00
fn extract_attribute < F > ( system : & System , f : F ) -> Vec < ( i64 , i64 ) >
where
F : ( FnMut ( Moon ) -> ( i64 , i64 ) ) ,
{
2019-12-12 22:36:43 +01:00
system . moons . clone ( ) . into_iter ( ) . map ( f ) . collect ( )
}
2019-12-12 22:23:26 +01:00
fn int ( ord : Ordering ) -> i64 {
match ord {
Ordering ::Less = > 1 ,
Ordering ::Equal = > 0 ,
Ordering ::Greater = > - 1 ,
}
2019-12-12 14:56:49 +01:00
}
2019-12-12 22:23:26 +01:00
fn gcd ( mut x : u64 , mut y : u64 ) -> u64 {
2019-12-12 14:56:49 +01:00
let mut remainder ;
while y ! = 0 {
remainder = x % y ;
x = y ;
y = remainder ;
}
x
}
2019-12-12 22:23:26 +01:00
fn lcm ( x : u64 , y : u64 ) -> u64 {
2019-12-12 14:56:49 +01:00
x * y / gcd ( x , y )
}
#[ rustfmt::skip ]
fn parse ( line : & str ) -> Moon {
2019-12-12 22:23:26 +01:00
let ( x , y , z ) = scan_fmt! ( line , " <x={}, y={}, z={}> " , i64 , i64 , i64 ) . unwrap ( ) ;
2019-12-12 14:56:49 +01:00
Moon { x , y , z , x_vel : 0 , y_vel : 0 , z_vel : 0 , }
}
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn test_gcd ( ) {
assert_eq! ( gcd ( 20 , 10 ) , 10 ) ;
assert_eq! ( gcd ( 20 , 15 ) , 5 ) ;
assert_eq! ( gcd ( 15 , 20 ) , 5 ) ;
}
#[ test ]
fn test_lcm ( ) {
assert_eq! ( lcm ( 20 , 10 ) , 20 ) ;
assert_eq! ( lcm ( 3 , 7 ) , 21 ) ;
assert_eq! ( lcm ( 7 , 3 ) , 21 ) ;
assert_eq! ( lcm ( lcm ( 7 , 3 ) , 5 ) , 105 ) ;
}
#[ test ]
#[ rustfmt::skip ]
fn test_parse ( ) {
assert_eq! ( parse ( " <x=-14, y=9, z=-4> " ) , Moon { x : - 14 , y : 9 , z : - 4 , x_vel : 0 , y_vel : 0 , z_vel : 0 } ) ;
assert_ne! ( parse ( " <x=-14, y=9, z=-4> " ) , Moon { x : 14 , y : 9 , z : - 4 , x_vel : 0 , y_vel : 0 , z_vel : 0 } ) ;
}
}