Following on from seating the table: a dealer who names six people and hasn't yet
said what a buy-in is gets told
Nobody at the table has chips left. Add a buy-in to deal.
with no buttons on the bar at all. Both halves are wrong for that table. "Left" says
the chips were had and lost, which sends the dealer looking for a rebuy — and a
blocked prompt with nothing to press is a dead end, when the fix is one field in the
sidebar the message never pointed at.
A table where nobody has ever bought in is now told what is actually true, and given
the way out:
Nobody has bought in yet. Set what everyone starts with, or give each seat
their chips. [ Set starting stack ]
The button opens Table Setup, scrolls the starting stack into view and selects it, so
the next thing typed is the answer. A seat with no stack of its own falls back to the
table default, which is why one number is enough to seat everybody's chips at once.
A table that genuinely busted out is a different situation and still says so, with no
starting-stack button — there the fix is a rebuy, not a default.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
182 lines
7.7 KiB
JavaScript
182 lines
7.7 KiB
JavaScript
// Sitting people down, which is the first thing anybody does with the tool.
|
|
//
|
|
// Naming a chair and dealing that chair in were two separate acts and the second was
|
|
// easy to miss. A dealer setting up typed six names, got six seats that still weren't
|
|
// in the hand, and the action bar hid itself because by its reckoning nobody was
|
|
// playing: six people at the table and a blank screen with no way forward.
|
|
const test = require("node:test");
|
|
const { describe } = require("node:test");
|
|
const assert = require("node:assert");
|
|
const { HoldemState, Betting, ActionBar, makeTable } = require("./load");
|
|
|
|
const emptyTable = (seatCount = 10) => {
|
|
const state = HoldemState.createDefault();
|
|
HoldemState.resizeSeats(state, seatCount);
|
|
return state;
|
|
};
|
|
|
|
const bar = state => Object.assign(Object.create(ActionBar), { state });
|
|
|
|
describe("the bar knows the difference between an empty table and a named one", () => {
|
|
test("a table nobody has touched gets no bar", () => {
|
|
assert.equal(bar(emptyTable()).isRunning(), false, "an empty felt stays uncluttered");
|
|
});
|
|
|
|
test("a table with names on it gets one, dealt in or not", () => {
|
|
const state = emptyTable();
|
|
state.seats[0].name = "Ann";
|
|
state.seats[1].name = "Ben";
|
|
|
|
assert.equal(Betting.liveSeats(state).length, 0, "nobody is in the hand");
|
|
assert.equal(bar(state).isRunning(), true, "but there are people at the table to say something to");
|
|
});
|
|
|
|
test("whitespace isn't a player", () => {
|
|
const state = emptyTable();
|
|
state.seats[0].name = " ";
|
|
assert.equal(bar(state).isRunning(), false);
|
|
});
|
|
|
|
test("a table that is playing still gets one", () => {
|
|
assert.equal(bar(makeTable({ seatCount: 6, seated: [0, 1] })).isRunning(), true);
|
|
});
|
|
});
|
|
|
|
describe("naming an empty chair sits somebody down", () => {
|
|
// What the seat editor's name field does, as the rule it applies rather than
|
|
// through the DOM: a chair with no name and nobody in it is empty, and putting a
|
|
// name on it is somebody arriving.
|
|
const nameSeat = (player, name) => {
|
|
const wasEmpty = !player.name.trim() && !player.inHand;
|
|
player.name = name;
|
|
if (wasEmpty && player.name.trim()) { player.inHand = true; player.folded = false; }
|
|
};
|
|
|
|
test("an empty chair gets dealt in", () => {
|
|
const state = emptyTable();
|
|
nameSeat(state.seats[0], "Ann");
|
|
assert.equal(state.seats[0].inHand, true);
|
|
assert.equal(state.seats[0].folded, false);
|
|
});
|
|
|
|
test("six names is six players, not six benched seats", () => {
|
|
const state = emptyTable();
|
|
state.defaultStack = 200; // a table that has told the tool what a buy-in is
|
|
["Ann", "Ben", "Cal", "Dee", "Eve", "Fay"].forEach((name, i) => nameSeat(state.seats[i], name));
|
|
|
|
assert.equal(state.seats.filter(p => p.inHand).length, 6);
|
|
assert.equal(bar(state).isRunning(), true);
|
|
assert.ok(Betting.blindPreview(state), "and there's a hand to deal");
|
|
});
|
|
|
|
test("renaming somebody who is sitting out doesn't deal them back in", () => {
|
|
// A correction, not an arrival. Dealing them in over a typo would be the tool
|
|
// overruling a dealer who sat them out on purpose.
|
|
const state = emptyTable();
|
|
state.seats[0].name = "Ann";
|
|
state.seats[0].inHand = false;
|
|
|
|
nameSeat(state.seats[0], "Anne");
|
|
assert.equal(state.seats[0].inHand, false, "still sitting out, now spelled right");
|
|
});
|
|
|
|
test("clearing a name doesn't sit anybody down", () => {
|
|
const state = emptyTable();
|
|
nameSeat(state.seats[0], "Ann");
|
|
nameSeat(state.seats[0], "");
|
|
assert.equal(state.seats[0].name, "");
|
|
});
|
|
|
|
test("a chair already in the hand is left alone", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0] });
|
|
state.seats[0].folded = true;
|
|
nameSeat(state.seats[0], "Ann");
|
|
assert.equal(state.seats[0].folded, true, "mid-hand a fold is not undone by typing");
|
|
});
|
|
});
|
|
|
|
describe("the way back from a table where everybody is sat out", () => {
|
|
test("there are named seats to offer", () => {
|
|
// Can't be reached by naming any more, but a table where everyone was sat out
|
|
// — or a save from before naming seated people — still lands here.
|
|
const state = emptyTable();
|
|
["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; });
|
|
|
|
const benched = state.seats
|
|
.map((player, index) => ({ player, index }))
|
|
.filter(({ player }) => (player.name || "").trim() && !player.inHand)
|
|
.map(({ index }) => index);
|
|
|
|
assert.deepEqual(benched, [0, 1, 2]);
|
|
assert.equal(Betting.blindPreview(state), null, "no hand to deal until they're in");
|
|
});
|
|
|
|
test("dealing them all in makes a hand possible", () => {
|
|
const state = emptyTable();
|
|
["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].stack = 100; });
|
|
|
|
state.seats.forEach(player => {
|
|
if ((player.name || "").trim()) { player.inHand = true; player.folded = false; }
|
|
});
|
|
|
|
assert.equal(Betting.liveSeats(state).length, 3);
|
|
assert.ok(Betting.blindPreview(state), "the table can be dealt");
|
|
});
|
|
|
|
test("an unnamed chair is never swept in by it", () => {
|
|
const state = emptyTable();
|
|
state.seats[0].name = "Ann";
|
|
state.seats.forEach(player => {
|
|
if ((player.name || "").trim()) { player.inHand = true; }
|
|
});
|
|
assert.equal(state.seats.filter(p => p.inHand).length, 1, "empty chairs stay empty");
|
|
});
|
|
});
|
|
|
|
describe("a table where nobody has bought in", () => {
|
|
// Different from a table that has busted out, and the difference is the whole
|
|
// point: one needs a starting stack named, the other needs a rebuy. Telling a
|
|
// dealer their brand-new players have "no chips left" sends them hunting for the
|
|
// wrong thing.
|
|
const neverBoughtIn = state => state.seats.every(p =>
|
|
!(Number(p.buyIns) > 0) && !(Number(p.stack) > 0));
|
|
|
|
test("six freshly named players have never bought in", () => {
|
|
const state = emptyTable();
|
|
["Ann", "Ben", "Cal", "Dee", "Eve", "Fay"].forEach((name, i) => {
|
|
state.seats[i].name = name;
|
|
state.seats[i].inHand = true;
|
|
});
|
|
|
|
assert.equal(neverBoughtIn(state), true);
|
|
assert.equal(Betting.blindPreview(state), null, "and there is no hand to deal");
|
|
});
|
|
|
|
test("a table that has busted out is not the same thing", () => {
|
|
const state = makeTable({ seatCount: 6, seated: [0, 1], stack: 200 });
|
|
state.seats.forEach(p => { p.buyIns = 200; p.stack = 0; });
|
|
|
|
assert.equal(neverBoughtIn(state), false, "they had chips — they lost them");
|
|
});
|
|
|
|
test("one player still holding chips is not a never-bought-in table either", () => {
|
|
const state = emptyTable();
|
|
["Ann", "Ben"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; });
|
|
state.seats[0].stack = 50;
|
|
|
|
assert.equal(neverBoughtIn(state), false);
|
|
});
|
|
|
|
test("naming a starting stack is enough to deal", () => {
|
|
// The one field the message now points at. A seat with no stack of its own
|
|
// falls back to the table default, so setting it seats everybody's chips at
|
|
// once — which is why it's the right thing to send the dealer to.
|
|
const state = emptyTable();
|
|
["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; });
|
|
assert.equal(Betting.blindPreview(state), null);
|
|
|
|
state.defaultStack = 200;
|
|
assert.ok(Betting.blindPreview(state), "one number and the table can play");
|
|
});
|
|
});
|