@firestone-hs/simulate-bgs-battle 1.1.722 → 1.1.723

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.
Files changed (55) hide show
  1. package/dist/cards/card.interface.d.ts +4 -0
  2. package/dist/cards/card.interface.js +4 -2
  3. package/dist/cards/card.interface.js.map +1 -1
  4. package/dist/cards/impl/_card-mappings.js +2 -0
  5. package/dist/cards/impl/_card-mappings.js.map +1 -1
  6. package/dist/cards/impl/hero-power/lightning-invocation.js +4 -1
  7. package/dist/cards/impl/hero-power/lightning-invocation.js.map +1 -1
  8. package/dist/cards/impl/minion/timewarped-embalmer.d.ts +2 -2
  9. package/dist/cards/impl/minion/timewarped-embalmer.js +5 -1
  10. package/dist/cards/impl/minion/timewarped-embalmer.js.map +1 -1
  11. package/dist/cards/impl/minion/timewarped-pagle.d.ts +2 -0
  12. package/dist/cards/impl/minion/timewarped-pagle.js +15 -0
  13. package/dist/cards/impl/minion/timewarped-pagle.js.map +1 -0
  14. package/dist/input-clone.d.ts +1 -0
  15. package/dist/input-clone.js +13 -1
  16. package/dist/input-clone.js.map +1 -1
  17. package/dist/mechanics/rally.js +14 -12
  18. package/dist/mechanics/rally.js.map +1 -1
  19. package/dist/mechanics/tavern-spell-repeat.js +3 -4
  20. package/dist/mechanics/tavern-spell-repeat.js.map +1 -1
  21. package/dist/simulate-bgs-battle.d.ts +1 -1
  22. package/dist/simulate-bgs-battle.js +80 -77
  23. package/dist/simulate-bgs-battle.js.map +1 -1
  24. package/dist/simulation/add-minion-to-board.js +3 -3
  25. package/dist/simulation/add-minion-to-board.js.map +1 -1
  26. package/dist/simulation/attack.js +3 -3
  27. package/dist/simulation/attack.js.map +1 -1
  28. package/dist/simulation/avenge.js +2 -2
  29. package/dist/simulation/avenge.js.map +1 -1
  30. package/dist/simulation/deathrattle-effects.js +48 -14
  31. package/dist/simulation/deathrattle-effects.js.map +1 -1
  32. package/dist/simulation/deathrattle-spawns.js +19 -12
  33. package/dist/simulation/deathrattle-spawns.js.map +1 -1
  34. package/dist/simulation/deathrattle-utils.d.ts +1 -0
  35. package/dist/simulation/deathrattle-utils.js +45 -5
  36. package/dist/simulation/deathrattle-utils.js.map +1 -1
  37. package/dist/simulation/on-attack.js +13 -14
  38. package/dist/simulation/on-attack.js.map +1 -1
  39. package/dist/simulation/reborn.js +11 -0
  40. package/dist/simulation/reborn.js.map +1 -1
  41. package/dist/simulation/simulator.d.ts +1 -0
  42. package/dist/simulation/simulator.js +39 -74
  43. package/dist/simulation/simulator.js.map +1 -1
  44. package/dist/simulation/spectator/resolve-trinket-hero-power.js +11 -2
  45. package/dist/simulation/spectator/resolve-trinket-hero-power.js.map +1 -1
  46. package/dist/simulation/spectator/spectator.d.ts +4 -0
  47. package/dist/simulation/spectator/spectator.js +39 -19
  48. package/dist/simulation/spectator/spectator.js.map +1 -1
  49. package/dist/simulation/stats.js +95 -66
  50. package/dist/simulation/stats.js.map +1 -1
  51. package/dist/simulation/summon-when-space.js +24 -21
  52. package/dist/simulation/summon-when-space.js.map +1 -1
  53. package/dist/utils.js +89 -48
  54. package/dist/utils.js.map +1 -1
  55. package/package.json +3 -2
