From 377e5dfdcc1be7de2de8327149423306cdc08efa Mon Sep 17 00:00:00 2001 From: Karma Riuk Date: Fri, 7 Feb 2025 18:24:57 +0100 Subject: [PATCH] implemented move ordering to improve on alpha_beta pruning --- cpp/src/main.cpp | 5 +- cpp/src/model/ais/ai.hpp | 11 ++++ cpp/src/model/ais/v3_AB_ordering.cpp | 99 ++++++++++++++++++++++++++++ cpp/src/model/pieces/pawn.cpp | 6 +- cpp/src/model/pieces/piece.cpp | 1 - cpp/src/model/utils/move.cpp | 19 ++++++ cpp/src/model/utils/move.hpp | 4 +- cpp/src/model/utils/utils.cpp | 41 ++++++------ cpp/src/model/utils/utils.hpp | 1 + 9 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 cpp/src/model/ais/v3_AB_ordering.cpp create mode 100644 cpp/src/model/utils/move.cpp diff --git a/cpp/src/main.cpp b/cpp/src/main.cpp index b6c15bc..9364384 100644 --- a/cpp/src/main.cpp +++ b/cpp/src/main.cpp @@ -24,8 +24,9 @@ int main(int argc, char* argv[]) { Board b = Board::setup_fen_position(pos); ai::v0_random p1(true, std::chrono::milliseconds(1000)); - // ai::v1_pure_minimax p2(false, std::chrono::milliseconds(150000)); - ai::v2_alpha_beta p2(false, std::chrono::milliseconds(150000)); + // ai::v1_pure_minimax p2(false, std::chrono::milliseconds(20000)); + // ai::v2_alpha_beta p2(false, std::chrono::milliseconds(20000)); + ai::v3_AB_ordering p2(false, std::chrono::milliseconds(20000)); 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 bcc869f..4e2af5b 100644 --- a/cpp/src/model/ais/ai.hpp +++ b/cpp/src/model/ais/ai.hpp @@ -54,4 +54,15 @@ namespace ai { Move _search(const Board&) override; int eval(const Board&) override; }; + + class v3_AB_ordering : public AI { + // looks two moves ahead, with alpha-beta pruning, with move ordering + int _search(const Board&, int, int, int); + + public: + v3_AB_ordering(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/v3_AB_ordering.cpp b/cpp/src/model/ais/v3_AB_ordering.cpp new file mode 100644 index 0000000..fa7b303 --- /dev/null +++ b/cpp/src/model/ais/v3_AB_ordering.cpp @@ -0,0 +1,99 @@ +#include "../pieces/piece.hpp" +#include "../utils/threadpool.hpp" +#include "../utils/utils.hpp" +#include "ai.hpp" + +#include +#include + +#define MULTITHREADED 1 + + +static int position_counter; + +Move ai::v3_AB_ordering::_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::v3_AB_ordering::_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(); + std::sort(moves.begin(), moves.end(), [&](Move& m1, Move& m2) { + return m1.score_guess(b) > m2.score_guess(b); + }); + + 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::v3_AB_ordering::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/pieces/pawn.cpp b/cpp/src/model/pieces/pawn.cpp index c877010..aeac3a7 100644 --- a/cpp/src/model/pieces/pawn.cpp +++ b/cpp/src/model/pieces/pawn.cpp @@ -19,7 +19,7 @@ std::vector pawn_moves(const Board& b, const Coords xy) { ret.push_back(Move{ xy.to_index(), left.to_index(), - (int8_t) (my_colour | piece) + (Piece) (my_colour | piece) }); else ret.push_back(Move{xy.to_index(), left.to_index()}); @@ -39,7 +39,7 @@ std::vector pawn_moves(const Board& b, const Coords xy) { ret.push_back(Move{ xy.to_index(), right.to_index(), - (int8_t) (my_colour | piece) + (Piece) (my_colour | piece) }); else ret.push_back(Move{xy.to_index(), right.to_index()}); @@ -68,7 +68,7 @@ std::vector pawn_moves(const Board& b, const Coords xy) { ret.push_back(Move{ xy.to_index(), new_xy.to_index(), - .promoting_to = (int8_t) (my_colour | piece) + (Piece) (my_colour | piece) }); else ret.push_back(Move{ diff --git a/cpp/src/model/pieces/piece.cpp b/cpp/src/model/pieces/piece.cpp index cfc44a1..10412bc 100644 --- a/cpp/src/model/pieces/piece.cpp +++ b/cpp/src/model/pieces/piece.cpp @@ -9,7 +9,6 @@ keep_only_blocking(const std::vector candidates, const Board& board) { if (candidates.size() == 0) return {}; - Colour my_colour = board.colour_at(candidates[0].source_square); std::vector ret; for (Move move : candidates) { Board board_after_move = board.make_move(move, false); diff --git a/cpp/src/model/utils/move.cpp b/cpp/src/model/utils/move.cpp new file mode 100644 index 0000000..118e2f2 --- /dev/null +++ b/cpp/src/model/utils/move.cpp @@ -0,0 +1,19 @@ +#include "move.hpp" + +#include "../board/board.hpp" +#include "utils.hpp" + +int Move::score_guess(const Board& b) const { + int ret = 0; + + Piece me_piece = b.piece_at(source_square); + Piece captured_piece = b.piece_at(target_square); + + if (captured_piece != Piece::None) + ret += 10 * piece_value(captured_piece) - piece_value(me_piece); + + if (promoting_to != Piece::None) + ret += piece_value(promoting_to); + + return ret; +} diff --git a/cpp/src/model/utils/move.hpp b/cpp/src/model/utils/move.hpp index e893176..ad2fe8d 100644 --- a/cpp/src/model/utils/move.hpp +++ b/cpp/src/model/utils/move.hpp @@ -11,7 +11,9 @@ struct Move { int8_t source_square; int8_t target_square; - int8_t promoting_to = Piece::None; + Piece promoting_to = Piece::None; + + int score_guess(const Board&) const; std::string to_string() const { std::stringstream ss; diff --git a/cpp/src/model/utils/utils.cpp b/cpp/src/model/utils/utils.cpp index ba03b73..9c45641 100644 --- a/cpp/src/model/utils/utils.cpp +++ b/cpp/src/model/utils/utils.cpp @@ -9,30 +9,27 @@ std::vector to_target_square(std::vector moves) { return ret; } +int piece_value(Piece p) { + switch (p) { + case Piece::Pawn: + return PawnValue; + case Piece::Knigt: + return KnightValue; + case Piece::Bishop: + return BishopValue; + case Piece::Rook: + return RookValue; + case Piece::Queen: + return QueenValue; + default: + return 0; + } +} + int count_material(const Board& b, int8_t colour) { int ret = 0; - for (int i = 0; i < 64; i++) { + 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; - } - } + ret += piece_value(b.piece_at(i)); return ret; } diff --git a/cpp/src/model/utils/utils.hpp b/cpp/src/model/utils/utils.hpp index b01707b..4602d55 100644 --- a/cpp/src/model/utils/utils.hpp +++ b/cpp/src/model/utils/utils.hpp @@ -8,6 +8,7 @@ std::vector to_target_square(std::vector); int count_material(const Board&, int8_t); +int piece_value(Piece); const int INFINITY = std::numeric_limits::max();