Skip to content

Instantly share code, notes, and snippets.

@ynonp
Created December 21, 2020 16:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ynonp/5e79f8986804a29b8680870ce166aba5 to your computer and use it in GitHub Desktop.
Save ynonp/5e79f8986804a29b8680870ce166aba5 to your computer and use it in GitHub Desktop.
defmodule Day21 do
def read_input do
File.read!("input/day21.txt")
|> String.split("\n", trim: true)
end
def ingredients_list(line) do
[ingredients_str, _alergens_str] = String.split(line, [" (contains ", ")"], trim: true)
String.split(ingredients_str, " ", trim: true)
end
# Input line: mxmxvkd kfcds sqjhc nhms (contains dairy, fish)
# Output map: %{
# dairy: [mxmxvkd, kfcds, sqjhc, nhms]
# fish: [mxmxvkd, kfcds, sqjhc, nhms]
# }
def parse_product(line) do
[ingredients_str, alergens_str] = String.split(line, [" (contains ", ")"], trim: true)
alergens = String.split(alergens_str, ", ", trim: true)
ingredients = String.split(ingredients_str, " ", trim: true)
for a <- alergens do
%{ a => [MapSet.new(ingredients)] }
end
|> Enum.reduce(&Map.merge/2)
end
def parse_product_inverse(line) do
[ingredients_str, alergens_str] = String.split(line, [" (contains ", ")"], trim: true)
alergens = String.split(alergens_str, ", ", trim: true)
ingredients = String.split(ingredients_str, " ", trim: true)
for i <- ingredients do
%{ i => alergens }
end
|> Enum.reduce(&Map.merge/2)
end
def maybe_dict(input) do
input
|> Enum.map(&parse_product_inverse/1)
|> Enum.reduce(fn
val, acc -> Map.merge(val, acc, fn _k, v1, v2 ->
v1 ++ v2
end)
end)
end
def not_dict(input) do
input
|> Enum.map(&parse_product/1)
|> Enum.reduce(fn
val, acc -> Map.merge(val, acc, fn _k, v1, v2 ->
v1 ++ v2
end)
end)
|> Enum.map(fn { k, v } ->
# v is a list of MapSet
# k is the name of the alerge
{
k,
MapSet.difference(
Enum.reduce(v, &MapSet.union/2),
Enum.reduce(v, &MapSet.intersection/2)
)
}
end)
|> Enum.into(%{})
end
def clear_maybe_dict(maybe_dict) do
singles = maybe_dict
|> Enum.filter(fn {k, v} -> Enum.count(v) == 1 end)
|> Enum.map(fn {_k, v} -> v end)
|> Enum.reduce(&MapSet.union/2)
IO.inspect(singles)
maybe_dict
|> Enum.map(fn { k, v } ->
{
k,
if Enum.count(v) <= 1 do
v
else
MapSet.difference(v, singles)
end
}
end)
|> Enum.into(%{})
end
def certainly_dict(maybe_dict, whatnot_dict) do
canstop = maybe_dict
|> Map.values
|> Enum.all?(fn v -> Enum.count(v) <= 1 end)
if canstop do
maybe_dict
else
maybe_dict
|> Enum.map(fn { k, v } ->
{
k,
Enum.reject(v, fn alergen ->
MapSet.member?(Map.get(whatnot_dict, alergen), k)
end)
|> Enum.into(MapSet.new)
}
end)
|> Enum.into(%{})
|> clear_maybe_dict()
|> IO.inspect
|> certainly_dict(whatnot_dict)
end
end
def count_occurences(input, stuff) do
input
|> Enum.map(&ingredients_list/1)
|> Enum.map(fn list -> Enum.count(list, fn i -> MapSet.member?(stuff, i) end) end)
|> Enum.sum
|> IO.inspect
end
def part1 do
input = read_input()
whatnot = not_dict(input)
|> IO.inspect
IO.inspect(maybe_dict(input))
required_ingredients = maybe_dict(input)
|> Enum.filter(fn {k, v} ->
Enum.all?(v, &(MapSet.member?(Map.get(whatnot, &1), k)))
end)
|> Enum.map(&Kernel.elem(&1, 0))
|> Enum.into(MapSet.new)
|> IO.inspect
count_occurences(input, required_ingredients)
found_it = certainly_dict(maybe_dict(input), whatnot)
found_it
|> Enum.filter(fn { k, v } -> Enum.count(v) > 0 end)
|> Enum.map(fn {k, v} -> {k, Enum.at(v, 0)} end)
|> Enum.sort_by(fn {k, v} -> v end)
|> Enum.map(fn {k, v} -> k end)
|> Enum.join(",")
|> IO.inspect
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment