defmodule MarsExplorer.InstructionsRunner do @moduledoc """ Module responsible for running the exploration instructions """ use Agent alias MarsExplorer.{HighlandGrid, ExplorationsController, Exploration} @doc """ Run a set of exploration instructions ## Example iex> instructions = [{:grid, %{north: 5, east: 5}}, {:place, %{north: 0, east: 0, direction: :north}}, [:move, :move]] [{:grid, %{north: 5, east: 5}}, {:place, %{north: 0, east: 0, direction: :north}}, [:move, :move]] iex> MarsExplorer.InstructionsRunner.run instructions [ %MarsExplorer.Exploration{ probe: %MarsExplorer.Probe{ north: 2, east: 0, direction: :north }, grid: %MarsExplorer.HighlandGrid{ north_limit: 5, east_limit: 5 } } ] """ @spec run([ {:grid, %{north: integer(), east: integer()}} | {:place, Exploration.placement()} | [:move | :turn_left | :turn_right] ]) :: [Exploration.t()] def run([{:grid, grid} | rest]) do grid = HighlandGrid.build(grid) ExplorationsController.set_grid(grid) run(rest) end def run([{:place, placement} | rest]) do grid = ExplorationsController.get_grid() if grid do case Exploration.place(grid, placement) do {:ok, exploration} -> ExplorationsController.set_running_exploration(exploration) run(rest, exploration) {:error, :invalid_placement} -> run(rest) end else run(rest) end end def run([_instruction | rest]), do: run(rest) def run([]), do: nil defp run([{:invalid, _instruction} | rest], exploration), do: run(rest, exploration) defp run([instructions | rest], exploration) when is_list(instructions) do new_exploration = execute_movements(instructions, exploration) ExplorationsController.update_running_exploration(new_exploration) run(rest, new_exploration) end defp run([{:place, _placement} | _rest] = instructions, exploration) do ExplorationsController.update_running_exploration(exploration) run(instructions) end defp run([], exploration) do ExplorationsController.update_running_exploration(exploration) ExplorationsController.get_running_exploration() |> ExplorationsController.add_exploration_to_explorations_list() explorations = ExplorationsController.get_explorations() ExplorationsController.clean_explorations() explorations end defp execute_movements([:move | rest], exploration) do new_exploration = exploration |> Exploration.move() |> case do {:ok, exploration} -> exploration {:error, :at_grid_limit} -> exploration end ExplorationsController.update_running_exploration(new_exploration) execute_movements(rest, new_exploration) end defp execute_movements([:turn_left | rest], exploration) do {:ok, new_exploration} = exploration |> Exploration.turn_left() ExplorationsController.update_running_exploration(new_exploration) execute_movements(rest, new_exploration) end defp execute_movements([:turn_right | rest], exploration) do {:ok, new_exploration} = exploration |> Exploration.turn_right() ExplorationsController.update_running_exploration(new_exploration) execute_movements(rest, new_exploration) end defp execute_movements([], exploration) do ExplorationsController.update_running_exploration(exploration) exploration end end