The thief-chase serves the blow, not the nearness

An automaton beside the wizard carrying its gold shuttled on and off
their square until every move was spent — the chase goal flipped
between "the thief's cell" and "the gold" with each step, because it
keyed on whether the thief stood underfoot. The chase now claims the
march only while the blow it exists to deliver is still live (an
attack in hand, the turn's attack unspent, casting open): those gates
hold still as the clockwork walks, so the goal cannot flip mid-march.
Caught with the strike live, it stands its ground; otherwise the gold
has its legs for the turn. A regression walks the exact scene and
forbids the A-B-A shuttle. The bot-vs-bot harness also learns to
route WARD's pending moment to the treasure's owner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-08-25 20:35:12 -04:00
co-authored by Claude Fable 5
parent 71a3c81670
commit 168e57ab91
2 changed files with 53 additions and 7 deletions
+13 -7
View File
@@ -1374,19 +1374,25 @@ export function automatonCommand(
const ownGoldCells = new Set(view.treasures
.filter((t) => t.owner === view.you && t.position && !t.carriedBy)
.map((t) => cellKey(t.position!)));
// A thief already underfoot is not a destination — with the chase
// moot, march on the real goals (deliver, grab) instead of standing
// on them forever.
const thiefAfar = thief && cellKey(thief.position) !== here ? thief : null;
// The chase exists to deliver a blow, so it claims the march only
// while that blow is still live: an attack in hand, this turn's
// attack unspent, casting not yet closed. Those gates hold still as
// the clockwork walks — a goal keyed to the thief's nearness flips
// underfoot and shuttles the walker on and off their square. Caught
// with the strike still live, it stands its ground (a goal underfoot
// ends the march); otherwise the gold has its legs for the turn.
const strikeLive = !view.turn.attackUsed && !view.turn.attackForbidden &&
!view.turn.actionsEnded && view.yourHand.some((c) => ATTACKS[c.cardId] != null);
const chasing = thief !== null && strikeLive;
const objectives = cursedIdiot && ownGoldCells.size > 0
? ownGoldCells
: thiefAfar
? new Set([cellKey(thiefAfar.position)])
: chasing
? new Set([cellKey(thief.position)])
: style === "berserker" && !self.carriedTreasureId
? (enemyCells.size > 0 ? enemyCells : gold)
: gold;
const path =
pathToward(view, self.position, objectives, { avoidNearEnemies: style === "worrier" && !thiefAfar, canUnlock }) ??
pathToward(view, self.position, objectives, { avoidNearEnemies: style === "worrier" && !chasing, canUnlock }) ??
pathToward(view, self.position, objectives, { canUnlock }) ??
pathToward(view, self.position, enemyCells, { canUnlock }) ??
// No clean road to anything: grit the teeth and wade the hazards,