2020-12-11 12:04:38 +01:00
#![ feature(test) ]
extern crate test ;
use aoc2020 ::{ common ::* , grid ::* } ;
use std ::{
collections ::HashMap , fmt ::{ self , Display , Formatter }
} ;
#[ derive(Debug, PartialEq, Clone, Copy) ]
enum Tile {
Floor ,
Empty ,
Occupied ,
}
impl Display for Tile {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> fmt ::Result {
f . write_str ( match self {
Tile ::Floor = > " . " ,
Tile ::Empty = > " L " ,
Tile ::Occupied = > " # " ,
} )
}
}
impl From < char > for Tile {
2020-12-11 14:45:41 +01:00
#[ inline ]
2020-12-11 12:04:38 +01:00
fn from ( c : char ) -> Self {
match c {
'.' = > Tile ::Floor ,
'L' = > Tile ::Empty ,
'#' = > Tile ::Occupied ,
_ = > unreachable! ( ) ,
}
}
}
impl Default for Tile {
fn default ( ) -> Self {
Tile ::Floor
}
}
2021-07-08 19:21:07 +02:00
type Parsed = HashMap < PositionND < 2 > , Tile > ;
2020-12-11 12:04:38 +01:00
fn read_input ( ) -> String {
read_file ( 11 )
}
fn parse_input ( raw : & str ) -> Parsed {
raw . lines ( )
. enumerate ( )
. flat_map ( move | ( y , l ) | {
2021-07-08 19:21:07 +02:00
l . chars ( ) . enumerate ( ) . map ( move | ( x , c ) | {
(
PositionND {
points : [ x as i64 , y as i64 ] ,
} ,
Tile ::from ( c ) ,
)
} )
2020-12-11 12:04:38 +01:00
} )
. collect ( )
}
2020-12-11 14:45:41 +01:00
#[ inline ]
2021-07-08 19:21:07 +02:00
fn occupied_neighbors ( pos : & PositionND < 2 > , grid : & Parsed ) -> usize {
2020-12-17 14:51:05 +01:00
pos . neighbors ( )
2020-12-11 12:04:38 +01:00
. iter ( )
2021-07-08 19:35:31 +02:00
. filter ( | p | grid . get ( p ) . unwrap_or ( & Tile ::Floor ) = = & Tile ::Occupied )
2020-12-11 12:04:38 +01:00
. count ( )
}
2020-12-11 13:32:03 +01:00
const DIRECTIONS : [ ( i64 , i64 ) ; 8 ] = [ ( 0 , 1 ) , ( 1 , 0 ) , ( 1 , 1 ) , ( 0 , - 1 ) , ( - 1 , 0 ) , ( - 1 , - 1 ) , ( - 1 , 1 ) , ( 1 , - 1 ) ] ;
2020-12-11 14:45:41 +01:00
#[ inline ]
2021-07-08 19:21:07 +02:00
fn neighbors_in_vision ( pos : & PositionND < 2 > , grid : & Parsed ) -> usize {
IntoIterator ::into_iter ( DIRECTIONS )
. map ( | ( x , y ) | PositionND { points : [ x , y ] } )
2020-12-11 13:32:03 +01:00
. map ( | p | {
( 1 .. )
. find_map ( | n | match grid . get ( & ( * pos + ( p * n ) ) ) {
Some ( & Tile ::Occupied ) = > Some ( true ) ,
Some ( & Tile ::Floor ) = > None ,
_ = > Some ( false ) ,
} )
. unwrap ( )
} )
. filter ( | & b | b )
. count ( )
}
2021-07-08 19:21:07 +02:00
fn make_step < F : Fn ( & PositionND < 2 > , & Parsed ) -> usize > ( previous : & mut Parsed , count_neighbors : F , limit : usize ) -> bool {
2020-12-11 14:45:41 +01:00
let readonly = previous . to_owned ( ) ;
2020-12-11 12:04:38 +01:00
let mut changed = false ;
2020-12-11 14:45:41 +01:00
for ( pos , tile ) in previous . iter_mut ( ) {
2020-12-11 12:04:38 +01:00
match tile {
Tile ::Floor = > ( ) ,
Tile ::Empty = > {
2021-07-08 19:35:31 +02:00
if count_neighbors ( pos , & readonly ) = = 0 {
2020-12-11 12:04:38 +01:00
* tile = Tile ::Occupied ;
changed = true ;
}
}
Tile ::Occupied = > {
2021-07-08 19:35:31 +02:00
if count_neighbors ( pos , & readonly ) > = limit {
2020-12-11 12:04:38 +01:00
* tile = Tile ::Empty ;
changed = true ;
}
}
}
}
2020-12-11 14:45:41 +01:00
changed
2020-12-11 12:04:38 +01:00
}
2021-07-08 19:21:07 +02:00
fn move_until_equilibrium < F : Fn ( & PositionND < 2 > , & Parsed ) -> usize > ( mut parsed : Parsed , count_neighbors : F , limit : usize ) -> usize {
2020-12-11 14:45:41 +01:00
while make_step ( & mut parsed , & count_neighbors , limit ) { }
parsed . iter ( ) . filter ( | ( _ , t ) | t = = & & Tile ::Occupied ) . count ( )
2020-12-11 12:04:38 +01:00
}
2020-12-11 13:32:03 +01:00
fn part1 ( parsed : & Parsed ) -> usize {
2020-12-11 14:45:41 +01:00
move_until_equilibrium ( parsed . to_owned ( ) , occupied_neighbors , 4 )
2020-12-11 13:32:03 +01:00
}
2020-12-11 12:04:38 +01:00
fn part2 ( parsed : & Parsed ) -> usize {
2020-12-11 14:45:41 +01:00
move_until_equilibrium ( parsed . to_owned ( ) , neighbors_in_vision , 5 )
2020-12-11 12:04:38 +01:00
}
fn main ( ) {
let input = parse_input ( & read_input ( ) ) ;
println! ( " Part 1: {} " , part1 ( & input ) ) ;
2020-12-11 13:32:03 +01:00
println! ( " Part 2: {} " , part2 ( & input ) ) ;
2020-12-11 12:04:38 +01:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2020 ::* ;
use paste ::paste ;
use test ::black_box ;
const TEST_INPUT : & str = " L.LL.LL.LL
LLLLLLL . LL
L . L . L .. L ..
LLLL . LL . LL
L . LL . LL . LL
L . LLLLL . LL
.. L . L .. .. .
LLLLLLLLLL
L . LLLLLL . L
L . LLLLL . LL " ;
const AFTER_1_STEP : & str = " #.##.##.##
#######. ##
#. #. #.. #..
####. ##. ##
#. ##. ##. ##
#. #####. ##
.. #. #.. .. .
##########
#. ######. #
#. #####. ##" ;
const AFTER_2_STEPS : & str = " #.LL.L#.##
#LLLLLL . L #
L . L . L .. L ..
#LLL . LL . L #
#. LL . LL . LL
#. LLLL #. ##
.. L . L .. .. .
#LLLLLLLL #
#. LLLLLL . L
#. #LLLL . ##" ;
#[ test ]
fn step_test ( ) {
2020-12-11 14:45:41 +01:00
let mut grid = parse_input ( TEST_INPUT ) ;
assert! ( make_step ( & mut grid , occupied_neighbors , 4 ) ) ;
2020-12-11 12:04:38 +01:00
let after_first = parse_input ( AFTER_1_STEP ) ;
2020-12-11 14:45:41 +01:00
assert_eq! ( draw_ascii ( & grid ) , draw_ascii ( & after_first ) ) ;
assert_eq! ( grid , after_first ) ;
2020-12-11 13:32:03 +01:00
2020-12-11 14:45:41 +01:00
assert! ( make_step ( & mut grid , occupied_neighbors , 4 ) ) ;
2020-12-11 12:04:38 +01:00
let after_second = parse_input ( AFTER_2_STEPS ) ;
2020-12-11 14:45:41 +01:00
assert_eq! ( grid , after_second ) ;
2020-12-11 12:04:38 +01:00
}
2020-12-11 13:32:03 +01:00
const P2_AFTER_1 : & str = " #.##.##.##
#######. ##
#. #. #.. #..
####. ##. ##
#. ##. ##. ##
#. #####. ##
.. #. #.. .. .
##########
#. ######. #
#. #####. ##" ;
const P2_AFTER_2 : & str = " #.LL.LL.L#
#LLLLLL . LL
L . L . L .. L ..
LLLL . LL . LL
L . LL . LL . LL
L . LLLLL . LL
.. L . L .. .. .
LLLLLLLLL #
#. LLLLLL . L
#. LLLLL . L #" ;
const P2_AFTER_3 : & str = " #.L#.##.L#
#L #####. LL
L . #. #.. #..
##L #. ##. ##
#. ##. #L . ##
#. #####. #L
.. #. #.. .. .
LLL ####LL #
#. L #####. L
#. L ####. L #" ;
#[ test ]
fn step_test_part2 ( ) {
2020-12-11 14:45:41 +01:00
let mut grid = parse_input ( TEST_INPUT ) ;
assert! ( make_step ( & mut grid , neighbors_in_vision , 5 ) ) ;
2020-12-11 13:32:03 +01:00
let after_1 = parse_input ( P2_AFTER_1 ) ;
2020-12-11 14:45:41 +01:00
assert_eq! ( draw_ascii ( & grid ) , draw_ascii ( & after_1 ) ) ;
assert_eq! ( & grid , & after_1 ) ;
2020-12-11 13:32:03 +01:00
2021-07-08 19:21:07 +02:00
assert! ( make_step ( & mut grid , neighbors_in_vision , 5 ) ) ;
2020-12-11 13:32:03 +01:00
let after_2 = parse_input ( P2_AFTER_2 ) ;
2020-12-11 14:45:41 +01:00
assert_eq! ( draw_ascii ( & grid ) , draw_ascii ( & after_2 ) ) ;
assert_eq! ( & grid , & after_2 ) ;
2020-12-11 13:32:03 +01:00
2020-12-11 14:45:41 +01:00
assert! ( make_step ( & mut grid , neighbors_in_vision , 5 ) ) ;
2020-12-11 13:32:03 +01:00
let after_3 = parse_input ( P2_AFTER_3 ) ;
2020-12-11 14:45:41 +01:00
assert_eq! ( draw_ascii ( & grid ) , draw_ascii ( & after_3 ) ) ;
assert_eq! ( & grid , & after_3 ) ;
2020-12-11 13:32:03 +01:00
}
2020-12-11 12:04:38 +01:00
test! ( part1 ( ) = = 37 ) ;
2020-12-11 13:32:03 +01:00
test! ( part2 ( ) = = 26 ) ;
bench! ( part1 ( ) = = 2164 ) ;
bench! ( part2 ( ) = = 1974 ) ;
bench_input! ( len = = 8372 ) ;
2020-12-11 12:04:38 +01:00
}