A player with nothing behind had a blank where their stack goes. So did a seat whose stack nobody had entered. On a table with side pots that is the one distinction the dealer is working from -- who can still be bet at, and who is already in for everything they have -- and the felt drew the two states identically. Ann $40 $160 has chips Ben $40 all-in for 40 Cal $40 $160 has chips Dee $12 all-in for 12 The action bar knew: it had already built "Main pot $48, Ann/Ben/Cal/Dee" and "Side pot 1 $84, Ann/Ben/Cal". The table it sits under didn't say a word. Betting.isAllIn is the same stack test nextToAct uses to pass over a player, which is the point: the seat the felt marks is exactly the seat the dealer will never be asked to act on again. There's a test asserting that equivalence rather than restating the condition. The second half of the condition matters. A seat with an empty stack that has put nothing in is a busted player who shouldn't have been dealt in, not a player who moved all-in, so it isn't marked. "ALL IN" in gold where the stack was, and a warmer border on the seat -- deliberately quieter than the acting-seat ring, because all-in is a standing fact about a player rather than a request for attention. 9 tests (172 total). They fail on a naive condition: dropping the committed-chips and folded checks fails 4 of them. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
99 lines
4.1 KiB
JavaScript
99 lines
4.1 KiB
JavaScript
// Who the felt marks "ALL IN".
|
|
//
|
|
// The mark's whole value is that a dealer can scan the ring and see who can still be
|
|
// bet at, so the test that matters most is the last one: the seats it marks are
|
|
// exactly the seats the betting round refuses to ask for another chip.
|
|
const test = require("node:test");
|
|
const assert = require("node:assert");
|
|
const { Betting, HoldemState, makeTable, chipsOnTable } = require("./load");
|
|
|
|
test("a player who moved all-in is marked", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1, 2, 3], stack: 100 });
|
|
state.seats[3].stack = 12;
|
|
Betting.beginHand(state);
|
|
Betting.allIn(state, state.actingSeatIndex);
|
|
|
|
assert.equal(Betting.isAllIn(state, state.seats[3]), true);
|
|
assert.equal(state.seats[3].stack, 0);
|
|
});
|
|
|
|
test("a player with chips behind is not", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1, 2], stack: 100 });
|
|
Betting.beginHand(state);
|
|
assert.equal(Betting.isAllIn(state, state.seats[0]), false);
|
|
assert.equal(Betting.isAllIn(state, state.seats[1]), false);
|
|
});
|
|
|
|
test("a busted player who was dealt in but has put nothing in is not", () => {
|
|
// An empty stack alone isn't all-in — it's someone who shouldn't have been dealt
|
|
// in. Calling that "ALL IN" on the felt would be a lie about a live pot.
|
|
const state = makeTable({ seatCount: 4, seated: [0, 1, 2], stack: 100 });
|
|
state.seats[2].stack = 0;
|
|
assert.equal(Betting.isAllIn(state, state.seats[2]), false);
|
|
});
|
|
|
|
test("a folded player with an empty stack is not", () => {
|
|
const state = makeTable({ seatCount: 4, seated: [0, 1, 2], stack: 100 });
|
|
state.seats[1].committed = 5;
|
|
state.seats[1].stack = 0;
|
|
state.seats[1].folded = true;
|
|
assert.equal(Betting.isAllIn(state, state.seats[1]), false);
|
|
});
|
|
|
|
test("an empty chair is not", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 100 });
|
|
assert.equal(Betting.isAllIn(state, state.seats[4]), false);
|
|
});
|
|
|
|
test("a seat with no stack of its own falls back to the table's and isn't marked", () => {
|
|
const state = makeTable({ seatCount: 4, seated: [0, 1, 2], stack: 100 });
|
|
state.defaultStack = 200;
|
|
state.seats[2].stack = null;
|
|
state.seats[2].committed = 10;
|
|
assert.equal(Betting.isAllIn(state, state.seats[2]), false);
|
|
});
|
|
|
|
test("the seats marked are exactly the ones the action passes over", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1, 2, 3], stack: 100 });
|
|
state.seats[1].stack = 8;
|
|
state.seats[3].stack = 30;
|
|
Betting.beginHand(state);
|
|
|
|
// Everyone in, two of them for less than the others can cover.
|
|
for (let step = 0; step < 4; step++) Betting.allIn(state, state.actingSeatIndex);
|
|
|
|
const marked = state.seats
|
|
.map((player, index) => (Betting.isAllIn(state, player) ? index : null))
|
|
.filter(index => index !== null);
|
|
const skipped = state.seats
|
|
.map((player, index) =>
|
|
player.inHand && !player.folded && Betting.nextToAct(state, index - 1) !== index ? index : null)
|
|
.filter(index => index !== null);
|
|
|
|
assert.deepEqual(marked, skipped);
|
|
assert.ok(marked.length > 0, "the scenario should actually produce all-in players");
|
|
});
|
|
|
|
test("nobody is marked once the next hand starts", () => {
|
|
const state = makeTable({ seatCount: 4, seated: [0, 1, 2], stack: 100 });
|
|
state.seats[2].stack = 5;
|
|
Betting.beginHand(state);
|
|
Betting.allIn(state, state.actingSeatIndex);
|
|
assert.ok(state.seats.some(player => Betting.isAllIn(state, player)));
|
|
|
|
// The short stack got there by winning nothing; give them chips back the way a
|
|
// dealer would before the next deal.
|
|
HoldemState.startNextHand(state);
|
|
state.seats[2].stack = 50;
|
|
assert.equal(state.seats.filter(player => Betting.isAllIn(state, player)).length, 0);
|
|
});
|
|
|
|
test("marking nobody doesn't move a chip", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1, 2, 3], stack: 100 });
|
|
state.seats[3].stack = 12;
|
|
Betting.beginHand(state);
|
|
const before = chipsOnTable(state);
|
|
state.seats.forEach(player => Betting.isAllIn(state, player));
|
|
assert.equal(chipsOnTable(state), before);
|
|
});
|