Skip to content

A simple foundation for games that use playing card deck rules

License

Notifications You must be signed in to change notification settings

AvalonOmnimedia/PlayingCardsEngine

Repository files navigation

PlayingCardsEngine

Build Status

This is a simple library to give you a jump start on writing your own card games!

In this repo, there are two pieces:

The engine

An example game

The engine

The engine is really a collection of components. You are not required to use all the components (particularly the GameRunner) if you do not want to.

Basic

Advanced

Basic Components

Deck

The most fundamental building block of a card game is the Deck. This is an abstract class with the most basic functionality. What is actually used for game play is the PlayingDeck. This is what players will draw from and possibly discard to (there is also a DiscardPile if that is necessary). The PlayingDeck starts empty and must have other decks added to it. For example:

A 52 card StandardDeck is included in the library, but you are free to make your own as well.

Hand

Like the Deck, the Hand is an abstract class with just the base functionality. Two flavors of hands are also included in the library: the FaceUpHand and the FaceDownStack.

Player

Yet another abstract class, the PlayerBase is slightly different because it is generic so that the correct implementation of Hand can be referenced.

Advanced Components

Phases

A phase is really just a class that implements the IGamePhase interface. It doesn't have any properties or methods on it; it just signals where you are in game play. If you like, you can create a sealed class that all your phases can then inherit from (gaining all the magic powers that Kotlin bestows on them) but it's not necessary either. Here are the phases in the example project:

sealed class GamePhase : IGamePhase
class Start : GamePhase(), IGameStart
class ReadyToFlip : GamePhase(), IRoundStart
class BattleStarted : GamePhase()
class RoundOver : GamePhase(), IRoundEnd
class GameOver : GamePhase(), IGameEnd

sealed class BattleOutcome : GamePhase()
class BattleWon : BattleOutcome()
class Tie : BattleOutcome()

Note: If using the GameRunner, phases must be a class, not an object. They are created through reflection, which does not work with objects.

Your phases can also be marked to distinguish between the start of a game, start of a round, etc. This is there for your use and is currently not used by the engine (but it may be in the future).

Actions

Actions implement the IGameAction interface and are how you move from one phase to the next. Here, again, is how they are setup in the example project:

sealed class GameAction : IGameAction
object Deal : GameAction()
object Flip : GameAction()
object CompareCards : GameAction()
object WinnerGetsCards : GameAction()
object AnteUp : GameAction()
object CountStacks : GameAction()

GameContext

The GameContextBase is an abstract class that will help you bring your various components along from phase to phase. It is the real state of your game. The one property that is not from the library is the cardComparator. Since every game has a distinct way of comparing the value of cards (i.e. are aces high or low, do suits matter, etc.), you must build this yourself. Here is the cardComparator from the example game:

class CardComparator : Comparator<Card> {
    override fun compare(card1: Card, card2: Card): Int {
        return when {
            card1.value > card2.value -> 1
            card1.value < card2.value -> -1
            else -> 0
        }
    }
}

GameRunner

Finally, the GameRunner. This is your state machine. Use the createRunner(...) method to build it, along with all of your phases. It is generic so the full power of your GameContextBase implementation can be utilized.

Each phase that is going to be used in your game must be registered upfront. You can register the phase using the phase<IGamePhase> method. Every registered phase can have three parts: onEntry functions, onExit functions, and on<IGameAction> transitions (and it can have multiple of each).

Here is a simple example from the included game:

phase<Start> {
    onEntry { TermUi.echo("Let's play!") }
    on<Deal> {
        onDeal()
        transitionTo<ReadyToFlip>()
    }
}

In the on<IGameAction> lambda, the parameter is the GameContext on the runner (in the example above, onDeal is a method on GameContext). The lambda must also return a KClass, which the helper function transitionTo<IGamePhase> will do for you.

Once all your phases are registered, all you need to do it call perform(IGameAction) on the GameRunner and, if an action has being added for the current phase, the runner will fire every transition lambda and marshal you into the next phase.


And that's it! More is planned for the library, so please stay tuned!

About

A simple foundation for games that use playing card deck rules

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages