Commit b7d0a457 authored by Vivien Kraus's avatar Vivien Kraus

Merge branch '34-rework-the-mcts-api' into 'develop'

Resolve "Rework the MCTS api"

Closes #34

See merge request gugurumbe/tarot!32
parents 9fcd01bc 709751f0
Pipeline #106192 failed with stages
in 4 minutes and 25 seconds
......@@ -24,6 +24,7 @@ sh autogen.sh VALAFLAGS="--target-glib=2.50" || exit 1
--prefix="$SOURCE_TOP/public" \
--enable-valgrind=yes \
--enable-renderer=yes \
--enable-tree-renderer=yes \
--enable-silent-rules=yes \
|| (cat config.log ; exit 1) \
|| exit 1
......
......@@ -57,8 +57,12 @@ AM_MISSING_PROG([G_IR_COMPILER], [g-ir-compiler])
AC_ARG_ENABLE([renderer], AS_HELP_STRING([--enable-renderer=yes],
[We are able to run the cairo renderer. You should need this option only if you modify maintainer files.]))
AM_CONDITIONAL([ENABLE_RENDERER], [test "x$enable_renderer" = "xyes"])
AC_ARG_ENABLE([tree-renderer], AS_HELP_STRING([--enable-tree-renderer=yes],
[We are able to run the tarot-cli program to render the example MCTS tree. You should need this option only if you modify maintainer files.]))
AM_CONDITIONAL([ENABLE_TREE_RENDERER], [test "x$enable_tree_renderer" = "xyes"])
AM_MISSING_PROG([JEKYLL], [jekyll])
PKG_INSTALLDIR
AM_MISSING_PROG([FDP], [fdp])
# Checks for libraries.
gl_lib_INIT
......@@ -109,6 +113,7 @@ AC_SEARCH_LIBS([cos], [m])
# Checks for header files.
AC_HEADER_STDBOOL
AC_CHECK_HEADERS_ONCE([stdint.h limits.h sysexits.h math.h tgmath.h])
AC_CHECK_HEADER([float.h],, [AC_MSG_ERROR([float.h not found.])])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
......
......@@ -18,4 +18,5 @@
AM_DISTCHECK_CONFIGURE_FLAGS += \
--enable-silent-rules=yes \
--disable-renderer
--disable-renderer \
--disable-tree-renderer
......@@ -17,7 +17,9 @@
# <http://www.gnu.org/licenses/>.
tarotfigureshtmldir = $(htmldir)/tarot.html/tarot-figures
dist_tarotfigureshtml_DATA = %reldir%/tarot-figures/example.png
dist_tarotfigureshtml_DATA = \
%reldir%/tarot-figures/example.png \
%reldir%/tarot-figures/example-tree.png
AM_MAKEINFOHTMLFLAGS += --css-include $(srcdir)/%reldir%/style.css
......
......@@ -19,7 +19,8 @@
info_TEXINFOS += %reldir%/tarot.texi
tarotfiguresdir = $(infodir)/tarot-figures
dist_tarotfigures_DATA = \
%reldir%/tarot-figures/example.png
%reldir%/tarot-figures/example.png \
%reldir%/tarot-figures/example-tree.png
%canon_reldir%_tarot_TEXINFOS = \
%reldir%/fdl.texi \
......@@ -29,6 +30,7 @@ dist_tarotfigures_DATA = \
EXTRA_DIST += \
%reldir%/tarot-figures/example.eps \
%reldir%/tarot-figures/example-tree.eps \
%reldir%/invoke-tarot-render-body.texi \
%reldir%/invoke-tarot-generate-body.texi \
%reldir%/invoke-tarot-cli-body.texi
......@@ -36,17 +38,22 @@ EXTRA_DIST += \
MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot-figures/example.png \
$(srcdir)/%reldir%/tarot-figures/example.eps \
$(srcdir)/%reldir%/tarot-figures/example-tree.png \
$(srcdir)/%reldir%/tarot-figures/example-tree.eps \
$(srcdir)/%reldir%/invoke-tarot-render-body.texi \
$(srcdir)/%reldir%/invoke-tarot-generate-body.texi \
$(srcdir)/%reldir%/invoke-tarot-cli-body.texi
$(srcdir)/%reldir%/tarot.info: %reldir%/tarot-figures/example.png
$(srcdir)/%reldir%/tarot.xml: %reldir%/tarot-figures/example.png
$(srcdir)/%reldir%/tarot.info: %reldir%/tarot-figures/example.png %reldir%/tarot-figures/example-tree.png
$(srcdir)/%reldir%/tarot.xml: %reldir%/tarot-figures/example.png %reldir%/tarot-figures/example-tree.png
$(srcdir)/%reldir%/tarot-figures/example.png: games/example.xml.png
@$(MKDIR_P) $(srcdir)/%reldir%/tarot-figures
$(AM_V_GEN) cp $(srcdir)/games/example.xml.png $(srcdir)/%reldir%/tarot-figures/example.png
$(srcdir)/%reldir%/tarot-figures/example-tree.eps: %reldir%/tarot-figures/example-tree.png
$(AM_V_GEN) $(CONVERT) $(srcdir)/%reldir%/tarot-figures/example-tree.png $(srcdir)/%reldir%/tarot-figures/example-tree.eps
$(srcdir)/%reldir%/tarot-figures/example.eps: %reldir%/tarot-figures/example.png
$(AM_V_GEN) $(CONVERT) $(srcdir)/%reldir%/tarot-figures/example.png $(srcdir)/%reldir%/tarot-figures/example.eps
......
......@@ -17,7 +17,8 @@
# <http://www.gnu.org/licenses/>.
dist_games_DATA += %reldir%/example.xml
EXTRA_DIST += %reldir%/example.xml.png
EXTRA_DIST += %reldir%/example.xml.png \
%reldir%/render_mcts_tree.xsl
MAINTAINERCLEANFILES += $(srcdir)/%reldir%/example.xml.png
......@@ -25,6 +26,10 @@ render_verbose = $(render_verbose_@AM_V@)
render_verbose_ = $(render_verbose_@AM_DEFAULT_V@)
render_verbose_0 = @echo " RENDER " $@;
tree_render_verbose = $(render_verbose_@AM_V@)
tree_render_verbose_ = $(render_verbose_@AM_DEFAULT_V@)
tree_render_verbose_0 = @echo " TREE " $@;
if ENABLE_RENDERER
if WITH_CAIRO
$(srcdir)/%reldir%/example.xml.png: %reldir%/example.xml src/cairo/tarot-render
......@@ -39,3 +44,16 @@ $(srcdir)/%reldir%/example.xml.png: %reldir%/example.xml
@echo "Error: the figure $@ should be rebuilt. Please pass --enable-renderer=yes to configure."
@false
endif # not ENABLE_RENDERER
if ENABLE_TREE_RENDERER
$(srcdir)/doc/tarot-figures/example-tree.png: %reldir%/example.xml src/tarot-cli
@$(MKDIR_P) $(srcdir)/%reldir%/tarot-figures
$(tree_render_verbose) src/tarot-cli --impute < $(srcdir)/%reldir%/example.xml \
| src/tarot-cli --mcts-tree \
| $(XSLTPROC) $(srcdir)/%reldir%/render_mcts_tree.xsl - \
| $(FDP) -Tpng -o $(srcdir)/doc/tarot-figures/example-tree.png
else # not ENABLE_TREE_RENDERER
$(srcdir)/doc/tarot-figures/example-tree.png: %reldir%/example.xml
@echo "Error: the figure $@ should be rebuilt. Please pass --enable-renderer=yes to configure."
@false
endif # not ENABLE_TREE_RENDERER
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="*" />
<xsl:template match="tree">
digraph G {
node [shape=record];
<xsl:apply-templates />
}
</xsl:template>
<xsl:template match="node/node">
<xsl:variable name="id" select="generate-id()" />
<xsl:value-of select="$id" /> [label="<xsl:apply-templates select="stats" />"];
<xsl:value-of select="generate-id(..)" /> -> <xsl:value-of select="$id" />;
<xsl:apply-templates select="node" />
</xsl:template>
<xsl:template match="tree/node">
<xsl:variable name="id" select="generate-id()" />
<xsl:value-of select="$id" /> [label="<xsl:apply-templates select="stats" />" fillcolor="yellow" style="filled"];
<xsl:apply-templates select="node" />
</xsl:template>
<xsl:template match="stats">
<xsl:text>{</xsl:text>
<xsl:apply-templates />
<xsl:text>|{</xsl:text>
<xsl:value-of select="@n-simulations" />
<xsl:text>|</xsl:text>
<xsl:value-of select="@sum-scores div @n-simulations" />
<xsl:text>}</xsl:text>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="card">
<xsl:text>play </xsl:text>
<xsl:value-of select="@card" />
</xsl:template>
</xsl:stylesheet>
......@@ -1935,7 +1935,8 @@ game_iterator_next_value (TarotGameIterator * iterator)
game_n_players (&(iterator->handle))
&& game_get_discard_according_to (&(iterator->handle),
game_get_dealt (&
(iterator->handle)),
(iterator->
handle)),
&cards) == 0)
|| game_get_discard (&(iterator->handle), &cards) == 0)
{
......
......@@ -598,6 +598,10 @@ counter_and_game_impute (TarotCounter * counter,
{
current_solution.cards[i].assigned = n_players;
}
if (current_solution.n_remaining == 0)
{
return 0;
}
/* Shuffle the deal once, otherwise the first player would have more
hearts. */
for (i = 1; i < current_solution.n_remaining; i++)
......
......@@ -19,6 +19,7 @@
*/
#include <config.h>
#include <tarot/xml.h>
#include <tarot/mcts_private.h>
TarotMcts *
......@@ -73,4 +74,43 @@ tarot_mcts_get_best (const TarotMcts * mcts, TarotGameEvent * event)
return mcts_get_best (mcts, event);
}
int
tarot_mcts_get (const TarotMcts * mcts, TarotMctsIterator node,
size_t * n_simulations, int *sum_scores,
TarotGameEvent * event)
{
return mcts_get (mcts, (TarotMctsTreeIterator) node, n_simulations,
sum_scores, event);
}
TarotMctsIterator
tarot_mcts_parent (const TarotMcts * mcts, TarotMctsIterator node)
{
return mcts_parent (mcts, (TarotMctsTreeIterator) node);
}
TarotMctsIterator
tarot_mcts_first_child (const TarotMcts * mcts, TarotMctsIterator node)
{
return mcts_first_child (mcts, (TarotMctsTreeIterator) node);
}
TarotMctsIterator
tarot_mcts_next_sibling (const TarotMcts * mcts, TarotMctsIterator node)
{
return mcts_next_sibling (mcts, (TarotMctsTreeIterator) node);
}
TarotMctsIterator
tarot_mcts_previous_sibling (const TarotMcts * mcts, TarotMctsIterator node)
{
return mcts_previous_sibling (mcts, (TarotMctsTreeIterator) node);
}
char *
tarot_mcts_save_to_xml (const TarotMcts * mcts)
{
return tarot_xml_save_mcts_tree (mcts);
}
#include <tarot/mcts_private_impl.h>
......@@ -61,6 +61,23 @@ extern "C"
const void *seed);
int tarot_mcts_get_best (const TarotMcts * mcts, TarotGameEvent * event);
typedef size_t TarotMctsIterator;
#define TAROT_MCTS_NULL ((TarotMctsIterator) (-1))
#define TAROT_MCTS_ROOT ((TarotMctsIterator) (0))
int tarot_mcts_get (const TarotMcts * mcts, TarotMctsIterator node,
size_t * n_simulations, int *sum_scores,
TarotGameEvent * event);
TarotMctsIterator tarot_mcts_parent (const TarotMcts * mcts,
TarotMctsIterator node);
TarotMctsIterator tarot_mcts_first_child (const TarotMcts * mcts,
TarotMctsIterator node);
TarotMctsIterator tarot_mcts_next_sibling (const TarotMcts * mcts,
TarotMctsIterator node);
TarotMctsIterator tarot_mcts_previous_sibling (const TarotMcts * mcts,
TarotMctsIterator node);
char *tarot_mcts_save_to_xml (const TarotMcts * mcts);
#ifdef __cplusplus
}
#endif /* __cplusplus */
......
......@@ -70,6 +70,19 @@ extern "C"
static void mcts_backpropagate (TarotMcts * mcts,
TarotMctsTreeIterator node,
size_t n_players, const int *score);
static int mcts_get (const TarotMcts * mcts, TarotMctsTreeIterator node,
size_t * n_simulations, int *sum_scores,
TarotGameEvent * event);
static TarotMctsTreeIterator mcts_parent (const TarotMcts * mcts,
TarotMctsTreeIterator node);
static TarotMctsTreeIterator mcts_first_child (const TarotMcts * mcts,
TarotMctsTreeIterator node);
static TarotMctsTreeIterator mcts_previous_sibling (const TarotMcts * mcts,
TarotMctsTreeIterator
node);
static TarotMctsTreeIterator mcts_next_sibling (const TarotMcts * mcts,
TarotMctsTreeIterator node);
#ifdef __cplusplus
}
#endif /* __cplusplus */
......
......@@ -22,6 +22,8 @@
#include <tarot/mcts_private.h>
#include <tarot/mcts_tree_private.h>
#include <tarot/simulation.h>
#include <float.h>
#include <string.h>
/* #define DEBUG_MCTS */
......@@ -31,6 +33,9 @@
#define DEBUG(...)
#endif /* not DEBUG_MCTS */
/* Add a few starting positions, or all of them if they are enumerable. */
static int mcts_setup_root (TarotMcts * mcts);
static inline TarotMcts *
mcts_alloc ()
{
......@@ -103,7 +108,7 @@ mcts_set_base (TarotMcts * mcts, const TarotGame * base)
tarot_mcts_tree_free (mcts->tree);
mcts->tree = new_tree;
tarot_mcts_tree_get (mcts->tree, TREE_ROOT)->event = NULL;
return 0;
return mcts_setup_root (mcts);
}
static int
......@@ -176,6 +181,7 @@ one_iteration (TarotMcts * mcts, size_t n_simulations,
}
}
DEBUG (stderr, "%s:%d: Expanding node %lu\n", __FILE__, __LINE__, node);
assert (node != TREE_NULL);
if (mcts_expand (mcts, &node, next, next_player) != 0)
{
/* No memory for child */
......@@ -263,9 +269,17 @@ mcts_get_best (const TarotMcts * mcts, TarotGameEvent * event)
static double
compute_score (const TarotMctsNode * node, double n_simulations)
{
double win_ratio = node->sum_scores / ((double) node->n_simulations);
double exploration_ratio = 1 - (node->n_simulations / n_simulations);
return win_ratio + MAX_NEGATIVE_SCORE_TO_EXPLORE * exploration_ratio;
if (node->n_simulations == 0)
{
/* This is an unexplored root! */
return DBL_MAX;
}
else
{
double win_ratio = node->sum_scores / ((double) node->n_simulations);
double exploration_ratio = 1 - (node->n_simulations / n_simulations);
return win_ratio + MAX_NEGATIVE_SCORE_TO_EXPLORE * exploration_ratio;
}
}
static void
......@@ -301,15 +315,13 @@ search_best_select (const TarotMcts * mcts, TarotMctsTreeIterator node,
}
}
/* May return an invalid index, meaning we have to try a new event
directly from the root */
static inline TarotMctsTreeIterator
mcts_select (const TarotMcts * mcts, size_t n_simulations)
{
if (n_simulations != 0)
{
TarotMctsTreeIterator best = TREE_NULL;
double value_best = MAX_NEGATIVE_SCORE_TO_EXPLORE * 0.75;
TarotMctsTreeIterator best = TREE_ROOT;
double value_best = -DBL_MAX;
search_best_select (mcts, TREE_ROOT, n_simulations, &best, &value_best);
return best;
}
......@@ -821,4 +833,290 @@ mcts_backpropagate (TarotMcts * mcts, TarotMctsTreeIterator node,
}
}
static inline int
mcts_get (const TarotMcts * mcts, TarotMctsTreeIterator node,
size_t * n_simulations, int *sum_scores, TarotGameEvent * event)
{
TarotMctsNode *data = tarot_mcts_tree_get (mcts->tree, node);
*n_simulations = 0;
*sum_scores = 0;
if (tarot_game_event_type (data->event) != TAROT_DOG_EVENT)
{
*n_simulations = data->n_simulations;
*sum_scores = data->sum_scores;
}
return tarot_game_event_copy (event, data->event);
}
static inline TarotMctsTreeIterator
mcts_parent (const TarotMcts * mcts, TarotMctsTreeIterator node)
{
return tarot_mcts_tree_parent (mcts->tree, node);
}
static inline TarotMctsTreeIterator
mcts_first_child (const TarotMcts * mcts, TarotMctsTreeIterator node)
{
return tarot_mcts_tree_first_child (mcts->tree, node);
}
static inline TarotMctsTreeIterator
mcts_previous_sibling (const TarotMcts * mcts, TarotMctsTreeIterator node)
{
return tarot_mcts_tree_previous_sibling (mcts->tree, node);
}
static inline TarotMctsTreeIterator
mcts_next_sibling (const TarotMcts * mcts, TarotMctsTreeIterator node)
{
return tarot_mcts_tree_next_sibling (mcts->tree, node);
}
static inline int
mcts_setup_bids (TarotMcts * mcts)
{
TarotPlayer who;
TarotBid mini;
TarotMctsTreeIterator root = TREE_NULL;
TarotGameEvent *event = tarot_game_event_alloc ();
TarotBid bid;
if (event == NULL)
{
return 1;
}
if (tarot_game_hint_bid (mcts->base, &who, &mini) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (tarot_game_event_set_bid (event, TAROT_PASS) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_game_event_free (event);
return 1;
}
for (bid = mini; bid < TAROT_DOUBLE_KEEP; bid++)
{
root = TREE_NULL;
if (tarot_game_event_set_bid (event, bid) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_game_event_free (event);
return 1;
}
}
tarot_game_event_free (event);
return 0;
}
static int
mcts_setup_decls (TarotMcts * mcts)
{
TarotPlayer who;
int allowed;
TarotMctsTreeIterator root = TREE_NULL;
TarotGameEvent *event = tarot_game_event_alloc ();
if (event == NULL)
{
return 1;
}
if (tarot_game_hint_decl (mcts->base, &who, &allowed) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (tarot_game_event_set_decl (event, 0) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (allowed)
{
root = TREE_NULL;
if (tarot_game_event_set_decl (event, 1) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_game_event_free (event);
return 1;
}
}
tarot_game_event_free (event);
return 0;
}
static int
mcts_setup_call (TarotMcts * mcts)
{
TarotPlayer who;
TarotNumber mini;
TarotMctsTreeIterator root = TREE_NULL;
TarotGameEvent *event = tarot_game_event_alloc ();
TarotNumber face;
if (event == NULL)
{
return 1;
}
if (tarot_game_hint_call (mcts->base, &who, &mini) != 0)
{
tarot_game_event_free (event);
return 1;
}
for (face = mini; face <= TAROT_KING; face++)
{
static TarotSuit suits[4] =
{ TAROT_HEARTS, TAROT_CLUBS, TAROT_DIAMONDS, TAROT_SPADES };
size_t j;
for (j = 0; j < 4; j++)
{
TarotCard c;
if (tarot_of (face, suits[j], &c) != 0)
{
assert (0);
}
root = TREE_NULL;
if (tarot_game_event_set_call (event, c) != 0)
{
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_game_event_free (event);
return 1;
}
}
}
tarot_game_event_free (event);
return 0;
}
static int
mcts_setup_discard (TarotMcts * mcts)
{
TarotPlayer who;
TarotMctsTreeIterator root = TREE_NULL;
TarotCardArray *prio, *additional;
size_t n;
TarotGameEvent *event = tarot_game_event_alloc ();
struct yarrow256_ctx rng;
static const char *seed = "default";
size_t i;
yarrow256_init (&rng, 0, NULL);
yarrow256_seed (&rng, strlen (seed), (const uint8_t *) seed);
if (event == NULL)
{
return 1;
}
prio = tarot_card_array_alloc ();
additional = tarot_card_array_alloc ();
if (tarot_game_hint_discard (mcts->base, &who, &n, prio, additional) != 0)
{
tarot_game_event_free (event);
tarot_card_array_free (prio);
tarot_card_array_free (additional);
return 1;
}
for (i = 0; i < 16; i++)
{
select_next_move_discard (event, n, prio, additional, &rng);
root = TREE_NULL;
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_card_array_free (prio);
tarot_card_array_free (additional);
tarot_game_event_free (event);
return 1;
}
}
tarot_card_array_free (prio);
tarot_card_array_free (additional);
tarot_game_event_free (event);
return 0;
}
static int
mcts_setup_card (TarotMcts * mcts)
{
TarotPlayer who;
TarotCardArray *playable = tarot_card_array_alloc ();
TarotMctsTreeIterator root = TREE_NULL;
TarotGameEvent *event = tarot_game_event_alloc ();
size_t i, n;
if (event == NULL)
{
return 1;
}
if (tarot_game_hint_card (mcts->base, &who, playable) != 0)
{
tarot_card_array_free (playable);
tarot_game_event_free (event);
return 1;
}
n = tarot_card_array_size (playable);
if (n == 0)
{
tarot_card_array_free (playable);
tarot_game_event_free (event);
return 1;
}
for (i = 0; i < n; i++)
{
root = TREE_NULL;
if (tarot_game_event_set_card
(event, tarot_card_array_get (playable, i)) != 0)
{
tarot_card_array_free (playable);
tarot_game_event_free (event);
return 1;
}
if (mcts_expand (mcts, &root, event, who) != 0)
{
tarot_card_array_free (playable);
tarot_game_event_free (event);
return 1;
}
}
tarot_card_array_free (playable);
tarot_game_event_free (event);
return 0;
}
static inline int
mcts_setup_root (TarotMcts * mcts)
{
switch (tarot_game_step (mcts->base))
{
case TAROT_BIDS:
return mcts_setup_bids (mcts);
case TAROT_DECLS:
return mcts_setup_decls (mcts);
case TAROT_CALL:
return mcts_setup_call (mcts);
case TAROT_DISCARD:
return mcts_setup_discard (mcts);
case TAROT_TRICKS:
return mcts_setup_card (mcts);
default:
break;
}
return 1;
}
#include <tarot/mcts_tree_private_impl.h>
......@@ -15,6 +15,7 @@ include = "#include \"assert.h\"";
include = "int tarot_run_status (char *input, char **output);";
include = "int tarot_run_impute (char *input, const char *seed, int retry, char **output);";
include = "int tarot_run_mcts (char *input, const char *seed, int n_iterations, char **output);";
include = "int tarot_run_mcts_tree (char *input, const char *seed, int n_iterations, char **output);";
include = "int tarot_run_new_game (size_t n_players, int with_call, char **output);";
include = "int tarot_run_deal_to (char *input, TarotPlayer whom, TarotCardArray *cards, char **output);";
include = "int tarot_run_deal_all (char *input, TarotPlayerArray *owners, char **output);";
......@@ -238,6 +239,10 @@ main = {
input = read_input ();
res = tarot_run_mcts (input, OPT_ARG (SEED), OPT_VALUE_N_ITERATIONS, &output);
break;
case INDEX_OPT_MCTS_TREE:
input = read_input ();
res = tarot_run_mcts_tree (input, OPT_ARG (SEED), OPT_VALUE_N_ITERATIONS, &output);
break;
case INDEX_OPT_NEW_GAME:
res = tarot_run_new_game (OPT_VALUE_N_PLAYERS, ENABLED_OPT (CALL_ALLOWED) && HAVE_OPT (CALL_ALLOWED), &output);
break;
......@@ -404,6 +409,16 @@ flag = {
doc = 'The Monte-Carlo Tree Search is a powerful method to play games. In this mode, the next move is chosen with this method. This mode requires that the next move has to be done by the player that has received cards, or the whole deal is known. Otherwise, the resulting move will be possible, but pointless: it will be the card that is the most advantageous to the player that has been dealt the cards, so highly unrealistic. The @command{--seed} can be used to influence the choice of the next move, but the @command{--n-iterations} is really important too.';
};
flag = {
name = mcts-tree;
no-preset;
equivalence = new-game;
descrip = "Debug the next move with MCTS";
doc = 'This commands works just like @command{--mcts}, but instead of playing the next move, dump the MCTS tree state for inspection. For instance, the example game gives the following tree:
@image{tarot-figures/example-tree,10cm}
';
};
flag = {
name = bid;
no-preset;
......
......@@ -76,6 +76,29 @@ namespace Tarot {
output = game.save_to_xml ();
return 0;
}
public static int run_mcts_tree (string input, string seed, int n_iterations, out string? output) {
var game = new Tarot.Game.alloc ();
var mcts = new Tarot.Mcts.alloc ();
output = null;
if (game.load_from_xml (input) != 0) {
stderr.printf ("ERROR: this game is not valid.\n");
return 1;
}
if (mcts.set_base (game) != 0) {
stderr.printf ("Could not set up the MCTS.\n");
return 1;
}
char[] seed_bytes = new char[seed.length];
for (long i = 0; i < seed.length; i++) {
seed_bytes[i] = seed[i];
}
if (mcts.run (n_iterations, seed_bytes) != 0) {
stderr.printf ("Could not explore this game.\n");
return 1;
}
output = mcts.save_to_xml ();
return 0;
}
public static int run_new_game (size_t n_players, bool with_call, out string? output) {
var game = new Tarot.Game.alloc ();
var e = new Tarot.GameEvent.alloc ();
......
......@@ -22,6 +22,7 @@
#define H_TAROT_XML_INCLUDED
#include <tarot/game_event.h>
#include <tarot/mcts.h>
#ifdef __cplusplus
extern "C"