feedback1 gaps: bots skip strategies built around a bond the mode switches off

GameData.bandBondIds reads a band's tie from its own data: the bond names
in <...> of its text (the official note naming the bond among them) and
the bond ids / bond pools of its blackboards (choices.json pools[pool].bond
for pool_char_later). botPickBand weighs a band tied to a bond in
modeInactiveBonds 0, keeps one rng draw per pick and falls back to the
default band when every offered band is excluded. In 标准 潘格尼尼,
克莱门莎 and 玛恩纳 are no longer picked (seeds 1-200: 5/200 solo and
23/800 co-op before, 0 after); the other modes keep the same picks.
This commit is contained in:
sganggs committed 2026-10-03 15:07:05 +08:00
1 parent 842e1e13c4
commit 481b76027d
2 files changed
+67 -8

No files matched your search

+43
View File
@@ -90,6 +90,9 @@ export class GameData {
for (const k of Object.keys(this.shopItemsByTier)) this.shopItemsByTier[k].sort();
this.bondIds = Object.keys(this._bonds).sort((a, b) => (numOr(this._bonds[a].identifier, 99) - numOr(this._bonds[b].identifier, 99)) || (a < b ? -1 : 1));
this.modeInactiveBonds = new Set(Array.isArray(this.mode.inactiveBondIds) ? this.mode.inactiveBondIds : []);
/** bond name → id (bandBondIds) and its memo */
this._bondByName = new Map(Object.entries(this._bonds).filter(([, r]) => r && typeof r.name === 'string').map(([id, r]) => [r.name, id]));
this._bandBonds = new Map();
this.inactiveEnemies = new Set(Array.isArray(this.mode.inactiveEnemyKeys) ? this.mode.inactiveEnemyKeys : []);
/** data/tuning.json (titles only, see the header) */
this.tuning = this.raw.tuning && typeof this.raw.tuning === 'object' ? this.raw.tuning : {};
@@ -458,6 +461,46 @@ export class GameData {
return b && Number.isInteger(b.totalHp) && b.totalHp > 0 ? b.totalHp : this.defaultStartLp;
}
/**
* The bonds a strategy's mechanic is built around, read from its own data (DESIGN §21.26): the bond names its text puts
* in <…> — the official note "在<X>部分干员缺席时体验可能不完整" names that bond too — and the bond ids and bond pools its
* effect's blackboards name (bb / bbStr values: 克莱门莎's `bond_id` egirShip, 玛恩纳's `bond` kazimierzShip, 潘格尼尼's
* `pool` pool_char_later → choices.json pools[pool].bond lateranoShip). Names map to ids through bonds.json; a bracketed
* name that is no bond (<寻呼模块>, <画卷>, <鸭爵> …) and a pool without a bond add nothing. Bond ids in data order,
* [] for an unknown band or one tied to no bond (华法琳, 阿米娅 …). The bot never picks a strategy tied to a bond the mode
* switches off (bot.js botPickBand: 标准's 潘格尼尼 / 克莱门莎 / 玛恩纳).
* @param {string} bandId
* @returns {string[]}
*/
bandBondIds(bandId) {
if (this._bandBonds.has(bandId)) return this._bandBonds.get(bandId);
const b = this.band(bandId);
const found = new Set();
if (b) {
for (const [, name] of String(b.desc || '').matchAll(/<([^<>]+)>/g)) {
const id = this._bondByName.get(name.trim());
if (id) found.add(id);
}
const pools = this.choices.pools && typeof this.choices.pools === 'object' ? this.choices.pools : {};
for (const buff of Array.isArray(b.buffs) ? b.buffs : []) {
for (const board of [buff && buff.bb, buff && buff.bbStr]) {
if (!board || typeof board !== 'object') continue;
for (const v of Object.values(board)) {
if (typeof v !== 'string') continue;
for (const part of v.split(',').map((s) => s.trim())) {
if (this.bond(part)) found.add(part);
const pool = own(pools, part);
if (pool && typeof pool.bond === 'string' && this.bond(pool.bond)) found.add(pool.bond);
}
}
}
}
}
const out = Object.freeze(this.bondIds.filter((id) => found.has(id)));
this._bandBonds.set(bandId, out);
return out;
}
/**
* Placeable (hand) tokens a chess sends to the hand when placed on the board: [{ tokenId, count }] — its manually
* deployable summons (tokens.json `placeable`: 医疗探机, 诅咒娃娃, 海嗣, 狼群, 流形, 爬行号·防护单元; user playtest #6)