Advent of Code 2015 Day 11 Part 2
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Get Inputs
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2015", "11", System.fetch_env!("LB_SESSION"))
My answer
has_straight_str = fn str ->
Regex.match?(
~r/(?=abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)/,
str
)
end
has_no_iol = fn str ->
Regex.match?(~r/^[^iol]*$/, str)
end
has_double_double = fn str ->
Regex.match?(~r/(.)\1.*(.)\2/, str)
end
valid_password? = fn password ->
has_straight_str.(password) && has_no_iol.(password) && has_double_double.(password)
end
to_alphabet_num = fn str ->
<<code>> = str
code - 97
end
from_alphabet_num = fn num ->
<<(num + 97)>>
end
skip_iol = fn password ->
index_map =
["i", "o", "l"]
|> Enum.reduce(%{}, fn char, index_map ->
case :binary.match(password, char) do
{index, _} -> Map.put(index_map, char, index)
_ -> index_map
end
end)
if index_map == %{} do
password
else
index_map
|> Enum.min_by(fn {_, index} -> index end)
|> then(fn {char, index} ->
password
|> String.codepoints()
|> Enum.with_index()
|> Enum.map(fn {code, code_index} ->
cond do
code_index == index ->
case char do
"i" -> "j"
"o" -> "p"
"l" -> "m"
_ -> code
end
code_index > index ->
"a"
true ->
code
end
end)
|> Enum.join()
end)
end
end
next_password = fn current ->
current
|> String.codepoints()
|> Enum.reverse()
|> Enum.with_index()
|> Enum.reduce(0, fn {str, digit}, num ->
num + to_alphabet_num.(str) * (26**digit)
end)
|> Kernel.+(1)
|> Integer.digits(26)
|> Enum.map(&from_alphabet_num.(&1))
|> Enum.join()
|> skip_iol.()
end
find_new_password = fn current_password ->
0..10000000
|> Enum.reduce_while(current_password, fn _, password ->
new_password = next_password.(password)
if valid_password?.(new_password) do
{:halt, new_password}
else
{:cont, new_password}
end
end)
end
puzzle_input
|> find_new_password.()
|> find_new_password.()