advent-2022-rust/src/days/day16/mod.rs
2023-07-30 16:42:17 +02:00

1177 lines
35 KiB
Rust

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<ResultType> {
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<ResultType> {
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<Flow> 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<I: Iterator<Item = Self>>(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<Err<Error<&str>>> for ValveError {
fn from(error: Err<Error<&str>>) -> 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<Self, Self::Error> {
Ok(extract_result(RawValve::parse)(value)?)
}
}
#[derive(Debug)]
struct Valve {
id: String,
idx: Index,
flow: Flow,
distances: Vec<Time>,
}
impl Valve {
fn find_neighbors<'a>(
others: &'a [RawValve],
id: &str,
) -> Result<&'a Vec<&'a str>, ValveError> {
others
.iter()
.find(|valve| valve.id == id)
.map(|valve| &valve.neighbors)
.ok_or_else(|| ValveError::ValveNotFound(id.to_owned()))
}
fn create(valve: &RawValve, idx: Index, others: &[RawValve]) -> Result<Valve, ValveError> {
let mut distances = Vec::with_capacity(others.len());
distances.push((valve.id, Time(0)));
let mut check_idx = 0;
while check_idx < distances.len() {
let (id, dist) = &distances[check_idx];
let mut new_items = Valve::find_neighbors(others, id)?
.iter()
.filter(|neighbor| distances.iter().all(|(other, _)| other != *neighbor))
.map(|id| (*id, dist.inc()))
.collect_vec();
distances.append(&mut new_items);
check_idx += 1;
}
distances.sort_unstable_by_key(|(id, _)| *id);
Ok(Valve {
id: valve.id.to_owned(),
idx,
flow: valve.flow,
distances: distances
.iter()
.map(|(_, dist)| if *dist > Time(0) { dist.inc() } else { *dist })
.collect_vec(),
})
}
fn zero_flow(valve: &RawValve, idx: Index) -> Valve {
Valve {
id: valve.id.to_owned(),
idx,
flow: valve.flow,
distances: vec![],
}
}
pub fn steps_to(&self, idx: Index) -> Time {
self.distances[*idx]
}
}
#[derive(Debug)]
struct ValveSystem {
valves: Vec<Valve>,
start_idx: Index,
}
impl ValveSystem {
fn build(lines: &str, start: &str) -> Result<ValveSystem, ValveError> {
let mut raw = extract_result(many1(RawValve::parse))(lines)?;
raw.sort_unstable_by_key(|valve| valve.id);
let start_idx = raw
.iter()
.enumerate()
.find(|(_, valve)| start == valve.id)
.map(|(idx, _)| Index(idx))
.ok_or_else(|| ValveError::ValveNotFound(start.to_owned()))?;
raw.iter()
.enumerate()
.map(|(idx, valve)| {
let idx = Index(idx);
Ok::<_, ValveError>(if idx == start_idx || valve.flow.is_positive() {
Valve::create(valve, idx, &raw)?
} else {
Valve::zero_flow(valve, idx)
})
})
.try_collect()
.map(|valves| ValveSystem { valves, start_idx })
}
pub fn get_closed_valves(&self) -> Vec<Index> {
self.valves
.iter()
.filter_map(|valve| valve.flow.is_positive().then_some(valve.idx))
.collect_vec()
}
#[allow(dead_code)]
fn get_valve_by_id(&self, id: &str) -> Result<&Valve, ValveError> {
self.valves
.iter()
.find(|valve| valve.id == id)
.ok_or(ValveError::ValveNotFound(id.to_owned()))
}
fn get_valve(&self, idx: Index) -> &Valve {
&self.valves[*idx]
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.valves.len()
}
pub fn single_actor(&self, time: Time) -> SingleActor {
SingleActor::new(self, time)
}
pub fn double_actor(&self, time: Time) -> DoubleActor {
DoubleActor::new(self, time)
}
pub fn maximum_flow<AG: ActorGroup>(&self, actor: AG) -> Result<Flow, ValveError> {
let start = ValveState::init(self, actor);
let mut queue = BinaryHeap::new();
queue.push(WeightedState(start));
let mut known = HashMap::new();
while let Some(state) = queue.pop() {
if state.is_finished() {
return Ok(state.flow);
}
let hashed = HashableState::new(&state);
if let Some(value) = known.get(&hashed) {
if *value >= state.flow {
continue;
}
}
known.insert(hashed, state.flow);
for next in state.calc_next_states(self) {
queue.push(WeightedState(next));
}
}
Err(ValveError::NoOptimumFound)
}
}
trait ActorGroup: Sized + Debug + Display + Clone + Eq + Hash {
fn is_finished(&self) -> bool;
fn working_on(&self) -> Vec<Index>;
fn get_now_time(&self) -> Option<Time>;
fn opened_now(&self) -> Vec<Index>;
fn opened_later(&self) -> Vec<Index>;
fn added_flow_now(&self, system: &ValveSystem) -> Flow;
fn flow_fixed(&self, system: &ValveSystem) -> Flow;
fn potential_flow(&self, closed: &[Index], system: &ValveSystem) -> Flow;
fn next_moves(&self, closed: &[Index], system: &ValveSystem) -> Vec<Self>;
fn show(&self, system: &ValveSystem) -> String;
fn calc_next_moves(&self, state: &ValveState<Self>, system: &ValveSystem) -> Vec<Self> {
self.next_moves(&state.closed, system)
.into_iter()
.unique()
.collect_vec()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Work {
AtWork(Index, Time),
Finished,
}
impl Work {
fn build(idx: Index, time: Time, steps: Time) -> Work {
if time > steps {
Work::AtWork(idx, time - steps)
} else {
Work::Finished
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct SingleActor(Work);
impl SingleActor {
fn new(system: &ValveSystem, time: Time) -> SingleActor {
SingleActor(Work::AtWork(system.start_idx, time))
}
}
impl Display for SingleActor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Work::AtWork(idx, time) => write!(f, "({:?}/{:?}", time, idx),
Work::Finished => write!(f, "!"),
}
}
}
impl ActorGroup for SingleActor {
fn is_finished(&self) -> bool {
matches!(self.0, Work::Finished)
}
fn show(&self, system: &ValveSystem) -> String {
match self.0 {
Work::AtWork(idx, _) => format!("{}", system.get_valve(idx).id),
Work::Finished => String::from("!"),
}
}
fn next_moves(&self, closed: &[Index], system: &ValveSystem) -> Vec<Self> {
let Work::AtWork(from_idx, time) = self.0 else {
return vec![SingleActor(Work::Finished)];
};
if closed.is_empty() {
return vec![SingleActor(Work::Finished)];
}
let location = system.get_valve(from_idx);
closed
.iter()
.map(move |to_idx| {
let steps = location.steps_to(*to_idx);
if time > steps {
SingleActor(Work::AtWork(*to_idx, time - steps))
} else {
SingleActor(Work::Finished)
}
})
.collect_vec()
}
fn working_on(&self) -> Vec<Index> {
match self.0 {
Work::AtWork(idx, _) => vec![idx],
Work::Finished => vec![],
}
}
fn opened_now(&self) -> Vec<Index> {
match self.0 {
Work::AtWork(idx, _) => vec![idx],
Work::Finished => vec![],
}
}
fn opened_later(&self) -> Vec<Index> {
vec![]
}
fn added_flow_now(&self, system: &ValveSystem) -> Flow {
match self.0 {
Work::AtWork(idx, time) => time * system.get_valve(idx).flow,
Work::Finished => Flow(0),
}
}
fn get_now_time(&self) -> Option<Time> {
match self.0 {
Work::AtWork(_, time) => Some(time),
Work::Finished => None,
}
}
fn flow_fixed(&self, _system: &ValveSystem) -> Flow {
Flow(0)
}
fn potential_flow(&self, closed: &[Index], system: &ValveSystem) -> Flow {
self.next_moves(closed, system)
.into_iter()
.filter_map(|work| match work {
SingleActor(Work::AtWork(idx, time)) => Some((idx, time)),
SingleActor(Work::Finished) => None,
})
.map(|(to_idx, time)| time * system.get_valve(to_idx).flow)
.sum()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum DoubleWork {
Simultaneous(Index, Index, Time),
Sequential(Index, Time, Index, Time),
OnlyOne(Index, Time),
Finished,
}
impl DoubleWork {
fn build(work0: Work, work1: Work) -> DoubleWork {
match (work0, work1) {
(Work::Finished, Work::Finished) => DoubleWork::Finished,
(Work::Finished, Work::AtWork(idx, time))
| (Work::AtWork(idx, time), Work::Finished) => DoubleWork::OnlyOne(idx, time),
(Work::AtWork(idx0, time0), Work::AtWork(idx1, time1)) if time0 == time1 => {
DoubleWork::Simultaneous(idx0.min(idx1), idx0.max(idx1), time0)
}
(Work::AtWork(idx0, time0), Work::AtWork(idx1, time1)) => {
if time0 > time1 {
DoubleWork::Sequential(idx0, time0, idx1, time1)
} else {
DoubleWork::Sequential(idx1, time1, idx0, time0)
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct DoubleActor(DoubleWork);
impl Display for DoubleActor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
DoubleWork::Simultaneous(idx0, idx1, time) => {
write!(f, "[{:?}/{:?}+{:?}]", time, idx0, idx1)
}
DoubleWork::Sequential(idx0, time0, idx1, _) => {
write!(f, "[{:?}/{:?}>{:?}]", time0, idx0, idx1)
}
DoubleWork::OnlyOne(time0, idx0) => {
write!(f, "({:?}/{:?})", time0, idx0)
}
DoubleWork::Finished => {
write!(f, "!")
}
}
}
}
impl DoubleActor {
fn new(system: &ValveSystem, time: Time) -> DoubleActor {
DoubleActor(DoubleWork::Simultaneous(
system.start_idx,
system.start_idx,
time,
))
}
}
impl ActorGroup for DoubleActor {
fn is_finished(&self) -> bool {
matches!(self.0, DoubleWork::Finished)
}
fn show(&self, system: &ValveSystem) -> String {
match self.0 {
DoubleWork::Simultaneous(idx1, idx2, _) => {
let name1 = &system.get_valve(idx1).id;
let name2 = &system.get_valve(idx2).id;
format!("({}/{})", name1, name2)
}
DoubleWork::Sequential(idx1, _, idx2, _) => {
let name1 = &system.get_valve(idx1).id;
let name2 = &system.get_valve(idx2).id;
format!("({}-{})", name1, name2)
}
DoubleWork::OnlyOne(idx, _) => {
let name = &system.get_valve(idx).id;
format!("({})", name)
}
DoubleWork::Finished => String::from("!"),
}
}
fn next_moves(&self, closed: &[Index], system: &ValveSystem) -> Vec<Self> {
match self.0 {
DoubleWork::Finished => vec![DoubleActor(DoubleWork::Finished)],
DoubleWork::OnlyOne(from_idx, time) => {
if closed.is_empty() {
vec![DoubleActor(DoubleWork::Finished)]
} else {
let location = system.get_valve(from_idx);
closed
.iter()
.map(|to_idx| {
let steps = location.steps_to(*to_idx);
if time > steps {
DoubleActor(DoubleWork::OnlyOne(*to_idx, time - steps))
} else {
DoubleActor(DoubleWork::Finished)
}
})
.collect_vec()
}
}
DoubleWork::Sequential(idx0, time0, idx1, time1) => {
let location0 = system.get_valve(idx0);
let result = closed
.iter()
.filter(|to_idx| **to_idx != idx1)
.map(|to_idx| {
let steps0 = location0.steps_to(*to_idx);
DoubleActor(DoubleWork::build(
Work::AtWork(idx1, time1),
Work::build(*to_idx, time0, steps0),
))
})
.collect_vec();
if result.is_empty() {
vec![DoubleActor(DoubleWork::OnlyOne(idx1, time1))]
} else {
result
}
}
DoubleWork::Simultaneous(idx0, idx1, time) => match closed.len() {
0 => vec![DoubleActor(DoubleWork::Finished)],
1 => [idx0, idx1]
.into_iter()
.map(|from_idx| {
let steps = system.get_valve(from_idx).steps_to(closed[0]);
DoubleActor(DoubleWork::build(
Work::build(closed[0], time, steps),
Work::Finished,
))
})
.collect_vec(),
_ => {
let location0 = system.get_valve(idx0);
let location1 = system.get_valve(idx1);
closed
.iter()
.permutations(2)
.map(move |to_idx| {
let steps0 = location0.steps_to(*to_idx[0]);
let steps1 = location1.steps_to(*to_idx[1]);
DoubleActor(DoubleWork::build(
Work::build(*to_idx[0], time, steps0),
Work::build(*to_idx[1], time, steps1),
))
})
.collect_vec()
}
},
}
}
fn working_on(&self) -> Vec<Index> {
match self.0 {
DoubleWork::Sequential(idx0, _, idx1, _) | DoubleWork::Simultaneous(idx0, idx1, _) => {
vec![idx0, idx1]
}
DoubleWork::OnlyOne(idx, _) => {
vec![idx]
}
DoubleWork::Finished => vec![],
}
}
fn opened_now(&self) -> Vec<Index> {
match self.0 {
DoubleWork::Simultaneous(idx0, idx1, _) => vec![idx0, idx1],
DoubleWork::Sequential(idx, _, _, _) | DoubleWork::OnlyOne(idx, _) => {
vec![idx]
}
DoubleWork::Finished => vec![],
}
}
fn opened_later(&self) -> Vec<Index> {
match self.0 {
DoubleWork::Sequential(_, _, idx, _) => vec![idx],
_ => vec![],
}
}
fn added_flow_now(&self, system: &ValveSystem) -> Flow {
match self.0 {
DoubleWork::Simultaneous(idx0, idx1, time) => {
time * (system.get_valve(idx0).flow + system.get_valve(idx1).flow)
}
DoubleWork::Sequential(idx, time, _, _) | DoubleWork::OnlyOne(idx, time) => {
time * system.get_valve(idx).flow
}
DoubleWork::Finished => Flow(0),
}
}
fn flow_fixed(&self, system: &ValveSystem) -> Flow {
match self.0 {
DoubleWork::Sequential(_, _, idx, time) => time * system.get_valve(idx).flow,
DoubleWork::OnlyOne(_, _)
| DoubleWork::Simultaneous(_, _, _)
| DoubleWork::Finished => Flow(0),
}
}
fn get_now_time(&self) -> Option<Time> {
match self.0 {
DoubleWork::Simultaneous(_, _, time)
| DoubleWork::Sequential(_, time, _, _)
| DoubleWork::OnlyOne(_, time) => Some(time),
DoubleWork::Finished => None,
}
}
fn potential_flow(&self, closed: &[Index], system: &ValveSystem) -> Flow {
match self.0 {
DoubleWork::Simultaneous(idx0, idx1, time) => {
let location0 = system.get_valve(idx0);
let location1 = system.get_valve(idx1);
closed
.iter()
.map(|to_idx| {
let steps0 = time.sub_to_zero(location0.steps_to(*to_idx));
let steps1 = time.sub_to_zero(location1.steps_to(*to_idx));
steps0.max(steps1) * system.get_valve(*to_idx).flow
})
.sum()
}
DoubleWork::Sequential(idx0, time0, idx1, time1) => {
let location0 = system.get_valve(idx0);
let location1 = system.get_valve(idx1);
closed
.iter()
.map(|to_idx| {
let steps0 = time0.sub_to_zero(location0.steps_to(*to_idx));
let steps1 = time1.sub_to_zero(location1.steps_to(*to_idx));
steps0.max(steps1) * system.get_valve(*to_idx).flow
})
.sum()
}
DoubleWork::OnlyOne(idx, time) => {
let location = system.get_valve(idx);
closed
.iter()
.map(|to_idx| {
let steps = time.sub_to_zero(location.steps_to(*to_idx));
steps * system.get_valve(*to_idx).flow
})
.sum()
}
DoubleWork::Finished => Flow(0),
}
}
}
#[derive(Debug)]
struct ValveState<AG: ActorGroup> {
closed: Vec<Index>,
time: Option<Time>,
flow: Flow,
potential: Flow,
actor: AG,
}
impl<AG: ActorGroup> ValveState<AG> {
#[inline]
pub fn flow_potential(&self) -> Flow {
self.flow + self.potential
}
#[allow(dead_code)]
pub fn make_history(path: &[(AG, Flow)], finished: bool, system: &ValveSystem) -> String {
let h = path
.iter()
.map(|(actor, _)| actor.show(system))
.join(" -> ");
if finished {
format!("{} -> !", h)
} else {
h
}
}
pub fn calc_next_states(&self, system: &ValveSystem) -> Vec<ValveState<AG>> {
self.actor
.calc_next_moves(self, system)
.into_iter()
.map(|actor| self.derive_state(actor, system))
.collect_vec()
}
fn derive_state(&self, actor: AG, system: &ValveSystem) -> ValveState<AG> {
if !actor.is_finished() {
let opened = actor.opened_now();
let closed = self
.closed
.iter()
.filter(|idx| !opened.contains(*idx))
.copied()
.collect_vec();
let added_flow = actor.added_flow_now(system);
let potential = actor.potential_flow(&closed, system);
ValveState {
closed,
time: self.actor.get_now_time(),
flow: self.flow + added_flow,
potential,
actor,
}
} else {
ValveState {
closed: vec![],
time: None,
flow: self.flow,
potential: Flow(0),
actor,
}
}
}
}
impl<AG: ActorGroup> ValveState<AG> {
pub fn is_finished(&self) -> bool {
self.time.is_none()
}
pub fn init(system: &ValveSystem, actor: AG) -> ValveState<AG> {
let closed = system.get_closed_valves();
let potential = actor.potential_flow(&closed, system);
ValveState {
closed,
time: actor.get_now_time(),
flow: Flow(0),
potential,
actor,
}
}
}
#[derive(Debug)]
struct WeightedState<AG: ActorGroup>(ValveState<AG>);
impl<AG: ActorGroup> PartialEq for WeightedState<AG> {
fn eq(&self, other: &Self) -> bool {
self.time == other.time && self.flow + self.flow_potential() == other.flow_potential()
}
}
impl<AG: ActorGroup> Eq for WeightedState<AG> {}
impl<AG: ActorGroup> PartialOrd for WeightedState<AG> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<AG: ActorGroup> Ord for WeightedState<AG> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.flow_potential()
.cmp(&other.flow_potential())
.then_with(|| self.time.cmp(&other.time))
}
}
impl<AG: ActorGroup> Deref for WeightedState<AG> {
type Target = ValveState<AG>;
fn deref<'b>(&'b self) -> &'b Self::Target {
&self.0
}
}
struct HashableState(Vec<Index>, Vec<Index>);
impl HashableState {
fn new<AG: ActorGroup>(state: &ValveState<AG>) -> Self {
HashableState(state.closed.clone(), state.actor.opened_later().clone())
}
}
impl PartialEq for HashableState {
fn eq(&self, other: &Self) -> bool {
if self.0.len() != other.0.len() || self.1.len() != other.1.len() {
return false;
}
for (c1, c2) in self.0.iter().zip(other.0.iter()) {
if *c1 != *c2 {
return false;
}
}
for (o1, o2) in self.1.iter().zip(other.1.iter()) {
if *o1 != *o2 {
return false;
}
}
true
}
}
impl Eq for HashableState {}
impl Hash for HashableState {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
for c in &self.0 {
c.hash(hasher);
}
if !self.1.is_empty() {
"!".hash(hasher);
for o in &self.1 {
o.hash(hasher);
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::common::file::read_string;
use anyhow::Result;
#[test]
fn test_part1() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let expected = ResultType::Integer(1651);
let result = day.part1(&lines)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn parse_plural() -> Result<()> {
let line = "Valve BB has flow rate=13; tunnels lead to valves CC, AA";
let expected = RawValve::create("BB", Flow(13), vec!["CC", "AA"]);
let result = line.try_into()?;
assert_eq!(expected, result);
Ok(())
}
#[test]
fn parse_singular() -> Result<()> {
let line = "Valve HH has flow rate=22; tunnel leads to valve GG";
let expected = RawValve::create("HH", Flow(22), vec!["GG"]);
let result = line.try_into()?;
assert_eq!(expected, result);
Ok(())
}
#[test]
fn parse_system() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let system = ValveSystem::build(&lines, "AA")?;
assert_eq!(10, system.len());
let aa = system.get_valve_by_id("AA")?;
let ee = system.get_valve_by_id("EE")?;
assert_eq!(Time(0), aa.steps_to(aa.idx));
assert_eq!(Time(3), aa.steps_to(ee.idx));
Ok(())
}
#[test]
fn single_state_init() -> Result<()> {
let day = Day {};
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);
assert_eq!(state.potential, Flow(39 + 4 + 60 + 6 + 42));
assert_eq!(state.time, Some(Time(5)));
Ok(())
}
#[test]
fn next_single_actor_states() -> Result<()> {
let day = Day {};
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);
let next = state.actor.calc_next_moves(&state, &system);
let expected = vec![
SingleActor(Work::AtWork(Index(1), Time(3))),
SingleActor(Work::AtWork(Index(2), Time(2))),
SingleActor(Work::AtWork(Index(3), Time(3))),
SingleActor(Work::AtWork(Index(4), Time(2))),
SingleActor(Work::Finished),
SingleActor(Work::AtWork(Index(9), Time(2))),
];
assert_eq!(next, expected);
Ok(())
}
#[test]
fn next_single_states() -> Result<()> {
let day = Day {};
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);
let next = state.calc_next_states(&system);
assert_eq!(next.len(), 6);
let result = &next[3];
assert_eq!(result.flow, Flow(3 * 3));
assert_eq!(
result.closed,
vec![Index(1), Index(2), Index(3), Index(7), Index(9)]
);
assert_eq!(result.potential, Flow(20 * 1));
Ok(())
}
#[test]
fn double_state_init() -> Result<()> {
let day = Day {};
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);
assert_eq!(state.potential, Flow(39 + 4 + 60 + 6 + 42));
assert_eq!(state.time, Some(Time(5)));
Ok(())
}
#[test]
fn next_double_actor_states() -> Result<()> {
let day = Day {};
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);
let next = state.actor.calc_next_moves(&state, &system);
let expected = vec![
DoubleActor(DoubleWork::Sequential(Index(1), Time(3), Index(2), Time(2))),
DoubleActor(DoubleWork::Simultaneous(Index(1), Index(3), Time(3))),
DoubleActor(DoubleWork::Sequential(Index(1), Time(3), Index(4), Time(2))),
DoubleActor(DoubleWork::OnlyOne(Index(1), Time(3))),
DoubleActor(DoubleWork::Sequential(Index(1), Time(3), Index(9), Time(2))),
DoubleActor(DoubleWork::Sequential(Index(3), Time(3), Index(2), Time(2))),
DoubleActor(DoubleWork::Simultaneous(Index(2), Index(4), Time(2))),
DoubleActor(DoubleWork::OnlyOne(Index(2), Time(2))),
DoubleActor(DoubleWork::Simultaneous(Index(2), Index(9), Time(2))),
DoubleActor(DoubleWork::Sequential(Index(3), Time(3), Index(4), Time(2))),
DoubleActor(DoubleWork::OnlyOne(Index(3), Time(3))),
DoubleActor(DoubleWork::Sequential(Index(3), Time(3), Index(9), Time(2))),
DoubleActor(DoubleWork::OnlyOne(Index(4), Time(2))),
DoubleActor(DoubleWork::Simultaneous(Index(4), Index(9), Time(2))),
DoubleActor(DoubleWork::OnlyOne(Index(9), Time(2))),
];
assert_eq!(next, expected);
Ok(())
}
#[test]
fn next_double_states() -> Result<()> {
let day = Day {};
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);
let next = state.calc_next_states(&system);
assert_eq!(next.len(), 15);
let result = &next[4];
assert_eq!(result.flow, Flow(13 * 3));
assert_eq!(result.potential, Flow(21 * 2 + 2 * 1));
assert_eq!(result.flow_potential(), Flow(13 * 3 + 21 * 2 + 2 * 1));
assert_eq!(
result.closed,
vec![Index(2), Index(3), Index(4), Index(7), Index(9)]
);
Ok(())
}
#[test]
fn special_double_stats() -> Result<()> {
let day = Day {};
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 {
closed: vec![Index(2), Index(4)],
time: Some(Time(15)),
flow: Flow(1628),
potential: Flow(79),
actor,
};
let next = state.calc_next_states(&system);
assert_eq!(next.len(), 2);
assert_eq!(
next[0].actor,
DoubleActor(DoubleWork::Sequential(
Index(2),
Time(17),
Index(4),
Time(15)
))
);
assert_eq!(next[0].flow, Flow(1662));
assert_eq!(next[0].potential, Flow(45));
assert_eq!(
next[1].actor,
DoubleActor(DoubleWork::Sequential(
Index(4),
Time(15),
Index(2),
Time(13)
))
);
Ok(())
}
#[test]
fn double_long_run() -> Result<()> {
let day = Day {};
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 {
closed: vec![Index(1), Index(2), Index(4), Index(9)],
time: Some(Time(10)),
flow: Flow(100),
potential: Flow(79),
actor,
};
let next = state.actor.calc_next_moves(&state, &system);
let expected = vec![
DoubleActor(DoubleWork::Simultaneous(Index(1), Index(7), Time(2))),
DoubleActor(DoubleWork::Sequential(Index(2), Time(3), Index(7), Time(2))),
DoubleActor(DoubleWork::Sequential(Index(4), Time(3), Index(7), Time(2))),
DoubleActor(DoubleWork::Sequential(Index(7), Time(2), Index(9), Time(1))),
];
assert_eq!(next, expected);
Ok(())
}
#[test]
fn test_part2() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let expected = ResultType::Integer(1707);
let result = day.part2(&lines)?;
assert_eq!(result, expected);
Ok(())
}
}