Cleanup for 2022 days 11 through 21: Turned into a lib and introduced parse errors

This commit is contained in:
Burnus 2023-05-15 18:07:16 +02:00
parent c7852c9791
commit fcb2fed515
26 changed files with 1095 additions and 879 deletions

View file

@ -1,4 +1,17 @@
use std::{fs, usize};
use core::fmt::Display;
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
InvalidDirection(char),
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidDirection(c) => write!(f, "Trying to parse invalid character {c} into Direction"),
}
}
}
#[derive(PartialEq)]
enum Shape {
@ -9,15 +22,15 @@ enum Shape {
Square,
}
impl Shape {
fn from(number: usize) -> Self {
match number%5 {
impl <T: Into<usize>> From <T> for Shape {
fn from(number: T) -> Self {
match number.into() % 5 {
0 => Shape::Minus,
1 => Shape::Plus,
2 => Shape::L,
3 => Shape::Pipe,
4 => Shape::Square,
_ => panic!("WTF? This can never happen."),
_ => unreachable!("number%5 can only ever be one of the values above"),
}
}
}
@ -25,6 +38,18 @@ impl Shape {
#[derive(PartialEq, Debug)]
enum Direction { Left, Right, Down }
impl TryFrom<char> for Direction {
type Error = ParseError;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'<' => Ok(Direction::Left),
'>' => Ok(Direction::Right),
c => Err(Self::Error::InvalidDirection(c)),
}
}
}
#[derive(PartialEq)]
enum State { Falling, Resting }
@ -292,7 +317,7 @@ impl Block {
self.bl_position.x = match direction {
Direction::Left => self.bl_position.x - 1,
Direction::Right => self.bl_position.x + 1,
_ => panic!("unexpected direction"),
Direction::Down => panic!("Didn't expect to be pushed down"),
};
for position in &old_positions {
arena.free(position);
@ -317,14 +342,10 @@ impl PlayArea {
fn occupy(&mut self, coordinates: &Position) {
self.blocked_tiles[coordinates.y][coordinates.x as usize] = true;
//self.max_y = self.max_y.max(coordinates.y as isize);
}
fn free(&mut self, coordinates: &Position) {
self.blocked_tiles[coordinates.y][coordinates.x as usize] = false;
// if coordinates.y as isize == self.max_y && !self.blocked_tiles[coordinates.y].iter().any(|&b| b) {
// self.max_y -= 1; // self.max_y.saturating_sub(1);
// }
}
fn new() -> Self {
@ -336,20 +357,6 @@ impl PlayArea {
}
}
fn parse_directions(winds: &str) -> Vec<Direction> {
winds.chars()
.filter(|c| ['<', '>'].contains(c))
.map(|c| match c {
'<' => Direction::Left,
'>' => Direction::Right,
_ => panic!("unexpected wind direction: {}", c),
}).collect()
}
fn read_file(path: &str) -> String {
fs::read_to_string(path)
.expect("File not Found")
}
fn solve_with_pattern(target: usize, directions: &[Direction]) -> usize {
let mut results: Vec<(usize, usize, usize)> = Vec::new();
@ -369,9 +376,9 @@ fn solve_with_pattern(target: usize, directions: &[Direction]) -> usize {
let state = (i, direction_index, arena.max_y as usize);
let old_results: Vec<(usize, usize, usize)> = results.iter().filter(|(old_i, old_direction, _)| old_i % 5 == i % 5 && *old_direction == direction_index).cloned().collect();
if old_results.len() > 1 {
let period = (i - old_results[1].0) as usize;
let period = i - old_results[1].0;
let period_growth = arena.max_y as usize - old_results[1].2;
let offset = results[target % period].2 as usize;
let offset = results[target % period].2;
return (target/period) * period_growth + offset;
} else {
results.push(state);
@ -383,30 +390,31 @@ fn solve_with_pattern(target: usize, directions: &[Direction]) -> usize {
0
}
fn main() {
let winds = read_file("input");
let directions = parse_directions(&winds);
[2022, 1_000_000_000_000].iter().for_each(|target| {
println!("After {} rocks have fallen, the tower is {} units high.", target, solve_with_pattern(*target, &directions));
});
pub fn run(input: &str) -> Result<(usize, usize), ParseError> {
let directions = input.chars().map(Direction::try_from).collect::<Result<Vec<_>, _>>()?;
let first = solve_with_pattern(2022, &directions);
let second = solve_with_pattern(1_000_000_000_000, &directions);
Ok((first, second))
}
#[test]
fn sample_input() {
let winds = read_file("tests/sample_input");
let directions = parse_directions(&winds);
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
assert_eq!(solve_with_pattern(2022, &directions), 3069);
assert_eq!(solve_with_pattern(1_000_000_000_000, &directions), 1514285714288);
}
#[test]
fn challenge_input() {
let winds = read_file("tests/input");
let directions = parse_directions(&winds);
assert_eq!(solve_with_pattern(2022, &directions), 3219);
assert_eq!(solve_with_pattern(1_000_000_000_000, &directions), 1582758620701);
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), Ok((3069, 1514285714288)));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), Ok((3219, 1582758620701)));
}
}