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

Merge branch '178-purge-the-useless-code' into 'develop'

Resolve "Purge the useless code"

Closes #178

See merge request !184
parents 83bd66de 2d31cf61
Pipeline #206694 passed with stages
in 43 minutes and 6 seconds
/* 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, 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/>.
*/
#include <config.h>
#include <tarot/mcts_private.h>
#include <tarot/simulation.h>
#include <tarot/counter_private.h>
#include <tarot/game_private.h>
#include <tarot/game_event_private.h>
#include <float.h>
#include <string.h>
#include <pool.h>
/* #define DEBUG_MCTS */
#ifdef DEBUG_MCTS
#define DEBUG fprintf
#else /* not DEBUG_MCS */
#define DEBUG(...)
#endif /* not DEBUG_MCTS */
#define TUNE_ITERATIONS 100
#define TUNE_PARAMETER 0.000828
#define TUNE_SIMULATION_RANDOM 0.000000
#define TUNE_SIMULATION_AGREED 0.781867
/* Add a few starting positions, or all of them if they are enumerable. */
static TarotMctsError mcts_setup_root (TarotMcts * mcts);
static inline size_t
mcts_construct (size_t max_mem, char *mem, size_t *alignment,
size_t pool_size)
{
size_t required = sizeof (TarotMcts) + pool_size;
*alignment = alignof (TarotMcts);
if (max_mem >= required)
{
TarotMcts *mcts = (TarotMcts *) mem;
mcts->root = NULL;
mcts->pool_tail = mem + sizeof (TarotMcts);
mcts->pool_remaining = pool_size;
mcts->pool_head = mem + sizeof (TarotMcts);
mcts->tune_parameter = TUNE_PARAMETER;
mcts->tune_simulation_random = TUNE_SIMULATION_RANDOM;
mcts->tune_simulation_agreed = TUNE_SIMULATION_AGREED;
game_initialize (&(mcts->base), 4, 0);
return max_mem;
}
return required;
}
static inline void
mcts_copy (TarotMcts * dest, const TarotMcts * source)
{
(void) dest;
(void) source;
fprintf (stderr, "%s:%d: Not implemented yet\n", __FILE__, __LINE__);
assert (0);
}
static inline TarotMctsError
mcts_set_base (TarotMcts * mcts, const TarotGame * base)
{
size_t freed = mcts->pool_head - mcts->pool_tail;
assert (mcts->pool_head >= mcts->pool_tail);
mcts->root = NULL;
mcts->pool_remaining += freed;
mcts->pool_head -= freed;
assert (mcts->pool_head == mcts->pool_tail);
game_copy (&(mcts->base), base);
return mcts_setup_root (mcts);
}
static TarotMctsNode *mcts_select (TarotMcts * mcts, size_t n_simulations);
static int mcts_load (const TarotMcts * mcts, const TarotMctsNode * leaf,
TarotGame * game);
static int mcts_impute (TarotGame * game, struct yarrow256_ctx *rng);
static int mcts_next_move (const TarotGame * game, TarotGameEvent * event,
struct yarrow256_ctx *rng);
static int mcts_expand (TarotMcts * mcts, TarotMctsNode ** node,
const TarotGameEvent * event,
TarotPlayer next_player);
static void mcts_simulate (double simulation_random,
double simulation_agreed,
struct yarrow256_ctx *rng, TarotGame * game,
size_t n_players, int *score);
static void mcts_backpropagate (TarotMctsNode * node, TarotPlayer taker,
size_t n_players, const int *score);
static TarotMctsError
one_iteration (TarotMcts * mcts, size_t n_simulations,
struct yarrow256_ctx *rng)
{
TarotMctsNode *node;
TarotGame game;
int score[5];
TarotGameEvent next;
unsigned int next_buffer[78];
TarotPlayer taker;
next.data = next_buffer;
game_initialize (&game, 4, 0);
/* SELECT */
node = mcts_select (mcts, n_simulations);
DEBUG (stderr, "%s:%d: Select node %p\n", __FILE__, __LINE__, node);
/* IMPUTE */
if (mcts_load (mcts, node, &game) != 0)
{
DEBUG (stderr, "%s:%d: Could not load til then.\n", __FILE__, __LINE__);
assert (0);
}
if (mcts_impute (&game, rng) != 0)
{
DEBUG (stderr, "%s:%d: Could not impute.\n", __FILE__, __LINE__);
return TAROT_MCTS_OK;
}
/* EXPAND */
if (game_step (&game) != TAROT_END)
{
TarotPlayer next_player = game_n_players (&game);
int has_next = (game_get_next (&game, &next_player) == TAROT_GAME_OK);
has_next = (game_get_next (&game, &next_player) == TAROT_GAME_OK);
if (!has_next)
{
assert (game_step (&game) == TAROT_DOG);
next_player = game_n_players (&game);
}
if (mcts_next_move (&game, &next, rng) != 0)
{
/* deal failed (petit sec) */
DEBUG (stderr, "%s:%d: Could not select the next move\n",
__FILE__, __LINE__);
return TAROT_MCTS_OK;
}
if (game_add_event (&game, &next) != 0)
{
DEBUG (stderr, "%s:%d: Could not add the next move\n", __FILE__,
__LINE__);
assert (0);
}
DEBUG (stderr, "%s:%d: Expanding node %p\n", __FILE__, __LINE__, node);
assert (node != NULL);
if (mcts_expand (mcts, &node, &next, next_player) != 0)
{
/* No memory for child */
DEBUG (stderr, "%s:%d: Could not expand\n", __FILE__, __LINE__);
return TAROT_MCTS_NOMEM;
}
DEBUG (stderr, "%s:%d: Expanded to node %p\n", __FILE__, __LINE__,
node);
}
/* SIMULATE */
assert (game_n_players (&game) <= 5);
mcts_simulate (mcts->tune_simulation_random, mcts->tune_simulation_agreed,
rng, &game, game_n_players (&game), score);
DEBUG (stderr, "%s:%d: Backpropagate scores\n", __FILE__, __LINE__);
/* BACKPROPAGATE */
game_get_taker (&game, &taker);
mcts_backpropagate (node, taker, game_n_players (&game), score);
return TAROT_MCTS_OK;
}
static inline void
mcts_seed (TarotMcts * mcts, size_t seed_size, const void *seed)
{
yarrow256_init (&(mcts->generator), 0, NULL);
yarrow256_seed (&(mcts->generator), seed_size, seed);
}
static inline TarotMctsError
mcts_run (TarotMcts * mcts, size_t n_iterations)
{
TarotMctsError ret = TAROT_MCTS_OK;
size_t i;
DEBUG (stderr, "Starting running the MCTS...\n");
for (i = 0; i < n_iterations; i++)
{
TarotMctsError next;
DEBUG (stderr, "MCTS %lu/%lu...\n", i, n_iterations);
next = one_iteration (mcts, i, &(mcts->generator));
if (next != TAROT_MCTS_OK && ret == TAROT_MCTS_OK)
{
ret = next;
}
}
return ret;
}
static inline void
mcts_set_parameter (TarotMcts * mcts, double value)
{
mcts->tune_parameter = value;
}
static inline double
mcts_get_parameter (const TarotMcts * mcts)
{
return mcts->tune_parameter;
}
static inline void
mcts_set_simulation_random (TarotMcts * mcts, double value)
{
mcts->tune_simulation_random = value;
}
static inline double
mcts_get_simulation_random (const TarotMcts * mcts)
{
return mcts->tune_simulation_random;
}
static inline void
mcts_set_simulation_agreed (TarotMcts * mcts, double value)
{
mcts->tune_simulation_agreed = value;
}
static inline double
mcts_get_simulation_agreed (const TarotMcts * mcts)
{
return mcts->tune_simulation_agreed;
}
static inline size_t
mcts_default_iterations ()
{
return TUNE_ITERATIONS;
}
static inline const TarotGameEvent *
mcts_get_best (const TarotMcts * mcts)
{
const TarotMctsNode *root = mcts->root;
const TarotMctsNode *best_event;
double best_score;
const TarotGameEvent *event;
size_t n_simulations;
int sum_scores;
best_event = root;
event = mcts_get (root, &n_simulations, &sum_scores);
if (event == NULL)
{
return NULL;
}
best_score = sum_scores / (n_simulations + 0.0);
for (root = mcts_next_sibling ((TarotMctsNode *) root);
root != NULL; root = mcts_next_sibling ((TarotMctsNode *) root))
{
double score;
event = mcts_get (root, &n_simulations, &sum_scores);
score = sum_scores / (n_simulations + 0.0);
if (score > best_score)
{
best_event = root;
}
}
return mcts_get (best_event, &n_simulations, &sum_scores);
}
static double
compute_score (double tune_parameter, const TarotMctsNode * node,
double n_simulations)
{
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 + tune_parameter * exploration_ratio;
}
}
static void
search_best_select (double tune_parameter, const TarotMctsNode * node,
TarotMctsNode ** best, double n_simulations,
double *best_value)
{
const TarotMctsNode *left = node->left;
const TarotMctsNode *right = node->right;
int left_ok = (left != NULL);
int right_ok = (right != NULL);
size_t this_n_simulations = n_simulations;
double value;
int sum_scores;
const TarotGameEvent *event =
mcts_get (node, &this_n_simulations, &sum_scores);
if (event_type (event) != TAROT_DOG_EVENT)
{
value = compute_score (tune_parameter, node, n_simulations);
if (value > *best_value)
{
*best_value = value;
*best = (TarotMctsNode *) node;
}
}
if (left_ok)
{
search_best_select (tune_parameter, left, best, this_n_simulations,
best_value);
}
if (right_ok)
{
search_best_select (tune_parameter, right, best, this_n_simulations,
best_value);
}
}
static inline TarotMctsNode *
mcts_select (TarotMcts * mcts, size_t n_simulations)
{
if (n_simulations != 0)
{
TarotMctsNode *best = mcts->root;
double value_best = -DBL_MAX;
search_best_select (mcts->tune_parameter, best, &best, n_simulations,
&value_best);
return best;
}
return mcts->root;
}
static inline void
restrict_deal_all_event (const TarotGame * game, TarotGameEvent * event,
TarotPlayer who)
{
size_t n_cards;
TarotCard cards[78];
if (game_get_deal_of (game, who, &n_cards, 0, 78, cards) != TAROT_GAME_OK)
{
assert (0);
}
event_set_deal (event, who, n_cards, cards);
}
static inline void
restrict_discard_event (const TarotGame * game, TarotGameEvent * event,
TarotPlayer who)
{
size_t n_cards;
TarotCard cards[78];
TarotPlayer taker;
int has_taker = (game_get_taker (game, &taker) == TAROT_GAME_OK);
assert (has_taker);
if (who == taker)
{
if (game_get_full_discard (game, &n_cards, 0, 78, cards) !=
TAROT_GAME_OK)
{
assert (0);
}
}
else
{
if (game_get_public_discard (game, &n_cards, 0, 78, cards) !=
TAROT_GAME_OK)
{
assert (0);
}
}
event_set_discard (event, n_cards, cards);
}
static int
mcts_load (const TarotMcts * mcts, const TarotMctsNode * leaf,
TarotGame * game)
{
DEBUG (stderr, "%s:%d: Load %p\n", __FILE__, __LINE__, leaf);
if (leaf != NULL)
{
const TarotMctsNode *parent = mcts_parent ((TarotMctsNode *) leaf);
size_t n_simulations;
int sum_scores;
const TarotGameEvent *event =
mcts_get (leaf, &n_simulations, &sum_scores);
DEBUG (stderr, "%s:%d: %p is the parent of %p\n", __FILE__, __LINE__,
parent, leaf);
if (mcts_load (mcts, parent, game) != 0)
{
return 1;
}
if (event == NULL)
{
DEBUG (stderr, "%s:%d: loading the root, no iterations yet\n",
__FILE__, __LINE__);
return 0;
}
if (game_add_event (game, event) != TAROT_GAME_OK)
{
if (event_type (event) == TAROT_DISCARD_EVENT)
{
/* We have to try with only the trumps, since we're not
imputed yet */
size_t n_full_discard;
size_t n_trump_free;
TarotCard full_discard[6];
TarotCard trump_free[6];
size_t max_full_discard =
sizeof (full_discard) / sizeof (full_discard[0]);
size_t max_trump_free =
sizeof (trump_free) / sizeof (trump_free[0]);
unsigned int event_restricted_data[78];
TarotGameEvent event_restricted = {
.data = event_restricted_data
};
size_t i;
int is_discard =
(event_get_discard
(event, &n_full_discard, 0, max_full_discard,
full_discard) == TAROT_EVENT_OK);
assert (is_discard);
n_trump_free = 0;
for (i = 0; i < n_full_discard; i++)
{
TarotCard c = full_discard[i];
if (c > 56 && c < 76)
{
trump_free[n_trump_free++] = c;
}
}
assert (n_trump_free <= max_trump_free);
event_set_discard (&event_restricted, n_trump_free, trump_free);
if (game_add_event (game, &event_restricted) != TAROT_GAME_OK)
{
DEBUG (stderr,
"%s:%d: could not add an event of type %d for node %p.\n",
__FILE__, __LINE__,
event_type (&event_restricted), leaf);
return 1;
}
}
else
{
DEBUG (stderr,
"%s:%d: could not add an event of type %d for node %p.\n",
__FILE__, __LINE__, event_type (event), leaf);
return 1;
}
}
else
{
DEBUG (stderr, "%s:%d: added an event of type %d for node %p\n",
__FILE__, __LINE__, event_type (event), leaf);
}
}
else
{
TarotGameIterator iterator;
const TarotGameEvent *event;
game_iterator_setup (&iterator, &(mcts->base));
while ((event = game_iterator_next_value (&iterator)) != NULL)
{
TarotPlayer next;
unsigned int added_data[78];
TarotGameEvent added = {.n = 0,.data = added_data };
int has_next =
(game_get_next (&(mcts->base), &next) == TAROT_GAME_OK);
switch (event_type (event))
{
case TAROT_DEAL_ALL_EVENT:
assert (has_next);
restrict_deal_all_event (&(mcts->base), &added, next);
break;
case TAROT_DISCARD_EVENT:
assert (has_next);
restrict_discard_event (&(mcts->base), &added, next);
break;
default:
event_copy (&added, event);
break;
}
if (game_add_event (game, &added) != TAROT_GAME_OK)
{
assert (0);
}
DEBUG (stderr, "%s:%d: added an event of type %d\n",
__FILE__, __LINE__, event_type (event));
}
}
return 0;
}
static int
mcts_impute (TarotGame * game, struct yarrow256_ctx *rng)
{
char imputation_seed[16];
size_t sz = sizeof (imputation_seed);
int ret = 0;
TarotCounter counter;
counter_initialize (&counter);
yarrow256_random (rng, sz, (void *) imputation_seed);
counter_load_from_game (&counter, game);
ret =
(counter_and_game_impute
(&counter, game, sz, (void *) imputation_seed) != TAROT_IMPUTATION_OK);
return ret;
}
static void
select_next_move_deal (size_t n_players, TarotGameEvent * event,
struct yarrow256_ctx *rng)
{
char deal_seed[16];
size_t sz = sizeof (deal_seed);
yarrow256_random (rng, sz, (void *) deal_seed);
event_set_deal_all_random (event, n_players, sz, (void *) deal_seed);
}
static void
select_next_move_bid (TarotGameEvent * event, TarotBid mini,
struct yarrow256_ctx *rng)
{
unsigned char random_byte;
double random_value;
yarrow256_random (rng, 1, &random_byte);
random_value = random_byte / 256.0;
if (random_value <= 0.75)
{
event_set_bid (event, TAROT_PASS);
}
else
{
/* Outbid */
event_set_bid (event, mini);
}
}
static void
select_next_move_decl (TarotGameEvent * event, int allowed,
struct yarrow256_ctx *rng)
{
char random_byte;
int has_decl;
yarrow256_random (rng, 1, (void *) &random_byte);
has_decl = allowed && (random_byte % 2 == 0);
event_set_decl (event, has_decl);
}
static void
select_next_move_call (TarotGameEvent * event, TarotNumber mini_face,
struct yarrow256_ctx *rng)
{
TarotNumber face;
static TarotSuit suits[4] =
{ TAROT_HEARTS, TAROT_CLUBS, TAROT_DIAMONDS, TAROT_SPADES };
TarotSuit suit;
TarotCard call;
unsigned int random_value;
if (mini_face == TAROT_KING)
{
face = TAROT_KING;
}
else
{
yarrow256_random (rng, sizeof (random_value), (void *) &random_value);
face = mini_face + random_value % (TAROT_KING - mini_face + 1);
}
yarrow256_random (rng, sizeof (random_value), (void *) &random_value);
suit = suits[random_value % 4];
if (tarot_of (face, suit, &call) != 0)
{
assert (0);
}
event_set_call (event, call);
}
static void
select_next_move_dog (TarotGameEvent * event, const TarotGame * imputed)
{