summaryrefslogtreecommitdiff
path: root/2020/rust/day03/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to '2020/rust/day03/src/main.rs')
-rw-r--r--2020/rust/day03/src/main.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/2020/rust/day03/src/main.rs b/2020/rust/day03/src/main.rs
new file mode 100644
index 0000000..900bd8a
--- /dev/null
+++ b/2020/rust/day03/src/main.rs
@@ -0,0 +1,151 @@
+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<bool>,
+}
+
+#[derive(Error, Debug)]
+pub enum TreeError {
+ #[error("Could not access TreeMap")]
+ Invalid,
+}
+
+impl TreeMap {
+ fn is_tree(&self, pos: CoordPair) -> Result<bool, TreeError> {
+ 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<T: AsRef<Path>>(filename: T) -> Result<TreeMap> {
+ 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<Vec<bool>> = 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<usize> {
+ 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<usize> {
+ 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),
+ }
+}
+