import assert from 'node:assert/strict'; import test from 'node:test'; import { baseDeck } from '../game/cards'; import { parseFreeformFactV2 } from '../game/truthtrance-freeform-facts-v2'; import { parseFreeformFactV3 } from '../game/truthtrance-freeform-facts-v3'; import { handInventoryFactMatches, type HandInventoryFact } from '../game/truthtrance-hand-inventory'; void test('freeform v3 recognizes current primary-role existence and count questions', () => { const cases: Array<[string, HandInventoryFact]> = [ ['Do you have a weapon?', {kind:'handInventory', category:'weapon', compare:'gte', value:1}], ['DO YOU HAVE ANY DEFENSES IN YOUR HAND?', {kind:'handInventory', category:'defense', compare:'gte', value:1}], ['Do you have at least 02 worthless cards?', {kind:'handInventory', category:'worthless', compare:'gte', value:2}], ['Do you have exactly 0 poison weapons?', {kind:'handInventory', category:'poisonWeapon', compare:'eq', value:0}], ['Do you have at most 3 projectile weapons in your hand?', {kind:'handInventory', category:'projectileWeapon', compare:'lte', value:3}], ['Do you have any poison defenses?', {kind:'handInventory', category:'poisonDefense', compare:'gte', value:1}], ['Do you have a projectile defense?', {kind:'handInventory', category:'projectileDefense', compare:'gte', value:1}], ]; for (const [text, fact] of cases) assert.deepEqual(parseFreeformFactV3(text), fact, text); }); void test('freeform v3 uses the existing physical hand category semantics', () => { const deck = baseDeck(); const weapon = deck.find(card => card.kind === 'projectile')!; const defense = deck.find(card => card.kind === 'shield')!; const worthless = deck.find(card => card.kind === 'worthless')!; assert.ok(weapon && defense && worthless); const hand = [weapon, defense, worthless]; for (const text of ['Do you have a weapon?', 'Do you have a defense?', 'Do you have exactly 1 worthless card?', 'Do you have exactly 0 poison weapons?']) { const fact = parseFreeformFactV3(text); assert.ok(fact && fact.kind === 'handInventory', text); assert.equal(handInventoryFactMatches(hand, fact), true, text); } const absent = parseFreeformFactV3('Do you have any poison defenses?'); assert.ok(absent && absent.kind === 'handInventory'); assert.equal(handInventoryFactMatches(hand, absent), false); }); void test('freeform v3 preserves v2 named-card, spice, hand-size and compound interpretations', () => { for (const text of [ 'Do you have a Shield?', 'Do you have a Cheap Hero?', 'Do you have at least 4 spice?', 'Do you have at most 3 cards in your hand?', 'Do you have a Shield and a Snooper?', ]) { const v2 = parseFreeformFactV2(text); assert.ok(v2, text); assert.deepEqual(parseFreeformFactV3(text), v2, text); } }); void test('freeform v3 leaves ambiguous, negative, compound and future wording manual', () => { for (const text of [ 'Do you have a weapons?', 'Do you have an at least?', 'Do you have a constructor?', 'Do you have a special card?', 'Do you have exactly -1 weapons?', 'Do you have exactly 1.5 weapons?', 'Do you have at least 9007199254740992 weapons?', 'Do you have a weapon or a Shield?', 'Do you have a weapon and a defense?', 'Do you not have a weapon?', 'Will you have a weapon?', 'Do you have a weapon this turn?', 'Do you have a weapon??', 'Do you have a weapon?\nPlease answer.', 'Do you have a weapon?'.padEnd(501, ' '), ]) assert.equal(parseFreeformFactV3(text), null, text); });