Last active
December 10, 2017 08:52
-
-
Save sasa1977/c580e36a94bb10d63465b3168d14345c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Day9 do | |
def part1(), do: | |
score_groups(input()) | |
def part2(), do: | |
count_garbage(input()) | |
defp input() do | |
{groups, "\n"} = parse(zero_or_more(group()), File.read!("input.txt")) | |
groups | |
end | |
defp score_groups(groups), do: | |
score_groups(groups, 1) | |
defp score_groups(elements, level) when is_list(elements), do: | |
elements |> Stream.map(&score_groups(&1, level)) |> Enum.sum() | |
defp score_groups({:group, members}, level), do: | |
level + score_groups(members, level + 1) | |
defp score_groups(_other, _level), do: 0 | |
defp count_garbage(elements) when is_list(elements), do: | |
elements |> Stream.map(&count_garbage/1) |> Enum.sum() | |
defp count_garbage({:group, members}), do: | |
count_garbage(members) | |
defp count_garbage({:garbage, chars}), do: | |
chars |> Stream.reject(&match?({:ignored, _}, &1)) |> Enum.count() | |
defp count_garbage(_other), do: | |
0 | |
defmacrop parser(parser_generator) do | |
quote do | |
fn input -> parse(unquote(parser_generator), input) end | |
end | |
end | |
defp group(), do: | |
parser( | |
sequence([char(?{), zero_or_more(group_element()), char(?})]) | |
|> map(fn [?{, members, ?}] -> {:group, members} end) | |
) | |
defp group_element(), do: | |
parser(one_of([group(), garbage(), char_except(?})])) | |
defp garbage(), do: | |
parser( | |
sequence([char(?<), zero_or_more(garbage_element()), char(?>)]) | |
|> map(fn [?<, members, ?>] -> {:garbage, members} end) | |
) | |
defp garbage_element(), do: | |
parser(one_of([ignored_char(), char_except(?>)])) | |
defp ignored_char(), do: | |
parser( | |
sequence([char(?!), char()]) | |
|> map(fn [?!, char] -> {:ignored, char} end) | |
) | |
defp parse(parser, input), do: | |
parser.(input) | |
defp map(parser, mapper) do | |
fn input -> | |
with {element, rest} <- parser.(input), do: | |
{mapper.(element), rest} | |
end | |
end | |
defp sequence(parsers) do | |
fn input -> | |
case parsers do | |
[] -> {[], input} | |
[parser | others] -> | |
with \ | |
{element, rest} <- parser.(input), | |
{other_elements, rest} <- parse(sequence(others), rest), | |
do: {[element | other_elements], rest} | |
end | |
end | |
end | |
defp char() do | |
fn | |
<<char::utf8, rest::binary>> -> {char, rest} | |
_other -> nil | |
end | |
end | |
defp char(value) do | |
fn | |
<<^value::utf8, rest::binary>> -> {value, rest} | |
_other -> nil | |
end | |
end | |
defp char_except(value) do | |
fn | |
<<^value::utf8, _::binary>> -> nil | |
<<char::utf8, rest::binary>> -> {char, rest} | |
end | |
end | |
defp zero_or_more(parser) do | |
fn input -> | |
case parser.(input) do | |
{element, rest} -> | |
{other_elements, rest} = parse(zero_or_more(parser), rest) | |
{[element | other_elements], rest} | |
nil -> {[], input} | |
end | |
end | |
end | |
defp one_of(parsers) do | |
fn input -> | |
case parsers do | |
[] -> nil | |
[parser | others] -> with nil <- parser.(input), do: parse(one_of(others), input) | |
end | |
end | |
end | |
end | |
Day9.part1() |> IO.inspect | |
Day9.part2() |> IO.inspect |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment