forked from phenom/healer-man
Release Healer Man 0.1.49
This commit is contained in:
1 parent
39425f2494
commit
612170afd0
91 files changed
+2278
-263
No files matched your search
+27
-5
@@ -443,6 +443,8 @@ def aggregate(raw: dict[str, Any]) -> dict[str, Any]:
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": raw["generatedAt"],
|
||||
"engine": raw["engine"],
|
||||
"policyId": raw["policyId"],
|
||||
"rotationVersion": raw["rotationVersion"],
|
||||
"config": raw["config"],
|
||||
"party": raw["party"],
|
||||
"summary": {
|
||||
@@ -508,6 +510,8 @@ def markdown_report(report: dict[str, Any]) -> str:
|
||||
"",
|
||||
f"Generated: {report['generatedAt']}",
|
||||
"",
|
||||
f"Policy: `{report['policyId']}` · rotation: `{report['rotationVersion']}`",
|
||||
"",
|
||||
f"Party source: **{config['source']}** · level **{config['level']}** · "
|
||||
f"{report['summary']['bosses']} bosses · {report['summary']['runs']} simulations",
|
||||
"",
|
||||
@@ -614,7 +618,7 @@ input {{ width:min(420px,90%); padding:9px 12px; margin:0 0 12px; color:inherit;
|
||||
th,td {{ padding:8px 10px; border-bottom:1px solid #21262d; text-align:right; white-space:nowrap; }} th {{ position:sticky; top:0; cursor:pointer; background:#161b22; }}
|
||||
th:first-child,td:first-child,th:nth-child(2),td:nth-child(2) {{ text-align:left; }} tr:hover {{ background:#161b22; }}
|
||||
</style></head><body><h1>Healer Man Balance Lab</h1>
|
||||
<p class="summary">{html.escape(report['generatedAt'])} · level {report['config']['level']} {html.escape(report['config']['source'])} party · {report['summary']['bosses']} bosses · {report['summary']['runs']} simulations. Click a heading to sort; filter boxes keep every row accessible.</p>
|
||||
<p class="summary">{html.escape(report['generatedAt'])} · policy {html.escape(report['policyId'])} · rotation {html.escape(report['rotationVersion'])} · level {report['config']['level']} {html.escape(report['config']['source'])} party · {report['summary']['bosses']} bosses · {report['summary']['runs']} simulations. Click a heading to sort; filter boxes keep every row accessible.</p>
|
||||
{member_table}{boss_table}
|
||||
<script>
|
||||
function filterTable(id,q){{q=q.toLowerCase();document.querySelectorAll('#'+id+' tbody tr').forEach(r=>r.hidden=!r.textContent.toLowerCase().includes(q));}}
|
||||
@@ -642,9 +646,13 @@ def create_reports(raw_path: Path, output_dir: Path) -> dict[str, Any]:
|
||||
"bossKey", "bossName", "dungeonTitle", "slot", "className", "specialization", "role",
|
||||
"dps", "bossDps", "hps", "dtps", "deathEvents",
|
||||
))
|
||||
run_rows = [{key: value for key, value in run.items() if key not in {"members", "bossAbilities"}} for run in raw["runs"]]
|
||||
run_rows = [{
|
||||
"policyId": raw["policyId"],
|
||||
"rotationVersion": raw["rotationVersion"],
|
||||
**{key: value for key, value in run.items() if key not in {"members", "bossAbilities"}},
|
||||
} for run in raw["runs"]]
|
||||
write_csv(output_dir / "runs.csv", run_rows, (
|
||||
"bossKey", "bossName", "bossCategory", "dungeonId", "dungeonTitle", "seed", "outcome",
|
||||
"policyId", "rotationVersion", "bossKey", "bossName", "bossCategory", "dungeonId", "dungeonTitle", "seed", "outcome",
|
||||
"elapsedSeconds", "timeToDefeatSeconds", "bossLevel", "bossMaxHealth", "bossRemainingHealth",
|
||||
"partyDamage", "bossDamage", "partyDps", "bossDpsTaken", "incomingDamage", "rawIncomingDamage",
|
||||
"incomingDtps", "deaths", "deathEvents", "bossAttacks",
|
||||
@@ -787,6 +795,8 @@ def suite_markdown(report: dict[str, Any]) -> str:
|
||||
"",
|
||||
f"Generated: {report['generatedAt']}",
|
||||
"",
|
||||
f"Policy: `{report['policyId']}` · rotation: `{report['rotationVersion']}`",
|
||||
"",
|
||||
f"**{summary['partyConfigurations']} party configurations**, **{summary['bosses']} bosses**, "
|
||||
f"and **{summary['runs']} simulations** at level **{report['level']}**.",
|
||||
"",
|
||||
@@ -938,7 +948,7 @@ input {{ width:min(420px,90%); padding:9px 12px; margin:0 0 12px; color:inherit;
|
||||
th,td {{ padding:8px 10px; border-bottom:1px solid #21262d; text-align:right; white-space:nowrap; }} th {{ position:sticky; top:0; cursor:pointer; background:#161b22; }}
|
||||
th:first-child,td:first-child,th:nth-child(2),td:nth-child(2),th:nth-child(3),td:nth-child(3) {{ text-align:left; }} tr:hover {{ background:#161b22; }}
|
||||
</style></head><body><h1>Healer Man Automatic Balance Suite</h1>
|
||||
<p class="summary">{html.escape(report['generatedAt'])} · level {report['level']} · {summary['partyConfigurations']} parties · {summary['bosses']} bosses · {summary['runs']} simulations. Every row is retained; click headings to sort or filter each table.</p>
|
||||
<p class="summary">{html.escape(report['generatedAt'])} · policy {html.escape(report['policyId'])} · rotation {html.escape(report['rotationVersion'])} · level {report['level']} · {summary['partyConfigurations']} parties · {summary['bosses']} bosses · {summary['runs']} simulations. Every row is retained; click headings to sort or filter each table.</p>
|
||||
{class_table}{ability_table}{party_table}{boss_table}
|
||||
<script>
|
||||
function filterTable(id,q){{q=q.toLowerCase();document.querySelectorAll('#'+id+' tbody tr').forEach(r=>r.hidden=!r.textContent.toLowerCase().includes(q));}}
|
||||
@@ -953,13 +963,19 @@ def create_suite_reports(entries: Sequence[dict[str, Any]], output_dir: Path, le
|
||||
class_groups: dict[tuple[str, ...], list[dict[str, Any]]] = defaultdict(list)
|
||||
class_boss_groups: dict[tuple[str, ...], list[dict[str, Any]]] = defaultdict(list)
|
||||
class_ability_groups: dict[tuple[str, ...], list[dict[str, Any]]] = defaultdict(list)
|
||||
policy_ids: set[str] = set()
|
||||
rotation_versions: set[str] = set()
|
||||
for entry in entries:
|
||||
raw = json.loads(Path(entry["rawPath"]).read_text(encoding="utf-8"))
|
||||
policy_ids.add(raw["policyId"])
|
||||
rotation_versions.add(raw["rotationVersion"])
|
||||
report = entry["report"]
|
||||
runs = raw["runs"]
|
||||
compact_runs.extend({
|
||||
"partyConfigurationId": entry["id"],
|
||||
"partySource": entry["source"],
|
||||
"policyId": raw["policyId"],
|
||||
"rotationVersion": raw["rotationVersion"],
|
||||
**{key: value for key, value in run.items() if key not in {"members", "bossAbilities"}},
|
||||
} for run in runs)
|
||||
defeated = [run for run in runs if run["outcome"] == "defeated"]
|
||||
@@ -1057,10 +1073,14 @@ def create_suite_reports(entries: Sequence[dict[str, Any]], output_dir: Path, le
|
||||
))
|
||||
party_rows.sort(key=lambda row: (row["source"], row["partyConfigurationId"]))
|
||||
boss_by_party.sort(key=lambda row: (row["partyConfigurationId"], -row["incomingDtps"], row["bossName"]))
|
||||
if len(policy_ids) != 1 or len(rotation_versions) != 1:
|
||||
raise RuntimeError("Balance Lab suite mixed combat policy or rotation versions")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": datetime.now().astimezone().isoformat(),
|
||||
"engine": "live-typescript-runtime",
|
||||
"policyId": next(iter(policy_ids)),
|
||||
"rotationVersion": next(iter(rotation_versions)),
|
||||
"level": level,
|
||||
"summary": {
|
||||
"partyConfigurations": len(entries),
|
||||
@@ -1113,7 +1133,7 @@ def create_suite_reports(entries: Sequence[dict[str, Any]], output_dir: Path, le
|
||||
"totalDamageTaken", "deathEvents",
|
||||
))
|
||||
write_csv(output_dir / "suite_runs.csv", compact_runs, (
|
||||
"partyConfigurationId", "partySource", "bossKey", "bossName", "bossCategory", "dungeonId",
|
||||
"partyConfigurationId", "partySource", "policyId", "rotationVersion", "bossKey", "bossName", "bossCategory", "dungeonId",
|
||||
"dungeonTitle", "seed", "outcome",
|
||||
"elapsedSeconds", "timeToDefeatSeconds", "bossLevel", "bossMaxHealth", "bossRemainingHealth",
|
||||
"partyDamage", "bossDamage", "partyDps", "bossDpsTaken", "incomingDamage", "rawIncomingDamage",
|
||||
@@ -1223,6 +1243,8 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
if automatic:
|
||||
write_json(output_dir / "suite-config.json", {
|
||||
"schemaVersion": 1,
|
||||
"policyId": catalog["policyId"],
|
||||
"rotationVersion": catalog["rotationVersion"],
|
||||
"mode": "automatic",
|
||||
"source": args.source,
|
||||
"level": args.level,
|
||||
|
||||
Reference in new issue
Block a user