// Calling the clock on a player. // // The bar already had a stopwatch saying how long somebody had been thinking. This is // what a dealer does about it: a procedure with a shape — a set time, a countdown said // out loud, and a ruling at the end that depends on what the player was facing. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { ActionBar, Betting, makeTable } = require("./load"); // The bar rebuilds the DOM when a clock starts; none of the rules below need it. const onTable = (state) => { ActionBar.state = state; ActionBar.render = () => {}; ActionBar.stopClock(); return ActionBar; }; const dealt = () => { const state = makeTable({ seatCount: 6, stack: 200, smallBlind: 1, bigBlind: 2, dealerSeatIndex: 0 }); Betting.beginHand(state); return state; }; describe("starting one", () => { test("it runs on the player who is actually on the clock", () => { const state = dealt(); const bar = onTable(state); assert.equal(bar.clockRunning(), false, "nothing runs until it is called"); bar.callClock(); assert.equal(bar.clockRunning(), true); assert.equal(bar.clockSeatIndex, state.actingSeatIndex); }); test("with nobody to act there is nothing to call", () => { const state = dealt(); state.actingSeatIndex = null; const bar = onTable(state); bar.callClock(); assert.equal(bar.clockRunning(), false); assert.equal(bar.clockLeft(), null); }); test("the dealer can put it away again", () => { const bar = onTable(dealt()); bar.callClock(); bar.stopClock(); assert.equal(bar.clockRunning(), false); assert.equal(bar.clockLeft(), null); }); }); describe("the count", () => { const at = (bar, seconds) => bar.clockLeft(bar.clockStartedAt + seconds * 1000); test("is the player's own time and then a countdown", () => { const bar = onTable(dealt()); bar.callClock(); assert.equal(at(bar, 0), bar.CLOCK_THINK_S + bar.CLOCK_COUNTDOWN_S); assert.equal(at(bar, 30), bar.CLOCK_COUNTDOWN_S, "thirty gone, the countdown starts"); assert.equal(at(bar, 35), 5); }); test("stops at zero rather than going negative", () => { const bar = onTable(dealt()); bar.callClock(); assert.equal(at(bar, 40), 0); assert.equal(at(bar, 600), 0, "ten minutes later it is still just up"); }); }); describe("what happens at zero", () => { test("facing a bet, the hand is dead", () => { const state = dealt(); const bar = onTable(state); // Whoever is up pre-flop is facing the big blind. const ruling = bar.clockRuling(); assert.equal(ruling.verb, "fold"); assert.match(ruling.label, /hand is dead/); assert.match(ruling.say, /facing \$2/); }); test("facing nothing, it is checked — slowness doesn't fold a free hand", () => { const state = dealt(); // Everyone calls round to the big blind, who has nothing to call. [3, 4, 5, 0, 1].forEach(seat => Betting.call(state, seat)); const bar = onTable(state); assert.equal(state.actingSeatIndex, 2, "the big blind is up"); const ruling = bar.clockRuling(); assert.equal(ruling.verb, "check"); assert.match(ruling.label, /check for/i); }); test("the ruling names the player, because the dealer has to say it out loud", () => { const state = dealt(); state.seats[state.actingSeatIndex].name = "Priya"; const ruling = onTable(state).clockRuling(); assert.match(ruling.label, /Priya/); assert.match(ruling.say, /Priya/); }); test("with nobody to act there is no ruling to make", () => { const state = dealt(); state.actingSeatIndex = null; assert.equal(onTable(state).clockRuling(), null); }); }); describe("what ends it", () => { test("the player acting", () => { const state = dealt(); const bar = onTable(state); bar.callClock(); const wasOn = state.actingSeatIndex; Betting.call(state, wasOn); assert.notEqual(state.actingSeatIndex, wasOn, "the action has moved"); assert.equal(bar.clockRunning(), false, "so the clock is no longer this player's"); }); test("and it doesn't follow the action to the next player", () => { // A clock is called on one decision. Left running it would put a countdown on // somebody nobody asked about. const state = dealt(); const bar = onTable(state); bar.callClock(); Betting.call(state, state.actingSeatIndex); assert.equal(bar.clockLeft(), null); }); });