feat(poker): rework the guide ranking table into tier columns
Rankings flow down the left column then the right with muted chevrons in the gaps marking the order, the text and example cards sit in their own centered columns, and each row wears its ladder tier. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
1b0e4ed074
commit
8634c17ff2
1 changed files with 60 additions and 32 deletions
|
|
@ -1,7 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { BookOpen } from "lucide-react";
|
||||
import { BookOpen, ChevronDown } from "lucide-react";
|
||||
import type { Card, Rank, Suit } from "@/lib/minigames/poker";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -17,7 +18,8 @@ interface PokerRanking {
|
|||
readonly name: string;
|
||||
readonly example: readonly Card[];
|
||||
readonly description: string;
|
||||
readonly color?: string;
|
||||
/** 0 (no styling) through 9 (royal prismatic); see handRankStyle.ts. */
|
||||
readonly tier: number;
|
||||
}
|
||||
|
||||
function card(rank: Rank, suit: Suit): Card {
|
||||
|
|
@ -29,57 +31,61 @@ const RANKINGS: readonly PokerRanking[] = [
|
|||
name: "High card",
|
||||
example: [card(14, "s"), card(13, "h"), card(9, "d"), card(5, "c"), card(2, "s")],
|
||||
description: "No matching ranks, the highest card wins.",
|
||||
tier: 0,
|
||||
},
|
||||
{
|
||||
name: "One pair",
|
||||
example: [card(9, "s"), card(9, "h"), card(13, "d"), card(7, "c"), card(3, "s")],
|
||||
description: "Two cards share the same rank.",
|
||||
tier: 1,
|
||||
},
|
||||
{
|
||||
name: "Two pair",
|
||||
example: [card(11, "s"), card(11, "h"), card(4, "d"), card(4, "c"), card(14, "s")],
|
||||
description: "Two different pairs plus a fifth card.",
|
||||
tier: 2,
|
||||
},
|
||||
{
|
||||
name: "Three of a kind",
|
||||
example: [card(7, "s"), card(7, "h"), card(7, "d"), card(13, "c"), card(2, "s")],
|
||||
description: "Three cards share the same rank.",
|
||||
tier: 3,
|
||||
},
|
||||
{
|
||||
name: "Straight",
|
||||
example: [card(5, "s"), card(6, "h"), card(7, "d"), card(8, "c"), card(9, "s")],
|
||||
description: "Five cards run in rank order.",
|
||||
color: "#ffff00",
|
||||
tier: 4,
|
||||
},
|
||||
{
|
||||
name: "Flush",
|
||||
example: [card(14, "h"), card(10, "h"), card(7, "h"), card(4, "h"), card(2, "h")],
|
||||
description: "Five cards share the same suit.",
|
||||
color: "#ffff00",
|
||||
tier: 5,
|
||||
},
|
||||
{
|
||||
name: "Full house",
|
||||
example: [card(13, "s"), card(13, "h"), card(13, "d"), card(8, "c"), card(8, "s")],
|
||||
description: "Three of one rank and two of another.",
|
||||
color: "#ffff00",
|
||||
tier: 6,
|
||||
},
|
||||
{
|
||||
name: "Four of a kind",
|
||||
example: [card(3, "s"), card(3, "h"), card(3, "d"), card(3, "c"), card(14, "s")],
|
||||
description: "Four cards share the same rank.",
|
||||
color: "#ffff00",
|
||||
tier: 7,
|
||||
},
|
||||
{
|
||||
name: "Straight flush",
|
||||
example: [card(5, "s"), card(6, "s"), card(7, "s"), card(8, "s"), card(9, "s")],
|
||||
description: "Five cards run in order and share a suit.",
|
||||
color: "#ffff00",
|
||||
tier: 8,
|
||||
},
|
||||
{
|
||||
name: "Royal flush",
|
||||
example: [card(10, "h"), card(11, "h"), card(12, "h"), card(13, "h"), card(14, "h")],
|
||||
description: "The ace-high straight flush, the strongest hand.",
|
||||
color: "#ffff00",
|
||||
tier: 9,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -102,32 +108,54 @@ export function PokerGuide() {
|
|||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-sm font-semibold uppercase tracking-wider">Hand rankings</h2>
|
||||
<ol className="grid gap-3 sm:grid-cols-2">
|
||||
{RANKINGS.map((ranking, index) => (
|
||||
<li key={ranking.name} className="flex items-center gap-3 border border-border p-3" style={ranking.color ? ({
|
||||
borderColor: ranking.color
|
||||
}) : {}}>
|
||||
<span className="w-5 shrink-0 font-mono text-xs text-muted-foreground">
|
||||
{index + 1}
|
||||
</span>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<ol className="grid gap-2 sm:grid-flow-col sm:grid-cols-2 sm:grid-rows-[auto_1rem_auto_1rem_auto_1rem_auto_1rem_auto]">
|
||||
{RANKINGS.flatMap((ranking, index) => {
|
||||
const isLast = index === RANKINGS.length - 1;
|
||||
const columnBreakAfter = index === 4;
|
||||
const nodes = [
|
||||
<li
|
||||
key={ranking.name}
|
||||
className={`relative flex items-center gap-3 border border-border p-3${
|
||||
ranking.tier > 0 ? ` poker-rank-tier-${ranking.tier}` : ""
|
||||
}`}
|
||||
>
|
||||
<span className="w-5 shrink-0 font-mono text-xs text-muted-foreground">
|
||||
{index + 1}
|
||||
</span>
|
||||
<div className="flex min-w-0 flex-1 flex-col justify-center gap-0">
|
||||
<span className="font-medium">{ranking.name}</span>
|
||||
<div className="flex gap-0.5" aria-label={`${ranking.name} example`}>
|
||||
{ranking.example.map((exampleCard, cardIndex) => (
|
||||
<CardView
|
||||
key={`${exampleCard.rank}-${exampleCard.suit}`}
|
||||
card={exampleCard}
|
||||
size="mini"
|
||||
dealIndex={cardIndex}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs leading-tight text-muted-foreground">
|
||||
{ranking.description}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground max-w-1/2">{ranking.description}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<div className="flex shrink-0 gap-0.5" aria-label={`${ranking.name} example`}>
|
||||
{ranking.example.map((exampleCard, cardIndex) => (
|
||||
<CardView
|
||||
key={`${exampleCard.rank}-${exampleCard.suit}`}
|
||||
card={exampleCard}
|
||||
size="mini"
|
||||
dealIndex={cardIndex}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</li>,
|
||||
];
|
||||
if (!isLast) {
|
||||
nodes.push(
|
||||
<div
|
||||
key={`${ranking.name}-arrow`}
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"flex items-center justify-center -my-3",
|
||||
columnBreakAfter && "sm:hidden",
|
||||
)}
|
||||
>
|
||||
<ChevronDown className="size-3.5 text-muted-foreground/70" />
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
return nodes;
|
||||
})}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue