Commit f5ec511742cd3ab50177e4d943fa101a2205f480

Authored by Hakeem
1 parent 80f590bd

Minimax needs 1 or 2 debugging

CMakeLists.txt
... ... @@ -10,12 +10,14 @@ set( HDRS
10 10 orangemonkey_ai.h
11 11 engine.h
12 12 utility.h
  13 + minimax_ai.h
13 14 )
14 15  
15 16 set( SRCS
16 17 orangemonkey_ai.cpp
17 18 engine.cpp
18 19 utility.cpp
  20 + minimax_ai.cpp
19 21 )
20 22  
21 23 add_library( ${PROJECT_NAME} STATIC
... ...
CMakeLists.txt.user
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <!DOCTYPE QtCreatorProject>
3   -<!-- Written by QtCreator 4.7.0, 2018-10-02T18:59:24. -->
  3 +<!-- Written by QtCreator 4.7.1, 2018-10-10T19:12:45. -->
4 4 <qtcreator>
5 5 <data>
6 6 <variable>EnvironmentId</variable>
... ...
engine.cpp
... ... @@ -27,15 +27,17 @@ namespace othello
27 27  
28 28 bool OthelloGameEngine::performMoveForCurrentHuman(const BitPos& board_pos)
29 29 {
30   -// if (!noLegalMoves(current_playerid)){
  30 + if (!noLegalMoves(current_playerid)){
  31 + forfeitTurn(false);
31 32 if (utility::isLegalMove(m_board, current_playerid, board_pos)){
32 33 utility::placeAndFlip(m_board, current_playerid, board_pos);
33 34 switchCurrentPlayerId();
34 35 return true;
35 36 }
36 37 return false;
37   -// }
38   - switchCurrentPlayerId();
  38 + }
  39 + //forfeitTurn(true);
  40 + //switchCurrentPlayerId();
39 41 return true;
40 42 }
41 43  
... ... @@ -46,20 +48,22 @@ namespace othello
46 48  
47 49 void OthelloGameEngine::think(const std::chrono::seconds& time_limit)
48 50 {
49   - forfeitTurn(false);
50   - PlayerType type = currentPlayerType();
51   - if (type == PlayerType::Human)
52   - return;
53   -
54   - //For AI
55   - if (!noLegalMoves(current_playerid)){
56   - if (current_playerid == PlayerId::One)
57   - aiThinkAndMove(m_player_one, time_limit);
58   - else
59   - aiThinkAndMove(m_player_two, time_limit);
60   -
61   - switchCurrentPlayerId();
62   - }
  51 + PlayerType type = currentPlayerType();
  52 + if (type == PlayerType::Human)
  53 + return;
  54 +
  55 + //For AI
  56 + if (!noLegalMoves(current_playerid)){
  57 + forfeitTurn(false);
  58 + if (current_playerid == PlayerId::One)
  59 + aiThinkAndMove(m_player_one, time_limit);
  60 + else
  61 + aiThinkAndMove(m_player_two, time_limit);
  62 +
  63 + switchCurrentPlayerId();
  64 + }
  65 + //forfeitTurn(true);
  66 + //switchCurrentPlayerId();
63 67 }
64 68  
65 69 void OthelloGameEngine::forfeitTurn(bool flag)
... ...
minimax_ai.cpp 0 → 100644
  1 +#include "minimax_ai.h"
  2 +#include "basic_types.h"
  3 +
  4 +namespace othello::minimax_ai {
  5 +
  6 +MiniMaxAI::MiniMaxAI(const PlayerId &/*player_id*/):max_depth{2}
  7 + {
  8 + m_costs = {
  9 + 120, -20, 20, 5, 5, 20, -20, 120,
  10 + -20, -40, -5, -5, -5, -5, -40, -20,
  11 + 20, -5, 15, 3, 3, 15, -5, 20,
  12 + 5, -5, 3, 3, 3, 3, -5, 5,
  13 + 5, -5, 3, 3, 3, 3, -5, 5,
  14 + 20, -5, 15, 3, 3, 15, -5, 20,
  15 + -20, -40, -5, -5, -5, -5, -40, -20,
  16 + 120, -20, 20, 5, 5, 20, -20, 120,
  17 + };
  18 + }
  19 +
  20 + void MiniMaxAI::think(const BitBoard &board, const PlayerId &player_id,
  21 + const std::chrono::seconds &max_time)
  22 + {
  23 + createTree(m_minimax_tree, board, player_id, max_depth);
  24 + computeCost(*m_minimax_tree.root, player_id);
  25 + computeMiniMax(*m_minimax_tree.root, player_id, true);
  26 + // best_move = *m_minimax_tree.root;
  27 +
  28 + //compute cost fn
  29 + // run minimax
  30 +
  31 + }
  32 +
  33 + BitPos MiniMaxAI::bestMove() const
  34 + {
  35 + const auto best_move = BitPos(m_best_move.value());
  36 + m_best_move = BitPos::invalid();
  37 + return best_move;
  38 + }
  39 +
  40 + void createChildren(detail::MiniMaxNode& parent, const PlayerId& player_id,
  41 + int depth)
  42 + {
  43 + if(depth == 0)
  44 + return;
  45 +
  46 + const auto legal_moves = utility::legalMoves(parent.data.board, player_id);
  47 +
  48 + for(const auto& legal_move : legal_moves) {
  49 + BitBoard board = parent.data.board;
  50 + utility::placeAndFlip(board, player_id, legal_move);
  51 +
  52 + auto child = std::make_unique<detail::MiniMaxNode>(board, legal_move);
  53 + parent.children.push_back(std::move(child));
  54 + }
  55 +
  56 + depth--; //instead of having depth-- inside the loop condition
  57 + for(auto& child : parent.children) {
  58 + createChildren(*child, utility::opponent(player_id), depth);
  59 + }
  60 + }
  61 +
  62 + void createTree(detail::MiniMaxTree& tree, const BitBoard& board,
  63 + const PlayerId& player_id, int depth)
  64 + {
  65 + tree.root = std::make_unique<detail::MiniMaxNode>(board, BitPos::invalid());
  66 +
  67 + createChildren(*tree.root, player_id, depth);
  68 + }
  69 +
  70 + void MiniMaxAI::computeCost(detail::MiniMaxNode& parent, const PlayerId &current_playerid)
  71 + {
  72 + if (parent.children.empty()){
  73 + //make m_costs(the array of positional) weights a vector
  74 + std::vector<int> costs(m_costs.begin(), m_costs.end());
  75 +
  76 + //name the player and opponent's bitset's variables
  77 + auto m_bitset_current_player = parent.data.board[size_t(current_playerid)];
  78 + auto m_bitset_opponent_player = parent.data.board[size_t(utility::opponent(current_playerid))];
  79 +
  80 + //make the bitset variables into vectors
  81 + std::vector<bool> bitset_player;
  82 + for (size_t b=0; b<64; ++b) {
  83 + bitset_player.push_back(m_bitset_current_player[b]);
  84 + }
  85 + std::vector<bool> bitset_opponent;
  86 + for (size_t b=0; b<64; ++b) {
  87 + bitset_opponent.push_back(m_bitset_opponent_player[b]);
  88 + }
  89 +
  90 + //Use tranform reduce to perform costs operations on m_cost and the bitsets
  91 + int current_player_score = std::transform_reduce(
  92 + costs.begin(),
  93 + costs.end(),
  94 + bitset_player.begin(),
  95 + 0,
  96 + [](auto a, auto b) {return a + b;},
  97 + [](auto a, auto b) {return a * b;}
  98 + );
  99 + int opponent_player_score = std::transform_reduce(
  100 + costs.begin(),
  101 + costs.end(),
  102 + bitset_opponent.begin(),
  103 + 0,
  104 + [](auto a, auto b) {return a + b;},
  105 + [](auto a, auto b) {return a * b;}
  106 + );
  107 +
  108 + //compute the cost function and store it in the minmaxdata
  109 + parent.data.cost = current_player_score - opponent_player_score;
  110 + }
  111 + for(const auto& child : parent.children) {
  112 + computeCost(*child, current_playerid);
  113 + }
  114 + }
  115 +
  116 + void MiniMaxAI::computeMiniMax(detail::MiniMaxNode& parent, const PlayerId &current_playerid, bool isMax)
  117 + {
  118 + if (parent.children.empty())
  119 + return;
  120 +
  121 + //Check for the highest costs in Max's leaves
  122 + if (isMax){
  123 + for(const auto& child : parent.children) {
  124 + computeCost(*child, current_playerid);
  125 + auto elem = std::max_element(parent.children.begin(),
  126 + parent.children.end(),
  127 + [](const auto& a, const auto& b) {return a->data.cost > b->data.cost;}
  128 + );
  129 + // elem->data.cost;
  130 + }
  131 + }
  132 + else{
  133 + //Check for the lowest costs in Max's leaves
  134 + for(const auto& child : parent.children) {
  135 + computeCost(*child, utility::opponent(current_playerid));
  136 + auto elem = std::max_element(parent.children.begin(),
  137 + parent.children.end(),
  138 + [](const auto& a, const auto& b) {return a->data.cost > b->data.cost;}
  139 + );
  140 + // elem->data.cost;
  141 + }
  142 + }
  143 + computeMiniMax(parent, current_playerid, !isMax);
  144 + }
  145 +} //namespace othello::minimax_ai
... ...
minimax_ai.h 0 → 100644
  1 +#ifndef MINIMAX_AI_H
  2 +#define MINIMAX_AI_H
  3 +
  4 +#include <player_interface.h>
  5 +#include <basic_types.h>
  6 +#include <utility.h>
  7 +#include <iostream>
  8 +#include <vector>
  9 +#include <array>
  10 +#include <execution>
  11 +
  12 +
  13 +namespace othello::minimax_ai
  14 +{
  15 +
  16 + namespace detail {
  17 +
  18 + // Data
  19 + struct MiniMaxData {
  20 + BitBoard board;
  21 + int cost;
  22 + BitPos move;
  23 + };
  24 +
  25 + // Node
  26 + struct MiniMaxNode {
  27 + std::vector<std::unique_ptr<MiniMaxNode>> children; // children
  28 + MiniMaxData data; // board data
  29 +
  30 + MiniMaxNode( const BitBoard& board, const BitPos& move) : data{board,0,move} {}
  31 + };
  32 +
  33 + // Tree
  34 + struct MiniMaxTree {
  35 + std::unique_ptr<MiniMaxNode> root {nullptr};
  36 + };
  37 + }
  38 +
  39 + void createTree(detail::MiniMaxTree& tree, const BitBoard& board,
  40 + const PlayerId& current_playerid, int depth);
  41 +
  42 + void createChildren(detail::MiniMaxNode& parent, const PlayerId& current_playerid,
  43 + int depth);
  44 +
  45 +
  46 +
  47 +
  48 + class MiniMaxAI : public AIInterface {
  49 +
  50 + // Constructors
  51 + public:
  52 + MiniMaxAI(const PlayerId& player_id);
  53 +
  54 + // PlayerInterface interface
  55 + public:
  56 + void think(const BitBoard &board, const PlayerId &player_id,
  57 + const std::chrono::seconds &max_time) override;
  58 +
  59 + BitPos bestMove() const override;
  60 +
  61 + void computeCost(detail::MiniMaxNode& parent, const PlayerId& current_playerid);
  62 +
  63 + void computeMiniMax(detail::MiniMaxNode& parent, const PlayerId& current_playerid, bool isMax);
  64 +
  65 +
  66 + private:
  67 + mutable BitPos m_best_move;
  68 + detail::MiniMaxTree m_minimax_tree;
  69 + std::array<int, othello::detail::computeBoardSize()> m_costs;
  70 + int max_depth;
  71 + };
  72 +
  73 +} // namespace othello::minimax_ai
  74 +#endif // MINIMAX_AI_H
... ...
orangemonkey_ai.cpp
... ... @@ -27,6 +27,9 @@ namespace othello::monkey_ais
27 27 std::advance(b_iter, dist(m_engine));
28 28  
29 29 m_best_move = *b_iter;
  30 +
  31 + const auto time_start = std::chrono::steady_clock::now();
  32 +
30 33 }
31 34  
32 35 BitPos OrangeMonkeyAI::bestMove() const {
... ...