-
Notifications
You must be signed in to change notification settings - Fork 0
/
system.rs
128 lines (113 loc) · 4.26 KB
/
system.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::part::Unit;
use crate::workflow::Workflow;
use crate::{
part::Part,
rule::{Action, Rule},
};
use std::ops::Range;
use std::{collections::HashMap, rc::Rc, str::FromStr};
pub(crate) struct SortingSystem {
map: HashMap<Rc<str>, Workflow>
}
impl SortingSystem {
pub(crate) fn process_part(&self, part: Part, workflow: &str) -> Option<Action> {
// If a part is sent to another workflow, it immediately switches to the start of that workflow instead and never returns.
// If a part is accepted (sent to A) or rejected (sent to R), the part immediately stops any further processing.
let mut wf = self
.map
.get(workflow)
.expect("SortingSystem::process() - Starting workflow unknown!!");
while let Some(Action::WorkFlow(next)) = wf.validate(part) {
wf = self
.map
.get(&next)
.expect("SortingSystem::process() - redirected to non-existent Workflow");
}
wf.validate(part)
}
pub(crate) fn total_combinations(&self, wf: &str, rngs: &[Range<Unit>; 4]) -> Unit {
let mut residual = rngs.clone();
self.map
.get(wf)
.expect("System::total_combinations - Workflow name doesn't exist")
.iter()
.map(|rule| {
// current ranges becomes the target
let mut target = residual.clone();
// Process rule into "Action" & "target" part ranges
match rule {
// Process Conditional rule into "target" and "remaining" ranges
Rule::ConAct(c, a) => {
let part = c.part() as usize;
// partition part range and update "target" and "remaining" accordingly
(target[part], residual[part]) = c.partition(&residual[part]);
(a, target)
},
// Pass-through action and target part ranges
Rule::Act(a) => (a, target),
}
})
.map(|(a,target)|{
// Process Action given "target" part ranges
match a {
Action::WorkFlow(next_wf) => self
.total_combinations(next_wf, &target),
Action::Accept => target
.iter()
.map(|r| r.len() as Unit)
.product(),
Action::Reject => 0,
}
})
.sum::<Unit>()
}
}
impl FromStr for SortingSystem {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut map: HashMap<Rc<str>, Workflow> = HashMap::new();
for line in s.lines() {
let wf = line.parse::<Workflow>()?;
map.insert(wf.key(), wf);
}
Ok(SortingSystem { map })
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::parse_puzzle_data;
#[test]
fn test_sortingsystem_combinations() {
let (_, wfs) = parse_puzzle_data("src/bin/day19/sample.txt");
let sum = wfs.total_combinations("in", &[1..4001, 1..4001, 1..4001, 1..4001]);
println!("Total combinations: {sum}");
// assert_eq!(sum,132_753_196_000_000);
assert_eq!(sum,167_409_079_868_000);
}
#[test]
fn test_sortingsystem_process() {
let (part, wfs) = parse_puzzle_data("src/bin/day19/sample.txt");
let sum = part
.iter()
.filter(|&&part| wfs.process_part(part, "in") == Some(Action::Accept))
.map(|p| p.sum())
.sum::<Unit>();
assert_eq!(sum, 19114);
}
#[test]
fn test_sortingsystem_parse() {
let inp =
std::fs::read_to_string("src/bin/day19/sample.txt").expect("cannot load sample.txt");
let wfs = inp.split("\n\n").next().unwrap();
let sorting = wfs
.parse::<SortingSystem>()
.expect("Failed to parse workflow");
wfs.lines().for_each(|line| {
let wf = line.parse::<Workflow>().expect("msg");
let found = sorting.map.get(&wf.key());
println!("{:?}", found);
assert_eq!(format!("{:?}", found), format!("{:?}", Some(wf)));
});
}
}