@@ -9,7 +9,10 @@ exports.LightningInvocation = {
9
9
  for (const heroPower of input.playerEntity.heroPowers) {
10
10
  if (exports.LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {
11
11
  (0, deathrattle_effects_1.applyLightningInvocationEnchantment)(input.playerBoard, input.playerEntity, input.playerEntity, input.opponentBoard, input.opponentEntity, input.gameState);
12
- return true;
12
+ return {
13
+ hasTriggered: true,
14
+ shouldRecomputeCurrentAttacker: true,
15
+ };
13
16
  }
14
17
  }
15
18
  },
@@ -1 +1 @@
1
- {"version":3,"file":"lightning-invocation.js","sourceRoot":"","sources":["../../../../src/cards/impl/hero-power/lightning-invocation.ts"],"names":[],"mappings":";;;AAEA,iFAA8F;AAIjF,QAAA,mBAAmB,GAAsB;IACrD,mBAAmB,EAAE,iBAAiB;IACtC,OAAO,EAAE,4CAA+D;IACxE,aAAa,EAAE,CAAC,OAAqB,EAAE,KAAe,EAAE,EAAE;QACzD,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE;YACtD,IAAI,2BAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;gBAC7E,IAAA,yDAAmC,EAClC,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,CACf,CAAC;gBACF,OAAO,IAAI,CAAC;aACZ;SACD;IACF,CAAC;CACD,CAAC","sourcesContent":["import { CardIds } from '../../../services/card-ids';\nimport { BoardTrinket } from '../../../bgs-player-entity';\r\nimport { applyLightningInvocationEnchantment } from '../../../simulation/deathrattle-effects';\r\nimport { SoCInput } from '../../../simulation/start-of-combat/start-of-combat-input';\r\nimport { StartOfCombatCard } from '../../card.interface';\r\n\r\nexport const LightningInvocation: StartOfCombatCard = {\r\n\tstartOfCombatTiming: 'start-of-combat',\r\n\tcardIds: [CardIds.LightningInvocationToken, CardIds.LightningInvocation],\r\n\tstartOfCombat: (trinket: BoardTrinket, input: SoCInput) => {\r\n\t\tfor (const heroPower of input.playerEntity.heroPowers) {\r\n\t\t\tif (LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {\r\n\t\t\t\tapplyLightningInvocationEnchantment(\r\n\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n};\r\n"]}
1
+ {"version":3,"file":"lightning-invocation.js","sourceRoot":"","sources":["../../../../src/cards/impl/hero-power/lightning-invocation.ts"],"names":[],"mappings":";;;AAIA,iFAA8F;AAIjF,QAAA,mBAAmB,GAAsB;IACrD,mBAAmB,EAAE,iBAAiB;IACtC,OAAO,EAAE,4CAA+D;IACxE,aAAa,EAAE,CAAC,OAAmE,EAAE,KAAe,EAAE,EAAE;QACvG,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE;YACtD,IAAI,2BAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;gBAC7E,IAAA,yDAAmC,EAClC,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,CACf,CAAC;gBAGF,OAAO;oBACN,YAAY,EAAE,IAAI;oBAClB,8BAA8B,EAAE,IAAI;iBACpC,CAAC;aACF;SACD;IACF,CAAC;CACD,CAAC","sourcesContent":["import { BgsPlayerEntity, BoardTrinket } from '../../../bgs-player-entity';\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { BoardSecret } from '../../../board-secret';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { applyLightningInvocationEnchantment } from '../../../simulation/deathrattle-effects';\r\nimport { SoCInput } from '../../../simulation/start-of-combat/start-of-combat-input';\r\nimport { StartOfCombatCard } from '../../card.interface';\r\n\r\nexport const LightningInvocation: StartOfCombatCard = {\r\n\tstartOfCombatTiming: 'start-of-combat',\r\n\tcardIds: [CardIds.LightningInvocationToken, CardIds.LightningInvocation],\r\n\tstartOfCombat: (trinket: BoardTrinket | BoardEntity | BgsPlayerEntity | BoardSecret, input: SoCInput) => {\r\n\t\tfor (const heroPower of input.playerEntity.heroPowers) {\r\n\t\t\tif (LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {\r\n\t\t\t\tapplyLightningInvocationEnchantment(\r\n\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t);\r\n\t\t\t\t// See https://replays.firestoneapp.com/?reviewId=09b7ace7-cbe1-4533-bf58-c26d04fa15e9&turn=11&action=8\r\n\t\t\t\t// 2 vs 4 minions, and the 2 side attacks after it becoems 2 vs 2\r\n\t\t\t\treturn {\r\n\t\t\t\t\thasTriggered: true,\r\n\t\t\t\t\tshouldRecomputeCurrentAttacker: true,\r\n\t\t\t\t};\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n};\r\n"]}
@@ -1,2 +1,2 @@
1
- import { DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';
2
- export declare const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard;
1
+ import { BeforeRebornSelfEffectCard, DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';
2
+ export declare const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard & BeforeRebornSelfEffectCard;
@@ -6,11 +6,15 @@ exports.TimewarpedEmbalmer = {
6
6
  cardIds: ["BG34_Giant_332", "BG34_Giant_332_G"],
7
7
  defaultCharges: (entity) => entity.scriptDataNum1,
8
8
  onOtherSpawned: (minion, input) => {
9
- if (minion.abiityChargesLeft > 0) {
9
+ if (minion.abiityChargesLeft > 0 && !input.spawned.reborn) {
10
10
  minion.abiityChargesLeft--;
11
11
  const target = input.spawned;
12
12
  (0, reborn_1.updateReborn)(target, true, input.board, input.hero, input.otherHero, input.gameState);
13
13
  }
14
14
  },
15
+ beforeRebornSelfEffect: (minion, input) => {
16
+ minion.scriptDataNum1 = 2;
17
+ minion.abiityChargesLeft = 2;
18
+ },
15
19
  };
16
20
  //# sourceMappingURL=timewarped-embalmer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"timewarped-embalmer.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-embalmer.ts"],"names":[],"mappings":";;;AACA,qDAAwD;AAK3C,QAAA,kBAAkB,GAA4C;IAC1E,OAAO,EAAE,sCAAwF;IAEjG,cAAc,EAAE,CAAC,MAAmB,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc;IAC9D,cAAc,EAAE,CAAC,MAAmB,EAAE,KAAwB,EAAE,EAAE;QACjE,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE;YACjC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC7B,IAAA,qBAAY,EAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SACtF;IACF,CAAC;CACD,CAAC","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { updateReborn } from '../../../keywords/reborn';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnOtherSpawnInput } from '../../../simulation/add-minion-to-board';\r\nimport { DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';\r\n\r\nexport const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard = {\r\n\tcardIds: [CardIds.TimewarpedEmbalmer_BG34_Giant_332, CardIds.TimewarpedEmbalmer_BG34_Giant_332_G],\r\n\t// It only resets at the start of the shop phase, so we need to rely on the value from the board state\r\n\tdefaultCharges: (entity: BoardEntity) => entity.scriptDataNum1,\r\n\tonOtherSpawned: (minion: BoardEntity, input: OnOtherSpawnInput) => {\r\n\t\tif (minion.abiityChargesLeft > 0) {\r\n\t\t\tminion.abiityChargesLeft--;\r\n\t\t\tconst target = input.spawned;\r\n\t\t\tupdateReborn(target, true, input.board, input.hero, input.otherHero, input.gameState);\r\n\t\t}\r\n\t},\r\n};\r\n"]}
1
+ {"version":3,"file":"timewarped-embalmer.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-embalmer.ts"],"names":[],"mappings":";;;AAEA,qDAAwD;AAM3C,QAAA,kBAAkB,GAAyE;IACvG,OAAO,EAAE,sCAAwF;IAEjG,cAAc,EAAE,CAAC,MAAmB,EAAE,EAAE,CAEvC,MAAM,CAAC,cAAc;IACtB,cAAc,EAAE,CAAC,MAAkC,EAAE,KAAwB,EAAE,EAAE;QAChF,IAAK,MAAsB,CAAC,iBAAiB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;YAC1E,MAAsB,CAAC,iBAAiB,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC7B,IAAA,qBAAY,EAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SACtF;IACF,CAAC;IACD,sBAAsB,EAAE,CAAC,MAAkC,EAAE,KAAwB,EAAE,EAAE;QACvF,MAAsB,CAAC,cAAc,GAAG,CAAC,CAAC;QAC1C,MAAsB,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC/C,CAAC;CACD,CAAC","sourcesContent":["import { BoardTrinket } from '../../../bgs-player-entity';\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { updateReborn } from '../../../keywords/reborn';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnOtherSpawnInput } from '../../../simulation/add-minion-to-board';\r\nimport { RebornEffectInput } from '../../../simulation/reborn';\r\nimport { BeforeRebornSelfEffectCard, DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';\r\n\r\nexport const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard & BeforeRebornSelfEffectCard = {\r\n\tcardIds: [CardIds.TimewarpedEmbalmer_BG34_Giant_332, CardIds.TimewarpedEmbalmer_BG34_Giant_332_G],\r\n\t// It only resets at the start of the shop phase, so we need to rely on the value from the board state\r\n\tdefaultCharges: (entity: BoardEntity) =>\r\n\t\t// Some charges can be used during the shop phase\r\n\t\tentity.scriptDataNum1,\r\n\tonOtherSpawned: (minion: BoardEntity | BoardTrinket, input: OnOtherSpawnInput) => {\r\n\t\tif ((minion as BoardEntity).abiityChargesLeft > 0 && !input.spawned.reborn) {\r\n\t\t\t(minion as BoardEntity).abiityChargesLeft--;\r\n\t\t\tconst target = input.spawned;\r\n\t\t\tupdateReborn(target, true, input.board, input.hero, input.otherHero, input.gameState);\r\n\t\t}\r\n\t},\r\n\tbeforeRebornSelfEffect: (minion: BoardEntity | BoardTrinket, input: RebornEffectInput) => {\r\n\t\t(minion as BoardEntity).scriptDataNum1 = 2;\r\n\t\t(minion as BoardEntity).abiityChargesLeft = 2;\r\n\t},\r\n};\r\n"]}
@@ -0,0 +1,2 @@
1
+ import { OnMinionKilledCard } from '../../card.interface';
2
+ export declare const TimewarpedPagle: OnMinionKilledCard;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimewarpedPagle = void 0;
4
+ const cards_in_hand_1 = require("../../../simulation/cards-in-hand");
5
+ exports.TimewarpedPagle = {
6
+ cardIds: ["BG34_Giant_208", "BG34_Giant_208_G"],
7
+ onMinionKilled: (_entity, input) => {
8
+ if (input.killer !== _entity || !input.killerIsAttacking) {
9
+ return { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };
10
+ }
11
+ (0, cards_in_hand_1.addCardsInHand)(input.attackingHero, input.attackingBoard, ["TB_BaconShop_Triples_01"], input.gameState);
12
+ return { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };
13
+ },
14
+ };
15
+ //# sourceMappingURL=timewarped-pagle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timewarped-pagle.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-pagle.ts"],"names":[],"mappings":";;;AAGA,qEAAmE;AAGtD,QAAA,eAAe,GAAuB;IAClD,OAAO,EAAE,sCAAkF;IAC3F,cAAc,EAAE,CAAC,OAAoB,EAAE,KAA0B,EAAE,EAAE;QACpE,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;YACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;SACtD;QACD,IAAA,8BAAc,EAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,cAAc,EAAE,2BAAsB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACnG,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;CACD,CAAC","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnMinionKilledInput } from '../../../simulation/attack';\r\nimport { addCardsInHand } from '../../../simulation/cards-in-hand';\r\nimport { OnMinionKilledCard } from '../../card.interface';\r\n\r\nexport const TimewarpedPagle: OnMinionKilledCard = {\r\n\tcardIds: [CardIds.TimewarpedPagle_BG34_Giant_208, CardIds.TimewarpedPagle_BG34_Giant_208_G],\r\n\tonMinionKilled: (_entity: BoardEntity, input: OnMinionKilledInput) => {\r\n\t\tif (input.killer !== _entity || !input.killerIsAttacking) {\r\n\t\t\treturn { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };\r\n\t\t}\r\n\t\taddCardsInHand(input.attackingHero, input.attackingBoard, [CardIds.TripleReward], input.gameState);\r\n\t\treturn { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };\r\n\t},\r\n};\r\n"]}
@@ -1,4 +1,5 @@
1
1
  import { BgsBattleInfo } from './bgs-battle-info';
2
2
  import { BgsBoardInfo } from './bgs-board-info';
3
+ export declare const buildTemplateCloner: (template: BgsBattleInfo) => (() => BgsBattleInfo);
3
4
  export declare const cloneInput3: (input: BgsBattleInfo) => BgsBattleInfo;
4
5
  export declare const cloneBoard: (board: BgsBoardInfo) => BgsBoardInfo;
@@ -1,6 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cloneBoard = exports.cloneInput3 = void 0;
3
+ exports.cloneBoard = exports.cloneInput3 = exports.buildTemplateCloner = void 0;
4
+ const buildTemplateCloner = (template) => {
5
+ const str = JSON.stringify(template);
6
+ if (!str.includes('"__proto__"')) {
7
+ try {
8
+ return new Function(`return ${str};`);
9
+ }
10
+ catch (e) {
11
+ }
12
+ }
13
+ return () => JSON.parse(str);
14
+ };
15
+ exports.buildTemplateCloner = buildTemplateCloner;
4
16
  const cloneInput3 = (input) => {
5
17
  const result = {
6
18
  gameState: {
@@ -1 +1 @@
1
- {"version":3,"file":"input-clone.js","sourceRoot":"","sources":["../src/input-clone.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\nimport { BoardEntity } from './board-entity';\r\n\r\n// const cloneInput = (input: BgsBattleInfo): BgsBattleInfo => {\r\n// \treturn structuredClone(input);\r\n// };\r\n// const cloneInput2 = (input: string): BgsBattleInfo => {\r\n// \treturn JSON.parse(input);\r\n// };\r\n\r\nexport const cloneInput3 = (input: BgsBattleInfo): BgsBattleInfo => {\r\n\tconst result: BgsBattleInfo = {\r\n\t\tgameState: {\r\n\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t},\r\n\t\theroHasDied: input.heroHasDied,\r\n\t\tplayerBoard: cloneBoard(input.playerBoard),\r\n\t\topponentBoard: cloneBoard(input.opponentBoard),\r\n\t\tplayerTeammateBoard: cloneBoard(input.playerTeammateBoard),\r\n\t\topponentTeammateBoard: cloneBoard(input.opponentTeammateBoard),\r\n\t\toptions: null,\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nexport const cloneBoard = (board: BgsBoardInfo): BgsBoardInfo => {\r\n\tif (!board) {\r\n\t\treturn null;\r\n\t}\r\n\tconst result: BgsBoardInfo = {\r\n\t\tplayer: {\r\n\t\t\t...board.player,\r\n\t\t\tquestEntities: board.player.questEntities?.map((quest) => ({ ...quest })),\r\n\t\t\tquestRewardEntities: board.player.questRewardEntities?.map((reward) => ({ ...reward })),\r\n\t\t\tquestRewards: board.player.questRewards?.map((reward) => reward),\r\n\t\t\thand: board.player.hand?.map((entity) => cloneEntity(entity)),\r\n\t\t\tsecrets: board.player.secrets?.map((secret) => ({ ...secret })).sort((a, b) => a.entityId - b.entityId),\r\n\t\t\ttrinkets: board.player.trinkets?.map((trinket) => ({ ...trinket, tags: { ...trinket.tags } })),\r\n\t\t\tglobalInfo: { ...board.player.globalInfo },\r\n\t\t\theroPowers: board.player.heroPowers?.map((heroPower) => ({ ...heroPower })),\r\n\t\t},\r\n\t\tboard: board.board.map((entity) => cloneEntity(entity)),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst cloneEntity = (entity: BoardEntity): BoardEntity => {\r\n\tconst result: BoardEntity = {\r\n\t\t...entity,\r\n\t\tenchantments: entity.enchantments.map((enchant) => ({ ...enchant })),\r\n\t\tpendingAttackBuffs: [],\r\n\t\tadditionalCards: entity.additionalCards?.map((card) => card),\r\n\t\trememberedDeathrattles: [],\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
1
+ {"version":3,"file":"input-clone.js","sourceRoot":"","sources":["../src/input-clone.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\nimport { BoardEntity } from './board-entity';\r\n\r\n// const cloneInput = (input: BgsBattleInfo): BgsBattleInfo => {\r\n// \treturn structuredClone(input);\r\n// };\r\n// const cloneInput2 = (input: string): BgsBattleInfo => {\r\n// \treturn JSON.parse(input);\r\n// };\r\n\r\n/**\r\n * Builds a function that returns a fresh deep copy of the template on every call.\r\n *\r\n * The template is serialized once, and compiled into a function body that rebuilds it as a\r\n * plain object literal. V8 parses that literal a single time, so each subsequent call only\r\n * allocates the objects, which is much faster than re-parsing JSON on every simulation\r\n * iteration (measured ~0.02ms/clone vs ~0.12ms for JSON.parse on a 7v7 board).\r\n */\r\nexport const buildTemplateCloner = (template: BgsBattleInfo): (() => BgsBattleInfo) => {\r\n\tconst str = JSON.stringify(template);\r\n\t// In an object literal (unlike JSON.parse), a \"__proto__\" key would set the prototype\r\n\t// instead of creating an own property. Our input shape never contains that key, but if\r\n\t// it ever does, fall back to the safe path\r\n\tif (!str.includes('\"__proto__\"')) {\r\n\t\ttry {\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-implied-eval\r\n\t\t\treturn new Function(`return ${str};`) as () => BgsBattleInfo;\r\n\t\t} catch (e) {\r\n\t\t\t// Code generation from strings can be disallowed (e.g. by a CSP when this runs\r\n\t\t\t// inside the app); JSON.parse is the fallback\r\n\t\t}\r\n\t}\r\n\treturn () => JSON.parse(str);\r\n};\r\n\r\nexport const cloneInput3 = (input: BgsBattleInfo): BgsBattleInfo => {\r\n\tconst result: BgsBattleInfo = {\r\n\t\tgameState: {\r\n\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t},\r\n\t\theroHasDied: input.heroHasDied,\r\n\t\tplayerBoard: cloneBoard(input.playerBoard),\r\n\t\topponentBoard: cloneBoard(input.opponentBoard),\r\n\t\tplayerTeammateBoard: cloneBoard(input.playerTeammateBoard),\r\n\t\topponentTeammateBoard: cloneBoard(input.opponentTeammateBoard),\r\n\t\toptions: null,\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nexport const cloneBoard = (board: BgsBoardInfo): BgsBoardInfo => {\r\n\tif (!board) {\r\n\t\treturn null;\r\n\t}\r\n\tconst result: BgsBoardInfo = {\r\n\t\tplayer: {\r\n\t\t\t...board.player,\r\n\t\t\tquestEntities: board.player.questEntities?.map((quest) => ({ ...quest })),\r\n\t\t\tquestRewardEntities: board.player.questRewardEntities?.map((reward) => ({ ...reward })),\r\n\t\t\tquestRewards: board.player.questRewards?.map((reward) => reward),\r\n\t\t\thand: board.player.hand?.map((entity) => cloneEntity(entity)),\r\n\t\t\tsecrets: board.player.secrets?.map((secret) => ({ ...secret })).sort((a, b) => a.entityId - b.entityId),\r\n\t\t\ttrinkets: board.player.trinkets?.map((trinket) => ({ ...trinket, tags: { ...trinket.tags } })),\r\n\t\t\tglobalInfo: { ...board.player.globalInfo },\r\n\t\t\theroPowers: board.player.heroPowers?.map((heroPower) => ({ ...heroPower })),\r\n\t\t},\r\n\t\tboard: board.board.map((entity) => cloneEntity(entity)),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst cloneEntity = (entity: BoardEntity): BoardEntity => {\r\n\tconst result: BoardEntity = {\r\n\t\t...entity,\r\n\t\tenchantments: entity.enchantments.map((enchant) => ({ ...enchant })),\r\n\t\tpendingAttackBuffs: [],\r\n\t\tadditionalCards: entity.additionalCards?.map((card) => card),\r\n\t\trememberedDeathrattles: [],\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
@@ -13,18 +13,20 @@ const triggerRally = (attackingBoard, attackingBoardHero, attacker, defendingBoa
13
13
  let damageDoneByAttacker = 0;
14
14
  let damageDoneByDefender = 0;
15
15
  const isAttackerRallying = (0, utils_1.hasEntityMechanic)(attacker, reference_data_1.GameTag.BACON_RALLY, gameState.allCards);
16
- const cardIds = [
17
- ...attackingBoard.map((e) => e.cardId),
18
- ...((_b = (_a = attackingBoardHero.secrets) === null || _a === void 0 ? void 0 : _a.map((e) => e.cardId)) !== null && _b !== void 0 ? _b : []),
19
- ];
20
- const triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))
21
- ? 3
22
- : cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))
23
- ? 2
24
- : 1;
25
- const numberOfRallyingCries = (_d = (_c = attackingBoardHero.questRewardEntities) === null || _c === void 0 ? void 0 : _c.filter((r) => r.cardId === "BG33_Reward_021").length) !== null && _d !== void 0 ? _d : 0;
26
- const multiplier = triggerMult + numberOfRallyingCries;
27
- const rallyLoops = !isAttackerRallying ? 1 : multiplier;
16
+ let rallyLoops = 1;
17
+ if (isAttackerRallying) {
18
+ const cardIds = [
19
+ ...attackingBoard.map((e) => e.cardId),
20
+ ...((_b = (_a = attackingBoardHero.secrets) === null || _a === void 0 ? void 0 : _a.map((e) => e.cardId)) !== null && _b !== void 0 ? _b : []),
21
+ ];
22
+ const triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))
23
+ ? 3
24
+ : cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))
25
+ ? 2
26
+ : 1;
27
+ const numberOfRallyingCries = (_d = (_c = attackingBoardHero.questRewardEntities) === null || _c === void 0 ? void 0 : _c.filter((r) => r.cardId === "BG33_Reward_021").length) !== null && _d !== void 0 ? _d : 0;
28
+ rallyLoops = triggerMult + numberOfRallyingCries;
29
+ }
28
30
  for (let i = 0; i < rallyLoops; i++) {
29
31
  const onAttackImpl = _card_mappings_1.cardMappings[attacker.cardId];
30
32
  if ((0, card_interface_1.hasRally)(onAttackImpl)) {
@@ -1 +1 @@
1
- {"version":3,"file":"rally.js","sourceRoot":"","sources":["../../src/mechanics/rally.ts"],"names":[],"mappings":"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","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { hasRally } from '../cards/card.interface';\r\nimport { cardMappings } from '../cards/impl/_card-mappings';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { fixEnchantments } from '../simulation/enchantments';\r\nimport { FullGameState } from '../simulation/internal-game-state';\r\nimport { hasEntityMechanic } from '../utils';\r\n\r\nconst DOUBLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376];\r\nconst TRIPLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376_G];\r\n\r\nexport const triggerRally = (\r\n\tattackingBoard: BoardEntity[],\r\n\tattackingBoardHero: BgsPlayerEntity,\r\n\tattacker: BoardEntity,\r\n\tdefendingBoard: BoardEntity[],\r\n\tdefendingBoardHero: BgsPlayerEntity,\r\n\tdefendingEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n): { dmgDoneByAttacker: number; dmgDoneByDefender: number } => {\r\n\tlet damageDoneByAttacker = 0;\r\n\tlet damageDoneByDefender = 0;\r\n\tconst isAttackerRallying = hasEntityMechanic(attacker, GameTag.BACON_RALLY, gameState.allCards);\r\n\r\n\tconst cardIds = [\r\n\t\t...attackingBoard.map((e) => e.cardId),\r\n\t\t...(attackingBoardHero.secrets?.map((e) => e.cardId) ?? []),\r\n\t];\r\n\tconst triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t? 3\r\n\t\t: cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t? 2\r\n\t\t: 1;\r\n\tconst numberOfRallyingCries =\r\n\t\tattackingBoardHero.questRewardEntities?.filter((r) => r.cardId === CardIds.RallyingCry_BG33_Reward_021)\r\n\t\t\t.length ?? 0;\r\n\tconst multiplier = triggerMult + numberOfRallyingCries;\r\n\tconst rallyLoops = !isAttackerRallying ? 1 : multiplier;\r\n\r\n\tfor (let i = 0; i < rallyLoops; i++) {\r\n\t\tconst onAttackImpl = cardMappings[attacker.cardId];\r\n\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(attacker, {\r\n\t\t\t\tattacker: attacker,\r\n\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\tgameState,\r\n\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t});\r\n\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t}\r\n\t\tconst enchantments = attacker.enchantments;\r\n\t\tfor (const enchantment of enchantments) {\r\n\t\t\tconst onAttackImpl = cardMappings[enchantment.cardId];\r\n\t\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\t\tlet enchantmentToMinion: BoardEntity = {\r\n\t\t\t\t\t...enchantment,\r\n\t\t\t\t\tentityId: attacker.entityId,\r\n\t\t\t\t\tattack: attacker.attack,\r\n\t\t\t\t\thealth: attacker.health,\r\n\t\t\t\t\tmaxHealth: attacker.maxHealth,\r\n\t\t\t\t\tmaxAttack: attacker.maxAttack,\r\n\t\t\t\t\tabiityChargesLeft: attacker.abiityChargesLeft,\r\n\t\t\t\t};\r\n\t\t\t\tenchantmentToMinion = fixEnchantments(enchantmentToMinion, gameState.allCards);\r\n\t\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(enchantmentToMinion, {\r\n\t\t\t\t\tattacker: attacker,\r\n\t\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\t\tgameState,\r\n\t\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t\t});\r\n\t\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn { dmgDoneByAttacker: damageDoneByAttacker, dmgDoneByDefender: damageDoneByDefender };\r\n};\r\n"]}
1
+ {"version":3,"file":"rally.js","sourceRoot":"","sources":["../../src/mechanics/rally.ts"],"names":[],"mappings":"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","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { hasRally } from '../cards/card.interface';\r\nimport { cardMappings } from '../cards/impl/_card-mappings';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { fixEnchantments } from '../simulation/enchantments';\r\nimport { FullGameState } from '../simulation/internal-game-state';\r\nimport { hasEntityMechanic } from '../utils';\r\n\r\nconst DOUBLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376];\r\nconst TRIPLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376_G];\r\n\r\nexport const triggerRally = (\r\n\tattackingBoard: BoardEntity[],\r\n\tattackingBoardHero: BgsPlayerEntity,\r\n\tattacker: BoardEntity,\r\n\tdefendingBoard: BoardEntity[],\r\n\tdefendingBoardHero: BgsPlayerEntity,\r\n\tdefendingEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n): { dmgDoneByAttacker: number; dmgDoneByDefender: number } => {\r\n\tlet damageDoneByAttacker = 0;\r\n\tlet damageDoneByDefender = 0;\r\n\tconst isAttackerRallying = hasEntityMechanic(attacker, GameTag.BACON_RALLY, gameState.allCards);\r\n\r\n\t// Only compute the multiplier when the attacker actually rallies, as this is a hot path\r\n\tlet rallyLoops = 1;\r\n\tif (isAttackerRallying) {\r\n\t\tconst cardIds = [\r\n\t\t\t...attackingBoard.map((e) => e.cardId),\r\n\t\t\t...(attackingBoardHero.secrets?.map((e) => e.cardId) ?? []),\r\n\t\t];\r\n\t\tconst triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t\t? 3\r\n\t\t\t: cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t\t? 2\r\n\t\t\t: 1;\r\n\t\tconst numberOfRallyingCries =\r\n\t\t\tattackingBoardHero.questRewardEntities?.filter((r) => r.cardId === CardIds.RallyingCry_BG33_Reward_021)\r\n\t\t\t\t.length ?? 0;\r\n\t\trallyLoops = triggerMult + numberOfRallyingCries;\r\n\t}\r\n\r\n\tfor (let i = 0; i < rallyLoops; i++) {\r\n\t\tconst onAttackImpl = cardMappings[attacker.cardId];\r\n\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(attacker, {\r\n\t\t\t\tattacker: attacker,\r\n\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\tgameState,\r\n\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t});\r\n\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t}\r\n\t\tconst enchantments = attacker.enchantments;\r\n\t\tfor (const enchantment of enchantments) {\r\n\t\t\tconst onAttackImpl = cardMappings[enchantment.cardId];\r\n\t\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\t\tlet enchantmentToMinion: BoardEntity = {\r\n\t\t\t\t\t...enchantment,\r\n\t\t\t\t\tentityId: attacker.entityId,\r\n\t\t\t\t\tattack: attacker.attack,\r\n\t\t\t\t\thealth: attacker.health,\r\n\t\t\t\t\tmaxHealth: attacker.maxHealth,\r\n\t\t\t\t\tmaxAttack: attacker.maxAttack,\r\n\t\t\t\t\tabiityChargesLeft: attacker.abiityChargesLeft,\r\n\t\t\t\t};\r\n\t\t\t\tenchantmentToMinion = fixEnchantments(enchantmentToMinion, gameState.allCards);\r\n\t\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(enchantmentToMinion, {\r\n\t\t\t\t\tattacker: attacker,\r\n\t\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\t\tgameState,\r\n\t\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t\t});\r\n\t\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn { dmgDoneByAttacker: damageDoneByAttacker, dmgDoneByDefender: damageDoneByDefender };\r\n};\r\n"]}
@@ -26,15 +26,14 @@ const getTavernSpellEffectRepeatCount = (input) => {
26
26
  repeats += proudPrivateerExtra;
27
27
  }
28
28
  const cathedrals = input.hero.trinkets.filter((t) => {
29
- var _a, _b;
29
+ var _a;
30
30
  return t.cardId === "BG30_MagicItem_434" &&
31
- t.scriptDataNum1 > 0 &&
32
- ((_b = (_a = t.tags) === null || _a === void 0 ? void 0 : _a[reference_data_1.GameTag.TRIGGER_VISUAL]) !== null && _b !== void 0 ? _b : 0) > 0;
31
+ !((_a = t.tags) === null || _a === void 0 ? void 0 : _a[reference_data_1.GameTag.TAG_SCRIPT_DATA_NUM_6]);
33
32
  });
34
33
  repeats += cathedrals.length;
35
34
  for (const cathedral of cathedrals) {
36
35
  cathedral.tags = (_b = cathedral.tags) !== null && _b !== void 0 ? _b : {};
37
- cathedral.tags[reference_data_1.GameTag.TRIGGER_VISUAL] = 0;
36
+ cathedral.tags[reference_data_1.GameTag.TAG_SCRIPT_DATA_NUM_6] = 1;
38
37
  }
39
38
  return repeats;
40
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"tavern-spell-repeat.js","sourceRoot":"","sources":["../../src/mechanics/tavern-spell-repeat.ts"],"names":[],"mappings":";;;AAAA,iEAAuD;AAQhD,MAAM,+BAA+B,GAAG,CAAC,KAAqB,EAAU,EAAE;;IAChF,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QAChC,OAAO,CAAC,CAAC;KACT;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAA0C,CAAC,CAAC;IACrG,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAwC,CAAC,CAAC;IACnG,IACC,CAAC,gBAAgB,IAAI,gBAAgB,CAAC;QACtC,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC5D;QACD,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACnC;IACD,MAAM,cAAc,GACnB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAuC,CAAC,CAAC,MAAM;QACjF,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAyC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,IAAI,cAAc,CAAC;IAE1B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,SAAS,0CAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAErE,IAAI,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QACjD,MAAM,mBAAmB,GACxB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAoC,CAAC,CAAC,MAAM;YAC9E,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAsC,CAAC,CAAC,MAAM,CAAC;QACtF,OAAO,IAAI,mBAAmB,CAAC;KAC/B;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE;;QACL,OAAA,CAAC,CAAC,MAAM,yBAAgD;YAExD,CAAC,CAAC,cAAc,GAAG,CAAC;YAapB,CAAC,MAAA,MAAA,CAAC,CAAC,IAAI,0CAAG,wBAAO,CAAC,cAAc,CAAC,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAA;KAAA,CAC5C,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC;IAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QACnC,SAAS,CAAC,IAAI,GAAG,MAAA,SAAS,CAAC,IAAI,mCAAI,EAAE,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,wBAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;KAC3C;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAvDW,QAAA,+BAA+B,mCAuD1C","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { CastSpellInput } from '../cards/card.interface';\r\nimport { CardIds } from '../services/card-ids';\r\n\r\n/**\r\n * Balinda Stonehearth: extra spell effect resolutions for spells you cast that target a friendly board minion.\r\n * Maelstrom Emergent: each copy adds +1 extra resolution (golden +2) for tavern spells you cast; stacks additively with Balinda.\r\n */\r\nexport const getTavernSpellEffectRepeatCount = (input: CastSpellInput): number => {\r\n\tif (input.source !== input.hero) {\r\n\t\treturn 1;\r\n\t}\r\n\tlet repeats = 1;\r\n\tconst hasGoldenBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883_G);\r\n\tconst hasNormalBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883);\r\n\tif (\r\n\t\t(hasGoldenBalinda || hasNormalBalinda) &&\r\n\t\tinput.target &&\r\n\t\tinput.board.some((e) => e.entityId === input.target.entityId)\r\n\t) {\r\n\t\trepeats = hasGoldenBalinda ? 3 : 2;\r\n\t}\r\n\tconst maelstromExtra =\r\n\t\tinput.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922).length +\r\n\t\t2 * input.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922_G).length;\r\n\trepeats += maelstromExtra;\r\n\r\n\tconst spellCard = input.gameState.allCards.getCard(input.spellCardId);\r\n\tconst isBountySpell = spellCard?.mechanics?.includes('BACON_BOUNTY');\r\n\t// Proud Privateer only affects bounties *you* (the hero) cast — same as Balinda / onTavernSpellCast.\r\n\tif (isBountySpell && input.source === input.hero) {\r\n\t\tconst proudPrivateerExtra =\r\n\t\t\tinput.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825).length +\r\n\t\t\t2 * input.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825_G).length;\r\n\t\trepeats += proudPrivateerExtra;\r\n\t}\r\n\r\n\tconst cathedrals = input.hero.trinkets.filter(\r\n\t\t(t) =>\r\n\t\t\tt.cardId === CardIds.ReplicaCathedral_BG30_MagicItem_434 &&\r\n\t\t\t// Total number of times it can trigger\r\n\t\t\tt.scriptDataNum1 > 0 &&\r\n\t\t\t// Total number of times it can still trigger (recruit + combat)\r\n\t\t\t// 2026-04-25:https://replays.firestoneapp.com/?reviewId=f7d1d5ec-b83d-49f5-9669-b063a3debc79&turn=15&action=1\r\n\t\t\t// It triggered during shop, and doesn't re-trigger during combat\r\n\t\t\t/**\r\n\t\t\t * \t{\r\n\t\t\t\t\t\"cardId\": \"BG30_MagicItem_434\",\r\n\t\t\t\t\t\"entityId\": 306,\r\n\t\t\t\t\t\"scriptDataNum1\": 1,\r\n\t\t\t\t\t\"scriptDataNum2\": 0,\r\n\t\t\t\t\t\"scriptDataNum6\": 1\r\n\t\t\t\t},\r\n\t\t\t */\r\n\t\t\t(t.tags?.[GameTag.TRIGGER_VISUAL] ?? 0) > 0,\r\n\t);\r\n\trepeats += cathedrals.length;\r\n\tfor (const cathedral of cathedrals) {\r\n\t\tcathedral.tags = cathedral.tags ?? {};\r\n\t\tcathedral.tags[GameTag.TRIGGER_VISUAL] = 0;\r\n\t}\r\n\r\n\treturn repeats;\r\n};\r\n"]}
1
+ {"version":3,"file":"tavern-spell-repeat.js","sourceRoot":"","sources":["../../src/mechanics/tavern-spell-repeat.ts"],"names":[],"mappings":";;;AAAA,iEAAuD;AAQhD,MAAM,+BAA+B,GAAG,CAAC,KAAqB,EAAU,EAAE;;IAChF,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QAChC,OAAO,CAAC,CAAC;KACT;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAA0C,CAAC,CAAC;IACrG,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAwC,CAAC,CAAC;IACnG,IACC,CAAC,gBAAgB,IAAI,gBAAgB,CAAC;QACtC,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC5D;QACD,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACnC;IACD,MAAM,cAAc,GACnB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAuC,CAAC,CAAC,MAAM;QACjF,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAyC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,IAAI,cAAc,CAAC;IAE1B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,SAAS,0CAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAErE,IAAI,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QACjD,MAAM,mBAAmB,GACxB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAoC,CAAC,CAAC,MAAM;YAC9E,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAsC,CAAC,CAAC,MAAM,CAAC;QACtF,OAAO,IAAI,mBAAmB,CAAC;KAC/B;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE;;QACL,OAAA,CAAC,CAAC,MAAM,yBAAgD;YAexD,CAAC,CAAA,MAAA,CAAC,CAAC,IAAI,0CAAG,wBAAO,CAAC,qBAAqB,CAAC,CAAA,CAAA;KAAA,CACzC,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC;IAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QACnC,SAAS,CAAC,IAAI,GAAG,MAAA,SAAS,CAAC,IAAI,mCAAI,EAAE,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,wBAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;KAClD;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAvDW,QAAA,+BAA+B,mCAuD1C","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { CastSpellInput } from '../cards/card.interface';\r\nimport { CardIds } from '../services/card-ids';\r\n\r\n/**\r\n * Balinda Stonehearth: extra spell effect resolutions for spells you cast that target a friendly board minion.\r\n * Maelstrom Emergent: each copy adds +1 extra resolution (golden +2) for tavern spells you cast; stacks additively with Balinda.\r\n */\r\nexport const getTavernSpellEffectRepeatCount = (input: CastSpellInput): number => {\r\n\tif (input.source !== input.hero) {\r\n\t\treturn 1;\r\n\t}\r\n\tlet repeats = 1;\r\n\tconst hasGoldenBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883_G);\r\n\tconst hasNormalBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883);\r\n\tif (\r\n\t\t(hasGoldenBalinda || hasNormalBalinda) &&\r\n\t\tinput.target &&\r\n\t\tinput.board.some((e) => e.entityId === input.target.entityId)\r\n\t) {\r\n\t\trepeats = hasGoldenBalinda ? 3 : 2;\r\n\t}\r\n\tconst maelstromExtra =\r\n\t\tinput.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922).length +\r\n\t\t2 * input.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922_G).length;\r\n\trepeats += maelstromExtra;\r\n\r\n\tconst spellCard = input.gameState.allCards.getCard(input.spellCardId);\r\n\tconst isBountySpell = spellCard?.mechanics?.includes('BACON_BOUNTY');\r\n\t// Proud Privateer only affects bounties *you* (the hero) cast — same as Balinda / onTavernSpellCast.\r\n\tif (isBountySpell && input.source === input.hero) {\r\n\t\tconst proudPrivateerExtra =\r\n\t\t\tinput.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825).length +\r\n\t\t\t2 * input.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825_G).length;\r\n\t\trepeats += proudPrivateerExtra;\r\n\t}\r\n\r\n\tconst cathedrals = input.hero.trinkets.filter(\r\n\t\t(t) =>\r\n\t\t\tt.cardId === CardIds.ReplicaCathedral_BG30_MagicItem_434 &&\r\n\t\t\t// 2026-04-25:https://replays.firestoneapp.com/?reviewId=f7d1d5ec-b83d-49f5-9669-b063a3debc79&turn=15&action=1\r\n\t\t\t// It triggered during shop, and doesn't re-trigger during combat\r\n\t\t\t/**\r\n\t\t\t * \t{\r\n\t\t\t\t\t\"cardId\": \"BG30_MagicItem_434\",\r\n\t\t\t\t\t\"entityId\": 306,\r\n\t\t\t\t\t\"scriptDataNum1\": 1,\r\n\t\t\t\t\t\"scriptDataNum2\": 0,\r\n\t\t\t\t\t\"scriptDataNum6\": 1\r\n\t\t\t\t},\r\n\t\t\t */\r\n\t\t\t// 2026-07-22: This is not relevant. I had the case where TRIGGER_VISUAL was 1 but it wouln't re-trigger\r\n\t\t\t// (t.tags?.[GameTag.TRIGGER_VISUAL] ?? 0) > 0,\r\n\t\t\t// Next try is SCRIPT_DATA_NUM_6, assuming 1 meaning it can't trigger\r\n\t\t\t!t.tags?.[GameTag.TAG_SCRIPT_DATA_NUM_6],\r\n\t);\r\n\trepeats += cathedrals.length;\r\n\tfor (const cathedral of cathedrals) {\r\n\t\tcathedral.tags = cathedral.tags ?? {};\r\n\t\tcathedral.tags[GameTag.TAG_SCRIPT_DATA_NUM_6] = 1;\r\n\t}\r\n\r\n\treturn repeats;\r\n};\r\n"]}
@@ -2,8 +2,8 @@ import { AllCardsService } from '@firestone-hs/reference-data';
2
2
  import { BgsBattleInfo } from './bgs-battle-info';
