// The chips the felt plays with are the chips the box is checked against. // // "Starting stack" is offered in the setup panel as the ordinary way to open a table — // "seats without their own stack start here" — and everything a dealer looks at reads // it: the puck, the action bar, what a player is allowed to bet. The money side did // not. It read `seat.stack` directly, which is null for exactly those seats, so a // three-handed table playing for $600 reported no chips in play, a balanced bank and a // standings list with nobody on it. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { Money, HoldemState, Betting } = require("./load"); const openedWithStartingStack = (stack = 200, names = ["Ann", "Ben", "Cal"]) => { const state = HoldemState.createDefault(); HoldemState.resizeSeats(state, 6); state.defaultStack = stack; names.forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; }); return state; }; describe("what the table is playing for", () => { test("is what the bank counts", () => { const state = openedWithStartingStack(); // What the felt says each of them has. assert.deepEqual(state.seats.slice(0, 3).map(p => Betting.stackOf(state, p)), [200, 200, 200]); assert.equal(HoldemState.bankReconciliation(state).chipsInPlay, 600); }); test("and the box being empty is a problem it now reports", () => { const bank = HoldemState.bankReconciliation(openedWithStartingStack()); assert.equal(bank.cashIn, 0); assert.equal(bank.difference, 600); assert.equal(bank.balanced, false, "six hundred dollars of chips nobody paid for"); }); test("empty chairs are not people holding two hundred dollars", () => { // The starting stack stands in for a seat that hasn't been given one, and every // empty chair is such a seat. Counting them would invent $600 of chips at an // empty table. const state = openedWithStartingStack(); assert.equal(HoldemState.bankReconciliation(state).chipsInPlay, 600, "three players, not six chairs"); const empty = HoldemState.createDefault(); empty.defaultStack = 200; assert.equal(HoldemState.bankReconciliation(empty).chipsInPlay, 0); assert.equal(HoldemState.bankReconciliation(empty).balanced, true); }); test("once the cash is written down it balances", () => { const state = openedWithStartingStack(); state.seats.slice(0, 3).forEach(p => { p.buyIns = 200; }); assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); test("a table where everyone has their own stack is unchanged", () => { const state = HoldemState.createDefault(); HoldemState.resizeSeats(state, 4); ["Ann", "Ben"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].stack = 150; state.seats[i].buyIns = 150; }); const bank = HoldemState.bankReconciliation(state); assert.equal(bank.chipsInPlay, 300); assert.equal(bank.balanced, true); }); }); describe("who is on the settle-up list", () => { test("everyone holding chips, including the ones holding the table's own stack", () => { const rows = HoldemState.sessionStandings(openedWithStartingStack()); assert.deepEqual(rows.map(r => r.name), ["Ann", "Ben", "Cal"]); assert.deepEqual(rows.map(r => r.chipsInFront), [200, 200, 200]); }); test("with nothing bought in yet, which is the thing to notice", () => { const rows = HoldemState.sessionStandings(openedWithStartingStack()); assert.deepEqual(rows.map(r => r.boughtIn), [0, 0, 0]); assert.deepEqual(rows.map(r => r.cashPosition), [200, 200, 200]); }); }); describe("naming the seats the cash is missing for", () => { test("says who, and which kind of missing it is", () => { const state = openedWithStartingStack(); state.seats[2].stack = 320; // Cal typed his own: an unrecorded rebuy const owing = HoldemState.seatsOwingCash(state); assert.deepEqual(owing.map(row => row.name), ["Ann", "Ben", "Cal"]); assert.deepEqual(owing.map(row => row.fromStartingStack), [true, true, false]); assert.deepEqual(owing.map(row => row.chips), [200, 200, 320]); }); test("a seat whose buy-in is written down isn't on the list", () => { const state = openedWithStartingStack(); state.seats[0].buyIns = 200; assert.deepEqual(HoldemState.seatsOwingCash(state).map(row => row.name), ["Ben", "Cal"]); }); test("and a table where nothing is owed has nobody on it", () => { const state = openedWithStartingStack(); state.seats.slice(0, 3).forEach(p => { p.buyIns = 200; }); assert.deepEqual(HoldemState.seatsOwingCash(state), []); }); }); describe("writing the starting stacks down", () => { test("records exactly what those seats are holding", () => { const state = openedWithStartingStack(); const done = HoldemState.recordStartingBuyIns(state); assert.equal(done.seats, 3); assert.equal(done.total, 600); assert.deepEqual(state.seats.slice(0, 3).map(p => p.buyIns), [200, 200, 200]); assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); test("and leaves somebody else's forgotten rebuy alone", () => { // Only the dealer knows what that was. Guessing would be the tool inventing // money, which is the fault this whole round is about. const state = openedWithStartingStack(); state.seats[2].stack = 320; HoldemState.recordStartingBuyIns(state); assert.deepEqual(state.seats.slice(0, 3).map(p => p.buyIns), [200, 200, 0]); const bank = HoldemState.bankReconciliation(state); assert.equal(bank.balanced, false); assert.equal(bank.difference, 320, "still exactly Cal's chips, and still reported"); }); test("does nothing on a table that doesn't need it", () => { const state = openedWithStartingStack(); state.seats.slice(0, 3).forEach(p => { p.buyIns = 200; }); const done = HoldemState.recordStartingBuyIns(state); assert.equal(done.seats, 0); assert.equal(done.total, 0); assert.deepEqual(state.seats.slice(0, 3).map(p => p.buyIns), [200, 200, 200]); }); }); describe("through a hand", () => { test("the money adds up from the deal to the payout", () => { const state = openedWithStartingStack(200, ["Ann", "Ben", "Cal", "Dee"]); HoldemState.recordStartingBuyIns(state); assert.equal(HoldemState.bankReconciliation(state).balanced, true); Betting.beginHand(state); assert.equal(HoldemState.bankReconciliation(state).balanced, true, "blinds are still on the table"); [3, 0, 1].forEach(() => { if (state.actingSeatIndex !== null) Betting.call(state, state.actingSeatIndex); }); assert.equal(HoldemState.bankReconciliation(state).balanced, true); const pot = Betting.potSoFar(state); Betting.sweepToHandTotals(state); Betting.creditWinnings(state, [{ id: state.seats[0].id, label: "Ann", amount: pot }]); HoldemState.startNextHand(state); const bank = HoldemState.bankReconciliation(state); assert.equal(bank.balanced, true); assert.equal(bank.chipsInPlay, 800, "four buy-ins of two hundred, wherever they have moved to"); assert.ok(state.seats[0].stack > 200, "and the winner is holding more than they sat down with"); }); });