112 lines
3.5 KiB
Rust
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
|
|
} |