The clockwork throws more spells and fewer fists
Punching was mostly deck arithmetic — nineteen castable damage cards in a 196-card deck — but three leaks made it worse. The march was burning the biggest NUMBER card on long walks, starving waterbolts and lightning; the biggest number is now reserved whenever an attack in hand wants it. Heave-Ho, priced for defense, was being cast without a treasure to throw (refused, turn stumbled) — now never cast. Illusionary Attack was cast without naming a spell to fake — now it fakes a fireball, the best-selling lie. And STONE DEAD joins the arsenal against anyone flashing displayed stones: number times the visible count, cast when the arithmetic beats the alternatives. Measured across thirty berserker mirrors: attack casts up a quarter, the tier handicap sharper (25-5), no stalls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b01e27c644
commit
13ce8c442c
@@ -383,6 +383,9 @@ function bestAttack(view: GameView, targetId: PlayerId, tier: TierTraits): Comma
|
||||
for (const c of view.yourHand) {
|
||||
const atk = ATTACKS[c.cardId];
|
||||
if (!atk) continue;
|
||||
// Heave-Ho throws the treasure we are carrying: priced for defense,
|
||||
// never worth casting.
|
||||
if (c.cardId === "heave-ho") continue;
|
||||
if (atk.sameSquare && !together) continue;
|
||||
// A wand needs its charges set by a number on first use.
|
||||
const isWand = c.cardId === "blaster-wand";
|
||||
@@ -401,12 +404,31 @@ function bestAttack(view: GameView, targetId: PlayerId, tier: TierTraits): Comma
|
||||
cmd: {
|
||||
type: "cast", instanceId: c.instanceId,
|
||||
target: { kind: "player", playerId: targetId },
|
||||
// An illusion needs a spell to imitate; a fireball sells best.
|
||||
...(c.cardId === "illusionary-attack" ? { params: { cardId: "fireball" } } : {}),
|
||||
...(withNumber ? { numberInstanceIds: [biggest.instanceId] } : {}),
|
||||
...(amplified ? { amplifyInstanceIds: [amplify.instanceId] } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
// STONE DEAD: number times the stones the victim carries — the displayed
|
||||
// ones are the visible floor of that count.
|
||||
const sd = view.yourHand.find((c) => c.cardId === "stone-dead");
|
||||
if (sd && biggest) {
|
||||
const shown = target.displayed.filter((c) => STONES.has(c.cardId)).length;
|
||||
const dmg = biggestValue * shown;
|
||||
if (dmg >= 4 && dmg > (best?.damage ?? 0)) {
|
||||
best = {
|
||||
damage: dmg,
|
||||
cmd: {
|
||||
type: "cast", instanceId: sd.instanceId,
|
||||
target: { kind: "player", playerId: targetId },
|
||||
numberInstanceIds: [biggest.instanceId],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return best?.cmd ?? null;
|
||||
}
|
||||
|
||||
@@ -690,9 +712,19 @@ export function automatonCommand(
|
||||
const movesLeft = view.turn.movementAllowance - view.turn.movementUsed;
|
||||
if (!view.turn.numberPlayedForMovement && path.distance > movesLeft) {
|
||||
const numbers = numbersInHand(view);
|
||||
const helper = numbers.find(
|
||||
// The war chest: when an attack in hand wants a number, the
|
||||
// biggest one is reserved — a waterbolt unfired outbids a longer
|
||||
// march, and walking is free next turn.
|
||||
const wantsNumber = view.yourHand.some((c) => {
|
||||
const atk = ATTACKS[c.cardId];
|
||||
if (!atk) return false;
|
||||
return atk.perNumber || atk.needsNumber === true ||
|
||||
(c.cardId === "blaster-wand" && view.wandCharges[c.instanceId] == null);
|
||||
});
|
||||
const spendable = wantsNumber ? numbers.slice(0, -1) : numbers;
|
||||
const helper = spendable.find(
|
||||
(n) => movesLeft + (cardDef(n.cardId).value ?? 0) >= path.distance,
|
||||
) ?? (path.distance > movesLeft + 2 ? numbers[numbers.length - 1] : undefined);
|
||||
) ?? (path.distance > movesLeft + 2 ? spendable[spendable.length - 1] : undefined);
|
||||
if (helper) {
|
||||
return { type: "playNumberForMovement", instanceId: helper.instanceId };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user