2019-12-13 13:40:32 +01:00
use itertools ::join ;
use std ::collections ::HashMap ;
2019-12-13 19:36:47 +01:00
use std ::fmt ::Display ;
use std ::hash ::BuildHasher ;
2019-12-15 12:36:46 +01:00
use std ::ops ::AddAssign ;
#[ macro_use ]
extern crate impl_ops ;
use std ::ops ;
2019-12-13 13:40:32 +01:00
#[ derive(Hash, PartialEq, Eq, Debug, Clone, Copy) ]
pub struct Position2D {
2019-12-13 19:29:22 +01:00
pub x : i64 ,
pub y : i64 ,
2019-12-13 13:40:32 +01:00
}
2019-12-15 16:16:20 +01:00
#[ derive(Clone, Copy, Debug) ]
2019-12-15 11:49:30 +01:00
pub enum Direction {
Up ,
Down ,
Left ,
Right ,
}
2019-12-15 16:16:20 +01:00
pub const ALL_DIRECTIONS : [ Direction ; 4 ] = [
Direction ::Up ,
Direction ::Down ,
Direction ::Left ,
Direction ::Right ,
] ;
2019-12-13 13:40:32 +01:00
struct Boundaries {
x_min : i64 ,
x_max : i64 ,
y_min : i64 ,
y_max : i64 ,
}
#[ rustfmt::skip ]
fn get_boundaries ( input : & [ & Position2D ] ) -> Boundaries {
let x_min = input . iter ( ) . min_by_key ( | k | k . x ) . map ( | p | p . x ) . unwrap_or ( 0 ) ;
let x_max = input . iter ( ) . max_by_key ( | k | k . x ) . map ( | p | p . x ) . unwrap_or ( 0 ) ;
let y_min = input . iter ( ) . min_by_key ( | k | k . y ) . map ( | p | p . y ) . unwrap_or ( 0 ) ;
let y_max = input . iter ( ) . max_by_key ( | k | k . y ) . map ( | p | p . y ) . unwrap_or ( 0 ) ;
Boundaries { x_min , x_max , y_min , y_max }
}
2019-12-15 11:49:30 +01:00
pub fn draw_ascii < T : Display , S : BuildHasher > (
coordinates : & HashMap < Position2D , T , S > ,
default : T ,
) -> String {
2019-12-13 13:40:32 +01:00
let b = get_boundaries ( & coordinates . keys ( ) . collect ::< Vec < _ > > ( ) ) ;
join (
( b . y_min ..= b . y_max ) . rev ( ) . map ( | y | {
2019-12-13 19:29:22 +01:00
( b . x_min ..= b . x_max )
2019-12-13 13:40:32 +01:00
. map ( | x | {
coordinates
. get ( & ( x , y ) . into ( ) )
. unwrap_or ( & default )
. to_string ( )
} )
. collect ::< String > ( )
} ) ,
" \n " ,
)
}
2019-12-15 16:16:20 +01:00
impl Position2D {
pub fn neighbors ( & self ) -> [ ( Direction , Position2D ) ; 4 ] {
2019-12-15 16:46:25 +01:00
[
( Direction ::Up , * self + Direction ::Up ) ,
( Direction ::Down , * self + Direction ::Down ) ,
( Direction ::Right , * self + Direction ::Right ) ,
( Direction ::Left , * self + Direction ::Left ) ,
]
2019-12-15 16:16:20 +01:00
}
}
2019-12-15 12:00:28 +01:00
impl Direction {
pub fn turn ( & mut self , turn_value : i64 ) {
* self = match turn_value {
- 1 = > match self {
Direction ::Up = > Direction ::Left ,
Direction ::Right = > Direction ::Up ,
Direction ::Down = > Direction ::Right ,
Direction ::Left = > Direction ::Down ,
} ,
1 = > match self {
Direction ::Up = > Direction ::Right ,
Direction ::Right = > Direction ::Down ,
Direction ::Down = > Direction ::Left ,
Direction ::Left = > Direction ::Up ,
} ,
0 = > * self ,
n = > unreachable! ( format! ( " Illegal turn value: {} " , n ) ) ,
}
}
}
2019-12-15 12:36:46 +01:00
impl_op! ( + | a : Position2D , b : Position2D | -> Position2D {
Position2D {
x : a . x + b . x ,
y : a . y + b . y }
} ) ;
impl_op! ( - | a : Position2D , b : Position2D | -> Position2D {
Position2D {
x : a . x - b . x ,
y : a . y - b . y ,
2019-12-15 11:49:30 +01:00
}
2019-12-15 12:36:46 +01:00
} ) ;
impl_op! ( + | a : Position2D , b : Direction | -> Position2D { a + match b {
Direction ::Up = > Position2D ::from ( ( 0 , 1 ) ) ,
Direction ::Right = > Position2D ::from ( ( 1 , 0 ) ) ,
Direction ::Left = > Position2D ::from ( ( - 1 , 0 ) ) ,
Direction ::Down = > Position2D ::from ( ( 0 , - 1 ) ) ,
}
} ) ;
2019-12-15 11:49:30 +01:00
2019-12-15 12:36:46 +01:00
impl AddAssign < Direction > for Position2D {
fn add_assign ( & mut self , rhs : Direction ) {
2019-12-15 11:49:30 +01:00
* self = * self + rhs ;
}
}
2019-12-15 12:36:46 +01:00
impl AddAssign for Position2D {
fn add_assign ( & mut self , rhs : Position2D ) {
* self = * self + rhs ;
2019-12-13 13:40:32 +01:00
}
}
impl From < ( i64 , i64 ) > for Position2D {
fn from ( tuple : ( i64 , i64 ) ) -> Position2D {
Position2D {
x : tuple . 0 ,
y : tuple . 1 ,
}
}
}
2019-12-15 16:16:20 +01:00
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn test_add ( ) {
assert_eq! (
Position2D { x : 0 , y : 2 } + Position2D { x : - 1 , y : 0 } ,
( - 1 , 2 ) . into ( )
) ;
2019-12-15 16:46:25 +01:00
assert_eq! ( Position2D { x : 0 , y : - 1 } + Direction ::Up , ( 0 , 0 ) . into ( ) ) ;
2019-12-15 16:16:20 +01:00
}
}