day03 finished

This commit is contained in:
Ruediger Ludwig 2023-01-28 06:00:12 +01:00
parent eb1ce68486
commit d7c85a75f6
8 changed files with 534 additions and 37 deletions

View file

@ -1,7 +1,6 @@
use itertools::Itertools;
use std::num::ParseIntError;
use anyhow::Result;
use thiserror::Error;
use super::template::{DayTrait, ResultType};
@ -14,13 +13,13 @@ impl DayTrait for Day {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> Result<ResultType> {
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let vector = Day::parse(lines)?;
let max = vector.iter().max().ok_or(CalorieError::Empty)?;
Ok(ResultType::IntResult(*max))
}
fn part2(&self, lines: &str) -> Result<ResultType> {
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let vector = Day::parse(lines)?;
let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum();
Ok(ResultType::IntResult(sum))

View file

@ -1,7 +1,6 @@
use std::cmp::Ordering;
use super::template::{DayTrait, ResultType};
use anyhow::Result;
use thiserror::Error;
const DAY_NUMBER: usize = 2;
@ -13,11 +12,11 @@ impl DayTrait for Day {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> Result<ResultType> {
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = lines
.split("\n")
.map(RPS::parse_line)
.collect::<Result<Vec<_>>>()?
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(first, second)| second.asses_pair(&first))
.sum();
@ -25,11 +24,11 @@ impl DayTrait for Day {
Ok(ResultType::IntResult(sum))
}
fn part2(&self, lines: &str) -> Result<ResultType> {
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = lines
.split("\n")
.map(Strategy::parse_line)
.collect::<Result<Vec<_>>>()?
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(first, second)| second.fullfill(&first).asses_pair(&first))
.sum();
@ -55,7 +54,7 @@ enum RPS {
}
impl RPS {
pub fn parse_line(line: &str) -> Result<(Self, Self)> {
pub fn parse_line(line: &str) -> Result<(Self, Self), RPSError> {
let mut parts = line.split(" ");
let (Some(first), Some(second)) = (parts.next(), parts.next()) else {
Err(RPSError::IllegalLine(line.to_owned()))?
@ -119,7 +118,7 @@ enum Strategy {
}
impl Strategy {
pub fn parse_line(line: &str) -> Result<(RPS, Self)> {
pub fn parse_line(line: &str) -> Result<(RPS, Self), RPSError> {
let mut parts = line.split(" ");
let (Some(first), Some(second)) = (parts.next(), parts.next()) else {
Err(RPSError::IllegalLine(line.to_owned()))?

144
src/days/day03/mod.rs Normal file
View file

@ -0,0 +1,144 @@
use itertools::Itertools;
use thiserror::Error;
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 3;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = lines
.split('\n')
.map(|rucksack| find_double(rucksack).and_then(priority))
.collect::<Result<Vec<_>, _>>()?
.iter()
.sum();
Ok(ResultType::IntResult(sum))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = lines
.split('\n')
.chunks(3)
.into_iter()
.map(|chunk| find_badge(&chunk.collect::<Vec<_>>()).and_then(priority))
.collect::<Result<Vec<_>, _>>()?
.iter()
.sum();
Ok(ResultType::IntResult(sum))
}
}
#[derive(Debug, Error)]
enum RucksackError {
#[error("No double item found")]
NoDoubleFound,
#[error("Not a valid char: {0}")]
InvalidChar(char),
#[error("Need at least two elves for common item")]
NeedAtLeastTwo,
#[error("No common badge was found")]
NoBadgeFound,
}
fn priority(c: char) -> Result<i64, RucksackError> {
match c {
'a'..='z' => Ok(c as i64 - 'a' as i64 + 1),
'A'..='Z' => Ok(c as i64 - 'A' as i64 + 27),
_ => Err(RucksackError::InvalidChar(c)),
}
}
fn find_double(content: &str) -> Result<char, RucksackError> {
let length = content.len() / 2;
let part1 = &content[..length];
let part2 = &content[length..];
for c in part1.chars() {
if part2.contains(c) {
return Ok(c);
}
}
Err(RucksackError::NoDoubleFound)?
}
fn find_badge<'a>(elves: &[&str]) -> Result<char, RucksackError> {
if elves.len() < 2 {
return Err(RucksackError::NeedAtLeastTwo);
};
for c in elves[0].chars() {
let mut found = true;
for other in &elves[1..] {
if !other.contains(c) {
found = false;
}
}
if found {
return Ok(c);
}
}
Err(RucksackError::NoBadgeFound)
}
#[cfg(test)]
mod test {
use super::*;
use crate::common::file::read_data;
use anyhow::Result;
#[test]
fn test_rucksack() -> Result<(), RucksackError> {
let input = "vJrwpWtwJgWrhcsFMMfFFhFp";
let expected = 16;
let result = find_double(input).and_then(priority)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part1() -> Result<()> {
let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(157);
let result = day.part1(&lines)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_find_badge() -> Result<(), RucksackError> {
let input = vec![
"vJrwpWtwJgWrhcsFMMfFFhFp",
"jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
"PmmdzqPrVvPwwTWBwg",
];
let expected = 18;
let result = find_badge(&input).and_then(priority)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part2() -> Result<()> {
let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(70);
let result = day.part2(&lines)?;
assert_eq!(result, expected);
Ok(())
}
}

View file

@ -1,5 +1,3 @@
use anyhow::Result;
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 0;
@ -11,11 +9,11 @@ impl DayTrait for Day {
DAY_NUMBER
}
fn part1(&self, _lines: &str) -> Result<ResultType> {
fn part1(&self, _lines: &str) -> anyhow::Result<ResultType> {
Ok(ResultType::NoResult)
}
fn part2(&self, _lines: &str) -> Result<ResultType> {
fn part2(&self, _lines: &str) -> anyhow::Result<ResultType> {
Ok(ResultType::NoResult)
}
}

View file

@ -1,5 +1,6 @@
mod day01;
mod day02;
mod day03;
mod template;
pub use template::DayTrait;
@ -9,12 +10,13 @@ pub mod day_provider {
use super::*;
use thiserror::Error;
const MAX_DAY: usize = 2;
const MAX_DAY: usize = 3;
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
match day_num {
1 => Ok(Box::new(day01::Day)),
2 => Ok(Box::new(day02::Day)),
3 => Ok(Box::new(day03::Day)),
_ => Err(ProviderError::InvalidNumber(day_num)),
}
}

View file

@ -5,39 +5,84 @@ mod macros;
use anyhow::Result;
use common::file::read_data;
use days::{day_provider, DayTrait, ResultType};
use std::env;
use std::{
env,
time::{Duration, Instant},
};
use thiserror::Error;
fn output(day: usize, part: usize, result: ResultType) -> () {
fn output(day: usize, part: usize, result: ResultType, time: Duration) -> () {
match result {
ResultType::IntResult(value) => {
println!("Day {:02} part {}: {}", day, part, value);
println!(
"Day {:02} part {}: {} ({})",
day,
part,
value,
time.as_secs_f64()
);
}
ResultType::StringResult(value) => {
println!("Day {:02} part {}: {}", day, part, value);
println!(
"Day {:02} part {}: {} ({})",
day,
part,
value,
time.as_secs_f32()
);
}
ResultType::LinesResult(value) => {
println!("Day {:02} part {}: {}", day, part, value[0]);
println!(
"Day {:02} part {}: {} ({})",
day,
part,
value[0],
time.as_secs_f32()
);
for line in &value[1..] {
println!(" part : {}", line);
}
}
ResultType::NoResult => {
println!("Day {:02} part {}: (None)", day, part);
}
ResultType::NoResult => {}
}
}
fn run(day: Box<dyn DayTrait>, part1: bool, part2: bool) -> Result<()> {
let lines = read_data(day.get_day_number(), "input.txt")?;
if part1 {
output(day.get_day_number(), 1, day.part1(&lines)?);
}
if part2 {
output(day.get_day_number(), 2, day.part2(&lines)?);
}
fn run_part(day: &Box<dyn DayTrait>, is_part1: bool, lines: &str) -> Result<Duration> {
let now = Instant::now();
let result = if is_part1 {
day.part1(lines)?
} else {
day.part2(lines)?
};
Ok(())
if matches!(result, ResultType::NoResult) {
Ok(Duration::ZERO)
} else {
let elapsed = now.elapsed();
output(
day.get_day_number(),
if is_part1 { 1 } else { 2 },
result,
elapsed,
);
Ok(elapsed)
}
}
fn run(day: &Box<dyn DayTrait>, part1: bool, part2: bool) -> Result<Duration> {
let lines = read_data(day.get_day_number(), "input.txt")?;
let elapsed1 = if part1 {
run_part(day, true, &lines)?
} else {
Duration::ZERO
};
let elapsed2 = if part2 {
run_part(day, false, &lines)?
} else {
Duration::ZERO
};
Ok(elapsed1 + elapsed2)
}
#[derive(Debug, Error)]
@ -52,9 +97,12 @@ enum ParamError {
fn run_on_parameters(params: &[String]) -> Result<()> {
match params.len() {
0 => {
let mut runtime = Duration::ZERO;
for day in day_provider::get_all_days() {
run(day, true, true)?;
runtime += run(&day, true, true)?;
}
println!();
println!("Runtime: {}", runtime.as_secs_f32());
}
1 => {
let mut parts = params[0].split("/");
@ -64,12 +112,13 @@ fn run_on_parameters(params: &[String]) -> Result<()> {
if let Some(part_str) = parts.next() {
match part_str.parse::<usize>()? {
1 => run(day, true, false)?,
2 => run(day, false, true)?,
1 => run(&day, true, false)?,
2 => run(&day, false, true)?,
p => Err(ParamError::UnknownPart(p))?,
}
};
} else {
run(day, true, true)?;
let runtime = run(&day, true, true)?;
println!("Runtime: {}", runtime.as_secs_f32());
}
}
}