Compare commits
30 Commits
d54ffbc7fa
...
main
Author | SHA1 | Date | |
---|---|---|---|
cb0fcc4702 | |||
87adca7e66 | |||
bcf4d66c49 | |||
a4d5dbbc84 | |||
b819e27f55 | |||
9552678efe | |||
0d7f030634 | |||
151da7eb51 | |||
2c31de83b7 | |||
79b5dd4a86 | |||
8d028ba087 | |||
71d90918b3 | |||
256b511569 | |||
4f96d189fa | |||
b64530cb3d | |||
e18146e00c | |||
db20e49f35 | |||
cd21c16da7 | |||
91624c62c1 | |||
30107cc6fa | |||
fadfab165a | |||
e1d216f06b | |||
e620e6f5cb | |||
db23a4c610 | |||
4157c9200e | |||
fbc1a5589a | |||
defd74fb63 | |||
c66a562886 | |||
84afaa2ada | |||
a1d4dadaa2 |
22
BasicEngine.cpp
Normal file
22
BasicEngine.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "BasicEngine.hpp"
|
||||||
|
#include "Search.hpp"
|
||||||
|
|
||||||
|
std::string BasicEngine::name() const {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
std::string BasicEngine::version() const {
|
||||||
|
return mVersion;
|
||||||
|
}
|
||||||
|
std::string BasicEngine::author() const {
|
||||||
|
return mAuthor;
|
||||||
|
}
|
||||||
|
void BasicEngine::newGame() {
|
||||||
|
|
||||||
|
}
|
||||||
|
PrincipalVariation BasicEngine::pv(const Board &board, const TimeInfo::Optional &timeInfo) {
|
||||||
|
(void)board;
|
||||||
|
(void)timeInfo;
|
||||||
|
auto pv =Search::start(board);
|
||||||
|
return pv;
|
||||||
|
}
|
23
BasicEngine.hpp
Normal file
23
BasicEngine.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef CHESS_ENGINE_BASICENGINE_HPP
|
||||||
|
#define CHESS_ENGINE_BASICENGINE_HPP
|
||||||
|
|
||||||
|
#include "Engine.hpp"
|
||||||
|
class BasicEngine final : public Engine {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string name() const override;
|
||||||
|
std::string version() const override;
|
||||||
|
std::string author() const override;
|
||||||
|
void newGame() override;
|
||||||
|
PrincipalVariation pv(const Board &board, const TimeInfo::Optional &timeInfo) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string mName = "Egon++";
|
||||||
|
std::string mVersion = "1.0~Egon+C";
|
||||||
|
std::string mAuthor = "Bols";
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CHESS_ENGINE_BASICENGINE_HPP
|
230
BitBoard.cpp
230
BitBoard.cpp
@@ -1,102 +1,9 @@
|
|||||||
#include <iostream>
|
#include "BitBoard.hpp"
|
||||||
#include "BitBoard.h"
|
|
||||||
|
|
||||||
//BitBoard::BitBoard() {
|
|
||||||
// mBoard = 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
BitBoard::BitBoard(uint64_t v) {
|
BitBoard::BitBoard(uint64_t v) {
|
||||||
mBoard = v;
|
mBoard = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard::operator bool() const {
|
|
||||||
return mBoard != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard::operator unsigned long long() const {
|
|
||||||
return mBoard;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator==(const BitBoard &rhs) const {
|
|
||||||
return mBoard == rhs.mBoard;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator!=(const BitBoard &rhs) const {
|
|
||||||
return !(rhs == *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator<(const BitBoard &rhs) const {
|
|
||||||
return mBoard < rhs.mBoard;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator>(const BitBoard &rhs) const {
|
|
||||||
return rhs < *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator<=(const BitBoard &rhs) const {
|
|
||||||
return !(rhs < *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BitBoard::operator>=(const BitBoard &rhs) const {
|
|
||||||
return !(*this < rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator&=(const BitBoard &rhs) {
|
|
||||||
mBoard &= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator|=(const BitBoard &rhs) {
|
|
||||||
mBoard |= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator^=(const BitBoard &rhs) {
|
|
||||||
mBoard ^= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator<<=(const BitBoard &rhs) {
|
|
||||||
mBoard <<= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator>>=(const BitBoard &rhs) {
|
|
||||||
mBoard >>= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard BitBoard::operator~() {
|
|
||||||
BitBoard result(*this);
|
|
||||||
result.mBoard = ~mBoard;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator+=(const BitBoard &rhs) {
|
|
||||||
mBoard += rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator-=(const BitBoard &rhs) {
|
|
||||||
mBoard -= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator*=(const BitBoard &rhs) {
|
|
||||||
mBoard *= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator/=(const BitBoard &rhs) {
|
|
||||||
mBoard /= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard &BitBoard::operator%=(const BitBoard &rhs) {
|
|
||||||
mBoard %= rhs.mBoard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const BitBoard &board) {
|
std::ostream &operator<<(std::ostream &os, const BitBoard &board) {
|
||||||
// For debugging only, performance isn't important
|
// For debugging only, performance isn't important
|
||||||
for (int i = 7; i >= 0; i--) {
|
for (int i = 7; i >= 0; i--) {
|
||||||
@@ -117,64 +24,109 @@ std::ostream &operator<<(std::ostream &os, const BitBoard &board) {
|
|||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
bool BitBoard::operator!() const {
|
|
||||||
return !mBoard;
|
BitBoard BitBoard::northFill() const {
|
||||||
|
BitBoard result(mBoard);
|
||||||
|
result |= (result << 8);
|
||||||
|
result |= (result << 16);
|
||||||
|
result |= (result << 32);
|
||||||
|
return (result << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BitBoard::operator&&(const BitBoard &rhs) const {
|
BitBoard BitBoard::southFill() const {
|
||||||
return mBoard && rhs.mBoard;
|
BitBoard result(mBoard);
|
||||||
}
|
result |= (result >> 8);
|
||||||
|
result |= (result >> 16);
|
||||||
bool BitBoard::operator||(const BitBoard &rhs) const {
|
result |= (result >> 32);
|
||||||
return mBoard || rhs.mBoard;
|
|
||||||
}
|
|
||||||
BitBoard::operator void *() const {
|
|
||||||
return (void *) mBoard;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard operator&(const BitBoard &lhs, const BitBoard &rhs) {
|
|
||||||
BitBoard result(lhs);
|
|
||||||
result &= rhs;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard operator|(const BitBoard &lhs, const BitBoard &rhs) {
|
BitBoard BitBoard::fileFill() const {
|
||||||
BitBoard result(lhs);
|
return northFill() | southFill();
|
||||||
result |= rhs;
|
}
|
||||||
|
|
||||||
|
BitBoard BitBoard::kingMoves(const BitBoard kings) {
|
||||||
|
BitBoard result = kings.east() | kings.west() | kings;
|
||||||
|
result |= (result.north() | result.south());
|
||||||
|
result ^= kings;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard operator^(const BitBoard &lhs, const BitBoard &rhs) {
|
BitBoard BitBoard::castlingMoves(BitBoard kings, BitBoard rooks, BitBoard empty) {
|
||||||
BitBoard result(lhs);
|
kings |= (kings.east() | kings.west()) & empty;
|
||||||
result ^= rhs;
|
kings |= (kings.east() | kings.west()) & empty;
|
||||||
return result;
|
kings |= kings.west() & empty;
|
||||||
|
rooks |= (rooks.east() | rooks.west()) & empty;
|
||||||
|
rooks |= (rooks.east() | rooks.west()) & empty;
|
||||||
|
rooks |= (rooks.east()) & empty;
|
||||||
|
|
||||||
|
return kings & rooks & CastlingSquares;
|
||||||
}
|
}
|
||||||
BitBoard operator<<(const BitBoard &lhs, const BitBoard &rhs) {
|
|
||||||
BitBoard result(lhs);
|
BitBoard BitBoard::bishopAttacks(BitBoard bishops, BitBoard empty) {
|
||||||
result <<= rhs;
|
BitBoard result = 0;
|
||||||
|
empty ^= bishops;
|
||||||
|
|
||||||
|
BitBoard diag1 = (bishops.northWest() | bishops.southEast() | bishops) & empty;
|
||||||
|
BitBoard diag2 = (bishops.northEast() | bishops.southWest() | bishops) & empty;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
result |= (diag1 | diag2);
|
||||||
|
diag1 = (diag1.northWest() | diag1.southEast()) & empty;
|
||||||
|
diag2 = (diag2.northEast() | diag2.southWest()) & empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result | diag1.northWest() | diag1.southEast() | diag2.northEast() | diag2.southWest()) & ~bishops;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBoard BitBoard::rookAttacks(BitBoard rooks, BitBoard empty) {
|
||||||
|
BitBoard result = 0;
|
||||||
|
empty ^= rooks;
|
||||||
|
|
||||||
|
BitBoard dir1 = (rooks.north() | rooks.south() | rooks) & empty;
|
||||||
|
BitBoard dir2 = (rooks.east() | rooks.west() | rooks) & empty;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
result |= (dir1 | dir2);
|
||||||
|
dir1 = (dir1.north() | dir1.south()) & empty;
|
||||||
|
dir2 = (dir2.east() | dir2.west()) & empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result | dir1.north() | dir1.south() | dir2.east() | dir2.west()) & ~rooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBoard BitBoard::queenAttacks(BitBoard queens, BitBoard empty) {
|
||||||
|
return rookAttacks(queens, empty) | bishopAttacks(queens, empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBoard BitBoard::knightMoves(BitBoard knights) {
|
||||||
|
BitBoard east, west, result;
|
||||||
|
|
||||||
|
east = knights.east();
|
||||||
|
west = knights.west();
|
||||||
|
result = (east | west) << 16;
|
||||||
|
result |= (east | west) >> 16;
|
||||||
|
east = east.east();
|
||||||
|
west = west.west();
|
||||||
|
result |= (east | west) << 8;
|
||||||
|
result |= (east | west) >> 8;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard operator>>(const BitBoard &lhs, const BitBoard &rhs) {
|
BitBoard BitBoard::pawnNorthAttacks(BitBoard pawns, BitBoard targets) {
|
||||||
BitBoard result(lhs);
|
return (pawns.northEast() | pawns.northWest()) & targets;
|
||||||
result >>= rhs;
|
}
|
||||||
return result;
|
BitBoard BitBoard::pawnNorthMoves(BitBoard pawns, BitBoard empty) {
|
||||||
|
pawns = pawns.north() & empty;
|
||||||
|
return (pawns | (pawns.north() & Rank4)) & empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard operator-(const BitBoard &lhs, const BitBoard &rhs) {
|
BitBoard BitBoard::pawnSouthAttacks(BitBoard pawns, BitBoard targets) {
|
||||||
BitBoard result(lhs);
|
return (pawns.southEast() | pawns.southWest()) & targets;
|
||||||
result -= rhs;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
BitBoard BitBoard::pawnSouthMoves(BitBoard pawns, BitBoard empty) {
|
||||||
BitBoard operator*(const BitBoard &lhs, const BitBoard &rhs) {
|
pawns = pawns.south() & empty;
|
||||||
BitBoard result(lhs);
|
return (pawns | (pawns.south() & Rank5)) & empty;
|
||||||
result *= rhs;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBoard operator%(const BitBoard &lhs, const BitBoard &rhs) {
|
|
||||||
BitBoard result(lhs);
|
|
||||||
result %= rhs;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
64
BitBoard.h
64
BitBoard.h
@@ -1,64 +0,0 @@
|
|||||||
#ifndef CHESS_ENGINE_BITBOARD_HPP
|
|
||||||
#define CHESS_ENGINE_BITBOARD_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <ostream>
|
|
||||||
#include "CastlingRights.hpp"
|
|
||||||
|
|
||||||
class BitBoard final {
|
|
||||||
public:
|
|
||||||
using U64 = uint64_t;
|
|
||||||
|
|
||||||
BitBoard(uint64_t v = 0);
|
|
||||||
|
|
||||||
// explicit operator uint64_t() const;
|
|
||||||
explicit operator bool() const;
|
|
||||||
explicit operator unsigned long long() const;
|
|
||||||
operator void*() const;
|
|
||||||
|
|
||||||
bool operator==(const BitBoard &rhs) const;
|
|
||||||
bool operator!=(const BitBoard &rhs) const;
|
|
||||||
bool operator<(const BitBoard &rhs) const;
|
|
||||||
bool operator>(const BitBoard &rhs) const;
|
|
||||||
bool operator<=(const BitBoard &rhs) const;
|
|
||||||
bool operator>=(const BitBoard &rhs) const;
|
|
||||||
|
|
||||||
// bitset operations
|
|
||||||
BitBoard &operator&=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator|=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator^=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator<<=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator>>=(const BitBoard &rhs);
|
|
||||||
BitBoard operator~();
|
|
||||||
|
|
||||||
bool operator!() const;
|
|
||||||
bool operator&&(const BitBoard &rhs) const;
|
|
||||||
bool operator||(const BitBoard &rhs) const;
|
|
||||||
|
|
||||||
BitBoard &operator+=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator-=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator*=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator/=(const BitBoard &rhs);
|
|
||||||
BitBoard &operator%=(const BitBoard &rhs);
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &os, const BitBoard &board);
|
|
||||||
// friend bool operator&&(const bool &lhs, const BitBoard &rhs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
U64 mBoard = {};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
BitBoard operator^(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator|(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator&(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator<<(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator>>(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator+(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator-(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
//BitBoard operator*(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
//BitBoard operator/(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
BitBoard operator%(const BitBoard &lhs, const BitBoard &rhs);
|
|
||||||
|
|
||||||
#endif //CHESS_ENGINE_BITBOARD_HPP
|
|
294
BitBoard.hpp
Normal file
294
BitBoard.hpp
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
#ifndef CHESS_ENGINE_BITBOARD_HPP
|
||||||
|
#define CHESS_ENGINE_BITBOARD_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ostream>
|
||||||
|
#include "Square.hpp"
|
||||||
|
|
||||||
|
enum DefinedBoards : uint64_t {
|
||||||
|
AFile = 0x0101010101010101,
|
||||||
|
BFile = AFile << 1,
|
||||||
|
CFile = AFile << 2,
|
||||||
|
DFile = AFile << 3,
|
||||||
|
EFile = AFile << 4,
|
||||||
|
FFile = AFile << 5,
|
||||||
|
GFile = AFile << 6,
|
||||||
|
HFile = AFile << 7,
|
||||||
|
Rank1 = 0x00000000000000FF,
|
||||||
|
Rank2 = Rank1 << 8,
|
||||||
|
Rank3 = Rank1 << 16,
|
||||||
|
Rank4 = Rank1 << 24,
|
||||||
|
Rank5 = Rank1 << 32,
|
||||||
|
Rank6 = Rank1 << 40,
|
||||||
|
Rank7 = Rank1 << 48,
|
||||||
|
Rank8 = Rank1 << 56,
|
||||||
|
WhiteCastlingRank = (1ULL << A2) - 1,
|
||||||
|
BlackCastlingRank = (~1ULL << H7),
|
||||||
|
CastlingRanks = WhiteCastlingRank | BlackCastlingRank,
|
||||||
|
CastlingSquares = (WhiteCastlingRank | BlackCastlingRank) & (CFile | GFile),
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitBoard final {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using U64 = uint64_t;
|
||||||
|
|
||||||
|
BitBoard(U64 v = 0);
|
||||||
|
|
||||||
|
explicit constexpr operator bool() const {
|
||||||
|
return mBoard != 0;
|
||||||
|
}
|
||||||
|
explicit constexpr operator unsigned int() const {
|
||||||
|
return mBoard;
|
||||||
|
}
|
||||||
|
explicit constexpr operator unsigned long() const {
|
||||||
|
return mBoard;
|
||||||
|
}
|
||||||
|
explicit constexpr operator unsigned long long() const {
|
||||||
|
return mBoard;
|
||||||
|
}
|
||||||
|
operator void *() const {
|
||||||
|
return (void *) mBoard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relational operators
|
||||||
|
constexpr bool operator==(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator!=(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator<(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator>(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator<=(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator>=(const BitBoard &rhs) const;
|
||||||
|
|
||||||
|
// Logical operators
|
||||||
|
constexpr bool operator!() const;
|
||||||
|
constexpr bool operator&&(const BitBoard &rhs) const;
|
||||||
|
constexpr bool operator||(const BitBoard &rhs) const;
|
||||||
|
|
||||||
|
// Bitwise operators
|
||||||
|
constexpr BitBoard &operator&=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator|=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator^=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator<<=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator>>=(const BitBoard &rhs);
|
||||||
|
BitBoard operator~() const {
|
||||||
|
BitBoard result(*this);
|
||||||
|
result.mBoard = ~mBoard;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
friend BitBoard operator^(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator|(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator&(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator<<(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator>>(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
|
||||||
|
// Arithmetic operators
|
||||||
|
constexpr BitBoard &operator+=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator-=(const BitBoard &rhs);
|
||||||
|
constexpr BitBoard &operator%=(const BitBoard &rhs);
|
||||||
|
friend BitBoard operator+(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator-(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
friend BitBoard operator%(const BitBoard &lhs, const BitBoard &rhs);
|
||||||
|
|
||||||
|
// Stream operator
|
||||||
|
friend std::ostream &operator<<(std::ostream &os, const BitBoard &board);
|
||||||
|
|
||||||
|
constexpr void clear(unsigned i);
|
||||||
|
constexpr void set(unsigned i);
|
||||||
|
|
||||||
|
BitBoard north() const;
|
||||||
|
BitBoard northEast() const;
|
||||||
|
BitBoard northWest() const;
|
||||||
|
BitBoard east() const;
|
||||||
|
BitBoard south() const;
|
||||||
|
BitBoard southEast() const;
|
||||||
|
BitBoard southWest() const;
|
||||||
|
BitBoard west() const;
|
||||||
|
|
||||||
|
BitBoard northFill() const;
|
||||||
|
BitBoard southFill() const;
|
||||||
|
BitBoard fileFill() const;
|
||||||
|
|
||||||
|
static BitBoard bishopAttacks(BitBoard pos, BitBoard empty);
|
||||||
|
static BitBoard rookAttacks(BitBoard rooks, BitBoard empty);
|
||||||
|
static BitBoard queenAttacks(BitBoard queens, BitBoard empty);
|
||||||
|
static BitBoard kingMoves(const BitBoard kings);
|
||||||
|
static BitBoard castlingMoves(BitBoard kings, BitBoard rooks, BitBoard empty);
|
||||||
|
static BitBoard knightMoves(BitBoard knights);
|
||||||
|
|
||||||
|
static BitBoard pawnNorthAttacks(BitBoard pawns, BitBoard targets);
|
||||||
|
static BitBoard pawnSouthAttacks(BitBoard pawns, BitBoard targets);
|
||||||
|
|
||||||
|
static BitBoard pawnNorthMoves(BitBoard pawns, BitBoard empty);
|
||||||
|
static BitBoard pawnSouthMoves(BitBoard pawns, BitBoard empty);
|
||||||
|
|
||||||
|
static BitBoard fromIndex(unsigned i);
|
||||||
|
|
||||||
|
// Returns the number of trailing 0-bits in b.
|
||||||
|
// WARN: Check for 0!
|
||||||
|
unsigned lsb() const;
|
||||||
|
unsigned pop();
|
||||||
|
constexpr int count() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
U64 mBoard = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Relational operators
|
||||||
|
constexpr bool BitBoard::operator==(const BitBoard &rhs) const {
|
||||||
|
return mBoard == rhs.mBoard;
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator!=(const BitBoard &rhs) const {
|
||||||
|
return !(rhs == *this);
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator<(const BitBoard &rhs) const {
|
||||||
|
return mBoard < rhs.mBoard;
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator>(const BitBoard &rhs) const {
|
||||||
|
return rhs < *this;
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator<=(const BitBoard &rhs) const {
|
||||||
|
return !(rhs < *this);
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator>=(const BitBoard &rhs) const {
|
||||||
|
return !(*this < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logical operators
|
||||||
|
constexpr bool BitBoard::operator!() const {
|
||||||
|
return !mBoard;
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator&&(const BitBoard &rhs) const {
|
||||||
|
return mBoard && rhs.mBoard;
|
||||||
|
}
|
||||||
|
constexpr bool BitBoard::operator||(const BitBoard &rhs) const {
|
||||||
|
return mBoard || rhs.mBoard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise operators
|
||||||
|
constexpr BitBoard &BitBoard::operator&=(const BitBoard &rhs) {
|
||||||
|
mBoard &= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator|=(const BitBoard &rhs) {
|
||||||
|
mBoard |= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator^=(const BitBoard &rhs) {
|
||||||
|
mBoard ^= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator<<=(const BitBoard &rhs) {
|
||||||
|
mBoard <<= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator>>=(const BitBoard &rhs) {
|
||||||
|
mBoard >>= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline BitBoard operator^(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result ^= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator|(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result |= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator&(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result &= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator<<(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result <<= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator>>(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result >>= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arithmetic operators
|
||||||
|
constexpr BitBoard &BitBoard::operator+=(const BitBoard &rhs) {
|
||||||
|
mBoard += rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator-=(const BitBoard &rhs) {
|
||||||
|
mBoard -= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr BitBoard &BitBoard::operator%=(const BitBoard &rhs) {
|
||||||
|
mBoard %= rhs.mBoard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline BitBoard operator+(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result += rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator-(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result -= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
inline BitBoard operator%(const BitBoard &lhs, const BitBoard &rhs) {
|
||||||
|
BitBoard result(lhs);
|
||||||
|
result %= rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void BitBoard::set(const unsigned i) {
|
||||||
|
mBoard |= (1ULL << i);
|
||||||
|
}
|
||||||
|
constexpr void BitBoard::clear(const unsigned i) {
|
||||||
|
mBoard &= ~(1ULL << i);
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::fromIndex(const unsigned i) {
|
||||||
|
return 1ULL << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline BitBoard BitBoard::north() const {
|
||||||
|
return (mBoard << 8);
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::northEast() const {
|
||||||
|
return (mBoard << 9) & ~AFile;
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::northWest() const {
|
||||||
|
return (mBoard << 7) & ~HFile;
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::west() const {
|
||||||
|
return (mBoard >> 1) & ~HFile;
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::east() const {
|
||||||
|
return (mBoard << 1) & ~AFile;
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::south() const {
|
||||||
|
return (mBoard >> 8);
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::southEast() const {
|
||||||
|
return (mBoard >> 7) & ~AFile;
|
||||||
|
}
|
||||||
|
inline BitBoard BitBoard::southWest() const {
|
||||||
|
return (mBoard >> 9) & ~HFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned BitBoard::lsb() const {
|
||||||
|
return __builtin_ctzll(mBoard);
|
||||||
|
}
|
||||||
|
inline unsigned BitBoard::pop() {
|
||||||
|
unsigned i = lsb();
|
||||||
|
mBoard &= mBoard - 1;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int BitBoard::count() const {
|
||||||
|
return __builtin_popcountll(mBoard);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //CHESS_ENGINE_BITBOARD_HPP
|
104
Board.cpp
104
Board.cpp
@@ -1,31 +1,28 @@
|
|||||||
#include "Board.hpp"
|
#include "Board.hpp"
|
||||||
|
#include "BoardState.hpp"
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
Board::Board() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Board::setPiece(const Square &square, const Piece::Optional &piece) {
|
void Board::setPiece(const Square &square, const Piece::Optional &piece) {
|
||||||
if (!piece.has_value())
|
if (!piece.has_value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto index = square.index();
|
auto index = square.index();
|
||||||
for (auto &bb : mPieceBBs) {
|
for (int i = 0; i < BB_NUM; i++) {
|
||||||
clearIndex(bb, index);
|
mPieceBBs[i].clear(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndex(mPieceBBs[toIndex(piece->type())], index);
|
mPieceBBs[toIndex(piece->type())].set(index);
|
||||||
setIndex(mPieceBBs[toIndex(piece->color())], index);
|
mPieceBBs[toIndex(piece->color())].set(index);
|
||||||
setIndex(mOccupiedBB, index);
|
mOccupiedBB.set(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Piece::Optional Board::piece(const Square &square) const {
|
Piece::Optional Board::piece(const Square &square) const {
|
||||||
BitBoard mask = indexToBitBoard(square.index());
|
BitBoard mask = BitBoard::fromIndex(square.index());
|
||||||
if (!(mOccupiedBB & mask)) {
|
if (!(mOccupiedBB & mask)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -35,6 +32,42 @@ Piece::Optional Board::piece(const Square &square) const {
|
|||||||
return Piece(c, t);
|
return Piece(c, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Board::evaluate() const {
|
||||||
|
int score = evaluate(mTurn);
|
||||||
|
score -= evaluate(!mTurn);
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Board::evaluate(const PieceColor color) const {
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
BitBoard bb;
|
||||||
|
BitBoard colorMask = mPieceBBs[toIndex(color)];
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
bb = mPieceBBs[i+2] & colorMask;
|
||||||
|
score += Piece::PieceValue[i] * bb.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoardState bs = BoardState(&mPieceBBs, mOccupiedBB, mTurn, mCR, mEPS);
|
||||||
|
// score += round(mMobilityWeight * MoveGenerator::Mobility(bs, color));
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Board::isCheckMate() const {
|
||||||
|
auto moves = MoveVec();
|
||||||
|
pseudoLegalMoves(moves);
|
||||||
|
|
||||||
|
if (!moves.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoardState bs = BoardState(&mPieceBBs, mOccupiedBB, mTurn, mCR, mEPS);
|
||||||
|
auto attacked = MoveGenerator::generateAttackedSquares(bs, ~mOccupiedBB, !mTurn);
|
||||||
|
|
||||||
|
return attacked & mPieceBBs[toIndex(PieceType::King)] & mPieceBBs[toIndex(mTurn)];
|
||||||
|
}
|
||||||
|
|
||||||
void Board::setTurn(PieceColor turn) {
|
void Board::setTurn(PieceColor turn) {
|
||||||
mTurn = turn;
|
mTurn = turn;
|
||||||
}
|
}
|
||||||
@@ -63,8 +96,8 @@ Square::Optional Board::enPassantSquare() const {
|
|||||||
|
|
||||||
void Board::makeMove(const Move &move) {
|
void Board::makeMove(const Move &move) {
|
||||||
|
|
||||||
BitBoard fromBB = indexToBitBoard(move.from().index());
|
BitBoard fromBB = BitBoard::fromIndex(move.from().index());
|
||||||
BitBoard toBB = indexToBitBoard(move.to().index());
|
BitBoard toBB = BitBoard::fromIndex(move.to().index());
|
||||||
BitBoard changeBB = fromBB ^ toBB;
|
BitBoard changeBB = fromBB ^ toBB;
|
||||||
|
|
||||||
// If Piece is captured
|
// If Piece is captured
|
||||||
@@ -119,7 +152,7 @@ void Board::makeMove(const Move &move) {
|
|||||||
}
|
}
|
||||||
void Board::handleEnPassant(const Move &move, const Piece &movedPiece) {
|
void Board::handleEnPassant(const Move &move, const Piece &movedPiece) {
|
||||||
if (movedPiece.type() == PieceType::Pawn && mEPS.has_value() && move.from().file() != move.to().file()) {
|
if (movedPiece.type() == PieceType::Pawn && mEPS.has_value() && move.from().file() != move.to().file()) {
|
||||||
auto epBB = indexToBitBoard(Square::fromCoordinates(move.to().file(), move.from().rank())->index());
|
auto epBB = BitBoard::fromIndex(Square::fromCoordinates(move.to().file(), move.from().rank())->index());
|
||||||
auto capturedPiece = Piece(!mTurn, PieceType::Pawn);
|
auto capturedPiece = Piece(!mTurn, PieceType::Pawn);
|
||||||
|
|
||||||
mPieceBBs[toIndex(capturedPiece.color())] ^= epBB;
|
mPieceBBs[toIndex(capturedPiece.color())] ^= epBB;
|
||||||
@@ -132,24 +165,51 @@ void Board::handlePawnDoubleAdvance(const Move &move, BitBoard bb, const Piece &
|
|||||||
if (movedPiece.type() == PieceType::Pawn) {
|
if (movedPiece.type() == PieceType::Pawn) {
|
||||||
auto fromR = move.from().rank();
|
auto fromR = move.from().rank();
|
||||||
auto toR = move.to().rank();
|
auto toR = move.to().rank();
|
||||||
auto diff = abs(static_cast<int>(fromR) - static_cast<int>(toR));
|
if (abs(static_cast<int>(fromR) - static_cast<int>(toR)) == 2) {
|
||||||
if (diff == 2 && (mPieceBBs[toIndex(PieceType::Pawn)] & (bb << 1 | bb >> 1) & getRankBB(static_cast<int>(toR)))) {
|
if (mPieceBBs[toIndex(PieceType::Pawn)] & mPieceBBs[toIndex(!mTurn)] // Get all other color pawns
|
||||||
|
& (bb.west() | bb.east())) { // is pawn left or right?
|
||||||
mEPS = Square::fromCoordinates(move.to().file(), std::max(fromR, toR) - 1);
|
mEPS = Square::fromCoordinates(move.to().file(), std::max(fromR, toR) - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
mEPS = std::nullopt;
|
mEPS = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::pseudoLegalMoves(MoveVec &moves) const {
|
void Board::pseudoLegalMoves(MoveVec &moves) const {
|
||||||
(void) moves;
|
auto pieces = mPieceBBs[toIndex(mTurn)];
|
||||||
|
|
||||||
|
while (pieces) {
|
||||||
|
auto sq = Square(pieces.pop());
|
||||||
|
pseudoLegalMovesFrom(sq, moves);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::pseudoLegalMovesFrom(const Square &from,
|
void Board::pseudoLegalMovesFrom(const Square &from, Board::MoveVec &moves) const {
|
||||||
Board::MoveVec &moves) const {
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
(void) from;
|
|
||||||
(void) moves;
|
if (!(fromBB & mPieceBBs[toIndex(mTurn)])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState bs = BoardState(&mPieceBBs, mOccupiedBB, mTurn, mCR, mEPS);
|
||||||
|
|
||||||
|
switch (pieceType(fromBB)) {
|
||||||
|
case PieceType::Pawn: MoveGenerator::generatePawnMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
case PieceType::Knight: MoveGenerator::generateKnightMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
case PieceType::Bishop: MoveGenerator::generateBishopMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
case PieceType::Rook: MoveGenerator::generateRookMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
case PieceType::Queen: MoveGenerator::generateQueenMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
case PieceType::King: MoveGenerator::generateKingMoves(bs, from, moves);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void Board::handleCastlingRights(const Piece &piece, const BitBoard &bb) {
|
void Board::handleCastlingRights(const Piece &piece, const BitBoard &bb) {
|
||||||
if (piece.type() != PieceType::King && piece.type() != PieceType::Rook) {
|
if (piece.type() != PieceType::King && piece.type() != PieceType::Rook) {
|
||||||
@@ -193,10 +253,10 @@ bool Board::isMoveCastling(const BitBoard &from, const BitBoard &to, const Piece
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (piece.color() == PieceColor::White) {
|
if (piece.color() == PieceColor::White) {
|
||||||
return (from & indexToBitBoard(E1));
|
return (from & BitBoard::fromIndex(E1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return from & indexToBitBoard(E8);
|
return from & BitBoard::fromIndex(E8);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Board &board) {
|
std::ostream &operator<<(std::ostream &os, const Board &board) {
|
||||||
|
82
Board.hpp
82
Board.hpp
@@ -5,7 +5,8 @@
|
|||||||
#include "Square.hpp"
|
#include "Square.hpp"
|
||||||
#include "Move.hpp"
|
#include "Move.hpp"
|
||||||
#include "CastlingRights.hpp"
|
#include "CastlingRights.hpp"
|
||||||
#include "BitBoard.h"
|
#include "BitBoard.hpp"
|
||||||
|
#include "MoveGenerator.hpp"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
@@ -13,20 +14,18 @@
|
|||||||
|
|
||||||
#define BB_NUM 8 // 6 pieces, 2 colors
|
#define BB_NUM 8 // 6 pieces, 2 colors
|
||||||
|
|
||||||
enum class BoardIndex {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Board {
|
class Board {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using Optional = std::optional<Board>;
|
using Optional = std::optional<Board>;
|
||||||
using MoveVec = std::vector<Move>;
|
using MoveVec = std::vector<Move>;
|
||||||
|
|
||||||
Board();
|
Board() = default;
|
||||||
|
|
||||||
void setPiece(const Square &square, const Piece::Optional &piece);
|
void setPiece(const Square &square, const Piece::Optional &piece);
|
||||||
Piece::Optional piece(const Square &square) const;
|
Piece::Optional piece(const Square &square) const;
|
||||||
|
std::vector<Piece> pieces(PieceColor color) const;
|
||||||
|
int evaluate() const;
|
||||||
void setTurn(PieceColor turn);
|
void setTurn(PieceColor turn);
|
||||||
PieceColor turn() const;
|
PieceColor turn() const;
|
||||||
void setCastlingRights(CastlingRights cr);
|
void setCastlingRights(CastlingRights cr);
|
||||||
@@ -39,61 +38,32 @@ public:
|
|||||||
void pseudoLegalMoves(MoveVec &moves) const;
|
void pseudoLegalMoves(MoveVec &moves) const;
|
||||||
void pseudoLegalMovesFrom(const Square &from, MoveVec &moves) const;
|
void pseudoLegalMovesFrom(const Square &from, MoveVec &moves) const;
|
||||||
|
|
||||||
|
static constexpr int toIndex(PieceType t) {
|
||||||
|
return static_cast<int>(t);
|
||||||
|
}
|
||||||
|
static constexpr int toIndex(PieceColor c) {
|
||||||
|
return static_cast<int>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isCheckMate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
BitBoard mPieceBBs[BB_NUM] = {};
|
BitBoard mPieceBBs[BB_NUM] = {};
|
||||||
BitBoard mOccupiedBB = 0;
|
BitBoard mOccupiedBB = BitBoard(0);
|
||||||
|
|
||||||
PieceColor mTurn = PieceColor::White;
|
PieceColor mTurn = PieceColor(PieceColor::White);
|
||||||
CastlingRights mCR = CastlingRights::None;
|
CastlingRights mCR = CastlingRights::None;
|
||||||
std::optional<Square> mEPS;
|
std::optional<Square> mEPS;
|
||||||
|
|
||||||
enum DefinedBoards : uint64_t {
|
constexpr static const double mMobilityWeight = 0.1;
|
||||||
AFile = 0x0101010101010101,
|
|
||||||
BFile = AFile << 1,
|
|
||||||
CFile = AFile << 2,
|
|
||||||
DFile = AFile << 3,
|
|
||||||
EFile = AFile << 4,
|
|
||||||
FFile = AFile << 5,
|
|
||||||
GFile = AFile << 6,
|
|
||||||
HFile = AFile << 7,
|
|
||||||
WhiteCastlingRank = (1ULL << A2) - 1,
|
|
||||||
BlackCastlingRank = (~1ULL << H7),
|
|
||||||
CastlingRanks = WhiteCastlingRank | BlackCastlingRank,
|
|
||||||
CastlingSquares = (WhiteCastlingRank | BlackCastlingRank) & (CFile | GFile),
|
|
||||||
};
|
|
||||||
|
|
||||||
void handleCastlingRights(const Piece &piece, const BitBoard &bb);
|
void handleCastlingRights(const Piece &piece, const BitBoard &bb);
|
||||||
|
|
||||||
// Check if the move is castling without checking the rights or validity.
|
// Check if the move is castling without checking the rights or validity.
|
||||||
static bool isMoveCastling(const BitBoard &from, const BitBoard &to, const Piece &piece);
|
static bool isMoveCastling(const BitBoard &from, const BitBoard &to, const Piece &piece);
|
||||||
|
|
||||||
static inline void clearIndex(BitBoard &b, const unsigned i) {
|
|
||||||
b &= ~(1ULL << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void setIndex(BitBoard &b, const unsigned i) {
|
|
||||||
b |= 1ULL << i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BitBoard indexToBitBoard(const unsigned i) {
|
|
||||||
return (1ULL << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BitBoard genShift(BitBoard x, const int s) {
|
|
||||||
return (s < 0) ? (x >> -s) : (s > 63) ? x : (x << s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BitBoard getRankBB(int r) {
|
|
||||||
return (genShift(1ULL, (r + 1) * 8) - 1) & genShift(~1ULL, r * 8 - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int toIndex(PieceType t) {
|
|
||||||
return static_cast<int>(t);
|
|
||||||
}
|
|
||||||
static inline int toIndex(PieceColor c) {
|
|
||||||
return static_cast<int>(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline PieceColor pieceColor(const BitBoard &mask) const {
|
inline PieceColor pieceColor(const BitBoard &mask) const {
|
||||||
auto color = PieceColor::White;
|
auto color = PieceColor::White;
|
||||||
if (!(mPieceBBs[static_cast<unsigned>(color)] & mask)) {
|
if (!(mPieceBBs[static_cast<unsigned>(color)] & mask)) {
|
||||||
@@ -114,22 +84,10 @@ private:
|
|||||||
return static_cast<PieceType>(0);
|
return static_cast<PieceType>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void applyMask(const BitBoard &mask) {
|
|
||||||
for (auto &item : mPieceBBs) {
|
|
||||||
item ^= mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of trailing 0-bits in b.
|
|
||||||
// WARN: Check for 0!
|
|
||||||
static inline int getLSB(const BitBoard b) {
|
|
||||||
return __builtin_ctzll(static_cast<unsigned long long>(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
void handlePawnDoubleAdvance(const Move &move, BitBoard bb, const Piece &movedPiece);
|
void handlePawnDoubleAdvance(const Move &move, BitBoard bb, const Piece &movedPiece);
|
||||||
void handleEnPassant(const Move &move, const Piece &movedPiece);
|
void handleEnPassant(const Move &move, const Piece &movedPiece);
|
||||||
|
int evaluate(const PieceColor color) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Board &board);
|
std::ostream &operator<<(std::ostream &os, const Board &board);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
42
BoardState.hpp
Normal file
42
BoardState.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef CHESS_ENGINE_BOARDSTATE_HPP
|
||||||
|
#define CHESS_ENGINE_BOARDSTATE_HPP
|
||||||
|
|
||||||
|
#include "Piece.hpp"
|
||||||
|
#include "BitBoard.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
|
#define BB_NUM 8
|
||||||
|
|
||||||
|
struct BoardState {
|
||||||
|
BoardState(const BitBoard (*const pieceBBs)[8],
|
||||||
|
const BitBoard occupiedBB,
|
||||||
|
const PieceColor turn,
|
||||||
|
const CastlingRights cr,
|
||||||
|
const std::optional<Square> eps)
|
||||||
|
: pieceBBs(pieceBBs),
|
||||||
|
occupiedBB(occupiedBB),
|
||||||
|
turn(turn),
|
||||||
|
cr(cr),
|
||||||
|
eps(eps) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const BitBoard (*const pieceBBs)[8];
|
||||||
|
const BitBoard occupiedBB;
|
||||||
|
const PieceColor turn;
|
||||||
|
const CastlingRights cr;
|
||||||
|
const std::optional<Square> eps;
|
||||||
|
|
||||||
|
inline PieceType pieceType(const BitBoard &mask) const {
|
||||||
|
for (int i = 2; i < BB_NUM; i++) {
|
||||||
|
if ((*pieceBBs)[i] & mask) {
|
||||||
|
return static_cast<PieceType>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not happen
|
||||||
|
return static_cast<PieceType>(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CHESS_ENGINE_BOARDSTATE_HPP
|
@@ -24,6 +24,10 @@ else ()
|
|||||||
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
add_compile_options(-O3 -march=native -funroll-loops)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(cplchess_lib OBJECT
|
add_library(cplchess_lib OBJECT
|
||||||
Square.cpp
|
Square.cpp
|
||||||
Move.cpp
|
Move.cpp
|
||||||
@@ -36,6 +40,9 @@ add_library(cplchess_lib OBJECT
|
|||||||
EngineFactory.cpp
|
EngineFactory.cpp
|
||||||
Uci.cpp
|
Uci.cpp
|
||||||
BitBoard.cpp
|
BitBoard.cpp
|
||||||
|
MoveGenerator.cpp
|
||||||
|
Search.cpp
|
||||||
|
BasicEngine.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(cplchess_lib PUBLIC .)
|
target_include_directories(cplchess_lib PUBLIC .)
|
||||||
|
@@ -11,6 +11,8 @@ enum class CastlingRights {
|
|||||||
BlackQueenside = 1 << 3,
|
BlackQueenside = 1 << 3,
|
||||||
White = WhiteKingside | WhiteQueenside,
|
White = WhiteKingside | WhiteQueenside,
|
||||||
Black = BlackKingside | BlackQueenside,
|
Black = BlackKingside | BlackQueenside,
|
||||||
|
KingSide = BlackKingside | WhiteKingside,
|
||||||
|
QueenSide = BlackQueenside | WhiteQueenside,
|
||||||
All = White | Black
|
All = White | Black
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
#include "EngineFactory.hpp"
|
#include "EngineFactory.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "BasicEngine.hpp"
|
||||||
|
|
||||||
std::unique_ptr<Engine> EngineFactory::createEngine() {
|
std::unique_ptr<Engine> EngineFactory::createEngine() {
|
||||||
return nullptr;
|
return std::make_unique<BasicEngine>();
|
||||||
}
|
}
|
||||||
|
39
Move.cpp
39
Move.cpp
@@ -3,10 +3,21 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
Move::Move(const Square &from, const Square &to, const std::optional<PieceType> &promotion)
|
|
||||||
: mFrom(from), mTo(to), mPromotion(promotion) {
|
Move::Move(const Square &from, const Square &to) : mFrom(from), mTo(to) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Move::Move(const Square &from, const Square &to, const std::optional<PieceType> &promotion, unsigned score)
|
||||||
|
: mFrom(from), mTo(to), mPromotion(promotion) {
|
||||||
|
mScore -= score;
|
||||||
|
}
|
||||||
|
|
||||||
|
Move::Move(const Square &from, const Square &to, unsigned int score) : mFrom(from), mTo(to) {
|
||||||
|
mScore -= score;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Move::Optional Move::fromUci(const std::string &uci) {
|
Move::Optional Move::fromUci(const std::string &uci) {
|
||||||
if (uci.length() < 4 || uci.length() > 5)
|
if (uci.length() < 4 || uci.length() > 5)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -48,17 +59,31 @@ std::ostream &operator<<(std::ostream &os, const Move &move) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const Move &lhs, const Move &rhs) {
|
bool operator<(const Move &lhs, const Move &rhs) {
|
||||||
if (!(lhs.from() == rhs.from()))
|
if (lhs.mScore != rhs.mScore) {
|
||||||
return lhs.from() < rhs.from();
|
return lhs.mScore < rhs.mScore;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(lhs.to() == rhs.to()))
|
if (lhs.mFrom.index() != rhs.mFrom.index()) {
|
||||||
return lhs.to() < rhs.to();
|
return lhs.mFrom.index() < rhs.mFrom.index();
|
||||||
|
}
|
||||||
|
if (lhs.mTo.index() != rhs.mTo.index()) {
|
||||||
|
return lhs.mTo.index() < rhs.mTo.index();
|
||||||
|
}
|
||||||
|
|
||||||
return lhs.promotion() < rhs.promotion();
|
return lhs.promotion() < rhs.promotion();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Move &lhs, const Move &rhs) {
|
bool operator==(const Move &lhs, const Move &rhs) {
|
||||||
return lhs.from() == rhs.from()
|
return lhs.score() == rhs.score()
|
||||||
|
&& lhs.from() == rhs.from()
|
||||||
&& lhs.to() == rhs.to()
|
&& lhs.to() == rhs.to()
|
||||||
&& lhs.promotion() == rhs.promotion();
|
&& lhs.promotion() == rhs.promotion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Move::score() const {
|
||||||
|
return mScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Move::setScore(unsigned int score) {
|
||||||
|
mScore = score;
|
||||||
|
}
|
||||||
|
24
Move.hpp
24
Move.hpp
@@ -7,14 +7,17 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
class Move {
|
class Move {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using Optional = std::optional<Move>;
|
using Optional = std::optional<Move>;
|
||||||
|
|
||||||
Move(const Square &from, const Square &to,
|
explicit Move(const Square &from, const Square &to);
|
||||||
const std::optional<PieceType> &promotion = std::nullopt);
|
explicit Move(const Square &from, const Square &to,
|
||||||
|
const std::optional<PieceType> &promotion, unsigned score = 0);
|
||||||
|
explicit Move(const Square &from, const Square &to, unsigned score);
|
||||||
|
|
||||||
static Optional fromUci(const std::string &uci);
|
static Optional fromUci(const std::string &uci);
|
||||||
|
|
||||||
@@ -22,16 +25,23 @@ public:
|
|||||||
Square to() const;
|
Square to() const;
|
||||||
std::optional<PieceType> promotion() const;
|
std::optional<PieceType> promotion() const;
|
||||||
|
|
||||||
|
|
||||||
|
friend bool operator<(const Move &lhs, const Move &rhs);
|
||||||
|
friend bool operator==(const Move &lhs, const Move &rhs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Square mFrom;
|
Square mFrom;
|
||||||
const Square mTo;
|
Square mTo;
|
||||||
const std::optional<PieceType> mPromotion;
|
std::optional<PieceType> mPromotion = std::nullopt;
|
||||||
|
unsigned mScore = std::numeric_limits<unsigned>::max();
|
||||||
|
public:
|
||||||
|
unsigned int score() const;
|
||||||
|
|
||||||
|
void setScore(unsigned int mScore);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Move &move);
|
std::ostream &operator<<(std::ostream &os, const Move &move);
|
||||||
|
|
||||||
// Needed for std::map, std::set
|
// Needed for std::map, std::set
|
||||||
bool operator<(const Move &lhs, const Move &rhs);
|
|
||||||
bool operator==(const Move &lhs, const Move &rhs);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
233
MoveGenerator.cpp
Normal file
233
MoveGenerator.cpp
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "MoveGenerator.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
#include "BoardState.hpp"
|
||||||
|
|
||||||
|
void MoveGenerator::generatePawnMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
|
||||||
|
BitBoard targets = 0;
|
||||||
|
if (bs.eps.has_value()) {
|
||||||
|
targets |= BitBoard::fromIndex(bs.eps.value().index());
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePawnMoves(bs, from, targets, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generatePawnMoves(const BoardState &bs, const Square &from, BitBoard targets, MoveVec &moves) {
|
||||||
|
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
targets |= (*bs.pieceBBs)[Board::toIndex(!bs.turn)];
|
||||||
|
|
||||||
|
BitBoard movesBB;
|
||||||
|
if (bs.turn == PieceColor::White) {
|
||||||
|
movesBB = BitBoard::pawnNorthAttacks(fromBB, targets) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
movesBB |= BitBoard::pawnNorthMoves(fromBB, ~bs.occupiedBB);
|
||||||
|
} else {
|
||||||
|
movesBB = BitBoard::pawnSouthAttacks(fromBB, targets) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
movesBB |= BitBoard::pawnSouthMoves(fromBB, ~bs.occupiedBB);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPromotion = movesBB & (Rank1 | Rank8);
|
||||||
|
if (isPromotion) {
|
||||||
|
generateMovesWithPromotion(bs, from, movesBB, moves);
|
||||||
|
} else {
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::Pawn, moves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generateBishopMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
auto movesBB = BitBoard::bishopAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::Bishop, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MoveGenerator::generateMoves(const BoardState &bs, const Square &from, BitBoard movesBB, PieceType pt, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
while (movesBB) {
|
||||||
|
auto to = Square(movesBB.pop());
|
||||||
|
auto toBB = BitBoard::fromIndex(to.index());
|
||||||
|
if (isCheck(bs, fromBB, toBB)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
moves.emplace_back(from, to, score(bs, toBB, pt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MoveGenerator::generateMovesWithPromotion(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
while (movesBB) {
|
||||||
|
auto to = Square(movesBB.pop());
|
||||||
|
auto toBB = BitBoard::fromIndex(to.index());
|
||||||
|
if (isCheck(bs, fromBB, toBB)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &kItem: Piece::PromotionTypes) {
|
||||||
|
|
||||||
|
unsigned s = Piece::PromotionScore[Board::toIndex(kItem) - 2] + score(bs, toBB, PieceType::Pawn);
|
||||||
|
moves.emplace_back(from, to, static_cast<PieceType>(kItem), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generateKingMoves(const BoardState &bs, const Square &from, MoveGenerator::MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
auto movesBB = BitBoard::kingMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
|
if ((bs.cr & CastlingRights::All) != CastlingRights::None) {
|
||||||
|
|
||||||
|
auto checkCR = CastlingRights::White;
|
||||||
|
auto castlingRank = WhiteCastlingRank;
|
||||||
|
if (bs.turn == PieceColor::Black) {
|
||||||
|
checkCR = CastlingRights::Black;
|
||||||
|
castlingRank = BlackCastlingRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCR &= bs.cr;
|
||||||
|
|
||||||
|
if (checkCR != CastlingRights::None) {
|
||||||
|
// Generate attacked squares
|
||||||
|
BitBoard target = CastlingRanks | bs.occupiedBB;
|
||||||
|
auto attacked = generateAttackedSquares(bs, ~target, !bs.turn);
|
||||||
|
|
||||||
|
movesBB |= BitBoard::castlingMoves(fromBB & ~attacked,
|
||||||
|
(*bs.pieceBBs)[Board::toIndex(PieceType::Rook)],
|
||||||
|
~bs.occupiedBB) & castlingRank;
|
||||||
|
|
||||||
|
if ((checkCR & CastlingRights::KingSide) == CastlingRights::None) {
|
||||||
|
movesBB &= ~GFile;
|
||||||
|
}
|
||||||
|
if ((checkCR & CastlingRights::QueenSide) == CastlingRights::None) {
|
||||||
|
movesBB &= ~CFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::King, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generateRookMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
auto movesBB = BitBoard::rookAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::Rook, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
auto movesBB = BitBoard::queenAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::Queen, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveGenerator::generateKnightMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
|
auto movesBB = BitBoard::knightMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
generateMoves(bs, from, movesBB, PieceType::Knight, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor) {
|
||||||
|
auto opponentBB = (*bs.pieceBBs)[Board::toIndex(opColor)];
|
||||||
|
BitBoard attacked = 0;
|
||||||
|
|
||||||
|
// pawns
|
||||||
|
if (opColor == PieceColor::White) {
|
||||||
|
attacked |= BitBoard::pawnNorthAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & opponentBB, target);
|
||||||
|
} else {
|
||||||
|
attacked |= BitBoard::pawnSouthAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & opponentBB, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// knights
|
||||||
|
attacked |= BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & opponentBB) & ~target;
|
||||||
|
|
||||||
|
// Bishop
|
||||||
|
attacked |= BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & opponentBB, target);
|
||||||
|
|
||||||
|
// Rook
|
||||||
|
attacked |= BitBoard::rookAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Rook)] & opponentBB, target);
|
||||||
|
|
||||||
|
// Queen
|
||||||
|
attacked |= BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB, target);
|
||||||
|
|
||||||
|
// King
|
||||||
|
attacked |= BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB) & ~target;
|
||||||
|
|
||||||
|
return attacked;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool MoveGenerator::isCheck(const BoardState &bs, const BitBoard &fromBB, const BitBoard &toBB) {
|
||||||
|
auto kingBB = (*bs.pieceBBs)[Board::toIndex(bs.turn)] & (*bs.pieceBBs)[Board::toIndex(PieceType::King)];
|
||||||
|
auto changeBB = (fromBB ^ toBB);
|
||||||
|
auto target = bs.occupiedBB ^ changeBB;
|
||||||
|
|
||||||
|
if (kingBB & fromBB) {
|
||||||
|
|
||||||
|
if (toBB & bs.occupiedBB) {
|
||||||
|
target ^= toBB;
|
||||||
|
}
|
||||||
|
if ((toBB & ~bs.occupiedBB)) {
|
||||||
|
kingBB ^= changeBB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto attacked = generateAttackedSquares(bs, ~target, !bs.turn);
|
||||||
|
|
||||||
|
return attacked & kingBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned MoveGenerator::score(const BoardState &bs, const BitBoard &toBB, const PieceType moved) {
|
||||||
|
|
||||||
|
unsigned score = 0;
|
||||||
|
auto opponentBB = bs.occupiedBB & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
|
|
||||||
|
// Check Capture
|
||||||
|
auto captureBB = toBB & opponentBB;
|
||||||
|
if (captureBB) {
|
||||||
|
auto captured = bs.pieceType(captureBB);
|
||||||
|
score = Piece::MVV_LVA[Board::toIndex(captured)][Board::toIndex(moved)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned MoveGenerator::Mobility(const BoardState &bs, PieceColor color) {
|
||||||
|
unsigned count = 0;
|
||||||
|
auto colorMask = (*bs.pieceBBs)[Board::toIndex(color)];
|
||||||
|
auto targets = (*bs.pieceBBs)[Board::toIndex(!color)];
|
||||||
|
|
||||||
|
// Pawns
|
||||||
|
auto pawnsBB = (*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & colorMask;
|
||||||
|
if (color == PieceColor::White) {
|
||||||
|
auto movesBB = BitBoard::pawnNorthAttacks(pawnsBB, targets) & ~colorMask;
|
||||||
|
movesBB |= BitBoard::pawnNorthMoves(pawnsBB, ~bs.occupiedBB);
|
||||||
|
count += movesBB.count();
|
||||||
|
} else {
|
||||||
|
auto movesBB = BitBoard::pawnSouthAttacks(pawnsBB, targets) & ~colorMask;
|
||||||
|
movesBB |= BitBoard::pawnSouthMoves(pawnsBB, ~bs.occupiedBB);
|
||||||
|
count += movesBB.count();
|
||||||
|
}
|
||||||
|
// Bishops
|
||||||
|
count += (BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & colorMask, ~bs.occupiedBB) &
|
||||||
|
~colorMask).count();
|
||||||
|
// Knights
|
||||||
|
count += (BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & colorMask) &
|
||||||
|
~colorMask).count();
|
||||||
|
// Rook
|
||||||
|
count += (BitBoard::rookAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Rook)] & colorMask, ~bs.occupiedBB) &
|
||||||
|
~colorMask).count();
|
||||||
|
// Queen
|
||||||
|
count += (BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & colorMask, ~bs.occupiedBB) &
|
||||||
|
~colorMask).count();
|
||||||
|
// King
|
||||||
|
count += (BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::King)] & colorMask) & ~colorMask).count();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
40
MoveGenerator.hpp
Normal file
40
MoveGenerator.hpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef CHESS_ENGINE_MOVEGENERATOR_HPP
|
||||||
|
#define CHESS_ENGINE_MOVEGENERATOR_HPP
|
||||||
|
|
||||||
|
#include "Piece.hpp"
|
||||||
|
#include "Square.hpp"
|
||||||
|
#include "Move.hpp"
|
||||||
|
#include "CastlingRights.hpp"
|
||||||
|
#include "BitBoard.hpp"
|
||||||
|
#include "BoardState.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class MoveGenerator {
|
||||||
|
public:
|
||||||
|
using MoveVec = std::vector<Move>;
|
||||||
|
|
||||||
|
static void generatePawnMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static void generateBishopMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static void generateKingMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static void generateRookMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static void generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static void generateKnightMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
|
static BitBoard generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor);
|
||||||
|
|
||||||
|
static unsigned int Mobility(const BoardState &bs, PieceColor color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void generatePawnMoves(const BoardState &bs, const Square &from, BitBoard targets, MoveVec &moves);
|
||||||
|
|
||||||
|
static void generateMoves(const BoardState &bs, const Square &from, BitBoard movesBB, PieceType pt, MoveVec &moves);
|
||||||
|
static void generateMovesWithPromotion(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves);
|
||||||
|
|
||||||
|
inline static bool isCheck(const BoardState &bs, const BitBoard &fromBB, const BitBoard &toBB);
|
||||||
|
inline static unsigned score(const BoardState &bs, const BitBoard &toBB, PieceType moved);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CHESS_ENGINE_MOVEGENERATOR_HPP
|
18
Piece.hpp
18
Piece.hpp
@@ -24,17 +24,34 @@ class Piece {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
constexpr static const PieceType PromotionTypes[4] = {PieceType::Knight, PieceType::Bishop, PieceType::Rook,
|
||||||
|
PieceType::Queen};
|
||||||
|
constexpr static const unsigned PieceValue[6] = {100, 350, 350, 525, 1000, 10000};
|
||||||
|
constexpr static const unsigned PromotionScore[6] = {0, 26, 36, 46, 56, 0};
|
||||||
|
|
||||||
|
constexpr static const unsigned MVV_LVA[6][6] = {
|
||||||
|
{15, 14, 13, 12, 11, 10}, // Pawn
|
||||||
|
{25, 24, 23, 22, 21, 20}, // Knight
|
||||||
|
{35, 34, 33, 32, 31, 30}, // Bishop
|
||||||
|
{45, 44, 43, 42, 41, 40}, // Rook
|
||||||
|
{55, 54, 53, 52, 51, 50}, // Queen
|
||||||
|
{65, 64, 63, 62, 61, 60}, // King
|
||||||
|
};
|
||||||
|
|
||||||
using Optional = std::optional<Piece>;
|
using Optional = std::optional<Piece>;
|
||||||
|
|
||||||
Piece(PieceColor color, PieceType type);
|
Piece(PieceColor color, PieceType type);
|
||||||
|
|
||||||
static Optional fromSymbol(char symbol);
|
static Optional fromSymbol(char symbol);
|
||||||
|
|
||||||
static char toSymbol(PieceType type);
|
static char toSymbol(PieceType type);
|
||||||
|
|
||||||
static std::optional<PieceType> pieceTypeFromSymbol(char symbol);
|
static std::optional<PieceType> pieceTypeFromSymbol(char symbol);
|
||||||
|
|
||||||
PieceColor color() const;
|
PieceColor color() const;
|
||||||
|
|
||||||
PieceType type() const;
|
PieceType type() const;
|
||||||
|
|
||||||
char toSymbol() const;
|
char toSymbol() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -43,6 +60,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const Piece &lhs, const Piece &rhs);
|
bool operator==(const Piece &lhs, const Piece &rhs);
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Piece &piece);
|
std::ostream &operator<<(std::ostream &os, const Piece &piece);
|
||||||
|
|
||||||
// Invert a color (White becomes Black and vice versa)
|
// Invert a color (White becomes Black and vice versa)
|
||||||
|
@@ -1,29 +1,53 @@
|
|||||||
#include "PrincipalVariation.hpp"
|
#include "PrincipalVariation.hpp"
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
PrincipalVariation::PrincipalVariation(const std::vector<Move> &v, bool isMate): mMoves(v), mIsMate(isMate) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PrincipalVariation::PrincipalVariation(const std::vector<Move> &v, Board &board): mMoves(v) {
|
||||||
|
for (const auto &kMove : v) {
|
||||||
|
board.makeMove(kMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsMate = board.isCheckMate();
|
||||||
|
if (mIsMate || v.empty()) {
|
||||||
|
mScore = static_cast<int>(v.size());
|
||||||
|
} else {
|
||||||
|
mScore = board.evaluate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PrincipalVariation::isMate() const {
|
bool PrincipalVariation::isMate() const {
|
||||||
return false;
|
return mIsMate;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PrincipalVariation::score() const {
|
int PrincipalVariation::score() const {
|
||||||
return 0;
|
return mScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t PrincipalVariation::length() const {
|
std::size_t PrincipalVariation::length() const {
|
||||||
return 0;
|
return mMoves.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrincipalVariation::MoveIter PrincipalVariation::begin() const {
|
PrincipalVariation::MoveIter PrincipalVariation::begin() const {
|
||||||
return nullptr;
|
return mMoves.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrincipalVariation::MoveIter PrincipalVariation::end() const {
|
PrincipalVariation::MoveIter PrincipalVariation::end() const {
|
||||||
return nullptr;
|
return mMoves.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const PrincipalVariation &pv) {
|
std::ostream &operator<<(std::ostream &os, const PrincipalVariation &pv) {
|
||||||
(void)pv;
|
os << "isMate=" << pv.isMate();
|
||||||
|
os << " score=" << pv.score();
|
||||||
|
os << " length=" << pv.length() << std::endl;
|
||||||
|
os << "Moves:" << std::endl;
|
||||||
|
for (const auto &item : pv) {
|
||||||
|
os << item << std::endl;
|
||||||
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
@@ -3,21 +3,31 @@
|
|||||||
|
|
||||||
#include "Move.hpp"
|
#include "Move.hpp"
|
||||||
#include "Piece.hpp"
|
#include "Piece.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class PrincipalVariation {
|
class PrincipalVariation {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using MoveIter = Move*;
|
using MoveIter = std::vector<Move>::const_iterator;
|
||||||
|
|
||||||
|
PrincipalVariation() = default;
|
||||||
|
PrincipalVariation(const std::vector<Move> &v, bool isMate);
|
||||||
|
PrincipalVariation(const std::vector<Move> &v, Board &board);
|
||||||
bool isMate() const;
|
bool isMate() const;
|
||||||
int score() const;
|
int score() const;
|
||||||
|
|
||||||
std::size_t length() const;
|
std::size_t length() const;
|
||||||
MoveIter begin() const;
|
MoveIter begin() const;
|
||||||
MoveIter end() const;
|
MoveIter end() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Move> mMoves;
|
||||||
|
bool mIsMate = false;
|
||||||
|
int mScore = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const PrincipalVariation &pv);
|
std::ostream &operator<<(std::ostream &os, const PrincipalVariation &pv);
|
||||||
|
86
Search.cpp
Normal file
86
Search.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "Search.hpp"
|
||||||
|
#include "Move.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
int Search::search(Node *node, const Board &board, unsigned depth, int alpha, int beta) {
|
||||||
|
|
||||||
|
if (depth == 0) {
|
||||||
|
return evaluate(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moves = Board::MoveVec();
|
||||||
|
board.pseudoLegalMoves(moves);
|
||||||
|
if (moves.empty()) {
|
||||||
|
return evaluate(board);
|
||||||
|
}
|
||||||
|
std::stable_sort(moves.begin(), moves.end());
|
||||||
|
|
||||||
|
int maxValue = kNegInfinity;
|
||||||
|
for (auto &kMove: moves) {
|
||||||
|
Board nodeBoard = board;
|
||||||
|
nodeBoard.makeMove(kMove);
|
||||||
|
|
||||||
|
Node child(kMove);
|
||||||
|
child.value = search(&child, nodeBoard, depth - 1, -beta, -alpha);
|
||||||
|
|
||||||
|
auto value = -child.value;
|
||||||
|
if (value > maxValue) {
|
||||||
|
maxValue = value;
|
||||||
|
node->next = std::make_unique<Node>(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha = std::max(alpha, value);
|
||||||
|
if (alpha >= beta) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node->value = maxValue;
|
||||||
|
|
||||||
|
return maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Search::evaluate(const Board &board) {
|
||||||
|
|
||||||
|
return board.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PrincipalVariation Search::start(const Board &board) {
|
||||||
|
auto rootMoves = Board::MoveVec();
|
||||||
|
board.pseudoLegalMoves(rootMoves);
|
||||||
|
std::stable_sort(rootMoves.begin(), rootMoves.end());
|
||||||
|
|
||||||
|
if (rootMoves.size() <= 1) {
|
||||||
|
auto b = board;
|
||||||
|
return PrincipalVariation(rootMoves, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RootNode> best = std::make_unique<RootNode>();
|
||||||
|
for (const auto &kMove: rootMoves) {
|
||||||
|
|
||||||
|
auto node = Node(kMove);
|
||||||
|
|
||||||
|
Board nodeBoard = board;
|
||||||
|
nodeBoard.makeMove(kMove);
|
||||||
|
|
||||||
|
auto value = -search(&node, nodeBoard, 5, kNegInfinity, kPosInfinity);
|
||||||
|
if (value > best->value) {
|
||||||
|
best->child = std::make_unique<Node>(node);
|
||||||
|
best->board = nodeBoard;
|
||||||
|
best->value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moves = std::vector<Move>();
|
||||||
|
auto current = std::move(best->child);
|
||||||
|
do {
|
||||||
|
moves.push_back(current->move);
|
||||||
|
current = std::move(current->next);
|
||||||
|
} while (current);
|
||||||
|
|
||||||
|
return PrincipalVariation(moves, best->board);
|
||||||
|
}
|
39
Search.hpp
Normal file
39
Search.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef CHESS_ENGINE_SEARCH_HPP
|
||||||
|
#define CHESS_ENGINE_SEARCH_HPP
|
||||||
|
|
||||||
|
#include "PrincipalVariation.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace Search {
|
||||||
|
|
||||||
|
const int kPosInfinity = std::numeric_limits<int>::max();
|
||||||
|
const int kNegInfinity = std::numeric_limits<int>::min();
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
explicit Node(const Move &move) : move(move) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Node(Node &node) : move(node.move) {
|
||||||
|
next = std::move(node.next);
|
||||||
|
value = node.value;
|
||||||
|
}
|
||||||
|
std::unique_ptr<Node> next;
|
||||||
|
Move move;
|
||||||
|
int value = kNegInfinity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RootNode {
|
||||||
|
std::unique_ptr<Node> child;
|
||||||
|
Board board;
|
||||||
|
int value = kNegInfinity;
|
||||||
|
};
|
||||||
|
|
||||||
|
int search(Node *node, const Board &board, unsigned depth, int alpha, int beta);
|
||||||
|
PrincipalVariation start(const Board &board);
|
||||||
|
int evaluate(const Board &board);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //CHESS_ENGINE_SEARCH_HPP
|
@@ -26,6 +26,9 @@ public:
|
|||||||
static Optional fromIndex(Index index);
|
static Optional fromIndex(Index index);
|
||||||
static Optional fromName(const std::string &name);
|
static Optional fromName(const std::string &name);
|
||||||
|
|
||||||
|
Square(Index index);
|
||||||
|
|
||||||
|
|
||||||
Coordinate file() const;
|
Coordinate file() const;
|
||||||
Coordinate rank() const;
|
Coordinate rank() const;
|
||||||
Index index() const;
|
Index index() const;
|
||||||
@@ -40,9 +43,6 @@ public:
|
|||||||
static const Square A8, B8, C8, D8, E8, F8, G8, H8;
|
static const Square A8, B8, C8, D8, E8, F8, G8, H8;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Square(Index index);
|
|
||||||
|
|
||||||
Index mIndex;
|
Index mIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
22
Tests/BitBoardTest.cpp
Normal file
22
Tests/BitBoardTest.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
#include "TestUtils.hpp"
|
||||||
|
|
||||||
|
#include "BitBoard.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
TEST_CASE("King attacks are correctly generated", "[Board][Fundamental]") {
|
||||||
|
auto board = BitBoard::fromIndex(G2);
|
||||||
|
board |= BitBoard::fromIndex(B7);
|
||||||
|
|
||||||
|
BitBoard ka = BitBoard::kingMoves(board);
|
||||||
|
REQUIRE((ka == (
|
||||||
|
BitBoard::fromIndex(G1) | BitBoard::fromIndex(G3) | BitBoard::fromIndex(H1) | BitBoard::fromIndex(H2)
|
||||||
|
| BitBoard::fromIndex(H3) | BitBoard::fromIndex(F1) | BitBoard::fromIndex(F2)
|
||||||
|
| BitBoard::fromIndex(F3) |
|
||||||
|
BitBoard::fromIndex(B8) | BitBoard::fromIndex(B6) | BitBoard::fromIndex(A6)
|
||||||
|
| BitBoard::fromIndex(A7) | BitBoard::fromIndex(A8) | BitBoard::fromIndex(C6)
|
||||||
|
| BitBoard::fromIndex(C7) | BitBoard::fromIndex(C8))));
|
||||||
|
}
|
@@ -224,6 +224,18 @@ TEST_CASE_PSEUDO_MOVES("Pseudo-legal bishop moves, empty board", "[Bishop]") {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_PSEUDO_MOVES("Pseudo-legal bishop moves, empty board side", "[Bishop]") {
|
||||||
|
testPseudoLegalMoves(
|
||||||
|
// https://lichess.org/editor/B7/8/8/8/8/8/8/8_w_-_-_0_1
|
||||||
|
"B7/8/8/8/8/8/8/8 w - - 0 1",
|
||||||
|
"a8",
|
||||||
|
{
|
||||||
|
"b7", "c6", "d5",
|
||||||
|
"e4", "f3", "g2", "h1"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_PSEUDO_MOVES("Pseudo-legal bishop moves, captures", "[Bishop]") {
|
TEST_CASE_PSEUDO_MOVES("Pseudo-legal bishop moves, captures", "[Bishop]") {
|
||||||
testPseudoLegalMoves(
|
testPseudoLegalMoves(
|
||||||
// https://lichess.org/editor/8/8/1p3P2/8/3B4/8/8/8_w_-_-_0_1
|
// https://lichess.org/editor/8/8/1p3P2/8/3B4/8/8/8_w_-_-_0_1
|
||||||
@@ -252,6 +264,41 @@ TEST_CASE_PSEUDO_MOVES("Pseudo-legal rook moves, empty board", "[Rook]") {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_PSEUDO_MOVES("Pseudo-legal rook moves, empty board side", "[Rook]") {
|
||||||
|
testPseudoLegalMoves(
|
||||||
|
// https://lichess.org/editor/3R4/8/8/8/8/8/8/8_w_-_-_0_1
|
||||||
|
"3R4/8/8/8/8/8/8/8 w - - 0 1",
|
||||||
|
"d8",
|
||||||
|
{
|
||||||
|
"d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
||||||
|
"a8", "b8", "c8",
|
||||||
|
"e8", "f8", "g8",
|
||||||
|
"h8"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE_PSEUDO_MOVES("legal rook moves, king check", "[Rook]") {
|
||||||
|
testPseudoLegalMoves(
|
||||||
|
// https://lichess.org/editor/5R1k/6pp/8/8/8/8/8/K7_b_-_-_0_1
|
||||||
|
"5R1k/6pp/8/8/8/8/8/K7 b - - 0 1",
|
||||||
|
"g7",
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_PSEUDO_MOVES("legal king moves, king stale", "[Rook]") {
|
||||||
|
testPseudoLegalMoves(
|
||||||
|
// https://lichess.org/editor/k7/8/8/6n1/r7/4K3/2q5/8_w_-_-_0_1
|
||||||
|
"k7/8/8/6n1/r7/4K3/2q5/8 w - - 0 1",
|
||||||
|
"e3",
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE_PSEUDO_MOVES("Pseudo-legal rook moves, captures", "[Rook]") {
|
TEST_CASE_PSEUDO_MOVES("Pseudo-legal rook moves, captures", "[Rook]") {
|
||||||
testPseudoLegalMoves(
|
testPseudoLegalMoves(
|
||||||
// https://lichess.org/editor/8/8/3p4/8/3R1P2/8/8/8_w_-_-_0_1
|
// https://lichess.org/editor/8/8/3p4/8/3R1P2/8/8/8_w_-_-_0_1
|
||||||
|
@@ -8,6 +8,7 @@ add_executable(tests
|
|||||||
BoardTests.cpp
|
BoardTests.cpp
|
||||||
FenTests.cpp
|
FenTests.cpp
|
||||||
EngineTests.cpp
|
EngineTests.cpp
|
||||||
|
BitBoardTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(tests cplchess_lib Catch2::Catch2)
|
target_link_libraries(tests cplchess_lib Catch2::Catch2)
|
||||||
|
@@ -25,6 +25,23 @@ static void testGameEnd(const char* fen, bool isMate) {
|
|||||||
REQUIRE(pv.length() == 0);
|
REQUIRE(pv.length() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testMove(const char *fen, const std::vector<std::string> &expectedMoves) {
|
||||||
|
auto engine = createEngine();
|
||||||
|
REQUIRE(engine != nullptr);
|
||||||
|
|
||||||
|
auto board = Fen::createBoard(fen);
|
||||||
|
REQUIRE(board.has_value());
|
||||||
|
|
||||||
|
for (const auto &moveName : expectedMoves) {
|
||||||
|
auto pv = engine->pv(board.value());
|
||||||
|
Move move = *pv.begin();
|
||||||
|
move.setScore(0);
|
||||||
|
REQUIRE(move == Move::fromUci(moveName));
|
||||||
|
board->makeMove(move);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Engine detects checkmate", "[Engine][Checkmate]") {
|
TEST_CASE("Engine detects checkmate", "[Engine][Checkmate]") {
|
||||||
auto fen = GENERATE(
|
auto fen = GENERATE(
|
||||||
// https://lichess.org/editor/4R2k/6pp/8/8/8/8/8/K7_b_-_-_0_1
|
// https://lichess.org/editor/4R2k/6pp/8/8/8/8/8/K7_b_-_-_0_1
|
||||||
@@ -46,3 +63,13 @@ TEST_CASE("Engine detects stalemate", "[Engine][Stalemate]") {
|
|||||||
|
|
||||||
testGameEnd(fen, false);
|
testGameEnd(fen, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Puzzles mateIn1_simple", "[Engine][Puzzle]") {
|
||||||
|
auto [fen, moves] = GENERATE(table<const char*, std::vector<std::string>>({
|
||||||
|
{"2bQ1k1r/1p3p2/p4b1p/4pNp1/2q5/8/PPP2PPP/1K1RR3 b - - 3 23",{"f6d8", "d1d8"}}, // https://lichess.org/gQa01loO/black#46
|
||||||
|
{"5rk1/ppp5/2n4p/3b2p1/6R1/P1B1P2P/1Pq5/R2Q2K1 b - - 3 29",{"c2f2"}}, // https://lichess.org/haKJxadR#57
|
||||||
|
{"r1b1k2r/ppp2pp1/2p5/2b1q1Bp/3PP1n1/2P5/PP2BPPP/RN1Q1RK1 b kq - 0 10",{"e5h2"}},
|
||||||
|
{"r3rk2/5ppQ/b1pp1q1p/p5n1/P1P5/2N5/1PB2PP1/R3R1K1 w - - 6 24", {"h7h8"}},
|
||||||
|
}));
|
||||||
|
testMove(fen, moves);
|
||||||
|
}
|
||||||
|
@@ -104,7 +104,7 @@ TEST_CASE("En passant square is correctly parsed", "[Fen][EnPassant]") {
|
|||||||
{
|
{
|
||||||
"rnbqkbnr/ppppp1pp/8/8/2PPPp2/8/PP3PPP/RNBQKBNR b KQkq e3 0 3",
|
"rnbqkbnr/ppppp1pp/8/8/2PPPp2/8/PP3PPP/RNBQKBNR b KQkq e3 0 3",
|
||||||
Square::E3
|
Square::E3
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
CAPTURE(fen, ep);
|
CAPTURE(fen, ep);
|
||||||
|
Reference in New Issue
Block a user