From 10de9828be9c78a23cf0966dbedb5422d19385cc Mon Sep 17 00:00:00 2001 From: Karma Riuk Date: Fri, 7 Feb 2025 17:49:08 +0100 Subject: [PATCH] implemented alpha beta pruning --- cpp/src/main.cpp | 6 +- cpp/src/model/ais/ai.hpp | 11 ++++ cpp/src/model/ais/v1_pure_minimax.cpp | 39 +---------- cpp/src/model/ais/v2_alpha_beta.cpp | 93 +++++++++++++++++++++++++++ cpp/src/model/utils/utils.cpp | 30 +++++++++ cpp/src/model/utils/utils.hpp | 10 +++ 6 files changed, 149 insertions(+), 40 deletions(-) create mode 100644 cpp/src/model/ais/v2_alpha_beta.cpp diff --git a/cpp/src/main.cpp b/cpp/src/main.cpp index 071e211..b6c15bc 100644 --- a/cpp/src/main.cpp +++ b/cpp/src/main.cpp @@ -2,6 +2,7 @@ #include "controller/controller.hpp" #include "controller/human_vs_ai.hpp" #include "controller/manual.hpp" +#include "model/ais/ai.hpp" #include "model/perft/perft.hpp" #include "view/gui.hpp" #include "view/noop.hpp" @@ -23,9 +24,8 @@ int main(int argc, char* argv[]) { Board b = Board::setup_fen_position(pos); ai::v0_random p1(true, std::chrono::milliseconds(1000)); - // ai::v1_simple p1(false, std::chrono::milliseconds(100000)); - ai::v1_pure_minimax p2(false, std::chrono::milliseconds(150000)); - // ai::v0_random p2(false, std::chrono::milliseconds(10000)); + // ai::v1_pure_minimax p2(false, std::chrono::milliseconds(150000)); + ai::v2_alpha_beta p2(false, std::chrono::milliseconds(150000)); NoOpView gui; AIvsAIController manual(b, gui, p1, p2); diff --git a/cpp/src/model/ais/ai.hpp b/cpp/src/model/ais/ai.hpp index 25503d5..bcc869f 100644 --- a/cpp/src/model/ais/ai.hpp +++ b/cpp/src/model/ais/ai.hpp @@ -43,4 +43,15 @@ namespace ai { Move _search(const Board&) override; int eval(const Board&) override; }; + + class v2_alpha_beta : public AI { + // looks two moves ahead, with alpha-beta pruning (no move ordering) + int _search(const Board&, int, int, int); + + public: + v2_alpha_beta(bool w, std::chrono::milliseconds tt): AI(w, tt) {} + + Move _search(const Board&) override; + int eval(const Board&) override; + }; } // namespace ai diff --git a/cpp/src/model/ais/v1_pure_minimax.cpp b/cpp/src/model/ais/v1_pure_minimax.cpp index 550ad9d..78832e1 100644 --- a/cpp/src/model/ais/v1_pure_minimax.cpp +++ b/cpp/src/model/ais/v1_pure_minimax.cpp @@ -1,14 +1,13 @@ #include "../pieces/piece.hpp" #include "../utils/threadpool.hpp" +#include "../utils/utils.hpp" #include "ai.hpp" #include #define MULTITHREADED 1 -static int INFINITY = std::numeric_limits::max(); - -int position_counter; +static int position_counter; Move ai::v1_pure_minimax::_search(const Board& b) { position_counter = 0; @@ -79,40 +78,6 @@ int ai::v1_pure_minimax::_search(const Board& b, int depth) { return best_evaluation; } -static int PawnValue = 100; -static int KnightValue = 300; -static int BishopValue = 320; -static int RookValue = 500; -static int QueenValue = 900; - -int count_material(const Board& b, int8_t colour) { - int ret = 0; - for (int i = 0; i < 64; i++) { - if (b.colour_at(i) == colour) - switch (b.piece_at(i)) { - case Piece::Pawn: - ret += PawnValue; - break; - case Piece::Knigt: - ret += KnightValue; - break; - case Piece::Bishop: - ret += BishopValue; - break; - case Piece::Rook: - ret += RookValue; - break; - case Piece::Queen: - ret += QueenValue; - break; - case Piece::King: - case Piece::None: - break; - } - } - return ret; -} - int ai::v1_pure_minimax::eval(const Board& b) { position_counter++; int white_eval = count_material(b, Colour::White); diff --git a/cpp/src/model/ais/v2_alpha_beta.cpp b/cpp/src/model/ais/v2_alpha_beta.cpp new file mode 100644 index 0000000..3a39474 --- /dev/null +++ b/cpp/src/model/ais/v2_alpha_beta.cpp @@ -0,0 +1,93 @@ +#include "../pieces/piece.hpp" +#include "../utils/threadpool.hpp" +#include "../utils/utils.hpp" +#include "ai.hpp" + +#include + +#define MULTITHREADED 1 + + +static int position_counter; + +Move ai::v2_alpha_beta::_search(const Board& b) { + position_counter = 0; + std::vector moves = b.all_legal_moves(); + + Move best_move; + int best_eval = -INFINITY; +#if MULTITHREADED + ThreadPool pool(std::thread::hardware_concurrency()); + + std::cout << "Have to look at " << moves.size() << " moves" << std::endl; + + std::map> futures; + for (const Move& move : moves) { + Board tmp_board = b.make_move(move); + futures.insert({move, pool.enqueue([&, tmp_board]() { + return _search(tmp_board, 3, -INFINITY, INFINITY); + })}); + } + + int counter = 0; + for (auto& [move, future] : futures) { + int eval = future.get(); + counter++; + if (!am_white) + eval *= -1; + if (eval > best_eval) { + best_eval = eval; + best_move = move; + } + } +#else + for (const Move& move : moves) { + Board tmp_board = b.make_move(move); + std::cout << "Looking at " << move << std::endl; + int eval = _search(tmp_board, 3); + if (!am_white) + eval *= -1; + if (eval > best_eval) { + best_eval = eval; + best_move = move; + } + } +#endif + std::cout << "Looked at " << position_counter << " positions" << std::endl; + return best_move; +} + +int ai::v2_alpha_beta::_search(const Board& b, int depth, int alpha, int beta) { + if (depth == 0 || stop_computation) + return eval(b); + + if (b.no_legal_moves()) { + if (b.is_check()) + return -INFINITY; + return 0; + } + + std::vector moves = b.all_legal_moves(); + + Move best_move; + for (const Move& move : moves) { + Board tmp_board = b.make_move(move); + int tmp_eval = -_search(tmp_board, depth - 1, -beta, -alpha); + if (tmp_eval >= beta) + return beta; + alpha = std::max(alpha, tmp_eval); + } + return alpha; +} + +int ai::v2_alpha_beta::eval(const Board& b) { + position_counter++; + int white_eval = count_material(b, Colour::White); + int black_eval = count_material(b, Colour::Black); + + int evaluation = white_eval - black_eval; + + int perspective = b.white_to_play ? 1 : -1; + + return perspective * evaluation; +} diff --git a/cpp/src/model/utils/utils.cpp b/cpp/src/model/utils/utils.cpp index 830ff78..ba03b73 100644 --- a/cpp/src/model/utils/utils.cpp +++ b/cpp/src/model/utils/utils.cpp @@ -1,8 +1,38 @@ #include "utils.hpp" +#include "../board/board.hpp" + std::vector to_target_square(std::vector moves) { std::vector ret; for (Move move : moves) ret.push_back(move.target_square); return ret; } + +int count_material(const Board& b, int8_t colour) { + int ret = 0; + for (int i = 0; i < 64; i++) { + if (b.colour_at(i) == colour) + switch (b.piece_at(i)) { + case Piece::Pawn: + ret += PawnValue; + break; + case Piece::Knigt: + ret += KnightValue; + break; + case Piece::Bishop: + ret += BishopValue; + break; + case Piece::Rook: + ret += RookValue; + break; + case Piece::Queen: + ret += QueenValue; + break; + case Piece::King: + case Piece::None: + break; + } + } + return ret; +} diff --git a/cpp/src/model/utils/utils.hpp b/cpp/src/model/utils/utils.hpp index ec5259d..b01707b 100644 --- a/cpp/src/model/utils/utils.hpp +++ b/cpp/src/model/utils/utils.hpp @@ -3,6 +3,16 @@ #include "move.hpp" #include +#include #include std::vector to_target_square(std::vector); +int count_material(const Board&, int8_t); + +const int INFINITY = std::numeric_limits::max(); + +const int PawnValue = 100; +const int KnightValue = 300; +const int BishopValue = 320; +const int RookValue = 500; +const int QueenValue = 900;