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

Day 4: The Ideal Stocking Stuffer

2015/4.livemd

Day 4: The Ideal Stocking Stuffer

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

Modules

defmodule AdventCoinMiner do
  @moduledoc """
  Functions to mine Advent Coins.
  """

  @doc """
  Finds the first successful index for mining, given the secret and difficulty. 

  ## Example 
  ```elixir
  iex> AdventCoinMiner.mine("hello", 2)
  33
  ```
  """
  @spec mine(String.t(), pos_integer()) :: pos_integer()
  def mine(secret, difficulty) do
    Stream.unfold(1, &{hash(secret, &1), &1 + 1})
    |> Enum.find(&success?(&1, difficulty))
    |> elem(0)
  end

  # returns a tuple {index, hash} using the md5 hashing algorithm
  defp hash(secret, index) do
    create_nonce(secret, index)
    |> then(fn nonce -> Base.encode16(:crypto.hash(:md5, nonce)) end)
    |> then(&{index, &1})
  end

  # checks the first n (difficulty) characters of the hash for zeroes
  defp success?(hash, difficulty) do
    {_, value} = hash
    String.starts_with?(value, String.duplicate("0", difficulty))
  end

  # concatenates the secret and current index
  defp create_nonce(secret, index), do: secret <> Integer.to_string(index)
end

Input

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

Part 1

input
|> Kino.Input.read()
|> AdventCoinMiner.mine(5)

Part 2

input
|> Kino.Input.read()
|> AdventCoinMiner.mine(6)