This repository has been archived on 2023-12-13. You can view files and clone it, but cannot push or open issues or pull requests.
advent-of-code-2022/day8/src/main.rs

421 lines
11 KiB
Rust
Raw Normal View History

2022-12-08 16:01:22 -05:00
use std::collections::HashSet;
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Hash)]
enum VisibleDirection {
Top,
Bottom,
Left,
Right,
}
use VisibleDirection::*;
#[derive(Debug, Default)]
struct Tree {
height: usize,
visible: HashSet<VisibleDirection>,
}
impl Tree {
fn new(height: usize) -> Self {
Tree {
height,
..Tree::default()
}
}
fn is_visible(&self) -> bool {
!self.visible.is_empty()
}
fn set_visible(&mut self, dir: VisibleDirection) -> &mut Self {
self.visible.insert(dir);
self
}
fn set_all_visible(&mut self) -> &mut Self {
self.set_visible(Top)
.set_visible(Bottom)
.set_visible(Left)
.set_visible(Right)
}
}
// ----------------------------------------------------------------------------
#[derive(Debug)]
struct Grid<T> {
width: usize,
vec: Vec<T>,
}
impl<T> Grid<T> {
2022-12-09 11:17:49 -05:00
pub fn new(width: usize) -> Self {
2022-12-08 16:01:22 -05:00
Grid {
width,
vec: Vec::new(),
}
}
// Convert x,y coordinate into linear array index
2022-12-09 11:17:49 -05:00
pub fn xy_idx(&self, x: usize, y: usize) -> usize {
2022-12-08 16:01:22 -05:00
(y * self.width) + x
}
/// Convert linear array index to x,y coordinate
2022-12-09 11:17:49 -05:00
pub fn idx_xy(&self, idx: usize) -> (usize, usize) {
2022-12-08 16:01:22 -05:00
(idx % self.width, idx / self.width)
}
2022-12-09 15:57:20 -05:00
pub fn get(&self, idx: usize) -> Option<&T> {
self.vec.get(idx)
}
pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
self.vec.get_mut(idx)
}
2022-12-09 11:17:49 -05:00
pub fn row_first_idx(&self, row: usize) -> usize {
2022-12-08 16:01:22 -05:00
let idx = row * self.width;
if idx < self.vec.len() {
idx
} else {
self.vec.len()
}
}
2022-12-09 11:17:49 -05:00
pub fn row_last_idx(&self, row: usize) -> usize {
2022-12-08 16:01:22 -05:00
if (row + 1) > self.num_rows() {
return self.vec.len();
}
self.row_first_idx(row + 1) - 1
}
2022-12-09 11:17:49 -05:00
pub fn num_rows(&self) -> usize {
self.vec.len() / self.width
2022-12-08 16:01:22 -05:00
}
2022-12-09 11:17:49 -05:00
pub fn num_cols(&self) -> usize {
2022-12-08 16:01:22 -05:00
self.width
}
2022-12-09 11:17:49 -05:00
pub fn get_row(&mut self, row_num: usize) -> &mut [T] {
2022-12-08 16:01:22 -05:00
let start = self.row_first_idx(row_num);
let end = self.row_last_idx(row_num);
&mut self.vec[start..=end]
}
2022-12-09 11:17:49 -05:00
pub fn get_row_indexes(&self, row_num: usize) -> Vec<usize> {
let start = self.row_first_idx(row_num);
let end = self.row_last_idx(row_num);
(start..=end).collect()
}
pub fn get_column_indexes(&self, col_num: usize) -> Vec<usize> {
2022-12-08 16:01:22 -05:00
let mut indexes = Vec::new();
2022-12-09 11:17:49 -05:00
if col_num >= self.num_cols() {
panic!(
"Asked for column {}, there are {} columns",
col_num,
self.num_cols()
);
2022-12-08 16:01:22 -05:00
}
for r in 0..self.num_rows() {
let idx = self.width * r + col_num;
indexes.push(idx);
}
indexes
}
}
impl Grid<Tree> {
2022-12-09 15:57:20 -05:00
pub fn from_file_str(file_str: &'static str) -> Grid<Tree> {
let lines: Vec<&str> = file_str.lines().collect();
let width = lines[0].len();
let mut grid: Grid<Tree> = Grid::new(width);
for line in lines {
let mut row: Vec<Tree> = line
.chars()
.map(|ch| Tree::new(ch.to_digit(10).unwrap() as usize))
.collect();
grid.vec.append(&mut row);
}
grid
}
2022-12-09 11:17:49 -05:00
fn mark_outer_trees_visible(&mut self) -> &mut Self {
2022-12-08 16:01:22 -05:00
fn set_row_visible(row: &mut [Tree]) {
2022-12-09 11:17:49 -05:00
row.iter_mut().for_each(|tree| {
tree.set_all_visible();
})
2022-12-08 16:01:22 -05:00
}
// Set top/bottom rows as visible
set_row_visible(self.get_row(0));
set_row_visible(self.get_row(self.num_rows() - 1));
// Set left/right cols as visible
self.get_column_indexes(0).into_iter().for_each(|id| {
self.vec[id].set_all_visible();
});
2022-12-09 11:17:49 -05:00
self.get_column_indexes(self.num_cols() - 1)
.into_iter()
.for_each(|id| {
self.vec[id].set_all_visible();
});
self
}
fn mark_visible(&mut self, dir: VisibleDirection) -> &mut Self {
let indexes: Vec<Vec<usize>> = match dir {
Top | Bottom => {
// Skip outer columns, as those are already marked visible
(1..(self.num_cols() - 1))
.map(|c| self.get_column_indexes(c))
.map(|column| {
if dir == Bottom {
column.into_iter().rev().collect()
} else {
column
}
})
.collect()
}
Left | Right => {
// Skip first and last rows, as those are already marked visible
(1..(self.num_rows() - 1))
.map(|r| self.get_row_indexes(r))
.map(|row| {
if dir == Right {
row.into_iter().rev().collect()
} else {
row
}
})
.collect()
}
};
for row_or_col in indexes {
let mut tallest = 0usize;
for idx in row_or_col {
2022-12-09 15:57:20 -05:00
let tree = self.get_mut(idx).unwrap();
2022-12-09 11:17:49 -05:00
if tallest < tree.height {
tree.set_visible(dir);
tallest = tree.height;
}
}
}
self
2022-12-08 16:01:22 -05:00
}
2022-12-09 11:17:49 -05:00
pub fn mark_visible_trees(&mut self) {
self.mark_outer_trees_visible()
.mark_visible(Top)
.mark_visible(Right)
.mark_visible(Bottom)
.mark_visible(Left);
2022-12-08 16:01:22 -05:00
}
pub fn get_visible_trees(&self) -> usize {
2022-12-09 11:17:49 -05:00
self.vec
2022-12-08 16:01:22 -05:00
.iter()
.filter(|tree| tree.is_visible())
.collect::<Vec<&Tree>>()
.len()
}
2022-12-09 17:22:48 -05:00
fn get_surrounding_trees(
&self,
reference: usize,
) -> (Vec<usize>, Vec<usize>, Vec<usize>, Vec<usize>) {
2022-12-09 15:57:20 -05:00
let (x, y) = self.idx_xy(reference);
2022-12-08 16:01:22 -05:00
2022-12-09 15:57:20 -05:00
let mut top = self.get_column_indexes(x); // col[0..=x]
let bottom = top.split_off(y + 1); // col[(x+1)..]
let mut left = self.get_row_indexes(y); // row[0..=y]
let right = left.split_off(x + 1); // row[(y+1)..]
2022-12-08 16:01:22 -05:00
2022-12-09 15:57:20 -05:00
// Remove the index for the current tree
let _ = top.pop();
let _ = left.pop();
2022-12-08 16:01:22 -05:00
2022-12-09 15:57:20 -05:00
// Reverse the top and left so the perspective is from the reference
top.reverse();
left.reverse();
(top, right, bottom, left)
}
2022-12-09 17:22:48 -05:00
fn get_viewing_distances(&self, reference: usize) -> [usize; 4] {
2022-12-09 15:57:20 -05:00
let (t, r, b, l) = self.get_surrounding_trees(reference);
2022-12-09 17:22:48 -05:00
let ref_tree_height = self.get(reference).unwrap().height;
[t, r, b, l]
.iter()
.map(|search| {
let mut count = 0usize;
for i in search {
let height = match self.get(*i) {
Some(h) => h.height,
None => 0,
};
count += 1;
if ref_tree_height <= height {
break;
}
}
count
})
.collect::<Vec<usize>>()
.try_into()
.unwrap()
}
fn get_scenic_score(&self, reference: usize) -> usize {
let [t, r, b, l] = self.get_viewing_distances(reference);
t * r * b * l
}
pub fn get_max_scenic_score(&self) -> usize {
self.vec
.iter()
.enumerate()
.map(|(idx, _)| idx)
.map(|idx| self.get_scenic_score(idx))
.max()
.unwrap()
2022-12-08 16:01:22 -05:00
}
}
// ----------------------------------------------------------------------------
2022-12-08 08:38:47 -05:00
fn main() {
let file_str = include_str!("input.txt");
2022-12-08 16:01:22 -05:00
let mut grid = Grid::from_file_str(file_str);
grid.mark_visible_trees();
let visible_num = grid.get_visible_trees();
2022-12-09 17:22:48 -05:00
let scenic_score = grid.get_max_scenic_score();
2022-12-08 16:01:22 -05:00
println!("Part 1: Number of visible trees: {}", visible_num);
2022-12-09 17:22:48 -05:00
println!("Part 2: Max scenic score: {}", scenic_score);
2022-12-08 08:38:47 -05:00
}
#[cfg(test)]
mod tests {
use super::*;
2022-12-08 16:01:22 -05:00
fn get_data() -> &'static str {
include_str!("test-input.txt")
}
2022-12-09 11:17:49 -05:00
#[test]
fn test_row_first_index() {
let grid = Grid::from_file_str(get_data());
assert_eq!(grid.row_first_idx(1), 5);
assert_eq!(grid.row_first_idx(0), 0);
assert_eq!(grid.row_first_idx(2), 10);
}
#[test]
fn test_row_last_index() {
let grid = Grid::from_file_str(get_data());
assert_eq!(grid.row_last_idx(0), 4);
assert_eq!(grid.row_last_idx(1), 9);
}
#[test]
fn test_get_column_indexes() {
2022-12-09 15:57:20 -05:00
let grid = Grid::from_file_str(get_data());
2022-12-09 11:17:49 -05:00
assert_eq!(grid.width, 5);
assert_eq!(grid.get_column_indexes(0), vec![0, 5, 10, 15, 20]);
assert_eq!(grid.get_column_indexes(1), vec![1, 6, 11, 16, 21]);
assert_eq!(grid.get_column_indexes(4), vec![4, 9, 14, 19, 24]);
}
2022-12-08 16:01:22 -05:00
#[test]
fn test_outer_visible_trees() {
let mut grid = Grid::from_file_str(get_data());
grid.mark_outer_trees_visible();
assert_eq!(grid.get_visible_trees(), 16usize);
}
2022-12-09 11:17:49 -05:00
#[test]
fn test_visible_trees() {
let mut grid = Grid::from_file_str(get_data());
grid.mark_visible_trees();
let visible = [(1usize, 1usize), (2, 1), (1, 2), (4, 3), (2, 3)];
for (x, y) in visible {
let idx = grid.xy_idx(x, y);
assert!(
grid.vec[idx].is_visible(),
"Tree {}({},{}) should be visible: {:#?}",
idx,
x,
y,
grid.vec[idx]
);
}
assert_eq!(grid.get_visible_trees(), 21usize);
}
2022-12-09 15:57:20 -05:00
#[test]
fn test_get_surrounding_trees() {
let grid = Grid::from_file_str(get_data());
let (t, r, b, l) = grid.get_surrounding_trees(7);
assert_eq!(t, vec![2]);
assert_eq!(r, vec![8, 9]);
assert_eq!(b, vec![12, 17, 22]);
assert_eq!(l, vec![6, 5]);
}
2022-12-09 17:22:48 -05:00
#[test]
fn test_get_viewing_distances() {
let grid = Grid::from_file_str(get_data());
assert_eq!(grid.get_viewing_distances(7), [1, 2, 2, 1]);
assert_eq!(grid.get_viewing_distances(17), [2, 2, 1, 2]);
}
#[test]
fn test_get_scenic_score() {
let grid = Grid::from_file_str(get_data());
assert_eq!(grid.get_scenic_score(7), 4);
assert_eq!(grid.get_scenic_score(17), 8);
assert_eq!(grid.get_max_scenic_score(), 8);
}
2022-12-08 16:01:22 -05:00
}