2019-12-15 16:16:20 +01:00
use grid ::* ;
use intcode ::* ;
use std ::collections ::HashMap ;
use std ::fmt ::{ self , Display , Formatter } ;
use std ::sync ::Mutex ;
#[ macro_use ]
extern crate lazy_static ;
#[ derive(Clone, Copy, PartialEq, Debug) ]
enum Tile {
Wall ,
Empty ,
Oxygen ,
2019-12-15 16:46:25 +01:00
O2 ,
2019-12-15 16:16:20 +01:00
}
#[ derive(Clone) ]
struct Robot {
pos : Position2D ,
dir : Direction ,
ic : IntComputer ,
steps : usize ,
}
impl Robot {
2019-12-15 16:46:25 +01:00
fn step ( & mut self , dir : Direction ) -> Tile {
self . dir = dir ;
2019-12-15 16:16:20 +01:00
self . ic . params . push ( int ( dir ) ) ;
match self . ic . run ( ) {
IntComputerResult ::Output ( 0 ) = > {
save ( self . pos + self . dir , Tile ::Wall ) ;
Tile ::Wall
}
IntComputerResult ::Output ( 1 ) = > {
self . pos + = self . dir ;
save ( self . pos , Tile ::Empty ) ;
self . steps + = 1 ;
Tile ::Empty
}
IntComputerResult ::Output ( 2 ) = > {
self . pos + = self . dir ;
self . steps + = 1 ;
save ( self . pos , Tile ::Oxygen ) ;
2019-12-15 16:46:25 +01:00
OXYGEN_BOT . lock ( ) . unwrap ( ) . push ( self . clone ( ) ) ;
2019-12-15 16:16:20 +01:00
Tile ::Oxygen
}
_ = > unreachable! ( " This roboter shouldn’t halt " ) ,
}
}
}
impl From < i64 > for Tile {
fn from ( i : i64 ) -> Self {
match i {
0 = > Tile ::Wall ,
1 = > Tile ::Empty ,
2 = > Tile ::Oxygen ,
_ = > unreachable! ( " Illegal tile " ) ,
}
}
}
impl Display for Tile {
fn fmt ( & self , f : & mut Formatter ) -> Result < ( ) , fmt ::Error > {
let c = match self {
Tile ::Wall = > '█' ,
Tile ::Empty = > ' ' ,
Tile ::Oxygen = > 'X' ,
2019-12-15 16:46:25 +01:00
Tile ::O2 = > 'O' ,
2019-12-15 16:16:20 +01:00
} ;
write! ( f , " {} " , c )
}
}
2019-12-15 16:46:25 +01:00
fn int ( dir : Direction ) -> i64 {
2019-12-15 16:16:20 +01:00
match dir {
Direction ::Up = > 1 ,
Direction ::Down = > 2 ,
Direction ::Left = > 3 ,
Direction ::Right = > 4 ,
}
}
lazy_static! {
static ref FIELD : Mutex < HashMap < Position2D , Tile > > = Mutex ::new ( HashMap ::new ( ) ) ;
2019-12-15 16:46:25 +01:00
static ref OXYGEN_BOT : Mutex < Vec < Robot > > = Mutex ::new ( Vec ::with_capacity ( 1 ) ) ;
2019-12-15 16:16:20 +01:00
}
#[ inline ]
fn get ( p : & Position2D ) -> Option < Tile > {
2019-12-15 16:46:25 +01:00
FIELD . lock ( ) . unwrap ( ) . get ( p ) . copied ( )
2019-12-15 16:16:20 +01:00
}
#[ inline ]
fn save ( p : Position2D , t : Tile ) {
FIELD . lock ( ) . unwrap ( ) . insert ( p , t ) ;
}
fn print_field ( ) {
2019-12-15 16:46:25 +01:00
if ENABLE_MAP_PRINT {
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 16 ) ) ;
println! ( " \n {} \n " , draw_ascii ( & FIELD . lock ( ) . unwrap ( ) , Tile ::Wall ) ) ;
}
}
#[ rustfmt::skip ]
fn is_dead_end ( pos : & Position2D ) -> bool {
pos . neighbors ( ) . iter ( ) . filter ( | ( _ , p ) | get ( p ) . unwrap ( ) = = Tile ::Empty ) . count ( ) = = 0
}
fn fill ( bot : Robot ) -> usize {
if is_dead_end ( & bot . pos ) {
return bot . steps ;
}
bot . pos
. neighbors ( )
. iter ( )
. map ( | ( dir , pos ) | {
if get ( pos ) . unwrap ( ) = = Tile ::Empty {
let mut clone = bot . clone ( ) ;
clone . pos + = * dir ;
save ( clone . pos , Tile ::O2 ) ;
clone . steps + = 1 ;
print_field ( ) ;
fill ( clone )
} else {
0
}
} )
. max ( )
. unwrap_or ( 0 )
2019-12-15 16:16:20 +01:00
}
fn explore ( bot : Robot ) {
let nbs = bot . pos . neighbors ( ) ;
for ( dir , pos ) in nbs . iter ( ) {
if get ( pos ) . is_none ( ) {
let mut clone = bot . clone ( ) ;
2019-12-15 16:46:25 +01:00
if clone . step ( * dir ) = = Tile ::Empty {
print_field ( ) ;
2019-12-15 16:16:20 +01:00
explore ( clone ) ;
}
}
}
}
2019-12-15 16:46:25 +01:00
#[ rustfmt::skip ]
2019-12-15 17:01:37 +01:00
fn find_generator ( ) -> Robot {
save ( ( 0 , 0 ) . into ( ) , Tile ::Empty ) ;
let bot = Robot {
pos : ( 0 , 0 ) . into ( ) ,
dir : Direction ::Up ,
ic : IntComputer ::new ( read_input ( ) , 0 , vec! [ ] ) ,
steps : 0 ,
} ;
explore ( bot ) ;
2019-12-15 16:46:25 +01:00
OXYGEN_BOT . lock ( ) . unwrap ( ) . pop ( ) . expect ( " No oxygen found in Part 1 " )
}
// Pretty visuals :wow:
const ENABLE_MAP_PRINT : bool = true ;
2019-12-15 16:16:20 +01:00
fn main ( ) {
2019-12-15 17:01:37 +01:00
let mut bot = find_generator ( ) ;
2019-12-15 16:46:25 +01:00
println! ( " Part 1: {} " , bot . steps ) ;
bot . steps = 0 ;
println! ( " Part 2: {} " , fill ( bot ) ) ;
2019-12-15 16:16:20 +01:00
}