// Shared money handling for every casino surface. Kept apart from the table's // rendering so the quick-split calculator can pull it in without dragging the whole // seat/chip UI along with it. // // Everything the dealer touches is money, and money is decimal, so every module used // to carry its own `Math.round(x * 100) / 100`. That's one rounding rule written out // sixteen times in six files, which is sixteen chances for one of them to drift. // It lives here now, and the modules that do arithmetic call these instead. const Money = { // Half a cent. Chip comparisons need a tolerance: a stack that lands on // 0.0000000001 after a split is empty as far as a dealer is concerned, and an // exact `> 0` would treat that player as still having chips to bet. EPSILON: 0.004, // All money maths goes through cents, so 0.1 + 0.2 never reaches a payout. toCents(amount) { return Math.round((Number(amount) || 0) * 100); }, fromCents(cents) { return cents / 100; }, round(amount) { return this.toCents(amount) / 100; }, // Cents only when there are cents. // // A chip game deals in whole numbers nearly all the time, and a trailing ".00" // is two characters that say nothing — measured at ~22px, against the 64px a // seat puck has to show a stack in. "$1,250,000.00" doesn't fit and wraps; // "$1,250,000" does. Amounts that genuinely carry cents still show them, so a // 25c-blind game reads exactly as it did. format(amount) { const value = Number(amount) || 0; const hasCents = this.toCents(value) % 100 !== 0; return `$${value.toLocaleString(undefined, { minimumFractionDigits: hasCents ? 2 : 0, maximumFractionDigits: 2, })}`; }, // The same amount for a spreadsheet rather than for a person: no currency symbol // and no thousands separators, because those are what make a column of money // import as text and refuse to add up. forSheet(amount) { return (this.toCents(amount) / 100).toFixed(2); }, // "+$40.00" / "-$40.00" rather than "$-40.00" — a standings column is read at a // glance, and the sign belongs out front where the eye lands first. formatSigned(amount) { const value = Number(amount) || 0; if (value > 0) return `+${this.format(value)}`; if (value < 0) return `-${this.format(Math.abs(value))}`; return this.format(0); }, }; if (typeof module !== "undefined" && module.exports) { module.exports = Money; }