card_game_engine 0.0.0

An extensible card game engine
Documentation
# Game Architecture

As games can be versatile and dynamic, the CGE needs to be able to support
various different 'archetypes' of games.

As such, it provides little enforcement on how a game _has_ to be structured,
and tries its best to provide the needed support to easily implement games with
it.

To see such an implementation in action, let us implement a game that is
similar to Uno with the CGE.


Let's see first what CGE has to offer as basic building blocks:

- Player objects
    - These represent, of course, the players of the game
    - As they are game objects, you can store information in them
    - They are special, as they serve as 'entry points' of interaction in the
      game
- Game Objects
    - Game Objects represent everything that can be interacted with in the
      game. For example cards, but also tokens, counters, etc...
- Game Zones
    - Game Zones are where Game Objects reside
    - You usually have zones for the hand of each player, a discard pile, a
      deck (shared or for each player), etc...

With these building blocks you can then start to build your game.

For a Uno kind of game, we will go with these rules:

- There are four colors of cards, each color has cards with values going from 0
  to 9
- There are special cards:
    - Four +2 cards, one of each color, that make the next player draw two
      cards and skip their turn
    - Two +4 cards, that make the next player draw four cards, skip their turn,
      and a new color is chosen for the discard pile
    - Four reverse card, one in each color, that reverses the turn order
    - Two color pick card, where the next color of the discard pile is chosen
- Each player starts the game with 7 cards in hand. Whoever goes down to 0
  first wins.
- Each turn a player has to either:
    - Draw a card and skip their turn
    - Play a single card that is allowed under the current color


So, let's see how such a game could be implemented using CGELang:


First, let's define the kind of cards that exist
```cgelang
data Color = Red | Blue | Green | Yellow

data Number = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine

data Special = Plus2 { color: Color } | Plus4 | Reverse { color: Color } | PickColor
```

> These data definitions each create a new type. The types are so-called sum
> types. We have chosen the valid range of values each can take. So for
> example, only the four colors written down are valid `Color`s. "Purple" would
> not work and the game would not be valid.

Let's do game startup:

```cgelang
data GameZone = DeckZone | DiscardPile | PlayerHand { id: PlayerId }

impl Zone for GameZone

data SimpleCard = SimpleCard { color: Color, number: Number }

impl Object for SimpleCard

data Plus2Card = Plus2Card { color: Color }

impl Object for Plus2Card

data ReverseCard = ReverseCard { color: Color }

impl Object for ReverseCard

data Plus4Card = Plus4Card

impl Object for Plus4Card

data PickColorCard = PickColorCard

impl Object for PickColor Card

func setupGame() {
    let cards = []
    for color in [Red, Blue, Green, Yellow] do
        for number in 0..10 do
            cards.push(SimpleCard { color, number });
            cards.push(SimpleCard { color, number });
        end

        cards.push(Plus2Card { color });
        cards.push(Plus2Card { color });
        cards.push(ReverseCard { color });
        cards.push(ReverseCard { color });
    end

    for _ in 0..4 do
        cards.push(Plus4Card);
        cards.push(PickColorCard);
    end

    let deck = createZone(DeckZone, cards)

    deck.shuffle()

    createZone(DiscardPile, [])

    for player in getAllPlayers() do
        let startingHand = deck.takeFront(7);

        createZone(PlayerHand(player), startingHand) 
    end
}

Game.registerEvent(GameSetup, setupGame)
```

A few things happen here:

- We create cards in every combination, as well as the special cards.
- Using `func <name>(<args...>) -> <return> do ... end` we create a function
  that we can then use to create the game objects.


Now, let's start defining the beginning of the game:

```cgelang
func startGame() {
    let deck = getZone(DeckZone)
    let discardPile = getZone(DiscardPile)

    discardPile.push(deck.takeFront(1))
}

Game.registerEvent(StartGame, startGame);
```

Now, it _could_ be that the first card is a special card. In which case it will
directly have effect on the current player! Here is how we detect this case:


```cgelang
func onPlayPlus2(fromZone, toZone: DiscardPile, obj: Plus2Card) {
    let deck = getZone(Deck)

    let hand = getZone(PlayerHand { player: currentPlayer })

    hand.push(deck.takeFront(2))
}

Game.registerEvent(ObjectMoveTo { toZone: DiscardPile, object: Plus2Card, .. }, onPlayPlus2);
```


Currently players can't really do anything, this is because we have not _allowed_ them to do so!
An important aspect in CGE is that players can allow do actions you have explicitely allowed.

Here's how we define that players can play a single card from their hand:


```cgelang
data PlayCard = PlayCard { card: Object }

impl Input for PlayCard

data DrawCard = DrawCard

impl Input for DrawCard

data ActivePlayer = ActivePlayer { player: PlayerId }

func startGameWithActivePlayer() {
    let players = getAllPlayers();

    let startPlayer = players.takeRandom();

    Game.setValue(ActivePlayer { player: startPlayer });
}

Game.registerEvent(GameStart, startGameWithActivePlayer);

data Phase = PlayCardOrDraw

func allowCurrentPlayerToDrawOrPlayCard() {
    let activePlayer: ActivePlayer = Game.getValue();
    Game.allowInputFrom(activePlayer.player, DrawCard);
    Game.allowInputFrom(activePlayer.player, PlayCard);
}

Game.registerFact(PlayCardOrDraw, allowCurrentPlayerToDrawOrPlayCard)
```

It is important to note that these inputs are recalculated _everytime_ the engine 'steps forward'