[object Object]

← 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

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=` &nbsp;·&nbsp; <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 →