Day 19 Part 1
This commit is contained in:
parent
339fda2f90
commit
d1ccd00ca3
1 changed files with 126 additions and 0 deletions
126
2020/day19/day19.ex
Normal file
126
2020/day19/day19.ex
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
defmodule RBNF do
|
||||
def build_grammar(rules) do
|
||||
rules
|
||||
|> Enum.into(%{}, fn [name, rbnf] ->
|
||||
{
|
||||
name,
|
||||
{:alt,
|
||||
String.split(rbnf, " | ")
|
||||
|> Enum.map(fn text ->
|
||||
seq =
|
||||
String.split(text, " ")
|
||||
|> Enum.map(fn chars ->
|
||||
cond do
|
||||
String.match?(chars, ~r/^\d+$/) -> {:ref, chars}
|
||||
String.match?(chars, ~r/^".*"$/) -> {:lit, String.slice(chars, 1..-2)}
|
||||
end
|
||||
end)
|
||||
|
||||
{:seq, seq}
|
||||
end)}
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
# Returns {matched_text, remaining_text}
|
||||
def parse(grammar, rule, text) do
|
||||
case rule do
|
||||
{:lit, char} ->
|
||||
if String.at(text, 0) == char do
|
||||
String.split_at(text, 1)
|
||||
else
|
||||
{nil, text}
|
||||
end
|
||||
|
||||
{:ref, name} ->
|
||||
parse(grammar, Map.fetch!(grammar, name), text)
|
||||
|
||||
{:seq, rules} ->
|
||||
{matches, remaining} =
|
||||
Enum.map_reduce(rules, text, fn rule, buffer ->
|
||||
if buffer == nil do
|
||||
# An earlier match failed -> bail out
|
||||
{nil, nil}
|
||||
else
|
||||
case parse(grammar, rule, buffer) do
|
||||
{nil, extra} -> {nil, nil}
|
||||
{matched, rest} -> {matched, rest}
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if Enum.all?(matches) do
|
||||
{Enum.join(matches), remaining}
|
||||
else
|
||||
{nil, text}
|
||||
end
|
||||
|
||||
{:alt, rules} ->
|
||||
res =
|
||||
Enum.reduce_while(rules, text, fn rule, _ ->
|
||||
case parse(grammar, rule, text) do
|
||||
{nil, _} -> {:cont, nil}
|
||||
{matched, rest} -> {:halt, {matched, rest}}
|
||||
end
|
||||
end)
|
||||
|
||||
case res do
|
||||
# Nothing matched
|
||||
nil -> {nil, text}
|
||||
{matched, rest} -> {matched, rest}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Day19 do
|
||||
defp read_input do
|
||||
Path.expand('input', Path.dirname(__ENV__.file))
|
||||
|> File.read!()
|
||||
end
|
||||
|
||||
defp test_input do
|
||||
"""
|
||||
0: 4 1 5
|
||||
1: 2 3 | 3 2
|
||||
2: 4 4 | 5 5
|
||||
3: 4 5 | 5 4
|
||||
4: "a"
|
||||
5: "b"
|
||||
|
||||
ababbb
|
||||
bababa
|
||||
abbbab
|
||||
aaabbb
|
||||
aaaabbb
|
||||
"""
|
||||
end
|
||||
|
||||
defp parse_input(text) do
|
||||
[rules, messages] = String.split(text, "\n\n", trim: true)
|
||||
{parse_rules(rules), parse_messages(messages)}
|
||||
end
|
||||
|
||||
defp parse_rules(text) do
|
||||
String.split(text, "\n", trim: true)
|
||||
|> Enum.map(&String.split(&1, ": "))
|
||||
end
|
||||
|
||||
defp parse_messages(text) do
|
||||
String.split(text, "\n", trim: true)
|
||||
end
|
||||
|
||||
def part1 do
|
||||
{rules, messages} = read_input() |> parse_input()
|
||||
grammar = RBNF.build_grammar(rules)
|
||||
|
||||
Enum.count(messages, fn msg ->
|
||||
case RBNF.parse(grammar, {:ref, "0"}, msg) do
|
||||
{msg, ""} -> true
|
||||
_ -> false
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
Day19.part1() |> IO.inspect()
|
||||
Loading…
Reference in a new issue