# `Monty.Runtime.Positioning`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/runtime/positioning.ex#L1)

Puts the agent in front of the object before an episode's first step.

Mirrors the positioning half of `ExperimentEnvironment.pre_episode` in
`tbp/monty/experiment/environment.py`: for each configured procedure, build it
for the episode's target object, ask the world what is visible, and keep
stepping the world with the actions the procedure answers until it is finished.
Only the *last* procedure's verdict is kept — Monty's rule, because the
procedures are a pipeline (walk towards the object, then aim at it), not
alternatives.

## Pure, like the rest of the port

Monty's loop mutates the environment and the procedure as it goes. Here the
world is threaded through explicitly — `observe.(world, actions)` answers the
observation, the agent's pose and the **next** world — and so is the procedure,
so a caller can watch the whole prologue happen without a process.

## What a failed positioning means

Monty raises when a single-object scene never gets the centre pixel onto the
target: the episode was set up wrong, and a run that starts anyway measures
nothing. This answers an error, so a job can report it rather than crash a
supervisor.

## The step limit is the port's

Monty's loop has no bound, and a procedure that never finishes hangs the
experiment. This one stops after `:max_steps` — Monty's own procedures finish in
a few dozen steps — and says so. The bound only fires where Monty would hang.

# `observe`

```elixir
@type observe() :: (term(), [Monty.Frameworks.Actions.Action.t()] -&gt;
                {map(), map(), term()})
```

A world and actions in; the observation, the agent's pose and the next world out.

# `outcome`

```elixir
@type outcome() :: %{
  steps: non_neg_integer() | nil,
  success: boolean(),
  procedures: [String.t() | atom()]
}
```

What positioning did: whether the last procedure ended in a good view, and how
many steps it took.

`steps` is `nil` when the world positioned the episode itself and did not
report how much it moved — a world that ran Monty's `pre_episode` did the work,
and inventing a number for it here would be worse than saying nothing.

# `prepare`

```elixir
@spec prepare(
  [Monty.Frameworks.Environments.PositioningProcedures.GetGoodViewFactory.t()],
  integer(),
  term(),
  observe(),
  keyword()
) :: {:ok, term(), outcome()} | {:error, term()}
```

Steps `world` until every procedure has finished.

Options:

  * `:distractors` — how many other objects the scene has. A single-object scene
    the procedure never reaches the target in is an error; a cluttered one only
    needs the procedure to give up.
  * `:max_steps` — the bound described in the moduledoc.

No procedures is Monty's "positioning is not configured": the world comes back
untouched and nothing is stepped.

---

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