2019-12-17 10:32:15 +01:00
use grid ::* ;
use intcode ::* ;
2019-12-17 21:28:10 +01:00
use itertools ::Itertools ;
2019-12-17 10:32:15 +01:00
use std ::char ;
2019-12-17 21:20:22 +01:00
use std ::collections ::{ HashMap , HashSet } ;
2019-12-17 12:38:58 +01:00
use std ::fmt ;
2019-12-17 14:48:46 +01:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
2019-12-17 12:38:58 +01:00
struct Movement {
rotation : i8 ,
distance : u8 ,
}
impl fmt ::Display for Movement {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> Result < ( ) , fmt ::Error > {
let dir_char = if self . rotation = = 1 { 'R' } else { 'L' } ;
2019-12-17 21:28:10 +01:00
write! ( f , " {},{} " , dir_char , self . distance )
2019-12-17 12:38:58 +01:00
}
}
#[ rustfmt::skip ]
fn find_commands ( field : & HashMap < Position2D , char > ) -> Vec < Movement > {
let mut robot_position = field . iter ( ) . find ( | ( _ , c ) | * c = = & '^' ) . unwrap ( ) . 0. to_owned ( ) ;
let mut robot_direction = Direction ::Up ;
let mut commands = Vec ::new ( ) ;
loop {
let mut steps = 0 ;
2019-12-17 21:20:22 +01:00
let turn = ( ( field . get ( & ( robot_position + ( robot_direction + 1 ) ) ) = = Some ( & '#' ) ) as i8 ) * 2 - 1 ;
2019-12-17 12:38:58 +01:00
robot_direction + = turn ;
while field . get ( & ( robot_position + robot_direction ) ) = = Some ( & '#' ) {
robot_position + = robot_direction ;
steps + = 1 ;
}
commands . push ( Movement {
distance : steps ,
rotation : turn ,
} ) ;
if robot_position . neighbors ( ) . iter ( ) . filter ( | ( _ , p ) | field . get ( p ) = = Some ( & '#' ) ) . count ( ) = = 1 {
break ;
}
}
commands
}
2019-12-17 10:32:15 +01:00
fn main ( ) {
// The main reason I use a hashmap here (instead of a 2D vector) is that my abstractions for
// ascii stuff all use maps ヽ( ゚ヮ・)ノ
2019-12-17 14:48:46 +01:00
let mut input = read_input ( ) ;
let field : HashMap < Position2D , char > = IntComputer ::without_params ( input . clone ( ) )
2019-12-17 10:32:15 +01:00
. get_all_outputs ( )
. iter ( )
. map ( | n | char ::from_u32 ( * n as u32 ) . unwrap ( ) )
. collect ::< String > ( )
. lines ( )
2019-12-17 21:28:10 +01:00
// this rev breaks part 1 but is necessary for part 2. remove it to get the part 1 solution
2019-12-17 14:48:46 +01:00
. rev ( )
2019-12-17 10:32:15 +01:00
. enumerate ( )
. flat_map ( move | ( y , s ) | s . chars ( ) . enumerate ( ) . map ( move | ( x , c ) | ( ( x , y ) . into ( ) , c ) ) )
. collect ( ) ;
let p1 = field
2019-12-17 12:38:58 +01:00
. iter ( )
. filter ( | ( pos , obj ) | {
* obj = = & '#'
& & pos
. neighbors ( )
. iter ( )
. all ( | ( _ , p ) | field . get ( & p ) = = Some ( & '#' ) )
} )
. fold ( 0 , | acc , ( pos , _ ) | acc + pos . x * pos . y ) ;
2019-12-17 10:32:15 +01:00
println! ( " Part 1: {} " , p1 ) ;
2019-12-17 12:38:58 +01:00
let commands = find_commands ( & field ) ;
2019-12-17 14:48:46 +01:00
let mut pos = 0 ;
2019-12-17 21:20:22 +01:00
let mut segments = Vec ::new ( ) ;
while pos < commands . len ( ) - 4 {
if let Some ( ( n , mov ) ) = ( 2 ..= 4 )
2019-12-17 14:48:46 +01:00
. filter_map ( | i | {
let reference = commands [ pos .. ] . windows ( i ) . next ( ) ;
2019-12-17 21:20:22 +01:00
if segments . contains ( & reference . unwrap ( ) ) {
Some ( ( ( i , 99 ) , reference ) )
2019-12-17 14:48:46 +01:00
} else {
2019-12-17 21:20:22 +01:00
let dupes = commands [ pos .. ]
. windows ( i )
. filter ( | & w | Some ( w ) = = reference )
. count ( ) ;
if dupes > 0 {
Some ( ( ( i , dupes ) , reference ) )
} else {
None
}
2019-12-17 14:48:46 +01:00
}
} )
2019-12-17 21:20:22 +01:00
. max_by_key ( | ( ( x , y ) , _ ) | x * y )
2019-12-17 14:48:46 +01:00
{
pos + = n . 0 ;
segments . push ( mov . unwrap ( ) ) ;
}
}
2019-12-17 21:28:10 +01:00
let filtered : HashSet < _ > = segments . clone ( ) . into_iter ( ) . collect ( ) ;
let mut filtered : Vec < _ > = filtered . into_iter ( ) . zip ( [ 'A' , 'B' , 'C' ] . iter ( ) ) . collect ( ) ;
2019-12-17 21:20:22 +01:00
filtered . sort_by_key ( | ( _ , c ) | c . to_owned ( ) ) ;
2019-12-17 14:48:46 +01:00
let mut instructions = Vec ::new ( ) ;
let mut pos = 0 ;
while pos < commands . len ( ) {
for i in 1 .. {
2019-12-17 21:20:22 +01:00
if filtered . iter ( ) . any ( | ( c , _ ) | c = = & & commands [ pos .. pos + i ] ) {
instructions . push ( & commands [ pos .. pos + i ] ) ;
2019-12-17 14:48:46 +01:00
pos + = i ;
break ;
}
}
}
input [ 0 ] = 2 ;
2019-12-17 21:28:10 +01:00
let path : Vec < i64 > = ( instructions
. iter ( )
. map ( | i | filtered . iter ( ) . find ( | ( f , _ ) | i = = f ) . unwrap ( ) . 1 )
. join ( " , " )
2019-12-17 21:20:22 +01:00
+ " \n "
2019-12-17 21:28:10 +01:00
+ & filtered
. into_iter ( )
. map ( | ( f , _ ) | f . iter ( ) . map ( | m | m . to_string ( ) ) . join ( " , " ) )
. join ( " \n " )
+ " \n n \n " )
. chars ( )
. map ( | c | c as i64 )
. rev ( )
. collect ( ) ;
2019-12-17 21:20:22 +01:00
println! (
" Part 2: {:?} " ,
2019-12-17 21:28:10 +01:00
IntComputer ::new ( input , 0 , path ) . get_all_outputs ( ) . pop ( ) . unwrap ( )
2019-12-17 21:20:22 +01:00
) ;
2019-12-17 10:32:15 +01:00
}