WOS Sim
← Back to Runs

Run Detail

868590df-0b7b-4283-9303-f57283ac05e3

Started23/06/2026, 07:22:24
Git SHA8908f1a3
Statedirty
Avg Error %0.20%
BH Sig Count4
Total Cases178
Passing158
Run Report

Code Changes Since Previous Run

Index: simulator/config/hero_definitions/Ahmose.json
===================================================================
--- simulator/config/hero_definitions/Ahmose.json prev run
+++ simulator/config/hero_definitions/Ahmose.json this run
@@ -98,9 +98,9 @@
"source": "infantry"
},
"effects": {
"BladeOfLight/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
12,
24,
36,
@@ -112,9 +112,9 @@
"infantry"
]
},
"duration": {
- "type": "turn",
+ "type": "attack",
"value": 1
}
},
"BladeOfLight/2": {
@@ -126,9 +126,9 @@
20,
25
],
"units": {
- "applies_to": "trigger.source"
+ "applies_to": "target"
},
"duration": {
"type": "turn",
"value": 1
Index: simulator/config/hero_definitions/Alonso.json
===================================================================
--- simulator/config/hero_definitions/Alonso.json prev run
+++ simulator/config/hero_definitions/Alonso.json this run
@@ -50,9 +50,9 @@
]
},
"effects": {
"IronStrength/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Bahiti.json
===================================================================
--- simulator/config/hero_definitions/Bahiti.json prev run
+++ simulator/config/hero_definitions/Bahiti.json this run
@@ -9,9 +9,9 @@
"type": "battle_start"
},
"effects": {
"SixthSense/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
12,
@@ -34,30 +34,23 @@
]
},
"effects": {
"Fluorescence/1": {
- "type": "extra_skill_attack",
+ "type": "active.hero.lethality.up",
"value": [
10,
20,
30,
40,
50
],
"units": {
- "applies_to": "trigger.source",
- "applies_vs": "trigger.target"
+ "applies_to": "trigger.source"
},
"duration": {
"type": "attack",
"value": 1
- },
- "trigger_damage_jobs": [
- {
- "source": "use.source",
- "target": "effect.applies_vs"
- }
- ]
+ }
}
}
}
}
Index: simulator/config/hero_definitions/Bradley.json
===================================================================
--- simulator/config/hero_definitions/Bradley.json prev run
+++ simulator/config/hero_definitions/Bradley.json this run
@@ -27,9 +27,9 @@
"type": "battle_start"
},
"effects": {
"PowerShot/1": {
- "type": "type.all.damage.up",
+ "type": "active.hero.lethality.up",
"value": [
6,
12,
18,
@@ -42,9 +42,9 @@
]
}
},
"PowerShot/2": {
- "type": "type.all.damage.up",
+ "type": "active.hero.lethality.up",
"value": [
5,
10,
15,
Index: simulator/config/hero_definitions/Edith.json
===================================================================
--- simulator/config/hero_definitions/Edith.json prev run
+++ simulator/config/hero_definitions/Edith.json this run
@@ -9,9 +9,9 @@
"type": "battle_start"
},
"effects": {
"StrategicBalance/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
12,
@@ -48,9 +48,9 @@
"type": "battle_start"
},
"effects": {
"Ironclad/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
12,
Index: simulator/config/hero_definitions/Gordon.json
===================================================================
--- simulator/config/hero_definitions/Gordon.json prev run
+++ simulator/config/hero_definitions/Gordon.json this run
@@ -11,9 +11,9 @@
"source": "lancer"
},
"effects": {
"VenomInfusion/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
20,
40,
60,
@@ -31,9 +31,9 @@
"value": 1
}
},
"VenomInfusion/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
4,
8,
12,
@@ -58,9 +58,9 @@
"every": 3
},
"effects": {
"ChemicalTerror/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
30,
60,
90,
@@ -79,9 +79,9 @@
},
"same_effect_stacking": "max"
},
"ChemicalTerror/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
6,
12,
18,
@@ -107,9 +107,9 @@
"every": 4
},
"effects": {
"ToxicRelease/1": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
6,
12,
18,
@@ -125,9 +125,9 @@
},
"same_effect_stacking": "max"
},
"ToxicRelease/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
6,
12,
18,
Index: simulator/config/hero_definitions/Greg.json
===================================================================
--- simulator/config/hero_definitions/Greg.json prev run
+++ simulator/config/hero_definitions/Greg.json this run
@@ -50,9 +50,9 @@
]
},
"effects": {
"DeterrenceOfLaw/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Hector.json
===================================================================
--- simulator/config/hero_definitions/Hector.json prev run
+++ simulator/config/hero_definitions/Hector.json this run
@@ -16,9 +16,9 @@
]
},
"effects": {
"SurvivalInstincts/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
30,
@@ -64,9 +64,9 @@
"value": 15
}
},
"Rampant/2": {
- "type": "active.hero.damage.up",
+ "type": "active.hero.lethality.up",
"value": [
20,
40,
60,
Index: simulator/config/hero_definitions/Jessie.json
===================================================================
--- simulator/config/hero_definitions/Jessie.json prev run
+++ simulator/config/hero_definitions/Jessie.json this run
@@ -27,9 +27,9 @@
"type": "battle_start"
},
"effects": {
"Bulwarks/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
12,
Index: simulator/config/hero_definitions/Logan.json
===================================================================
--- simulator/config/hero_definitions/Logan.json prev run
+++ simulator/config/hero_definitions/Logan.json this run
@@ -30,9 +30,9 @@
"type": "battle_start"
},
"effects": {
"LionIntimidation/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
12,
Index: simulator/config/hero_definitions/Lynn.json
===================================================================
--- simulator/config/hero_definitions/Lynn.json prev run
+++ simulator/config/hero_definitions/Lynn.json this run
@@ -39,9 +39,9 @@
"type": "battle_start"
},
"effects": {
"MelancholicBallad/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
4,
8,
12,
Index: simulator/config/hero_definitions/Mia.json
===================================================================
--- simulator/config/hero_definitions/Mia.json prev run
+++ simulator/config/hero_definitions/Mia.json this run
@@ -16,9 +16,9 @@
]
},
"effects": {
"BadLuckStreak/1": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
10,
20,
30,
@@ -89,9 +89,9 @@
]
},
"effects": {
"RitualDeciphering/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Molly.json
===================================================================
--- simulator/config/hero_definitions/Molly.json prev run
+++ simulator/config/hero_definitions/Molly.json this run
@@ -16,9 +16,9 @@
]
},
"effects": {
"CallingOfTheStorm/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Natalia.json
===================================================================
--- simulator/config/hero_definitions/Natalia.json prev run
+++ simulator/config/hero_definitions/Natalia.json this run
@@ -16,9 +16,9 @@
]
},
"effects": {
"WildlingRoar/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Philly.json
===================================================================
--- simulator/config/hero_definitions/Philly.json prev run
+++ simulator/config/hero_definitions/Philly.json this run
@@ -83,9 +83,9 @@
]
},
"effects": {
"NumbingSpores/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
30,
Index: simulator/config/hero_definitions/Renee.json
===================================================================
--- simulator/config/hero_definitions/Renee.json prev run
+++ simulator/config/hero_definitions/Renee.json this run
@@ -6,56 +6,62 @@
"NightmareTrace": {
"description": "Troops place Dream Marks on targets every two turns, dealing extra Lancer damage once next turn",
"trigger": {
"type": "turn",
- "every": 2,
- "source": "self.lancer",
- "target": "enemy"
+ "every": 2
},
"effects": {
"NightmareTrace/1": {
- "type": "active.hero.damage.up",
+ "type": "extra_skill_attack",
"value": [
40,
80,
120,
160,
200
],
"units": {
- "applies_to": "lancer",
- "applies_vs": "target"
+ "applies_to": [
+ "lancer"
+ ],
+ "applies_vs": "any"
},
"duration": {
"type": "attack",
"value": 1,
"delay": 1
},
- "same_effect_stacking": "max"
+ "same_effect_stacking": "max",
+ "trigger_damage_jobs": [
+ {
+ "source": "use.source",
+ "target": "use.target"
+ }
+ ]
}
}
},
"Dreamcatcher": {
"description": "Dream Marks highlight enemy vulnerabilities, increasing damage dealt by Lancers to marked targets",
"trigger": {
"type": "turn",
- "every": 2,
- "source": "self.lancer",
- "target": "enemy"
+ "every": 2
},
"effects": {
"Dreamcatcher/1": {
- "type": "active.hero.damage.up",
+ "type": "active.hero.lethality.up",
"value": [
30,
60,
90,
120,
150
],
"units": {
- "applies_to": "lancer",
- "applies_vs": "target"
+ "applies_to": [
+ "lancer"
+ ],
+ "applies_vs": "any"
},
"duration": {
"type": "turn",
"value": 1,
@@ -69,24 +75,23 @@
"description": "Dream Marks expose enemy weaknesses, increasing damage taken by marked targets for all troops",
"trigger": {
"type": "turn",
"every": 2,
- "source": "self.lancer",
- "target": "enemy"
+ "source": "self.any"
},
"effects": {
"Dreamslice/1": {
- "type": "active.hero.damage.up",
+ "type": "active.hero.defense.down",
"value": [
15,
30,
45,
60,
75
],
"units": {
- "applies_to": "any",
- "applies_vs": "target"
+ "applies_to": "target",
+ "applies_vs": "any"
},
"duration": {
"type": "turn",
"value": 1,
Index: simulator/config/hero_definitions/Wayne.json
===================================================================
--- simulator/config/hero_definitions/Wayne.json prev run
+++ simulator/config/hero_definitions/Wayne.json this run
@@ -27,9 +27,9 @@
"duration": {
"type": "attack",
"value": 1
},
- "same_effect_stacking": "add",
+ "same_effect_stacking": "max",
"trigger_damage_jobs": [
{
"source": "use.source",
"target": "use.target"
@@ -122,9 +122,10 @@
100,
100
],
"units": {
- "applies_to": "trigger.source"
+ "applies_to": "trigger.source",
+ "applies_vs": "trigger.target"
},
"duration": {
"type": "attack",
"value": 1
Index: simulator/config/hero_definitions/WuMing.json
===================================================================
--- simulator/config/hero_definitions/WuMing.json prev run
+++ simulator/config/hero_definitions/WuMing.json this run
@@ -9,9 +9,9 @@
"type": "battle_start"
},
"effects": {
"ShadowsEvasion/1": {
- "type": "type.normal.defense.up",
+ "type": "type.normal.damageTaken.down",
"value": [
5,
10,
15,
@@ -24,9 +24,9 @@
]
}
},
"ShadowsEvasion/2": {
- "type": "type.skill.defense.up",
+ "type": "type.skill.damageTaken.down",
"value": [
6,
12,
18,
Index: simulator/src/classifierDamage.test.ts
===================================================================
--- simulator/src/classifierDamage.test.ts prev run
+++ simulator/src/classifierDamage.test.ts this run
@@ -144,12 +144,12 @@
["active.hero.damageTaken.up", "active.hero.damageTaken.up"],
["active.hero.damageTaken.down", "active.hero.damageTaken.down"],
["active.troop.damageTaken.up", "active.troop.damageTaken.up"],
["active.troop.damageTaken.down", "active.troop.damageTaken.down"],
- ["type.normal.defense.up", "type.normal.defense.up"],
- ["type.normal.defense.down", "type.normal.defense.down"],
- ["type.skill.defense.up", "type.skill.defense.up"],
- ["type.skill.defense.down", "type.skill.defense.down"]
+ ["type.normal.damageTaken.up", "type.normal.damageTaken.up"],
+ ["type.normal.damageTaken.down", "type.normal.damageTaken.down"],
+ ["type.skill.damageTaken.up", "type.skill.damageTaken.up"],
+ ["type.skill.damageTaken.down", "type.skill.damageTaken.down"]
]);
const defenderEffectTypes = new Set([
"active.hero.defense.up",
"active.hero.defense.down",
@@ -158,12 +158,12 @@
"active.hero.damageTaken.up",
"active.hero.damageTaken.down",
"active.troop.damageTaken.up",
"active.troop.damageTaken.down",
- "type.normal.defense.up",
- "type.normal.defense.down",
- "type.skill.defense.up",
- "type.skill.defense.down"
+ "type.normal.damageTaken.up",
+ "type.normal.damageTaken.down",
+ "type.skill.damageTaken.up",
+ "type.skill.damageTaken.down"
]);
for (const [type, expectedBucket] of expected) {
const ownerSide = defenderEffectTypes.has(type) ? "defender" : "attacker";
Index: simulator/src/damage.ts
===================================================================
--- simulator/src/damage.ts prev run
+++ simulator/src/damage.ts this run
@@ -111,9 +111,9 @@
export function calculateDamageJob(
job: DamageJob,
fighters: Record<SideId, ResolvedFighter>,
activeEffects: ActiveEffect[],
- options: { trace?: boolean; effectIndex: EffectIndex; staticDamageProfile?: StaticDamageProfile; scratch?: DamageScratch }
+ options: { trace?: boolean; effectIndex: EffectIndex; staticDamageProfile?: StaticDamageProfile; scratch?: DamageScratch; capToDefenderTroops?: boolean }
): DamageResult {
if (!options?.effectIndex) throw new Error("calculateDamageJob requires an effectIndex");
// The damage math is one path; `trace` only decides whether we also capture the (expensive)
// per-bucket contributor/aggregation detail. `detail` drives the existing helpers unchanged.
@@ -168,9 +168,10 @@
const staticTraceEntries = [staticProfile.offense[job.attackerSide][job.attackerUnit], staticProfile.defense[job.defenderSide][job.defenderUnit]];
const traceBuckets = needsTraceBuckets ? toTraceBuckets(buckets, staticTraceEntries) : undefined;
const expressionDetail = traceEnabled ? "full" : "fast";
const { rawDamage, aggregationGroups } = evaluateDefaultDamageExpression(job, buckets, expressionDetail, staticProfile);
- const kills = Math.min(defenderTroops, Math.max(0, rawDamage));
+ const uncappedKills = Math.max(0, rawDamage);
+ const kills = options.capToDefenderTroops === false ? uncappedKills : Math.min(defenderTroops, uncappedKills);
const trace = traceEnabled
? {
roundStartTroops: {
attacker: { ...job.roundStartTroops.attacker },
Index: simulator/src/damageBuckets.ts
===================================================================
--- simulator/src/damageBuckets.ts prev run
+++ simulator/src/damageBuckets.ts this run
@@ -119,21 +119,21 @@
damage: {
up: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" },
down: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" }
},
- defense: {
- up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" },
- down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" }
+ damageTaken: {
+ up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" },
+ down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" }
}
},
skill: {
damage: {
up: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" },
down: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" }
},
- defense: {
- up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" },
- down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" }
+ damageTaken: {
+ up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" },
+ down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" }
}
}
},
source: {
Index: simulator/src/index.ts
===================================================================
--- simulator/src/index.ts prev run
+++ simulator/src/index.ts this run
@@ -1,7 +1,7 @@
export { buildSimulatorConfig, loadSimulatorConfig } from "./config";
export type { RawSimulatorConfig } from "./config";
-export { simulateBattle, prepareBattle, runPrepared, signedRemainingScore } from "./simulator";
+export { bearFighterInput, prepareBattle, runPrepared, simulateBattle, simulateBearBattle, signedRemainingScore } from "./simulator";
export type { CompiledBattle } from "./simulator";
export { BattleInputBuilder } from "./battleInputBuilder";
export { applyHeroGenerationStats } from "./resolve";
export { discoverTestcaseFiles, runTestcases, adaptTestcaseEntry } from "./testcases";
Index: simulator/src/simulator.test.ts
===================================================================
--- simulator/src/simulator.test.ts prev run
+++ simulator/src/simulator.test.ts this run
@@ -5,10 +5,10 @@
import { loadSimulatorConfig } from "./config";
import { createSeededRng, chancePasses } from "./effects";
import { applyHeroGenerationStats, resolveFighter } from "./resolve";
-import { prepareBattle, runPrepared, simulateBattle, signedRemainingScore } from "./simulator";
-import type { BattleInput, EffectIntentDefinition, ResolvedSkill, SimulatorConfig, SkillFile, UnitType } from "./types";
+import { prepareBattle, runPrepared, simulateBattle, simulateBearBattle, signedRemainingScore } from "./simulator";
+import type { BattleInput, EffectIntentDefinition, FighterInput, ResolvedSkill, SimulatorConfig, SkillFile, UnitType } from "./types";
test("runPrepared reuses the compiled input seed when no override is supplied", () => {
const config = loadSimulatorConfig();
const stats = {
@@ -61,8 +61,117 @@
assert.deepEqual(result.resolved.attacker.heroes, []);
assert.ok(result.skillReport.attacker.some((entry) => entry.sourceKind === "troop_skill"));
});
+test("simulateBearBattle runs exactly 10 rounds and leaves the bear army unchanged", () => {
+ const player: FighterInput = {
+ name: "Player",
+ troops: { infantry_t1: 100 },
+ stats: {
+ infantry: {
+ attack: 100000,
+ defense: 100,
+ lethality: 100000,
+ health: 100
+ }
+ },
+ heroes: {}
+ };
+
+ const result = simulateBearBattle(player, minimalConfig(), "bear-fixed");
+
+ assert.equal(result.rounds, 10);
+ assert.equal(result.remaining.defender.infantry, 5000);
+ assert.equal(result.remaining.defender.lancer, 0);
+ assert.equal(result.remaining.defender.marksman, 0);
+ assert.ok(result.score > 5000);
+});
+
+test("simulateBearBattle always resolves the player army as a rally attacker", () => {
+ const config = sameEffectStackingConfig("RallyHero", "add", "active.hero.lethality.up");
+ config.heroDefinitions.RallyHero.skills.Overlap.requirements = [
+ { level: 1, type: "engagement_type", value: "rally" }
+ ];
+ const player: FighterInput = {
+ name: "Player",
+ troops: { infantry_t1: 100 },
+ stats: {
+ infantry: {
+ attack: 100,
+ defense: 100,
+ lethality: 100,
+ health: 100
+ }
+ },
+ heroes: { RallyHero: { skill_1: 1 } }
+ };
+
+ const result = simulateBearBattle(player, config, "bear-rally");
+
+ assert.ok(result.skillReport.attacker.some((row) => row.skillName === "Overlap"));
+});
+
+test("simulateBearBattle scores uncapped per-attack damage", () => {
+ const config = minimalConfig({
+ Main: {
+ name: "Main",
+ skills: {
+ MainBuff: {
+ trigger: { type: "battle_start" },
+ effects: { buff: { type: "active.hero.lethality.up", value: 100 } }
+ }
+ }
+ },
+ DefensiveJoiner: {
+ name: "DefensiveJoiner",
+ skills: {
+ DefensiveBuff: {
+ trigger: { type: "battle_start" },
+ effects: { buff: { type: "active.hero.defense.up", value: 100 } }
+ }
+ }
+ },
+ DamageJoiner: {
+ name: "DamageJoiner",
+ skills: {
+ DamageBuff: {
+ trigger: { type: "battle_start" },
+ effects: { buff: { type: "active.hero.lethality.up", value: 100 } }
+ }
+ }
+ }
+ });
+ const player: FighterInput = {
+ name: "Player",
+ troops: { infantry_t1: 10000 },
+ stats: {
+ infantry: {
+ attack: 1000,
+ defense: 100,
+ lethality: 1000,
+ health: 100
+ }
+ },
+ heroes: { Main: { skill_1: 1 } }
+ };
+
+ const defensive = simulateBearBattle(
+ { ...player, joiner_heroes: { DefensiveJoiner: { skill_1: 1 } } },
+ config,
+ "bear-uncapped-defensive",
+ { mode: "trace" }
+ );
+ const damage = simulateBearBattle(
+ { ...player, joiner_heroes: { DamageJoiner: { skill_1: 1 } } },
+ config,
+ "bear-uncapped-damage",
+ { mode: "trace" }
+ );
+
+ assert.ok(defensive.attacks.some((attack) => attack.kills > 5000));
+ assert.ok(damage.score > defensive.score);
+});
+
test("simulateBattle calculates all same-round damage from the round-start troop snapshot", () => {
const config = loadSimulatorConfig();
const result = simulateBattle(
{
@@ -1853,5 +1962,4 @@
troopSkills: { name: "troop skills", skills: {} },
diagnostics: { legacyFields: [], effectTypes: {}, unsupportedEffects: [], ambiguousTurnTriggerSelectors: [] }
};
}
-
Index: simulator/src/simulator.ts
===================================================================
--- simulator/src/simulator.ts prev run
+++ simulator/src/simulator.ts this run
@@ -1,8 +1,9 @@
import type {
ActiveEffect,
AttackIntent,
AttackOutcome,
+ BearBattleResult,
BattleInput,
BattleRandomness,
BattleResult,
BattleTrace,
@@ -39,8 +40,11 @@
import { emptyTroops, resolveFighter } from "./resolve";
import { buildStaticDamageProfile, type StaticDamageProfile } from "./staticDamageProfile";
const DEFAULT_MAX_ROUNDS = 1500;
+const BEAR_ROUNDS = 10;
+const BEAR_DEFENSE = 36;
+const BEAR_TROOP_ID = "bear_infantry";
const REPORT_KEY_CACHE = new WeakMap<ResolvedSkill, string>();
interface Runtime {
activeEffects: ActiveEffect[];
@@ -80,14 +84,62 @@
winner: SideId | "draw";
rounds: number;
attacks: AttackOutcome[];
trace?: BattleTrace;
+ score?: number;
}
+interface RunLoopOptions {
+ capRoundKills: boolean;
+ capJobKills: boolean;
+ commitLosses: boolean;
+ scoreSide?: {
+ attackerSide: SideId;
+ defenderSide: SideId;
+ };
+}
+
export function simulateBattle(input: BattleInput, config: SimulatorConfig, options: SimulationOptions = {}): BattleResult {
return buildBattleResult(runBattle(input, config, options));
}
+export function bearFighterInput(): FighterInput {
+ return {
+ name: "Bear",
+ troops: { [BEAR_TROOP_ID]: 5000 },
+ stats: {
+ infantry: { attack: 0, defense: 0, lethality: 0, health: 0 }
+ },
+ heroes: {},
+ joiner_heroes: {}
+ };
+}
+
+export function simulateBearBattle(
+ player: FighterInput,
+ config: SimulatorConfig,
+ seed: string | number = "bear-default",
+ options: SimulationOptions = {}
+): BearBattleResult {
+ const input: BattleInput = {
+ attacker: player,
+ defender: bearFighterInput(),
+ seed,
+ maxRounds: BEAR_ROUNDS,
+ engagement_type: "rally"
+ };
+ const run = runBattle(input, configWithBearTroop(config), options, undefined, {
+ capRoundKills: false,
+ capJobKills: false,
+ commitLosses: false,
+ scoreSide: { attackerSide: "attacker", defenderSide: "defender" }
+ });
+ return {
+ ...buildBattleResult(run),
+ score: run.score ?? bearScore(run.attacks)
+ };
+}
+
/**
* A resolved battle ready to run many times with different seeds. When battle-start is deterministic,
* the entire pre-loop runtime (`template`) is seed-independent and built once; each run clones its
* mutable state and reuses the skills + static profile by reference. For stochastic battle-start the
@@ -189,30 +241,63 @@
};
return { attacker: cloneSide(reports.attacker), defender: cloneSide(reports.defender) };
}
-function runBattle(input: BattleInput, config: SimulatorConfig, options: SimulationOptions, prepared?: CompiledBattle): BattleRun {
+function configWithBearTroop(config: SimulatorConfig): SimulatorConfig {
+ return {
+ ...config,
+ troopStats: {
+ ...config.troopStats,
+ [BEAR_TROOP_ID]: {
+ id: BEAR_TROOP_ID,
+ type: "infantry",
+ tier: 1,
+ stats: {
+ attack: 0,
+ defense: BEAR_DEFENSE,
+ lethality: 0,
+ health: 10
+ }
+ }
+ }
+ };
+}
+
+function runBattle(
+ input: BattleInput,
+ config: SimulatorConfig,
+ options: SimulationOptions,
+ prepared?: CompiledBattle,
+ loopOptions: RunLoopOptions = { capRoundKills: true, capJobKills: true, commitLosses: true }
+): BattleRun {
if (prepared?.template) {
const fighters: Record<SideId, ResolvedFighter> = {
attacker: cloneFighterForRun(prepared.fighters.attacker),
defender: cloneFighterForRun(prepared.fighters.defender)
};
const runtime = cloneRuntime(prepared.template, createSeededRng(input.seed ?? "simulator-default"));
- return runLoop(input, fighters, runtime, options);
+ return runLoop(input, fighters, runtime, options, loopOptions);
}
const attacker = prepared ? cloneFighterForRun(prepared.fighters.attacker) : resolveFighter(input.attacker, "attacker", config, input.engagement_type);
const defender = prepared ? cloneFighterForRun(prepared.fighters.defender) : resolveFighter(input.defender, "defender", config, input.engagement_type);
const fighters: Record<SideId, ResolvedFighter> = { attacker, defender };
const runtime = setupRuntime(fighters, input, input.seed ?? "simulator-default");
- return runLoop(input, fighters, runtime, options);
+ return runLoop(input, fighters, runtime, options, loopOptions);
}
-function runLoop(input: BattleInput, fighters: Record<SideId, ResolvedFighter>, runtime: Runtime, options: SimulationOptions): BattleRun {
+function runLoop(
+ input: BattleInput,
+ fighters: Record<SideId, ResolvedFighter>,
+ runtime: Runtime,
+ options: SimulationOptions,
+ loopOptions: RunLoopOptions
+): BattleRun {
const mode = options.mode ?? "standard";
const recorder = createRecorder(mode, runtime.skillReports, () => buildResolved(fighters.attacker, fighters.defender));
const maxRounds = input.maxRounds ?? DEFAULT_MAX_ROUNDS;
let rounds = 0;
+ let score = 0;
for (let round = 1; round <= maxRounds; round += 1) {
if (winnerFor(fighters)) break;
rounds = round;
const roundStartTroops = snapshotTroops(fighters);
@@ -244,16 +329,25 @@
const result = calculateDamageJob(job, fighters, runtime.activeEffects, {
trace: recorder.capturesTrace,
effectIndex: runtime.effectIndex,
staticDamageProfile: runtime.staticDamageProfile,
- scratch: recorder.capturesTrace ? undefined : runtime.damageScratch
+ scratch: recorder.capturesTrace ? undefined : runtime.damageScratch,
+ capToDefenderTroops: loopOptions.capJobKills
});
results.push({ job, result });
+ if (
+ loopOptions.scoreSide &&
+ job.attackerSide === loopOptions.scoreSide.attackerSide &&
+ job.defenderSide === loopOptions.scoreSide.defenderSide
+ ) {
+ score += result.kills;
+ }
consumeEffects(runtime, result.consumedEffectIds, result.consumedEffectUseKey, result.consumedEffectUseId, result.consumedEffectUseIds);
}
- capRoundKills(results, roundStartTroops);
- commitRound(cancelled, results, fighters, runtime);
+ if (loopOptions.capRoundKills) capRoundKills(results, roundStartTroops);
+ if (loopOptions.commitLosses) commitRound(cancelled, results, fighters, runtime);
+ else commitRoundCounters(cancelled, results, runtime);
for (const entry of cancelled) recorder.recordCancelled(entry.intent, entry.effectId, entry.reason, entry.consumedEffectIds);
for (const entry of results) recorder.recordDamageJob(entry.job, entry.result);
recorder.recordRound(round, roundStartTroops, intents, jobs);
@@ -265,9 +359,10 @@
runtime,
winner,
rounds,
attacks: recorder.attacks,
- trace: recorder.trace
+ trace: recorder.trace,
+ score
};
}
interface CancelledAttack {
@@ -768,25 +863,31 @@
// Apply the round's effects to fighter state: remove killed troops and bump attack/received
// counters. Every declared attack (each damage job and each cancelled attack) counts as one
// attack for its attacker unit and one received for its defender unit.
function commitRound(cancelled: CancelledAttack[], results: DamageJobResult[], fighters: Record<SideId, ResolvedFighter>, runtime: Runtime): void {
- for (const entry of cancelled) {
- runtime.counters.attacks[entry.intent.attackerSide][entry.intent.attackerUnit] += 1;
- runtime.counters.received[entry.intent.defenderSide][entry.intent.defenderUnit] += 1;
- }
+ commitRoundCounters(cancelled, results, runtime);
const losses: Record<SideId, Record<UnitType, number>> = { attacker: emptyTroops(), defender: emptyTroops() };
for (const { job, result } of results) {
losses[job.defenderSide][job.defenderUnit] += result.kills;
- runtime.counters.attacks[job.attackerSide][job.attackerUnit] += 1;
- runtime.counters.received[job.defenderSide][job.defenderUnit] += 1;
}
for (const side of ["attacker", "defender"] as SideId[]) {
for (const unit of UNIT_TYPES) {
fighters[side].troops[unit] = Math.max(0, fighters[side].troops[unit] - losses[side][unit]);
}
}
}
+function commitRoundCounters(cancelled: CancelledAttack[], results: DamageJobResult[], runtime: Runtime): void {
+ for (const entry of cancelled) {
+ runtime.counters.attacks[entry.intent.attackerSide][entry.intent.attackerUnit] += 1;
+ runtime.counters.received[entry.intent.defenderSide][entry.intent.defenderUnit] += 1;
+ }
+ for (const { job, result } of results) {
+ runtime.counters.attacks[job.attackerSide][job.attackerUnit] += 1;
+ runtime.counters.received[job.defenderSide][job.defenderUnit] += 1;
+ }
+}
+
function expireInactive(runtime: Runtime, round: number): void {
let write = 0;
for (let read = 0; read < runtime.activeEffects.length; read += 1) {
const effect = runtime.activeEffects[read];
@@ -853,8 +954,14 @@
if (result.winner === "defender") return -total(result.remaining.defender);
return 0;
}
+function bearScore(attacks: AttackOutcome[]): number {
+ return attacks
+ .filter((attack) => attack.attackerSide === "attacker" && attack.defenderSide === "defender")
+ .reduce((sum, attack) => sum + attack.kills, 0);
+}
+
function total(troops: Record<UnitType, number>): number {
return UNIT_TYPES.reduce((sum, unit) => sum + troops[unit], 0);
}
Index: simulator/src/types.ts
===================================================================
--- simulator/src/types.ts prev run
+++ simulator/src/types.ts this run
@@ -392,8 +392,12 @@
randomness: BattleRandomness;
trace?: BattleTrace;
}
+export interface BearBattleResult extends BattleResult {
+ score: number;
+}
+
export interface BattleRandomness {
deterministic: boolean;
chanceSkillIds: Record<SideId, string[]>;
}
Show Raw Dirty State Patch (vs Clean Baseline)
diff --git a/simulator/config/hero_definitions/Ahmose.json b/simulator/config/hero_definitions/Ahmose.json
--- a/simulator/config/hero_definitions/Ahmose.json
+++ b/simulator/config/hero_definitions/Ahmose.json
@@ -24,7 +24,7 @@
}
},
"ViperFormation/2": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
15,
@@ -46,7 +46,7 @@
"same_effect_stacking": "max"
},
"ViperFormation/3": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
25,
@@ -75,7 +75,7 @@
},
"effects": {
"PrayerOfFlame/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
20,
40,
@@ -99,7 +99,7 @@
},
"effects": {
"BladeOfLight/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
12,
24,
@@ -118,7 +118,7 @@
}
},
"BladeOfLight/2": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
5,
10,
@@ -131,8 +131,7 @@
},
"duration": {
"type": "turn",
- "value": 1,
- "delay": 1
+ "value": 1
},
"same_effect_stacking": "add"
}
diff --git a/simulator/config/hero_definitions/Alonso.json b/simulator/config/hero_definitions/Alonso.json
--- a/simulator/config/hero_definitions/Alonso.json
+++ b/simulator/config/hero_definitions/Alonso.json
@@ -51,7 +51,7 @@
},
"effects": {
"IronStrength/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/Bahiti.json b/simulator/config/hero_definitions/Bahiti.json
--- a/simulator/config/hero_definitions/Bahiti.json
+++ b/simulator/config/hero_definitions/Bahiti.json
@@ -10,7 +10,7 @@
},
"effects": {
"SixthSense/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
@@ -35,7 +35,7 @@
},
"effects": {
"Fluorescence/1": {
- "type": "extra_skill_attack",
+ "type": "active.hero.lethality.up",
"value": [
10,
20,
@@ -44,19 +44,12 @@
50
],
"units": {
- "applies_to": "trigger.source",
- "applies_vs": "trigger.target"
+ "applies_to": "trigger.source"
},
"duration": {
"type": "attack",
"value": 1
- },
- "trigger_damage_jobs": [
- {
- "source": "use.source",
- "target": "effect.applies_vs"
- }
- ]
+ }
}
}
}
diff --git a/simulator/config/hero_definitions/Bradley.json b/simulator/config/hero_definitions/Bradley.json
--- a/simulator/config/hero_definitions/Bradley.json
+++ b/simulator/config/hero_definitions/Bradley.json
@@ -67,7 +67,7 @@
},
"effects": {
"TacticalAssistance/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
6,
12,
diff --git a/simulator/config/hero_definitions/Edith.json b/simulator/config/hero_definitions/Edith.json
--- a/simulator/config/hero_definitions/Edith.json
+++ b/simulator/config/hero_definitions/Edith.json
@@ -10,7 +10,7 @@
},
"effects": {
"StrategicBalance/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
@@ -25,7 +25,7 @@
}
},
"StrategicBalance/2": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
4,
8,
@@ -49,7 +49,7 @@
},
"effects": {
"Ironclad/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
diff --git a/simulator/config/hero_definitions/Flint.json b/simulator/config/hero_definitions/Flint.json
--- a/simulator/config/hero_definitions/Flint.json
+++ b/simulator/config/hero_definitions/Flint.json
@@ -10,7 +10,7 @@
},
"effects": {
"Pyromaniac/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
20,
40,
diff --git a/simulator/config/hero_definitions/Gordon.json b/simulator/config/hero_definitions/Gordon.json
--- a/simulator/config/hero_definitions/Gordon.json
+++ b/simulator/config/hero_definitions/Gordon.json
@@ -12,7 +12,7 @@
},
"effects": {
"VenomInfusion/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
20,
40,
@@ -32,7 +32,7 @@
}
},
"VenomInfusion/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
4,
8,
@@ -59,7 +59,7 @@
},
"effects": {
"ChemicalTerror/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
30,
60,
@@ -80,7 +80,7 @@
"same_effect_stacking": "max"
},
"ChemicalTerror/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
6,
12,
@@ -108,7 +108,7 @@
},
"effects": {
"ToxicRelease/1": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
6,
12,
@@ -126,7 +126,7 @@
"same_effect_stacking": "max"
},
"ToxicRelease/2": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
6,
12,
diff --git a/simulator/config/hero_definitions/Greg.json b/simulator/config/hero_definitions/Greg.json
--- a/simulator/config/hero_definitions/Greg.json
+++ b/simulator/config/hero_definitions/Greg.json
@@ -17,7 +17,7 @@
},
"effects": {
"SwordOfJustice/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
8,
16,
@@ -51,7 +51,7 @@
},
"effects": {
"DeterrenceOfLaw/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/Gwen.json b/simulator/config/hero_definitions/Gwen.json
--- a/simulator/config/hero_definitions/Gwen.json
+++ b/simulator/config/hero_definitions/Gwen.json
@@ -10,7 +10,7 @@
},
"effects": {
"EagleVision/1": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
5,
10,
diff --git a/simulator/config/hero_definitions/Hector.json b/simulator/config/hero_definitions/Hector.json
--- a/simulator/config/hero_definitions/Hector.json
+++ b/simulator/config/hero_definitions/Hector.json
@@ -17,7 +17,7 @@
},
"effects": {
"SurvivalInstincts/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
@@ -40,7 +40,7 @@
},
"effects": {
"Rampant/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
100,
125,
diff --git a/simulator/config/hero_definitions/Jeronimo.json b/simulator/config/hero_definitions/Jeronimo.json
--- a/simulator/config/hero_definitions/Jeronimo.json
+++ b/simulator/config/hero_definitions/Jeronimo.json
@@ -47,7 +47,7 @@
},
"effects": {
"EXpertSwordsmanship/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
6,
12,
diff --git a/simulator/config/hero_definitions/Jessie.json b/simulator/config/hero_definitions/Jessie.json
--- a/simulator/config/hero_definitions/Jessie.json
+++ b/simulator/config/hero_definitions/Jessie.json
@@ -28,7 +28,7 @@
},
"effects": {
"Bulwarks/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
diff --git a/simulator/config/hero_definitions/Logan.json b/simulator/config/hero_definitions/Logan.json
--- a/simulator/config/hero_definitions/Logan.json
+++ b/simulator/config/hero_definitions/Logan.json
@@ -31,7 +31,7 @@
},
"effects": {
"LionIntimidation/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
4,
8,
diff --git a/simulator/config/hero_definitions/Lynn.json b/simulator/config/hero_definitions/Lynn.json
--- a/simulator/config/hero_definitions/Lynn.json
+++ b/simulator/config/hero_definitions/Lynn.json
@@ -40,7 +40,7 @@
},
"effects": {
"MelancholicBallad/1": {
- "type": "active.hero.lethality.down",
+ "type": "active.hero.damage.down",
"value": [
4,
8,
diff --git a/simulator/config/hero_definitions/Mia.json b/simulator/config/hero_definitions/Mia.json
--- a/simulator/config/hero_definitions/Mia.json
+++ b/simulator/config/hero_definitions/Mia.json
@@ -17,7 +17,7 @@
},
"effects": {
"BadLuckStreak/1": {
- "type": "active.hero.defense.down",
+ "type": "active.hero.damageTaken.up",
"value": [
10,
20,
@@ -90,7 +90,7 @@
},
"effects": {
"RitualDeciphering/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/Molly.json b/simulator/config/hero_definitions/Molly.json
--- a/simulator/config/hero_definitions/Molly.json
+++ b/simulator/config/hero_definitions/Molly.json
@@ -17,7 +17,7 @@
},
"effects": {
"CallingOfTheStorm/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/Natalia.json b/simulator/config/hero_definitions/Natalia.json
--- a/simulator/config/hero_definitions/Natalia.json
+++ b/simulator/config/hero_definitions/Natalia.json
@@ -17,7 +17,7 @@
},
"effects": {
"WildlingRoar/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/Norah.json b/simulator/config/hero_definitions/Norah.json
--- a/simulator/config/hero_definitions/Norah.json
+++ b/simulator/config/hero_definitions/Norah.json
@@ -26,7 +26,7 @@
}
},
"CombinedArms/2": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
3,
6,
@@ -96,7 +96,7 @@
},
"effects": {
"Momentum/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
5,
10,
diff --git a/simulator/config/hero_definitions/Philly.json b/simulator/config/hero_definitions/Philly.json
--- a/simulator/config/hero_definitions/Philly.json
+++ b/simulator/config/hero_definitions/Philly.json
@@ -84,7 +84,7 @@
},
"effects": {
"NumbingSpores/1": {
- "type": "active.hero.defense.up",
+ "type": "active.hero.damageTaken.down",
"value": [
10,
20,
diff --git a/simulator/config/hero_definitions/WuMing.json b/simulator/config/hero_definitions/WuMing.json
--- a/simulator/config/hero_definitions/WuMing.json
+++ b/simulator/config/hero_definitions/WuMing.json
@@ -10,7 +10,7 @@
},
"effects": {
"ShadowsEvasion/1": {
- "type": "type.normal.defense.up",
+ "type": "type.normal.damageTaken.down",
"value": [
5,
10,
@@ -25,7 +25,7 @@
}
},
"ShadowsEvasion/2": {
- "type": "type.skill.defense.up",
+ "type": "type.skill.damageTaken.down",
"value": [
6,
12,
@@ -48,7 +48,7 @@
},
"effects": {
"CrescentUplift/1": {
- "type": "active.hero.lethality.up",
+ "type": "active.hero.damage.up",
"value": [
4,
8,
diff --git a/simulator/src/classifierDamage.test.ts b/simulator/src/classifierDamage.test.ts
--- a/simulator/src/classifierDamage.test.ts
+++ b/simulator/src/classifierDamage.test.ts
@@ -145,10 +145,10 @@
["active.hero.damageTaken.down", "active.hero.damageTaken.down"],
["active.troop.damageTaken.up", "active.troop.damageTaken.up"],
["active.troop.damageTaken.down", "active.troop.damageTaken.down"],
- ["type.normal.defense.up", "type.normal.defense.up"],
- ["type.normal.defense.down", "type.normal.defense.down"],
- ["type.skill.defense.up", "type.skill.defense.up"],
- ["type.skill.defense.down", "type.skill.defense.down"]
+ ["type.normal.damageTaken.up", "type.normal.damageTaken.up"],
+ ["type.normal.damageTaken.down", "type.normal.damageTaken.down"],
+ ["type.skill.damageTaken.up", "type.skill.damageTaken.up"],
+ ["type.skill.damageTaken.down", "type.skill.damageTaken.down"]
]);
const defenderEffectTypes = new Set([
"active.hero.defense.up",
@@ -159,10 +159,10 @@
"active.hero.damageTaken.down",
"active.troop.damageTaken.up",
"active.troop.damageTaken.down",
- "type.normal.defense.up",
- "type.normal.defense.down",
- "type.skill.defense.up",
- "type.skill.defense.down"
+ "type.normal.damageTaken.up",
+ "type.normal.damageTaken.down",
+ "type.skill.damageTaken.up",
+ "type.skill.damageTaken.down"
]);
for (const [type, expectedBucket] of expected) {
diff --git a/simulator/src/damageBuckets.ts b/simulator/src/damageBuckets.ts
--- a/simulator/src/damageBuckets.ts
+++ b/simulator/src/damageBuckets.ts
@@ -120,9 +120,9 @@
up: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" },
down: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" }
},
- defense: {
- up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" },
- down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" }
+ damageTaken: {
+ up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "normal" },
+ down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "normal" }
}
},
skill: {
@@ -130,9 +130,9 @@
up: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" },
down: { role: "attacker", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" }
},
- defense: {
- up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" },
- down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" }
+ damageTaken: {
+ up: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "numerator", appliesTo: "skill" },
+ down: { role: "defender", valueType: "pct", update: "add_pct_factor", placement: "denominator", appliesTo: "skill" }
}
}
},

Accuracy Results

178 / 178
FileCase#AdjS nG nS μG μBias%tqPW
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_90115302.05302.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_10111-1618.0-1618.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_11211-376.0-376.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_12311-6.0-6.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_134111462.01462.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_14511525.0525.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_156+0.013%11-254.0-254.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_167+0.008%111425.01425.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_178-0.003%11986.0986.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_189+0.017%11-177.0-177.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_191011-489.0-489.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2011-0.006%11712.0712.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2112+0.013%11-193.0-193.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2213111309.01309.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_231411-277.0-277.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2415-0.008%11585.0585.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_251611-45.0-45.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2617-0.002%11798.0798.00.00%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_10+0.05%11735.0741.0-0.32%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_21+0.05%11-62.0-61.0-0.06%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_32+0.05%11771.0778.0-0.38%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_43+0.05%11345.0353.0-0.45%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_54+0.043%11264.0264.00.00%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_65+0.05%11-90.0-89.0-0.07%P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_10+0.05%11345.0342.00.19%F
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_211001-3729.0-3729.00.00%P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_321001-2907.3-2823.0-0.39%-3.170.033P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_431001-528.3-519.0-0.07%-1.410.68P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_5410019303.89282.00.17%0.640.87P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_751001-2273.5-2295.00.16%2.320.25P
testcases/4-testcases_no-heroes_infantry_fc5.jsonNitro_Beast_30_1010013-45545.1-45251.0-0.22%-0.580.87P
testcases/emulator_verified/ahmose_solo_nc.jsonahmose_solo0-0.05%112636.02633.00.06%F
testcases/emulator_verified/alonso_attacker_600_all.jsonalonso_attacker_600_all01006-256.4-253.0-0.14%-0.750.87P
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer01004-463.5-464.50.04%1.820.59P
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer11002-459.6-460.00.02%0.480.88P
testcases/emulator_verified/alonso_minxxx_defender.jsonalonso_minxxx_defender010081453.41450.40.14%0.460.88P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo01002-393.4-393.0-0.02%-0.190.96P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo11001-353.4-349.0-0.26%-1.650.59P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo21002-393.1-390.0-0.19%-1.450.66P
testcases/emulator_verified/alonso_solo.jsonalonso_solo0100101243.41104.82.97%1.200.70P
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v201006-493.2-513.71.58%0.870.87P
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v2110019-10.010.6-1.59%-1.060.77P
testcases/emulator_verified/alonso_vs_alonso.jsonalonso_vs_alonso010081551.01554.6-0.17%-0.660.87P
testcases/emulator_verified/alonso_vs_alonso_inf_lancer.jsonalonso_vs_alonso_inf_lancer01008-751.5-749.0-0.04%-0.470.88P
testcases/emulator_verified/alonso_wip_attacker.jsonalonso_wip_attacker010081768.21768.9-0.03%-0.400.90P
testcases/emulator_verified/ambusher_no_marksmen.jsonambusher_no_marksmen01001-103480.7-105686.00.72%1.260.68P
testcases/emulator_verified/attdef_jasser_patrick_minxxx_attacks_nc.jsonattdef_jasser_patrick_minxxx_attacks0+0.05%13-807.0-807.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_nc.jsonattdef_jasser_patrick013-1138.0-1138.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_swap_nc.jsonattdef_jasser_patrick_swap0131088.01088.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks0-0.05%11807.0807.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks1-0.05%12807.0807.00.00%P
testcases/emulator_verified/attdef_test_minxxx_attacks_nc.jsonattdef_test_minxxx_attacks_nc0-0.05%111133.01131.00.08%F
testcases/emulator_verified/attdef_test_wip_attacks_nc.jsonattdef_test_wip_attacks_nc012-1138.0-1138.00.00%P
testcases/emulator_verified/bahiti_norah_combo_nc.jsonbahiti_norah_combo_nc01001186.8269.0-3.42%-0.580.87P
testcases/emulator_verified/bahiti_solo.jsonbahiti_solo010022221.12206.00.32%0.390.90P
testcases/emulator_verified/bahiti_solo_nc.jsonbahiti_solo_nc010013677.03675.00.04%0.380.90P
testcases/emulator_verified/determinism_test_normal_nc.jsondeterminism_test_normal014852.0852.00.00%P
testcases/emulator_verified/determinism_test_swapped_nc.jsondeterminism_test_swapped011-852.0-852.00.00%P
testcases/emulator_verified/flint_reina_zinman_combo.jsonflint_reina_zinman_combo01001-445.1-439.0-0.15%-0.990.79P
testcases/emulator_verified/flint_solo_nc.jsonflint_solo0+0.05%112861.02862.0-0.02%P
testcases/emulator_verified/flint_zinman_combo_nc.jsonflint_zinman_combo_nc0111005.01005.00.00%P
testcases/emulator_verified/greg_mia_combo.jsongreg_mia_combo010042416.22508.3-1.97%-1.610.59P
testcases/emulator_verified/greg_mia_combo.jsongreg_mia_combo1-0.05%10053482.43432.81.06%5.570.0F
testcases/emulator_verified/greg_mia_defender_current.jsongreg_mia_defender_current0100183347.63351.6-0.09%-0.700.87P
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current0133752.03752.00.00%P
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current1+0.05%133652.03652.00.00%P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current010063685.93685.80.00%0.030.99P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current110023585.43585.00.01%0.120.96P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current2100203593.83594.6-0.02%-0.630.87P
testcases/emulator_verified/greg_solo.jsongreg_solo010012704.62697.00.16%0.590.87P
testcases/emulator_verified/gwen_norah_combo_nc.jsongwen_norah_combo_nc010012697.82668.00.64%0.480.88P
testcases/emulator_verified/gwen_solo_nc.jsongwen_solo0-0.05%153017.03006.00.24%F
testcases/emulator_verified/hector_patrick_combo.jsonhector_patrick_combo01001-590.7-590.0-0.05%-0.970.79P
testcases/emulator_verified/hector_renee_wayne.jsonhector_renee_wayne0-0.05%10011607.61597.00.53%3.830.0055F
testcases/emulator_verified/hector_solo.jsonhector_solo01005-388.8-394.40.12%0.510.88P
testcases/emulator_verified/jasser_solo_nc.jsonjasser_solo0+0.05%112740.02740.00.00%P
testcases/emulator_verified/jessie_solo_nc.jsonjessie_solo0-0.05%1379.079.00.00%P
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo0+0.05%112822.02822.00.00%P
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo1+0.05%113214.03215.0-0.02%P
testcases/emulator_verified/logan_bahiti_combo_nc.jsonlogan_bahiti_combo_nc01001-507.1-517.00.55%0.800.87P
testcases/emulator_verified/logan_gwen_combo_nc.jsonlogan_gwen_combo_nc0-0.05%11-400.0-403.00.13%F
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo01001-551.1-553.00.08%1.130.72P
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo11002551.1550.50.03%0.510.88P
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo21002551.0551.00.00%0.010.99P
testcases/emulator_verified/logan_reina_bahiti_combo_nc.jsonlogan_reina_bahiti_combo_nc01001-495.2-482.0-0.60%-0.890.87P
testcases/emulator_verified/logan_solo_minxxx_nc.jsonlogan_solo_minxxx011-1995.0-1995.00.00%P
testcases/emulator_verified/logan_solo_v2_nc.jsonlogan_solo_v2011-1134.0-1134.00.00%P
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo0+0.05%112797.02797.00.00%P
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo1+0.05%113127.03127.00.00%P
testcases/emulator_verified/lynn_solo.jsonlynn_solo01005-185.1-181.2-0.08%-0.440.89P
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current0100103540.83543.9-0.07%-0.630.87P
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current1100203439.73449.2-0.21%-2.460.24P
testcases/emulator_verified/mia_solo.jsonmia_solo01001-269.3-260.0-0.27%-0.970.79P
testcases/emulator_verified/mia_solo.jsonmia_solo110011297.91293.00.22%0.590.87P
testcases/emulator_verified/mia_solo.jsonmia_solo21001-185.0-180.0-0.32%-1.010.79P
testcases/emulator_verified/mia_solo.jsonmia_solo31001879.6879.00.04%0.140.96P
testcases/emulator_verified/mia_solo.jsonmia_solo41001969.9967.00.21%0.650.87P
testcases/emulator_verified/molly_lynn_combo.jsonmolly_lynn_combo01005-402.8-402.80.00%0.040.99P
testcases/emulator_verified/molly_solo.jsonmolly_solo01005244.0244.8-0.07%-1.670.59P
testcases/emulator_verified/natalia_solo.jsonnatalia_solo0+0.05%10012614.52664.0-1.06%-2.390.24F
testcases/emulator_verified/norah_greg_combo.jsonnorah_greg_combo01005-366.7-366.0-0.04%-0.700.87P
testcases/emulator_verified/norah_hector_zinman_combo.jsonnorah_hector_zinman_combo01005-269.4-270.40.06%1.360.68P
testcases/emulator_verified/norah_s2_inf_only_A.jsonnorah_s2_inf_only_A0+0.05%100492.798.5-0.35%-1.190.70P
testcases/emulator_verified/norah_s2_inf_only_B_nc.jsonnorah_s2_inf_only_B0+0.05%13507.0507.00.00%P
testcases/emulator_verified/norah_s2_splash_A.jsonnorah_s2_splash_A010011785.21780.00.23%0.350.92P
testcases/emulator_verified/norah_s2_splash_B_nc.jsonnorah_s2_splash_B0131900.01900.00.00%P
testcases/emulator_verified/norah_s2_splash_C_nc.jsonnorah_s2_splash_C0131153.01153.00.00%P
testcases/emulator_verified/norah_s2_splash_D.jsonnorah_s2_splash_D01003829.5827.30.12%0.720.87P
testcases/emulator_verified/norah_solo.jsonnorah_solo010072740.12753.1-0.28%-0.710.87P
testcases/emulator_verified/norah_zinman_combo_nc.jsonnorah_zinman_combo_nc01001679.3670.00.39%0.130.96P
testcases/emulator_verified/patrick_solo_nc.jsonpatrick_solo0+0.001%112581.02582.0-0.02%P
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo01001-558.5-556.0-0.14%-1.310.68P
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo11004-454.3-458.30.22%1.600.59P
testcases/emulator_verified/philly_solo.jsonphilly_solo010022262.32253.00.20%0.160.96P
testcases/emulator_verified/philly_solo.jsonphilly_solo110033620.83619.70.02%0.150.96P
testcases/emulator_verified/reina_attacker_wip.jsonreina_attacker_wip01001-1991.7-1992.00.01%0.520.88P
testcases/emulator_verified/reina_bahiti_combo_nc.jsonreina_bahiti_combo_nc01001-483.1-502.01.05%1.160.70P
testcases/emulator_verified/reina_logan_combo_simulator.jsonreina_logan_combo_simulator01001-549.5-553.00.14%1.280.68P
testcases/emulator_verified/reina_solo_wip.jsonreina_solo_wip010011991.61992.0-0.02%-0.590.87P
testcases/emulator_verified/renee_solo_nc.jsonrenee_solo_nc0+0.05%15-563.0-554.0-0.48%F
testcases/emulator_verified/seo_yoon_solo_nc.jsonseo_yoon_solo0-0.05%13-619.0-620.00.10%P
testcases/emulator_verified/sergey_norah_zinman_combo_nc.jsonsergey_norah_zinman_combo_nc010011328.91372.0-1.44%-0.710.87P
testcases/emulator_verified/sergey_reina_combo_nc.jsonsergey_reina_combo_nc01001-303.3-295.0-0.35%-0.290.93P
testcases/emulator_verified/sergey_solo_nc.jsonsergey_solo0-0.05%121348.01348.00.00%P
testcases/emulator_verified/simple_001_nc.jsonsimple_00101001-186.0-186.00.00%P
testcases/emulator_verified/troop_check_nc.jsontroop_check011-279.0-279.00.00%P
testcases/emulator_verified/type_triangle_A_nc.jsontype_triangle_A_nc0+0.05%11-895.0-895.00.00%P
testcases/emulator_verified/type_triangle_B_nc.jsontype_triangle_B_nc011-929.0-929.00.00%P
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo01004232.9237.0-0.23%-0.110.96P
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo11007310.4346.6-2.01%-1.770.59P
testcases/emulator_verified/wayne_reina_combo.jsonwayne_reina_combo010081550.41559.4-0.37%-1.710.59P
testcases/emulator_verified/wayne_reina_wuming_combo.jsonwayne_reina_wuming_combo010081698.21694.00.18%1.990.45P
testcases/emulator_verified/wayne_s1_solo.jsonwayne_s1_solo01005254.0251.80.36%1.300.68P
testcases/emulator_verified/wayne_s2_solo.jsonwayne_s2_solo01002363.7361.50.43%1.250.68P
testcases/emulator_verified/wos425_wip_nohero_control_nc.jsonwos425_wip_nohero_control_nc011774.0774.00.00%P
testcases/emulator_verified/wos425_wip_patrick_health_nc.jsonwos425_wip_patrick_health_nc011762.0762.00.00%P
testcases/emulator_verified/wos425_wip_philly_defense.jsonwos425_wip_philly_defense01005768.4768.6-0.02%-0.590.87P
testcases/emulator_verified/wos425_wip_sergey_damage_taken_nc.jsonwos425_wip_sergey_damage_taken_nc011749.0749.00.00%P
testcases/emulator_verified/wos425_wip_sergey_patrick_combo_nc.jsonwos425_wip_sergey_patrick_combo_nc011726.0726.00.00%P
testcases/emulator_verified/wos425_wip_sergey_philly_combo.jsonwos425_wip_sergey_philly_combo01005738.1738.2-0.01%-0.240.96P
testcases/emulator_verified/wos427_wip_sergey_philly_840.jsonwos427_wip_sergey_philly_8400100417.922.5-0.28%-0.310.93P
testcases/emulator_verified/wu_ming_solo_nc.jsonwu_ming_solo0111392.01392.00.00%P
testcases/emulator_verified/zinman_solo_nc.jsonzinman_solo0+0.05%113177.03177.00.00%P
testcases/heroes_unittests/Flint_tc.jsondaut_viper_10+0.05%15-151.0-149.8-0.05%F
testcases/heroes_unittests/Flint_tc.jsondaut_viper_21-0.05%165752.05732.70.27%F
testcases/heroes_unittests/Hector_tc.jsondaut_viper_101001-441.6-441.00.00%-0.130.96P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_10+0.05%11-62.0-61.0-0.06%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_21+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_32-0.025%11481.0481.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_43-0.017%11217.0217.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_5411578.0578.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_65+0.05%11-97.0-96.0-0.06%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_76+0.05%11-75.0-74.0-0.05%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_8711-988.0-988.00.00%P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_101001-173.5-181.00.17%1.570.59P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_21+0.05%10071591.81625.1-0.77%-2.250.26F
testcases/heroes_unittests/Mia_tc.jsondaut_viper_3210061122.01124.2-0.10%-1.480.66P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_431001668.1671.0-0.13%-0.310.93P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_541004-124.3-121.8-0.11%-1.250.68P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_10+0.05%11-62.0-61.0-0.06%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_21+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_32-0.025%11481.0481.00.00%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_431001-63.5-68.00.33%3.730.0055P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_541001-190.3-190.0-0.02%-0.210.96P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_10+0.05%11-66.0-62.0-0.18%F
testcases/heroes_unittests/Reina_tc.jsondaut_viper_2111-40.0-40.00.00%P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_4210061413.41428.3-0.46%-0.280.93P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_531001-201.9-202.00.01%0.060.99P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_10+0.05%11-62.0-61.0-0.06%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_61+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_32-0.025%11481.0481.00.00%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_43+0.05%114339.04341.0-0.04%F
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_54+0.05%113802.03805.0-0.06%F
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_65-0.006%11917.0915.00.04%F
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_76-0.004%112130.02128.00.04%F
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_10-0.05%11-122.0-122.00.00%P
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_21-0.025%114018.04018.00.00%P
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_3211-130.0-130.00.00%P