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

A policy that replays a written-down sequence of actions, for ever.

Mirrors `tbp/monty/frameworks/models/motor_policies.py::PredefinedPolicy`.
One action per line of a JSON file, read once at construction and then cycled:
step `n` of the episode is action `n` of the file, wrapped round. It exits
because unit tests and recorded-session experiments need a body that moves
the same way twice, and no policy can promise that as simply as a list.

The file is read with `ActionJSONDecoder`, the same decoder an action crosses
a wire with, so a file is Monty's own action serialisation and nothing bespoke.

Monty indexes with `bounding % len(action_list)`, so a file with no actions
fails on the first step with a division by zero. This port refuses at
construction instead — same failure, one step earlier, and a message that says
which file.

# `t`

```elixir
@type t() :: %Monty.Frameworks.Models.MotorPolicies.PredefinedPolicy{
  action_list: [Monty.Frameworks.Actions.Action.t()],
  agent_id: atom() | String.t(),
  episode_step: non_neg_integer()
}
```

# `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()}
```

The next action of the file, and the policy that has taken one more step.

The percept, the state, the observations and the goal are all ignored: the
sequence is the whole of what this policy knows.

# `new`

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

A replay for `:agent_id` from the actions written in `:file`.

`:file` is a path with one JSON action per line, blank lines allowed.

# `read_action_file`

```elixir
@spec read_action_file(Path.t()) :: [Monty.Frameworks.Actions.Action.t()]
```

The actions in `file`, in the order they are written.

A blank line is not an action. A line that is not an action is an error naming
the line, because a replay that silently skipped one would be a different
experiment.

# `reset`

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

Starts the next episode at the first action of the file.

# `state_dict`

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

How far into the file the replay has got.

---

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