From 62a35ecafdf81d2d6462349d2e64ad35bb79458f Mon Sep 17 00:00:00 2001 From: Karma Riuk Date: Sun, 16 Feb 2025 10:12:06 +0100 Subject: [PATCH] made ai better at endgames (it's just a start) --- src/main.cpp | 18 +++++----- src/model/ais/ai.hpp | 11 ++++++ src/model/ais/v5_better_endgames.cpp | 52 ++++++++++++++++++++++++++++ src/model/board/board.hpp | 2 +- src/model/utils/utils.cpp | 7 ++-- src/model/utils/utils.hpp | 2 +- 6 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 src/model/ais/v5_better_endgames.cpp diff --git a/src/main.cpp b/src/main.cpp index f2d798c..7a1aeab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,24 +13,26 @@ int main(int argc, char* argv[]) { // std::string pos = // "r2qkb1r/2p1pppp/p1n1b3/1p6/B2P4/2P1P3/P4PPP/R1BQK1NR w KQkq - 0 9 "; - // std::string pos = "8/6K1/5P2/8/1k6/8/8/8 w - - 0 1"; + std::string pos = "3r4/3r4/3k4/8/3K4/8/8/8 w - - 0 1"; // pos for ai timing< - std::string pos = - "r3k2r/p1ppqpb1/Bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPB1PPP/R3K2R b KQkq - 0 3"; + // std::string pos = + // "r3k2r/p1ppqpb1/Bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPB1PPP/R3K2R b KQkq - 0 + // 3"; 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(20000)); // ai::v2_alpha_beta p2(false, std::chrono::milliseconds(20000)); - ai::v3_AB_ordering p2(false, std::chrono::milliseconds(20000)); + // ai::v3_AB_ordering p2(false, std::chrono::milliseconds(20000)); // ai::v4_search_captures p2(false, std::chrono::milliseconds(20000)); + ai::v5_better_endgame p2(false, std::chrono::milliseconds(20000)); - // GUI gui; - NoOpView gui; - AIvsAIController manual(b, gui, p1, p2); - // HumanVsAIController manual(b, gui, p2); + GUI gui; + // NoOpView gui; + // AIvsAIController manual(b, gui, p1, p2); + HumanVsAIController manual(b, gui, p2); Controller& controller = manual; diff --git a/src/model/ais/ai.hpp b/src/model/ais/ai.hpp index 40ae39a..46298e9 100644 --- a/src/model/ais/ai.hpp +++ b/src/model/ais/ai.hpp @@ -76,4 +76,15 @@ namespace ai { v4_search_captures(bool w, std::chrono::milliseconds tt) : v3_AB_ordering(w, tt) {} }; + + class v5_better_endgame : public v4_search_captures { + // same as v4, but with a better evaluation function, that forces the + // king towards the corner of the board for endgames + + public: + v5_better_endgame(bool w, std::chrono::milliseconds tt) + : v4_search_captures(w, tt) {} + + int eval(const Board&) override; + }; } // namespace ai diff --git a/src/model/ais/v5_better_endgames.cpp b/src/model/ais/v5_better_endgames.cpp new file mode 100644 index 0000000..f8e0887 --- /dev/null +++ b/src/model/ais/v5_better_endgames.cpp @@ -0,0 +1,52 @@ +#include "../pieces/piece.hpp" +#include "../utils/utils.hpp" +#include "ai.hpp" + +#include + +static int force_king_to_corner( + int8_t attacking_king, int8_t defending_king, float endgame_weight +) { + int eval = 0; + Coords def_xy = Coords::from_index(defending_king); + + Coords def_dist_to_center{ + std::max(3 - def_xy.x, def_xy.x - 4), + std::max(3 - def_xy.y, def_xy.y - 4) + }; + eval += def_dist_to_center.x + def_dist_to_center.y; + + // make attacking king go closer to defending king to cut off escape routes + Coords attack_xy = Coords::from_index(attacking_king); + Coords dist_between_kings{ + std::abs(attack_xy.x - def_xy.x), + std::abs(attack_xy.y - def_xy.y) + }; + int distance = dist_between_kings.x + dist_between_kings.y; + eval += 14 - distance; + + return (int) (eval * 10 * endgame_weight); +} + +static float endgame_phase_weight(int material_count_no_pawns) { + static int endgame_material_start = + RookValue * 2 + BishopValue + KnightValue; + + float multiplier = 1.f / endgame_material_start; + return 1.f - std::min(1.f, material_count_no_pawns * multiplier); +} + +int ai::v5_better_endgame::eval(const Board& b) { + int old_eval = v4_search_captures::eval(b); + Colour attacking_colour = b.white_to_play ? White : Black; + Colour defending_colour = b.white_to_play ? Black : White; + return old_eval + + force_king_to_corner( + b.get_king_of(attacking_colour), + b.get_king_of(defending_colour), + endgame_phase_weight( + count_material(b, attacking_colour, false) + + count_material(b, defending_colour, false) + ) + ); +} diff --git a/src/model/board/board.hpp b/src/model/board/board.hpp index 96d5392..6c3d2db 100644 --- a/src/model/board/board.hpp +++ b/src/model/board/board.hpp @@ -8,7 +8,6 @@ struct Board { private: - int8_t get_king_of(int8_t) const; bool _no_legal_moves_for(Colour) const; bool _is_check_for(Colour) const; bool nlm = false, check = false; @@ -25,6 +24,7 @@ struct Board { static Board setup_fen_position(std::string fen); + int8_t get_king_of(int8_t) const; Board skip_turn() const; Board make_move(Move, bool = true) const; std::string to_fen() const; diff --git a/src/model/utils/utils.cpp b/src/model/utils/utils.cpp index 9c45641..53ceb78 100644 --- a/src/model/utils/utils.cpp +++ b/src/model/utils/utils.cpp @@ -26,10 +26,13 @@ int piece_value(Piece p) { } } -int count_material(const Board& b, int8_t colour) { +int count_material(const Board& b, int8_t colour, bool count_pawns) { int ret = 0; - for (int i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) { + if (b.piece_at(i) == Pawn && !count_pawns) + continue; if (b.colour_at(i) == colour) ret += piece_value(b.piece_at(i)); + } return ret; } diff --git a/src/model/utils/utils.hpp b/src/model/utils/utils.hpp index 4602d55..3978421 100644 --- a/src/model/utils/utils.hpp +++ b/src/model/utils/utils.hpp @@ -7,7 +7,7 @@ #include std::vector to_target_square(std::vector); -int count_material(const Board&, int8_t); +int count_material(const Board&, int8_t, bool = true); int piece_value(Piece); const int INFINITY = std::numeric_limits::max();