Files
JoshHeaps.Net/tests/holdem/wagerVerb.test.js
T
jheapsandClaude Opus 5 1af82dfddf Call a bet a bet and a raise a raise
The bar decided which word to use from what the acting player still owes. That gets
the big blind's option backwards -- they owe nothing, so it offered "Bet", but their
own blind is a live bet and putting more in is a raise. And it gets the other half
wrong too: open the pad to make a first bet on the flop and the confirm button said
"Raise to", then wrote "Ann's raise to $10" into the undo control for it.

  preflop, action back to the big blind
    was:  Fold | Check | Bet        pad: "Raise to"
    now:  Fold | Check | Raise      pad: "Raise to"
  flop, nobody has bet
    was:  Fold | Check | Bet        pad: "Raise to"   undo: "Ann's raise to $10"
    now:  Fold | Check | Bet        pad: "Bet"        undo: "Ann's bet of $10"

The arithmetic was always right -- minRaiseTo returns two big blinds for the big
blind's option and one for an opening bet, and I checked both before touching
anything. This is only what it is called. For a tool a dealer announces the action
from, that is not a small thing: a dealer who says "bet" when the big blind puts more
in gets corrected by the table.

The test is whether there is a live bet to raise, not whether this player owes
anything, and it is one helper rather than a condition written out five times.

The warnings follow the same word. Under-minimum with a bet in front of you leaves
you calling it; with an empty pot there is nothing to call:

  "$3 is under the $4 minimum. A legal raise is $4 or more -- otherwise it's a
   call, or all-in."
  "$1 is under the $2 minimum. A legal bet is $2 or more -- otherwise it's a
   check, or all-in."

Also the field's screen-reader label, which announced "Raise to amount" for a bet.

8 tests (206 total), including a table playing without blinds -- which opens with a
bet, and is the one preflop case where the old condition happened to be right.

Driven through the UI at seven viewports in both spots: every label correct, contrast
clean, no console errors.

One thing found and deliberately not touched. Fuzzing 40 hands with random actions
and rebuys -- chips stayed conserved to the cent throughout, no negative stacks, and
the acting seat was always a seat that could act -- turned up one disagreement:
isStreetComplete returns false during an all-in run-out where one player still has
chips and hasn't acted, while actingSeatIndex is correctly null. It has no symptom,
because the bar picks its layout from actingSeatIndex and shows "Deal Turn" as it
should. Changing a betting-round predicate with no observable fault behind it is a
worse trade than leaving it written down.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
2026-09-01 13:23:02 -06:00

84 lines
3.3 KiB
JavaScript

// Whether putting more in is called a bet or a raise.
//
// The arithmetic was always right — minRaiseTo returns two big blinds for the big
// blind's option and one for an opening bet. This is only what it's called, which for
// a tool a dealer announces the action from is not a small thing.
const test = require("node:test");
const assert = require("node:assert");
const { Betting, makeTable } = require("./load");
// Walks the preflop action round to the big blind, who is owed nothing and gets to
// choose between checking and putting more in.
function toTheBigBlindsOption() {
const state = makeTable({ seatCount: 6, seated: [0, 1, 2, 3], stack: 100, dealerSeatIndex: 0 });
Betting.beginHand(state);
const bigBlind = state.seats.findIndex(player => Betting.committed(player) === state.bigBlind);
while (state.actingSeatIndex !== null && state.actingSeatIndex !== bigBlind) {
Betting.call(state, state.actingSeatIndex);
}
return { state, bigBlind };
}
test("the big blind's option is a raise, not a bet", () => {
const { state, bigBlind } = toTheBigBlindsOption();
assert.equal(state.actingSeatIndex, bigBlind);
assert.equal(Betting.toCall(state, state.seats[bigBlind]), 0, "they owe nothing");
assert.equal(Betting.wagerVerb(state), "raise", "but their own blind is a live bet");
});
test("and the minimum there is a real raise, not a call of the blind", () => {
const { state } = toTheBigBlindsOption();
assert.equal(Betting.minRaiseTo(state), state.bigBlind * 2);
});
test("facing the blinds preflop is a raise", () => {
const state = makeTable({ seatCount: 6, seated: [0, 1, 2, 3], stack: 100 });
Betting.beginHand(state);
assert.equal(Betting.wagerVerb(state), "raise");
});
test("first in on the flop is a bet", () => {
const { state, bigBlind } = toTheBigBlindsOption();
Betting.check(state, bigBlind);
Betting.closeStreet(state);
assert.equal(state.street, "flop");
assert.equal(Betting.wagerVerb(state), "bet");
assert.equal(Betting.minRaiseTo(state), state.bigBlind, "a first bet is one big blind");
});
test("after that bet it's a raise again", () => {
const { state, bigBlind } = toTheBigBlindsOption();
Betting.check(state, bigBlind);
Betting.closeStreet(state);
Betting.raiseTo(state, state.actingSeatIndex, 10);
assert.equal(Betting.wagerVerb(state), "raise");
});
test("and a bet again on the next street once it's checked around", () => {
const { state, bigBlind } = toTheBigBlindsOption();
Betting.check(state, bigBlind);
Betting.closeStreet(state);
while (state.actingSeatIndex !== null) Betting.check(state, state.actingSeatIndex);
Betting.closeStreet(state);
assert.equal(state.street, "turn");
assert.equal(Betting.wagerVerb(state), "bet");
});
test("a table playing without blinds opens with a bet", () => {
const state = makeTable({ seatCount: 6, seated: [0, 1, 2], stack: 100, smallBlind: 0, bigBlind: 0 });
Betting.beginHand(state);
assert.equal(state.currentBet, 0);
assert.equal(Betting.wagerVerb(state), "bet");
});
test("asking doesn't change anything", () => {
const { state } = toTheBigBlindsOption();
const before = JSON.stringify(state);
Betting.wagerVerb(state);
assert.equal(JSON.stringify(state), before);
});