defmodule Navigator do @doc """ Count steps for the first part. # Examples iex> Navigator.count_steps(["R"], %{"AAA" => ["AAA", "ZZZ"], "ZZZ" => ["ZZZ", "ZZZ"]}) 1 """ @spec count_steps(list(String.t()), map()) :: integer() def count_steps(instructions, maps) do count_steps(instructions, instructions, maps, "AAA", 0) end defp count_steps(full_instruction_list, [dir | next_instructions], maps, state, steps) do new_state = pick_destination(maps[state], dir) case new_state do "ZZZ" -> steps + 1 _ -> # Go to the beginning of the list of instructions if we are at the end. next_instructions = case next_instructions do [] -> full_instruction_list _ -> next_instructions end count_steps(full_instruction_list, next_instructions, maps, new_state, steps + 1) end end @doc """ Count steps for the second part. # Examples iex> Navigator.count_ghost_steps(["R"], %{"BBA" => ["BBA", "BBZ"], "BBZ" => ["BBZ", "BBZ"]}) 1 """ def count_ghost_steps(instructions, maps) do maps |> Map.keys() |> Stream.filter(fn x -> String.ends_with?(x, "A") end) |> Stream.map(fn x -> count_ghost_steps(instructions, instructions, maps, x, 0) end) # compute least common multiple |> Enum.reduce(1, fn x, acc -> Math.lcm(x, acc) end) end defp count_ghost_steps(full_instruction_list, [dir | next_instructions], maps, state, steps) do new_state = pick_destination(maps[state], dir) if String.ends_with?(new_state, "Z") do steps + 1 else # Go to the beginning of the list of instructions if we are at the end. next_instructions = case next_instructions do [] -> full_instruction_list _ -> next_instructions end count_ghost_steps(full_instruction_list, next_instructions, maps, new_state, steps + 1) end end @doc """ Pick destination. # Examples iex> Navigator.pick_destination(["AAA", "BBB"], "L") "AAA" iex> Navigator.pick_destination(["AAA", "BBB"], "R") "BBB" """ def pick_destination(destinations, direction) do case direction do "L" -> hd(destinations) "R" -> hd(tl(destinations)) end end end