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

Policies: how a body decides what to do with the space it is in.

Mirrors `tbp/monty/frameworks/models/motor_policies.py`, the largest file in
Monty's motor system. Ported so far:

  * `...MotorPolicies.NoGoalProvided` — **ported**: raised when a policy needs
    a goal and was given none;
  * `...MotorPolicies.SurfacePolicyTelemetry` — **ported**: why a surface
    policy chose what it chose;
  * `...MotorPolicies.PolicyStatus` — **ported**: `:ready` or `:in_progress`;
  * `...MotorPolicies.MotorPolicyResult` — **ported**: what one call decided;
  * `...MotorPolicies.RuntimeMotorPolicy` and `...MotorPolicies.MotorPolicy` —
    **ported**: the two interfaces, the second a policy that can also be
    reset;
  * `...MotorPolicies.BasePolicy` — **ported**: one sampled action, whatever
    the step looked like;
  * `...MotorPolicies.InformedPolicy` — **ported**: sample while the sensor is
    on the object, step back while it is not, and jump to a goal when one
    arrives;
  * `...MotorPolicies.SurfacePolicy` — **ported**: walk the surface of an
    object, and search for it when it is lost;
  * `...MotorPolicies.NaiveScanPolicy` — **ported**: scan the object in a
    square spiral, one leg longer each time;
  * `...MotorPolicies.InformedPolicyRandomWalk` — **ported**: `InformedPolicy`'s
    walk without the rest of it, which is the default the distant configs give
    their selector;
  * `...MotorPolicies.PredefinedPolicy` — **ported**: replay the actions written
    in a JSON file, cycled for ever;
  * `...MotorPolicies.JumpToGoal` — **ported**: teleport to a goal, and undo
    the teleport when it lands somewhere useless;
  * `undo_last_action/1` — **ported**: the module-level function — Monty calls
    it `fixme_undo_last_action` — that turns an action into its opposite.

Still planned: nothing — every class in Monty's `motor_policies.py` is ported.
`DistantPolicySelector`, the distant agent's selector, is ported under
`Monty.Frameworks.Models.MotorPolicySelectors`.

A policy here is a struct plus the module named after it, so a policy can be
stored in a selector as a value and stepped without a process — the same shape
as `Monty.Frameworks.Models.ObjectModel` and the goal generators.

# `depth_at_center`

```elixir
@spec depth_at_center(map(), atom() | String.t(), String.t()) :: float()
```

The depth at the centre pixel of a sensor's view.

Monty's `PositioningProcedure.depth_at_center`, a static method of the
positioning protocol; the function lives with the procedures now and this is a
delegation, so a policy can ask the question without reaching into the
environment layer (`docs/monty-differences.md` §2.7).

# `enforce_pi_bounds`

```elixir
@spec enforce_pi_bounds(float()) :: float()
```

The same angle, brought back between `-π` and `π`.

Monty's `enforce_pi_bounds`, as a loop of `±2π`: a plain `rem/2` would do it in
one step, but the loop is what the angles' meaning in Monty is, and it is what
the port follows.

# `from_config`

```elixir
@spec from_config(map()) :: Monty.Frameworks.Models.MotorPolicies.MotorPolicy.t()
```

The policy a config describes.

    policy:
      _target_: tbp.monty.frameworks.models.motor_policies.SurfacePolicyCurvatureInformed
      agent_id: agent_id_0
      alpha: 0.1
      action_sampler:
        _target_: ...ConstantSampler

The class decides which parameters are required: each policy's own `new/1`
fetches what it cannot do without, so a config that leaves one out fails here
rather than at the first step of the episode.

# `projected_angle_from_vec`

```elixir
@spec projected_angle_from_vec(Monty.Geometry.Rotation.vector()) :: float()
```

The orientation a direction in the agent's plane points in, in radians.

Monty's `projected_angle_from_vec`: `atan2(x, y)`, in that order, because theta
of zero means "along +y" — up in the agent's frame — rather than "along +x".

# `projected_vec_from_angle`

```elixir
@spec projected_vec_from_angle(float()) :: Monty.Geometry.Rotation.vector()
```

The direction in the agent's plane that an orientation points in.

Monty's `projected_vec_from_angle`. The quarter-turn offsets on `cos` and `sin`
are what put theta of zero on +y and keep a tangential step in the plane.

# `successful_jump`

```elixir
@spec successful_jump(struct()) :: struct()
```

Ask a policy what it does now that a jump has landed.

Monty calls `self._handle_successful_jump()`, which a subclass overrides;
here the struct's module is asked instead (`docs/monty-differences.md` §2.9).

# `theta_change`

```elixir
@spec theta_change(float(), float()) :: float()
```

The signed change from one orientation to another, in radians.

Monty's `theta_change`: a plain subtraction with the loop-around corrected, so
that 0 to `3π/2` is a quarter turn back rather than three quarters forward.

# `undo_last_action`

```elixir
@spec undo_last_action(Monty.Frameworks.Actions.Action.t()) ::
  Monty.Frameworks.Actions.Action.t()
```

The action that would undo `action`.

Mirrors `fixme_undo_last_action`. Only the six actions an informed policy
samples can be undone, and each is undone by itself with its amount negated —
a `LookDown` is undone by a `LookDown` of the opposite rotation, not by a
`LookUp`, because the policy is reversing a move it made rather than
commanding the agent. `MoveTangentially` keeps its direction and negates its
distance, which is what Monty's comment on that line says.

Monty's `fixme_` prefix is the project saying it would rather have
`Action.undo`; the name here leaves the complaint out and the doc keeps it.
Monty raises `TypeError` for anything else and so does this, as an
`ArgumentError`: a policy configured with an action it cannot undo has nothing
sensible to do with a percept that left the object. That is `docs/monty-differences.md`
§2.8, along with the rest of the vocabulary differences.

---

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