The dealer table is a two-handed, big-screen tool. A dealer standing over a chopped pot with a phone in one hand and players waiting needs an answer in seconds, not a table to set up first — so /casino/texasholdem/quicksplit is the whole job on one screen: type the pot, tap how many ways, tap the smallest chip in play, read the payout. Nothing to configure, nothing saved. It splits in whole chips rather than raw arithmetic, because a dealer can't push half a chip across the felt: everyone gets the same stack and the remainder is called out as odd chips going one at a time to the first players left of the button. A pot that doesn't divide into the chosen denomination says so instead of quietly hiding the difference. Money formatting moves out of render.js into its own file so this screen can use it without pulling in the whole seat/chip UI. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
167 lines
5.7 KiB
JavaScript
167 lines
5.7 KiB
JavaScript
// The quick-split screen: pot in, payout out, nothing to set up and nothing saved.
|
||
// A dealer opens this one-handed with players waiting, so every control is a tap and
|
||
// the answer updates as they type rather than behind a "calculate" button.
|
||
const QuickSplit = {
|
||
WAY_CHOICES: [2, 3, 4, 5],
|
||
CHIP_CHOICES: [
|
||
{ value: 0.01, label: "1¢" },
|
||
{ value: 0.25, label: "25¢" },
|
||
{ value: 1, label: "$1" },
|
||
{ value: 5, label: "$5" },
|
||
{ value: 25, label: "$25" },
|
||
],
|
||
MAX_WAYS: 12,
|
||
|
||
ways: 2,
|
||
chip: 1,
|
||
|
||
mount() {
|
||
this.potInput = document.getElementById("potInput");
|
||
this.clearPotBtn = document.getElementById("clearPotBtn");
|
||
|
||
this.renderWayChoices();
|
||
this.renderChipChoices();
|
||
|
||
this.potInput.addEventListener("input", () => {
|
||
this.sanitizePotInput();
|
||
this.update();
|
||
});
|
||
|
||
this.clearPotBtn.addEventListener("click", () => {
|
||
this.potInput.value = "";
|
||
this.potInput.focus();
|
||
this.update();
|
||
});
|
||
|
||
document.getElementById("resetSplitBtn").addEventListener("click", () => {
|
||
this.potInput.value = "";
|
||
this.ways = 2;
|
||
this.chip = 1;
|
||
this.renderWayChoices();
|
||
this.renderChipChoices();
|
||
this.potInput.focus();
|
||
this.update();
|
||
});
|
||
|
||
this.update();
|
||
},
|
||
|
||
// Keeps the field to something parseable without fighting the typist: digits and a
|
||
// single decimal point, at most two places.
|
||
sanitizePotInput() {
|
||
const cleaned = this.potInput.value
|
||
.replace(/[^0-9.]/g, "")
|
||
.replace(/(\..*)\./g, "$1")
|
||
.replace(/(\.\d\d)\d+/, "$1");
|
||
if (cleaned !== this.potInput.value) this.potInput.value = cleaned;
|
||
},
|
||
|
||
makeChoiceButton(label, isSelected, onPick) {
|
||
const btn = document.createElement("button");
|
||
btn.type = "button";
|
||
btn.className = "choiceChip";
|
||
btn.textContent = label;
|
||
btn.setAttribute("aria-pressed", String(isSelected));
|
||
btn.classList.toggle("selected", isSelected);
|
||
btn.addEventListener("click", onPick);
|
||
return btn;
|
||
},
|
||
|
||
renderWayChoices() {
|
||
const row = document.getElementById("waysChoices");
|
||
row.innerHTML = "";
|
||
|
||
this.WAY_CHOICES.forEach(count => {
|
||
row.appendChild(
|
||
this.makeChoiceButton(String(count), this.ways === count, () => {
|
||
this.ways = count;
|
||
this.renderWayChoices();
|
||
this.update();
|
||
})
|
||
);
|
||
});
|
||
|
||
// Beyond a five-way chop it's rare enough to hide behind a stepper rather than
|
||
// spend a row of buttons on it.
|
||
const more = document.createElement("div");
|
||
more.className = "waysStepper";
|
||
|
||
const minus = this.makeStepButton("−", () => this.stepWays(-1));
|
||
const plus = this.makeStepButton("+", () => this.stepWays(1));
|
||
const readout = document.createElement("span");
|
||
readout.className = "waysReadout";
|
||
readout.textContent = `${this.ways} ways`;
|
||
|
||
more.appendChild(minus);
|
||
more.appendChild(readout);
|
||
more.appendChild(plus);
|
||
row.appendChild(more);
|
||
},
|
||
|
||
makeStepButton(label, onPick) {
|
||
const btn = document.createElement("button");
|
||
btn.type = "button";
|
||
btn.className = "stepBtn";
|
||
btn.textContent = label;
|
||
btn.addEventListener("click", onPick);
|
||
return btn;
|
||
},
|
||
|
||
stepWays(delta) {
|
||
this.ways = Math.max(2, Math.min(this.MAX_WAYS, this.ways + delta));
|
||
this.renderWayChoices();
|
||
this.update();
|
||
},
|
||
|
||
renderChipChoices() {
|
||
const row = document.getElementById("chipChoices");
|
||
row.innerHTML = "";
|
||
this.CHIP_CHOICES.forEach(choice => {
|
||
row.appendChild(
|
||
this.makeChoiceButton(choice.label, this.chip === choice.value, () => {
|
||
this.chip = choice.value;
|
||
this.renderChipChoices();
|
||
this.update();
|
||
})
|
||
);
|
||
});
|
||
},
|
||
|
||
update() {
|
||
const potValue = Number(this.potInput.value);
|
||
const pot = Number.isNaN(potValue) ? 0 : potValue;
|
||
this.clearPotBtn.hidden = this.potInput.value === "";
|
||
|
||
const result = SplitCalculator.split(pot, this.ways, this.chip);
|
||
|
||
document.getElementById("splitResultLabel").textContent =
|
||
`Each of ${result.players} players gets`;
|
||
document.getElementById("splitResultAmount").textContent = Money.format(result.each);
|
||
|
||
const oddNote = document.getElementById("splitOddNote");
|
||
if (result.valid && result.oddChips > 0) {
|
||
oddNote.textContent = result.oddChips === 1
|
||
? `plus one odd ${Money.format(result.chip)} chip to the first player left of the button`
|
||
: `plus ${result.oddChips} odd ${Money.format(result.chip)} chips — one each to the ` +
|
||
`first ${result.oddChips} players left of the button`;
|
||
oddNote.hidden = false;
|
||
} else {
|
||
oddNote.hidden = true;
|
||
}
|
||
|
||
const dustNote = document.getElementById("splitDustNote");
|
||
if (result.valid && result.dust > 0) {
|
||
dustNote.textContent =
|
||
`${Money.format(result.dust)} doesn't divide into ${Money.format(result.chip)} chips — ` +
|
||
`pick a smaller denomination.`;
|
||
dustNote.hidden = false;
|
||
} else {
|
||
dustNote.hidden = true;
|
||
}
|
||
|
||
document.getElementById("splitResult").classList.toggle("hasAmount", result.valid && result.each > 0);
|
||
},
|
||
};
|
||
|
||
document.addEventListener("DOMContentLoaded", () => QuickSplit.mount());
|