Rolls and odds
rollCards deals cards from a pool, rarer tiers coming up less often. With a seed, the same pool deals the same cards every time, in a browser or on a server. chances answers what the odds of each card are, which is the number people ask to see.
Deal a hand
import { rollCards } from "@react-cards-opening/core";
const hand = rollCards({ pool: allCards, count: 5, seed: `${userId}:2026-10-11` });| Option | Type | What it is |
|---|---|---|
pool | readonly C[] | The cards that can come up. |
count | number | How many to deal. Never more than the pool holds. |
rarities? | readonly RarityTier[] | Your tiers. The six defaults otherwise. |
seed? | string | Leave it out for a roll that differs every time. |
No card is dealt twice. A card whose tier weighs nothing, or is not in the list, is never dealt.
Read the odds
import { chances } from "@react-cards-opening/core";
const odds = chances(allCards); // Map<id, 0..1>
const label = `${(odds.get("ash-phoenix")! * 100).toFixed(1)}%`;A card's chance is its tier's weight over the weights of every card in the pool. The numbers are for the next single draw; they add up to one.
One card, with the same odds
pickCard draws a single card by tier weight and leaves it in the pool, so the same card can come up twice. It is what fills the strip of a reel with what could have been. Give it your own dice to make it repeatable.
import { createRng, pickCard } from "@react-cards-opening/core";
const rng = createRng("strip:42");
const tiles = Array.from({ length: 56 }, () => pickCard(allCards, { random: rng.next }));Roll on the server
A roll in the browser is a roll the visitor can redo. Deal on the server, store the result, and send only the dealt cards down. The seed makes it cheap to check later: the same inputs give the same hand.
import { rollCards } from "@react-cards-opening/core";
export async function dealFor(userId: string) {
const dealt = await db.deals.find(userId);
if (dealt) return dealt.cards;
const cards = rollCards({ pool: CATALOG, count: 5, seed: `${userId}:${SEASON}` });
await db.deals.insert({ userId, cards });
return cards;
}A seeded source of your own
import { createRng } from "@react-cards-opening/core";
const rng = createRng("season-4");
rng.next(); // a float in [0, 1)
rng.int(6); // an integer in [0, 6)
rng.pick(["heads", "tails"]);
const art = rng.fork("art"); // a separate stream that does not disturb this one