# `Monty.Frameworks.Models.MotorPolicies.InformedPolicy`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/frameworks/models/motor_policies/informed_policy.ex#L1)

A policy that reads the percept: stay on the object, or step back onto it.

Mirrors `tbp/monty/frameworks/models/motor_policies.py::InformedPolicy`, the
first policy that is worth running. It samples an action while the sensor is
on the object; when the sensor arrives somewhere with nothing under it, it
takes the *opposite* of the last action, which is Monty's way of getting back
onto the object without a map. The remembered action is inverted again each
time it is used, so a sensor that keeps leaving the object walks back and
forth across the edge instead of walking away.

## Jumps

With `:use_goal_driven_actions` it will also teleport — `SetAgentPose` at the
goal's location, facing along the goal's direction — which is how a goal
generator's "look at this part of the mug" reaches the body. A jump can land
in empty space or inside an object, so the next step judges it from the depth
at the centre of the view finder: more than a metre means nothing is there,
and the jump is undone with the pose and sensor rotation it started from.

Monty's `_handle_successful_jump` hook is `handle_successful_jump/1`, a no-op
here and overridden by `SurfacePolicy`. The jump step itself,
`goal_driven_actions/4`, is public for the same reason: `SurfacePolicy` is a
subclass in Monty and a second struct here, so it calls the shared step rather
than inheriting it (`docs/monty-differences.md` §2.9).

## What "empty space" means

`_should_undo_jump` reads `observations[agent_id][sensor_id]["depth"]` and
takes its centre pixel, as Monty's positioning procedure does. That is the
shape Monty's habitats report and the shape this reads; when an environment is
ported, the reader moves to `PositioningProcedures` where Monty keeps it
(`docs/monty-differences.md` §2.7).

# `jump_state`

```elixir
@type jump_state() :: %{
  :agent_id =&gt; atom() | String.t(),
  :use_goal_driven_actions =&gt; boolean(),
  :is_jumping =&gt; boolean(),
  :is_undoing_jump =&gt; boolean(),
  :pre_jump_state =&gt;
    Monty.Frameworks.Models.MotorSystemState.AgentState.t() | nil,
  :undo_jump_actions =&gt; [Monty.Frameworks.Actions.Action.t()],
  optional(atom()) =&gt; term()
}
```

A policy that can be driven by goals: the fields the jump machinery reads.

Both policies have them, because Monty gives every subclass the jump state
through inheritance; here they are matched as a map instead
(`docs/monty-differences.md` §2.9).

# `t`

```elixir
@type t() :: %Monty.Frameworks.Models.MotorPolicies.InformedPolicy{
  action_sampler: Monty.Frameworks.Actions.ActionSamplers.ActionSampler.t(),
  agent_id: atom() | String.t(),
  is_jumping: boolean(),
  is_undoing_jump: boolean(),
  pre_jump_state: Monty.Frameworks.Models.MotorSystemState.AgentState.t() | nil,
  rng: :rand.state() | nil,
  undo_action: Monty.Frameworks.Actions.Action.t() | nil,
  undo_jump_actions: [Monty.Frameworks.Actions.Action.t()],
  use_goal_driven_actions: boolean()
}
```

# `call`

```elixir
@spec call(
  t(),
  Monty.Context.t(),
  map() | nil,
  Monty.Frameworks.Models.MotorSystemState.t(),
  Monty.Cmp.Message.t() | nil,
  Monty.Cmp.Goal.t() | nil
) :: {Monty.Frameworks.Models.MotorPolicies.MotorPolicyResult.t(), t()}
```

One step: a jump if one was asked for, otherwise a sample or a step back.

A jump that is still in flight is judged here first: undoing it takes the
whole step, and landing on something means the sample can continue as usual.

# `goal_driven_actions`

```elixir
@spec goal_driven_actions(jump_state(), map() | nil, map(), Monty.Cmp.Goal.t() | nil) ::
  {:result, Monty.Frameworks.Models.MotorPolicies.MotorPolicyResult.t(),
   jump_state()}
  | {:continue, jump_state()}
```

One step's worth of goal-driven work: judge a jump in flight, or start one.

Answers `{:result, result, policy}` when the step is decided here, and
`{:continue, policy}` when the caller should go on to its own behaviour.

# `handle_successful_jump`

```elixir
@spec handle_successful_jump(t()) :: t()
```

What to do once a jump has landed with the object in view.

Monty's `_handle_successful_jump`, a no-op here and overridden by
`SurfacePolicy`; the policy's own module is asked, because a struct cannot
override a private function (`docs/monty-differences.md` §2.9).

# `new`

```elixir
@spec new(keyword()) :: t()
```

A policy that samples for `:agent_id`, and jumps when `:use_goal_driven_actions`.

# `reset`

```elixir
@spec reset(jump_state()) :: jump_state()
```

Starts the next episode: no jump in flight, and no action to undo.

# `set_agent_pose_from_goal`

```elixir
@spec set_agent_pose_from_goal(
  %{:agent_id =&gt; atom() | String.t(), optional(atom()) =&gt; term()},
  Monty.Cmp.Goal.t()
) :: Monty.Frameworks.Actions.SetAgentPose.t()
```

The pose that looks along the goal's direction, from the goal's location.

Monty's `_derive_set_agent_pose_from_goal`, in both `InformedPolicy` and
`JumpToGoal`.

# `shared_sensor_rotation`

```elixir
@spec shared_sensor_rotation(%{sensors: map()}) :: Monty.Geometry.Rotation.t()
```

The orientation every sensor shares, or a refusal to plan a jump.

Every sensor an action moves is moved at once, so they all keep the
orientation the jump was planned in. Monty asserts it rather than assuming it,
and so does this — with the empty case spelled out, which Monty's loop does not
(`docs/monty-differences.md` §1.5). Public because `JumpToGoal` needs the same check
and Monty writes it out twice (§2.9).

# `state_dict`

```elixir
@spec state_dict(t()) :: map()
```

Nothing to report: what it knows is the action it would undo.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
