From 506eff857eea3a5064c07112f23a31beda9db2ba Mon Sep 17 00:00:00 2001 From: kageru Date: Sat, 19 Dec 2020 14:00:03 +0100 Subject: [PATCH] Generify day 19 over all pattern vectors --- 2020/src/bin/day19.rs | 82 +++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/2020/src/bin/day19.rs b/2020/src/bin/day19.rs index 146e089..14c613c 100644 --- a/2020/src/bin/day19.rs +++ b/2020/src/bin/day19.rs @@ -3,18 +3,14 @@ extern crate test; use aoc2020::common::*; use itertools::Itertools; -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone)] enum Rule { Char(u8), - And((usize, usize)), - AndOr((usize, usize), (usize, usize)), - Or((usize, usize)), + And(Vec), + AndOr(Vec, Vec), + Any(Vec), Useless(usize), // just delegates to another rule and makes parsing annoying - // This is why you don’t change the requirements once the implementation is done. REEEEEE - Special8(usize, (usize, usize)), - Special11((usize, usize), (usize, usize, usize)), - // If Rule was a recursive struct, this could have been defined as And(first, And(second, third)), but alas - Triple((usize, usize, usize)), + Either(Vec, Vec), } type Parsed<'a> = (RuleSet, &'a str); @@ -23,40 +19,38 @@ type Parsed<'a> = (RuleSet, &'a str); struct RuleSet(Vec); impl RuleSet { + fn and_combinations(&self, s: &str, rules: &[usize], start: usize) -> Vec { + if let Some(&rule) = rules.first() { + if s.len() <= start { + return vec![]; + } + self.matches(&s[start..], self.0[rule].clone()) + .into_iter() + .flat_map(|i| self.and_combinations(s, &rules[1..], start + i).into_iter()) + .collect() + } else { + vec![start] + } + } + fn matches(&self, s: &str, rule: Rule) -> Vec { if s.len() == 0 { return vec![]; } match rule { Rule::Char(c) => (s.as_bytes().first() == Some(&c)).then(|| vec![1]).unwrap_or_else(Vec::new), - Rule::And((r1, r2)) => self - .matches(s, self.0[r1]) + Rule::And(v) => self.and_combinations(s, v.as_slice(), 0), + Rule::AndOr(v1, v2) => self + .matches(s, Rule::And(v1)) .into_iter() - .flat_map(|i| self.matches(&s[i..], self.0[r2]).into_iter().map(move |j| i + j)) + .chain(self.matches(s, Rule::And(v2))) .collect(), - Rule::AndOr((r1, r2), (r3, r4)) => self - .matches(s, Rule::And((r1, r2))) - .into_iter() - .chain(self.matches(s, Rule::And((r3, r4)))) - .collect(), - Rule::Or((r1, r2)) => self.matches(s, self.0[r1]).into_iter().chain(self.matches(s, self.0[r2])).collect(), - Rule::Useless(r) => self.matches(s, self.0[r]), - // part 2 shit below: - Rule::Special8(first, second) => self - .matches(s, self.0[first]) - .into_iter() - .chain(self.matches(s, Rule::And(second))) - .collect(), - Rule::Special11(first, second) => self + Rule::Any(v) => v.into_iter().flat_map(|r| self.matches(s, self.0[r].clone())).collect(), + Rule::Useless(r) => self.matches(s, self.0[r].clone()), + Rule::Either(first, second) => self .matches(s, Rule::And(first)) .into_iter() - .chain(self.matches(s, Rule::Triple(second))) - .collect(), - Rule::Triple((r1, r2, r3)) => self - .matches(s, self.0[r1]) - .into_iter() - .flat_map(|i| self.matches(&s[i..], self.0[r2]).into_iter().map(move |j| i + j)) - .flat_map(|i| self.matches(&s[i..], self.0[r3]).into_iter().map(move |j| i + j)) + .chain(self.matches(s, Rule::And(second))) .collect(), } } @@ -80,12 +74,12 @@ fn parse_input<'a>(raw: &'a str) -> Parsed<'a> { split .clone() .next_tuple() - .map(|(a, b, c, d)| Rule::AndOr((a, b), (c, d))) - .or_else(|| split.next_tuple().map(|(a, b)| Rule::Or((a, b)))) + .map(|(a, b, c, d)| Rule::AndOr(vec![a, b], vec![c, d])) + .or_else(|| split.next_tuple().map(|(a, b)| Rule::Any(vec![a, b]))) .unwrap() } chr if chr.contains('"') => Rule::Char(chr.bytes().skip_while(|&b| b != b'"').skip(1).next().unwrap()), - and if and.contains(' ') => Rule::And(and.split(' ').map(|s| s.parse().unwrap()).next_tuple().unwrap()), + and if and.contains(' ') => Rule::And(and.split(' ').map(|s| s.parse().unwrap()).collect()), useless => Rule::Useless(useless.parse().unwrap()), }) .collect(), @@ -94,15 +88,21 @@ fn parse_input<'a>(raw: &'a str) -> Parsed<'a> { } fn part1((rules, input): &Parsed) -> usize { - input.lines().filter(|l| rules.matches(l, rules.0[0]).contains(&l.len())).count() + input + .lines() + .filter(|l| rules.matches(l, rules.0[0].clone()).contains(&l.len())) + .count() } fn part2(parsed: &Parsed) -> usize { let mut rules = parsed.0.clone(); - rules.0[8] = Rule::Special8(42, (42, 8)); - rules.0[11] = Rule::Special11((42, 31), (42, 11, 31)); - dbg!(parsed.1.lines().map(|l| (l, rules.matches(l, rules.0[0]))).collect_vec()); - parsed.1.lines().filter(|l| rules.matches(l, rules.0[0]).contains(&l.len())).count() + rules.0[8] = Rule::Either(vec![42], vec![42, 8]); + rules.0[11] = Rule::Either(vec![42, 31], vec![42, 11, 31]); + parsed + .1 + .lines() + .filter(|l| rules.matches(l, rules.0[0].clone()).contains(&l.len())) + .count() } fn main() {