Skip to content

Commit 14e5504

Browse files
committed
some modifications related to PV storing and retrieving
1 parent 3c4f1f4 commit 14e5504

File tree

5 files changed

+112
-148
lines changed

5 files changed

+112
-148
lines changed

main.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55
#include "tt.hpp"
66
#include <iostream>
77
#include <ctime>
8-
int main() {
8+
int main()
9+
{
910
chess::Position board;
1011
int prevScore = 0;
1112

12-
for (int depth = 1; depth <=6; ++depth) {
13+
for (int depth = 1; depth <= 6; ++depth)
14+
{
1315
clock_t t1 = clock();
1416

1517
search::tt.newSearch(); // Reset timestamps per iteration
16-
int score = search::alphaBeta(board, -32767, 32767, depth, 0);
18+
int score = search::alphaBeta(board, -MATE(0), MATE(0), depth, 0);
1719

1820
clock_t t2 = clock();
1921
double seconds = double(t2 - t1) / CLOCKS_PER_SEC;
@@ -26,7 +28,6 @@ int main() {
2628
prevScore = score;
2729
search::nodes = 0;
2830
search::tt.clear_stats();
29-
3031
}
3132

3233
return 0;

move_ordering.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ inline chess::Move counterMove[64][64];
2323
inline int16_t history[2][64][64] = {};
2424
inline chess::Move killerMoves[2][128] = {};
2525

26-
inline void scoreMoves(chess::Position& board, chess::Movelist& moves, int ply) {
26+
inline void moveOrder(chess::Position& board, chess::Movelist& moves, int ply) {
2727
static thread_local std::vector<std::pair<chess::Move, int>> scoredMoves;
2828
scoredMoves.clear();
2929
scoredMoves.reserve(moves.size());

search.cpp

Lines changed: 100 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,122 @@
11
#include "search.hpp"
22

3-
namespace search {
3+
namespace search
4+
{
5+
// Transposition table
6+
TranspositionTable tt(20);
7+
// Principal variation
8+
chess::Move pv[256];
49

5-
TranspositionTable tt(20); // 16MB
6-
chess::Move PV[128];
7-
int pvLength = 0;
8-
const auto INVALID_MOVE = chess::Move();
10+
void updatePV(int ply, chess::Move best_move) {
11+
chess::Move newPV[256];
12+
newPV[ply] = best_move;
913

10-
void clearPV() {
11-
std::memset(PV, 0, sizeof(PV));
12-
pvLength = 0;
13-
}
14-
15-
void printPV(chess::Position &board) {
16-
std::cout << "PV: ";
17-
for (int i = 0; i < pvLength && PV[i] != INVALID_MOVE; ++i)
18-
std::cout << PV[i] << " ";
19-
std::cout << std::endl;
20-
}
21-
22-
int16_t quiescence(chess::Position &board, int16_t alpha, int16_t beta,
23-
int ply) {
24-
nodes++;
25-
int16_t standPat = eval(board);
26-
27-
if (standPat >= beta)
28-
return beta;
29-
if (standPat > alpha)
30-
alpha = standPat;
31-
32-
chess::Movelist moves;
33-
chess::movegen::legalmoves<chess::movegen::MoveGenType::CAPTURE>(moves,
34-
board);
35-
move_ordering::scoreMoves(board, moves, ply);
36-
37-
for (auto &move : moves) {
38-
board.makeMove(move);
39-
int16_t score = -quiescence(board, -beta, -alpha, ply + 1);
40-
board.unmakeMove(move);
41-
42-
if (score >= beta)
43-
return beta;
44-
if (score > alpha)
45-
alpha = score;
46-
}
14+
int i = ply + 1;
15+
for (; i < 256 && pv[i] != chess::Move(); ++i)
16+
newPV[i] = pv[i];
4717

48-
return alpha;
49-
}
18+
newPV[i] = chess::Move(); // null-terminate
5019

51-
void updatePV(int ply, chess::Move move) {
52-
for (int j = ply; j < 128; ++j)
53-
PV[j] = INVALID_MOVE;
54-
PV[ply] = move;
55-
pvLength = 0;
56-
while (pvLength < 128 && PV[pvLength] != INVALID_MOVE)
57-
++pvLength;
20+
std::memcpy(pv + ply, newPV + ply, sizeof(chess::Move) * (i - ply + 1));
5821
}
5922

60-
int16_t alphaBeta(chess::Position &board, int16_t alpha, int16_t beta,
61-
int depth, int ply) {
62-
++nodes;
63-
64-
auto hash = board.hash();
65-
if (TTEntry *entry = tt.probe(hash)) {
66-
if (entry->depth() >= depth) {
67-
if (entry->flag() == 0) return entry->score();
68-
if (entry->flag() == 1 && entry->score() >= beta) return entry->score();
69-
if (entry->flag() == 2 && entry->score() <= alpha) return entry->score();
23+
int16_t alphaBeta(chess::Position &board, int16_t alpha, int16_t beta, int depth, int ply)
24+
{
25+
nodes++;
26+
auto ttEntry = tt.probe(board.hash());
27+
if (ttEntry)
28+
{
29+
if (ttEntry->depth() >= depth)
30+
{
31+
if (ttEntry->flag() == TTFlag::EXACT)
32+
return ttEntry->score();
33+
else if (ttEntry->flag() == TTFlag::LOWERBOUND)
34+
alpha = std::max(alpha, ttEntry->score());
35+
else if (ttEntry->flag() == TTFlag::UPPERBOUND)
36+
beta = std::min(beta, ttEntry->score());
37+
if (alpha >= beta)
38+
return ttEntry->score();
39+
}
7040
}
71-
}
72-
73-
if (!board.inCheck() && depth >= 3) {
74-
board.makeNullMove();
75-
int16_t nullScore = -alphaBeta(board, -beta, -beta + 1, depth - 3, ply + 1);
76-
board.unmakeNullMove();
77-
78-
if (nullScore >= beta) {
79-
return beta; // Null move cutoff
41+
if (ply == 0)
42+
{
43+
memset(pv, 0, sizeof(pv));
44+
nodes = 0;
45+
tt.clear_stats();
46+
}
47+
if (depth == 0)
48+
return quiescence(board, alpha, beta, ply);
49+
50+
chess::Movelist moves;
51+
chess::movegen::legalmoves(moves, board);
52+
if (moves.size() == 0)
53+
{
54+
if (board.inCheck())
55+
return MATE(ply);
56+
else
57+
return 0;
8058
}
81-
}
82-
83-
if (!board.inCheck() && depth <= 3) {
84-
const int RFP_MARGIN[] = {0, 200, 150, 100};
85-
int16_t standPat = eval(board);
86-
if (standPat + RFP_MARGIN[depth] <= alpha)
87-
return alpha;
88-
}
89-
90-
chess::Movelist moves;
91-
chess::movegen::legalmoves(moves, board);
92-
93-
if (moves.empty()) {
94-
return board.inCheck() ? MATE(ply) : 0;
95-
}
96-
97-
if (ply == 0) clearPV();
98-
if (depth == 0) return quiescence(board, alpha, beta, ply);
99-
100-
move_ordering::scoreMoves(board, moves, ply);
101-
chess::Move bestMove;
102-
int16_t bestScore = -MATE(0);
103-
int moveCount = 0;
104-
105-
for (auto &move : moves) {
106-
++moveCount;
107-
108-
board.makeMove(move);
109-
int newDepth = depth - 1;
110-
111-
bool doLMR = depth >= 3 && moveCount > 3 && !board.inCheck();
112-
if (doLMR && newDepth > 1) newDepth--;
11359

114-
if (newDepth <= 0) newDepth = 1;
60+
move_ordering::moveOrder(board, moves, ply);
61+
chess::Move bestmove = moves[0];
62+
bool foundPV = false;
11563

116-
int16_t score = -alphaBeta(board, -alpha - 1, -alpha, newDepth, ply + 1);
117-
board.pop();
64+
for (const auto &move : moves)
65+
{
66+
board.makeMove(move);
67+
int16_t score = -alphaBeta(board, -beta, -alpha, depth - 1, ply + 1);
68+
board.pop();
11869

119-
if (score > bestScore) {
120-
if (score > alpha && score < beta && doLMR && depth > 1) {
121-
board.makeMove(move);
122-
score = -alphaBeta(board, -beta, -alpha, depth - 1, ply + 1);
123-
board.pop();
70+
if (score >= beta)
71+
{
72+
tt.store(board.hash(), move, score, depth, TTFlag::LOWERBOUND);
73+
return beta;
74+
}
75+
if (score > alpha)
76+
{
77+
alpha = score;
78+
bestmove = move;
79+
updatePV(ply, bestmove); // Move + child PV
80+
foundPV = true;
12481
}
125-
126-
bestScore = score;
127-
bestMove = move;
128-
updatePV(ply, bestMove);
12982
}
13083

131-
if (score >= beta) {
132-
tt.store(hash, move, beta, depth, 1);
133-
move_ordering::updateKillers(move, ply);
134-
return beta;
135-
}
84+
tt.store(board.hash(), bestmove, alpha, depth,
85+
foundPV ? TTFlag::EXACT : TTFlag::UPPERBOUND);
86+
return alpha;
87+
}
13688

137-
if (score > alpha) {
138-
alpha = score;
89+
int16_t quiescence(chess::Position &board, int16_t alpha, int16_t beta, int ply)
90+
{
91+
int16_t standPat = eval(board);
92+
if (standPat >= beta)
93+
return beta;
94+
if (standPat > alpha)
95+
alpha = standPat;
96+
97+
chess::Movelist moves;
98+
chess::movegen::legalmoves<chess::movegen::MoveGenType::CAPTURE>(moves, board);
99+
move_ordering::moveOrder(board, moves, ply);
100+
101+
for (const auto &move : moves)
102+
{
103+
board.makeMove(move);
104+
int16_t score = -quiescence(board, -beta, -alpha, ply + 1);
105+
board.pop();
106+
107+
if (score >= beta)
108+
return beta;
109+
if (score > alpha)
110+
alpha = score;
139111
}
112+
return alpha;
140113
}
141114

142-
uint8_t flag = (bestScore <= alpha) ? 2 : (bestScore >= beta) ? 1 : 0;
143-
tt.store(board.hash(), bestMove, bestScore, depth, flag);
144-
145-
if (ply == 0) {
146-
pvLength = 0;
147-
while (pvLength < 128 && PV[pvLength] != INVALID_MOVE)
148-
++pvLength;
115+
void printPV(chess::Position &board)
116+
{
117+
std::cout << "Principal Variation: ";
118+
for (int i = 0; i < 256 && pv[i] != chess::Move(); i++)
119+
std::cout << pv[i] << " ";
120+
std::cout << std::endl;
149121
}
150-
151-
return bestScore;
152122
}
153-
154-
} // namespace search

tt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void TranspositionTable::clear() {
1818
void TranspositionTable::newSearch() {
1919
currentTime++;
2020
}
21-
void TranspositionTable::store(uint64_t hash, const chess::Move& bestMove, int16_t score, uint8_t depth, uint8_t flag) {
21+
void TranspositionTable::store(uint64_t hash, const chess::Move& bestMove, int16_t score, uint8_t depth, TTFlag flag) {
2222
size_t index = (hash & sizeMask) * 2; // Two entries per bucket
2323
TTEntry& e0 = table[index];
2424
TTEntry& e1 = table[index + 1];

tt.hpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,28 @@
1010
inline uint64_t tthits = 0;
1111
inline uint64_t ttmiss = 0;
1212
inline uint64_t ttDepthHits[256]={};
13+
enum TTFlag : uint8_t { EXACT = 0, LOWERBOUND = 1, UPPERBOUND = 2 };
1314

1415
struct TTEntry {
1516
uint64_t hash; // 64-bit Zobrist hash
1617
chess::Move bestMove; // Typically 16-bit move encoding
1718
uint64_t packed; // Packed metadata field
1819

1920
uint8_t depth() const { return packed >> 56; }
20-
uint8_t flag() const { return (packed >> 54) & 0x3; }
21+
TTFlag flag() const { return static_cast<TTFlag>((packed >> 54) & 0x3); }
2122
int16_t score() const { return (int16_t)((packed >> 38) & 0xFFFF); }
2223
uint64_t timestamp() const { return (packed >> 1) & 0x1FFFFFFFFFULL; }
2324
bool valid() const { return packed & 1; }
2425

25-
void set(uint8_t d, uint8_t f, int16_t s, uint64_t ts, bool v) {
26+
void set(uint8_t d, TTFlag f, int16_t s, uint64_t ts, bool v) {
2627
packed = (uint64_t(d) << 56) |
27-
(uint64_t(f & 0x3) << 54) |
28+
(uint64_t(f) << 54) |
2829
(uint64_t(uint16_t(s)) << 38) |
2930
((ts & 0x1FFFFFFFFFULL) << 1) |
3031
(v ? 1ULL : 0);
3132
}
3233
};
3334

34-
35-
static_assert(sizeof(uint64_t) == 8, "Unexpected uint64_t size");
36-
static_assert(sizeof(chess::Move) <= 4, "chess::Move should be compact");
37-
static_assert(sizeof(uint64_t) * 2 + sizeof(chess::Move) <= 24, "TTEntry layout larger than expected");
38-
39-
#pragma message("[compile-time] ✅ TTEntry layout verified")
4035
class TranspositionTable {
4136
private:
4237
TTEntry* table = nullptr;
@@ -55,6 +50,6 @@ class TranspositionTable {
5550
std::memset(ttDepthHits, 0, sizeof(ttDepthHits));
5651
}
5752
void newSearch();
58-
void store(uint64_t hash, const chess::Move& bestMove, int16_t score, uint8_t depth, uint8_t flag);
53+
void store(uint64_t hash, const chess::Move& bestMove, int16_t score, uint8_t depth, TTFlag flag);
5954
TTEntry* probe(uint64_t hash);
6055
};

0 commit comments

Comments
 (0)