@theomegafett/rps-game-logic
v1.2.4
Published
Core game logic for a Rock-Paper-Scissors deck-builder with AI opponents, hybrid cards, and a full game controller for Node.js and browser.
Maintainers
Readme
@theomegafett/rps-game-logic
Core game logic for Rock Paper Scissors card game with deck building mechanics. ~8KB minzipped
Features
- ✅ Pure JavaScript - No dependencies, works in Node.js and browsers
- 🎴 Card Management - Complete card type system with hybrid and special cards
- 📦 Deck Building - Deck validation with rarity limits
- 🎮 Game Controller - Full game state management and round resolution
- 🧠 AI Difficulty System - Three modes with probabilistic card counting (Easy, Normal, Hard)
- 🧪 Well-Tested - Comprehensive JSDoc documentation with automated tests
Installation
From NPM (Published Package)
npm install @theomegafett/rps-game-logicView on NPM
🔗 npmjs.com/package/@theomegafett/rps-game-logic
Why This Package?
✅ Zero dependencies - Pure JavaScript, works anywhere
✅ TypeScript support - Ships with .d.ts for full IntelliSense
✅ AI included - Three difficulty modes with card counting
✅ Tree-shakeable - Import only what you need
✅ Tiny - ~8KB minzipped
✅ Well-tested - Comprehensive test suite
Zero to 60 Seconds
import { Deck, GameController, CardType } from "@theomegafett/rps-game-logic";
// Create a valid 10-card deck
const deck = new Deck();
[
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
].forEach((t) => deck.addCard(t));
// Play a game
const game = new GameController();
game.setupGame(deck);
const result = game.playRound(0); // Play first card
console.log(result); // { result: "Player Wins", playerCard, aiCard }Try in Browser (No Install)
<script type="module">
import {
Deck,
GameController,
CardType,
} from "https://cdn.jsdelivr.net/npm/@theomegafett/rps-game-logic@latest/+esm";
const deck = new Deck();
for (let i = 0; i < 10; i++) {
deck.addCard([CardType.ROCK, CardType.PAPER, CardType.SCISSORS][i % 3]);
}
const game = new GameController();
game.setupGame(deck);
console.log("Round 1:", game.playRound(0));
console.log("Round 2:", game.playRound(0));
</script>Installation & Usage
ESM (Modern)
import {
Card,
Deck,
GameController,
CardType,
} from "@theomegafett/rps-game-logic";CommonJS (Node.js)
const {
Deck,
GameController,
CardType,
} = require("@theomegafett/rps-game-logic");Subpath Imports (Tree-shaking)
import { Deck } from "@theomegafett/rps-game-logic/deck";
import { getWinner } from "@theomegafett/rps-game-logic/game";
import { DECK_MIN, DECK_MAX } from "@theomegafett/rps-game-logic/constants";Quick Start
import {
Card,
Deck,
GameController,
CardType,
getWinner,
chooseCard,
} from "@theomegafett/rps-game-logic";
// Create a deck
const deck = new Deck();
deck.addCard(CardType.ROCK);
deck.addCard(CardType.PAPER);
deck.addCard(CardType.SCISSORS);
// ... add more cards
// Start a game
const game = new GameController();
game.setupGame(deck);
// Play a round
const result = game.playRound(0); // Play first card in hand
console.log(result); // { result: "Player Wins", playerCard, aiCard }API Reference
CardType
Enum of all available card types:
CardType.ROCK;
CardType.PAPER;
CardType.SCISSORS;
CardType.ROCK_DRAW; // Draw 1 card after playing
CardType.PAPER_DRAW; // Draw 1 card after playing
CardType.SCISSORS_DRAW; // Draw 1 card after playing
CardType.PAPER_ROCK; // Hybrid card
CardType.ROCK_SCISSORS; // Hybrid card
CardType.SCISSORS_PAPER; // Hybrid card
CardType.BLOCK_DRAW_TWO; // Block scoring, draw 2 cards
CardType.BLOCK_DISCARD; // Block scoring, opponent discards 1Card
const card = new Card(CardType.ROCK);
card.type; // "Rock"
card.baseTypes; // ["Rock"]Deck
const deck = new Deck();
deck.addCard(cardType); // Add a card (respects limits)
deck.isValid(); // Check if deck has 10+ cards
deck.shuffle(); // Shuffle the deck
deck.countCardType(cardType); // Count cards of a type
deck.size(); // Get card count
deck.copy(); // Create a copyPlayer
const player = new Player("Player Name", deck);
player.drawCard(); // Draw a card from deck to hand
player.playCard(index); // Play a card from hand
player.hand; // Current hand (array of Cards)
player.deck; // Remaining deck (array of Cards)GameController
const game = new GameController();
game.setupGame(playerDeck); // Initialize game with player's deck
game.playRound(cardIndex); // Play a round
game.roundsWon; // Player wins count
game.roundsLost; // AI wins count
game.player; // Player object
game.ai; // AI objectgetWinner(card1, card2)
Standalone function to determine winner between two cards:
import { getWinner, Card, CardType } from "@theomegafett/rps-game-logic";
const rock = new Card(CardType.ROCK);
const scissors = new Card(CardType.SCISSORS);
getWinner(rock, scissors); // Returns "card1"Returns: "card1" | "card2" | "draw" | "blocked"
AI Difficulty System (v1.1.0+)
chooseCard(aiHand, oppRemainingCounts, oppHistoryCounts, mode)
Intelligent AI card selection with three difficulty modes:
import {
chooseCard,
initializeCounts,
updateCounts,
} from "@theomegafett/rps-game-logic";
// Initialize tracking
const oppRemainingCounts = initializeCounts(opponentDeck);
const oppHistoryCounts = { Rock: 0, Paper: 0, Scissors: 0 };
// AI chooses card based on difficulty
const cardIndex = chooseCard(
aiHand, // Array of Card objects
oppRemainingCounts, // Cards left in opponent's deck
oppHistoryCounts, // Cards opponent has played
"hard" // 'easy' | 'normal' | 'hard'
);
// Play the chosen card
const aiCard = aiHand[cardIndex];
// Update tracking after opponent plays
updateCounts(opponentCard, oppRemainingCounts, oppHistoryCounts);Difficulty Modes:
| Mode | Behavior | Use Case |
| ---------- | --------------------------------- | -------------------- |
| 'easy' | Picks worst-EV card intentionally | Tutorials, beginners |
| 'normal' | Random uniform choice | Classic RPS feel |
| 'hard' | Bayesian + card counting | Competitive AI |
Safe to hot-swap mid-game! Changing difficulty only affects future decisions.
AI Helper Functions
// Initialize deck tracking
const counts = initializeCounts(deck);
// Returns: { Rock: 4, Paper: 3, Scissors: 3 }
// Update after opponent plays
updateCounts(playedCard, remainingCounts, historyCounts);
// Mutates count objects in-placeGame Rules
Basic Rules
- 🪨 Rock beats Scissors
- ✂️ Scissors beats Paper
- 📄 Paper beats Rock
Deck Building
- Minimum 10 cards, maximum 20 cards
- Rarity limits:
- Common (max 4 each): Rock, Paper, Scissors
- Uncommon (max 2 each): Rock+Draw, Paper+Draw, Scissors+Draw
- Rare (max 1-2 each): Hybrids, Block+Discard
- Legendary (max 1): Block+Draw Two
Special Cards
- Hybrid Cards: Can win as either type (e.g., Paper-Rock beats both Rock and Scissors)
- Draw Cards: Draw additional cards after playing
- Block Cards: Prevent scoring for that round
Constants
import {
DECK_MIN,
DECK_MAX,
INITIAL_HAND_SIZE,
CARD_LIMITS,
} from "@theomegafett/rps-game-logic";
DECK_MIN; // 10
DECK_MAX; // 20
INITIAL_HAND_SIZE; // 3
CARD_LIMITS; // Object with max counts and rarity for each card typeExamples
Standalone Winner Determination
import { getWinner, Card, CardType } from "@theomegafett/rps-game-logic";
const rock = new Card(CardType.ROCK);
const paper = new Card(CardType.PAPER);
console.log(getWinner(rock, paper)); // "card2" (paper wins)Full Game Loop
import { Deck, GameController, CardType } from "@theomegafett/rps-game-logic";
const deck = new Deck();
// Build deck...
deck.addCard(CardType.ROCK);
// ... (add 9+ more cards)
const game = new GameController();
game.setupGame(deck);
while (game.player.hand.length > 0 && game.ai.hand.length > 0) {
const result = game.playRound(0);
console.log(result.result);
}
console.log(`Final Score: ${game.roundsWon} - ${game.roundsLost}`);Module Exports
// Main export
import * as RPS from "@theomegafett/rps-game-logic";
// Named imports
import {
Card,
Deck,
Player,
GameController,
} from "@theomegafett/rps-game-logic";
// Specific module imports
import { Card } from "@theomegafett/rps-game-logic/card";
import { Deck } from "@theomegafett/rps-game-logic/deck";
import { getWinner } from "@theomegafett/rps-game-logic/game";
import { CARD_LIMITS } from "@theomegafett/rps-game-logic/constants";TypeScript Support
✅ Ships with .d.ts files - Full IntelliSense in VS Code and TypeScript projects out of the box.
import { GameController, Deck, CardType } from "@theomegafett/rps-game-logic";
const deck: Deck = new Deck();
[
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
CardType.PAPER,
CardType.SCISSORS,
CardType.ROCK,
].forEach((t) => deck.addCard(t));
const game: GameController = new GameController();
game.setupGame(deck);
const result = game.playRound(0);
// Full autocomplete for result.playerCard, result.aiCard, etc!All types auto-completed with full documentation in VS Code!
License
MIT
