Current section
Files
Jump to
Current section
Files
lib/core/env.ex
defmodule Gyx.Core.Env do
@moduledoc """
This behaviour is intended to be followed for any `Environment` implementation
The most critical function to be exposed is `step/1` , which serves as a direct bridge
between the environment and any agent.
Here, an important design question to address is the fundamental difference between
the environment state (its internal representation) and an _observation_ of such state.
In principle, the environment returns an observation as part of step/1 response.
Should it be a way to obtain an evironment state abstraction as suposed to be shown
to an agent? i.e. an indirect observation.
"""
alias Gyx.Core.Exp
@type initial_state :: Exp.t()
@type observation :: any()
@type action :: any()
@type environment :: any()
@doc "Sets the state of the environment to its default"
@callback reset(environment) :: initial_state()
@doc "Gets an environment representation usable by the agent"
@callback observe(environment) :: observation()
@doc """
Recieves an agent's `action` and responds to it,
informing the agent back with a reward, a modified environment
and a termination signal
"""
@callback step(environment, action()) :: Exp.t() | {:error, reason :: String.t()}
defmacro __using__(_params) do
quote do
@before_compile Gyx.Core.Env
@behaviour Gyx.Core.Env
@enforce_keys [:action_space, :observation_space]
@impl true
def observe(environment), do: GenServer.call(environment, :observe)
@impl true
def step(environment, action) do
case action_checked = GenServer.call(environment, {:check, action}) do
{:error, _} -> action_checked
{:ok, action} -> GenServer.call(environment, {:act, action})
end
end
end
end
defmacro __before_compile__(_) do
quote do
def handle_call({:check, action}, _from, state = %__MODULE__{action_space: action_space}) do
case Gyx.Core.Spaces.contains?(action_space, action) do
false -> {:reply, {:error, "invalid_action"}, state}
_ -> {:reply, {:ok, action}, state}
end
end
end
end
end