3
3
  import { CardsData } from './cards/cards-data';
4
4
  import { SimulationResult } from './simulation-result';
5
+ export { isValidDeathrattleEnchantment } from './simulation/deathrattle-utils';
5
6
  export declare const assignCards: (cards: AllCardsService) => void;
6
7
  declare const _default: (event: any) => Promise<any>;
7
8
  export default _default;
8
9
  export declare const simulateBattle: (battleInput: BgsBattleInfo, cards: AllCardsService, cardsData: CardsData) => Generator<SimulationResult, SimulationResult, void>;
9
- export declare const isValidDeathrattleEnchantment: (cardId: string) => boolean;
@@ -1,16 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isValidDeathrattleEnchantment = exports.simulateBattle = exports.assignCards = void 0;
3
+ exports.simulateBattle = exports.assignCards = exports.isValidDeathrattleEnchantment = void 0;
4
4
  const reference_data_1 = require("@firestone-hs/reference-data");
5
- const card_interface_1 = require("./cards/card.interface");
6
5
  const cards_data_1 = require("./cards/cards-data");
7
- const _card_mappings_1 = require("./cards/impl/_card-mappings");
8
6
  const debug_state_1 = require("./debug-state");
9
7
  const input_clone_1 = require("./input-clone");
10
8
  const input_sanitation_1 = require("./input-sanitation");
