Next: , Previous: , Up: Programming with libtarot   [Contents][Index]


4.3 Adding game events to a game

A game is basically an accumulation of game events. You may add events to a game, and iterate over the events in a game.

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <tarot.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#ifndef LOCALEDIR
#define LOCALEDIR "/usr/share/locale"
#endif /* NOT LOCALEDIR */

int
main ()
{
  TarotGame *game;
  TarotGameEvent *event;
  const TarotGameEvent *read_only_event;
  static const char *deal_card_names[] =
    {
     "10H", "JH", "CH", "QH", "KH",
     "11T", "12T", "13T", "14T", "15T", "16T", "17T", "18T", "19T",
     "EXC"
    };
  TarotCard deal_cards[sizeof (deal_card_names) / sizeof (deal_card_names[0])];
  size_t i;
  TarotGameIterator *iterator;
  TarotCard *dog_cards;
  size_t n_cards;
  
  if (tarot_init (LOCALEDIR) != 0)
    {
      fprintf (stderr, "Error: could not initialize libtarot.\n");
      return EXIT_FAILURE;
    }
  game = tarot_game_alloc ();
  
  /* The game has 5 players, with call */
  event = tarot_game_event_alloc_setup (5, 1);
  if (tarot_game_add_event (game, event) != TAROT_GAME_OK)
    {
      fprintf (stderr, "The game refused to be set up for 5 players, with call!\n");
      return EXIT_FAILURE;
    }
  tarot_game_event_free (event);

  /* Deal the cards */
  for (i = 0; i < sizeof (deal_cards) / sizeof (deal_cards[0]); i++)
    {
      deal_cards[i] = tarot_string_to_card_c (deal_card_names[i]);
      assert (deal_cards[i] != ((TarotCard) (-1)));
    }
  event = tarot_game_event_alloc_deal (tarot_string_to_player_c ("P5"),
                                       sizeof (deal_cards) / sizeof (deal_cards[0]),
                                       deal_cards);
  if (tarot_game_add_event (game, event) != TAROT_GAME_OK)
    {
      fprintf (stderr, "The game refused to deal cards to P5!\n");
      return EXIT_FAILURE;
    }
  tarot_game_event_free (event);

  /* Auto-reveal */
  if (tarot_game_can_autoreveal_alloc (game, &n_cards, &dog_cards))
    {
      /* This is a game for which we know the dog and we should add it! */
      event = tarot_game_event_alloc_dog (n_cards, dog_cards);
      free (dog_cards);
      if (tarot_game_check_event (game, event))
        {
          printf ("The game is effectively waiting for the dog.\n");
        }
      if (tarot_game_add_event (game, event) != TAROT_GAME_OK)
        {
          fprintf (stderr, "Could not auto-reveal the dog!\n");
          return EXIT_FAILURE;
        }
      tarot_game_event_free (event);
    }

  /* Iterate over the events */
  iterator = tarot_game_iterator (game);
  while ((read_only_event = tarot_game_iterator_next_value (iterator)) != NULL)
    {
      switch (tarot_game_event_type (read_only_event))
        {
        case TAROT_SETUP_EVENT:
          {
            size_t n_players;
            int with_call;
            if (tarot_game_event_get_setup (read_only_event, &n_players, &with_call) != TAROT_EVENT_OK)
              {
                fprintf (stderr, "This is not possible.\n");
                return EXIT_FAILURE;
              }
            printf ("There is a setup event!  The game has %lu players (with call? %d)\n",
                    n_players, with_call);
          }
          break;
        case TAROT_DEAL_EVENT:
          {
            TarotCard *cards;
            TarotPlayer who;
            char *player_name, *card_1_name, *card_2_name;
            TarotCard card2;
            size_t has_card_2;
            if (tarot_game_event_get_deal_alloc (read_only_event, &who, &n_cards, &cards) != TAROT_EVENT_OK)
              {
                return EXIT_FAILURE;
              }
            if (n_cards < 2)
              {
                return EXIT_FAILURE;
              }
            /* Query only a subset (of size 1... i.e. only one card)
               from the deal event */
            if (tarot_game_event_get_deal (read_only_event, &who, &has_card_2, 1, 1, &card2) != TAROT_EVENT_OK)
              {
                return EXIT_FAILURE;
              }
            assert (has_card_2 > 0);
            assert (card2 == cards[1]);
            player_name = tarot_player_to_string_alloc (who);
            card_1_name = tarot_card_to_string_alloc (cards[0]);
            card_2_name = tarot_card_to_string_alloc (cards[1]);
            printf ("There is a deal event!  The lucky player %s receives cards %s, %s, ...\n",
                    player_name, card_1_name, card_2_name);
            free (player_name);
            free (card_1_name);
            free (card_2_name);
            free (cards);
          }
          break;
        default:
          fprintf (stderr, "Unknown game event.\n");
          return EXIT_FAILURE;
        }
    }
  tarot_game_iterator_free (iterator);
  tarot_game_free (game);
  tarot_quit ();
  return EXIT_SUCCESS;
}
struct: TarotGame

A state that describes a game at a certain point in time.

struct: TarotGameEvent

The state for a complete game event, that you should not be able to mutate from the public API.

enum: TarotGameEventT

An enumeration of all possible game event types:

enum: TarotGameEventError

An enumeration of everything that could go wrong when manipulating events (see ‘TarotGameError’, and especially ‘TAROT_GAME_iNVEV’, for the validity of events). It has:

Function: TarotGame * tarot_game_alloc (void)
Function: TarotGameEvent * tarot_game_event_alloc_setup (size_t n_players, int with_call)
Function: TarotGameEvent * tarot_game_event_alloc_deal (TarotPlayer myself, size_t n_cards, const TarotCard *cards)
Function: TarotGameEvent * tarot_game_event_alloc_deal_all (size_t n_owners, const TarotPlayer *owners)
Function: TarotGameEvent * tarot_game_event_alloc_deal_all_random (size_t nplayers, size_t seedsize, const void *seed)
Function: TarotGameEvent * tarot_game_event_alloc_bid (TarotBid bid)
Function: TarotGameEvent * tarot_game_event_alloc_decl (int slam)
Function: TarotGameEvent * tarot_game_event_alloc_call (TarotCard call)
Function: TarotGameEvent * tarot_game_event_alloc_dog (size_t n_cards, const TarotCard *cards)
Function: TarotGameEvent * tarot_game_event_alloc_discard (size_t n_cards, const TarotCard *cards)
Function: TarotGameEvent * tarot_game_event_alloc_handful (size_t n_cards, const TarotCard *cards)
Function: TarotGameEvent * tarot_game_event_alloc_card (TarotCard card)
Function: TarotCounter * tarot_counter_alloc (void)

Allocate a new object. The return value is never ‘NULL’, it is not required to check for allocation failures as the whole program terminates if the internal memory allocation failed.

The game event allocators are used to instanciate all different game event types. The additional ‘tarot_game_event_alloc_deal_all_random’ deals all cards at random, for a given nplayers number of players, with seed (with seedsize, not 0, number of bytes). The actual random generator may vary from one version of libtarot to another.

Please note that the event instanciation cannot fail, even if the game events are not acceptable for the current game, and even if the game events are not acceptable for any game. For instance, you may create a dog event with 7 cards, or deal more than 78 cards, or the random seed for the random deal may lead to a petit sec. You should always check the events against the current game.

Function: TarotGameEventT tarot_game_event_type (const TarotGameEvent *event)

Return the type of event.

Function: TarotGameEventError tarot_game_event_get_setup (const TarotGameEvent *event, size_t *n_players, int *with_call)
Function: TarotGameEventError tarot_game_event_get_deal (const TarotGameEvent *event, TarotPlayer *myself, size_t *ncards, size_t start, size_t max, TarotCard *cards)
Function: TarotGameEventError tarot_game_event_get_deal_alloc (const TarotGameEvent *event, TarotPlayer *myself, size_t *ncards, TarotCard **cards)
Function: TarotGameEventError tarot_game_event_get_deal_all (const TarotGameEvent *event, size_t *nowners, size_t start, size_t max, TarotPlayer *owners)
Function: TarotGameEventError tarot_game_event_get_deal_all_alloc (const TarotGameEvent *event, size_t *nowners, TarotPlayer **owners)
Function: TarotGameEventError tarot_game_event_get_bid (const TarotGameEvent *event, TarotBid *bid)
Function: TarotGameEventError tarot_game_event_get_decl (const TarotGameEvent *event, int *slam)
Function: TarotGameEventError tarot_game_event_get_call (const TarotGameEvent *event, TarotCard *call)
Function: TarotGameEventError tarot_game_event_get_dog (const TarotGameEvent *event, size_t *ncards, size_t start, size_t max, TarotCard *cards)
Function: TarotGameEventError tarot_game_event_get_dog_alloc (const TarotGameEvent *event, size_t *ncards, TarotCard **cards)
Function: TarotGameEventError tarot_game_event_get_discard (const TarotGameEvent *event, size_t *ncards, size_t start, size_t max, TarotCard *cards)
Function: TarotGameEventError tarot_game_event_get_discard_alloc (const TarotGameEvent *event, size_t *ncards, TarotCard **cards)
Function: TarotGameEventError tarot_game_event_get_handful (const TarotGameEvent *event, size_t *ncards, size_t start, size_t max, TarotCard *cards)
Function: TarotGameEventError tarot_game_event_get_handful_alloc (const TarotGameEvent *event, size_t *ncards, TarotCard **cards)
Function: TarotGameEventError tarot_game_event_get_card (const TarotGameEvent *event, TarotCard *card)

