69 lines
1.4 KiB
Crystal
69 lines
1.4 KiB
Crystal
|
require "advent"
|
||
|
|
||
|
rlines, _, strings = input(2020, 19).partition("\n\n")
|
||
|
strings = strings.lines
|
||
|
rules = {} of String => String
|
||
|
rlines.lines.map do |l|
|
||
|
rule, _, text = l.partition(": ")
|
||
|
rules[rule] = text
|
||
|
end
|
||
|
|
||
|
alias Matcher = Proc(String,Int32,Array(Int32))
|
||
|
|
||
|
def char(c)
|
||
|
->(str : String, i : Int32) { str[i]? == c ? [i+1] : [] of Int32 }
|
||
|
end
|
||
|
|
||
|
def any(ps)
|
||
|
->(str : String, i : Int32) { ps.flat_map { |p| p.call(str, i) } }
|
||
|
end
|
||
|
|
||
|
def seq(ps)
|
||
|
->(str : String, i : Int32) {
|
||
|
base = [i]
|
||
|
ps.each do |p|
|
||
|
base = base.flat_map { |i| p.call(str, i) }
|
||
|
end
|
||
|
base
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def to_regex(rules, rule)
|
||
|
end
|
||
|
|
||
|
def build_rule(rules, built, rule) : Matcher
|
||
|
if exists = built[rule]?
|
||
|
return exists
|
||
|
end
|
||
|
|
||
|
body = rules[rule]
|
||
|
return built[rule] = char body[1] if body.matches? /"."/
|
||
|
|
||
|
branches = [] of Matcher
|
||
|
top = any(branches)
|
||
|
built[rule] = top
|
||
|
branches.concat(body.split(" | ").map { |b| seq(b.split(" ").map { |subrule| build_rule(rules, built, subrule) }) })
|
||
|
top
|
||
|
end
|
||
|
|
||
|
def part1(input)
|
||
|
rules, lines = input
|
||
|
matcher = build_rule(rules, {} of String => Matcher, "0")
|
||
|
lines.count do |l|
|
||
|
matcher.call(l, 0).includes? l.size
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def part2(input)
|
||
|
rules, lines = input
|
||
|
rules["8"] = "42 | 42 8"
|
||
|
rules["11"] = "42 31 | 42 11 31"
|
||
|
matcher = build_rule(rules, {} of String => Matcher, "0")
|
||
|
lines.count do |l|
|
||
|
matcher.call(l, 0).includes? l.size
|
||
|
end
|
||
|
end
|
||
|
|
||
|
puts part1({rules,strings})
|
||
|
puts part2({rules,strings})
|