The clockwork learns numbers, summons, creatures — and temperament
The automaton now plays number cards where they matter: attached to value-scaled attacks (waterbolt, powerthrust) choosing the biggest for damage, and played for movement when the goal is just out of stride. It raises its monsters when no wizard is in spell range and commands them every turn — creatures march at enemies by the same hazard-shy BFS and maul whoever shares their square. Targets are picked by lowest life. And it has moods. The HUNTER plays the classic game: gold first, violence when convenient. The BERSERKER hunts wizards over treasure and finishes games by last-wizard-standing. The WORRIER paths around enemies, counters at a lower threshold, shields against chaos, and never brawls. Hosts pick the temperament when seating one (or the workshop assigns at random); it persists with the seat, shows in the roster, and the tournament harness proves every pairing finishes. Across ten seeds of berserker versus hunter: six treasure wins to four kills — the moods play genuinely different games. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
01101ca24a
commit
5a407a1e4c
@@ -6,7 +6,7 @@ import {
|
||||
type PlayerId,
|
||||
} from "../src/game";
|
||||
import { viewFor } from "../src/view";
|
||||
import { automatonCommand, automatonFallback } from "../src/automaton";
|
||||
import { automatonCommand, automatonFallback, type AutomatonStyle } from "../src/automaton";
|
||||
|
||||
/** Whose input does the maze want right now? */
|
||||
function actingSeat(state: GameState): PlayerId {
|
||||
@@ -20,8 +20,9 @@ function actingSeat(state: GameState): PlayerId {
|
||||
}
|
||||
|
||||
/** Drive a full bot-vs-bot game; returns the final state and command count. */
|
||||
function playOut(seed: number, players: number, expansion = true) {
|
||||
function playOut(seed: number, players: number, expansion = true, styles: AutomatonStyle[] = []) {
|
||||
const ids = Array.from({ length: players }, (_, i) => `bot${i + 1}`);
|
||||
const styleOf = new Map(ids.map((id, i) => [id, styles[i] ?? "hunter"]));
|
||||
let { state } = createGame({
|
||||
playerIds: ids,
|
||||
seed,
|
||||
@@ -34,7 +35,7 @@ function playOut(seed: number, players: number, expansion = true) {
|
||||
while (state.phase === "playing" && commands < CAP) {
|
||||
const seat = actingSeat(state);
|
||||
const view = viewFor(state, seat);
|
||||
const cmd = automatonCommand(view) ?? automatonFallback(view);
|
||||
const cmd = automatonCommand(view, styleOf.get(seat)) ?? automatonFallback(view);
|
||||
let r = applyCommand(state, seat, cmd);
|
||||
if (!r.ok) {
|
||||
const fb = automatonFallback(view);
|
||||
@@ -83,4 +84,17 @@ describe("automaton vs automaton", () => {
|
||||
const { state } = playOut(7, 4);
|
||||
expect(state.phase).toBe("finished");
|
||||
});
|
||||
|
||||
it("every temperament finishes its wars", () => {
|
||||
for (const styles of [
|
||||
["berserker", "hunter"], ["worrier", "hunter"], ["berserker", "worrier"],
|
||||
] as AutomatonStyle[][]) {
|
||||
let finished = 0;
|
||||
for (const seed of [3, 17, 29]) {
|
||||
const { state } = playOut(seed, 2, true, styles);
|
||||
if (state.phase === "finished") finished++;
|
||||
}
|
||||
expect(finished).toBeGreaterThanOrEqual(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user