Files
advent-of-code-2020-rust/src/day19.rs

112 lines
3.5 KiB
Rust

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::<usize>().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<usize, Rule>) -> HashSet<usize> {
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<String>,
options: Option<Vec<Vec<usize>>>
}
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::<usize>().unwrap()).collect::<Vec<_>>()).collect::<Vec<_>>();
Rule {
rule_type: Options,
letters: Option::None,
options: Option::Some(options)
}
}
}
}
#[derive(Debug, PartialEq, Clone)]
enum RuleType {
Letter, Options
}