Compare commits

..

59 Commits

Author SHA1 Message Date
Marco Costalba
235df6a887 Stockfish 1.1a
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 12:03:46 +01:00
Marco Costalba
8d86c87e1e Add "Null driven IID" UCI option (default true)
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 10:57:40 +01:00
Marco Costalba
c172af1b61 Null move driven internal iterative deepening
When a null move fails low due to a capture, try
to detect if without the capture we are above beta,
in this case there is a good possibility this is
a cut-node and the capture is just a null move
artifact due to side to move change. So if we still
don't have a TT move it's a good time to start an IID.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-08 10:46:52 +01:00
Marco Costalba
b7c36d078b Stockfish 1.1
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 17:18:59 +01:00
Marco Costalba
db1b0bfa1d Revert see() shortcut for LxH and equal captures
It happens that more then 70% of cases are HxL, where
we call see() anyway. The mesured saving of calling
see is about 0,5% of total time, but considering the
added burden in score_captures() the saving is only
0,35% locally and due to more difficult inlining of
the function it ends up that we have no advantage at all,
possibly a small slow down!

So revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:32:23 +01:00
Marco Costalba
6f946f823c Fix two gcc warnings in san.cpp
One good, the other silly.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:32:11 +01:00
Marco Costalba
2de78ebc7b Fix an Intel warning in san.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 12:31:48 +01:00
Marco Costalba
32934c0c8d MovePicker: avoid calling see() for LxH and equal captures
No functional change but should speed-up the captures scoring.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:37:19 +01:00
Marco Costalba
b4fcfed55b Null capture pruning
Null move can fail low because of a capture artifact due
to the side to move change. Try to detect this condition
and fail high instead.

This pruning is very powerful, around 7% of nodes, but is
still experimental so is disabled by default.

Set UseNullCapturePruning to true to enable.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:31:14 +01:00
Marco Costalba
dc2302b701 Position::move_is_capture() does not handle MOVE_NONE
Actually square 0 can be dirty, so that move_is_capture(0)
can return any random values.

Add an assert to be sure it is caught.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:18:31 +01:00
Marco Costalba
268c12bf31 Allow to call Position::print() from MovePicker
Fix a recursion issue that gives a stack overflow.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-06 11:18:08 +01:00
Marco Costalba
9f943a132a san.cpp: rewrite broken move_from_san
Use a state machine to parse the input. Fixed
the many broken cases.

Tested on more then 15 milions nodes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-05 09:06:51 +01:00
Marco Costalba
20390fcb3c san.cpp cleanup
Hopefully no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-05 09:06:30 +01:00
Marco Costalba
d95b9189d8 Revert opponent time advantage logic
Strength increase was due to an hidden bug introduced
by the patch, namely the time per move to /30 instead
of /40 (see previous patch).

After testing this feature do not add any substantial
increase so is removed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 19:23:33 +01:00
Marco Costalba
9d5d69e435 Revert sigmoid interpolator
After deep test (1000 games) it seems do not improve anything,
actually seems slightly weaker.

So remove it for now.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 21:16:47 +01:00
Marco Costalba
08265aef81 san.cpp pass position as constant reference
Make a copy of the position when needed instead
of passing as a reference. It is cleaner and
let us to simplify also Position::print()

A small space inflate while there.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 01:25:16 +01:00
Marco Costalba
9d1e4d041d piece_type_to_char() default argument in declaration
Default argument should be in declaration where it
is visible through header include, not in definition.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-30 00:38:33 +01:00
Marco Costalba
cff3a6d33e Revert threat move ordering
Does not seem to improve anything.

Anyhow idea is nice, maybe we still have to find
correct recipe.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-26 11:22:30 +01:00
Marco Costalba
20a2ca366f Tweak allocated time per move
It seems better to give more time in middle game then
at the end.

Also Toga uses the same limit.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:41:06 +01:00
Marco Costalba
c3ba5fb9d3 Rewrok the extendeable patch
Cleanup and document.

The real functional change is that not mate threat
moves are never pruned, as could happen before.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:49 +01:00
Marco Costalba
eba8925d81 MovePicker: take advantage of threat move for ordering
If the null move was refuted by a capture then give a
bonus if we move away the captured piece.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:34 +01:00
Marco Costalba
ee9f650242 Use extendable instead of depth extension
We can have depth(0) also in problematic cases
according to how extensions are tweaked by the user.

In any case we don't want to prune these moves.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-25 20:40:12 +01:00
Marco Costalba
8a0dd93c56 Generate moves for powerful pieces first
This seems to reduce searched nodes by a
surprising 2.5%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-24 20:43:29 +01:00
Marco Costalba
5a72ff128e Benchmark: print nodes searched at the end of testing
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-24 20:43:11 +01:00
Marco Costalba
3f63dd1023 Easy debug macro enabling
Now you don't need to toggle show_debug_xxxx anymore

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 15:59:26 +01:00
Marco Costalba
62ab7e4612 Introduce node limited benchmarking
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 11:56:14 +01:00
Marco Costalba
1867785231 Introduce depth limited benchmarking
Also print some more info.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-22 11:02:05 +01:00
Marco Costalba
bac4da70c9 Remove an include in movepick.h
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:41 +01:00
Marco Costalba
8189ae9e1c Fix a silly bug that disabled second killer
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:26 +01:00
Marco Costalba
1d525bb45f qsearch: restore pruning of pv nodes with negative SEE
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
Signed-off-by: unknown <Marco@.(none)>
2008-11-19 22:15:14 +01:00
Marco Costalba
da7d872eda Fix Intel warnings and init_search_stack argument
Should be a reference not a copy!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:46:12 +01:00
Marco Costalba
49d52b8266 Set killer slots number to 2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba
75d001addd MovePicker: fix a nasty bug in EvalInfo optimization
EvalInfo has missing attack info when a specialized
endgame function is used.

We missed this case and were using an empty attack bitboard
instead so that no captures were generated for endgames.

