Hold'em dealer: 55 polish rounds, one merge #101
@@ -11,6 +11,11 @@ const ActionBar = {
|
||||
mount(state, hooks) {
|
||||
this.state = state;
|
||||
this.hooks = hooks;
|
||||
// Whoever was already on the clock when this page loaded — see tankUnknownFor.
|
||||
this.tankUnknownFor = state.bettingOpen &&
|
||||
state.actingSeatIndex !== null && state.actingSeatIndex !== undefined
|
||||
? state.actingSeatIndex
|
||||
: null;
|
||||
this.watchHeight();
|
||||
this.render();
|
||||
},
|
||||
@@ -207,6 +212,14 @@ const ActionBar = {
|
||||
tankStartedAt: null,
|
||||
tankInterval: null,
|
||||
|
||||
// The seat that was already being asked to act when this page loaded, if there was
|
||||
// one. Its clock is unknowable: the action moved to them before this session
|
||||
// started — maybe two minutes ago, maybe last night — and starting from zero would
|
||||
// have the bar say they had just begun thinking. Which it did, every time a dealer
|
||||
// refreshed mid-hand. Cleared the moment the action moves, because from then on the
|
||||
// clock is measuring something it actually saw.
|
||||
tankUnknownFor: null,
|
||||
|
||||
// Restarts only when the action actually moves — a re-render for any other reason
|
||||
// (a chip edit, a name change) must not put a thinking player back to zero.
|
||||
syncTankTimer() {
|
||||
@@ -220,6 +233,8 @@ const ActionBar = {
|
||||
}
|
||||
|
||||
if (this.tankSeatIndex !== index) {
|
||||
// Once the action has moved under our own eyes, nothing is unknown any more.
|
||||
if (this.tankSeatIndex !== null) this.tankUnknownFor = null;
|
||||
this.tankSeatIndex = index;
|
||||
this.tankStartedAt = Date.now();
|
||||
}
|
||||
@@ -243,13 +258,33 @@ const ActionBar = {
|
||||
const el = document.getElementById("actionTank");
|
||||
if (!el || this.tankStartedAt === null) return;
|
||||
|
||||
if (this.tankUnknownFor !== null && this.tankSeatIndex === this.tankUnknownFor) {
|
||||
el.textContent = "—";
|
||||
el.title = "The action was already on this seat when the page loaded, so there's nothing to time from yet. The clock starts when the action next moves.";
|
||||
el.classList.remove("tankNudge", "tankSlow");
|
||||
return;
|
||||
}
|
||||
|
||||
const seconds = Math.floor((Date.now() - this.tankStartedAt) / 1000);
|
||||
const mins = Math.floor(seconds / 60);
|
||||
el.textContent = `${mins}:${String(seconds % 60).padStart(2, "0")}`;
|
||||
el.title = "How long this player has been thinking";
|
||||
el.textContent = this.formatTank(seconds);
|
||||
el.classList.toggle("tankNudge", seconds >= this.NUDGE_AFTER_S && seconds < this.SLOW_AFTER_S);
|
||||
el.classList.toggle("tankSlow", seconds >= this.SLOW_AFTER_S);
|
||||
},
|
||||
|
||||
// "0:07", "4:01", and past the hour "1:05:03" rather than "65:03" — a table left
|
||||
// open on a hand does reach that, and sixty-five minutes written as though it were
|
||||
// a minutes-and-seconds reading is a number the eye gets wrong.
|
||||
formatTank(seconds) {
|
||||
const pad = value => String(value).padStart(2, "0");
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const rest = seconds % 60;
|
||||
return hours > 0
|
||||
? `${hours}:${pad(minutes)}:${pad(rest)}`
|
||||
: `${minutes}:${pad(rest)}`;
|
||||
},
|
||||
|
||||
// Shown only once someone is all-in and the money has actually split — a single
|
||||
// undivided pot needs no announcement, and a line that's always there stops being
|
||||
// read.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// How the think-clock reads.
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const path = require("path");
|
||||
|
||||
const ActionBar = require(path.join(
|
||||
__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "js", "casino", "holdem", "actionBar.js"));
|
||||
|
||||
test("seconds under a minute", () => {
|
||||
assert.equal(ActionBar.formatTank(0), "0:00");
|
||||
assert.equal(ActionBar.formatTank(7), "0:07");
|
||||
assert.equal(ActionBar.formatTank(59), "0:59");
|
||||
});
|
||||
|
||||
test("minutes and seconds", () => {
|
||||
assert.equal(ActionBar.formatTank(60), "1:00");
|
||||
assert.equal(ActionBar.formatTank(241), "4:01");
|
||||
assert.equal(ActionBar.formatTank(3599), "59:59");
|
||||
});
|
||||
|
||||
test("past the hour it says the hour", () => {
|
||||
// A table left open on a hand reaches this, and "65:03" is a number the eye reads
|
||||
// as minutes and seconds when it isn't.
|
||||
assert.equal(ActionBar.formatTank(3600), "1:00:00");
|
||||
assert.equal(ActionBar.formatTank(3903), "1:05:03");
|
||||
assert.equal(ActionBar.formatTank(45296), "12:34:56");
|
||||
});
|
||||
|
||||
test("the minutes are padded once there are hours, and not before", () => {
|
||||
// "1:5:03" would be wrong; "05:03" on its own would be odd.
|
||||
assert.equal(ActionBar.formatTank(3903), "1:05:03");
|
||||
assert.equal(ActionBar.formatTank(303), "5:03");
|
||||
});
|
||||
|
||||
test("a day is still a readable number rather than a wrapped one", () => {
|
||||
assert.equal(ActionBar.formatTank(90000), "25:00:00");
|
||||
});
|
||||
Reference in New Issue
Block a user