@@ -2,6 +2,7 @@ package eu.sim642.adventofcode2020
2
2
3
3
import scala .collection .mutable
4
4
import scala .util .matching .Regex
5
+ import scala .util .parsing .combinator .RegexParsers
5
6
6
7
object Day19 {
7
8
@@ -15,22 +16,147 @@ object Day19 {
15
16
16
17
case class Input (rules : Rules , messages : Seq [String ])
17
18
18
- def rules2Regex (rules : Rules ): Regex = {
19
+ // TODO: keep solution with regex for part 1
20
+ // TODO: figure out why parser combinators didn't work right
19
21
20
- val memo = mutable.Map .empty[Int , String ]
21
- def helper (rule : Rule ): String = rule match {
22
- case Literal (char) => char.toString
23
- case Sub (i) => memo.getOrElseUpdate(i, helper(rules(i)))
24
- case Concat (left, right) => helper(left) + helper(right)
25
- case Choice (left, right) => " (" + helper(left) + " |" + helper(right) + " )"
22
+ // copied & modified from 2015 Day 19
23
+ // TODO: move earley parser to library
24
+ // https://en.wikipedia.org/wiki/Earley_parser
25
+ // https://old.reddit.com/r/adventofcode/comments/3xflz8/day_19_solutions/cy6gv3z/
26
+ type Production [N , T ] = (N , Seq [Either [N , T ]])
27
+ def earley [N , T ](grammar : Seq [Production [N , T ]], initial : N , input : Seq [T ]): Boolean = {
28
+
29
+ def nProductions (n : N ): Seq [Production [N , T ]] = grammar.filter(_._1 == n)
30
+
31
+ case class State (production : Production [N , T ], dot : Int , j : Int , count : Int ) {
32
+ def isComplete : Boolean = dot >= production._2.length
33
+ def current : Either [N , T ] = production._2(dot)
34
+ }
35
+
36
+ val S = IndexedSeq .fill(input.length + 1 )(mutable.LinkedHashSet .empty[State ])
37
+ val initialProductions = nProductions(initial)
38
+ for (production <- initialProductions)
39
+ S (0 ).add(State (production, 0 , 0 , 0 ))
40
+
41
+ for (k <- 0 to input.length) {
42
+ val SkQueue = S (k).to(mutable.Queue )
43
+
44
+ def addSk (state : State ): Unit = {
45
+ if (S (k).add(state))
46
+ SkQueue .enqueue(state)
47
+ }
48
+
49
+ while (SkQueue .nonEmpty) {
50
+ val state @ State ((n, _), _, j, count) = SkQueue .dequeue()
51
+ if (! state.isComplete) {
52
+ state.current match {
53
+ case Left (n) =>
54
+ // prediction
55
+ for (production <- nProductions(n))
56
+ addSk(State (production, 0 , k, 0 ))
57
+ case Right (t) =>
58
+ // scanning
59
+ if (k < input.length && t == input(k))
60
+ S (k + 1 ).add(state.copy(dot = state.dot + 1 ))
61
+ }
62
+ }
63
+ else {
64
+ // completion
65
+ for {
66
+ jState <- S (j)
67
+ if ! jState.isComplete && jState.current == Left (n)
68
+ } addSk(jState.copy(dot = jState.dot + 1 , count = jState.count + 1 + count))
69
+ }
70
+ }
26
71
}
27
72
28
- helper( Sub ( 0 )).r
73
+ S .last.exists(s => s.isComplete && initialProductions.contains(s.production))
29
74
}
30
75
31
- def countMatchingMessages (input : Input ): Int = {
32
- val regex = rules2Regex(input.rules)
33
- input.messages.count(regex.matches)
76
+ sealed trait Part {
77
+ // sealed trait Part extends RegexParsers {
78
+
79
+ /* def rules2Regex(rules: Rules): Regex = {
80
+
81
+ val memo = mutable.Map.empty[Int, String]
82
+ def helper(rule: Rule): String = rule match {
83
+ case Literal(char) => char.toString
84
+ case Sub(i) => memo.getOrElseUpdate(i, helper(rules(i)))
85
+ case Concat(left, right) => helper(left) + helper(right)
86
+ case Choice(left, right) => "(" + helper(left) + "|" + helper(right) + ")"
87
+ }
88
+
89
+ //println(s"42: ${helper(Sub(42))}")
90
+ //println(s"31: ${helper(Sub(31))}")
91
+
92
+ helper(Sub(0)).r
93
+ }
94
+
95
+ def countMatchingMessages(input: Input): Int = {
96
+ val regex = rules2Regex(input.rules)
97
+ input.messages.count(regex.matches)
98
+ }*/
99
+
100
+ /* def rules2Parser(rules: Rules): Parser[Unit] = {
101
+
102
+ val memo = mutable.Map.empty[Int, Parser[Unit]]
103
+ def helper(rule: Rule): Parser[Unit] = rule match {
104
+ case Literal(char) => literal(char.toString) ^^^ ()
105
+ case Sub(i) => memo.getOrElseUpdate(i, helper(rules(i)))
106
+ case Concat(left, right) => helper(left) ~ helper(right) ^^^ ()
107
+ case Choice(left, right) => helper(left) | helper(right)
108
+ }
109
+
110
+ //println(s"42: ${helper(Sub(42))}")
111
+ //println(s"31: ${helper(Sub(31))}")
112
+
113
+ helper(Sub(0))
114
+ }
115
+
116
+ def countMatchingMessages(input: Day19.Input): Int = {
117
+ val parser = rules2Parser(input.rules)
118
+ input.messages.count(message => {
119
+ parseAll(parser, message) match {
120
+ case Success(_, _) => true
121
+ case _ => false
122
+ }
123
+ })
124
+ }*/
125
+
126
+ def rules2Grammar (rules : Rules ): Seq [Production [Int , Char ]] = {
127
+
128
+ def helper (rule : Rule ): Seq [Seq [Either [Int , Char ]]] = rule match {
129
+ case Literal (char) => Seq (Seq (Right (char)))
130
+ case Sub (i) => Seq (Seq (Left (i)))
131
+ case Concat (left, right) =>
132
+ for {
133
+ l <- helper(left)
134
+ r <- helper(right)
135
+ } yield l ++ r
136
+ case Choice (left, right) => helper(left) ++ helper(right)
137
+ }
138
+
139
+ def helper2 (i : Int , rule : Rule ): Seq [Production [Int , Char ]] = helper(rule).map(i -> _)
140
+
141
+ // println(s"42: ${helper(Sub(42))}")
142
+ // println(s"31: ${helper(Sub(31))}")
143
+
144
+ rules.toSeq.flatMap({ case (i, rule) => helper2(i, rule) })
145
+ }
146
+
147
+ def countMatchingMessages (input : Day19 .Input ): Int = {
148
+ val grammar = rules2Grammar(input.rules)
149
+ input.messages.count(earley(grammar, 0 , _))
150
+ }
151
+ }
152
+
153
+ object Part1 extends Part
154
+
155
+ object Part2 extends Part {
156
+ override def rules2Grammar (rules : Rules ) = {
157
+ val newRules = rules + (8 -> Choice (Sub (42 ), Concat (Sub (42 ), Sub (8 )))) + (11 -> Choice (Concat (Sub (42 ), Sub (31 )), Concat (Concat (Sub (42 ), Sub (11 )), Sub (31 ))))
158
+ super .rules2Grammar(newRules)
159
+ }
34
160
}
35
161
36
162
@@ -69,6 +195,7 @@ object Day19 {
69
195
lazy val input : String = io.Source .fromInputStream(getClass.getResourceAsStream(" day19.txt" )).mkString.trim
70
196
71
197
def main (args : Array [String ]): Unit = {
72
- println(countMatchingMessages(parseInput(input)))
198
+ println(Part1 .countMatchingMessages(parseInput(input)))
199
+ println(Part2 .countMatchingMessages(parseInput(input)))
73
200
}
74
201
}
0 commit comments