use anyhow::{Result, anyhow}; use std::env::args; use std::fs::File; use std::io::{BufRead, BufReader}; use std::ops::AddAssign; use std::path::Path; use thiserror::Error; #[derive(Clone,Copy)] struct CoordPair { x: usize, y: usize, } impl AddAssign for CoordPair { fn add_assign(&mut self, other: Self) { *self = Self { x: self.x + other.x, y: self.y + other.y, }; } } #[derive(Debug)] struct TreeMap { width: usize, height: usize, map: Vec, } #[derive(Error, Debug)] pub enum TreeError { #[error("Could not access TreeMap")] Invalid, } impl TreeMap { fn is_tree(&self, pos: CoordPair) -> Result { let mut p = pos; p.x %= self.width; self.map.get(p.y * self.width + p.x).copied().ok_or(TreeError::Invalid) } } fn read_input>(filename: T) -> Result { let f = File::open(filename)?; let reader = BufReader::new(f); let tree_map = reader.lines() .fold(Ok(TreeMap { width: 0, height: 0, map: vec![] }), |acc, l| { let line = l?; let row: Result> = line.chars().map(|c| match c { '#' => Ok(true), '.' => Ok(false), _ => Err(anyhow!("Invalid map character: '{}'", c)), }).collect(); match acc { Ok(mut a) => { a.map.append(&mut row?); a.height += 1; Ok(a) }, Err(err) => Err(err) } }); match tree_map { Ok(mut tm) => { tm.width = tm.map.len()/tm.height; Ok(tm) }, err => err, } } fn part1(input: &TreeMap) -> Result { let mut pos = CoordPair { x: 0, y: 0, }; let slope = CoordPair { x: 3, y: 1, }; let mut collision_count = 0; while pos.y < input.height { if input.is_tree(pos)? { collision_count += 1; } pos += slope; } Ok(collision_count) } fn part2(input: &TreeMap) -> Result { let slopes = vec![ CoordPair { x: 1, y: 1, }, CoordPair { x: 3, y: 1, }, CoordPair { x: 5, y: 1, }, CoordPair { x: 7, y: 1, }, CoordPair { x: 1, y: 2, }, ]; let res = slopes.iter().map(|slope| { let mut pos = CoordPair { x: 0, y: 0, }; let mut collision_count = 0; while pos.y < input.height { if input.is_tree(pos)? { collision_count += 1; } pos += *slope; } Ok(collision_count) }).product(); res } fn main() { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = match args().nth(1) { Some(f) => f, None => { eprintln!("Missing input filename"); std::process::exit(1); }, }; match read_input(filename) { Ok(input) => { if do_part_1 { match part1(&input) { Ok(solution) => println!("Part1: would encounter {} trees", solution), Err(err) => { eprintln!("Part1, no solution found: {}", err); std::process::exit(1); } }; } if do_part_2 { match part2(&input) { Ok(solution) => println!("Part2: {} is the product of all tree collisions", solution), Err(err) => { eprintln!("Part2, no solution found: {}", err); std::process::exit(1); } }; } }, Err(err) => eprintln!("Could not read input: {}", err), } }