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:
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 ©, 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);