Commit c509fa11 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Remove the predictor API, the useless features functions, and the CNN

parent ba269c1d
......@@ -50,14 +50,11 @@
/doc/petit-sec.c
/doc/players-and-cards
/doc/players-and-cards.c
/doc/tarot-cnn-features.man
/doc/tarot-cnn.man
/doc/tarot-deal.man
/doc/tarot-features.man
/doc/tarot-figures
/doc/tarot-mcts.man
/doc/tarot-perceptron-bootstrap.man
/doc/tarot-stacking.man
/doc/tarot-status.man
/doc/tarot-tune
/doc/tarot.info
......@@ -84,9 +81,6 @@
/ltmain.sh
/m4
/missing
/model/linear-*.h
/model/stacking.h
/model/tree-*.h
/nsi/setup.nsi
/nsi/tarot.ico
/po/*.gmo
......@@ -112,8 +106,6 @@
/snippet
/src/gettext.h
/src/libtarot/cards/check-cards
/src/libtarot/features/features-header-encoded.txt
/src/libtarot/features/features-header.txt
/src/libtarot/gobject/tarot-enums-stamp
/src/libtarot/gobject/tarot-gobject.c
/src/libtarot/gobject/tarot-gobject.h
......@@ -150,14 +142,11 @@
/src/tarot-app/resources.c
/src/tarot-app/server.c
/src/tarot-app/tarot
/src/tarot-app/tarot-cnn
/src/tarot-app/tarot-cnn-features
/src/tarot-app/tarot-deal
/src/tarot-app/tarot-en
/src/tarot-app/tarot-features
/src/tarot-app/tarot-mcts
/src/tarot-app/tarot-parser-stamp
/src/tarot-app/tarot-stacking
/src/tarot-app/tarot-status
/src/tarot-app/tarot-vala-stamp
/src/tarot-app/tarot_app.h
......
......@@ -43,7 +43,6 @@ include screenshots/Makefile.am
include nsi/Makefile.am
include debian/Makefile.am
include flatpak/Makefile.am
include model/Makefile.am
include git-check.am
include copyright/Makefile.am
include guix/Makefile.am
......
# tarot implements the rules of the tarot game
# Copyright (C) 2019 Vivien Kraus
# Copyright (C) 2019, 2020 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
......@@ -18,20 +18,12 @@ dist_man6_MANS += \
%reldir%/tarot-status.man \
%reldir%/tarot-deal.man \
%reldir%/tarot-mcts.man \
%reldir%/tarot-stacking.man \
%reldir%/tarot-features.man \
%reldir%/tarot-cnn-features.man \
%reldir%/tarot-cnn.man \
%reldir%/tarot-perceptron-bootstrap.man
MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot.man \
$(srcdir)/%reldir%/tarot-status.man \
$(srcdir)/%reldir%/tarot-deal.man \
$(srcdir)/%reldir%/tarot-mcts.man \
$(srcdir)/%reldir%/tarot-stacking.man \
$(srcdir)/%reldir%/tarot-features.man \
$(srcdir)/%reldir%/tarot-cnn-features.man \
$(srcdir)/%reldir%/tarot-cnn.man \
$(srcdir)/%reldir%/tarot-perceptron-bootstrap.man
help2man_verbose = $(help2man_verbose_@AM_V@)
......@@ -54,22 +46,6 @@ $(srcdir)/%reldir%/tarot-mcts.man: src/tarot-app/tarot_mcts.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-mcts
$(help2man_verbose) ./pre-inst-env help2man tarot-mcts > $@
$(srcdir)/%reldir%/tarot-stacking.man: src/tarot-app/tarot_stacking.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-stacking
$(help2man_verbose) ./pre-inst-env help2man tarot-stacking > $@
$(srcdir)/%reldir%/tarot-features.man: src/tarot-app/tarot_features.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-features
$(help2man_verbose) ./pre-inst-env help2man tarot-features > $@
$(srcdir)/%reldir%/tarot-cnn-features.man: src/tarot-app/tarot_cnn_features.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-cnn-features
$(help2man_verbose) ./pre-inst-env help2man tarot-cnn-features > $@
$(srcdir)/%reldir%/tarot-cnn.man: src/tarot-app/tarot_cnn.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-cnn
$(help2man_verbose) ./pre-inst-env help2man tarot-cnn > $@
$(srcdir)/%reldir%/tarot-perceptron-bootstrap.man: src/tarot-app/tarot_perceptron_bootstrap.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-perceptron-bootstrap
$(help2man_verbose) ./pre-inst-env help2man tarot-perceptron-bootstrap > $@
# tarot implements the rules of the tarot game
# Copyright (C) 2017, 2018, 2019 Vivien Kraus
# Copyright (C) 2017, 2018, 2019, 2020 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
......@@ -715,16 +715,7 @@ The tarot distribution provides a few other command-line programs:
- =--seed=STRING=: set the random seed for the pseudo-random number
generator;
- =tarot-mcts=: reads a game from the standard input, play the next
move as the MCTS, and print the modified game;
- =tarot-stacking=: reads a game from the standard input, play the
next move as the stacking algorithm, and print the modified game;
- =tarot-features=: reads a game from the standard input, and compute
high-level features that can be used for machine learning as an ARFF
file, with a header and a data section. It has an =--encoded=
option to output the features as numerical data (so that it works
better with a regression). The header is distributed with this
package, but the programs will refuse to run if the header is not
compatible with the tarot version.
move as the MCTS, and print the modified game.
* Programming with libtarot
Libtarot is the library that you can use in your own program. In
order to use it, you may use the =pkg-config= configuration file that
......@@ -2025,54 +2016,8 @@ data.
** The features
All features are actually redundant with a clever use of the
/TarotGame/ and /TarotCounter/. However, this interface will
highlight useful high-level features.
The easiest way to use the features is encode them as /ARFF/
(Attribute-Relation File Format, a tool from Weka) and then use them
with any machine learning toolkit. The Attribute-Relation File Format
is an extension of the CSV format, with the notable addition of a
header giving more information on the columns. In this format, all
features are represented as columns. An event within the context of a
game is represented as a row.
#+attr_texinfo: :options {const char *} tarot_features_arff_header (int encoded):
#+begin_deftypefun
Return the textual representation of the ARFF header, including the
=@data= line. There are two possible representations of the features:
/decoded/ (/encoded/ == 0), which means that the types of the features
may vary (for instance, the features whose types are a bid are simply
enumerations), and may not be suitable for numerical computations.
For instance, the number of players will be represented as a numeric
feature, but the bounds and its integer nature will be left implicit.
It may be difficult to encode it /a posteriori/ for certain
applications. By contrast, when features are encoded, then those that
are not boolean or numeric will be encoded such that you will be able
to use more linear algebra operations. However, decoding them
afterwards will be difficult.
For instance, the /stacking/ predictor works with encoded data for the
Lasso regression, but it works with decoded data for the regression
tree.
#+end_deftypefun
#+attr_texinfo: :options {size_t} tarot_features_arff_data (const TarotGame *@var{base}, const TarotGameEvent *added, int @var{score}, int @var{encoded}, size_t @var{max}, char *@var{dest})
#+begin_deftypefun
Represent the event that would be /added/ to /base/ (and its
associated /score) as a line in ARFF. It can optionally work with
/encoded/ features. Fill /dest/, up to /max/ bytes, and return the
number of bytes (excluding the final NUL). /dest/ will always be
NUL-terminated after this function call. So if you want to query the
size for the line, add 1 to the return value.
#+end_deftypefun
#+attr_texinfo: :options {char *} tarot_features_arff_data_alloc (const TarotGame *@var{base}, const TarotGameEvent *@var{added}, int @var{score}, int @var{encoded})
#+begin_deftypefun
Get the ARFF line, just like =tarot_features_arff_data=, but allocate
and return the line.
#+end_deftypefun
If you want to access individual features easily, you can always use
them individually.
highlight useful high-level features. These are the list of
implemented high-level features that you may want to use:
#+attr_texinfo: :options {void} tarot_features_variant (const TarotGame *@var{game}, size_t *@var{nplayers}, int *@var{withcall})
#+begin_deftypefun
......@@ -2284,51 +2229,6 @@ Count how many /half points/ our card is worth (the excuse is worth 9)
and whether it is an /oudler/.
#+end_deftypefun
** The predictor interface
The learnt models are used to implement a new AI.
#+attr_texinfo: :options {struct} TarotPredictor
#+begin_deftp
A wrapper around a game state, that can predict the score for
different event queries.
#+end_deftp
#+attr_texinfo: :options {TarotPredictor *} tarot_mcts_alloc ()
#+begin_deftypefun
#+texinfo: @deftypefunx {size_t} tarot_mcts_construct (size_t @var{max_mem}, char *@var{mem}, size_t *@var{alignment})
Allocate a new predictor.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_predictor_free (TarotPredictor *@var{predictor})
#+begin_deftypefun
Dispose of /predictor/.
#+end_deftypefun
#+attr_texinfo: :options {TarotPredictor *} tarot_predictor_dup (const TarotPredictor *@var{predictor})
#+begin_deftypefun
#+texinfo: @deftypefunx {void} tarot_predictor_copy (TarotPredictor *@var{dest}, const TarotPredictor *@var{predictor})
Copy /predictor/. If /dest/ is given, then fill it with the copy.
Otherwise, allocate the copy and return it.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_mcts_set_game (TarotPredictor *@var{predictor}, const TarotGame *@var{game})
#+begin_deftypefun
Set up the /game/ for which we will have to /predict/.
#+end_deftypefun
#+attr_texinfo: :options {const TarotGameEvent *} tarot_predictor_best (const TarotPredictor *@var{predictor}, double *@var{score})
#+begin_deftypefun
Get the best event than the next player can play. If {{{person}}} can
play, then return the best event and set /score/ to the predicted
score. Otherwise, return =NULL=.
#+end_deftypefun
#+attr_texinfo: :options {size_t} tarot_predictor_eval (const TarotPredictor *@var{predictor}, size_t @var{ncandidates}, TarotGameEvent **@var{candidates}, size_t @var{start}, size_t @var{max}, double *@var{score})
#+begin_deftypefun
Predict the final score when playing each of the /candidate/ events.
Write the results to /score/, starting at /start/, up to /max/
elements. Return the position of the first invalid candidate.
#+end_deftypefun
** Online learning with a multi-layer perceptron
Static learning models are great for the AI, because we learn them at
compile time. However, it may be a good addition to have a learning
......@@ -2643,48 +2543,6 @@ Get the RMSE error between the situation predicted in all situations
in the /validation game/ and the actual score, averaged over all
players.
#+end_deftypefun
** High-level game insights
Given the ML building blocks, we have some opportunities for a few
interesting applications.
#+attr_texinfo: :options {TarotPlayer} tarot_features_strongest_player (size_t @var{nplayers}, int @var{withcall}, size_t @var{ncards}, const TarotPlayer *@var{owners}, double *@var{confidence}, TarotCard *@var{call})
#+begin_deftypefun
Given a deal for all players as the 78-player /owners/ array (of size
/ncards/), find the strongest player in the game and set /confidence/
to a real value which is positive if the AI would actually take, and
negative if no player would take. If /withcall/ is true, then /call/
is set to the best card the AI taker would call.
If the deal has a /petit sec/, then this function will return any
player while setting /confidence/ to a low value.
This function is especially useful for determining the duplicaté
options.
#+end_deftypefun
#+attr_texinfo: :options {double} tarot_features_errors (const TarotGame *@var{game})
#+begin_deftypefun
When a player plays a card in /game/, {{{person}}} has several
possibilities. The choice that has been made is not necessarily the
best. There is a difference between the predicted score for the best
possible move, and the actual move. This function sums up all of
these differences.
So, the return value of the function reflects how unlikely the game
is. This is very useful when imputing the cards of the other players:
if the taker is not likely, then it is stupid to even consider what to
do with this state of imputation.
#+end_deftypefun
#+attr_texinfo: :options {TarotImputationError} tarot_counter_and_game_impute_likely (TarotCounter *@var{counter}, TarotGame *@var{game}, size_t @var{n}, @var{seed})
#+begin_deftypefun
Impute the unknown cards in /game/, which is synchronized with
/counter/. Use the /n/ bytes of /seed/.
The imputation is not uniform; the taker tends to receive more strong
cards than the others. More precisely, this function tries to have a
low /error/ sum (see =tarot_features_errors=).
#+end_deftypefun
** Playing a game against the AI
libtarot has an API endpoint to run a game against the AI.
......
# tarot implements the rules of the tarot game
# Copyright (C) 2019, 2020 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, version 3 of the License.
#
# 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/>.
modeldir = $(pkgdatadir)/model
dist_model_DATA = \
%reldir%/tree-1.model \
%reldir%/tree-2.model \
%reldir%/tree-3.model \
%reldir%/tree-4.model \
%reldir%/tree-5.model \
%reldir%/linear-1.model \
%reldir%/linear-2.model \
%reldir%/linear-3.model \
%reldir%/linear-4.model \
%reldir%/linear-5.model \
%reldir%/stacking.model
MODEL_SOURCE_HEADER_FILES = \
$(srcdir)/%reldir%/tree-1.h \
$(srcdir)/%reldir%/tree-2.h \
$(srcdir)/%reldir%/tree-3.h \
$(srcdir)/%reldir%/tree-4.h \
$(srcdir)/%reldir%/tree-5.h \
$(srcdir)/%reldir%/linear-1.h \
$(srcdir)/%reldir%/linear-2.h \
$(srcdir)/%reldir%/linear-3.h \
$(srcdir)/%reldir%/linear-4.h \
$(srcdir)/%reldir%/linear-5.h \
$(srcdir)/%reldir%/stacking.h
MAINTAINERCLEANFILES += $(MODEL_SOURCE_HEADER_FILES)
dist_noinst_SCRIPTS += $(PREDICTORS)
learn_verbose = $(learn_verbose_@AM_V@)
learn_verbose_ = $(learn_verbose_@AM_DEFAULT_V@)
learn_verbose_0 = @echo " LEARN " $@;
EXTRA_DIST += $(MODEL_SOURCE_HEADER_FILES) %reldir%/gen-c-code.xsl
BUILT_SOURCES += $(MODEL_SOURCE_HEADER_FILES)
AM_CPPFLAGS += -I %reldir% -I $(srcdir)/%reldir%
.model.h: %reldir%/gen-c-code.xsl
@$(MKDIR_P) %reldir%
$(AM_V_GEN) $(XSLTPROC) $(srcdir)/%reldir%/gen-c-code.xsl $< | $(INDENT) > $@-t
@mv $@-t $@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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, version 3 of the License.
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/>.
-->
<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 name="header">
<xsl:text>/* Do not edit this file, it has been generated from gen-c-code.xsl */&#xA;</xsl:text>
</xsl:template>
<xsl:template match="tree">
<xsl:call-template name="header" />
<xsl:apply-templates />
</xsl:template>
<xsl:template name="print-split-condition">
<xsl:param name="variable" />
<xsl:param name="operator" />
<xsl:param name="value" />
<xsl:value-of select="translate($variable, '-', '_')" />
<xsl:choose>
<xsl:when test="$operator = '='">
<xsl:text> == </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$operator" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="contains($value, ',')">
<xsl:value-of select="substring-before($value, ',')" />
<xsl:text> || </xsl:text>
<xsl:call-template name="print-split-condition">
<xsl:with-param name="variable" select="$variable" />
<xsl:with-param name="operator" select="$operator" />
<xsl:with-param name="value" select="substring-after($value, ',')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="split">
<xsl:text>if (</xsl:text>
<xsl:call-template name="print-split-condition">
<xsl:with-param name="variable" select="@variable" />
<xsl:with-param name="operator" select="@operator" />
<xsl:with-param name="value" select="@value" />
</xsl:call-template>
<xsl:text>) {score = </xsl:text>
<xsl:value-of select="@score" />
<xsl:text>; </xsl:text>
<xsl:apply-templates />
<xsl:text>}</xsl:text>
</xsl:template>
<!-- Write the linear prediction -->
<xsl:template match="linear-model">
<xsl:call-template name="header" />
<xsl:text>score = 0;&#xA;</xsl:text>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="interaction">
<xsl:text>score += </xsl:text>
<xsl:value-of select="@weight" />
<xsl:text>*(1</xsl:text>
<xsl:apply-templates />
<xsl:text>);&#xA;</xsl:text>
</xsl:template>
<xsl:template match="variable[@name != '(Intercept)']">
<xsl:text>* </xsl:text>
<xsl:value-of select="translate(@name, '-', '_')" />
</xsl:template>
<xsl:template match="variable[contains(@name, '_eq_')]">
<xsl:text>* (</xsl:text>
<xsl:value-of select="substring-before(@name, '_eq_')" />
<xsl:text> == </xsl:text>
<xsl:value-of select="substring-after(@name, '_eq_')" />
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template match="variable[contains(@name, '_leq_')]">
<xsl:text>* (</xsl:text>
<xsl:value-of select="substring-before(@name, '_leq_')" />
<xsl:text> &lt;= </xsl:text>
<xsl:value-of select="substring-after(@name, '_leq_')" />
<xsl:text>)</xsl:text>
</xsl:template>
</xsl:stylesheet>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<linear-model>
<interaction weight="-223.641119" p-value="0.000000">
<variable name="(Intercept)"/>
</interaction>
<interaction weight="-0.022306" p-value="0.346321">
<variable name="tree-1"/>
</interaction>
<interaction weight="1.131477" p-value="0.000000">
<variable name="tree-2"/>
</interaction>
<interaction weight="-0.008823" p-value="0.592032">
<variable name="tree-3"/>
</interaction>
<interaction weight="-0.245747" p-value="0.000000">
<variable name="linear-1"/>
</interaction>
<interaction weight="0.674400" p-value="0.000000">
<variable name="linear-2"/>
</interaction>
<interaction weight="-0.061812" p-value="0.011879">
<variable name="linear-3"/>
</interaction>
<interaction weight="0.031269" p-value="0.133451">
<variable name="linear-4"/>
</interaction>
<interaction weight="0.415052" p-value="0.000000">
<variable name="linear-5"/>
</interaction>
</linear-model>
<?xml version="1.0" encoding="utf-8"?>
<tree>
<split variable="team_attack" operator="=" value="yes" score="-93.545880">
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="-569.292500">
<split variable="with_call" operator="=" value="no" score="-849.142900">
<split variable="n_players" operator="&gt;=" value="3.5" score="-1085.655000">
<split variable="n_players" operator="&lt;" value="3.5" score="-582.947400"/>
</split>
<split variable="with_call" operator="=" value="yes" score="-157.747900">
<split variable="bid_max_trump" operator="&gt;=" value="13.5" score="-318.818200"/>
<split variable="bid_max_trump" operator="&lt;" value="13.5" score="-35.556650">
<split variable="partner_known" operator="=" value="yes" score="-186.896100"/>
<split variable="partner_known" operator="=" value="no" score="440.081600"/>
</split>
</split>
</split>
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="13.688600">
<split variable="n_players" operator="&lt;" value="3.5" score="-123.848400"/>
<split variable="n_players" operator="&gt;=" value="3.5" score="50.236130"/>
</split>
</split>
<split variable="team_attack" operator="=" value="no" score="39.145630">
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="-7.832336"/>
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="257.440800">
<split variable="partner_known" operator="=" value="no" score="-15.487890"/>
<split variable="partner_known" operator="=" value="yes" score="305.157900"/>
</split>
</split>
</split>
</tree>
<?xml version="1.0" encoding="utf-8"?>
<tree>
<split variable="team_attack" operator="=" value="yes" score="-100.561200">
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="-562.253400">
<split variable="with_call" operator="=" value="no" score="-895.970100">
<split variable="n_players" operator="&gt;=" value="3.5" score="-1164.897000">
<split variable="n_players" operator="&lt;" value="3.5" score="-582.947400"/>
</split>
<split variable="with_call" operator="=" value="yes" score="-223.787500"/>
</split>
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="15.945680">
<split variable="n_players" operator="&lt;" value="3.5" score="-118.566400"/>
<split variable="n_players" operator="&gt;=" value="3.5" score="46.541580">
<split variable="strategy_bid" operator="=" value="TAROT_DOUBLE_KEEP" score="-742.509800"/>
<split variable="strategy_bid" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="52.456860"/>
</split>
</split>
</split>
<split variable="team_attack" operator="=" value="no" score="42.653190">
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="-9.417570"/>
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="258.644500">
<split variable="with_call" operator="=" value="yes" score="140.694800"/>
<split variable="with_call" operator="=" value="no" score="322.706200"/>
</split>
</split>
</split>
</tree>
<?xml version="1.0" encoding="utf-8"?>
<tree>
<split variable="team_attack" operator="=" value="yes" score="-114.604200">
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="-648.883500">
<split variable="with_call" operator="=" value="no" score="-897.229100">
<split variable="n_players" operator="&gt;=" value="4.5" score="-1406.400000">
<split variable="n_players" operator="&lt;" value="4.5" score="-801.477900">
<split variable="bid_has_twentyone" operator="=" value="no" score="-888.154100"/>
<split variable="bid_has_twentyone" operator="=" value="yes" score="-502.167500"/>
</split>
</split>
<split variable="with_call" operator="=" value="yes" score="-358.335500"/>
</split>
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_PUSH,TAROT_TAKE" score="19.133450">
<split variable="n_players" operator="&lt;" value="3.5" score="-161.058100"/>
<split variable="n_players" operator="&gt;=" value="3.5" score="53.478760">
<split variable="strategy_outbid" operator="&gt;=" value="2" score="-784.980400"/>
<split variable="strategy_outbid" operator="&lt;" value="2" score="59.925520"/>
</split>
</split>
</split>
<split variable="team_attack" operator="=" value="no" score="47.994250">
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_PUSH,TAROT_TAKE" score="-10.747140"/>
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="288.791500"/>
</split>
</split>
</tree>
<?xml version="1.0" encoding="utf-8"?>
<tree>
<split variable="team_attack" operator="=" value="yes" score="-115.605400">
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="-579.660800">
<split variable="with_call" operator="=" value="no" score="-920.307700">
<split variable="n_players" operator="&gt;=" value="3.5" score="-1194.210000">
<split variable="n_players" operator="&lt;" value="3.5" score="-603.157900"/>
</split>
<split variable="with_call" operator="=" value="yes" score="-211.375300">
<split variable="bid_n_marriages" operator="&lt;" value="0.5" score="-256.026400"/>
<split variable="bid_n_marriages" operator="&gt;=" value="0.5" score="323.263200"/>
</split>
</split>
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="8.158825">
<split variable="n_players" operator="&lt;" value="3.5" score="-112.069500"/>
<split variable="n_players" operator="&gt;=" value="3.5" score="42.955920">
<split variable="strategy_bid" operator="=" value="TAROT_DOUBLE_KEEP" score="-690.468100"/>
<split variable="strategy_bid" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="48.778720"/>
</split>
</split>
</split>
<split variable="team_attack" operator="=" value="no" score="49.256240">
<split variable="bid_minimum" operator="=" value="TAROT_PASS,TAROT_TAKE,TAROT_PUSH" score="-6.230303"/>
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="263.480800">
<split variable="partner_known" operator="=" value="no" score="39.237110"/>
<split variable="partner_known" operator="=" value="yes" score="303.497000"/>
</split>
</split>
</split>
</tree>
<?xml version="1.0" encoding="utf-8"?>
<tree>
<split variable="team_attack" operator="=" value="yes" score="-124.740400">
<split variable="bid_minimum" operator="=" value="TAROT_DOUBLE_KEEP" score="-655.485300">
<split variable="with_call" operator="=" value="no" score="-960.374600">
<split variable="n_players" operator="&gt;=" value="3.5" score="-1245.478000">
<split variable="n_players" operator="&lt;" value="3.5" score="-543.000000"/>
</split>
<split variable="with_call" operator="=" value="yes" score="-315.058800"/>