11
9
  const shared_state_1 = require("./simulation/shared-state");
12
10
  const simulator_1 = require("./simulation/simulator");
13
11
  const spectator_1 = require("./simulation/spectator/spectator");
12
+ var deathrattle_utils_1 = require("./simulation/deathrattle-utils");
13
+ Object.defineProperty(exports, "isValidDeathrattleEnchantment", { enumerable: true, get: function () { return deathrattle_utils_1.isValidDeathrattleEnchantment; } });
14
+ const MAX_SAMPLE_RESIMS = 500;
14
15
  let globalCards = new reference_data_1.AllCardsService();
15
16
  const assignCards = (cards) => {
16
17
  globalCards = cards;
@@ -89,45 +90,65 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
89
90
  ...(p.chosenCardId && { chosenCardId: p.chosenCardId }),
90
91
  }));
91
92
  }
93
+ const cloneTemplate = (0, input_clone_1.buildTemplateCloner)((0, input_clone_1.cloneInput3)(inputReady));
94
+ const runSingleBattle = () => {
95
+ const input = cloneTemplate();
96
+ let playerInitialState = null;
97
+ let opponentInitialState = null;
98
+ const buildInitialStates = () => {
99
+ const inputClone = cloneTemplate();
100
+ playerInitialState = {
101
+ player: inputClone.playerBoard.player,
102
+ board: inputClone.playerBoard.board,
103
+ teammate: inputClone.playerTeammateBoard,
104
+ };
105
+ opponentInitialState = {
106
+ player: inputClone.opponentBoard.player,
107
+ board: inputClone.opponentBoard.board,
108
+ teammate: inputClone.opponentTeammateBoard,
109
+ };
110
+ };
111
+ const gameState = {
112
+ allCards: cards,
113
+ cardsData: cardsData,
114
+ spectator: spectator,
115
+ sharedState: new shared_state_1.SharedState(),
116
+ currentTurn: input.gameState.currentTurn,
117
+ validTribes: input.gameState.validTribes,
118
+ anomalies: input.gameState.anomalies,
119
+ gameState: {
120
+ player: {
121
+ player: input.playerBoard.player,
122
+ board: input.playerBoard.board,
123
+ teammate: input.playerTeammateBoard,
124
+ },
125
+ opponent: {
126
+ player: input.opponentBoard.player,
127
+ board: input.opponentBoard.board,
128
+ teammate: input.opponentTeammateBoard,
129
+ },
130
+ get playerInitial() {
131
+ if (!playerInitialState) {
132
+ buildInitialStates();
133
+ }
134
+ return playerInitialState;
135
+ },
136
+ get opponentInitial() {
137
+ if (!opponentInitialState) {
138
+ buildInitialStates();
139
+ }
140
+ return opponentInitialState;
141
+ },
142
+ },
143
+ };
144
+ const simulator = new simulator_1.Simulator(gameState);
145
+ return simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);
146
+ };
92
147
  try {
93
148
  !((_o = battleInput.options) === null || _o === void 0 ? void 0 : _o.skipInfoLogs) && console.time('simulation');
94
149
  const outcomes = {};
95
150
  for (let i = 0; i < numberOfSimulations; i++) {
96
- const input = (0, input_clone_1.cloneInput3)(inputReady);
97
- const inputClone = (0, input_clone_1.cloneInput3)(inputReady);
98
- const gameState = {
99
- allCards: cards,
100
- cardsData: cardsData,
101
- spectator: spectator,
102
- sharedState: new shared_state_1.SharedState(),
103
- currentTurn: input.gameState.currentTurn,
104
- validTribes: input.gameState.validTribes,
105
- anomalies: input.gameState.anomalies,
106
- gameState: {
107
- player: {
108
- player: input.playerBoard.player,
109
- board: input.playerBoard.board,
110
- teammate: input.playerTeammateBoard,
111
- },
112
- opponent: {
113
- player: input.opponentBoard.player,
114
- board: input.opponentBoard.board,
115
- teammate: input.opponentTeammateBoard,
116
- },
117
- playerInitial: {
118
- player: inputClone.playerBoard.player,
119
- board: inputClone.playerBoard.board,
120
- teammate: inputClone.playerTeammateBoard,
121
- },
122
- opponentInitial: {
123
- player: inputClone.opponentBoard.player,
124
- board: inputClone.opponentBoard.board,
125
- teammate: inputClone.opponentTeammateBoard,
126
- },
127
- },
128
- };
129
- const simulator = new simulator_1.Simulator(gameState);
130
- const battleResult = simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);
151
+ const battleResult = runSingleBattle();
131
152
  if (!battleResult) {
132
153
  continue;
133
154
  }
@@ -152,7 +173,6 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
152
173
  else if (battleResult.result === 'tied') {
153
174
  simulationResult.tied++;
154
175
  }
155
- spectator.commitBattleResult(battleResult.result);
156
176
  if (Date.now() - start > maxAcceptableDuration && !hideMaxSimulationDurationWarning) {
157
177
  console.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms');
158
178
  break;
@@ -164,6 +184,27 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
164
184
  }
165
185
  updateSimulationResult(simulationResult, inputReady, damageConfidence);
166
186
  !((_q = battleInput.options) === null || _q === void 0 ? void 0 : _q.skipInfoLogs) && console.timeEnd('simulation');
187
+ if (includeOutcomeSamples) {
188
+ const neededOutcomes = [];
189
+ simulationResult.won > 0 && neededOutcomes.push('won');
190
+ simulationResult.lost > 0 && neededOutcomes.push('lost');
191
+ simulationResult.tied > 0 && neededOutcomes.push('tied');
192
+ const missingOutcomes = new Set(neededOutcomes);
193
+ if (missingOutcomes.size > 0) {
194
+ spectator.startRecording();
195
+ for (let i = 0; i < MAX_SAMPLE_RESIMS && missingOutcomes.size > 0; i++) {
196
+ const battleResult = runSingleBattle();
197
+ if (!(battleResult === null || battleResult === void 0 ? void 0 : battleResult.result)) {
198
+ continue;
199
+ }
200
+ spectator.commitBattleResult(battleResult.result);
201
+ if (spectator.hasSampleFor(battleResult.result)) {
202
+ missingOutcomes.delete(battleResult.result);
203
+ }
204
+ }
205
+ spectator.stopRecording();
206
+ }
207
+ }
167
208
  spectator.prune();
168
209
  simulationResult.outcomeSamples = spectator.buildOutcomeSamples(battleInput.gameState);
169
210
  simulationResult.damageWons = [];
@@ -223,42 +264,4 @@ const checkRounding = (roundedValue, initialValue, totalValue) => {
223
264
  }
224
265
  return roundedValue;
225
266
  };
