## ~sgiath/aoc-2018

9c6648a29f7f3b4a93876449528597be0fe5d79f — Sgiath 2 years ago
```Add comments and structure code
```
```3 files changed, 114 insertions(+), 32 deletions(-)

M lib/day01/solution.ex
M lib/day02/solution.ex
```
`M lib/day01/solution.ex => lib/day01/solution.ex +2 -13`
```@@ 33,12 33,6 @@ defmodule AdventOfCode.Day01 do

Starting with a frequency of zero, what is the resulting frequency after all of
the changes in frequency have been applied?
-
-  ## Example
-
-      497
-
"""
@spec part1(input :: File.Stream.t()) :: integer()
def part1(input) do

@@ 74,12 68,6 @@ defmodule AdventOfCode.Day01 do
- +7, +7, -2, -7, -4 first reaches 14 twice.

What is the first frequency your device reaches twice?
-
-  ## Example
-
-      558
-
"""
@spec part2(input :: File.Stream.t()) :: integer()
def part2(input) do

@@ 97,8 85,9 @@ defmodule AdventOfCode.Day01 do
end)
end

+  @doc "Convert lines of file to list of integers"
@spec convert_file(file_stream :: File.Stream.t()) :: Enumerable.t()
-  defp convert_file(file_stream) do
+  def convert_file(file_stream) do
file_stream
|> Stream.map(&Integer.parse/1)
|> Stream.map(fn {num, _trailing} -> num end)

```
`M lib/day02/solution.ex => lib/day02/solution.ex +109 -18`
```@@ 1,34 1,125 @@
+  @moduledoc """
+  Advent of Code - Box IDs - 2th December
+
+
+  Author: Filip Vavera <FilipVavera@sgiath.net>
+  """
+
+  @doc """
+  Late at night, you sneak to the warehouse - who knows what kinds of paradoxes you
+  could cause if you were discovered - and use your fancy wrist device to quickly scan
+  every box and produce a list of the likely candidates (your puzzle input).
+
+  To make sure you didn't miss any, you scan the likely candidate boxes again, counting
+  the number that have an ID containing exactly two of any letter and then separately
+  counting those with exactly three of any letter. You can multiply those two counts
+  together to get a rudimentary checksum and compare it to what your device predicts.
+
+  For example, if you see the following box IDs:
+
+    - abcdef contains no letters that appear exactly two or three times.
+    - bababc contains two a and three b, so it counts for both.
+    - abbcde contains two b, but no letter appears exactly three times.
+    - abcccd contains three c, but no letter appears exactly two times.
+    - aabcdd contains two a and two d, but it only counts once.
+    - abcdee contains two e.
+    - ababab contains three a and three b, but it only counts once.
+
+  Of these box IDs, four of them contain a letter which appears exactly twice, and
+  three of them contain a letter which appears exactly three times. Multiplying these
+  together produces a checksum of 4 * 3 = 12.
+
+  What is the checksum for your list of box IDs?
+  """
+  @spec part1(Stream.t()) :: integer()
def part1(input) do
-    {pairs, tripples} =
-      Enum.reduce(input, {0, 0}, fn id, {pairs, tripples} ->
-        {pair, tripple} =
-          id
-          |> String.to_charlist()
-          |> Enum.reduce(%{}, fn char, acc -> Map.update(acc, char, 1, &(&1 + 1)) end)
-          |> Enum.reduce({0, 0}, fn
-            {_char, 2}, {_pairs, x} -> {1, x}
-            {_char, 3}, {x, _tripples} -> {x, 1}
-            _, acc -> acc
-          end)
-
-        {pair + pairs, tripple + tripples}
+    # Compute number of words containing pairs and triples
+    {pairs, triples} =
+      Enum.reduce(input, {0, 0}, fn id, {pairs, triples} ->
+        # Get pair and triple for one word
+        {pair, triple} = get_pair_and_triple(id)
+
+        # Sum it with accumulator
+        {pair + pairs, triple + triples}
end)

-    pairs * tripples
+    # Compute final checksum
+    pairs * triples
end

+  @doc "Compute if string contains pair or triple"
+  @spec get_pair_and_triple(string :: String.t()) :: {0 | 1, 0 | 1}
+  def get_pair_and_triple(string) when is_binary(string) do
+    string
+    |> String.to_charlist()
+    # Count occurrence of different characters in ID
+    |> Enum.reduce(%{}, fn char, acc -> Map.update(acc, char, 1, &(&1 + 1)) end)
+    # Count pairs and triples
+    |> Enum.reduce({0, 0}, &pt_counts/2)
+  end
+
+  @doc "Reducer which counts if pair or triple occurred"
+  @spec pt_count(counts :: {char(), integer()}, accumulator :: {0 | 1, 0 | 1}) :: {0 | 1, 0 | 1}
+  def pt_counts(counts, accumulator \\ {0, 0})
+  def pt_counts({_char, 2}, {_pairs, tripples}), do: {1, tripples}
+  def pt_counts({_char, 3}, {pairs, _tripples}), do: {pairs, 1}
+  def pt_counts(_counts, accumulator), do: accumulator
+
+  @doc """
+  Confident that your list of box IDs is complete, you're ready to find the boxes full
+  of prototype fabric.
+
+  The boxes will have IDs which differ by exactly one character at the same position
+  in both strings. For example, given the following box IDs:
+
+    - abcde
+    - fghij
+    - klmno
+    - pqrst
+    - fguij
+    - axcye
+    - wvxyz
+
+  The IDs abcde and axcye are close, but they differ by two characters (the second and
+  fourth). However, the IDs fghij and fguij differ by exactly one character, the third
+  (h and u). Those must be the correct boxes.
+
+  What letters are common between the two correct box IDs? (In the example above, this
+  is found by removing the differing character from either ID, producing fgij.)
+  """
+  @spec part2(input :: Stream.t()) :: String.t()
def part2(input) do
input
-    |> Enum.to_list()
+    # Convert to actual list and also every string ID to charlist
+    |> Enum.map(&String.to_charlist/1)
|> boxes()
end

+  @doc "Find similar ID for the first ID in the list. If not found call recursively"
+  @spec boxes(list :: list(charlist())) :: String.t()
-    if correct_box = Enum.find
+    Enum.find_value(tail, &similar(&1, head)) || boxes(tail)
end

-  defp difference(x, y) do
-    true
+  @doc "Decide if two words are similar - different in just one char"
+  @spec similar(word1 :: charlist(), word2 :: charlist()) :: String.t() | nil
+  def similar(word1, word2) do
+    word1
+    |> Enum.zip(word2)
+    |> Enum.split_with(fn {bit1, bit2} -> bit1 == bit2 end)
+    |> case do
+      # Different chars is list with one element
+      {same, [_char] = _different} ->
+        # Convert zipped charlists to string
+        same
+        |> Enum.map(fn {x, _} -> x end)
+        |> List.to_string()
+
+      # Everything else is invalid
+      _ ->
+        nil
+    end
end
end

```
`M lib/mix/tasks/advent.ex => lib/mix/tasks/advent.ex +3 -1`
```@@ 34,7 34,9 @@ defmodule Mix.Tasks.Advent do

defp execute(day, part) do