Add whole-territory Truthtrance force questions
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
7552bb5394
commit
f1007a4ea8
9 files changed
+149
-21
No files matched your search
@@ -14,7 +14,7 @@ function markup(value = fact, game: MobileBoard = {}, target = player) {
|
||||
|
||||
void test('force fact controls name physical pools and comparisons without revealing a hidden marker value', () => {
|
||||
const html = markup();
|
||||
for (const value of ['reserves', 'tanks', 'location', 'total', 'normal', 'elite', 'eq', 'gte', 'lte'])
|
||||
for (const value of ['reserves', 'tanks', 'territory', 'location', 'total', 'normal', 'elite', 'eq', 'gte', 'lte'])
|
||||
assert.match(html, new RegExp(`value="${value}"`));
|
||||
assert.match(html, /Number of force counters/);
|
||||
assert.match(html, /Each physical counter counts once, including advisors/);
|
||||
@@ -23,6 +23,17 @@ void test('force fact controls name physical pools and comparisons without revea
|
||||
assert.match(html, /min="0"/); assert.match(html, /max="9007199254740991"/);
|
||||
});
|
||||
|
||||
void test('whole-territory choice names the territory without requiring a sector', () => {
|
||||
const whole: ForceCountFact = { ...fact, zone: { kind: 'territory', territory: 'wind_pass' } };
|
||||
assert.equal(forceCountInputError(whole, {}, player), null);
|
||||
const html = markup(whole);
|
||||
assert.match(html, /Entire board territory/);
|
||||
assert.match(html, /Force territory/);
|
||||
assert.match(html, /Wind Pass/);
|
||||
assert.doesNotMatch(html, /Force sector/);
|
||||
assert.ok(forceCountInputError({ ...whole, zone: { kind: 'territory', territory: 'hidden_mobile_stronghold' } }, {}, player));
|
||||
});
|
||||
|
||||
void test('location choices use the room board and the selected territory sectors', () => {
|
||||
const html = markup({ ...fact, zone: { kind: 'location', territory: 'carthag', sector: 11 } });
|
||||
assert.match(html, /Force territory/); assert.match(html, /Carthag/);
|
||||
|
||||
@@ -37,6 +37,7 @@ void test('parser accepts exact live pools and rejects extra, mutually exclusive
|
||||
for (const zone of [
|
||||
{ kind: 'reserves' },
|
||||
{ kind: 'tanks' },
|
||||
{ kind: 'territory', territory: 'imperial_basin' },
|
||||
{ kind: 'location', territory: 'imperial_basin', sector: 9 },
|
||||
])
|
||||
assert.deepEqual(
|
||||
@@ -47,6 +48,8 @@ void test('parser accepts exact live pools and rejects extra, mutually exclusive
|
||||
for (const malformed of [
|
||||
{ ...input, privateHint: true },
|
||||
{ ...input, zone: { kind: 'reserves', territory: 'arrakeen' } },
|
||||
{ ...input, zone: { kind: 'territory', territory: 'unknown' } },
|
||||
{ ...input, zone: { kind: 'territory', territory: 'arrakeen', sector: 10 } },
|
||||
{
|
||||
...input,
|
||||
zone: {
|
||||
@@ -82,6 +85,27 @@ void test('parser accepts exact live pools and rejects extra, mutually exclusive
|
||||
assert.throws(() => parseForceCountFact({ ...input, value }, locations));
|
||||
});
|
||||
|
||||
void test('territory-wide facts sum physical sectors and their typed subsets without counting nearby or interior territories', () => {
|
||||
const player: ForceCountPlayer = {
|
||||
faction: 'ixians', reserves: 9, tanks: 0,
|
||||
forces: { 'wind_pass:14': 4, 'wind_pass:15': 3, 'imperial_basin:9': 5, 'hidden_mobile_stronghold:0': 2 },
|
||||
elites: { reserves: 1, tanks: 0, forces: { 'wind_pass:14': 1, 'wind_pass:15': 2 } },
|
||||
};
|
||||
const zone = { kind: 'territory', territory: 'wind_pass' } as const;
|
||||
for (const [counter, value] of [['total', 7], ['normal', 4], ['elite', 3]] as const) {
|
||||
assert.equal(forceCountFactMatches(player, fact(zone, counter, 'eq', value)), true);
|
||||
assert.equal(forceCountFactMatches(player, fact(zone, counter, 'gte', value + 1)), false);
|
||||
}
|
||||
assert.equal(forceCountFactText(fact(zone, 'total', 'eq', 7)),
|
||||
'you currently have exactly 7 physical forces in Wind Pass across all sectors');
|
||||
assert.throws(() => forceCountFactMatches({ ...player,
|
||||
elites: { ...player.elites!, forces: { 'wind_pass:14': 5 } } }, fact(zone)), /exceeds/);
|
||||
assert.throws(() => forceCountFactMatches({ ...player,
|
||||
forces: { ...player.forces, 'wind_pass:14': null } } as unknown as ForceCountPlayer, fact(zone)), /nonnegative/);
|
||||
assert.throws(() => forceCountFactMatches({ ...player,
|
||||
elites: { ...player.elites!, forces: { 'wind_pass:15': null } } } as unknown as ForceCountPlayer, fact(zone)), /nonnegative/);
|
||||
});
|
||||
|
||||
void test('current totals include elites while normal counts subtract the tracked physical subset in every pool', () => {
|
||||
const player: ForceCountPlayer = {
|
||||
faction: 'ixians',
|
||||
|
||||
@@ -94,6 +94,40 @@ void test('Truthtrance counts physical total, normal and elite forces in reserve
|
||||
}
|
||||
});
|
||||
|
||||
void test('a whole-territory Basic question counts separate sectors and every bot answers the same real fact', () => {
|
||||
const initial = knowledgeGame(false);
|
||||
const fremen = player(initial, 'f');
|
||||
// Preserve the original ten on-board and ten reserve Fremen counters.
|
||||
fremen.forces = { 'wind_pass:14': 4, 'wind_pass:15': 6 };
|
||||
const zone: ForceFact['zone'] = { kind: 'territory', territory: 'wind_pass' };
|
||||
answer(ask(initial, 'f', fact(zone, 'total', 10)), 'f', 'yes');
|
||||
answer(ask(initial, 'f', fact(zone, 'total', 11, 'gte')), 'f', 'no');
|
||||
for (const difficulty of DIFFICULTIES) {
|
||||
const waiting = ask(initial, 'f', fact(zone, 'total', 10));
|
||||
const view = viewGame(waiting, 'f');
|
||||
view.players.find(candidate => candidate.id === 'f')!.bot = difficulty;
|
||||
const response = botActions(view);
|
||||
assert.deepEqual(response, [{ type: 'truthAnswer', answer: 'yes' }]);
|
||||
const done = act(waiting, 'f', response[0]);
|
||||
assert.equal(done.truthtrance, null);
|
||||
assert.equal(done.truthHistory!.at(-1)!.answer, 'yes');
|
||||
}
|
||||
});
|
||||
|
||||
void test('whole-territory typed facts aggregate elite and normal counters across sectors', () => {
|
||||
const initial = typedGame();
|
||||
const fremen = player(initial, 'f');
|
||||
const before = knowledgePhysical(initial);
|
||||
fremen.forces = { 'wind_pass:14': 4, 'wind_pass:15': 3 };
|
||||
fremen.elites!.reserves = 0;
|
||||
fremen.elites!.forces = { 'wind_pass:14': 1, 'wind_pass:15': 1 };
|
||||
assert.deepEqual(knowledgePhysical(initial), before);
|
||||
const zone: ForceFact['zone'] = { kind: 'territory', territory: 'wind_pass' };
|
||||
answer(ask(initial, 'f', fact(zone, 'total', 7)), 'f', 'yes');
|
||||
answer(ask(initial, 'f', fact(zone, 'normal', 5)), 'f', 'yes');
|
||||
answer(ask(initial, 'f', fact(zone, 'elite', 2)), 'f', 'yes');
|
||||
});
|
||||
|
||||
void test('Basic untracked starred factions allow total counts while ordinary absent elite tracking means zero elites', () => {
|
||||
const initial = knowledgeGame(false);
|
||||
assert.equal(player(initial, 'f').elites, undefined);
|
||||
@@ -230,6 +264,8 @@ void test('the live HMS interior is an exact physical location distinct from its
|
||||
assert.equal(player(initial, 'i').elites!.forces[MOBILE_LOCATION], 3);
|
||||
for (const [counter, value] of [['total', 6], ['normal', 3], ['elite', 3]] as const)
|
||||
assertIxSkillsCustody(answer(ask(initial, 'i', fact({ kind: 'location', territory: MOBILE_STRONGHOLD, sector: 0 }, counter, value), 'e'), 'i', 'yes'));
|
||||
answer(ask(initial, 'i', fact({ kind: 'territory', territory: MOBILE_STRONGHOLD }, 'total', 6), 'e'), 'i', 'yes');
|
||||
answer(ask(initial, 'i', fact({ kind: 'territory', territory: 'polar_sink' }, 'total', 0), 'e'), 'i', 'yes');
|
||||
answer(ask(initial, 'i', fact({ kind: 'location', territory: 'polar_sink', sector: 0 }, 'total', 0), 'e'), 'i', 'yes');
|
||||
const waiting = declared(initial, 'e');
|
||||
reject(waiting, 'e', { type: 'truthAsk', question: { kind: 'fact', target: 'i',
|
||||
@@ -243,10 +279,13 @@ void test('a Discovery location becomes queryable only after its genuine reveal'
|
||||
const hidden = declared(game, 'g');
|
||||
reject(hidden, 'g', { type: 'truthAsk', question: { kind: 'fact', target: 'a',
|
||||
fact: fact({ kind: 'location', territory: 'cistern', sector: 0 }, 'total', 0) } });
|
||||
reject(hidden, 'g', { type: 'truthAsk', question: { kind: 'fact', target: 'a',
|
||||
fact: fact({ kind: 'territory', territory: 'cistern' }, 'total', 0) } });
|
||||
game = act(game, 'a', { type: 'discovery', token: token.id, reveal: false });
|
||||
game = act(game, 'a', { type: 'discovery', token: token.id, reveal: true });
|
||||
assert.equal(game.discoveries!.tokens.find(candidate => candidate.id === token.id)!.revealedTurn, game.turn);
|
||||
answer(ask(game, 'a', fact({ kind: 'location', territory: 'cistern', sector: 0 }, 'total', 0), 'g'), 'a', 'yes');
|
||||
answer(ask(game, 'a', fact({ kind: 'territory', territory: 'cistern' }, 'total', 0), 'g'), 'a', 'yes');
|
||||
});
|
||||
|
||||
void test('Homeworld reserve facts read the faction aggregate and do not add a separate Homeworld query', () => {
|
||||
|
||||
Reference in new issue
Block a user