Add solution to day 9 part 2
parent
95a984fac4
commit
e45adacf15
|
@ -6,9 +6,25 @@ version = 3
|
||||||
name = "day9"
|
name = "day9"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.33"
|
version = "1.0.33"
|
||||||
|
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
itertools = "0.10"
|
||||||
|
|
142
day9/src/main.rs
142
day9/src/main.rs
|
@ -1,4 +1,6 @@
|
||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
@ -10,55 +12,78 @@ enum Error {
|
||||||
RowOutOfRange(usize),
|
RowOutOfRange(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_low_point_indices(
|
/// Get the indices (row, col) of all adjacent items that are in the input
|
||||||
|
fn get_adjacent_indices(
|
||||||
input: &[Vec<u32>],
|
input: &[Vec<u32>],
|
||||||
depth: usize,
|
(depth, col): (usize, usize),
|
||||||
) -> Result<impl Iterator<Item = usize> + '_, Error> {
|
) -> Result<Vec<(usize, usize)>, Error> {
|
||||||
let maybe_row = input.get(depth);
|
let maybe_row = input.get(depth);
|
||||||
if maybe_row.is_none() {
|
if maybe_row.is_none() {
|
||||||
return Err(Error::RowOutOfRange(depth));
|
return Err(Error::RowOutOfRange(depth));
|
||||||
}
|
}
|
||||||
|
|
||||||
let row = maybe_row.unwrap();
|
let row = maybe_row.unwrap();
|
||||||
let iter = row.iter().enumerate().filter_map(move |(i, &item)| {
|
let mut res = vec![];
|
||||||
let adjacent = {
|
|
||||||
let mut res = vec![
|
|
||||||
row.get(i + 1),
|
|
||||||
input.get(depth + 1).map(|below_row| {
|
|
||||||
below_row
|
|
||||||
.get(i)
|
|
||||||
.expect("below row does not match row length")
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
if i > 0 {
|
if col > 0 {
|
||||||
let left = row.get(i - 1);
|
res.push((depth, col - 1));
|
||||||
res.push(left);
|
}
|
||||||
}
|
|
||||||
if depth > 0 {
|
|
||||||
let above = input.get(depth - 1).map(|above_row| {
|
|
||||||
above_row
|
|
||||||
.get(i)
|
|
||||||
.expect("above row does not match row length")
|
|
||||||
});
|
|
||||||
res.push(above);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
if row.get(col + 1).is_some() {
|
||||||
};
|
res.push((depth, col + 1));
|
||||||
|
}
|
||||||
|
|
||||||
if adjacent
|
let below = input.get(depth + 1).map(|below_row| {
|
||||||
.into_iter()
|
below_row
|
||||||
.filter(Option::is_some)
|
.get(col)
|
||||||
.all(|adjacent_item| item < *adjacent_item.unwrap())
|
.expect("below row does not match row length")
|
||||||
{
|
|
||||||
Some(i)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
if below.is_some() {
|
||||||
|
res.push((depth + 1, col));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(iter)
|
let have_above_item = || {
|
||||||
|
input
|
||||||
|
.get(depth - 1)
|
||||||
|
.map(|above_row| {
|
||||||
|
above_row
|
||||||
|
.get(col)
|
||||||
|
.expect("above row does not match row length")
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
};
|
||||||
|
if depth > 0 && have_above_item() {
|
||||||
|
res.push((depth - 1, col));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the indices of all low points in the input
|
||||||
|
fn find_low_point_indices(input: &[Vec<u32>], depth: usize) -> Result<Vec<usize>, Error> {
|
||||||
|
let maybe_row = input.get(depth);
|
||||||
|
if maybe_row.is_none() {
|
||||||
|
return Err(Error::RowOutOfRange(depth));
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = maybe_row.unwrap();
|
||||||
|
let mut res = vec![];
|
||||||
|
for (i, &item) in row.iter().enumerate() {
|
||||||
|
let adjacent_res = get_adjacent_indices(input, (depth, i));
|
||||||
|
if let Err(err) = adjacent_res {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjacent_res
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.all(|(adjacent_depth, adjacent_col)| item < input[adjacent_depth][adjacent_col])
|
||||||
|
{
|
||||||
|
res.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(input: &[Vec<u32>]) -> u32 {
|
fn part1(input: &[Vec<u32>]) -> u32 {
|
||||||
|
@ -68,12 +93,54 @@ fn part1(input: &[Vec<u32>]) -> u32 {
|
||||||
.map(|(depth, row)| {
|
.map(|(depth, row)| {
|
||||||
find_low_point_indices(input, depth)
|
find_low_point_indices(input, depth)
|
||||||
.expect("failed to get row for depth analysis")
|
.expect("failed to get row for depth analysis")
|
||||||
|
.into_iter()
|
||||||
.map(|idx| row[idx] + 1)
|
.map(|idx| row[idx] + 1)
|
||||||
.sum::<u32>()
|
.sum::<u32>()
|
||||||
})
|
})
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part2(input: &[Vec<u32>]) -> u32 {
|
||||||
|
let basin_sizes = input.iter().enumerate().flat_map(|(depth, _)| {
|
||||||
|
let low_points =
|
||||||
|
find_low_point_indices(input, depth).expect("failed to get row for depth analysis");
|
||||||
|
|
||||||
|
low_points.into_iter().map(move |low_point_idx| {
|
||||||
|
let mut to_visit = [(depth, low_point_idx)]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
let mut visited = HashSet::<(usize, usize)>::new();
|
||||||
|
// 1 includes the low point
|
||||||
|
let mut num_in_basin = 1;
|
||||||
|
|
||||||
|
// Flood the board, terminating our search once we hit a nine
|
||||||
|
while !to_visit.is_empty() {
|
||||||
|
let (visiting_row, visiting_col) = to_visit.pop_front().unwrap();
|
||||||
|
let visiting = input[visiting_row][visiting_col];
|
||||||
|
let adjacent_iter = get_adjacent_indices(input, (visiting_row, visiting_col))
|
||||||
|
.expect("failed to get adjacent items for bfs");
|
||||||
|
for (adjacent_row, adjacent_col) in adjacent_iter {
|
||||||
|
if visited.contains(&(adjacent_row, adjacent_col)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let adjacent = input[adjacent_row][adjacent_col];
|
||||||
|
// flows from high to low, nine can't be part of the basin
|
||||||
|
if adjacent > visiting && adjacent != 9 {
|
||||||
|
num_in_basin += 1;
|
||||||
|
to_visit.push_back((adjacent_row, adjacent_col));
|
||||||
|
visited.insert((adjacent_row, adjacent_col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
num_in_basin
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
basin_sizes.sorted().rev().take(3).product()
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input_file_name = env::args().nth(1).expect("No input filename specified");
|
let input_file_name = env::args().nth(1).expect("No input filename specified");
|
||||||
let input_file = File::open(input_file_name).expect("Could not open input file");
|
let input_file = File::open(input_file_name).expect("Could not open input file");
|
||||||
|
@ -91,4 +158,5 @@ fn main() {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
println!("Part 1: {}", part1(&input_lines));
|
println!("Part 1: {}", part1(&input_lines));
|
||||||
|
println!("Part 2: {}", part2(&input_lines));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue