summaryrefslogtreecommitdiff
path: root/2022/rust/day08/src
diff options
context:
space:
mode:
authorcos <cos>2022-12-11 13:34:12 +0000
committercos <cos>2022-12-11 15:06:21 +0000
commit24b0a259fa19bf6cba1693d9bca2a7c3859f3568 (patch)
tree1c1eddf1a147ad1b87dc94d46071feda491e4628 /2022/rust/day08/src
parent4d9e8678663a1308065d8c4769db5a3e49f3c786 (diff)
downloadadventofcode-24b0a259fa19bf6cba1693d9bca2a7c3859f3568.zip
Add day08, 2022
Diffstat (limited to '2022/rust/day08/src')
-rw-r--r--2022/rust/day08/src/main.rs211
1 files changed, 211 insertions, 0 deletions
diff --git a/2022/rust/day08/src/main.rs b/2022/rust/day08/src/main.rs
new file mode 100644
index 0000000..1a17d93
--- /dev/null
+++ b/2022/rust/day08/src/main.rs
@@ -0,0 +1,211 @@
+use {
+ anyhow::{
+ anyhow,
+ Context,
+ Result,
+ },
+ std::{
+ env::args,
+ fs::File,
+ io::{
+ BufRead,
+ BufReader,
+ },
+ path::Path,
+ },
+};
+
+#[derive(Clone,Copy,Debug)]
+enum Direction {
+ Up,
+ Down,
+ Left,
+ Right,
+}
+
+#[derive(Clone,Debug,PartialEq)]
+struct Position {
+ x: usize,
+ y: usize,
+}
+
+#[derive(Debug)]
+struct Trees {
+ size: usize,
+ heights: Vec<u8>,
+}
+
+impl Trees {
+ fn new_with_size(size: usize, heights: Vec<u8>) -> Result<Self> {
+ let len = heights.len();
+ if size * size == len {
+ Ok(Self { size, heights, })
+ } else {
+ Err(anyhow!("Could not create Trees: {size}² ≠ {len}."))
+ }
+ }
+
+ fn size(&self) -> usize {
+ self.size
+ }
+
+ fn height(&self, position: &Position) -> u8 {
+ let x = position.x as usize;
+ let y = position.y as usize;
+ if let Some(height) = self.heights.get(y * self.size + x) {
+ *height
+ } else {
+ panic!("Invalid attempt to access element x: {x}, y: {y} in Trees with size: {}.",
+ self.size);
+ }
+ }
+
+ fn is_visible(&self, position: &Position, direction: Direction) -> bool {
+ let target_height = self.height(position);
+
+ match direction {
+ Direction::Up => {
+ for distance in 0..position.y {
+ let cmp_height = self.height(&Position { x: position.x, y: distance });
+ if cmp_height >= target_height {
+ return false;
+ }
+ }
+ },
+ Direction::Down => {
+ for distance in (position.y + 1)..self.size {
+ let cmp_height = self.height(&Position { x: position.x, y: distance });
+ if cmp_height >= target_height {
+ return false;
+ }
+ }
+ },
+ Direction::Left => {
+ for distance in 0..position.x {
+ let cmp_height = self.height(&Position { x: distance, y: position.y });
+ if cmp_height >= target_height {
+ return false;
+ }
+ }
+ },
+ Direction::Right => {
+ for distance in (position.x + 1)..self.size {
+ let cmp_height = self.height(&Position { x: distance, y: position.y });
+ if cmp_height >= target_height {
+ return false;
+ }
+ }
+ },
+ }
+ true
+ }
+
+ fn distance(&self, start: &Position, direction: &Direction) -> usize {
+ let target_height = self.height(start);
+ let position = start.clone();
+ let mut distance = 0;
+
+ match direction {
+ Direction::Up => {
+ for i in (0..position.y).rev() {
+ let cmp_height = self.height(&Position { x: position.x, y: i });
+ distance += 1;
+ if cmp_height >= target_height {
+ break;
+ }
+ }
+ },
+ Direction::Down => {
+ for i in (position.y + 1)..self.size {
+ let cmp_height = self.height(&Position { x: position.x, y: i });
+ distance += 1;
+ if cmp_height >= target_height {
+ break;
+ }
+ }
+ },
+ Direction::Left => {
+ for i in (0..position.x).rev() {
+ let cmp_height = self.height(&Position { x: i, y: position.y });
+ distance += 1;
+ if cmp_height >= target_height {
+ break;
+ }
+ }
+ },
+ Direction::Right => {
+ for i in (position.x + 1)..self.size {
+ let cmp_height = self.height(&Position { x: i, y: position.y });
+ distance += 1;
+ if cmp_height >= target_height {
+ break;
+ }
+ }
+ },
+ }
+ distance
+ }
+}
+
+fn read_input<T: AsRef<Path>>(filename: T) -> Result<Trees> {
+ let reader = BufReader::new(File::open(filename)?);
+ let mut size = 0;
+
+ let heights = reader.lines().map(
+ |v| {
+ size += 1;
+ let s = v?;
+ s.bytes().map(|height| match height {
+ b'0'..=b'9' => Ok(height - b'0'),
+ _ => Err(anyhow!("Invalid digit: '{height}'")),
+ }).collect::<Result<Vec<_>>>()
+ }
+ ).collect::<Result<Vec<_>>>()?.into_iter().flatten().collect();
+
+ Trees::new_with_size(size, heights)
+}
+
+fn part1(input: &Trees) -> Result<usize> {
+ let mut positions: Vec<Position> = vec![];
+
+ for direction in [Direction::Up, Direction::Down, Direction::Left, Direction::Right].iter() {
+ for y in 0..input.size() {
+ for x in 0..input.size() {
+ let position = Position { x, y };
+ if input.is_visible(&position, *direction) && !positions.contains(&position) {
+ positions.push(position);
+ }
+ }
+ }
+ }
+
+ Ok(positions.len())
+}
+
+fn part2(input: &Trees) -> Result<usize> {
+ (0..input.size()).flat_map(|y|
+ (0..input.size()).map(move |x| {
+ let position = Position { x, y };
+ [Direction::Up, Direction::Down, Direction::Left, Direction::Right].iter()
+ .map(|direction| {
+ input.distance(&position, direction)
+ }).product()
+ }
+ )).max().ok_or_else(|| anyhow!("Really, a tree patch with a 1-by-1 grid?"))
+}
+
+fn main() -> Result<()> {
+ let ( do_part_1, do_part_2 ) = aoc::do_parts();
+
+ let filename = args().nth(1).ok_or_else(|| anyhow!("Missing input filename"))?;
+ let input = read_input(filename).context("Could not read input")?;
+ if do_part_1 {
+ let solution = part1(&input).context("No solution for part 1")?;
+ println!("Part1, solution found to be: {}", solution);
+ }
+ if do_part_2 {
+ let solution = part2(&input).context("No solution for part 2")?;
+ println!("Part2, solution found to be: {}", solution);
+ }
+ Ok(())
+}