generated from janezicmatej/aoc-template
solution: day 19
This commit is contained in:
parent
0000034077
commit
00000350d1
|
@ -0,0 +1,17 @@
|
|||
px{a<2006:qkq,m>2090:A,rfg}
|
||||
pv{a>1716:R,A}
|
||||
lnx{m>1548:A,A}
|
||||
rfg{s<537:gd,x>2440:R,A}
|
||||
qs{s>3448:A,lnx}
|
||||
qkq{x<1416:A,crn}
|
||||
crn{x>2662:A,R}
|
||||
in{s<1351:px,qqz}
|
||||
qqz{s>2770:qs,m<1801:hdj,R}
|
||||
gd{a>3333:R,R}
|
||||
hdj{m>838:A,pv}
|
||||
|
||||
{x=787,m=2655,a=1222,s=2876}
|
||||
{x=1679,m=44,a=2067,s=496}
|
||||
{x=2036,m=264,a=79,s=2244}
|
||||
{x=2461,m=1339,a=466,s=291}
|
||||
{x=2127,m=1623,a=2188,s=1013}
|
|
@ -0,0 +1,387 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Index, IndexMut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
enum Gear {
|
||||
X,
|
||||
M,
|
||||
A,
|
||||
S,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GearParseError;
|
||||
|
||||
impl TryFrom<&str> for Gear {
|
||||
type Error = GearParseError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
use Gear::*;
|
||||
Ok(match value {
|
||||
"x" => X,
|
||||
"m" => M,
|
||||
"a" => A,
|
||||
"s" => S,
|
||||
_ => return Err(GearParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
enum Comparator {
|
||||
Lt,
|
||||
Gt,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ComparatorParseError;
|
||||
|
||||
impl TryFrom<char> for Comparator {
|
||||
type Error = ComparatorParseError;
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
use Comparator::*;
|
||||
Ok(match value {
|
||||
'<' => Lt,
|
||||
'>' => Gt,
|
||||
_ => return Err(ComparatorParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||
enum Resolver {
|
||||
Accepted,
|
||||
Rejected,
|
||||
Delegated(String),
|
||||
}
|
||||
|
||||
impl From<&str> for Resolver {
|
||||
fn from(value: &str) -> Self {
|
||||
use Resolver::*;
|
||||
match value {
|
||||
"A" => Accepted,
|
||||
"R" => Rejected,
|
||||
x => Delegated(x.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
struct Workflow {
|
||||
workflows: Vec<WorkflowInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseWorkflowError;
|
||||
|
||||
impl TryFrom<&str> for Workflow {
|
||||
type Error = ParseWorkflowError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
workflows: value.split(',').filter_map(|x| x.try_into().ok()).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
enum WorkflowInner {
|
||||
Resolver(Resolver),
|
||||
Rule((Gear, Comparator, usize, Resolver)),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseWorkflowInnerError;
|
||||
|
||||
impl TryFrom<&str> for WorkflowInner {
|
||||
type Error = ParseWorkflowInnerError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
if !value.contains(':') {
|
||||
return Ok(WorkflowInner::Resolver(Resolver::from(value)));
|
||||
}
|
||||
|
||||
let (rest, resolver) = value.split_once(':').unwrap();
|
||||
let resolver = Resolver::from(resolver);
|
||||
|
||||
let (gear, number) = rest.split_once(|x| x == '<' || x == '>').unwrap();
|
||||
let gear = Gear::try_from(gear).map_err(|_| ParseWorkflowInnerError)?;
|
||||
let number = number.parse().map_err(|_| ParseWorkflowInnerError)?;
|
||||
|
||||
let comparator = if value.contains('<') { '<' } else { '>' };
|
||||
let comparator = Comparator::try_from(comparator).map_err(|_| ParseWorkflowInnerError)?;
|
||||
|
||||
Ok(WorkflowInner::Rule((gear, comparator, number, resolver)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Xmas {
|
||||
x: usize,
|
||||
m: usize,
|
||||
a: usize,
|
||||
s: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseXmasError;
|
||||
|
||||
impl FromStr for Xmas {
|
||||
type Err = ParseXmasError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.strip_prefix('{').ok_or(ParseXmasError)?;
|
||||
let s = s.strip_suffix('}').ok_or(ParseXmasError)?;
|
||||
let xmas: Vec<_> = s
|
||||
.split(',')
|
||||
.filter_map(|x| x.split_once('='))
|
||||
.map(|x| x.1)
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
x: xmas[0],
|
||||
m: xmas[1],
|
||||
a: xmas[2],
|
||||
s: xmas[3],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Gear> for Xmas {
|
||||
type Output = usize;
|
||||
fn index(&self, index: Gear) -> &Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &self.x,
|
||||
M => &self.m,
|
||||
A => &self.a,
|
||||
S => &self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Gear> for Xmas {
|
||||
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &mut self.x,
|
||||
M => &mut self.m,
|
||||
A => &mut self.a,
|
||||
S => &mut self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Xmas {
|
||||
fn sum(&self) -> usize {
|
||||
self.x + self.m + self.a + self.s
|
||||
}
|
||||
|
||||
fn apply(&self, workflow: &Workflow) -> Resolver {
|
||||
for w in workflow.workflows.iter() {
|
||||
match w {
|
||||
WorkflowInner::Resolver(x) => {
|
||||
return x.clone();
|
||||
}
|
||||
WorkflowInner::Rule((g, c, n, r)) => {
|
||||
let is_match = match c {
|
||||
Comparator::Gt => self[*g] > *n,
|
||||
Comparator::Lt => self[*g] < *n,
|
||||
};
|
||||
if is_match {
|
||||
return r.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct XmasRange {
|
||||
x: (usize, usize),
|
||||
m: (usize, usize),
|
||||
a: (usize, usize),
|
||||
s: (usize, usize),
|
||||
}
|
||||
|
||||
impl Index<Gear> for XmasRange {
|
||||
type Output = (usize, usize);
|
||||
fn index(&self, index: Gear) -> &Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &self.x,
|
||||
M => &self.m,
|
||||
A => &self.a,
|
||||
S => &self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Gear> for XmasRange {
|
||||
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &mut self.x,
|
||||
M => &mut self.m,
|
||||
A => &mut self.a,
|
||||
S => &mut self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XmasRange {
|
||||
fn size(&self) -> usize {
|
||||
(self.x.1 - self.x.0 + 1)
|
||||
* (self.m.1 - self.m.0 + 1)
|
||||
* (self.a.1 - self.a.0 + 1)
|
||||
* (self.s.1 - self.s.0 + 1)
|
||||
}
|
||||
|
||||
fn divide(self, workflow: &Workflow) -> Vec<(Self, Resolver)> {
|
||||
let mut processed = Vec::new();
|
||||
let mut rest = vec![self];
|
||||
|
||||
for w in workflow.workflows.iter() {
|
||||
let mut new_rest = Vec::new();
|
||||
|
||||
while let Some(xmas_range) = rest.pop() {
|
||||
match w {
|
||||
WorkflowInner::Resolver(r) => processed.push((xmas_range, r.clone())),
|
||||
WorkflowInner::Rule((g, c, n, r)) => {
|
||||
let compare = |x: usize, y: usize| match c {
|
||||
Comparator::Gt => x > y,
|
||||
Comparator::Lt => x < y,
|
||||
};
|
||||
|
||||
let mut min_ok = usize::MAX;
|
||||
let mut max_ok = usize::MIN;
|
||||
let mut min_e = usize::MAX;
|
||||
let mut max_e = usize::MIN;
|
||||
|
||||
for i in xmas_range[*g].0..=xmas_range[*g].1 {
|
||||
if compare(i, *n) {
|
||||
max_ok = if max_ok < i { i } else { max_ok };
|
||||
min_ok = if min_ok > i { i } else { min_ok };
|
||||
} else {
|
||||
max_e = if max_e < i { i } else { max_e };
|
||||
min_e = if min_e > i { i } else { min_e };
|
||||
}
|
||||
}
|
||||
|
||||
if min_e <= max_e {
|
||||
let mut r = xmas_range;
|
||||
r[*g] = (min_e, max_e);
|
||||
new_rest.push(r);
|
||||
}
|
||||
if min_ok <= max_ok {
|
||||
let mut p = xmas_range;
|
||||
p[*g] = (min_ok, max_ok);
|
||||
processed.push((p, r.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rest = new_rest;
|
||||
}
|
||||
|
||||
processed
|
||||
}
|
||||
}
|
||||
|
||||
fn build_map(input: &str) -> HashMap<&str, Workflow> {
|
||||
let mut map = HashMap::new();
|
||||
for (s, w) in input
|
||||
.split("\n\n")
|
||||
.next()
|
||||
.unwrap()
|
||||
.lines()
|
||||
.filter_map(|x| x.strip_suffix('}'))
|
||||
.filter_map(|x| x.split_once('{'))
|
||||
.filter_map(|(s, w)| Workflow::try_from(w).ok().map(|x| (s, x)))
|
||||
{
|
||||
map.insert(s, w);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let map = build_map(input);
|
||||
let xmas_vec: Vec<Xmas> = input
|
||||
.split("\n\n")
|
||||
.nth(1)?
|
||||
.lines()
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect();
|
||||
|
||||
let mut total = 0;
|
||||
|
||||
'outer: for xmas in xmas_vec.into_iter() {
|
||||
let mut workflow = &map["in"];
|
||||
'apply: loop {
|
||||
match xmas.apply(workflow) {
|
||||
Resolver::Accepted => break 'apply,
|
||||
Resolver::Rejected => continue 'outer,
|
||||
Resolver::Delegated(x) => workflow = &map[x.as_str()],
|
||||
}
|
||||
}
|
||||
|
||||
total += xmas.sum();
|
||||
}
|
||||
|
||||
Some(total)
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let map = build_map(input);
|
||||
|
||||
let mut finished = Vec::new();
|
||||
let mut stack = vec![(
|
||||
XmasRange {
|
||||
x: (1, 4000),
|
||||
m: (1, 4000),
|
||||
a: (1, 4000),
|
||||
s: (1, 4000),
|
||||
},
|
||||
Resolver::Delegated("in".to_string()),
|
||||
)];
|
||||
|
||||
while let Some((xmas_range, resolver)) = stack.pop() {
|
||||
match resolver {
|
||||
Resolver::Accepted => finished.push(xmas_range),
|
||||
Resolver::Rejected => {
|
||||
continue;
|
||||
}
|
||||
Resolver::Delegated(x) => {
|
||||
let workflow = &map[x.as_str()];
|
||||
stack.append(&mut xmas_range.divide(workflow))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(finished.iter().map(XmasRange::size).sum())
|
||||
}
|
||||
aoc::solution!(19);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 19)),
|
||||
Some(19114)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 19)),
|
||||
Some(167409079868000)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue