2021-04-29 19:14:28 +02:00
#![ feature(test, bool_to_option) ]
2020-12-19 12:32:28 +01:00
extern crate test ;
use aoc2020 ::common ::* ;
use itertools ::Itertools ;
2020-12-19 14:00:03 +01:00
#[ derive(Debug, PartialEq, Clone) ]
2020-12-19 12:32:28 +01:00
enum Rule {
Char ( u8 ) ,
2020-12-19 14:00:03 +01:00
And ( Vec < usize > ) ,
AndOr ( Vec < usize > , Vec < usize > ) ,
Any ( Vec < usize > ) ,
2020-12-19 12:32:28 +01:00
Useless ( usize ) , // just delegates to another rule and makes parsing annoying
2020-12-19 14:00:03 +01:00
Either ( Vec < usize > , Vec < usize > ) ,
2020-12-19 12:32:28 +01:00
}
type Parsed < ' a > = ( RuleSet , & ' a str ) ;
#[ derive(Debug, Clone) ]
struct RuleSet ( Vec < Rule > ) ;
impl RuleSet {
2020-12-19 14:00:03 +01:00
fn and_combinations ( & self , s : & str , rules : & [ usize ] , start : usize ) -> Vec < usize > {
if let Some ( & rule ) = rules . first ( ) {
if s . len ( ) < = start {
return vec! [ ] ;
}
self . matches ( & s [ start .. ] , self . 0 [ rule ] . clone ( ) )
. into_iter ( )
. flat_map ( | i | self . and_combinations ( s , & rules [ 1 .. ] , start + i ) . into_iter ( ) )
. collect ( )
} else {
vec! [ start ]
}
}
2020-12-19 13:02:01 +01:00
fn matches ( & self , s : & str , rule : Rule ) -> Vec < usize > {
2021-04-29 19:14:28 +02:00
if s . is_empty ( ) {
2020-12-19 13:02:01 +01:00
return vec! [ ] ;
2020-12-19 12:50:48 +01:00
}
2020-12-19 13:02:01 +01:00
match rule {
Rule ::Char ( c ) = > ( s . as_bytes ( ) . first ( ) = = Some ( & c ) ) . then ( | | vec! [ 1 ] ) . unwrap_or_else ( Vec ::new ) ,
2020-12-19 14:00:03 +01:00
Rule ::And ( v ) = > self . and_combinations ( s , v . as_slice ( ) , 0 ) ,
Rule ::AndOr ( v1 , v2 ) = > self
. matches ( s , Rule ::And ( v1 ) )
2020-12-19 13:02:01 +01:00
. into_iter ( )
2020-12-19 14:00:03 +01:00
. chain ( self . matches ( s , Rule ::And ( v2 ) ) )
2020-12-19 13:02:01 +01:00
. collect ( ) ,
2020-12-19 14:00:03 +01:00
Rule ::Any ( v ) = > v . into_iter ( ) . flat_map ( | r | self . matches ( s , self . 0 [ r ] . clone ( ) ) ) . collect ( ) ,
Rule ::Useless ( r ) = > self . matches ( s , self . 0 [ r ] . clone ( ) ) ,
Rule ::Either ( first , second ) = > self
2020-12-19 13:02:01 +01:00
. matches ( s , Rule ::And ( first ) )
. into_iter ( )
2020-12-19 14:00:03 +01:00
. chain ( self . matches ( s , Rule ::And ( second ) ) )
2020-12-19 13:02:01 +01:00
. collect ( ) ,
}
2020-12-19 12:32:28 +01:00
}
}
fn read_input ( ) -> String {
read_file ( 19 )
}
2021-04-29 19:14:28 +02:00
fn parse_input ( raw : & '_ str ) -> Parsed < '_ > {
2020-12-19 12:32:28 +01:00
let ( rules , inputs ) = raw . split_once ( " \n \n " ) . unwrap ( ) ;
let rules = RuleSet (
rules
. lines ( )
. filter_map ( | l | l . split_once ( " : " ) )
// why isn’t this already sorted like it was in the example?
. sorted_by_key ( | t | t . 0. parse ::< i32 > ( ) . unwrap ( ) )
. map ( | ( _ , r ) | match r {
or if or . contains ( '|' ) = > {
let mut split = or . split ( ' ' ) . filter ( | s | s ! = & " | " ) . map ( | s | s . parse ( ) . unwrap ( ) ) ;
split
. clone ( )
. next_tuple ( )
2020-12-19 14:00:03 +01:00
. map ( | ( a , b , c , d ) | Rule ::AndOr ( vec! [ a , b ] , vec! [ c , d ] ) )
. or_else ( | | split . next_tuple ( ) . map ( | ( a , b ) | Rule ::Any ( vec! [ a , b ] ) ) )
2020-12-19 12:32:28 +01:00
. unwrap ( )
}
2021-04-29 19:14:28 +02:00
chr if chr . contains ( '"' ) = > Rule ::Char ( chr . bytes ( ) . skip_while ( | & b | b ! = b '"' ) . nth ( 1 ) . unwrap ( ) ) ,
2020-12-19 14:00:03 +01:00
and if and . contains ( ' ' ) = > Rule ::And ( and . split ( ' ' ) . map ( | s | s . parse ( ) . unwrap ( ) ) . collect ( ) ) ,
2020-12-19 12:32:28 +01:00
useless = > Rule ::Useless ( useless . parse ( ) . unwrap ( ) ) ,
} )
. collect ( ) ,
) ;
( rules , inputs )
}
fn part1 ( ( rules , input ) : & Parsed ) -> usize {
2020-12-19 14:00:03 +01:00
input
. lines ( )
. filter ( | l | rules . matches ( l , rules . 0 [ 0 ] . clone ( ) ) . contains ( & l . len ( ) ) )
. count ( )
2020-12-19 12:32:28 +01:00
}
fn part2 ( parsed : & Parsed ) -> usize {
let mut rules = parsed . 0. clone ( ) ;
2020-12-19 14:00:03 +01:00
rules . 0 [ 8 ] = Rule ::Either ( vec! [ 42 ] , vec! [ 42 , 8 ] ) ;
rules . 0 [ 11 ] = Rule ::Either ( vec! [ 42 , 31 ] , vec! [ 42 , 11 , 31 ] ) ;
parsed
. 1
. lines ( )
. filter ( | l | rules . matches ( l , rules . 0 [ 0 ] . clone ( ) ) . contains ( & l . len ( ) ) )
. count ( )
2020-12-19 12:32:28 +01:00
}
fn main ( ) {
let raw = read_input ( ) ;
let input = parse_input ( & raw ) ;
println! ( " Part 1: {} " , part1 ( & input ) ) ;
println! ( " Part 2: {} " , part2 ( & input ) ) ;
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2020 ::* ;
use paste ::paste ;
use test ::black_box ;
const TEST_INPUT : & str = r #" 0: 8 11
1 : " a "
2 : 1 24 | 14 4
3 : 5 14 | 16 1
4 : 1 1
5 : 1 14 | 15 1
6 : 14 14 | 1 14
7 : 14 5 | 1 21
8 : 42
9 : 14 27 | 1 26
10 : 23 14 | 28 1
11 : 42 31
12 : 24 14 | 19 1
13 : 14 3 | 1 12
14 : " b "
15 : 1 | 14
16 : 15 1 | 14 14
17 : 14 2 | 1 7
18 : 15 15
19 : 14 1 | 14 14
20 : 14 14 | 1 15
21 : 14 1 | 1 14
22 : 14 14
23 : 25 1 | 22 14
24 : 14 1
25 : 1 1 | 1 14
26 : 14 22 | 1 20
27 : 1 6 | 14 18
28 : 16 1
29 : 29
30 : 30
31 : 14 17 | 1 13
32 : 32
33 : 33
34 : 34
35 : 35
36 : 36
37 : 37
38 : 38
39 : 39
40 : 40
41 : 41
42 : 9 14 | 10 1
abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa
bbabbbbaabaabba
babbbbaabbbbbabbbbbbaabaaabaaa
aaabbbbbbaaaabaababaabababbabaaabbababababaaa
bbbbbbbaaaabbbbaaabbabaaa
bbbababbbbaaaaaaaabbababaaababaabab
ababaaaaaabaaab
ababaaaaabbbaba
baabbaaaabbaaaababbaababb
abbbbabbbbaaaababbbbbbaaaababb
aaaaabbaabaaaaababaa
aaaabbaaaabbaaa
aaaabbaabbaaaaaaabbbabbbaaabbaabaaa
babaaabbbaaabaababbaabababaaab
aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba " #;
2020-12-19 12:50:48 +01:00
2020-12-19 12:32:28 +01:00
test! ( part1 ( ) = = 3 ) ;
test! ( part2 ( ) = = 12 ) ;
bench! ( part1 ( ) = = 235 ) ;
2020-12-19 13:02:01 +01:00
bench! ( part2 ( ) = = 379 ) ;
2020-12-19 12:32:28 +01:00
// bench_input!(0.len == 0);
}