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

Advent of Code 2015 Day 11 Part 2

2015_day11_part2.livemd

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(&amp;from_alphabet_num.(&amp;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.()