miaoda-game-deck-core
v0.3.0
Published
Engine-agnostic deck/pile engine for card games: named zones, deterministic shuffle with optional authoritative RNG injection, automatic reshuffle, hand limits, atomic selected-card moves, saves, and pure snapshot operations for reducer/command state. Car
Downloads
1,122
Maintainers
Readme
miaoda-game-deck-core
Use this package for draw piles, hands, discard piles, exhaust zones, tables, and other card-game zones. It owns card movement, deterministic shuffles, mid-draw reshuffles, hand overflow policies, change events, and snapshots. Card effects and game rules remain yours.
Install and create a deck
pnpm add miaoda-game-deck-coreimport { Deck } from 'miaoda-game-deck-core';
const deck = new Deck({
seed: 1234,
zones: [
{ id: 'draw', cards: cardList },
{ id: 'hand', limit: 10 },
{ id: 'discard' },
{ id: 'exhaust' },
],
});
deck.shuffle();
const opening = deck.draw(5);
deck.move('strike-1', 'discard');
deck.moveAll('hand', 'discard');
deck.draw(5); // reshuffles discard automatically when draw emptiesCards need a stable string id; other card fields are preserved as your data. cardsIn(zone) returns cards bottom-to-top, with the last card as the pile top.
Draw and move rules
- Drawing across an empty draw pile reshuffles discard into draw and continues the same request.
- Drawing from completely empty zones returns fewer cards, never holes or an infinite loop.
moveManyis atomic: if any selected card is missing from the declared source, nothing moves.- Hand
limitis enforced bydraw, withoverflow: 'stop'(default),'allow', or'spill'to another zone. DirectmoveandmoveAlldo not enforce the limit.
deck.draw(3, { to: 'hand', overflow: 'spill', spillTo: 'discard' });
deck.moveMany(['a', 'b'], 'table', { from: 'hand' });Events for animation
deck.onChange((event) => queueCardAnimation(event));Each mutation emits one event. A draw that needs a reshuffle emits reshuffle then draw synchronously; queue those events so the pile collapse finishes before cards fan out. exhaust is represented as a normal move with to: 'exhaust'. Empty moveAll is a silent no-op.
Determinism, snapshots, and hidden information
The seeded RNG state is included in toJSON, so restoring a snapshot reproduces future shuffles. Deck.fromJSON creates a new instance; loadJSON replaces an existing instance and keeps its listeners.
Snapshots contain every hidden card and private RNG state. Never send them directly to an untrusted player; project zones, card IDs, and deck events at the game/server boundary. applyDeckOperation(snapshot, operation) is the data-first option for command reducers and AI simulations.
Composition boundaries
Use turn-core for who acts and when, command-core for atomic deterministic commands and replay, and card-specific rules packages for legal plays and scoring. This package only manages zones and piles.
