Prototype conditional future battle Truthtrance
Verify and publish checkpoint container / container (push) Failing after 11m11s

This commit is contained in:
Quintine committed 2026-10-28 21:17:44 +11:00
1 parent 71009d73e3
commit 02695aea7c
15 files changed
+721 -8

No files matched your search

+27 -1
View File
@@ -117,6 +117,7 @@ export type TruthQuestion =
| ({ kind: 'shipment'; target: string } & ShipmentClaim)
| { kind: 'mixedShipment'; target: string; mixed: MixedShipmentExpression }
| { kind: 'battlePlan'; target: string; territory: string; claim: PlanClaim }
| { kind: 'futureBattleLeader'; target: string; turn: number; leader: string }
| { kind: 'fact'; target: string; fact: TruthFact }
| {
kind: 'freeform';
@@ -269,6 +270,9 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
if (v.kind !== 'charity' && v.kind !== 'fact' && v.kind !== 'freeform' &&
g.charityPromises?.length && liveCharityPromises(g.charityPromises,v.target as string,g.turn).length)
throw new TruthError('Finish this player’s current Charity answer before binding another action question.');
if (v.kind !== 'fact' && v.kind !== 'freeform' && v.kind !== 'futureBattleLeader' && g.futureBattleLeaderPromises?.some(row =>
row.player === v.target && row.turn === g.turn && (row.status === 'waiting' || row.status === 'bound')))
throw new TruthError('Finish this player’s conditional battle answer before binding another action question.');
if (v.kind === 'charity') {
check(Object.keys(v).sort().join(',') === 'kind,target,turn' && v.turn === g.turn &&
g.status === 'playing' && !g.advanced && g.phase >= 0 && g.phase <= 2 && !g.charityOrder &&
@@ -593,6 +597,18 @@ function parseQuestion(g: Game, asker: string, value: unknown): TruthQuestion {
return { kind: 'mixedBattlePlan', target: v.target as string, battle: g.battle.event,
territory: g.battle.territory, mixed };
}
if (v.kind === 'futureBattleLeader') {
const target = g.players.find(player => player.id === v.target)!;
check(Object.keys(v).sort().join(',') === 'kind,leader,target,turn' &&
v.turn === g.turn && typeof v.leader === 'string' &&
leaders(target.faction).some(leader => leader.id === v.leader) &&
g.status === 'playing' && !g.advanced && g.phase === 5 && !g.expansions.length &&
!g.techTokens && !g.strongholdCards && !g.nexusCards && !g.homeworlds &&
!g.leaderSkills && !g.discoveryEnabled && !g.ecazTreachery && !g.battle &&
!g.response && !g.decision && !g.phaseOpening && !g.pendingKarama && !g.pendingTreacheryDiscard,
'Choose a native leader and a quiet Basic turn before Battle.');
return { kind: 'futureBattleLeader', target: v.target, turn: g.turn, leader: v.leader };
}
if (v.kind === 'battlePlan') {
check(
g.phase === 6 &&
@@ -670,7 +686,8 @@ export function validateSavedTruthtrance(g: Game) {
'The saved Truthtrance must retain each committed physical card in its holder’s hand.',
);
}
if (window?.question?.kind === 'fact' || window?.question?.kind === 'movement' || window?.question?.kind === 'traitorCall') {
if (window?.question?.kind === 'fact' || window?.question?.kind === 'movement' ||
window?.question?.kind === 'traitorCall' || window?.question?.kind === 'futureBattleLeader') {
check((window.stage === 'answer' || window.stage === 'unknown') && window.queue.length > 0,
'The saved fact question must retain its asking player and answer stage.');
check(JSON.stringify(parseQuestion(g, window.queue[0].player, window.question)) === JSON.stringify(window.question),
@@ -794,6 +811,8 @@ export function truthQuestionText(
return `Will you ${shipmentClaimText(q)} this turn?`;
if (q.kind === 'battlePlan')
return `In this battle in ${locationName(q.territory)}, will it be true that ${planClaimText(q.claim, leaderName)}?`;
if (q.kind === 'futureBattleLeader')
return `If you fight a battle later this turn, will you use ${leaderName(q.leader)} in your first such battle?`;
const clause = (f: TruthFact): string => {
if (isKnowledgeFact(f)) return knowledgeFactText(f);
if (f.kind === 'noFieldValue') return noFieldValueFactText(f);
@@ -941,6 +960,8 @@ export function resolveTruthAction(
mixedBattleAnswers?: (g: Game, p: Player, claim: MixedBattleClaim) => TruthAnswer[];
bindMixedBattle?: (g: Game, p: Player, claim: MixedBattleClaim, answer: boolean, asker: string) => void;
battleAnswers: (g: Game, p: Player, claim: PlanClaim) => TruthAnswer[];
futureBattleLeaderAnswers: (g: Game, p: Player, question: Extract<TruthQuestion, {kind: 'futureBattleLeader'}>) => TruthAnswer[];
bindFutureBattleLeader: (g: Game, p: Player, question: Extract<TruthQuestion, {kind: 'futureBattleLeader'}>, answer: boolean, asker: string) => void;
bindBattle: (
g: Game,
p: Player,
@@ -1315,6 +1336,11 @@ export function resolveTruthAction(
current.player,
);
}
if (q.kind === 'futureBattleLeader') {
check(effects.futureBattleLeaderAnswers(g, p, q).includes(a.answer),
'Choose an answer compatible with your available native leaders and earlier commitments.');
if (a.answer !== 'unknown') effects.bindFutureBattleLeader(g, p, q, a.answer === 'yes', current.player);
}
// Freeform semantics cannot be certified by a deterministic engine. AI does not invent an answer.
if (q.kind === 'freeform' && p.bot)
check(