A TADS3 module implementing playing cards

Another little module, this one implementing several different kinds of playing cards: playingCards github repo.

This just implements the cards/decks themselves, and a couple of basic actions (>SHUFFLE, >DEAL, and >DISCARD) for interacting with them. It doesn’t implement any in-game playing card game mechanics.

The main utility the module provides is juggling all of the vocabulary for the individual cards. Specifically it does this without implementing each individual card as a separate in-game object.

I won’t bother to walk through all of the module internals here (unless someone is interested in them), but there’s a bunch of gymnastics happening to adjust logical rankings and vocab likelihoods and so on.

Very simple version is:

  • Game implementor creates an instance of some Deck subclass
  • The Deck, when initialized, automagically creates a single PlayingCardUnthing and PlayingCardType instance for its specific playing card type
  • The PlayingCardUnthing handles card-related actions when there are no card-related objects in scope
  • The PlayingCardType handles the slight-of-hand needed to map the vocabulary for individual cards (“3C” or “three of clubs”) to the in-game object currently best suited to handle it (usually based on whether or not the player is currently holding the card in question).
    PlayingCardType also has a report manager that handles merging action reports involving invividual cards.
  • >DEALing cards creates a PlayingCardHand instance for each player receiving cards, unless they already have one. Under the hood the instance is moved into and out of the actor’s inventory as appropriate (so they’re never holding a “hand” containing no cards)
  • >DISCARDing cards creates a DiscardPile instance (if one doesn’t already exist for the deck the cards came from). It’s an in-game object provided mostly for flavor, but examining it will provide an approximate description of how many cards have been discarded.

By default the module includes support for the familiar 52-card French-suited deck via StandardDeck. It also includes definitions for the 40-card Spanish-suited deck, hanafuda cards, and the 78-card tarot deck (using the Waite-Rider suit and arcana names). Support for each of these decks can be enabled at compile time by specifying a preprocessor flag (-D PLAYING_CARDS_SPANISH, -D PLAYING_CARDS_HANAFUDA, and -D PLAYING_CARDS_TAROT, respectively).

I’m separately working on a module for implementing in-game card games, but that’s not done yet—I started out writing just a draw poker implementation, and refactoring that I broke out the card/deck definitions, and I’m currently thinking I want to write a generic card game module and make poker a special case of it, but I might change my mind.


Bump for a minor update. The internals of the PlayingCardsHand have been changed. Now instead of each player having a single card hand object, they get one per deck. Presumably in most cases that will work out to be the same thing, but now multiple simultaneous hands of cards (per actor) are theoretically supported. Multiple hands won’t work in-game for the player character without some tweaking to the hand objects’ vocabulary: by default, a hand object is disambiguated by their owner (“your cards” versus “Alice’s cards”), so without tweaking you’d end up with something like “Which cards do you mean, your cards or your cards?”

To make this slightly less of a trap for the unwary, now by default CardDeck objects will block travel by Actors carrying hands of cards dealt from them. I think I’m not going to bother implementing a more general solution because I think the way I’m going to resolve the issue in the cardGame module I’m working on is to make card games weakly modal—once the player starts a card game they can’t get up and leave without quitting the game.