128 lines
3.1 KiB
Rust
128 lines
3.1 KiB
Rust
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<ResultType> {
|
|
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<ResultType> {
|
|
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<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(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_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(())
|
|
}
|
|
}
|