Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 10: Elves Look, Elves Say

2015/10.livemd

Day 10: Elves Look, Elves Say

Mix.install([
  {:kino, "~> 0.12.3"}
])

Input

input = Kino.Input.textarea("Please paste your puzzle input:")

Shared Modules

defmodule LookAndSay do
  @doc """
  Given a string, returns the next string in the look and say sequence.

  The accumulator has shape {res, {count, ch}} to keep track of repeated 
  occurrences of a character, where res is the resulting string. 

  ## Example
  iex> LookAndSay.next("3224")
  "132214"
  """
  def next(str) do
    str
    |> String.graphemes()
    |> Enum.reduce({"", {0, ""}}, &process_char/2)
    |> finalise()
  end

  @doc """
  Base case for LookAndSay.next/2 when count is zero.
  """
  def next(str, 0) do
    str
  end

  @doc """
  Iterates on LookAndSay.next/1 for a given positive integer
  """
  def next(str, count) do
    next(str)
    |> next(count - 1)
  end

  # base case for first call to reducer
  defp process_char(char, {"", {0, ""}}) do
    {"", {1, char}}
  end

  # when current char being processed is the same as previous
  defp process_char(char, {res, {count, l}}) when char == l do
    {res, {count + 1, l}}
  end

  # when current char being processed is different to previous
  defp process_char(char, {res, {count, l}}) do
    {res <> Integer.to_string(count) <> l, {1, char}}
  end

  # takes final accumulator from LookAndSay.next/2 and 
  # concatenates needed characters
  defp finalise({res, {count, l}}) do
    res <> Integer.to_string(count) <> l
  end
end

Part 1

We use LookAndSay.next/2 to iterate our input 40 times. Once we have the string we can count the characters using String.length/1.

input
|> Kino.Input.read()
|> LookAndSay.next(40)
|> String.length()

Part 2

The same as part 1 but with 50 iterations.

input
|> Kino.Input.read()
|> LookAndSay.next(50)
|> String.length()