<commit_before>#include "monte_carlo_agent.hpp"
#include "MonteCarloTree/monte_carlo_tree.hpp"
#include "random_agent.hpp"
#include "reversi.hpp"
#include "util.hpp"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iomanip>
#include <limits>
#include <sstream>

using Tree = MonteCarloTree<GameState, coord>;

constexpr auto SIM_TIME_SEC = 3;

coord MonteCarloAgent::pick_move(const GameState& game_state)
{
    return this->monte_carlo_tree_search(game_state);
}

coord MonteCarloAgent::monte_carlo_tree_search(const GameState& game_state)
{
    auto tree_root_ptr = this->reversi_tree.get_existing_node(game_state);

    if (tree_root_ptr.get() == nullptr)
    {
        tree_root_ptr = this->reversi_tree.add_root_node(game_state);
    }

    auto get_now = []() { return std::chrono::system_clock::now(); };
    auto start_time = get_now();

    unsigned simulations = 0;
    while (get_now() - start_time < std::chrono::seconds{ SIM_TIME_SEC }) {
        auto selected_node_ptr = this->tree_policy(tree_root_ptr);
        unsigned result = this->simulate(selected_node_ptr);
        this->back_propagate(selected_node_ptr, result);
        ++simulations;
    }

    show("\n" + this->node_scores_str(tree_root_ptr->get_children()));
    show(std::to_string(simulations) + " simulations performed.\n");
    auto best_child = this->winningest_node(tree_root_ptr->get_children());
    return best_child->get_move();
}

std::shared_ptr<ReversiNode> MonteCarloAgent::tree_policy(std::shared_ptr<ReversiNode> node_ptr)
{
    const GameState& game_state = node_ptr->get_game_state();
    const Board& board = game_state.get_board();

    if (game_state.get_legal_moves().size() == 0) {
        if (is_game_over(board)) {
            // no available moves + game is over
            return node_ptr;
        } else {
            // no available moves + game is NOT over, so must pass turn
            // passing a turn acts as a "move" and gets its own node
            auto pass_legal_moves = legal_moves(board, opponent(this->color));
            GameState pass_state{ board, pass_legal_moves, opponent(this->color) };
            auto pass_node = this->reversi_tree.add_node(std::move(pass_state), { 0, 0 }, *node_ptr); // TODO: move coord should not be {0, 0}
            return pass_node;
        }
    } else {
        // this node has children to select from

        const auto& legal_moves_vec = game_state.get_legal_moves();<commit_msg>update message printing to show reuse of nodes<commit_after>#include "monte_carlo_agent.hpp"
#include "MonteCarloTree/monte_carlo_tree.hpp"
#include "random_agent.hpp"
#include "reversi.hpp"
#include "util.hpp"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iomanip>
#include <limits>
#include <sstream>

using Tree = MonteCarloTree<GameState, coord>;

constexpr auto SIM_TIME_SEC = 3;

coord MonteCarloAgent::pick_move(const GameState& game_state)
{
    return this->monte_carlo_tree_search(game_state);
}

coord MonteCarloAgent::monte_carlo_tree_search(const GameState& game_state)
{
    auto tree_root_ptr = this->reversi_tree.get_existing_node(game_state);

    if (tree_root_ptr.get() == nullptr)
    {
        tree_root_ptr = this->reversi_tree.add_root_node(game_state);
    }

    auto get_now = []() { return std::chrono::system_clock::now(); };
    auto start_time = get_now();

    unsigned simulations = 0;
    while (get_now() - start_time < std::chrono::seconds{ SIM_TIME_SEC }) {
        auto selected_node_ptr = this->tree_policy(tree_root_ptr);
        unsigned result = this->simulate(selected_node_ptr);
        this->back_propagate(selected_node_ptr, result);
        ++simulations;
    }

    unsigned effective_simulations = 0;
    for (const auto& child : tree_root_ptr->get_children())
    {
        effective_simulations += child->get_plays();
    }

    show("\n" + this->node_scores_str(tree_root_ptr->get_children()));
    show(std::to_string(simulations) +
        " simulations performed. (effective: " +
        std::to_string(effective_simulations) +
        " +" +
        std::to_string(effective_simulations - simulations) +
        ")\n");
    auto best_child = this->winningest_node(tree_root_ptr->get_children());
    return best_child->get_move();
}

std::shared_ptr<ReversiNode> MonteCarloAgent::tree_policy(std::shared_ptr<ReversiNode> node_ptr)
{
    const GameState& game_state = node_ptr->get_game_state();
    const Board& board = game_state.get_board();

    if (game_state.get_legal_moves().size() == 0) {
        if (is_game_over(board)) {
            // no available moves + game is over
            return node_ptr;
        } else {
            // no available moves + game is NOT over, so must pass turn
            // passing a turn acts as a "move" and gets its own node
            auto pass_legal_moves = legal_moves(board, opponent(this->color));
            GameState pass_state{ board, pass_legal_moves, opponent(this->color) };
            auto pass_node = this->reversi_tree.add_node(std::move(pass_state), { 0, 0 }, *node_ptr); // TODO: move coord should not be {0, 0}
            return pass_node;
        }
    } else {
        // this node has children to select from

        const auto& legal_moves_vec = game_state.get_legal_moves();
{'fraction_non_alphanumeric': 0.12123508240197008, 'fraction_numerical': 0.0028414472437961733, 'mean_word_length': 3.6153846153846154, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5134596', 'n_tokens_mistral': 1747, 'n_tokens_neox': 1712, 'n_words': 355}