After testing the EvalInfo optimization gave worst results,
so after a (long) debug session this nasty bug was found.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba
7daaf03b39 Add and use update_killers()
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba
2e778445d5 Add and use move_is_killer() helper
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:48 +01:00
Marco Costalba
93bc05cf69 Convert killers to a vector
Add infrastructure to threat killer moves as a vector,
this will allow us to easily parametrize the number of
killer moves, instead of hardcode this value to two as is now.

This patch just add the infrastructure, no functional
change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba
a7227ac26f qsearch: do not prune pv nodes with negative SEE
Also small micro-optimization, take a line out of
the moves loop.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba
bb0da595a7 Disable per-square MVV/LVA for now
Needs more testing.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:47 +01:00
Marco Costalba
20d7197a9b MovePicker: use EvalInfo to skip generating captures
When we know already no captures are possible in a given
position.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba
f4758ced90 Position::to_fen(): fix a bug in side to move representation
Was introduced almost two months ago in patch:
"Space inflate Position::to_fen()"

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba
3c05bd70eb Print the move in addition to position
Teach Position::print() to optionally print a
given move in san notation in addition to
the ASCII representation of the board.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba
7000e100bd Enable per-square MVV/LVA
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba
2ed22e4fc8 MovePicker:find bad captures during scoring
Instead of pospone until picking. No functional
change and probably no performance change but it is
needed for following patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:46 +01:00
Marco Costalba
940c53c366 MovePicker: introduce per square MVV/LVA ordering
Just added the infrastructure, no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:45 +01:00
Marco Costalba
4df8651c82 Fix hashfull info
Do not count has a replacement when a TT entry is
written in an empty slot.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-16 12:37:45 +01:00
Marco Costalba
2d4e2bc62a Fix in ok_to_history(): castle move is not a capture
It is erroneusly considered a capture because king
moves on the same square of the rook.

Use the correct function Position::move_is_capture()
instead of the open coded (and buggy) one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba
d89a03cc35 Small tidyup of TranspositionTable::store()
Hopefully without bugs this time!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba
34b1d0538b Fix a logic bug in TranspositionTable::store()
Make the logic work as advertised in the function
description.

Still a fallback from TT cleanup.

This should be less serious then the one in retrieve(),
but it's still a bug.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba
cdf1f23bc5 Micro optimization of update_history()
Remove an useless comparison.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba
e86468cc63 Use cut-off checks in qsearch as killer moves
Killers should not be captures, but checks are not
and are produced also in qsearch.

Use this information, will be useful for move ordering.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:40 +01:00
Marco Costalba
e7bad2687a Smaller null move reduction when depth is high
Lower probability to miss something important.

It seems to increase strenght. Idea form Cyclone.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba
3f610e2b13 Introduce LastIterations variable
Is set during the last iteration.

Sometime also during the second last.

During the last iteration is set in the 95% of cases.

During the second last is set in the 40% of cases.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba
6cc11272e2 Restore development versioning and LSN filtering
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-10 19:19:39 +01:00
Marco Costalba
a28c17ecb6 Fix a missed initialization in get_option_value()
Spotted and reported by Dann Corbit.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:59:11 +01:00
Marco Costalba
ec23692433 Stockfish 1.01
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:56:30 +01:00
Marco Costalba
787d358554 Fix compile under Ubuntu 64bit
Some missing includes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:56:30 +01:00
Marco Costalba
ff0d9dad2b Fix a serious bug in TranspositionTable::retrieve()
Reported by Tord Romstad.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:56:30 +01:00
Marco Costalba
046fd4926f Restore development versioning
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:56:30 +01:00
Marco Costalba
c595185b3c Restore LSN filtering
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-11-04 20:56:18 +01:00
21 changed files with 664 additions and 476 deletions

View File

@@ -71,8 +71,8 @@ void benchmark(const std::string& commandLine) {
std::istringstream csVal(commandLine);
std::istringstream csStr(commandLine);
std::string ttSize, threads, fileName;
int val, secsPerPos;
std::string ttSize, threads, fileName, limitType;
int val, secsPerPos, maxDepth, maxNodes;
csStr >> ttSize;
csVal >> val;
@@ -95,8 +95,18 @@ void benchmark(const std::string& commandLine) {
set_option_value("Use Search Log", "true");
set_option_value("Search Log Filename", "bench.txt");
csVal >> secsPerPos;
csVal >> val;
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;
@@ -121,12 +131,21 @@ void benchmark(const std::string& commandLine) {
for (int i = 0; i < 16; i++)
positions.push_back(std::string(BenchmarkPositions[i]));
int startTime = get_system_time();
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};
int dummy[2] = {0, 0};
Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0};
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;
}

View File

@@ -1120,13 +1120,7 @@ namespace {
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
// Linearized sigmoid interpolator
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));
Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
return Value(int(result) & ~(GrainSize - 1));
}

View File

@@ -23,6 +23,7 @@
////
#include <cassert>
#include <cstring>
#include "history.h"

View File

@@ -71,16 +71,18 @@ int main(int argc, char *argv[]) {
// Process command line arguments
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> "
<< "[time = 60s] [fen positions file = default]"
<< "[time = 60s] [fen positions file = default] "
<< "[time, depth or node limited = time]"
<< std::endl;
exit(0);
}
string time = argc > 4 ? argv[4] : "60";
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;
}

View File

@@ -23,6 +23,7 @@
////
#include <cassert>
#include <cstring>
#include <map>
#include "material.h"

View File

@@ -37,7 +37,7 @@
/// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
const std::string EngineVersion = "1.0";
const std::string EngineVersion = "1.1a";
////
@@ -60,21 +60,21 @@ extern int Bioskey();
////
//// Debug
////
extern bool dbg_show_mean;
extern bool dbg_show_hit_rate;
extern long dbg_cnt0;
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_before() { dbg_cnt0++; }
inline void dbg_after() { dbg_cnt1++; }
inline void dbg_before() { dbg_show_hit_rate = true; dbg_cnt0++; }
inline void dbg_after() { dbg_show_hit_rate = true; dbg_cnt1++; }
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern bool dbg_show_mean;
extern bool dbg_show_hit_rate;
#endif // !defined(MISC_H_INCLUDED)

