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">
<title>Thursday Pool — Console</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0b0e11;color:#e8e4d8;font-family:Georgia,'Times New Roman',serif;min-height:100vh}
.wrap{max-width:1100px;margin:0 auto;padding:12px}
header{display:flex;justify-content:space-between;align-items:baseline;border-bottom:2px solid #b3893a;padding-bottom:8px;margin-bottom:12px}
h1{font-size:22px;letter-spacing:2px;color:#b3893a;text-transform:uppercase}
.meta{font-size:14px;color:#8a8577}
.meta b{color:#e8e4d8}
/* TV board */
.board{border:1px solid #2a2f36;background:#101418;padding:14px}
.board h2{font-size:13px;letter-spacing:3px;color:#8a8577;text-transform:uppercase;margin-bottom:10px}
table{width:100%;border-collapse:collapse;font-size:17px}
th{font-size:11px;letter-spacing:2px;color:#6b665a;text-transform:uppercase;text-align:left;padding:4px 8px;border-bottom:1px solid #2a2f36}
td{padding:7px 8px;border-bottom:1px solid #1c2126}
tr.out td{color:#4a463d;text-decoration:line-through}
tr.now td{background:#1d2410;color:#f4e9b0;font-size:20px}
.chips span{display:inline-block;width:14px;height:14px;border-radius:50%;background:#b3893a;margin-right:3px;vertical-align:middle}
.chips .empty{background:#2a2f36}
/* turn panel */
.turn{margin-top:14px;border:1px solid #b3893a;background:#141109;padding:16px;text-align:center}
.turn .who{font-size:34px;color:#f4e9b0}
.turn .clock{font-size:64px;font-weight:bold;font-variant-numeric:tabular-nums;margin:6px 0}
.turn .clock.low{color:#d84b3a}
.turn .clock.paused{color:#6b665a}
.pips{font-size:26px;letter-spacing:10px;color:#b3893a}
.pips .down{color:#4a463d}
.shots{font-size:15px;color:#8a8577;margin-top:4px}
/* controls */
.controls{margin-top:14px;display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
button{font-family:inherit;font-size:16px;padding:14px 6px;background:#1c2126;color:#e8e4d8;border:1px solid #3a4048;cursor:pointer;border-radius:3px}
button:active{background:#2a3138}
button.big{font-size:20px;padding:20px 6px;background:#243010;border-color:#4a6b1a;color:#cfe89a}
button.warn{background:#301410;border-color:#6b2a1a;color:#e8a89a}
button.ghost{background:transparent;color:#8a8577;font-size:13px;padding:8px}
.row2{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-top:8px}
.tv body,.tv .controls,.tv .row2,.tv header .meta{display:none}
.tv .board{font-size:130%}
.tv .turn .who{font-size:56px}
.tv .turn .clock{font-size:110px}
.flash{animation:fl .5s infinite alternate}
@keyframes fl{from{background:#141109}to{background:#3a1408}}
/* settle modal */
.modal{position:fixed;inset:0;background:rgba(0,0,0,.85);display:none;align-items:center;justify-content:center;padding:20px}
.modal.show{display:flex}
.card{background:#101418;border:2px solid #b3893a;padding:24px;max-width:480px;width:100%}
.card h2{color:#b3893a;letter-spacing:2px;margin-bottom:14px;font-size:18px;text-transform:uppercase}
.card .line{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #1c2126;font-size:18px}
.card .line b{color:#f4e9b0}
.card .total{font-size:26px;margin-top:12px}
.card button{margin-top:16px;width:100%}
input{font-family:inherit;font-size:16px;padding:10px;background:#0b0e11;color:#e8e4d8;border:1px solid #3a4048;width:100%}
</style>
</head>
<body>
<div class="wrap" id="wrap">
<header>
<h1>Thursday Pool</h1>
<div class="meta">Round <b id="mRound">3</b> · Pot <b id="mPot">$240</b> · <b id="mAlive">11</b> alive
<button class="ghost" onclick="document.body.classList.toggle('tv')">TV</button>
</div>
</header>
<div class="board">
<h2>Standings</h2>
<table><thead><tr><th>Player</th><th>Chips</th><th>This round</th><th>Total shots</th></tr></thead>
<tbody id="tbody"></tbody></table>
</div>
<div class="turn" id="turnPanel">
<div class="who" id="tWho">—</div>
<div class="clock" id="tClock">1:30</div>
<div class="pips" id="tPips">● ● ●</div>
<div class="shots" id="tShots">0 shots</div>
</div>
<div class="controls">
<button class="big" onclick="doShot()">+ SHOT</button>
<button class="big" onclick="doBall()">BALL DOWN</button>
<button class="warn" onclick="doTimeout()">CLOCK — CHIP</button>
<button onclick="doUndo()">UNDO</button>
</div>
<div class="row2">
<button class="ghost" onclick="addPlayer()">+ Add player ($20)</button>
<button class="ghost" onclick="cycleClock()">Clock: <span id="clockLen">90s</span></button>
<button class="ghost" onclick="settle()">End night / Settle</button>
</div>
</div>
<div class="modal" id="modal"><div class="card" id="card"></div></div>
<script>
"use strict";
// ---------- state ----------
const NAMES=["Moose","Denny","Ray","Big Tom","Sully","Kev","Whistle","Petey","Frank","Doc","Lenny","Gibby"];
let clockLen=90, clockLeft=90, tick=null, history=[];
let S=null;
function newPlayer(name){return{name,chips:5,out:false,shotsThisRound:null,totalShots:0,rounds:0};}
function seed(){
S={players:NAMES.map(newPlayer),round:3,turnIdx:0,cur:null,pot:0,sidePotPaid:false,over:false};
// simulate rounds 1-2
S.players.forEach((p,i)=>{
let s1=3+(i*7)%4, s2=3+(i*5)%4;
p.totalShots=s1+s2; p.rounds=2;
if(s1>4)p.chips--; if(s2>4)p.chips--;
});
S.players[7].chips=0; S.players[7].out=true; // Whistle is done
S.pot=S.players.length*20;
// side pot after round 2: $2 x active, to fewest total shots
const act=S.players.filter(p=>!p.out);
const best=act.reduce((a,b)=>a.totalShots<=b.totalShots?a:b);
S.sidePotPaid=true;
S.sidePotWinner=best.name; S.sidePotAmt=act.length*2;
startTurn();
}
// ---------- turn engine ----------
function startTurn(){
const act=S.players.filter(p=>!p.out);
if(act.length<=1){finish();return;}
// advance to next active player
do{ S.turnIdx=(S.turnIdx+1)%S.players.length; }while(S.players[S.turnIdx].out);
const p=S.players[S.turnIdx];
S.cur={name:p.name,shots:0,balls:0,done:false};
clockLeft=clockLen;
render(); startClock();
}
function startClock(){
stopClock();
tick=setInterval(()=>{
clockLeft--;
if(clockLeft<=0){stopClock();clockLeft=0;render();document.getElementById('turnPanel').classList.add('flash');}
renderClock();
},1000);
}
function stopClock(){if(tick){clearInterval(tick);tick=null;}}
function cycleClock(){clockLen=clockLen===60?90:clockLen===90?120:60;clockLeft=clockLen;render();}
function push(){history.push(JSON.stringify({S,clockLeft}));if(history.length>200)history.shift();}
function doShot(){if(!S.cur||S.cur.done)return;push();S.cur.shots++;stopClock();render();}
function doBall(){
if(!S.cur||S.cur.done)return;push();
S.cur.balls++;S.cur.shots++;
if(S.cur.balls>=3)endTurn(false);
else{stopClock();render();}
}
function doTimeout(){
if(!S.cur||S.cur.done)return;push();
S.cur.done=true;stopClock();
const p=S.players[S.turnIdx];p.chips--;p.totalShots+=S.cur.shots;p.rounds++;
if(p.chips<=0){p.chips=0;p.out=true;log(p.name+" is OUT.");}
S.cur.done=true;render();
setTimeout(nextOrRound,900);
}
function endTurn(timedOut){
const p=S.players[S.turnIdx];
p.totalShots+=S.cur.shots;p.rounds++;
if(S.cur.shots>4){p.chips--;log(p.name+" took "+S.cur.shots+" — chip gone.");}
if(p.chips<=0){p.chips=0;p.out=true;log(p.name+" is OUT.");}
S.cur.done=true;render();
setTimeout(nextOrRound,900);
}
function nextOrRound(){
const act=S.players.filter(p=>!p.out);
if(act.length<=1){finish();return;}
// did the round complete? everyone active has shot this round
const shotCount=act.filter(p=>p.rounds>=S.round).length;
if(shotCount>=act.length){S.round++;log("— Round "+S.round+" —");}
startTurn();
}
function doUndo(){
if(!history.length)return;
const h=JSON.parse(history.pop());S=h.S;clockLeft=h.clockLeft;
document.getElementById('turnPanel').classList.remove('flash');
stopClock();if(S.cur&&!S.cur.done)startClock();
render();
}
function addPlayer(){
const n=prompt("Name?");if(!n)return;
S.players.push(newPlayer(n));S.pot+=20;render();
}
function log(m){console.log(m);}
// ---------- settlement ----------
function finish(){
S.over=true;stopClock();
const act=S.players.filter(p=>!p.out);
if(act.length>1){ // tie -> sudden death note
settleModal("Sudden death: "+act.map(p=>p.name).join(" vs ")+". Lowest shots next round takes it; run settle again after.");
return;
}
settleModal(null);
}
function settle(){
const act=S.players.filter(p=>!p.out);
if(act.length!==1){settleModal("More than one player still has chips. Play it out, or settle anyway?");return;}
settleModal(null);
}
function settleModal(note){
const act=S.players.filter(p=>!p.out);
const winner=act[0];
const rest=S.players.filter(p=>p.out).sort((a,b)=>b.rounds-a.rounds||a.totalShots-b.totalShots);
const second=rest[0];
let html="<h2>Settlement</h2>";
if(note)html+="<p style='color:#d8a84b;margin-bottom:10px'>"+note+"</p>";
html+="<div class='line'><span>Pot ("+S.players.length+" × $20)</span><b>$"+S.pot+"</b></div>";
if(S.sidePotPaid)html+="<div class='line'><span>Side pot (round 2 hot hand)</span><b>"+S.sidePotWinner+" — $"+S.sidePotAmt+"</b></div>";
if(second)html+="<div class='line'><span>Second: "+second.name+"</span><b>$20</b></div>";
if(winner)html+="<div class='line total'><span>Winner: "+winner.name+"</span><b>$"+(S.pot-20)+"</b></div>";
html+="<button onclick=\"document.getElementById('modal').classList.remove('show')\">Close</button>";
document.getElementById('card').innerHTML=html;
document.getElementById('modal').classList.add('show');
}
// ---------- render ----------
function render(){
document.getElementById('mRound').textContent=S.round;
document.getElementById('mPot').textContent="$"+S.pot;
const act=S.players.filter(p=>!p.out);
document.getElementById('mAlive').textContent=act.length;
const tb=document.getElementById('tbody');tb.innerHTML="";
const sorted=[...S.players].sort((a,b)=>(a.out-b.out)||(b.chips-a.chips)||(a.totalShots-b.totalShots));
sorted.forEach(p=>{
const tr=document.createElement('tr');
if(p.out)tr.className='out';
if(S.cur&&S.cur.name===p.name&&!p.out)tr.className='now';
let chips="";for(let i=0;i<5;i++)chips+="<span class='"+(i<p.chips?"":"empty")+"'></span>";
tr.innerHTML="<td>"+p.name+"</td><td class='chips'>"+chips+"</td><td>"+
(S.cur&&S.cur.name===p.name&&!p.out?S.cur.shots+" so far":(p.rounds>=S.round?"done":"—"))+
"</td><td>"+p.totalShots+"</td>";
tb.appendChild(tr);
});
if(S.cur){
document.getElementById('tWho').textContent=S.cur.name;
document.getElementById('tPips').innerHTML=[0,1,2].map(i=>"<span class='"+(i<S.cur.balls?"down":"")+"'>●</span>").join(" ");
document.getElementById('tShots').textContent=S.cur.shots+" shot"+(S.cur.shots===1?"":"s")+(S.cur.shots>4?" — over four, chip at risk":"");
}
renderClock();
}
function renderClock(){
const el=document.getElementById('tClock');
const m=Math.floor(clockLeft/60),s=clockLeft%60;
el.textContent=m+":"+String(s).padStart(2,"0");
el.className="clock"+(clockLeft<=15?" low":"")+(tick?"":" paused");
}
seed();
</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 Pool — Playback</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0b0e11;color:#e8e4d8;font-family:Georgia,'Times New Roman',serif}
.wrap{max-width:1100px;margin:0 auto;padding:12px}
header{display:flex;justify-content:space-between;align-items:baseline;border-bottom:2px solid #b3893a;padding-bottom:8px;margin-bottom:12px}
h1{font-size:20px;letter-spacing:2px;color:#b3893a;text-transform:uppercase}
.board{border:1px solid #2a2f36;background:#101418;padding:14px}
.board h2{font-size:12px;letter-spacing:3px;color:#8a8577;text-transform:uppercase;margin-bottom:8px}
table{width:100%;border-collapse:collapse;font-size:15px}
th{font-size:10px;letter-spacing:2px;color:#6b665a;text-transform:uppercase;text-align:left;padding:3px 8px;border-bottom:1px solid #2a2f36}
td{padding:5px 8px;border-bottom:1px solid #1c2126}
tr.out td{color:#4a463d;text-decoration:line-through}
tr.now td{background:#1d2410;color:#f4e9b0}
.chips span{display:inline-block;width:11px;height:11px;border-radius:50%;background:#b3893a;margin-right:2px}
.chips .empty{background:#2a2f36}
.event{margin:14px 0;border:1px solid #b3893a;background:#141109;padding:16px;min-height:110px}
.event .tag{font-size:11px;letter-spacing:3px;color:#8a8577;text-transform:uppercase}
.event .txt{font-size:24px;color:#f4e9b0;margin-top:8px}
.event .sub{font-size:14px;color:#8a8577;margin-top:6px}
.transport{display:flex;gap:8px;align-items:center;margin-top:10px;flex-wrap:wrap}
button{font-family:inherit;font-size:15px;padding:10px 16px;background:#1c2126;color:#e8e4d8;border:1px solid #3a4048;cursor:pointer;border-radius:3px}
button:disabled{opacity:.35;cursor:default}
input[type=range]{flex:1;min-width:160px;accent-color:#b3893a}
.stepno{font-size:13px;color:#8a8577;font-variant-numeric:tabular-nums;min-width:90px;text-align:right}
.speed{display:flex;align-items:center;gap:6px;font-size:13px;color:#8a8577}
.log{margin-top:10px;font-size:13px;color:#6b665a;max-height:110px;overflow-y:auto;border-top:1px solid #1c2126;padding-top:8px;line-height:1.7}
</style>
</head>
<body>
<div class="wrap">
<header><h1>Thursday Pool — Playback</h1><div style="font-size:13px;color:#8a8577">seed 20240104 · same rules as the console</div></header>
<div class="board"><h2>Scorecard</h2>
<table><thead><tr><th>Player</th><th>Chips</th><th>Rounds</th><th>Total shots</th><th>Status</th></tr></thead><tbody id="tbody"></tbody></table>
</div>
<div class="event"><div class="tag" id="eTag">ready</div><div class="txt" id="eTxt">Press play.</div><div class="sub" id="eSub"></div></div>
<div class="transport">
<button id="bBack" onclick="stepTo(idx-1)">◀ Back</button>
<button id="bPlay" onclick="togglePlay()">Play</button>
<button id="bFwd" onclick="stepTo(idx+1)">Step ▶</button>
<input type="range" id="scrub" min="0" value="0" oninput="stepTo(+this.value)">
<span class="stepno" id="stepno">0 / 0</span>
<span class="speed">speed <input type="range" id="spd" min="1" max="10" value="4" style="width:90px" oninput="if(playing){pause();play()}"></span>
</div>
<div class="log" id="log"></div>
</div>
<script>
"use strict";
// ---------- seeded RNG ----------
function mulberry32(a){return function(){a|=0;a=a+0x6D2B79F5|0;var t=Math.imul(a^a>>>15,1|a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;}}
const rnd=mulberry32(20240104);
const ri=n=>Math.floor(rnd()*n);
const pick=a=>a[ri(a.length)];
// ---------- generate the night as a list of steps ----------
const NAMES=["Moose","Denny","Ray","Big Tom","Sully","Kev","Whistle","Petey","Frank","Doc","Lenny","Gibby"];
const steps=[];
function ev(tag,txt,sub){steps.push({tag,txt,sub:sub||""});}
let players=NAMES.map(n=>({name:n,chips:5,out:false,total:0,rounds:0}));
let pot=players.length*20, round=1, over=false;
ev("OPEN","Doors open. Twelve names on the pad.","Pot: $"+pot+" — twenty a head, five chips each.");
// side pot after round 2
let sidePotDone=false, sidePotWinner=null, sidePotAmt=0;
while(!over && round<=12){
ev("ROUND","Round "+round+".","Nobody's in a hurry.");
const act=()=>players.filter(p=>!p.out);
for(const p of players){
if(p.out)continue;
const timeout=rnd()<0.08;
let shots=timeout?ri(3):3+ri(5); // 3..7, timeouts cut short
ev("TURN",p.name+" steps up.","Clock starts: 90 seconds.");
for(let b=0;b<3;b++){
if(timeout&&b===2)break;
ev("SHOT",p.name+" shoots — ball "+(b+1)+" down.","Shot "+(b+1)+" of the turn.");
}
p.rounds++;p.total+=shots;
if(timeout){
p.chips--;
ev("CLOCK","Clock beats "+p.name+". Chip gone.",""+p.chips+" chip"+(p.chips===1?"":"s")+" left.");
}else if(shots>4){
p.chips--;
ev("TURN",p.name+" clears in "+shots+" — over four. Chip gone.",""+p.chips+" chip"+(p.chips===1?"":"s")+" left.");
}else{
ev("TURN",p.name+" clears in "+shots+". Clean.",""+p.chips+" chips left.");
}
if(p.chips<=0){p.chips=0;p.out=true;ev("OUT",p.name+" is out. Back to drinking.",act().length+" still alive.");}
if(act().length<=1){over=true;break;}
}
if(!sidePotDone&&round===2&&!over){
sidePotDone=true;
const a=act();
sidePotAmt=a.length*2;
sidePotWinner=a.reduce((x,y)=>x.total<=y.total?x:y).name;
ev("SIDE POT","Side pot: "+sidePotWinner+" takes $"+sidePotAmt+" for the hot hand through two rounds.","$2 a head from everyone still in.");
}
round++;
}
const winner=act()[0];
const rest=players.filter(p=>p.out).sort((a,b)=>b.rounds-a.rounds||a.total-b.total);
const second=rest[0];
ev("WINNER",winner.name+" takes the table.","Last man with chips.");
ev("SETTLE","Settlement: pot $"+pot+". "+second.name+" gets $20 for second. "+winner.name+" gets $"+(pot-20)+".",
sidePotDone?"Plus the side pot: "+sidePotWinner+" — $"+sidePotAmt+".":"No side pot.");
// ---------- replay engine: state = fold(steps[0..idx]) ----------
function stateAt(n){
const P=NAMES.map(nm=>({name:nm,chips:5,out:false,total:0,rounds:0}));
let pot=players.length*20, round=1, cur=null, side=null;
for(let i=1;i<=n;i++){
const s=steps[i], t=s.tag;
if(t==="ROUND")round++;
else if(t==="TURN"&&s.txt.indexOf("clears")>=0||t==="CLOCK"){
// shots already counted per SHOT events; resolve chip loss here
}
}
// simpler: recompute by replaying SHOT/TURN/CLOCK/OUT tags
P.forEach(p=>{p.chips=5;p.out=false;p.total=0;p.rounds=0;});
let shooter=null,shots=0;
for(let i=1;i<=n;i++){
const s=steps[i],t=s.tag;
if(t==="ROUND"){round++;}
else if(t==="TURN"&&s.txt.indexOf("steps up")>=0){shooter=P.find(p=>s.txt.startsWith(p.name));shots=0;}
else if(t==="SHOT"){shots++;}
else if(t==="TURN"&&shooter){shooter.rounds++;shooter.total+=shots;if(shots>4)shooter.chips--;shooter=null;}
else if(t==="CLOCK"&&shooter){shooter.rounds++;shooter.total+=shots;shooter.chips--;shooter=null;}
else if(t==="OUT"){const p=P.find(q=>s.txt.startsWith(q.name+" is out"));if(p){p.out=true;p.chips=0;}}
else if(t==="SIDE POT"){side={w:s.txt.split(" takes")[0],a:+s.txt.split("$")[1]};}
else if(t==="WINNER"||t==="SETTLE"){/* end state */}
}
return {P,round,cur:shooter,side};
}
let idx=0, playing=false, timer=null;
function stepTo(n){
n=Math.max(0,Math.min(steps.length-1,n));
idx=n;
const s=steps[idx];
document.getElementById('eTag').textContent=s.tag;
document.getElementById('eTxt').textContent=s.txt;
document.getElementById('eSub').textContent=s.sub;
document.getElementById('scrub').value=idx;
document.getElementById('stepno').textContent=idx+" / "+(steps.length-1);
document.getElementById('bBack').disabled=idx===0;
document.getElementById('bFwd').disabled=idx===steps.length-1;
// scorecard
const st=stateAt(idx);
const tb=document.getElementById('tbody');tb.innerHTML="";
[...st.P].sort((a,b)=>(a.out-b.out)||(b.chips-a.chips)||(a.total-b.total)).forEach(p=>{
const tr=document.createElement('tr');
if(p.out)tr.className='out';
if(st.cur&&st.cur.name===p.name)tr.className='now';
let c="";for(let i=0;i<5;i++)c+="<span class='"+(i<p.chips?"":"empty")+"'></span>";
tr.innerHTML="<td>"+p.name+"</td><td class='chips'>"+c+"</td><td>"+p.rounds+"</td><td>"+p.total+"</td><td>"+
(p.out?"out":(st.cur&&st.cur.name===p.name?"shooting":"in"))+"</td>";
tb.appendChild(tr);
});
// log tail
const lg=document.getElementById('log');
lg.innerHTML=steps.slice(Math.max(0,idx-8),idx+1).map((e,i)=>(Math.max(0,idx-8)+i)+". <b>"+e.tag+"</b> "+e.txt).join("<br>");
lg.scrollTop=lg.scrollHeight;
}
function togglePlay(){playing?pause():play();}
function play(){
if(idx>=steps.length-1)stepTo(0);
playing=true;document.getElementById('bPlay').textContent="Pause";
const spd=+document.getElementById('spd').value;
timer=setInterval(()=>{if(idx>=steps.length-1){pause();return;}stepTo(idx+1);},2200/spd);
}
function pause(){playing=false;clearInterval(timer);document.getElementById('bPlay').textContent="Play";}
stepTo(0);
</script>
</body>
</html>