minimax_ai.cpp 5.33 KB
#include "minimax_ai.h"
#include "basic_types.h"

namespace othello::minimax_ai {

MiniMaxAI::MiniMaxAI(const PlayerId &/*player_id*/):max_depth{2}
    {
        m_costs = {
            120, -20,  20,   5,   5,  20, -20, 120,
            -20, -40,  -5,  -5,  -5,  -5, -40, -20,
             20,  -5,  15,   3,   3,  15,  -5,  20,
              5,  -5,   3,   3,   3,   3,  -5,   5,
              5,  -5,   3,   3,   3,   3,  -5,   5,
             20,  -5,  15,   3,   3,  15,  -5,  20,
            -20, -40,  -5,  -5,  -5,  -5, -40, -20,
            120, -20,  20,   5,   5,  20, -20, 120,
        };
    }

    void MiniMaxAI::think(const BitBoard &board, const PlayerId &player_id,
                          const std::chrono::seconds &max_time)
    {
        createTree(m_minimax_tree, board, player_id, max_depth);
        computeCost(*m_minimax_tree.root, player_id);
        computeMiniMax(*m_minimax_tree.root, player_id, true);
     //   best_move = *m_minimax_tree.root;

        //compute cost fn
        // run minimax

    }

    BitPos MiniMaxAI::bestMove() const
    {
        const auto best_move = BitPos(m_best_move.value());
        m_best_move = BitPos::invalid();
        return best_move;
    }

    void createChildren(detail::MiniMaxNode& parent, const PlayerId& player_id,
                        int depth)
    {
        if(depth == 0)
            return;

        const auto legal_moves = utility::legalMoves(parent.data.board, player_id);

        for(const auto& legal_move : legal_moves) {
            BitBoard board = parent.data.board;
            utility::placeAndFlip(board, player_id, legal_move);

            auto child = std::make_unique<detail::MiniMaxNode>(board, legal_move);
            parent.children.push_back(std::move(child));
        }

        depth--; //instead of having depth-- inside the loop condition
        for(auto& child : parent.children) {
             createChildren(*child, utility::opponent(player_id), depth);
        }
    }

    void createTree(detail::MiniMaxTree& tree, const BitBoard& board,
                    const PlayerId& player_id, int depth)
    {
        tree.root = std::make_unique<detail::MiniMaxNode>(board, BitPos::invalid());

        createChildren(*tree.root, player_id, depth);
    }

    void MiniMaxAI::computeCost(detail::MiniMaxNode& parent, const PlayerId &current_playerid)
    {
        if (parent.children.empty()){
            //make m_costs(the array of positional) weights a vector
            std::vector<int> costs(m_costs.begin(), m_costs.end());

            //name the player and opponent's bitset's variables
            auto m_bitset_current_player = parent.data.board[size_t(current_playerid)];
            auto m_bitset_opponent_player = parent.data.board[size_t(utility::opponent(current_playerid))];

            //make the bitset variables into vectors
            std::vector<bool> bitset_player;
            for (size_t b=0; b<64; ++b) {
              bitset_player.push_back(m_bitset_current_player[b]);
            }
            std::vector<bool> bitset_opponent;
            for (size_t b=0; b<64; ++b) {
              bitset_opponent.push_back(m_bitset_opponent_player[b]);
            }

            //Use tranform reduce to perform costs operations on m_cost and the bitsets
            int current_player_score = std::transform_reduce(
                costs.begin(),
                costs.end(),
                bitset_player.begin(),
                0,
                [](auto a, auto b) {return a + b;},
                [](auto a, auto b) {return a * b;}
            );
            int opponent_player_score = std::transform_reduce(
                costs.begin(),
                costs.end(),
                bitset_opponent.begin(),
                0,
                [](auto a, auto b) {return a + b;},
                [](auto a, auto b) {return a * b;}
            );

            //compute the cost function and store it in the minmaxdata
            parent.data.cost = current_player_score - opponent_player_score;
        }
        for(const auto& child : parent.children) {
            computeCost(*child, current_playerid);         
        }
    }

    void MiniMaxAI::computeMiniMax(detail::MiniMaxNode& parent, const PlayerId &current_playerid, bool isMax)
    {
        if (parent.children.empty())
            return;

        //Check for the highest costs in Max's leaves
        if (isMax){
            for(const auto& child : parent.children) {
                computeCost(*child, current_playerid);
                auto elem = std::max_element(parent.children.begin(),
                                 parent.children.end(),
                                 [](const auto& a, const auto& b) {return a->data.cost > b->data.cost;}
                );
     //           elem->data.cost;
            }
        }
        else{
            //Check for the lowest costs in Max's leaves
            for(const auto& child : parent.children) {
                computeCost(*child, utility::opponent(current_playerid));
                auto elem = std::max_element(parent.children.begin(),
                                     parent.children.end(),
                                     [](const auto& a, const auto& b) {return a->data.cost > b->data.cost;}
                );
       //         elem->data.cost;
            }
        }
        computeMiniMax(parent, current_playerid, !isMax);
    }
} //namespace othello::minimax_ai