DiscordPHP-NHA Documentation

Ladder
in package

FinalYes

The deterministic decision ladder — "what would a sensible agent do here?" computed straight from an observation, no LLM. Two entry points:

  • suggestion() — the full ladder (defend → finish parts → arm → stance nudge → one speculative combine → build → stockpile → sell a glut → reposition). PromptBuilder shows its pick to the model as the "SUGGESTED next action" anchor, and AutoPlayer falls back to it whenever the model's pick is refused by a guardrail.
  • defensiveAction() — just rung 0 (combat), which AutoPlayer applies as a hard override before it ever consults the model.

Split out of AgentBrain (which is now only the LLM round-trip) so the fallback logic has one home and its callers stop reaching into that class's statics. The rungs are diagrammed in docs/PLAYBOOK.md; keep it in sync.

Tags
since
3.1.34

Table of Contents

Constants

CREDIT_FLOOR  : mixed = 300
Economy targets for the ladder.
HOARD_CAP  : mixed = 80
RESEARCH_SURPLUS  : mixed = 60
Research only fires when at least two raws sit this deep — a genuine surplus, not the stockpile.
RESOURCE_TARGET  : mixed = 30

Methods

defensiveAction()  : array{verb: string, args: array, reason: string}|null
The defensive move when the agent is in (or just came out of) a fight: heal if badly hurt and able, shoot back if armed and the attacker is in range, otherwise break contact. Returns `null` when there is no threat.
suggestion()  : array{verb: string, args: array, why: string}|null
A deterministic "what would the ladder do" pick, surfaced to anchor a weak model. Mirrors {@see Playbook}'s priorities: finish parts → gamble one novel combine → build for reliable points → sell a glut → harvest only when short → reposition. Returns `null` when nothing is obviously right (the model is then on its own).
armMove()  : array{verb: string, args: array, why: string}|null
Rung 1c: buy a minimum survival kit when out of combat and unequipped — a medicine first, then a weapon, then ammo for it. Returns `null` once the kit is covered or the credits are too thin.
bestMedicine()  : string|null
Best self-heal medicine on hand, strongest first, or `null`.
combatMove()  : array{verb: string, args: array, why: string}|null
Rung 0: the defensive move when the agent is in (or just came out of) a fight — heal if badly hurt and able, shoot back if armed and the attacker is in range, otherwise break contact. Returns `null` when there is no threat.
stanceMove()  : array{verb: string, args: array, why: string}|null
Rung 1d: a deterministic nudge toward the active stance, ahead of the generic ladder. Returns `null` for `homestead` (the generic ladder already plays it) and whenever the stance has nothing pressing to add this turn.

Constants

CREDIT_FLOOR

Economy targets for the ladder.

public mixed CREDIT_FLOOR = 300
  • CREDIT_FLOOR — keep at least this many credits; only sell to climb back to it, or to fund a project (buy-to-build / invest).
  • RESOURCE_TARGET — stockpile each raw up to here (harvest / walk toward deposits until reached); never sell below it outside a credit emergency.
  • HOARD_CAP — the one non-credit sell trigger: shed the excess above this so a "never sell" rule cannot deadlock into mining forever.

HOARD_CAP

public mixed HOARD_CAP = 80

RESEARCH_SURPLUS

Research only fires when at least two raws sit this deep — a genuine surplus, not the stockpile.

public mixed RESEARCH_SURPLUS = 60

RESOURCE_TARGET

public mixed RESOURCE_TARGET = 30

Methods

defensiveAction()

The defensive move when the agent is in (or just came out of) a fight: heal if badly hurt and able, shoot back if armed and the attacker is in range, otherwise break contact. Returns `null` when there is no threat.

public static defensiveAction(array<string, mixed> $raw) : array{verb: string, args: array, reason: string}|null

Public so AutoPlayer can apply it as a hard override — combat defence never waits on the LLM.

Parameters
$raw : array<string, mixed>

The normalised observation.

Return values
array{verb: string, args: array, reason: string}|null

suggestion()

A deterministic "what would the ladder do" pick, surfaced to anchor a weak model. Mirrors {@see Playbook}'s priorities: finish parts → gamble one novel combine → build for reliable points → sell a glut → harvest only when short → reposition. Returns `null` when nothing is obviously right (the model is then on its own).

public static suggestion(array<string, mixed> $raw, array<string, bool> $tried[, array<string, bool> $worldKnown = [] ][, bool $allowSpeculation = true ][, string $stance = 'homestead' ]) : array{verb: string, args: array, why: string}|null

Also reused by AutoPlayer as the infrastructure fallback when a research combine is refused — pass the whole tried+known combine space as both $tried and $worldKnown and the ladder skips its speculative-combine rung and drops straight to build / wealth / harvest.

Parameters
$raw : array<string, mixed>

The normalised observation.

$tried : array<string, bool>

a+b => true for combine sets submitted THIS session.

$worldKnown : array<string, bool> = []

a+b => true for sets the whole world has already invented.

$allowSpeculation : bool = true

When false, the speculative-combine rung is skipped entirely — used by the infrastructure fallback, where research is finished.

$stance : string = 'homestead'
Return values
array{verb: string, args: array, why: string}|null

armMove()

Rung 1c: buy a minimum survival kit when out of combat and unequipped — a medicine first, then a weapon, then ammo for it. Returns `null` once the kit is covered or the credits are too thin.

private static armMove(array<string, mixed> $inv, int $credits) : array{verb: string, args: array, why: string}|null
Parameters
$inv : array<string, mixed>
$credits : int
Return values
array{verb: string, args: array, why: string}|null

bestMedicine()

Best self-heal medicine on hand, strongest first, or `null`.

private static bestMedicine(array<string|int, mixed> $inv) : string|null
Parameters
$inv : array<string|int, mixed>
Return values
string|null

combatMove()

Rung 0: the defensive move when the agent is in (or just came out of) a fight — heal if badly hurt and able, shoot back if armed and the attacker is in range, otherwise break contact. Returns `null` when there is no threat.

private static combatMove(array<string, mixed> $raw, array<string, mixed> $inv, float $hp, float $hpMax, int $x, int $y, int $tick) : array{verb: string, args: array, why: string}|null
Parameters
$raw : array<string, mixed>
$inv : array<string, mixed>
$hp : float
$hpMax : float
$x : int
$y : int
$tick : int
Return values
array{verb: string, args: array, why: string}|null

stanceMove()

Rung 1d: a deterministic nudge toward the active stance, ahead of the generic ladder. Returns `null` for `homestead` (the generic ladder already plays it) and whenever the stance has nothing pressing to add this turn.

private static stanceMove(string $stance, array<string, mixed> $raw, array<string, mixed> $inv, array<string, int> $raws, int $credits, int $x, int $y) : array{verb: string, args: array, why: string}|null
Parameters
$stance : string
$raw : array<string, mixed>
$inv : array<string, mixed>
$raws : array<string, int>
$credits : int
$x : int
$y : int
Return values
array{verb: string, args: array, why: string}|null
On this page

Search results