From 8efc14f61f964f2a2e87676708433c5dc9230e76 Mon Sep 17 00:00:00 2001 From: JoshHeaps Date: Tue, 1 Sep 2026 23:36:14 -0600 Subject: [PATCH] Show the banker the amount to count out, not just whether the player is up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The standings had a column headed "Cash out" and it did not hold an amount to pay. It held the difference against the player's buy-in. Measured on a table where Ann bought in for $200 and sits behind $340: PLAYER NET CASH OUT Ann $0 +$140 She is handed $340. The column called "Cash out" says $140, under a heading that invites reading it as the payout, at the exact moment somebody is counting money onto the table. The number a banker's hands actually follow — what the player is holding — was computed all along and written to the CSV, and never shown on screen. Three columns now, and they say what they are: Player, Chips, Up/down. Chips is a plain amount and never signed, because it is a quantity of money to hand over. Up/down keeps the sign and the colour, because it is a result. The two differ by exactly the buy-in, which is asserted rather than assumed. Chips counts what is out in the middle for the hand in progress as still theirs, for the same reason the up/down figure does: the panel is read mid-hand constantly and a stack that drops by the size of a bet looks like money going missing. Net — won less paid in, from the hand log — comes off the screen to make room. It is the weakest of the three: it only moves when a pot is resolved through the wizard, so at a table where the dealer settles pots by hand it reads $0 all night beside two columns that don't, and up/down already answers "how are they doing". It stays in the CSV export, which loses nothing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb --- JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml | 6 +- JoshHeaps.Net/wwwroot/css/casino/holdem.css | 29 +++++++--- .../wwwroot/js/casino/holdem/render.js | 12 +++- .../wwwroot/js/casino/holdem/state.js | 8 +++ tests/holdem/bankAudit.test.js | 55 +++++++++++++++++++ 5 files changed, 97 insertions(+), 13 deletions(-) diff --git a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml index 617e5ff..64c9b18 100644 --- a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml +++ b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml @@ -110,8 +110,9 @@ Player In Won - Net - Cash out + Net + Chips + Up/down
@@ -270,6 +271,7 @@ + diff --git a/JoshHeaps.Net/wwwroot/css/casino/holdem.css b/JoshHeaps.Net/wwwroot/css/casino/holdem.css index eabcf70..fe8e893 100644 --- a/JoshHeaps.Net/wwwroot/css/casino/holdem.css +++ b/JoshHeaps.Net/wwwroot/css/casino/holdem.css @@ -1816,31 +1816,44 @@ button, white-space: nowrap; } -/* The per-hand columns are detail: how the night's swings happened. They're the - first thing to go when there isn't room, because Net and Cash out are what the - banker is actually reading. */ +/* The per-hand columns are detail: how the night's swings happened. They're the first + to go when there isn't room, and Net goes with them — it only moves when a pot is + resolved through the wizard, so on a table where the dealer settles pots by hand it + reads $0 all night beside two columns that don't. + + What survives at every width is the pair the banker actually works from: what the + player is holding, and whether that leaves them up or down. */ .standingsIn, .standingsWon, +.standingsNet, .standingsInHead, -.standingsWonHead { +.standingsWonHead, +.standingsNetHead { display: none; } @container (min-width: 30rem) { .standingsRow { - grid-template-columns: minmax(5rem, 1fr) auto auto auto auto; + grid-template-columns: minmax(5rem, 1fr) auto auto auto auto auto; } .standingsIn, .standingsWon, + .standingsNet, .standingsInHead, - .standingsWonHead { + .standingsWonHead, + .standingsNetHead { display: block; } } -/* The settle-up column is the one a banker reads while counting out cash, so it - carries the weight the per-hand columns don't. */ +/* The chips column is the one a banker's hands follow while counting money onto the + table, so it carries the weight. Up/down is read once, at the end. */ +.standingsChips { + font-weight: 600; + color: #e6e6e6; +} + .standingsCashOut { font-weight: 600; } diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js index 836d0bd..5a2755f 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js @@ -818,9 +818,15 @@ const Render = { netEl.classList.toggle("netUp", row.net > 0); netEl.classList.toggle("netDown", row.net < 0); - // What they'd actually be handed if the night ended now: chips in front of - // them less the cash they gave the banker. This was computed and written to - // the CSV all along, but never shown to the person doing the paying out. + // The cash to count out, which is a plain amount and never signed — this + // is the one the banker's hands follow. It sat only in the CSV: on screen + // the column headed "Cash out" held the difference against their buy-in + // instead, so a player up $140 on a $340 stack read as "$140" at the exact + // moment somebody was counting money onto the table. + node.querySelector(".standingsChips").textContent = Money.format(row.chipsInFront); + + // And whether that leaves them up or down, which is the question the rest + // of the table asks and the one the old column was actually answering. const cashEl = node.querySelector(".standingsCashOut"); cashEl.textContent = Money.formatSigned(row.cashPosition); cashEl.classList.toggle("netUp", row.cashPosition > 0); diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js index a950af9..01b4b80 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js @@ -683,6 +683,14 @@ const HoldemState = { net: Money.round(t.won - t.in), boughtIn, chipsNow, + // Everything still theirs: the stack in front of them plus whatever + // is out in the middle for the hand in progress. This is the number + // a banker counts out when somebody leaves, and it was the one + // figure the standings never showed — the column called "Cash out" + // held the difference against their buy-in, so a player up $140 on + // a $340 stack read as "$140" at the exact moment cash changed + // hands. + chipsInFront: Money.round(chipsNow + inFrontOfThem), cashPosition: Money.round(chipsNow + inFrontOfThem - boughtIn), notes, }; diff --git a/tests/holdem/bankAudit.test.js b/tests/holdem/bankAudit.test.js index eb2f2b2..9223d5b 100644 --- a/tests/holdem/bankAudit.test.js +++ b/tests/holdem/bankAudit.test.js @@ -167,3 +167,58 @@ describe("does the cash box cover the chips on the table", () => { assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); }); + +describe("the number the banker counts out", () => { + // The standings had two signed columns and neither was an amount to pay. A player + // up $140 on a $340 stack read "$140" under a heading saying "Cash out", at the + // exact moment somebody was counting money onto the table. + test("chips in front is what they are holding, not what they are up", () => { + const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 340 }); + state.seats[0].name = "Ann"; + state.seats[0].buyIns = 200; + state.seats[1].buyIns = 200; + state.seats[1].stack = 60; + + const rows = HoldemState.sessionStandings(state); + const ann = rows.find(r => r.name === "Ann"); + assert.equal(ann.chipsInFront, 340, "the cash to count out"); + assert.equal(ann.cashPosition, 140, "and separately, that they are up $140"); + }); + + test("a player who is down still has chips to be handed", () => { + const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 60 }); + state.seats[0].name = "Cal"; + state.seats.forEach(p => { p.buyIns = 200; }); + + const cal = HoldemState.sessionStandings(state).find(r => r.name === "Cal"); + assert.equal(cal.chipsInFront, 60, "they leave with $60 of chips"); + assert.equal(cal.cashPosition, -140, "having lost $140 of the $200 they brought"); + }); + + test("chips still out in the middle are counted as theirs", () => { + // Cashing out mid-hand isn't a thing, but the panel is read mid-hand constantly + // and a stack that drops by the size of a bet looks like money going missing. + const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 200 }); + state.seats[0].name = "Dee"; + state.seats.forEach(p => { p.buyIns = 200; }); + Betting.beginHand(state); + Betting.raiseTo(state, 0, 50); + + const dee = HoldemState.sessionStandings(state).find(r => r.name === "Dee"); + assert.equal(dee.chipsInFront, 200, "stack plus what's in the middle for them"); + assert.equal(dee.cashPosition, 0, "so they are still level"); + }); + + test("chips and up/down always differ by exactly the buy-in", () => { + const state = makeTable({ seatCount: 9, seated: [0, 1, 2, 3], stack: 100 }); + [340, 150, 60, 250].forEach((chips, i) => { + state.seats[i].stack = chips; + state.seats[i].buyIns = 200 + i * 50; + }); + + HoldemState.sessionStandings(state).forEach(row => { + assert.equal(Money.round(row.chipsInFront - row.boughtIn), row.cashPosition, + `${row.name}'s two columns should tell one story`); + }); + }); +});