2020-12-03 08:54:04 +01:00
#![ feature(test) ]
2021-04-29 19:14:28 +02:00
#![ allow(clippy::ptr_arg) ]
2020-12-03 08:54:04 +01:00
extern crate test ;
2020-12-10 14:44:42 +01:00
use aoc2020 ::common ::* ;
2020-12-03 08:54:04 +01:00
use itertools ::Itertools ;
2020-12-10 14:44:42 +01:00
use std ::iter ;
2020-12-03 08:54:04 +01:00
2020-12-03 10:24:03 +01:00
#[ derive(Debug, PartialEq, Copy, Clone) ]
2020-12-03 08:54:04 +01:00
enum Tile {
Free ,
Tree ,
}
type Forest = Vec < Vec < Tile > > ;
const STEP_RIGHT : [ usize ; 5 ] = [ 1 , 3 , 5 , 7 , 1 ] ;
const STEP_DOWN : [ usize ; 5 ] = [ 1 , 1 , 1 , 1 , 2 ] ;
2020-12-04 18:36:43 +01:00
const TREE : u8 = b '#' ;
const FREE : u8 = b '.' ;
2020-12-03 08:54:04 +01:00
2020-12-04 14:23:48 +01:00
impl From < u8 > for Tile {
2020-12-03 11:39:48 +01:00
#[ inline ]
2020-12-04 14:23:48 +01:00
fn from ( b : u8 ) -> Self {
match b {
FREE = > Tile ::Free ,
TREE = > Tile ::Tree ,
2020-12-03 08:54:04 +01:00
_ = > unreachable! ( ) ,
}
}
}
fn read_input ( ) -> String {
2020-12-10 14:44:42 +01:00
read_file ( 3 )
2020-12-03 08:54:04 +01:00
}
fn parse_input ( raw : & str ) -> Forest {
2020-12-04 14:23:48 +01:00
raw . lines ( ) . map ( | l | l . bytes ( ) . map_into ( ) . collect ( ) ) . collect ( )
2020-12-03 08:54:04 +01:00
}
fn count_all_paths ( forest : & Forest ) -> usize {
2020-12-03 10:24:03 +01:00
STEP_RIGHT
. iter ( )
. zip ( STEP_DOWN . iter ( ) )
2020-12-03 08:54:04 +01:00
. map ( | ( & r , & d ) | count_trees ( forest , r , d ) )
. product ( )
}
fn count_trees ( forest : & Forest , step_right : usize , step_down : usize ) -> usize {
2020-12-04 11:08:06 +01:00
iter ::successors ( Some ( ( 0 , 0 ) ) , | ( y , x ) | {
Some ( (
y + step_down ,
Some ( x + step_right )
. filter ( | & it | it < forest [ 0 ] . len ( ) )
2020-12-08 11:32:59 +01:00
. unwrap_or_else ( | | ( x + step_right ) - forest [ 0 ] . len ( ) ) ,
2020-12-04 11:08:06 +01:00
) )
} )
. map_while ( | ( y , x ) | forest . get ( y ) . map ( | r | r [ x ] ) )
. filter ( | & t | t = = Tile ::Tree )
. count ( )
2020-12-03 08:54:04 +01:00
}
fn main ( ) {
let forest = parse_input ( & read_input ( ) ) ;
let p1 = count_trees ( & forest , STEP_RIGHT [ 1 ] , STEP_DOWN [ 1 ] ) ;
println! ( " Part 1: {} " , p1 ) ;
let p2 = count_all_paths ( & forest ) ;
println! ( " Part 2: {} " , p2 ) ;
}
2020-12-03 11:32:32 +01:00
#[ allow(unused) ]
2020-12-03 08:54:04 +01:00
mod tests {
use super ::* ;
2020-12-10 14:01:25 +01:00
use aoc2020 ::* ;
use paste ::paste ;
2020-12-03 11:32:32 +01:00
use test ::{ self , black_box } ;
2020-12-03 08:54:04 +01:00
2020-12-10 14:01:25 +01:00
const TEST_INPUT : & str = " ..##.......
2020-12-03 08:54:04 +01:00
#.. . #.. . #..
. #.. .. #.. #.
.. #. #.. . #. #
. #.. . ##.. #.
.. #. ##.. .. .
. #. #. #.. .. #
. #.. .. .. .. #
#. ##.. . #.. .
#.. . ##.. .. #
. #.. #.. . #. #" ;
2020-12-03 11:32:32 +01:00
fn count_trees_imperative ( forest : & Forest , step_right : usize , step_down : usize ) -> usize {
let mut x = 0 ;
let mut y = 0 ;
let mut trees = 0 ;
let width = forest [ 0 ] . len ( ) ;
while y < forest . len ( ) {
trees + = ( forest [ y ] [ x ] = = Tile ::Tree ) as usize ;
y + = step_down ;
2020-12-04 14:23:48 +01:00
x + = step_right ;
// branch-free version of if x >= width { x -= width }
x - = width * ( ( x > = width ) as usize ) ;
2020-12-03 11:32:32 +01:00
}
2020-12-04 18:36:43 +01:00
trees
2020-12-03 11:32:32 +01:00
}
2020-12-10 16:41:54 +01:00
test! ( count_trees ( STEP_RIGHT [ 1 ] , STEP_DOWN [ 1 ] ) = = 7 ) ;
test! ( count_trees_imperative ( STEP_RIGHT [ 1 ] , STEP_DOWN [ 1 ] ) = = 7 ) ;
test! ( count_all_paths ( ) = = 336 ) ;
2020-12-03 11:32:32 +01:00
2020-12-10 16:41:54 +01:00
bench! ( count_all_paths ( ) = = 4723283400 ) ;
bench! ( count_trees ( STEP_RIGHT [ 1 ] , STEP_DOWN [ 1 ] ) = = 187 ) ;
bench! ( count_trees_imperative ( STEP_RIGHT [ 1 ] , STEP_DOWN [ 1 ] ) = = 187 ) ;
bench_input! ( len = = 323 ) ;
2020-12-03 08:54:04 +01:00
}