← back to Model Arena
yolo idea E: tournament bracket — seeded single-elim visualization over a battle's AI-referee consensus scores (Semis/Final/Champion), $0 client-side
89557233ce938ec6cee5b624a348f3fa02d88dc0 · 2026-07-23 00:56:53 -0700 · Steve
Files touched
M public/index.htmlM yolo/IDEAS.md
Diff
commit 89557233ce938ec6cee5b624a348f3fa02d88dc0
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 23 00:56:53 2026 -0700
yolo idea E: tournament bracket — seeded single-elim visualization over a battle's AI-referee consensus scores (Semis/Final/Champion), $0 client-side
---
public/index.html | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
yolo/IDEAS.md | 1 +
2 files changed, 82 insertions(+)
diff --git a/public/index.html b/public/index.html
index d831ae1..92ae974 100644
--- a/public/index.html
+++ b/public/index.html
@@ -121,6 +121,19 @@ tr.clk{cursor:pointer}tr.clk:hover td{background:rgba(0,229,255,.06)}
.gcard .noimg{height:130px;display:flex;align-items:center;justify-content:center;color:var(--dim);font-size:11px;background:var(--panel2)}
.gcard .gm{padding:7px 9px;font-size:11px}.gcard .gm .gt{color:#fff;display:block;margin-bottom:3px}
.gcard .gm .gs{color:var(--dim)}
+/* tournament bracket */
+.brwrap{display:flex;gap:44px;align-items:center;overflow:auto;padding:10px 4px 24px}
+.brcol{display:flex;flex-direction:column;justify-content:space-around;gap:18px;min-width:190px}
+.brcol h4{color:var(--dim);font-size:11px;letter-spacing:1px;text-transform:uppercase;text-align:center;margin-bottom:2px}
+.match{display:flex;flex-direction:column;gap:5px;position:relative}
+.seed{display:flex;align-items:center;gap:8px;border:1px solid var(--line);background:var(--panel);padding:7px 10px;font-size:12px}
+.seed.win{border-color:var(--gold);color:#fff}
+.seed.lose{opacity:.5}
+.seed .sd{color:var(--dim);font-size:10px;min-width:26px}
+.seed .sc{margin-left:auto;color:#c8a6ff;font-weight:700}
+.seed.bye{opacity:.35;font-style:italic}
+.champ{border:1px solid var(--gold);background:rgba(255,199,46,.08);padding:14px 18px;text-align:center;box-shadow:0 0 22px rgba(255,199,46,.25)}
+.champ .k{font-size:26px}.champ b{color:var(--gold);font-size:16px}
</style>
</head>
<body>
@@ -174,6 +187,13 @@ tr.clk{cursor:pointer}tr.clk:hover td{background:rgba(0,229,255,.06)}
<div class="arena" id="d-arena"></div>
</section>
+ <section id="bracket" class="view">
+ <span class="back" id="br-back">← back to battle</span>
+ <h2 id="br-title"></h2>
+ <div class="note" id="br-note"></div>
+ <div id="br-wrap" class="brwrap"></div>
+ </section>
+
<section id="profile" class="view">
<span class="back" id="p-back">← back to leaderboard</span>
<h2 id="p-title"></h2>
@@ -352,6 +372,11 @@ async function renderDetail(id, statusOnly){
const nc = await (await fetch(API+'/api/challenges/'+c.id+'/remix',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'})).json();
if (nc.id) openDetail(nc.id); };
}
+ // Bracket button — when ≥2 artifacts have AI scores
+ if (c.runs.filter(r=>r.status==='done'&&typeof r.aiScore==='number').length>=2){
+ const bx=document.createElement('span'); bx.innerHTML=` · <button class="btn" id="bracket-btn">🏆 View bracket</button>`;
+ ai.appendChild(bx); $('#bracket-btn').onclick=()=>openBracket(c);
+ }
const arena = $('#d-arena');
const sig = c.runs.map(r=>r.model+':'+r.status+':'+(r.aiScore??'')+':'+(r.thumb?'t':'')).join('|') + '|' + c.winner + '|' + c.aiPick + '|' + c.judging;
if (statusOnly && arena.dataset.sig === sig) return c;
@@ -440,6 +465,62 @@ async function loadBoard(){
renderH2H(d);
}
+// ---- tournament bracket (seeded single-elim over a battle's AI-referee scores) ----
+$('#br-back').onclick = ()=>{ if(current) openDetail(current.id); };
+function openBracket(c){
+ show('bracket');
+ $('#br-title').textContent = '🏆 ' + c.title + ' — bracket';
+ // seed by AI consensus score (desc); only rendered artifacts with a score qualify
+ const field = c.runs.filter(r=>r.status==='done' && typeof r.aiScore==='number')
+ .map(r=>({id:r.model, label:mLabel(r.model), score:r.aiScore})).sort((a,b)=>b.score-a.score);
+ if (field.length<2){ $('#br-note').textContent='Need at least 2 AI-scored artifacts — run the AI referee first.'; $('#br-wrap').innerHTML=''; return; }
+ let size=1; while(size<field.length) size*=2; // pad to power of 2 with byes
+ const seeds = field.map((f,i)=>({...f, seed:i+1}));
+ while(seeds.length<size) seeds.push({id:null,label:'(bye)',score:-1,seed:seeds.length+1,bye:true});
+ // standard bracket seeding order (1 vs last, etc.)
+ const order=[]; (function build(n){ let a=[1,2]; for(let r=4;r<=n;r*=2){ const t=[]; for(const s of a){ t.push(s); t.push(r+1-s);} a=t;} order.push(...a); })(size);
+ let round = order.map(pos=>seeds[pos-1]);
+ const rounds=[]; let matches=[];
+ // round 0 pairs
+ for(let i=0;i<round.length;i+=2) matches.push([round[i],round[i+1]]);
+ const names=['Round 1','Quarterfinals','Semifinals','Final'];
+ const roundData=[];
+ while(matches.length>=1){
+ roundData.push(matches);
+ if(matches.length===1 && matches[0].length===2 && !matches[0].__final){ /* will resolve champ below */ }
+ const winners = matches.map(([a,b])=>{ if(b&&b.bye) return a; if(a&&a.bye) return b; return (a.score>=b.score)?a:b; });
+ if(winners.length===1){ roundData.__champ=winners[0]; break; }
+ const nm=[]; for(let i=0;i<winners.length;i+=2) nm.push([winners[i],winners[i+1]]);
+ matches=nm;
+ }
+ $('#br-note').textContent = `${field.length} models seeded by 🤖 consensus score, single elimination. Champion advances by higher AI score each match. (Human 👑 crown = ${c.winner?mLabel(c.winner):'—'} · AI pick = ${c.aiPick?mLabel(c.aiPick):'—'})`;
+ const wrap=$('#br-wrap'); wrap.innerHTML='';
+ const nRounds=roundData.length;
+ roundData.forEach((ms,ri)=>{
+ const col=document.createElement('div'); col.className='brcol';
+ const title = nRounds-ri<=names.length ? names[names.length-(nRounds-ri)] : ('Round '+(ri+1));
+ col.innerHTML=`<h4>${ri===nRounds-1?'Final':title}</h4>`;
+ ms.forEach(([a,b])=>{
+ const win = (b&&b.bye)?a:(a&&a.bye)?b:((a.score>=b.score)?a:b);
+ const m=document.createElement('div'); m.className='match';
+ m.appendChild(seedEl(a,win)); if(b) m.appendChild(seedEl(b,win));
+ col.appendChild(m);
+ });
+ wrap.appendChild(col);
+ });
+ if(roundData.__champ){
+ const col=document.createElement('div'); col.className='brcol';
+ const ch=roundData.__champ;
+ col.innerHTML=`<h4>Champion</h4><div class="champ"><div class="k">👑</div><b>${ch.label}</b><div class="note" style="margin-top:6px">🤖 ${ch.score}</div></div>`;
+ wrap.appendChild(col);
+ }
+}
+function seedEl(s,win){ const d=document.createElement('div');
+ d.className='seed'+(s.bye?' bye':'')+(s&&win&&s.id===win.id?' win':(s&&!s.bye?' lose':''));
+ d.innerHTML=`<span class="sd">${s.bye?'':'#'+s.seed}</span>${s.label}${s.bye?'':`<span class="sc">${s.score}</span>`}`;
+ return d;
+}
+
// ---- model profile ----
$('#p-back').onclick = ()=>{ show('board'); loadBoard(); };
async function openProfile(mid){
diff --git a/yolo/IDEAS.md b/yolo/IDEAS.md
index 229d829..4f99025 100644
--- a/yolo/IDEAS.md
+++ b/yolo/IDEAS.md
@@ -36,3 +36,4 @@ Answering the /contrarian critique ("one builder vote = theater", "toy demos not
- [DONE] #B Export/share — GET /export/:id returns a self-contained standalone HTML of a battle (all artifacts inlined via sandboxed iframe srcdoc, scoreboard, winner/AI-pick), downloadable; verified it renders offline from file://. '⬇ export' button on each battle.
- [DONE] #C Model profile page — click any leaderboard row → that model's full body of work (every artifact thumbnail across battles, won/AI-pick badges, per-battle stats) + aggregate stats. /api/models/:id/profile.
- [DONE] #D Head-to-head matrix — pairwise win-loss record (row vs column) from crowned battles, green/red-coded, below the leaderboard. Grok dominant (3-0 vs Kimi/Hermes/Gemma).
+- [DONE] #E Tournament bracket — reframes a battle's AI-referee scores as a seeded single-elim bracket (standard 1-vs-last seeding, byes to power-of-2, higher consensus score advances) → Semis/Final/Champion visualization. Client-side, instant, $0. Smoke Test → champion Kimi (agrees with human crown + AI pick).
← 0c31fff yolo: regression note (documents the b-cat hidden-view click
·
back to Model Arena
·
yolo: RUN COMPLETE — 14 wild ideas across 3 iterations, fina c7f1316 →