Last active
April 9, 2025 20:06
-
-
Save TheBlackPlague/14a891dd40815adf7f4d061b2a043b11 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Copyright (c) 2023 StockDory authors. See the list of authors for more details. | |
// Licensed under LGPL-3.0. | |
// | |
#ifndef STOCKDORY_PERFTRUNNER_H | |
#define STOCKDORY_PERFTRUNNER_H | |
#include <chrono> | |
#include <iostream> | |
#include <nanothread/nanothread.h> | |
#include "../../Backend/Board.h" | |
#include "../../Backend/ThreadPool.h" | |
#include "../../Backend/Move/MoveList.h" | |
#include "PerftEntry.h" | |
// using PEntry = StockDory::Perft::PerftEntry<9>; | |
namespace StockDory | |
{ | |
class PerftRunner | |
{ | |
static Board PerftBoard; | |
// static TranspositionTable<PEntry> TranspositionTable; | |
template<Color Color, bool Divide, bool Sync = false, bool TT = false> | |
struct PerftLayer | |
{ | |
static inline uint64_t Perft(Board& board, const uint8_t depth) | |
{ | |
return PerftRunner::Perft<Color, Divide, Sync, TT>(board, depth); | |
} | |
template<Piece Piece> | |
static inline uint64_t PerftLoop( Board& board, const uint8_t depth, | |
const PinBitBoard& pin, const CheckBitBoard& check, | |
const BitBoardIterator& iterator) | |
{ | |
return PerftRunner::PerftLoop<Piece, Color, Divide, Sync, TT>(board, depth, pin, check, iterator); | |
} | |
}; | |
template<MoveType T> | |
struct BoardLayer | |
{ | |
static inline PreviousState Move(Board& board, | |
const Square from, const Square to, | |
const Piece promotion = NAP) | |
{ | |
return board.Move<T>(from, to, promotion); | |
} | |
static inline void UndoMove(Board& board, const PreviousState& state, | |
const Square from, const Square to) | |
{ | |
board.UndoMove<T>(state, from, to); | |
} | |
}; | |
public: | |
template<Color Color, bool Divide, bool Sync = false, bool TT = false> | |
static inline uint64_t Perft(Board& board, const uint8_t depth) | |
{ | |
uint64_t nodes = 0; | |
using PLayer = PerftLayer<Color, Divide, Sync, TT>; | |
// if (TT) { | |
// const ZobristHash hash = board.Zobrist(); | |
// PEntry& entry = TranspositionTable[hash]; | |
// std::pair<bool, uint64_t> result = entry.Nodes(hash, depth); | |
// | |
// if (result.first) return result.second; | |
// } | |
const PinBitBoard pin = board.Pin<Color, Opposite(Color)>(); | |
if (const CheckBitBoard check = board.Check<Opposite(Color)>(); check.DoubleCheck) { | |
const BitBoardIterator kings (board.PieceBoard<Color>(King )); | |
nodes += PLayer::template PerftLoop<King>(board, depth, pin, check, kings); | |
} else { | |
const BitBoardIterator pawns (board.PieceBoard<Color>(Pawn )); | |
const BitBoardIterator knights (board.PieceBoard<Color>(Knight)); | |
const BitBoardIterator bishops (board.PieceBoard<Color>(Bishop)); | |
const BitBoardIterator rooks (board.PieceBoard<Color>(Rook )); | |
const BitBoardIterator queens (board.PieceBoard<Color>(Queen )); | |
const BitBoardIterator kings (board.PieceBoard<Color>(King )); | |
if (Sync || depth < 5) { | |
nodes += PLayer::template PerftLoop<Pawn >(board, depth, pin, check, pawns ); | |
nodes += PLayer::template PerftLoop<Knight>(board, depth, pin, check, knights); | |
nodes += PLayer::template PerftLoop<Bishop>(board, depth, pin, check, bishops); | |
nodes += PLayer::template PerftLoop<Rook >(board, depth, pin, check, rooks ); | |
nodes += PLayer::template PerftLoop<Queen >(board, depth, pin, check, queens ); | |
nodes += PLayer::template PerftLoop<King >(board, depth, pin, check, kings ); | |
} else { | |
auto coroutines = coro::when_all( | |
PerftCoroutine<Pawn , Color, Divide, Sync, TT>(board, depth, pin, check, pawns ), | |
PerftCoroutine<Knight, Color, Divide, Sync, TT>(board, depth, pin, check, knights), | |
PerftCoroutine<Bishop, Color, Divide, Sync, TT>(board, depth, pin, check, bishops), | |
PerftCoroutine<Rook , Color, Divide, Sync, TT>(board, depth, pin, check, rooks ), | |
PerftCoroutine<Queen , Color, Divide, Sync, TT>(board, depth, pin, check, queens ), | |
PerftCoroutine<King , Color, Divide, Sync, TT>(board, depth, pin, check, kings ) | |
); | |
std::apply([&nodes](auto&&... value) -> void | |
{ | |
((nodes += value.return_value()), ...); | |
}, coro::sync_wait(coroutines)); | |
} | |
} | |
// if (TT) { | |
// const ZobristHash hash = board.Zobrist(); | |
// PEntry& entry = TranspositionTable[hash]; | |
// entry.Insert(hash, depth, nodes); | |
// } | |
return nodes; | |
} | |
template<Piece Piece, Color Color, bool Divide, bool Sync = false, bool TT = false> | |
static inline coro::task<uint64_t> PerftCoroutine(const Board board, const uint8_t depth, | |
const PinBitBoard& pin, const CheckBitBoard& check, | |
BitBoardIterator pIterator) | |
{ | |
using PLayer = PerftLayer<Color, Divide, Sync, TT>; | |
co_await CoroThreadPool.schedule(); | |
co_return PLayer::template PerftLoop<Piece>(board, depth, pin, check, pIterator); | |
} | |
private: | |
template<Piece Piece, Color Color, bool Divide, bool Sync = false, bool TT = false> | |
static inline uint64_t PerftLoop(Board& board, const uint8_t depth, | |
const PinBitBoard& pin, const CheckBitBoard& check, | |
BitBoardIterator pIterator) | |
{ | |
uint64_t nodes = 0; | |
using PLayer = PerftLayer<Opposite(Color), false, Sync, TT>; | |
using BLayer = BoardLayer<TT ? PERFT | ZOBRIST : STANDARD>; | |
if (depth == 1) | |
for (Square sq = pIterator.Value(); sq != NASQ; sq = pIterator.Value()) { | |
const MoveList<Piece, Color> moves (board, sq, pin, check); | |
const uint8_t count = moves.Count(); | |
if (moves.Promotion(sq)) nodes += count * 4; | |
else nodes += count; | |
if (Divide && count) { | |
BitBoardIterator mIterator = moves.Iterator(); | |
for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { | |
if (moves.Promotion(sq)) { | |
LogMove<Queen >(sq, m, 1); | |
LogMove<Rook >(sq, m, 1); | |
LogMove<Bishop>(sq, m, 1); | |
LogMove<Knight>(sq, m, 1); | |
} else LogMove(sq, m, 1); | |
} | |
} | |
} | |
else if (Sync || depth < 5) | |
for (Square sq = pIterator.Value(); sq != NASQ; sq = pIterator.Value()) { | |
const MoveList<Piece, Color> moves (board, sq, pin, check); | |
BitBoardIterator mIterator = moves.Iterator(); | |
for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { | |
if (moves.Promotion(sq)) { | |
PreviousState state = BLayer::Move (board, sq, m, Queen ); | |
const uint64_t queenNodes = PLayer::Perft(board, depth - 1); | |
BLayer::UndoMove(board, state, sq, m); | |
nodes += queenNodes; | |
if (Divide) LogMove<Queen >(sq, m, queenNodes); | |
state = BLayer::Move (board, sq, m, Rook ); | |
const uint64_t rookNodes = PLayer::Perft(board, depth - 1); | |
BLayer::UndoMove(board, state, sq, m); | |
nodes += rookNodes; | |
if (Divide) LogMove<Rook >(sq, m, rookNodes); | |
state = BLayer::Move (board, sq, m, Bishop); | |
const uint64_t bishopNodes = PLayer::Perft(board, depth - 1); | |
BLayer::UndoMove(board, state, sq, m); | |
nodes += bishopNodes; | |
if (Divide) LogMove<Bishop>(sq, m, bishopNodes); | |
state = BLayer::Move (board, sq, m, Knight); | |
const uint64_t knightNodes = PLayer::Perft(board, depth - 1); | |
BLayer::UndoMove(board, state, sq, m); | |
nodes += knightNodes; | |
if (Divide) LogMove<Knight>(sq, m, knightNodes); | |
} else { | |
const PreviousState state = BLayer::Move (board, sq, m); | |
const uint64_t perftNodes = PLayer::Perft(board, depth - 1); | |
BLayer::UndoMove(board, state, sq, m); | |
nodes += perftNodes; | |
if (Divide) LogMove(sq, m, perftNodes); | |
} | |
} | |
} | |
else { | |
std::array<Square , 8> psq = {}; | |
std::array<uint64_t, 8> result = {}; | |
const uint8_t count = pIterator.ToArray(psq); | |
using Block = drjit::blocked_range<uint8_t>; | |
auto Loop = [depth, &board, &pin, &check, &psq](const Block block) -> uint64_t | |
{ | |
const uint8_t start = block.begin(); | |
const uint8_t end = block. end(); | |
const uint8_t nextDepth = depth - 1; | |
Board parallelBoard = board; | |
uint64_t parallelNodes = 0; | |
for (uint8_t i = start; i < end; i++) { | |
const Square sq = psq[i]; | |
MoveList<Piece, Color> moves (parallelBoard, sq, pin, check); | |
if (moves.Count() < 1) continue; | |
BitBoardIterator mIterator = moves.Iterator(); | |
for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { | |
if (moves.Promotion(sq)) { | |
PreviousState state = BLayer::Move (parallelBoard, sq, m, Queen ); | |
const uint64_t queenNodes = PLayer::Perft(parallelBoard, nextDepth); | |
BLayer::UndoMove(parallelBoard, state, sq, m); | |
parallelNodes += queenNodes; | |
if (Divide) LogMove<Queen >(sq, m, queenNodes); | |
state = BLayer::Move (parallelBoard, sq, m, Rook ); | |
const uint64_t rookNodes = PLayer::Perft(parallelBoard, nextDepth); | |
BLayer::UndoMove(parallelBoard, state, sq, m); | |
parallelNodes += rookNodes; | |
if (Divide) LogMove<Rook >(sq, m, rookNodes); | |
state = BLayer::Move (parallelBoard, sq, m, Bishop); | |
const uint64_t bishopNodes = PLayer::Perft(parallelBoard, nextDepth); | |
BLayer::UndoMove(parallelBoard, state, sq, m); | |
parallelNodes += bishopNodes; | |
if (Divide) LogMove<Bishop>(sq, m, bishopNodes); | |
state = BLayer::Move (parallelBoard, sq, m, Knight); | |
const uint64_t knightNodes = PLayer::Perft(parallelBoard, nextDepth); | |
BLayer::UndoMove(parallelBoard, state, sq, m); | |
parallelNodes += knightNodes; | |
if (Divide) LogMove<Knight>(sq, m, knightNodes); | |
} else { | |
const PreviousState state = BLayer::Move (parallelBoard, sq, m); | |
const uint64_t perftNodes = PLayer::Perft(parallelBoard, nextDepth); | |
BLayer::UndoMove(parallelBoard, state, sq, m); | |
parallelNodes += perftNodes; | |
if (Divide) LogMove(sq, m, perftNodes); | |
} | |
} | |
} | |
return parallelNodes; | |
}; | |
drjit::parallel_for( | |
Block (0, count), | |
[&Loop, &result](const Block block) -> void | |
{ | |
result[block.begin()] = Loop(block); | |
}, | |
ThreadPool | |
); | |
for (size_t i = 0; i < 8; i++) nodes += result[i]; | |
} | |
return nodes; | |
} | |
template<Piece Promotion = NAP> | |
static void LogMove(const Square from, const Square to, const uint64_t nodes) | |
{ | |
std::string logEntry = ToString(from) + ToString(to); | |
if (Promotion != NAP) logEntry += static_cast<char>(tolower(FirstLetter(Promotion))); | |
logEntry += ": " + std::to_string(nodes) + "\n"; | |
std::cout << logEntry; | |
} | |
public: | |
static void SetBoard(const std::string& fen) | |
{ | |
PerftBoard = Board(fen); | |
} | |
static void SetBoard(const Board& board) | |
{ | |
PerftBoard = board; | |
} | |
// static void SetTranspositionTable(const uint64_t bytes) | |
// { | |
// std::cout << "Allocating table using defined bytes (" << bytes << ")\n"; | |
// TranspositionTable = StockDory::TranspositionTable<PEntry>(bytes); | |
// std::cout << "Table: " << TranspositionTable.Size() << " entries\n"; | |
// std::cout << "Table: " << TranspositionTable.Size() * sizeof(PEntry) << " bytes\n"; | |
// } | |
template<bool Divide, bool TT = false> | |
static void Perft(const uint8_t depth) | |
{ | |
static const std::regex comma ("(\\d)(?=(\\d{3})+(?!\\d))"); | |
std::cout << "Running PERFT @ depth " << static_cast<uint32_t>(depth) << " "; | |
std::cout << "[Maximum Concurrency: " << pool_size(ThreadPool) << "t]:"; | |
std::cout << std::endl; | |
const auto start = std::chrono::high_resolution_clock::now(); | |
uint64_t nodes = 0; | |
if (pool_size(ThreadPool) > 1) | |
nodes = PerftBoard.ColorToMove() == White | |
? Perft<White, Divide, false, TT>(PerftBoard, depth) | |
: Perft<Black, Divide, false, TT>(PerftBoard, depth); | |
else | |
nodes = PerftBoard.ColorToMove() == White | |
? Perft<White, Divide, true , TT>(PerftBoard, depth) | |
: Perft<Black, Divide, true , TT>(PerftBoard, depth); | |
const auto stop = std::chrono::high_resolution_clock::now(); | |
const auto time = std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count(); | |
const double_t t = static_cast<double_t>(time) / 1000000; | |
const uint64_t nps = static_cast<uint64_t>(nodes / t ); | |
std::cout << std::endl; | |
std::cout << "Nodes searched: " << std::regex_replace(std::to_string(nodes), comma, "$1,"); | |
std::cout << std::endl; | |
std::cout << "Time taken: " << t << "s"; | |
std::cout << std::endl; | |
std::cout << "Speed: " << std::regex_replace(std::to_string(nps), comma, "$1,") << " nps"; | |
std::cout << std::endl; | |
} | |
}; | |
} // Perft | |
StockDory::Board StockDory::PerftRunner::PerftBoard = Board(); | |
// StockDory::TranspositionTable<PEntry> StockDory::Perft::PerftRunner::TranspositionTable = | |
// StockDory::TranspositionTable<PEntry>(0); | |
#endif //STOCKDORY_PERFTRUNNER_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment