// The community cards, as a thing the table remembers. // // The board is the one part of a hand this tool never had a place for: hole cards go on // a seat, chips go in the middle, and the five cards everybody plays lived only on the // felt in front of the dealer. It is opt-in, because most nights nobody wants to enter // cards at all — and everything that reads it has to behave exactly as it always did // when it is off. const test = require("node:test"); const { describe } = require("node:test"); const assert = require("node:assert"); const { Betting, HoldemState, makeTable } = require("./load"); const dealt = (over = {}) => { const state = { ...makeTable({ seatCount: 6, stack: 200, smallBlind: 1, bigBlind: 2 }), ...over }; Betting.beginHand(state); return state; }; describe("a new table", () => { test("has five empty slots and is not tracking them", () => { const state = HoldemState.createDefault(); assert.deepEqual(state.board, ["", "", "", "", ""]); assert.equal(state.trackBoard, false, "off by default: a dealer who never asked for this should not be asked for cards"); }); }); describe("how many cards are out", () => { test("none, three, four, five", () => { assert.equal(Betting.boardCardsFor("preflop"), 0); assert.equal(Betting.boardCardsFor("flop"), 3); assert.equal(Betting.boardCardsFor("turn"), 4); assert.equal(Betting.boardCardsFor("river"), 5); }); test("and nothing at all for a street that isn't one", () => { assert.equal(Betting.boardCardsFor("showdown"), 0); assert.equal(Betting.boardCardsFor(undefined), 0); }); test("it matches the streets the betting engine actually deals", () => { // If a street is ever added, this is the table that has to hear about it — // a street with no entry silently draws no cards. Betting.STREETS.forEach(street => { assert.ok(Object.prototype.hasOwnProperty.call(Betting.BOARD_CARDS, street), `${street} has no card count`); }); }); }); describe("clearing it", () => { test("the next hand takes the board with it", () => { const state = dealt(); state.board = ["AH", "KD", "9C", "4S", "2H"]; HoldemState.startNextHand(state); assert.deepEqual(state.board, ["", "", "", "", ""]); }); test("so does a misdeal", () => { const state = dealt(); state.board = ["AH", "KD", "9C", "", ""]; Betting.takeHandBack(state); assert.deepEqual(state.board, ["", "", "", "", ""]); }); test("and so does dealing a hand, however the table got here", () => { // Dealing twice off the same button — after a misdeal, or a table set up and // then dealt again — would otherwise rank the last hand's board against this // hand's cards, and it would look exactly like a real answer. const state = dealt(); state.board = ["AH", "KD", "9C", "4S", "2H"]; Betting.beginHand(state); assert.deepEqual(state.board, ["", "", "", "", ""]); }); test("but turning the tracking off does not", () => { // A dealer who taps the toggle mid-hand can tap it back; wiping the flop on // the way past would cost them cards they had already entered. const state = dealt({ trackBoard: true }); state.board = ["AH", "KD", "9C", "", ""]; state.trackBoard = false; assert.deepEqual(state.board, ["AH", "KD", "9C", "", ""]); }); }); describe("a board off a file", () => { const migrated = board => HoldemState.migrate({ seats: [HoldemState.makePlayer()], board, }).board; test("a table saved before boards existed gets an empty one, not tracked", () => { const parsed = HoldemState.migrate({ seats: [HoldemState.makePlayer()] }); assert.deepEqual(parsed.board, ["", "", "", "", ""]); assert.equal(parsed.trackBoard, false); }); test("it is always five slots, however many arrived", () => { assert.equal(migrated(["AH"]).length, 5); assert.equal(migrated(["AH", "KD", "9C", "4S", "2H", "3D", "5S"]).length, 5); assert.equal(migrated("not an array").length, 5); assert.equal(migrated(null).length, 5); }); test("anything that isn't a card becomes an empty slot", () => { assert.deepEqual(migrated(["AH", "ZZ", 7, null, "10H"]), ["AH", "", "", "", ""]); }); test("and a real one keeps its place, upper-cased", () => { assert.deepEqual(migrated(["ah", " kd ", "9c", "", ""]), ["AH", "KD", "9C", "", ""]); }); test("tracking is only on if the file said so, in so many words", () => { const on = HoldemState.migrate({ seats: [HoldemState.makePlayer()], trackBoard: true }); const truthy = HoldemState.migrate({ seats: [HoldemState.makePlayer()], trackBoard: "yes" }); assert.equal(on.trackBoard, true); assert.equal(truthy.trackBoard, false, "a string is a file that has been edited, not a yes"); }); });