2021-12-09 10:46:49 +01:00
#![ feature(test) ]
extern crate test ;
2021-12-09 21:14:08 +01:00
use std ::collections ::HashSet ;
2021-12-09 10:46:49 +01:00
use aoc2021 ::common ::* ;
2021-12-11 22:54:57 +01:00
const DAY : usize = 9 ;
2021-12-09 10:46:49 +01:00
type Parsed = Vec < Vec < u8 > > ;
fn parse_input ( raw : & str ) -> Parsed {
raw . lines ( ) . map ( | l | l . bytes ( ) . map ( | b | b - b '0' ) . collect ( ) ) . collect ( )
}
fn part1 ( parsed : & Parsed ) -> usize {
2021-12-09 21:14:08 +01:00
find_lows ( parsed ) . into_iter ( ) . map ( | ( x , y ) | parsed [ x ] [ y ] as usize + 1 ) . sum ( )
}
fn find_lows ( parsed : & Parsed ) -> Vec < ( usize , usize ) > {
2021-12-09 10:46:49 +01:00
( 0 .. parsed . len ( ) )
. flat_map ( | x | ( 0 .. parsed [ x ] . len ( ) ) . map ( move | y | ( x , y ) ) )
2021-12-09 21:14:08 +01:00
. filter ( | & ( x , y ) | {
2021-12-09 10:46:49 +01:00
// There’s gotta be some incomplete_windows or similar that makes this not as horrible
let cur = parsed [ x ] [ y ] ;
2021-12-09 21:14:08 +01:00
all_neighbors ( x , y ) . into_iter ( ) . filter_map ( | ( x , y ) | parsed . get ( x ) . and_then ( | ys | ys . get ( y ) ) ) . all ( | & n | n > cur )
2021-12-09 10:46:49 +01:00
} )
2021-12-09 21:14:08 +01:00
. collect ( )
2021-12-09 10:46:49 +01:00
}
2021-12-09 11:10:21 +01:00
fn all_neighbors ( x : usize , y : usize ) -> Vec < ( usize , usize ) > {
[ x . checked_add ( 1 ) . map ( | x | ( x , y ) ) , x . checked_sub ( 1 ) . map ( | x | ( x , y ) ) , y . checked_add ( 1 ) . map ( | y | ( x , y ) ) , y . checked_sub ( 1 ) . map ( | y | ( x , y ) ) ]
. into_iter ( )
. flatten ( )
. collect ( )
2021-12-09 10:46:49 +01:00
}
2021-12-09 21:14:08 +01:00
fn grow_basin ( parsed : & Parsed , points_in_basin : & mut HashSet < ( usize , usize ) > , ( x , y ) : ( usize , usize ) ) -> bool {
let cur = parsed [ x ] [ y ] ;
let mut new_points = Vec ::new ( ) ;
for ( x , y ) in all_neighbors ( x , y ) . into_iter ( ) . filter ( | p | ! points_in_basin . contains ( p ) ) {
if parsed . get ( x ) . and_then ( | ys | ys . get ( y ) ) . unwrap_or ( & 0 ) > & cur {
new_points . push ( ( x , y ) ) ;
}
}
if new_points . iter ( ) . any ( | & p | grow_basin ( parsed , points_in_basin , p ) ) {
points_in_basin . insert ( ( x , y ) ) ;
true
} else {
false
}
}
2021-12-09 10:46:49 +01:00
fn part2 ( parsed : & Parsed ) -> usize {
2021-12-09 21:14:08 +01:00
let lows = find_lows ( parsed ) ;
let mut basins = Vec ::new ( ) ;
for ( x , y ) in lows {
let mut points_in_basin = HashSet ::new ( ) ;
grow_basin ( parsed , & mut points_in_basin , ( x , y ) ) ;
basins . push ( points_in_basin ) ;
}
basins . sort_unstable_by_key ( HashSet ::len ) ;
basins . reverse ( ) ;
println! ( " {basins:?} " ) ;
2021-12-11 13:07:08 +01:00
// basins.iter().take(3).map(|b| b.len()).product();
unimplemented! ( )
2021-12-09 10:46:49 +01:00
}
fn main ( ) {
let input = parse_input ( & read_file ( DAY ) ) ;
println! ( " Part 1: {} " , part1 ( & input ) ) ;
println! ( " Part 2: {} " , part2 ( & input ) ) ;
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2021 ::* ;
const TEST_INPUT : & str = " 2199943210
3987894921
9856789892
8767896789
9899965678 " ;
test! ( part1 ( ) = = 15 ) ;
2021-12-11 13:07:08 +01:00
// test!(part2() == 1134);
2021-12-09 10:46:49 +01:00
bench! ( part1 ( ) = = 478 ) ;
2021-12-09 21:14:08 +01:00
// bench!(part2() == 0);
2021-12-09 10:46:49 +01:00
bench_input! ( Vec ::len = > 100 ) ;
}