@firestone-hs/simulate-bgs-battle 1.1.756 → 1.1.758
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/battle-info-hash.d.ts +9 -0
- package/dist/battle-info-hash.js +39 -0
- package/dist/battle-info-hash.js.map +1 -0
- package/dist/cards/impl/minion/cthun.js +18 -15
- package/dist/cards/impl/minion/cthun.js.map +1 -1
- package/dist/cards/impl/minion/underrot-spawn.js +1 -3
- package/dist/cards/impl/minion/underrot-spawn.js.map +1 -1
- package/dist/simulate-bgs-battle.d.ts +2 -0
- package/dist/simulate-bgs-battle.js +11 -5
- package/dist/simulate-bgs-battle.js.map +1 -1
- package/dist/simulation-result.d.ts +2 -0
- package/dist/simulation-result.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const hashBattleInfo: (battleInfo: BattleInfoHashInput | null | undefined) => string | null;
|
|
2
|
+
interface BattleInfoHashInput {
|
|
3
|
+
readonly playerBoard?: unknown;
|
|
4
|
+
readonly playerTeammateBoard?: unknown;
|
|
5
|
+
readonly opponentBoard?: unknown;
|
|
6
|
+
readonly opponentTeammateBoard?: unknown;
|
|
7
|
+
readonly gameState?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashBattleInfo = void 0;
|
|
4
|
+
const hashBattleInfo = (battleInfo) => {
|
|
5
|
+
var _a, _b, _c, _d, _e;
|
|
6
|
+
if (!battleInfo) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const payload = {
|
|
10
|
+
gameState: (_a = battleInfo.gameState) !== null && _a !== void 0 ? _a : null,
|
|
11
|
+
opponentBoard: (_b = battleInfo.opponentBoard) !== null && _b !== void 0 ? _b : null,
|
|
12
|
+
opponentTeammateBoard: (_c = battleInfo.opponentTeammateBoard) !== null && _c !== void 0 ? _c : null,
|
|
13
|
+
playerBoard: (_d = battleInfo.playerBoard) !== null && _d !== void 0 ? _d : null,
|
|
14
|
+
playerTeammateBoard: (_e = battleInfo.playerTeammateBoard) !== null && _e !== void 0 ? _e : null,
|
|
15
|
+
};
|
|
16
|
+
return fnv1a32(stableStringify(payload));
|
|
17
|
+
};
|
|
18
|
+
exports.hashBattleInfo = hashBattleInfo;
|
|
19
|
+
const stableStringify = (value) => {
|
|
20
|
+
var _a;
|
|
21
|
+
if (value === null || typeof value !== 'object') {
|
|
22
|
+
return (_a = JSON.stringify(value)) !== null && _a !== void 0 ? _a : 'null';
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return `[${value.map((entry) => stableStringify(entry)).join(',')}]`;
|
|
26
|
+
}
|
|
27
|
+
const record = value;
|
|
28
|
+
const keys = Object.keys(record).sort();
|
|
29
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(',')}}`;
|
|
30
|
+
};
|
|
31
|
+
const fnv1a32 = (text) => {
|
|
32
|
+
let hash = 0x811c9dc5;
|
|
33
|
+
for (let i = 0; i < text.length; i++) {
|
|
34
|
+
hash ^= text.charCodeAt(i);
|
|
35
|
+
hash = Math.imul(hash, 0x01000193);
|
|
36
|
+
}
|
|
37
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=battle-info-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"battle-info-hash.js","sourceRoot":"","sources":["../src/battle-info-hash.ts"],"names":[],"mappings":";;;AAKO,MAAM,cAAc,GAAG,CAAC,UAAkD,EAAiB,EAAE;;IACnG,IAAI,CAAC,UAAU,EAAE;QAChB,OAAO,IAAI,CAAC;KACZ;IACD,MAAM,OAAO,GAAG;QACf,SAAS,EAAE,MAAA,UAAU,CAAC,SAAS,mCAAI,IAAI;QACvC,aAAa,EAAE,MAAA,UAAU,CAAC,aAAa,mCAAI,IAAI;QAC/C,qBAAqB,EAAE,MAAA,UAAU,CAAC,qBAAqB,mCAAI,IAAI;QAC/D,WAAW,EAAE,MAAA,UAAU,CAAC,WAAW,mCAAI,IAAI;QAC3C,mBAAmB,EAAE,MAAA,UAAU,CAAC,mBAAmB,mCAAI,IAAI;KAC3D,CAAC;IACF,OAAO,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC;AAZW,QAAA,cAAc,kBAYzB;AAUF,MAAM,eAAe,GAAG,CAAC,KAAc,EAAU,EAAE;;IAClD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QAChD,OAAO,MAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,mCAAI,MAAM,CAAC;KACvC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;KACrE;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACrG,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE;IACxC,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;KACnC;IACD,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnD,CAAC,CAAC","sourcesContent":["/**\r\n * Stable hash of the boards and game state that were simulated.\r\n * Excludes options and debugState: the feedback lambda rewrites options before resimulating.\r\n * Keep in sync with api-feedback/src/battle-info-hash.ts.\r\n */\r\nexport const hashBattleInfo = (battleInfo: BattleInfoHashInput | null | undefined): string | null => {\r\n\tif (!battleInfo) {\r\n\t\treturn null;\r\n\t}\r\n\tconst payload = {\r\n\t\tgameState: battleInfo.gameState ?? null,\r\n\t\topponentBoard: battleInfo.opponentBoard ?? null,\r\n\t\topponentTeammateBoard: battleInfo.opponentTeammateBoard ?? null,\r\n\t\tplayerBoard: battleInfo.playerBoard ?? null,\r\n\t\tplayerTeammateBoard: battleInfo.playerTeammateBoard ?? null,\r\n\t};\r\n\treturn fnv1a32(stableStringify(payload));\r\n};\r\n\r\ninterface BattleInfoHashInput {\r\n\treadonly playerBoard?: unknown;\r\n\treadonly playerTeammateBoard?: unknown;\r\n\treadonly opponentBoard?: unknown;\r\n\treadonly opponentTeammateBoard?: unknown;\r\n\treadonly gameState?: unknown;\r\n}\r\n\r\nconst stableStringify = (value: unknown): string => {\r\n\tif (value === null || typeof value !== 'object') {\r\n\t\treturn JSON.stringify(value) ?? 'null';\r\n\t}\r\n\tif (Array.isArray(value)) {\r\n\t\treturn `[${value.map((entry) => stableStringify(entry)).join(',')}]`;\r\n\t}\r\n\tconst record = value as Record<string, unknown>;\r\n\tconst keys = Object.keys(record).sort();\r\n\treturn `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(',')}}`;\r\n};\r\n\r\nconst fnv1a32 = (text: string): string => {\r\n\tlet hash = 0x811c9dc5;\r\n\tfor (let i = 0; i < text.length; i++) {\r\n\t\thash ^= text.charCodeAt(i);\r\n\t\thash = Math.imul(hash, 0x01000193);\r\n\t}\r\n\treturn (hash >>> 0).toString(16).padStart(8, '0');\r\n};\r\n"]}
|
|
@@ -6,27 +6,30 @@ const stats_1 = require("../../../simulation/stats");
|
|
|
6
6
|
exports.CThun = {
|
|
7
7
|
cardIds: ["BGFYM_000", "BGFYM_000_G"],
|
|
8
8
|
onAwaken: (minion, input) => {
|
|
9
|
-
var _a, _b
|
|
9
|
+
var _a, _b;
|
|
10
10
|
const others = input.board.filter((e) => e.entityId !== minion.entityId && e.health > 0 && !e.definitelyDead);
|
|
11
11
|
if (!others.length) {
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const double = minion.cardId === "BGFYM_000_G" ? 2 : 1;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < attack; i++) {
|
|
20
|
-
const target = (0, utils_1.pickRandom)(others, minion);
|
|
21
|
-
attackByEntity.set(target.entityId, ((_c = attackByEntity.get(target.entityId)) !== null && _c !== void 0 ? _c : 0) + 1);
|
|
22
|
-
}
|
|
23
|
-
for (let i = 0; i < health; i++) {
|
|
24
|
-
const target = (0, utils_1.pickRandom)(others, minion);
|
|
25
|
-
healthByEntity.set(target.entityId, ((_d = healthByEntity.get(target.entityId)) !== null && _d !== void 0 ? _d : 0) + 1);
|
|
26
|
-
}
|
|
27
|
-
for (const other of others) {
|
|
28
|
-
(0, stats_1.modifyStats)(other, minion, (_e = attackByEntity.get(other.entityId)) !== null && _e !== void 0 ? _e : 0, (_f = healthByEntity.get(other.entityId)) !== null && _f !== void 0 ? _f : 0, input.board, input.hero, input.gameState);
|
|
15
|
+
const attackShares = splitEvenly(((_a = minion.attack) !== null && _a !== void 0 ? _a : 0) * double, others, minion);
|
|
16
|
+
const healthShares = splitEvenly(((_b = minion.health) !== null && _b !== void 0 ? _b : 0) * double, others, minion);
|
|
17
|
+
for (let i = 0; i < others.length; i++) {
|
|
18
|
+
(0, stats_1.modifyStats)(others[i], minion, attackShares[i], healthShares[i], input.board, input.hero, input.gameState);
|
|
29
19
|
}
|
|
30
20
|
},
|
|
31
21
|
};
|
|
22
|
+
const splitEvenly = (total, targets, source) => {
|
|
23
|
+
const base = Math.floor(total / targets.length);
|
|
24
|
+
let remaining = total - base * targets.length;
|
|
25
|
+
const shares = targets.map(() => base);
|
|
26
|
+
const leftoverTargets = [...targets];
|
|
27
|
+
while (remaining > 0 && leftoverTargets.length) {
|
|
28
|
+
const target = (0, utils_1.pickRandom)(leftoverTargets, source);
|
|
29
|
+
shares[targets.indexOf(target)] += 1;
|
|
30
|
+
leftoverTargets.splice(leftoverTargets.indexOf(target), 1);
|
|
31
|
+
remaining--;
|
|
32
|
+
}
|
|
33
|
+
return shares;
|
|
34
|
+
};
|
|
32
35
|
//# sourceMappingURL=cthun.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cthun.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/cthun.ts"],"names":[],"mappings":";;;AAWA,mDAAqD;AACrD,qDAAwD;AAG3C,QAAA,KAAK,GAAc;IAC/B,OAAO,EAAE,4BAAoD;IAC7D,QAAQ,EAAE,CAAC,MAAmB,EAAE,KAAuB,EAAE,EAAE;;QAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QAC9G,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YACnB,OAAO;SACP;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,kBAA8B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,MAAM,
|
|
1
|
+
{"version":3,"file":"cthun.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/cthun.ts"],"names":[],"mappings":";;;AAWA,mDAAqD;AACrD,qDAAwD;AAG3C,QAAA,KAAK,GAAc;IAC/B,OAAO,EAAE,4BAAoD;IAC7D,QAAQ,EAAE,CAAC,MAAmB,EAAE,KAAuB,EAAE,EAAE;;QAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QAC9G,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YACnB,OAAO;SACP;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,kBAA8B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,CAAC,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAChF,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,MAAA,MAAM,CAAC,MAAM,mCAAI,CAAC,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAChF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACvC,IAAA,mBAAW,EAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SAC3G;IACF,CAAC;CACD,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,OAAsB,EAAE,MAAmB,EAAY,EAAE;IAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,SAAS,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,eAAe,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IACrC,OAAO,SAAS,GAAG,CAAC,IAAI,eAAe,CAAC,MAAM,EAAE;QAC/C,MAAM,MAAM,GAAG,IAAA,kBAAU,EAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,eAAe,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3D,SAAS,EAAE,CAAC;KACZ;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC","sourcesContent":["/**\r\n * C'Thun (Tier 3, 1/1)\r\n * Deity. After this awakens, give this minion's stats split amongst your other minions.\r\n *\r\n * Golden C'Thun (Tier 3, 2/2)\r\n * Deity. After this awakens, give double this minion's stats split amongst your other minions.\r\n *\r\n * Only fires on BG_OldGod awaken, not generic spawn / reborn.\r\n */\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { pickRandom } from '../../../services/utils';\r\nimport { modifyStats } from '../../../simulation/stats';\r\nimport { DeityAwakenInput, DeityCard } from '../../card.interface';\r\n\r\nexport const CThun: DeityCard = {\r\n\tcardIds: [CardIds.Cthun_BGFYM_000, CardIds.Cthun_BGFYM_000_G],\r\n\tonAwaken: (minion: BoardEntity, input: DeityAwakenInput) => {\r\n\t\tconst others = input.board.filter((e) => e.entityId !== minion.entityId && e.health > 0 && !e.definitelyDead);\r\n\t\tif (!others.length) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst double = minion.cardId === CardIds.Cthun_BGFYM_000_G ? 2 : 1;\r\n\t\tconst attackShares = splitEvenly((minion.attack ?? 0) * double, others, minion);\r\n\t\tconst healthShares = splitEvenly((minion.health ?? 0) * double, others, minion);\r\n\t\tfor (let i = 0; i < others.length; i++) {\r\n\t\t\tmodifyStats(others[i], minion, attackShares[i], healthShares[i], input.board, input.hero, input.gameState);\r\n\t\t}\r\n\t},\r\n};\r\n\r\nconst splitEvenly = (total: number, targets: BoardEntity[], source: BoardEntity): number[] => {\r\n\tconst base = Math.floor(total / targets.length);\r\n\tlet remaining = total - base * targets.length;\r\n\tconst shares = targets.map(() => base);\r\n\tconst leftoverTargets = [...targets];\r\n\twhile (remaining > 0 && leftoverTargets.length) {\r\n\t\tconst target = pickRandom(leftoverTargets, source);\r\n\t\tshares[targets.indexOf(target)] += 1;\r\n\t\tleftoverTargets.splice(leftoverTargets.indexOf(target), 1);\r\n\t\tremaining--;\r\n\t}\r\n\treturn shares;\r\n};\r\n"]}
|
|
@@ -8,9 +8,7 @@ exports.UnderrotSpawn = {
|
|
|
8
8
|
deathrattleSpawn: (minion, input) => {
|
|
9
9
|
const quantity = minion.cardId === "BG36_116_G" ? 2 : 1;
|
|
10
10
|
const atk = minion.scriptDataNum1 || quantity;
|
|
11
|
-
const tentacleId =
|
|
12
|
-
? "BGFYM_002t_G"
|
|
13
|
-
: "BGFYM_002t";
|
|
11
|
+
const tentacleId = "BGFYM_002t";
|
|
14
12
|
(0, deathrattle_spawns_1.simplifiedSpawnEntitiesWithAddToBoard)(tentacleId, quantity, input, minion, input.deadEntityIndexFromRight);
|
|
15
13
|
for (const target of input.boardWithDeadEntity) {
|
|
16
14
|
(0, stats_1.modifyStats)(target, minion, atk, 0, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.gameState);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"underrot-spawn.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/underrot-spawn.ts"],"names":[],"mappings":";;;AAUA,+EAA+F;AAC/F,qDAAwD;AAG3C,QAAA,aAAa,GAAyB;IAClD,OAAO,EAAE,0BAAkE;IAC3E,gBAAgB,EAAE,CAAC,MAAmB,EAAE,KAAgC,EAAE,EAAE;QAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,iBAAqC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,IAAI,QAAQ,CAAC;QAC9C,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"underrot-spawn.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/underrot-spawn.ts"],"names":[],"mappings":";;;AAUA,+EAA+F;AAC/F,qDAAwD;AAG3C,QAAA,aAAa,GAAyB;IAClD,OAAO,EAAE,0BAAkE;IAC3E,gBAAgB,EAAE,CAAC,MAAmB,EAAE,KAAgC,EAAE,EAAE;QAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,iBAAqC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,IAAI,QAAQ,CAAC;QAC9C,MAAM,UAAU,eAA2C,CAAC;QAC5D,IAAA,0DAAqC,EAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAE3G,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE;YAC/C,IAAA,mBAAW,EACV,MAAM,EACN,MAAM,EACN,GAAG,EACH,CAAC,EACD,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,uBAAuB,EAC7B,KAAK,CAAC,SAAS,CACf,CAAC;SACF;QACD,OAAO,EAAE,CAAC;IACX,CAAC;CACD,CAAC","sourcesContent":["/**\r\n * Underrot Spawn (Tier 2, 2/2)\r\n * Deathrattle: Summon a 0/2 Tentacle with Taunt. Give your minions +1 Attack.\r\n *\r\n * Golden Underrot Spawn (Tier 2, 4/4)\r\n * Deathrattle: Summon two 0/2 Tentacles with Taunt. Give your minions +2 Attack.\r\n */\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { DeathrattleTriggeredInput } from '../../../simulation/deathrattle-on-trigger';\r\nimport { simplifiedSpawnEntitiesWithAddToBoard } from '../../../simulation/deathrattle-spawns';\r\nimport { modifyStats } from '../../../simulation/stats';\r\nimport { DeathrattleSpawnCard } from '../../card.interface';\r\n\r\nexport const UnderrotSpawn: DeathrattleSpawnCard = {\r\n\tcardIds: [CardIds.UnderrotSpawn_BG36_116, CardIds.UnderrotSpawn_BG36_116_G],\r\n\tdeathrattleSpawn: (minion: BoardEntity, input: DeathrattleTriggeredInput) => {\r\n\t\tconst quantity = minion.cardId === CardIds.UnderrotSpawn_BG36_116_G ? 2 : 1;\r\n\t\tconst atk = minion.scriptDataNum1 || quantity;\r\n\t\tconst tentacleId = CardIds.AberrantTentacleToken_BGFYM_002t;\r\n\t\tsimplifiedSpawnEntitiesWithAddToBoard(tentacleId, quantity, input, minion, input.deadEntityIndexFromRight);\r\n\r\n\t\tfor (const target of input.boardWithDeadEntity) {\r\n\t\t\tmodifyStats(\r\n\t\t\t\ttarget,\r\n\t\t\t\tminion,\r\n\t\t\t\tatk,\r\n\t\t\t\t0,\r\n\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\tinput.gameState,\r\n\t\t\t);\r\n\t\t}\r\n\t\treturn [];\r\n\t},\r\n};\r\n"]}
|
|
@@ -14,6 +14,8 @@ export type { OnAttackInput } from './simulation/on-attack';
|
|
|
14
14
|
export { triggerBattlecry } from './simulation/battlecries';
|
|
15
15
|
export { doFullAttack } from './simulation/attack';
|
|
16
16
|
export { applyOnAttackEffects } from './simulation/on-attack';
|
|
17
|
+
export { hashBattleInfo } from './battle-info-hash';
|
|
18
|
+
export declare const SIMULATOR_VERSION: string;
|
|
17
19
|
export declare const ensureCardsLoaded: (cards?: AllCardsService) => Promise<boolean>;
|
|
18
20
|
export declare const assignCards: (cards: AllCardsService) => void;
|
|
19
21
|
export interface SimulateSingleCombatOptions {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.simulateBattle = exports.simulateSingleCombat = exports.assignCards = exports.ensureCardsLoaded = exports.applyOnAttackEffects = exports.doFullAttack = exports.triggerBattlecry = exports.triggerRally = exports.hasRally = exports.isValidDeathrattleEnchantment = exports.withRng = exports.random = exports.getActiveRng = exports.defaultRng = exports.createSeededRng = void 0;
|
|
3
|
+
exports.simulateBattle = exports.simulateSingleCombat = exports.assignCards = exports.ensureCardsLoaded = exports.SIMULATOR_VERSION = exports.hashBattleInfo = exports.applyOnAttackEffects = exports.doFullAttack = exports.triggerBattlecry = exports.triggerRally = exports.hasRally = exports.isValidDeathrattleEnchantment = exports.withRng = exports.random = exports.getActiveRng = exports.defaultRng = exports.createSeededRng = void 0;
|
|
4
4
|
const reference_data_1 = require("@firestone-hs/reference-data");
|
|
5
|
+
const battle_info_hash_1 = require("./battle-info-hash");
|
|
5
6
|
const cards_data_1 = require("./cards/cards-data");
|
|
6
7
|
const debug_state_1 = require("./debug-state");
|
|
7
8
|
const input_clone_1 = require("./input-clone");
|
|
@@ -30,6 +31,9 @@ var attack_1 = require("./simulation/attack");
|
|
|
30
31
|
Object.defineProperty(exports, "doFullAttack", { enumerable: true, get: function () { return attack_1.doFullAttack; } });
|
|
31
32
|
var on_attack_1 = require("./simulation/on-attack");
|
|
32
33
|
Object.defineProperty(exports, "applyOnAttackEffects", { enumerable: true, get: function () { return on_attack_1.applyOnAttackEffects; } });
|
|
34
|
+
var battle_info_hash_2 = require("./battle-info-hash");
|
|
35
|
+
Object.defineProperty(exports, "hashBattleInfo", { enumerable: true, get: function () { return battle_info_hash_2.hashBattleInfo; } });
|
|
36
|
+
exports.SIMULATOR_VERSION = require('../package.json').version;
|
|
33
37
|
const MAX_SAMPLE_RESIMS = 500;
|
|
34
38
|
const CARDS_LOAD_ATTEMPTS = 3;
|
|
35
39
|
let globalCards = new reference_data_1.AllCardsService();
|
|
@@ -174,7 +178,7 @@ exports.default = async (event) => {
|
|
|
174
178
|
return response;
|
|
175
179
|
};
|
|
176
180
|
const simulateBattle = function* (battleInput, cards, cardsData) {
|
|
177
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
181
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
178
182
|
if (!hasLoadedCards(cards)) {
|
|
179
183
|
console.error('[simulate-bgs-battle] reference cards are empty, cannot simulate battle', cards);
|
|
180
184
|
return null;
|
|
@@ -205,6 +209,8 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
|
|
|
205
209
|
lostLethalPercent: undefined,
|
|
206
210
|
averageDamageWon: undefined,
|
|
207
211
|
averageDamageLost: undefined,
|
|
212
|
+
simulatorVersion: exports.SIMULATOR_VERSION,
|
|
213
|
+
inputHash: (_l = (0, battle_info_hash_1.hashBattleInfo)(battleInput)) !== null && _l !== void 0 ? _l : undefined,
|
|
208
214
|
};
|
|
209
215
|
const spectator = new spectator_1.Spectator(includeOutcomeSamples);
|
|
210
216
|
const inputReady = (0, input_sanitation_1.buildFinalInput)(battleInput, cards, cardsData);
|
|
@@ -212,7 +218,7 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
|
|
|
212
218
|
const cloneTemplate = (0, input_clone_1.buildTemplateCloner)((0, input_clone_1.cloneInput3)(inputReady));
|
|
213
219
|
const runOne = () => runSingleBattle(cloneTemplate, cards, cardsData, spectator, rng_1.defaultRng);
|
|
214
220
|
try {
|
|
215
|
-
!((
|
|
221
|
+
!((_m = battleInput.options) === null || _m === void 0 ? void 0 : _m.skipInfoLogs) && console.time('simulation');
|
|
216
222
|
const outcomes = {};
|
|
217
223
|
for (let i = 0; i < numberOfSimulations; i++) {
|
|
218
224
|
const battleResult = runOne();
|
|
@@ -231,7 +237,7 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
|
|
|
231
237
|
simulationResult.lost++;
|
|
232
238
|
simulationResult.damageLost += battleResult.damageDealt;
|
|
233
239
|
simulationResult.damageLosts.push(battleResult.damageDealt);
|
|
234
|
-
outcomes[battleResult.damageDealt] = ((
|
|
240
|
+
outcomes[battleResult.damageDealt] = ((_o = outcomes[battleResult.damageDealt]) !== null && _o !== void 0 ? _o : 0) + 1;
|
|
235
241
|
if (battleInput.playerBoard.player.hpLeft &&
|
|
236
242
|
battleResult.damageDealt >= battleInput.playerBoard.player.hpLeft) {
|
|
237
243
|
simulationResult.lostLethal++;
|
|
@@ -250,7 +256,7 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
|
|
|
250
256
|
}
|
|
251
257
|
}
|
|
252
258
|
updateSimulationResult(simulationResult, inputReady, damageConfidence);
|
|
253
|
-
!((
|
|
259
|
+
!((_p = battleInput.options) === null || _p === void 0 ? void 0 : _p.skipInfoLogs) && console.timeEnd('simulation');
|
|
254
260
|
if (includeOutcomeSamples) {
|
|
255
261
|
const neededOutcomes = [];
|
|
256
262
|
simulationResult.won > 0 && neededOutcomes.push('won');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulate-bgs-battle.js","sourceRoot":"","sources":["../src/simulate-bgs-battle.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { BgsBattleInfo } from './bgs-battle-info';\r\nimport { CardsData } from './cards/cards-data';\r\nimport { debugState } from './debug-state';\r\nimport { buildTemplateCloner, cloneInput3 } from './input-clone';\r\nimport { buildFinalInput } from './input-sanitation';\r\nimport { createSeededRng, defaultRng, Rng, withRng } from './services/rng';\r\nimport { sleep } from './services/utils';\r\nimport { SimulationResult } from './simulation-result';\r\nimport { combatProfileAdd, combatProfileEnabled, combatProfileTickFight } from './simulation/combat-profile';\r\nimport { FullGameState } from './simulation/internal-game-state';\r\nimport { SharedState } from './simulation/shared-state';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { noopSpectator, Spectator } from './simulation/spectator/spectator';\r\nimport { SingleSimulationResult } from './single-simulation-result';\r\n\r\nexport { createSeededRng, defaultRng, getActiveRng, random, withRng } from './services/rng';\r\nexport type { Rng } from './services/rng';\r\nexport { isValidDeathrattleEnchantment } from './simulation/deathrattle-utils';\r\n/** Tavern Activate / Hand of Deios-style Rally triggers (requires a combat FullGameState). */\r\nexport { hasRally } from './cards/card.interface';\r\nexport type { RallyCard } from './cards/card.interface';\r\nexport { triggerRally } from './mechanics/rally';\r\nexport type { OnAttackInput } from './simulation/on-attack';\r\n/** Tavern / Hand of Deios-style Battlecry re-triggers. */\r\nexport { triggerBattlecry } from './simulation/battlecries';\r\n/** Full attack resolution (on-attack, damage, after-attack, deaths) for tavern attacks e.g. Fishbait. */\r\nexport { doFullAttack } from './simulation/attack';\r\nexport { applyOnAttackEffects } from './simulation/on-attack';\r\n\r\n// Cap for the post-hoc outcome sample capture phase: rare outcomes (e.g. a 0.1% tie\r\n// chance) might never show up again when re-simulating, so we stop hunting after this\r\n// many extra battles\r\nconst MAX_SAMPLE_RESIMS = 500;\r\nconst CARDS_LOAD_ATTEMPTS = 3;\r\n\r\nlet globalCards = new AllCardsService();\r\n/** In-flight / completed cards init for this warm container (avoids re-fetch per invoke). */\r\nlet cardsInitPromise: Promise<boolean> | null = null;\r\n\r\n/** `getCards()` materializes the full DB (~14ms). Validate once per instance. */\r\nconst verifiedCardDbs = new WeakSet<object>();\r\n\r\nconst hasLoadedCards = (cards: AllCardsService | null | undefined): boolean => {\r\n\tif (!cards) {\r\n\t\treturn false;\r\n\t}\r\n\tif (verifiedCardDbs.has(cards as object)) {\r\n\t\treturn true;\r\n\t}\r\n\t// Prefer a single-card lookup. getCards() copies the whole DB (~14ms).\r\n\tconst probe = cards.getCard?.('CS2_122');\r\n\tif (probe?.id) {\r\n\t\tverifiedCardDbs.add(cards as object);\r\n\t\treturn true;\r\n\t}\r\n\tconst loaded = !!cards.getCards?.()?.length;\r\n\tif (loaded) {\r\n\t\tverifiedCardDbs.add(cards as object);\r\n\t}\r\n\treturn loaded;\r\n};\r\n\r\nconst loadCardsWithRetry = async (cards: AllCardsService): Promise<boolean> => {\r\n\tfor (let attempt = 1; attempt <= CARDS_LOAD_ATTEMPTS; attempt++) {\r\n\t\ttry {\r\n\t\t\tawait cards.initializeCardsDb();\r\n\t\t\tif (hasLoadedCards(cards)) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tconsole.warn('[simulate-bgs-battle] cards still empty after initializeCardsDb', attempt);\r\n\t\t} catch (e) {\r\n\t\t\tconsole.warn('[simulate-bgs-battle] cards load attempt failed', attempt, e);\r\n\t\t}\r\n\t\tif (attempt < CARDS_LOAD_ATTEMPTS) {\r\n\t\t\tawait sleep(250 * attempt);\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n};\r\n\r\n/**\r\n * Load reference cards once per warm container. Retries transient CDN/fetch failures.\r\n * Returns false when cards could not be loaded after retries.\r\n */\r\nexport const ensureCardsLoaded = async (cards: AllCardsService = globalCards): Promise<boolean> => {\r\n\tif (hasLoadedCards(cards)) {\r\n\t\treturn true;\r\n\t}\r\n\tif (!cardsInitPromise) {\r\n\t\tcardsInitPromise = loadCardsWithRetry(cards).then((ok) => {\r\n\t\t\tif (!ok) {\r\n\t\t\t\t// Allow the next invoke to retry after a hard failure.\r\n\t\t\t\tcardsInitPromise = null;\r\n\t\t\t}\r\n\t\t\treturn ok;\r\n\t\t});\r\n\t}\r\n\treturn cardsInitPromise;\r\n};\r\n\r\nexport const assignCards = (cards: AllCardsService) => {\r\n\tglobalCards = cards;\r\n\tcardsInitPromise = hasLoadedCards(cards) ? Promise.resolve(true) : null;\r\n};\r\n\r\nexport interface SimulateSingleCombatOptions {\r\n\t/** If set, creates a Mulberry32 RNG from this seed. Ignored when `rng` is provided. */\r\n\treadonly seed?: number;\r\n\t/** Explicit RNG instance. Takes precedence over `seed`. */\r\n\treadonly rng?: Rng;\r\n\t/** When true, records spectator actions (slower). Default false. */\r\n\treadonly includeSpectator?: boolean;\r\n}\r\n\r\n/**\r\n * Run exactly one combat with a deterministic (or default) RNG.\r\n * Intended for the full-game / self-play env — not for Firestone win% Monte Carlo.\r\n */\r\nexport const simulateSingleCombat = (\r\n\tbattleInput: BgsBattleInfo,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n\toptions: SimulateSingleCombatOptions = {},\r\n): SingleSimulationResult => {\r\n\tconst profile = combatProfileEnabled();\r\n\tconst getCardsT0 = profile ? performance.now() : 0;\r\n\tif (!hasLoadedCards(cards)) {\r\n\t\tconsole.error('[simulate-bgs-battle] reference cards are empty, cannot simulate combat', cards);\r\n\t\treturn null;\r\n\t}\r\n\tif (profile) {\r\n\t\tcombatProfileAdd('getCardsMs', performance.now() - getCardsT0);\r\n\t}\r\n\r\n\tconst rng = options.rng ?? (options.seed !== undefined ? createSeededRng(options.seed) : defaultRng);\r\n\tconst includeSpectator = options.includeSpectator ?? false;\r\n\r\n\tapplyDebugStateFromInput(battleInput);\r\n\ttry {\r\n\t\tconst buildT0 = profile ? performance.now() : 0;\r\n\t\tconst inputReady = buildFinalInput(battleInput, cards, cardsData);\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('buildFinalInputMs', performance.now() - buildT0);\r\n\t\t}\r\n\t\t// One fight: cloneInput3 per use (battle + optional initial-state snapshot).\r\n\t\t// Firestone Monte Carlo keeps buildTemplateCloner for 8000 sims.\r\n\t\tconst cloneTemplate = () => {\r\n\t\t\tconst cloneT0 = profile ? performance.now() : 0;\r\n\t\t\tconst cloned = cloneInput3(inputReady);\r\n\t\t\tif (profile) {\r\n\t\t\t\tcombatProfileAdd('cloneMs', performance.now() - cloneT0);\r\n\t\t\t}\r\n\t\t\treturn cloned;\r\n\t\t};\r\n\t\tconst spectator = includeSpectator ? new Spectator(true) : noopSpectator;\r\n\t\tif (includeSpectator) {\r\n\t\t\tspectator.startRecording();\r\n\t\t}\r\n\t\tconst runT0 = profile ? performance.now() : 0;\r\n\t\tconst result = withRng(rng, () => runSingleBattle(cloneTemplate, cards, cardsData, spectator, rng));\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('runBattleMs', performance.now() - runT0);\r\n\t\t\tcombatProfileTickFight();\r\n\t\t}\r\n\t\treturn result;\r\n\t} finally {\r\n\t\tclearDebugStateFromInput(battleInput);\r\n\t}\r\n};\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tif (!event.body?.length) {\r\n\t\tconsole.warn('missing event body', event);\r\n\t\treturn {\r\n\t\t\tstatusCode: 400,\r\n\t\t\tisBase64Encoded: false,\r\n\t\t\tbody: null,\r\n\t\t};\r\n\t}\r\n\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tconst cards = globalCards;\r\n\tconst cardsLoaded = await ensureCardsLoaded(cards);\r\n\tif (!cardsLoaded) {\r\n\t\tconsole.error('[simulate-bgs-battle] failed to load reference cards after retries');\r\n\t\treturn {\r\n\t\t\tstatusCode: 503,\r\n\t\t\tisBase64Encoded: false,\r\n\t\t\tbody: JSON.stringify({ error: 'cards_unavailable' }),\r\n\t\t};\r\n\t}\r\n\tconst cardsData = new CardsData(cards, false);\r\n\tcardsData.inititialize(\r\n\t\tbattleInput.gameState?.validTribes ?? battleInput.options?.validTribes,\r\n\t\tbattleInput.gameState?.anomalies ?? [],\r\n\t);\r\n\tconst battleIterator = simulateBattle(battleInput, cards, cardsData);\r\n\r\n\t// Iterate through all intermediate results to reach the final result\r\n\tlet result = battleIterator.next();\r\n\twhile (!result.done) {\r\n\t\tresult = battleIterator.next();\r\n\t}\r\n\r\n\tconst simulationResult = result.value;\r\n\t// console.debug('simulationResult', simulationResult);\r\n\r\n\tconst response = {\r\n\t\tstatusCode: 200,\r\n\t\tisBase64Encoded: false,\r\n\t\tbody: JSON.stringify(simulationResult),\r\n\t};\r\n\treturn response;\r\n};\r\n\r\nexport const simulateBattle = function* (\r\n\tbattleInput: BgsBattleInfo,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n): Generator<SimulationResult, SimulationResult, void> {\r\n\tif (!hasLoadedCards(cards)) {\r\n\t\tconsole.error('[simulate-bgs-battle] reference cards are empty, cannot simulate battle', cards);\r\n\t\treturn null;\r\n\t}\r\n\t// !battleInput.options?.skipInfoLogs && console.time('full-sim');\r\n\tconst start = Date.now();\r\n\tconst maxAcceptableDuration = battleInput.options?.maxAcceptableDuration || 8000;\r\n\tconst hideMaxSimulationDurationWarning = battleInput.options?.hideMaxSimulationDurationWarning ?? false;\r\n\tconst numberOfSimulations = battleInput.options?.numberOfSimulations || 8000;\r\n\tconst intermediateSteps = battleInput.options?.intermediateResults ?? 200;\r\n\tconst damageConfidence = battleInput.options?.damageConfidence ?? 0.9;\r\n\tconst includeOutcomeSamples = battleInput.options?.includeOutcomeSamples ?? true;\r\n\tconst simulationResult: SimulationResult = {\r\n\t\twonLethal: 0,\r\n\t\twon: 0,\r\n\t\ttied: 0,\r\n\t\tlost: 0,\r\n\t\tlostLethal: 0,\r\n\t\tdamageWons: [],\r\n\t\tdamageWon: 0,\r\n\t\tdamageWonRange: null,\r\n\t\tdamageLosts: [],\r\n\t\tdamageLost: 0,\r\n\t\tdamageLostRange: null,\r\n\t\twonLethalPercent: undefined,\r\n\t\twonPercent: undefined,\r\n\t\ttiedPercent: undefined,\r\n\t\tlostPercent: undefined,\r\n\t\tlostLethalPercent: undefined,\r\n\t\taverageDamageWon: undefined,\r\n\t\taverageDamageLost: undefined,\r\n\t};\r\n\r\n\tconst spectator = new Spectator(includeOutcomeSamples);\r\n\tconst inputReady = buildFinalInput(battleInput, cards, cardsData);\r\n\r\n\tapplyDebugStateFromInput(battleInput);\r\n\r\n\t// cloneInput3 normalizes the input (sorted secrets, reset pendingAttackBuffs, etc.).\r\n\t// The compiled cloner re-materializes that normalized shape as an object literal, which is\r\n\t// faster than JSON.parse of a template string (~0.02ms vs ~0.12ms per clone on a 7v7\r\n\t// board), which was itself faster than generic recursive cloners (~0.19-0.23ms) and\r\n\t// structuredClone (~0.46ms)\r\n\tconst cloneTemplate = buildTemplateCloner(cloneInput3(inputReady));\r\n\r\n\tconst runOne = () => runSingleBattle(cloneTemplate, cards, cardsData, spectator, defaultRng);\r\n\r\n\ttry {\r\n\t\t!battleInput.options?.skipInfoLogs && console.time('simulation');\r\n\t\tconst outcomes = {};\r\n\t\tfor (let i = 0; i < numberOfSimulations; i++) {\r\n\t\t\tconst battleResult = runOne();\r\n\t\t\tif (!battleResult) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (battleResult.result === 'won') {\r\n\t\t\t\tsimulationResult.won++;\r\n\t\t\t\tsimulationResult.damageWon += battleResult.damageDealt;\r\n\t\t\t\tsimulationResult.damageWons.push(battleResult.damageDealt);\r\n\t\t\t\tif (battleResult.damageDealt >= battleInput.opponentBoard.player.hpLeft) {\r\n\t\t\t\t\tsimulationResult.wonLethal++;\r\n\t\t\t\t}\r\n\t\t\t} else if (battleResult.result === 'lost') {\r\n\t\t\t\tsimulationResult.lost++;\r\n\t\t\t\tsimulationResult.damageLost += battleResult.damageDealt;\r\n\t\t\t\tsimulationResult.damageLosts.push(battleResult.damageDealt);\r\n\t\t\t\toutcomes[battleResult.damageDealt] = (outcomes[battleResult.damageDealt] ?? 0) + 1;\r\n\t\t\t\tif (\r\n\t\t\t\t\tbattleInput.playerBoard.player.hpLeft &&\r\n\t\t\t\t\tbattleResult.damageDealt >= battleInput.playerBoard.player.hpLeft\r\n\t\t\t\t) {\r\n\t\t\t\t\tsimulationResult.lostLethal++;\r\n\t\t\t\t}\r\n\t\t\t} else if (battleResult.result === 'tied') {\r\n\t\t\t\tsimulationResult.tied++;\r\n\t\t\t}\r\n\r\n\t\t\tif (Date.now() - start > maxAcceptableDuration && !hideMaxSimulationDurationWarning) {\r\n\t\t\t\t// Can happen in case of inifinite boards, or a bug. Don't hog the user's computer in that case\r\n\t\t\t\tconsole.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms');\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// Yield intermediate result every 200 iterations\r\n\t\t\tif (!!intermediateSteps && i > 0 && i % intermediateSteps === 0) {\r\n\t\t\t\tupdateSimulationResult(simulationResult, inputReady, damageConfidence);\r\n\t\t\t\tyield simulationResult;\r\n\t\t\t}\r\n\t\t}\r\n\t\tupdateSimulationResult(simulationResult, inputReady, damageConfidence);\r\n\t\t!battleInput.options?.skipInfoLogs && console.timeEnd('simulation');\r\n\r\n\t\t// Capture outcome samples post-hoc: re-run a few battles with action recording turned\r\n\t\t// on, until each outcome that actually occurred has a sample. This is much cheaper\r\n\t\t// than recording during the main loop, where almost all recorded actions get discarded.\r\n\t\t// This phase intentionally ignores maxAcceptableDuration: when the main loop was cut\r\n\t\t// short by the time budget, we would otherwise end up with no samples at all.\r\n\t\tif (includeOutcomeSamples) {\r\n\t\t\tconst neededOutcomes: ('won' | 'lost' | 'tied')[] = [];\r\n\t\t\tsimulationResult.won > 0 && neededOutcomes.push('won');\r\n\t\t\tsimulationResult.lost > 0 && neededOutcomes.push('lost');\r\n\t\t\tsimulationResult.tied > 0 && neededOutcomes.push('tied');\r\n\t\t\tconst missingOutcomes = new Set(neededOutcomes);\r\n\t\t\tif (missingOutcomes.size > 0) {\r\n\t\t\t\tspectator.startRecording();\r\n\t\t\t\tfor (let i = 0; i < MAX_SAMPLE_RESIMS && missingOutcomes.size > 0; i++) {\r\n\t\t\t\t\tconst battleResult = runOne();\r\n\t\t\t\t\tif (!battleResult?.result) {\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tspectator.commitBattleResult(battleResult.result);\r\n\t\t\t\t\tif (spectator.hasSampleFor(battleResult.result)) {\r\n\t\t\t\t\t\tmissingOutcomes.delete(battleResult.result);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tspectator.stopRecording();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tspectator.prune();\r\n\t\tsimulationResult.outcomeSamples = spectator.buildOutcomeSamples(battleInput.gameState);\r\n\t\t// Avoid sending this verbose data\r\n\r\n\t\tsimulationResult.damageWons = [];\r\n\t\tsimulationResult.damageLosts = [];\r\n\t\t// !battleInput.options?.skipInfoLogs && console.timeEnd('full-sim');\r\n\t\treturn simulationResult;\r\n\t} finally {\r\n\t\tclearDebugStateFromInput(battleInput);\r\n\t}\r\n};\r\n\r\ntype CloneTemplate = () => BgsBattleInfo;\r\n\r\nconst runSingleBattle = (\r\n\tcloneTemplate: CloneTemplate,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n\tspectator: Spectator,\r\n\trng: Rng,\r\n): SingleSimulationResult => {\r\n\tconst input: BgsBattleInfo = cloneTemplate();\r\n\t// The \"initial\" states are only read by a handful of cards (and mutated by some, like\r\n\t// San'layn Scribe), so we only pay for the second clone when they are actually accessed\r\n\tlet playerInitialState = null;\r\n\tlet opponentInitialState = null;\r\n\tconst buildInitialStates = () => {\r\n\t\tconst inputClone: BgsBattleInfo = cloneTemplate();\r\n\t\tplayerInitialState = {\r\n\t\t\tplayer: inputClone.playerBoard.player,\r\n\t\t\tboard: inputClone.playerBoard.board,\r\n\t\t\tteammate: inputClone.playerTeammateBoard,\r\n\t\t};\r\n\t\topponentInitialState = {\r\n\t\t\tplayer: inputClone.opponentBoard.player,\r\n\t\t\tboard: inputClone.opponentBoard.board,\r\n\t\t\tteammate: inputClone.opponentTeammateBoard,\r\n\t\t};\r\n\t};\r\n\tconst gameState: FullGameState = {\r\n\t\tallCards: cards,\r\n\t\tcardsData: cardsData,\r\n\t\tspectator: spectator,\r\n\t\tsharedState: new SharedState(rng),\r\n\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\tnumberOfPlayersAlive: input.gameState.numberOfPlayersAlive,\r\n\t\tapplyDamageCap: input.options?.applyDamageCap === true,\r\n\t\tapplyIceBlock: input.options?.applyIceBlock === true,\r\n\t\tisDuos: !!input.playerTeammateBoard || !!input.opponentTeammateBoard,\r\n\t\tvalidTribes: input.gameState.validTribes,\r\n\t\tanomalies: input.gameState.anomalies,\r\n\t\tgameState: {\r\n\t\t\tplayer: {\r\n\t\t\t\tplayer: input.playerBoard.player,\r\n\t\t\t\tboard: input.playerBoard.board,\r\n\t\t\t\tteammate: input.playerTeammateBoard,\r\n\t\t\t},\r\n\t\t\topponent: {\r\n\t\t\t\tplayer: input.opponentBoard.player,\r\n\t\t\t\tboard: input.opponentBoard.board,\r\n\t\t\t\tteammate: input.opponentTeammateBoard,\r\n\t\t\t},\r\n\t\t\tget playerInitial() {\r\n\t\t\t\tif (!playerInitialState) {\r\n\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t}\r\n\t\t\t\treturn playerInitialState;\r\n\t\t\t},\r\n\t\t\tget opponentInitial() {\r\n\t\t\t\tif (!opponentInitialState) {\r\n\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t}\r\n\t\t\t\treturn opponentInitialState;\r\n\t\t\t},\r\n\t\t},\r\n\t};\r\n\tconst simulator = new Simulator(gameState);\r\n\treturn simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);\r\n};\r\n\r\nconst applyDebugStateFromInput = (battleInput: BgsBattleInfo) => {\r\n\tif (!battleInput.debugState) {\r\n\t\treturn;\r\n\t}\r\n\tdebugState.active = true;\r\n\tdebugState.forcedCurrentAttacker = battleInput.debugState.forcedCurrentAttacker;\r\n\tdebugState.forcedFaceOffBase =\r\n\t\tbattleInput.debugState.forcedFaceOffBase?.map((f) => ({\r\n\t\t\tattacker: { ...f.attacker },\r\n\t\t\tdefender: { ...f.defender },\r\n\t\t})) ?? [];\r\n\tconst rawPicks = battleInput.debugState.forcedRandomPicks ?? [];\r\n\tdebugState.forcedRandomPicksBase = rawPicks.map((p) => ({\r\n\t\tsource: { ...p.source },\r\n\t\ttarget: { ...p.target },\r\n\t\t...(p.chosenCardId && { chosenCardId: p.chosenCardId }),\r\n\t}));\r\n};\r\n\r\nconst clearDebugStateFromInput = (battleInput: BgsBattleInfo) => {\r\n\tif (battleInput.debugState) {\r\n\t\tdebugState.active = false;\r\n\t}\r\n};\r\n\r\nconst updateSimulationResult = (simulationResult: SimulationResult, input: BgsBattleInfo, damageConfidence: number) => {\r\n\tconst totalMatches = simulationResult.won + simulationResult.tied + simulationResult.lost;\r\n\tsimulationResult.wonPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.won)) / totalMatches) / 10,\r\n\t\tsimulationResult.won,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.wonLethalPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10,\r\n\t\tsimulationResult.wonLethal,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lost)) / totalMatches) / 10,\r\n\t\tsimulationResult.lost,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostLethalPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10,\r\n\t\tsimulationResult.lostLethal,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.tiedPercent = checkRounding(\r\n\t\tMath.max(0, 100 - simulationResult.lostPercent - simulationResult.wonPercent),\r\n\t\tsimulationResult.tied,\r\n\t\ttotalMatches,\r\n\t);\r\n\r\n\t// simulationResult.wonLethalPercent = Math.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10;\r\n\t// simulationResult.lostLethalPercent = Math.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10;\r\n\tconst totalDamageWon = simulationResult.damageWons.reduce((a, b) => a + b, 0);\r\n\tconst totalDamageLost = simulationResult.damageLosts.reduce((a, b) => a + b, 0);\r\n\tconst damageWonRange = calculateDamageRange(simulationResult.damageWons, damageConfidence);\r\n\tconst damageLostRange = calculateDamageRange(simulationResult.damageLosts, damageConfidence);\r\n\tsimulationResult.averageDamageWon = simulationResult.won ? totalDamageWon / simulationResult.won : 0;\r\n\tsimulationResult.averageDamageLost = simulationResult.lost ? totalDamageLost / simulationResult.lost : 0;\r\n\tsimulationResult.damageWonRange = simulationResult.won ? damageWonRange : null;\r\n\tsimulationResult.damageLostRange = simulationResult.lost ? damageLostRange : null;\r\n\r\n\tif (\r\n\t\tsimulationResult.averageDamageWon > 0 &&\r\n\t\tsimulationResult.averageDamageWon < input.playerBoard.player.tavernTier\r\n\t) {\r\n\t\tconsole.warn('average damage won issue');\r\n\t}\r\n\tif (\r\n\t\tsimulationResult.averageDamageLost > 0 &&\r\n\t\tsimulationResult.averageDamageLost < input.opponentBoard.player.tavernTier\r\n\t) {\r\n\t\tconsole.warn('average damage lost issue');\r\n\t}\r\n};\r\n\r\nconst calculateDamageRange = (damageArray: number[], damageConfidence: number): { min: number; max: number } => {\r\n\tif (damageArray.length === 0) {\r\n\t\treturn { min: 0, max: 0 };\r\n\t}\r\n\r\n\t// Sort the array\r\n\tconst sortedDamage = [...damageArray].sort((a, b) => a - b);\r\n\r\n\t// Calculate the 10th and 90th percentiles\r\n\tconst percentile = (arr: number[], p: number) => {\r\n\t\tconst index = Math.floor(p * arr.length);\r\n\t\treturn arr[index];\r\n\t};\r\n\r\n\tconst minDamage = percentile(sortedDamage, 1 - damageConfidence);\r\n\tconst maxDamage = percentile(sortedDamage, damageConfidence);\r\n\r\n\treturn { min: minDamage, max: maxDamage };\r\n};\r\n\r\nconst checkRounding = (roundedValue: number, initialValue: number, totalValue: number): number => {\r\n\tif (roundedValue === 0 && initialValue !== 0) {\r\n\t\treturn 0.01;\r\n\t}\r\n\tif (roundedValue === 100 && initialValue !== totalValue) {\r\n\t\treturn 99.9;\r\n\t}\r\n\treturn roundedValue;\r\n};\r\n\r\n// const cleanEnchantmentsForEntity = (\r\n// \tenchantments: { cardId: string; originEntityId?: number; timing: number }[],\r\n// \tentityIds: readonly number[],\r\n// ): { cardId: string; originEntityId?: number; timing: number }[] => {\r\n// \treturn enchantments.filter(\r\n// \t\t(enchant) =>\r\n// \t\t\tentityIds.indexOf(enchant.originEntityId) !== -1 ||\r\n// \t\t\tvalidEnchantments.indexOf(enchant.cardId as CardIds) !== -1,\r\n// \t);\r\n// };\r\n"]}
|
|
1
|
+
{"version":3,"file":"simulate-bgs-battle.js","sourceRoot":"","sources":["../src/simulate-bgs-battle.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { hashBattleInfo } from './battle-info-hash';\r\nimport { BgsBattleInfo } from './bgs-battle-info';\r\nimport { CardsData } from './cards/cards-data';\r\nimport { debugState } from './debug-state';\r\nimport { buildTemplateCloner, cloneInput3 } from './input-clone';\r\nimport { buildFinalInput } from './input-sanitation';\r\nimport { createSeededRng, defaultRng, Rng, withRng } from './services/rng';\r\nimport { sleep } from './services/utils';\r\nimport { SimulationResult } from './simulation-result';\r\nimport { combatProfileAdd, combatProfileEnabled, combatProfileTickFight } from './simulation/combat-profile';\r\nimport { FullGameState } from './simulation/internal-game-state';\r\nimport { SharedState } from './simulation/shared-state';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { noopSpectator, Spectator } from './simulation/spectator/spectator';\r\nimport { SingleSimulationResult } from './single-simulation-result';\r\n\r\nexport { createSeededRng, defaultRng, getActiveRng, random, withRng } from './services/rng';\r\nexport type { Rng } from './services/rng';\r\nexport { isValidDeathrattleEnchantment } from './simulation/deathrattle-utils';\r\n/** Tavern Activate / Hand of Deios-style Rally triggers (requires a combat FullGameState). */\r\nexport { hasRally } from './cards/card.interface';\r\nexport type { RallyCard } from './cards/card.interface';\r\nexport { triggerRally } from './mechanics/rally';\r\nexport type { OnAttackInput } from './simulation/on-attack';\r\n/** Tavern / Hand of Deios-style Battlecry re-triggers. */\r\nexport { triggerBattlecry } from './simulation/battlecries';\r\n/** Full attack resolution (on-attack, damage, after-attack, deaths) for tavern attacks e.g. Fishbait. */\r\nexport { doFullAttack } from './simulation/attack';\r\nexport { applyOnAttackEffects } from './simulation/on-attack';\r\nexport { hashBattleInfo } from './battle-info-hash';\r\n\r\n/** Inlined from package.json when this module is bundled. A stale window keeps the old value. */\r\n// eslint-disable-next-line @typescript-eslint/no-var-requires\r\nexport const SIMULATOR_VERSION: string = require('../package.json').version;\r\n\r\n// Cap for the post-hoc outcome sample capture phase: rare outcomes (e.g. a 0.1% tie\r\n// chance) might never show up again when re-simulating, so we stop hunting after this\r\n// many extra battles\r\nconst MAX_SAMPLE_RESIMS = 500;\r\nconst CARDS_LOAD_ATTEMPTS = 3;\r\n\r\nlet globalCards = new AllCardsService();\r\n/** In-flight / completed cards init for this warm container (avoids re-fetch per invoke). */\r\nlet cardsInitPromise: Promise<boolean> | null = null;\r\n\r\n/** `getCards()` materializes the full DB (~14ms). Validate once per instance. */\r\nconst verifiedCardDbs = new WeakSet<object>();\r\n\r\nconst hasLoadedCards = (cards: AllCardsService | null | undefined): boolean => {\r\n\tif (!cards) {\r\n\t\treturn false;\r\n\t}\r\n\tif (verifiedCardDbs.has(cards as object)) {\r\n\t\treturn true;\r\n\t}\r\n\t// Prefer a single-card lookup. getCards() copies the whole DB (~14ms).\r\n\tconst probe = cards.getCard?.('CS2_122');\r\n\tif (probe?.id) {\r\n\t\tverifiedCardDbs.add(cards as object);\r\n\t\treturn true;\r\n\t}\r\n\tconst loaded = !!cards.getCards?.()?.length;\r\n\tif (loaded) {\r\n\t\tverifiedCardDbs.add(cards as object);\r\n\t}\r\n\treturn loaded;\r\n};\r\n\r\nconst loadCardsWithRetry = async (cards: AllCardsService): Promise<boolean> => {\r\n\tfor (let attempt = 1; attempt <= CARDS_LOAD_ATTEMPTS; attempt++) {\r\n\t\ttry {\r\n\t\t\tawait cards.initializeCardsDb();\r\n\t\t\tif (hasLoadedCards(cards)) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tconsole.warn('[simulate-bgs-battle] cards still empty after initializeCardsDb', attempt);\r\n\t\t} catch (e) {\r\n\t\t\tconsole.warn('[simulate-bgs-battle] cards load attempt failed', attempt, e);\r\n\t\t}\r\n\t\tif (attempt < CARDS_LOAD_ATTEMPTS) {\r\n\t\t\tawait sleep(250 * attempt);\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n};\r\n\r\n/**\r\n * Load reference cards once per warm container. Retries transient CDN/fetch failures.\r\n * Returns false when cards could not be loaded after retries.\r\n */\r\nexport const ensureCardsLoaded = async (cards: AllCardsService = globalCards): Promise<boolean> => {\r\n\tif (hasLoadedCards(cards)) {\r\n\t\treturn true;\r\n\t}\r\n\tif (!cardsInitPromise) {\r\n\t\tcardsInitPromise = loadCardsWithRetry(cards).then((ok) => {\r\n\t\t\tif (!ok) {\r\n\t\t\t\t// Allow the next invoke to retry after a hard failure.\r\n\t\t\t\tcardsInitPromise = null;\r\n\t\t\t}\r\n\t\t\treturn ok;\r\n\t\t});\r\n\t}\r\n\treturn cardsInitPromise;\r\n};\r\n\r\nexport const assignCards = (cards: AllCardsService) => {\r\n\tglobalCards = cards;\r\n\tcardsInitPromise = hasLoadedCards(cards) ? Promise.resolve(true) : null;\r\n};\r\n\r\nexport interface SimulateSingleCombatOptions {\r\n\t/** If set, creates a Mulberry32 RNG from this seed. Ignored when `rng` is provided. */\r\n\treadonly seed?: number;\r\n\t/** Explicit RNG instance. Takes precedence over `seed`. */\r\n\treadonly rng?: Rng;\r\n\t/** When true, records spectator actions (slower). Default false. */\r\n\treadonly includeSpectator?: boolean;\r\n}\r\n\r\n/**\r\n * Run exactly one combat with a deterministic (or default) RNG.\r\n * Intended for the full-game / self-play env — not for Firestone win% Monte Carlo.\r\n */\r\nexport const simulateSingleCombat = (\r\n\tbattleInput: BgsBattleInfo,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n\toptions: SimulateSingleCombatOptions = {},\r\n): SingleSimulationResult => {\r\n\tconst profile = combatProfileEnabled();\r\n\tconst getCardsT0 = profile ? performance.now() : 0;\r\n\tif (!hasLoadedCards(cards)) {\r\n\t\tconsole.error('[simulate-bgs-battle] reference cards are empty, cannot simulate combat', cards);\r\n\t\treturn null;\r\n\t}\r\n\tif (profile) {\r\n\t\tcombatProfileAdd('getCardsMs', performance.now() - getCardsT0);\r\n\t}\r\n\r\n\tconst rng = options.rng ?? (options.seed !== undefined ? createSeededRng(options.seed) : defaultRng);\r\n\tconst includeSpectator = options.includeSpectator ?? false;\r\n\r\n\tapplyDebugStateFromInput(battleInput);\r\n\ttry {\r\n\t\tconst buildT0 = profile ? performance.now() : 0;\r\n\t\tconst inputReady = buildFinalInput(battleInput, cards, cardsData);\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('buildFinalInputMs', performance.now() - buildT0);\r\n\t\t}\r\n\t\t// One fight: cloneInput3 per use (battle + optional initial-state snapshot).\r\n\t\t// Firestone Monte Carlo keeps buildTemplateCloner for 8000 sims.\r\n\t\tconst cloneTemplate = () => {\r\n\t\t\tconst cloneT0 = profile ? performance.now() : 0;\r\n\t\t\tconst cloned = cloneInput3(inputReady);\r\n\t\t\tif (profile) {\r\n\t\t\t\tcombatProfileAdd('cloneMs', performance.now() - cloneT0);\r\n\t\t\t}\r\n\t\t\treturn cloned;\r\n\t\t};\r\n\t\tconst spectator = includeSpectator ? new Spectator(true) : noopSpectator;\r\n\t\tif (includeSpectator) {\r\n\t\t\tspectator.startRecording();\r\n\t\t}\r\n\t\tconst runT0 = profile ? performance.now() : 0;\r\n\t\tconst result = withRng(rng, () => runSingleBattle(cloneTemplate, cards, cardsData, spectator, rng));\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('runBattleMs', performance.now() - runT0);\r\n\t\t\tcombatProfileTickFight();\r\n\t\t}\r\n\t\treturn result;\r\n\t} finally {\r\n\t\tclearDebugStateFromInput(battleInput);\r\n\t}\r\n};\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tif (!event.body?.length) {\r\n\t\tconsole.warn('missing event body', event);\r\n\t\treturn {\r\n\t\t\tstatusCode: 400,\r\n\t\t\tisBase64Encoded: false,\r\n\t\t\tbody: null,\r\n\t\t};\r\n\t}\r\n\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tconst cards = globalCards;\r\n\tconst cardsLoaded = await ensureCardsLoaded(cards);\r\n\tif (!cardsLoaded) {\r\n\t\tconsole.error('[simulate-bgs-battle] failed to load reference cards after retries');\r\n\t\treturn {\r\n\t\t\tstatusCode: 503,\r\n\t\t\tisBase64Encoded: false,\r\n\t\t\tbody: JSON.stringify({ error: 'cards_unavailable' }),\r\n\t\t};\r\n\t}\r\n\tconst cardsData = new CardsData(cards, false);\r\n\tcardsData.inititialize(\r\n\t\tbattleInput.gameState?.validTribes ?? battleInput.options?.validTribes,\r\n\t\tbattleInput.gameState?.anomalies ?? [],\r\n\t);\r\n\tconst battleIterator = simulateBattle(battleInput, cards, cardsData);\r\n\r\n\t// Iterate through all intermediate results to reach the final result\r\n\tlet result = battleIterator.next();\r\n\twhile (!result.done) {\r\n\t\tresult = battleIterator.next();\r\n\t}\r\n\r\n\tconst simulationResult = result.value;\r\n\t// console.debug('simulationResult', simulationResult);\r\n\r\n\tconst response = {\r\n\t\tstatusCode: 200,\r\n\t\tisBase64Encoded: false,\r\n\t\tbody: JSON.stringify(simulationResult),\r\n\t};\r\n\treturn response;\r\n};\r\n\r\nexport const simulateBattle = function* (\r\n\tbattleInput: BgsBattleInfo,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n): Generator<SimulationResult, SimulationResult, void> {\r\n\tif (!hasLoadedCards(cards)) {\r\n\t\tconsole.error('[simulate-bgs-battle] reference cards are empty, cannot simulate battle', cards);\r\n\t\treturn null;\r\n\t}\r\n\t// !battleInput.options?.skipInfoLogs && console.time('full-sim');\r\n\tconst start = Date.now();\r\n\tconst maxAcceptableDuration = battleInput.options?.maxAcceptableDuration || 8000;\r\n\tconst hideMaxSimulationDurationWarning = battleInput.options?.hideMaxSimulationDurationWarning ?? false;\r\n\tconst numberOfSimulations = battleInput.options?.numberOfSimulations || 8000;\r\n\tconst intermediateSteps = battleInput.options?.intermediateResults ?? 200;\r\n\tconst damageConfidence = battleInput.options?.damageConfidence ?? 0.9;\r\n\tconst includeOutcomeSamples = battleInput.options?.includeOutcomeSamples ?? true;\r\n\tconst simulationResult: SimulationResult = {\r\n\t\twonLethal: 0,\r\n\t\twon: 0,\r\n\t\ttied: 0,\r\n\t\tlost: 0,\r\n\t\tlostLethal: 0,\r\n\t\tdamageWons: [],\r\n\t\tdamageWon: 0,\r\n\t\tdamageWonRange: null,\r\n\t\tdamageLosts: [],\r\n\t\tdamageLost: 0,\r\n\t\tdamageLostRange: null,\r\n\t\twonLethalPercent: undefined,\r\n\t\twonPercent: undefined,\r\n\t\ttiedPercent: undefined,\r\n\t\tlostPercent: undefined,\r\n\t\tlostLethalPercent: undefined,\r\n\t\taverageDamageWon: undefined,\r\n\t\taverageDamageLost: undefined,\r\n\t\tsimulatorVersion: SIMULATOR_VERSION,\r\n\t\tinputHash: hashBattleInfo(battleInput) ?? undefined,\r\n\t};\r\n\r\n\tconst spectator = new Spectator(includeOutcomeSamples);\r\n\tconst inputReady = buildFinalInput(battleInput, cards, cardsData);\r\n\r\n\tapplyDebugStateFromInput(battleInput);\r\n\r\n\t// cloneInput3 normalizes the input (sorted secrets, reset pendingAttackBuffs, etc.).\r\n\t// The compiled cloner re-materializes that normalized shape as an object literal, which is\r\n\t// faster than JSON.parse of a template string (~0.02ms vs ~0.12ms per clone on a 7v7\r\n\t// board), which was itself faster than generic recursive cloners (~0.19-0.23ms) and\r\n\t// structuredClone (~0.46ms)\r\n\tconst cloneTemplate = buildTemplateCloner(cloneInput3(inputReady));\r\n\r\n\tconst runOne = () => runSingleBattle(cloneTemplate, cards, cardsData, spectator, defaultRng);\r\n\r\n\ttry {\r\n\t\t!battleInput.options?.skipInfoLogs && console.time('simulation');\r\n\t\tconst outcomes = {};\r\n\t\tfor (let i = 0; i < numberOfSimulations; i++) {\r\n\t\t\tconst battleResult = runOne();\r\n\t\t\tif (!battleResult) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (battleResult.result === 'won') {\r\n\t\t\t\tsimulationResult.won++;\r\n\t\t\t\tsimulationResult.damageWon += battleResult.damageDealt;\r\n\t\t\t\tsimulationResult.damageWons.push(battleResult.damageDealt);\r\n\t\t\t\tif (battleResult.damageDealt >= battleInput.opponentBoard.player.hpLeft) {\r\n\t\t\t\t\tsimulationResult.wonLethal++;\r\n\t\t\t\t}\r\n\t\t\t} else if (battleResult.result === 'lost') {\r\n\t\t\t\tsimulationResult.lost++;\r\n\t\t\t\tsimulationResult.damageLost += battleResult.damageDealt;\r\n\t\t\t\tsimulationResult.damageLosts.push(battleResult.damageDealt);\r\n\t\t\t\toutcomes[battleResult.damageDealt] = (outcomes[battleResult.damageDealt] ?? 0) + 1;\r\n\t\t\t\tif (\r\n\t\t\t\t\tbattleInput.playerBoard.player.hpLeft &&\r\n\t\t\t\t\tbattleResult.damageDealt >= battleInput.playerBoard.player.hpLeft\r\n\t\t\t\t) {\r\n\t\t\t\t\tsimulationResult.lostLethal++;\r\n\t\t\t\t}\r\n\t\t\t} else if (battleResult.result === 'tied') {\r\n\t\t\t\tsimulationResult.tied++;\r\n\t\t\t}\r\n\r\n\t\t\tif (Date.now() - start > maxAcceptableDuration && !hideMaxSimulationDurationWarning) {\r\n\t\t\t\t// Can happen in case of inifinite boards, or a bug. Don't hog the user's computer in that case\r\n\t\t\t\tconsole.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms');\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// Yield intermediate result every 200 iterations\r\n\t\t\tif (!!intermediateSteps && i > 0 && i % intermediateSteps === 0) {\r\n\t\t\t\tupdateSimulationResult(simulationResult, inputReady, damageConfidence);\r\n\t\t\t\tyield simulationResult;\r\n\t\t\t}\r\n\t\t}\r\n\t\tupdateSimulationResult(simulationResult, inputReady, damageConfidence);\r\n\t\t!battleInput.options?.skipInfoLogs && console.timeEnd('simulation');\r\n\r\n\t\t// Capture outcome samples post-hoc: re-run a few battles with action recording turned\r\n\t\t// on, until each outcome that actually occurred has a sample. This is much cheaper\r\n\t\t// than recording during the main loop, where almost all recorded actions get discarded.\r\n\t\t// This phase intentionally ignores maxAcceptableDuration: when the main loop was cut\r\n\t\t// short by the time budget, we would otherwise end up with no samples at all.\r\n\t\tif (includeOutcomeSamples) {\r\n\t\t\tconst neededOutcomes: ('won' | 'lost' | 'tied')[] = [];\r\n\t\t\tsimulationResult.won > 0 && neededOutcomes.push('won');\r\n\t\t\tsimulationResult.lost > 0 && neededOutcomes.push('lost');\r\n\t\t\tsimulationResult.tied > 0 && neededOutcomes.push('tied');\r\n\t\t\tconst missingOutcomes = new Set(neededOutcomes);\r\n\t\t\tif (missingOutcomes.size > 0) {\r\n\t\t\t\tspectator.startRecording();\r\n\t\t\t\tfor (let i = 0; i < MAX_SAMPLE_RESIMS && missingOutcomes.size > 0; i++) {\r\n\t\t\t\t\tconst battleResult = runOne();\r\n\t\t\t\t\tif (!battleResult?.result) {\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tspectator.commitBattleResult(battleResult.result);\r\n\t\t\t\t\tif (spectator.hasSampleFor(battleResult.result)) {\r\n\t\t\t\t\t\tmissingOutcomes.delete(battleResult.result);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tspectator.stopRecording();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tspectator.prune();\r\n\t\tsimulationResult.outcomeSamples = spectator.buildOutcomeSamples(battleInput.gameState);\r\n\t\t// Avoid sending this verbose data\r\n\r\n\t\tsimulationResult.damageWons = [];\r\n\t\tsimulationResult.damageLosts = [];\r\n\t\t// !battleInput.options?.skipInfoLogs && console.timeEnd('full-sim');\r\n\t\treturn simulationResult;\r\n\t} finally {\r\n\t\tclearDebugStateFromInput(battleInput);\r\n\t}\r\n};\r\n\r\ntype CloneTemplate = () => BgsBattleInfo;\r\n\r\nconst runSingleBattle = (\r\n\tcloneTemplate: CloneTemplate,\r\n\tcards: AllCardsService,\r\n\tcardsData: CardsData,\r\n\tspectator: Spectator,\r\n\trng: Rng,\r\n): SingleSimulationResult => {\r\n\tconst input: BgsBattleInfo = cloneTemplate();\r\n\t// The \"initial\" states are only read by a handful of cards (and mutated by some, like\r\n\t// San'layn Scribe), so we only pay for the second clone when they are actually accessed\r\n\tlet playerInitialState = null;\r\n\tlet opponentInitialState = null;\r\n\tconst buildInitialStates = () => {\r\n\t\tconst inputClone: BgsBattleInfo = cloneTemplate();\r\n\t\tplayerInitialState = {\r\n\t\t\tplayer: inputClone.playerBoard.player,\r\n\t\t\tboard: inputClone.playerBoard.board,\r\n\t\t\tteammate: inputClone.playerTeammateBoard,\r\n\t\t};\r\n\t\topponentInitialState = {\r\n\t\t\tplayer: inputClone.opponentBoard.player,\r\n\t\t\tboard: inputClone.opponentBoard.board,\r\n\t\t\tteammate: inputClone.opponentTeammateBoard,\r\n\t\t};\r\n\t};\r\n\tconst gameState: FullGameState = {\r\n\t\tallCards: cards,\r\n\t\tcardsData: cardsData,\r\n\t\tspectator: spectator,\r\n\t\tsharedState: new SharedState(rng),\r\n\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\tnumberOfPlayersAlive: input.gameState.numberOfPlayersAlive,\r\n\t\tapplyDamageCap: input.options?.applyDamageCap === true,\r\n\t\tapplyIceBlock: input.options?.applyIceBlock === true,\r\n\t\tisDuos: !!input.playerTeammateBoard || !!input.opponentTeammateBoard,\r\n\t\tvalidTribes: input.gameState.validTribes,\r\n\t\tanomalies: input.gameState.anomalies,\r\n\t\tgameState: {\r\n\t\t\tplayer: {\r\n\t\t\t\tplayer: input.playerBoard.player,\r\n\t\t\t\tboard: input.playerBoard.board,\r\n\t\t\t\tteammate: input.playerTeammateBoard,\r\n\t\t\t},\r\n\t\t\topponent: {\r\n\t\t\t\tplayer: input.opponentBoard.player,\r\n\t\t\t\tboard: input.opponentBoard.board,\r\n\t\t\t\tteammate: input.opponentTeammateBoard,\r\n\t\t\t},\r\n\t\t\tget playerInitial() {\r\n\t\t\t\tif (!playerInitialState) {\r\n\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t}\r\n\t\t\t\treturn playerInitialState;\r\n\t\t\t},\r\n\t\t\tget opponentInitial() {\r\n\t\t\t\tif (!opponentInitialState) {\r\n\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t}\r\n\t\t\t\treturn opponentInitialState;\r\n\t\t\t},\r\n\t\t},\r\n\t};\r\n\tconst simulator = new Simulator(gameState);\r\n\treturn simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);\r\n};\r\n\r\nconst applyDebugStateFromInput = (battleInput: BgsBattleInfo) => {\r\n\tif (!battleInput.debugState) {\r\n\t\treturn;\r\n\t}\r\n\tdebugState.active = true;\r\n\tdebugState.forcedCurrentAttacker = battleInput.debugState.forcedCurrentAttacker;\r\n\tdebugState.forcedFaceOffBase =\r\n\t\tbattleInput.debugState.forcedFaceOffBase?.map((f) => ({\r\n\t\t\tattacker: { ...f.attacker },\r\n\t\t\tdefender: { ...f.defender },\r\n\t\t})) ?? [];\r\n\tconst rawPicks = battleInput.debugState.forcedRandomPicks ?? [];\r\n\tdebugState.forcedRandomPicksBase = rawPicks.map((p) => ({\r\n\t\tsource: { ...p.source },\r\n\t\ttarget: { ...p.target },\r\n\t\t...(p.chosenCardId && { chosenCardId: p.chosenCardId }),\r\n\t}));\r\n};\r\n\r\nconst clearDebugStateFromInput = (battleInput: BgsBattleInfo) => {\r\n\tif (battleInput.debugState) {\r\n\t\tdebugState.active = false;\r\n\t}\r\n};\r\n\r\nconst updateSimulationResult = (simulationResult: SimulationResult, input: BgsBattleInfo, damageConfidence: number) => {\r\n\tconst totalMatches = simulationResult.won + simulationResult.tied + simulationResult.lost;\r\n\tsimulationResult.wonPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.won)) / totalMatches) / 10,\r\n\t\tsimulationResult.won,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.wonLethalPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10,\r\n\t\tsimulationResult.wonLethal,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lost)) / totalMatches) / 10,\r\n\t\tsimulationResult.lost,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostLethalPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10,\r\n\t\tsimulationResult.lostLethal,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.tiedPercent = checkRounding(\r\n\t\tMath.max(0, 100 - simulationResult.lostPercent - simulationResult.wonPercent),\r\n\t\tsimulationResult.tied,\r\n\t\ttotalMatches,\r\n\t);\r\n\r\n\t// simulationResult.wonLethalPercent = Math.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10;\r\n\t// simulationResult.lostLethalPercent = Math.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10;\r\n\tconst totalDamageWon = simulationResult.damageWons.reduce((a, b) => a + b, 0);\r\n\tconst totalDamageLost = simulationResult.damageLosts.reduce((a, b) => a + b, 0);\r\n\tconst damageWonRange = calculateDamageRange(simulationResult.damageWons, damageConfidence);\r\n\tconst damageLostRange = calculateDamageRange(simulationResult.damageLosts, damageConfidence);\r\n\tsimulationResult.averageDamageWon = simulationResult.won ? totalDamageWon / simulationResult.won : 0;\r\n\tsimulationResult.averageDamageLost = simulationResult.lost ? totalDamageLost / simulationResult.lost : 0;\r\n\tsimulationResult.damageWonRange = simulationResult.won ? damageWonRange : null;\r\n\tsimulationResult.damageLostRange = simulationResult.lost ? damageLostRange : null;\r\n\r\n\tif (\r\n\t\tsimulationResult.averageDamageWon > 0 &&\r\n\t\tsimulationResult.averageDamageWon < input.playerBoard.player.tavernTier\r\n\t) {\r\n\t\tconsole.warn('average damage won issue');\r\n\t}\r\n\tif (\r\n\t\tsimulationResult.averageDamageLost > 0 &&\r\n\t\tsimulationResult.averageDamageLost < input.opponentBoard.player.tavernTier\r\n\t) {\r\n\t\tconsole.warn('average damage lost issue');\r\n\t}\r\n};\r\n\r\nconst calculateDamageRange = (damageArray: number[], damageConfidence: number): { min: number; max: number } => {\r\n\tif (damageArray.length === 0) {\r\n\t\treturn { min: 0, max: 0 };\r\n\t}\r\n\r\n\t// Sort the array\r\n\tconst sortedDamage = [...damageArray].sort((a, b) => a - b);\r\n\r\n\t// Calculate the 10th and 90th percentiles\r\n\tconst percentile = (arr: number[], p: number) => {\r\n\t\tconst index = Math.floor(p * arr.length);\r\n\t\treturn arr[index];\r\n\t};\r\n\r\n\tconst minDamage = percentile(sortedDamage, 1 - damageConfidence);\r\n\tconst maxDamage = percentile(sortedDamage, damageConfidence);\r\n\r\n\treturn { min: minDamage, max: maxDamage };\r\n};\r\n\r\nconst checkRounding = (roundedValue: number, initialValue: number, totalValue: number): number => {\r\n\tif (roundedValue === 0 && initialValue !== 0) {\r\n\t\treturn 0.01;\r\n\t}\r\n\tif (roundedValue === 100 && initialValue !== totalValue) {\r\n\t\treturn 99.9;\r\n\t}\r\n\treturn roundedValue;\r\n};\r\n\r\n// const cleanEnchantmentsForEntity = (\r\n// \tenchantments: { cardId: string; originEntityId?: number; timing: number }[],\r\n// \tentityIds: readonly number[],\r\n// ): { cardId: string; originEntityId?: number; timing: number }[] => {\r\n// \treturn enchantments.filter(\r\n// \t\t(enchant) =>\r\n// \t\t\tentityIds.indexOf(enchant.originEntityId) !== -1 ||\r\n// \t\t\tvalidEnchantments.indexOf(enchant.cardId as CardIds) !== -1,\r\n// \t);\r\n// };\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulation-result.js","sourceRoot":"","sources":["../src/simulation-result.ts"],"names":[],"mappings":"","sourcesContent":["import { GameSample } from './simulation/spectator/game-sample';\r\n\r\nexport interface SimulationResult {\r\n\twonLethal: number;\r\n\twon: number;\r\n\ttied: number;\r\n\tlost: number;\r\n\tlostLethal: number;\r\n\tdamageWon: number;\r\n\tdamageWons: number[];\r\n\tdamageWonRange: {\r\n\t\tmin: number;\r\n\t\tmax: number;\r\n\t};\r\n\tdamageLost: number;\r\n\tdamageLosts: number[];\r\n\tdamageLostRange: {\r\n\t\tmin: number;\r\n\t\tmax: number;\r\n\t};\r\n\twonLethalPercent: number;\r\n\twonPercent: number;\r\n\ttiedPercent: number;\r\n\tlostPercent: number;\r\n\tlostLethalPercent: number;\r\n\taverageDamageWon: number;\r\n\taverageDamageLost: number;\r\n\toutcomeSamples?: OutcomeSamples;\r\n}\r\n\r\nexport interface OutcomeSamples {\r\n\twon: readonly GameSample[];\r\n\tlost: readonly GameSample[];\r\n\ttied: readonly GameSample[];\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"simulation-result.js","sourceRoot":"","sources":["../src/simulation-result.ts"],"names":[],"mappings":"","sourcesContent":["import { GameSample } from './simulation/spectator/game-sample';\r\n\r\nexport interface SimulationResult {\r\n\twonLethal: number;\r\n\twon: number;\r\n\ttied: number;\r\n\tlost: number;\r\n\tlostLethal: number;\r\n\tdamageWon: number;\r\n\tdamageWons: number[];\r\n\tdamageWonRange: {\r\n\t\tmin: number;\r\n\t\tmax: number;\r\n\t};\r\n\tdamageLost: number;\r\n\tdamageLosts: number[];\r\n\tdamageLostRange: {\r\n\t\tmin: number;\r\n\t\tmax: number;\r\n\t};\r\n\twonLethalPercent: number;\r\n\twonPercent: number;\r\n\ttiedPercent: number;\r\n\tlostPercent: number;\r\n\tlostLethalPercent: number;\r\n\taverageDamageWon: number;\r\n\taverageDamageLost: number;\r\n\toutcomeSamples?: OutcomeSamples;\r\n\t/** Package version of the simulator build that produced this result. */\r\n\tsimulatorVersion?: string;\r\n\t/** Hash of the boards and game state passed into this simulation. */\r\n\tinputHash?: string;\r\n}\r\n\r\nexport interface OutcomeSamples {\r\n\twon: readonly GameSample[];\r\n\tlost: readonly GameSample[];\r\n\ttied: readonly GameSample[];\r\n}\r\n"]}
|