Commit 2ba6fa62 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Implement the solo game

parent 4b9e7b53
......@@ -1570,7 +1570,6 @@ also set.
#+end_deftypefun
** TODO The card counter
** TODO Artificial intelligence through the Monte-Carlo Tree Search
#+attr_texinfo: :options {struct} TarotMcts
#+begin_deftp
......@@ -1685,7 +1684,6 @@ card points and {{{per}}} opponents will play low-valued cards.
Return the least number of iterations to achieve state-of-the-art card
playing with MCTS.
#+end_deftypefun
** The libtarot test suite
In this section we present the test suite to show different aspects of
libtarot.
......@@ -2630,6 +2628,66 @@ player while setting /confidence/ to a low value.
This function is especially useful for determining the duplicaté
options.
#+end_deftypefun
** Playing a game against the AI
libtarot has an API endpoint to run a game against the AI.
#+attr_texinfo: :options {struct} TarotSolo
#+begin_deftp
A game, where all players but the specific dealt player play using the
AI.
#+end_deftp
#+attr_texinfo: :options {size_t} tarot_solo_construct (size_t @var{maxmem}, char *@var{memout}, size_t *@var{alignment}
#+begin_deftypefun
Construct a new solo game in /memout/, writing at most /maxmem/ bytes.
Output the alignment requirement for /memout/ in /alignment/, and
return the required size.
#+end_deftypefun
#+attr_texinfo: :options {TarotSolo *} tarot_solo_alloc (void)
#+begin_deftypefun
#+texinfo: @deftypefunx {void} tarot_solo_free (TarotSolo *@var{solo})
Allocate and free a new game against the AI.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_solo_copy (TarotSolo *@var{dest}, const TarotSolo *@var{source})
#+begin_deftypefun
#+texinfo: @deftypefunx {void} tarot_solo_copy_as (TarotSolo *@var{dest}, const TarotSolo *@var{source}, TarotPlayer @var{who})
Copy /source/ to /dest/ (optionally changing the human player to
/who/).
#+end_deftypefun
#+attr_texinfo: :options {TarotSolo *} tarot_solo_dup (const TarotSolo *@var{source})
#+begin_deftypefun
Allocate and return a copy of /source/.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_solo_get (const TarotSolo *@var{solo}, TarotGame *@var{game})
#+begin_deftypefun
#+texinfo: @deftypefunx {TarotGame *} tarot_solo_get_alloc (const TarotSolo *@var{solo})
Get the /game/ as known by the human player in /solo/. The second
function allocates the results.
#+end_deftypefun
#+attr_texinfo: :options {TarotGameError} tarot_solo_setup (TarotSolo *@var{solo}, size_t @var{nplayers}, int @var{withcall}, TarotPlayer @var{player}, size_t @var{nowners}, const TarotPlayer *@var{owners}, TarotPlayer @var{taker}, TarotCard @var{call})
#+begin_deftypefun
#+texinfo: @deftypefunx {TarotGameError} tarot_solo_setup_random (TarotSolo *@var{solo}, size_t @var{nplayers}, int @var{withcall}, size_t @var{seedsize}, const char *@var{seed})
Set up the /solo/ game for /nplayers/ players (and /withcall/ if there
is a call). The /random/-suffixed function will select an interesting
game at random. Otherwise, you can specify which /player/ to play,
the individual /owners/ for each card (an array of size /nowners/,
which should be 78), the expected /taker/ and /call/ if relevant.
This function returns an error code. The only error that could happen
is a /petit sec/. There cannot be any /duplicaté/-related ticking
bomb as seen in the /duplicaté/ section, so nothing bad to fear here.
#+end_deftypefun
#+attr_texinfo: :options {TarotGameError} tarot_solo_add (TarotSolo *@var{game}, const TarotGameEvent *@var{event})
#+begin_deftypefun
Try and add /event/ to /game/. This function may only fail if /event/
is not correct for the human player.
#+end_deftypefun
* GNU Free Documentation License
:PROPERTIES:
:APPENDIX: t
......
......@@ -46,3 +46,4 @@ include %reldir%/simulation/Makefile.am
include %reldir%/xml/Makefile.am
include %reldir%/features/Makefile.am
include %reldir%/cnn/Makefile.am
include %reldir%/solo/Makefile.am
......@@ -31,6 +31,7 @@
#include <tarot/mcts.h>
#include <tarot/features.h>
#include <tarot/cnn.h>
#include <tarot/solo.h>
#ifdef __cplusplus
extern "C"
......
......@@ -388,6 +388,12 @@ extern "C"
int cnn;
};
struct TarotSolo
{
TarotGame game;
TarotPlayer myself;
};
#ifdef __cplusplus
}
#endif /* __cplusplus */
......
......@@ -85,6 +85,7 @@ TAROT_BOXED_TYPE (GameEvent, game_event)
TAROT_BOXED_TYPE (GameIterator, game_iterator)
TAROT_BOXED_TYPE (Mcts, mcts)
TAROT_BOXED_TYPE (Predictor, predictor)
TAROT_BOXED_TYPE (Solo, solo)
TAROT_BOXED_TYPE (XmlParser, xml_parser)
TAROT_BOXED_TYPE (XmlParserIterator, xml_parser_iterator)
/*** END file-header ***/
......
......@@ -50,6 +50,7 @@ extern "C"
GType tarot_game_iterator_get_type (void);
GType tarot_mcts_get_type (void);
GType tarot_predictor_get_type (void);
GType tarot_solo_get_type (void);
GType tarot_xml_parser_get_type (void);
GType tarot_xml_parser_iterator_get_type (void);
......
# tarot implements the rules of the tarot game
# Copyright (C) 2019 Vivien Kraus
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AM_CPPFLAGS += -I $(srcdir)/%reldir% -I %reldir%
tarotinclude_HEADERS += %reldir%/tarot/solo.h
src_libtarot_libtarot_la_SOURCES += \
%reldir%/solo.c \
%reldir%/tarot/solo_private.h \
%reldir%/tarot/solo_private_impl.h
ENUM_HEADER_FILES += %reldir%/tarot/solo.h
INTROSPECTED_SOURCES += %reldir%/solo.c
/* tarot implements the rules of the tarot game
* Copyright (C) 2019 Vivien Kraus
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <tarot/solo_private.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdalign.h>
#include "xalloc.h"
#ifdef __FRAMAC__
#undef alignof
#define alignof(x) (16)
#endif /* __FRAMAC__ */
TarotSolo *
tarot_solo_alloc (void)
{
size_t alignment;
size_t required = solo_construct (0, NULL, &alignment);
size_t unused;
void *mem = xmalloc (required);
assert (alignment >= 1 && alignment <= alignof (max_align_t));
unused = solo_construct (required, mem, &alignment);
assert (unused == required);
(void) unused;
return mem;
}
TarotSolo *
tarot_solo_dup (const TarotSolo * source)
{
TarotSolo *ret = tarot_solo_alloc ();
tarot_solo_copy (ret, source);
return ret;
}
void
tarot_solo_free (TarotSolo * solo)
{
free (solo);
}
size_t
tarot_solo_construct (size_t max_mem, char *mem, size_t *alignment)
{
return solo_construct (max_mem, mem, alignment);
}
void
tarot_solo_copy (TarotSolo * dest, const TarotSolo * source)
{
solo_copy (dest, source);
}
void
tarot_solo_copy_as (TarotSolo * dest, const TarotSolo * source,
TarotPlayer who)
{
solo_copy_as (dest, source, who);
}
void
tarot_solo_get (const TarotSolo * solo, TarotGame * game)
{
solo_get (solo, game);
}
TarotGame *
tarot_solo_get_alloc (const TarotSolo * solo)
{
TarotGame *ret = tarot_game_alloc ();
solo_get (solo, ret);
return ret;
}
TarotGameError
tarot_solo_setup (TarotSolo * solo, size_t n_players, int with_call,
TarotPlayer myself, size_t n_owners,
const TarotPlayer * owners, TarotPlayer taker,
TarotCard call)
{
return solo_setup (solo, n_players, with_call, myself, n_owners, owners,
taker, call);
}
TarotGameError
tarot_solo_setup_random (TarotSolo * solo, size_t n_players, int with_call,
size_t seed_size, const void *seed)
{
return solo_setup_random (solo, n_players, with_call, seed_size, seed);
}
TarotGameError
tarot_solo_add (TarotSolo * solo, const TarotGameEvent * event)
{
return solo_add (solo, event);
}
#include <tarot/solo_private_impl.h>
/* tarot implements the rules of the tarot game
* Copyright (C) 2019 Vivien Kraus
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_TAROT_SOLO_INCLUDED
#define H_TAROT_SOLO_INCLUDED
#include <stddef.h>
#include <tarot/game.h>
#include <tarot/player.h>
#include <tarot/cards.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
struct TarotSolo;
typedef struct TarotSolo TarotSolo;
/**
* tarot_solo_alloc: (constructor)
*/
TarotSolo *tarot_solo_alloc (void);
TarotSolo *tarot_solo_dup (const TarotSolo *source);
void tarot_solo_free (TarotSolo *solo);
/**
* tarot_solo_construct:
* @max_mem: available number of bytes in @mem
* @mem_out: (array length=max_mem): available aligned memory
* @alignment: (out): the game alignment
*/
size_t tarot_solo_con(size_t max_mem, char *mem_out, size_t *alignment);
void tarot_solo_copy (TarotSolo * dest, const TarotSolo * source);
void tarot_solo_copy_as (TarotSolo *dest, const TarotSolo *source, TarotPlayer who);
/**
* tarot_solo_get:
* @game: (out):
*/
void tarot_solo_get (const TarotSolo *solo, TarotGame *game);
TarotGame *tarot_solo_get_alloc (const TarotSolo *solo);
/**
* tarot_solo_setup:
* @with_call: (type boolean):
*/
TarotGameError tarot_solo_setup (TarotSolo *solo, size_t n_players, int with_call, TarotPlayer myself, size_t n_owners, const TarotPlayer *owners, TarotPlayer taker, TarotCard call);
/**
* tarot_solo_setup_random:
* @seed: (array length=seed_size) (type char):
* @with_call: (type boolean):
*/
TarotGameError tarot_solo_setup_random (TarotSolo *solo, size_t n_players, int with_call, size_t seed_size, const void *seed);
TarotGameError tarot_solo_add (TarotSolo *solo, const TarotGameEvent *event);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* not H_TAROT_SOLO_INCLUDED */
/* tarot implements the rules of the tarot game
* Copyright (C) 2019 Vivien Kraus
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_TAROT_SOLO_PRIVATE_INCLUDED
#define H_TAROT_SOLO_PRIVATE_INCLUDED
#include <stddef.h>
#include <tarot_private.h>
#include <tarot/solo.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
static size_t solo_construct (size_t max_mem, char *mem_out,
size_t *alignment);
static void solo_copy (TarotSolo * dest, const TarotSolo * source);
static void solo_copy_as (TarotSolo * dest, const TarotSolo * source,
TarotPlayer who);
static void solo_get (const TarotSolo * solo, TarotGame * game);
static TarotGameError solo_setup (TarotSolo * solo, size_t n_players,
int with_call, TarotPlayer myself,
size_t n_owners,
const TarotPlayer * owners,
TarotPlayer taker, TarotCard call);
static TarotGameError solo_setup_random (TarotSolo * solo, size_t n_players,
int with_call, size_t seed_size,
const unsigned char *seed);
static TarotGameError solo_add (TarotSolo * solo,
const TarotGameEvent * event);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* not H_TAROT_SOLO_PRIVATE_INCLUDED */
/* tarot implements the rules of the tarot game
* Copyright (C) 2018, 2019 Vivien Kraus
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_TAROT_SOLO_PRIVATE_IMPL_INCLUDED
#define H_TAROT_SOLO_PRIVATE_IMPL_INCLUDED
#include <tarot/solo_private.h>
#include <tarot/game_private.h>
#include <tarot/game_event_private.h>
#include <tarot/features_private.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <nettle/yarrow.h>
#include <float.h>
static inline void solo_advance (TarotSolo * dest);
static inline size_t
solo_construct (size_t max_mem, char *mem, size_t *alignment)
{
size_t required = sizeof (TarotSolo);
*alignment = alignof (TarotSolo);
if (max_mem >= required)
{
TarotSolo *solo = (TarotSolo *) mem;
game_initialize (&(solo->game), 4, 0);
solo->myself = 0;
return max_mem;
}
return required;
}
static inline void
solo_copy (TarotSolo * dest, const TarotSolo * source)
{
game_copy (&(dest->game), &(source->game));
dest->myself = source->myself;
}
static inline void
solo_copy_as (TarotSolo * dest, const TarotSolo * source, TarotPlayer who)
{
TarotGameError err = game_copy_as (&(dest->game), &(source->game), who);
if (err != TAROT_GAME_OK)
{
assert (0);
}
dest->myself = who;
solo_advance (dest);
}
static inline void
solo_get (const TarotSolo * solo, TarotGame * game)
{
TarotGameError err = game_copy_as (game, &(solo->game), solo->myself);
if (err != TAROT_GAME_OK)
{
assert (0);
}
}
static inline TarotGameError
solo_setup (TarotSolo * solo, size_t n_players, int with_call,
TarotPlayer myself, size_t n_owners, const TarotPlayer * owners,
TarotPlayer taker, TarotCard call)
{
/* To check whether these options are valid, we first run a game
* with noone doing a slam. If we can accept the call, then these
* options are valid. */
TarotGame check;
TarotGameEvent setup_event;
TarotGameEvent decl_event;
size_t i;
game_initialize (&check, 4, 0);
if (game_duplicate_set_deal (&check, n_owners, owners) != TAROT_GAME_OK)
{
return TAROT_GAME_INVEV;
}
if (game_duplicate_set_taker (&check, taker) != TAROT_GAME_OK)
{
return TAROT_GAME_INVEV;
}
if (game_duplicate_set_call (&check, call) != TAROT_GAME_OK)
{
return TAROT_GAME_INVEV;
}
setup_event.t = TAROT_SETUP_EVENT;
setup_event.u.setup.n_players = n_players;
setup_event.u.setup.with_call = with_call;
decl_event.t = TAROT_DECL_EVENT;
decl_event.u.decl = 0;
if (game_add_event (&check, &setup_event) != TAROT_GAME_OK)
{
return TAROT_GAME_INVEV;
}
for (i = 0; i < n_players; i++)
{
if (game_add_event (&check, &decl_event) != TAROT_GAME_OK)
{
return TAROT_GAME_INVEV;
}
}
assert (game_step (&check) == TAROT_DOG);
/* These options are correct */
if (game_add_event (&(solo->game), &setup_event) != TAROT_GAME_OK)
{
return TAROT_GAME_TOOLATE;
}
if (game_duplicate_set_deal (&(solo->game), n_owners, owners) !=
TAROT_GAME_OK)
{
assert (0);
}
if (game_duplicate_set_taker (&(solo->game), taker) != TAROT_GAME_OK)
{
assert (0);
}
if (game_duplicate_set_call (&(solo->game), call) != TAROT_GAME_OK)
{
assert (0);
}
assert (myself < n_players);
solo->myself = myself;
solo_advance (solo);
return TAROT_GAME_OK;
}
static inline TarotGameError
solo_setup_random (TarotSolo * solo, size_t n_players, int with_call,
size_t seed_size, const unsigned char *seed)
{
struct yarrow256_ctx generator;
unsigned char next_seed[256];
TarotPlayer best_owners[78];
TarotPlayer best_taker;
TarotCard best_call;
double best_prediction = -DBL_MAX;
size_t i;
unsigned int is_taker;
unsigned int other_player;
TarotPlayer myself;
unsigned char random_buffer[sizeof (unsigned int)];
yarrow256_init (&generator, 0, NULL);
yarrow256_seed (&generator, seed_size, seed);
for (i = 0; i < 10; i++)
{
TarotGameEvent ev;
unsigned int ev_data[78];
size_t j;
TarotPlayer candidate_owners[78];
TarotPlayer candidate_taker;
TarotCard candidate_call = (TarotCard) (-1);
double candidate_prediction;
ev.n = 78;
ev.data = ev_data;
yarrow256_random (&generator, sizeof (next_seed), next_seed);
event_set_deal_all_random (&ev, n_players, sizeof (next_seed),
next_seed);
assert (ev.n == 78);
for (j = 0; j < ev.n; j++)
{
candidate_owners[j] = ev.data[j];
}
candidate_taker =
features_strongest_player (n_players, with_call, 78, candidate_owners,
&candidate_prediction, &candidate_call);
if (candidate_prediction >= best_prediction)
{
for (j = 0; j < 78; j++)
{
best_owners[j] = candidate_owners[j];
}
best_taker = candidate_taker;
best_call = candidate_call;
best_prediction = candidate_prediction;
if (candidate_prediction > 0)
{
i = 10;
}
}
}
yarrow256_random (&generator, sizeof (random_buffer), random_buffer);
memcpy (&is_taker, random_buffer, sizeof (is_taker));
other_player = is_taker / 2;
is_taker = is_taker % 2;
if (is_taker)
{
myself = best_taker;
}
else
{
myself = (other_player % n_players);
}
return solo_setup (solo, n_players, with_call, myself, 78, best_owners,
best_taker, best_call);
}
static inline TarotGameError
solo_add (TarotSolo * solo, const TarotGameEvent * event)
{
TarotGameError err = game_add_event (&(solo->game), event);
solo_advance (solo);
return err;