Day 2: I Was Told There Would Be No Math
Mix.install([
{:kino, "~> 0.12.3"}
])
Modules
defmodule Present do
@moduledoc """
Functions for calculating the various material requirements of
decorating presents.
"""
@typedoc "A box"
@type box :: {pos_integer(), pos_integer(), pos_integer()}
@doc """
Calculates the total wrapping paper needed for a list of boxes.
## Example
```elixir
iex> Present.total_wrapping_paper([{2, 3, 4}, {1, 1, 5}])
81
```
"""
@spec total_wrapping_paper(list(box())) :: pos_integer()
def total_wrapping_paper(boxes) do
boxes
|> Stream.map(&wrapping_paper/1)
|> Enum.sum()
end
@doc """
Calculates the total ribbon needed for a list of boxes.
## Example
```elixir
iex> Present.total_ribbon([{2, 3, 4}, {1, 1, 5}])
43
```
"""
@spec total_ribbon(list(box())) :: pos_integer()
def total_ribbon(boxes) do
boxes
|> Stream.map(&ribbon/1)
|> Enum.sum()
end
# the wrapping paper needed for a box
defp wrapping_paper(box) do
surface_area(box) + smallest_side_area(box)
end
# the ribbon needed for a box
defp ribbon(box) do
volume(box) + smallest_perimeter(box)
end
# the surface area of a box
defp surface_area(box) do
{l, w, h} = box
2 * l * w + 2 * l * h + 2 * w * h
end
# the area of the smallest side of a box
defp smallest_side_area(box) do
{l, w, h} = box
Enum.min([l * w, l * h, w * h])
end
# the volume of a box
defp volume(box) do
{l, w, h} = box
l * w * h
end
# the minimal perimeter of a box
defp smallest_perimeter(box) do
{l, w, h} = box
Enum.min([2 * (l + w), 2 * (l + h), 2 * (w + h)])
end
end
defmodule Parser do
@moduledoc """
For parsing a string into a list of box types.
"""
@doc ~S"""
Takes a string of newline separated dimensions and
returns a list of boxes.
## Example
```elixir
iex> Parser.parse("4x2x8\n5x6x7")
[{4, 2, 8}, {5, 6, 7}]
```
"""
@spec parse(String.t()) :: list(Present.box())
def parse(str) do
str
|> String.split("\n")
|> Enum.map(&parse_line/1)
end
# parse a line eg. "5x2x3" maps to {5, 2, 3}
defp parse_line(str) do
str
|> String.split("x")
|> Enum.map(&String.to_integer/1)
|> List.to_tuple()
end
end
Input
input = Kino.Input.textarea("Please paste your puzzle input:")
Part 1
input
|> Kino.Input.read()
|> Parser.parse()
|> Present.total_wrapping_paper()
Part 2
input
|> Kino.Input.read()
|> Parser.parse()
|> Present.total_ribbon()