day04 finished

This commit is contained in:
Ruediger Ludwig 2023-01-29 15:12:26 +01:00
parent d7c85a75f6
commit 33eb92e9d1
10 changed files with 1184 additions and 55 deletions

6
data/day04/example01.txt Normal file
View file

@ -0,0 +1,6 @@
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8

1000
data/day04/input.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,27 @@
use itertools::Itertools;
use std::{fs, io}; use std::{fs, io};
pub fn read_data(day_num: usize, file: &str) -> io::Result<String> { fn read_data(day_num: usize, file: &str) -> io::Result<String> {
fs::read_to_string(format!("data/day{:02}/{}", day_num, file)) fs::read_to_string(format!("data/day{:02}/{}", day_num, file))
} }
pub fn read_lines(day_num: usize, file: &str) -> io::Result<Vec<String>> {
let lines = read_data(day_num, file)?;
let x = lines
.split('\n')
.with_position()
.filter_map(|line| match line {
itertools::Position::First(line)
| itertools::Position::Middle(line)
| itertools::Position::Only(line) => Some(line.to_owned()),
itertools::Position::Last(line) => {
if line.len() == 0 {
None
} else {
Some(line.to_owned())
}
}
})
.collect();
Ok(x)
}

View file

@ -13,13 +13,13 @@ impl DayTrait for Day {
DAY_NUMBER DAY_NUMBER
} }
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> { fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let vector = Day::parse(lines)?; let vector = Day::parse(lines)?;
let max = vector.iter().max().ok_or(CalorieError::Empty)?; let max = vector.iter().max().ok_or(CalorieError::Empty)?;
Ok(ResultType::IntResult(*max)) Ok(ResultType::IntResult(*max))
} }
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> { fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let vector = Day::parse(lines)?; let vector = Day::parse(lines)?;
let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum(); let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum();
Ok(ResultType::IntResult(sum)) Ok(ResultType::IntResult(sum))
@ -27,9 +27,9 @@ impl DayTrait for Day {
} }
impl Day { impl Day {
fn parse(lines: &str) -> Result<Vec<i64>, CalorieError> { fn parse(lines: &[String]) -> Result<Vec<i64>, CalorieError> {
Ok(lines Ok(lines
.split("\n") .iter()
.batching(|it| { .batching(|it| {
let result = it let result = it
.take_while(|line| line.len() != 0) .take_while(|line| line.len() != 0)
@ -57,13 +57,13 @@ pub enum CalorieError {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::common::file::read_data; use crate::common::file::read_lines;
use anyhow::Result; use anyhow::Result;
#[test] #[test]
fn test_part1() -> Result<()> { fn test_part1() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(24_000); let expected = ResultType::IntResult(24_000);
let result = day.part1(&lines)?; let result = day.part1(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);
@ -74,7 +74,7 @@ mod test {
#[test] #[test]
fn test_part2() -> Result<()> { fn test_part2() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(45_000); let expected = ResultType::IntResult(45_000);
let result = day.part2(&lines)?; let result = day.part2(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);
@ -84,7 +84,7 @@ mod test {
#[test] #[test]
fn test_parse() -> Result<()> { fn test_parse() -> Result<()> {
let lines = read_data(DAY_NUMBER, "example01.txt")?; let lines = read_lines(DAY_NUMBER, "example01.txt")?;
let expected = vec![6000, 4000, 11000, 24000, 10000]; let expected = vec![6000, 4000, 11000, 24000, 10000];
let result = Day::parse(&lines)?; let result = Day::parse(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);

View file

@ -12,10 +12,10 @@ impl DayTrait for Day {
DAY_NUMBER DAY_NUMBER
} }
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> { fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines let sum = lines
.split("\n") .iter()
.map(RPS::parse_line) .map(|line| RPS::parse_line(&line))
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?
.into_iter() .into_iter()
.map(|(first, second)| second.asses_pair(&first)) .map(|(first, second)| second.asses_pair(&first))
@ -24,10 +24,10 @@ impl DayTrait for Day {
Ok(ResultType::IntResult(sum)) Ok(ResultType::IntResult(sum))
} }
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> { fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines let sum = lines
.split("\n") .iter()
.map(Strategy::parse_line) .map(|line| Strategy::parse_line(&line))
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?
.into_iter() .into_iter()
.map(|(first, second)| second.fullfill(&first).asses_pair(&first)) .map(|(first, second)| second.fullfill(&first).asses_pair(&first))
@ -155,7 +155,7 @@ impl TryFrom<&str> for Strategy {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::common::file::read_data; use crate::common::file::read_lines;
use anyhow::Result; use anyhow::Result;
#[test] #[test]
@ -182,7 +182,7 @@ mod test {
#[test] #[test]
fn test_part1() -> Result<()> { fn test_part1() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(15); let expected = ResultType::IntResult(15);
let result = day.part1(&lines)?; let result = day.part1(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);
@ -214,7 +214,7 @@ mod test {
#[test] #[test]
fn test_part2() -> Result<()> { fn test_part2() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::NoResult; let expected = ResultType::NoResult;
let result = day.part2(&lines)?; let result = day.part2(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);

View file

@ -1,8 +1,7 @@
use super::template::{DayTrait, ResultType};
use itertools::Itertools; use itertools::Itertools;
use thiserror::Error; use thiserror::Error;
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 3; const DAY_NUMBER: usize = 3;
pub struct Day; pub struct Day;
@ -12,25 +11,25 @@ impl DayTrait for Day {
DAY_NUMBER DAY_NUMBER
} }
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> { fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines let sum = lines
.split('\n')
.map(|rucksack| find_double(rucksack).and_then(priority))
.collect::<Result<Vec<_>, _>>()?
.iter() .iter()
.sum(); .map(|line| find_double(&line))
.map_ok(priority)
.flatten_ok()
.fold_ok(0, |a, b| a + b)?;
Ok(ResultType::IntResult(sum)) Ok(ResultType::IntResult(sum))
} }
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> { fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines let sum = lines
.split('\n') .iter()
.chunks(3) .chunks(3)
.into_iter() .into_iter()
.map(|chunk| find_badge(&chunk.collect::<Vec<_>>()).and_then(priority)) .map(|chunk| find_badge(&chunk.collect::<Vec<_>>()))
.collect::<Result<Vec<_>, _>>()? .map_ok(priority)
.iter() .flatten_ok()
.sum(); .fold_ok(0, |a, b| a + b)?;
Ok(ResultType::IntResult(sum)) Ok(ResultType::IntResult(sum))
} }
} }
@ -72,7 +71,7 @@ fn find_double(content: &str) -> Result<char, RucksackError> {
Err(RucksackError::NoDoubleFound)? Err(RucksackError::NoDoubleFound)?
} }
fn find_badge<'a>(elves: &[&str]) -> Result<char, RucksackError> { fn find_badge<'a>(elves: &[&String]) -> Result<char, RucksackError> {
if elves.len() < 2 { if elves.len() < 2 {
return Err(RucksackError::NeedAtLeastTwo); return Err(RucksackError::NeedAtLeastTwo);
}; };
@ -93,7 +92,7 @@ fn find_badge<'a>(elves: &[&str]) -> Result<char, RucksackError> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::common::file::read_data; use crate::common::file::read_lines;
use anyhow::Result; use anyhow::Result;
#[test] #[test]
@ -109,7 +108,7 @@ mod test {
#[test] #[test]
fn test_part1() -> Result<()> { fn test_part1() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(157); let expected = ResultType::IntResult(157);
let result = day.part1(&lines)?; let result = day.part1(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);
@ -117,24 +116,10 @@ mod test {
Ok(()) 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] #[test]
fn test_part2() -> Result<()> { fn test_part2() -> Result<()> {
let day = Day {}; let day = Day {};
let lines = read_data(day.get_day_number(), "example01.txt")?; let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(70); let expected = ResultType::IntResult(70);
let result = day.part2(&lines)?; let result = day.part2(&lines)?;
assert_eq!(result, expected); assert_eq!(result, expected);

114
src/days/day04/mod.rs Normal file
View file

@ -0,0 +1,114 @@
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use std::num::ParseIntError;
use thiserror::Error;
const DAY_NUMBER: usize = 4;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines
.iter()
.map(|line| parse(&line))
.filter_ok(fully_contained)
.fold_ok(0i64, |a, _| a + 1)?;
Ok(ResultType::IntResult(sum))
}
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
let sum = lines
.iter()
.map(|line| parse(&line))
.filter_ok(overlaps)
.fold_ok(0i64, |a, _| a + 1)?;
Ok(ResultType::IntResult(sum))
}
}
#[derive(Debug, Error)]
enum SectionError {
#[error("Can't parse line: {0}")]
InvalidLine(String),
#[error("Can't parse range: {0}")]
InvalidRange(String),
#[error("Could not parse Number")]
NotInteger(#[from] ParseIntError),
}
type Range = (i64, i64);
fn parse_range(range: &str) -> Result<Range, SectionError> {
let nums: Vec<_> = range
.split('-')
.map(|num| num.parse::<i64>())
.try_collect()?;
if nums.len() != 2 {
Err(SectionError::InvalidLine(range.to_owned()))
} else {
Ok(nums.iter().copied().collect_tuple().unwrap())
}
}
fn parse(line: &str) -> Result<(Range, Range), SectionError> {
let ranges: Vec<_> = line.split(',').map(parse_range).try_collect()?;
if ranges.len() != 2 {
Err(SectionError::InvalidRange(line.to_owned()))
} else {
Ok(ranges.iter().copied().collect_tuple().unwrap())
}
}
fn fully_contained((first, second): &(Range, Range)) -> bool {
(first.0 >= second.0 && first.1 <= second.1) || (second.0 >= first.0 && second.1 <= first.1)
}
fn overlaps((first, second): &(Range, Range)) -> bool {
first.0 <= second.1 && second.0 <= first.1
}
#[cfg(test)]
mod test {
use super::*;
use crate::common::file::read_lines;
use anyhow::Result;
#[test]
fn test_parse() -> Result<()> {
let input = "2-4,6-8";
let expected = ((2, 4), (6, 8));
let result = parse(input)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part1() -> Result<()> {
let day = Day {};
let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(2);
let result = day.part1(&lines)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part2() -> Result<()> {
let day = Day {};
let lines = read_lines(day.get_day_number(), "example01.txt")?;
let expected = ResultType::IntResult(4);
let result = day.part2(&lines)?;
assert_eq!(result, expected);
Ok(())
}
}

View file

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

View file

@ -11,6 +11,6 @@ pub enum ResultType {
pub trait DayTrait { pub trait DayTrait {
fn get_day_number(&self) -> usize; fn get_day_number(&self) -> usize;
fn part1(&self, lines: &str) -> Result<ResultType>; fn part1(&self, lines: &[String]) -> Result<ResultType>;
fn part2(&self, lines: &str) -> Result<ResultType>; fn part2(&self, lines: &[String]) -> Result<ResultType>;
} }

View file

@ -3,7 +3,7 @@ mod days;
mod macros; mod macros;
use anyhow::Result; use anyhow::Result;
use common::file::read_data; use common::file::read_lines;
use days::{day_provider, DayTrait, ResultType}; use days::{day_provider, DayTrait, ResultType};
use std::{ use std::{
env, env,
@ -47,7 +47,7 @@ fn output(day: usize, part: usize, result: ResultType, time: Duration) -> () {
} }
} }
fn run_part(day: &Box<dyn DayTrait>, is_part1: bool, lines: &str) -> Result<Duration> { fn run_part(day: &Box<dyn DayTrait>, is_part1: bool, lines: &[String]) -> Result<Duration> {
let now = Instant::now(); let now = Instant::now();
let result = if is_part1 { let result = if is_part1 {
day.part1(lines)? day.part1(lines)?
@ -70,7 +70,7 @@ fn run_part(day: &Box<dyn DayTrait>, is_part1: bool, lines: &str) -> Result<Dura
} }
fn run(day: &Box<dyn DayTrait>, part1: bool, part2: bool) -> Result<Duration> { fn run(day: &Box<dyn DayTrait>, part1: bool, part2: bool) -> Result<Duration> {
let lines = read_data(day.get_day_number(), "input.txt")?; let lines = read_lines(day.get_day_number(), "input.txt")?;
let elapsed1 = if part1 { let elapsed1 = if part1 {
run_part(day, true, &lines)? run_part(day, true, &lines)?
} else { } else {