Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ Engine Features
- History Move Heuristic
- Triangular PV-Table
- Iterative Deepening
- PV Sorting
73 changes: 60 additions & 13 deletions src/engine/movepicker/movepicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#define MIN_EVAL (INT_MIN + 1)

int MovePicker::score(const Move &move) const
int MovePicker::score(const Move &move)
{
// clang-format off
static const int MVV_LVA[6][6] = {
Expand All @@ -26,6 +26,11 @@ int MovePicker::score(const Move &move) const
};
// clang-format on

if (_pv_table[0][_current_depth] == move.getEncoded())
{
return 20000;
}

if (move.isCapture())
{
return MVV_LVA[move.getPiece()][move.getCapturedPiece()];
Expand Down Expand Up @@ -86,19 +91,24 @@ int MovePicker::negamax(int alpha, int beta, int depth, const Board &board)
{
_current_nodes++;

// Terminal Node
if (board.getHalfMoveClock() == 100)
{
// Draw
return 0;
}

// Forced Terminal Node
if (depth == 0)
{
_pv_length[_current_depth] = _current_depth;
return quiescence(alpha, beta, -1, board);
}

Move best_move = Move();
bool first_node = true;
bool has_legal_moves = false;

Move best_move = Move();
std::vector<Move> moves = movegen::generatePseudoLegalMoves(board);
std::sort(moves.begin(), moves.end(), _move_more_than_key);
for (const Move &move : moves)
Expand All @@ -108,10 +118,34 @@ int MovePicker::negamax(int alpha, int beta, int depth, const Board &board)
int king_sq = bitboard::bitScanForward(backup.getPieces(backup.getOpponent(), KING));
if (!backup.isSquareAttacked(king_sq, backup.getSideToMove()))
{
int score;
has_legal_moves = true;
_current_depth++;
int score = -negamax(-beta, -alpha, depth - 1, backup);
_current_depth--;

if (first_node)
{
first_node = false;

_current_depth++;
score = -negamax(-beta, -alpha, depth - 1, backup);
_current_depth--;
}
else
{
// Perform a Null Window Search
_current_depth++;
score = -negamax(-alpha - 1, -alpha, depth - 1, backup);
_current_depth--;

// If this move failed to prove to be bad,
// re-search with normal bounds
if ((score > alpha) && (score < beta))
{
_current_depth++;
score = -negamax(-beta, -alpha, depth - 1, backup);
_current_depth--;
}
}

if (score >= beta)
{
// Killer Move Heuristic
Expand All @@ -136,14 +170,17 @@ int MovePicker::negamax(int alpha, int beta, int depth, const Board &board)
}
}

// Terminal Node
if (!has_legal_moves)
{
// Check Mate
int king_sq = bitboard::bitScanForward(board.getPieces(board.getSideToMove(), KING));
if (board.isSquareAttacked(king_sq, board.getOpponent()))
{
return MIN_EVAL + _current_depth;
}

// Stale Mate
return 0;
}

Expand Down Expand Up @@ -238,6 +275,12 @@ void MovePicker::addToPrincipalVariation(Move const &move)
_pv_length[_current_depth] = _pv_length[_current_depth + 1];
}

void MovePicker::clearSearchCounters()
{
_current_nodes = 0;
_current_depth = 0;
}

// Public Methods

int MovePicker::getMaxDepth() const
Expand All @@ -257,14 +300,13 @@ void MovePicker::setMaxDepth(int depth)

MovePicker::SearchResult MovePicker::findBestMove()
{
this->clearState();
this->clearTables();

// Iterative Deepening
int alpha = 0;
int alpha{};
for (int depth = 1; depth <= _max_depth; depth++)
{
_current_nodes = 0;
_current_depth = 0;
this->clearSearchCounters();
alpha = search(depth);
}

Expand All @@ -273,18 +315,23 @@ MovePicker::SearchResult MovePicker::findBestMove()
return res;
}

/**
* @brief This function corresponds to one of
* the loops of findBestMove()
*
* @param depth
* @return MovePicker::SearchResult
*/
MovePicker::SearchResult MovePicker::findBestMove(int depth)
{
_current_nodes = 0;
_current_depth = 0;

this->clearSearchCounters();
int alpha = search(depth);
SearchResult res = SearchResult{alpha, _current_nodes, _pv_length[0]};
memcpy(&res.pv, &_pv_table[0], (unsigned long)_pv_length[0] * sizeof(int));
return res;
}

void MovePicker::clearState()
void MovePicker::clearTables()
{
memset(_history_moves, 0, sizeof(_history_moves));
memset(_killer_moves, 0, sizeof(_killer_moves));
Expand Down
10 changes: 7 additions & 3 deletions src/engine/movepicker/movepicker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <engine/defs.hpp>

#include <vector>

class Board;
class Move;

Expand All @@ -16,7 +18,7 @@ class MovePicker
int _max_depth;

int _current_nodes;
int _current_depth{};
int _current_depth;

int _killer_moves[2][MAX_DEPTH]{};
int _history_moves[N_SIDES][N_PIECES][N_SQUARES]{};
Expand Down Expand Up @@ -55,7 +57,7 @@ class MovePicker
* @param move
* @return move score
*/
int score(const Move &move) const;
int score(const Move &move);

int search(int depth);
int negamax(int alpha, int beta, int depth, const Board &board);
Expand All @@ -65,6 +67,8 @@ class MovePicker
void addToHistoryMoves(Move const &move);
void addToPrincipalVariation(Move const &move);

void clearSearchCounters();

public:
explicit MovePicker(Board &board) : _board(board), _max_depth(6), _current_nodes(0), _move_more_than_key{*this}
{
Expand All @@ -74,7 +78,7 @@ class MovePicker

void setMaxDepth(int depth);

void clearState();
void clearTables();

/**
* @brief Searches the current position with max_depth
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/uci/uci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ namespace uci
// TODO: Refactor AI class to inherit from Board ??
MovePicker ai = MovePicker(board);
ai.setMaxDepth(max_depth);
ai.clearState();
ai.clearTables();

MovePicker::SearchResult result;
for (int depth = 1; depth <= ai.getMaxDepth(); depth++)
Expand Down