fix(dashboard): dual y-axis for losses+alpha panel

This commit is contained in:
2026-08-24 09:21:43 +02:00
parent 4a6e3347c6
commit 55d69a9352
2 changed files with 319 additions and 314 deletions
+272 -260
View File
@@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="2520" viewBox="0 0 1400 2520" font-family="sans-serif">
<rect width="1400" height="2520" fill="white"/>
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="1720" viewBox="0 0 1400 1720" font-family="sans-serif">
<rect width="1400" height="1720" fill="white"/>
<text x="700" y="32" text-anchor="middle" font-size="21" font-weight="bold">SAC-LSTM campaign dashboard - live run (current only)</text>
<text x="700" y="56" text-anchor="middle" font-size="12" fill="#555">generated 2026-08-24 09:14:55 - auto-reloads every 60 s (open this file in Chrome)</text>
<text x="700" y="56" text-anchor="middle" font-size="12" fill="#555">generated 2026-08-24 09:21:32 - auto-reloads every 60 s (open this file in Chrome)</text>
<text x="70" y="100" font-size="15" font-weight="bold">Test matches - win % vs opponents</text>
<text x="70" y="115" font-size="11" fill="#555">raw dots = single test matches, thick = rolling-mean-10</text>
<line x1="70" y1="720.0" x2="697" y2="720.0" stroke="#dddddd"/>
@@ -14,28 +14,32 @@
<line x1="70" y1="720" x2="70" y2="120" stroke="black"/>
<line x1="70.0" y1="720" x2="70.0" y2="724" stroke="black"/>
<text x="70.0" y="737" text-anchor="middle" font-size="11">1</text>
<line x1="127.0" y1="720" x2="127.0" y2="724" stroke="black"/>
<text x="127.0" y="737" text-anchor="middle" font-size="11">2</text>
<line x1="184.0" y1="720" x2="184.0" y2="724" stroke="black"/>
<text x="184.0" y="737" text-anchor="middle" font-size="11">3</text>
<line x1="241.0" y1="720" x2="241.0" y2="724" stroke="black"/>
<text x="241.0" y="737" text-anchor="middle" font-size="11">4</text>
<line x1="298.0" y1="720" x2="298.0" y2="724" stroke="black"/>
<text x="298.0" y="737" text-anchor="middle" font-size="11">5</text>
<line x1="355.0" y1="720" x2="355.0" y2="724" stroke="black"/>
<text x="355.0" y="737" text-anchor="middle" font-size="11">6</text>
<line x1="412.0" y1="720" x2="412.0" y2="724" stroke="black"/>
<text x="412.0" y="737" text-anchor="middle" font-size="11">7</text>
<line x1="469.0" y1="720" x2="469.0" y2="724" stroke="black"/>
<text x="469.0" y="737" text-anchor="middle" font-size="11">8</text>
<line x1="526.0" y1="720" x2="526.0" y2="724" stroke="black"/>
<text x="526.0" y="737" text-anchor="middle" font-size="11">9</text>
<line x1="583.0" y1="720" x2="583.0" y2="724" stroke="black"/>
<text x="583.0" y="737" text-anchor="middle" font-size="11">10</text>
<line x1="640.0" y1="720" x2="640.0" y2="724" stroke="black"/>
<text x="640.0" y="737" text-anchor="middle" font-size="11">11</text>
<line x1="118.2" y1="720" x2="118.2" y2="724" stroke="black"/>
<text x="118.2" y="737" text-anchor="middle" font-size="11">2</text>
<line x1="166.5" y1="720" x2="166.5" y2="724" stroke="black"/>
<text x="166.5" y="737" text-anchor="middle" font-size="11">3</text>
<line x1="214.7" y1="720" x2="214.7" y2="724" stroke="black"/>
<text x="214.7" y="737" text-anchor="middle" font-size="11">4</text>
<line x1="262.9" y1="720" x2="262.9" y2="724" stroke="black"/>
<text x="262.9" y="737" text-anchor="middle" font-size="11">5</text>
<line x1="311.2" y1="720" x2="311.2" y2="724" stroke="black"/>
<text x="311.2" y="737" text-anchor="middle" font-size="11">6</text>
<line x1="359.4" y1="720" x2="359.4" y2="724" stroke="black"/>
<text x="359.4" y="737" text-anchor="middle" font-size="11">7</text>
<line x1="407.6" y1="720" x2="407.6" y2="724" stroke="black"/>
<text x="407.6" y="737" text-anchor="middle" font-size="11">8</text>
<line x1="455.8" y1="720" x2="455.8" y2="724" stroke="black"/>
<text x="455.8" y="737" text-anchor="middle" font-size="11">9</text>
<line x1="504.1" y1="720" x2="504.1" y2="724" stroke="black"/>
<text x="504.1" y="737" text-anchor="middle" font-size="11">10</text>
<line x1="552.3" y1="720" x2="552.3" y2="724" stroke="black"/>
<text x="552.3" y="737" text-anchor="middle" font-size="11">11</text>
<line x1="600.5" y1="720" x2="600.5" y2="724" stroke="black"/>
<text x="600.5" y="737" text-anchor="middle" font-size="11">12</text>
<line x1="648.8" y1="720" x2="648.8" y2="724" stroke="black"/>
<text x="648.8" y="737" text-anchor="middle" font-size="11">13</text>
<line x1="697.0" y1="720" x2="697.0" y2="724" stroke="black"/>
<text x="697.0" y="737" text-anchor="middle" font-size="11">12</text>
<text x="697.0" y="737" text-anchor="middle" font-size="11">14</text>
<line x1="66" y1="720.0" x2="70" y2="720.0" stroke="black"/>
<text x="63" y="724.0" text-anchor="end" font-size="11">0</text>
<line x1="66" y1="600.0" x2="70" y2="600.0" stroke="black"/>
@@ -51,69 +55,75 @@
<text x="383" y="753" text-anchor="middle" font-size="12">test match number (each opponent)</text>
<text x="16" y="420" text-anchor="middle" font-size="12" transform="rotate(-90 16 420)">win rate (%)</text>
<circle cx="70.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="127.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="184.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="241.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="298.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="355.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="412.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="469.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="526.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="583.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="640.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="697.0" cy="660.0" r="2" fill="#d62728" opacity="0.25"/>
<polyline points="70.0,720.0 127.0,720.0 184.0,720.0 241.0,720.0 298.0,720.0 355.0,720.0 412.0,720.0 469.0,720.0 526.0,720.0 583.0,720.0 640.0,720.0 697.0,714.0" fill="none" stroke="#d62728" stroke-width="3.5" opacity="1.0"/>
<circle cx="118.2" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="166.5" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="214.7" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="262.9" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="311.2" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="359.4" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="407.6" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="455.8" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="504.1" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="552.3" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="600.5" cy="660.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="648.8" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="697.0" cy="720.0" r="2" fill="#d62728" opacity="0.25"/>
<polyline points="70.0,720.0 118.2,720.0 166.5,720.0 214.7,720.0 262.9,720.0 311.2,720.0 359.4,720.0 407.6,720.0 455.8,720.0 504.1,720.0 552.3,720.0 600.5,714.0 648.8,714.0 697.0,714.0" fill="none" stroke="#d62728" stroke-width="3.5" opacity="1.0"/>
<circle cx="70.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="127.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="184.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="241.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="298.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="355.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="412.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="469.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="526.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="583.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="640.0" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="697.0" cy="480.0" r="2" fill="#1f77b4" opacity="0.25"/>
<polyline points="70.0,720.0 127.0,720.0 184.0,720.0 241.0,720.0 298.0,720.0 355.0,720.0 412.0,720.0 469.0,720.0 526.0,720.0 583.0,720.0 640.0,720.0 697.0,696.0" fill="none" stroke="#1f77b4" stroke-width="3.5" opacity="1.0"/>
<circle cx="118.2" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="166.5" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="214.7" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="262.9" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="311.2" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="359.4" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="407.6" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="455.8" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="504.1" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="552.3" cy="720.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="600.5" cy="480.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="648.8" cy="240.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="697.0" cy="660.0" r="2" fill="#1f77b4" opacity="0.25"/>
<polyline points="70.0,720.0 118.2,720.0 166.5,720.0 214.7,720.0 262.9,720.0 311.2,720.0 359.4,720.0 407.6,720.0 455.8,720.0 504.1,720.0 552.3,720.0 600.5,696.0 648.8,648.0 697.0,642.0" fill="none" stroke="#1f77b4" stroke-width="3.5" opacity="1.0"/>
<circle cx="70.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="127.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="184.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="241.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="298.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="355.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="412.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="469.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="526.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="583.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="640.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="697.0" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<polyline points="70.0,720.0 127.0,720.0 184.0,720.0 241.0,720.0 298.0,720.0 355.0,720.0 412.0,720.0 469.0,720.0 526.0,720.0 583.0,720.0 640.0,720.0 697.0,720.0" fill="none" stroke="#2ca02c" stroke-width="3.5" opacity="1.0"/>
<circle cx="118.2" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="166.5" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="214.7" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="262.9" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="311.2" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="359.4" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="407.6" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="455.8" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="504.1" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="552.3" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="600.5" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="648.8" cy="720.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="697.0" cy="660.0" r="2" fill="#2ca02c" opacity="0.25"/>
<polyline points="70.0,720.0 118.2,720.0 166.5,720.0 214.7,720.0 262.9,720.0 311.2,720.0 359.4,720.0 407.6,720.0 455.8,720.0 504.1,720.0 552.3,720.0 600.5,720.0 648.8,720.0 697.0,714.0" fill="none" stroke="#2ca02c" stroke-width="3.5" opacity="1.0"/>
<line x1="82" y1="772" x2="110" y2="772" stroke="#d62728" stroke-width="3"/>
<text x="116" y="776" font-size="12">Corners - 12 evals</text>
<text x="116" y="776" font-size="12">Corners - 14 evals</text>
<line x1="82" y1="790" x2="110" y2="790" stroke="#1f77b4" stroke-width="3"/>
<text x="116" y="794" font-size="12">Crazy - 12 evals</text>
<text x="116" y="794" font-size="12">Crazy - 14 evals</text>
<line x1="82" y1="808" x2="110" y2="808" stroke="#2ca02c" stroke-width="3"/>
<text x="116" y="812" font-size="12">Target - 12 evals</text>
<text x="747" y="100" font-size="15" font-weight="bold">Training losses &amp; alpha (log scale)</text>
<text x="116" y="812" font-size="12">Target - 14 evals</text>
<text x="747" y="100" font-size="15" font-weight="bold">Training losses (log) &amp; alpha (linear)</text>
<text x="747" y="115" font-size="11" fill="#555">training_metrics.jsonl - big early spikes are normal</text>
<line x1="747" y1="720.0" x2="1375" y2="720.0" stroke="#dddddd"/>
<line x1="747" y1="570.0" x2="1375" y2="570.0" stroke="#dddddd"/>
<line x1="747" y1="420.0" x2="1375" y2="420.0" stroke="#dddddd"/>
<line x1="747" y1="270.0" x2="1375" y2="270.0" stroke="#dddddd"/>
<line x1="747" y1="120.0" x2="1375" y2="120.0" stroke="#dddddd"/>
<line x1="747" y1="720" x2="1375" y2="720" stroke="black"/>
<line x1="747" y1="720.0" x2="1329" y2="720.0" stroke="#dddddd"/>
<line x1="747" y1="570.0" x2="1329" y2="570.0" stroke="#dddddd"/>
<line x1="747" y1="420.0" x2="1329" y2="420.0" stroke="#dddddd"/>
<line x1="747" y1="270.0" x2="1329" y2="270.0" stroke="#dddddd"/>
<line x1="747" y1="120.0" x2="1329" y2="120.0" stroke="#dddddd"/>
<line x1="747" y1="720" x2="1329" y2="720" stroke="black"/>
<line x1="747" y1="720" x2="747" y2="120" stroke="black"/>
<line x1="747.0" y1="720" x2="747.0" y2="724" stroke="black"/>
<text x="747.0" y="737" text-anchor="middle" font-size="11">1</text>
<line x1="904.0" y1="720" x2="904.0" y2="724" stroke="black"/>
<text x="904.0" y="737" text-anchor="middle" font-size="11">7</text>
<line x1="1061.0" y1="720" x2="1061.0" y2="724" stroke="black"/>
<text x="1061.0" y="737" text-anchor="middle" font-size="11">14</text>
<line x1="1218.0" y1="720" x2="1218.0" y2="724" stroke="black"/>
<text x="1218.0" y="737" text-anchor="middle" font-size="11">20</text>
<line x1="1375.0" y1="720" x2="1375.0" y2="724" stroke="black"/>
<text x="1375.0" y="737" text-anchor="middle" font-size="11">26</text>
<line x1="892.5" y1="720" x2="892.5" y2="724" stroke="black"/>
<text x="892.5" y="737" text-anchor="middle" font-size="11">8</text>
<line x1="1038.0" y1="720" x2="1038.0" y2="724" stroke="black"/>
<text x="1038.0" y="737" text-anchor="middle" font-size="11">16</text>
<line x1="1183.5" y1="720" x2="1183.5" y2="724" stroke="black"/>
<text x="1183.5" y="737" text-anchor="middle" font-size="11">23</text>
<line x1="1329.0" y1="720" x2="1329.0" y2="724" stroke="black"/>
<text x="1329.0" y="737" text-anchor="middle" font-size="11">30</text>
<line x1="743" y1="720.0" x2="747" y2="720.0" stroke="black"/>
<text x="740" y="724.0" text-anchor="end" font-size="11">0.01</text>
<line x1="743" y1="570.0" x2="747" y2="570.0" stroke="black"/>
@@ -124,19 +134,36 @@
<text x="740" y="274.0" text-anchor="end" font-size="11">10</text>
<line x1="743" y1="120.0" x2="747" y2="120.0" stroke="black"/>
<text x="740" y="124.0" text-anchor="end" font-size="11">100</text>
<text x="1061" y="753" text-anchor="middle" font-size="12">metric line number</text>
<text x="1038" y="753" text-anchor="middle" font-size="12">metric line number</text>
<text x="16" y="420" text-anchor="middle" font-size="12" transform="rotate(-90 16 420)">loss (log)</text>
<polyline points="747.0,281.9 772.1,253.6 797.2,243.9 822.4,213.5 847.5,207.6 872.6,386.8 897.7,424.4 922.8,307.1 948.0,293.6 973.1,282.6 998.2,242.7 1023.3,171.4 1048.4,158.6 1073.6,169.7 1098.7,191.2 1123.8,253.6 1148.9,327.8 1174.0,625.2 1199.2,506.4 1224.3,374.2 1249.4,425.9 1274.5,351.1 1299.6,357.9 1324.8,330.0 1349.9,251.4 1375.0,339.4" fill="none" stroke="#1f77b4" stroke-width="1.8" opacity="1.0"/>
<polyline points="747.0,348.9 772.1,307.9 797.2,284.5 822.4,261.3 847.5,244.2 872.6,235.2 897.7,236.5 922.8,241.4 948.0,270.7 973.1,292.4 998.2,378.9 1023.3,302.9 1048.4,277.7 1073.6,264.4 1098.7,254.8 1123.8,246.5 1148.9,242.0 1174.0,251.8 1199.2,262.0 1224.3,273.4 1249.4,295.3 1274.5,327.5 1299.6,335.9 1324.8,438.0 1349.9,400.5 1375.0,500.9" fill="none" stroke="#ff7f0e" stroke-width="1.8" opacity="1.0"/>
<polyline points="747.0,420.0 772.1,420.1 797.2,420.2 822.4,420.3 847.5,420.3 872.6,420.4 897.7,420.5 922.8,420.5 948.0,420.6 973.1,420.6 998.2,420.7 1023.3,420.7 1048.4,420.7 1073.6,420.6 1098.7,420.5 1123.8,420.4 1148.9,420.4 1174.0,420.3 1199.2,420.2 1224.3,420.1 1249.4,420.2 1274.5,420.3 1299.6,420.3 1324.8,420.4 1349.9,420.5 1375.0,420.7" fill="none" stroke="#9467bd" stroke-width="1.8" opacity="1.0"/>
<polyline points="747.0,281.9 767.1,253.6 787.1,243.9 807.2,213.5 827.3,207.6 847.3,386.8 867.4,424.4 887.5,307.1 907.6,293.6 927.6,282.6 947.7,242.7 967.8,171.4 987.8,158.6 1007.9,169.7 1028.0,191.2 1048.0,253.6 1068.1,327.8 1088.2,625.2 1108.2,506.4 1128.3,374.2 1148.4,425.9 1168.4,351.1 1188.5,357.9 1208.6,330.0 1228.7,251.4 1248.7,339.4 1268.8,331.4 1288.9,283.3 1308.9,320.4 1329.0,339.4" fill="none" stroke="#1f77b4" stroke-width="1.8" opacity="1.0"/>
<polyline points="747.0,348.9 767.1,307.9 787.1,284.5 807.2,261.3 827.3,244.2 847.3,235.2 867.4,236.5 887.5,241.4 907.6,270.7 927.6,292.4 947.7,378.9 967.8,302.9 987.8,277.7 1007.9,264.4 1028.0,254.8 1048.0,246.5 1068.1,242.0 1088.2,251.8 1108.2,262.0 1128.3,273.4 1148.4,295.3 1168.4,327.5 1188.5,335.9 1208.6,438.0 1228.7,400.5 1248.7,500.9 1268.8,390.5 1288.9,329.2 1308.9,316.2 1329.0,311.3" fill="none" stroke="#ff7f0e" stroke-width="1.8" opacity="1.0"/>
<line x1="747" y1="720.0" x2="1329" y2="720.0" stroke="#e9dcf5"/>
<line x1="747" y1="570.0" x2="1329" y2="570.0" stroke="#e9dcf5"/>
<line x1="747" y1="420.0" x2="1329" y2="420.0" stroke="#e9dcf5"/>
<line x1="747" y1="270.0" x2="1329" y2="270.0" stroke="#e9dcf5"/>
<line x1="747" y1="120.0" x2="1329" y2="120.0" stroke="#e9dcf5"/>
<line x1="1329" y1="720" x2="1329" y2="120" stroke="#9467bd"/>
<line x1="1329" y1="720.0" x2="1333" y2="720.0" stroke="#9467bd"/>
<text x="1336" y="724.0" font-size="11" fill="#9467bd">0</text>
<line x1="1329" y1="570.0" x2="1333" y2="570.0" stroke="#9467bd"/>
<text x="1336" y="574.0" font-size="11" fill="#9467bd">0.25</text>
<line x1="1329" y1="420.0" x2="1333" y2="420.0" stroke="#9467bd"/>
<text x="1336" y="424.0" font-size="11" fill="#9467bd">0.5</text>
<line x1="1329" y1="270.0" x2="1333" y2="270.0" stroke="#9467bd"/>
<text x="1336" y="274.0" font-size="11" fill="#9467bd">0.75</text>
<line x1="1329" y1="120.0" x2="1333" y2="120.0" stroke="#9467bd"/>
<text x="1336" y="124.0" font-size="11" fill="#9467bd">1</text>
<text x="1346" y="420" text-anchor="middle" font-size="12" fill="#9467bd" transform="rotate(90 1346 420)">alpha</text>
<polyline points="747.0,120.2 767.1,120.9 787.1,121.6 807.2,122.3 827.3,123.1 847.3,123.8 867.4,124.1 887.5,124.5 907.6,125.2 927.6,125.6 947.7,126.3 967.8,126.9 987.8,126.1 1007.9,125.4 1028.0,124.7 1048.0,124.0 1068.1,123.3 1088.2,122.6 1108.2,121.9 1128.3,121.2 1148.4,121.7 1168.4,122.4 1188.5,123.2 1208.6,123.9 1228.7,124.6 1248.7,126.0 1268.8,126.7 1288.9,127.4 1308.9,128.1 1329.0,128.5" fill="none" stroke="#9467bd" stroke-width="1.8" opacity="1.0"/>
<line x1="759" y1="772" x2="787" y2="772" stroke="#1f77b4" stroke-width="3"/>
<text x="793" y="776" font-size="12">critic_loss</text>
<line x1="759" y1="790" x2="787" y2="790" stroke="#ff7f0e" stroke-width="3"/>
<text x="793" y="794" font-size="12">|actor_loss|</text>
<line x1="759" y1="808" x2="787" y2="808" stroke="#9467bd" stroke-width="3"/>
<text x="793" y="812" font-size="12">alpha</text>
<text x="70" y="940" font-size="15" font-weight="bold">Alpha temperature</text>
<text x="70" y="955" font-size="11" fill="#555">training_metrics.jsonl - high = exploring, low = exploiting</text>
<text x="70" y="940" font-size="15" font-weight="bold">Throughput - games per hour</text>
<text x="70" y="955" font-size="11" fill="#555">method: training_metrics.jsonl 'epoch' deltas; 1 row = one 10-game chunk</text>
<line x1="70" y1="1428.0" x2="697" y2="1428.0" stroke="#dddddd"/>
<line x1="70" y1="1310.5" x2="697" y2="1310.5" stroke="#dddddd"/>
<line x1="70" y1="1193.0" x2="697" y2="1193.0" stroke="#dddddd"/>
@@ -144,31 +171,78 @@
<line x1="70" y1="958.0" x2="697" y2="958.0" stroke="#dddddd"/>
<line x1="70" y1="1428" x2="697" y2="1428" stroke="black"/>
<line x1="70" y1="1428" x2="70" y2="958" stroke="black"/>
<line x1="70.0" y1="1428" x2="70.0" y2="1432" stroke="black"/>
<text x="70.0" y="1445" text-anchor="middle" font-size="11">1</text>
<line x1="92.4" y1="1428" x2="92.4" y2="1432" stroke="black"/>
<text x="92.4" y="1445" text-anchor="middle" font-size="11">2</text>
<line x1="137.2" y1="1428" x2="137.2" y2="1432" stroke="black"/>
<text x="137.2" y="1445" text-anchor="middle" font-size="11">4</text>
<line x1="182.0" y1="1428" x2="182.0" y2="1432" stroke="black"/>
<text x="182.0" y="1445" text-anchor="middle" font-size="11">6</text>
<line x1="226.8" y1="1428" x2="226.8" y2="1432" stroke="black"/>
<text x="226.8" y="1445" text-anchor="middle" font-size="11">7</text>
<line x1="383.5" y1="1428" x2="383.5" y2="1432" stroke="black"/>
<text x="383.5" y="1445" text-anchor="middle" font-size="11">14</text>
<text x="226.8" y="1445" text-anchor="middle" font-size="11">8</text>
<line x1="271.5" y1="1428" x2="271.5" y2="1432" stroke="black"/>
<text x="271.5" y="1445" text-anchor="middle" font-size="11">10</text>
<line x1="316.3" y1="1428" x2="316.3" y2="1432" stroke="black"/>
<text x="316.3" y="1445" text-anchor="middle" font-size="11">12</text>
<line x1="361.1" y1="1428" x2="361.1" y2="1432" stroke="black"/>
<text x="361.1" y="1445" text-anchor="middle" font-size="11">14</text>
<line x1="405.9" y1="1428" x2="405.9" y2="1432" stroke="black"/>
<text x="405.9" y="1445" text-anchor="middle" font-size="11">16</text>
<line x1="450.7" y1="1428" x2="450.7" y2="1432" stroke="black"/>
<text x="450.7" y="1445" text-anchor="middle" font-size="11">18</text>
<line x1="495.5" y1="1428" x2="495.5" y2="1432" stroke="black"/>
<text x="495.5" y="1445" text-anchor="middle" font-size="11">20</text>
<line x1="540.2" y1="1428" x2="540.2" y2="1432" stroke="black"/>
<text x="540.2" y="1445" text-anchor="middle" font-size="11">20</text>
<line x1="697.0" y1="1428" x2="697.0" y2="1432" stroke="black"/>
<text x="697.0" y="1445" text-anchor="middle" font-size="11">26</text>
<text x="540.2" y="1445" text-anchor="middle" font-size="11">22</text>
<line x1="585.0" y1="1428" x2="585.0" y2="1432" stroke="black"/>
<text x="585.0" y="1445" text-anchor="middle" font-size="11">24</text>
<line x1="629.8" y1="1428" x2="629.8" y2="1432" stroke="black"/>
<text x="629.8" y="1445" text-anchor="middle" font-size="11">26</text>
<line x1="674.6" y1="1428" x2="674.6" y2="1432" stroke="black"/>
<text x="674.6" y="1445" text-anchor="middle" font-size="11">28</text>
<line x1="66" y1="1428.0" x2="70" y2="1428.0" stroke="black"/>
<text x="63" y="1432.0" text-anchor="end" font-size="11">0</text>
<line x1="66" y1="1310.5" x2="70" y2="1310.5" stroke="black"/>
<text x="63" y="1314.5" text-anchor="end" font-size="11">0.25</text>
<text x="63" y="1314.5" text-anchor="end" font-size="11">1653</text>
<line x1="66" y1="1193.0" x2="70" y2="1193.0" stroke="black"/>
<text x="63" y="1197.0" text-anchor="end" font-size="11">0.5</text>
<text x="63" y="1197.0" text-anchor="end" font-size="11">3305</text>
<line x1="66" y1="1075.5" x2="70" y2="1075.5" stroke="black"/>
<text x="63" y="1079.5" text-anchor="end" font-size="11">0.75</text>
<text x="63" y="1079.5" text-anchor="end" font-size="11">4958</text>
<line x1="66" y1="958.0" x2="70" y2="958.0" stroke="black"/>
<text x="63" y="962.0" text-anchor="end" font-size="11">1</text>
<text x="383" y="1461" text-anchor="middle" font-size="12">metric line number</text>
<text x="16" y="1193" text-anchor="middle" font-size="12" transform="rotate(-90 16 1193)">alpha</text>
<polyline points="70.0,958.1 95.1,958.7 120.2,959.3 145.2,959.8 170.3,960.4 195.4,961.0 220.5,961.2 245.6,961.5 270.6,962.1 295.7,962.4 320.8,962.9 345.9,963.4 371.0,962.8 396.0,962.3 421.1,961.7 446.2,961.1 471.3,960.6 496.4,960.0 521.4,959.5 546.5,958.9 571.6,959.3 596.7,959.9 621.8,960.5 646.8,961.0 671.9,961.6 697.0,962.7" fill="none" stroke="#9467bd" stroke-width="1.8" opacity="1.0"/>
<text x="747" y="940" font-size="15" font-weight="bold">Throughput - games per hour</text>
<text x="747" y="955" font-size="11" fill="#555">method: training_metrics.jsonl 'epoch' deltas; 1 row = one 10-game chunk</text>
<text x="63" y="962.0" text-anchor="end" font-size="11">6611</text>
<text x="383" y="1461" text-anchor="middle" font-size="12">chunk interval (10-game chunks)</text>
<text x="16" y="1193" text-anchor="middle" font-size="12" transform="rotate(-90 16 1193)">games / hour</text>
<circle cx="70.0" cy="1159.8" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="92.4" cy="1413.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="114.8" cy="1148.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="137.2" cy="1404.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="159.6" cy="1153.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="182.0" cy="1399.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="204.4" cy="1000.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="226.8" cy="1399.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="249.1" cy="1021.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="271.5" cy="1394.8" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="293.9" cy="1376.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="316.3" cy="1394.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="338.7" cy="1130.1" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="361.1" cy="1399.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="383.5" cy="1176.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="405.9" cy="1402.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="428.3" cy="1158.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="450.7" cy="1400.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="473.1" cy="1149.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="495.5" cy="1400.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="517.9" cy="1171.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="540.2" cy="1405.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="562.6" cy="1162.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="585.0" cy="1416.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="607.4" cy="1375.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="629.8" cy="1415.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="652.2" cy="1162.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="674.6" cy="1415.1" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="697.0" cy="1075.1" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<polyline points="70.0,1286.6 92.4,1276.3 137.2,1276.6 159.6,1200.1 204.4,1208.1 226.8,1385.3 249.1,1264.9 293.9,1289.2 316.3,1279.0 361.1,1149.2 383.5,1400.6 428.3,1171.7 450.7,1405.2 495.5,1162.2 517.9,1416.5 540.2,1375.5 585.0,1415.7 607.4,1162.0 652.2,1415.1 674.6,1075.1" fill="none" stroke="#2ca02c" stroke-width="3.5" opacity="1.0"/>
<text x="747" y="940" font-size="15" font-weight="bold">Max score per eval cycle</text>
<text x="747" y="955" font-size="11" fill="#555">campaign_v4_stdout.log - max of the 10 deterministic round scores per eval</text>
<line x1="747" y1="1428.0" x2="1375" y2="1428.0" stroke="#dddddd"/>
<line x1="747" y1="1310.5" x2="1375" y2="1310.5" stroke="#dddddd"/>
<line x1="747" y1="1193.0" x2="1375" y2="1193.0" stroke="#dddddd"/>
@@ -176,170 +250,108 @@
<line x1="747" y1="958.0" x2="1375" y2="958.0" stroke="#dddddd"/>
<line x1="747" y1="1428" x2="1375" y2="1428" stroke="black"/>
<line x1="747" y1="1428" x2="747" y2="958" stroke="black"/>
<line x1="773.2" y1="1428" x2="773.2" y2="1432" stroke="black"/>
<text x="773.2" y="1445" text-anchor="middle" font-size="11">2</text>
<line x1="825.5" y1="1428" x2="825.5" y2="1432" stroke="black"/>
<text x="825.5" y="1445" text-anchor="middle" font-size="11">4</text>
<line x1="877.8" y1="1428" x2="877.8" y2="1432" stroke="black"/>
<text x="877.8" y="1445" text-anchor="middle" font-size="11">6</text>
<line x1="930.2" y1="1428" x2="930.2" y2="1432" stroke="black"/>
<text x="930.2" y="1445" text-anchor="middle" font-size="11">8</text>
<line x1="982.5" y1="1428" x2="982.5" y2="1432" stroke="black"/>
<text x="982.5" y="1445" text-anchor="middle" font-size="11">10</text>
<line x1="1034.8" y1="1428" x2="1034.8" y2="1432" stroke="black"/>
<text x="1034.8" y="1445" text-anchor="middle" font-size="11">12</text>
<line x1="1087.2" y1="1428" x2="1087.2" y2="1432" stroke="black"/>
<text x="1087.2" y="1445" text-anchor="middle" font-size="11">14</text>
<line x1="1139.5" y1="1428" x2="1139.5" y2="1432" stroke="black"/>
<text x="1139.5" y="1445" text-anchor="middle" font-size="11">16</text>
<line x1="1191.8" y1="1428" x2="1191.8" y2="1432" stroke="black"/>
<text x="1191.8" y="1445" text-anchor="middle" font-size="11">18</text>
<line x1="1244.2" y1="1428" x2="1244.2" y2="1432" stroke="black"/>
<text x="1244.2" y="1445" text-anchor="middle" font-size="11">20</text>
<line x1="1296.5" y1="1428" x2="1296.5" y2="1432" stroke="black"/>
<text x="1296.5" y="1445" text-anchor="middle" font-size="11">22</text>
<line x1="1348.8" y1="1428" x2="1348.8" y2="1432" stroke="black"/>
<text x="1348.8" y="1445" text-anchor="middle" font-size="11">24</text>
<line x1="747.0" y1="1428" x2="747.0" y2="1432" stroke="black"/>
<text x="747.0" y="1445" text-anchor="middle" font-size="11">1</text>
<line x1="795.3" y1="1428" x2="795.3" y2="1432" stroke="black"/>
<text x="795.3" y="1445" text-anchor="middle" font-size="11">2</text>
<line x1="843.6" y1="1428" x2="843.6" y2="1432" stroke="black"/>
<text x="843.6" y="1445" text-anchor="middle" font-size="11">3</text>
<line x1="891.9" y1="1428" x2="891.9" y2="1432" stroke="black"/>
<text x="891.9" y="1445" text-anchor="middle" font-size="11">4</text>
<line x1="940.2" y1="1428" x2="940.2" y2="1432" stroke="black"/>
<text x="940.2" y="1445" text-anchor="middle" font-size="11">5</text>
<line x1="988.5" y1="1428" x2="988.5" y2="1432" stroke="black"/>
<text x="988.5" y="1445" text-anchor="middle" font-size="11">6</text>
<line x1="1036.8" y1="1428" x2="1036.8" y2="1432" stroke="black"/>
<text x="1036.8" y="1445" text-anchor="middle" font-size="11">7</text>
<line x1="1085.2" y1="1428" x2="1085.2" y2="1432" stroke="black"/>
<text x="1085.2" y="1445" text-anchor="middle" font-size="11">8</text>
<line x1="1133.5" y1="1428" x2="1133.5" y2="1432" stroke="black"/>
<text x="1133.5" y="1445" text-anchor="middle" font-size="11">9</text>
<line x1="1181.8" y1="1428" x2="1181.8" y2="1432" stroke="black"/>
<text x="1181.8" y="1445" text-anchor="middle" font-size="11">10</text>
<line x1="1230.1" y1="1428" x2="1230.1" y2="1432" stroke="black"/>
<text x="1230.1" y="1445" text-anchor="middle" font-size="11">11</text>
<line x1="1278.4" y1="1428" x2="1278.4" y2="1432" stroke="black"/>
<text x="1278.4" y="1445" text-anchor="middle" font-size="11">12</text>
<line x1="1326.7" y1="1428" x2="1326.7" y2="1432" stroke="black"/>
<text x="1326.7" y="1445" text-anchor="middle" font-size="11">13</text>
<line x1="1375.0" y1="1428" x2="1375.0" y2="1432" stroke="black"/>
<text x="1375.0" y="1445" text-anchor="middle" font-size="11">14</text>
<line x1="743" y1="1428.0" x2="747" y2="1428.0" stroke="black"/>
<text x="740" y="1432.0" text-anchor="end" font-size="11">0</text>
<line x1="743" y1="1310.5" x2="747" y2="1310.5" stroke="black"/>
<text x="740" y="1314.5" text-anchor="end" font-size="11">1653</text>
<text x="740" y="1314.5" text-anchor="end" font-size="11">52</text>
<line x1="743" y1="1193.0" x2="747" y2="1193.0" stroke="black"/>
<text x="740" y="1197.0" text-anchor="end" font-size="11">3305</text>
<text x="740" y="1197.0" text-anchor="end" font-size="11">103</text>
<line x1="743" y1="1075.5" x2="747" y2="1075.5" stroke="black"/>
<text x="740" y="1079.5" text-anchor="end" font-size="11">4958</text>
<text x="740" y="1079.5" text-anchor="end" font-size="11">155</text>
<line x1="743" y1="958.0" x2="747" y2="958.0" stroke="black"/>
<text x="740" y="962.0" text-anchor="end" font-size="11">6611</text>
<text x="1061" y="1461" text-anchor="middle" font-size="12">chunk interval (10-game chunks)</text>
<text x="16" y="1193" text-anchor="middle" font-size="12" transform="rotate(-90 16 1193)">games / hour</text>
<circle cx="747.0" cy="1159.8" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="773.2" cy="1413.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="799.3" cy="1148.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="825.5" cy="1404.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="851.7" cy="1153.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="877.8" cy="1399.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="904.0" cy="1000.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="930.2" cy="1399.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="956.3" cy="1021.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="982.5" cy="1394.8" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1008.7" cy="1376.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1034.8" cy="1394.4" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1061.0" cy="1130.1" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1087.2" cy="1399.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1113.3" cy="1176.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1139.5" cy="1402.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1165.7" cy="1158.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1191.8" cy="1400.0" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1218.0" cy="1149.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1244.2" cy="1400.6" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1270.3" cy="1171.7" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1296.5" cy="1405.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1322.7" cy="1162.2" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1348.8" cy="1416.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<circle cx="1375.0" cy="1375.5" r="1.6" fill="#7f7f7f" opacity="0.25"/>
<polyline points="747.0,1286.6 773.2,1276.3 799.3,1276.6 825.5,1200.1 877.8,1208.1 904.0,1376.2 930.2,1394.4 956.3,1130.1 1008.7,1399.7 1034.8,1176.5 1061.0,1402.0 1087.2,1158.0 1139.5,1400.0 1165.7,1149.2 1191.8,1400.6 1218.0,1171.7 1270.3,1405.2 1296.5,1162.2 1322.7,1416.5 1348.8,1375.5" fill="none" stroke="#2ca02c" stroke-width="3.5" opacity="1.0"/>
<text x="70" y="1648" font-size="15" font-weight="bold">Max score per eval cycle</text>
<text x="70" y="1663" font-size="11" fill="#555">campaign_v4_stdout.log - max of the 10 deterministic round scores per eval</text>
<line x1="70" y1="2270.0" x2="697" y2="2270.0" stroke="#dddddd"/>
<line x1="70" y1="2119.0" x2="697" y2="2119.0" stroke="#dddddd"/>
<line x1="70" y1="1968.0" x2="697" y2="1968.0" stroke="#dddddd"/>
<line x1="70" y1="1817.0" x2="697" y2="1817.0" stroke="#dddddd"/>
<line x1="70" y1="1666.0" x2="697" y2="1666.0" stroke="#dddddd"/>
<line x1="70" y1="2270" x2="697" y2="2270" stroke="black"/>
<line x1="70" y1="2270" x2="70" y2="1666" stroke="black"/>
<line x1="70.0" y1="2270" x2="70.0" y2="2274" stroke="black"/>
<text x="70.0" y="2287" text-anchor="middle" font-size="11">1</text>
<line x1="127.0" y1="2270" x2="127.0" y2="2274" stroke="black"/>
<text x="127.0" y="2287" text-anchor="middle" font-size="11">2</text>
<line x1="184.0" y1="2270" x2="184.0" y2="2274" stroke="black"/>
<text x="184.0" y="2287" text-anchor="middle" font-size="11">3</text>
<line x1="241.0" y1="2270" x2="241.0" y2="2274" stroke="black"/>
<text x="241.0" y="2287" text-anchor="middle" font-size="11">4</text>
<line x1="298.0" y1="2270" x2="298.0" y2="2274" stroke="black"/>
<text x="298.0" y="2287" text-anchor="middle" font-size="11">5</text>
<line x1="355.0" y1="2270" x2="355.0" y2="2274" stroke="black"/>
<text x="355.0" y="2287" text-anchor="middle" font-size="11">6</text>
<line x1="412.0" y1="2270" x2="412.0" y2="2274" stroke="black"/>
<text x="412.0" y="2287" text-anchor="middle" font-size="11">7</text>
<line x1="469.0" y1="2270" x2="469.0" y2="2274" stroke="black"/>
<text x="469.0" y="2287" text-anchor="middle" font-size="11">8</text>
<line x1="526.0" y1="2270" x2="526.0" y2="2274" stroke="black"/>
<text x="526.0" y="2287" text-anchor="middle" font-size="11">9</text>
<line x1="583.0" y1="2270" x2="583.0" y2="2274" stroke="black"/>
<text x="583.0" y="2287" text-anchor="middle" font-size="11">10</text>
<line x1="640.0" y1="2270" x2="640.0" y2="2274" stroke="black"/>
<text x="640.0" y="2287" text-anchor="middle" font-size="11">11</text>
<line x1="697.0" y1="2270" x2="697.0" y2="2274" stroke="black"/>
<text x="697.0" y="2287" text-anchor="middle" font-size="11">12</text>
<line x1="66" y1="2270.0" x2="70" y2="2270.0" stroke="black"/>
<text x="63" y="2274.0" text-anchor="end" font-size="11">0</text>
<line x1="66" y1="2119.0" x2="70" y2="2119.0" stroke="black"/>
<text x="63" y="2123.0" text-anchor="end" font-size="11">52</text>
<line x1="66" y1="1968.0" x2="70" y2="1968.0" stroke="black"/>
<text x="63" y="1972.0" text-anchor="end" font-size="11">103</text>
<line x1="66" y1="1817.0" x2="70" y2="1817.0" stroke="black"/>
<text x="63" y="1821.0" text-anchor="end" font-size="11">155</text>
<line x1="66" y1="1666.0" x2="70" y2="1666.0" stroke="black"/>
<text x="63" y="1670.0" text-anchor="end" font-size="11">207</text>
<text x="383" y="2303" text-anchor="middle" font-size="12">eval cycle number</text>
<text x="16" y="1968" text-anchor="middle" font-size="12" transform="rotate(-90 16 1968)">best single-round score</text>
<circle cx="70.0" cy="2053.9" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="127.0" cy="1977.9" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="184.0" cy="2182.4" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="241.0" cy="2229.1" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="298.0" cy="2232.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="355.0" cy="2179.5" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="412.0" cy="2223.3" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="469.0" cy="2179.5" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="526.0" cy="2223.3" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="583.0" cy="2205.7" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="640.0" cy="2185.3" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="697.0" cy="2077.2" r="2" fill="#d62728" opacity="0.25"/>
<polyline points="70.0,2053.9 127.0,1977.9 184.0,2182.4 241.0,2229.1 298.0,2232.0 355.0,2179.5 412.0,2223.3 469.0,2179.5 526.0,2223.3 583.0,2205.7 640.0,2185.3 697.0,2077.2" fill="none" stroke="#d62728" stroke-width="2.5" opacity="1.0"/>
<circle cx="70.0" cy="2237.9" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="127.0" cy="2243.7" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="184.0" cy="2205.7" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="241.0" cy="2188.2" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="298.0" cy="2199.9" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="355.0" cy="2086.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="412.0" cy="2144.4" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="469.0" cy="2129.8" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="526.0" cy="2138.6" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="583.0" cy="1919.5" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="640.0" cy="2182.4" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="697.0" cy="2065.6" r="2" fill="#1f77b4" opacity="0.25"/>
<polyline points="70.0,2237.9 127.0,2243.7 184.0,2205.7 241.0,2188.2 298.0,2199.9 355.0,2086.0 412.0,2144.4 469.0,2129.8 526.0,2138.6 583.0,1919.5 640.0,2182.4 697.0,2065.6" fill="none" stroke="#1f77b4" stroke-width="2.5" opacity="1.0"/>
<circle cx="70.0" cy="2001.3" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="127.0" cy="2007.1" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="184.0" cy="2001.3" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="241.0" cy="2167.8" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="298.0" cy="2235.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="355.0" cy="2182.4" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="412.0" cy="1720.9" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="469.0" cy="2176.5" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="526.0" cy="2194.1" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="583.0" cy="2077.2" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="640.0" cy="2115.2" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="697.0" cy="2118.1" r="2" fill="#2ca02c" opacity="0.25"/>
<polyline points="70.0,2001.3 127.0,2007.1 184.0,2001.3 241.0,2167.8 298.0,2235.0 355.0,2182.4 412.0,1720.9 469.0,2176.5 526.0,2194.1 583.0,2077.2 640.0,2115.2 697.0,2118.1" fill="none" stroke="#2ca02c" stroke-width="2.5" opacity="1.0"/>
<polyline points="70.0,2001.3 127.0,1977.9 184.0,2001.3 241.0,2167.8 298.0,2199.9 355.0,2086.0 412.0,1720.9 469.0,2129.8 526.0,2138.6 583.0,1919.5 640.0,2115.2 697.0,2065.6" fill="none" stroke="#555555" stroke-width="2.5" opacity="1.0" stroke-dasharray="6 4"/>
<line x1="82" y1="2322" x2="110" y2="2322" stroke="#d62728" stroke-width="3"/>
<text x="116" y="2326" font-size="12">Corners - 12 evals</text>
<line x1="82" y1="2340" x2="110" y2="2340" stroke="#1f77b4" stroke-width="3"/>
<text x="116" y="2344" font-size="12">Crazy - 12 evals</text>
<line x1="82" y1="2358" x2="110" y2="2358" stroke="#2ca02c" stroke-width="3"/>
<text x="116" y="2362" font-size="12">Target - 12 evals</text>
<line x1="82" y1="2376" x2="110" y2="2376" stroke="#555555" stroke-width="3"/>
<text x="116" y="2380" font-size="12">combined max</text>
<rect x="0" y="2340" width="1400" height="180" fill="#f2f2f2"/>
<text x="16" y="2361" font-size="15" font-weight="bold">How to read</text>
<text x="1384" y="2360" text-anchor="end" font-size="11" fill="#666">Regenerate anytime: python3 tools/plot_progress.py</text>
<text x="16" y="2383" font-size="13">Test matches: dots are single fights, thick line shows trend.</text>
<text x="16" y="2400" font-size="13">Loss spikes are normal early; endless growth is bad.</text>
<text x="16" y="2417" font-size="13">Alpha high means experimenting; falling too fast freezes habits.</text>
<text x="16" y="2434" font-size="13">Throughput flat is healthy; dips mean something slowed.</text>
<text x="16" y="2451" font-size="13">Max score: best single-round score the bot managed in that eval cycle.</text>
<text x="16" y="2468" font-size="13">This file reloads itself in Chrome every sixty seconds.</text>
<text x="16" y="2485" font-size="13">Regenerate anytime with tools/watch_dashboard.sh or the python command.</text>
<text x="740" y="962.0" text-anchor="end" font-size="11">207</text>
<text x="1061" y="1461" text-anchor="middle" font-size="12">eval cycle number</text>
<text x="16" y="1193" text-anchor="middle" font-size="12" transform="rotate(-90 16 1193)">best single-round score</text>
<circle cx="747.0" cy="1259.8" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="795.3" cy="1200.7" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="843.6" cy="1359.8" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="891.9" cy="1396.2" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="940.2" cy="1398.5" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="988.5" cy="1357.5" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1036.8" cy="1391.6" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1085.2" cy="1357.5" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1133.5" cy="1391.6" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1181.8" cy="1378.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1230.1" cy="1362.1" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1278.4" cy="1278.0" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1326.7" cy="1291.6" r="2" fill="#d62728" opacity="0.25"/>
<circle cx="1375.0" cy="1425.7" r="2" fill="#d62728" opacity="0.25"/>
<polyline points="747.0,1259.8 795.3,1200.7 843.6,1359.8 891.9,1396.2 940.2,1398.5 988.5,1357.5 1036.8,1391.6 1085.2,1357.5 1133.5,1391.6 1181.8,1378.0 1230.1,1362.1 1278.4,1278.0 1326.7,1291.6 1375.0,1425.7" fill="none" stroke="#d62728" stroke-width="2.5" opacity="1.0"/>
<circle cx="747.0" cy="1403.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="795.3" cy="1407.5" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="843.6" cy="1378.0" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="891.9" cy="1364.4" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="940.2" cy="1373.5" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="988.5" cy="1284.8" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1036.8" cy="1330.3" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1085.2" cy="1318.9" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1133.5" cy="1325.7" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1181.8" cy="1155.3" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1230.1" cy="1359.8" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1278.4" cy="1268.9" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1326.7" cy="1271.2" r="2" fill="#1f77b4" opacity="0.25"/>
<circle cx="1375.0" cy="1273.5" r="2" fill="#1f77b4" opacity="0.25"/>
<polyline points="747.0,1403.0 795.3,1407.5 843.6,1378.0 891.9,1364.4 940.2,1373.5 988.5,1284.8 1036.8,1330.3 1085.2,1318.9 1133.5,1325.7 1181.8,1155.3 1230.1,1359.8 1278.4,1268.9 1326.7,1271.2 1375.0,1273.5" fill="none" stroke="#1f77b4" stroke-width="2.5" opacity="1.0"/>
<circle cx="747.0" cy="1218.9" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="795.3" cy="1223.5" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="843.6" cy="1218.9" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="891.9" cy="1348.5" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="940.2" cy="1400.7" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="988.5" cy="1359.8" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1036.8" cy="1000.7" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1085.2" cy="1355.3" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1133.5" cy="1368.9" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1181.8" cy="1278.0" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1230.1" cy="1307.5" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1278.4" cy="1309.8" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1326.7" cy="1291.6" r="2" fill="#2ca02c" opacity="0.25"/>
<circle cx="1375.0" cy="1198.5" r="2" fill="#2ca02c" opacity="0.25"/>
<polyline points="747.0,1218.9 795.3,1223.5 843.6,1218.9 891.9,1348.5 940.2,1400.7 988.5,1359.8 1036.8,1000.7 1085.2,1355.3 1133.5,1368.9 1181.8,1278.0 1230.1,1307.5 1278.4,1309.8 1326.7,1291.6 1375.0,1198.5" fill="none" stroke="#2ca02c" stroke-width="2.5" opacity="1.0"/>
<polyline points="747.0,1218.9 795.3,1200.7 843.6,1218.9 891.9,1348.5 940.2,1373.5 988.5,1284.8 1036.8,1000.7 1085.2,1318.9 1133.5,1325.7 1181.8,1155.3 1230.1,1307.5 1278.4,1268.9 1326.7,1271.2 1375.0,1198.5" fill="none" stroke="#555555" stroke-width="2.5" opacity="1.0" stroke-dasharray="6 4"/>
<line x1="759" y1="1480" x2="787" y2="1480" stroke="#d62728" stroke-width="3"/>
<text x="793" y="1484" font-size="12">Corners - 14 evals</text>
<line x1="759" y1="1498" x2="787" y2="1498" stroke="#1f77b4" stroke-width="3"/>
<text x="793" y="1502" font-size="12">Crazy - 14 evals</text>
<line x1="759" y1="1516" x2="787" y2="1516" stroke="#2ca02c" stroke-width="3"/>
<text x="793" y="1520" font-size="12">Target - 14 evals</text>
<line x1="759" y1="1534" x2="787" y2="1534" stroke="#555555" stroke-width="3"/>
<text x="793" y="1538" font-size="12">combined max</text>
<rect x="0" y="1540" width="1400" height="180" fill="#f2f2f2"/>
<text x="16" y="1561" font-size="15" font-weight="bold">How to read</text>
<text x="1384" y="1560" text-anchor="end" font-size="11" fill="#666">Regenerate anytime: python3 tools/plot_progress.py</text>
<text x="16" y="1583" font-size="13">Test matches: dots are single fights, thick line shows trend.</text>
<text x="16" y="1600" font-size="13">Loss spikes are normal early; endless growth is bad.</text>
<text x="16" y="1617" font-size="13">Throughput flat is healthy; dips mean something slowed.</text>
<text x="16" y="1634" font-size="13">Max score: best single-round score the bot managed in that eval cycle.</text>
<text x="16" y="1651" font-size="13">This file reloads itself in Chrome every sixty seconds.</text>
<text x="16" y="1668" font-size="13">Regenerate anytime with tools/watch_dashboard.sh or the python command.</text>
<script type="text/javascript"><![CDATA[ setTimeout(function(){ location.reload(); }, 60000); ]]></script>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+47 -54
View File
@@ -3,17 +3,17 @@
Pure-stdlib SVG output (matplotlib not available on this box).
Generates ONE file:
docs/campaign_dashboard.svg - five panels, current (v2) run only:
docs/campaign_dashboard.svg - four panels, current (v2) run only:
1. test-match win % vs opponents (campaign_v4_stdout.log eval lines)
2. critic_loss / |actor_loss| (training_metrics.jsonl, shared log-y)
3. alpha temperature (training_metrics.jsonl, linear)
4. throughput, games/hour buckets (training_metrics.jsonl 'epoch' deltas;
1 metrics row == one 10-game chunk,
counts match stdout chunk markers)
5. max score per eval cycle (campaign_v4_stdout.log eval blocks;
eval_log.jsonl only ever holds the
LATEST cycle, so history comes from
the stdout log)
2. critic_loss / |actor_loss| / alpha (training_metrics.jsonl; losses log
left axis, alpha linear right axis)
3. throughput, games/hour buckets (training_metrics.jsonl 'epoch' deltas;
1 metrics row == one 10-game chunk,
counts match stdout chunk markers)
4. max score per eval cycle (campaign_v4_stdout.log eval blocks;
eval_log.jsonl only ever holds the
LATEST cycle, so history comes from
the stdout log)
plus an embedded JS snippet that reloads the page every 60 s when the SVG is
opened as a top-level document in Chrome.
@@ -42,10 +42,10 @@ GAMES_PER_ROW = 10 # one training_metrics.jsonl row per 10-round chunk
COLORS = {"Corners": "#d62728", "Crazy": "#1f77b4", "Target": "#2ca02c",
"RamFire": "#ff7f0e", "SacTwin": "#9467bd"}
CRITIC_C, ACTOR_C = "#1f77b4", "#ff7f0e"
ALPHA_C = "#9467bd" # same purple as the dedicated alpha panel
ALPHA_C = "#9467bd" # alpha line on the losses panel
RELOAD_JS = ('<script type="text/javascript"><![CDATA[ '
'setTimeout(function(){ location.reload(); }, 60000); ]]></script>')
W, H = 1400, 2520
W, H = 1400, 1720
TITLE_H, GUIDE_H = 80, 180
# rows: (header_y, panel_top_y, panel_bottom_y, x_left, x_right)
C1_L, C1_R = 70, 697
@@ -55,19 +55,16 @@ ROWS = {
2: (100, 120, 720, C2_L, C2_R),
3: (940, 958, 1428, C1_L, C1_R),
4: (940, 958, 1428, C2_L, C2_R),
5: (1648, 1666, 2270, C1_L, C1_R), # third row, left column; right slot empty
}
PANEL_TITLES = [
"Test matches - win % vs opponents",
"Training losses & alpha (log scale)",
"Alpha temperature",
"Training losses (log) & alpha (linear)",
"Throughput - games per hour",
"Max score per eval cycle",
]
GUIDE_LINES = [
"Test matches: dots are single fights, thick line shows trend.",
"Loss spikes are normal early; endless growth is bad.",
"Alpha high means experimenting; falling too fast freezes habits.",
"Throughput flat is healthy; dips mean something slowed.",
"Max score: best single-round score the bot managed in that eval cycle.",
"This file reloads itself in Chrome every sixty seconds.",
@@ -218,9 +215,9 @@ def dots(pts, color, r=2, opacity=0.25):
f'fill="{color}" opacity="{opacity}"/>\n' for x, y in pts)
def hgrid(x0, x1, ys):
def hgrid(x0, x1, ys, color="#dddddd"):
return "".join(f'<line x1="{x0}" y1="{y:.1f}" x2="{x1}" y2="{y:.1f}" '
f'stroke="#dddddd"/>\n' for y in ys)
f'stroke="{color}"/>\n' for y in ys)
def axis(x0, y0, x1, y1, xt, yt, xlabel, ylabel, ylog=False):
@@ -368,45 +365,40 @@ def panel_losses(rows, geo):
s += f'<text x="{x0 + 10}" y="{pt + 40}" font-size="12" fill="#a00">' \
"no valid loss points</text>\n"
return
allv = [v for _, v in critic + actor + alpha]
lo, hi = math.floor(math.log10(min(allv))), math.ceil(math.log10(max(allv)))
if lo == hi:
hi = lo + 1
x1 -= 46 # room on the right for the twin alpha axis labels
n = len(rows)
xm = lambda i: x0 + (x1 - x0) * i / max(n - 1, 1)
base = critic + actor or alpha # log-domain source (losses in practice)
lo = math.floor(math.log10(min(v for _, v in base)))
hi = math.ceil(math.log10(max(v for _, v in base)))
if lo == hi:
hi = lo + 1
ym = map_fn(pb, pt, lo, hi, log=True)
s += hgrid(x0, x1, [ym(10 ** e) for e in range(lo, hi + 1)])
s += axis(x0, pb, x1, pt, ticks_linear(1, n, x0, x1, n=5),
ticks_log(lo, hi, pb, pt), "metric line number", "loss (log)")
s += polyline([(xm(i), ym(v)) for i, v in critic], CRITIC_C, 1.8)
s += polyline([(xm(i), ym(v)) for i, v in actor], ACTOR_C, 1.8)
s += polyline([(xm(i), ym(v)) for i, v in alpha], ALPHA_C, 1.8)
if alpha: # twin axis: alpha on its own linear scale, purple like the line
ahi = max(1.0, max(v for _, v in alpha))
yma = map_fn(pb, pt, 0, ahi)
s += hgrid(x0, x1, [yma(ahi * k / 4) for k in range(5)], "#e9dcf5")
mid = (pt + pb) // 2
s += f'<line x1="{x1}" y1="{pb}" x2="{x1}" y2="{pt}" stroke="{ALPHA_C}"/>\n'
for v, py in ticks_linear(0, ahi, pb, pt, n=5, fmt="{:.3g}"):
s += (f'<line x1="{x1}" y1="{py:.1f}" x2="{x1 + 4}" y2="{py:.1f}" '
f'stroke="{ALPHA_C}"/>\n'
f'<text x="{x1 + 7}" y="{py + 4:.1f}" font-size="11" '
f'fill="{ALPHA_C}">{esc(v)}</text>\n')
s += (f'<text x="{x1 + 17}" y="{mid}" text-anchor="middle" font-size="12" '
f'fill="{ALPHA_C}" transform="rotate(90 {x1 + 17} {mid})">alpha</text>\n')
s += polyline([(xm(i), yma(v)) for i, v in alpha], ALPHA_C, 1.8)
items = [(CRITIC_C, "critic_loss"), (ACTOR_C, "|actor_loss|")]
if alpha:
items.append((ALPHA_C, "alpha"))
s += legend(items, x0 + 12, pb + 52)
return s
def panel_alpha(rows, geo):
_, pt, pb, x0, x1 = geo
s = ""
alpha = metric_col(rows, "alpha")
if not alpha:
s += f'<text x="{x0 + 10}" y="{pt + 40}" font-size="12" fill="#a00">' \
"no alpha points</text>\n"
return
n = len(rows)
hi = max(1.0, max(v for _, v in alpha))
xm = lambda i: x0 + (x1 - x0) * i / max(n - 1, 1)
ym = map_fn(pb, pt, 0, hi)
s += hgrid(x0, x1, [ym(v) for v in
[hi * k / 4 for k in range(5)]])
s += axis(x0, pb, x1, pt, ticks_linear(1, n, x0, x1, n=5),
ticks_linear(0, hi, pb, pt, n=5, fmt="{:.3g}"),
"metric line number", "alpha")
s += polyline([(xm(i), ym(v)) for i, v in alpha], "#9467bd", 1.8)
return s
def panel_throughput(rows, geo):
_, pt, pb, x0, x1 = geo
s = ""
@@ -470,14 +462,11 @@ def build_dashboard(campaign, metrics_f, out):
"training_metrics.jsonl - big early spikes are normal",
lambda: panel_losses(rows, ROWS[2])),
(ROWS[3], PANEL_TITLES[2],
"training_metrics.jsonl - high = exploring, low = exploiting",
lambda: panel_alpha(rows, ROWS[3])),
(ROWS[4], PANEL_TITLES[3],
"method: training_metrics.jsonl 'epoch' deltas; 1 row = one 10-game chunk",
lambda: panel_throughput(rows, ROWS[4])),
(ROWS[5], PANEL_TITLES[4],
lambda: panel_throughput(rows, ROWS[3])),
(ROWS[4], PANEL_TITLES[3],
"campaign_v4_stdout.log - max of the 10 deterministic round scores per eval",
lambda: panel_max_score(maxes, ROWS[5])),
lambda: panel_max_score(maxes, ROWS[4])),
]
for geo, title, sub, drawer in drawers:
s += header(geo[3], geo[0], title, sub)
@@ -540,14 +529,18 @@ def selftest():
assert esc(t) in text, f"panel title missing: {t}"
assert text.count(PANEL_TITLES[0]) == 1
assert "How to read" in text, "reading guide missing"
assert 'width="1400"' in text and 'height="2520"' in text
# circles: 4 eval dots (panel 1) + 2 throughput rate dots (panel 4)
# + 2 cycles x 2 opponents max-score dots (panel 5)
assert 'width="1400"' in text and 'height="1720"' in text
# circles: 4 eval dots (panel 1) + 2 throughput rate dots (panel 3)
# + 2 cycles x 2 opponents max-score dots (panel 4)
assert text.count("<circle") == 10, text.count("<circle")
# 2 trends + 3 losses-panel polylines (critic, actor, alpha)
# + 1 dedicated alpha panel + 1 throughput
# + 1 throughput
# + 3 max-score panel (Corners, Crazy, combined; Target absent in fixture)
assert text.count("<polyline") == 10, text.count("<polyline")
assert text.count("<polyline") == 9, text.count("<polyline")
# losses panel twin axis: "alpha" text = legend + right ylabel; purple
# fills = 5 right-axis tick labels + rotated ylabel
assert text.count(">alpha<") == 2, text.count(">alpha<")
assert text.count('fill="#9467bd"') == 6, text.count('fill="#9467bd"')
assert "combined max" in text, "max-score combined line missing"
# orientation guard: a known rising series (10% -> 90%) rendered through