A bit of an overly-fussy implementation for Day 3.

This commit is contained in:
2020-12-03 08:31:35 -08:00
parent 12819f0eb8
commit deeaf11835
6 changed files with 508 additions and 0 deletions

100
src/bin/tobaggan.rs Normal file
View File

@@ -0,0 +1,100 @@
use advent2020::errors::{MapParseError, TopLevelError};
use advent2020::map::Map;
use std::convert::TryFrom;
use std::env;
use std::fs;
#[derive(Clone, Debug)]
enum Square {
Empty,
Tree,
}
impl TryFrom<char> for Square {
type Error = MapParseError;
fn try_from(c: char) -> Result<Self, Self::Error> {
match c {
'.' => Ok(Square::Empty),
'#' => Ok(Square::Tree),
_ => Err(MapParseError::UnexpectedCharacter(c)),
}
}
}
struct Encounters {
trees_encountered: usize,
clear_spots_encountered: usize,
}
fn trail_for_slope(map: &Map<Square>, run: usize, fall: usize) -> Encounters {
let mut current_x = 0;
let mut current_y = 0;
let mut encounters = Encounters {
trees_encountered: 0,
clear_spots_encountered: 0,
};
loop {
match map.at(current_x, current_y) {
None => break,
Some(Square::Empty) => encounters.clear_spots_encountered += 1,
Some(Square::Tree) => encounters.trees_encountered += 1,
}
current_x += run;
current_y += fall;
}
encounters
}
fn real_main() -> Result<(), TopLevelError> {
let mut maybe_map = None;
for argument in env::args().skip(1) {
let fname = argument.clone();
let contents = fs::read_to_string(argument)?;
match Map::<Square>::try_from(contents.as_str()) {
Err(e) => eprintln!("Skipping file {}: Parse error: {}", fname, e),
Ok(v) => {
maybe_map = Some(v);
break;
}
}
}
let map = match maybe_map {
None => return Err(TopLevelError::NoInputFound),
Some(x) => x,
};
let initial = trail_for_slope(&map, 3, 1);
println!(
"For the initial slope, encountered {} trees and {} open spaces",
initial.trees_encountered, initial.clear_spots_encountered
);
let mut product = 1;
let slopes = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)];
for (run, fall) in slopes.iter() {
let encounters = trail_for_slope(&map, *run, *fall);
println!(
"For slope ({},{}), encountered {} trees and {} open spaces",
run, fall, encounters.trees_encountered, encounters.clear_spots_encountered
);
product *= encounters.trees_encountered;
}
println!("The product of the trees encountered is {}", product);
Ok(())
}
fn main() {
match real_main() {
Err(e) => eprintln!("ERROR: {}", e),
Ok(_) => {}
}
}

View File

@@ -15,6 +15,7 @@ macro_rules! convert_error {
pub enum TopLevelError {
IOError(io::Error),
NoInputFound,
NoSolutionFound,
UnknownError,
}
@@ -23,6 +24,7 @@ impl fmt::Display for TopLevelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TopLevelError::IOError(e) => write!(f, "IO error: {}", e),
TopLevelError::NoInputFound => write!(f, "No valid inputs found"),
TopLevelError::NoSolutionFound => write!(f, "No solution found."),
TopLevelError::UnknownError => {
write!(f, "Unknown error occurred; this shouldn't be possible.")
@@ -51,3 +53,19 @@ impl fmt::Display for PasswordParseError {
convert_error!(ParseIntError, PasswordParseError, StringToIntError);
convert_error!(nom::Err<()>, PasswordParseError, NomError);
pub enum MapParseError {
UnexpectedCharacter(char),
UnevenLines(usize),
}
impl fmt::Display for MapParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MapParseError::UnevenLines(l) => write!(f, "Map has uneven width at line {}", l),
MapParseError::UnexpectedCharacter(c) => {
write!(f, "Unexpected character parsing map: {}", c)
}
}
}
}

View File

@@ -1 +1,2 @@
pub mod errors;
pub mod map;

55
src/map.rs Normal file
View File

@@ -0,0 +1,55 @@
use crate::errors::MapParseError;
use std::convert::TryFrom;
pub struct Map<A: Clone> {
width: usize,
_height: usize,
data: Vec<Vec<A>>,
}
impl<'a, E, X> TryFrom<&'a str> for Map<X>
where
X: Clone + TryFrom<char, Error = E>,
E: From<MapParseError>,
{
type Error = E;
fn try_from(s: &str) -> Result<Map<X>, E> {
let mut width = 0;
let mut _height = 0;
let mut data = Vec::new();
for line in s.lines() {
let mut current_line = Vec::with_capacity(width);
for char in line.chars() {
let item = X::try_from(char)?;
current_line.push(item);
}
_height += 1;
if width == 0 {
width = current_line.len();
} else {
if width != current_line.len() {
return Err(E::from(MapParseError::UnevenLines(_height)));
}
}
data.push(current_line);
}
Ok(Map {
width,
_height,
data,
})
}
}
impl<X: Clone> Map<X> {
pub fn at(&self, x: usize, y: usize) -> Option<X> {
let row = self.data.get(y)?;
let wrapped_x = x % self.width;
Some(row[wrapped_x].clone())
}
}