stellar

Stellar - UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

commit ac4acc635c69b6c2189acbbdf7521dea23fb2f0b
parent 817c22d36e7eaea29025ea4014b58eb648108687
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Thu, 31 Aug 2023 23:26:31 +0200

Passed, doubled, isolated pawn evaluation

Diffstat:
MCMakeLists.txt | 2+-
Msrc/engine/CMakeLists.txt | 1+
Msrc/engine/engine.cpp | 32++++----------------------------
Asrc/engine/evaluate.cpp | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/engine/evaluate.hpp | 12++++++++++++
Msrc/utils/CMakeLists.txt | 2+-
Asrc/utils/utils.cpp | 16++++++++++++++++
Msrc/utils/utils.hpp | 2++
8 files changed, 177 insertions(+), 30 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stellar - VERSION 0.0.29 + VERSION 0.0.30 DESCRIPTION "Chess engine written in C" HOMEPAGE_URL https://git.dimitrijedobrota.com/stellar.git LANGUAGES C CXX diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(engine + evaluate.cpp engine.cpp uci.cpp ) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp @@ -6,6 +6,7 @@ #include "attack.hpp" #include "board.hpp" +#include "evaluate.hpp" #include "move.hpp" #include "movelist.hpp" #include "piece.hpp" @@ -156,31 +157,6 @@ std::vector<int> move_list_sort(MoveList &list, const Move best) { return index; } -int16_t evaluate(const Board &board) { - Color side = board.get_side(); - Color sideOther = (side == Color::BLACK) ? Color::WHITE : Color::BLACK; - - U64 occupancy = board.get_bitboard_color(side); - uint8_t square_i; - - int16_t score = 0; - for (const piece::Type type : piece::TypeIter()) { - U64 bitboard = board.get_bitboard_piece(type); - bitboard_for_each_bit(square_i, bitboard) { - Square square = static_cast<Square>(square_i); - if (bit_get(occupancy, square_i)) { - score += piece::score(type); - score += piece::score(type, side, square); - } else { - score -= piece::score(type); - score -= piece::score(type, sideOther, square); - } - } - } - - return score; -} - int stats_move_make(Board &copy, const Move move) { copy = board; if (!move.make(board)) { @@ -228,7 +204,7 @@ int16_t quiescence(int16_t alpha, int16_t beta) { pv_length[ply] = ply; nodes++; - int score = evaluate(board); + int score = evaluate::score_position(board); if (ply > MAX_PLY - 1) return score; if (score >= beta) return beta; if (score > alpha) alpha = score; @@ -285,11 +261,11 @@ int16_t negamax(int16_t alpha, int16_t beta, uint8_t depth, bool null) { if (alpha < -MATE_VALUE) alpha = -MATE_VALUE; if (beta > MATE_VALUE - 1) beta = MATE_VALUE - 1; if (alpha >= beta) return alpha; - // if (ply > MAX_PLY - 1) return evaluate(board); + // if (ply > MAX_PLY - 1) return evaluate::score_position(board); if (!pv_node && !isCheck) { static constexpr const U32 score_pawn = piece::score(piece::Type::PAWN); - int16_t staticEval = evaluate(board); + int16_t staticEval = evaluate::score_position(board); // evaluation pruning if (depth < 3 && abs(beta - 1) > -MATE_VALUE + 100) { diff --git a/src/engine/evaluate.cpp b/src/engine/evaluate.cpp @@ -0,0 +1,140 @@ +#include "evaluate.hpp" +#include "utils.hpp" +#include <array> + +namespace evaluate { + +typedef std::array<U64, 8> mask_fr_array; +inline constexpr const mask_fr_array mask_rank = []() constexpr -> mask_fr_array { + mask_fr_array mask_rank; + U64 mask = 0xFF; + for (uint8_t rank = 0; rank < 8; rank++) { + mask_rank[rank] = mask; + mask = nortOne(mask); + } + return mask_rank; +}(); + +inline constexpr const mask_fr_array mask_file = []() constexpr -> mask_fr_array { + mask_fr_array mask_file; + U64 mask = 0x0101010101010101; + for (uint8_t file = 0; file < 8; file++) { + mask_file[file] = mask; + mask = eastOne(mask); + } + return mask_file; +}(); + +inline constexpr const mask_fr_array mask_isolated = []() constexpr -> mask_fr_array { + mask_fr_array mask_isolated; + + mask_isolated[0] = 0x0202020202020202; + + U64 mask = 0x0505050505050505; + for (uint8_t file = 1; file < 8; file++) { + mask_isolated[file] = mask; + mask = eastOne(mask); + } + + return mask_isolated; +}(); + +typedef std::array<U64, 64> mask_passed_array; +inline constexpr const mask_passed_array mask_passed_white = []() constexpr -> mask_passed_array { + mask_passed_array mask_passed_white; + for (uint8_t file = 0; file < 8; file++) { + U64 mask = mask_file[file] | mask_isolated[file]; + for (uint8_t rank = 0; rank < 8; rank++) { + mask = nortOne(mask); + mask_passed_white[rank * 8 + file] = mask; + } + } + return mask_passed_white; +}(); + +inline constexpr const mask_passed_array mask_passed_black = []() constexpr -> mask_passed_array { + mask_passed_array mask_passed_black; + for (uint8_t file = 0; file < 8; file++) { + U64 mask = mask_file[file] | mask_isolated[file]; + for (int8_t rank = 7; rank >= 0; rank--) { + mask = soutOne(mask); + mask_passed_black[rank * 8 + file] = mask; + } + } + return mask_passed_black; +}(); + +inline constexpr uint8_t get_file(uint8_t square) { return square & 0x07; } +inline constexpr uint8_t get_rank(uint8_t square) { return square >> 3; } + +inline constexpr const int8_t penalty_pawn_double = -10; +inline constexpr const int8_t penalty_pawn_isolated = -10; + +inline constexpr const std::array<int16_t, 8> bonus_pawn_passed = {0, 10, 30, 50, 75, 100, 150, 200}; + +using piece::Type::PAWN; + +int16_t score_position(const Board &board) { + uint8_t square_i; + int16_t score = 0; + + // Score all White pieces except pawns + for (const piece::Type type : ++piece::TypeIter()) { + U64 bitboard = board.get_bitboard_piece(type, Color::WHITE); + bitboard_for_each_bit(square_i, bitboard) { + Square square = static_cast<Square>(square_i); + score += piece::score(type); + score += piece::score(type, Color::WHITE, square); + } + } + + // Score all Black pieces except pawns + for (const piece::Type type : ++piece::TypeIter()) { + U64 bitboard = board.get_bitboard_piece(type, Color::BLACK); + bitboard_for_each_bit(square_i, bitboard) { + Square square = static_cast<Square>(square_i); + score -= piece::score(type); + score -= piece::score(type, Color::BLACK, square); + } + } + + U64 pawns = board.get_bitboard_piece(PAWN); + U64 pawnsW = board.get_bitboard_piece(PAWN, Color::WHITE); + U64 pawnsB = board.get_bitboard_piece(PAWN, Color::BLACK); + + for (uint8_t file = 0; file < 8; file++) { + // check doubled and isolated pawns + U64 pawns_file = pawns & mask_file[file]; + + uint8_t pawnsW_count = bit_count(pawns_file & pawnsW); + if (pawnsW_count > 1) score += (pawnsW_count - 1) * penalty_pawn_double; + if (!(mask_isolated[file] & pawnsW)) score += pawnsW_count * penalty_pawn_isolated; + + uint8_t pawnsB_count = bit_count(pawns_file & pawnsB); + if (pawnsB_count > 1) score -= (pawnsB_count - 1) * penalty_pawn_double; + if (!(mask_isolated[file] & pawnsB)) score -= pawnsB_count * penalty_pawn_isolated; + } + + // Score White pawns, bonus for passed + bitboard_for_each_bit(square_i, pawnsW) { + Square square = static_cast<Square>(square_i); + score += piece::score(PAWN); + score += piece::score(PAWN, Color::WHITE, square); + if (!(pawnsB & mask_passed_white[square_i])) score += bonus_pawn_passed[get_rank(square_i)]; + } + + // previous bitboard_for_each_bit consumed this mask + pawnsW = board.get_bitboard_piece(PAWN, Color::WHITE); + + // Score White pawns, bonus for passed + bitboard_for_each_bit(square_i, pawnsB) { + Square square = static_cast<Square>(square_i); + score -= piece::score(PAWN); + score -= piece::score(PAWN, Color::BLACK, square); + if (!(pawnsW & mask_passed_black[square_i])) score -= bonus_pawn_passed[7 - get_rank(square_i)]; + } + + return board.get_side() == Color::WHITE ? score : -score; +} + +} // namespace evaluate diff --git a/src/engine/evaluate.hpp b/src/engine/evaluate.hpp @@ -0,0 +1,12 @@ +#ifndef STELLAR_EVALUATE_H +#define STELLAR_EVALUATE_H + +#include "board.hpp" + +namespace evaluate { + +int16_t score_position(const Board &board); + +} + +#endif diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt @@ -1,3 +1,3 @@ -add_library(utils INTERFACE) +add_library(utils utils.cpp) target_include_directories(utils INTERFACE .) stellar_target_precompile_headers(attack INTERFACE "utils.hpp") diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp @@ -0,0 +1,16 @@ +#include "utils.hpp" +#include <iostream> + +void bitboard_print(U64 bitboard) { + for (int rank = 0; rank < 8; rank++) { + for (int file = 0; file < 8; file++) { + uint8_t square = (7 - rank) * 8 + file; + if (!file) printf(" %d ", 8 - rank); + std::cout << bit_get(bitboard, square) << " "; + } + std::cout << "\n"; + } + + std::cout << "\n A B C D E F G H\n\n"; + std::cout << " Bitboard: " << std::hex << bitboard << std::dec; +} diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp @@ -108,6 +108,8 @@ constexpr uint8_t bit_lsb_index(U64 bitboard) { #define bitboard_for_each_bit(var, bb) \ for (var = bit_lsb_index(bb); bb; bit_lsb_pop(bb), var = bit_lsb_index(bb)) +void bitboard_print(U64 bitboard); + // board moving inline constexpr const U64 universe = C64(0xffffffffffffffff); inline constexpr const U64 notAFile = C64(0xfefefefefefefefe);