implemented move ordering to improve on alpha_beta

pruning
This commit is contained in:
Karma Riuk 2025-02-07 18:24:57 +01:00
parent 10de9828be
commit 377e5dfdcc
9 changed files with 158 additions and 29 deletions

View File

@ -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);

View File

@ -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

View File

@ -0,0 +1,99 @@
#include "../pieces/piece.hpp"
#include "../utils/threadpool.hpp"
#include "../utils/utils.hpp"
#include "ai.hpp"
#include <algorithm>
#include <map>
#define MULTITHREADED 1
static int position_counter;
Move ai::v3_AB_ordering::_search(const Board& b) {
position_counter = 0;
std::vector<Move> 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<Move, std::future<int>> 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<Move> 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;
}

View File

@ -19,7 +19,7 @@ std::vector<Move> 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<Move> 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<Move> 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{

View File

@ -9,7 +9,6 @@ keep_only_blocking(const std::vector<Move> candidates, const Board& board) {
if (candidates.size() == 0)
return {};
Colour my_colour = board.colour_at(candidates[0].source_square);
std::vector<Move> ret;
for (Move move : candidates) {
Board board_after_move = board.make_move(move, false);

View File

@ -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;
}

View File

@ -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;

View File

@ -9,30 +9,27 @@ std::vector<int8_t> to_target_square(std::vector<Move> 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;
}

View File

@ -8,6 +8,7 @@
std::vector<int8_t> to_target_square(std::vector<Move>);
int count_material(const Board&, int8_t);
int piece_value(Piece);
const int INFINITY = std::numeric_limits<int>::max();