View File

@@ -122,15 +122,16 @@ int generate_captures(const Position& pos, MoveStack* mlist) {
Bitboard target = pos.pieces_of_color(opposite_color(us));
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)
mlist = generate_pawn_captures<WHITE>(pos, mlist);
else
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);
return int(mlist - mlist_start);
}

View File

@@ -26,6 +26,7 @@
#include <cassert>
#include "history.h"
#include "evaluate.h"
#include "movegen.h"
#include "movepick.h"
#include "search.h"
@@ -44,7 +45,9 @@ namespace {
int MainSearchPhaseIndex;
int EvasionsPhaseIndex;
int QsearchWithChecksPhaseIndex;
int QsearchNoCapturesPhaseIndex;
int QsearchWithoutChecksPhaseIndex;
int NoMovesPhaseIndex;
}
@@ -62,28 +65,38 @@ namespace {
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, bool pvnode, Move ttm, Move mk,
Move k1, Move k2, Depth d) : pos(p) {
pvNode = pvnode;
MovePicker::MovePicker(const Position& p, bool pv, Move ttm,
const SearchStack& ss, Depth d, EvalInfo* ei) : pos(p) {
pvNode = pv;
ttMove = ttm;
mateKiller = (mk == ttm)? MOVE_NONE : mk;
killer1 = k1;
killer2 = k2;
mateKiller = (ss.mateKiller == ttm)? MOVE_NONE : ss.mateKiller;
killer1 = ss.killers[0];
killer2 = ss.killers[1];
depth = d;
movesPicked = 0;
numOfMoves = 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())
phaseIndex = EvasionsPhaseIndex;
phaseIndex = EvasionsPhaseIndex;
else if (depth > Depth(0))
phaseIndex = MainSearchPhaseIndex;
phaseIndex = MainSearchPhaseIndex;
else if (depth == Depth(0))
phaseIndex = QsearchWithChecksPhaseIndex;
phaseIndex = (noCaptures ? QsearchNoCapturesPhaseIndex : QsearchWithChecksPhaseIndex);
else
phaseIndex = QsearchWithoutChecksPhaseIndex;
phaseIndex = (noCaptures ? NoMovesPhaseIndex : QsearchWithoutChecksPhaseIndex);
dc = p.discovered_check_candidates(us);
pinned = p.pinned_pieces(p.side_to_move());
finished = false;
@@ -211,6 +224,8 @@ void MovePicker::score_captures() {
// where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce
// the subtree size.
// While scoring captures it moves all captures with negative SEE values
// to the badCaptures[] array.
Move m;
int seeValue;
@@ -225,8 +240,15 @@ void MovePicker::score_captures() {
else
moves[i].score = int(pos.midgame_value_of_piece_on(move_to(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;
badCaptures[numOfBadCaptures++] = moves[i];
moves[i--] = moves[--numOfMoves];
}
}
}
@@ -248,10 +270,11 @@ void MovePicker::score_noncaptures() {
else
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)
hs += 1000;
// pst based scoring
moves[i].score = hs + pos.mg_pst_delta(m);
}
}
@@ -270,7 +293,6 @@ void MovePicker::score_evasions() {
} else
moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m);
}
// FIXME try psqt also here
}
void MovePicker::score_qcaptures() {
@@ -288,8 +310,11 @@ void MovePicker::score_qcaptures() {
}
/// find_best_index() loops across the moves and returns index of
/// the highest scored one.
/// find_best_index() loops across the moves and returns index of
/// 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() {
@@ -304,14 +329,48 @@ int MovePicker::find_best_index() {
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
/// from a list of generated moves (moves[] or badCaptures[], depending on
/// the current move generation phase). It takes care not to return the
/// 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() {
@@ -325,23 +384,8 @@ Move MovePicker::pick_move_from_list() {
while (movesPicked < numOfMoves)
{
int bestScore = -10000000;
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;
}
}
bestIndex = find_best_index();
if (bestIndex != -1) // Found a good capture
{
move = moves[bestIndex].move;
@@ -461,8 +505,9 @@ MovePicker::MovegenPhase MovePicker::current_move_type() const {
/// MovePicker::init_phase_table() initializes the PhaseTable[],
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
/// and QsearchWithoutChecksPhaseIndex variables. It is only called once
/// during program startup, and never again while the program is running.
/// QsearchNoCapturesPhaseIndex, QsearchWithoutChecksPhaseIndex and
/// NoMovesPhaseIndex variables. It is only called once during program
/// startup, and never again while the program is running.
void MovePicker::init_phase_table() {
@@ -491,8 +536,17 @@ void MovePicker::init_phase_table() {
PhaseTable[i++] = PH_QCHECKS;
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
QsearchWithoutChecksPhaseIndex = i - 1;
PhaseTable[i++] = PH_QCAPTURES;
PhaseTable[i++] = PH_STOP;
// Do not generate any move
NoMovesPhaseIndex = i - 1;
PhaseTable[i++] = PH_STOP;
}

View File

@@ -34,6 +34,9 @@
//// Types
////
struct EvalInfo;
struct SearchStack;
/// 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
/// moves we have reason to believe are good. The most important method is
@@ -60,7 +63,7 @@ public:
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(Lock &lock);
int number_of_moves() const;
@@ -77,6 +80,7 @@ private:
void score_qcaptures();
Move pick_move_from_list();
int find_best_index();
int find_best_index(Bitboard* squares, int values[]);
const Position& pos;
Move ttMove, mateKiller, killer1, killer2;

View File

@@ -23,6 +23,7 @@
////
#include <cassert>
#include <cstring>
#include "pawns.h"

View File

@@ -72,7 +72,7 @@ const SquareDelta PawnPush[2] = {
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];
}

View File

@@ -124,7 +124,7 @@ inline SquareDelta pawn_push(Color c) {
//// 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 bool piece_is_ok(Piece pc);
extern bool piece_type_is_ok(PieceType pt);

View File

@@ -31,6 +31,7 @@
#include "movepick.h"
#include "position.h"
#include "psqtab.h"
#include "san.h"
#include "ucioption.h"
@@ -38,6 +39,8 @@
//// Variables
////
extern SearchStack EmptySearchStack;
int Position::castleRightsMask[64];
Key Position::zobrist[2][8][64];
@@ -49,6 +52,7 @@ Key Position::zobSideToMove;
Value Position::MgPieceSquareTable[16][64];
Value Position::EgPieceSquareTable[16][64];
static bool RequestPending = false;
////
//// Functions
@@ -242,7 +246,7 @@ const std::string Position::to_fen() const {
fen += (rank > RANK_1 ? '/' : ' ');
}
fen += (sideToMove == WHITE ? 'w' : 'b') + ' ';
fen += (sideToMove == WHITE ? "w " : "b ");
if (castleRights != NO_CASTLES)
{
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
/// the standard output.
/// the standard output. If a move is given then also the san is print.
void Position::print() const {
char pieceStrings[][8] =
{"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
"| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
};
void Position::print(Move m) const {
for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
std::cout << "+---+---+---+---+---+---+---+---+\n";
for(File file = FILE_A; file <= FILE_H; file++) {
Square sq = make_square(file, rank);
Piece piece = piece_on(sq);
if(piece == EMPTY)
std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
else
std::cout << pieceStrings[piece];
}
std::cout << "|\n";
static const std::string pieceLetters = " PNBRQK PNBRQK .";
// Check for reentrancy, as example when called from inside
// MovePicker that is used also here in move_to_san()
if (RequestPending)
return;
RequestPending = true;
std::cout << std::endl;
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";
std::cout << to_fen() << std::endl;
std::cout << key << std::endl;
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{
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 is a capture.
/// position is a capture. Move must not be MOVE_NONE.
bool Position::move_is_capture(Move m) const {
assert(m != MOVE_NONE);
return ( !square_is_empty(move_to(m))
&& (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
)
@@ -1905,8 +1927,7 @@ bool Position::is_mate() {
if (is_check())
{
MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE,
MOVE_NONE, MOVE_NONE, Depth(0));
MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0));
return mp.get_next_move() == MOVE_NONE;
}
return false;

View File

@@ -128,7 +128,7 @@ public:
// Text input/output
void from_fen(const std::string &fen);
const std::string to_fen() const;
void print() const;
void print(Move m = MOVE_NONE) const;
// Copying
void copy(const Position &pos);
@@ -174,13 +174,13 @@ public:
// Number of pieces of each color and type
int piece_count(Color c, PieceType pt) const;
// The en passant square:
// The en passant square
Square ep_square() const;
// Current king position for each color
Square king_square(Color c) const;
// Castling rights.
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const;

View File

@@ -31,6 +31,7 @@
#include "movepick.h"
#include "san.h"
extern SearchStack EmptySearchStack;
////
//// Local definitions
@@ -50,7 +51,7 @@ namespace {
/// 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 score_string(Value v);
}
@@ -61,206 +62,222 @@ namespace {
////
/// 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.
const std::string move_to_san(Position &pos, Move m) {
std::string str;
const std::string move_to_san(const Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
if(m == MOVE_NONE) {
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;
std::string san = "";
from = move_from(m);
to = move_to(m);
pc = pos.piece_on(move_from(m));
str = "";
if(type_of_piece(pc) == PAWN) {
if(pos.move_is_capture(m))
str += file_to_char(square_file(move_from(m)));
}
else {
str += piece_type_to_char(type_of_piece(pc), true);
Ambiguity amb = move_ambiguity(pos, m);
switch(amb) {
case AMBIGUITY_NONE:
break;
case AMBIGUITY_FILE:
str += file_to_char(square_file(from));
break;
case AMBIGUITY_RANK:
str += rank_to_char(square_rank(from));
break;
case AMBIGUITY_BOTH:
str += square_to_string(from);
break;
default:
assert(false);
if (m == MOVE_NONE)
return "(none)";
else if (m == MOVE_NULL)
return "(null)";
else if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
else
{
Piece pc = pos.piece_on(move_from(m));
if (type_of_piece(pc) != PAWN)
{
san += piece_type_to_char(type_of_piece(pc), true);
Square from = move_from(m);
switch (move_ambiguity(pos, m)) {
case AMBIGUITY_NONE:
break;
case AMBIGUITY_FILE:
san += file_to_char(square_file(from));
break;
case AMBIGUITY_RANK:
san += rank_to_char(square_rank(from));
break;
case AMBIGUITY_BOTH:
san += square_to_string(from);
break;
default:
assert(false);
}
}
if (pos.move_is_capture(m))
{
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
// Position::move_is_check doesn't detect all checks (not castling moves,
// promotions and en passant captures).
UndoInfo u;
pos.do_move(m, u);
if(pos.is_check())
str += pos.is_mate()? "#" : "+";
pos.undo_move(m, u);
Position p(pos);
p.do_move(m, u);
if (p.is_check())
san += p.is_mate()? "#" : "+";
return str;
return san;
}
/// 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
/// 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());
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, OnePly);
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
// Castling moves
if(movestr == "O-O-O") {
Move m;
while((m = mp.get_next_move()) != MOVE_NONE)
if(move_is_long_castle(m) && pos.pl_move_is_legal(m))
return m;
return MOVE_NONE;
}
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;
}
if (movestr == "O-O-O" || movestr == "O-O-O+")
{
Move m;
while ((m = mp.get_next_move()) != MOVE_NONE)
if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
return m;
// 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;
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;
// 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;
to = make_square(toFile, toRank);
int matches = 0;
while((m = mp.get_next_move()) != MOVE_NONE) {
bool match = true;
if(pos.type_of_piece_on(move_from(m)) != pt)
match = false;
else if(move_to(m) != to)
match = false;
else if(move_promotion(m) != promotion)
match = false;
else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
match = false;
else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
match = false;
if(match) {
move = m;
matches++;
}
}
if(matches == 1)
return move;
else
return MOVE_NONE;
while ((m = mp.get_next_move()) != MOVE_NONE)
if ( pos.type_of_piece_on(move_from(m)) == pt
&& move_to(m) == to
&& move_promotion(m) == promotion
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
{
move = m;
matches++;
}
return (matches == 1 ? move : 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
/// inserted at the beginning of the new line.
const std::string line_to_san(const Position &pos, Move line[], int startColumn,
bool breakLines) {
Position p = Position(pos);
const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
UndoInfo u;
std::stringstream s;
std::string moveStr;
size_t length, maxLength;
size_t length = 0;
size_t maxLength = 80 - startColumn;
Position p(pos);
length = 0;
maxLength = 80 - startColumn;
for (int i = 0; line[i] != MOVE_NONE; i++)
{
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++) {
moveStr = move_to_san(p, line[i]);
length += moveStr.length() + 1;
if(breakLines && length > maxLength) {
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);
if (line[i] == MOVE_NULL)
p.do_null_move(u);
else
p.do_move(line[i], u);
}
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
/// 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[]) {
std::stringstream s;
// Depth
s << std::setw(2) << std::setfill(' ') << depth << " ";
s << std::setw(2) << depth << " ";
// Score
s << std::setw(8) << score_string(score);
// Time
s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
s << std::setw(8) << time_string(time) << " ";
// Nodes
if(nodes < 1000000ULL)
s << std::setw(8) << std::setfill(' ') << nodes << " ";
else if(nodes < 1000000000ULL)
s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
if (nodes < 1000000ULL)
s << std::setw(8) << nodes << " ";
else if (nodes < 1000000000ULL)
s << std::setw(7) << nodes/1000ULL << 'k' << " ";
else
s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
// PV
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 {
Ambiguity move_ambiguity(Position &pos, Move m) {
Square from, to;
Piece pc;
Ambiguity move_ambiguity(const Position& pos, Move m) {
from = move_from(m);
to = move_to(m);
pc = pos.piece_on(from);
Square from = move_from(m);
Square to = move_to(m);
Piece pc = pos.piece_on(from);
// King moves are never ambiguous, because there is never two kings of
// the same color.
if(type_of_piece(pc) == KING)
return AMBIGUITY_NONE;
if (type_of_piece(pc) == KING)
return AMBIGUITY_NONE;
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, OnePly);
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
Move mv, moveList[8];
int i, j, n;
n = 0;
while((mv = mp.get_next_move()) != MOVE_NONE)
if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
&& pos.pl_move_is_legal(mv))
moveList[n++] = mv;
if(n == 1)
return AMBIGUITY_NONE;
int n = 0;
while ((mv = mp.get_next_move()) != MOVE_NONE)
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
moveList[n++] = mv;
j = 0;
for(i = 0; i < n; i++)
if(square_file(move_from(moveList[i])) == square_file(from))
j++;
if(j == 1)
return AMBIGUITY_FILE;
if (n == 1)
return AMBIGUITY_NONE;
j = 0;
for(i = 0; i < n; i++)
if(square_rank(move_from(moveList[i])) == square_rank(from))
j++;
if(j == 1)
return AMBIGUITY_RANK;
int f = 0, r = 0;
for (int i = 0; i < n; i++)
{
if (square_file(move_from(moveList[i])) == square_file(from))
f++;
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;
}
const std::string time_string(int milliseconds) {
std::stringstream s;
s << std::setfill('0');
int hours = milliseconds / (1000 * 60 * 60);
int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
int hours = milliseconds / (1000*60*60);
int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
if(hours)
s << hours << ':';
s << std::setw(2) << std::setfill('0') << minutes << ':';
s << std::setw(2) << std::setfill('0') << seconds;
if (hours)
s << hours << ':';
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const std::string score_string(Value v) {
std::stringstream s;
if(abs(v) >= VALUE_MATE - 200) {
if(v < 0)
s << "-#" << (VALUE_MATE + v) / 2;
else
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
}
else {
float floatScore = float(v) / float(PawnValueMidgame);
if(v >= 0)
s << '+';
s << std::setprecision(2) << std::fixed << floatScore;
else if(v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
{
float floatScore = float(v) / float(PawnValueMidgame);
if (v >= 0)
s << '+';
s << std::setprecision(2) << std::fixed << floatScore;
}
return s.str();
}
}

View File

@@ -36,12 +36,9 @@
//// Prototypes
////
extern const std::string move_to_san(Position &pos, Move m);
extern Move move_from_san(Position &pos, const std::string &str);
extern const std::string line_to_san(const Position &pos, Move line[],
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 move_to_san(const Position& pos, Move m);
extern Move move_from_san(const Position& pos, const std::string& str);
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
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)

View File

@@ -106,6 +106,9 @@ namespace {
const bool UseIIDAtPVNodes = true;
const bool UseIIDAtNonPVNodes = false;
// Use null move driven internal iterative deepening?
bool UseNullDrivenIID = true;
// Internal iterative deepening margin. At Non-PV moves, when
// UseIIDAtNonPVNodes is true, we do an internal iterative deepening search
// when the static evaluation is at most IIDMargin below beta.
@@ -144,7 +147,7 @@ namespace {
bool UseFutilityPruning = true;
// 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 FutilityMargin1 = Value(0x100);
Value FutilityMargin2 = Value(0x300);
@@ -167,27 +170,28 @@ namespace {
Depth PawnEndgameExtension[2] = {OnePly, OnePly};
Depth MateThreatExtension[2] = {Depth(0), Depth(0)};
// Search depth at iteration 1:
// Search depth at iteration 1
const Depth InitialDepth = OnePly /*+ OnePly/2*/;
// Node counters
int NodesSincePoll;
int NodesBetweenPolls = 30000;
// Iteration counter:
// Iteration counter
int Iteration;
bool LastIterations;
// Scores and number of times the best move changed for each iteration:
Value ValueByIteration[PLY_MAX_PLUS_2];
int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
// MultiPV mode:
// MultiPV mode
int MultiPV = 1;
// Time managment variables
int SearchStartTime;
int MaxNodes, MaxDepth;
int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, TimeAdvantage;
int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
Move BestRootMove, PonderMove, EasyMove;
int RootMoveNumber;
bool InfiniteSearch;
@@ -237,19 +241,20 @@ namespace {
Depth depth, int ply, int threadID);
void sp_search(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_node(const Position &pos, SearchStack ss[], int ply, int threadID);
void update_pv(SearchStack ss[], int ply);
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
bool connected_moves(const Position &pos, Move m1, Move m2);
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
bool singleReply, bool mateThreat);
bool move_is_killer(Move m, const SearchStack& ss);
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_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_history(const Position &pos, Move m);
void update_history(const Position& pos, Move m, Depth depth,
Move movesSearched[], int moveCount);
void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount);
void update_killers(Move m, SearchStack& ss);
bool fail_high_ply_1();
int current_search_time();
@@ -297,6 +302,9 @@ Lock IOLock;
History H; // Should be made local?
// The empty search stack
SearchStack EmptySearchStack;
////
//// Functions
@@ -385,6 +393,7 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
if (UseLogFile)
LogFile.open(get_option_value_string("Search Log Filename").c_str(), std::ios::out | std::ios::app);
UseNullDrivenIID = get_option_value_bool("Null driven IID");
UseQSearchFutilityPruning = get_option_value_bool("Futility Pruning (Quiescence Search)");
UseFutilityPruning = get_option_value_bool("Futility Pruning (Main Search)");
@@ -422,16 +431,14 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
int myIncrement = increment[side_to_move];
int oppTime = time[1 - side_to_move];
TimeAdvantage = myTime - oppTime;
if (!movesToGo) // Sudden death time control
{
if (increment)
{
if (myIncrement)
{
MaxSearchTime = myTime / 30 + myIncrement;
AbsoluteMaxSearchTime = Max(myTime / 4, myIncrement - 100);
} else { // Blitz game without increment
MaxSearchTime = myTime / 40;
MaxSearchTime = myTime / 30;
AbsoluteMaxSearchTime = myTime / 8;
}
}
@@ -561,6 +568,9 @@ void init_threads() {
// Wait until the thread has finished launching:
while (!Threads[i].running);
}
// Init also the empty search stack
init_search_stack(EmptySearchStack);
}
@@ -617,6 +627,7 @@ namespace {
ValueByIteration[0] = Value(0);
ValueByIteration[1] = rml.get_move_score(0);
Iteration = 1;
LastIterations = false;
EasyMove = rml.scan_for_easy_move();
@@ -671,9 +682,8 @@ namespace {
ExtraSearchTime = BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2)
+ BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
// If we need some more and we are in time advantage take it
if (ExtraSearchTime > 0 && TimeAdvantage > 2 * MaxSearchTime)
ExtraSearchTime += MaxSearchTime / 2;
// Try to guess if the current iteration is the last one or the last two
LastIterations = (current_search_time() > ((MaxSearchTime + ExtraSearchTime)*58) / 128);
// 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
@@ -766,7 +776,8 @@ namespace {
<< " currmovenumber " << i + 1 << std::endl;
// 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;
// Make the move, and search it
@@ -783,7 +794,7 @@ namespace {
if (Problem && StopOnPonderhit)
StopOnPonderhit = false;
}
}
else
{
value = -search(pos, ss, -alpha, newDepth, 1, true, 0);
@@ -933,16 +944,14 @@ namespace {
// Initialize a MovePicker object for the current position, and prepare
// to search all moves
MovePicker mp = MovePicker(pos, true, ttMove, ss[ply].mateKiller,
ss[ply].killer1, ss[ply].killer2, depth);
MovePicker mp = MovePicker(pos, true, ttMove, ss[ply], depth);
Move move, movesSearched[256];
int moveCount = 0;
Value value, bestValue = -VALUE_INFINITE;
Bitboard dcCandidates = mp.discovered_check_candidates();
bool isCheck = pos.is_check();
bool mateThreat = MateThreatExtension[1] > Depth(0)
&& pos.has_mate_threat(opposite_color(pos.side_to_move()));
bool mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move()));
// Loop through all legal moves until no moves remain or a beta cutoff
// occurs.
@@ -955,7 +964,6 @@ namespace {
bool singleReply = (isCheck && mp.number_of_moves() == 1);
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
movesSearched[moveCount++] = ss[ply].currentMove = move;
@@ -967,7 +975,8 @@ namespace {
ss[ply].currentMoveCaptureValue = Value(0);
// 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;
// Make and search the move
@@ -981,14 +990,12 @@ namespace {
// 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 ( depth >= 2*OnePly
&& ext == Depth(0)
&& moveCount >= LMRPVMoves
&& !dangerous
&& !moveIsCapture
&& !move_promotion(move)
&& !moveIsPassedPawnPush
&& !move_is_castle(move)
&& move != ss[ply].killer1
&& move != ss[ply].killer2)
&& !move_is_killer(move, ss[ply]))
{
ss[ply].reduction = OnePly;
value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true, threadID);
@@ -1070,11 +1077,7 @@ namespace {
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
update_history(pos, m, depth, movesSearched, moveCount);
if (m != ss[ply].killer1)
{
ss[ply].killer2 = ss[ply].killer1;
ss[ply].killer1 = m;
}
update_killers(m, ss[ply]);
}
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
}
@@ -1125,12 +1128,13 @@ namespace {
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
ss[ply].currentMove = ttMove; // can be MOVE_NONE ?
ss[ply].currentMove = ttMove; // can be MOVE_NONE
return value_from_tt(tte->value(), ply);
}
Value approximateEval = quick_evaluate(pos);
bool mateThreat = false;
bool nullDrivenIID = false;
bool isCheck = pos.is_check();
// Null move search
@@ -1143,7 +1147,22 @@ namespace {
UndoInfo 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 there is a good possibility that this is a cut-node.
// We will do an IID later to find a ttMove.
if ( UseNullDrivenIID
&& nullValue < beta
&& depth > 6 * OnePly
&& ttMove == MOVE_NONE
&& ss[ply + 1].currentMove != MOVE_NONE
&& pos.move_is_capture(ss[ply + 1].currentMove)
&& pos.see(ss[ply + 1].currentMove) * PawnValueMidgame + nullValue > beta - IIDMargin)
nullDrivenIID = true;
pos.undo_null_move(u);
if (nullValue >= beta)
@@ -1163,8 +1182,10 @@ namespace {
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
if (nullValue == value_mated_in(ply + 2))
{
mateThreat = true;
nullDrivenIID = false;
}
ss[ply].threatMove = ss[ply + 1].currentMove;
if ( depth < ThreatDepth
&& ss[ply - 1].reduction
@@ -1188,11 +1209,23 @@ namespace {
search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
ttMove = ss[ply].pv[ply];
}
else if (nullDrivenIID)
{
// The null move failed low due to a suspicious capture. Perhaps we
// are facing a null capture artifact due to the side to move change
// and this is a cut-node. So it's a good time to search for a ttMove.
Move tm = ss[ply].threatMove;
assert(tm != MOVE_NONE);
search(pos, ss, beta, Min(depth/2, depth-3*OnePly), ply, false, threadID);
ttMove = ss[ply].pv[ply];
ss[ply].threatMove = tm;
}
// Initialize a MovePicker object for the current position, and prepare
// to search all moves:
MovePicker mp = MovePicker(pos, false, ttMove, ss[ply].mateKiller,
ss[ply].killer1, ss[ply].killer2, depth);
MovePicker mp = MovePicker(pos, false, ttMove, ss[ply], depth);
Move move, movesSearched[256];
int moveCount = 0;
@@ -1214,19 +1247,18 @@ namespace {
bool singleReply = (isCheck && mp.number_of_moves() == 1);
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
movesSearched[moveCount++] = ss[ply].currentMove = move;
// 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;
// Futility pruning
if ( useFutilityPruning
&& ext == Depth(0)
&& !dangerous
&& !moveIsCapture
&& !moveIsPassedPawnPush
&& !move_promotion(move))
{
if ( moveCount >= 2 + int(depth)
@@ -1254,15 +1286,13 @@ namespace {
// 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 ( depth >= 2*OnePly
&& ext == Depth(0)
&& moveCount >= LMRNonPVMoves
if ( depth >= 2*OnePly
&& moveCount >= LMRNonPVMoves
&& !dangerous
&& !moveIsCapture
&& !move_promotion(move)
&& !moveIsPassedPawnPush
&& !move_is_castle(move)
&& move != ss[ply].killer1
&& move != ss[ply].killer2)
&& !move_is_killer(move, ss[ply]))
{
ss[ply].reduction = OnePly;
value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true, threadID);
@@ -1321,11 +1351,7 @@ namespace {
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
update_history(pos, m, depth, movesSearched, moveCount);
if (m != ss[ply].killer1)
{
ss[ply].killer2 = ss[ply].killer1;
ss[ply].killer1 = m;
}
update_killers(m, ss[ply]);
}
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
}
@@ -1382,12 +1408,13 @@ namespace {
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth == 0) will be generated.
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, depth);
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, depth, &ei);
Move move;
int moveCount = 0;
Bitboard dcCandidates = mp.discovered_check_candidates();
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
// occurs.
@@ -1396,20 +1423,17 @@ namespace {
{
assert(move_is_ok(move));
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
moveCount++;
ss[ply].currentMove = move;
// Futility pruning
if ( UseQSearchFutilityPruning
&& enoughMaterial
&& !isCheck
&& !moveIsCheck
&& !pvNode
&& !move_promotion(move)
&& !moveIsPassedPawnPush
&& beta - alpha == 1
&& pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame)
&& !pos.move_is_check(move, dcCandidates)
&& !pos.move_is_passed_pawn_push(move))
{
Value futilityValue = staticValue
+ Max(pos.midgame_value_of_piece_on(move_to(move)),
@@ -1425,7 +1449,7 @@ namespace {
}
}
// Don't search captures and checks with negative SEE values.
// Don't search captures and checks with negative SEE values
if ( !isCheck
&& !move_promotion(move)
&& (pos.midgame_value_of_piece_on(move_from(move)) >
@@ -1463,6 +1487,13 @@ namespace {
// Update transposition table
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;
}
@@ -1497,7 +1528,6 @@ namespace {
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
lock_grab(&(sp->lock));
int moveCount = ++sp->moves;
@@ -1506,14 +1536,14 @@ namespace {
ss[sp->ply].currentMove = move;
// 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;
// Prune?
if ( useFutilityPruning
&& ext == Depth(0)
&& !dangerous
&& !moveIsCapture
&& !moveIsPassedPawnPush
&& !move_promotion(move)
&& moveCount >= 2 + int(sp->depth)
&& ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
@@ -1525,14 +1555,12 @@ namespace {
// 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 ( ext == Depth(0)
if ( !dangerous
&& moveCount >= LMRNonPVMoves
&& !moveIsCapture
&& !moveIsPassedPawnPush
&& !move_promotion(move)
&& !move_is_castle(move)
&& move != ss[sp->ply].killer1
&& move != ss[sp->ply].killer2)
&& !move_is_killer(move, ss[sp->ply]))
{
ss[sp->ply].reduction = OnePly;
value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1, true, threadID);
@@ -1610,7 +1638,6 @@ namespace {
{
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
@@ -1624,7 +1651,8 @@ namespace {
ss[sp->ply].currentMove = move;
// 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;
// Make and search the move.
@@ -1633,14 +1661,12 @@ namespace {
// 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 ( ext == Depth(0)
if ( !dangerous
&& moveCount >= LMRPVMoves
&& !moveIsCapture
&& !moveIsPassedPawnPush
&& !move_promotion(move)
&& !move_is_castle(move)
&& move != ss[sp->ply].killer1
&& move != ss[sp->ply].killer2)
&& !move_is_killer(move, ss[sp->ply]))
{
ss[sp->ply].reduction = OnePly;
value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1, true, threadID);
@@ -1871,17 +1897,28 @@ namespace {
// init_search_stack() initializes a search stack at the beginning of a
// 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[]) {
for(int i = 0; i < 3; i++) {
ss[i].pv[i] = MOVE_NONE;
ss[i].pv[i+1] = MOVE_NONE;
ss[i].currentMove = MOVE_NONE;
ss[i].mateKiller = MOVE_NONE;
ss[i].killer1 = MOVE_NONE;
ss[i].killer2 = MOVE_NONE;
ss[i].threatMove = MOVE_NONE;
ss[i].reduction = Depth(0);
for (int i = 0; i < 3; i++)
{
ss[i].pv[i] = MOVE_NONE;
ss[i].pv[i+1] = MOVE_NONE;
ss[i].currentMove = MOVE_NONE;
ss[i].threatMove = MOVE_NONE;
ss[i].reduction = Depth(0);
for (int j = 0; j < KILLER_MAX; j++)
ss[i].killers[j] = MOVE_NONE;
}
}
@@ -1905,13 +1942,13 @@ namespace {
NodesSincePoll = 0;
}
}
ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = 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].reduction = Depth(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)
print_current_line(ss, ply, threadID);
@@ -2014,14 +2051,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,
// 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,
bool check, bool singleReply, bool mateThreat) {
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
bool singleReply, bool mateThreat, bool* dangerous) {
Depth result = Depth(0);
*dangerous = check || singleReply || mateThreat;
if (check)
result += CheckExtension[pvNode];
@@ -2029,26 +2084,37 @@ namespace {
if (singleReply)
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)
result += MateThreatExtension[pvNode];
if ( pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
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
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
&& !move_promotion(m))
{
result += PawnEndgameExtension[pvNode];
*dangerous = true;
}
if ( pvNode
&& pos.move_is_capture(m)
&& pos.type_of_piece_on(move_to(m)) != PAWN
&& pos.see(m) >= 0)
{
result += OnePly/2;
*dangerous = true;
}
return Min(result, OnePly);
}
@@ -2137,13 +2203,11 @@ namespace {
// 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) {
return pos.square_is_empty(move_to(m))
&& !move_promotion(m)
&& !move_is_ep(m);
return !pos.move_is_capture(m) && !move_promotion(m);
}
@@ -2156,8 +2220,26 @@ namespace {
H.success(pos.piece_on(move_from(m)), m, depth);
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]);
}
}
// 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

View File

@@ -41,6 +41,7 @@
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = 102;
const int KILLER_MAX = 2;
////
@@ -56,8 +57,9 @@ struct SearchStack {
Move pv[PLY_MAX];
Move currentMove;
Value currentMoveCaptureValue;
Move mateKiller, killer1, killer2;
Move mateKiller;
Move threatMove;
Move killers[KILLER_MAX];
Depth reduction;
};
@@ -66,10 +68,9 @@ struct SearchStack {
//// Global variables
////
extern SearchStack EmptySearchStack;
extern TranspositionTable TT;
extern int ActiveThreads;
extern Lock SMPLock;
// Perhaps better to make H local, and pass as parameter to MovePicker?

View File

@@ -25,6 +25,7 @@
//// Includes
////
#include <cstdlib> // for abs()
#include <string>
#include "color.h"

View File

@@ -24,6 +24,7 @@
#include <cassert>
#include <cmath>
#include <cstring>
#include "tt.h"
@@ -107,29 +108,25 @@ void TranspositionTable::store(const Position &pos, Value v, Depth d,
TTEntry *tte, *replace;
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
{
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
writes++;
return;
}
if ((tte+i)->key() == pos.get_key()) // overwrite old
if (!tte->key() || tte->key() == pos.get_key()) // empty or overwrite old
{
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;
}
if ( i == 0 // already is (replace == tte+i), common case
|| replace->generation() < (tte+i)->generation())
else if (i == 0) // replace would be a no-op in this common case
continue;
if ( replace->generation() > (tte+i)->generation()
|| (tte+i)->depth() < replace->depth())
replace = tte+i;
int c1 = (replace->generation() == generation ? 2 : 0);
int c2 = (tte->generation() == generation ? -2 : 0);
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);
writes++;
@@ -144,9 +141,8 @@ const TTEntry* TranspositionTable::retrieve(const Position &pos) const {
TTEntry *tte = first_entry(pos);
for (int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++, tte++)
{
tte += i;
if (tte->key() == pos.get_key())
return tte;
}

View File

@@ -120,6 +120,7 @@ namespace {
o.push_back(Option("Full Depth Moves (non-PV nodes)", 3, 1, 100));
o.push_back(Option("Threat Depth", 5, 0, 100));
o.push_back(Option("Selective Plies", 7, 0, 10));
o.push_back(Option("Null driven IID", true));
o.push_back(Option("Futility Pruning (Main Search)", true));
o.push_back(Option("Futility Pruning (Quiescence Search)", true));
o.push_back(Option("Futility Margin 0", 50, 0, 1000));
@@ -176,7 +177,7 @@ namespace {
template<typename T>
T get_option_value(const std::string& optionName) {
T ret;
T ret = T();
Options::iterator it = option_with_name(optionName);
if (it != options.end())