Advent of Code solutions in Rust

feat: part 1 of day 19 of AoC 2020

+189 -2
+187
aoc_2020/src/day19.rs
··· 1 + use anyhow::{Context, bail}; 2 + use aoc_companion::prelude::*; 3 + use itertools::{Either, Itertools}; 4 + 5 + pub(crate) struct Door { 6 + rules: Vec<Rule>, 7 + messages: Vec<Box<[u8]>>, 8 + } 9 + 10 + impl<'input> Solution<'input> for Door { 11 + fn parse(input: &'input str) -> Result<Self> { 12 + let Some((rules, messages)) = input.split_once("\n\n") else { 13 + bail!("missing empty line separating rules from messages"); 14 + }; 15 + let rules: Vec<Rule> = rules 16 + .lines() 17 + .map(|line| -> Result<(usize, Rule)> { 18 + let Some((idx, body)) = line.split_once(':') else { 19 + bail!("missing colon separating rule index from body"); 20 + }; 21 + let idx = idx.parse().context("failed to parse rule index")?; 22 + 23 + let rule = if let Some(lit_str) = body.trim().strip_prefix('"') { 24 + let Some(lit_str) = lit_str.strip_suffix('"') else { 25 + bail!("missing closing quotation mark in string literal rule"); 26 + }; 27 + let &[byte] = lit_str.as_bytes() else { 28 + bail!("string literal rule is more than one ASCII character"); 29 + }; 30 + Rule::Literal(byte) 31 + } else { 32 + body.split('|') 33 + .map(|alt| { 34 + alt.split_whitespace() 35 + .map(|ref_str| { 36 + ref_str 37 + .parse() 38 + .context("failed to parse rule reference index") 39 + .map(RuleRef) 40 + }) 41 + .try_collect() 42 + .map(All) 43 + }) 44 + .try_collect() 45 + .map(Rule::Any)? 46 + }; 47 + 48 + Ok((idx, rule)) 49 + }) 50 + .try_fold(Vec::new(), |mut rules, res| -> Result<Vec<Rule>> { 51 + let (idx, rule) = res?; 52 + if rules.len() <= idx { 53 + rules.resize_with(idx + 1, || Rule::Any(vec![])); 54 + } 55 + rules[idx] = rule; 56 + Ok(rules) 57 + })?; 58 + 59 + let messages = messages 60 + .lines() 61 + .map(|line| line.as_bytes().to_vec().into_boxed_slice()) 62 + .collect(); 63 + 64 + Ok(Self { rules, messages }) 65 + } 66 + 67 + fn part1(&self) -> usize { 68 + self.messages 69 + .iter() 70 + .filter(matches(RuleRef(0), &self.rules)) 71 + .count() 72 + } 73 + } 74 + 75 + fn munch<'c>( 76 + RuleRef(rule_idx): RuleRef, 77 + rules: &[Rule], 78 + candidate: &'c [u8], 79 + ) -> impl Iterator<Item = &'c [u8]> { 80 + let rule = &rules[rule_idx]; 81 + match rule { 82 + Rule::Literal(b) => Either::Left(candidate.strip_prefix(&[*b]).into_iter()), 83 + Rule::Any(alternatives) => { 84 + Either::Right(alternatives.iter().flat_map(move |All(rule_seq)| { 85 + rule_seq 86 + .iter() 87 + .copied() 88 + .fold(vec![candidate], |rests, rule_ref| { 89 + rests 90 + .into_iter() 91 + .flat_map(|rest| munch(rule_ref, rules, rest)) 92 + .collect() 93 + }) 94 + })) 95 + } 96 + } 97 + } 98 + 99 + fn matches<C: AsRef<[u8]>>(rule_ref: RuleRef, rules: &[Rule]) -> impl Fn(&C) -> bool { 100 + move |candidate| munch(rule_ref, rules, candidate.as_ref()).contains(b"".as_slice()) 101 + } 102 + 103 + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 104 + struct RuleRef(usize); 105 + 106 + #[derive(Clone, Debug, PartialEq, Eq)] 107 + struct All(Vec<RuleRef>); 108 + 109 + #[derive(Clone, Debug, PartialEq, Eq)] 110 + enum Rule { 111 + Literal(u8), 112 + Any(Vec<All>), 113 + } 114 + 115 + #[cfg(test)] 116 + mod tests { 117 + use super::*; 118 + 119 + const EXAMPLE_INPUT: &str = r#"0: 4 1 5 120 + 1: 2 3 | 3 2 121 + 2: 4 4 | 5 5 122 + 3: 4 5 | 5 4 123 + 4: "a" 124 + 5: "b" 125 + 126 + ababbb 127 + bababa 128 + abbbab 129 + aaabbb 130 + aaaabbb"#; 131 + 132 + fn example_rules() -> Vec<Rule> { 133 + vec![ 134 + Rule::Any(vec![All(vec![RuleRef(4), RuleRef(1), RuleRef(5)])]), 135 + Rule::Any(vec![ 136 + All(vec![RuleRef(2), RuleRef(3)]), 137 + All(vec![RuleRef(3), RuleRef(2)]), 138 + ]), 139 + Rule::Any(vec![ 140 + All(vec![RuleRef(4), RuleRef(4)]), 141 + All(vec![RuleRef(5), RuleRef(5)]), 142 + ]), 143 + Rule::Any(vec![ 144 + All(vec![RuleRef(4), RuleRef(5)]), 145 + All(vec![RuleRef(5), RuleRef(4)]), 146 + ]), 147 + Rule::Literal(b'a'), 148 + Rule::Literal(b'b'), 149 + ] 150 + } 151 + 152 + const EXAMPLE_MESSAGES: &[&[u8]] = &[b"ababbb", b"bababa", b"abbbab", b"aaabbb", b"aaaabbb"]; 153 + 154 + #[test] 155 + fn parse_example_input() { 156 + let Door { rules, messages } = Door::parse(EXAMPLE_INPUT).unwrap(); 157 + itertools::assert_equal(rules, example_rules()); 158 + itertools::assert_equal( 159 + messages.iter().map(|m| m.as_ref()), 160 + EXAMPLE_MESSAGES.iter().copied(), 161 + ); 162 + } 163 + 164 + #[test] 165 + fn munch_some_rules() { 166 + let rules = example_rules(); 167 + itertools::assert_equal(munch(RuleRef(4), &rules, b"ababbb"), [b"babbb"]); 168 + itertools::assert_equal(munch(RuleRef(3), &rules, b"babbb"), [b"bbb"]); 169 + itertools::assert_equal( 170 + munch(RuleRef(2), &rules, b"babbb"), 171 + std::iter::empty::<&[u8]>(), 172 + ); 173 + itertools::assert_equal(munch(RuleRef(2), &rules, b"bbb"), [b"b"]); 174 + itertools::assert_equal(munch(RuleRef(1), &rules, b"babbb"), [b"b"]); 175 + itertools::assert_equal(munch(RuleRef(0), &rules, b"ababbb"), [b""]); 176 + } 177 + 178 + #[test] 179 + fn matching_example_messages() { 180 + itertools::assert_equal( 181 + EXAMPLE_MESSAGES 182 + .iter() 183 + .map(matches(RuleRef(0), &example_rules())), 184 + [true, false, true, false, false], 185 + ); 186 + } 187 + }
+2 -2
aoc_2020/src/main.rs
··· 18 18 mod day16; 19 19 mod day17; 20 20 mod day18; 21 - // mod day19; 21 + mod day19; 22 22 // mod day20; 23 23 // mod day21; 24 24 // mod day22; ··· 49 49 door!(2020-12-16 ~> day16), 50 50 door!(2020-12-17 ~> day17), 51 51 door!(2020-12-18 ~> day18), 52 - // door!(2020-12-19 ~> day19), 52 + door!(2020-12-19 ~> day19), 53 53 // door!(2020-12-20 ~> day20), 54 54 // door!(2020-12-21 ~> day21), 55 55 // door!(2020-12-22 ~> day22),