From a5f19ecae1368ce0add6e0037f970270effdc165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Ludwig?= Date: Sat, 29 Jul 2023 17:02:25 +0200 Subject: [PATCH] Made everything a bit more rustic --- src/common/file.rs | 11 +++-- src/days/day01/mod.rs | 22 ++++++---- src/days/day02/mod.rs | 18 ++++---- src/days/day03/mod.rs | 22 +++++----- src/days/day04/mod.rs | 18 ++++---- src/days/day05/mod.rs | 44 +++++++++++--------- src/days/day06/mod.rs | 14 +++---- src/days/day07/mod.rs | 26 +++++++----- src/days/day08/mod.rs | 47 +++++++++++---------- src/days/day09/mod.rs | 21 +++++----- src/days/day10/mod.rs | 25 +++++++----- src/days/day11/mod.rs | 37 ++++++++++------- src/days/day12/mod.rs | 31 ++++++++------ src/days/day13/mod.rs | 21 +++++----- src/days/day14/mod.rs | 95 +++++++++++++++++++++++-------------------- src/days/day15/mod.rs | 25 ++++++++---- src/days/day16/mod.rs | 37 +++++++++-------- src/days/day17/mod.rs | 80 ++++++++++++++++++++++++------------ src/days/day18/mod.rs | 27 +++++++----- src/days/day__/mod.rs | 19 ++++++--- src/days/template.rs | 4 +- src/main.rs | 6 +-- 22 files changed, 376 insertions(+), 274 deletions(-) diff --git a/src/common/file.rs b/src/common/file.rs index 5712052..3c5b86c 100644 --- a/src/common/file.rs +++ b/src/common/file.rs @@ -5,13 +5,16 @@ fn format_path(day_num: usize, file: &str) -> String { format!("data/day{day_num:02}/{file}") } -pub fn read_lines(day_num: usize, file: &str) -> io::Result> { - Ok(fs::read_to_string(format_path(day_num, file))? +pub fn read_string(day_num: usize, file: &str) -> io::Result { + Ok(fs::read_to_string(format_path(day_num, file))?) +} + +pub fn split_lines<'a>(lines: &'a str) -> impl Iterator + 'a { + lines .split('\n') .with_position() .filter_map(|(pos, line)| match pos { itertools::Position::Last if line.is_empty() => None, - _ => Some(line.to_owned()), + _ => Some(line), }) - .collect()) } diff --git a/src/days/day01/mod.rs b/src/days/day01/mod.rs index 0bda729..6419b99 100644 --- a/src/days/day01/mod.rs +++ b/src/days/day01/mod.rs @@ -3,6 +3,8 @@ use std::num::ParseIntError; use thiserror::Error; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; pub struct Day; @@ -13,13 +15,15 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { + let lines = split_lines(lines); let vector = Day::parse(lines)?; let max = vector.iter().max().ok_or(CalorieError::Empty)?; Ok(ResultType::Integer(*max)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { + let lines = split_lines(lines); let vector = Day::parse(lines)?; let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum(); Ok(ResultType::Integer(sum)) @@ -27,9 +31,8 @@ impl DayTrait for Day { } impl Day { - fn parse(lines: &[String]) -> Result, CalorieError> { + fn parse<'a>(lines: impl Iterator) -> Result, CalorieError> { Ok(lines - .iter() .batching(|it| { let result = it .take_while(|line| !line.is_empty()) @@ -57,13 +60,13 @@ pub enum CalorieError { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(24_000); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -74,7 +77,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(45_000); let result = day.part2(&lines)?; assert_eq!(result, expected); @@ -84,9 +87,10 @@ mod test { #[test] fn test_parse() -> Result<()> { - let lines = read_lines(DAY_NUMBER, "example01.txt")?; + let lines = read_string(DAY_NUMBER, "example01.txt")?; + let lines = split_lines(&lines); let expected = vec![6000, 4000, 11000, 24000, 10000]; - let result = Day::parse(&lines)?; + let result = Day::parse(lines)?; assert_eq!(result, expected); Ok(()) diff --git a/src/days/day02/mod.rs b/src/days/day02/mod.rs index 73fc25e..9f19241 100644 --- a/src/days/day02/mod.rs +++ b/src/days/day02/mod.rs @@ -1,5 +1,7 @@ use std::cmp::Ordering; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use thiserror::Error; @@ -12,9 +14,8 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part1(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .map(|line| Rps::parse_line(line)) .collect::, _>>()? .into_iter() @@ -24,9 +25,8 @@ impl DayTrait for Day { Ok(ResultType::Integer(sum)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part2(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .map(|line| Strategy::parse_line(line)) .collect::, _>>()? .into_iter() @@ -155,7 +155,7 @@ impl TryFrom<&str> for Strategy { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -182,7 +182,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(15); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -214,7 +214,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(12); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day03/mod.rs b/src/days/day03/mod.rs index f263f63..a86653c 100644 --- a/src/days/day03/mod.rs +++ b/src/days/day03/mod.rs @@ -1,3 +1,5 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use itertools::Itertools; use thiserror::Error; @@ -11,9 +13,8 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part1(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .map(|line| find_double(line)) .map_ok(priority) .flatten_ok() @@ -21,12 +22,11 @@ impl DayTrait for Day { Ok(ResultType::Integer(sum)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part2(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .chunks(3) .into_iter() - .map(|chunk| find_badge(&chunk.collect::>())) + .map(|chunk| find_badge(&chunk.collect_vec())) .map_ok(priority) .flatten_ok() .fold_ok(0, |a, b| a + b)?; @@ -71,7 +71,7 @@ fn find_double(content: &str) -> Result { Err(RucksackError::NoDoubleFound)? } -fn find_badge(elves: &[&String]) -> Result { +fn find_badge(elves: &[&str]) -> Result { if elves.len() < 2 { return Err(RucksackError::NeedAtLeastTwo); }; @@ -92,7 +92,7 @@ fn find_badge(elves: &[&String]) -> Result { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -108,7 +108,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(157); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -119,7 +119,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(70); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day04/mod.rs b/src/days/day04/mod.rs index 2696dc7..62a84a9 100644 --- a/src/days/day04/mod.rs +++ b/src/days/day04/mod.rs @@ -1,3 +1,5 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use itertools::Itertools; use std::num::ParseIntError; @@ -12,18 +14,16 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part1(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .map(|line| parse(line)) .filter_ok(fully_contained) .fold_ok(0i64, |a, _| a + 1)?; Ok(ResultType::Integer(sum)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let sum = lines - .iter() + fn part2(&self, lines: &str) -> anyhow::Result { + let sum = split_lines(lines) .map(|line| parse(line)) .filter_ok(overlaps) .fold_ok(0i64, |a, _| a + 1)?; @@ -77,7 +77,7 @@ fn overlaps((first, second): &(Range, Range)) -> bool { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -93,7 +93,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(2); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -104,7 +104,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(4); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day05/mod.rs b/src/days/day05/mod.rs index 98a292f..57429eb 100644 --- a/src/days/day05/mod.rs +++ b/src/days/day05/mod.rs @@ -1,4 +1,7 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; +use itertools::Itertools; use std::num::ParseIntError; use thiserror::Error; @@ -11,7 +14,7 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { let (mut stacks, commands) = parse(lines)?; for command in commands { command.with_turn(&mut stacks)?; @@ -19,7 +22,7 @@ impl DayTrait for Day { Ok(ResultType::String(get_word(&stacks))) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { let (mut stacks, commands) = parse(lines)?; for command in commands { command.without_turn(&mut stacks)?; @@ -57,11 +60,13 @@ fn parse_crate(line: &str) -> Vec> { .collect() } -fn parse_all_crates(line_it: &mut dyn Iterator) -> Result, CrateError> { - let rows: Vec<_> = line_it +fn parse_all_crates<'a>( + line_it: &mut dyn Iterator, +) -> Result, CrateError> { + let rows = line_it .take_while(|line| !line.is_empty()) .map(|row| parse_crate(row)) - .collect(); + .collect_vec(); let Some(stacks_count) = rows.iter().map(|row| row.len()).max() else { return Err(CrateError::NoCargoStacksGiven); }; @@ -79,11 +84,11 @@ fn parse_all_crates(line_it: &mut dyn Iterator) -> Result Result<(Vec, Vec), CrateError> { - let mut iter = line.iter(); +fn parse<'a>(lines: &'a str) -> Result<(Vec, Vec), CrateError> { + let mut iter = split_lines(lines); let stacks = parse_all_crates(&mut iter)?; let moves: Vec<_> = iter - .map(|command| Move::try_from(command.as_str())) + .map(|command| Move::try_from(command)) .collect::>()?; Ok((stacks, moves)) } @@ -167,7 +172,7 @@ impl TryFrom<&str> for Move { mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -191,14 +196,15 @@ mod test { #[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(); + " [D] ", + "[N] [C] ", + "[Z] [M] [P]", + " 1 2 3 ", + "", + "move 1 from to 2 1", + ] + .join("\n"); + let mut input = split_lines(&lines); let expected = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()]; let result = parse_all_crates(&mut input)?; @@ -221,7 +227,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::String("CMZ".to_owned()); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -243,7 +249,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::String("MCD".to_owned()); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day06/mod.rs b/src/days/day06/mod.rs index 7a04a3f..6a4bc51 100644 --- a/src/days/day06/mod.rs +++ b/src/days/day06/mod.rs @@ -10,13 +10,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let count = find_marker(&lines[0], 4)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let count = find_marker(lines.trim_end(), 4)?; Ok(ResultType::Integer(count as i64)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let count = find_marker(&lines[0], 14)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let count = find_marker(lines.trim_end(), 14)?; Ok(ResultType::Integer(count as i64)) } } @@ -82,7 +82,7 @@ fn find_marker(word: &str, marker_length: usize) -> Result { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -98,7 +98,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(10); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -109,7 +109,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(29); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day07/mod.rs b/src/days/day07/mod.rs index 8c4c791..beb77c6 100644 --- a/src/days/day07/mod.rs +++ b/src/days/day07/mod.rs @@ -4,6 +4,8 @@ use std::{ rc::{Rc, Weak}, }; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use thiserror::Error; @@ -16,7 +18,8 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { + let lines = split_lines(lines); let root = Directory::parse(lines)?; let result = root .iter() @@ -32,9 +35,10 @@ impl DayTrait for Day { Ok(ResultType::Integer(result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { const AVAIL: i64 = 70_000_000; const REQUIRED: i64 = 30_000_000; + let lines = split_lines(lines); let root = Directory::parse(lines)?; let to_free = REQUIRED - (AVAIL - root.size()); let result = root @@ -167,7 +171,7 @@ impl Directory { sub_node.map(|node| Directory::create(node.clone())) } - pub fn parse(lines: &[String]) -> Result { + pub fn parse<'a>(lines: impl Iterator) -> Result { let root = Directory::root(); let mut current = root.clone(); for line in lines { @@ -269,7 +273,7 @@ impl Iterator for DirectoryIterator { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -298,8 +302,9 @@ mod test { #[test] fn test_total_size() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let dir = Directory::parse(&lines)?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let lines = split_lines(&lines); + let dir = Directory::parse(lines)?; assert_eq!(dir.size(), 48381165); Ok(()) @@ -308,9 +313,10 @@ mod test { #[test] fn test_iterator() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let lines = split_lines(&lines); let expected = vec!["/", "a", "e", "d"]; - let dir = Directory::parse(&lines)?; + let dir = Directory::parse(lines)?; let result: Vec<_> = dir.iter().map(|dir| dir.name()).collect(); assert_eq!(result, expected); @@ -320,7 +326,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(95437); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -331,7 +337,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(24933642); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day08/mod.rs b/src/days/day08/mod.rs index 77b5fcc..aa671ea 100644 --- a/src/days/day08/mod.rs +++ b/src/days/day08/mod.rs @@ -1,3 +1,5 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use itertools::{iproduct, FoldWhile, Itertools}; use thiserror::Error; @@ -11,14 +13,14 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let forest = Forest::parse(lines)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let forest = Forest::try_from(lines)?; let result = forest.count_visible(); Ok(ResultType::Integer(result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let forest = Forest::parse(lines)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let forest = Forest::try_from(lines)?; let result = forest.best_score(); Ok(ResultType::Integer(result)) } @@ -67,18 +69,6 @@ impl Forest { }) } - pub fn parse(lines: &[String]) -> Result { - lines - .iter() - .map(|line| { - line.chars() - .map(|height| height.to_digit(10).ok_or(ForestError::NoLegalDigit(height))) - .collect::, _>>() - }) - .collect::, _>>() - .and_then(Forest::create) - } - pub fn count_visible(&self) -> i64 { let mut vis_count = 2 * (self.width + self.height - 2) as i64; let mut visible = vec![vec![false; self.width - 2]; self.height - 2]; @@ -191,17 +181,32 @@ impl Forest { } } +impl TryFrom<&str> for Forest { + type Error = ForestError; + + fn try_from(lines: &str) -> Result { + split_lines(lines) + .map(|line| { + line.chars() + .map(|height| height.to_digit(10).ok_or(ForestError::NoLegalDigit(height))) + .collect::, _>>() + }) + .collect::, _>>() + .and_then(Forest::create) + } +} + #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_parse() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let forest = Forest::parse(&lines)?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let forest = Forest::try_from(lines.as_str())?; assert_eq!(forest.width, 5); assert_eq!(forest.height, 5); @@ -211,7 +216,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(21); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -222,7 +227,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(8); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day09/mod.rs b/src/days/day09/mod.rs index 533acf4..7f11710 100644 --- a/src/days/day09/mod.rs +++ b/src/days/day09/mod.rs @@ -1,5 +1,6 @@ use super::template::{DayTrait, ResultType}; -use crate::common::{direction::Direction, pos::Pos}; +use crate::common::{direction::Direction, file::split_lines, pos::Pos}; +use itertools::Itertools; use std::{collections::HashSet, num::ParseIntError}; use thiserror::Error; @@ -12,14 +13,14 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { let commands = parse_commands(lines)?; let mut rope = Rope::create(2); let result = rope.run(&commands); Ok(ResultType::Integer(result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { let commands = parse_commands(lines)?; let mut rope = Rope::create(10); let result = rope.run(&commands); @@ -39,12 +40,12 @@ enum RopeError { StepParseError(#[from] ParseIntError), } -fn parse_commands(lines: &[String]) -> Result, RopeError> { - lines.iter().map(parse_line).collect::>() +fn parse_commands(lines: &str) -> Result, RopeError> { + split_lines(lines).map(parse_line).try_collect() } -fn parse_line(line: &String) -> Result<(Direction, usize), RopeError> { - match line.split_whitespace().collect::>()[..] { +fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> { + match line.split_whitespace().collect_vec()[..] { [direction, steps] => { let direction = match direction { "U" => Direction::North, @@ -125,13 +126,13 @@ impl Rope { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(13); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -142,7 +143,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example02.txt")?; + let lines = read_string(day.get_day_number(), "example02.txt")?; let expected = ResultType::Integer(36); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day10/mod.rs b/src/days/day10/mod.rs index 13bfdcf..255231b 100644 --- a/src/days/day10/mod.rs +++ b/src/days/day10/mod.rs @@ -1,3 +1,5 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use std::{num::ParseIntError, slice::Iter}; use thiserror::Error; @@ -11,18 +13,16 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let instructions = lines - .iter() + fn part1(&self, lines: &str) -> anyhow::Result { + let instructions = split_lines(lines) .map(|line| Instruction::parse(line)) .collect::, _>>()?; let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]); Ok(ResultType::Integer(result as i64)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let instructions = lines - .iter() + fn part2(&self, lines: &str) -> anyhow::Result { + let instructions = split_lines(lines) .map(|line| Instruction::parse(line)) .collect::, _>>()?; let result = CpuCycles::draw(&instructions); @@ -128,8 +128,9 @@ impl<'a> Iterator for CpuCycles<'a> { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; + use itertools::Itertools; #[test] fn test_simple() -> Result<()> { @@ -144,7 +145,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(13140); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -155,8 +156,12 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let expected = ResultType::Lines(read_lines(day.get_day_number(), "expected01.txt")?); + let lines = read_string(day.get_day_number(), "example01.txt")?; + let expected_lines = read_string(day.get_day_number(), "expected01.txt")?; + let expected_lines = split_lines(&expected_lines) + .map(|s| s.to_owned()) + .collect_vec(); + let expected = ResultType::Lines(expected_lines); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day11/mod.rs b/src/days/day11/mod.rs index 2e8b3c7..058cb57 100644 --- a/src/days/day11/mod.rs +++ b/src/days/day11/mod.rs @@ -1,3 +1,5 @@ +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use once_cell::sync::Lazy; use regex::Regex; @@ -13,13 +15,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let troop = Troop::parse(lines)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let troop = Troop::try_from(lines)?; Ok(ResultType::Integer(troop.play(20))) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let troop = Troop::parse(lines)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let troop = Troop::try_from(lines)?; Ok(ResultType::Integer(troop.play_again(10_000))) } } @@ -90,7 +92,7 @@ impl Monkey { } pub fn parse_one( - iterator: &mut dyn Iterator, + iterator: &mut dyn Iterator, ) -> Result, MonkeyError> { if let Some(line) = iterator.next() { let number: usize = Monkey::parse_line(&MONKEY, line)?.parse()?; @@ -142,16 +144,20 @@ struct Troop { monkeys: Vec, } -impl Troop { - pub fn parse(lines: &[String]) -> Result { - let mut iter = lines.iter(); +impl TryFrom<&str> for Troop { + type Error = MonkeyError; + + fn try_from(value: &str) -> Result { + let mut iter = split_lines(value); let mut monkeys = Vec::new(); while let Some(monkey) = Monkey::parse_one(&mut iter)? { monkeys.push(monkey); } Ok(Troop { monkeys }) } +} +impl Troop { fn do_play(&self, rounds: usize, alter: F) -> i64 where F: Fn(i64) -> i64, @@ -191,13 +197,14 @@ impl Troop { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_parse() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let mut lines = split_lines(&lines); let expected = Monkey { number: 0, items: vec![79, 98], @@ -206,7 +213,7 @@ mod test { good_monkey: 2, bad_monkey: 3, }; - let result = Monkey::parse_one(&mut lines.iter())?.unwrap(); + let result = Monkey::parse_one(&mut lines)?.unwrap(); assert_eq!(result, expected); Ok(()) @@ -215,9 +222,9 @@ mod test { #[test] fn test_parse_all() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = 4; - let result = Troop::parse(&lines)?; + let result = Troop::try_from(lines.as_str())?; assert_eq!(result.monkeys.len(), expected); Ok(()) @@ -226,7 +233,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(10605); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -237,7 +244,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(2713310158); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day12/mod.rs b/src/days/day12/mod.rs index c3f6ab1..1c099d0 100644 --- a/src/days/day12/mod.rs +++ b/src/days/day12/mod.rs @@ -1,6 +1,6 @@ use std::collections::{BinaryHeap, HashSet}; -use crate::common::pos::Pos; +use crate::common::{file::split_lines, pos::Pos}; use super::template::{DayTrait, ResultType}; use thiserror::Error; @@ -14,13 +14,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let valley = Valley::parse(lines)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let valley = Valley::try_from(lines)?; Ok(ResultType::Integer(valley.walk()? as i64)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let valley = Valley::parse(lines)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let valley = Valley::try_from(lines)?; Ok(ResultType::Integer(valley.walk_short()? as i64)) } } @@ -158,13 +158,16 @@ struct Valley { width: usize, } -impl Valley { - pub fn parse(lines: &[String]) -> Result { +impl TryFrom<&str> for Valley { + type Error = ValleyError; + + fn try_from(lines: &str) -> Result { + let lines = split_lines(lines); let mut map = Vec::new(); let mut start = None; let mut exit = None; let mut valley_width = None; - for (y, row) in lines.iter().enumerate() { + for (y, row) in lines.enumerate() { let mut height_row = Vec::new(); for (x, height_char) in row.chars().enumerate() { match height_char { @@ -205,7 +208,9 @@ impl Valley { width, }) } +} +impl Valley { fn get_height(&self, pos: Pos) -> char { self.map[pos.y()][pos.x()] } @@ -255,14 +260,14 @@ impl Valley { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_parse() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let valley = Valley::parse(&lines)?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let valley = Valley::try_from(lines.as_str())?; assert_eq!(valley.width, 8); assert_eq!(valley.start, Pos::new(0, 0)); assert_eq!(valley.exit, Pos::new(5, 2)); @@ -273,7 +278,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(31); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -284,7 +289,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(29); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day13/mod.rs b/src/days/day13/mod.rs index 0e33a74..f6b146a 100644 --- a/src/days/day13/mod.rs +++ b/src/days/day13/mod.rs @@ -1,5 +1,7 @@ use std::cmp::Ordering; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use itertools::Itertools; use thiserror::Error; @@ -13,7 +15,7 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { let packets = Packets::parse_all(lines)?; let result: i64 = packets .iter() @@ -30,7 +32,7 @@ impl DayTrait for Day { Ok(ResultType::Integer(result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { let small = Packets::parse("[[2]]")?; let large = Packets::parse("[[6]]")?; let mut pos_small = 1; @@ -144,9 +146,8 @@ impl Packets { Err(PacketError::PrematureEndOfInput) } - pub fn parse_all(lines: &[String]) -> Result, PacketError> { - lines - .iter() + pub fn parse_all(lines: &str) -> Result, PacketError> { + split_lines(lines) .filter(|line| !line.is_empty()) .map(|line| Packets::parse(line)) .collect::, _>>() @@ -156,7 +157,7 @@ impl Packets { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] @@ -174,7 +175,7 @@ mod test { #[test] fn test_parse_all() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let result = Packets::parse_all(&lines)?; assert_eq!(result.len(), 16); Ok(()) @@ -183,7 +184,7 @@ mod test { #[test] fn test_compare_all() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = vec![true, true, false, true, false, true, false, false]; let result = Packets::parse_all(&lines)?; let compare = result @@ -199,7 +200,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(13); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -210,7 +211,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(140); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day14/mod.rs b/src/days/day14/mod.rs index e8ace33..56a1e02 100644 --- a/src/days/day14/mod.rs +++ b/src/days/day14/mod.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use std::{collections::HashSet, num::ParseIntError}; use thiserror::Error; -use crate::common::pos::Pos; +use crate::common::{file::split_lines, pos::Pos}; use super::template::{DayTrait, ResultType}; @@ -15,13 +15,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let cave = Cave::parse(lines)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let cave = Cave::try_from(lines)?; Ok(ResultType::Integer(cave.drop_bottomless() as i64)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let cave = Cave::parse(lines)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let cave = Cave::try_from(lines)?; Ok(ResultType::Integer(cave.drop_floor())) } } @@ -51,6 +51,46 @@ struct Cave { height: i32, } +impl TryFrom<&str> for Cave { + type Error = CaveError; + + fn try_from(lines: &str) -> Result { + let lines = split_lines(lines); + let mut cave: HashSet> = HashSet::new(); + for line in lines { + cave.extend(Cave::parse_one(line)?.iter()); + } + if cave.is_empty() { + Err(CaveError::EmptyCave) + } else { + let mut max_x = i32::MIN; + let mut min_x = i32::MAX; + let mut max_y = i32::MIN; + for block in &cave { + if block.x() < min_x { + min_x = block.x(); + } + if block.x() > max_x { + max_x = block.x(); + } + if block.y() > max_y { + max_y = block.y() + } + } + let mut map = vec![vec![false; (max_x - min_x + 1) as usize]; (max_y + 1) as usize]; + for block in cave { + map[block.y() as usize][(block.x() - min_x) as usize] = true; + } + Ok(Cave { + map, + min_x, + max_x, + height: max_y + 1, + }) + } + } +} + impl Cave { fn get(&self, map: &[Vec], pos: Pos) -> bool { if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x { @@ -118,41 +158,6 @@ impl Cave { Ok(blocks) } - pub fn parse(lines: &[String]) -> Result { - let mut cave: HashSet> = HashSet::new(); - for line in lines { - cave.extend(Cave::parse_one(line)?.iter()); - } - if cave.is_empty() { - Err(CaveError::EmptyCave) - } else { - let mut max_x = i32::MIN; - let mut min_x = i32::MAX; - let mut max_y = i32::MIN; - for block in &cave { - if block.x() < min_x { - min_x = block.x(); - } - if block.x() > max_x { - max_x = block.x(); - } - if block.y() > max_y { - max_y = block.y() - } - } - let mut map = vec![vec![false; (max_x - min_x + 1) as usize]; (max_y + 1) as usize]; - for block in cave { - map[block.y() as usize][(block.x() - min_x) as usize] = true; - } - Ok(Cave { - map, - min_x, - max_x, - height: max_y + 1, - }) - } - } - pub fn drop_bottomless(&self) -> u32 { let mut drops = 0; let mut filled_map = self.map.to_vec(); @@ -243,7 +248,7 @@ impl Cave { #[cfg(test)] mod test { use super::*; - use crate::{common::file::read_lines, hashset}; + use crate::{common::file::read_string, hashset}; use anyhow::Result; #[test] @@ -259,8 +264,8 @@ mod test { #[test] fn test_parse_all() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let result = Cave::parse(&lines)?; + let lines = read_string(day.get_day_number(), "example01.txt")?; + let result = Cave::try_from(lines.as_str())?; assert_eq!(result.min_x, 494); assert_eq!(result.max_x, 503); assert_eq!(result.height, 10); @@ -271,7 +276,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(24); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -282,7 +287,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(93); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day15/mod.rs b/src/days/day15/mod.rs index 28e4ee4..8292730 100644 --- a/src/days/day15/mod.rs +++ b/src/days/day15/mod.rs @@ -1,5 +1,5 @@ use super::template::{DayTrait, ResultType}; -use crate::common::pos::Pos; +use crate::common::{file::split_lines, pos::Pos}; use itertools::Itertools; use once_cell::sync::Lazy; use regex::Regex; @@ -15,14 +15,22 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { + let lines = split_lines(lines).collect_vec(); + if lines.len() < 2 { + Err(SensorError::ToFewLines)?; + } let row = lines[0].parse()?; let (sensors, beacons) = Day::parse_all(&lines[1..])?; let result = Day::count_coverage_at(&sensors, &beacons, row); Ok(ResultType::Integer(result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { + let lines = split_lines(lines).collect_vec(); + if lines.len() < 2 { + Err(SensorError::ToFewLines)?; + } let (sensors, _) = Day::parse_all(&lines[1..])?; let mid_lines = Day::dividing_lines(&sensors); let points = mid_lines @@ -43,7 +51,7 @@ impl DayTrait for Day { } impl Day { - fn parse_all(lines: &[String]) -> Result<(HashSet, HashSet>), SensorError> { + fn parse_all(lines: &[&str]) -> Result<(HashSet, HashSet>), SensorError> { let mut sensors = HashSet::new(); let mut beacons = HashSet::new(); for line in lines { @@ -91,6 +99,9 @@ enum SensorError { #[error("Did not find exactly one point for the sensor: {0}")] NotExactlyOneResult(usize), + + #[error("Too few lines in input.txt")] + ToFewLines, } #[derive(Debug)] @@ -256,7 +267,7 @@ impl Sensor { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_parse() -> Result<()> { @@ -295,7 +306,7 @@ mod test { #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(26); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -306,7 +317,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(56000011); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day16/mod.rs b/src/days/day16/mod.rs index 1e7cfb1..67dd832 100644 --- a/src/days/day16/mod.rs +++ b/src/days/day16/mod.rs @@ -5,6 +5,8 @@ use std::iter::Sum; use std::ops::{Add, Deref, Mul, Sub}; use std::{collections::BinaryHeap, num::ParseIntError}; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; use const_format::concatcp; use itertools::Itertools; @@ -21,13 +23,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { let system = ValveSystem::build(lines, "AA")?; let result = system.maximum_flow(system.single_actor(Time(30)))?; Ok(ResultType::Integer(*result)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { let system = ValveSystem::build(lines, "AA")?; let result = system.maximum_flow(system.double_actor(Time(26)))?; Ok(ResultType::Integer(*result)) @@ -264,10 +266,9 @@ struct ValveSystem { } impl ValveSystem { - fn build(lines: &[String], start: &str) -> Result { - let mut raw = lines - .iter() - .map(|line| RawValve::try_from(line.as_str())) + fn build(lines: &str, start: &str) -> Result { + let mut raw = split_lines(lines) + .map(|line| RawValve::try_from(line)) .collect::, _>>()?; raw.sort_unstable_by_key(|valve| valve.id); @@ -912,13 +913,13 @@ impl Hash for HashableState { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(1651); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -949,7 +950,7 @@ mod test { #[test] fn parse_system() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; assert_eq!(10, system.len()); @@ -966,7 +967,7 @@ mod test { #[test] fn single_state_init() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.single_actor(Time(5)); @@ -980,7 +981,7 @@ mod test { #[test] fn next_single_actor_states() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.single_actor(Time(5)); let state = ValveState::init(&system, actor); @@ -1002,7 +1003,7 @@ mod test { #[test] fn next_single_states() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.single_actor(Time(6)); let state = ValveState::init(&system, actor); @@ -1023,7 +1024,7 @@ mod test { #[test] fn double_state_init() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.double_actor(Time(5)); let state = ValveState::init(&system, actor); @@ -1037,7 +1038,7 @@ mod test { #[test] fn next_double_actor_states() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.double_actor(Time(5)); let state = ValveState::init(&system, actor); @@ -1068,7 +1069,7 @@ mod test { #[test] fn next_double_states() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = system.double_actor(Time(5)); let state = ValveState::init(&system, actor); @@ -1090,7 +1091,7 @@ mod test { #[test] fn special_double_stats() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = DoubleActor(DoubleWork::Simultaneous(Index(1), Index(7), Time(19))); let state = ValveState { @@ -1132,7 +1133,7 @@ mod test { #[test] fn double_long_run() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let system = ValveSystem::build(&lines, "AA")?; let actor = DoubleActor(DoubleWork::Sequential(Index(3), Time(5), Index(7), Time(2))); let state = ValveState { @@ -1158,7 +1159,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(1707); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day17/mod.rs b/src/days/day17/mod.rs index 07f954a..b64a7d6 100644 --- a/src/days/day17/mod.rs +++ b/src/days/day17/mod.rs @@ -1,6 +1,6 @@ use std::{cell::Cell, marker::PhantomData, ops::Index}; -use crate::common::file::read_lines; +use crate::common::file::{read_string, split_lines}; use super::template::{DayTrait, ResultType}; use itertools::Itertools; @@ -16,16 +16,28 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + fn part1(&self, lines: &str) -> anyhow::Result { + let pushes = Dispenser::new( + lines + .trim_end() + .chars() + .map(|c| Push::parse(c)) + .try_collect()?, + )?; let result = Day::run(pushes, 2022)?; Ok(ResultType::Integer(result as i64)) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + fn part2(&self, lines: &str) -> anyhow::Result { + let pushes = Dispenser::new( + lines + .trim_end() + .chars() + .map(|c| Push::parse(c)) + .try_collect()?, + )?; let result = Day::run(pushes, 1000000000000)?; @@ -35,8 +47,8 @@ impl DayTrait for Day { impl Day { fn run(push_cycle: Dispenser, max_cycles: i64) -> Result { - let raw = read_lines(DAY_NUMBER, "blocks.txt")?; - let rock_cycle = Dispenser::new(Rock::parse(&raw)?); + let raw = read_string(DAY_NUMBER, "blocks.txt")?; + let rock_cycle = Dispenser::new(Rock::parse(&raw)?)?; let mut cycle = 0; let mut stack = Stack::new(); @@ -114,6 +126,9 @@ enum RockError { #[error("Unknown direction: {0}")] UnknownDirection(char), + + #[error("A Dispenser must never be empty")] + EmptyDispenserNotAllowed, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -158,9 +173,8 @@ impl Rock { }) } - pub fn parse(lines: &[String]) -> Result, RockError> { - lines - .iter() + pub fn parse(lines: &str) -> Result, RockError> { + split_lines(lines) .batching(|it| { let blocks = it .skip_while(|line| line.is_empty()) @@ -330,11 +344,15 @@ where } impl<'a, T> Dispenser<'a, T> { - pub fn new(data: Vec) -> Self { - Dispenser { - data, - current: Cell::new(0), - _marker: PhantomData, + pub fn new(data: Vec) -> Result { + if data.is_empty() { + Err(RockError::EmptyDispenserNotAllowed) + } else { + Ok(Dispenser { + data, + current: Cell::new(0), + _marker: PhantomData, + }) } } @@ -351,13 +369,13 @@ impl<'a, T> Dispenser<'a, T> { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(3068); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -368,7 +386,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(1514285714288); let result = day.part2(&lines)?; assert_eq!(result, expected); @@ -379,7 +397,7 @@ mod test { #[test] fn read_blocks() -> Result<()> { let day = Day {}; - let raw = read_lines(day.get_day_number(), "blocks.txt")?; + let raw = read_string(day.get_day_number(), "blocks.txt")?; let rocks = Rock::parse(&raw)?; let expected = Rock::new(vec![vec![true]; 4])?; assert_eq!(rocks[3], expected); @@ -396,10 +414,16 @@ mod test { #[test] fn drop_one() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); - let raw = read_lines(day.get_day_number(), "blocks.txt")?; - let rocks = Dispenser::new(Rock::parse(&raw)?); + let lines = read_string(day.get_day_number(), "example01.txt")?; + let pushes = Dispenser::new( + lines + .trim_end() + .chars() + .map(|c| Push::parse(c)) + .try_collect()?, + )?; + let raw = read_string(day.get_day_number(), "blocks.txt")?; + let rocks = Dispenser::new(Rock::parse(&raw)?)?; let mut stack = Stack::new(); stack.one_rock(rocks.next(), &pushes); @@ -412,8 +436,14 @@ mod test { #[test] fn drop_some() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; - let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + let lines = read_string(day.get_day_number(), "example01.txt")?; + let pushes = Dispenser::new( + lines + .trim_end() + .chars() + .map(|c| Push::parse(c)) + .try_collect()?, + )?; let result = Day::run(pushes, 10)?; diff --git a/src/days/day18/mod.rs b/src/days/day18/mod.rs index 1885f3d..d5d339b 100644 --- a/src/days/day18/mod.rs +++ b/src/days/day18/mod.rs @@ -2,6 +2,8 @@ use itertools::Itertools; use std::{collections::HashSet, num::ParseIntError}; use thiserror::Error; +use crate::common::file::split_lines; + use super::template::{DayTrait, ResultType}; const DAY_NUMBER: usize = 18; @@ -13,13 +15,13 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, lines: &[String]) -> anyhow::Result { - let blob = Blob::parse(lines)?; + fn part1(&self, lines: &str) -> anyhow::Result { + let blob = Blob::try_from(lines)?; Ok(ResultType::Integer(blob.count_sides())) } - fn part2(&self, lines: &[String]) -> anyhow::Result { - let blob = Blob::parse(lines)?; + fn part2(&self, lines: &str) -> anyhow::Result { + let blob = Blob::try_from(lines)?; Ok(ResultType::Integer(blob.count_outside())) } } @@ -131,15 +133,18 @@ struct Blob { droplets: HashSet, } -impl Blob { - fn parse(lines: &[String]) -> Result { - let droplets = lines - .iter() +impl TryFrom<&str> for Blob { + type Error = DropletError; + + fn try_from(lines: &str) -> Result { + let droplets = split_lines(lines) .map(|line| Droplet::parse(line)) .try_collect()?; Ok(Blob { droplets }) } +} +impl Blob { fn extent(&self) -> Option { if self.droplets.is_empty() { return None; @@ -198,13 +203,13 @@ impl Blob { #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(64); let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -215,7 +220,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(58); let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/day__/mod.rs b/src/days/day__/mod.rs index e67b4e2..fe2759b 100644 --- a/src/days/day__/mod.rs +++ b/src/days/day__/mod.rs @@ -1,6 +1,7 @@ use super::template::{DayTrait, ResultType}; +use thiserror::Error; -const DAY_NUMBER: usize = 0; +const DAY_NUMBER: usize = todo!(); pub struct Day; @@ -9,25 +10,31 @@ impl DayTrait for Day { DAY_NUMBER } - fn part1(&self, _lines: &[String]) -> anyhow::Result { + fn part1(&self, lines: &str) -> anyhow::Result { Ok(ResultType::Nothing) } - fn part2(&self, _lines: &[String]) -> anyhow::Result { + fn part2(&self, lines: &str) -> anyhow::Result { Ok(ResultType::Nothing) } } +#[derive(Debug, Error)] +enum DayError { + #[error("Dummy")] + Dummy, +} + #[cfg(test)] mod test { use super::*; - use crate::common::file::read_lines; + use crate::common::file::read_string; use anyhow::Result; #[test] fn test_part1() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Nothing; let result = day.part1(&lines)?; assert_eq!(result, expected); @@ -38,7 +45,7 @@ mod test { #[test] fn test_part2() -> Result<()> { let day = Day {}; - let lines = read_lines(day.get_day_number(), "example01.txt")?; + let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Nothing; let result = day.part2(&lines)?; assert_eq!(result, expected); diff --git a/src/days/template.rs b/src/days/template.rs index 73a779d..fe84e9a 100644 --- a/src/days/template.rs +++ b/src/days/template.rs @@ -11,6 +11,6 @@ pub enum ResultType { pub trait DayTrait { fn get_day_number(&self) -> usize; - fn part1(&self, lines: &[String]) -> Result; - fn part2(&self, lines: &[String]) -> Result; + fn part1(&self, lines: &str) -> Result; + fn part2(&self, lines: &str) -> Result; } diff --git a/src/main.rs b/src/main.rs index 7e90532..07a8ec7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ mod days; mod macros; use anyhow::Result; -use common::file::read_lines; +use common::file::read_string; use days::{day_provider, DayTrait, ResultType}; use std::{ env, @@ -47,7 +47,7 @@ fn output(day: usize, part: usize, result: ResultType, time: Duration) { } } -fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &[String]) -> Result { +fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &str) -> Result { let now = Instant::now(); let result = if is_part1 { day.part1(lines)? @@ -70,7 +70,7 @@ fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &[String]) -> Result Result { - let lines = read_lines(day.get_day_number(), "input.txt")?; + let lines = read_string(day.get_day_number(), "input.txt")?; let elapsed1 = if part1 { run_part(day, true, &lines)? } else {