use super::util; use regex::Regex; use crate::day19::RuleType::{Letter, Options}; use std::collections::{HashMap, HashSet}; pub fn solve() { let lines = util::read_file("input/day19.txt"); let mut rules_map = HashMap::new(); lines.iter() .take_while(|s| s != &&String::from("")) .for_each(|s| { let mut line_split = s.split(": "); let idx = line_split.next().unwrap().parse::().unwrap(); let rule = Rule::parse(line_split.next().unwrap()); rules_map.insert(idx, rule); }); let messages: Vec<&String> = lines[(rules_map.len() + 1)..].iter().collect(); println!("{:?}", try_match(&messages[0], &0, &rules_map)); let part1 = messages.iter().filter(|m| try_match(m, &0, &rules_map).contains(&m.len())).count(); println!("Day X Part 1: {}", part1); // We update the rules to include the loops: rules_map.insert(8, Rule::parse("42 | 42 8")); rules_map.insert(11, Rule::parse("42 31 | 42 11 31")); let part2 = messages.iter().filter(|m| try_match(m, &0, &rules_map).contains(&m.len())).count(); println!("Day X Part 2: {}", part2); } /// Calculates and returns until when the string was matched fn try_match(message: &str, rule_idx: &usize, rules: &HashMap) -> HashSet { let rule = rules.get(&rule_idx).unwrap(); if rule.rule_type == Letter { if message.starts_with(rule.letters.as_ref().unwrap()) { let mut res = HashSet::new(); // Note: kind of a hack here, all the "letter" based matchers are of length 1 res.insert(rule.letters.as_ref().unwrap().len().clone()); return res; } else { return HashSet::new(); } } else { return rule.options.as_ref().unwrap().iter() .flat_map(|options| { let mut try_from = HashSet::new(); try_from.insert(0); let mut next_try_from = HashSet::new(); for option_rule_idx in options { for start_idx in try_from { try_match(&message[start_idx..], &option_rule_idx, rules) .iter() .for_each(|m| { if m != &0 { next_try_from.insert(start_idx + m); () }}) } try_from = next_try_from.clone(); if try_from.is_empty() { break; } next_try_from.clear(); } return try_from; }).collect(); } } #[derive(Debug, Clone)] struct Rule { rule_type: RuleType, letters: Option, options: Option>> } impl Rule { fn parse(input: &str) -> Rule { lazy_static! { static ref LETTER_RULE_MATCHER: Regex = Regex::new(r###"^"([a-z])"$"###).unwrap(); } return if LETTER_RULE_MATCHER.is_match(input) { Rule { rule_type: Letter, letters: Option::Some(LETTER_RULE_MATCHER.captures(input).unwrap().get(1).map(|m| String::from(m.as_str())).unwrap()), options: Option::None, } } else { let options = input.split(" | ") .map(|s| s.split(" ").map(|o| o.parse::().unwrap()).collect::>()).collect::>(); Rule { rule_type: Options, letters: Option::None, options: Option::Some(options) } } } } #[derive(Debug, PartialEq, Clone)] enum RuleType { Letter, Options }