Query event. If its type is correct, then return ‘TAROT_EVENT_OK’. Otherwise, return ‘TAROT_EVENT_ERRTP’. The owners and cards arguments for the ‘_alloc’-suffixed functions are allocate with standard ‘malloc’ (or rather, ‘xmalloc’ in order to protect against memory allocation failures) and thus should be discarded with ‘free’ when used. If this behavior is not wanted, you may use the same functions with no ‘_alloc’ suffix. These functions will fill owners / cards from 0 up to max (or * nowners / * ncards if max is large enough) with the owners and cards of the event, skipping the start first players / cards. Anyway, * ncards and * nowners are always set to the total number of cards / owners.

Function: TarotGameIterator * tarot_game_iterator (const TarotGame *game)

game events in game. This is not always the exact list of events that built game, since game imputation will convert simple ‘deal’ events to full ‘deal_all’ events, and the empty handful events will be removed.

Function: void tarot_game_copy (TarotGame *dest, const TarotGame *source)
Function: TarotGameError tarot_game_copy_as (TarotGame *dest, const TarotGame *source, TarotPlayer player)

Copy game source to allocated game dest, optionally restricting it to what player knows about source.

If we do not know the cards of player or we do not know some part of the discard that player would know, then the second function may fail.

It cannot fail if the following are met:

Function: TarotGame * tarot_game_dup (const TarotGame *object)
Function: TarotGameEvent * tarot_game_event_dup (const TarotGameEvent *object)
Function: TarotCounter * tarot_counter_dup (const TarotCounter *object)

Return a new allocated copy of object.

Function: void tarot_game_free (TarotGame *object)
Function: void tarot_game_iterator_free (TarotGameIterator *object)
Function: void tarot_game_event_free (TarotGameEvent *object)
Function: void tarot_counter_free (TarotCounter *counter)

Release object that has been allocated with the corresponding ‘tarot_*_alloc’ or ‘tarot_*_iterator’ function.

Function: enum TarotGameError

An enumeration of errror codes for the game API. It has:

Function: TarotGameError tarot_game_duplicate_set_deal (TarotGame *game, size_t nowners, const TarotPlayer *owners)
Function: TarotGameError tarot_game_duplicate_set_taker (TarotGame *game, TarotPlayer taker)
Function: TarotGameError tarot_game_duplicate_set_call (TarotGame *game, TarotCard call)

Set the corresponding duplicaté option in game. Return an error code. Be aware that these functions can only catch errors if they are called just before the duplicaté option takes effect. Otherwise, the error will be signaled later in the game, usually by refusing to add the last event before the duplicaté option would kick in. This may be very confusing to debug.

Let us illustrate this behavior by setting a call that would be invalid later on.

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <tarot.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#ifndef LOCALEDIR
#define LOCALEDIR "/usr/share/locale"
#endif /* NOT LOCALEDIR */

#ifndef DATADIR
#define DATADIR "/usr/share"
#endif /* NOT LOCALEDIR */

#define P1 0
#define P2 1
#define P3 2
#define P4 3
#define P5 4
#define DOG 5

static inline TarotCard
queen_of_diamonds (void)
{
  TarotCard c;
  if (tarot_of (TAROT_QUEEN, TAROT_DIAMONDS, &c) != 0)
    {
      abort ();
    }
  return c;
}

