47:51Ranking major U.S. Open-Weights Models. (Where does Ripple 65B fit in?)
Nov 6, 2026 · YouTube
10:57"Crouton Alpha": the latest super-efficient coding model by Meta?
Nov 3, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
22:47Mistral Large 4 Is Cheap. But Is It Good Enough to Compete?
Oct 6, 2026 · YouTube
16:54Stealth Union Alpha: Is this another cheap GPT model? (better than Luna, cheaper than Terra?)
Sep 16, 2026 · YouTube
16:45DeepSeek V4.1 Flash Is Shockingly Good for the Price
Sep 10, 2026 · YouTube
17:37Tencent Hy4 Preview Tested: Is It Really Better Than Kimi K3 and GLM 5.3?
Aug 28, 2026 · YouTube
23:58Qwen 3.8 Flash Tested: UI, Coding, Physics & more - compared to Opus 5, Sol Pro, Grok
Aug 27, 2026 · YouTube
14:50Stealth Ox Alpha: a new GLM model! Full test (coding & writing) against Grok, Gemini, GPT & others
Aug 21, 2026 · YouTube
My buddy runs a bar. I told him I'd build him something for Thursdays. He's the one talking here — I just write down what he says and hand it to you.
It's not real pool. Three balls on the table, you shoot until all three are down, and we count how many shots it took you. Low is good. Everybody takes a turn, then we go around again — that's a round.
Twenty bucks to play. You start with five chips. Take more than four shots to clear the table and it costs you a chip. Out of chips and you're out, go back to drinking. Last one with chips left takes the pot.
There's a clock on your turn — a minute, minute and a half, we've never really settled it. Clock runs out and you're not at the table, that's a chip.
It's a Thursday. Nine o'clock, everybody's had a few, the jukebox is loud and half the room is outside smoking. Twelve, sixteen people usually, however many show up. It runs two, three hours and nobody's in a hurry.
Last year I ran the whole thing on a legal pad and it got away from me around round four. Two guys arguing with me about what they shot, me trying to remember whose turn it was, somebody's beer on the pad. Never again.
I'm running this off my phone, one-handed, standing at the table with a beer in the other. There's a TV over the bar and that's the only screen anybody in the room can see — from across the room, in the dark, so it's got to be big. People want to know where they stand without walking over and asking me.
My phone's on 15% by ten. The bar's wifi is the bar's wifi.
Twenty a head, all of it in the pot. Winner takes it — mostly. We kick a little to second. And there's a side thing we do for whoever's shooting best early on, I never wrote that part down properly.
The app doesn't hold the money. I do. But at the end of the night it has to tell me what I owe who, and I'm reading that off my phone with sixteen people standing around the table.
Two pages.
The console — the thing I run it from, and the thing that goes up on the TV. Open it and there's already a game going. I'm not typing in twelve names to find out whether it works.
The playback — a separate page that plays a whole night through, start to finish, so I can watch it work before Thursday. One step at a time, a step being one thing happening, with the scorecard filling in as it goes and a slider for how fast it runs. I want to slow it down, back it up, and read what just happened. Show me the way a night actually goes.
The playback runs the same rules the console runs. If the two of them drift apart, the playback is a cartoon and it's no use to me.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — these run in a sandboxed frame where those throw.html blocks: the console first, the playback second. Leave yourself room for the second one.I know I wasn't clear about everything. Everything you had to decide for me, one line each — what you decided and why. Short.
The first fenced html block.
The second fenced html block.
It's Thursday, it's nine o'clock, and I'm running this for real. What comes up that this doesn't handle? Be straight with me. I'd much rather hear it now than find out with sixteen people around a pool table and three hundred dollars sitting on the bar.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<title>THURSDAY - CONSOLE</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:#0a0a0a;color:#fff;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif}
button{font:inherit}
.top{position:sticky;top:0;z-index:5;background:#111;border-bottom:3px solid #222;display:flex;flex-wrap:wrap;gap:8px;padding:10px 12px;align-items:center}
.pill{background:#1e1e1e;border:1px solid #333;border-radius:999px;padding:6px 12px;font-weight:800;letter-spacing:.04em;font-size:13px}
.pill b{color:#facc15}
.pill.live{border-color:#facc15;color:#facc15}
.spacer{flex:1}
.btn{border:0;border-radius:10px;padding:10px 14px;font-weight:800;cursor:pointer}
.btn-tv{background:#facc15;color:#111}
.btn-ghost{background:#222;color:#fff;border:1px solid #333}
.wrap{max-width:1100px;margin:0 auto;padding:12px 12px 220px}
.tv{border:2px solid #222;border-radius:16px;overflow:hidden;background:#0f0f0f}
.tv-head{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;background:#161616;border-bottom:2px solid #222}
.tv-head h1{font-size:18px;letter-spacing:.12em}
.tv-head span{font-size:12px;color:#aaa}
.board{width:100%;border-collapse:collapse}
.board th{font-size:11px;letter-spacing:.1em;color:#888;text-align:left;padding:10px 8px;border-bottom:1px solid #222}
.board td{padding:10px 8px;border-bottom:1px solid #1a1a1a;font-weight:700}
.rank{width:36px;text-align:center;color:#888}
.name{font-size:18px}
.chips{letter-spacing:2px}
.chips i{font-style:normal}
.chips .on{color:#facc15;text-shadow:0 0 8px #facc15}
.chips .off{color:#333}
.avg{color:#aaa;font-weight:600}
.out{opacity:.35}
.out .name{text-decoration:line-through}
.badge{font-size:11px;padding:3px 7px;border-radius:999px;background:#222;border:1px solid #333;color:#aaa}
.badge.lead{background:#facc15;color:#111;border-color:#facc15}
.badge.outb{background:#3f1a1a;color:#ff8a8a;border-color:#5a2222}
.turn{margin:14px 0;background:#161616;border:2px solid #facc15;border-radius:16px;padding:14px}
.turn-top{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.who{flex:1;min-width:160px}
.who small{color:#facc15;letter-spacing:.14em;font-size:11px;font-weight:800}
.who div{font-size:28px;font-weight:900;line-height:1}
.timer{width:140px;text-align:center;background:#000;border:2px solid #333;border-radius:12px;padding:8px}
.timer b{font-size:32px;display:block;line-height:1}
.timer small{color:#888;font-size:11px;letter-spacing:.08em}
.timer.warn{border-color:#ef4444;color:#ef4444}
.shots{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin-top:12px}
.shots button{padding:16px 0;border-radius:12px;border:2px solid #333;background:#1f1f1f;color:#fff;font-size:20px;font-weight:900}
.shots button.safe{border-color:#22c55e}
.shots button.hit{border-color:#ef4444;background:#2a1212}
.shots button:active{transform:scale(.97)}
.row2{display:flex;gap:8px;margin-top:10px}
.row2 button{flex:1;padding:14px;border-radius:12px;font-weight:900}
.btn-time{background:#ef4444;color:#fff;border:0}
.btn-undo{background:#222;color:#fff;border:1px solid #333}
.payout{margin-top:14px;background:#0f1410;border:1px solid #1e3a1e;border-radius:12px;padding:12px}
.payout h3{font-size:12px;letter-spacing:.12em;color:#86efac}
.payout div{font-size:13px;line-height:1.6;color:#d1fae5}
.log{margin-top:14px;background:#111;border:1px solid #222;border-radius:12px;padding:10px;max-height:160px;overflow:auto;font-size:13px;line-height:1.5}
.log b{color:#facc15}
body.tv-only .wrap{padding:0;max-width:none}
body.tv-only .top,body.tv-only .turn,body.tv-only .payout,body.tv-only .log,body.tv-only .row2{display:none}
body.tv-only .tv{border:0;border-radius:0;min-height:100vh}
body.tv-only .tv-head h1{font-size:28px}
body.tv-only .board td{padding:16px 10px}
body.tv-only .name{font-size:28px}
body.tv-only .board th{font-size:14px}
@media(max-width:700px){.name{font-size:16px}.timer{width:110px}.timer b{font-size:26px}}
</style>
</head>
<body>
<div class="top">
<div class="pill">POT <b id="pot">$280</b> • 20×<span id="pcount">14</span></div>
<div class="pill">ROUND <b id="round">1</b> • <span id="remain">14 left</span></div>
<div class="pill live" id="clockPill">⏱ 01:30</div>
<div class="spacer"></div>
<button class="btn btn-ghost" id="undoBtn">UNDO</button>
<button class="btn btn-tv" id="tvBtn">TV MODE</button>
</div>
<div class="wrap">
<div class="tv" id="tv">
<div class="tv-head">
<h1>THURSDAY NIGHT • 3-BALL</h1>
<span id="tvSub">FIRST TO STAY ALIVE • >4 COSTS A CHIP</span>
</div>
<table class="board" id="board"></table>
</div>
<div class="turn" id="turnBox">
<div class="turn-top">
<div class="who">
<small>AT THE TABLE</small>
<div id="whoName">MICK</div>
<div style="font-size:13px;color:#aaa;font-weight:600" id="whoMeta">5 chips • Avg — • Round 1</div>
</div>
<div class="timer" id="timerBox">
<b id="timer">01:30</b>
<small>TAP TO PAUSE</small>
</div>
</div>
<div class="shots">
<button class="safe" data-s="1">1</button>
<button class="safe" data-s="2">2</button>
<button class="safe" data-s="3">3</button>
<button class="safe" data-s="4">4</button>
<button class="hit" data-s="5">5</button>
<button class="hit" data-s="6">6+</button>
</div>
<div class="row2">
<button class="btn-time" id="timeoutBtn">⏱ TIMEOUT — LOSE CHIP</button>
<button class="btn-undo" id="nextBtn">SKIP →</button>
</div>
</div>
<div class="payout" id="payout"></div>
<div class="log" id="log"></div>
</div>
<script>
const NAMES=["Mick","Dee","Jules","Rico","T-Bone","Slim","Carla","Dutch","Marty","Lou","Frankie","Sheila","Hank","Viv"];
const ENTRY=20, START_CHIPS=5, CLOCK=90;
let players=NAMES.map((n,i)=>({id:i,name:n,chips:START_CHIPS,history:[],out:false,avg:0}));
let round=1, cur=0, secs=CLOCK, tick=null, paused=false, over=false, history=[];
let earlyBird=null;
function active(){return players.filter(p=>!p.out)}
function curPlayer(){ const a=active(); return a.length? a[cur % a.length] : null}
function pot(){return players.length*ENTRY}
function fmt(s){const m=Math.floor(s/60),r=s%60;return String(m).padStart(2,'0')+':'+String(r).padStart(2,'0')}
function avg(p){return p.history.length? (p.history.reduce((a,b)=>a+b,0)/p.history.length).toFixed(1) : '—'}
function pushLog(t){const el=document.getElementById('log'); const d=document.createElement('div'); d.innerHTML=t; el.prepend(d);}
function snapshot(){history.push(JSON.stringify({players:players.map(p=>({...p,history:[...p.history]})),round,cur,secs,over,earlyBird}))}
function undo(){
if(!history.length) return;
const s=JSON.parse(history.pop());
players=s.players; round=s.round; cur=s.cur; secs=s.secs; over=s.over; earlyBird=s.earlyBird;
if(tick) clearInterval(tick);
if(!over && !paused) startClock();
render();
pushLog('<b>↩ UNDO</b> — back one step');
}
function startClock(){
if(tick) clearInterval(tick);
tick=setInterval(()=>{
if(paused||over) return;
secs--; if(secs<=0){ secs=0; timeout(true); }
renderClock();
},1000);
}
function renderClock(){
const pill=document.getElementById('clockPill');
const t=document.getElementById('timer');
const box=document.getElementById('timerBox');
t.textContent=fmt(secs);
pill.textContent='⏱ '+fmt(secs);
if(secs<=15){box.classList.add('warn'); pill.style.color='#ef4444'; pill.style.borderColor='#ef4444'} else {box.classList.remove('warn'); pill.style.color=''; pill.style.borderColor=''}
}
function record(shots, isTimeout){
if(over) return;
const p=curPlayer(); if(!p) return;
snapshot();
const real=p.history.length;
let lost=0;
if(isTimeout) lost=1;
else if(shots>4) lost=1;
p.history.push(isTimeout? 9 : shots);
if(lost){ p.chips=Math.max(0,p.chips-1); if(p.chips===0) p.out=true; }
// early bird after round 3 completes
const a=active();
// advance
let nextIdx=cur+1;
// if we wrapped, new round
if(nextIdx>=a.length){
// check if round completed (all active have history length == round)
const needRound = Math.max(...players.map(x=>x.history.length));
if(needRound>=round){
round++;
if(round===4 && !earlyBird){
// compute early bird: lowest avg among those with 3 rounds
let best=null, bestAvg=99;
players.forEach(pl=>{
if(pl.history.filter(v=>v!==9).length>=3){
const av=pl.history.filter(v=>v!==9).slice(0,3).reduce((s,v)=>s+v,0)/3;
if(av<bestAvg){bestAvg=av; best=pl}
}
});
earlyBird=best? best.name : null;
if(earlyBird) pushLog('<b>★ EARLY BIRD</b> — '+earlyBird+' ('+bestAvg.toFixed(1)+' avg first 3)');
}
}
nextIdx=0;
// if players eliminated, cur may be out of range
}
// skip eliminated for next turn
cur=nextIdx % Math.max(1, active().length);
secs=CLOCK;
// check win
if(active().length===1){ over=true; clearInterval(tick); pushLog('<b>🏆 GAME OVER</b> — '+active()[0].name+' takes it'); }
else if(active().length===0){ over=true; clearInterval(tick); }
else {
const np=curPlayer();
pushLog((isTimeout? '<b>⏱ TIMEOUT</b> — '+p.name+' not at table <span style="color:#ff8a8a">−1 chip → '+p.chips+'</span>' : '<b>'+p.name+'</b> shoots <b>'+shots+'</b>'+(lost?' <span style="color:#ff8a8a">−1 chip → '+p.chips+'</span>':' <span style="color:#86efac">safe</span>')) + (p.out?' <span style="color:#ff8a8a">OUT</span>':''));
}
render();
}
function timeout(auto){
record(0,true);
}
function render(){
document.getElementById('pot').textContent='$'+pot();
document.getElementById('pcount').textContent=players.length;
document.getElementById('round').textContent=round + (over?' — FINAL':'');
document.getElementById('remain').textContent=active().length+' left';
// board sorted by chips desc, avg asc
const sorted=[...players].sort((a,b)=>{
if(a.out!==b.out) return a.out?1:-1;
if(b.chips!==a.chips) return b.chips-a.chips;
const aa=a.history.filter(v=>v!==9).length? a.history.filter(v=>v!==9).reduce((s,v)=>s+v,0)/a.history.filter(v=>v!==9).length : 99;
const bb=b.history.filter(v=>v!==9).length? b.history.filter(v=>v!==9).reduce((s,v)=>s+v,0)/b.history.filter(v=>v!==9).length : 99;
return aa-bb;
});
const curP=curPlayer();
let html='<tr><th class="rank">#</th><th>PLAYER</th><th>CHIPS</th><th>LAST</th><th>AVG</th><th></th></tr>';
sorted.forEach((p,i)=>{
const isCur=curP && p.id===curP.id && !over && !p.out;
const chips='<span class="chips">'+[0,1,2,3,4].map(k=>'<i class="'+(k<p.chips?'on':'off')+'">●</i>').join('')+'</span> <small style="color:#aaa">'+p.chips+'</small>';
const last=p.history.length? (p.history[p.history.length-1]===9?'TIME':p.history[p.history.length-1]) : '—';
const badge = p.out? '<span class="badge outb">OUT</span>' : i===0 && !p.out && active().length>1? '<span class="badge lead">LEAD</span>' : isCur? '<span class="badge" style="border-color:#facc15;color:#facc15">SHOOTING</span>' : '';
html+='<tr class="'+(p.out?'out':'')+'" style="'+(isCur?'background:#1a1a0a':'' )+'"><td class="rank">'+(i+1)+'</td><td class="name">'+p.name+'</td><td>'+chips+'</td><td>'+last+'</td><td class="avg">'+avg(p)+'</td><td>'+badge+'</td></tr>';
});
document.getElementById('board').innerHTML=html;
// turn box
if(over){
document.getElementById('whoName').textContent='GAME OVER';
document.getElementById('whoMeta').textContent= active().length? active()[0].name+' wins • Tap UNDO to rewind' : 'No one left';
document.getElementById('timer').textContent='00:00';
} else if(curP){
document.getElementById('whoName').textContent=curP.name.toUpperCase();
document.getElementById('whoMeta').textContent=curP.chips+' chips • Avg '+avg(curP)+' • Round '+round+' • '+active().length+' alive';
}
renderClock();
// payout
const potVal=pot();
let w=Math.floor(potVal*0.7), s=Math.floor(potVal*0.2), e=potVal-w-s;
// if early bird not decided, show projected
const leader=sorted.find(p=>!p.out);
const second=sorted.filter(p=>!p.out)[1];
let payoutHtml='<h3>PAYOUT — I HOLD THE CASH</h3>';
if(over && leader){
payoutHtml+='<div>🏆 <b>'+leader.name+'</b> — $'+w+' (70%) 🥈 '+(second? second.name+' — $'+s+' (20%)' : 'no second')+' ★ '+(earlyBird? earlyBird+' — $'+e+' (10% early)':'Early — $'+e)+'</div>';
} else {
payoutHtml+='<div>Projected: 1st '+(leader?leader.name:'—')+' $'+w+' • 2nd '+(second?second.name:'—')+' $'+s+' • Early '+(earlyBird||'after R3')+' $'+e+' <span style="color:#888">Pot $'+potVal+'</span></div>';
}
payoutHtml+='<div style="color:#888;font-size:12px;margin-top:4px">App never holds money. Read this off your phone at the end.</div>';
document.getElementById('payout').innerHTML=payoutHtml;
}
document.querySelectorAll('.shots button').forEach(b=> b.addEventListener('click',()=> record(parseInt(b.dataset.s,10),false)));
document.getElementById('timeoutBtn').addEventListener('click',()=> timeout(false));
document.getElementById('nextBtn').addEventListener('click',()=>{
snapshot(); cur=(cur+1)%Math.max(1,active().length); secs=CLOCK; pushLog('<b>→ SKIP</b> — next shooter'); render();
});
document.getElementById('undoBtn').addEventListener('click',undo);
document.getElementById('timerBox').addEventListener('click',()=>{paused=!paused; document.querySelector('#timerBox small').textContent=paused?'PAUSED — TAP TO RESUME':'TAP TO PAUSE';});
document.getElementById('tvBtn').addEventListener('click',()=>{
document.body.classList.toggle('tv-only');
document.getElementById('tvBtn').textContent=document.body.classList.contains('tv-only')?'EXIT TV':'TV MODE';
window.scrollTo(0,0);
});
// init log + clock
pushLog('<b>Game on.</b> 14 players • $280 pot • 5 chips each • >4 costs a chip • 90s clock');
pushLog('<b>Tip:</b> Big buttons for one hand. TV MODE fills the bar TV. Timer auto-deducts on 0.');
render(); startClock();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>THURSDAY - PLAYBACK</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{background:#0a0a0a;color:#fff;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif}
.top{position:sticky;top:0;z-index:5;background:#111;border-bottom:2px solid #222;padding:10px 12px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.top h1{font-size:14px;letter-spacing:.12em}
.pill{background:#1e1e1e;border:1px solid #333;border-radius:999px;padding:6px 12px;font-size:12px;font-weight:800}
.pill b{color:#facc15}
.controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap;background:#161616;border:1px solid #222;border-radius:12px;padding:10px;margin:12px}
.controls button{border:0;border-radius:10px;padding:10px 14px;font-weight:900;cursor:pointer}
#playBtn{background:#facc15;color:#111;min-width:90px}
.ghost{background:#222;color:#fff;border:1px solid #333 !important}
.range{flex:1;min-width:140px}
.range input{width:100%}
.speed{min-width:110px;text-align:center;font-size:12px;color:#aaa}
.speed b{color:#fff}
.event{margin:12px;background:#161616;border:2px solid #facc15;border-radius:14px;padding:14px}
.event small{color:#facc15;letter-spacing:.12em;font-weight:800;font-size:11px}
.event div{font-size:22px;font-weight:900;line-height:1.2;margin-top:4px}
.event p{color:#aaa;font-size:13px;margin-top:6px;line-height:1.4}
.wrap{max-width:1100px;margin:0 auto;padding:0 12px 20px}
.card{background:#0f0f0f;border:1px solid #222;border-radius:12px;overflow:hidden}
.card h2{font-size:12px;letter-spacing:.12em;color:#888;padding:10px 12px;border-bottom:1px solid #222;background:#161616}
table{width:100%;border-collapse:collapse}
th{font-size:10px;letter-spacing:.08em;color:#888;text-align:center;padding:8px 4px;border-bottom:1px solid #222}
td{padding:8px 4px;text-align:center;border-bottom:1px solid #1a1a1a;font-weight:700;font-size:13px}
td.name{text-align:left;padding-left:10px;white-space:nowrap}
.chip{font-size:11px;letter-spacing:1px}
.on{color:#facc15} .off{color:#333}
.cell-safe{background:#0f1a0f} .cell-hit{background:#2a1212;color:#ff8a8a} .cell-time{background:#1a0f0f;color:#ff8a8a;font-size:11px}
.outrow{opacity:.35}
.log{max-height:220px;overflow:auto;padding:8px}
.log div{padding:6px 8px;border-bottom:1px solid #1a1a1a;font-size:13px;line-height:1.4}
.log div.active{background:#1a1a0a;border-left:3px solid #facc15}
.muted{color:#888}
</style>
</head>
<body>
<div class="top">
<h1>PLAYBACK — FULL NIGHT</h1>
<div class="pill">STEP <b id="stepN">0</b> / <span id="stepT">0</span></div>
<div class="pill">ROUND <b id="roundN">1</b></div>
<div class="pill">POT <b>$280</b> • 14 PLAYERS</div>
</div>
<div class="wrap">
<div class="controls">
<button id="playBtn">▶ PLAY</button>
<button class="ghost" id="prevBtn">◀ BACK</button>
<button class="ghost" id="nextBtn">NEXT ▶</button>
<div class="range"><input type="range" id="scrub" min="0" max="0" value="0"></div>
<div class="range" style="max-width:160px"><label style="font-size:11px;color:#888;letter-spacing:.08em">SPEED</label><input type="range" id="speed" min="0.5" max="3" step="0.5" value="1"></div>
<div class="speed"><b id="speedV">1×</b><br>auto-play</div>
</div>
<div class="event" id="eventBox">
<small id="eventKicker">READY</small>
<div id="eventTitle">Press PLAY to watch Thursday unfold — one thing at a time.</div>
<p id="eventDesc">Same rules as the console: 5 chips, >4 costs a chip, timeout costs a chip, last alive takes 70% / second 20% / early bird 10%. Scorecard fills as we go.</p>
</div>
<div class="card" style="margin-top:12px">
<h2>SCORECARD — SHOTS PER ROUND ( <span style="color:#86efac">1-4 safe</span> • <span style="color:#ff8a8a">5+ / TIME = −1 chip</span> )</h2>
<div style="overflow:auto">
<table id="board"></table>
</div>
</div>
<div class="card" style="margin-top:12px">
<h2>WHAT JUST HAPPENED — STEP LOG</h2>
<div class="log" id="log"></div>
</div>
</div>
<script>
const NAMES=["Mick","Dee","Jules","Rico","T-Bone","Slim","Carla","Dutch","Marty","Lou","Frankie","Sheila","Hank","Viv"];
const START=5, ENTRY=20;
// seeded sim to generate a realistic night
function mulberry(a){return function(){let t=a+=0x6D2B79F5; t=Math.imul(t^t>>>15,t|1); t^=t+Math.imul(t^t>>>7,t|61); return ((t^t>>>14)>>>0)/4294967296}}
const rnd=mulberry(0xC0FFEE);
function pickShots(){
const r=rnd();
if(r<0.05) return 1;
if(r<0.20) return 2;
if(r<0.50) return 3;
if(r<0.75) return 4;
if(r<0.90) return 5;
return 6;
}
let players=NAMES.map((n,i)=>({id:i,name:n}));
let steps=[];
let chips=NAMES.map(()=>START);
let histories=NAMES.map(()=>[]);
let round=1;
let alive=NAMES.map((_,i)=>i);
let earlyBird=null, earlyAvg=99;
function isAlive(i){return chips[i]>0}
function aliveList(){return alive.filter(isAlive)}
// generate until 1 left or 12 rounds
for(let r=1;r<=12;r++){
const order=[...aliveList()];
// shuffle order each round slightly to feel real
for(let i=order.length-1;i>0;i--){ const j=Math.floor(rnd()*(i+1)); const t=order[i]; order[i]=order[j]; order[j]=t; }
for(const pid of order){
if(!isAlive(pid)) continue;
const timeout = rnd()<0.04; // 4% timeout
const shots = timeout? 0 : pickShots();
const lost = timeout || shots>4 ? 1 : 0;
const before=chips[pid];
if(lost) chips[pid]=Math.max(0,chips[pid]-1);
histories[pid].push(timeout?9:shots);
steps.push({round:r, pid, shots, timeout, lost, before, after:chips[pid], out:chips[pid]===0});
if(chips[pid]===0){
// eliminated
}
if(aliveList().length<=1) break;
}
if(r===3){
let best=null, bestA=99;
for(let i=0;i<NAMES.length;i++){
const h=histories[i].filter(v=>v!==9).slice(0,3);
if(h.length===3){ const a=h.reduce((s,v)=>s+v,0)/3; if(a<bestA){bestA=a; best=i} }
}
if(best!==null){ earlyBird=best; earlyAvg=bestA; }
}
if(aliveList().length<=1) break;
round=r+1;
}
// add final payout step
steps.push({round:round, pid:-1, shots:0, timeout:false, lost:0, before:0, after:0, out:false, final:true});
const total=steps.length;
document.getElementById('stepT').textContent=total-1;
document.getElementById('scrub').max=total-1;
let idx=0, playing=false, timer=null;
function stateAt(k){
// reconstruct chips/histories up to k inclusive
const c=NAMES.map(()=>START);
const h=NAMES.map(()=>[]);
let eb=null;
for(let i=0;i<=k;i++){
const s=steps[i];
if(s.final) continue;
if(s.pid>=0){
h[s.pid].push(s.timeout?9:s.shots);
if(s.lost) c[s.pid]=Math.max(0,c[s.pid]-1);
}
}
// early bird after 3
if(k>=0){
const maxR=Math.max(0,...steps.slice(0,k+1).filter(s=>!s.final).map(s=>s.round));
if(maxR>=3){
let best=null, bestA=99;
for(let i=0;i<NAMES.length;i++){
const filt=h[i].filter(v=>v!==9).slice(0,3);
if(filt.length===3){ const a=filt.reduce((s,v)=>s+v,0)/3; if(a<bestA){bestA=a; best=i}}
}
eb=best;
}
}
return {c,h,eb}
}
function render(){
const s=steps[idx];
const st=stateAt(idx);
document.getElementById('stepN').textContent=idx;
document.getElementById('roundN').textContent=s? s.round : 1;
document.getElementById('scrub').value=idx;
// event box
const kicker=document.getElementById('eventKicker');
const title=document.getElementById('eventTitle');
const desc=document.getElementById('eventDesc');
if(!s || idx===0 && !s.final){
// show first step
}
if(s && s.final){
const aliveIdx=NAMES.map((_,i)=>i).filter(i=>st.c[i]>0);
const sorted=aliveIdx.sort((a,b)=> st.c[b]-st.c[a] || (st.h[a].filter(v=>v!==9).reduce((x,y)=>x+y,0)/(st.h[a].filter(v=>v!==9).length||99) - st.h[b].filter(v=>v!==9).reduce((x,y)=>x+y,0)/(st.h[b].filter(v=>v!==9).length||99)));
const winner=sorted[0], second=sorted[1];
const pot=NAMES.length*ENTRY, w=Math.floor(pot*0.7), sec=Math.floor(pot*0.2), e=pot-w-sec;
kicker.textContent='FINAL — PAYOUT';
title.textContent= winner!=null ? NAMES[winner]+' wins $'+w+' • '+(second!=null? NAMES[second]+' $'+sec : 'no second')+' • Early '+(st.eb!=null? NAMES[st.eb]+' $'+e : '— $'+e) : 'No winner';
desc.textContent='Pot $'+pot+' (14×$20). Winner 70% / Second 20% / Early Bird 10% (lowest avg first 3 rounds). You hold the cash — read this off your phone.';
} else if(s){
if(s.timeout){
kicker.textContent='ROUND '+s.round+' • TIMEOUT';
title.textContent=NAMES[s.pid]+' — not at table → loses a chip ('+s.before+' → '+s.after+')'+(s.out?' — OUT':'');
desc.textContent='Clock hit 0. Same as >4 shots: costs one chip. '+(s.out? NAMES[s.pid]+' is out of chips and done for the night.' : 'Still alive with '+s.after+' chips.');
} else {
const safe=s.shots<=4;
kicker.textContent='ROUND '+s.round+' • SHOT '+(idx+1)+' OF '+(total-1);
title.textContent=NAMES[s.pid]+' shoots '+s.shots+' '+(safe? '— safe' : '— costs a chip ('+s.before+' → '+s.after+')')+(s.out?' — OUT':'');
desc.textContent=safe? '4 or under keeps your chips. Low is good.' : '5 or more costs one chip. '+(s.out? 'That was the last chip.' : s.after+' chips left.');
}
}
// board
const maxRound=Math.max(...steps.filter(x=>!x.final).map(x=>x.round),1);
let html='<tr><th style="text-align:left;padding-left:10px">PLAYER</th>';
for(let r=1;r<=maxRound;r++) html+='<th>R'+r+'</th>';
html+='<th>CHIPS</th><th>AVG</th></tr>';
// sort by chips desc then avg
const order=NAMES.map((_,i)=>i).sort((a,b)=>{
if(st.c[a]!==st.c[b]) return st.c[b]-st.c[a];
const aa=st.h[a].filter(v=>v!==9), bb=st.h[b].filter(v=>v!==9);
const av=aa.length? aa.reduce((s,v)=>s+v,0)/aa.length : 99;
const bv=bb.length? bb.reduce((s,v)=>s+v,0)/bb.length : 99;
return av-bv;
});
order.forEach(pid=>{
const c=st.c[pid], h=st.h[pid];
const avg=h.filter(v=>v!==9).length? (h.filter(v=>v!==9).reduce((s,v)=>s+v,0)/h.filter(v=>v!==9).length).toFixed(1) : '—';
const isOut=c===0;
html+='<tr class="'+(isOut?'outrow':'')+'"><td class="name">'+NAMES[pid]+(st.eb===pid?' ★':'')+'</td>';
for(let r=1;r<=maxRound;r++){
const v=h[r-1];
if(v===undefined) html+='<td class="muted">—</td>';
else if(v===9) html+='<td class="cell-time">TIME</td>';
else if(v>4) html+='<td class="cell-hit">'+v+'</td>';
else html+='<td class="cell-safe">'+v+'</td>';
}
const dots=[0,1,2,3,4].map(k=>'<span class="'+(k<c?'on':'off')+'">●</span>').join('');
html+='<td class="chip">'+dots+' '+c+'</td><td>'+avg+'</td></tr>';
});
document.getElementById('board').innerHTML=html;
// log
const log=document.getElementById('log');
log.innerHTML='';
steps.slice(0,idx+1).forEach((st2,i)=>{
if(st2.final){
const d=document.createElement('div'); d.className=i===idx?'active':''; d.textContent='— FINAL — payout calculated'; log.appendChild(d); return;
}
const d=document.createElement('div'); d.className=i===idx?'active':'';
const txt= st2.timeout ? 'R'+st2.round+' — '+NAMES[st2.pid]+' TIMEOUT (−1 → '+st2.after+')'+(st2.out?' OUT':'') : 'R'+st2.round+' — '+NAMES[st2.pid]+' shoots '+st2.shots+(st2.lost?' (−1 → '+st2.after+')':' (safe)')+(st2.out?' OUT':'');
d.textContent=(i===idx?'▶ ':'')+txt;
log.appendChild(d);
});
log.scrollTop=log.scrollHeight;
}
function play(){
if(playing) return;
playing=true; document.getElementById('playBtn').textContent='⏸ PAUSE';
const sp=parseFloat(document.getElementById('speed').value);
const delay=Math.max(120, 900/sp);
timer=setInterval(()=>{
if(idx < total-1){ idx++; render(); } else { pause(); }
}, delay);
}
function pause(){
playing=false; document.getElementById('playBtn').textContent='▶ PLAY';
if(timer) clearInterval(timer);
}
document.getElementById('playBtn').addEventListener('click',()=> playing? pause(): play());
document.getElementById('prevBtn').addEventListener('click',()=>{ pause(); idx=Math.max(0,idx-1); render(); });
document.getElementById('nextBtn').addEventListener('click',()=>{ pause(); idx=Math.min(total-1,idx+1); render(); });
document.getElementById('scrub').addEventListener('input',e=>{ pause(); idx=parseInt(e.target.value,10); render(); });
document.getElementById('speed').addEventListener('input',e=>{
document.getElementById('speedV').textContent=e.target.value+'×';
if(playing){ pause(); play(); }
});
document.addEventListener('keydown',e=>{
if(e.key==='ArrowRight'){ pause(); idx=Math.min(total-1,idx+1); render(); }
if(e.key==='ArrowLeft'){ pause(); idx=Math.max(0,idx-1); render(); }
if(e.key===' '){ e.preventDefault(); playing? pause(): play(); }
});
render();
</script>
</body>
</html>