Add Truthtrance weapon and defense types
Verify and publish checkpoint container / container (push) Failing after 11m16s

This commit is contained in:
Quintine committed 2026-10-29 03:46:42 +11:00
1 parent 89589878fc
commit 6eb417ff32
7 files changed
+117 -12

No files matched your search

@@ -0,0 +1,17 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { CardCountFields } from '../components/truthtrance-card-count';
import type { HandInventoryFact } from '../game/truthtrance-hand-inventory';
void test('inventory controls offer precise printed primary attack and defense types', () => {
const value: HandInventoryFact = { kind: 'handInventory', category: 'poisonWeapon', compare: 'gte', value: 1 };
const html = renderToStaticMarkup(createElement(CardCountFields, { value, onChange() {} }));
for (const category of ['projectileWeapon', 'poisonWeapon', 'projectileDefense', 'poisonDefense'])
assert.match(html, new RegExp(`value="${category}"`));
assert.match(html, /Poison Blade and Shield Snooper match both/);
assert.match(html, /Mirror Weapon has no fixed poison or/);
assert.match(html, /Alternate uses and faction powers do not change/);
assert.match(html, /Number of cards/);
});
+37 -3
View File
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
import { baseDeck, ixDeck, treacheryDeck } from '../game/cards';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import { CARD_CATEGORIES, printedCardCategory } from '../game/card-category';
import { CARD_CATEGORIES, printedCardCategory, type CardCategory } from '../game/card-category';
import { ecazTreacheryCards } from '../game/ecaz-cards';
import {
applyAction,
@@ -31,7 +31,7 @@ const inventoryFact = (
void test('the complete implemented catalog has one exhaustive primary printed role, including Ix alternate-slot and canonical Richese edges', () => {
const catalog = [...baseDeck(), ...ixDeck(), ...richeseCards(), ...ecazTreacheryCards()];
const before = structuredClone(catalog);
const counts: Record<HandInventoryFact['category'], number> = {
const counts: Record<'all' | CardCategory, number> = {
all: 60, weapon: 17, defense: 13, worthless: 6, hero: 2, special: 22,
};
assert.deepEqual(CARD_CATEGORIES, ['weapon', 'defense', 'worthless', 'hero', 'special']);
@@ -51,7 +51,7 @@ void test('the complete implemented catalog has one exhaustive primary printed r
assert.equal(printedCardCategory({ ...canonical, id: `forged-${canonical.id}` }), 'special');
}
for (const [category, count] of Object.entries(counts) as [
HandInventoryFact['category'], number,
'all' | CardCategory, number,
][]) {
assert.ok(handInventoryFactMatches(catalog, inventoryFact(category, 'eq', count)));
assert.ok(!handInventoryFactMatches(catalog, inventoryFact(category, 'gte', count + 1)));
@@ -119,6 +119,33 @@ void test('inventory question text identifies current custody, the comparator, a
handInventoryFactText(inventoryFact('special', 'lte', 0)),
'you currently hold at most 0 Special cards without a primary battle role',
);
assert.equal(
handInventoryFactText(inventoryFact('poisonWeapon', 'gte', 2)),
'you currently hold at least 2 cards whose primary role is a poison weapon',
);
});
void test('primary battle subtypes count hybrids and default roles without inventing conditional roles', () => {
const catalog = [...baseDeck(), ...ixDeck(), ...richeseCards()];
const names = ['Poison Blade', 'Poison Tooth', 'Weirding Way', 'Chemistry',
'Shield Snooper', 'Portable Snooper', 'Artillery Strike', 'Stone Burner', 'Mirror Weapon'];
const hand = names.map(name => {
const card = catalog.find(candidate => candidate.name === name);
assert.ok(card, name);
return card;
});
for (const [category, count] of [
['projectileWeapon', 2], ['poisonWeapon', 2],
['projectileDefense', 1], ['poisonDefense', 3],
] as const) {
assert.ok(handInventoryFactMatches(hand, inventoryFact(category, 'eq', count)), category);
assert.ok(!handInventoryFactMatches(hand, inventoryFact(category, 'gte', count + 1)), category);
assert.deepEqual(parseHandInventoryFact(inventoryFact(category, 'eq', count)), inventoryFact(category, 'eq', count));
}
const weirding = hand.find(card => card.name === 'Weirding Way')!;
const chemistry = hand.find(card => card.name === 'Chemistry')!;
assert.ok(handInventoryFactMatches([weirding, chemistry], inventoryFact('projectileDefense', 'eq', 0)));
assert.ok(handInventoryFactMatches([weirding, chemistry], inventoryFact('poisonWeapon', 'eq', 0)));
});
function takeDeck(g: Game, id: string, name: string) {
@@ -224,6 +251,10 @@ void test('real declaration, priority, question, and answer use only current han
[inventoryFact('worthless', 'eq', 0), 'yes'],
[inventoryFact('hero', 'gte', 2), 'no'],
[inventoryFact('special', 'eq', 1), 'yes'],
[inventoryFact('projectileWeapon', 'eq', 1), 'yes'],
[inventoryFact('poisonWeapon', 'eq', 1), 'yes'],
[inventoryFact('projectileDefense', 'eq', 1), 'no'],
[inventoryFact('poisonDefense', 'eq', 1), 'yes'],
];
for (const [fact, expected] of cases) {
const initial = fixture();
@@ -353,6 +384,9 @@ void test('equivalent hidden inventories yield the same outsider projection and
inventoryFact('weapon', 'gte', 1),
inventoryFact('defense', 'lte', 0),
inventoryFact('special', 'eq', 1),
inventoryFact('poisonWeapon', 'eq', 1),
inventoryFact('projectileDefense', 'eq', 0),
inventoryFact('poisonDefense', 'eq', 1),
]) {
const first = fixture();
first.players[1].bot = difficulty;