92 lines
4.8 KiB
JavaScript
92 lines
4.8 KiB
JavaScript
// Mode-off bonds stay out of the battle (computeBonds) and show in the views when the player has members
|
|
// (offBondCounts → bondList `off: true`, the strip's grey 本局禁用 discs). 标准模拟's inactiveBondIdList
|
|
// (activity_table mode_single_funny): 投资人 and 奇迹 count the hand (BOARD_AND_DECK); 突袭 counts the board.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { GameData } from '../../server/match/gamedata.js';
|
|
import { computeBonds, bondList, bondSnapshot, offBondCounts } from '../../server/match/bondsMeta.js';
|
|
import { DATA } from './harness.js';
|
|
|
|
const funny = new GameData(DATA, 'mode_single_funny');
|
|
const open = new GameData(DATA, 'mode_multi_normal');
|
|
let uid = 1;
|
|
const piece = (id) => ({ uid: uid++, kind: 'chess', id, items: [] });
|
|
const state = ({ board = [], hand = [], layers = {}, bondCountBonus } = {}) => {
|
|
const b = new Map();
|
|
board.forEach((p, i) => b.set(`${9 + (i % 4)},${2 + Math.floor(i / 4)}`, p));
|
|
const h = new Array(10).fill(null);
|
|
hand.forEach((p, i) => { h[9 - i] = p; });
|
|
return { board: b, hand: h, layers, bondCountBonus };
|
|
};
|
|
const members = (bond, n) => Object.values(DATA.chess)
|
|
.filter((c) => c.visible && !c.isGolden && c.bonds.includes(bond))
|
|
.slice(0, n)
|
|
.map((c) => c.chessId);
|
|
|
|
const INVESTOR = members('investShip', 2);
|
|
const RAIDER = members('raidShip', 1);
|
|
const YAN = members('yanShip', 1);
|
|
|
|
test('标准模拟: an 投资人 member in hand and a 突袭 member on the board are off entries, never in computeBonds', () => {
|
|
assert.equal(INVESTOR.length, 2);
|
|
assert.equal(RAIDER.length, 1);
|
|
const ps = state({
|
|
board: [piece(RAIDER[0]), piece(YAN[0])],
|
|
hand: [piece(INVESTOR[0])],
|
|
layers: { investShip: 12, raidShip: 4, lateranoShip: 9 },
|
|
});
|
|
const bonds = computeBonds(funny, ps);
|
|
assert.ok(!('investShip' in bonds), '投资人 stays out of the battle input');
|
|
assert.ok(!('raidShip' in bonds), '突袭 stays out of the battle input');
|
|
assert.ok(!('lateranoShip' in bonds));
|
|
assert.equal(bondSnapshot(bonds).investShip, undefined);
|
|
assert.ok(bonds.yanShip, 'an enabled bond is unchanged');
|
|
|
|
const off = offBondCounts(funny, ps);
|
|
assert.equal(off.investShip.count, 1, 'a hand member counts for 投资人');
|
|
assert.equal(off.investShip.layers, 12);
|
|
assert.equal(off.raidShip.count, 1);
|
|
assert.equal(off.raidShip.layers, 4);
|
|
assert.ok(!('lateranoShip' in off), 'an inactive bond with zero members is omitted, layers or not');
|
|
|
|
const list = bondList(funny, bonds, { full: true, off });
|
|
const invest = list.find((x) => x.bondId === 'investShip');
|
|
const raid = list.find((x) => x.bondId === 'raidShip');
|
|
assert.deepEqual(
|
|
{ count: invest.count, active: invest.active, tier: invest.tier, layers: invest.layers, off: invest.off, countsHand: invest.countsHand },
|
|
{ count: 1, active: false, tier: 0, layers: 12, off: true, countsHand: true },
|
|
);
|
|
assert.equal(raid.off, true);
|
|
assert.equal(raid.active, false);
|
|
assert.equal(raid.tier, 0);
|
|
assert.equal(raid.countsHand, false);
|
|
assert.ok(list.findIndex((x) => x.off) > list.findIndex((x) => x.bondId === 'yanShip'), 'off entries follow the live bonds');
|
|
assert.ok(list.slice(list.findIndex((x) => x.off)).every((x) => x.off), 'the off entries are a suffix');
|
|
const pub = bondList(funny, bonds, { off });
|
|
assert.equal(pub.find((x) => x.bondId === 'investShip').off, true);
|
|
assert.equal('countsHand' in pub.find((x) => x.bondId === 'investShip'), false, 'm.public omits countsHand');
|
|
});
|
|
|
|
test('a BOARD_AND_DECK off bond counts the hand, not the temporary slots, and not twice', () => {
|
|
const [a, b] = INVESTOR;
|
|
const handOnly = offBondCounts(funny, state({ hand: [piece(a), piece(b)] }));
|
|
assert.equal(handOnly.investShip.count, 2);
|
|
assert.ok(!('raidShip' in (handOnly || {})), '突袭 in nobody\'s hand: a board bond with no board member is omitted');
|
|
const raidHand = offBondCounts(funny, state({ hand: [piece(RAIDER[0])] }));
|
|
assert.equal(raidHand, null, '突袭 does not count a hand member');
|
|
const both = offBondCounts(funny, state({ board: [piece(a)], hand: [piece(a), piece(DATA.chess[a].goldenId)] }));
|
|
assert.equal(both.investShip.count, 1, 'normal and elite of one operator, board and hand, count once');
|
|
const tempOnly = { board: new Map(), hand: new Array(10).fill(null), layers: {}, temp: [piece(a)] };
|
|
assert.equal(offBondCounts(funny, tempOnly), null, 'the 5 temporary slots never count');
|
|
});
|
|
|
|
test('a mode with an empty inactive set returns null, and those bonds stay in computeBonds', () => {
|
|
assert.equal(open.modeInactiveBonds.size, 0);
|
|
const ps = state({ board: [piece(RAIDER[0])], hand: [piece(INVESTOR[0])] });
|
|
assert.equal(offBondCounts(open, ps), null);
|
|
const bonds = computeBonds(open, ps);
|
|
assert.equal(bonds.investShip.count, 1);
|
|
assert.equal(bonds.raidShip.count, 1);
|
|
assert.equal(bondList(open, bonds).some((x) => x.off), false);
|
|
});
|