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)