105 lines
3.2 KiB
Rust
105 lines
3.2 KiB
Rust
use crate::utils;
|
|
use std::collections::HashSet;
|
|
|
|
type Coord = i8;
|
|
type Coords = ( Coord, Coord );
|
|
type Count = u16;
|
|
type Height = u8;
|
|
type Trails = Vec<Coords>;
|
|
|
|
pub fn answer(text : String) -> ( Count, Count ) {
|
|
let grid : Grid = Grid::from(text);
|
|
let ( mut p1, mut p2 ) : ( Count, Count ) = ( 0, 0 );
|
|
|
|
for (y, line) in grid.items.iter().enumerate() {
|
|
for (x, _) in line.iter().enumerate() {
|
|
let ( score, unique ) = grid.trail_maps_from(&(x as Coord), &(y as Coord));
|
|
|
|
p1 = p1 + unique;
|
|
p2 = p2 + score;
|
|
}
|
|
}
|
|
|
|
( p1, p2 )
|
|
}
|
|
|
|
fn char_to_height(s : char) -> Option<Height> {
|
|
s.to_string().parse::<Height>().ok()
|
|
}
|
|
|
|
struct Grid {
|
|
items : Vec<Vec<Option<Height>>>
|
|
}
|
|
impl Grid {
|
|
fn all_neighbours_of_height(&self, height : Height, nodes : Trails) -> Trails {
|
|
nodes.iter()
|
|
.flat_map(|(x, y)| self.neighbours_of_height(height, *x, *y))
|
|
.collect()
|
|
}
|
|
|
|
fn from(s : String) -> Grid {
|
|
Grid {
|
|
items : s.split("\n")
|
|
.map(|line| line.chars().map(char_to_height).collect())
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
fn get(&self, x : &Coord, y : &Coord) -> Option<Height> {
|
|
if *x < 0 || *y < 0 {
|
|
None
|
|
} else {
|
|
self.items
|
|
.get(*y as usize)
|
|
.and_then(|line| line.get(*x as usize))
|
|
.and_then(|a| *a)
|
|
}
|
|
}
|
|
|
|
fn neighbours_of_height(&self, height : Height, x : Coord, y : Coord) -> Vec<(Coord, Coord)> {
|
|
let mut v = Vec::new();
|
|
let offsets : [ (Coord, Coord); 4 ] = [
|
|
( x - 1, y ), ( x + 1, y ), ( x, y - 1 ), ( x, y + 1 )
|
|
];
|
|
|
|
for ( x, y ) in offsets.iter() {
|
|
if let Some(h) = self.get(&x, &y) {
|
|
if h == height {
|
|
v.push(( *x, *y ));
|
|
}
|
|
}
|
|
}
|
|
|
|
v
|
|
}
|
|
|
|
fn trail_maps_from(&self, x : &Coord, y : &Coord) -> ( Count, Count ) {
|
|
match self.get(x, y) {
|
|
Some(h) => {
|
|
if h == 0 {
|
|
let h0 : Trails = Vec::from([ (*x, *y) ]);
|
|
let h1 : Trails = self.all_neighbours_of_height(1, h0);
|
|
let h2 : Trails = self.all_neighbours_of_height(2, h1);
|
|
let h3 : Trails = self.all_neighbours_of_height(3, h2);
|
|
let h4 : Trails = self.all_neighbours_of_height(4, h3);
|
|
let h5 : Trails = self.all_neighbours_of_height(5, h4);
|
|
let h6 : Trails = self.all_neighbours_of_height(6, h5);
|
|
let h7 : Trails = self.all_neighbours_of_height(7, h6);
|
|
let h8 : Trails = self.all_neighbours_of_height(8, h7);
|
|
let h9 : Trails = self.all_neighbours_of_height(9, h8);
|
|
|
|
let score : Count = h9.len()
|
|
.try_into().expect("Answer should be <65K");
|
|
let unique : Count = h9.iter().collect::<HashSet<_>>()
|
|
.len().try_into().expect("Answer should be <65K");
|
|
|
|
( score, unique )
|
|
|
|
} else {
|
|
( 0, 0 )
|
|
}
|
|
},
|
|
None => ( 0, 0 ),
|
|
}
|
|
}
|
|
} |