Files
Dune/game/truthtrance-hand-inventory.ts
T
Quintine 6eb417ff32
Verify and publish checkpoint container / container (push) Failing after 11m16s
Add Truthtrance weapon and defense types
2026-10-29 03:46:42 +11:00

108 lines
3.8 KiB
TypeScript

import type { Card } from './cards';
import {
CARD_CATEGORIES,
printedCardCategory,
type CardCategory,
} from './card-category';
import { defenseTypes, weaponTypes } from './battle-cards';
export const HAND_INVENTORY_TYPES = [
'projectileWeapon', 'poisonWeapon', 'projectileDefense', 'poisonDefense',
] as const;
type HandInventoryType = (typeof HAND_INVENTORY_TYPES)[number];
/** A current custody question, not a promise to retain cards or use them. */
export type HandInventoryFact = {
kind: 'handInventory';
category: 'all' | CardCategory | HandInventoryType;
compare: 'eq' | 'gte' | 'lte';
value: number;
};
export const HAND_INVENTORY_LABELS: Record<
HandInventoryFact['category'],
string
> = {
all: 'All cards in hand',
weapon: 'Weapons · primary role',
defense: 'Defenses · primary role',
worthless: 'Worthless cards',
hero: 'Cheap Heroes and Heroines',
special: 'Special cards without a primary battle role',
projectileWeapon: 'Projectile weapons · primary role',
poisonWeapon: 'Poison weapons · primary role',
projectileDefense: 'Projectile defenses · primary role',
poisonDefense: 'Poison defenses · primary role',
};
export function parseHandInventoryFact(
v: Record<string, unknown>,
): HandInventoryFact {
if (
v.kind !== 'handInventory' ||
(v.category !== 'all' &&
!CARD_CATEGORIES.includes(v.category as CardCategory) &&
!HAND_INVENTORY_TYPES.includes(v.category as HandInventoryType))
)
throw new Error('Choose all held cards or a known primary card role.');
if (v.compare !== 'eq' && v.compare !== 'gte' && v.compare !== 'lte')
throw new Error('Compare held cards using exactly, at least, or at most.');
if (
typeof v.value !== 'number' ||
!Number.isSafeInteger(v.value) ||
v.value < 0
)
throw new Error(
'Choose a nonnegative whole card count within the safe integer range.',
);
return {
kind: 'handInventory',
category: v.category as HandInventoryFact['category'],
compare: v.compare,
value: v.value,
};
}
export function handInventoryFactMatches(
hand: readonly Card[],
fact: HandInventoryFact,
): boolean {
const category = fact.category;
const count = category === 'all' ? hand.length : hand.filter((card) => {
const primary = printedCardCategory(card);
if (category === 'projectileWeapon' || category === 'poisonWeapon')
return primary === 'weapon' && weaponTypes(card).includes(category === 'projectileWeapon' ? 'projectile' : 'poison');
if (category === 'projectileDefense' || category === 'poisonDefense')
return primary === 'defense' && defenseTypes(card).includes(category === 'projectileDefense' ? 'shield' : 'snooper');
return primary === category;
}).length;
return fact.compare === 'eq'
? count === fact.value
: fact.compare === 'gte'
? count >= fact.value
: count <= fact.value;
}
export function handInventoryFactText(fact: HandInventoryFact): string {
const comparison =
fact.compare === 'eq'
? 'exactly'
: fact.compare === 'gte'
? 'at least'
: 'at most';
const cards = fact.value === 1 ? 'card' : 'cards';
const description: Record<HandInventoryFact['category'], string> = {
all: `${cards} in your hand`,
weapon: `${cards} whose primary role is a weapon`,
defense: `${cards} whose primary role is a defense`,
worthless: `Worthless ${cards}`,
hero: `Cheap Hero or Cheap Heroine ${cards}`,
special: `Special ${cards} without a primary battle role`,
projectileWeapon: `${cards} whose primary role is a projectile weapon`,
poisonWeapon: `${cards} whose primary role is a poison weapon`,
projectileDefense: `${cards} whose primary role is a projectile defense`,
poisonDefense: `${cards} whose primary role is a poison defense`,
};
return `you currently hold ${comparison} ${fact.value} ${description[fact.category]}`;
}