npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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.

Readme

@theomegafett/rps-game-logic

npm version npm downloads min size minzip size types license issues

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-logic

View 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 1

Card

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 copy

Player

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 object

getWinner(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-place

Game 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 type

Examples

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

Repository

GitHub - RPS Card Game