Support Truthtrance Storm dial comparisons

This commit is contained in:
Quintine committed 2026-10-27 22:21:38 +11:00
1 parent c8889e94e2
commit 31b3c1ab87
12 files changed
+288 -18

No files matched your search

+16 -6
View File
@@ -1,7 +1,11 @@
/** A future choice names only the actor's original native dial opportunity.
* The accepted value stays in the normal private storm dial until reveal. */
export type StormDialSource = { turn: number; player: string };
export type StormDialClaim = { kind: 'stormDial'; source: string; value: number };
export type StormDialClaim = {
kind: 'stormDial'; source: string; value: number;
/** Older saved claims omit this field and retain exact-value meaning. */
compare?: 'eq' | 'gte' | 'lte';
};
export type StormDialOpportunity = StormDialSource & { source: string; min: number; max: number };
export type StormDialGame = {
status: string;
@@ -43,15 +47,20 @@ export function parseStormDialSource(source: unknown): StormDialSource | null {
return { turn: tuple[1], player: tuple[2] };
}
/** Parse only an exact future native choice within that turn's real dial range. */
/** Parse a future native choice within that turn's real dial range. */
export function parseStormDialClaim(value: unknown): StormDialClaim {
check(object(value) && Object.keys(value).sort().join(',') === 'kind,source,value' && value.kind === 'stormDial',
'Choose one original storm-dial opportunity and exact value.');
check(object(value) &&
(Object.keys(value).sort().join(',') === 'kind,source,value' ||
Object.keys(value).sort().join(',') === 'compare,kind,source,value') &&
value.kind === 'stormDial' &&
(!Object.hasOwn(value, 'compare') || value.compare === 'eq' || value.compare === 'gte' || value.compare === 'lte'),
'Choose one original storm-dial opportunity and value comparison.');
const source = parseStormDialSource(value.source);
check(source && typeof value.value === 'number' && Number.isSafeInteger(value.value) &&
value.value >= (source.turn === 1 ? 0 : 1) && value.value <= (source.turn === 1 ? 20 : 3),
'Choose a value within the original turn’s native storm-dial range.');
return { kind: 'stormDial', source: value.source as string, value: value.value };
return { kind: 'stormDial', source: value.source as string, value: value.value,
...(Object.hasOwn(value, 'compare') ? { compare: value.compare as 'eq' | 'gte' | 'lte' } : {}) };
}
/** Compare the actual accepted native submission, never an invented default. */
@@ -60,7 +69,8 @@ export function stormDialMatches(claim: StormDialClaim, actual: unknown): boolea
check(typeof actual === 'number' && Number.isSafeInteger(actual) &&
actual >= (source.turn === 1 ? 0 : 1) && actual <= (source.turn === 1 ? 20 : 3),
'Retain the actual native storm-dial submission.');
return parsed.value === actual;
return parsed.compare === 'gte' ? actual >= parsed.value :
parsed.compare === 'lte' ? actual <= parsed.value : actual === parsed.value;
}
/** Public eligibility only. Reads whether a value is sealed, never that value.