# 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` });
```

| 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

```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](https://bettertyped.github.io/react-cards-opening/docs/components/card-spin/) 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`

```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
```

## Related

- [Rarity tiers](https://bettertyped.github.io/react-cards-opening/docs/rarity-tiers/)
- [Custom tiers](https://bettertyped.github.io/react-cards-opening/docs/guides/custom-tiers/)
- [Hiding places](https://bettertyped.github.io/react-cards-opening/docs/core/spots/)
- [Keep opened cards](https://bettertyped.github.io/react-cards-opening/docs/guides/keep-opened-cards/)

Source: https://bettertyped.github.io/react-cards-opening/docs/core/rolls/
