advent-of-code/2019-elixir/lib/solutions/day_08.ex
2021-11-01 16:45:03 +01:00

217 lines
4.5 KiB
Elixir

defmodule Aoc19.Solution.Day08 do
@name "Day 8: Space Image Format"
@behaviour Solution
@image_width 25
@image_height 6
@black " "
@white "X"
@impl Solution
def get_name, do: @name
@impl Solution
def parse!(str), do: str
@impl Solution
@doc """
Solution for the first part of "Day 8: Space Image Format".
"""
def solve_first_part(data), do: data |> _solve_first_part(@image_width, @image_height)
@impl Solution
@doc """
Solution for the second part of "Day 8: Space Image Format".
"""
def solve_second_part(data) do
data |> _solve_second_part(@image_width, @image_height) |> reveal(@image_width)
end
@doc """
Actual solution for part 1, but with the ability to test.
## Examples
iex> Aoc19.Solution.Day08._solve_first_part("123456789012", 3, 2)
1
"""
def _solve_first_part(data, image_width, image_height) do
data
|> layers(image_width, image_height)
|> fewest_zeros
|> multiply()
end
@doc """
Actual solution for part 2, but with the ability to test.
## Examples
iex> Aoc19.Solution.Day08._solve_second_part("0222112222120000", 2, 2)
[" ", "X", "X", " "]
"""
def _solve_second_part(data, image_width, image_height) do
data
|> layers(image_width, image_height)
|> pixels()
|> colors
end
@doc """
get layers from image data.
## Examples
iex> Aoc19.Solution.Day08.layers("123456789012", 3, 2)
["123456", "789012"]
iex> Aoc19.Solution.Day08.layers("0222112222120000", 2, 2)
["0222", "1122", "2212", "0000"]
"""
def layers(data, x, y) do
data
|> String.codepoints()
|> Enum.chunk_every(x * y)
|> Enum.map(&Enum.join/1)
end
@doc """
Get layers with the fewest zeros.
## Examples
iex> Aoc19.Solution.Day08.fewest_zeros(["123456", "789012"])
"123456"
iex> Aoc19.Solution.Day08.fewest_zeros(["103406", "789012"])
"789012"
"""
def fewest_zeros(layers) do
layers
|> Enum.map(fn layer -> {layer, layer} end)
|> Enum.map(fn {layer, data} ->
{layer, data |> String.replace("0", "") |> String.length()}
end)
|> Enum.sort(fn a, b -> elem(a, 1) > elem(b, 1) end)
|> Enum.at(0)
|> elem(0)
end
@doc """
return the number of 1 digits multiplied by the number of 2 digits of a layer.
## Examples
iex> Aoc19.Solution.Day08.multiply("121226")
6
iex> Aoc19.Solution.Day08.multiply("293416")
1
"""
def multiply(layer) do
{x, y} =
layer
|> String.codepoints()
|> Enum.map(&String.to_integer/1)
|> Enum.reduce({0, 0}, fn
1, {x, y} -> {x + 1, y}
2, {x, y} -> {x, y + 1}
_, acc -> acc
end)
x * y
end
@doc """
Regroup a set of layers to pixels.
## Examples
iex> Aoc19.Solution.Day08.pixels(["0222", "1122", "2212", "0000"])
[[0, 1, 2, 0], [2, 1, 2, 0], [2, 2, 1, 0], [2, 2, 2, 0]]
iex> Aoc19.Solution.Day08.pixels(["123456", "789012"])
[[1, 7], [2, 8], [3, 9], [4, 0], [5, 1], [6, 2]]
"""
def pixels(_layers, {count, _depth}, pixels, pos) when pos > count,
do: pixels |> Enum.reverse()
def pixels(layers, {count, depth}, pixels, pos) do
pixel =
0..depth
|> Enum.map(fn layer ->
layers
|> Enum.at(layer)
|> Enum.at(pos)
|> String.to_integer()
end)
pixels(layers, {count, depth}, [pixel | pixels], pos + 1)
end
def pixels(layers) do
count = layers |> Enum.at(0) |> String.length()
depth = layers |> length
pixels(
layers |> Enum.map(&String.codepoints/1),
{count - 1, depth - 1},
[],
0
)
end
def colors(pixels) do
pixels
|> Enum.map(&color/1)
end
@doc """
determine color of pixel based on layer.
## Examples
iex> Aoc19.Solution.Day08.color([0, 1, 2, 0])
" "
iex> Aoc19.Solution.Day08.color([2, 1, 2, 0])
"X"
iex> Aoc19.Solution.Day08.color([2, 2, 1, 0])
"X"
iex> Aoc19.Solution.Day08.color([2, 2, 2, 0])
" "
iex> Aoc19.Solution.Day08.color([2, 2, 2, 2, 1, 2, 2, 0])
"X"
"""
def color([2 | queue]), do: color(queue)
def color([1 | _]), do: @white
def color([0 | _]), do: @black
@doc """
determine color of pixel based on layer.
## Examples
iex> Aoc19.Solution.Day08.reveal(["X", " ", " ", "X"], 2)
"\\nX \\n X"
"""
def reveal(pixels, breakpoint) do
output =
pixels
|> Enum.chunk_every(breakpoint)
|> Enum.map(&Enum.join/1)
|> Enum.join("\n")
"\n" <> output
end
end