[TASK] Solved day 19 (haven't solved day 18 yet tho, coming up!)
This commit is contained in:
112
src/day19.rs
Normal file
112
src/day19.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user