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,viewport-fit=cover">
<title>Thursday Pool — Console</title>
<style>
:root{--bg:#07090c;--pn:#12161d;--ln:#252d3a;--fg:#f4f7fb;--dim:#8b97a8;--gr:#35e08a;--rd:#ff4f57;--am:#ffc233;--bl:#4aa3ff}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;overflow:hidden;background:var(--bg);color:var(--fg);
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;-webkit-text-size-adjust:100%}
body{display:flex;flex-direction:column}
#board{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
#hero{position:relative;padding:1.4vmin 2vmin .8vmin;border-bottom:2px solid var(--ln);background:#0b0e13}
#meta{font-size:clamp(11px,2.1vmin,26px);letter-spacing:.14em;color:var(--dim);text-transform:uppercase;font-weight:700}
#now{display:flex;align-items:baseline;gap:2vmin;margin-top:.4vmin}
#nowname{font-size:clamp(28px,7.4vmin,120px);font-weight:900;line-height:1;letter-spacing:-.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#clk{margin-left:auto;font-variant-numeric:tabular-nums;font-size:clamp(24px,6.4vmin,104px);font-weight:900;line-height:1;color:var(--gr);padding:0 .5vmin}
#clk.warn{color:var(--am)}#clk.dead{color:var(--rd)}#clk.pause{color:var(--dim)}
#deck{font-size:clamp(11px,2.1vmin,26px);color:var(--dim);margin-top:.4vmin;font-weight:600}
#tvbtn{position:absolute;top:.6vmin;right:.6vmin;background:#1b2230;color:var(--dim);border:1px solid var(--ln);
border-radius:8px;font-size:12px;padding:6px 9px;font-weight:800}
#rows{flex:1 1 auto;overflow:hidden;display:flex;flex-direction:column;padding:.4vmin 0}
.row{display:flex;align-items:center;gap:1.2vmin;padding:.25vmin 2vmin;flex:1 1 0;min-height:0;border-bottom:1px solid #10141b}
.row.up{background:#152232}
.rk{width:2.6ch;color:var(--dim);font-size:clamp(11px,2.4vmin,30px);font-weight:800;text-align:right}
.nm{flex:1 1 auto;font-size:clamp(15px,3.5vmin,52px);font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pips{display:flex;gap:.7vmin}
.pip{width:clamp(9px,2.1vmin,30px);height:clamp(9px,2.1vmin,30px);border-radius:50%;background:#20293a;display:block}
.pip.on{background:var(--gr)}
.st{width:6.5ch;text-align:right;font-variant-numeric:tabular-nums;font-size:clamp(12px,2.6vmin,34px);color:var(--dim);font-weight:700}
.st b{color:var(--fg)}
.lastc{width:3.5ch;text-align:center;font-size:clamp(12px,2.6vmin,34px);font-weight:900;font-variant-numeric:tabular-nums}
.row.out{opacity:.42}.row.out .nm{text-decoration:line-through}
#tick{border-top:2px solid var(--ln);background:#0b0e13;padding:.6vmin 2vmin;font-size:clamp(11px,2.3vmin,28px);font-weight:700;min-height:3.6vmin}
#tick .g{color:var(--gr)}#tick .b{color:var(--rd)}#tick .o{color:var(--am)}#tick .d{color:var(--dim)}
#rules{padding:.3vmin 2vmin .5vmin;color:#4d5769;font-size:clamp(9px,1.7vmin,20px);letter-spacing:.1em;font-weight:700;text-transform:uppercase}
#pad{flex:0 0 auto;display:grid;grid-template-columns:repeat(4,1fr);gap:6px;padding:6px;background:#040507;border-top:2px solid var(--ln)}
#pad button{border:0;border-radius:12px;background:var(--pn);color:var(--fg);font-size:clamp(17px,4.4vmin,30px);
font-weight:900;padding:16px 4px;min-height:58px;font-family:inherit}
#pad button:active{filter:brightness(1.5)}
.bgr{background:#0f3b26;color:var(--gr);box-shadow:inset 0 0 0 2px #1d6b45}
.brd{background:#3d1418;color:var(--rd);box-shadow:inset 0 0 0 2px #7a2029}
.bam{background:#3d3010;color:var(--am);box-shadow:inset 0 0 0 2px #7a5f16}
.bsm{font-size:clamp(11px,2.6vmin,18px);letter-spacing:.06em;background:#161b24;color:#b9c3d1}
body.tv #pad{display:none}
#pay{flex:1 1 auto;overflow:auto;padding:2vmin;display:none}
#pay h1{font-size:clamp(24px,6vmin,90px);margin:0 0 1vmin;letter-spacing:-.02em}
.potline{color:var(--am);font-size:clamp(13px,2.8vmin,34px);font-weight:800;margin-bottom:1.5vmin}
.payrow{display:flex;align-items:baseline;gap:1.5vmin;border-bottom:1px solid var(--ln);padding:1vmin 0}
.payrow .n{flex:1 1 auto;font-size:clamp(18px,4.6vmin,64px);font-weight:900}
.payrow .a{font-size:clamp(20px,5.2vmin,72px);font-weight:900;color:var(--gr);font-variant-numeric:tabular-nums}
.why{color:var(--dim);font-size:clamp(10px,2vmin,24px);font-weight:700;padding:.3vmin 0 1vmin}
.fin{color:var(--dim);font-size:clamp(11px,2.2vmin,26px);margin-top:2vmin;line-height:1.6;font-weight:600}
#ovl{position:fixed;inset:0;background:rgba(3,5,8,.94);display:none;align-items:center;justify-content:center;padding:14px;z-index:9}
#ovl .card{background:var(--pn);border:2px solid var(--ln);border-radius:16px;padding:14px;width:100%;max-width:520px;max-height:92%;overflow:auto}
#ovl h2{margin:0 0 10px;font-size:19px;letter-spacing:.06em;text-transform:uppercase;color:var(--dim)}
#ovl input{width:100%;font-size:24px;padding:12px;border-radius:10px;border:2px solid var(--ln);background:#080b10;color:var(--fg);font-family:inherit;font-weight:800}
#ovl button{width:100%;margin-top:8px;padding:15px;border-radius:12px;border:0;font-size:18px;font-weight:900;background:#1d2532;color:var(--fg);font-family:inherit}
#ovl .ok{background:#0f3b26;color:var(--gr)}#ovl .no{background:#3d1418;color:var(--rd)}
</style></head><body>
<div id="board">
<div id="hero">
<button id="tvbtn">BOARD</button>
<div id="meta"></div>
<div id="now"><span id="nowname"></span><span id="clk">1:30</span></div>
<div id="deck"></div>
</div>
<div id="rows"></div>
<div id="tick"></div>
<div id="rules"></div>
<div id="pay"></div>
</div>
<div id="pad">
<button class="bgr" data-a="shot" data-n="3">3</button>
<button class="bgr" data-a="shot" data-n="4">4</button>
<button class="brd" data-a="shot" data-n="5">5</button>
<button class="brd" data-a="shot" data-n="6">6</button>
<button class="brd" data-a="shot" data-n="7">7</button>
<button class="brd" data-a="shot" data-n="8">8</button>
<button class="brd" data-a="shot" data-n="9">9+</button>
<button class="bam" data-a="clock">CLOCK</button>
<button class="bsm" data-a="undo">UNDO</button>
<button class="bsm" data-a="add">+ BUY IN</button>
<button class="bsm" data-a="walk">WALKED</button>
<button class="bsm" data-a="menu">MENU</button>
</div>
<div id="ovl"></div>
<script>
/* ===================== HOUSE RULES ENGINE =====================
Identical text in console and playback. A night is a list of
events; the state is always reduce(events). Undo = pop. */
var R={BUYIN:20,CHIPS:5,PAR:4,CLOCK:90,W1:0.70,W2:0.20,WHOT:0.10,HOTR:3,HOTMIN:2};
function mkP(n){return{name:n,chips:R.CHIPS,alive:true,out:null,outRound:null,turns:0,shots:0,clocks:0,rounds:{}};}
function mkS(){return{roster:[],cur:0,round:1,over:false,endType:null,elim:[],feed:[],results:null};}
function avgOf(p){return p.turns?p.shots/p.turns:99;}
function aliveList(S){var a=[],i;for(i=0;i<S.roster.length;i++){if(S.roster[i].alive)a.push(S.roster[i]);}return a;}
function curP(S){return S.roster[S.cur]||null;}
function say(S,t,k){S.feed.push({r:S.round,t:t,k:k||'d'});}
function advance(S){var n=S.roster.length;if(!n)return;var i=S.cur,r=S.round,k;
for(k=0;k<n*4+4;k++){i++;if(i>=n){i=0;r++;}if(S.roster[i]&&S.roster[i].alive){S.cur=i;S.round=r;return;}}
S.cur=0;S.round=r;}
function r5(x){return Math.round(x/5)*5;}
function hotHand(S){var best=null,i,p,k,tot,cnt,a;
for(i=0;i<S.roster.length;i++){p=S.roster[i];tot=0;cnt=0;
for(k=1;k<=R.HOTR;k++){if(p.rounds[k]&&p.rounds[k].n!=null){tot+=p.rounds[k].n;cnt++;}}
if(cnt>=R.HOTMIN){a=tot/cnt;if(!best||a<best.a-1e-9)best={p:p,a:a,cnt:cnt};}}
return best;}
function finish(S,type){
S.over=true;S.endType=type;var i;
var alv=aliveList(S).slice().sort(function(a,b){
return (b.chips-a.chips)||(avgOf(a)-avgOf(b))||(S.roster.indexOf(a)-S.roster.indexOf(b));});
var order=alv.slice(),el=S.elim.slice().reverse();
for(i=0;i<el.length;i++)order.push(S.roster[el[i].i]);
var tie=[];
if(alv.length===0&&S.elim.length){
var lastR=S.elim[S.elim.length-1].round,grp=[];
for(i=0;i<S.elim.length;i++)if(S.elim[i].round===lastR&&S.elim[i].how==='busted')grp.push(S.roster[S.elim[i].i]);
if(grp.length>1)tie=grp;}
var pot=R.BUYIN*S.roster.length;
var a=r5(pot*R.W1),b=r5(pot*R.W2),h=r5(pot*R.WHOT);
a+=pot-(a+b+h);
var hot=hotHand(S),pay={},why={};
function give(n,amt,rsn){if(!n||!amt)return;pay[n]=(pay[n]||0)+amt;why[n]=why[n]?why[n]+' + '+rsn:rsn;}
if(tie.length>1){
var pool=a+b,each=Math.floor(pool/tie.length/5)*5,rem=pool-each*tie.length;
for(i=0;i<tie.length;i++)give(tie[i].name,each+(i===0?rem:0),'tied for the pot');
}else{
give(order[0]?order[0].name:'HOUSE',a+(order[1]?0:b),'winner');
if(order[1])give(order[1].name,b,'runner-up');}
if(hot)give(hot.p.name,h,'hot start ('+hot.a.toFixed(2)+' avg thru R'+R.HOTR+')');
else if(order[0])give(order[0].name,h,'no hot-start qualifier, rolls up');
var lines=[],k;for(k in pay)lines.push({name:k,amt:pay[k],why:why[k]});
lines.sort(function(x,y){return y.amt-x.amt;});
S.results={pot:pot,lines:lines,order:order,tie:tie,hot:hot,type:type};
say(S,'NIGHT OVER — '+(tie.length>1?'tied at the top':(order[0]?order[0].name+' takes it':'nobody left')),'o');}
function checkOver(S){if(!S.over&&S.roster.length&&aliveList(S).length<=1)finish(S,'natural');}
function apply(S,e){
if(e.t==='join'){var np=mkP(e.name);S.roster.push(np);
say(S,np.name+' bought in — $'+R.BUYIN+', '+R.CHIPS+' chips','g');return;}
if(S.over)return;
if(e.t==='end'){finish(S,'called');return;}
if(e.t==='quit'){var q=S.roster[e.i];if(!q||!q.alive)return;
q.alive=false;q.out='walked';q.outRound=S.round;S.elim.push({i:e.i,round:S.round,how:'walked'});
say(S,q.name+' walked out — forfeits '+q.chips+' chip'+(q.chips===1?'':'s'),'b');
if(S.cur===e.i)advance(S);checkOver(S);return;}
var p=curP(S);if(!p||!p.alive)return;
if(e.t==='shot'){
p.turns++;p.shots+=e.n;p.rounds[S.round]={n:e.n};
if(e.n>R.PAR){p.chips--;p.rounds[S.round].pen=1;
say(S,p.name+' cleared in '+e.n+' — over par, -1 chip ('+Math.max(0,p.chips)+' left)','b');}
else say(S,p.name+' cleared in '+e.n+' — safe','g');
}else if(e.t==='clock'){
p.clocks++;p.chips--;p.rounds[S.round]={c:1};
say(S,p.name+' missed the clock — -1 chip ('+Math.max(0,p.chips)+' left)','b');
}else return;
if(p.chips<=0){p.chips=0;p.alive=false;p.out='busted';p.outRound=S.round;
S.elim.push({i:S.cur,round:S.round,how:'busted'});
say(S,p.name+' is out of chips — done, back to drinking','o');}
advance(S);checkOver(S);}
function reduce(events){var S=mkS(),i;for(i=0;i<events.length;i++)apply(S,events[i]);return S;}
function rng(s){s=s>>>0;return function(){s=(Math.imul(s,1664525)+1013904223)>>>0;return s/4294967296;};}
/* =================== END ENGINE =================== */
var NAMES=['Diesel','Marge','Tank','Kelso','Birdy','Sully','Ramona','Hoss','Pepper','Duke','Fitz','Wanda','Moose','Vera'];
function seedNight(){
var rnd=rng(48271),ev=[],i,S,g=0,tbl=[3,3,4,4,4,4,4,5,5,6,7,8];
for(i=0;i<NAMES.length;i++)ev.push({t:'join',name:NAMES[i]});
S=reduce(ev);
while(g++<33&&!S.over){
if(rnd()<0.05)ev.push({t:'clock'});else ev.push({t:'shot',n:tbl[Math.floor(rnd()*tbl.length)]});
S=reduce(ev);}
return ev;}
var EV=seedNight(),S=reduce(EV),clk=R.CLOCK,run=true,tk=S.round+'/'+S.cur;
var $=function(id){return document.getElementById(id);};
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
function mmss(s){var m=Math.floor(s/60);var x=s%60;return m+':'+(x<10?'0':'')+x;}
function Do(e){EV.push(e);after();}
function undo(){if(EV.length)EV.pop();after();}
function after(){S=reduce(EV);var k=S.round+'/'+S.cur;if(k!==tk){tk=k;clk=R.CLOCK;run=!S.over;}render();}
function pips(c){var s='',i;for(i=0;i<R.CHIPS;i++)s+='<span class="pip'+(i<c?' on':'')+'"></span>';return s;}
function lastCell(p,rd){var c=p.rounds[rd]||p.rounds[rd-1];if(!c)return '<span style="color:#3b4454">-</span>';
if(c.c)return '<span style="color:var(--am)">C</span>';
return '<span style="color:'+(c.pen?'var(--rd)':'var(--gr)')+'">'+c.n+'</span>';}
function render(){
if(S.over){$('pay').style.display='block';$('rows').style.display='none';$('tick').style.display='none';
$('rules').style.display='none';renderPay();$('meta').textContent='NIGHT SETTLED';
$('nowname').textContent='PAY UP';$('clk').textContent='';$('deck').textContent='';return;}
$('pay').style.display='none';$('rows').style.display='flex';$('tick').style.display='block';$('rules').style.display='block';
var alv=aliveList(S),pot=R.BUYIN*S.roster.length,p=curP(S);
$('meta').textContent='ROUND '+S.round+' \u00b7 POT $'+pot+' \u00b7 '+alv.length+' LEFT OF '+S.roster.length;
if(!S.roster.length){$('nowname').textContent='NO PLAYERS';$('deck').textContent='tap + BUY IN';}
else{
$('nowname').textContent=p?p.name.toUpperCase():'\u2014';
var deck=[],i=S.cur,n=S.roster.length,g=0;
while(deck.length<3&&g++<n*2){i++;if(i>=n)i=0;if(i===S.cur)break;if(S.roster[i].alive)deck.push(S.roster[i].name);}
$('deck').textContent=deck.length?'on deck: '+deck.join(' \u00b7 '):'last one at the table';}
paintClock();
var list=S.roster.slice().sort(function(a,b){
if(a.alive!==b.alive)return a.alive?-1:1;
if(a.alive)return (b.chips-a.chips)||(avgOf(a)-avgOf(b))||a.name.localeCompare(b.name);
return (b.outRound-a.outRound)||a.name.localeCompare(b.name);});
var h='',k,q,rk=0;
for(k=0;k<list.length;k++){q=list[k];
if(q.alive)rk++;
h+='<div class="row'+(q===p?' up':'')+(q.alive?'':' out')+'">'
+'<span class="rk">'+(q.alive?rk:'\u2013')+'</span>'
+'<span class="nm">'+esc(q.name)+'</span>'
+'<span class="pips">'+pips(q.chips)+'</span>'
+'<span class="lastc">'+lastCell(q,S.round)+'</span>'
+'<span class="st">'+(q.turns?'<b>'+avgOf(q).toFixed(2)+'</b> avg':' ')+'</span>'
+'</div>';}
$('rows').innerHTML=h;
var f=S.feed.slice(-3).reverse(),t='';
for(k=0;k<f.length;k++)t+='<div class="'+f[k].k+'">'+(k?'':'\u25b8 ')+'R'+f[k].r+' '+esc(f[k].t)+'</div>';
$('tick').innerHTML=t;
$('rules').textContent='PAR '+R.PAR+' \u00b7 OVER PAR = 1 CHIP \u00b7 CLOCK '+mmss(R.CLOCK)+' \u00b7 $'+R.BUYIN+' IN, WINNER TAKES '+Math.round(R.W1*100)+'%';
}
function paintClock(){
var e=$('clk');if(S.over||!S.roster.length){e.textContent='';return;}
e.textContent=clk<=0?'CLOCK':mmss(clk);
e.className=clk<=0?'dead':(!run?'pause':(clk<=15?'warn':''));}
function renderPay(){
var r=S.results,h='',i,q;
h+='<h1>PAY OUT</h1><div class="potline">POT $'+r.pot+' \u00b7 '+S.roster.length+' buy-ins @ $'+R.BUYIN
+(r.type==='called'?' \u00b7 night called by the house':'')+'</div>';
var tot=0;
for(i=0;i<r.lines.length;i++){tot+=r.lines[i].amt;
h+='<div class="payrow"><span class="n">'+esc(r.lines[i].name)+'</span><span class="a">$'+r.lines[i].amt+'</span></div>'
+'<div class="why">'+esc(r.lines[i].why)+'</div>';}
h+='<div class="potline">TOTAL OUT $'+tot+' \u00b7 you\u2019re square</div>';
h+='<div class="fin"><b>Finish order:</b> ';
for(i=0;i<r.order.length;i++){q=r.order[i];
h+=(i?' \u00b7 ':'')+(i+1)+'. '+esc(q.name)+' ('+(q.alive?q.chips+' chips':'out R'+q.outRound)+')';}
h+='<br><b>Averages:</b> ';
var ss=S.roster.slice().sort(function(a,b){return avgOf(a)-avgOf(b);});
for(i=0;i<ss.length;i++)h+=(i?' \u00b7 ':'')+esc(ss[i].name)+' '+(ss[i].turns?avgOf(ss[i]).toFixed(2):'\u2013');
h+='</div><div style="margin-top:2vmin"><button onclick="reopen()" style="padding:14px 18px;border:0;border-radius:12px;background:#1d2532;color:#f4f7fb;font-weight:900;font-size:16px;font-family:inherit">UNDO SETTLE \u2014 KEEP PLAYING</button></div>';
$('pay').innerHTML=h;}
function reopen(){for(var i=EV.length-1;i>=0;i--){if(EV[i].t==='end'){EV.splice(i,1);break;}}after();}
$('pad').addEventListener('click',function(e){
var b=e.target;while(b&&b.tagName!=='BUTTON')b=b.parentNode;if(!b)return;
var a=b.getAttribute('data-a');
if(a==='shot'){if(curP(S))Do({t:'shot',n:+b.getAttribute('data-n')});}
else if(a==='clock'){if(curP(S))Do({t:'clock'});}
else if(a==='undo')undo();
else if(a==='add')openAdd();
else if(a==='walk')openWalk();
else if(a==='menu')openMenu();});
$('clk').addEventListener('click',function(){run=!run;paintClock();});
$('tvbtn').addEventListener('click',function(){document.body.classList.toggle('tv');
$('tvbtn').textContent=document.body.classList.contains('tv')?'CONTROLS':'BOARD';});
function ovl(h){var o=$('ovl');o.innerHTML='<div class="card">'+h+'</div>';o.style.display='flex';}
function closeOvl(){$('ovl').style.display='none';}
function openAdd(){ovl('<h2>Buy in \u2014 $'+R.BUYIN+'</h2><input id="nm" autocapitalize="words" placeholder="name">'
+'<button class="ok" onclick="addP()">ADD TO THE TABLE</button><button onclick="closeOvl()">cancel</button>');
setTimeout(function(){var n=$('nm');if(n)n.focus();},60);}
function addP(){var v=($('nm').value||'').trim();if(v){Do({t:'join',name:v.slice(0,16)});}closeOvl();}
function openWalk(){var h='<h2>Who walked out?</h2>',i,q;
for(i=0;i<S.roster.length;i++){q=S.roster[i];if(!q.alive)continue;
h+='<button onclick="walk('+i+')">'+esc(q.name)+' \u2014 '+q.chips+' chips</button>';}
h+='<button class="no" onclick="closeOvl()">cancel</button>';ovl(h);}
function walk(i){Do({t:'quit',i:i});closeOvl();}
function openMenu(){ovl('<h2>Menu</h2>'
+'<button class="no" onclick="endNight()">END NIGHT NOW \u2014 SETTLE UP</button>'
+'<button onclick="undo();closeOvl()">undo last entry</button>'
+'<button onclick="resetDemo()">reset to demo game</button>'
+'<button onclick="emptyTable()">empty table (new night)</button>'
+'<button onclick="closeOvl()">back</button>');}
function endNight(){Do({t:'end'});closeOvl();}
function resetDemo(){EV=seedNight();tk='';after();closeOvl();}
function emptyTable(){EV=[];tk='';after();closeOvl();}
setInterval(function(){if(run&&!S.over&&clk>0){clk--;paintClock();}},1000);
render();
</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>
:root{--bg:#07090c;--pn:#12161d;--ln:#252d3a;--fg:#f4f7fb;--dim:#8b97a8;--gr:#35e08a;--rd:#ff4f57;--am:#ffc233;--bl:#4aa3ff}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;overflow:hidden;background:var(--bg);color:var(--fg);
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif}
body{display:flex;flex-direction:column}
header{padding:8px 12px;border-bottom:2px solid var(--ln);background:#0b0e13;flex:0 0 auto}
#hd{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;font-size:12px;letter-spacing:.13em;
text-transform:uppercase;color:var(--dim);font-weight:800}
#hd b{color:var(--fg)}
#narr{margin-top:6px;font-size:clamp(16px,3vw,30px);font-weight:900;line-height:1.25}
#narr.g{color:var(--gr)}#narr.b{color:var(--rd)}#narr.o{color:var(--am)}#narr.d{color:var(--fg)}
#prev2{margin-top:3px;color:#5d6779;font-size:12px;font-weight:700;min-height:15px}
#mid{flex:1 1 auto;overflow:auto;padding:0 0 8px}
table{border-collapse:separate;border-spacing:0;width:100%;font-variant-numeric:tabular-nums}
th,td{padding:5px 6px;font-size:13px;text-align:center;white-space:nowrap;border-bottom:1px solid #141922}
th{position:sticky;top:0;background:#0b0e13;color:var(--dim);font-size:11px;letter-spacing:.1em;z-index:2}
td.nm,th.nm{position:sticky;left:0;background:#0d1117;text-align:left;font-weight:800;font-size:14px;z-index:1;
max-width:130px;overflow:hidden;text-overflow:ellipsis}
tr.up td{background:#152232}tr.up td.nm{background:#1a2a3d}
tr.out{opacity:.45}tr.out td.nm{text-decoration:line-through}
td.pen{color:var(--rd);font-weight:900}td.ok{color:var(--gr);font-weight:800}
td.clk{color:var(--am);font-weight:900}td.emp{color:#2c3444}
td.chips{font-weight:900}
th.cur,td.cur{box-shadow:inset 2px 0 0 var(--bl),inset -2px 0 0 var(--bl)}
#payp{padding:12px;border-top:2px solid var(--am);background:#12100a;display:none}
#payp h2{margin:0 0 6px;font-size:20px;color:var(--am);letter-spacing:.08em}
.pl{display:flex;gap:10px;align-items:baseline;border-bottom:1px solid #2c2612;padding:5px 0}
.pl .n{flex:1 1 auto;font-weight:900;font-size:18px}
.pl .a{font-weight:900;font-size:20px;color:var(--gr)}
.pl .w{color:var(--dim);font-size:12px;font-weight:700}
footer{flex:0 0 auto;background:#040507;border-top:2px solid var(--ln);padding:8px}
#btns{display:grid;grid-template-columns:repeat(6,1fr);gap:6px}
#btns button{border:0;border-radius:10px;background:#161b24;color:var(--fg);font-weight:900;font-size:15px;
padding:13px 2px;font-family:inherit;min-height:46px}
#btns button:active{filter:brightness(1.6)}
#play{background:#0f3b26;color:var(--gr)}#play.on{background:#3d1418;color:var(--rd)}
.sl{display:flex;align-items:center;gap:9px;margin-top:8px;color:var(--dim);font-size:11px;
letter-spacing:.1em;font-weight:800;text-transform:uppercase}
.sl input{flex:1 1 auto;height:30px}
.sl span{min-width:78px;text-align:right;color:var(--fg)}
</style></head><body>
<header>
<div id="hd"></div>
<div id="narr"></div>
<div id="prev2"></div>
</header>
<div id="mid"><div id="card"></div><div id="payp"></div></div>
<footer>
<div id="btns">
<button data-a="first">⏮</button>
<button data-a="pr">« RND</button>
<button data-a="prev">◂</button>
<button id="play" data-a="play">▶ PLAY</button>
<button data-a="next">▸</button>
<button data-a="nr">RND »</button>
</div>
<div class="sl">STEP<input id="scrub" type="range" min="0" max="1" value="0"><span id="stepn">0/0</span></div>
<div class="sl">SPEED<input id="spd" type="range" min="0" max="8" value="3"><span id="spdn">2/s</span></div>
<div class="sl" style="justify-content:flex-end">
<button data-a="new" style="border:0;border-radius:8px;background:#1d2532;color:#b9c3d1;font-weight:900;
font-size:12px;padding:9px 12px;font-family:inherit">ROLL A DIFFERENT NIGHT</button></div>
</footer>
<script>
/* ===================== HOUSE RULES ENGINE =====================
Identical text in console and playback. A night is a list of
events; the state is always reduce(events). Undo = pop. */
var R={BUYIN:20,CHIPS:5,PAR:4,CLOCK:90,W1:0.70,W2:0.20,WHOT:0.10,HOTR:3,HOTMIN:2};
function mkP(n){return{name:n,chips:R.CHIPS,alive:true,out:null,outRound:null,turns:0,shots:0,clocks:0,rounds:{}};}
function mkS(){return{roster:[],cur:0,round:1,over:false,endType:null,elim:[],feed:[],results:null};}
function avgOf(p){return p.turns?p.shots/p.turns:99;}
function aliveList(S){var a=[],i;for(i=0;i<S.roster.length;i++){if(S.roster[i].alive)a.push(S.roster[i]);}return a;}
function curP(S){return S.roster[S.cur]||null;}
function say(S,t,k){S.feed.push({r:S.round,t:t,k:k||'d'});}
function advance(S){var n=S.roster.length;if(!n)return;var i=S.cur,r=S.round,k;
for(k=0;k<n*4+4;k++){i++;if(i>=n){i=0;r++;}if(S.roster[i]&&S.roster[i].alive){S.cur=i;S.round=r;return;}}
S.cur=0;S.round=r;}
function r5(x){return Math.round(x/5)*5;}
function hotHand(S){var best=null,i,p,k,tot,cnt,a;
for(i=0;i<S.roster.length;i++){p=S.roster[i];tot=0;cnt=0;
for(k=1;k<=R.HOTR;k++){if(p.rounds[k]&&p.rounds[k].n!=null){tot+=p.rounds[k].n;cnt++;}}
if(cnt>=R.HOTMIN){a=tot/cnt;if(!best||a<best.a-1e-9)best={p:p,a:a,cnt:cnt};}}
return best;}
function finish(S,type){
S.over=true;S.endType=type;var i;
var alv=aliveList(S).slice().sort(function(a,b){
return (b.chips-a.chips)||(avgOf(a)-avgOf(b))||(S.roster.indexOf(a)-S.roster.indexOf(b));});
var order=alv.slice(),el=S.elim.slice().reverse();
for(i=0;i<el.length;i++)order.push(S.roster[el[i].i]);
var tie=[];
if(alv.length===0&&S.elim.length){
var lastR=S.elim[S.elim.length-1].round,grp=[];
for(i=0;i<S.elim.length;i++)if(S.elim[i].round===lastR&&S.elim[i].how==='busted')grp.push(S.roster[S.elim[i].i]);
if(grp.length>1)tie=grp;}
var pot=R.BUYIN*S.roster.length;
var a=r5(pot*R.W1),b=r5(pot*R.W2),h=r5(pot*R.WHOT);
a+=pot-(a+b+h);
var hot=hotHand(S),pay={},why={};
function give(n,amt,rsn){if(!n||!amt)return;pay[n]=(pay[n]||0)+amt;why[n]=why[n]?why[n]+' + '+rsn:rsn;}
if(tie.length>1){
var pool=a+b,each=Math.floor(pool/tie.length/5)*5,rem=pool-each*tie.length;
for(i=0;i<tie.length;i++)give(tie[i].name,each+(i===0?rem:0),'tied for the pot');
}else{
give(order[0]?order[0].name:'HOUSE',a+(order[1]?0:b),'winner');
if(order[1])give(order[1].name,b,'runner-up');}
if(hot)give(hot.p.name,h,'hot start ('+hot.a.toFixed(2)+' avg thru R'+R.HOTR+')');
else if(order[0])give(order[0].name,h,'no hot-start qualifier, rolls up');
var lines=[],k;for(k in pay)lines.push({name:k,amt:pay[k],why:why[k]});
lines.sort(function(x,y){return y.amt-x.amt;});
S.results={pot:pot,lines:lines,order:order,tie:tie,hot:hot,type:type};
say(S,'NIGHT OVER — '+(tie.length>1?'tied at the top':(order[0]?order[0].name+' takes it':'nobody left')),'o');}
function checkOver(S){if(!S.over&&S.roster.length&&aliveList(S).length<=1)finish(S,'natural');}
function apply(S,e){
if(e.t==='join'){var np=mkP(e.name);S.roster.push(np);
say(S,np.name+' bought in — $'+R.BUYIN+', '+R.CHIPS+' chips','g');return;}
if(S.over)return;
if(e.t==='end'){finish(S,'called');return;}
if(e.t==='quit'){var q=S.roster[e.i];if(!q||!q.alive)return;
q.alive=false;q.out='walked';q.outRound=S.round;S.elim.push({i:e.i,round:S.round,how:'walked'});
say(S,q.name+' walked out — forfeits '+q.chips+' chip'+(q.chips===1?'':'s'),'b');
if(S.cur===e.i)advance(S);checkOver(S);return;}
var p=curP(S);if(!p||!p.alive)return;
if(e.t==='shot'){
p.turns++;p.shots+=e.n;p.rounds[S.round]={n:e.n};
if(e.n>R.PAR){p.chips--;p.rounds[S.round].pen=1;
say(S,p.name+' cleared in '+e.n+' — over par, -1 chip ('+Math.max(0,p.chips)+' left)','b');}
else say(S,p.name+' cleared in '+e.n+' — safe','g');
}else if(e.t==='clock'){
p.clocks++;p.chips--;p.rounds[S.round]={c:1};
say(S,p.name+' missed the clock — -1 chip ('+Math.max(0,p.chips)+' left)','b');
}else return;
if(p.chips<=0){p.chips=0;p.alive=false;p.out='busted';p.outRound=S.round;
S.elim.push({i:S.cur,round:S.round,how:'busted'});
say(S,p.name+' is out of chips — done, back to drinking','o');}
advance(S);checkOver(S);}
function reduce(events){var S=mkS(),i;for(i=0;i<events.length;i++)apply(S,events[i]);return S;}
function rng(s){s=s>>>0;return function(){s=(Math.imul(s,1664525)+1013904223)>>>0;return s/4294967296;};}
/* =================== END ENGINE =================== */
var POOL=['Diesel','Marge','Tank','Kelso','Birdy','Sully','Ramona','Hoss','Pepper','Duke','Fitz','Wanda','Moose','Vera',
'Chuy','Ginny','Ace','Slim','Dot','Ruben','Trish','Q','Lou','Nix'];
function makeNight(seed){
var rnd=rng(seed),pool=POOL.slice(),i,j,t;
for(i=pool.length-1;i>0;i--){j=Math.floor(rnd()*(i+1));t=pool[i];pool[i]=pool[j];pool[j]=t;}
var n=10+Math.floor(rnd()*7),ev=[],skill=[];
for(i=0;i<n;i++){ev.push({t:'join',name:pool[i]});skill.push(3.5+rnd()*1.5);}
var S=reduce(ev),g=0,late=false,walk=false,extra=n;
while(!S.over&&g++<900){
if(!late&&S.round===2&&rnd()<0.05&&extra<pool.length){
ev.push({t:'join',name:pool[extra]});skill.push(3.5+rnd()*1.5);extra++;late=true;S=reduce(ev);continue;}
if(!walk&&S.round>=4&&rnd()<0.02){
var al=[],k;for(k=0;k<S.roster.length;k++)if(S.roster[k].alive)al.push(k);
if(al.length>2){ev.push({t:'quit',i:al[Math.floor(rnd()*al.length)]});walk=true;S=reduce(ev);continue;}}
var sk=skill[S.cur]||4.1;
if(rnd()<0.035)ev.push({t:'clock'});
else{var v=Math.round(sk+(rnd()+rnd()+rnd()-1.5)*1.7);if(v<3)v=3;if(v>9)v=9;ev.push({t:'shot',n:v});}
S=reduce(ev);}
if(!S.over)ev.push({t:'end'});
return ev;}
var EV=[],ST=[],idx=0,playing=false,tmr=null,SPEEDS=[0.25,0.5,1,2,3,4,6,10,16];
var $=function(id){return document.getElementById(id);};
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
function build(seed){
EV=makeNight(seed);ST=[];var i;for(i=0;i<=EV.length;i++)ST.push(reduce(EV.slice(0,i)));
idx=0;$('scrub').max=EV.length;$('scrub').value=0;draw();}
function maxRound(S){var m=S.round,i,k;for(i=0;i<S.roster.length;i++)for(k in S.roster[i].rounds){if(+k>m)m=+k;}return m;}
function draw(){
var S=ST[idx],i,k,q,mr=maxRound(S),alv=aliveList(S);
$('scrub').value=idx;$('stepn').textContent=idx+'/'+EV.length;
var f=S.feed.length?S.feed[S.feed.length-1]:{t:'Table\u2019s empty. Press PLAY.',k:'d',r:1};
$('narr').className=f.k;$('narr').textContent=(idx?'R'+f.r+' \u00b7 ':'')+f.t;
var pv=S.feed.slice(-3,-1).reverse(),ps=[];
for(i=0;i<pv.length;i++)ps.push('R'+pv[i].r+' '+pv[i].t);
$('prev2').textContent=ps.join(' | ');
var p=curP(S);
$('hd').innerHTML='STEP <b>'+idx+'</b> of '+EV.length+' ROUND <b>'+(S.over?'\u2014':S.round)+'</b>'
+' POT <b>$'+(R.BUYIN*S.roster.length)+'</b> IN <b>'+alv.length+'/'+S.roster.length+'</b>'
+' '+(S.over?'<b style="color:var(--am)">SETTLED</b>':'AT THE TABLE <b>'+(p?esc(p.name):'\u2014')+'</b>');
var h='<table><tr><th class="nm">player</th><th>chips</th>';
for(k=1;k<=mr;k++)h+='<th'+(!S.over&&k===S.round?' class="cur"':'')+'>R'+k+'</th>';
h+='<th>avg</th></tr>';
var list=S.roster.slice().sort(function(a,b){
if(a.alive!==b.alive)return a.alive?-1:1;
if(a.alive)return (b.chips-a.chips)||(avgOf(a)-avgOf(b));
return (b.outRound-a.outRound);});
for(i=0;i<list.length;i++){q=list[i];
h+='<tr class="'+(q===p&&!S.over?'up':'')+(q.alive?'':' out')+'"><td class="nm">'+esc(q.name)+'</td>'
+'<td class="chips" style="color:'+(q.chips>2?'var(--gr)':(q.chips?'var(--am)':'var(--rd)'))+'">'
+(q.alive?q.chips:(q.out==='walked'?'WALK':'OUT'))+'</td>';
for(k=1;k<=mr;k++){var c=q.rounds[k],cls=(!S.over&&k===S.round)?' cur':'';
if(!c)h+='<td class="emp'+cls+'">\u00b7</td>';
else if(c.c)h+='<td class="clk'+cls+'">C</td>';
else h+='<td class="'+(c.pen?'pen':'ok')+cls+'">'+c.n+'</td>';}
h+='<td style="color:var(--dim)">'+(q.turns?avgOf(q).toFixed(2):'\u2013')+'</td></tr>';}
h+='</table>';$('card').innerHTML=h;
if(S.over&&S.results){
var r=S.results,ph='<h2>PAY OUT \u2014 POT $'+r.pot+'</h2>',tot=0;
for(i=0;i<r.lines.length;i++){tot+=r.lines[i].amt;
ph+='<div class="pl"><span class="n">'+esc(r.lines[i].name)+'</span><span class="w">'+esc(r.lines[i].why)
+'</span><span class="a">$'+r.lines[i].amt+'</span></div>';}
ph+='<div class="pl"><span class="n" style="color:var(--dim);font-size:13px">TOTAL OUT</span><span class="a">$'+tot+'</span></div>';
ph+='<div class="w" style="margin-top:8px">Finish: ';
for(i=0;i<r.order.length;i++)ph+=(i?' \u00b7 ':'')+(i+1)+'. '+esc(r.order[i].name);
ph+=(r.type==='called'?' (house called it)':'')+'</div>';
$('payp').innerHTML=ph;$('payp').style.display='block';
}else $('payp').style.display='none';}
function go(i){idx=Math.max(0,Math.min(EV.length,i));draw();}
function stop(){playing=false;if(tmr){clearTimeout(tmr);tmr=null;}$('play').className='';$('play').innerHTML='▶ PLAY';}
function start(){if(idx>=EV.length)idx=0;playing=true;$('play').className='on';$('play').innerHTML='❚❚ STOP';tick();}
function tick(){if(!playing)return;
if(idx>=EV.length){stop();return;}
go(idx+1);
tmr=setTimeout(tick,1000/SPEEDS[+$('spd').value]);}
function roundJump(dir){var S=ST[idx],r=S.round,i;
if(dir>0){for(i=idx+1;i<=EV.length;i++)if(ST[i].round>r||ST[i].over){go(i);return;}go(EV.length);}
else{for(i=idx-1;i>=0;i--)if(ST[i].round<r){var t=ST[i].round;
while(i>0&&ST[i-1].round===t)i--;go(i);return;}go(0);}}
$('btns').addEventListener('click',function(e){
var b=e.target;while(b&&b.tagName!=='BUTTON')b=b.parentNode;if(!b)return;
var a=b.getAttribute('data-a');
if(a==='play'){playing?stop():start();return;}
if(a==='new'){stop();build((Math.random()*1e9)|0);return;}
stop();
if(a==='first')go(0);else if(a==='prev')go(idx-1);else if(a==='next')go(idx+1);
else if(a==='pr')roundJump(-1);else if(a==='nr')roundJump(1);});
$('scrub').addEventListener('input',function(){stop();go(+this.value);});
$('spd').addEventListener('input',function(){var s=SPEEDS[+this.value];
$('spdn').textContent=(s<1?('1 per '+(1/s)+'s'):s+'/s');});
document.addEventListener('keydown',function(e){
if(e.key==='ArrowRight'){stop();go(idx+1);}else if(e.key==='ArrowLeft'){stop();go(idx-1);}
else if(e.key===' '){e.preventDefault();playing?stop():start();}});
$('spdn').textContent='2/s';
build(20240711);
</script></body></html>