use super::template::{DayTrait, ResultType}; use crate::common::parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1}; use itertools::Itertools; use nom::{ branch::alt, bytes::complete::tag, character::complete::{alpha1, i64}, error::Error, multi::{many1, separated_list1}, Err, IResult, }; use std::collections::BinaryHeap; use std::collections::HashMap; use std::fmt::{Debug, Display}; use std::hash::Hash; use std::iter::Sum; use std::ops::{Add, Deref, Mul, Sub}; use thiserror::Error; const DAY_NUMBER: usize = 16; pub struct Day; impl DayTrait for Day { fn get_day_number(&self) -> usize { DAY_NUMBER } 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: &str) -> anyhow::Result { let system = ValveSystem::build(lines, "AA")?; let result = system.maximum_flow(system.double_actor(Time(26)))?; Ok(ResultType::Integer(*result)) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] struct Time(usize); impl Time { #[inline] fn sub_to_zero(&self, other: Time) -> Time { if self.0 > other.0 { Time(self.0 - other.0) } else { Time(0) } } #[inline] fn inc(&self) -> Self { Self(self.0 + 1) } } impl Deref for Time { type Target = usize; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl Add for Time { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) } } impl Sub for Time { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self::Output { Self(self.0 - rhs.0) } } impl Mul for Time { type Output = Flow; fn mul(self, rhs: Flow) -> Self::Output { Flow(self.0 as i64 * *rhs) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] struct Flow(i64); impl Deref for Flow { type Target = i64; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl Add for Flow { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) } } impl Sum for Flow { fn sum>(iter: I) -> Self { iter.fold(Self(0), |a, b| a + b) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Index(usize); impl Deref for Index { type Target = usize; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(Debug, Error)] enum ValveError { #[error("Not a valid description: {0}")] ParsingError(String), #[error("Not a valid valve: {0}")] ValveNotFound(String), #[error("Did nt find optimum flow")] NoOptimumFound, } impl From>> for ValveError { fn from(error: Err>) -> Self { ValveError::ParsingError(error.to_string()) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] struct RawValve<'a> { id: &'a str, flow: Flow, neighbors: Vec<&'a str>, } impl<'a> RawValve<'a> { fn create(id: &'a str, flow: Flow, neighbors: Vec<&'a str>) -> Self { RawValve { id, flow, neighbors, } } fn parse_start(input: &str) -> IResult<&str, (&str, Flow)> { let input = ignore(tag("Valve"))(input)?; let (input, valve) = trim1(alpha1)(input)?; let input = ignore(tag("has flow rate="))(input)?; let (input, flow) = trim0(i64)(input)?; let input = ignore(tag(";"))(input)?; Ok((input, (valve, Flow(flow)))) } fn parse_singular(input: &str) -> IResult<&str, Vec<&str>> { let input = ignore(tag("tunnel leads to valve "))(input)?; let (input, valve) = alpha1(input)?; Ok((input, vec![valve])) } fn parse_plural(input: &str) -> IResult<&str, Vec<&str>> { let input = ignore(tag("tunnels lead to valves "))(input)?; let (input, valve) = separated_list1(tag(","), trim0(alpha1))(input)?; Ok((input, valve)) } fn parse(input: &str) -> IResult<&str, RawValve> { let (input, (id, flow)) = RawValve::parse_start(input)?; let (input, neighbors) = trim_left1(eol_terminated(alt(( RawValve::parse_plural, RawValve::parse_singular, ))))(input)?; Ok((input, RawValve::create(id, flow, neighbors))) } } impl<'a> TryFrom<&'a str> for RawValve<'a> { type Error = ValveError; fn try_from(value: &'a str) -> Result { Ok(extract_result(RawValve::parse)(value)?) } } #[derive(Debug)] struct Valve { id: String, idx: Index, flow: Flow, distances: Vec