int
main ()
{
  TarotGame *game;
  TarotGameEvent *event;
  static const TarotPlayer owners[78] =
    {
     /* Hearts */
     /* A, 2,  3,   4,   5,   6,   7,   8,   9,   10,  J,   C,   Q,   K */
     P5,  P1,  P5,  P3,  DOG, P2,  P1,  P4,  P3,  P2,  P4,  P5,  P3,  P3,
     /* Clubs */
     DOG, P3,  P5,  P2,  P3,  P1,  P4,  P5,  P4,  P4,  P2,  P2,  DOG, P4,
     /* Diamonds */
     P2,  P1,  P3,  P1,  P4,  P4,  P5,  P3,  P3,  P2,  P4,  P2,  P4,  P3,
     /* Spades */
     P2,  P1,  P4,  P5,  P2,  P1,  P1,  P3,  P3,  P2,  P1,  P2,  P5,  P5,
     /* Trumps */
     P1,  P1,  P1,  P3,  P5,  P2,  P3,  P4,  P2,  P5,  P5,  P4,  P1,  P2,  P4,  P5,  P1,  P1,  P5,  P5,  P4,
     /* Excuse */
     P3
    };
  TarotGameError code;
  int i;

  tarot_set_datadir (DATADIR);  
  if (tarot_init (LOCALEDIR) != 0)
    {
      fprintf (stderr, "Error: could not initialize libtarot.\n");
      return EXIT_FAILURE;
    }
  game = tarot_game_alloc ();

  /* Set the duplicaté options early */
  code = tarot_game_duplicate_set_deal (game, 78, owners);
  assert (code == TAROT_GAME_OK);
  code = tarot_game_duplicate_set_taker (game, P2);
  assert (code == TAROT_GAME_OK);
  /* This is impossible, P2 does not have all kings...  It has none in
   * fact. */
  code = tarot_game_duplicate_set_call (game, queen_of_diamonds ());
  /* However, since game has not accepted the deal yet, it has no
   * reason to refuse. */
  assert (code == TAROT_GAME_OK); /* !!! */
  
  /* The game has 5 players, with call */
  event = tarot_game_event_alloc_setup (5, 1);
  if (tarot_game_add_event (game, event) != TAROT_GAME_OK)
    {
      abort ();
    }
  tarot_game_event_free (event);

  /* Now the game has jumped to the declaration phase */
  assert (tarot_game_step (game) == TAROT_DECLS);

  /* There are no problems for the first 4 declarations */
  for (i = 0; i < 4; i++)
    {
      event = tarot_game_event_alloc_decl (0);
      if (tarot_game_add_event (game, event) != TAROT_GAME_OK)
        {
          abort ();
        }
      tarot_game_event_free (event);
    }

  /* Let's check the fifth. */
  event = tarot_game_event_alloc_decl (0);
  /* This event is valid. */
  if (!tarot_game_check_event (game, event))
    {
      abort ();
    }
  /* But we cannot add it! */
  if (tarot_game_add_event (game, event) != TAROT_GAME_INVEV)
    {
      abort ();
    }
  tarot_game_event_free (event);
  /* How is that?  The game has advanced to the call phase though! */
  assert (tarot_game_step (game) == TAROT_CALL);

  /* But the duplicaté call is refused. */
  tarot_game_free (game);
  tarot_quit ();
  return EXIT_SUCCESS;
}
Function: int tarot_game_check_event (const TarotGame *game, const TarotGameEvent *event)
Function: TarotGameError tarot_game_add_event (TarotGame *game, const TarotGameEvent *event)
Function: int tarot_game_check_card (const TarotGame *game, TarotCard card, int *cannotfollow, TarotSuit *leadsuit, int *cannotovertrump, TarotNumber *maxtrump, int *cannotundertrump)

Check that event can be added to game. ‘tarot_game_add_event’ actually adds event to game and returns an error code, while ‘tarot_game_check_event’ simply returns a boolean.

The ‘tarot_game_check_card’ function also checks that card may be played in game, but also computes game information a posteriori: cannotfollow is set to whether card is not of the leadsuit, and so on.

Please note that if you have set duplicaté options on game, then adding a totally acceptable setup event or the last slam declaration may fail for seemingly no reason, while the check went all right. This may be due to the duplicaté settings being invalid. By adding the event, though, the game has advanced to just before the invalid duplicaté event.

Function: int tarot_game_can_autoreveal (const TarotGame *game, size_t *ndog, size_t start, size_t max, TarotCard *dog)
Function: int tarot_game_can_autoreveal_alloc (const TarotGame *game, size_t *n, TarotCard **dog)

Check if game should reveal the dog, and game knows the dog. If so, query dog with the same semantics as the ‘tarot_game_event_get_dog’ / ‘tarot_game_event_get_dog_alloc’ functions.

Function: const TarotGameEvent * tarot_game_iterator_next_value (TarotGameIterator *iterator)

Return the data associated to the current position of iterator, and advance iterator to the next position.


Next: , Previous: , Up: Programming with libtarot   [Contents][Index]