2021-12-17 12:01:43 +01:00
#![ feature(test) ]
extern crate test ;
2021-12-17 17:32:35 +01:00
use std ::ops ::RangeInclusive ;
2021-12-17 12:01:43 +01:00
type TargetArea = ( RangeInclusive < isize > , RangeInclusive < isize > ) ;
type Probe = ( ( isize , isize ) , ( isize , isize ) ) ;
#[ derive(Debug, PartialEq) ]
enum ProbeStatus {
Hit ,
Miss ,
NoLongerReachable ,
}
2021-12-17 14:06:31 +01:00
#[ inline ]
2021-12-17 13:26:10 +01:00
fn calc_status ( ( ( xvel , _ ) , ( x , y ) ) : & Probe , ( xtarget , ytarget ) : & TargetArea ) -> ProbeStatus {
2021-12-17 12:01:43 +01:00
if xtarget . contains ( x ) & & ytarget . contains ( y ) {
ProbeStatus ::Hit
2021-12-17 13:26:10 +01:00
} else if y < ytarget . start ( ) | | x > xtarget . end ( ) | | ( xvel = = & 0 & & ! xtarget . contains ( x ) ) {
2021-12-17 12:01:43 +01:00
ProbeStatus ::NoLongerReachable
} else {
ProbeStatus ::Miss
}
}
2021-12-17 14:06:31 +01:00
#[ inline ]
fn step ( ( ( xvel , yvel ) , ( x , y ) ) : & Probe ) -> Probe {
2021-12-17 12:01:43 +01:00
( ( xvel - xvel . signum ( ) , yvel - 1 ) , ( x + xvel , y + yvel ) )
}
2021-12-17 17:32:35 +01:00
fn get_target ( ) -> TargetArea {
2021-12-17 13:26:10 +01:00
( 34 ..= 67 , - 215 ..= - 186 )
}
2021-12-17 17:32:35 +01:00
fn part1 ( hits : & [ isize ] ) -> isize {
( 1 ..= * hits . iter ( ) . max ( ) . unwrap ( ) ) . sum ( )
2021-12-17 12:01:43 +01:00
}
2021-12-17 17:32:35 +01:00
fn find_hits ( target : & TargetArea ) -> Vec < isize > {
// Doing y in the outer loop and x in the inner would allow us to call last() instead of max()
// in part1, however, for reasons unknown to me, that makes this function 20% slower.
2021-12-17 12:51:01 +01:00
( 1 ..= * target . 0. end ( ) )
. flat_map ( move | x | ( * target . 1. start ( ) .. 250 ) . map ( move | y | ( x , y ) ) )
2021-12-17 17:32:35 +01:00
. filter_map ( | ( startx , starty ) | {
let mut probe = ( ( startx , starty ) , ( 0 , 0 ) ) ;
2021-12-17 12:01:43 +01:00
loop {
2021-12-17 14:06:31 +01:00
probe = step ( & probe ) ;
2021-12-17 12:01:43 +01:00
match calc_status ( & probe , target ) {
2021-12-17 17:32:35 +01:00
ProbeStatus ::Hit = > return Some ( starty ) ,
2021-12-17 13:26:10 +01:00
ProbeStatus ::Miss = > continue ,
2021-12-17 12:32:46 +01:00
ProbeStatus ::NoLongerReachable = > return None ,
2021-12-17 12:01:43 +01:00
}
}
2021-12-17 12:32:46 +01:00
} )
. collect ( )
2021-12-17 12:01:43 +01:00
}
fn main ( ) {
2021-12-17 17:32:35 +01:00
let hits = find_hits ( & get_target ( ) ) ;
2021-12-17 12:32:46 +01:00
println! ( " Part 1: {} " , part1 ( & hits ) ) ;
println! ( " Part 2: {} " , hits . len ( ) ) ;
2021-12-17 12:01:43 +01:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2021 ::* ;
#[ test ]
fn part1_test ( ) {
let input = ( 20 ..= 30 , - 10 ..= - 5 ) ;
let hits = find_hits ( & input ) ;
2021-12-17 12:32:46 +01:00
assert_eq! ( part1 ( & hits ) , 45 ) ;
2021-12-17 12:01:43 +01:00
}
#[ test ]
fn part2_test ( ) {
let input = ( 20 ..= 30 , - 10 ..= - 5 ) ;
let hits = find_hits ( & input ) ;
2021-12-17 12:32:46 +01:00
assert_eq! ( hits . len ( ) , 112 ) ;
2021-12-17 12:01:43 +01:00
}
#[ bench ]
fn bench_find_hits ( b : & mut test ::Bencher ) {
2021-12-17 17:32:35 +01:00
let input = get_target ( ) ;
b . iter ( | | assert_eq! ( find_hits ( test ::black_box ( & input ) ) . len ( ) , 2040 ) )
2021-12-17 12:01:43 +01:00
}
#[ bench ]
fn bench_part1 ( b : & mut test ::Bencher ) {
2021-12-17 17:32:35 +01:00
let input = get_target ( ) ;
2021-12-17 12:01:43 +01:00
let hits = find_hits ( & input ) ;
2021-12-17 17:32:35 +01:00
b . iter ( | | assert_eq! ( part1 ( test ::black_box ( & hits ) ) , 23005 ) )
2021-12-17 12:01:43 +01:00
}
}