Skip to content

Commit 3c4f1f4

Browse files
committed
few PV bugs
1 parent cdd24bb commit 3c4f1f4

File tree

4 files changed

+138
-160
lines changed

4 files changed

+138
-160
lines changed

eval.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class MoveStack {
143143
* @param move
144144
*/
145145
constexpr void push(const_reference move) noexcept {
146-
assert(size_ < constants::MAX_MOVES);
146+
assert(size_ < 256);
147147
moves_[size_++] = move;
148148
}
149149

@@ -152,7 +152,7 @@ class MoveStack {
152152
* @param move
153153
*/
154154
constexpr void push(value_type&& move) noexcept {
155-
assert(size_ < constants::MAX_MOVES);
155+
assert(size_ < 256);
156156
moves_[size_++] = std::move(move);
157157
}
158158

@@ -240,8 +240,8 @@ inline U64 southWest(U64 b) { return (b & ~FILE_A) >> 9; }
240240
#define getPawnAttacks(a, b) chess::attacks::pawn(a, b).getBits()
241241
#define MAX_PLY 245
242242
#define MAX 32767 // for black
243-
#define MAX_MATE 32000
244-
#define MATE(i) ((i < 0 ? -1 : 1) * (MAX_MATE - i))
243+
#define MAX_MATE 31999
244+
#define MATE(i) MAX_MATE-i
245245
#define MATE_DISTANCE(i) (i - MAX_MATE)
246246
int eval(chess::Position &RESTRICT board);
247247
int piece_value(chess::PieceType piece);

main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// File: main.cpp
22
#include "chess.hpp"
3+
#include "eval.h"
34
#include "search.hpp"
45
#include "tt.hpp"
56
#include <iostream>
67
#include <ctime>
78
int main() {
8-
chess::Position board("3r2k1/p4ppp/8/8/8/2R1P3/PQ3PPP/6K1 b - - 0 1");
9+
chess::Position board;
910
int prevScore = 0;
1011

1112
for (int depth = 1; depth <=6; ++depth) {

search.cpp

Lines changed: 132 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,154 @@
11
#include "search.hpp"
2-
namespace search
3-
{
4-
TranspositionTable tt(20); // 16MB
5-
chess::Move PV[128];
6-
int pvLength = 0;
7-
const auto INVALID_MOVE = chess::Move();
8-
void clearPV()
9-
{
10-
std::memset(PV, 0, sizeof(PV));
11-
pvLength = 0;
12-
}
132

14-
void printPV(chess::Position &board)
15-
{
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-
}
3+
namespace search {
214

22-
int16_t quiescence(chess::Position &board, int16_t alpha, int16_t beta, int ply)
23-
{
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, board);
34-
move_ordering::scoreMoves(board, moves, ply);
35-
36-
for (auto &move : moves)
37-
{
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-
}
47-
48-
return alpha;
49-
}
50-
void updatePV(int ply, chess::Move move)
51-
{
52-
53-
// Clear the rest to avoid garbage from previous searches
54-
for (int j = ply; j < 128; ++j)
55-
PV[j] = INVALID_MOVE;
56-
PV[ply] = move;
57-
// Count PV length from root
58-
pvLength = 0;
59-
while (pvLength < 128 && PV[pvLength] != INVALID_MOVE)
60-
++pvLength;
61-
}
62-
int16_t alphaBeta(chess::Position &board, int16_t alpha, int16_t beta, int depth, int ply)
63-
{
64-
++nodes;
65-
66-
auto hash = board.hash();
67-
if (TTEntry *entry = tt.probe(hash))
68-
{
69-
if (entry->depth() >= depth)
70-
{
71-
if (entry->flag() == 0)
72-
{
73-
// No PV update here (exact TT hit)
74-
return entry->score();
75-
}
76-
else if (entry->flag() == 1 && entry->score() >= beta)
77-
{
78-
// Beta cutoff
79-
return entry->score();
80-
}
81-
else if (entry->flag() == 2 && entry->score() <= alpha)
82-
{
83-
// Alpha cutoff
84-
return entry->score();
85-
}
86-
}
87-
}
5+
TranspositionTable tt(20); // 16MB
6+
chess::Move PV[128];
7+
int pvLength = 0;
8+
const auto INVALID_MOVE = chess::Move();
889

89-
chess::Movelist moves;
90-
chess::movegen::legalmoves(moves, board);
91-
92-
if (moves.empty())
93-
{
94-
if (board.inCheck())
95-
{
96-
return MATE(ply);
97-
}
98-
else
99-
{
100-
return 0;
101-
}
102-
}
10+
void clearPV() {
11+
std::memset(PV, 0, sizeof(PV));
12+
pvLength = 0;
13+
}
10314

104-
if (ply == 0)
105-
{
106-
clearPV();
107-
}
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+
}
47+
48+
return alpha;
49+
}
50+
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;
58+
}
59+
60+
int16_t alphaBeta(chess::Position &board, int16_t alpha, int16_t beta,
61+
int depth, int ply) {
62+
++nodes;
10863

109-
if (depth == 0)
110-
{
111-
return eval(board); // Evaluate the leaf node
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();
11270
}
71+
}
11372

114-
move_ordering::scoreMoves(board, moves, ply);
115-
chess::Move bestMove;
116-
int16_t bestScore = -MATE(0);
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();
11777

118-
// Search with first move (narrow window) PVS (Principal Variation Search)
119-
auto firstMove = moves.begin();
120-
board.makeMove(*firstMove);
121-
int16_t score = -alphaBeta(board, -beta, -alpha + 1, depth - 1, ply + 1);
122-
board.pop();
123-
124-
if (score > bestScore)
125-
{
126-
bestScore = score;
127-
bestMove = *firstMove;
128-
129-
// Update PV for this best move
130-
updatePV(ply, bestMove);
78+
if (nullScore >= beta) {
79+
return beta; // Null move cutoff
13180
}
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;
132107

133-
// Now search the remaining moves (full window)
134-
for (auto &move : moves)
135-
{
136-
if (move == bestMove)
137-
continue; // Skip the best move (already searched)
108+
board.makeMove(move);
109+
int newDepth = depth - 1;
138110

111+
bool doLMR = depth >= 3 && moveCount > 3 && !board.inCheck();
112+
if (doLMR && newDepth > 1) newDepth--;
113+
114+
if (newDepth <= 0) newDepth = 1;
115+
116+
int16_t score = -alphaBeta(board, -alpha - 1, -alpha, newDepth, ply + 1);
117+
board.pop();
118+
119+
if (score > bestScore) {
120+
if (score > alpha && score < beta && doLMR && depth > 1) {
139121
board.makeMove(move);
140-
score = -alphaBeta(board, -alpha - 1, -alpha, depth - 1, ply + 1);
122+
score = -alphaBeta(board, -beta, -alpha, depth - 1, ply + 1);
141123
board.pop();
124+
}
142125

143-
if (score > bestScore)
144-
{
145-
bestScore = score;
146-
bestMove = move;
147-
148-
// Update PV for this best move
149-
updatePV(ply, bestMove);
150-
}
151-
152-
if (score >= beta)
153-
{
154-
tt.store(hash, move, beta, depth, 1); // Store as lowerbound
155-
move_ordering::updateKillers(move, ply);
156-
return beta;
157-
}
126+
bestScore = score;
127+
bestMove = move;
128+
updatePV(ply, bestMove);
158129
}
159130

160-
// Store best move in TT
161-
uint8_t flag = (bestScore <= alpha) ? 2 : (bestScore >= beta) ? 1 : 0;
162-
tt.store(board.hash(), bestMove, bestScore, depth, flag);
131+
if (score >= beta) {
132+
tt.store(hash, move, beta, depth, 1);
133+
move_ordering::updateKillers(move, ply);
134+
return beta;
135+
}
163136

164-
// After the full search, update pvLength based on the current PV
165-
if (ply == 0)
166-
{
167-
pvLength = 0;
168-
while (pvLength < 128 && PV[pvLength] != INVALID_MOVE)
169-
++pvLength;
137+
if (score > alpha) {
138+
alpha = score;
170139
}
140+
}
141+
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;
149+
}
171150

172-
return bestScore;
151+
return bestScore;
173152
}
174153

175-
}
154+
} // namespace search

search.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
#include "tt.hpp"
66
#include "move_ordering.hpp"
77
namespace search {
8-
9-
constexpr int MAX_DEPTH = 128;
108

119
inline uint64_t nodes = 0;
1210

0 commit comments

Comments
 (0)