@devarshmavani/card-engine
v0.1.1
Published
A reusable, type-safe card game engine library for TypeScript. Build card games like Rummy, Teen Patti, Poker with fully typed game states and events.
Maintainers
Readme
Card Engine
A reusable, type-safe card game engine library for TypeScript. Build card games like Rummy, Teen Patti, Do Teen Panch, Poker, and more with fully typed game states and events.
Features
- 🎯 Fully Type-Safe - Generic game states and events with complete TypeScript support
- 🎮 Game Agnostic - Build any card game by extending the RuleEngine
- 📦 Tree-Shakeable - Import only what you need
- 🔄 Serializable - Full game state serialization for multiplayer/persistence
- 🎪 Event-Driven - Type-safe event system for UI synchronization
- 🌐 Universal - Works in Node.js and browser environments
- 📝 Pure Logic - No DOM/UI dependencies
Installation
npm install @devarsh-mavani-19/card-engineQuick Start
import { Game, Player, RuleEngine, GameState } from '@devarsh-mavani-19/card-engine';
// Define your game state
interface MyGameState extends GameState {
currentRound: number;
tricks: number;
}
// Create your game rules
class MyGameRules extends RuleEngine<MyGameState> {
getInitialCardCount(): number {
return 5;
}
canPlayCard(player: Player, card: Card, state: MyGameState): boolean {
// Your game logic
return true;
}
// ... implement other required methods
}
// Create and start a game
const game = new Game<MyGameState>(new MyGameRules());
game.addPlayer(new Player('p1', 'Alice'));
game.addPlayer(new Player('p2', 'Bob'));
// Listen to type-safe events
game.on('gameStarted', (data) => {
console.log(`Game started with ${data.players.length} players`);
console.log(`Round: ${data.state.currentRound}`);
});
game.start();Core Classes
Card
Represents a single card with rank, suit, and optional metadata.
import { Card } from '@devarsh-mavani-19/card-engine';
const card = new Card('A', 'hearts');
console.log(card.toString()); // "A of hearts"Deck
Manages a deck of cards with shuffle, draw, and reset operations.
import { Deck } from '@devarsh-mavani-19/card-engine';
const deck = Deck.createStandardDeck(); // 52 cards
deck.shuffle();
const cards = deck.draw(5); // Draw 5 cardsPlayer
Represents a player with a hand of cards.
import { Player } from '@devarsh-mavani-19/card-engine';
const player = new Player('p1', 'Alice');
player.receiveCard(card);
const playedCard = player.playCard(card);RuleEngine
Abstract class for implementing game-specific rules.
import { RuleEngine, GameState } from '@devarsh-mavani-19/card-engine';
interface MyState extends GameState {
// Your game state
}
class MyRules extends RuleEngine<MyState> {
canPlayCard(player, card, state) { /* ... */ }
isWinningHand(player, state) { /* ... */ }
onCardPlayed(player, card, state) { /* ... */ }
validateMove(move, state) { /* ... */ }
getInitialCardCount() { /* ... */ }
getWinners(state) { /* ... */ }
isGameOver(state) { /* ... */ }
}Game
Main game controller with turn management and state handling.
import { Game } from '@devarsh-mavani-19/card-engine';
const game = new Game<MyState>(new MyRules());
game.addPlayer(player);
game.start();
game.playCard(card);
game.nextTurn();Type-Safe Events
All events are fully typed based on your game state:
game.on('gameStarted', (data) => {
// data: { players: Player[]; state: MyState }
});
game.on('cardPlayed', (data) => {
// data: { player: Player; card: Card }
});
game.on('turnStarted', (player) => {
// player: Player
});
game.on('gameEnded', (data) => {
// data: { winners: string[]; state: MyState }
});Serialization
Save and restore complete game state:
// Serialize
const serialized = game.serialize();
const json = JSON.stringify(serialized);
// Deserialize
const restored = Game.deserialize(JSON.parse(json), new MyRules());Example: Do Teen Panch
import { Game, Player, RuleEngine, GameState, Card } from '@devarsh-mavani-19/card-engine';
interface DoTeenPanchState extends GameState {
roundNumber: number;
currentSuit?: string;
tricks: { playerId: string; card: Card }[];
playerScores: Map<string, number>;
}
class DoTeenPanchRules extends RuleEngine<DoTeenPanchState> {
getInitialCardCount() {
return 5;
}
canPlayCard(player: Player, card: Card, state: DoTeenPanchState): boolean {
if (!state.currentSuit) return true;
const hasSuit = player.getHand().some(c => c.suit === state.currentSuit);
return !hasSuit || card.suit === state.currentSuit;
}
// ... implement other methods
}
const game = new Game<DoTeenPanchState>(new DoTeenPanchRules());
game.addPlayer(new Player('p1', 'Alice'));
game.addPlayer(new Player('p2', 'Bob'));
game.start();Architecture
card-engine/
├── core/
│ ├── Game.ts # Main game controller
│ ├── RuleEngine.ts # Abstract rule engine
│ └── EventEmitter.ts # Type-safe event system
├── models/
│ ├── Card.ts # Card representation
│ ├── Deck.ts # Deck management
│ └── Player.ts # Player management
└── index.ts # Public exportsLicense
MIT
Author
devarsh-mavani-19 ([email protected])
Contributing
Issues and pull requests are welcome on GitHub.
