Commit 578d3b9f authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Implement the duplicaté API

I have just added three functions to prepare the deal, bids and call
for later.  It is a little unfortunate that these configurations
cannot be tested ahead of time, but everything has been documented.
parent 4ae34561
Pipeline #172436 passed with stages
in 30 minutes and 19 seconds
......@@ -38,6 +38,7 @@
/depcomp
/doc/basic-initialization
/doc/basic-initialization.c
/doc/duplicate-a-posteriori.c
/doc/events
/doc/events.c
/doc/org-stamp
......
......@@ -19,13 +19,15 @@ check_PROGRAMS += \
%reldir%/players-and-cards \
%reldir%/events \
%reldir%/xml \
%reldir%/petit-sec
%reldir%/petit-sec \
%reldir%/duplicate-a-posteriori
%canon_reldir%_basic_initialization_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
%canon_reldir%_players_and_cards_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
%canon_reldir%_events_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
%canon_reldir%_xml_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
%canon_reldir%_petit_sec_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
%canon_reldir%_duplicate_a_posteriori_LDADD = src/libtarot/libtarot.la $(AM_LDADD)
BUILT_SOURCES += \
%reldir%/players-and-cards.c \
......
......@@ -20,6 +20,7 @@ TANGLED = \
$(srcdir)/%reldir%/events.c \
$(srcdir)/%reldir%/petit-sec.c \
$(srcdir)/%reldir%/xml.c \
$(srcdir)/%reldir%/duplicate-a-posteriori.c \
$(srcdir)/%reldir%/tarot-tune \
$(srcdir)/tuneconfig.h \
$(srcdir)/%reldir%/tarot-figures/convergence-parameter.eps \
......
......@@ -620,6 +620,28 @@ the previously computed amount. Then the defendant have added
A new game can then begin, with the new first player being the
previous second.
** Duplicaté
Since the fourth variant is quite tough, it is quite common that all
players have to pass. In order to avoid these situations, the tarot
tournaments use the /duplicaté/ technique: every table plays the same
game, and all players of the same positions can compare themselves to
the other players at the same positions in the other tables. So the
tournament organizers can decide in advance the deal and the bids (the
taker pushes, the other pass) so that the game is kept interesting.
The other events, such as the slam declarations, dog and discard are
not fixed. The difference between deal and bids and the others is
that:
- a common deal lets us use duplicaté;
- a definite taker lets the competitors play in teams, with the taker
of one team playing against three other players of the other team.
In order to extend this scheme to the 5-player variant, we also define
the call a priori, so that we can still make teams. However, this is
of course not interesting in real life, because if the in-game teams
corresponded to known players, the partner would not be a surprise to
anyone. In electronic tarot, it is possible to play as teams with
duplicaté, at the condition of fixing the call.
* The tarot program
The command-line interface =tarot= has two modes: either it
processes an XML game doing one game action, or it opens a shell to
......@@ -1137,7 +1159,150 @@ An enumeration of errror codes for the game API. It has:
- =TAROT_GAME_OK= = 0, if the operation succeeded;
- =TAROT_GAME_INVXML=, when loading from a document that is not an XML document;
- =TAROT_GAME_INVEV=, when adding an invalid or unexpected event;
- =TAROT_GAME_NA=, when querying game properties that are not set.
- =TAROT_GAME_NA=, when querying game properties that are not set;
- =TAROT_GAME_TOOLATE=, when we set a duplicaté option after it would
have been used.
#+end_deftypefun
#+attr_texinfo: :options {TarotGameError} tarot_game_duplicate_set_deal (TarotGame *@var{game}, size_t @var{nowners}, const TarotPlayer *@var{owners})
#+begin_deftypefun
#+texinfo: @deftypefunx {TarotGameError} tarot_game_duplicate_set_taker (TarotGame *@var{game}, TarotPlayer @var{taker})
#+texinfo: @deftypefunx {TarotGameError} tarot_game_duplicate_set_call (TarotGame *@var{game}, TarotCard @var{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.
#+BEGIN_SRC C -i :noweb yes :tangle duplicate-a-posteriori.c
#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;
}
#+END_SRC
#+end_deftypefun
#+attr_texinfo: :options {int} tarot_game_check_event (const TarotGame *@var{game}, const TarotGameEvent *@var{event})
......@@ -1152,6 +1317,13 @@ 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.
#+end_deftypefun
#+attr_texinfo: :options {int} tarot_game_can_autoreveal (const TarotGame *@var{game}, size_t *@var{ndog}, size_t start, size_t max, TarotCard *@var{dog})
......
......@@ -282,6 +282,12 @@ extern "C"
{
TarotGameOptions opt;
TarotGameU u;
int has_duplicate_deal;
TarotPlayer duplicate_deal[78];
int has_duplicate_taker;
TarotPlayer duplicate_taker;
int has_duplicate_call;
TarotCard duplicate_call;
};
typedef union
......
......@@ -80,6 +80,25 @@ tarot_game_step (const TarotGame * game)
return game_step (game);
}
TarotGameError
tarot_game_duplicate_set_deal (TarotGame * game, size_t n_owners,
const TarotPlayer * owners)
{
return game_duplicate_set_deal (game, n_owners, owners);
}
TarotGameError
tarot_game_duplicate_set_taker (TarotGame * game, TarotPlayer taker)
{
return game_duplicate_set_taker (game, taker);
}
TarotGameError
tarot_game_duplicate_set_call (TarotGame * game, TarotCard call)
{
return game_duplicate_set_call (game, call);
}
size_t
tarot_game_iterator_construct (size_t max_mem, char *mem, size_t *alignment,
const TarotGame * game)
......
......@@ -42,7 +42,8 @@ extern "C"
TAROT_GAME_OK = 0,
TAROT_GAME_INVXML,
TAROT_GAME_INVEV,
TAROT_GAME_NA
TAROT_GAME_NA,
TAROT_GAME_TOOLATE
} TarotGameError;
/**
......@@ -64,6 +65,14 @@ extern "C"
TarotStep tarot_game_step (const TarotGame * game);
/**
* tarot_game_duplicate_set_deal:
* @owners: (array length=n_owners):
*/
TarotGameError tarot_game_duplicate_set_deal (TarotGame *game, size_t n_owners, const TarotPlayer *owners);
TarotGameError tarot_game_duplicate_set_taker (TarotGame *game, TarotPlayer taker);
TarotGameError tarot_game_duplicate_set_call (TarotGame *game, TarotCard call);
/**
* tarot_game_iterator_construct:
* @mem_out: (array length=max_mem):
......
......@@ -29,6 +29,13 @@ extern "C"
int with_call);
static void game_copy (TarotGame * dest, const TarotGame * source);
static TarotStep game_step (const TarotGame * game);
static TarotGameError game_duplicate_set_deal (TarotGame * game,
size_t n_owners,
const TarotPlayer * owners);
static TarotGameError game_duplicate_set_taker (TarotGame * game,
TarotPlayer taker);
static TarotGameError game_duplicate_set_call (TarotGame * game,
TarotCard call);
static void game_iterator_setup (TarotGameIterator * iterator,
const TarotGame * game);
static size_t game_save_to_xml (const TarotGame * game, size_t start,
......
......@@ -46,6 +46,12 @@ struct TarotGameHandle
TarotDoscardHandle doscard;
TarotHandfuls *handfuls;
TarotTricksHandle tricks;
int *has_duplicate_deal;
TarotPlayer *duplicate_deal;
int *has_duplicate_taker;
TarotPlayer *duplicate_taker;
int *has_duplicate_call;
TarotCard *duplicate_call;
};
typedef struct TarotGameHandle TarotGameHandle;
......@@ -54,6 +60,12 @@ static void
game_generalize (TarotGame * game, TarotGameHandle * handle)
{
memcpy (&(handle->opt), &(game->opt), sizeof (TarotGameOptions));
handle->has_duplicate_deal = &(game->has_duplicate_deal);
handle->duplicate_deal = game->duplicate_deal;
handle->has_duplicate_taker = &(game->has_duplicate_taker);
handle->duplicate_taker = &(game->duplicate_taker);
handle->has_duplicate_call = &(game->has_duplicate_call);
handle->duplicate_call = &(game->duplicate_call);
switch (game->opt.n_players)
{
case 3:
......@@ -123,6 +135,33 @@ game_set_step (TarotGameHandle * handle, TarotStep step)
*(handle->step) = step;
}
static inline void
game_set_duplicate_deal (TarotGameHandle * handle, size_t n,
const TarotPlayer * owner)
{
size_t i;
assert (n == 78);
*(handle->has_duplicate_deal) = 1;
for (i = 0; i < n; i++)
{
handle->duplicate_deal[i] = owner[i];
}
}
static inline void
game_set_duplicate_taker (TarotGameHandle * handle, TarotPlayer taker)
{
*(handle->has_duplicate_taker) = 1;
*(handle->duplicate_taker) = taker;
}
static inline void
game_set_duplicate_call (TarotGameHandle * handle, TarotCard call)
{
*(handle->has_duplicate_call) = 1;
*(handle->duplicate_call) = call;
}
static inline int
game_get_dealt (TarotGameHandle * handle)
{
......@@ -135,6 +174,38 @@ game_get_step (const TarotGameHandle * handle)
return *(handle->step);
}
/* Returns NULL if no duplicate deal */
static inline const TarotPlayer *
game_get_duplicate_deal (const TarotGameHandle * handle, size_t *n)
{
if (*(handle->has_duplicate_deal))
{
*n = 78;
return handle->duplicate_deal;
}
return NULL;
}
static inline const TarotPlayer *
game_get_duplicate_taker (const TarotGameHandle * handle)
{
if (*(handle->has_duplicate_taker))
{
return handle->duplicate_taker;
}
return NULL;
}
static inline const TarotCard *
game_get_duplicate_call (const TarotGameHandle * handle)
{
if (*(handle->has_duplicate_call))
{
return handle->duplicate_call;
}
return NULL;
}
static inline void
game_initialize (TarotGame * game, size_t n_players, int with_call)
{
......@@ -154,12 +225,19 @@ game_initialize (TarotGame * game, size_t n_players, int with_call)
doscard_initialize (&(handle.doscard), n_players);
handfuls_initialize (handle.handfuls, n_players);
tricks_initialize (&(handle.tricks), n_players);
*(handle.has_duplicate_deal) = 0;
*(handle.has_duplicate_taker) = 0;
*(handle.has_duplicate_call) = 0;
}
static inline void
game_copy (TarotGame * dest, const TarotGame * source)
{
TarotGameHandle hdest, hsource;
size_t n;
const TarotPlayer *owners;
const TarotPlayer *taker;
const TarotPlayer *call;
game_initialize (dest, source->opt.n_players, source->opt.with_call);
game_generalize (dest, &hdest);
game_generalize ((TarotGame *) source, &hsource);
......@@ -193,6 +271,31 @@ game_copy (TarotGame * dest, const TarotGame * source)
{
assert (0);
}
if ((owners = game_get_duplicate_deal (&hsource, &n)) != NULL)
{
assert (n == 78);
game_set_duplicate_deal (&hdest, n, owners);
}
else
{
*(hdest.has_duplicate_deal) = 0;
}
if ((taker = game_get_duplicate_taker (&hsource)) != NULL)
{
game_set_duplicate_taker (&hdest, *taker);
}
else
{
*(hdest.has_duplicate_taker) = 0;
}
if ((call = game_get_duplicate_call (&hsource)) != NULL)
{
game_set_duplicate_call (&hdest, *call);
}
else
{
*(hdest.has_duplicate_call) = 0;
}
}
static inline TarotStep
......@@ -203,6 +306,85 @@ game_step (const TarotGame * game)
return game_get_step (&handle);
}
static inline TarotGameError
game_duplicate_set_deal (TarotGame * game, size_t n_owners,
const TarotPlayer * owners)
{
TarotGameHandle handle;
TarotStep step;
game_generalize ((TarotGame *) game, &handle);
step = game_get_step (&handle);
if (step == TAROT_SETUP || step == TAROT_DEAL)
{
game_set_duplicate_deal (&handle, n_owners, owners);
if (step == TAROT_DEAL)
{
int err = game_step_advance (&handle);
if (err != 0)
{
return TAROT_GAME_INVEV;
}
}
return TAROT_GAME_OK;
}
return TAROT_GAME_TOOLATE;
}
static inline TarotGameError
game_duplicate_set_taker (TarotGame * game, TarotPlayer taker)
{
TarotGameHandle handle;
TarotStep step;
size_t n_bids;
int has_bids;
game_generalize ((TarotGame *) game, &handle);
step = game_get_step (&handle);
has_bids = (game_get_bids (game, &n_bids, 0, 0, NULL) == TAROT_GAME_OK);
if (step == TAROT_BIDS)
{
assert (has_bids);
}
if (step == TAROT_SETUP || step == TAROT_DEAL
|| (step == TAROT_BIDS && n_bids == 0))
{
game_set_duplicate_taker (&handle, taker);
if (step == TAROT_BIDS)
{
int err = game_step_advance (&handle);
if (err != 0)
{
return TAROT_GAME_INVEV;
}
}
return TAROT_GAME_OK;
}
return TAROT_GAME_TOOLATE;
}
static inline TarotGameError
game_duplicate_set_call (TarotGame * game, TarotCard call)
{
TarotGameHandle handle;
TarotStep step;
game_generalize ((TarotGame *) game, &handle);
step = game_get_step (&handle);
if (step == TAROT_SETUP || step == TAROT_DEAL || step == TAROT_BIDS
|| step == TAROT_DECLS || step == TAROT_CALL)
{
game_set_duplicate_call (&handle, call);
if (step == TAROT_CALL)
{
int err = game_step_advance (&handle);
if (err != 0)
{
return TAROT_GAME_INVEV;
}
}
return TAROT_GAME_OK;
}
return TAROT_GAME_TOOLATE;
}
static inline void
game_iterator_setup (TarotGameIterator * iterator, const TarotGame * game)
{
......@@ -1630,6 +1812,11 @@ game_create_hand (size_t n_max, size_t *n, TarotCard * buffer)
return ret;
}
static inline int game_add_deal_all (TarotGameHandle * game,
const TarotPlayer * owners);
static inline int game_add_bid (TarotGameHandle * game, TarotBid bid);
static inline int game_add_call (TarotGameHandle * game, TarotCard call);
static inline int
game_step_advance (TarotGameHandle * game)
{
......@@ -1644,6 +1831,14 @@ game_step_advance (TarotGameHandle * game)
{
TarotPlayer i;
int ok = 0;
const TarotPlayer *owners;
size_t n_owners;
owners = game_get_duplicate_deal (game, &n_owners);
if (owners != NULL)
{
assert (n_owners == 78);
return game_add_deal_all (game, owners);
}
for (i = 0; i < game->opt.n_players; ++i)
{
if (hands_known (&(game->hands), i))
......@@ -1664,7 +1859,22 @@ game_step_advance (TarotGameHandle * game)
while (0);
break;
case TAROT_BIDS:
if (!bids_has_next (game->bids, game->opt.n_players))
if (bids_has_next (game->bids, game->opt.n_players))
{
const TarotPlayer *forced_taker = game_get_duplicate_taker (game);
if (forced_taker != NULL)
{
if (*forced_taker == bids_next (game->bids))
{
return game_add_bid (game, TAROT_PUSH);
}
else
{
return game_add_bid (game, TAROT_PASS);
}
}
}
else
{
if (bids_has_taker (game->bids, game->opt.n_players))
{
......@@ -1692,6 +1902,7 @@ game_step_advance (TarotGameHandle * game)
assert (error == 0);
(void) error;
game_set_step (game, TAROT_CALL);
return game_step_advance (game);
}
else
{
......@@ -1725,7 +1936,15 @@ game_step_advance (TarotGameHandle * game)
}
break;
case TAROT_CALL:
if (!call_waiting (game->call))
if (call_waiting (game->call))
{
const TarotCard *forced_call = game_get_duplicate_call (game);
if (forced_call != NULL)
{
return game_add_call (game, *forced_call);
}
}
else
{
TarotBid contract = bids_contract (game->bids);
TarotPlayer taker = bids_taker (game->bids);
......@@ -1831,6 +2050,7 @@ game_add_deal_all (TarotGameHandle * game, const TarotPlayer * owners)
TarotCard i;
if (error == 0)
{
*(game->has_duplicate_deal) = 0;
game_set_dealt (game, game->opt.n_players);
error = error || hands_set_all (&(game->hands), owners);