Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
/**
|
|
* Deck creation and deterministic shuffling for the poker engine.
|
|
* Pure: randomness is always injected; no module-level state.
|
|
*/
|
|
|
|
import type { Card, Rank, Suit } from "./types";
|
|
|
|
const SUITS: readonly Suit[] = ["s", "h", "d", "c"];
|
|
const MIN_RANK: Rank = 2;
|
|
const MAX_RANK: Rank = 14;
|
|
|
|
/** A fresh, ordered 52-card deck (order is deterministic before shuffling). */
|
|
export function createDeck(): Card[] {
|
|
const deck: Card[] = [];
|
|
for (const suit of SUITS) {
|
|
for (let rank = MIN_RANK; rank <= MAX_RANK; rank += 1) {
|
|
deck.push({ rank: rank as Rank, suit });
|
|
}
|
|
}
|
|
return deck;
|
|
}
|
|
|
|
/**
|
|
* Small, fast seeded PRNG (mulberry32). Used so a whole multi-hand game can
|
|
* be replayed from a single integer seed stored in the table state.
|
|
*/
|
|
export function mulberry32(seed: number): () => number {
|
|
let a = seed >>> 0;
|
|
return () => {
|
|
a = (a + 0x6d2b79f5) >>> 0;
|
|
let t = a;
|
|
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
|
|
/** Fisher-Yates shuffle; returns a new array, never mutates the input. */
|
|
export function shuffleDeck(cards: readonly Card[], rng: () => number): Card[] {
|
|
const deck = [...cards];
|
|
for (let i = deck.length - 1; i > 0; i -= 1) {
|
|
const j = Math.floor(rng() * (i + 1));
|
|
const tmp = deck[i];
|
|
deck[i] = deck[j];
|
|
deck[j] = tmp;
|
|
}
|
|
return deck;
|
|
}
|
|
|
|
const GOLDEN_RATIO_32 = 0x9e3779b9;
|
|
|
|
/** Per-hand seed derived from the table's base seed and the hand number. */
|
|
export function handSeed(baseSeed: number, handNumber: number): number {
|
|
return (baseSeed + Math.imul(handNumber, GOLDEN_RATIO_32)) >>> 0;
|
|
}
|
|
|
|
/** The exact shuffled deck used for a given hand of a given game seed. */
|
|
export function createShuffledDeck(baseSeed: number, handNumber: number): Card[] {
|
|
return shuffleDeck(createDeck(), mulberry32(handSeed(baseSeed, handNumber)));
|
|
}
|