// Chips the pot was priced with that nobody actually paid. // // There are two ways a contribution gets into this tool. Tapping the action moves // chips: the bet leaves the stack, sits in front of the player, and is swept into their // hand total at the end of the street. The other is the "without live betting" panel — // a base pot amount, or chips counted out on a seat — where the dealer is recording a // hand they are watching rather than driving, and nothing ever leaves a stack. // // The pot wizard prices both the same way and awarding it credits the winner, so in // that second mode the table simply grew. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { Money, Betting, HoldemState, chipsOnTable } = require("./load"); const basePotTable = (base = 50, stack = 200) => { const state = HoldemState.createDefault(); HoldemState.resizeSeats(state, 4); state.basePotAmount = base; ["Ann", "Ben", "Cal"].forEach((name, i) => { state.seats[i].name = name; state.seats[i].inHand = true; state.seats[i].stack = stack; state.seats[i].buyIns = stack; }); return state; }; const pricedPot = (state) => state.seats.reduce( (sum, p) => sum + HoldemState.computeContribution(p, state.chipLegend, state.basePotAmount), 0); describe("what nobody paid", () => { test("comes off the stacks that owe it", () => { const state = basePotTable(); const collected = Betting.collectAssumedContributions(state); assert.equal(collected.total, 150); assert.deepEqual(collected.taken.map(row => row.name), ["Ann", "Ben", "Cal"]); assert.deepEqual(state.seats.slice(0, 3).map(p => p.stack), [150, 150, 150]); }); test("and lands where the rest of the tool looks for chips in the middle", () => { const state = basePotTable(); Betting.collectAssumedContributions(state); assert.deepEqual(state.seats.slice(0, 3).map(p => p.amountOverride), [50, 50, 50]); assert.equal(HoldemState.unresolvedContributions(state), 150); }); test("chips counted out on a seat are money too", () => { const state = basePotTable(0); state.seats[0].chipCounts = { red: 4 }; // four $5 chips const collected = Betting.collectAssumedContributions(state); assert.equal(collected.total, 20); assert.equal(state.seats[0].stack, 180); }); }); describe("what has already been paid", () => { test("a live bet is left exactly alone", () => { const state = basePotTable(0); state.smallBlind = 5; state.bigBlind = 10; Betting.beginHand(state); const before = state.seats.map(p => Betting.stackOf(state, p)); const collected = Betting.collectAssumedContributions(state); assert.equal(collected.total, 0, "the blinds are already off their stacks"); assert.deepEqual(state.seats.map(p => Betting.stackOf(state, p)), before); }); test("nor is a swept street taken a second time", () => { const state = basePotTable(0); state.smallBlind = 5; state.bigBlind = 10; Betting.beginHand(state); Betting.sweepToHandTotals(state); const before = state.seats.map(p => Betting.stackOf(state, p)); assert.equal(Betting.collectAssumedContributions(state).total, 0); assert.deepEqual(state.seats.map(p => Betting.stackOf(state, p)), before); }); test("a base pot on top of a live bet only collects the difference", () => { // The base pot says "assume everyone is in for fifty". Somebody who has already // put twenty in owes thirty of it, not fifty. const state = basePotTable(50); state.seats[0].committed = 20; state.seats[0].stack = 180; const collected = Betting.collectAssumedContributions(state); const ann = collected.taken.find(row => row.name === "Ann"); assert.equal(ann.amount, 30); assert.equal(state.seats[0].stack, 150, "fifty in the middle, either way"); }); }); describe("what it will not do", () => { test("take more than a player is holding", () => { const state = basePotTable(50); state.seats[1].stack = 20; Betting.collectAssumedContributions(state); assert.equal(state.seats[1].stack, 0, "all of it, and no further"); assert.equal(state.seats[1].amountOverride, 20); }); test("touch an empty chair", () => { const state = basePotTable(); const before = { ...state.seats[3] }; Betting.collectAssumedContributions(state); assert.equal(state.seats[3].stack, before.stack); assert.equal(state.seats[3].amountOverride, before.amountOverride); }); test("or a seat that isn't in the hand", () => { // The base pot stands in for "everyone put the same in", and somebody sitting // out did not. const state = basePotTable(); state.seats[2].inHand = false; const collected = Betting.collectAssumedContributions(state); assert.deepEqual(collected.taken.map(row => row.name), ["Ann", "Ben"]); assert.equal(state.seats[2].stack, 200); }); }); describe("the whole hand", () => { test("conserves the chips on the table, which it did not before", () => { const state = basePotTable(); const total = chipsOnTable(state); const pot = pricedPot(state); Betting.collectAssumedContributions(state); Betting.creditWinnings(state, [{ id: state.seats[0].id, label: "Ann", amount: pot }]); HoldemState.startNextHand(state); assert.equal(chipsOnTable(state), total, "six hundred before, six hundred after"); assert.deepEqual(state.seats.slice(0, 3).map(p => p.stack), [300, 150, 150]); assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); test("and the bank check stays balanced all the way through", () => { const state = basePotTable(); assert.equal(HoldemState.bankReconciliation(state).balanced, true); Betting.collectAssumedContributions(state); assert.equal(HoldemState.bankReconciliation(state).balanced, true, "chips are in the middle, not gone"); Betting.creditWinnings(state, [{ id: state.seats[0].id, label: "Ann", amount: 150 }]); HoldemState.startNextHand(state); assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); test("a live-betting hand is unchanged by any of this", () => { const state = basePotTable(0); state.smallBlind = 5; state.bigBlind = 10; const total = chipsOnTable(state); Betting.beginHand(state); [3, 0, 1, 2].forEach(() => { if (state.actingSeatIndex !== null) Betting.call(state, state.actingSeatIndex); }); Betting.sweepToHandTotals(state); const pot = HoldemState.unresolvedContributions(state); Betting.collectAssumedContributions(state); Betting.creditWinnings(state, [{ id: state.seats[0].id, label: "Ann", amount: pot }]); HoldemState.startNextHand(state); assert.equal(chipsOnTable(state), total); assert.equal(HoldemState.bankReconciliation(state).balanced, true); }); test("collecting twice takes nothing the second time", () => { // The pot wizard can be opened, cancelled and opened again. const state = basePotTable(); Betting.collectAssumedContributions(state); const stacks = state.seats.map(p => p.stack); const again = Betting.collectAssumedContributions(state); assert.equal(again.total, 0); assert.deepEqual(state.seats.map(p => p.stack), stacks); }); });