From 59f78b90699386ba15a8a60821eba8b3b64e877f Mon Sep 17 00:00:00 2001 From: inga-lovinde <52715130+inga-lovinde@users.noreply.github.com> Date: Sun, 20 Dec 2020 03:33:03 +0100 Subject: [PATCH] Solution for day 19 --- day19/Cargo.toml | 11 ++++ day19/src/main.rs | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 day19/Cargo.toml create mode 100644 day19/src/main.rs diff --git a/day19/Cargo.toml b/day19/Cargo.toml new file mode 100644 index 0000000..4c7920a --- /dev/null +++ b/day19/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "day19" +version = "0.1.0" +authors = ["inga-lovinde <52715130+inga-lovinde@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4.0" +regex = "1" diff --git a/day19/src/main.rs b/day19/src/main.rs new file mode 100644 index 0000000..7ed7d1a --- /dev/null +++ b/day19/src/main.rs @@ -0,0 +1,146 @@ +use std::error::Error; +use std::io::{self, BufRead}; +use std::str::FromStr; + +#[macro_use] extern crate lazy_static; +use regex::Regex; + +enum Rule { + None, + Char(char), + Ref(usize), + Sequence(Vec), + Choice(Vec), +} + +impl Default for Rule { + fn default() -> Self { + Rule::None + } +} + +impl FromStr for Rule { + type Err = Box; + + fn from_str(s: &str) -> Result { + lazy_static! { + static ref CHAR_RE: Regex = Regex::new(r#"^"(\w)"$"#).unwrap(); + static ref REF_RE: Regex = Regex::new(r"^(\d+)$").unwrap(); + static ref CHOICE_SEPARATOR_RE: Regex = Regex::new(r"\s*\|\s*").unwrap(); + static ref SEQ_SEPARATOR_RE: Regex = Regex::new(r"\s+").unwrap(); + } + + if let Some(char_captures) = CHAR_RE.captures(s) { + return Ok(Rule::Char(char_captures[1].chars().next().unwrap())); + } + + if let Some(ref_captures) = REF_RE.captures(s) { + return Ok(Rule::Ref(ref_captures[1].parse()?)); + } + + if CHOICE_SEPARATOR_RE.is_match(s) { + return Ok(Rule::Choice(CHOICE_SEPARATOR_RE.split(s).map(|s| s.parse()).collect::>()?)); + } + + if SEQ_SEPARATOR_RE.is_match(s) { + return Ok(Rule::Sequence(SEQ_SEPARATOR_RE.split(s).map(|s| s.parse()).collect::>()?)); + } + + return Err(Box::from(format!("Failed to parse string '{}'", s))); + } +} + +struct RuleWithIndex { + index: usize, + rule: Rule, +} + +impl FromStr for RuleWithIndex { + type Err = Box; + + fn from_str(s: &str) -> Result { + lazy_static! { + static ref RULE_RE: Regex = Regex::new(r"^(\d+):\s*([\S].*)$").unwrap(); + } + + match RULE_RE.captures(s) { + Some(captures) => Ok(Self { + index: captures[1].parse()?, + rule: captures[2].parse()?, + }), + None => Err(Box::from(format!("Failed to parse string '{}'", s))), + } + } +} + +fn get_match(rules: &[Rule], rule: &Rule, data: &[char], offset: usize) -> Vec { + match rule { + Rule::None => vec![], + Rule::Char(ch) => { + if offset < data.len() && data[offset] == *ch { + vec![offset + 1] + } else { + vec![] + } + }, + Rule::Ref(rule_index) => get_match(&rules, &rules[*rule_index], &data, offset), + Rule::Sequence(rules_sequence) => { + let mut current_offsets = vec![offset]; + for subrule in rules_sequence { + current_offsets = current_offsets.into_iter() + .flat_map(|current_offset| get_match(&rules, subrule, &data, current_offset)) + .collect(); + } + + current_offsets + }, + Rule::Choice(rules_choice) => rules_choice.iter() + .flat_map(|subrule| get_match(&rules, subrule, &data, offset)) + .collect(), + } +} + +fn is_match(rules: &[Rule], rule: &Rule, data: &[char]) -> bool { + get_match(&rules, &rule, &data, 0).contains(&data.len()) +} + +fn main() { + let stdin = io::stdin(); + let mut lines = stdin.lock().lines().into_iter(); + let mut rules_lines: Vec = Vec::new(); + loop { + let line = lines.next().unwrap().unwrap(); + if line == "" { + break; + } + + rules_lines.push(line.parse().unwrap()); + } + + let mut rules: Vec = Vec::new(); + rules.resize_with( + rules_lines.iter().map(|rule_with_index| rule_with_index.index).max().unwrap() + 1, + Default::default + ); + for rule_line in rules_lines { + rules[rule_line.index] = rule_line.rule; + } + + let mut matches_count = 0; + loop { + match lines.next() { + Some(Ok(line)) => { + let result = is_match(&rules, &Rule::Ref(0), &line.chars().collect::>()); + println!("Match: {}", result); + if result { + matches_count += 1; + } + } + _ => { + break; + } + } + } + + println!("{}", matches_count); +}