226
- const VALID_DEATHRATTLE_ENCHANTMENTS = [
227
- "BG_BOT_312e",
228
- "TB_BaconUps_032e",
229
- "UNG_999t2e",
230
- "BG21_000e",
231
- "BG21_000_Ge",
232
- "BG21_HERO_030p",
233
- "BG21_HERO_030pe",
234
- "BG22_HERO_001_Buddy_e1",
235
- "BG22_HERO_001_Buddy_e2",
236
- "BG22_HERO_001_Buddy_e4",
237
- "BG22_HERO_001_Buddy_e3",
238
- "BG22_HERO_001p_t1e",
239
- "BG22_HERO_001p_t4_s",
240
- "BG28_603e",
241
- "BG30_MagicItem_917e",
242
- "BG30_MagicItem_411e",
243
- "BG30_MagicItem_952e",
244
- "BG31_HERO_801ptee",
245
- "BG32_172e",
246
- "BG32_172_Ge",
247
- "BG20_HERO_282e2",
248
- ];
249
- const validDeathrattleEnchantmentsFromMapping = [];
250
- const isValidDeathrattleEnchantment = (cardId) => {
251
- if (VALID_DEATHRATTLE_ENCHANTMENTS.includes(cardId)) {
252
- return true;
253
- }
254
- if (validDeathrattleEnchantmentsFromMapping.length === 0) {
255
- for (const cardImpl of Object.values(_card_mappings_1.cardMappings)) {
256
- if ((0, card_interface_1.hasDeathrattleSpawnEnchantment)(cardImpl)) {
257
- validDeathrattleEnchantmentsFromMapping.push(...cardImpl.cardIds);
258
- }
259
- }
260
- }
261
- return validDeathrattleEnchantmentsFromMapping.includes(cardId);
262
- };
263
- exports.isValidDeathrattleEnchantment = isValidDeathrattleEnchantment;
264
267
  //# sourceMappingURL=simulate-bgs-battle.js.map
