diff --git a/SAC_LSTM_Bot/docs/graph_eval_winrates.svg b/SAC_LSTM_Bot/docs/graph_eval_winrates.svg
index 109a9cd..e2ae9f2 100644
--- a/SAC_LSTM_Bot/docs/graph_eval_winrates.svg
+++ b/SAC_LSTM_Bot/docs/graph_eval_winrates.svg
@@ -1,45 +1,314 @@
-
+\n')
+ bm = bucket_means(v1, BUCKETS)
+ if bm:
+ fb = [xf((i + 0.5) / len(bm)) for i in range(len(bm))]
+ s += polyline(list(zip(fb, map(ymb, bm))), V1_COLOR, 1.5)
+ items_b.append((V1_COLOR, f"Run 1 (old) — {len(v1)} evals in {len(bm)} buckets"))
+ c3 = series_v2.get("Corners", [])
+ if c3:
+ rm = rolling(c3, TREND_WINDOW)
+ fr = [xf((i + 1) / len(rm)) for i in range(len(rm))]
+ s += polyline(list(zip(fr, map(ymb, rm))), COLORS["Corners"], 3.5)
+ items_b.append((COLORS["Corners"], f"Run 3 trend (rolling-{TREND_WINDOW})"))
+ s += legend(items_b, x0 + 12, y1b + 14)
+
out.write_text(s)
return 1
@@ -278,6 +338,10 @@ def selftest():
assert ser == {"Corners": [30.0, 50.0], "Crazy": [70.0]}, ser
assert rolling([10] * 25, 20)[-1] == 10.0
assert rolling([1, 2, 3], 20) == [1.0, 1.5, 2.0]
+ assert bucket_means(list(range(1287)), BUCKETS) is not None
+ assert len(bucket_means(list(range(1287)), BUCKETS)) == BUCKETS
+ bm = bucket_means([0, 10], BUCKETS)
+ assert bm == [0.0, 10.0], bm # fewer points than buckets -> no empty buckets
(td / "m.jsonl").write_text(
'{"critic_loss": 10, "actor_loss": -2, "alpha": 0.5}\n'
"not json\n"
@@ -286,6 +350,11 @@ def selftest():
assert len(rows) == 2 and rows[1]["critic_loss"] == 100
ok = graph_losses(rows, td / "g2.svg") and graph_eval(ser, {"Corners": [0, 10]}, td / "g1.svg")
assert ok and (td / "g1.svg").stat().st_size > 500
+ g1 = (td / "g1.svg").read_text()
+ assert 'width="1200"' in g1, "canvas must be >=1200 wide"
+ assert g1.count("= 2) + 2 # trends + v1 buckets + v3 trend
+ assert g1.count("