Day 3
Setup
input = Aoc.get_input(3)
textarea = Kino.Input.textarea("Puzzle input", default: input)
lines = Kino.Input.read(textarea) |> String.split("\n", trim: true)
Part 1
lines
|> Enum.reduce(%{}, fn line, acc ->
line
|> String.split("", trim: true)
|> Enum.with_index()
|> Enum.reduce(acc, fn
{"0", i}, acc -> Map.update(acc, i, {1, 0}, fn {zeroes, ones} -> {zeroes + 1, ones} end)
{"1", i}, acc -> Map.update(acc, i, {0, 1}, fn {zeroes, ones} -> {zeroes, ones + 1} end)
end)
end)
|> Enum.reduce({"", ""}, fn {_, {zeroes, ones}}, {gamma, epsilon} ->
if zeroes > ones do
{gamma <> "0", epsilon <> "1"}
else
{gamma <> "1", epsilon <> "0"}
end
end)
|> then(fn {gamma, epsilon} ->
{gamma, ""} = Integer.parse(gamma, 2)
{epsilon, ""} = Integer.parse(epsilon, 2)
{gamma, epsilon}
end)
|> Tuple.product()
Part 2
bits =
lines
|> Enum.at(0)
|> String.length()
|> IO.inspect(label: "bits")
oxygen =
0..bits
|> Enum.reduce_while(lines, fn i, lines ->
lines
|> Enum.group_by(&String.at(&1, i))
|> then(fn group ->
zeroes = Map.get(group, "0") |> Enum.count()
ones = Map.get(group, "1") |> Enum.count()
if zeroes > ones do
Map.get(group, "0")
else
Map.get(group, "1")
end
end)
|> case do
[last] -> {:halt, last}
lines -> {:cont, lines}
end
end)
|> Integer.parse(2)
|> elem(0)
|> IO.inspect(label: "oxygen")
co2 =
0..bits
|> Enum.reduce_while(lines, fn i, lines ->
lines
|> Enum.group_by(&String.at(&1, i))
|> then(fn group ->
zeroes = Map.get(group, "0") |> Enum.count()
ones = Map.get(group, "1") |> Enum.count()
if zeroes > ones do
Map.get(group, "1")
else
Map.get(group, "0")
end
end)
|> case do
[last] -> {:halt, last}
lines -> {:cont, lines}
end
end)
|> Integer.parse(2)
|> elem(0)
|> IO.inspect(label: "co2")
oxygen * co2
Part 1 revisited
A little bit less noise in the pipeline
lines
|> Enum.map(&String.split(&1, "", trim: true))
|> Enum.zip()
|> Enum.map(&Tuple.to_list/1)
|> Enum.map(&Enum.frequencies(&1))
|> Enum.map(&Enum.max_by(&1, fn {_, v} -> v end))
|> Enum.map(&elem(&1, 0))
|> Enum.reduce([0, 0], fn most_freq, [gamma, epsilon] ->
case most_freq do
"0" -> [2 * gamma + 1, 2 * epsilon]
"1" -> [2 * gamma, 2 * epsilon + 1]
end
end)
|> Enum.product()
Part 2 revisited
Collapse into a nicer pipeline.
Enum.reduce_while is used in place of recursion.
One of the tricky bits is passing &>/2 and &<=/2 to the Enum.max_by functions.
I was originally passing Enum.max_by and Enum.min_by but they were both sorting with &>=/2 and &<=/2, which does not split the groups properly.
bits =
lines
|> Enum.at(0)
|> String.length()
|> IO.inspect(label: "bits")
[&>/2, &<=/2]
|> Enum.map(fn sorter ->
0..bits
|> Enum.reduce_while(lines, fn i, lines ->
lines
|> Enum.group_by(&String.at(&1, i))
|> Enum.max_by(fn {_k, v} -> Enum.count(v) end, sorter)
|> elem(1)
|> case do
[last] -> {:halt, last}
lines -> {:cont, lines}
end
end)
|> Integer.parse(2)
|> elem(0)
end)
|> IO.inspect()
|> Enum.product()