@@ -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 { hasDeathrattleSpawnEnchantment } from './cards/card.interface';\r\nimport { CardsData } from './cards/cards-data';\r\nimport { cardMappings } from './cards/impl/_card-mappings';\r\nimport { debugState } from './debug-state';\r\nimport { cloneInput3 } from './input-clone';\r\nimport { buildFinalInput } from './input-sanitation';\r\nimport { CardIds } from './services/card-ids';\r\nimport { SimulationResult } from './simulation-result';\r\nimport { FullGameState } from './simulation/internal-game-state';\r\nimport { SharedState } from './simulation/shared-state';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { Spectator } from './simulation/spectator/spectator';\r\n\r\nlet globalCards = new AllCardsService();\r\n\r\nexport const assignCards = (cards: AllCardsService) => {\r\n\tglobalCards = cards;\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}\r\n\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tconst cards = globalCards;\r\n\tawait cards.initializeCardsDb();\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 (!cards?.getCards()?.length) {\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\t// Apply debug state from input when present (e.g. from bug report)\r\n\tif (battleInput.debugState) {\r\n\t\tdebugState.active = true;\r\n\t\tdebugState.forcedCurrentAttacker = battleInput.debugState.forcedCurrentAttacker;\r\n\t\tdebugState.forcedFaceOffBase = battleInput.debugState.forcedFaceOffBase.map((f) => ({\r\n\t\t\tattacker: { ...f.attacker },\r\n\t\t\tdefender: { ...f.defender },\r\n\t\t}));\r\n\t\tconst rawPicks = battleInput.debugState.forcedRandomPicks ?? [];\r\n\t\tdebugState.forcedRandomPicksBase = rawPicks.map((p) => ({\r\n\t\t\tsource: { ...p.source },\r\n\t\t\ttarget: { ...p.target },\r\n\t\t\t...(p.chosenCardId && { chosenCardId: p.chosenCardId }),\r\n\t\t}));\r\n\t}\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 input: BgsBattleInfo = cloneInput3(inputReady);\r\n\t\t\tconst inputClone: BgsBattleInfo = cloneInput3(inputReady);\r\n\t\t\tconst gameState: FullGameState = {\r\n\t\t\t\tallCards: cards,\r\n\t\t\t\tcardsData: cardsData,\r\n\t\t\t\tspectator: spectator,\r\n\t\t\t\tsharedState: new SharedState(),\r\n\t\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\t\tgameState: {\r\n\t\t\t\t\tplayer: {\r\n\t\t\t\t\t\tplayer: input.playerBoard.player,\r\n\t\t\t\t\t\tboard: input.playerBoard.board,\r\n\t\t\t\t\t\tteammate: input.playerTeammateBoard,\r\n\t\t\t\t\t},\r\n\t\t\t\t\topponent: {\r\n\t\t\t\t\t\tplayer: input.opponentBoard.player,\r\n\t\t\t\t\t\tboard: input.opponentBoard.board,\r\n\t\t\t\t\t\tteammate: input.opponentTeammateBoard,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tplayerInitial: {\r\n\t\t\t\t\t\tplayer: inputClone.playerBoard.player,\r\n\t\t\t\t\t\tboard: inputClone.playerBoard.board,\r\n\t\t\t\t\t\tteammate: inputClone.playerTeammateBoard,\r\n\t\t\t\t\t},\r\n\t\t\t\t\topponentInitial: {\r\n\t\t\t\t\t\tplayer: inputClone.opponentBoard.player,\r\n\t\t\t\t\t\tboard: inputClone.opponentBoard.board,\r\n\t\t\t\t\t\tteammate: inputClone.opponentTeammateBoard,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t\tconst simulator = new Simulator(gameState);\r\n\t\t\tconst battleResult = simulator.simulateSingleBattle(\r\n\t\t\t\tgameState.gameState.player,\r\n\t\t\t\tgameState.gameState.opponent,\r\n\t\t\t);\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\t\t\tspectator.commitBattleResult(battleResult.result);\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\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\tif (battleInput.debugState) {\r\n\t\t\tdebugState.active = false;\r\n\t\t}\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// Used when triggering random deathrattles\r\nconst VALID_DEATHRATTLE_ENCHANTMENTS = [\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantment_BG_BOT_312e,\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantment_TB_BaconUps_032e,\r\n\tCardIds.LivingSpores_LivingSporesEnchantment,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment_BG21_000e,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment_BG21_000_Ge,\r\n\tCardIds.Sneed_SneedsReplicator,\r\n\tCardIds.SneedsReplicator_ReplicateEnchantment,\r\n\tCardIds.EarthRecollectionEnchantment, // Spirit Raptor\r\n\tCardIds.FireRecollectionEnchantment,\r\n\tCardIds.LightningRecollectionEnchantment,\r\n\tCardIds.WaterRecollectionEnchantment,\r\n\tCardIds.EarthInvocation_ElementEarthEnchantment, // Summon a 1/1\r\n\tCardIds.LightningInvocation, // Deal 1 damage to 5 enemy minions\r\n\t// CardIds.SurfNSurf_CrabRidingEnchantment_BG27_004e,\r\n\t// CardIds.SurfNSurf_CrabRidingEnchantment_BG27_004_Ge,\r\n\t// CardIds.RecurringNightmare_NightmareInsideEnchantment_BG26_055e,\r\n\t// CardIds.RecurringNightmare_NightmareInsideEnchantment_BG26_055_Ge,\r\n\tCardIds.BoonOfBeetles_BeetleSwarmEnchantment_BG28_603e,\r\n\tCardIds.RustyTrident_TridentsTreasureEnchantment_BG30_MagicItem_917e,\r\n\tCardIds.HoggyBank_GemInTheBankEnchantment_BG30_MagicItem_411e,\r\n\tCardIds.JarredFrostling_FrostyGlobeEnchantment_BG30_MagicItem_952e,\r\n\tCardIds.CaduceusReactor_CaduceusReactorEnchantment_BG31_HERO_801ptee,\r\n\tCardIds.AutoAssembler_AutoAssemblerEnchantment_BG32_172e,\r\n\tCardIds.AutoAssembler_AutoAssemblerEnchantment_BG32_172_Ge,\r\n\tCardIds.TamsinRoame_ImpendingSacrificeEnchantment_BG20_HERO_282e2,\r\n];\r\nconst validDeathrattleEnchantmentsFromMapping = [];\r\nexport const isValidDeathrattleEnchantment = (cardId: string): boolean => {\r\n\tif (VALID_DEATHRATTLE_ENCHANTMENTS.includes(cardId as CardIds)) {\r\n\t\treturn true;\r\n\t}\r\n\tif (validDeathrattleEnchantmentsFromMapping.length === 0) {\r\n\t\tfor (const cardImpl of Object.values(cardMappings)) {\r\n\t\t\t// Also includes non-enchantments, but since we only match this against the enchants list, it's fine\r\n\t\t\tif (hasDeathrattleSpawnEnchantment(cardImpl)) {\r\n\t\t\t\tvalidDeathrattleEnchantmentsFromMapping.push(...cardImpl.cardIds);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn validDeathrattleEnchantmentsFromMapping.includes(cardId);\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 { 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 { SimulationResult } from './simulation-result';\r\nimport { FullGameState } from './simulation/internal-game-state';\r\nimport { SharedState } from './simulation/shared-state';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { Spectator } from './simulation/spectator/spectator';\r\n\r\nexport { isValidDeathrattleEnchantment } from './simulation/deathrattle-utils';\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\n\r\nlet globalCards = new AllCardsService();\r\n\r\nexport const assignCards = (cards: AllCardsService) => {\r\n\tglobalCards = cards;\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}\r\n\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tconst cards = globalCards;\r\n\tawait cards.initializeCardsDb();\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 (!cards?.getCards()?.length) {\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\t// Apply debug state from input when present (e.g. from bug report)\r\n\tif (battleInput.debugState) {\r\n\t\tdebugState.active = true;\r\n\t\tdebugState.forcedCurrentAttacker = battleInput.debugState.forcedCurrentAttacker;\r\n\t\tdebugState.forcedFaceOffBase = battleInput.debugState.forcedFaceOffBase.map((f) => ({\r\n\t\t\tattacker: { ...f.attacker },\r\n\t\t\tdefender: { ...f.defender },\r\n\t\t}));\r\n\t\tconst rawPicks = battleInput.debugState.forcedRandomPicks ?? [];\r\n\t\tdebugState.forcedRandomPicksBase = rawPicks.map((p) => ({\r\n\t\t\tsource: { ...p.source },\r\n\t\t\ttarget: { ...p.target },\r\n\t\t\t...(p.chosenCardId && { chosenCardId: p.chosenCardId }),\r\n\t\t}));\r\n\t}\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 runSingleBattle = () => {\r\n\t\tconst input: BgsBattleInfo = cloneTemplate();\r\n\t\t// The \"initial\" states are only read by a handful of cards (and mutated by some, like\r\n\t\t// San'layn Scribe), so we only pay for the second clone when they are actually accessed\r\n\t\tlet playerInitialState = null;\r\n\t\tlet opponentInitialState = null;\r\n\t\tconst buildInitialStates = () => {\r\n\t\t\tconst inputClone: BgsBattleInfo = cloneTemplate();\r\n\t\t\tplayerInitialState = {\r\n\t\t\t\tplayer: inputClone.playerBoard.player,\r\n\t\t\t\tboard: inputClone.playerBoard.board,\r\n\t\t\t\tteammate: inputClone.playerTeammateBoard,\r\n\t\t\t};\r\n\t\t\topponentInitialState = {\r\n\t\t\t\tplayer: inputClone.opponentBoard.player,\r\n\t\t\t\tboard: inputClone.opponentBoard.board,\r\n\t\t\t\tteammate: inputClone.opponentTeammateBoard,\r\n\t\t\t};\r\n\t\t};\r\n\t\tconst gameState: FullGameState = {\r\n\t\t\tallCards: cards,\r\n\t\t\tcardsData: cardsData,\r\n\t\t\tspectator: spectator,\r\n\t\t\tsharedState: new SharedState(),\r\n\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\tgameState: {\r\n\t\t\t\tplayer: {\r\n\t\t\t\t\tplayer: input.playerBoard.player,\r\n\t\t\t\t\tboard: input.playerBoard.board,\r\n\t\t\t\t\tteammate: input.playerTeammateBoard,\r\n\t\t\t\t},\r\n\t\t\t\topponent: {\r\n\t\t\t\t\tplayer: input.opponentBoard.player,\r\n\t\t\t\t\tboard: input.opponentBoard.board,\r\n\t\t\t\t\tteammate: input.opponentTeammateBoard,\r\n\t\t\t\t},\r\n\t\t\t\tget playerInitial() {\r\n\t\t\t\t\tif (!playerInitialState) {\r\n\t\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn playerInitialState;\r\n\t\t\t\t},\r\n\t\t\t\tget opponentInitial() {\r\n\t\t\t\t\tif (!opponentInitialState) {\r\n\t\t\t\t\t\tbuildInitialStates();\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn opponentInitialState;\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t};\r\n\t\tconst simulator = new Simulator(gameState);\r\n\t\treturn simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);\r\n\t};\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 = runSingleBattle();\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 = runSingleBattle();\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\tif (battleInput.debugState) {\r\n\t\t\tdebugState.active = false;\r\n\t\t}\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"]}