use super::template::{DayTrait, ResultType}; use crate::common::file::split_lines; use itertools::Itertools; use thiserror::Error; 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 { let sum = split_lines(lines) .map(find_double) .map_ok(priority) .flatten_ok() .fold_ok(0, |a, b| a + b)?; Ok(ResultType::Integer(sum)) } fn part2(&self, lines: &str) -> anyhow::Result { let sum = split_lines(lines) .chunks(3) .into_iter() .map(|chunk| find_badge(&chunk.collect_vec())) .map_ok(priority) .flatten_ok() .fold_ok(0, |a, b| a + b)?; Ok(ResultType::Integer(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 { 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 { 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(elves: &[&str]) -> Result { 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_string; 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_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(157); let result = day.part1(&lines)?; assert_eq!(result, expected); Ok(()) } #[test] fn test_part2() -> Result<()> { let day = Day {}; let lines = read_string(day.get_day_number(), "example01.txt")?; let expected = ResultType::Integer(70); let result = day.part2(&lines)?; assert_eq!(result, expected); Ok(()) } }