Skip to content
react-cards-opening

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

ts
import { rollCards } from "@react-cards-opening/core";

const hand = rollCards({ pool: allCards, count: 5, seed: `${userId}:2026-10-11` });
OptionTypeWhat it is
poolreadonly C[]The cards that can come up.
countnumberHow many to deal. Never more than the pool holds.
rarities?readonly RarityTier[]Your tiers. The six defaults otherwise.
seed?stringLeave 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

ts
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.

ts
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.

server.ts
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

ts
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

Last updated . Read this page as Markdown.