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 🙏

© 2024 – Pkg Stats / Ryan Hefner

unocab

v0.8.0

Published

An UNO game engine

Downloads

38

Readme

Unocab

Unocab is an UNO game engine. Usage is simple:

// On creation of a Game, an UNO deck of 108 cards is
// shuffled and assigned to `game.state.cache.deck`.
const game = new Game({
  // Whether to stack +2s. If this is enabled, and
  // say three +2s are played one after the other,
  // the person to draw next will pick up 6 cards.
  // This option is enabled by default.
  stackPlusTwos: true,

  // The initial seed determines all the random events
  // of the game. Hence if you create two games with
  // the same initial seed and the same number of players
  // join the game at the same time, the shuffled deck and
  // the hands of the players will be the same.
  initialSeed: "unocab rocks!",

  // Explained in the next README section.
  shorthandMode: false,
});

// At the start of the game, the engine also plays the
// first card of the deck which is not a +4 or color
// switcher. After this point, players continue the
// game based on the type and color of this card.

// Whenever new players join, a hand of 7 cards is
// assigned to each of them. A hand is formed by
// drawing the top card of the deck 7 times. This
// is random, since the deck has been shuffled.
const [p1, p2] = ["player1", "player2"];
game.join(p1, p2);

// let's assume the first card was a Red 4, and p1 has
// a Red 6 and a +4.
game.play(p1, { type: "six", color: "red" });

game.draw(p2);
game.pass(p2);

game.play(p1, { type: "plus-four" });
game.switchColor(p1, "blue");

game.callBluff(p2);

// ... more moves

const end = game.hasEnded();
if (end) console.log(end.loser + " lost the game!");

Full vs Shorthand mode

During creation of a Game, you can optionally pass in shorthandMode: true. In shorthand mode, the engine only keeps as much information about the game as is needed for it to function. Otherwise, and by default, all information about the game is kept in the events array (game.state.events). This allows you to have a history of all the happenings in that particular game, and also allows you to jump to an arbitrary point in the game. For example, to undo a move, you could use game.jumpToEventIndex(-2). This function is not usable in shorthand mode, and the events array will only keep 5 events at most.

TLDR: Use shorthand mode if you don't want to use jumpToEventIndex or keep the entire game history.

Serialising and Deserialising

A game can be serialised into JSON using game.serialise(), and cloned using game.clone(). A game can be deserialised using new Game({ serialisedState: gameJson }).

However, when deserialising a game, arguments like shorthandMode or stackPlusTwos are not allowed, since they were already set when the game was being initiated.

Error Reporting

If a player tries to perform an invalid move, such as drawing twice in a row or playing a card that is not allowed, an UnocabError will be thrown. The message provides an adequate description of what the problem was, but you can also access information pertaining to the error using the code and data fields in an UnocabError. Please have a look at errors.ts for the variations of error codes and the data they provide.

Types

Unocab provides Zod types for most of the objects it uses in case you're building something like a REST API and want to verify the structure. Please glance over the types.ts file to figure out what Zod types are provided.

GameEvent

A game event is an object with a type that denotes what kind of event it is (e.g. draw or card_played), and additional info about that particular event, such as the card property in a card_played event.

It looks like { type: "card_played", by: "player-one", card: { type: "six", color: "red" } }.

NOTE: In full mode (aka not shorthand mode), the events array will also contain events with type game_info. These events are not a result of player's actions, but are added by the engine to denote certain happenings. For example, when a player calls bluff, a game_info event will be added afterwards to signify whether the bluff call succeeded or failed. This can be helpful for something like implementation of animations when re-playing the game.

For more information about what events exist, please glance over the types.ts file.

Fetching valid events

To obtain a list of "options" for the active player in a game, use game.validEvents(playerId). This will return an array of GameEvents which the player is allowed to act on. These events can directly be used like game.processNewEvent(event), which acts as an alternative to functions like game.draw or game.pass, which internally use processNewEvent as well.

Demo implementations

Please refer to impls/cli-uno.ts for a cli-based game with an AI. There's also impls/autoplay.ts, which is an AI vs AI game.

(AI here naturally means if conditions, ofcourse!)