Implement Search
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#include "BasicEngine.hpp"
|
#include "BasicEngine.hpp"
|
||||||
|
#include "Search.hpp"
|
||||||
|
|
||||||
std::string BasicEngine::name() const {
|
std::string BasicEngine::name() const {
|
||||||
return mName;
|
return mName;
|
||||||
@@ -15,5 +16,5 @@ void BasicEngine::newGame() {
|
|||||||
PrincipalVariation BasicEngine::pv(const Board &board, const TimeInfo::Optional &timeInfo) {
|
PrincipalVariation BasicEngine::pv(const Board &board, const TimeInfo::Optional &timeInfo) {
|
||||||
(void)board;
|
(void)board;
|
||||||
(void)timeInfo;
|
(void)timeInfo;
|
||||||
return PrincipalVariation();
|
return Search::start(board);
|
||||||
}
|
}
|
23
BitBoard.cpp
23
BitBoard.cpp
@@ -1,4 +1,3 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "BitBoard.hpp"
|
#include "BitBoard.hpp"
|
||||||
|
|
||||||
BitBoard::BitBoard(uint64_t v) {
|
BitBoard::BitBoard(uint64_t v) {
|
||||||
@@ -67,11 +66,12 @@ BitBoard BitBoard::castlingMoves(BitBoard kings, BitBoard rooks, BitBoard empty)
|
|||||||
|
|
||||||
BitBoard BitBoard::bishopAttacks(BitBoard bishops, BitBoard empty) {
|
BitBoard BitBoard::bishopAttacks(BitBoard bishops, BitBoard empty) {
|
||||||
BitBoard result = 0;
|
BitBoard result = 0;
|
||||||
BitBoard diag1 = bishops;
|
|
||||||
BitBoard diag2 = bishops;
|
|
||||||
empty ^= bishops;
|
empty ^= bishops;
|
||||||
|
|
||||||
for (int i = 0; i < 7; i++) {
|
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);
|
result |= (diag1 | diag2);
|
||||||
diag1 = (diag1.northWest() | diag1.southEast()) & empty;
|
diag1 = (diag1.northWest() | diag1.southEast()) & empty;
|
||||||
diag2 = (diag2.northEast() | diag2.southWest()) & empty;
|
diag2 = (diag2.northEast() | diag2.southWest()) & empty;
|
||||||
@@ -82,17 +82,18 @@ BitBoard BitBoard::bishopAttacks(BitBoard bishops, BitBoard empty) {
|
|||||||
|
|
||||||
BitBoard BitBoard::rookAttacks(BitBoard rooks, BitBoard empty) {
|
BitBoard BitBoard::rookAttacks(BitBoard rooks, BitBoard empty) {
|
||||||
BitBoard result = 0;
|
BitBoard result = 0;
|
||||||
BitBoard diag1 = rooks;
|
|
||||||
BitBoard diag2 = rooks;
|
|
||||||
empty ^= rooks;
|
empty ^= rooks;
|
||||||
|
|
||||||
for (int i = 0; i < 7; i++) {
|
BitBoard dir1 = (rooks.north() | rooks.south() | rooks) & empty;
|
||||||
result |= (diag1 | diag2);
|
BitBoard dir2 = (rooks.east() | rooks.west() | rooks) & empty;
|
||||||
diag1 = (diag1.north() | diag1.south()) & empty;
|
|
||||||
diag2 = (diag2.east() | diag2.west()) & 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 | diag1.north() | diag1.south() | diag2.east() | diag2.west()) & ~rooks;
|
return (result | dir1.north() | dir1.south() | dir2.east() | dir2.west()) & ~rooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard BitBoard::queenAttacks(BitBoard queens, BitBoard empty) {
|
BitBoard BitBoard::queenAttacks(BitBoard queens, BitBoard empty) {
|
||||||
|
@@ -127,7 +127,7 @@ public:
|
|||||||
unsigned pop();
|
unsigned pop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
U64 mBoard = {};
|
U64 mBoard = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relational operators
|
// Relational operators
|
||||||
|
68
Board.cpp
68
Board.cpp
@@ -32,6 +32,45 @@ 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 {
|
||||||
|
// Pawns
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
BitBoard bb;
|
||||||
|
BitBoard colorMask = mPieceBBs[toIndex(color)];
|
||||||
|
for (int i = 2; i < 8; i++) {
|
||||||
|
bb = mPieceBBs[i] & colorMask;
|
||||||
|
while (bb) {
|
||||||
|
bb.pop();
|
||||||
|
score += mPieceValue[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)];
|
||||||
|
|
||||||
|
return moves.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void Board::setTurn(PieceColor turn) {
|
void Board::setTurn(PieceColor turn) {
|
||||||
mTurn = turn;
|
mTurn = turn;
|
||||||
}
|
}
|
||||||
@@ -143,10 +182,11 @@ void Board::handlePawnDoubleAdvance(const Move &move, BitBoard bb, const Piece &
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Board::pseudoLegalMoves(MoveVec &moves) const {
|
void Board::pseudoLegalMoves(MoveVec &moves) const {
|
||||||
auto occupied = mOccupiedBB;
|
auto pieces = mPieceBBs[toIndex(mTurn)];
|
||||||
while(occupied) {
|
|
||||||
auto to = Square(occupied.pop());
|
while (pieces) {
|
||||||
pseudoLegalMovesFrom(to, moves);
|
auto sq = Square(pieces.pop());
|
||||||
|
pseudoLegalMovesFrom(sq, moves);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,29 +198,21 @@ void Board::pseudoLegalMovesFrom(const Square &from, Board::MoveVec &moves) cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
BoardState bs = BoardState(&mPieceBBs, mOccupiedBB, mTurn, mCR, mEPS);
|
BoardState bs = BoardState(&mPieceBBs, mOccupiedBB, mTurn, mCR, mEPS);
|
||||||
auto p = Piece(mTurn, pieceType(fromBB));
|
|
||||||
|
|
||||||
BitBoard movesBB;
|
switch (pieceType(fromBB)) {
|
||||||
switch (p.type()) {
|
|
||||||
case PieceType::Pawn: MoveGenerator::generatePawnMoves(bs, from, moves);
|
case PieceType::Pawn: MoveGenerator::generatePawnMoves(bs, from, moves);
|
||||||
return;
|
break;
|
||||||
case PieceType::Knight: MoveGenerator::generateKnightMoves(bs, from, moves);
|
case PieceType::Knight: MoveGenerator::generateKnightMoves(bs, from, moves);
|
||||||
return;
|
break;
|
||||||
case PieceType::Bishop: MoveGenerator::generateBishopMoves(bs, from, moves);
|
case PieceType::Bishop: MoveGenerator::generateBishopMoves(bs, from, moves);
|
||||||
return;
|
break;
|
||||||
case PieceType::Rook: MoveGenerator::generateRookMoves(bs, from, moves);
|
case PieceType::Rook: MoveGenerator::generateRookMoves(bs, from, moves);
|
||||||
return;
|
break;
|
||||||
case PieceType::Queen: MoveGenerator::generateQueenMoves(bs, from, moves);
|
case PieceType::Queen: MoveGenerator::generateQueenMoves(bs, from, moves);
|
||||||
return;
|
break;
|
||||||
|
|
||||||
case PieceType::King: MoveGenerator::generateKingMoves(bs, from, moves);
|
case PieceType::King: MoveGenerator::generateKingMoves(bs, from, moves);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (movesBB) {
|
|
||||||
auto to = Square(movesBB.pop());
|
|
||||||
moves.emplace_back(from, to, std::nullopt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) {
|
||||||
|
12
Board.hpp
12
Board.hpp
@@ -21,13 +21,11 @@ public:
|
|||||||
using MoveVec = std::vector<Move>;
|
using MoveVec = std::vector<Move>;
|
||||||
|
|
||||||
Board() = default;
|
Board() = default;
|
||||||
Board(const Board &) = default;
|
|
||||||
Board(Board &&other) = default;
|
|
||||||
Board &operator=(const Board &) = default;
|
|
||||||
Board &operator=(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);
|
||||||
@@ -47,6 +45,9 @@ public:
|
|||||||
return static_cast<int>(c);
|
return static_cast<int>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isCheckMate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
BitBoard mPieceBBs[BB_NUM] = {};
|
BitBoard mPieceBBs[BB_NUM] = {};
|
||||||
@@ -56,6 +57,8 @@ private:
|
|||||||
CastlingRights mCR = CastlingRights::None;
|
CastlingRights mCR = CastlingRights::None;
|
||||||
std::optional<Square> mEPS;
|
std::optional<Square> mEPS;
|
||||||
|
|
||||||
|
constexpr static const int mPieceValue[8] = {0,0, 100, 350, 350, 525, 1000, 10000};
|
||||||
|
|
||||||
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.
|
||||||
@@ -83,6 +86,7 @@ private:
|
|||||||
|
|
||||||
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);
|
||||||
|
@@ -25,7 +25,7 @@ else ()
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
add_compile_options(-O3 -march=native)
|
add_compile_options(-O3 -march=native -funroll-loops)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(cplchess_lib OBJECT
|
add_library(cplchess_lib OBJECT
|
||||||
|
6
Move.hpp
6
Move.hpp
@@ -23,9 +23,9 @@ public:
|
|||||||
std::optional<PieceType> promotion() const;
|
std::optional<PieceType> promotion() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Square mFrom;
|
Square mFrom;
|
||||||
const Square mTo;
|
Square mTo;
|
||||||
const std::optional<PieceType> mPromotion;
|
std::optional<PieceType> mPromotion;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Move &move);
|
std::ostream &operator<<(std::ostream &os, const Move &move);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#include <iostream>
|
||||||
#include "MoveGenerator.hpp"
|
#include "MoveGenerator.hpp"
|
||||||
#include "Board.hpp"
|
#include "Board.hpp"
|
||||||
#include "BoardState.hpp"
|
#include "BoardState.hpp"
|
||||||
@@ -28,9 +29,9 @@ void MoveGenerator::generatePawnMoves(const BoardState &bs, const Square &from,
|
|||||||
|
|
||||||
bool isPromotion = movesBB & (Rank1 | Rank8);
|
bool isPromotion = movesBB & (Rank1 | Rank8);
|
||||||
if (isPromotion) {
|
if (isPromotion) {
|
||||||
generateMovesWithPromotion(from, movesBB, moves);
|
generateMovesWithPromotion(bs, from, movesBB, moves);
|
||||||
} else {
|
} else {
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,19 +39,29 @@ void MoveGenerator::generateBishopMoves(const BoardState &bs, const Square &from
|
|||||||
auto fromBB = BitBoard::fromIndex(from.index());
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
auto movesBB = BitBoard::bishopAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
auto movesBB = BitBoard::bishopAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveGenerator::generateMoves(const Square &from, BitBoard movesBB, MoveVec &moves) {
|
void MoveGenerator::generateMoves(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
while (movesBB) {
|
while (movesBB) {
|
||||||
auto to = Square(movesBB.pop());
|
auto to = Square(movesBB.pop());
|
||||||
moves.emplace_back(from, to, std::nullopt);
|
if (isCheck(bs, fromBB, to)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
moves.emplace_back(from, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveGenerator::generateMovesWithPromotion(const Square &from, BitBoard movesBB, MoveVec &moves) {
|
void MoveGenerator::generateMovesWithPromotion(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves) {
|
||||||
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
while (movesBB) {
|
while (movesBB) {
|
||||||
auto to = Square(movesBB.pop());
|
auto to = Square(movesBB.pop());
|
||||||
|
if (isCheck(bs, fromBB, to)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &kItem : Piece::PromotionTypes) {
|
for (const auto &kItem : Piece::PromotionTypes) {
|
||||||
moves.emplace_back(from, to, static_cast<PieceType>(kItem));
|
moves.emplace_back(from, to, static_cast<PieceType>(kItem));
|
||||||
}
|
}
|
||||||
@@ -74,7 +85,7 @@ void MoveGenerator::generateKingMoves(const BoardState &bs, const Square &from,
|
|||||||
if (checkCR != CastlingRights::None) {
|
if (checkCR != CastlingRights::None) {
|
||||||
// Generate attacked squares
|
// Generate attacked squares
|
||||||
BitBoard target = CastlingRanks | bs.occupiedBB;
|
BitBoard target = CastlingRanks | bs.occupiedBB;
|
||||||
auto attacked = generateAttackedSquares(bs, target, !bs.turn);
|
auto attacked = generateAttackedSquares(bs, ~target, !bs.turn);
|
||||||
|
|
||||||
movesBB |= BitBoard::castlingMoves(fromBB & ~attacked,
|
movesBB |= BitBoard::castlingMoves(fromBB & ~attacked,
|
||||||
(*bs.pieceBBs)[Board::toIndex(PieceType::Rook)],
|
(*bs.pieceBBs)[Board::toIndex(PieceType::Rook)],
|
||||||
@@ -89,28 +100,28 @@ void MoveGenerator::generateKingMoves(const BoardState &bs, const Square &from,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveGenerator::generateRookMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
void MoveGenerator::generateRookMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
auto fromBB = BitBoard::fromIndex(from.index());
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
auto movesBB = BitBoard::rookAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
auto movesBB = BitBoard::rookAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveGenerator::generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
void MoveGenerator::generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
auto fromBB = BitBoard::fromIndex(from.index());
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
auto movesBB = BitBoard::queenAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
auto movesBB = BitBoard::queenAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
|
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveGenerator::generateKnightMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
void MoveGenerator::generateKnightMoves(const BoardState &bs, const Square &from, MoveVec &moves) {
|
||||||
|
|
||||||
auto fromBB = BitBoard::fromIndex(from.index());
|
auto fromBB = BitBoard::fromIndex(from.index());
|
||||||
auto movesBB = BitBoard::knightMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
auto movesBB = BitBoard::knightMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)];
|
||||||
generateMoves(from, movesBB, moves);
|
generateMoves(bs, from, movesBB, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor) {
|
BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor) {
|
||||||
@@ -125,7 +136,7 @@ BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// knights
|
// knights
|
||||||
attacked |= BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & opponentBB) & target;
|
attacked |= BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & opponentBB) & ~target;
|
||||||
|
|
||||||
// Bishop
|
// Bishop
|
||||||
attacked |= BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & opponentBB, target);
|
attacked |= BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & opponentBB, target);
|
||||||
@@ -137,7 +148,20 @@ BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard t
|
|||||||
attacked |= BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB, target);
|
attacked |= BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB, target);
|
||||||
|
|
||||||
// King
|
// King
|
||||||
attacked |= BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB) & target;
|
attacked |= BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB) & ~target;
|
||||||
|
|
||||||
return attacked;
|
return attacked;
|
||||||
}
|
}
|
||||||
|
inline bool MoveGenerator::isCheck(const BoardState &bs, const BitBoard &fromBB, const Square &to) {
|
||||||
|
auto kingBB = (*bs.pieceBBs)[Board::toIndex(bs.turn)] & (*bs.pieceBBs)[Board::toIndex(PieceType::King)];
|
||||||
|
auto toBB = BitBoard::fromIndex(to.index());
|
||||||
|
auto changeBB = (fromBB ^ toBB);
|
||||||
|
auto target = bs.occupiedBB ^ changeBB;
|
||||||
|
auto attacked = generateAttackedSquares(bs, ~target, !bs.turn);
|
||||||
|
|
||||||
|
if (kingBB & fromBB) {
|
||||||
|
kingBB ^= changeBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attacked & kingBB;
|
||||||
|
}
|
||||||
|
@@ -23,13 +23,15 @@ public:
|
|||||||
static void generateRookMoves(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 generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves);
|
||||||
static void generateKnightMoves(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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void generatePawnMoves(const BoardState &bs, const Square &from, BitBoard targets, MoveVec &moves);
|
static void generatePawnMoves(const BoardState &bs, const Square &from, BitBoard targets, MoveVec &moves);
|
||||||
|
|
||||||
static void generateMoves(const Square &from, BitBoard movesBB, MoveVec &moves);
|
static void generateMoves(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves);
|
||||||
static void generateMovesWithPromotion(const Square &from, BitBoard movesBB, MoveVec &moves);
|
static void generateMovesWithPromotion(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves);
|
||||||
static BitBoard generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor);
|
|
||||||
|
inline static bool isCheck(const BoardState &bs, const BitBoard &fromBB, const Square &to);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //CHESS_ENGINE_MOVEGENERATOR_HPP
|
#endif //CHESS_ENGINE_MOVEGENERATOR_HPP
|
||||||
|
@@ -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) {
|
os << "isMate=" << pv.isMate();
|
||||||
(void)pv;
|
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,23 +3,33 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
72
Search.cpp
72
Search.cpp
@@ -1,3 +1,75 @@
|
|||||||
#include "Search.hpp"
|
#include "Search.hpp"
|
||||||
|
#include "Move.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
|
int Search::search(Node *node, const Board &board, unsigned depth) {
|
||||||
|
|
||||||
|
if (depth == 0) {
|
||||||
|
return evaluate(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moves = Board::MoveVec();
|
||||||
|
board.pseudoLegalMoves(moves);
|
||||||
|
if (moves.empty()) {
|
||||||
|
return evaluate(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxValue = kNegInfinity;
|
||||||
|
for (auto &kMove: moves) {
|
||||||
|
Board nodeBoard = board;
|
||||||
|
nodeBoard.makeMove(kMove);
|
||||||
|
|
||||||
|
Node child(kMove);
|
||||||
|
child.value = search(&child, nodeBoard, depth - 1);
|
||||||
|
|
||||||
|
auto value = -child.value;
|
||||||
|
if (value > maxValue) {
|
||||||
|
maxValue = value;
|
||||||
|
node->next = std::make_unique<Node>(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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, 4);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
32
Search.hpp
32
Search.hpp
@@ -1,8 +1,38 @@
|
|||||||
#ifndef CHESS_ENGINE_SEARCH_HPP
|
#ifndef CHESS_ENGINE_SEARCH_HPP
|
||||||
#define CHESS_ENGINE_SEARCH_HPP
|
#define CHESS_ENGINE_SEARCH_HPP
|
||||||
|
|
||||||
class Search {
|
#include "PrincipalVariation.hpp"
|
||||||
|
#include "Board.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace Search {
|
||||||
|
|
||||||
|
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 + 10;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
PrincipalVariation start(const Board &board);
|
||||||
|
int evaluate(const Board &board);
|
||||||
|
}
|
||||||
|
|
||||||
#endif //CHESS_ENGINE_SEARCH_HPP
|
#endif //CHESS_ENGINE_SEARCH_HPP
|
||||||
|
@@ -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
|
||||||
|
@@ -25,6 +25,22 @@ 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();
|
||||||
|
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 +62,11 @@ 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
|
||||||
|
}));
|
||||||
|
testMove(fen, moves);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user