From c144bb9800a6f84ee7fecb0b71905893df6f35ac Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 18 Sep 2016 00:05:29 +0200 Subject: [PATCH] DroidFish: Updated stockfish engine to git version from 2016-09-17. --- DroidFish/jni/stockfish/evaluate.cpp | 88 +++---- DroidFish/jni/stockfish/evaluate.h | 1 - DroidFish/jni/stockfish/main.cpp | 6 +- DroidFish/jni/stockfish/material.cpp | 7 +- DroidFish/jni/stockfish/misc.cpp | 2 +- DroidFish/jni/stockfish/movepick.cpp | 275 +++++++++++---------- DroidFish/jni/stockfish/movepick.h | 36 ++- DroidFish/jni/stockfish/pawns.cpp | 1 + DroidFish/jni/stockfish/pawns.h | 2 + DroidFish/jni/stockfish/position.cpp | 212 ++++++++-------- DroidFish/jni/stockfish/position.h | 98 ++++---- DroidFish/jni/stockfish/psqt.cpp | 14 +- DroidFish/jni/stockfish/search.cpp | 90 ++++--- DroidFish/jni/stockfish/search.h | 12 +- DroidFish/jni/stockfish/syzygy/tbprobe.cpp | 12 +- DroidFish/jni/stockfish/thread.h | 5 +- DroidFish/jni/stockfish/types.h | 48 ++-- 17 files changed, 457 insertions(+), 452 deletions(-) diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 177d293..3095838 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -206,21 +206,15 @@ namespace { #undef S #undef V - // King danger constants and variables. The king danger scores are looked-up - // in KingDanger[]. Various little "meta-bonuses" measuring the strength - // of the enemy attack are added up into an integer, which is used as an - // index to KingDanger[]. - Score KingDanger[512]; - // KingAttackWeights[PieceType] contains king attack weights by piece type - const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 7, 5, 4, 1 }; + const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 }; // Penalties for enemy's safe checks - const int QueenContactCheck = 89; - const int QueenCheck = 62; - const int RookCheck = 57; - const int BishopCheck = 48; - const int KnightCheck = 78; + const int QueenContactCheck = 997; + const int QueenCheck = 695; + const int RookCheck = 638; + const int BishopCheck = 538; + const int KnightCheck = 874; // eval_init() initializes king and attack bitboards for a given color @@ -360,7 +354,8 @@ namespace { if (Pt == QUEEN) { // Penalty if any relative pin or discovered attack against the queen - if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s)) + Bitboard pinners; + if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, pinners)) score -= WeakQueen; } } @@ -400,7 +395,7 @@ namespace { const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); Bitboard undefended, b, b1, b2, safe, other; - int attackUnits; + int kingDanger; const Square ksq = pos.square(Us); // King shelter and enemy pawns storm @@ -418,24 +413,24 @@ namespace { b = ei.attackedBy[Them][ALL_PIECES] & ~ei.attackedBy[Us][ALL_PIECES] & ei.kingRing[Us] & ~pos.pieces(Them); - // Initialize the 'attackUnits' variable, which is used later on as an - // index into the KingDanger[] array. The initial value is based on the + // Initialize the 'kingDanger' variable, which will be transformed + // later into a king danger score. The initial value is based on the // number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king and the quality of // the pawn shelter (current 'score' value). - attackUnits = std::min(72, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) - + 9 * ei.kingAdjacentZoneAttacksCount[Them] - + 21 * popcount(undefended) - + 12 * (popcount(b) + !!ei.pinnedPieces[Us]) - - 64 * !pos.count(Them) - - mg_value(score) / 8; + kingDanger = std::min(807, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + + 101 * ei.kingAdjacentZoneAttacksCount[Them] + + 235 * popcount(undefended) + + 134 * (popcount(b) + !!ei.pinnedPieces[Us]) + - 717 * !pos.count(Them) + - 7 * mg_value(score) / 5 - 5; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king reachable by the enemy queen... b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); // ...and keep squares supported by another enemy piece - attackUnits += QueenContactCheck * popcount(b & ei.attackedBy2[Them]); + kingDanger += QueenContactCheck * popcount(b & ei.attackedBy2[Them]); // Analyse the safe enemy's checks which are possible on next move... safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); @@ -450,7 +445,7 @@ namespace { // Enemy queen safe checks if ((b1 | b2) & ei.attackedBy[Them][QUEEN] & safe) - attackUnits += QueenCheck, score -= SafeCheck; + kingDanger += QueenCheck, score -= SafeCheck; // For other pieces, also consider the square safe if attacked twice, // and only defended by a queen. @@ -460,14 +455,14 @@ namespace { // Enemy rooks safe and other checks if (b1 & ei.attackedBy[Them][ROOK] & safe) - attackUnits += RookCheck, score -= SafeCheck; + kingDanger += RookCheck, score -= SafeCheck; else if (b1 & ei.attackedBy[Them][ROOK] & other) score -= OtherCheck; // Enemy bishops safe and other checks if (b2 & ei.attackedBy[Them][BISHOP] & safe) - attackUnits += BishopCheck, score -= SafeCheck; + kingDanger += BishopCheck, score -= SafeCheck; else if (b2 & ei.attackedBy[Them][BISHOP] & other) score -= OtherCheck; @@ -475,14 +470,14 @@ namespace { // Enemy knights safe and other checks b = pos.attacks_from(ksq) & ei.attackedBy[Them][KNIGHT]; if (b & safe) - attackUnits += KnightCheck, score -= SafeCheck; + kingDanger += KnightCheck, score -= SafeCheck; else if (b & other) score -= OtherCheck; - // Finally, extract the king danger score from the KingDanger[] - // array and subtract the score from the evaluation. - score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; + // Compute the king danger score and subtract it from the evaluation + if (kingDanger > 0) + score -= make_score(std::min(kingDanger * kingDanger / 4096, 2 * int(BishopValueMg)), 0); } // King tropism: firstly, find squares that opponent attacks in our king flank @@ -706,9 +701,9 @@ namespace { // ...count safe + (behind & safe) with a single popcount int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); bonus = std::min(16, bonus); - int weight = pos.count(Us); + int weight = pos.count(Us) - 2 * ei.pi->open_files(); - return make_score(bonus * weight * weight / 22, 0); + return make_score(bonus * weight * weight / 18, 0); } @@ -779,23 +774,22 @@ Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); + Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; EvalInfo ei; - Score score, mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; - - // Initialize score by reading the incrementally updated scores included in - // the position object (material + piece square tables). Score is computed - // internally from the white point of view. - score = pos.psq_score(); // Probe the material hash table ei.me = Material::probe(pos); - score += ei.me->imbalance(); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.me->specialized_eval_exists()) return ei.me->evaluate(pos); + // Initialize score by reading the incrementally updated scores included in + // the position object (material + piece square tables) and the material + // imbalance. Score is computed internally from the white point of view. + Score score = pos.psq_score() + ei.me->imbalance(); + // Probe the pawn hash table ei.pi = Pawns::probe(pos); score += ei.pi->pawns_score(); @@ -920,19 +914,3 @@ std::string Eval::trace(const Position& pos) { return ss.str(); } - - -/// init() computes evaluation weights, usually at startup - -void Eval::init() { - - const int MaxSlope = 322; - const int Peak = 47410; - int t = 0; - - for (int i = 0; i < 400; ++i) - { - t = std::min(Peak, std::min(i * i - 16, t + MaxSlope)); - KingDanger[i] = make_score(t * 268 / 7700, 0); - } -} diff --git a/DroidFish/jni/stockfish/evaluate.h b/DroidFish/jni/stockfish/evaluate.h index 26661dc..7f655f6 100644 --- a/DroidFish/jni/stockfish/evaluate.h +++ b/DroidFish/jni/stockfish/evaluate.h @@ -31,7 +31,6 @@ namespace Eval { const Value Tempo = Value(20); // Must be visible to search -void init(); std::string trace(const Position& pos); template diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index 6fee591..7187d30 100644 --- a/DroidFish/jni/stockfish/main.cpp +++ b/DroidFish/jni/stockfish/main.cpp @@ -21,7 +21,6 @@ #include #include "bitboard.h" -#include "evaluate.h" #include "position.h" #include "search.h" #include "thread.h" @@ -29,6 +28,10 @@ #include "uci.h" #include "syzygy/tbprobe.h" +namespace PSQT { + void init(); +} + int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; @@ -39,7 +42,6 @@ int main(int argc, char* argv[]) { Position::init(); Bitbases::init(); Search::init(); - Eval::init(); Pawns::init(); Threads.init(); Tablebases::init(Options["SyzygyPath"]); diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index f73977b..f6c4e2d 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -31,13 +31,10 @@ namespace { // Polynomial material imbalance parameters - // pair pawn knight bishop rook queen - const int Linear[6] = { 1667, -168, -1027, -166, 238, -138 }; - const int QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen - { 0 }, // Bishop pair + {1667 }, // Bishop pair { 40, 2 }, // Pawn { 32, 255, -3 }, // Knight OUR PIECES { 0, 104, 4, 0 }, // Bishop @@ -100,7 +97,7 @@ namespace { if (!pieceCount[Us][pt1]) continue; - int v = Linear[pt1]; + int v = 0; for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 680e220..03b3179 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -32,7 +32,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "2016-08-28"; +const string Version = "2016-09-17"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 78765be..ea58738 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -26,13 +26,12 @@ namespace { enum Stages { - MAIN_SEARCH, GOOD_CAPTURES, KILLERS, QUIET, BAD_CAPTURES, - EVASION, ALL_EVASIONS, - QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS, - QSEARCH_WITHOUT_CHECKS, QCAPTURES_2, - PROBCUT, PROBCUT_CAPTURES, - RECAPTURE, RECAPTURES, - STOP + MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLERS, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES, + EVASION, EVASIONS_INIT, ALL_EVASIONS, + PROBCUT, PROBCUT_INIT, PROBCUT_CAPTURES, + QSEARCH_WITH_CHECKS, QCAPTURES_1_INIT, QCAPTURES_1, QCHECKS, + QSEARCH_NO_CHECKS, QCAPTURES_2_INIT, QCAPTURES_2, + QSEARCH_RECAPTURES, QRECAPTURES }; // Our insertion sort, which is guaranteed to be stable, as it should be @@ -77,7 +76,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s) stage = pos.checkers() ? EVASION : MAIN_SEARCH; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) @@ -92,17 +91,17 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) stage = QSEARCH_WITH_CHECKS; else if (d > DEPTH_QS_RECAPTURES) - stage = QSEARCH_WITHOUT_CHECKS; + stage = QSEARCH_NO_CHECKS; else { - stage = RECAPTURE; + stage = QSEARCH_RECAPTURES; recaptureSquare = s; - ttm = MOVE_NONE; + return; } ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Value th) @@ -118,7 +117,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th) && pos.capture(ttm) && pos.see(ttm) > threshold ? ttm : MOVE_NONE; - endMoves += (ttMove != MOVE_NONE); + stage += (ttMove == MOVE_NONE); } @@ -180,70 +179,6 @@ void MovePicker::score() { } -/// generate_next_stage() generates, scores, and sorts the next bunch of moves -/// when there are no more moves to try for the current stage. - -void MovePicker::generate_next_stage() { - - assert(stage != STOP); - - cur = moves; - - switch (++stage) { - - case GOOD_CAPTURES: case QCAPTURES_1: case QCAPTURES_2: - case PROBCUT_CAPTURES: case RECAPTURES: - endMoves = generate(pos, moves); - score(); - break; - - case KILLERS: - killers[0] = ss->killers[0]; - killers[1] = ss->killers[1]; - killers[2] = countermove; - cur = killers; - endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]); - break; - - case QUIET: - endMoves = generate(pos, moves); - score(); - if (depth < 3 * ONE_PLY) - { - ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m) - { return m.value > VALUE_ZERO; }); - insertion_sort(cur, goodQuiet); - } else - insertion_sort(cur, endMoves); - break; - - case BAD_CAPTURES: - // Just pick them in reverse order to get correct ordering - cur = moves + MAX_MOVES - 1; - endMoves = endBadCaptures; - break; - - case ALL_EVASIONS: - endMoves = generate(pos, moves); - if (endMoves - moves > 1) - score(); - break; - - case CHECKS: - endMoves = generate(pos, moves); - break; - - case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS: - case PROBCUT: case RECAPTURE: case STOP: - stage = STOP; - break; - - default: - assert(false); - } -} - - /// next_move() is the most important method of the MovePicker class. It returns /// a new pseudo legal move every time it is called, until there are no more moves /// left. It picks the move with the biggest value from a list of generated moves @@ -253,80 +188,170 @@ Move MovePicker::next_move() { Move move; - while (true) - { - while (cur == endMoves && stage != STOP) - generate_next_stage(); + switch (stage) { - switch (stage) { + case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: + case QSEARCH_NO_CHECKS: case PROBCUT: + ++stage; + return ttMove; - case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: - case QSEARCH_WITHOUT_CHECKS: case PROBCUT: - ++cur; - return ttMove; + case CAPTURES_INIT: + endBadCaptures = cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; - case GOOD_CAPTURES: + case GOOD_CAPTURES: + while (cur < endMoves) + { move = pick_best(cur++, endMoves); if (move != ttMove) { if (pos.see_sign(move) >= VALUE_ZERO) return move; - // Losing capture, move it to the tail of the array - *endBadCaptures-- = move; + // Losing capture, move it to the beginning of the array + *endBadCaptures++ = move; } - break; + } - case KILLERS: - move = *cur++; - if ( move != MOVE_NONE - && move != ttMove - && pos.pseudo_legal(move) - && !pos.capture(move)) - return move; - break; + ++stage; + move = ss->killers[0]; // First killer move + if ( move != MOVE_NONE + && move != ttMove + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; - case QUIET: + case KILLERS: + ++stage; + move = ss->killers[1]; // Second killer move + if ( move != MOVE_NONE + && move != ttMove + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; + + case COUNTERMOVE: + ++stage; + move = countermove; + if ( move != MOVE_NONE + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1] + && pos.pseudo_legal(move) + && !pos.capture(move)) + return move; + + case QUIET_INIT: + cur = endBadCaptures; + endMoves = generate(pos, cur); + score(); + if (depth < 3 * ONE_PLY) + { + ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m) + { return m.value > VALUE_ZERO; }); + insertion_sort(cur, goodQuiet); + } else + insertion_sort(cur, endMoves); + ++stage; + + case QUIET: + while (cur < endMoves) + { move = *cur++; if ( move != ttMove - && move != killers[0] - && move != killers[1] - && move != killers[2]) + && move != ss->killers[0] + && move != ss->killers[1] + && move != countermove) return move; - break; + } + ++stage; + cur = moves; // Point to beginning of bad captures - case BAD_CAPTURES: - return *cur--; + case BAD_CAPTURES: + if (cur < endBadCaptures) + return *cur++; + break; - case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2: + case EVASIONS_INIT: + cur = moves; + endMoves = generate(pos, cur); + if (endMoves - cur - (ttMove != MOVE_NONE) > 1) + score(); + ++stage; + + case ALL_EVASIONS: + while (cur < endMoves) + { move = pick_best(cur++, endMoves); if (move != ttMove) return move; + } + break; + + case PROBCUT_INIT: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; + + case PROBCUT_CAPTURES: + while (cur < endMoves) + { + move = pick_best(cur++, endMoves); + if ( move != ttMove + && pos.see(move) > threshold) + return move; + } + break; + + case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; + + case QCAPTURES_1: case QCAPTURES_2: + while (cur < endMoves) + { + move = pick_best(cur++, endMoves); + if (move != ttMove) + return move; + } + if (stage == QCAPTURES_2) break; + cur = moves; + endMoves = generate(pos, cur); + ++stage; - case PROBCUT_CAPTURES: - move = pick_best(cur++, endMoves); - if (move != ttMove && pos.see(move) > threshold) - return move; - break; + case QCHECKS: + while (cur < endMoves) + { + move = cur++->move; + if (move != ttMove) + return move; + } + break; - case RECAPTURES: + case QSEARCH_RECAPTURES: + cur = moves; + endMoves = generate(pos, cur); + score(); + ++stage; + + case QRECAPTURES: + while (cur < endMoves) + { move = pick_best(cur++, endMoves); if (to_sq(move) == recaptureSquare) return move; - break; - - case CHECKS: - move = *cur++; - if (move != ttMove) - return move; - break; - - case STOP: - return MOVE_NONE; - - default: - assert(false); } + break; + + default: + assert(false); } + + return MOVE_NONE; } diff --git a/DroidFish/jni/stockfish/movepick.h b/DroidFish/jni/stockfish/movepick.h index 8028d48..6fbd8be 100644 --- a/DroidFish/jni/stockfish/movepick.h +++ b/DroidFish/jni/stockfish/movepick.h @@ -26,7 +26,6 @@ #include "movegen.h" #include "position.h" -#include "search.h" #include "types.h" @@ -45,9 +44,7 @@ struct Stats { const T* operator[](Piece pc) const { return table[pc]; } T* operator[](Piece pc) { return table[pc]; } void clear() { std::memset(table, 0, sizeof(table)); } - void update(Piece pc, Square to, Move m) { table[pc][to] = m; } - void update(Piece pc, Square to, Value v) { if (abs(int(v)) >= 324) @@ -68,31 +65,32 @@ typedef Stats CounterMoveHistoryStats; struct FromToStats { - Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } - void clear() { std::memset(table, 0, sizeof(table)); } + Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } + void clear() { std::memset(table, 0, sizeof(table)); } + void update(Color c, Move m, Value v) { - void update(Color c, Move m, Value v) - { - if (abs(int(v)) >= 324) - return; + if (abs(int(v)) >= 324) + return; - Square f = from_sq(m); - Square t = to_sq(m); + Square from = from_sq(m); + Square to = to_sq(m); - table[c][f][t] -= table[c][f][t] * abs(int(v)) / 324; - table[c][f][t] += int(v) * 32; - } + table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324; + table[c][from][to] += int(v) * 32; + } private: - Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; + Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; }; + /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo legal move each time it is called, until there are no moves left, /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// beta algorithm, MovePicker attempts to return the moves which are most likely /// to get a cut-off first. +namespace Search { struct Stack; } class MovePicker { public: @@ -107,8 +105,7 @@ public: private: template void score(); - void generate_next_stage(); - ExtMove* begin() { return moves; } + ExtMove* begin() { return cur; } ExtMove* end() { return endMoves; } const Position& pos; @@ -116,12 +113,11 @@ private: Move countermove; Depth depth; Move ttMove; - ExtMove killers[3]; Square recaptureSquare; Value threshold; int stage; - ExtMove* endBadCaptures = moves + MAX_MOVES - 1; - ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves; + ExtMove *cur, *endMoves, *endBadCaptures; + ExtMove moves[MAX_MOVES]; }; #endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/DroidFish/jni/stockfish/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 72496fc..b83b780 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -215,6 +215,7 @@ Entry* probe(const Position& pos) { e->key = key; e->score = evaluate(pos, e) - evaluate(pos, e); e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]); + e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); return e; } diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index 24843e3..e26ae67 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -38,6 +38,7 @@ struct Entry { Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int pawn_asymmetry() const { return asymmetry; } + int open_files() const { return openFiles; } int semiopen_file(Color c, File f) const { return semiopenFiles[c] & (1 << f); @@ -74,6 +75,7 @@ struct Entry { int semiopenFiles[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int asymmetry; + int openFiles; }; typedef HashTable Table; diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index bc8e92a..c193f26 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -20,7 +20,8 @@ #include #include -#include // For std::memset, std::memcmp +#include // For offsetof() +#include // For std::memset, std::memcmp #include #include @@ -34,17 +35,18 @@ using std::string; +namespace PSQT { + extern Score psq[PIECE_NB][SQUARE_NB]; +} + namespace Zobrist { - Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; Key side; - Key exclusion; } -Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion; } - namespace { const string PieceToChar(" PNBRQK pnbrqk"); @@ -112,10 +114,9 @@ void Position::init() { PRNG rng(1070372); - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[c][pt][s] = rng.rand(); + for (Piece pc : Pieces) + for (Square s = SQ_A1; s <= SQ_H8; ++s) + Zobrist::psq[pc][s] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); @@ -132,7 +133,6 @@ void Position::init() { } Zobrist::side = rng.rand(); - Zobrist::exclusion = rng.rand(); } @@ -182,7 +182,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); - std::fill_n(&pieceList[0][0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); + std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; ss >> std::noskipws; @@ -198,7 +198,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(color_of(Piece(idx)), type_of(Piece(idx)), sq); + put_piece(Piece(idx), sq); ++sq; } } @@ -296,8 +296,8 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info(StateInfo* si) const { - si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE)); - si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK)); + si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinnersForKing[WHITE]); + si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinnersForKing[BLACK]); Square ksq = square(~sideToMove); @@ -328,8 +328,8 @@ void Position::set_state(StateInfo* si) const { { Square s = pop_lsb(&b); Piece pc = piece_on(s); - si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s]; - si->psq += PSQT::psq[color_of(pc)][type_of(pc)][s]; + si->key ^= Zobrist::psq[pc][s]; + si->psq += PSQT::psq[pc][s]; } if (si->epSquare != SQ_NONE) @@ -343,17 +343,17 @@ void Position::set_state(StateInfo* si) const { for (Bitboard b = pieces(PAWN); b; ) { Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; + si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; } - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt) - si->materialKey ^= Zobrist::psq[c][pt][cnt]; + for (Piece pc : Pieces) + { + if (type_of(pc) != PAWN && type_of(pc) != KING) + si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) - si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; + for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) + si->materialKey ^= Zobrist::psq[pc][cnt]; + } } @@ -420,24 +420,25 @@ Phase Position::game_phase() const { } -/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) that -/// are blocking attacks on the square 's' from 'sliders'. A piece blocks a slider -/// if removing that piece from the board would result in a position where square 's' -/// is attacked. For example, a king-attack blocking piece can be either a pinned or -/// a discovered check piece, according if its color is the opposite or the same of -/// the color of the slider. +/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) +/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a +/// slider if removing that piece from the board would result in a position where +/// square 's' is attacked. For example, a king-attack blocking piece can be either +/// a pinned or a discovered check piece, according if its color is the opposite +/// or the same of the color of the slider. The pinners bitboard get filled with +/// real and potential pinners. -Bitboard Position::slider_blockers(Bitboard sliders, Square s) const { +Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const { - Bitboard b, pinners, result = 0; + Bitboard b, p, result = 0; // Pinners are sliders that attack 's' when a pinned piece is removed - pinners = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) - | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + pinners = p = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) + | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; - while (pinners) + while (p) { - b = between_bb(s, pop_lsb(&pinners)) & pieces(); + b = between_bb(s, pop_lsb(&p)) & pieces(); if (!more_than_one(b)) result |= b; @@ -661,23 +662,24 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); - assert(color_of(piece_on(from)) == us); - assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == (type_of(m) != CASTLING ? them : us)); - assert(captured != KING); + assert(color_of(pc) == us); + assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(type_of(captured) != KING); if (type_of(m) == CASTLING) { - assert(pt == KING); + assert(pc == make_piece(us, KING)); + assert(captured == make_piece(us, ROOK)); Square rfrom, rto; do_castling(us, from, to, rfrom, rto); - captured = NO_PIECE_TYPE; - st->psq += PSQT::psq[us][ROOK][rto] - PSQT::psq[us][ROOK][rfrom]; - k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; + k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + captured = NO_PIECE; } if (captured) @@ -686,13 +688,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. - if (captured == PAWN) + if (type_of(captured) == PAWN) { if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(pc == make_piece(us, PAWN)); assert(to == st->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); @@ -701,28 +703,28 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { board[capsq] = NO_PIECE; // Not done by remove_piece() } - st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; + st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists - remove_piece(them, captured, capsq); + remove_piece(captured, capsq); // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[them][captured][capsq]; - st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; + k ^= Zobrist::psq[captured][capsq]; + st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; prefetch(thisThread->materialTable[st->materialKey]); // Update incremental scores - st->psq -= PSQT::psq[them][captured][capsq]; + st->psq -= PSQT::psq[captured][capsq]; // Reset rule 50 counter st->rule50 = 0; } // Update hash key - k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; + k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset en passant square if (st->epSquare != SQ_NONE) @@ -741,10 +743,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) - move_piece(us, pt, from, to); + move_piece(pc, from, to); // If the moving piece is a pawn do some special extra work - if (pt == PAWN) + if (type_of(pc) == PAWN) { // Set en-passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 @@ -756,29 +758,29 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else if (type_of(m) == PROMOTION) { - PieceType promotion = promotion_type(m); + Piece promotion = make_piece(us, promotion_type(m)); assert(relative_rank(us, to) == RANK_8); - assert(promotion >= KNIGHT && promotion <= QUEEN); + assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); - remove_piece(us, PAWN, to); - put_piece(us, promotion, to); + remove_piece(pc, to); + put_piece(promotion, to); // Update hash keys - k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; - st->pawnKey ^= Zobrist::psq[us][PAWN][to]; - st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] - ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->pawnKey ^= Zobrist::psq[pc][to]; + st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] + ^ Zobrist::psq[pc][pieceCount[pc]]; // Update incremental score - st->psq += PSQT::psq[us][promotion][to] - PSQT::psq[us][PAWN][to]; + st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; // Update material st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable - st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; prefetch(thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter @@ -786,10 +788,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } // Update incremental scores - st->psq += PSQT::psq[us][pt][to] - PSQT::psq[us][pt][from]; + st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; // Set capture piece - st->capturedType = captured; + st->capturedPiece = captured; // Update the key with the final value st->key = k; @@ -818,20 +820,20 @@ void Position::undo_move(Move m) { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(to)); + Piece pc = piece_on(to); assert(empty(from) || type_of(m) == CASTLING); - assert(st->capturedType != KING); + assert(type_of(st->capturedPiece) != KING); if (type_of(m) == PROMOTION) { assert(relative_rank(us, to) == RANK_8); - assert(pt == promotion_type(m)); - assert(pt >= KNIGHT && pt <= QUEEN); + assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); - remove_piece(us, pt, to); - put_piece(us, PAWN, to); - pt = PAWN; + remove_piece(pc, to); + pc = make_piece(us, PAWN); + put_piece(pc, to); } if (type_of(m) == CASTLING) @@ -841,9 +843,9 @@ void Position::undo_move(Move m) { } else { - move_piece(us, pt, to, from); // Put the piece back at the source square + move_piece(pc, to, from); // Put the piece back at the source square - if (st->capturedType) + if (st->capturedPiece) { Square capsq = to; @@ -851,14 +853,14 @@ void Position::undo_move(Move m) { { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(type_of(pc) == PAWN); assert(to == st->previous->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(capsq) == NO_PIECE); - assert(st->capturedType == PAWN); + assert(st->capturedPiece == make_piece(~us, PAWN)); } - put_piece(~us, st->capturedType, capsq); // Restore the captured piece + put_piece(st->capturedPiece, capsq); // Restore the captured piece } } @@ -871,7 +873,7 @@ void Position::undo_move(Move m) { /// Position::do_castling() is a helper used to do/undo a castling move. This -/// is a bit tricky, especially in Chess960. +/// is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -881,11 +883,11 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 - remove_piece(us, KING, Do ? from : to); - remove_piece(us, ROOK, Do ? rfrom : rto); + remove_piece(make_piece(us, KING), Do ? from : to); + remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(us, KING, Do ? to : from); - put_piece(us, ROOK, Do ? rto : rfrom); + put_piece(make_piece(us, KING), Do ? to : from); + put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } @@ -935,17 +937,16 @@ void Position::undo_null_move() { Key Position::key_after(Move m) const { - Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = piece_on(to); Key k = st->key ^ Zobrist::side; if (captured) - k ^= Zobrist::psq[~us][captured][to]; + k ^= Zobrist::psq[captured][to]; - return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from]; + return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; } @@ -1001,8 +1002,17 @@ Value Position::see(Move m) const { // If the opponent has no attackers we are finished stm = ~stm; stmAttackers = attackers & pieces(stm); + occupied ^= to; // For the case when captured piece is a pinner + + // Don't allow pinned pieces to attack as long all pinners (this includes also + // potential ones) are on their original square. When a pinner moves to the + // exchange-square or get captured on it, we fall back to standard SEE behaviour. + if ( (stmAttackers & pinned_pieces(stm)) + && (st->pinnersForKing[stm] & occupied) == st->pinnersForKing[stm]) + stmAttackers &= ~pinned_pieces(stm); + if (!stmAttackers) - return swapList[0]; + return swapList[0]; // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing @@ -1022,6 +1032,10 @@ Value Position::see(Move m) const { captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); stm = ~stm; stmAttackers = attackers & pieces(stm); + if ( (stmAttackers & pinned_pieces(stm)) + && (st->pinnersForKing[stm] & occupied) == st->pinnersForKing[stm]) + stmAttackers &= ~pinned_pieces(stm); + ++slIndex; } while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture @@ -1140,17 +1154,15 @@ bool Position::pos_is_ok(int* failedStep) const { } if (step == Lists) - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - if (pieceCount[c][pt] != popcount(pieces(c, pt))) - return false; + for (Piece pc : Pieces) + { + if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))) + return false; - for (int i = 0; i < pieceCount[c][pt]; ++i) - if ( board[pieceList[c][pt][i]] != make_piece(c, pt) - || index[pieceList[c][pt][i]] != i) - return false; - } + for (int i = 0; i < pieceCount[pc]; ++i) + if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) + return false; + } if (step == Castling) for (Color c = WHITE; c <= BLACK; ++c) diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 7fce1db..9830619 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -22,25 +22,13 @@ #define POSITION_H_INCLUDED #include -#include // For offsetof() #include -#include // For std::unique_ptr +#include // For std::unique_ptr #include -#include #include "bitboard.h" #include "types.h" -class Position; -class Thread; - -namespace PSQT { - - extern Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - - void init(); -} - /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the @@ -58,12 +46,13 @@ struct StateInfo { Score psq; Square epSquare; - // Not copied when making a move + // Not copied when making a move (will be recomputed anyhow) Key key; Bitboard checkersBB; - PieceType capturedType; + Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; + Bitboard pinnersForKing[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; }; @@ -75,9 +64,9 @@ typedef std::unique_ptr> StateListPtr; /// pieces, side to move, hash keys, castling info, etc. Important methods are /// do_move() and undo_move(), used by the search to update node info when /// traversing the search tree. +class Thread; class Position { - public: static void init(); @@ -121,7 +110,7 @@ public: Bitboard attacks_from(Piece pc, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; - Bitboard slider_blockers(Bitboard sliders, Square s) const; + Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves bool legal(Move m) const; @@ -131,7 +120,7 @@ public: bool gives_check(Move m) const; bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; - PieceType captured_piece_type() const; + Piece captured_piece() const; // Piece specific bool pawn_passed(Color c, Square s) const; @@ -143,14 +132,13 @@ public: void do_null_move(StateInfo& st); void undo_null_move(); - // Static exchange evaluation + // Static Exchange Evaluation Value see(Move m) const; Value see_sign(Move m) const; // Accessing hash keys Key key() const; Key key_after(Move m) const; - Key exclusion_key() const; Key material_key() const; Key pawn_key() const; @@ -178,9 +166,9 @@ private: void set_check_info(StateInfo* si) const; // Other helpers - void put_piece(Color c, PieceType pt, Square s); - void remove_piece(Color c, PieceType pt, Square s); - void move_piece(Color c, PieceType pt, Square from, Square to); + void put_piece(Piece pc, Square s); + void remove_piece(Piece pc, Square s); + void move_piece(Piece pc, Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); @@ -188,8 +176,8 @@ private: Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; - int pieceCount[COLOR_NB][PIECE_TYPE_NB]; - Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; + int pieceCount[PIECE_NB]; + Square pieceList[PIECE_NB][16]; int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; @@ -245,16 +233,16 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { } template inline int Position::count(Color c) const { - return pieceCount[c][Pt]; + return pieceCount[make_piece(c, Pt)]; } template inline const Square* Position::squares(Color c) const { - return pieceList[c][Pt]; + return pieceList[make_piece(c, Pt)]; } template inline Square Position::square(Color c) const { - assert(pieceCount[c][Pt] == 1); - return pieceList[c][Pt][0]; + assert(pieceCount[make_piece(c, Pt)] == 1); + return pieceList[make_piece(c, Pt)][0]; } inline Square Position::ep_square() const { @@ -359,8 +347,8 @@ inline void Position::set_nodes_searched(uint64_t n) { } inline bool Position::opposite_bishops() const { - return pieceCount[WHITE][BISHOP] == 1 - && pieceCount[BLACK][BISHOP] == 1 + return pieceCount[W_BISHOP] == 1 + && pieceCount[B_BISHOP] == 1 && opposite_colors(square(WHITE), square(BLACK)); } @@ -369,66 +357,64 @@ inline bool Position::is_chess960() const { } inline bool Position::capture_or_promotion(Move m) const { - assert(is_ok(m)); return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); } inline bool Position::capture(Move m) const { - - // Castling is encoded as "king captures the rook" assert(is_ok(m)); + // Castling is encoded as "king captures rook" return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; } -inline PieceType Position::captured_piece_type() const { - return st->capturedType; +inline Piece Position::captured_piece() const { + return st->capturedPiece; } inline Thread* Position::this_thread() const { return thisThread; } -inline void Position::put_piece(Color c, PieceType pt, Square s) { +inline void Position::put_piece(Piece pc, Square s) { - board[s] = make_piece(c, pt); + board[s] = pc; byTypeBB[ALL_PIECES] |= s; - byTypeBB[pt] |= s; - byColorBB[c] |= s; - index[s] = pieceCount[c][pt]++; - pieceList[c][pt][index[s]] = s; - pieceCount[c][ALL_PIECES]++; + byTypeBB[type_of(pc)] |= s; + byColorBB[color_of(pc)] |= s; + index[s] = pieceCount[pc]++; + pieceList[pc][index[s]] = s; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; } -inline void Position::remove_piece(Color c, PieceType pt, Square s) { +inline void Position::remove_piece(Piece pc, Square s) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] - // are not guaranteed to be invariant to a do_move() + undo_move() sequence. + // are not invariant to a do_move() + undo_move() sequence. byTypeBB[ALL_PIECES] ^= s; - byTypeBB[pt] ^= s; - byColorBB[c] ^= s; + byTypeBB[type_of(pc)] ^= s; + byColorBB[color_of(pc)] ^= s; /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ - Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; + Square lastSquare = pieceList[pc][--pieceCount[pc]]; index[lastSquare] = index[s]; - pieceList[c][pt][index[lastSquare]] = lastSquare; - pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; - pieceCount[c][ALL_PIECES]--; + pieceList[pc][index[lastSquare]] = lastSquare; + pieceList[pc][pieceCount[pc]] = SQ_NONE; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; } -inline void Position::move_piece(Color c, PieceType pt, Square from, Square to) { +inline void Position::move_piece(Piece pc, Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[c] ^= from_to_bb; + byTypeBB[type_of(pc)] ^= from_to_bb; + byColorBB[color_of(pc)] ^= from_to_bb; board[from] = NO_PIECE; - board[to] = make_piece(c, pt); + board[to] = pc; index[to] = index[from]; - pieceList[c][pt][index[to]] = to; + pieceList[pc][index[to]] = to; } #endif // #ifndef POSITION_H_INCLUDED diff --git a/DroidFish/jni/stockfish/psqt.cpp b/DroidFish/jni/stockfish/psqt.cpp index 60180ad..26cd1a0 100644 --- a/DroidFish/jni/stockfish/psqt.cpp +++ b/DroidFish/jni/stockfish/psqt.cpp @@ -99,25 +99,25 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { #undef S -Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +Score psq[PIECE_NB][SQUARE_NB]; // init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { - for (PieceType pt = PAWN; pt <= KING; ++pt) + for (Piece pc = W_PAWN; pc <= W_KING; ++pc) { - PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; - PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; + PieceValue[MG][~pc] = PieceValue[MG][pc]; + PieceValue[EG][~pc] = PieceValue[EG][pc]; - Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); + Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = std::min(file_of(s), FILE_H - file_of(s)); - psq[WHITE][pt][ s] = v + Bonus[pt][rank_of(s)][f]; - psq[BLACK][pt][~s] = -psq[WHITE][pt][s]; + psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f]; + psq[~pc][~s] = -psq[pc][s]; } } } diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index cf10674..86b7a11 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -29,6 +29,7 @@ #include "misc.h" #include "movegen.h" #include "movepick.h" +#include "position.h" #include "search.h" #include "timeman.h" #include "thread.h" @@ -157,7 +158,6 @@ namespace { EasyMoveManager EasyMove; Value DrawValue[COLOR_NB]; - CounterMoveHistoryStats CounterMoveHistory; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -208,13 +208,13 @@ void Search::init() { void Search::clear() { TT.clear(); - CounterMoveHistory.clear(); for (Thread* th : Threads) { th->history.clear(); th->counterMoves.clear(); th->fromTo.clear(); + th->counterMoveHistory.clear(); } Threads.main()->previousScore = VALUE_INFINITE; @@ -506,7 +506,7 @@ void Thread::search() { if ( rootMoves.size() == 1 || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628 - || (mainThread->easyMovePlayed = doEasyMove)) + || (mainThread->easyMovePlayed = doEasyMove, doEasyMove)) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -560,7 +560,7 @@ namespace { TTEntry* tte; Key posKey; Move ttMove, move, excludedMove, bestMove; - Depth extension, newDepth, predictedDepth; + Depth extension, newDepth; Value bestValue, value, ttValue, eval, nullValue; bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning; @@ -622,7 +622,7 @@ namespace { // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = excludedMove ? pos.exclusion_key() : pos.key(); + posKey = pos.key() ^ Key(excludedMove); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0] @@ -650,7 +650,7 @@ namespace { } // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) + if ((ss-1)->moveCount == 1 && !pos.captured_piece()) { Value penalty = Value(d * d + 4 * d + 1); Square prevSq = to_sq((ss-1)->currentMove); @@ -725,8 +725,8 @@ namespace { // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY - && eval + razor_margin[depth / ONE_PLY] <= alpha - && ttMove == MOVE_NONE) + && ttMove == MOVE_NONE + && eval + razor_margin[depth / ONE_PLY] <= alpha) { if ( depth <= ONE_PLY && eval + razor_margin[3 * ONE_PLY] <= alpha) @@ -788,9 +788,8 @@ namespace { } // Step 9. ProbCut (skipped when in check) - // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) - // and a reduced search returns a value much above beta, we can (almost) - // safely prune the previous move. + // If we have a good enough capture and a reduced search returns a value + // much above beta, we can (almost) safely prune the previous move. if ( !PvNode && depth >= 5 * ONE_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY) @@ -802,13 +801,13 @@ namespace { assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NULL); - MovePicker mp(pos, ttMove, PieceValue[MG][pos.captured_piece_type()]); + MovePicker mp(pos, ttMove, rbeta - ss->staticEval); while ((move = mp.next_move()) != MOVE_NONE) if (pos.legal(move)) { ss->currentMove = move; - ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(move)]; + ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)]; pos.do_move(move, st, pos.gives_check(move)); value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); pos.undo_move(move); @@ -921,42 +920,41 @@ moves_loop: // When in check search starts from here newDepth = depth - ONE_PLY + extension; // Step 13. Pruning at shallow depth - if ( !rootNode - && !captureOrPromotion + if ( !rootNode && !inCheck - && !givesCheck - && !pos.advanced_pawn_push(move) && bestValue > VALUE_MATED_IN_MAX_PLY) { - // Move count based pruning - if (moveCountPruning) - continue; - - predictedDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); - - // Countermoves based pruning - if ( predictedDepth < 3 * ONE_PLY - && move != ss->killers[0] - && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) - && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO) - && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh))) - continue; - - // Futility pruning: parent node - if ( predictedDepth < 7 * ONE_PLY - && ss->staticEval + 256 + 200 * predictedDepth / ONE_PLY <= alpha) - continue; - - // Prune moves with negative SEE at low depths and below a decreasing - // threshold at higher depths. - if (predictedDepth < 8 * ONE_PLY) + if ( !captureOrPromotion + && !givesCheck + && !pos.advanced_pawn_push(move)) { - Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO - : -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY) / ONE_PLY; + // Move count based pruning + if (moveCountPruning) + continue; - if (pos.see_sign(move) < see_v) + // Reduced depth of the next LMR search + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; + + // Countermoves based pruning + if ( lmrDepth < 3 + && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) + && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO) + && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh))) + continue; + + // Futility pruning: parent node + if ( lmrDepth < 7 + && ss->staticEval + 256 + 200 * lmrDepth <= alpha) + continue; + + // Prune moves with negative SEE + if ( lmrDepth < 8 + && pos.see_sign(move) < Value(-35 * lmrDepth * lmrDepth)) continue; } + else if ( depth < 7 * ONE_PLY + && pos.see_sign(move) < Value(-35 * depth / ONE_PLY * depth / ONE_PLY)) + continue; } // Speculative prefetch as early as possible @@ -970,7 +968,7 @@ moves_loop: // When in check search starts from here } ss->currentMove = move; - ss->counterMoves = &CounterMoveHistory[moved_piece][to_sq(move)]; + ss->counterMoves = &thisThread->counterMoveHistory[moved_piece][to_sq(move)]; // Step 14. Make the move pos.do_move(move, st, givesCheck); @@ -1140,7 +1138,7 @@ moves_loop: // When in check search starts from here } // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) + if ((ss-1)->moveCount == 1 && !pos.captured_piece()) { Value penalty = Value(d * d + 4 * d + 1); Square prevSq = to_sq((ss-1)->currentMove); @@ -1149,7 +1147,7 @@ moves_loop: // When in check search starts from here } // Bonus for prior countermove that caused the fail low else if ( depth >= 3 * ONE_PLY - && !pos.captured_piece_type() + && !pos.captured_piece() && is_ok((ss-1)->currentMove)) { int d = depth / ONE_PLY; @@ -1643,7 +1641,7 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) RootInTB = root_probe_wdl(pos, rootMoves, TB::Score); // Only probe during search if winning - if (TB::RootInTB && TB::Score <= VALUE_DRAW) + if (RootInTB && TB::Score <= VALUE_DRAW) Cardinality = 0; } diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index 599d058..121ab8d 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -25,11 +25,10 @@ #include #include "misc.h" -#include "position.h" +#include "movepick.h" #include "types.h" -template struct Stats; -typedef Stats CounterMoveStats; +class Position; namespace Search { @@ -49,6 +48,7 @@ struct Stack { CounterMoveStats* counterMoves; }; + /// RootMove struct is used for moves at the root of the tree. For each root move /// we store a score and a PV (really a refutation in the case of moves which /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. @@ -68,6 +68,7 @@ struct RootMove { typedef std::vector RootMoves; + /// LimitsType struct stores information sent by GUI about available time to /// search the current move, maximum depth/time, if we are in analysis mode or /// if we have to ponder while it's our opponent's turn to move. @@ -89,8 +90,9 @@ struct LimitsType { TimePoint startTime; }; -/// The SignalsType struct stores atomic flags updated during the search -/// typically in an async fashion e.g. to stop the search by the GUI. + +/// SignalsType struct stores atomic flags updated during the search, typically +/// in an async fashion e.g. to stop the search by the GUI. struct SignalsType { std::atomic_bool stop, stopOnPonderhit; diff --git a/DroidFish/jni/stockfish/syzygy/tbprobe.cpp b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp index 6f6627a..0281ccc 100644 --- a/DroidFish/jni/stockfish/syzygy/tbprobe.cpp +++ b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp @@ -22,7 +22,7 @@ #include "tbcore.cpp" namespace Zobrist { - extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + extern Key psq[PIECE_NB][SQUARE_NB]; } int Tablebases::MaxCardinality = 0; @@ -60,11 +60,11 @@ static uint64 calc_key(Position& pos, int mirror) color = !mirror ? WHITE : BLACK; for (pt = PAWN; pt <= KING; ++pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - key ^= Zobrist::psq[WHITE][pt][i - 1]; + key ^= Zobrist::psq[make_piece(WHITE, pt)][i - 1]; color = ~color; for (pt = PAWN; pt <= KING; ++pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) - key ^= Zobrist::psq[BLACK][pt][i - 1]; + key ^= Zobrist::psq[make_piece(BLACK, pt)][i - 1]; return key; } @@ -83,11 +83,11 @@ static uint64 calc_key_from_pcs(int *pcs, int mirror) color = !mirror ? 0 : 8; for (pt = PAWN; pt <= KING; ++pt) for (i = 0; i < pcs[color + pt]; i++) - key ^= Zobrist::psq[WHITE][pt][i]; + key ^= Zobrist::psq[make_piece(WHITE, pt)][i]; color ^= 8; for (pt = PAWN; pt <= KING; ++pt) for (i = 0; i < pcs[color + pt]; i++) - key ^= Zobrist::psq[BLACK][pt][i]; + key ^= Zobrist::psq[make_piece(BLACK, pt)][i]; return key; } @@ -123,7 +123,7 @@ static int probe_wdl_table(Position& pos, int *success) key = pos.material_key(); // Test for KvK. - if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0])) + if (key == (Zobrist::psq[W_KING][0] ^ Zobrist::psq[B_KING][0])) return 0; ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 8181163..195c3b3 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -66,11 +66,12 @@ public: Position rootPos; Search::RootMoves rootMoves; Depth rootDepth; + Depth completedDepth; + std::atomic_bool resetCalls; HistoryStats history; MoveStats counterMoves; FromToStats fromTo; - Depth completedDepth; - std::atomic_bool resetCalls; + CounterMoveHistoryStats counterMoveHistory; }; diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index 1440f73..e5a7035 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -183,13 +183,13 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, - PawnValueMg = 198, PawnValueEg = 258, - KnightValueMg = 817, KnightValueEg = 896, - BishopValueMg = 836, BishopValueEg = 907, - RookValueMg = 1270, RookValueEg = 1356, - QueenValueMg = 2521, QueenValueEg = 2658, + PawnValueMg = 188, PawnValueEg = 248, + KnightValueMg = 753, KnightValueEg = 832, + BishopValueMg = 826, BishopValueEg = 897, + RookValueMg = 1285, RookValueEg = 1371, + QueenValueMg = 2513, QueenValueEg = 2650, - MidgameLimit = 15581, EndgameLimit = 3998 + MidgameLimit = 15258, EndgameLimit = 3915 }; enum PieceType { @@ -205,6 +205,10 @@ enum Piece { PIECE_NB = 16 }; +const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; +extern Value PieceValue[PHASE_NB][PIECE_NB]; + enum Depth { ONE_PLY = 1, @@ -261,24 +265,24 @@ enum Rank { enum Score : int { SCORE_ZERO }; inline Score make_score(int mg, int eg) { - return Score((mg << 16) + eg); + return Score((eg << 16) + mg); } /// Extracting the signed lower and upper 16 bits is not so trivial because /// according to the standard a simple cast to short is implementation defined /// and so is a right shift of a signed integer. -inline Value mg_value(Score s) { - - union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) }; - return Value(mg.s); -} - inline Value eg_value(Score s) { - union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) }; + union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) }; return Value(eg.s); } +inline Value mg_value(Score s) { + + union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) }; + return Value(mg.s); +} + #define ENABLE_BASE_OPERATORS_ON(T) \ inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ @@ -326,16 +330,18 @@ inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } -extern Value PieceValue[PHASE_NB][PIECE_NB]; - inline Color operator~(Color c) { - return Color(c ^ BLACK); + return Color(c ^ BLACK); // Toggle color } inline Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } +inline Piece operator~(Piece pc) { + return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT +} + inline CastlingRight operator|(Color c, CastlingSide s) { return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); } @@ -349,11 +355,11 @@ inline Value mated_in(int ply) { } inline Square make_square(File f, Rank r) { - return Square((r << 3) | f); + return Square((r << 3) + f); } inline Piece make_piece(Color c, PieceType pt) { - return Piece((c << 3) | pt); + return Piece((c << 3) + pt); } inline PieceType type_of(Piece pc) { @@ -415,12 +421,12 @@ inline PieceType promotion_type(Move m) { } inline Move make_move(Square from, Square to) { - return Move(to | (from << 6)); + return Move((from << 6) + to); } template inline Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)); + return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } inline bool is_ok(Move m) {