games: no mercy on the felt, and the bill that went to the wrong window
The engine has been able to play No Mercy since aca523e. Now a browser can.
The switch is a switch, not a fourth table: the tier is still the table size,
because that is what you are paid for, and the deck is the other dial. Six faces
the normal box does not print, sized by the card's own vars and never by the box
they sit in. The stack says what the bill is on the felt, in the turn line and on
the button, and under it the deck is dead — you cannot draw your way out of a
bill somebody has run up and pointed at you.
The wild draws glow. That started as decoration and turned out to be doing work:
No Mercy prints a coloured +4 right beside the wild one, and in a hand of twenty
the glow is what tells them apart.
A buried seat is not an empty one, which is the whole trap here — a seat killed
at twenty-five holds no cards, and neither does a seat that just went out and
won. The view asks the engine which it is instead of counting to zero, so the
winner is never the corpse.
Two bugs, both found in a browser and neither findable anywhere else:
The felt's stack bill was writing into the chip bar. It was [data-pending], and
so is the bar's "your chips are still coming" readout — and the bar lives inside
the table's own root and comes first in the document. A stack quietly overwrote
the escrow message and never appeared on the felt at all. A table's attributes
are not a private namespace.
And hold'em, re-driven on the 20M-hand policy (six hands, got up 61 ahead of a
100 buy-in, money conserved to the chip — Phase 4 closed), let you click a button
that did nothing: Deal, Leave and Top up stayed alive through the whole deal
animation, where send() drops the click on purpose. The lock is on the buttons
now, not only in the variable.
Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ
This commit is contained in:
@@ -39,6 +39,15 @@
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div data-discard class="pete-uno-discard" aria-label="The card in play"></div>
|
||||
<p data-colour class="pete-uno-colour" aria-live="polite"></p>
|
||||
<!-- The bill. While a stack stands it is the only thing on the table
|
||||
that matters, so it is said on the felt and not only on a button.
|
||||
|
||||
It is data-*bill*, not data-pending: the chip bar already owns
|
||||
data-pending (chips still in flight from a buy-in), the chip bar is
|
||||
inside this root, and it comes first in the document — so a stack
|
||||
was quietly writing "+10 coming your way" over the escrow readout
|
||||
and never showing up on the felt at all. -->
|
||||
<p data-bill class="pete-uno-pending hidden" aria-live="assertive"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -100,7 +109,7 @@
|
||||
<!-- Playing: shown while a game is live. -->
|
||||
<section data-playing class="hidden rounded-3xl bg-[color:var(--card)] p-5 sm:p-6 shadow-pete border-2 border-[color:var(--ink)]/10">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<p class="text-sm text-[color:var(--ink)]/50">
|
||||
<p data-hint class="text-sm text-[color:var(--ink)]/50">
|
||||
Click a card that lights up. Nothing lights up? Draw one.
|
||||
</p>
|
||||
<div class="ml-auto flex flex-wrap items-center gap-2">
|
||||
@@ -109,6 +118,14 @@
|
||||
hover:bg-[color:var(--ink)]/5 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
|
||||
Draw
|
||||
</button>
|
||||
<!-- Giving in to a stack. It is a decision, not a draw — you are paying a
|
||||
bill somebody ran up and pointing at you — so it gets a button of its
|
||||
own, and the button says what it costs. -->
|
||||
<button type="button" data-take
|
||||
class="hidden rounded-full bg-[#cc3d4a] px-6 py-3 font-display text-lg font-bold text-white shadow-pete
|
||||
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
|
||||
Take <span data-take-n>0</span>
|
||||
</button>
|
||||
<button type="button" data-pass
|
||||
class="hidden rounded-full bg-[color:var(--accent)] px-6 py-3 font-display text-lg font-bold text-white shadow-pete
|
||||
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
|
||||
@@ -122,21 +139,52 @@
|
||||
<!-- Betting: shown between games. -->
|
||||
<section data-betting class="rounded-3xl bg-[color:var(--card)] p-5 sm:p-6 shadow-pete border-2 border-[color:var(--ink)]/10">
|
||||
|
||||
<div class="text-xs font-semibold uppercase tracking-wider text-[color:var(--ink)]/50">Who are you playing?</div>
|
||||
<div class="mt-2 grid gap-2 sm:grid-cols-3">
|
||||
{{range .Tables}}
|
||||
<button type="button" data-tier="{{.Slug}}"
|
||||
class="pete-tier rounded-2xl border-2 p-3 text-left transition">
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<span class="font-display text-lg font-bold">{{.Name}}</span>
|
||||
<span class="font-display text-lg font-bold tabular-nums text-[color:var(--accent)]">{{printf "%.1f" .Base}}×</span>
|
||||
</div>
|
||||
<p class="mt-0.5 text-xs text-[color:var(--ink)]/50">{{.Blurb}}</p>
|
||||
<p class="mt-1.5 text-xs font-semibold text-[color:var(--ink)]/40">
|
||||
{{.Bots}} bot{{if gt .Bots 1}}s{{end}} · seven cards each
|
||||
</p>
|
||||
</button>
|
||||
{{end}}
|
||||
<!-- Two dials, and they are different dials. The table size is the tier — it is
|
||||
what you're paid for. No Mercy is the *deck*, and it is a switch across all
|
||||
three sizes, which is why it sits up here beside the question rather than
|
||||
being a fourth table. -->
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="text-xs font-semibold uppercase tracking-wider text-[color:var(--ink)]/50">Who are you playing?</div>
|
||||
<div class="pete-seg" role="group" aria-label="Which rules">
|
||||
<button type="button" data-rules="normal" class="pete-seg-btn">UNO</button>
|
||||
<button type="button" data-rules="nomercy" class="pete-seg-btn">No Mercy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-grid="normal">
|
||||
<div class="mt-2 grid gap-2 sm:grid-cols-3">
|
||||
{{range .Tables}}
|
||||
<button type="button" data-tier="{{.Slug}}"
|
||||
class="pete-tier rounded-2xl border-2 p-3 text-left transition">
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<span class="font-display text-lg font-bold">{{.Name}}</span>
|
||||
<span class="font-display text-lg font-bold tabular-nums text-[color:var(--accent)]">{{printf "%.1f" .Base}}×</span>
|
||||
</div>
|
||||
<p class="mt-0.5 text-xs text-[color:var(--ink)]/50">{{.Blurb}}</p>
|
||||
<p class="mt-1.5 text-xs font-semibold text-[color:var(--ink)]/40">
|
||||
{{.Bots}} bot{{if gt .Bots 1}}s{{end}} · seven cards each
|
||||
</p>
|
||||
</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-grid="nomercy" class="hidden">
|
||||
<div class="mt-2 grid gap-2 sm:grid-cols-3">
|
||||
{{range .NoMercy}}
|
||||
<button type="button" data-tier="{{.Slug}}"
|
||||
class="pete-tier pete-tier-nm rounded-2xl border-2 p-3 text-left transition">
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<span class="font-display text-lg font-bold">{{.Name}}</span>
|
||||
<span class="font-display text-lg font-bold tabular-nums text-[color:var(--accent)]">{{printf "%.1f" .Base}}×</span>
|
||||
</div>
|
||||
<p class="mt-0.5 text-xs text-[color:var(--ink)]/50">{{.Blurb}}</p>
|
||||
<p class="mt-1.5 text-xs font-semibold text-[color:var(--ink)]/40">
|
||||
{{.Bots}} bot{{if gt .Bots 1}}s{{end}} · 168 cards
|
||||
</p>
|
||||
</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 flex flex-wrap items-center gap-x-6 gap-y-4">
|
||||
@@ -162,9 +210,16 @@
|
||||
Deal
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-3 text-center text-xs text-[color:var(--ink)]/40">
|
||||
<!-- No Mercy pays *less*, which reads like a typo until you've played one: the
|
||||
mercy rule buries bots too, and every bot it buries is one fewer seat that
|
||||
can beat you to the last card. Say so, rather than let it look like a bug. -->
|
||||
<p data-note="normal" class="mt-3 text-center text-xs text-[color:var(--ink)]/40">
|
||||
Normal rules: no stacking a +2 on a +2, and a reverse is a skip when it's just the two of you.
|
||||
</p>
|
||||
<p data-note="nomercy" class="hidden mt-3 text-center text-xs text-[color:var(--ink)]/40">
|
||||
168 cards, +6s and +10s. Draw cards stack, you draw until you can play, and {{.MercyLimit}} cards in
|
||||
your hand puts you out of the game. It pays less than normal UNO because it buries the bots too.
|
||||
</p>
|
||||
<p data-table-msg class="hidden mt-3 rounded-2xl bg-[color:var(--ink)]/5 px-4 py-2 text-sm font-semibold"></p>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user