diff --git a/.gitignore b/.gitignore
index 6192df9..93c3eb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ build/*
!build/makefile-clang
*~
.vscode/*
+.idea/*
+cmake-build-debug/*
diff --git a/build/makefile b/build/makefile
index 3e48d70..05d19ca 100644
--- a/build/makefile
+++ b/build/makefile
@@ -1,5 +1,5 @@
EXE = seer
-SRC = ../src/seer.cc ../syzygy/tbprobe.c
+SRC = ../src/seer.cc ../syzygy/tbprobe.c ../src/search/*.cc ../src/chess/*.cc ../src/engine/*.cc
CC = g++
EVALFILE = weights/0xddf7a131.bin
@@ -12,7 +12,7 @@ INCBIN = ../incbin
SYZYGY = ../syzygy
CXXFLAGS += -std=c++$(CXXSTANDARD)
-CXXFLAGS += -O3 -g -DNDEBUG -march=native -mtune=native -fopenmp -flto
+CXXFLAGS += -O3 -g -DNDEBUG -march=native -mtune=native -fopenmp -flto -fwhole-program
CXXFLAGS += -Wall -Wextra
CXXFLAGS += -fconstexpr-ops-limit=$(OPSLIMIT)
CXXFLAGS += -I$(INCLUDE) -I$(INCBIN) -I$(SYZYGY)
diff --git a/build/makefile-clang b/build/makefile-clang
index a5e8765..4f31891 100644
--- a/build/makefile-clang
+++ b/build/makefile-clang
@@ -1,5 +1,5 @@
EXE = seer
-SRC = ../src/seer.cc ../syzygy/tbprobe.c
+SRC = ../src/seer.cc ../syzygy/tbprobe.c ../src/search/*.cc ../src/chess/*.cc ../src/engine/*.cc
CC = clang++
EVALFILE = weights/0xddf7a131.bin
@@ -12,7 +12,7 @@ INCBIN = ../incbin
SYZYGY = ../syzygy
CXXFLAGS += -std=c++$(CXXSTANDARD)
-CXXFLAGS += -O3 -g -DNDEBUG -march=native -mtune=native -fopenmp
+CXXFLAGS += -O3 -g -DNDEBUG -march=native -mtune=native -fopenmp -flto
CXXFLAGS += -Wall -Wextra -Wpedantic
CXXFLAGS += -fconstexpr-steps=$(OPSLIMIT)
CXXFLAGS += -I$(INCLUDE) -I$(INCBIN) -I$(SYZYGY)
diff --git a/include/board.h b/include/board.h
deleted file mode 100644
index cc94ed2..0000000
--- a/include/board.h
+++ /dev/null
@@ -1,1042 +0,0 @@
-/*
- Seer is a UCI chess engine by Connor McMonigle
- Copyright (C) 2021 Connor McMonigle
- Seer is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Seer is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace chess {
-
-template
-struct move_generator_mode {
- static constexpr bool noisy = noisy_value;
- static constexpr bool check = check_value;
- static constexpr bool quiet = quiet_value;
-};
-
-namespace generation_mode {
-
-using noisy_and_check = move_generator_mode;
-using quiet_and_check = move_generator_mode;
-using noisy = move_generator_mode;
-using check = move_generator_mode;
-using quiet = move_generator_mode;
-using all = move_generator_mode;
-
-} // namespace generation_mode
-
-template
-constexpr T material_value(const piece_type& pt) {
- constexpr std::array values = {100, 300, 300, 450, 900, std::numeric_limits::max()};
- return values[static_cast(pt)];
-}
-
-template
-constexpr T phase_value(const piece_type& pt) {
- constexpr std::array values = {0, 1, 1, 2, 4, 0};
- return values[static_cast(pt)];
-}
-
-struct move_generator_info {
- square_set occ;
- square_set last_rank;
- square_set checkers;
- square_set checker_rays;
- square_set pinned;
- square_set king_danger;
- square_set king_diagonal;
- square_set king_horizontal;
-};
-
-struct board {
- sided_manifest man_{};
- sided_latent lat_{};
-
- bool turn() const { return lat_.ply_count % 2 == 0; }
- bool is_rule50_draw() const { return lat_.half_clock >= 100; }
-
- zobrist::hash_type hash() const { return man_.hash() ^ lat_.hash(); }
-
- template
- std::tuple least_valuable_attacker(const square& tgt, const square_set& ignore) const {
- const auto p_mask = pawn_attack_tbl>.look_up(tgt);
- const auto p_attackers = p_mask & man_.us().pawn() & ~ignore;
- if (p_attackers.any()) { return std::tuple(piece_type::pawn, *p_attackers.begin()); }
-
- const auto n_mask = knight_attack_tbl.look_up(tgt);
- const auto n_attackers = n_mask & man_.us().knight() & ~ignore;
- if (n_attackers.any()) { return std::tuple(piece_type::knight, *n_attackers.begin()); }
-
- const square_set occ = (man_.white.all() | man_.black.all()) & ~ignore;
-
- const auto b_mask = bishop_attack_tbl.look_up(tgt, occ);
- const auto b_attackers = b_mask & man_.us().bishop() & ~ignore;
- if (b_attackers.any()) { return std::tuple(piece_type::bishop, *b_attackers.begin()); }
-
- const auto r_mask = rook_attack_tbl.look_up(tgt, occ);
- const auto r_attackers = r_mask & man_.us().rook() & ~ignore;
- if (r_attackers.any()) { return std::tuple(piece_type::rook, *r_attackers.begin()); }
-
- const auto q_mask = b_mask | r_mask;
- const auto q_attackers = q_mask & man_.us().queen() & ~ignore;
- if (q_attackers.any()) { return std::tuple(piece_type::queen, *q_attackers.begin()); }
-
- const auto k_mask = king_attack_tbl.look_up(tgt);
- const auto k_attackers = k_mask & man_.us().king() & ~ignore;
- if (k_attackers.any()) { return std::tuple(piece_type::king, *k_attackers.begin()); }
-
- return std::tuple(piece_type::pawn, tgt);
- }
-
- template
- std::tuple checkers(const square_set& occ) const {
- const auto b_check_mask = bishop_attack_tbl.look_up(man_.us().king().item(), occ);
- const auto r_check_mask = rook_attack_tbl.look_up(man_.us().king().item(), occ);
- const auto n_check_mask = knight_attack_tbl.look_up(man_.us().king().item());
- const auto p_check_mask = pawn_attack_tbl.look_up(man_.us().king().item());
- const auto q_check_mask = b_check_mask | r_check_mask;
-
- const auto b_checkers = (b_check_mask & (man_.them().bishop() | man_.them().queen()));
- const auto r_checkers = (r_check_mask & (man_.them().rook() | man_.them().queen()));
-
- square_set checker_rays_{};
- for (const auto sq : b_checkers) { checker_rays_ |= bishop_attack_tbl.look_up(sq, occ) & b_check_mask; }
- for (const auto sq : r_checkers) { checker_rays_ |= rook_attack_tbl.look_up(sq, occ) & r_check_mask; }
-
- const auto checkers_ = (b_check_mask & man_.them().bishop() & occ) | (r_check_mask & man_.them().rook() & occ) |
- (n_check_mask & man_.them().knight() & occ) | (p_check_mask & man_.them().pawn() & occ) |
- (q_check_mask & man_.them().queen() & occ);
- return std::tuple(checkers_, checker_rays_);
- }
-
- template
- square_set threat_mask() const {
- // idea from koivisto
- const square_set occ = man_.white.all() | man_.black.all();
-
- square_set threats{};
- square_set vulnerable = man_.them().all();
-
- vulnerable &= ~man_.them().pawn();
- square_set pawn_attacks{};
- for (const auto sq : man_.us().pawn()) { pawn_attacks |= pawn_attack_tbl.look_up(sq); }
- threats |= pawn_attacks & vulnerable;
-
- vulnerable &= ~(man_.them().knight() | man_.them().bishop());
- square_set minor_attacks{};
- for (const auto sq : man_.us().knight()) { minor_attacks |= knight_attack_tbl.look_up(sq); }
- for (const auto sq : man_.us().bishop()) { minor_attacks |= bishop_attack_tbl.look_up(sq, occ); }
- threats |= minor_attacks & vulnerable;
-
- vulnerable &= ~man_.them().rook();
- square_set rook_attacks{};
- for (const auto sq : man_.us().rook()) { rook_attacks |= rook_attack_tbl.look_up(sq, occ); }
- threats |= rook_attacks & vulnerable;
-
- return threats;
- }
-
- template
- bool creates_threat_(const move& mv) const {
- const square_set occ = man_.white.all() | man_.black.all();
- auto attacks = [&occ](const piece_type& piece, const square& sq) {
- switch (piece) {
- case piece_type::pawn: return pawn_attack_tbl.look_up(sq);
- case piece_type::knight: return knight_attack_tbl.look_up(sq);
- case piece_type::bishop: return bishop_attack_tbl.look_up(sq, occ);
- case piece_type::rook: return rook_attack_tbl.look_up(sq, occ);
- default: return square_set{};
- }
- };
-
- const square_set current_attacks = attacks(mv.piece(), mv.from());
- const square_set next_attacks = attacks(mv.piece(), mv.to());
- const square_set new_attacks = next_attacks & ~current_attacks;
-
- const square_set vulnerable = [&, this] {
- switch (mv.piece()) {
- case piece_type::pawn: return man_.them().all() & ~(man_.them().pawn() | man_.them().king());
- case piece_type::knight: return man_.them().rook() | man_.them().queen();
- case piece_type::bishop: return man_.them().rook() | man_.them().queen();
- case piece_type::rook: return man_.them().queen();
- default: return square_set{};
- }
- }();
-
- return (new_attacks & vulnerable).any();
- }
-
- bool creates_threat(const move& mv) const { return turn() ? creates_threat_(mv) : creates_threat_(mv); }
-
- template
- square_set king_danger() const {
- const square_set occ = (man_.white.all() | man_.black.all()) & ~man_.us().king();
- square_set k_danger{};
- for (const auto sq : man_.them().pawn()) { k_danger |= pawn_attack_tbl>.look_up(sq); }
- for (const auto sq : man_.them().knight()) { k_danger |= knight_attack_tbl.look_up(sq); }
- for (const auto sq : man_.them().king()) { k_danger |= king_attack_tbl.look_up(sq); }
- for (const auto sq : man_.them().rook()) { k_danger |= rook_attack_tbl.look_up(sq, occ); }
- for (const auto sq : man_.them().bishop()) { k_danger |= bishop_attack_tbl.look_up(sq, occ); }
- for (const auto sq : man_.them().queen()) {
- k_danger |= rook_attack_tbl.look_up(sq, occ);
- k_danger |= bishop_attack_tbl.look_up(sq, occ);
- }
- return k_danger;
- }
-
- template
- square_set pinned() const {
- const square_set occ = man_.white.all() | man_.black.all();
- const auto k_x_diag = bishop_attack_tbl.look_up(man_.us().king().item(), square_set{});
- const auto k_x_hori = rook_attack_tbl.look_up(man_.us().king().item(), square_set{});
- const auto b_check_mask = bishop_attack_tbl.look_up(man_.us().king().item(), occ);
- const auto r_check_mask = rook_attack_tbl.look_up(man_.us().king().item(), occ);
- square_set pinned_set{};
- for (const auto sq : (k_x_hori & (man_.them().queen() | man_.them().rook()))) {
- pinned_set |= r_check_mask & rook_attack_tbl.look_up(sq, occ) & man_.us().all();
- }
- for (const auto sq : (k_x_diag & (man_.them().queen() | man_.them().bishop()))) {
- pinned_set |= b_check_mask & bishop_attack_tbl.look_up(sq, occ) & man_.us().all();
- }
- return pinned_set;
- }
-
- template
- void add_en_passant(move_list& mv_ls) const {
- if constexpr (!mode::noisy) { return; }
- if (lat_.them().ep_mask().any()) {
- const square_set occ = man_.white.all() | man_.black.all();
- const square ep_square = lat_.them().ep_mask().item();
- const square_set enemy_pawn_mask = pawn_push_tbl>.look_up(ep_square, square_set{});
- const square_set from_mask = pawn_attack_tbl>.look_up(ep_square) & man_.us().pawn();
- for (const auto from : from_mask) {
- const square_set occ_ = (occ & ~square_set{from.bit_board()} & ~enemy_pawn_mask) | lat_.them().ep_mask();
- if (!std::get<0>(checkers(occ_)).any()) {
- mv_ls.push(from, ep_square, piece_type::pawn, false, piece_type::pawn, true, enemy_pawn_mask.item());
- }
- }
- }
- }
-
- template
- void add_castle(const move_generator_info& info, move_list& result) const {
- if constexpr (!mode::noisy) { return; }
- if (lat_.us().oo() && !(castle_info.oo_mask & (info.king_danger | info.occ)).any()) {
- result.push(castle_info.start_king, castle_info.oo_rook, piece_type::king, true, piece_type::rook);
- }
- if (lat_.us().ooo() && !(castle_info.ooo_danger_mask & info.king_danger).any() && !(castle_info.ooo_occ_mask & info.occ).any()) {
- result.push(castle_info.start_king, castle_info.ooo_rook, piece_type::king, true, piece_type::rook);
- }
- }
-
- template
- void add_normal_pawn(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().pawn() & ~info.pinned)) {
- const auto to_quiet = pawn_push_tbl.look_up(from, info.occ);
- const auto to_noisy = pawn_attack_tbl.look_up(from) & man_.them().all();
- if constexpr (mode::quiet) {
- for (const auto to : (to_quiet & ~info.last_rank)) { result.push(from, to, piece_type::pawn); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_noisy & ~info.last_rank)) { result.push(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- }
- for (const auto to : (to_quiet & info.last_rank)) {
- if constexpr (mode::quiet) { result.push_under_promotions(from, to, piece_type::pawn); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn); }
- }
- for (const auto to : (to_noisy & info.last_rank)) {
- // for historical reasons, underpromotion captures are considered quiet
- if constexpr (mode::quiet) { result.push_under_promotions(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_normal_knight(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().knight() & ~info.pinned)) {
- const auto to_mask = knight_attack_tbl.look_up(from);
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::knight); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::knight, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_normal_bishop(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().bishop() & ~info.pinned)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ);
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::bishop); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::bishop, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_normal_rook(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().rook() & ~info.pinned)) {
- const auto to_mask = rook_attack_tbl.look_up(from, info.occ);
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::rook); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::rook, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_normal_queen(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().queen() & ~info.pinned)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ) | rook_attack_tbl.look_up(from, info.occ);
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::queen); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::queen, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_pinned_pawn(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().pawn() & info.pinned & info.king_diagonal)) {
- const auto to_mask = pawn_attack_tbl.look_up(from) & info.king_diagonal;
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & ~info.last_rank & man_.them().all())) {
- result.push(from, to, piece_type::pawn, true, man_.them().occ(to));
- }
- }
- for (const auto to : (to_mask & info.last_rank & man_.them().all())) {
- if constexpr (mode::quiet) { result.push_under_promotions(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- }
- }
- for (const auto from : (man_.us().pawn() & info.pinned & info.king_horizontal)) {
- const auto to_mask = pawn_push_tbl.look_up(from, info.occ) & info.king_horizontal;
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.last_rank)) { result.push(from, to, piece_type::pawn); }
- }
- for (const auto to : (to_mask & info.last_rank)) {
- if constexpr (mode::quiet) { result.push_under_promotions(from, to, piece_type::pawn); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn); }
- }
- }
- }
-
- template
- void add_pinned_bishop(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().bishop() & info.pinned & info.king_diagonal)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ) & info.king_diagonal;
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::bishop); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::bishop, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_pinned_rook(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().rook() & info.pinned & info.king_horizontal)) {
- const auto to_mask = rook_attack_tbl.look_up(from, info.occ) & info.king_horizontal;
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::rook); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::rook, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_pinned_queen(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().queen() & info.pinned & info.king_diagonal)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ) & info.king_diagonal;
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::queen); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::queen, true, man_.them().occ(to)); }
- }
- }
- for (const auto from : (man_.us().queen() & info.pinned & info.king_horizontal)) {
- const auto to_mask = rook_attack_tbl.look_up(from, info.occ) & info.king_horizontal;
- if constexpr (mode::quiet) {
- for (const auto to : (to_mask & ~info.occ)) { result.push(from, to, piece_type::queen); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_mask & man_.them().all())) { result.push(from, to, piece_type::queen, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_checked_pawn(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().pawn() & ~info.pinned)) {
- const auto to_quiet = info.checker_rays & pawn_push_tbl.look_up(from, info.occ);
- const auto to_noisy = info.checkers & pawn_attack_tbl.look_up(from);
- if constexpr (mode::check) {
- for (const auto to : (to_quiet & ~info.last_rank)) { result.push(from, to, piece_type::pawn); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : (to_noisy & ~info.last_rank)) { result.push(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- }
- for (const auto to : (to_quiet & info.last_rank)) {
- if constexpr (mode::check) { result.push_under_promotions(from, to, piece_type::pawn); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn); }
- }
- for (const auto to : (to_noisy & info.last_rank)) {
- if constexpr (mode::check) { result.push_under_promotions(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- if constexpr (mode::noisy) { result.push_queen_promotion(from, to, piece_type::pawn, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_checked_knight(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().knight() & ~info.pinned)) {
- const auto to_mask = knight_attack_tbl.look_up(from);
- const auto to_quiet = info.checker_rays & to_mask;
- const auto to_noisy = info.checkers & to_mask;
- if constexpr (mode::check) {
- for (const auto to : to_quiet) { result.push(from, to, piece_type::knight); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : to_noisy) { result.push(from, to, piece_type::knight, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_checked_rook(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().rook() & ~info.pinned)) {
- const auto to_mask = rook_attack_tbl.look_up(from, info.occ);
- const auto to_quiet = info.checker_rays & to_mask;
- const auto to_noisy = info.checkers & to_mask;
- if constexpr (mode::check) {
- for (const auto to : to_quiet) { result.push(from, to, piece_type::rook); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : to_noisy) { result.push(from, to, piece_type::rook, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_checked_bishop(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().bishop() & ~info.pinned)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ);
- const auto to_quiet = info.checker_rays & to_mask;
- const auto to_noisy = info.checkers & to_mask;
- if constexpr (mode::check) {
- for (const auto to : to_quiet) { result.push(from, to, piece_type::bishop); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : to_noisy) { result.push(from, to, piece_type::bishop, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_checked_queen(const move_generator_info& info, move_list& result) const {
- for (const auto from : (man_.us().queen() & ~info.pinned)) {
- const auto to_mask = bishop_attack_tbl.look_up(from, info.occ) | rook_attack_tbl.look_up(from, info.occ);
- const auto to_quiet = info.checker_rays & to_mask;
- const auto to_noisy = info.checkers & to_mask;
- if constexpr (mode::check) {
- for (const auto to : to_quiet) { result.push(from, to, piece_type::queen); }
- }
- if constexpr (mode::noisy) {
- for (const auto to : to_noisy) { result.push(from, to, piece_type::queen, true, man_.them().occ(to)); }
- }
- }
- }
-
- template
- void add_king(const move_generator_info& info, move_list& result) const {
- const square_set to_mask = ~info.king_danger & king_attack_tbl.look_up(man_.us().king().item());
- if (info.checkers.any() ? mode::check : mode::quiet) {
- for (const square to : (to_mask & ~info.occ)) { result.push(man_.us().king().item(), to, piece_type::king); }
- }
- if (mode::noisy) {
- for (const square to : (to_mask & man_.them().all())) {
- result.push(man_.us().king().item(), to, piece_type::king, true, man_.them().occ(to));
- }
- }
- }
-
- template
- move_generator_info get_move_generator_info() const {
- const auto [checkers_, checker_rays_] = checkers(man_.white.all() | man_.black.all());
-
- const move_generator_info info{
- man_.white.all() | man_.black.all(),
- pawn_delta::last_rank,
- checkers_,
- checker_rays_,
- pinned(),
- king_danger(),
- bishop_attack_tbl.look_up(man_.us().king().item(), square_set{}),
- rook_attack_tbl.look_up(man_.us().king().item(), square_set{}),
- };
-
- return info;
- }
-
- template
- move_list generate_moves_() const {
- const move_generator_info info = get_move_generator_info();
- const size_t num_checkers = info.checkers.count();
- move_list result{};
-
- if (num_checkers == 0) {
- add_normal_pawn(info, result);
- add_normal_knight(info, result);
- add_normal_rook(info, result);
- add_normal_bishop(info, result);
- add_normal_queen(info, result);
- add_castle(info, result);
- if (info.pinned.any()) {
- add_pinned_pawn(info, result);
- add_pinned_bishop(info, result);
- add_pinned_rook(info, result);
- add_pinned_queen(info, result);
- }
- } else if (num_checkers == 1) {
- add_checked_pawn(info, result);
- add_checked_knight(info, result);
- add_checked_rook(info, result);
- add_checked_bishop(info, result);
- add_checked_queen(info, result);
- }
- add_king(info, result);
- add_en_passant(result);
- return result;
- }
-
- template
- move_list generate_moves() const {
- return turn() ? generate_moves_() : generate_moves_();
- }
-
- template
- bool is_legal_(const move& mv) const {
- if (mv.is_castle_oo() || mv.is_castle_ooo() || mv.is_enpassant()) {
- const move_generator_info info = get_move_generator_info();
- move_list list{};
- add_castle(info, list);
- add_en_passant(list);
- return list.has(mv);
- }
-
- if (!man_.us().all().is_member(mv.from())) { return false; }
- if (man_.us().all().is_member(mv.to())) { return false; }
- if (mv.piece() != man_.us().occ(mv.from())) { return false; }
-
- if (mv.is_capture() != man_.them().all().is_member(mv.to())) { return false; }
- if (mv.is_capture() && mv.captured() != man_.them().occ(mv.to())) { return false; }
- if (!mv.is_capture() && mv.captured() != static_cast(0)) { return false; }
-
- if (!mv.is_enpassant() && mv.enpassant_sq() != square::from_index(0)) { return false; }
- if (!mv.is_promotion() && mv.promotion() != static_cast(0)) { return false; }
-
- const move_generator_info info = get_move_generator_info();
-
- const bool is_noisy = (!mv.is_promotion() || mv.promotion() == chess::piece_type::queen) && (mv.is_capture() || mv.is_promotion());
- if (!mode::noisy && is_noisy) { return false; }
- if (!mode::check && info.checkers.any() && !is_noisy) { return false; }
- if (!mode::quiet && !info.checkers.any() && !is_noisy) { return false; }
-
- const square_set rook_mask = rook_attack_tbl.look_up(mv.from(), info.occ);
- const square_set bishop_mask = bishop_attack_tbl.look_up(mv.from(), info.occ);
-
- const bool legal_from_to = [&] {
- const auto pawn_mask = (mv.is_capture() ? pawn_attack_tbl.look_up(mv.from()) : pawn_push_tbl.look_up(mv.from(), info.occ));
- switch (mv.piece()) {
- case piece_type::pawn: return pawn_mask.is_member(mv.to());
- case piece_type::knight: return knight_attack_tbl.look_up(mv.from()).is_member(mv.to());
- case piece_type::bishop: return bishop_mask.is_member(mv.to());
- case piece_type::rook: return rook_mask.is_member(mv.to());
- case piece_type::queen: return (bishop_mask | rook_mask).is_member(mv.to());
- case piece_type::king: return king_attack_tbl.look_up(mv.from()).is_member(mv.to());
- default: return false;
- }
- }();
-
- if (!legal_from_to) { return false; }
-
- if (mv.piece() == piece_type::king && info.king_danger.is_member(mv.to())) { return false; }
- if (info.checkers.any() && mv.piece() != piece_type::king) {
- if (info.checkers.count() >= 2) { return false; }
- if (info.pinned.is_member(mv.from())) { return false; }
- if (!(info.checkers | info.checker_rays).is_member(mv.to())) { return false; }
- }
-
- if (info.pinned.is_member(mv.from())) {
- const square_set piece_diagonal = bishop_mask;
- const square_set piece_horizontal = rook_mask;
- const bool same_diagonal = info.king_diagonal.is_member(mv.from()) && (info.king_diagonal & piece_diagonal).is_member(mv.to());
- const bool same_horizontal = info.king_horizontal.is_member(mv.from()) && (info.king_horizontal & piece_horizontal).is_member(mv.to());
- if (!same_diagonal && !same_horizontal) { return false; }
- }
-
- if (mv.is_promotion()) {
- if (mv.piece() != piece_type::pawn) { return false; }
- if (!info.last_rank.is_member(mv.to())) { return false; }
- if (mv.promotion() <= piece_type::pawn || mv.promotion() > piece_type::queen) { return false; }
- }
-
- return true;
- }
-
- template
- bool is_legal(const move& mv) const {
- return turn() ? is_legal_(mv) : is_legal_(mv);
- }
-
- template
- bool is_check_() const {
- return std::get<0>(checkers(man_.white.all() | man_.black.all())).any();
- }
-
- bool is_check() const { return turn() ? is_check_() : is_check_(); }
-
- square_set us_threat_mask() const { return turn() ? threat_mask() : threat_mask(); }
-
- square_set them_threat_mask() const { return turn() ? threat_mask() : threat_mask(); }
-
- template
- bool see_ge_(const move& mv, const T& threshold) const {
- const square tgt_sq = mv.to();
- auto used_mask = square_set{};
-
- auto on_sq = mv.is_promotion() ? mv.promotion() : mv.piece();
- used_mask.insert(mv.from());
-
- T value = [&] {
- T val{-threshold};
- if (mv.is_promotion()) { val += material_value(mv.promotion()) - material_value(mv.piece()); }
- if (mv.is_capture() && !mv.is_castle_ooo() && !mv.is_castle_oo()) { val += material_value(mv.captured()); }
- return val;
- }();
-
- for (;;) {
- if (value < 0) { return false; }
- if ((value - material_value(on_sq)) >= 0) { return true; }
-
- {
- const auto [p, sq] = least_valuable_attacker