From 89d79a0f7d66b47753bceeeb9d6e63a8fe1ce93c Mon Sep 17 00:00:00 2001 From: JoshHeaps Date: Tue, 1 Sep 2026 01:22:15 -0600 Subject: [PATCH] Post the blinds to the players rather than to the chairs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blinds were assigned by seat offset — always button+1 and button+2 — which is only right when everyone is dealt in and there are three or more of them. Heads-up it had the blinds backwards. The button posted the big blind and acted second before the flop; the rule is the opposite on both counts. That's the end of every home tournament, dealt wrong. Worse, the offsets don't care whether anyone is sitting in those chairs. With two players at a nine-handed table, or with the seats left of the button sitting out, the blinds landed on empty seats and simply weren't posted — no money in the middle, and SB/BB badges pointing at nobody. Blinds now walk the players who are actually dealt in, starting from the button, with heads-up as its own case: the button is the small blind, acts first pre-flop, and the big blind acts first afterwards. The badges come from the same function as the money, so what the table shows and what gets posted can't disagree. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb --- .../wwwroot/js/casino/holdem/betting.js | 34 ++++++++++++++----- .../wwwroot/js/casino/holdem/state.js | 32 +++++++++++++++-- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/betting.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/betting.js index bb2f628..c944c2d 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/betting.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/betting.js @@ -184,9 +184,19 @@ const Betting = { const nextStreetIndex = this.STREETS.indexOf(state.street || "preflop") + 1; state.street = this.STREETS[nextStreetIndex] || "river"; - state.actingSeatIndex = this.liveSeats(state).length > 1 - ? this.nextLiveIndex(state, state.dealerSeatIndex) - : null; + + if (this.liveSeats(state).length <= 1) { + state.actingSeatIndex = null; + return; + } + + // After the flop the first live player left of the button acts — which heads-up + // is the big blind, since the button is the small blind. Naming the big blind + // directly also covers a button parked on a seat nobody is sitting in. + const { bbIndex, headsUp } = HoldemState.getBlindSeats(state); + state.actingSeatIndex = headsUp && bbIndex !== null && !state.seats[bbIndex].folded + ? bbIndex + : this.nextLiveIndex(state, state.dealerSeatIndex); }, isHandOver(state) { @@ -207,15 +217,23 @@ const Betting = { const sb = Number(state.smallBlind) || 0; const bb = Number(state.bigBlind) || 0; - const sbIndex = this.seatAt(state, state.dealerSeatIndex, 1); - const bbIndex = this.seatAt(state, state.dealerSeatIndex, 2); + const { sbIndex, bbIndex, headsUp } = HoldemState.getBlindSeats(state); - if (sb > 0 && state.seats[sbIndex].inHand) this.putIn(state, sbIndex, sb); - if (bb > 0 && state.seats[bbIndex].inHand) this.putIn(state, bbIndex, bb); + if (sbIndex === null || bbIndex === null) { + state.currentBet = 0; + state.actingSeatIndex = null; + return; + } + + if (sb > 0) this.putIn(state, sbIndex, sb); + if (bb > 0) this.putIn(state, bbIndex, bb); state.currentBet = this.round(Math.max(this.committed(state.seats[sbIndex]), this.committed(state.seats[bbIndex]))); state.lastRaiseSize = bb || 0; - state.actingSeatIndex = this.nextLiveIndex(state, bbIndex); + + // Heads-up the button is the small blind and acts first before the flop; with + // three or more it's the seat after the big blind. + state.actingSeatIndex = headsUp ? sbIndex : this.nextLiveIndex(state, bbIndex); }, // Winners get their share put back in front of them. diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js index 67b4a4e..7bb1aa0 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/state.js @@ -163,10 +163,36 @@ const HoldemState = { state.dealerSeatIndex = this.nextSeatIndex(state, state.dealerSeatIndex, 1); }, + // Seats dealt into this hand, in blind order starting from the seat after the + // button. Blinds follow the players, not the chairs — the seats immediately left + // of the button are empty often enough that counting chairs posts them to nobody. + liveOrderFromButton(state) { + const order = []; + for (let step = 1; step <= state.seatCount; step++) { + const index = this.nextSeatIndex(state, state.dealerSeatIndex, step); + if (state.seats[index].inHand) order.push(index); + } + return order; + }, + + // Who posts what. Heads-up is its own rule: with two players the button posts the + // small blind, which is the opposite of the usual order. getBlindSeats(state) { - const sbIndex = this.nextSeatIndex(state, state.dealerSeatIndex, 1); - const bbIndex = this.nextSeatIndex(state, state.dealerSeatIndex, 2); - return { dealerIndex: state.dealerSeatIndex, sbIndex, bbIndex }; + const live = this.liveOrderFromButton(state); + const dealerIndex = state.dealerSeatIndex; + + if (live.length < 2) { + return { dealerIndex, sbIndex: null, bbIndex: null, headsUp: false }; + } + + if (live.length === 2) { + const buttonIsPlaying = state.seats[dealerIndex].inHand; + const sbIndex = buttonIsPlaying ? dealerIndex : live[0]; + const bbIndex = live.find(i => i !== sbIndex); + return { dealerIndex, sbIndex, bbIndex, headsUp: true }; + } + + return { dealerIndex, sbIndex: live[0], bbIndex: live[1], headsUp: false }; }, startNextHand(state) {