From: Fabian Fichter Date: Fri, 8 Mar 2019 21:43:44 +0000 (+0100) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=f00225ae2a1928b92b27c50ab216c22f8d2cb393;p=fairystockfish.git Merge official-stockfish/master bench: 3428840 --- f00225ae2a1928b92b27c50ab216c22f8d2cb393 diff --cc .travis.yml index 3f9fffb,ea5bda1..6275d1d --- a/.travis.yml +++ b/.travis.yml @@@ -34,9 -34,13 +34,9 @@@ matrix - os: osx compiler: clang env: - - COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) + - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2) - COMP=clang -branches: - only: - - master - before_script: - cd src diff --cc Readme.md index 3e945b5,a68ef56..721936b --- a/Readme.md +++ b/Readme.md @@@ -1,62 -1,17 +1,55 @@@ -## Overview - -[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) -[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) +## Fairy-Stockfish + +[![Build Status](https://travis-ci.org/ianfab/Fairy-Stockfish.svg?branch=master)](https://travis-ci.org/ianfab/Fairy-Stockfish) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/ianfab/Fairy-Stockfish?svg=true)](https://ci.appveyor.com/project/ianfab/Fairy-Stockfish) + +Fairy-Stockfish is a Stockfish fork designed for the support of (fairy) chess variants and to make the addition/configuration of new variants as simple and flexible as possible. The goal of the project is to create an engine supporting a large variety of chess-like games, equipped with the powerful search of Stockfish. It is complementary to Stockfish forks more specialized for certain chess variants, such as [multi-variant Stockfish](https://github.com/ddugovic/Stockfish), [Seirawan-Stockfish](https://github.com/ianfab/Seirawan-Stockfish), [Makruk-Stockfish](https://github.com/ianfab/Makruk-Stockfish), etc., supporting more variants with the tradeoff of slightly lower performance. + +Besides chess, the currently supported games are: + +**Regional and historical games** +- [Shatranj](https://en.wikipedia.org/wiki/Shatranj), [Courier](https://en.wikipedia.org/wiki/Courier_chess) +- [Makruk](https://en.wikipedia.org/wiki/Makruk), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Ai-Wok +- [Sittuyin](https://en.wikipedia.org/wiki/Sittuyin) +- [Shatar](https://en.wikipedia.org/wiki/Shatar), [Jeson Mor](https://en.wikipedia.org/wiki/Jeson_Mor) +- [Shogi](https://en.wikipedia.org/wiki/Shogi) + +**Chess variants** +- [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess) +- [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess) +- [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html) +- [Amazon](https://en.wikipedia.org/wiki/Amazon_(chess)), [Chigorin](https://en.wikipedia.org/wiki/Chigorin_Chess), [Almost chess](https://en.wikipedia.org/wiki/Almost_Chess), [Hoppel-Poppel](http://www.chessvariants.com/diffmove.dir/hoppel-poppel.html) +- [Antichess](https://lichess.org/variant/antichess), [Giveaway](http://www.chessvariants.com/diffobjective.dir/giveaway.old.html), [Losers](https://www.chessclub.com/help/Wild17), [Codrus](http://www.binnewirtz.com/Schlagschach1.htm) +- [Extinction](https://en.wikipedia.org/wiki/Extinction_chess), [Kinglet](https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_Chess) +- [King of the Hill](https://en.wikipedia.org/wiki/King_of_the_Hill_(chess)), [Racing Kings](https://en.wikipedia.org/wiki/V._R._Parton#Racing_Kings) +- [Three-check](https://en.wikipedia.org/wiki/Three-check_chess), Five-check +- [Los Alamos](https://en.wikipedia.org/wiki/Los_Alamos_chess) +- [Horde](https://en.wikipedia.org/wiki/Dunsany%27s_Chess#Horde_Chess) + +**Shogi variants** +- [Minishogi](https://en.wikipedia.org/wiki/Minishogi), [EuroShogi](https://en.wikipedia.org/wiki/EuroShogi), [Judkins shogi](https://en.wikipedia.org/wiki/Judkins_shogi) +- [Kyoto shogi](https://en.wikipedia.org/wiki/Kyoto_shogi), [Microshogi](https://en.wikipedia.org/wiki/Micro_shogi) +- [Dobutsu shogi](https://en.wikipedia.org/wiki/Dōbutsu_shōgi), [Goro goro shogi](https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi#Variation) + +**Related games** +- [Breakthrough](https://en.wikipedia.org/wiki/Breakthrough_(board_game)) +- [Clobber](https://en.wikipedia.org/wiki/Clobber) +- [Connect4](https://en.wikipedia.org/wiki/Connect_Four), [Tic-Tac-Toe](https://en.wikipedia.org/wiki/Tic-tac-toe) + +See the [Fairy-Stockfish Wiki](https://github.com/ianfab/Fairy-Stockfish/wiki) for more info. + +## Stockfish +### Overview - Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is - not a complete chess program and requires some UCI-compatible GUI - (e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess - Partner or Fritz) in order to be used comfortably. Read the - documentation for your GUI of choice for information about how to use + [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine + derived from Glaurung 2.1. It is not a complete chess program and requires a + UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, + Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. + Read the documentation for your GUI of choice for information about how to use Stockfish with it. - This version of Stockfish supports up to 512 cores. The engine defaults - to one search thread, so it is therefore recommended to inspect the value of - the *Threads* UCI parameter, and to make sure it equals the number of CPU - cores on your computer. - This version of Stockfish has support for Syzygybases. - - - ### Files + ## Files This distribution of Stockfish consists of the following files: diff --cc src/bitboard.h index d9642b8,f8440a2..88be5a1 --- a/src/bitboard.h +++ b/src/bitboard.h @@@ -178,15 -135,7 +178,14 @@@ constexpr bool more_than_one(Bitboard b return b & (b - 1); } +/// board_size_bb() returns a bitboard representing all the squares +/// on a board with given size. + +inline Bitboard board_size_bb(File f, Rank r) { + return BoardSizeBB[f][r]; +} + inline bool opposite_colors(Square s1, Square s2) { - return bool(DarkSquares & s1) != bool(DarkSquares & s2); } diff --cc src/endgame.cpp index ddf7e48,66ee54d..d378cf8 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@@ -45,32 -45,19 +45,32 @@@ namespace // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. constexpr int PushToCorners[SQUARE_NB] = { - 200, 190, 180, 170, 160, 150, 140, 130, - 190, 180, 170, 160, 150, 140, 130, 140, - 180, 170, 155, 140, 140, 125, 140, 150, - 170, 160, 140, 120, 110, 140, 150, 160, - 160, 150, 140, 110, 120, 140, 160, 170, - 150, 140, 125, 140, 140, 155, 170, 180, - 140, 130, 140, 150, 160, 170, 180, 190, - 130, 140, 150, 160, 170, 180, 190, 200 + 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160, + 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480, + 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800, + 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120, + 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440, + 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760, + 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080, + 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 }; + // Table used to drive the king towards the edge of the board + // in KSF vs K. + constexpr int PushToOpposingSideEdges[SQUARE_NB] = { + 30, 10, 5, 0, 0, 5, 10, 30, + 40, 20, 5, 0, 0, 5, 20, 40, + 50, 30, 10, 0, 0, 10, 30, 50, + 60, 40, 20, 10, 10, 20, 40, 60, + 70, 50, 30, 20, 20, 30, 50, 70, + 80, 60, 40, 30, 30, 40, 60, 80, + 90, 70, 60, 50, 50, 60, 70, 90, + 100, 90, 80, 70, 70, 80, 90, 100 + }; + // Tables used to drive a piece towards or away from another piece - constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; - constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; + constexpr int PushClose[FILE_NB] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + constexpr int PushAway [FILE_NB] = { 0, 5, 20, 40, 60, 80, 90, 100 }; // Pawn Rank based scaling factors used in KRPPKRP endgame constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 }; diff --cc src/evaluate.cpp index 2af32c5,53a5400..9243055 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@@ -157,29 -151,26 +157,29 @@@ namespace S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7) }; + // KingProximity contains a penalty according to distance from king + constexpr Score KingProximity = S(2, 2); + // Assorted bonuses and penalties - constexpr Score BishopPawns = S( 3, 8); - constexpr Score CloseEnemies = S( 7, 0); + constexpr Score BishopPawns = S( 3, 7); + constexpr Score CloseEnemies = S( 8, 0); constexpr Score CorneredBishop = S( 50, 50); - constexpr Score Hanging = S( 62, 34); - constexpr Score KingProtector = S( 6, 7); - constexpr Score KnightOnQueen = S( 20, 12); - constexpr Score LongDiagonalBishop = S( 44, 0); - constexpr Score MinorBehindPawn = S( 16, 0); - constexpr Score PawnlessFlank = S( 18, 94); - constexpr Score RestrictedPiece = S( 7, 6); - constexpr Score RookOnPawn = S( 10, 28); - constexpr Score SliderOnQueen = S( 49, 21); - constexpr Score ThreatByKing = S( 21, 84); - constexpr Score ThreatByPawnPush = S( 48, 42); - constexpr Score ThreatByRank = S( 14, 3); - constexpr Score ThreatBySafePawn = S(169, 99); - constexpr Score TrappedRook = S( 98, 5); - constexpr Score WeakQueen = S( 51, 10); - constexpr Score WeakUnopposedPawn = S( 14, 20); + constexpr Score Hanging = S( 69, 36); + constexpr Score KingProtector = S( 7, 8); + constexpr Score KnightOnQueen = S( 16, 12); + constexpr Score LongDiagonalBishop = S( 45, 0); + constexpr Score MinorBehindPawn = S( 18, 3); + constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score RookOnPawn = S( 10, 32); + constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score ThreatByKing = S( 24, 89); + constexpr Score ThreatByPawnPush = S( 48, 39); + constexpr Score ThreatByRank = S( 13, 0); + constexpr Score ThreatBySafePawn = S(173, 94); + constexpr Score TrappedRook = S( 96, 4); + constexpr Score WeakQueen = S( 49, 15); + constexpr Score WeakUnopposedPawn = S( 12, 23); #undef S @@@ -261,40 -250,28 +261,35 @@@ // Squares occupied by those pawns, by our king or queen, or controlled by enemy pawns // are excluded from the mobility area. - mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); + if (pos.must_capture()) + mobilityArea[Us] = AllSquares; + else + mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them) | shift(pos.pieces(Them, SHOGI_PAWN))); // Initialise attackedBy bitboards for kings and pawns - attackedBy[Us][KING] = pos.attacks_from(pos.square(Us)); - attackedBy[Us][PAWN] = pe->pawn_attacks(Us); - attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; - attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN]; + attackedBy[Us][KING] = pos.count(Us) ? pos.attacks_from(Us, pos.square(Us)) : 0; + attackedBy[Us][PAWN] = pe->pawn_attacks(Us); + attackedBy[Us][SHOGI_PAWN] = shift(pos.pieces(Us, SHOGI_PAWN)); + attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN] | attackedBy[Us][SHOGI_PAWN]; + attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN]) + | (attackedBy[Us][KING] & attackedBy[Us][SHOGI_PAWN]) + | (attackedBy[Us][PAWN] & attackedBy[Us][SHOGI_PAWN]); - kingRing[Us] = kingAttackersCount[Them] = 0; + // Init our king safety tables + kingRing[Us] = attackedBy[Us][KING]; - if (relative_rank(Us, pos.square(Us)) == RANK_1) ++ if (pos.count(Us) && relative_rank(Us, pos.square(Us), pos.max_rank()) == RANK_1) + kingRing[Us] |= shift(kingRing[Us]); - // Init our king safety tables only if we are going to use them - if ((pos.count(Us) && pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg) || pos.captures_to_hand()) - { - kingRing[Us] = attackedBy[Us][KING]; - if (relative_rank(Us, pos.square(Us), pos.max_rank()) == RANK_1) - kingRing[Us] |= shift(kingRing[Us]); - - if (file_of(pos.square(Us)) == pos.max_file()) - kingRing[Us] |= shift(kingRing[Us]); - - else if (file_of(pos.square(Us)) == FILE_A) - kingRing[Us] |= shift(kingRing[Us]); - if (file_of(pos.square(Us)) == FILE_H) ++ if (pos.count(Us) && file_of(pos.square(Us)) == pos.max_file()) + kingRing[Us] |= shift(kingRing[Us]); - kingRing[Us] &= pos.board_bb(); - else if (file_of(pos.square(Us)) == FILE_A) ++ else if (pos.count(Us) && file_of(pos.square(Us)) == FILE_A) + kingRing[Us] |= shift(kingRing[Us]); - kingAttackersCount[Them] = popcount(kingRing[Us] & (pe->pawn_attacks(Them) | shift(pos.pieces(Them, SHOGI_PAWN)))); - kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; - } + kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); + kingRing[Us] &= ~double_pawn_attacks_bb(pos.pieces(Us, PAWN)); + kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; ++ kingRing[Us] &= pos.board_bb(); } @@@ -332,10 -305,10 +327,10 @@@ attackedBy[Us][Pt] |= b; attackedBy[Us][ALL_PIECES] |= b; - if (b & kingRing[Them] & ~double_pawn_attacks_bb(pos.pieces(Them, PAWN))) + if (b & kingRing[Them]) { kingAttackersCount[Us]++; - kingAttackersWeight[Us] += KingAttackWeights[Pt]; + kingAttackersWeight[Us] += KingAttackWeights[std::min(int(Pt), QUEEN + 1)]; kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); } @@@ -492,87 -419,65 +487,84 @@@ int tropism = popcount(b1) + popcount(b2); // Main king safety evaluation - if ((kingAttackersCount[Them] > 1 - pos.count(Them)) || pos.captures_to_hand()) + int kingDanger = 0; + unsafeChecks = 0; + + // Attacked squares defended at most once by our queen or king + weak = attackedBy[Them][ALL_PIECES] + & ~attackedBy2[Us] + & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]); + + // Analyse the safe enemy's checks which are possible on next move + safe = ~pos.pieces(Them); + safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]); + - b1 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); - b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); - - // Enemy queen safe checks - if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN]) - kingDanger += QueenSafeCheck; - - b1 &= attackedBy[Them][ROOK]; - b2 &= attackedBy[Them][BISHOP]; - - // Enemy rooks checks - if (b1 & safe) - kingDanger += RookSafeCheck; - else - unsafeChecks |= b1; - - // Enemy bishops checks - if (b2 & safe) - kingDanger += BishopSafeCheck; - else - unsafeChecks |= b2; ++ std::function get_attacks = [this](Color c, PieceType pt) { ++ return attackedBy[c][pt] | (pos.captures_to_hand() && pos.count_in_hand(c, pt) ? ~pos.pieces() : 0); ++ }; ++ for (PieceType pt : pos.piece_types()) + { - int kingDanger = 0; - unsafeChecks = 0; - - // Attacked squares defended at most once by our queen or king - weak = attackedBy[Them][ALL_PIECES] - & ~attackedBy2[Us] - & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]); - - // Analyse the safe enemy's checks which are possible on next move - safe = ~pos.pieces(Them); - safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]); - - std::function get_attacks = [this](Color c, PieceType pt) { - return attackedBy[c][pt] | (pos.captures_to_hand() && pos.count_in_hand(c, pt) ? ~pos.pieces() : 0); - }; - for (PieceType pt : pos.piece_types()) ++ switch (pt) + { - switch (pt) ++ case QUEEN: ++ b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & safe & ~attackedBy[Us][QUEEN] & pos.board_bb(); ++ if (b) ++ kingDanger += QueenSafeCheck; ++ break; ++ case ROOK: ++ case BISHOP: ++ case KNIGHT: ++ b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb(); ++ if (b & safe) ++ kingDanger += pt == ROOK ? RookSafeCheck ++ : pt == BISHOP ? BishopSafeCheck ++ : KnightSafeCheck; ++ else ++ unsafeChecks |= b; ++ break; ++ case PAWN: ++ if (pos.captures_to_hand() && pos.count_in_hand(Them, pt)) + { - case QUEEN: - b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & safe & ~attackedBy[Us][QUEEN] & pos.board_bb(); - if (b) - kingDanger += QueenSafeCheck; - break; - case ROOK: - case BISHOP: - case KNIGHT: - b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb(); - if (b & safe) - kingDanger += pt == ROOK ? RookSafeCheck - : pt == BISHOP ? BishopSafeCheck - : KnightSafeCheck; - else - unsafeChecks |= b; - break; - case PAWN: - if (pos.captures_to_hand() && pos.count_in_hand(Them, pt)) - { - b = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb(); - if (b & safe) - kingDanger += OtherSafeCheck; - else - unsafeChecks |= b; - } - break; - case SHOGI_PAWN: - case KING: - break; - default: - b = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb(); ++ b = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb(); + if (b & safe) + kingDanger += OtherSafeCheck; + else + unsafeChecks |= b; + } ++ break; ++ case SHOGI_PAWN: ++ case KING: ++ break; ++ default: ++ b = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb(); ++ if (b & safe) ++ kingDanger += OtherSafeCheck; ++ else ++ unsafeChecks |= b; + } - - if (pos.max_check_count()) - kingDanger *= 2; - - // Unsafe or occupied checking squares will also be considered, as long as - // the square is in the attacker's mobility area. - unsafeChecks &= mobilityArea[Them]; - - kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] - + 69 * kingAttacksCount[Them] * (1 + 2 * !!pos.max_check_count()) - + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + !!pos.max_check_count()) - + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) - + tropism * tropism / 4 - - 873 * !(pos.count(Them) || pos.captures_to_hand()) / (1 + !!pos.max_check_count()) - - 6 * mg_value(score) / 8 - + mg_value(mobility[Them] - mobility[Us]) - - 30; - - // Transform the kingDanger units into a Score, and subtract it from the evaluation - if (kingDanger > 0) - score -= make_score(std::min(kingDanger * kingDanger / 4096, 3000), kingDanger / 16); + } - // Enemy knights checks - b = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; - if (b & safe) - kingDanger += KnightSafeCheck; - else - unsafeChecks |= b; ++ if (pos.max_check_count()) ++ kingDanger *= 2; + + // Unsafe or occupied checking squares will also be considered, as long as + // the square is in the attacker's mobility area. + unsafeChecks &= mobilityArea[Them]; + + kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] - + 69 * kingAttacksCount[Them] - + 185 * popcount(kingRing[Us] & weak) - + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) - + tropism * tropism / 4 - - 873 * !pos.count(Them) - - 6 * mg_value(score) / 8 - + mg_value(mobility[Them] - mobility[Us]) - - 30; ++ + 69 * kingAttacksCount[Them] * (1 + 2 * !!pos.max_check_count()) ++ + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + !!pos.max_check_count()) ++ + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) ++ + tropism * tropism / 4 ++ - 873 * !(pos.count(Them) || pos.captures_to_hand()) / (1 + !!pos.max_check_count()) ++ - 6 * mg_value(score) / 8 ++ + mg_value(mobility[Them] - mobility[Us]) ++ - 30; + + // Transform the kingDanger units into a Score, and subtract it from the evaluation + if (kingDanger > 0) - score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); ++ score -= make_score(std::min(kingDanger * kingDanger / 4096, 3000), kingDanger / 16); + // Penalty when our king is on a pawnless flank if (!(pos.pieces(PAWN) & kingFlank)) score -= PawnlessFlank; @@@ -603,30 -504,12 +595,30 @@@ Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted; Score score = SCORE_ZERO; + // Bonuses for variants with mandatory captures + if (pos.must_capture()) + { + // Penalties for possible captures + score -= make_score(100, 100) * popcount(attackedBy[Us][ALL_PIECES] & pos.pieces(Them)); + + // Bonus if we threaten to force captures + Bitboard moves = 0, piecebb = pos.pieces(Us); + while (piecebb) + { + Square s = pop_lsb(&piecebb); + if (type_of(pos.piece_on(s)) != KING) + moves |= pos.moves_from(Us, type_of(pos.piece_on(s)), s); + } + score += make_score(200, 200) * popcount(attackedBy[Them][ALL_PIECES] & moves & ~pos.pieces()); + score += make_score(200, 200) * popcount(attackedBy[Them][ALL_PIECES] & moves & ~pos.pieces() & ~attackedBy2[Us]); + } + // Non-pawn enemies - nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN, SHOGI_PAWN); - nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN); ++ nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN, SHOGI_PAWN); // Squares strongly protected by the enemy, either because they defend the // square with a pawn, or because they defend the square twice and we don't. -- stronglyProtected = attackedBy[Them][PAWN] ++ stronglyProtected = (attackedBy[Them][PAWN] | attackedBy[Them][SHOGI_PAWN]) | (attackedBy2[Them] & ~attackedBy2[Us]); // Non-pawn enemies, strongly protected diff --cc src/movegen.cpp index 5eb3c3a,e767897..d0473cd --- a/src/movegen.cpp +++ b/src/movegen.cpp @@@ -355,7 -277,7 +355,7 @@@ namespace *moveList++ = make_move(ksq, pop_lsb(&b)); } - if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) - if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us)) ++ if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us)) { if (pos.is_chess960()) { diff --cc src/pawns.cpp index ed79aab,edd670b..2e40ef7 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@@ -97,12 -97,12 +97,12 @@@ namespace // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); stoppers = theirPawns & passed_pawn_mask(Us, s); - lever = theirPawns & PawnAttacks[Us][s]; - leverPush = theirPawns & PawnAttacks[Us][s + Up]; - doubled = ourPawns & (s - Up); + lever = theirPawns & PseudoAttacks[Us][PAWN][s]; + leverPush = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : 0; + doubled = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? ourPawns & (s - Up) : 0; neighbours = ourPawns & adjacent_files_bb(f); phalanx = neighbours & rank_bb(s); - supported = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? neighbours & rank_bb(s - Up) : 0; - support = neighbours & rank_bb(s - Up); ++ support = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? neighbours & rank_bb(s - Up) : 0; // A pawn is backward when it is behind all pawns of the same color // on the adjacent files and cannot be safely advanced. @@@ -119,22 -118,21 +119,22 @@@ && popcount(phalanx) >= popcount(leverPush)) e->passedPawns[Us] |= s; - else if ( stoppers == SquareBB[s + Up] - && relative_rank(Us, s) >= RANK_5) + else if ( relative_rank(Them, s, pos.max_rank()) > RANK_1 + && stoppers == SquareBB[s + Up] + && relative_rank(Us, s, pos.max_rank()) >= RANK_5) { - b = shift(supported) & ~theirPawns; + b = shift(support) & ~theirPawns; while (b) - if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)])) + if (!more_than_one(theirPawns & PseudoAttacks[Us][PAWN][pop_lsb(&b)])) e->passedPawns[Us] |= s; } // Score this pawn - if (supported | phalanx) - score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s, pos.max_rank())]; + if (support | phalanx) - score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)]; ++ score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s, pos.max_rank())]; else if (!neighbours) - score -= Isolated, e->weakUnopposed[Us] += !opposed; + score -= Isolated * (1 + 2 * pos.must_capture()), e->weakUnopposed[Us] += !opposed; else if (backward) score -= Backward, e->weakUnopposed[Us] += !opposed; @@@ -241,14 -214,13 +241,14 @@@ Value Entry::evaluate_shelter(const Pos for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); - int ourRank = b ? relative_rank(Us, backmost_sq(Us, b), pos.max_rank()) : 0; - Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; ++ Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b), pos.max_rank()) : RANK_1; b = theirPawns & file_bb(f); - int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : 0; - Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; ++ Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : RANK_1; - int d = std::min(f, ~f); - safety += ShelterStrength[d][ourRank]; + int d = std::min(std::min(f, File(pos.max_file() - f)), FILE_D); + // higher weight for pawns on second rank and missing shelter in drop variants + safety += ShelterStrength[d][ourRank] * (1 + (pos.captures_to_hand() && ourRank <= RANK_2)); safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3) : UnblockedStorm[d][theirRank]; } diff --cc src/position.cpp index 3aa68ef,bd5daa6..21c8e74 --- a/src/position.cpp +++ b/src/position.cpp @@@ -627,21 -476,11 +627,21 @@@ const string Position::fen() const if (can_castle(BLACK_OOO)) ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); - if (!can_castle(WHITE) && !can_castle(BLACK)) + if (!can_castle(ANY_CASTLING)) ss << '-'; - ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") - << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; + // check count + if (max_check_count()) + ss << " " << (max_check_count() - st->checksGiven[WHITE]) << "+" << (max_check_count() - st->checksGiven[BLACK]); + + // Counting limit and counting ply, or ep-square and 50-move rule counter + if (st->countingLimit) + ss << " " << st->countingLimit << " " << st->countingPly; + else + ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ") + << st->rule50; + + ss << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } diff --cc src/psqt.cpp index ea44619,a4a5e0a..748baaa --- a/src/psqt.cpp +++ b/src/psqt.cpp @@@ -44,17 -35,9 +44,9 @@@ namespace PSQT // type on a given square a (middlegame, endgame) score pair is assigned. Table // is defined for files A..D and white side: it is symmetric for black side and // second half of the files. -constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { +constexpr Score Bonus[PIECE_TYPE_NB][RANK_NB][int(FILE_NB) / 2] = { { }, - { // Pawn - { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, - { S(-11,-3), S( 7, -1), S( 7, 7), S(17, 2) }, - { S(-16,-2), S( -3, 2), S( 23, 6), S(23,-1) }, - { S(-14, 7), S( -7, -4), S( 20,-8), S(24, 2) }, - { S( -5,13), S( -2, 10), S( -1,-1), S(12,-8) }, - { S(-11,16), S(-12, 6), S( -2, 1), S( 4,16) }, - { S( -2, 1), S( 20,-12), S(-10, 6), S(-2,25) } - }, + { }, { // Knight { S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) }, { S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) }, @@@ -106,11 -87,23 +98,22 @@@ constexpr Score KingBonus[RANK_NB][int( { S(122, 87), S(159,164), S(85, 174), S(36, 189) }, { S(87, 40), S(120, 99), S(64, 128), S(25, 141) }, { S(64, 5), S(87, 60), S(49, 75), S(0, 75) } - } }; -constexpr Score PBonus[RANK_NB][FILE_NB] = - { // Pawn - { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, - { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) }, - { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) }, - { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) }, - { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) }, - { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) }, - { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) } ++constexpr Score PBonus[RANK_NB][FILE_NB] = ++ { // Pawn ++ { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, ++ { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) }, ++ { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) }, ++ { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) }, ++ { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) }, ++ { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) }, ++ { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) } + }; + #undef S -Score psq[PIECE_NB][SQUARE_NB]; +Score psq[PIECE_NB][SQUARE_NB + 1]; // 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 @@@ -126,30 -117,13 +129,31 @@@ void init(const Variant* v) Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); - for (Square s = SQ_A1; s <= SQ_H8; ++s) + // For drop variants, halve the piece values + if (v->capturesToHand || !v->checking) + score = make_score(mg_value(score) * int(MidgameLimit / 2) / (MidgameLimit + mg_value(score)), + eg_value(score) * int(MidgameLimit / 2) / (MidgameLimit + eg_value(score))); + + // For antichess variants, use negative piece values + if ( v->extinctionValue == VALUE_MATE + && v->extinctionPieceTypes.find(ALL_PIECES) != v->extinctionPieceTypes.end()) + score = -score / 8; + else if (v->bareKingValue == VALUE_MATE) + score = -score / 8; + + for (Square s = SQ_A1; s <= SQ_MAX; ++s) { - File f = std::min(file_of(s), ~file_of(s)); - psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] - : Bonus[pc][rank_of(s)][f]); - psq[~pc][~s] = -psq[pc][s]; + File f = std::max(std::min(file_of(s), File(v->maxFile - file_of(s))), FILE_A); + Rank r = rank_of(s); - psq[ pc][ s] = score + ( pt == KING ? KingBonus[std::min(r, RANK_8)][std::min(f, FILE_D)] ++ psq[ pc][ s] = score + ( pt == PAWN ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)] ++ : pt == KING ? KingBonus[std::min(r, RANK_8)][std::min(f, FILE_D)] + : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)] + : make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - 8)); + psq[~pc][rank_of(s) <= v->maxRank ? relative_square(BLACK, s, v->maxRank) : s] = -psq[pc][s]; } + // pieces in pocket + psq[ pc][SQ_NONE] = score + make_score(20, 20); + psq[~pc][SQ_NONE] = -psq[pc][SQ_NONE]; } } diff --cc src/search.cpp index 5bfcf80,4a541c5..6f81a99 --- a/src/search.cpp +++ b/src/search.cpp @@@ -761,27 -764,18 +769,28 @@@ namespace } // Step 7. Razoring (~2 Elo) - if ( depth < 2 * ONE_PLY + if ( !rootNode // The required rootNode PV handling is not available in qsearch + && depth < 2 * ONE_PLY + && !pos.must_capture() + && !pos.capture_the_flag_piece() + && !pos.max_check_count() - && eval <= alpha - RazorMargin) + && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); improving = ss->staticEval >= (ss-2)->staticEval || (ss-2)->staticEval == VALUE_NONE; + // Skip early pruning in case of mandatory capture + if (pos.must_capture() && MoveList(pos).size()) + goto moves_loop; + // Step 8. Futility pruning: child node (~30 Elo) - if ( !rootNode + if ( !PvNode && depth < 7 * ONE_PLY - && eval - futility_margin(depth, improving) >= beta + && !( pos.extinction_value() == -VALUE_MATE + && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end()) + && (pos.checking_permitted() || !pos.capture_the_flag_piece()) + && eval - futility_margin(depth, improving) * (1 + !!pos.max_check_count()) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@@ -841,11 -833,10 +850,11 @@@ // much above beta, we can (almost) safely prune the previous move. if ( !PvNode && depth >= 5 * ONE_PLY + && (pos.pieces() ^ pos.pieces(CLOBBER_PIECE)) && abs(beta) < VALUE_MATE_IN_MAX_PLY) { - Value rbeta = std::min(beta + 216 * (1 + !!pos.max_check_count() + (pos.extinction_value() != VALUE_NONE)) - 48 * improving, VALUE_INFINITE); - MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory); - Value raisedBeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE); ++ Value raisedBeta = std::min(beta + 216 * (1 + !!pos.max_check_count() + (pos.extinction_value() != VALUE_NONE)) - 48 * improving, VALUE_INFINITE); + MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE @@@ -982,8 -969,7 +991,8 @@@ moves_loop: // When in check, search st { if ( !captureOrPromotion && !givesCheck + && (!pos.must_capture() || !pos.attackers_to(to_sq(move), ~pos.side_to_move())) - && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= Value(5000))) + && !pos.advanced_pawn_push(move)) { // Move count based pruning (~30 Elo) if (moveCountPruning)