Exercise Ecaz card variant and canonicalize advisor stances
This commit is contained in:
1 parent
6ac5dabdee
commit
c961a93f62
10 files changed
+185
-40
No files matched your search
@@ -387,6 +387,19 @@ void test('new advisors cannot flip into occupied fighters during their arrival
|
||||
/cannot become fighters/,
|
||||
);
|
||||
});
|
||||
void test('an unlocked ordinary advisor move keeps the same private and public views after JSON', () => {
|
||||
let g = fixture();
|
||||
army(g, 'a', { 'arrakeen:10': 1, 'imperial_basin:10': 1 });
|
||||
army(g, 'b', { 'arrakeen:10': 2, 'imperial_basin:10': 1 }, [
|
||||
'arrakeen', 'imperial_basin',
|
||||
]);
|
||||
g.active = 'b';
|
||||
g = move(g, 'b', 'arrakeen:10', 'imperial_basin', 10, 1);
|
||||
assert.deepEqual(g.players[1].advisors?.imperial_basin, {});
|
||||
for (const player of g.players)
|
||||
assert.deepEqual(viewGame(g, player.id), viewGame(JSON.parse(JSON.stringify(g)) as Game, player.id));
|
||||
});
|
||||
|
||||
void test('advisor setup waits for Fremen, follows their placement, and becomes fighters only when alone', () => {
|
||||
for (const occupied of [false, true]) {
|
||||
let g = createGame('ADSETUP2', newPlayer('a', 'Atreides', 'atreides'));
|
||||
@@ -506,6 +519,22 @@ void test('Guild cross-shipping matches existing advisors and preserves their ar
|
||||
);
|
||||
});
|
||||
|
||||
void test('an unlocked Guild advisor arrival projects identically after JSON continuation', () => {
|
||||
let g = fixture();
|
||||
g.players[2].faction = 'guild';
|
||||
g.players[2].ally = 'b';
|
||||
g.players[1].ally = 'h';
|
||||
army(g, 'a', { 'red_chasm:7': 1 });
|
||||
army(g, 'b', { 'arrakeen:10': 2, 'red_chasm:7': 1 }, ['arrakeen', 'red_chasm']);
|
||||
g.active = 'b';
|
||||
g = allow(applyAction(g, 'b', {
|
||||
type: 'guildShip', from: 'arrakeen:10', territory: 'red_chasm',
|
||||
sector: 7, amount: 2,
|
||||
}));
|
||||
assert.deepEqual(g.players[1].advisors?.red_chasm, {});
|
||||
assert.deepEqual(viewGame(g, 'a'), viewGame(JSON.parse(JSON.stringify(g)) as Game, 'a'));
|
||||
});
|
||||
|
||||
void test('accompanying advisors may select a valid sector of the shipment territory, with no reserve loss on a rejected choice', () => {
|
||||
let g = fixture();
|
||||
g = applyAction(g, 'a', {
|
||||
|
||||
@@ -94,6 +94,31 @@ function assertFailedEvidence(
|
||||
return report;
|
||||
}
|
||||
|
||||
void test('Ecaz card samples keep all three physical identities through saved resume', (t) => {
|
||||
const area = temporary(t);
|
||||
const out = join(area, 'ecaz-cards');
|
||||
const result = run(out, '--profile', 'ecaz-treachery', '--players', '2',
|
||||
'--rules', 'advanced', '--seed', '1000', '--max-actions', '1');
|
||||
assert.equal(result.status, 1);
|
||||
assertFailedEvidence(out, 'ecaz-treachery-2-advanced', 1137, false);
|
||||
const snapshot = join(out, 'failed-ecaz-treachery-2-advanced.json');
|
||||
const game = json<{
|
||||
ecazTreachery: boolean;
|
||||
deck: { id: string }[];
|
||||
discard: { id: string }[];
|
||||
players: { hand: { id: string }[] }[];
|
||||
}>(snapshot);
|
||||
assert.equal(game.ecazTreachery, true);
|
||||
assert.deepEqual(
|
||||
[...game.deck, ...game.discard, ...game.players.flatMap(player => player.hand)]
|
||||
.map(card => card.id).filter(id => id.startsWith('ecaz-')).sort(),
|
||||
['ecaz-harass-withdraw', 'ecaz-recruits', 'ecaz-reinforcements'],
|
||||
);
|
||||
const resumed = join(area, 'resumed');
|
||||
assert.equal(run(resumed, '--resume', snapshot, '--seed', '1000', '--max-actions', '1').status, 1);
|
||||
assertFailedEvidence(resumed, 'ecaz-treachery-2-advanced', 1137, true);
|
||||
});
|
||||
|
||||
void test('Moritani Skills samples preserve their full module on saved continuation and reject Advanced before running', (t) => {
|
||||
const area = temporary(t);
|
||||
const out = join(area, 'skills');
|
||||
|
||||
@@ -727,6 +727,9 @@ void test('Ecaz Occupy permits its unlocked BG ally to choose fighters beside Ec
|
||||
settled(advisors);
|
||||
continue;
|
||||
}
|
||||
const unlockedAdvisors = move(g, { ...action, fighters: false });
|
||||
assert.deepEqual(p(unlockedAdvisors, 'a').advisors?.sietch_tabr, {});
|
||||
assert.deepEqual(viewGame(unlockedAdvisors, 'a'), viewGame(reload(unlockedAdvisors), 'a'));
|
||||
let done = applyAction(reload(g), 'a', action);
|
||||
assert.equal(done.response?.kind, 'advisorFlip');
|
||||
done = allow(reload(done));
|
||||
|
||||
@@ -206,6 +206,26 @@ void test('Junction cross-transport preserves BG advisor locks and forbids mergi
|
||||
homeworldGameIntegrity(g);
|
||||
});
|
||||
|
||||
void test('an unlocked Junction advisor group has identical views across JSON recovery', () => {
|
||||
let g = fixture();
|
||||
g.active = 'beneGesserit';
|
||||
g.movementRemaining = ['beneGesserit', 'atreides', 'guild'];
|
||||
const bg = player(g, 'beneGesserit');
|
||||
bg.forces = { 'arrakeen:10': 2, 'tueks_sietch:5': 1 };
|
||||
bg.reserves = 17;
|
||||
bg.advisors = { arrakeen: {}, tueks_sietch: {} };
|
||||
g = offered(g);
|
||||
const option = viewGame(g, bg.id).junctionTransport!;
|
||||
g = applyAction(g, bg.id, {
|
||||
type: 'junctionShip', event: option.event, offer: option.offer!.event,
|
||||
destination: 'tueks_sietch:5',
|
||||
sources: { 'arrakeen:10': { normal: 1, elite: 0 } },
|
||||
});
|
||||
assert.deepEqual(player(g, bg.id).advisors!.tueks_sietch, {});
|
||||
assert.deepEqual(viewGame(g, bg.id), viewGame(JSON.parse(JSON.stringify(g)) as Game, bg.id));
|
||||
homeworldGameIntegrity(g);
|
||||
});
|
||||
|
||||
void test('Junction distinguishes native Fremen Arrakis arrival from a foreign Homeworld departure for BG and technology', () => {
|
||||
let native = fixture(true);
|
||||
native.active = 'fremen';
|
||||
|
||||
Reference in new issue
Block a user