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