mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 10:57:43 +00:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7c36d078b | |||
| db1b0bfa1d | |||
| 6f946f823c | |||
| 2de78ebc7b | |||
| 32934c0c8d | |||
| b4fcfed55b | |||
| dc2302b701 | |||
| 268c12bf31 | |||
| 9f943a132a | |||
| 20390fcb3c | |||
| d95b9189d8 | |||
| 9d5d69e435 | |||
| 08265aef81 | |||
| 9d1e4d041d | |||
| cff3a6d33e | |||
| 20a2ca366f | |||
| c3ba5fb9d3 | |||
| eba8925d81 | |||
| ee9f650242 | |||
| 8a0dd93c56 | |||
| 5a72ff128e | |||
| 3f63dd1023 | |||
| 62ab7e4612 | |||
| 1867785231 | |||
| bac4da70c9 | |||
| 8189ae9e1c | |||
| 1d525bb45f | |||
| da7d872eda | |||
| 49d52b8266 | |||
| 75d001addd | |||
| 7daaf03b39 | |||
| 2e778445d5 | |||
| 93bc05cf69 | |||
| a7227ac26f | |||
| bb0da595a7 | |||
| 20d7197a9b | |||
| f4758ced90 | |||
| 3c05bd70eb | |||
| 7000e100bd | |||
| 2ed22e4fc8 | |||
| 940c53c366 | |||
| 4df8651c82 | |||
| 2d4e2bc62a | |||
| d89a03cc35 | |||
| 34b1d0538b | |||
| cdf1f23bc5 | |||
| e86468cc63 | |||
| e7bad2687a | |||
| 3f610e2b13 | |||
| 6cc11272e2 | |||
| a28c17ecb6 |
+24
-5
@@ -71,8 +71,8 @@ void benchmark(const std::string& commandLine) {
|
|||||||
|
|
||||||
std::istringstream csVal(commandLine);
|
std::istringstream csVal(commandLine);
|
||||||
std::istringstream csStr(commandLine);
|
std::istringstream csStr(commandLine);
|
||||||
std::string ttSize, threads, fileName;
|
std::string ttSize, threads, fileName, limitType;
|
||||||
int val, secsPerPos;
|
int val, secsPerPos, maxDepth, maxNodes;
|
||||||
|
|
||||||
csStr >> ttSize;
|
csStr >> ttSize;
|
||||||
csVal >> val;
|
csVal >> val;
|
||||||
@@ -95,8 +95,18 @@ void benchmark(const std::string& commandLine) {
|
|||||||
set_option_value("Use Search Log", "true");
|
set_option_value("Use Search Log", "true");
|
||||||
set_option_value("Search Log Filename", "bench.txt");
|
set_option_value("Search Log Filename", "bench.txt");
|
||||||
|
|
||||||
csVal >> secsPerPos;
|
csVal >> val;
|
||||||
csVal >> fileName;
|
csVal >> fileName;
|
||||||
|
csVal >> limitType;
|
||||||
|
|
||||||
|
secsPerPos = maxDepth = maxNodes = 0;
|
||||||
|
|
||||||
|
if (limitType == "time")
|
||||||
|
secsPerPos = val * 1000;
|
||||||
|
else if (limitType == "depth")
|
||||||
|
maxDepth = val;
|
||||||
|
else
|
||||||
|
maxNodes = val;
|
||||||
|
|
||||||
std::vector<std::string> positions;
|
std::vector<std::string> positions;
|
||||||
|
|
||||||
@@ -121,12 +131,21 @@ void benchmark(const std::string& commandLine) {
|
|||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
positions.push_back(std::string(BenchmarkPositions[i]));
|
positions.push_back(std::string(BenchmarkPositions[i]));
|
||||||
|
|
||||||
|
int startTime = get_system_time();
|
||||||
std::vector<std::string>::iterator it;
|
std::vector<std::string>::iterator it;
|
||||||
for (it = positions.begin(); it != positions.end(); ++it)
|
int cnt = 1;
|
||||||
|
int64_t totalNodes = 0;
|
||||||
|
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
|
||||||
{
|
{
|
||||||
Move moves[1] = {MOVE_NONE};
|
Move moves[1] = {MOVE_NONE};
|
||||||
int dummy[2] = {0, 0};
|
int dummy[2] = {0, 0};
|
||||||
Position pos(*it);
|
Position pos(*it);
|
||||||
think(pos, true, false, 0, dummy, dummy, 0, 0, 0, secsPerPos * 1000, moves);
|
std::cout << "\nProcessing position " << cnt << '/' << positions.size() << std::endl << std::endl;
|
||||||
|
think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves);
|
||||||
|
totalNodes += nodes_searched();
|
||||||
}
|
}
|
||||||
|
std::cout << "\nProcessing time (ms) " << get_system_time() - startTime << std::endl
|
||||||
|
<< "Nodes searched " << totalNodes << std::endl
|
||||||
|
<< "Press any key to exit" << std::endl;
|
||||||
|
std::cin >> fileName;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-7
@@ -1120,13 +1120,7 @@ namespace {
|
|||||||
|
|
||||||
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
|
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
|
||||||
|
|
||||||
// Linearized sigmoid interpolator
|
Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
|
||||||
int sph = int(ph);
|
|
||||||
sph -= (64 - sph) / 4;
|
|
||||||
sph = Min(PHASE_MIDGAME, Max(PHASE_ENDGAME, sph));
|
|
||||||
|
|
||||||
Value result = Value(int((mv * sph + ev * (128 - sph)) / 128));
|
|
||||||
|
|
||||||
return Value(int(result) & ~(GrainSize - 1));
|
return Value(int(result) & ~(GrainSize - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -71,16 +71,18 @@ int main(int argc, char *argv[]) {
|
|||||||
// Process command line arguments
|
// Process command line arguments
|
||||||
if (argc >= 2 && string(argv[1]) == "bench")
|
if (argc >= 2 && string(argv[1]) == "bench")
|
||||||
{
|
{
|
||||||
if (argc < 4 || argc > 6)
|
if (argc < 4 || argc > 7)
|
||||||
{
|
{
|
||||||
std::cout << "Usage: glaurung bench <hash size> <threads> "
|
std::cout << "Usage: glaurung bench <hash size> <threads> "
|
||||||
<< "[time = 60s] [fen positions file = default]"
|
<< "[time = 60s] [fen positions file = default] "
|
||||||
|
<< "[time, depth or node limited = time]"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
string time = argc > 4 ? argv[4] : "60";
|
string time = argc > 4 ? argv[4] : "60";
|
||||||
string fen = argc > 5 ? argv[5] : "default";
|
string fen = argc > 5 ? argv[5] : "default";
|
||||||
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen);
|
string lim = argc > 6 ? argv[6] : "time";
|
||||||
|
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -37,7 +37,7 @@
|
|||||||
/// Version number. If this is left empty, the current date (in the format
|
/// Version number. If this is left empty, the current date (in the format
|
||||||
/// YYMMDD) is used as a version number.
|
/// YYMMDD) is used as a version number.
|
||||||
|
|
||||||
const std::string EngineVersion = "1.01";
|
const std::string EngineVersion = "1.1";
|
||||||
|
|
||||||
|
|
||||||
////
|
////
|
||||||
@@ -60,21 +60,21 @@ extern int Bioskey();
|
|||||||
////
|
////
|
||||||
//// Debug
|
//// Debug
|
||||||
////
|
////
|
||||||
|
extern bool dbg_show_mean;
|
||||||
|
extern bool dbg_show_hit_rate;
|
||||||
|
|
||||||
extern long dbg_cnt0;
|
extern long dbg_cnt0;
|
||||||
extern long dbg_cnt1;
|
extern long dbg_cnt1;
|
||||||
|
|
||||||
inline void dbg_hit_on(bool b) { dbg_cnt0++; if (b) dbg_cnt1++; }
|
inline void dbg_hit_on(bool b) { dbg_show_hit_rate = true; dbg_cnt0++; if (b) dbg_cnt1++; }
|
||||||
inline void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
|
inline void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||||
|
|
||||||
inline void dbg_before() { dbg_cnt0++; }
|
inline void dbg_before() { dbg_show_hit_rate = true; dbg_cnt0++; }
|
||||||
inline void dbg_after() { dbg_cnt1++; }
|
inline void dbg_after() { dbg_show_hit_rate = true; dbg_cnt1++; }
|
||||||
|
|
||||||
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
|
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
|
||||||
|
|
||||||
extern void dbg_print_hit_rate();
|
extern void dbg_print_hit_rate();
|
||||||
extern void dbg_print_mean();
|
extern void dbg_print_mean();
|
||||||
|
|
||||||
extern bool dbg_show_mean;
|
|
||||||
extern bool dbg_show_hit_rate;
|
|
||||||
|
|
||||||
#endif // !defined(MISC_H_INCLUDED)
|
#endif // !defined(MISC_H_INCLUDED)
|
||||||
|
|||||||
+5
-4
@@ -122,15 +122,16 @@ int generate_captures(const Position& pos, MoveStack* mlist) {
|
|||||||
Bitboard target = pos.pieces_of_color(opposite_color(us));
|
Bitboard target = pos.pieces_of_color(opposite_color(us));
|
||||||
MoveStack* mlist_start = mlist;
|
MoveStack* mlist_start = mlist;
|
||||||
|
|
||||||
|
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||||
|
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||||
|
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||||
|
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||||
|
|
||||||
if (us == WHITE)
|
if (us == WHITE)
|
||||||
mlist = generate_pawn_captures<WHITE>(pos, mlist);
|
mlist = generate_pawn_captures<WHITE>(pos, mlist);
|
||||||
else
|
else
|
||||||
mlist = generate_pawn_captures<BLACK>(pos, mlist);
|
mlist = generate_pawn_captures<BLACK>(pos, mlist);
|
||||||
|
|
||||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
|
||||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
|
||||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
|
||||||
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
|
||||||
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
|
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
|
||||||
return int(mlist - mlist_start);
|
return int(mlist - mlist_start);
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-37
@@ -26,6 +26,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "history.h"
|
#include "history.h"
|
||||||
|
#include "evaluate.h"
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
@@ -44,7 +45,9 @@ namespace {
|
|||||||
int MainSearchPhaseIndex;
|
int MainSearchPhaseIndex;
|
||||||
int EvasionsPhaseIndex;
|
int EvasionsPhaseIndex;
|
||||||
int QsearchWithChecksPhaseIndex;
|
int QsearchWithChecksPhaseIndex;
|
||||||
|
int QsearchNoCapturesPhaseIndex;
|
||||||
int QsearchWithoutChecksPhaseIndex;
|
int QsearchWithoutChecksPhaseIndex;
|
||||||
|
int NoMovesPhaseIndex;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,28 +65,38 @@ namespace {
|
|||||||
/// search captures, promotions and some checks) and about how important good
|
/// search captures, promotions and some checks) and about how important good
|
||||||
/// move ordering is at the current node.
|
/// move ordering is at the current node.
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, bool pvnode, Move ttm, Move mk,
|
MovePicker::MovePicker(const Position& p, bool pv, Move ttm,
|
||||||
Move k1, Move k2, Depth d) : pos(p) {
|
const SearchStack& ss, Depth d, EvalInfo* ei) : pos(p) {
|
||||||
pvNode = pvnode;
|
pvNode = pv;
|
||||||
ttMove = ttm;
|
ttMove = ttm;
|
||||||
mateKiller = (mk == ttm)? MOVE_NONE : mk;
|
mateKiller = (ss.mateKiller == ttm)? MOVE_NONE : ss.mateKiller;
|
||||||
killer1 = k1;
|
killer1 = ss.killers[0];
|
||||||
killer2 = k2;
|
killer2 = ss.killers[1];
|
||||||
depth = d;
|
depth = d;
|
||||||
movesPicked = 0;
|
movesPicked = 0;
|
||||||
numOfMoves = 0;
|
numOfMoves = 0;
|
||||||
numOfBadCaptures = 0;
|
numOfBadCaptures = 0;
|
||||||
dc = p.discovered_check_candidates(p.side_to_move());
|
|
||||||
|
// With EvalInfo we are able to know how many captures are possible before
|
||||||
|
// generating them. So avoid generating in case we know are zero.
|
||||||
|
Color us = pos.side_to_move();
|
||||||
|
Color them = opposite_color(us);
|
||||||
|
bool noCaptures = ei
|
||||||
|
&& (ei->attackedBy[us][0] & pos.pieces_of_color(them)) == 0
|
||||||
|
&& !ei->mi->specialized_eval_exists()
|
||||||
|
&& (pos.ep_square() == SQ_NONE)
|
||||||
|
&& !pos.has_pawn_on_7th(us);
|
||||||
|
|
||||||
if (p.is_check())
|
if (p.is_check())
|
||||||
phaseIndex = EvasionsPhaseIndex;
|
phaseIndex = EvasionsPhaseIndex;
|
||||||
else if (depth > Depth(0))
|
else if (depth > Depth(0))
|
||||||
phaseIndex = MainSearchPhaseIndex;
|
phaseIndex = MainSearchPhaseIndex;
|
||||||
else if (depth == Depth(0))
|
else if (depth == Depth(0))
|
||||||
phaseIndex = QsearchWithChecksPhaseIndex;
|
phaseIndex = (noCaptures ? QsearchNoCapturesPhaseIndex : QsearchWithChecksPhaseIndex);
|
||||||
else
|
else
|
||||||
phaseIndex = QsearchWithoutChecksPhaseIndex;
|
phaseIndex = (noCaptures ? NoMovesPhaseIndex : QsearchWithoutChecksPhaseIndex);
|
||||||
|
|
||||||
|
dc = p.discovered_check_candidates(us);
|
||||||
pinned = p.pinned_pieces(p.side_to_move());
|
pinned = p.pinned_pieces(p.side_to_move());
|
||||||
|
|
||||||
finished = false;
|
finished = false;
|
||||||
@@ -211,6 +224,8 @@ void MovePicker::score_captures() {
|
|||||||
// where it is possible to recapture with the hanging piece). Exchanging
|
// where it is possible to recapture with the hanging piece). Exchanging
|
||||||
// big pieces before capturing a hanging piece probably helps to reduce
|
// big pieces before capturing a hanging piece probably helps to reduce
|
||||||
// the subtree size.
|
// the subtree size.
|
||||||
|
// While scoring captures it moves all captures with negative SEE values
|
||||||
|
// to the badCaptures[] array.
|
||||||
Move m;
|
Move m;
|
||||||
int seeValue;
|
int seeValue;
|
||||||
|
|
||||||
@@ -225,8 +240,15 @@ void MovePicker::score_captures() {
|
|||||||
else
|
else
|
||||||
moves[i].score = int(pos.midgame_value_of_piece_on(move_to(m)))
|
moves[i].score = int(pos.midgame_value_of_piece_on(move_to(m)))
|
||||||
-int(pos.type_of_piece_on(move_from(m)));
|
-int(pos.type_of_piece_on(move_from(m)));
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Losing capture, move it to the badCaptures[] array
|
||||||
|
assert(numOfBadCaptures < 63);
|
||||||
moves[i].score = seeValue;
|
moves[i].score = seeValue;
|
||||||
|
badCaptures[numOfBadCaptures++] = moves[i];
|
||||||
|
moves[i--] = moves[--numOfMoves];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,10 +270,11 @@ void MovePicker::score_noncaptures() {
|
|||||||
else
|
else
|
||||||
hs = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
hs = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
||||||
|
|
||||||
// Ensure moves in history are always sorted as first
|
// Ensure history is always preferred to pst
|
||||||
if (hs > 0)
|
if (hs > 0)
|
||||||
hs += 1000;
|
hs += 1000;
|
||||||
|
|
||||||
|
// pst based scoring
|
||||||
moves[i].score = hs + pos.mg_pst_delta(m);
|
moves[i].score = hs + pos.mg_pst_delta(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,7 +293,6 @@ void MovePicker::score_evasions() {
|
|||||||
} else
|
} else
|
||||||
moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
||||||
}
|
}
|
||||||
// FIXME try psqt also here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovePicker::score_qcaptures() {
|
void MovePicker::score_qcaptures() {
|
||||||
@@ -289,7 +311,10 @@ void MovePicker::score_qcaptures() {
|
|||||||
|
|
||||||
|
|
||||||
/// find_best_index() loops across the moves and returns index of
|
/// find_best_index() loops across the moves and returns index of
|
||||||
/// the highest scored one.
|
/// the highest scored one. There is also a second version that
|
||||||
|
/// lowers the priority of moves that attack the same square,
|
||||||
|
/// so that if the best move that attack a square fails the next
|
||||||
|
/// move picked attacks a different square if any, not the same one.
|
||||||
|
|
||||||
int MovePicker::find_best_index() {
|
int MovePicker::find_best_index() {
|
||||||
|
|
||||||
@@ -304,14 +329,48 @@ int MovePicker::find_best_index() {
|
|||||||
return bestIndex;
|
return bestIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MovePicker::find_best_index(Bitboard* squares, int values[]) {
|
||||||
|
|
||||||
|
int hs;
|
||||||
|
Move m;
|
||||||
|
Square to;
|
||||||
|
int bestScore = -10000000, bestIndex = -1;
|
||||||
|
|
||||||
|
for (int i = movesPicked; i < numOfMoves; i++)
|
||||||
|
{
|
||||||
|
m = moves[i].move;
|
||||||
|
to = move_to(m);
|
||||||
|
|
||||||
|
if (!bit_is_set(*squares, to))
|
||||||
|
{
|
||||||
|
// Init at first use
|
||||||
|
set_bit(squares, to);
|
||||||
|
values[to] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hs = moves[i].score - values[to];
|
||||||
|
if (hs > bestScore)
|
||||||
|
{
|
||||||
|
bestIndex = i;
|
||||||
|
bestScore = hs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestIndex != -1)
|
||||||
|
{
|
||||||
|
// Raise value of the picked square, so next attack
|
||||||
|
// to the same square will get low priority.
|
||||||
|
to = move_to(moves[bestIndex].move);
|
||||||
|
values[to] += 0xB00;
|
||||||
|
}
|
||||||
|
return bestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker::pick_move_from_list() picks the move with the biggest score
|
/// MovePicker::pick_move_from_list() picks the move with the biggest score
|
||||||
/// from a list of generated moves (moves[] or badCaptures[], depending on
|
/// from a list of generated moves (moves[] or badCaptures[], depending on
|
||||||
/// the current move generation phase). It takes care not to return the
|
/// the current move generation phase). It takes care not to return the
|
||||||
/// transposition table move if that has already been serched previously.
|
/// transposition table move if that has already been serched previously.
|
||||||
/// While picking captures in the PH_GOOD_CAPTURES phase (i.e. while picking
|
|
||||||
/// non-losing captures in the main search), it moves all captures with
|
|
||||||
/// negative SEE values to the badCaptures[] array.
|
|
||||||
|
|
||||||
Move MovePicker::pick_move_from_list() {
|
Move MovePicker::pick_move_from_list() {
|
||||||
|
|
||||||
@@ -325,23 +384,8 @@ Move MovePicker::pick_move_from_list() {
|
|||||||
|
|
||||||
while (movesPicked < numOfMoves)
|
while (movesPicked < numOfMoves)
|
||||||
{
|
{
|
||||||
int bestScore = -10000000;
|
bestIndex = find_best_index();
|
||||||
bestIndex = -1;
|
|
||||||
for (int i = movesPicked; i < numOfMoves; i++)
|
|
||||||
{
|
|
||||||
if (moves[i].score < 0)
|
|
||||||
{
|
|
||||||
// Losing capture, move it to the badCaptures[] array
|
|
||||||
assert(numOfBadCaptures < 63);
|
|
||||||
badCaptures[numOfBadCaptures++] = moves[i];
|
|
||||||
moves[i--] = moves[--numOfMoves];
|
|
||||||
}
|
|
||||||
else if (moves[i].score > bestScore)
|
|
||||||
{
|
|
||||||
bestIndex = i;
|
|
||||||
bestScore = moves[i].score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bestIndex != -1) // Found a good capture
|
if (bestIndex != -1) // Found a good capture
|
||||||
{
|
{
|
||||||
move = moves[bestIndex].move;
|
move = moves[bestIndex].move;
|
||||||
@@ -461,8 +505,9 @@ MovePicker::MovegenPhase MovePicker::current_move_type() const {
|
|||||||
|
|
||||||
/// MovePicker::init_phase_table() initializes the PhaseTable[],
|
/// MovePicker::init_phase_table() initializes the PhaseTable[],
|
||||||
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
|
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
|
||||||
/// and QsearchWithoutChecksPhaseIndex variables. It is only called once
|
/// QsearchNoCapturesPhaseIndex, QsearchWithoutChecksPhaseIndex and
|
||||||
/// during program startup, and never again while the program is running.
|
/// NoMovesPhaseIndex variables. It is only called once during program
|
||||||
|
/// startup, and never again while the program is running.
|
||||||
|
|
||||||
void MovePicker::init_phase_table() {
|
void MovePicker::init_phase_table() {
|
||||||
|
|
||||||
@@ -491,8 +536,17 @@ void MovePicker::init_phase_table() {
|
|||||||
PhaseTable[i++] = PH_QCHECKS;
|
PhaseTable[i++] = PH_QCHECKS;
|
||||||
PhaseTable[i++] = PH_STOP;
|
PhaseTable[i++] = PH_STOP;
|
||||||
|
|
||||||
|
// Quiescence search with checks only and no captures
|
||||||
|
QsearchNoCapturesPhaseIndex = i - 1;
|
||||||
|
PhaseTable[i++] = PH_QCHECKS;
|
||||||
|
PhaseTable[i++] = PH_STOP;
|
||||||
|
|
||||||
// Quiescence search without checks
|
// Quiescence search without checks
|
||||||
QsearchWithoutChecksPhaseIndex = i - 1;
|
QsearchWithoutChecksPhaseIndex = i - 1;
|
||||||
PhaseTable[i++] = PH_QCAPTURES;
|
PhaseTable[i++] = PH_QCAPTURES;
|
||||||
PhaseTable[i++] = PH_STOP;
|
PhaseTable[i++] = PH_STOP;
|
||||||
|
|
||||||
|
// Do not generate any move
|
||||||
|
NoMovesPhaseIndex = i - 1;
|
||||||
|
PhaseTable[i++] = PH_STOP;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -34,6 +34,9 @@
|
|||||||
//// Types
|
//// Types
|
||||||
////
|
////
|
||||||
|
|
||||||
|
struct EvalInfo;
|
||||||
|
struct SearchStack;
|
||||||
|
|
||||||
/// MovePicker is a class which is used to pick one legal move at a time from
|
/// MovePicker is a class which is used to pick one legal move at a time from
|
||||||
/// the current position. It is initialized with a Position object and a few
|
/// the current position. It is initialized with a Position object and a few
|
||||||
/// moves we have reason to believe are good. The most important method is
|
/// moves we have reason to believe are good. The most important method is
|
||||||
@@ -60,7 +63,7 @@ public:
|
|||||||
PH_STOP
|
PH_STOP
|
||||||
};
|
};
|
||||||
|
|
||||||
MovePicker(const Position& p, bool pvnode, Move ttm, Move mk, Move k1, Move k2, Depth d);
|
MovePicker(const Position& p, bool pvnode, Move ttm, const SearchStack& ss, Depth d, EvalInfo* ei = NULL);
|
||||||
Move get_next_move();
|
Move get_next_move();
|
||||||
Move get_next_move(Lock &lock);
|
Move get_next_move(Lock &lock);
|
||||||
int number_of_moves() const;
|
int number_of_moves() const;
|
||||||
@@ -77,6 +80,7 @@ private:
|
|||||||
void score_qcaptures();
|
void score_qcaptures();
|
||||||
Move pick_move_from_list();
|
Move pick_move_from_list();
|
||||||
int find_best_index();
|
int find_best_index();
|
||||||
|
int find_best_index(Bitboard* squares, int values[]);
|
||||||
|
|
||||||
const Position& pos;
|
const Position& pos;
|
||||||
Move ttMove, mateKiller, killer1, killer2;
|
Move ttMove, mateKiller, killer1, killer2;
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ const SquareDelta PawnPush[2] = {
|
|||||||
|
|
||||||
static const char PieceChars[] = " pnbrqk";
|
static const char PieceChars[] = " pnbrqk";
|
||||||
|
|
||||||
char piece_type_to_char(PieceType pt, bool upcase = false) {
|
char piece_type_to_char(PieceType pt, bool upcase) {
|
||||||
return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
|
return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -124,7 +124,7 @@ inline SquareDelta pawn_push(Color c) {
|
|||||||
//// Prototypes
|
//// Prototypes
|
||||||
////
|
////
|
||||||
|
|
||||||
extern char piece_type_to_char(PieceType pt, bool upcase);
|
extern char piece_type_to_char(PieceType pt, bool upcase = false);
|
||||||
extern PieceType piece_type_from_char(char c);
|
extern PieceType piece_type_from_char(char c);
|
||||||
extern bool piece_is_ok(Piece pc);
|
extern bool piece_is_ok(Piece pc);
|
||||||
extern bool piece_type_is_ok(PieceType pt);
|
extern bool piece_type_is_ok(PieceType pt);
|
||||||
|
|||||||
+45
-24
@@ -31,6 +31,7 @@
|
|||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "psqtab.h"
|
#include "psqtab.h"
|
||||||
|
#include "san.h"
|
||||||
#include "ucioption.h"
|
#include "ucioption.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -38,6 +39,8 @@
|
|||||||
//// Variables
|
//// Variables
|
||||||
////
|
////
|
||||||
|
|
||||||
|
extern SearchStack EmptySearchStack;
|
||||||
|
|
||||||
int Position::castleRightsMask[64];
|
int Position::castleRightsMask[64];
|
||||||
|
|
||||||
Key Position::zobrist[2][8][64];
|
Key Position::zobrist[2][8][64];
|
||||||
@@ -49,6 +52,7 @@ Key Position::zobSideToMove;
|
|||||||
Value Position::MgPieceSquareTable[16][64];
|
Value Position::MgPieceSquareTable[16][64];
|
||||||
Value Position::EgPieceSquareTable[16][64];
|
Value Position::EgPieceSquareTable[16][64];
|
||||||
|
|
||||||
|
static bool RequestPending = false;
|
||||||
|
|
||||||
////
|
////
|
||||||
//// Functions
|
//// Functions
|
||||||
@@ -242,7 +246,7 @@ const std::string Position::to_fen() const {
|
|||||||
|
|
||||||
fen += (rank > RANK_1 ? '/' : ' ');
|
fen += (rank > RANK_1 ? '/' : ' ');
|
||||||
}
|
}
|
||||||
fen += (sideToMove == WHITE ? 'w' : 'b') + ' ';
|
fen += (sideToMove == WHITE ? "w " : "b ");
|
||||||
if (castleRights != NO_CASTLES)
|
if (castleRights != NO_CASTLES)
|
||||||
{
|
{
|
||||||
if (can_castle_kingside(WHITE)) fen += 'K';
|
if (can_castle_kingside(WHITE)) fen += 'K';
|
||||||
@@ -263,29 +267,45 @@ const std::string Position::to_fen() const {
|
|||||||
|
|
||||||
|
|
||||||
/// Position::print() prints an ASCII representation of the position to
|
/// Position::print() prints an ASCII representation of the position to
|
||||||
/// the standard output.
|
/// the standard output. If a move is given then also the san is print.
|
||||||
|
|
||||||
void Position::print() const {
|
void Position::print(Move m) const {
|
||||||
char pieceStrings[][8] =
|
|
||||||
{"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
|
|
||||||
"| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
|
|
||||||
};
|
|
||||||
|
|
||||||
for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
|
static const std::string pieceLetters = " PNBRQK PNBRQK .";
|
||||||
std::cout << "+---+---+---+---+---+---+---+---+\n";
|
|
||||||
for(File file = FILE_A; file <= FILE_H; file++) {
|
// Check for reentrancy, as example when called from inside
|
||||||
Square sq = make_square(file, rank);
|
// MovePicker that is used also here in move_to_san()
|
||||||
Piece piece = piece_on(sq);
|
if (RequestPending)
|
||||||
if(piece == EMPTY)
|
return;
|
||||||
std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
|
|
||||||
else
|
RequestPending = true;
|
||||||
std::cout << pieceStrings[piece];
|
|
||||||
}
|
std::cout << std::endl;
|
||||||
std::cout << "|\n";
|
if (m != MOVE_NONE)
|
||||||
|
{
|
||||||
|
std::string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
|
||||||
|
std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl;
|
||||||
}
|
}
|
||||||
std::cout << "+---+---+---+---+---+---+---+---+\n";
|
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||||
std::cout << to_fen() << std::endl;
|
{
|
||||||
std::cout << key << std::endl;
|
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
|
||||||
|
for (File file = FILE_A; file <= FILE_H; file++)
|
||||||
|
{
|
||||||
|
Square sq = make_square(file, rank);
|
||||||
|
Piece piece = piece_on(sq);
|
||||||
|
if (piece == EMPTY && square_color(sq) == WHITE)
|
||||||
|
piece = NO_PIECE;
|
||||||
|
|
||||||
|
char col = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
|
||||||
|
std::cout << '|' << col << pieceLetters[piece] << col;
|
||||||
|
}
|
||||||
|
std::cout << '|' << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl
|
||||||
|
<< "Fen is: " << to_fen() << std::endl
|
||||||
|
<< "Key is: " << key << std::endl;
|
||||||
|
|
||||||
|
RequestPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -655,10 +675,12 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
|
|||||||
|
|
||||||
|
|
||||||
/// Position::move_is_capture() tests whether a move from the current
|
/// Position::move_is_capture() tests whether a move from the current
|
||||||
/// position is a capture.
|
/// position is a capture. Move must not be MOVE_NONE.
|
||||||
|
|
||||||
bool Position::move_is_capture(Move m) const {
|
bool Position::move_is_capture(Move m) const {
|
||||||
|
|
||||||
|
assert(m != MOVE_NONE);
|
||||||
|
|
||||||
return ( !square_is_empty(move_to(m))
|
return ( !square_is_empty(move_to(m))
|
||||||
&& (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
|
&& (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
|
||||||
)
|
)
|
||||||
@@ -1905,8 +1927,7 @@ bool Position::is_mate() {
|
|||||||
|
|
||||||
if (is_check())
|
if (is_check())
|
||||||
{
|
{
|
||||||
MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE,
|
MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0));
|
||||||
MOVE_NONE, MOVE_NONE, Depth(0));
|
|
||||||
return mp.get_next_move() == MOVE_NONE;
|
return mp.get_next_move() == MOVE_NONE;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+3
-3
@@ -128,7 +128,7 @@ public:
|
|||||||
// Text input/output
|
// Text input/output
|
||||||
void from_fen(const std::string &fen);
|
void from_fen(const std::string &fen);
|
||||||
const std::string to_fen() const;
|
const std::string to_fen() const;
|
||||||
void print() const;
|
void print(Move m = MOVE_NONE) const;
|
||||||
|
|
||||||
// Copying
|
// Copying
|
||||||
void copy(const Position &pos);
|
void copy(const Position &pos);
|
||||||
@@ -174,13 +174,13 @@ public:
|
|||||||
// Number of pieces of each color and type
|
// Number of pieces of each color and type
|
||||||
int piece_count(Color c, PieceType pt) const;
|
int piece_count(Color c, PieceType pt) const;
|
||||||
|
|
||||||
// The en passant square:
|
// The en passant square
|
||||||
Square ep_square() const;
|
Square ep_square() const;
|
||||||
|
|
||||||
// Current king position for each color
|
// Current king position for each color
|
||||||
Square king_square(Color c) const;
|
Square king_square(Color c) const;
|
||||||
|
|
||||||
// Castling rights.
|
// Castling rights
|
||||||
bool can_castle_kingside(Color c) const;
|
bool can_castle_kingside(Color c) const;
|
||||||
bool can_castle_queenside(Color c) const;
|
bool can_castle_queenside(Color c) const;
|
||||||
bool can_castle(Color c) const;
|
bool can_castle(Color c) const;
|
||||||
|
|||||||
+257
-245
@@ -31,6 +31,7 @@
|
|||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
#include "san.h"
|
#include "san.h"
|
||||||
|
|
||||||
|
extern SearchStack EmptySearchStack;
|
||||||
|
|
||||||
////
|
////
|
||||||
//// Local definitions
|
//// Local definitions
|
||||||
@@ -50,7 +51,7 @@ namespace {
|
|||||||
|
|
||||||
/// Functions
|
/// Functions
|
||||||
|
|
||||||
Ambiguity move_ambiguity(Position &pos, Move m);
|
Ambiguity move_ambiguity(const Position& pos, Move m);
|
||||||
const std::string time_string(int milliseconds);
|
const std::string time_string(int milliseconds);
|
||||||
const std::string score_string(Value v);
|
const std::string score_string(Value v);
|
||||||
}
|
}
|
||||||
@@ -61,206 +62,222 @@ namespace {
|
|||||||
////
|
////
|
||||||
|
|
||||||
/// move_to_san() takes a position and a move as input, where it is assumed
|
/// move_to_san() takes a position and a move as input, where it is assumed
|
||||||
/// that the move is a legal move from the position. The return value is
|
/// that the move is a legal move from the position. The return value is
|
||||||
/// a string containing the move in short algebraic notation.
|
/// a string containing the move in short algebraic notation.
|
||||||
|
|
||||||
const std::string move_to_san(Position &pos, Move m) {
|
const std::string move_to_san(const Position& pos, Move m) {
|
||||||
std::string str;
|
|
||||||
|
|
||||||
assert(pos.is_ok());
|
assert(pos.is_ok());
|
||||||
assert(move_is_ok(m));
|
assert(move_is_ok(m));
|
||||||
|
|
||||||
if(m == MOVE_NONE) {
|
std::string san = "";
|
||||||
str = "(none)";
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
else if(m == MOVE_NULL) {
|
|
||||||
str = "(null)";
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
else if(move_is_long_castle(m))
|
|
||||||
str = "O-O-O";
|
|
||||||
else if(move_is_short_castle(m))
|
|
||||||
str = "O-O";
|
|
||||||
else {
|
|
||||||
Square from, to;
|
|
||||||
Piece pc;
|
|
||||||
|
|
||||||
from = move_from(m);
|
if (m == MOVE_NONE)
|
||||||
to = move_to(m);
|
return "(none)";
|
||||||
pc = pos.piece_on(move_from(m));
|
else if (m == MOVE_NULL)
|
||||||
|
return "(null)";
|
||||||
str = "";
|
else if (move_is_long_castle(m))
|
||||||
|
san = "O-O-O";
|
||||||
if(type_of_piece(pc) == PAWN) {
|
else if (move_is_short_castle(m))
|
||||||
if(pos.move_is_capture(m))
|
san = "O-O";
|
||||||
str += file_to_char(square_file(move_from(m)));
|
else
|
||||||
}
|
{
|
||||||
else {
|
Piece pc = pos.piece_on(move_from(m));
|
||||||
str += piece_type_to_char(type_of_piece(pc), true);
|
if (type_of_piece(pc) != PAWN)
|
||||||
|
{
|
||||||
Ambiguity amb = move_ambiguity(pos, m);
|
san += piece_type_to_char(type_of_piece(pc), true);
|
||||||
switch(amb) {
|
Square from = move_from(m);
|
||||||
|
switch (move_ambiguity(pos, m)) {
|
||||||
case AMBIGUITY_NONE:
|
case AMBIGUITY_NONE:
|
||||||
break;
|
break;
|
||||||
|
case AMBIGUITY_FILE:
|
||||||
case AMBIGUITY_FILE:
|
san += file_to_char(square_file(from));
|
||||||
str += file_to_char(square_file(from));
|
break;
|
||||||
break;
|
case AMBIGUITY_RANK:
|
||||||
|
san += rank_to_char(square_rank(from));
|
||||||
case AMBIGUITY_RANK:
|
break;
|
||||||
str += rank_to_char(square_rank(from));
|
case AMBIGUITY_BOTH:
|
||||||
break;
|
san += square_to_string(from);
|
||||||
|
break;
|
||||||
case AMBIGUITY_BOTH:
|
default:
|
||||||
str += square_to_string(from);
|
assert(false);
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
default:
|
if (pos.move_is_capture(m))
|
||||||
assert(false);
|
{
|
||||||
|
if (type_of_piece(pc) == PAWN)
|
||||||
|
san += file_to_char(square_file(move_from(m)));
|
||||||
|
san += "x";
|
||||||
|
}
|
||||||
|
san += square_to_string(move_to(m));
|
||||||
|
if (move_promotion(m))
|
||||||
|
{
|
||||||
|
san += '=';
|
||||||
|
san += piece_type_to_char(move_promotion(m), true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(pos.move_is_capture(m))
|
|
||||||
str += "x";
|
|
||||||
|
|
||||||
str += square_to_string(move_to(m));
|
|
||||||
|
|
||||||
if(move_promotion(m)) {
|
|
||||||
str += "=";
|
|
||||||
str += piece_type_to_char(move_promotion(m), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the move check? We don't use pos.move_is_check(m) here, because
|
// Is the move check? We don't use pos.move_is_check(m) here, because
|
||||||
// Position::move_is_check doesn't detect all checks (not castling moves,
|
// Position::move_is_check doesn't detect all checks (not castling moves,
|
||||||
// promotions and en passant captures).
|
// promotions and en passant captures).
|
||||||
UndoInfo u;
|
UndoInfo u;
|
||||||
pos.do_move(m, u);
|
Position p(pos);
|
||||||
if(pos.is_check())
|
p.do_move(m, u);
|
||||||
str += pos.is_mate()? "#" : "+";
|
if (p.is_check())
|
||||||
pos.undo_move(m, u);
|
san += p.is_mate()? "#" : "+";
|
||||||
|
|
||||||
return str;
|
return san;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// move_from_san() takes a position and a string as input, and tries to
|
/// move_from_san() takes a position and a string as input, and tries to
|
||||||
/// interpret the string as a move in short algebraic notation. On success,
|
/// interpret the string as a move in short algebraic notation. On success,
|
||||||
/// the move is returned. On failure (i.e. if the string is unparsable, or
|
/// the move is returned. On failure (i.e. if the string is unparsable, or
|
||||||
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
|
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
|
||||||
|
|
||||||
Move move_from_san(Position &pos, const std::string &movestr) {
|
Move move_from_san(const Position& pos, const std::string& movestr) {
|
||||||
|
|
||||||
assert(pos.is_ok());
|
assert(pos.is_ok());
|
||||||
|
|
||||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
|
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
|
||||||
MOVE_NONE, OnePly);
|
|
||||||
|
|
||||||
// Castling moves
|
// Castling moves
|
||||||
if(movestr == "O-O-O") {
|
if (movestr == "O-O-O" || movestr == "O-O-O+")
|
||||||
Move m;
|
{
|
||||||
while((m = mp.get_next_move()) != MOVE_NONE)
|
Move m;
|
||||||
if(move_is_long_castle(m) && pos.pl_move_is_legal(m))
|
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||||
return m;
|
if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
|
||||||
return MOVE_NONE;
|
return m;
|
||||||
}
|
|
||||||
else if(movestr == "O-O") {
|
|
||||||
Move m;
|
|
||||||
while((m = mp.get_next_move()) != MOVE_NONE)
|
|
||||||
if(move_is_short_castle(m) && pos.pl_move_is_legal(m))
|
|
||||||
return m;
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal moves
|
|
||||||
const char *cstr = movestr.c_str();
|
|
||||||
const char *c;
|
|
||||||
char *cc;
|
|
||||||
char str[10];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Initialize str[] by making a copy of movestr with the characters
|
|
||||||
// 'x', '=', '+' and '#' removed.
|
|
||||||
cc = str;
|
|
||||||
for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
|
|
||||||
if(!strchr("x=+#", *c)) {
|
|
||||||
*cc = strchr("nrq", *c)? toupper(*c) : *c;
|
|
||||||
cc++;
|
|
||||||
}
|
|
||||||
*cc = '\0';
|
|
||||||
|
|
||||||
size_t left = 0, right = strlen(str) - 1;
|
|
||||||
PieceType pt = NO_PIECE_TYPE, promotion;
|
|
||||||
Square to;
|
|
||||||
File fromFile = FILE_NONE;
|
|
||||||
Rank fromRank = RANK_NONE;
|
|
||||||
|
|
||||||
// Promotion?
|
|
||||||
if(strchr("BNRQ", str[right])) {
|
|
||||||
promotion = piece_type_from_char(str[right]);
|
|
||||||
right--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
promotion = NO_PIECE_TYPE;
|
|
||||||
|
|
||||||
// Find the moving piece:
|
|
||||||
if(left < right) {
|
|
||||||
if(strchr("BNRQK", str[left])) {
|
|
||||||
pt = piece_type_from_char(str[left]);
|
|
||||||
left++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pt = PAWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the to square:
|
|
||||||
if(left < right) {
|
|
||||||
if(str[right] < '1' || str[right] > '8' ||
|
|
||||||
str[right-1] < 'a' || str[right-1] > 'h')
|
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
|
|
||||||
right -= 2;
|
|
||||||
}
|
}
|
||||||
else
|
else if (movestr == "O-O" || movestr == "O-O+")
|
||||||
|
{
|
||||||
|
Move m;
|
||||||
|
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||||
|
if (move_is_short_castle(m) && pos.pl_move_is_legal(m))
|
||||||
|
return m;
|
||||||
|
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
|
|
||||||
// Find the file and/or rank of the from square:
|
|
||||||
if(left <= right) {
|
|
||||||
if(strchr("abcdefgh", str[left])) {
|
|
||||||
fromFile = file_from_char(str[left]);
|
|
||||||
left++;
|
|
||||||
}
|
|
||||||
if(strchr("12345678", str[left]))
|
|
||||||
fromRank = rank_from_char(str[left]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for a matching move:
|
// Normal moves. We use a simple FSM to parse the san string.
|
||||||
|
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
|
||||||
|
static const std::string pieceLetters = "KQRBN";
|
||||||
|
PieceType pt = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
|
||||||
|
File fromFile = FILE_NONE, toFile = FILE_NONE;
|
||||||
|
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
|
||||||
|
Square to;
|
||||||
|
int state = START;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < movestr.length(); i++)
|
||||||
|
{
|
||||||
|
char type, c = movestr[i];
|
||||||
|
if (pieceLetters.find(c) != std::string::npos)
|
||||||
|
type = 'P';
|
||||||
|
else if (c >= 'a' && c <= 'h')
|
||||||
|
type = 'F';
|
||||||
|
else if (c >= '1' && c <= '8')
|
||||||
|
type = 'R';
|
||||||
|
else
|
||||||
|
type = c;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'P':
|
||||||
|
if (state == START)
|
||||||
|
{
|
||||||
|
pt = piece_type_from_char(c);
|
||||||
|
state = TO_FILE;
|
||||||
|
}
|
||||||
|
else if (state == PROMOTION)
|
||||||
|
{
|
||||||
|
promotion = piece_type_from_char(c);
|
||||||
|
state = (i < movestr.length() - 1) ? CHECK : END;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
if (state == START)
|
||||||
|
{
|
||||||
|
pt = PAWN;
|
||||||
|
fromFile = toFile = file_from_char(c);
|
||||||
|
state = TO_RANK;
|
||||||
|
}
|
||||||
|
else if (state == TO_FILE)
|
||||||
|
{
|
||||||
|
toFile = file_from_char(c);
|
||||||
|
state = TO_RANK;
|
||||||
|
}
|
||||||
|
else if (state == TO_RANK && toFile != FILE_NONE)
|
||||||
|
{
|
||||||
|
// Previous file was for disambiguation
|
||||||
|
fromFile = toFile;
|
||||||
|
toFile = file_from_char(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
if (state == TO_RANK)
|
||||||
|
{
|
||||||
|
toRank = rank_from_char(c);
|
||||||
|
state = (i < movestr.length() - 1) ? PROMOTION_OR_CHECK : END;
|
||||||
|
}
|
||||||
|
else if (state == TO_FILE && fromRank == RANK_NONE)
|
||||||
|
{
|
||||||
|
// It's a disambiguation rank instead of a file
|
||||||
|
fromRank = rank_from_char(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
case 'x': case 'X':
|
||||||
|
if (state == TO_RANK)
|
||||||
|
{
|
||||||
|
// Previous file was for disambiguation, or it's a pawn capture
|
||||||
|
fromFile = toFile;
|
||||||
|
state = TO_FILE;
|
||||||
|
}
|
||||||
|
else if (state != TO_FILE)
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (state == PROMOTION_OR_CHECK)
|
||||||
|
state = PROMOTION;
|
||||||
|
else
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
case '+': case '#':
|
||||||
|
if (state == PROMOTION_OR_CHECK || state == CHECK)
|
||||||
|
state = END;
|
||||||
|
else
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return MOVE_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != END)
|
||||||
|
return MOVE_NONE;
|
||||||
|
|
||||||
|
// Look for a matching move
|
||||||
Move m, move = MOVE_NONE;
|
Move m, move = MOVE_NONE;
|
||||||
|
to = make_square(toFile, toRank);
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
|
|
||||||
while((m = mp.get_next_move()) != MOVE_NONE) {
|
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||||
bool match = true;
|
if ( pos.type_of_piece_on(move_from(m)) == pt
|
||||||
if(pos.type_of_piece_on(move_from(m)) != pt)
|
&& move_to(m) == to
|
||||||
match = false;
|
&& move_promotion(m) == promotion
|
||||||
else if(move_to(m) != to)
|
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
|
||||||
match = false;
|
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
|
||||||
else if(move_promotion(m) != promotion)
|
{
|
||||||
match = false;
|
move = m;
|
||||||
else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
|
matches++;
|
||||||
match = false;
|
}
|
||||||
else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
|
return (matches == 1 ? move : MOVE_NONE);
|
||||||
match = false;
|
|
||||||
if(match) {
|
|
||||||
move = m;
|
|
||||||
matches++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(matches == 1)
|
|
||||||
return move;
|
|
||||||
else
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -271,34 +288,31 @@ Move move_from_san(Position &pos, const std::string &movestr) {
|
|||||||
/// length of 80 characters. After a line break, 'startColumn' spaces are
|
/// length of 80 characters. After a line break, 'startColumn' spaces are
|
||||||
/// inserted at the beginning of the new line.
|
/// inserted at the beginning of the new line.
|
||||||
|
|
||||||
const std::string line_to_san(const Position &pos, Move line[], int startColumn,
|
const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
|
||||||
bool breakLines) {
|
|
||||||
Position p = Position(pos);
|
|
||||||
UndoInfo u;
|
UndoInfo u;
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
std::string moveStr;
|
std::string moveStr;
|
||||||
size_t length, maxLength;
|
size_t length = 0;
|
||||||
|
size_t maxLength = 80 - startColumn;
|
||||||
|
Position p(pos);
|
||||||
|
|
||||||
length = 0;
|
for (int i = 0; line[i] != MOVE_NONE; i++)
|
||||||
maxLength = 80 - startColumn;
|
{
|
||||||
|
moveStr = move_to_san(p, line[i]);
|
||||||
|
length += moveStr.length() + 1;
|
||||||
|
if (breakLines && length > maxLength)
|
||||||
|
{
|
||||||
|
s << '\n' << std::setw(startColumn) << ' ';
|
||||||
|
length = moveStr.length() + 1;
|
||||||
|
}
|
||||||
|
s << moveStr << ' ';
|
||||||
|
|
||||||
for(int i = 0; line[i] != MOVE_NONE; i++) {
|
if (line[i] == MOVE_NULL)
|
||||||
moveStr = move_to_san(p, line[i]);
|
p.do_null_move(u);
|
||||||
length += moveStr.length() + 1;
|
else
|
||||||
if(breakLines && length > maxLength) {
|
p.do_move(line[i], u);
|
||||||
s << "\n";
|
|
||||||
for(int j = 0; j < startColumn; j++)
|
|
||||||
s << " ";
|
|
||||||
length = moveStr.length() + 1;
|
|
||||||
}
|
|
||||||
s << moveStr << " ";
|
|
||||||
|
|
||||||
if(line[i] == MOVE_NULL)
|
|
||||||
p.do_null_move(u);
|
|
||||||
else
|
|
||||||
p.do_move(line[i], u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,26 +321,26 @@ const std::string line_to_san(const Position &pos, Move line[], int startColumn,
|
|||||||
/// It is used to write search information to the log file (which is created
|
/// It is used to write search information to the log file (which is created
|
||||||
/// when the UCI parameter "Use Search Log" is "true").
|
/// when the UCI parameter "Use Search Log" is "true").
|
||||||
|
|
||||||
const std::string pretty_pv(const Position &pos, int time, int depth,
|
const std::string pretty_pv(const Position& pos, int time, int depth,
|
||||||
uint64_t nodes, Value score, Move pv[]) {
|
uint64_t nodes, Value score, Move pv[]) {
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
|
||||||
// Depth
|
// Depth
|
||||||
s << std::setw(2) << std::setfill(' ') << depth << " ";
|
s << std::setw(2) << depth << " ";
|
||||||
|
|
||||||
// Score
|
// Score
|
||||||
s << std::setw(8) << score_string(score);
|
s << std::setw(8) << score_string(score);
|
||||||
|
|
||||||
// Time
|
// Time
|
||||||
s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
|
s << std::setw(8) << time_string(time) << " ";
|
||||||
|
|
||||||
// Nodes
|
// Nodes
|
||||||
if(nodes < 1000000ULL)
|
if (nodes < 1000000ULL)
|
||||||
s << std::setw(8) << std::setfill(' ') << nodes << " ";
|
s << std::setw(8) << nodes << " ";
|
||||||
else if(nodes < 1000000000ULL)
|
else if (nodes < 1000000000ULL)
|
||||||
s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
|
s << std::setw(7) << nodes/1000ULL << 'k' << " ";
|
||||||
else
|
else
|
||||||
s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
|
s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
|
||||||
|
|
||||||
// PV
|
// PV
|
||||||
s << line_to_san(pos, pv, 30, true);
|
s << line_to_san(pos, pv, 30, true);
|
||||||
@@ -337,82 +351,80 @@ const std::string pretty_pv(const Position &pos, int time, int depth,
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Ambiguity move_ambiguity(Position &pos, Move m) {
|
Ambiguity move_ambiguity(const Position& pos, Move m) {
|
||||||
Square from, to;
|
|
||||||
Piece pc;
|
|
||||||
|
|
||||||
from = move_from(m);
|
Square from = move_from(m);
|
||||||
to = move_to(m);
|
Square to = move_to(m);
|
||||||
pc = pos.piece_on(from);
|
Piece pc = pos.piece_on(from);
|
||||||
|
|
||||||
// King moves are never ambiguous, because there is never two kings of
|
// King moves are never ambiguous, because there is never two kings of
|
||||||
// the same color.
|
// the same color.
|
||||||
if(type_of_piece(pc) == KING)
|
if (type_of_piece(pc) == KING)
|
||||||
return AMBIGUITY_NONE;
|
return AMBIGUITY_NONE;
|
||||||
|
|
||||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
|
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
|
||||||
MOVE_NONE, OnePly);
|
|
||||||
Move mv, moveList[8];
|
Move mv, moveList[8];
|
||||||
int i, j, n;
|
|
||||||
|
|
||||||
n = 0;
|
int n = 0;
|
||||||
while((mv = mp.get_next_move()) != MOVE_NONE)
|
while ((mv = mp.get_next_move()) != MOVE_NONE)
|
||||||
if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
|
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
|
||||||
&& pos.pl_move_is_legal(mv))
|
moveList[n++] = mv;
|
||||||
moveList[n++] = mv;
|
|
||||||
if(n == 1)
|
|
||||||
return AMBIGUITY_NONE;
|
|
||||||
|
|
||||||
j = 0;
|
if (n == 1)
|
||||||
for(i = 0; i < n; i++)
|
return AMBIGUITY_NONE;
|
||||||
if(square_file(move_from(moveList[i])) == square_file(from))
|
|
||||||
j++;
|
|
||||||
if(j == 1)
|
|
||||||
return AMBIGUITY_FILE;
|
|
||||||
|
|
||||||
j = 0;
|
int f = 0, r = 0;
|
||||||
for(i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
if(square_rank(move_from(moveList[i])) == square_rank(from))
|
{
|
||||||
j++;
|
if (square_file(move_from(moveList[i])) == square_file(from))
|
||||||
if(j == 1)
|
f++;
|
||||||
return AMBIGUITY_RANK;
|
|
||||||
|
if (square_rank(move_from(moveList[i])) == square_rank(from))
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
if (f == 1)
|
||||||
|
return AMBIGUITY_FILE;
|
||||||
|
|
||||||
|
if (r == 1)
|
||||||
|
return AMBIGUITY_RANK;
|
||||||
|
|
||||||
return AMBIGUITY_BOTH;
|
return AMBIGUITY_BOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const std::string time_string(int milliseconds) {
|
const std::string time_string(int milliseconds) {
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
s << std::setfill('0');
|
||||||
|
|
||||||
int hours = milliseconds / (1000 * 60 * 60);
|
int hours = milliseconds / (1000*60*60);
|
||||||
int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
|
int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
|
||||||
int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
|
int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
|
||||||
|
|
||||||
if(hours)
|
if (hours)
|
||||||
s << hours << ':';
|
s << hours << ':';
|
||||||
s << std::setw(2) << std::setfill('0') << minutes << ':';
|
|
||||||
s << std::setw(2) << std::setfill('0') << seconds;
|
|
||||||
|
|
||||||
|
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const std::string score_string(Value v) {
|
const std::string score_string(Value v) {
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
|
||||||
if(abs(v) >= VALUE_MATE - 200) {
|
if (v >= VALUE_MATE - 200)
|
||||||
if(v < 0)
|
|
||||||
s << "-#" << (VALUE_MATE + v) / 2;
|
|
||||||
else
|
|
||||||
s << "#" << (VALUE_MATE - v + 1) / 2;
|
s << "#" << (VALUE_MATE - v + 1) / 2;
|
||||||
}
|
else if(v <= -VALUE_MATE + 200)
|
||||||
else {
|
s << "-#" << (VALUE_MATE + v) / 2;
|
||||||
float floatScore = float(v) / float(PawnValueMidgame);
|
else
|
||||||
if(v >= 0)
|
{
|
||||||
s << '+';
|
float floatScore = float(v) / float(PawnValueMidgame);
|
||||||
s << std::setprecision(2) << std::fixed << floatScore;
|
if (v >= 0)
|
||||||
|
s << '+';
|
||||||
|
|
||||||
|
s << std::setprecision(2) << std::fixed << floatScore;
|
||||||
}
|
}
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,9 @@
|
|||||||
//// Prototypes
|
//// Prototypes
|
||||||
////
|
////
|
||||||
|
|
||||||
extern const std::string move_to_san(Position &pos, Move m);
|
extern const std::string move_to_san(const Position& pos, Move m);
|
||||||
extern Move move_from_san(Position &pos, const std::string &str);
|
extern Move move_from_san(const Position& pos, const std::string& str);
|
||||||
extern const std::string line_to_san(const Position &pos, Move line[],
|
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
|
||||||
int startColumn, bool breakLines);
|
extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, Move pv[]);
|
||||||
extern const std::string pretty_pv(const Position &pos, int time, int depth,
|
|
||||||
uint64_t nodes, Value score, Move pv[]);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // !defined(SAN_H_INCLUDED)
|
#endif // !defined(SAN_H_INCLUDED)
|
||||||
|
|||||||
+184
-99
@@ -133,6 +133,9 @@ namespace {
|
|||||||
// evaluation of the position is more than NullMoveMargin below beta.
|
// evaluation of the position is more than NullMoveMargin below beta.
|
||||||
const Value NullMoveMargin = Value(0x300);
|
const Value NullMoveMargin = Value(0x300);
|
||||||
|
|
||||||
|
// Use null capture pruning?
|
||||||
|
const bool UseNullCapturePruning = false;
|
||||||
|
|
||||||
// Pruning criterions. See the code and comments in ok_to_prune() to
|
// Pruning criterions. See the code and comments in ok_to_prune() to
|
||||||
// understand their precise meaning.
|
// understand their precise meaning.
|
||||||
const bool PruneEscapeMoves = false;
|
const bool PruneEscapeMoves = false;
|
||||||
@@ -144,7 +147,7 @@ namespace {
|
|||||||
bool UseFutilityPruning = true;
|
bool UseFutilityPruning = true;
|
||||||
|
|
||||||
// Margins for futility pruning in the quiescence search, at frontier
|
// Margins for futility pruning in the quiescence search, at frontier
|
||||||
// nodes, and at pre-frontier nodes:
|
// nodes, and at pre-frontier nodes
|
||||||
Value FutilityMargin0 = Value(0x80);
|
Value FutilityMargin0 = Value(0x80);
|
||||||
Value FutilityMargin1 = Value(0x100);
|
Value FutilityMargin1 = Value(0x100);
|
||||||
Value FutilityMargin2 = Value(0x300);
|
Value FutilityMargin2 = Value(0x300);
|
||||||
@@ -167,27 +170,28 @@ namespace {
|
|||||||
Depth PawnEndgameExtension[2] = {OnePly, OnePly};
|
Depth PawnEndgameExtension[2] = {OnePly, OnePly};
|
||||||
Depth MateThreatExtension[2] = {Depth(0), Depth(0)};
|
Depth MateThreatExtension[2] = {Depth(0), Depth(0)};
|
||||||
|
|
||||||
// Search depth at iteration 1:
|
// Search depth at iteration 1
|
||||||
const Depth InitialDepth = OnePly /*+ OnePly/2*/;
|
const Depth InitialDepth = OnePly /*+ OnePly/2*/;
|
||||||
|
|
||||||
// Node counters
|
// Node counters
|
||||||
int NodesSincePoll;
|
int NodesSincePoll;
|
||||||
int NodesBetweenPolls = 30000;
|
int NodesBetweenPolls = 30000;
|
||||||
|
|
||||||
// Iteration counter:
|
// Iteration counter
|
||||||
int Iteration;
|
int Iteration;
|
||||||
|
bool LastIterations;
|
||||||
|
|
||||||
// Scores and number of times the best move changed for each iteration:
|
// Scores and number of times the best move changed for each iteration:
|
||||||
Value ValueByIteration[PLY_MAX_PLUS_2];
|
Value ValueByIteration[PLY_MAX_PLUS_2];
|
||||||
int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
|
int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
|
||||||
|
|
||||||
// MultiPV mode:
|
// MultiPV mode
|
||||||
int MultiPV = 1;
|
int MultiPV = 1;
|
||||||
|
|
||||||
// Time managment variables
|
// Time managment variables
|
||||||
int SearchStartTime;
|
int SearchStartTime;
|
||||||
int MaxNodes, MaxDepth;
|
int MaxNodes, MaxDepth;
|
||||||
int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, TimeAdvantage;
|
int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
|
||||||
Move BestRootMove, PonderMove, EasyMove;
|
Move BestRootMove, PonderMove, EasyMove;
|
||||||
int RootMoveNumber;
|
int RootMoveNumber;
|
||||||
bool InfiniteSearch;
|
bool InfiniteSearch;
|
||||||
@@ -237,19 +241,20 @@ namespace {
|
|||||||
Depth depth, int ply, int threadID);
|
Depth depth, int ply, int threadID);
|
||||||
void sp_search(SplitPoint *sp, int threadID);
|
void sp_search(SplitPoint *sp, int threadID);
|
||||||
void sp_search_pv(SplitPoint *sp, int threadID);
|
void sp_search_pv(SplitPoint *sp, int threadID);
|
||||||
|
void init_search_stack(SearchStack& ss);
|
||||||
void init_search_stack(SearchStack ss[]);
|
void init_search_stack(SearchStack ss[]);
|
||||||
void init_node(const Position &pos, SearchStack ss[], int ply, int threadID);
|
void init_node(const Position &pos, SearchStack ss[], int ply, int threadID);
|
||||||
void update_pv(SearchStack ss[], int ply);
|
void update_pv(SearchStack ss[], int ply);
|
||||||
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
|
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
|
||||||
bool connected_moves(const Position &pos, Move m1, Move m2);
|
bool connected_moves(const Position &pos, Move m1, Move m2);
|
||||||
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
|
bool move_is_killer(Move m, const SearchStack& ss);
|
||||||
bool singleReply, bool mateThreat);
|
Depth extension(const Position &pos, Move m, bool pvNode, bool check, bool singleReply, bool mateThreat, bool* dangerous);
|
||||||
bool ok_to_do_nullmove(const Position &pos);
|
bool ok_to_do_nullmove(const Position &pos);
|
||||||
bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d);
|
bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d);
|
||||||
bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
|
bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
|
||||||
bool ok_to_history(const Position &pos, Move m);
|
bool ok_to_history(const Position &pos, Move m);
|
||||||
void update_history(const Position& pos, Move m, Depth depth,
|
void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount);
|
||||||
Move movesSearched[], int moveCount);
|
void update_killers(Move m, SearchStack& ss);
|
||||||
|
|
||||||
bool fail_high_ply_1();
|
bool fail_high_ply_1();
|
||||||
int current_search_time();
|
int current_search_time();
|
||||||
@@ -297,6 +302,9 @@ Lock IOLock;
|
|||||||
|
|
||||||
History H; // Should be made local?
|
History H; // Should be made local?
|
||||||
|
|
||||||
|
// The empty search stack
|
||||||
|
SearchStack EmptySearchStack;
|
||||||
|
|
||||||
|
|
||||||
////
|
////
|
||||||
//// Functions
|
//// Functions
|
||||||
@@ -422,16 +430,14 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
|
|||||||
int myIncrement = increment[side_to_move];
|
int myIncrement = increment[side_to_move];
|
||||||
int oppTime = time[1 - side_to_move];
|
int oppTime = time[1 - side_to_move];
|
||||||
|
|
||||||
TimeAdvantage = myTime - oppTime;
|
|
||||||
|
|
||||||
if (!movesToGo) // Sudden death time control
|
if (!movesToGo) // Sudden death time control
|
||||||
{
|
{
|
||||||
if (increment)
|
if (myIncrement)
|
||||||
{
|
{
|
||||||
MaxSearchTime = myTime / 30 + myIncrement;
|
MaxSearchTime = myTime / 30 + myIncrement;
|
||||||
AbsoluteMaxSearchTime = Max(myTime / 4, myIncrement - 100);
|
AbsoluteMaxSearchTime = Max(myTime / 4, myIncrement - 100);
|
||||||
} else { // Blitz game without increment
|
} else { // Blitz game without increment
|
||||||
MaxSearchTime = myTime / 40;
|
MaxSearchTime = myTime / 30;
|
||||||
AbsoluteMaxSearchTime = myTime / 8;
|
AbsoluteMaxSearchTime = myTime / 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,6 +567,9 @@ void init_threads() {
|
|||||||
// Wait until the thread has finished launching:
|
// Wait until the thread has finished launching:
|
||||||
while (!Threads[i].running);
|
while (!Threads[i].running);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init also the empty search stack
|
||||||
|
init_search_stack(EmptySearchStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -617,6 +626,7 @@ namespace {
|
|||||||
ValueByIteration[0] = Value(0);
|
ValueByIteration[0] = Value(0);
|
||||||
ValueByIteration[1] = rml.get_move_score(0);
|
ValueByIteration[1] = rml.get_move_score(0);
|
||||||
Iteration = 1;
|
Iteration = 1;
|
||||||
|
LastIterations = false;
|
||||||
|
|
||||||
EasyMove = rml.scan_for_easy_move();
|
EasyMove = rml.scan_for_easy_move();
|
||||||
|
|
||||||
@@ -671,9 +681,8 @@ namespace {
|
|||||||
ExtraSearchTime = BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2)
|
ExtraSearchTime = BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2)
|
||||||
+ BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
|
+ BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
|
||||||
|
|
||||||
// If we need some more and we are in time advantage take it
|
// Try to guess if the current iteration is the last one or the last two
|
||||||
if (ExtraSearchTime > 0 && TimeAdvantage > 2 * MaxSearchTime)
|
LastIterations = (current_search_time() > ((MaxSearchTime + ExtraSearchTime)*58) / 128);
|
||||||
ExtraSearchTime += MaxSearchTime / 2;
|
|
||||||
|
|
||||||
// Stop search if most of MaxSearchTime is consumed at the end of the
|
// Stop search if most of MaxSearchTime is consumed at the end of the
|
||||||
// iteration. We probably don't have enough time to search the first
|
// iteration. We probably don't have enough time to search the first
|
||||||
@@ -766,7 +775,8 @@ namespace {
|
|||||||
<< " currmovenumber " << i + 1 << std::endl;
|
<< " currmovenumber " << i + 1 << std::endl;
|
||||||
|
|
||||||
// Decide search depth for this move
|
// Decide search depth for this move
|
||||||
ext = extension(pos, move, true, pos.move_is_check(move), false, false);
|
bool dangerous;
|
||||||
|
ext = extension(pos, move, true, pos.move_is_check(move), false, false, &dangerous);
|
||||||
newDepth = (Iteration - 2) * OnePly + ext + InitialDepth;
|
newDepth = (Iteration - 2) * OnePly + ext + InitialDepth;
|
||||||
|
|
||||||
// Make the move, and search it
|
// Make the move, and search it
|
||||||
@@ -933,16 +943,14 @@ namespace {
|
|||||||
|
|
||||||
// Initialize a MovePicker object for the current position, and prepare
|
// Initialize a MovePicker object for the current position, and prepare
|
||||||
// to search all moves
|
// to search all moves
|
||||||
MovePicker mp = MovePicker(pos, true, ttMove, ss[ply].mateKiller,
|
MovePicker mp = MovePicker(pos, true, ttMove, ss[ply], depth);
|
||||||
ss[ply].killer1, ss[ply].killer2, depth);
|
|
||||||
|
|
||||||
Move move, movesSearched[256];
|
Move move, movesSearched[256];
|
||||||
int moveCount = 0;
|
int moveCount = 0;
|
||||||
Value value, bestValue = -VALUE_INFINITE;
|
Value value, bestValue = -VALUE_INFINITE;
|
||||||
Bitboard dcCandidates = mp.discovered_check_candidates();
|
Bitboard dcCandidates = mp.discovered_check_candidates();
|
||||||
bool isCheck = pos.is_check();
|
bool isCheck = pos.is_check();
|
||||||
bool mateThreat = MateThreatExtension[1] > Depth(0)
|
bool mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move()));
|
||||||
&& pos.has_mate_threat(opposite_color(pos.side_to_move()));
|
|
||||||
|
|
||||||
// Loop through all legal moves until no moves remain or a beta cutoff
|
// Loop through all legal moves until no moves remain or a beta cutoff
|
||||||
// occurs.
|
// occurs.
|
||||||
@@ -955,7 +963,6 @@ namespace {
|
|||||||
bool singleReply = (isCheck && mp.number_of_moves() == 1);
|
bool singleReply = (isCheck && mp.number_of_moves() == 1);
|
||||||
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
|
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
|
||||||
bool moveIsCapture = pos.move_is_capture(move);
|
bool moveIsCapture = pos.move_is_capture(move);
|
||||||
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
|
|
||||||
|
|
||||||
movesSearched[moveCount++] = ss[ply].currentMove = move;
|
movesSearched[moveCount++] = ss[ply].currentMove = move;
|
||||||
|
|
||||||
@@ -967,7 +974,8 @@ namespace {
|
|||||||
ss[ply].currentMoveCaptureValue = Value(0);
|
ss[ply].currentMoveCaptureValue = Value(0);
|
||||||
|
|
||||||
// Decide the new search depth
|
// Decide the new search depth
|
||||||
Depth ext = extension(pos, move, true, moveIsCheck, singleReply, mateThreat);
|
bool dangerous;
|
||||||
|
Depth ext = extension(pos, move, true, moveIsCheck, singleReply, mateThreat, &dangerous);
|
||||||
Depth newDepth = depth - OnePly + ext;
|
Depth newDepth = depth - OnePly + ext;
|
||||||
|
|
||||||
// Make and search the move
|
// Make and search the move
|
||||||
@@ -981,14 +989,12 @@ namespace {
|
|||||||
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
||||||
// if the move fails high will be re-searched at full depth.
|
// if the move fails high will be re-searched at full depth.
|
||||||
if ( depth >= 2*OnePly
|
if ( depth >= 2*OnePly
|
||||||
&& ext == Depth(0)
|
|
||||||
&& moveCount >= LMRPVMoves
|
&& moveCount >= LMRPVMoves
|
||||||
|
&& !dangerous
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_is_castle(move)
|
&& !move_is_castle(move)
|
||||||
&& move != ss[ply].killer1
|
&& !move_is_killer(move, ss[ply]))
|
||||||
&& move != ss[ply].killer2)
|
|
||||||
{
|
{
|
||||||
ss[ply].reduction = OnePly;
|
ss[ply].reduction = OnePly;
|
||||||
value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true, threadID);
|
value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true, threadID);
|
||||||
@@ -1070,11 +1076,7 @@ namespace {
|
|||||||
if (ok_to_history(pos, m)) // Only non capture moves are considered
|
if (ok_to_history(pos, m)) // Only non capture moves are considered
|
||||||
{
|
{
|
||||||
update_history(pos, m, depth, movesSearched, moveCount);
|
update_history(pos, m, depth, movesSearched, moveCount);
|
||||||
if (m != ss[ply].killer1)
|
update_killers(m, ss[ply]);
|
||||||
{
|
|
||||||
ss[ply].killer2 = ss[ply].killer1;
|
|
||||||
ss[ply].killer1 = m;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
|
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
|
||||||
}
|
}
|
||||||
@@ -1131,6 +1133,7 @@ namespace {
|
|||||||
|
|
||||||
Value approximateEval = quick_evaluate(pos);
|
Value approximateEval = quick_evaluate(pos);
|
||||||
bool mateThreat = false;
|
bool mateThreat = false;
|
||||||
|
bool nullCapturePruning = false;
|
||||||
bool isCheck = pos.is_check();
|
bool isCheck = pos.is_check();
|
||||||
|
|
||||||
// Null move search
|
// Null move search
|
||||||
@@ -1143,7 +1146,21 @@ namespace {
|
|||||||
|
|
||||||
UndoInfo u;
|
UndoInfo u;
|
||||||
pos.do_null_move(u);
|
pos.do_null_move(u);
|
||||||
Value nullValue = -search(pos, ss, -(beta-1), depth-4*OnePly, ply+1, false, threadID);
|
int R = (depth > 7 ? 4 : 3);
|
||||||
|
|
||||||
|
Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
|
||||||
|
|
||||||
|
// Check for a null capture artifact, if the value without the null capture
|
||||||
|
// is above beta then mark the node as a suspicious failed low. We will verify
|
||||||
|
// later if we are really under threat.
|
||||||
|
if ( UseNullCapturePruning
|
||||||
|
&& nullValue < beta
|
||||||
|
&& depth < 5 * OnePly
|
||||||
|
&& ss[ply + 1].currentMove != MOVE_NONE
|
||||||
|
&& pos.move_is_capture(ss[ply + 1].currentMove)
|
||||||
|
&& pos.see(ss[ply + 1].currentMove) * PawnValueMidgame + nullValue > beta)
|
||||||
|
nullCapturePruning = true;
|
||||||
|
|
||||||
pos.undo_null_move(u);
|
pos.undo_null_move(u);
|
||||||
|
|
||||||
if (nullValue >= beta)
|
if (nullValue >= beta)
|
||||||
@@ -1170,6 +1187,26 @@ namespace {
|
|||||||
&& ss[ply - 1].reduction
|
&& ss[ply - 1].reduction
|
||||||
&& connected_moves(pos, ss[ply - 1].currentMove, ss[ply].threatMove))
|
&& connected_moves(pos, ss[ply - 1].currentMove, ss[ply].threatMove))
|
||||||
return beta - 1;
|
return beta - 1;
|
||||||
|
|
||||||
|
if (nullCapturePruning && !mateThreat)
|
||||||
|
{
|
||||||
|
// The null move failed low due to a suspicious capture. Verify if
|
||||||
|
// position is really dangerous or we are facing a null capture
|
||||||
|
// artifact due to the side to move change. So search this
|
||||||
|
// position with a reduced depth and see if we still fail low.
|
||||||
|
Move tm = ss[ply].threatMove;
|
||||||
|
|
||||||
|
assert(tm != MOVE_NONE);
|
||||||
|
|
||||||
|
Value v = search(pos, ss, beta, depth-3*OnePly, ply, false, threadID);
|
||||||
|
if (v >= beta)
|
||||||
|
return beta;
|
||||||
|
|
||||||
|
// Restore stack and update ttMove if was empty
|
||||||
|
ss[ply].threatMove = tm;
|
||||||
|
if (ttMove == MOVE_NONE)
|
||||||
|
ttMove = ss[ply].pv[ply];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Null move search not allowed, try razoring
|
// Null move search not allowed, try razoring
|
||||||
@@ -1191,8 +1228,7 @@ namespace {
|
|||||||
|
|
||||||
// Initialize a MovePicker object for the current position, and prepare
|
// Initialize a MovePicker object for the current position, and prepare
|
||||||
// to search all moves:
|
// to search all moves:
|
||||||
MovePicker mp = MovePicker(pos, false, ttMove, ss[ply].mateKiller,
|
MovePicker mp = MovePicker(pos, false, ttMove, ss[ply], depth);
|
||||||
ss[ply].killer1, ss[ply].killer2, depth);
|
|
||||||
|
|
||||||
Move move, movesSearched[256];
|
Move move, movesSearched[256];
|
||||||
int moveCount = 0;
|
int moveCount = 0;
|
||||||
@@ -1214,19 +1250,18 @@ namespace {
|
|||||||
bool singleReply = (isCheck && mp.number_of_moves() == 1);
|
bool singleReply = (isCheck && mp.number_of_moves() == 1);
|
||||||
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
|
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
|
||||||
bool moveIsCapture = pos.move_is_capture(move);
|
bool moveIsCapture = pos.move_is_capture(move);
|
||||||
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
|
|
||||||
|
|
||||||
movesSearched[moveCount++] = ss[ply].currentMove = move;
|
movesSearched[moveCount++] = ss[ply].currentMove = move;
|
||||||
|
|
||||||
// Decide the new search depth
|
// Decide the new search depth
|
||||||
Depth ext = extension(pos, move, false, moveIsCheck, singleReply, mateThreat);
|
bool dangerous;
|
||||||
|
Depth ext = extension(pos, move, false, moveIsCheck, singleReply, mateThreat, &dangerous);
|
||||||
Depth newDepth = depth - OnePly + ext;
|
Depth newDepth = depth - OnePly + ext;
|
||||||
|
|
||||||
// Futility pruning
|
// Futility pruning
|
||||||
if ( useFutilityPruning
|
if ( useFutilityPruning
|
||||||
&& ext == Depth(0)
|
&& !dangerous
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_promotion(move))
|
&& !move_promotion(move))
|
||||||
{
|
{
|
||||||
if ( moveCount >= 2 + int(depth)
|
if ( moveCount >= 2 + int(depth)
|
||||||
@@ -1254,15 +1289,13 @@ namespace {
|
|||||||
|
|
||||||
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
||||||
// if the move fails high will be re-searched at full depth.
|
// if the move fails high will be re-searched at full depth.
|
||||||
if ( depth >= 2*OnePly
|
if ( depth >= 2*OnePly
|
||||||
&& ext == Depth(0)
|
&& moveCount >= LMRNonPVMoves
|
||||||
&& moveCount >= LMRNonPVMoves
|
&& !dangerous
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_is_castle(move)
|
&& !move_is_castle(move)
|
||||||
&& move != ss[ply].killer1
|
&& !move_is_killer(move, ss[ply]))
|
||||||
&& move != ss[ply].killer2)
|
|
||||||
{
|
{
|
||||||
ss[ply].reduction = OnePly;
|
ss[ply].reduction = OnePly;
|
||||||
value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true, threadID);
|
value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true, threadID);
|
||||||
@@ -1321,11 +1354,7 @@ namespace {
|
|||||||
if (ok_to_history(pos, m)) // Only non capture moves are considered
|
if (ok_to_history(pos, m)) // Only non capture moves are considered
|
||||||
{
|
{
|
||||||
update_history(pos, m, depth, movesSearched, moveCount);
|
update_history(pos, m, depth, movesSearched, moveCount);
|
||||||
if (m != ss[ply].killer1)
|
update_killers(m, ss[ply]);
|
||||||
{
|
|
||||||
ss[ply].killer2 = ss[ply].killer1;
|
|
||||||
ss[ply].killer1 = m;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
|
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
|
||||||
}
|
}
|
||||||
@@ -1382,12 +1411,13 @@ namespace {
|
|||||||
// Initialize a MovePicker object for the current position, and prepare
|
// Initialize a MovePicker object for the current position, and prepare
|
||||||
// to search the moves. Because the depth is <= 0 here, only captures,
|
// to search the moves. Because the depth is <= 0 here, only captures,
|
||||||
// queen promotions and checks (only if depth == 0) will be generated.
|
// queen promotions and checks (only if depth == 0) will be generated.
|
||||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
|
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, depth, &ei);
|
||||||
MOVE_NONE, depth);
|
|
||||||
Move move;
|
Move move;
|
||||||
int moveCount = 0;
|
int moveCount = 0;
|
||||||
Bitboard dcCandidates = mp.discovered_check_candidates();
|
Bitboard dcCandidates = mp.discovered_check_candidates();
|
||||||
bool isCheck = pos.is_check();
|
bool isCheck = pos.is_check();
|
||||||
|
bool pvNode = (beta - alpha != 1);
|
||||||
|
bool enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
|
||||||
|
|
||||||
// Loop through the moves until no moves remain or a beta cutoff
|
// Loop through the moves until no moves remain or a beta cutoff
|
||||||
// occurs.
|
// occurs.
|
||||||
@@ -1396,20 +1426,17 @@ namespace {
|
|||||||
{
|
{
|
||||||
assert(move_is_ok(move));
|
assert(move_is_ok(move));
|
||||||
|
|
||||||
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
|
|
||||||
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
|
|
||||||
|
|
||||||
moveCount++;
|
moveCount++;
|
||||||
ss[ply].currentMove = move;
|
ss[ply].currentMove = move;
|
||||||
|
|
||||||
// Futility pruning
|
// Futility pruning
|
||||||
if ( UseQSearchFutilityPruning
|
if ( UseQSearchFutilityPruning
|
||||||
|
&& enoughMaterial
|
||||||
&& !isCheck
|
&& !isCheck
|
||||||
&& !moveIsCheck
|
&& !pvNode
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& !moveIsPassedPawnPush
|
&& !pos.move_is_check(move, dcCandidates)
|
||||||
&& beta - alpha == 1
|
&& !pos.move_is_passed_pawn_push(move))
|
||||||
&& pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame)
|
|
||||||
{
|
{
|
||||||
Value futilityValue = staticValue
|
Value futilityValue = staticValue
|
||||||
+ Max(pos.midgame_value_of_piece_on(move_to(move)),
|
+ Max(pos.midgame_value_of_piece_on(move_to(move)),
|
||||||
@@ -1425,7 +1452,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't search captures and checks with negative SEE values.
|
// Don't search captures and checks with negative SEE values
|
||||||
if ( !isCheck
|
if ( !isCheck
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& (pos.midgame_value_of_piece_on(move_from(move)) >
|
&& (pos.midgame_value_of_piece_on(move_from(move)) >
|
||||||
@@ -1463,6 +1490,13 @@ namespace {
|
|||||||
// Update transposition table
|
// Update transposition table
|
||||||
TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_EXACT);
|
TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_EXACT);
|
||||||
|
|
||||||
|
// Update killers only for good check moves
|
||||||
|
Move m = ss[ply].currentMove;
|
||||||
|
if (alpha >= beta && ok_to_history(pos, m)) // Only non capture moves are considered
|
||||||
|
{
|
||||||
|
// Wrong to update history when depth is <= 0
|
||||||
|
update_killers(m, ss[ply]);
|
||||||
|
}
|
||||||
return bestValue;
|
return bestValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1497,7 +1531,6 @@ namespace {
|
|||||||
|
|
||||||
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
|
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
|
||||||
bool moveIsCapture = pos.move_is_capture(move);
|
bool moveIsCapture = pos.move_is_capture(move);
|
||||||
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
|
|
||||||
|
|
||||||
lock_grab(&(sp->lock));
|
lock_grab(&(sp->lock));
|
||||||
int moveCount = ++sp->moves;
|
int moveCount = ++sp->moves;
|
||||||
@@ -1506,14 +1539,14 @@ namespace {
|
|||||||
ss[sp->ply].currentMove = move;
|
ss[sp->ply].currentMove = move;
|
||||||
|
|
||||||
// Decide the new search depth.
|
// Decide the new search depth.
|
||||||
Depth ext = extension(pos, move, false, moveIsCheck, false, false);
|
bool dangerous;
|
||||||
|
Depth ext = extension(pos, move, false, moveIsCheck, false, false, &dangerous);
|
||||||
Depth newDepth = sp->depth - OnePly + ext;
|
Depth newDepth = sp->depth - OnePly + ext;
|
||||||
|
|
||||||
// Prune?
|
// Prune?
|
||||||
if ( useFutilityPruning
|
if ( useFutilityPruning
|
||||||
&& ext == Depth(0)
|
&& !dangerous
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& moveCount >= 2 + int(sp->depth)
|
&& moveCount >= 2 + int(sp->depth)
|
||||||
&& ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
|
&& ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
|
||||||
@@ -1525,14 +1558,12 @@ namespace {
|
|||||||
|
|
||||||
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
||||||
// if the move fails high will be re-searched at full depth.
|
// if the move fails high will be re-searched at full depth.
|
||||||
if ( ext == Depth(0)
|
if ( !dangerous
|
||||||
&& moveCount >= LMRNonPVMoves
|
&& moveCount >= LMRNonPVMoves
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& !move_is_castle(move)
|
&& !move_is_castle(move)
|
||||||
&& move != ss[sp->ply].killer1
|
&& !move_is_killer(move, ss[sp->ply]))
|
||||||
&& move != ss[sp->ply].killer2)
|
|
||||||
{
|
{
|
||||||
ss[sp->ply].reduction = OnePly;
|
ss[sp->ply].reduction = OnePly;
|
||||||
value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1, true, threadID);
|
value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1, true, threadID);
|
||||||
@@ -1610,7 +1641,6 @@ namespace {
|
|||||||
{
|
{
|
||||||
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
|
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
|
||||||
bool moveIsCapture = pos.move_is_capture(move);
|
bool moveIsCapture = pos.move_is_capture(move);
|
||||||
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
|
|
||||||
|
|
||||||
assert(move_is_ok(move));
|
assert(move_is_ok(move));
|
||||||
|
|
||||||
@@ -1624,7 +1654,8 @@ namespace {
|
|||||||
ss[sp->ply].currentMove = move;
|
ss[sp->ply].currentMove = move;
|
||||||
|
|
||||||
// Decide the new search depth.
|
// Decide the new search depth.
|
||||||
Depth ext = extension(pos, move, true, moveIsCheck, false, false);
|
bool dangerous;
|
||||||
|
Depth ext = extension(pos, move, true, moveIsCheck, false, false, &dangerous);
|
||||||
Depth newDepth = sp->depth - OnePly + ext;
|
Depth newDepth = sp->depth - OnePly + ext;
|
||||||
|
|
||||||
// Make and search the move.
|
// Make and search the move.
|
||||||
@@ -1633,14 +1664,12 @@ namespace {
|
|||||||
|
|
||||||
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
// Try to reduce non-pv search depth by one ply if move seems not problematic,
|
||||||
// if the move fails high will be re-searched at full depth.
|
// if the move fails high will be re-searched at full depth.
|
||||||
if ( ext == Depth(0)
|
if ( !dangerous
|
||||||
&& moveCount >= LMRPVMoves
|
&& moveCount >= LMRPVMoves
|
||||||
&& !moveIsCapture
|
&& !moveIsCapture
|
||||||
&& !moveIsPassedPawnPush
|
|
||||||
&& !move_promotion(move)
|
&& !move_promotion(move)
|
||||||
&& !move_is_castle(move)
|
&& !move_is_castle(move)
|
||||||
&& move != ss[sp->ply].killer1
|
&& !move_is_killer(move, ss[sp->ply]))
|
||||||
&& move != ss[sp->ply].killer2)
|
|
||||||
{
|
{
|
||||||
ss[sp->ply].reduction = OnePly;
|
ss[sp->ply].reduction = OnePly;
|
||||||
value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1, true, threadID);
|
value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1, true, threadID);
|
||||||
@@ -1871,17 +1900,28 @@ namespace {
|
|||||||
|
|
||||||
// init_search_stack() initializes a search stack at the beginning of a
|
// init_search_stack() initializes a search stack at the beginning of a
|
||||||
// new search from the root.
|
// new search from the root.
|
||||||
|
void init_search_stack(SearchStack& ss) {
|
||||||
|
|
||||||
|
ss.pv[0] = MOVE_NONE;
|
||||||
|
ss.pv[1] = MOVE_NONE;
|
||||||
|
ss.currentMove = MOVE_NONE;
|
||||||
|
ss.threatMove = MOVE_NONE;
|
||||||
|
ss.reduction = Depth(0);
|
||||||
|
for (int j = 0; j < KILLER_MAX; j++)
|
||||||
|
ss.killers[j] = MOVE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
void init_search_stack(SearchStack ss[]) {
|
void init_search_stack(SearchStack ss[]) {
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
ss[i].pv[i] = MOVE_NONE;
|
for (int i = 0; i < 3; i++)
|
||||||
ss[i].pv[i+1] = MOVE_NONE;
|
{
|
||||||
ss[i].currentMove = MOVE_NONE;
|
ss[i].pv[i] = MOVE_NONE;
|
||||||
ss[i].mateKiller = MOVE_NONE;
|
ss[i].pv[i+1] = MOVE_NONE;
|
||||||
ss[i].killer1 = MOVE_NONE;
|
ss[i].currentMove = MOVE_NONE;
|
||||||
ss[i].killer2 = MOVE_NONE;
|
ss[i].threatMove = MOVE_NONE;
|
||||||
ss[i].threatMove = MOVE_NONE;
|
ss[i].reduction = Depth(0);
|
||||||
ss[i].reduction = Depth(0);
|
for (int j = 0; j < KILLER_MAX; j++)
|
||||||
|
ss[i].killers[j] = MOVE_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1905,13 +1945,13 @@ namespace {
|
|||||||
NodesSincePoll = 0;
|
NodesSincePoll = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE;
|
ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE;
|
||||||
ss[ply+2].mateKiller = MOVE_NONE;
|
ss[ply+2].mateKiller = MOVE_NONE;
|
||||||
ss[ply+2].killer1 = ss[ply+2].killer2 = MOVE_NONE;
|
|
||||||
ss[ply].threatMove = MOVE_NONE;
|
ss[ply].threatMove = MOVE_NONE;
|
||||||
ss[ply].reduction = Depth(0);
|
ss[ply].reduction = Depth(0);
|
||||||
ss[ply].currentMoveCaptureValue = Value(0);
|
ss[ply].currentMoveCaptureValue = Value(0);
|
||||||
|
for (int j = 0; j < KILLER_MAX; j++)
|
||||||
|
ss[ply+2].killers[j] = MOVE_NONE;
|
||||||
|
|
||||||
if(Threads[threadID].printCurrentLine)
|
if(Threads[threadID].printCurrentLine)
|
||||||
print_current_line(ss, ply, threadID);
|
print_current_line(ss, ply, threadID);
|
||||||
@@ -2014,14 +2054,32 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// move_is_killer() checks if the given move is among the
|
||||||
|
// killer moves of that ply.
|
||||||
|
|
||||||
|
bool move_is_killer(Move m, const SearchStack& ss) {
|
||||||
|
|
||||||
|
const Move* k = ss.killers;
|
||||||
|
for (int i = 0; i < KILLER_MAX; i++, k++)
|
||||||
|
if (*k == m)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// extension() decides whether a move should be searched with normal depth,
|
// extension() decides whether a move should be searched with normal depth,
|
||||||
// or with extended depth. Certain classes of moves (checking moves, in
|
// or with extended depth. Certain classes of moves (checking moves, in
|
||||||
// particular) are searched with bigger depth than ordinary moves.
|
// particular) are searched with bigger depth than ordinary moves and in
|
||||||
|
// any case are marked as 'dangerous'. Note that also if a move is not
|
||||||
|
// extended, as example because the corresponding UCI option is set to zero,
|
||||||
|
// the move is marked as 'dangerous' so, at least, we avoid to prune it.
|
||||||
|
|
||||||
Depth extension(const Position &pos, Move m, bool pvNode,
|
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
|
||||||
bool check, bool singleReply, bool mateThreat) {
|
bool singleReply, bool mateThreat, bool* dangerous) {
|
||||||
|
|
||||||
Depth result = Depth(0);
|
Depth result = Depth(0);
|
||||||
|
*dangerous = check || singleReply || mateThreat;
|
||||||
|
|
||||||
if (check)
|
if (check)
|
||||||
result += CheckExtension[pvNode];
|
result += CheckExtension[pvNode];
|
||||||
@@ -2029,26 +2087,37 @@ namespace {
|
|||||||
if (singleReply)
|
if (singleReply)
|
||||||
result += SingleReplyExtension[pvNode];
|
result += SingleReplyExtension[pvNode];
|
||||||
|
|
||||||
if (pos.move_is_pawn_push_to_7th(m))
|
|
||||||
result += PawnPushTo7thExtension[pvNode];
|
|
||||||
|
|
||||||
if (pos.move_is_passed_pawn_push(m))
|
|
||||||
result += PassedPawnExtension[pvNode];
|
|
||||||
|
|
||||||
if (mateThreat)
|
if (mateThreat)
|
||||||
result += MateThreatExtension[pvNode];
|
result += MateThreatExtension[pvNode];
|
||||||
|
|
||||||
|
if (pos.move_is_pawn_push_to_7th(m))
|
||||||
|
{
|
||||||
|
result += PawnPushTo7thExtension[pvNode];
|
||||||
|
*dangerous = true;
|
||||||
|
}
|
||||||
|
if (pos.move_is_passed_pawn_push(m))
|
||||||
|
{
|
||||||
|
result += PassedPawnExtension[pvNode];
|
||||||
|
*dangerous = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
|
if ( pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
|
||||||
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
|
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
|
||||||
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
|
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
|
||||||
&& !move_promotion(m))
|
&& !move_promotion(m))
|
||||||
|
{
|
||||||
result += PawnEndgameExtension[pvNode];
|
result += PawnEndgameExtension[pvNode];
|
||||||
|
*dangerous = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( pvNode
|
if ( pvNode
|
||||||
&& pos.move_is_capture(m)
|
&& pos.move_is_capture(m)
|
||||||
&& pos.type_of_piece_on(move_to(m)) != PAWN
|
&& pos.type_of_piece_on(move_to(m)) != PAWN
|
||||||
&& pos.see(m) >= 0)
|
&& pos.see(m) >= 0)
|
||||||
|
{
|
||||||
result += OnePly/2;
|
result += OnePly/2;
|
||||||
|
*dangerous = true;
|
||||||
|
}
|
||||||
|
|
||||||
return Min(result, OnePly);
|
return Min(result, OnePly);
|
||||||
}
|
}
|
||||||
@@ -2137,13 +2206,11 @@ namespace {
|
|||||||
|
|
||||||
|
|
||||||
// ok_to_history() returns true if a move m can be stored
|
// ok_to_history() returns true if a move m can be stored
|
||||||
// in history. Should be a non capturing move.
|
// in history. Should be a non capturing move nor a promotion.
|
||||||
|
|
||||||
bool ok_to_history(const Position& pos, Move m) {
|
bool ok_to_history(const Position& pos, Move m) {
|
||||||
|
|
||||||
return pos.square_is_empty(move_to(m))
|
return !pos.move_is_capture(m) && !move_promotion(m);
|
||||||
&& !move_promotion(m)
|
|
||||||
&& !move_is_ep(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2156,8 +2223,26 @@ namespace {
|
|||||||
H.success(pos.piece_on(move_from(m)), m, depth);
|
H.success(pos.piece_on(move_from(m)), m, depth);
|
||||||
|
|
||||||
for (int i = 0; i < moveCount - 1; i++)
|
for (int i = 0; i < moveCount - 1; i++)
|
||||||
if (ok_to_history(pos, movesSearched[i]) && m != movesSearched[i])
|
{
|
||||||
|
assert(m != movesSearched[i]);
|
||||||
|
if (ok_to_history(pos, movesSearched[i]))
|
||||||
H.failure(pos.piece_on(move_from(movesSearched[i])), movesSearched[i]);
|
H.failure(pos.piece_on(move_from(movesSearched[i])), movesSearched[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update_killers() add a good move that produced a beta-cutoff
|
||||||
|
// among the killer moves of that ply.
|
||||||
|
|
||||||
|
void update_killers(Move m, SearchStack& ss) {
|
||||||
|
|
||||||
|
if (m == ss.killers[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = KILLER_MAX - 1; i > 0; i--)
|
||||||
|
ss.killers[i] = ss.killers[i - 1];
|
||||||
|
|
||||||
|
ss.killers[0] = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail_high_ply_1() checks if some thread is currently resolving a fail
|
// fail_high_ply_1() checks if some thread is currently resolving a fail
|
||||||
|
|||||||
+4
-3
@@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
const int PLY_MAX = 100;
|
const int PLY_MAX = 100;
|
||||||
const int PLY_MAX_PLUS_2 = 102;
|
const int PLY_MAX_PLUS_2 = 102;
|
||||||
|
const int KILLER_MAX = 2;
|
||||||
|
|
||||||
|
|
||||||
////
|
////
|
||||||
@@ -56,8 +57,9 @@ struct SearchStack {
|
|||||||
Move pv[PLY_MAX];
|
Move pv[PLY_MAX];
|
||||||
Move currentMove;
|
Move currentMove;
|
||||||
Value currentMoveCaptureValue;
|
Value currentMoveCaptureValue;
|
||||||
Move mateKiller, killer1, killer2;
|
Move mateKiller;
|
||||||
Move threatMove;
|
Move threatMove;
|
||||||
|
Move killers[KILLER_MAX];
|
||||||
Depth reduction;
|
Depth reduction;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,10 +68,9 @@ struct SearchStack {
|
|||||||
//// Global variables
|
//// Global variables
|
||||||
////
|
////
|
||||||
|
|
||||||
|
extern SearchStack EmptySearchStack;
|
||||||
extern TranspositionTable TT;
|
extern TranspositionTable TT;
|
||||||
|
|
||||||
extern int ActiveThreads;
|
extern int ActiveThreads;
|
||||||
|
|
||||||
extern Lock SMPLock;
|
extern Lock SMPLock;
|
||||||
|
|
||||||
// Perhaps better to make H local, and pass as parameter to MovePicker?
|
// Perhaps better to make H local, and pass as parameter to MovePicker?
|
||||||
|
|||||||
+11
-15
@@ -108,29 +108,25 @@ void TranspositionTable::store(const Position &pos, Value v, Depth d,
|
|||||||
TTEntry *tte, *replace;
|
TTEntry *tte, *replace;
|
||||||
|
|
||||||
tte = replace = first_entry(pos);
|
tte = replace = first_entry(pos);
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++, tte++)
|
||||||
{
|
{
|
||||||
if (!(tte+i)->key()) // still empty
|
if (!tte->key() || tte->key() == pos.get_key()) // empty or overwrite old
|
||||||
{
|
|
||||||
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
|
|
||||||
writes++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((tte+i)->key() == pos.get_key()) // overwrite old
|
|
||||||
{
|
{
|
||||||
if (m == MOVE_NONE)
|
if (m == MOVE_NONE)
|
||||||
m = (tte+i)->move();
|
m = tte->move();
|
||||||
|
|
||||||
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
|
*tte = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( i == 0 // already is (replace == tte+i), common case
|
else if (i == 0) // replace would be a no-op in this common case
|
||||||
|| replace->generation() < (tte+i)->generation())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( replace->generation() > (tte+i)->generation()
|
int c1 = (replace->generation() == generation ? 2 : 0);
|
||||||
|| (tte+i)->depth() < replace->depth())
|
int c2 = (tte->generation() == generation ? -2 : 0);
|
||||||
replace = tte+i;
|
int c3 = (tte->depth() < replace->depth() ? 1 : 0);
|
||||||
|
|
||||||
|
if (c1 + c2 + c3 > 0)
|
||||||
|
replace = tte;
|
||||||
}
|
}
|
||||||
*replace = TTEntry(pos.get_key(), v, type, d, m, generation);
|
*replace = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||||
writes++;
|
writes++;
|
||||||
|
|||||||
+1
-1
@@ -176,7 +176,7 @@ namespace {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
T get_option_value(const std::string& optionName) {
|
T get_option_value(const std::string& optionName) {
|
||||||
|
|
||||||
T ret;
|
T ret = T();
|
||||||
Options::iterator it = option_with_name(optionName);
|
Options::iterator it = option_with_name(optionName);
|
||||||
|
|
||||||
if (it != options.end())
|
if (it != options.end())
|
||||||
|
|||||||
Reference in New Issue
Block a user