day05 finished
This commit is contained in:
parent
01f6afed9e
commit
8e4face21e
4 changed files with 780 additions and 1 deletions
253
src/days/day05/mod.rs
Normal file
253
src/days/day05/mod.rs
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use std::num::ParseIntError;
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 5;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let (mut stacks, commands) = parse(lines)?;
|
||||
for command in commands {
|
||||
command.with_turn(&mut stacks)?;
|
||||
}
|
||||
Ok(ResultType::String(get_word(&stacks)))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let (mut stacks, commands) = parse(lines)?;
|
||||
for command in commands {
|
||||
command.without_turn(&mut stacks)?;
|
||||
}
|
||||
Ok(ResultType::String(get_word(&stacks)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum CrateError {
|
||||
#[error("Illegal move: {0}")]
|
||||
IllegalMove(String),
|
||||
|
||||
#[error("Cant Parse Integer in Move")]
|
||||
NotAnInteger(#[from] ParseIntError),
|
||||
|
||||
#[error("No crate stacks given")]
|
||||
NoCargoStacksGiven,
|
||||
|
||||
#[error("Illegal move: Too few stacks have {0} needed {1}")]
|
||||
TooFewStacks(usize, usize),
|
||||
|
||||
#[error("Illegal move: Too few crates have {0} needed {1}")]
|
||||
TooFewCrates(String, usize),
|
||||
|
||||
#[error("Stack number must not be 0")]
|
||||
NoZeroStack,
|
||||
}
|
||||
|
||||
fn parse_crate(line: &str) -> Vec<Option<char>> {
|
||||
line.chars()
|
||||
.skip(1)
|
||||
.step_by(4)
|
||||
.map(|c| if c == ' ' { None } else { Some(c) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_all_crates(line_it: &mut dyn Iterator<Item = &String>) -> Result<Vec<String>, CrateError> {
|
||||
let rows: Vec<_> = line_it
|
||||
.take_while(|line| !line.is_empty())
|
||||
.map(|row| parse_crate(row))
|
||||
.collect();
|
||||
let Some(stacks_count) = rows.iter().map(|row| row.len()).max() else {
|
||||
return Err(CrateError::NoCargoStacksGiven);
|
||||
};
|
||||
let mut stacks = Vec::with_capacity(stacks_count);
|
||||
for stack_num in 0..stacks_count {
|
||||
let mut stack = String::from("");
|
||||
for row in &rows {
|
||||
if let Some(Some(one_crate)) = row.get(stack_num) {
|
||||
stack.push(*one_crate);
|
||||
}
|
||||
}
|
||||
stacks.push(stack);
|
||||
}
|
||||
|
||||
Ok(stacks)
|
||||
}
|
||||
|
||||
fn parse(line: &[String]) -> Result<(Vec<String>, Vec<Move>), CrateError> {
|
||||
let mut iter = line.iter();
|
||||
let stacks = parse_all_crates(&mut iter)?;
|
||||
let moves: Vec<_> = iter
|
||||
.map(|command| Move::try_from(command.as_str()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok((stacks, moves))
|
||||
}
|
||||
|
||||
fn get_word(stacks: &[String]) -> String {
|
||||
stacks
|
||||
.iter()
|
||||
.map(|stack| stack.chars().next().unwrap())
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct Move {
|
||||
from_stack: usize,
|
||||
to_stack: usize,
|
||||
amount: usize,
|
||||
}
|
||||
|
||||
impl Move {
|
||||
pub fn new(amount: usize, from_stack: usize, to_stack: usize) -> Move {
|
||||
Move {
|
||||
amount,
|
||||
from_stack,
|
||||
to_stack,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_single(
|
||||
&self,
|
||||
stacks: &mut Vec<String>,
|
||||
func: fn(&str) -> String,
|
||||
) -> Result<(), CrateError> {
|
||||
if self.to_stack >= stacks.len() {
|
||||
return Err(CrateError::TooFewStacks(stacks.len(), self.to_stack));
|
||||
}
|
||||
let Some(from_stack) = stacks.get(self.from_stack ) else {
|
||||
return Err(CrateError::TooFewStacks(stacks.len(), self.from_stack));
|
||||
};
|
||||
if from_stack.len() < self.amount {
|
||||
return Err(CrateError::TooFewCrates(from_stack.to_owned(), self.amount));
|
||||
}
|
||||
let from_stack = from_stack.to_owned();
|
||||
let (start, end) = from_stack.split_at(self.amount);
|
||||
stacks[self.from_stack] = end.to_owned();
|
||||
stacks[self.to_stack] = format!("{}{}", func(start), stacks[self.to_stack]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn with_turn(&self, stacks: &mut Vec<String>) -> Result<(), CrateError> {
|
||||
self.apply_single(stacks, |crates| crates.chars().rev().collect())
|
||||
}
|
||||
|
||||
pub fn without_turn(&self, stacks: &mut Vec<String>) -> Result<(), CrateError> {
|
||||
self.apply_single(stacks, |crates| crates.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Move {
|
||||
type Error = CrateError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let parts: Vec<_> = value.split_whitespace().collect();
|
||||
if parts.len() != 6 {
|
||||
return Err(CrateError::IllegalMove(value.to_owned()));
|
||||
}
|
||||
|
||||
let amount: usize = parts[1].parse()?;
|
||||
let from_stack: usize = parts[3].parse()?;
|
||||
let to_stack: usize = parts[5].parse()?;
|
||||
|
||||
if from_stack == 0 || to_stack == 0 {
|
||||
return Err(CrateError::NoZeroStack);
|
||||
}
|
||||
|
||||
Ok(Move::new(amount, from_stack - 1, to_stack - 1))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
use crate::common::file::read_lines;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_parse_crates() -> Result<()> {
|
||||
let line = " [D] ";
|
||||
let expected = vec![None, Some('D'), None];
|
||||
let result = parse_crate(line);
|
||||
assert_eq!(result, expected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_move() -> Result<()> {
|
||||
let line = "move 3 from 2 to 1";
|
||||
let expected = Move::new(3, 1, 0);
|
||||
let result = Move::try_from(line)?;
|
||||
assert_eq!(result, expected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_all_crates() -> Result<()> {
|
||||
let lines = vec![
|
||||
" [D] ".to_owned(),
|
||||
"[N] [C] ".to_owned(),
|
||||
"[Z] [M] [P]".to_owned(),
|
||||
" 1 2 3 ".to_owned(),
|
||||
"".to_owned(),
|
||||
"move 1 from to 2 1".to_owned(),
|
||||
];
|
||||
let mut input = lines.iter();
|
||||
let expected = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()];
|
||||
let result = parse_all_crates(&mut input)?;
|
||||
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_turn() -> Result<()> {
|
||||
let mut stacks = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()];
|
||||
let command = Move::new(2, 1, 0);
|
||||
let expected = vec!["CDNZ1".to_owned(), "M2".to_owned(), "P3".to_owned()];
|
||||
command.with_turn(&mut stacks)?;
|
||||
assert_eq!(stacks, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::String("CMZ".to_owned());
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_without_turn() -> Result<()> {
|
||||
let mut stacks = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()];
|
||||
let command = Move::new(2, 1, 0);
|
||||
let expected = vec!["DCNZ1".to_owned(), "M2".to_owned(), "P3".to_owned()];
|
||||
command.without_turn(&mut stacks)?;
|
||||
assert_eq!(stacks, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::String("MCD".to_owned());
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ mod day01;
|
|||
mod day02;
|
||||
mod day03;
|
||||
mod day04;
|
||||
mod day05;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -11,7 +12,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 4;
|
||||
const MAX_DAY: usize = 5;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -19,6 +20,7 @@ pub mod day_provider {
|
|||
2 => Ok(Box::new(day02::Day)),
|
||||
3 => Ok(Box::new(day03::Day)),
|
||||
4 => Ok(Box::new(day04::Day)),
|
||||
5 => Ok(Box::new(day05::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue