miaoda-game-crazy-eights-rules
v0.3.2
Published
Verified basic Crazy Eights rules with legal plays, wild-suit calls, last-card penalties, reshuffling, scoring, safe player views, and deterministic JSON state.
Maintainers
Readme
miaoda-game-crazy-eights-rules
Immutable, engine-neutral rules for fixed profile pagat-basic-single-deck-2to8: two to eight
players, matching rank or suit, wild Eights with suit calls, optional drawing, last-card declaration
penalties, stock recycling, hidden hands, and one-hand scoring. This is not an UNO, Switch, or
Mau-Mau profile.
pnpm add miaoda-game-crazy-eights-rulesRun a game
import {
applyCrazyEightsAction,
createCrazyEightsGame,
createCrazyEightsPlayerView,
} from 'miaoda-game-crazy-eights-rules';
let state = createCrazyEightsGame({
playerIds: ['human', 'bot-1', 'bot-2'],
seed: 2026,
});
const playerId = state.activePlayerId;
const view = createCrazyEightsPlayerView(state, playerId);
const play = view.legalActions.playOptions[0];
const action = play
? {
type: 'play-card' as const,
playerId,
cardId: play.cardId,
...(play.calledSuits[0] ? { calledSuit: play.calledSuits[0] } : {}),
declaredLastCard: play.requiresLastCardDeclaration,
}
: { type: 'draw-card' as const, playerId };
const result = applyCrazyEightsAction(state, action);
if (result.ok) state = result.state;An Eight's play option lists all allowed called suits. Drawing is allowed even when a play exists and
ends the turn; the drawn card cannot be played immediately. Omitting a required
declaredLastCard remains a legal play and immediately applies the two-card penalty.
Project events
import { projectCrazyEightsEvents } from 'miaoda-game-crazy-eights-rules';
if (result.ok) {
const publicEvents = projectCrazyEightsEvents(result.events);
broadcast(publicEvents);
}The projector returns detached, public events. A draw reports only playerId and count; it never
contains the drawn card id. Unknown runtime event types throw instead of being forwarded.
Restore a snapshot
import { restoreCrazyEightsState } from 'miaoda-game-crazy-eights-rules';
const parsed: unknown = JSON.parse(savedJson);
state = restoreCrazyEightsState(parsed);restoreCrazyEightsState validates the complete fixed-profile state and returns a detached copy.
Use validateCrazyEightsState when a non-throwing validation result is preferable.
Keep authoritative hands and stock on a trusted host. Send each player only
createCrazyEightsPlayerView(state, playerId) plus projected events. Localize stable rejection codes
instead of displaying diagnostic messages.
