glaurung-2.2/ 0000755 0001750 0001750 00000000000 11123154606 012562 5 ustar oliver oliver glaurung-2.2/Readme.txt 0000644 0001750 0001750 00000035116 11123154606 014526 0 ustar oliver oliver 1. Introduction
---------------
Glaurung is a free UCI chess engine. It is not a complete chess
program, but requires some UCI compatible GUI (like XBoard with
PolyGlot, eboard, José, 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 Glaurung with your
GUI.
Glaurung 2 is a completely rewritten version of Glaurung. Apart from
the parallel search code, almost no code is shared with Glaurung
1.2.1, the previous stable version. The new program is clearly
stronger than the old, but has a less attractive style of play,
because there are still a few major holes in its evaluation function
(most notably space and development).
This version of Glaurung supports up to 8 CPUs, but has not been
tested thoroughly with more than 2. The program tries to detect the
number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always
correct. 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.
2. Files
--------
This distribution of Glaurung consists of the following files:
* Readme.txt, the file you are currently reading.
* Copying.txt, a text file containing the GNU General Public
License.
* src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Glaurung on Unix-like
systems. For further information about how to compile Glaurung
yourself, read section 4 below.
* MacOSX/, a subdirectory containing excutables for Apple Macintosh
computers running Mac OS X 10.4 (Tiger) and newer. There are two
executables, one for OS X 10.4, and one for OS X 10.5. The
executable for OS X 10.4 will work in 10.5 as well, but the one
for 10.5 is faster.
* LinuxX86/, a subdirectory containing 32-bit and 64-bit x86 GNU/Linux
executables (not available yet).
* Windows/, a subdirectory containing 32-bit and 64-bit Windows
executables.
* polyglot.ini, for using Glaurung with Fabien Letouzey's PolyGlot
adapter.
3. Opening books
----------------
This version of Glaurung has experimental support for PolyGlot opening
books. For information about how to create such books, consult the
PolyGlot documentation. The book file can be selected by setting the
UCI parameter "Book File".
A book file contributed by Salvo Spitaleri can be found on the
Glaurung web page.
4. Compiling it yourself
------------------------
On Unix-like systems, it should usually be possible to compile
Glaurung directly from the source code with the included Makefile.
The exception is computer with big-endian CPUs, like PowerPC
Macintoshes. Some of the bitboard routines in the current version of
Glaurung are endianness-sensitive, and won't work on a big-endian CPU.
Ensuring that the line with #define USE_32BIT_ATTACKS" near the top
of bitboard.h is commented out should solve this problem.
Commenting out the line with "#define USE_32BIT_ATTACKS" near the
There is also a problem with compiling Glaurung on certain 64-bit
systems, regardless of the endianness. If Glaurung segfaults
immediately after startup, try to comment out the line with
"#define USE_FOLDED_BITSCAN" near the beginning of bitboard.h and
recompile.
Finally, even if Glaurung does work without any changes on your
computer, it might be possible to improve the performance by changing
some of the #define directives in bitboard.h. The default settings
are optimized for 64-bit CPUs. On 32-bit CPUs, it is probably better
to switch on USE_32BIT_ATTACKS, and to use BITCOUNT_SWAR_32 instead of
BITCOUNT_SWAR_64. For computers with very little memory (like
handheld devices), it is possible to conserve memory by defining
USE_COMPACT_ROOK_ATTACKS.
5. History
----------
2007-05-06: Glaurung 2 - epsilon
--------------------------------
The first public release, and the first version of my new program
which is able to match the old Glaurung 1.2.1 on a single CPU. Lots
of features and chess knowledge is still missing.
2007-05-10: Glaurung 2 - epsilon/2
----------------------------------
This version is very close to 2 - epsilon. The major changes are:
* A number of compatibility problems which appeared when trying to
compile Glaurung 2 - epsilon on various operating systems and CPUs
have been solved.
* Fixed a major bug in the detection of rooks trapped inside a
friendly king.
* Added knowledge about several types of drawn endgames.
* Fixed a few FRC related bugs. FRC now works, but because of
serious holes in the evaluation function the program plays very
badly.
* A slightly more sophisticated king safety evaluation.
2007-06-07: Glaurung 2 - epsilon/3
----------------------------------
The first public version with support for multiple CPUs. Unless you
have a dual-core (or better) computer, use Glaurung with a PolyGlot
book, or runs games with ponder on, you may want to skip this version,
which is almost certainly no stronger than 2 - epsilon/2 when running
on a single CPU. The main changes compared to the previous version
are:
* Parallel search, with support for 1-4 CPUs. The program currently
always allocates a separate pawn hash table and material hash
table for four threads, which is a pure waste of RAM if your
computer has just a single CPU. This will be fixed in a future
version.
* Fixed a bug in book randomization. When using Polyglot books, the
previous version would always select exactly the same move in the
same position after a restart of the program. Thanks to Pavel
Háse for pointing this out.
* Fixed a UCI pondering bug: Glaurung no longer instantly prints its
best move when the maximum depth is reached during a ponder
search, as the previous version did. According to the UCI
protocol, it is not allowed to print the best move before the
engine has received the "stop" or "quit" command.
* Additional search information: The new version displays hash
saturation and the current line(s) of search.
* Several minor bug fixes and optimizations in the search and
evaluation.
2007-06-08: Glaurung 2 - epsilon/4
----------------------------------
A bugfix release, with only a single important change:
* Fixed a very serious pondering bug. As pointed out by Marc
Lacrosse, the previous version would lose on time in almost every
single game with pondering enabled. The new version handles
pondering correctly (or so I hope). When playing with ponder
off, the new version is identical to version 2 - epsilon/3.
2007-06-25: Glaurung 2 - epsilon/5
----------------------------------
Another minor update, including the following improvements and bug
fixes:
* As Werner Schüle discovered, the previous version would sometimes
stop thinking and lose on time right before delivering checkmate
(which is of course a very unfortunate moment to lose on time).
I haven't been able to reproduce Werner's problem on my computer
(probably because I run a different OS), but I have fixed the bug
which I suspect caused the time losses. I hope the time losses
will no longer occur with 2 - epsilon/5.
* The program is now slightly less resource-hungry on computers
with less than 4 CPU cores: The previous version would always
allocated separate pawn and material hash tables for four
threads, even when running on a single-core CPU. The new version
only allocates pawn and material hash tables for the threads
which are actually used.
* A minor reorganization of the memory layout has made the parallel
search about 10% more efficient (at least on my computer, but the
results are likely to vary considerably on different systems).
* The Intel Mac OS X binary is much faster than before, thanks to
the Intel C++ compiler (previous versions were compiled with
GCC).
* A few other very minor bug fixes and enhancements.
2007-11-21: Glaurung 2.0
------------------------
The first stable (or so I hope) and feature-complete version of
Glaurung 2. The following are the main changes compared to the
previous version:
* The license has been changed from GPL version 2 to GPL version 3.
* MultiPV mode.
* Support for the "searchmoves" option in the UCI "go" command.
This means that it is possible to ask Glaurung to exclude some
moves from its analysis, or to restrict its analysis to just a
handful of moves selected by the user. This feature must also be
supported by the GUI under which Glaurung is run. Glaurung's own
GUI does currently not support this feature.
* Chess960 support now works. The program still plays this game
very badly, because of lack of opening knowledge.
* Much more aggressive pruning in the last few plies of the main
search.
* Somewhat better scaling on multi-CPU systems, and support for up
to 8 CPUs.
* Lots of new UCI parameters.
* Improved time managment, especially in games with pondering on
(i.e. when the engine is allowed to think when it's the
opponent's turn to move).
* Some evaluation improvements, and some new basic endgame
patterns.
* The program should no longer crash if the game lasts longer than
1000 plies.
* Many minor bug fixes and other tiny improvements throughout the
code.
* More generously commented code, and numerous cosmetic changes in
coding style.
2007-11-22: Glaurung 2.0.1
--------------------------
* Fixed (or so I hope) a bug which would occasionally cause one of
the search threads to get stuck forever in its idle loop.
2008-05-14: Glaurung 2.1
------------------------
This version contains far too many changes to list them all, but most
of them are minor and cosmetic. The most important and noticable
changes are a lot of new UCI parameters, and many improvements in the
evaluation function. The highlights are:
* Extensive changes in the evaluation function. The addition of
king safety is the most important improvement, but there are also
numerous little improvements elsewhere in the evaluation. There
is still much work left to do in the evaluation function, though.
Space and development are still missing, and the tuning is likely
to be very poor. Currently, the program is optimized for an
entertaining style rather than maximum strength.
* More accurate forward pruning. The previous version used the
null move refutation move to improve the pruning accuracy by
means of a very simple trick: It did not allow pruning of any
moves with the piece captured by the null move refutation move.
In Glaurung 2.1, this has been enhanced: It does not allow
pruning of moves which defend the destination square of the null
move refutation move, nor of moves which block the ray of the
piece in the case that the moving piece in the null move
refutation move is a slider.
* More conservative use of LMR at PV nodes. The previous version
searched the first 6 moves with full depth, 2.1 by default
searches the first 14 moves with full depth (but there is a new
UCI parameter for configuring this). I am not at all sure
whether this is an improvement. More thorough testing is
required.
* Feedback from the evaluation to the search. The search passes an
object of type 'EvalInfo' to the eval, and the eval fills this
struct with various potentially useful information (like the sets
of squares attacked by each piece type, the middle game and
endgame components of the eval, etc.). At the moment, almost
none of this information is actually used by the search. The
only exception is that the evaluation function is now used to
adjust the futility pruning margin in the quiescence search.
* Less extensions. This hurts the programs performance a lot in most
test suites, but I hope it improves the branching factor in deep
searches.
* A very long list of new UCI parameters, especially for tuning the
evaluation.
2008-12-20: Glaurung 2.2
------------------------
Most of the work in this version was done in May and June 2008, in
preparation for the Open Polish Computer Chess Championships in the
end of June. In my last-minute tests before the tournament, it seemed
that the development version performed worse than Glaurung 2.1; I
therefore used version 2.1 in the OPCCC. More recently, I have
discovered to my great surprise that the development version *does*
seem to play stronger than 2.1 -- something must have been wrong with
my tests in June. I've therefore decided to release the version that
didn't play in Poland, with a handful of very minor improvements and
bug fixes done in December 2008.
Here are the main changes compared to version 2.1:
* Space evaluation. This is probably the most significant
improvement, and makes Glaurung play more actively and
aggressively, particularly in the opening.
* Considers "rook brakes" in passed pawn evaluation.
* Some new basic endgame knowledge was added.
* Uses only the approximate eval, and not the full eval, for
razoring. This looks risky, but seems to work OK in practice.
This idea was stolen from Marco Costalba's new chess engine
"Stockfish".
* Lots of minor bug fixes and parameter changes in the search and
evaluation.
6. Terms of use
---------------
Glaurung is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or
using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Glaurung in
some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named
Copying.txt.
7. Feedback
-----------
The author's e-mail address is tord@glaurungchess.com
glaurung-2.2/LinuxX86/ 0000755 0001750 0001750 00000000000 11123710547 014171 5 ustar oliver oliver glaurung-2.2/MacOSX/ 0000755 0001750 0001750 00000000000 11123710560 013651 5 ustar oliver oliver glaurung-2.2/src/ 0000755 0001750 0001750 00000000000 11123214517 013347 5 ustar oliver oliver glaurung-2.2/src/movegen.cpp 0000644 0001750 0001750 00000110471 11123021252 015507 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "movegen.h"
////
//// Local definitions
////
namespace {
int generate_white_pawn_captures(const Position &pos, MoveStack *mlist);
int generate_black_pawn_captures(const Position &pos, MoveStack *mlist);
int generate_white_pawn_noncaptures(const Position &pos, MoveStack *mlist);
int generate_black_pawn_noncaptures(const Position &pos, MoveStack *mlist);
int generate_knight_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target);
int generate_bishop_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target);
int generate_rook_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target);
int generate_queen_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target);
int generate_king_moves(const Position &pos, MoveStack *mlist,
Square from, Bitboard target);
int generate_castle_moves(const Position &pos, MoveStack *mlist, Color us);
}
////
//// Functions
////
/// generate_captures generates() all pseudo-legal captures and queen
/// promotions. The return value is the number of moves generated.
int generate_captures(const Position &pos, MoveStack *mlist) {
Color us = pos.side_to_move();
Bitboard target = pos.pieces_of_color(opposite_color(us));
int n = 0;
assert(pos.is_ok());
assert(!pos.is_check());
if(us == WHITE)
n += generate_white_pawn_captures(pos, mlist);
else
n += generate_black_pawn_captures(pos, mlist);
n += generate_knight_moves(pos, mlist+n, us, target);
n += generate_bishop_moves(pos, mlist+n, us, target);
n += generate_rook_moves(pos, mlist+n, us, target);
n += generate_queen_moves(pos, mlist+n, us, target);
n += generate_king_moves(pos, mlist+n, pos.king_square(us), target);
return n;
}
/// generate_noncaptures() generates all pseudo-legal non-captures and
/// underpromotions. The return value is the number of moves generated.
int generate_noncaptures(const Position &pos, MoveStack *mlist) {
Color us = pos.side_to_move();
Bitboard target = pos.empty_squares();
int n = 0;
assert(pos.is_ok());
assert(!pos.is_check());
if(us == WHITE)
n += generate_white_pawn_noncaptures(pos, mlist);
else
n += generate_black_pawn_noncaptures(pos, mlist);
n += generate_knight_moves(pos, mlist+n, us, target);
n += generate_bishop_moves(pos, mlist+n, us, target);
n += generate_rook_moves(pos, mlist+n, us, target);
n += generate_queen_moves(pos, mlist+n, us, target);
n += generate_king_moves(pos, mlist+n, pos.king_square(us), target);
n += generate_castle_moves(pos, mlist+n, us);
return n;
}
/// generate_checks() generates all pseudo-legal non-capturing, non-promoting
/// checks, except castling moves (will add this later). It returns the
/// number of generated moves.
int generate_checks(const Position &pos, MoveStack *mlist, Bitboard dc) {
Color us, them;
Square ksq, from, to;
Bitboard empty, checkSqs, b1, b2, b3;
int n = 0;
assert(pos.is_ok());
assert(!pos.is_check());
us = pos.side_to_move();
them = opposite_color(us);
ksq = pos.king_square(them);
assert(pos.piece_on(ksq) == king_of_color(them));
dc = pos.discovered_check_candidates(us);
empty = pos.empty_squares();
// Pawn moves. This is somewhat messy, and we use separate code for white
// and black, because we can't shift by negative numbers in C/C++. :-(
if(us == WHITE) {
// Pawn moves which give discovered check. This is possible only if the
// pawn is not on the same file as the enemy king, because we don't
// generate captures.
// Find all friendly pawns not on the enemy king's file:
b1 = pos.pawns(us) & ~file_bb(ksq);
// Discovered checks, single pawn pushes:
b2 = b3 = ((b1 & dc) << 8) & ~Rank8BB & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_N, to);
}
// Discovered checks, double pawn pushes:
b3 = ((b2 & Rank3BB) << 8) & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
}
// Direct checks. These are possible only for pawns on neighboring files
// of the enemy king:
b1 &= (~dc & neighboring_files_bb(ksq));
// Direct checks, single pawn pushes:
b2 = (b1 << 8) & empty;
b3 = b2 & pos.black_pawn_attacks(ksq);
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_N, to);
}
// Direct checks, double pawn pushes:
b3 = ((b2 & Rank3BB) << 8) & empty & pos.black_pawn_attacks(ksq);
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
}
}
else { // (us == BLACK)
// Pawn moves which give discovered check. This is possible only if the
// pawn is not on the same file as the enemy king, because we don't
// generate captures.
// Find all friendly pawns not on the enemy king's file:
b1 = pos.pawns(us) & ~file_bb(ksq);
// Discovered checks, single pawn pushes:
b2 = b3 = ((b1 & dc) >> 8) & ~Rank1BB & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_S, to);
}
// Discovered checks, double pawn pushes:
b3 = ((b2 & Rank6BB) >> 8) & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
}
// Direct checks. These are possible only for pawns on neighboring files
// of the enemy king:
b1 &= (~dc & neighboring_files_bb(ksq));
// Direct checks, single pawn pushes:
b2 = (b1 >> 8) & empty;
b3 = b2 & pos.white_pawn_attacks(ksq);
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_S, to);
}
// Direct checks, double pawn pushes:
b3 = ((b2 & Rank6BB) >> 8) & empty & pos.white_pawn_attacks(ksq);
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
}
}
// Knight moves
b1 = pos.knights(us);
if(b1) {
// Discovered knight checks:
b2 = b1 & dc;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.knight_attacks(from) & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
// Direct knight checks:
b2 = b1 & ~dc;
checkSqs = pos.knight_attacks(ksq) & empty;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.knight_attacks(from) & checkSqs;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
}
// Bishop moves
b1 = pos.bishops(us);
if(b1) {
// Discovered bishop checks:
b2 = b1 & dc;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.bishop_attacks(from) & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
// Direct bishop checks:
b2 = b1 & ~dc;
checkSqs = pos.bishop_attacks(ksq) & empty;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.bishop_attacks(from) & checkSqs;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
}
// Rook moves
b1 = pos.rooks(us);
if(b1) {
// Discovered rook checks:
b2 = b1 & dc;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.rook_attacks(from) & empty;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
// Direct rook checks:
b2 = b1 & ~dc;
checkSqs = pos.rook_attacks(ksq) & empty;
while(b2) {
from = pop_1st_bit(&b2);
b3 = pos.rook_attacks(from) & checkSqs;
while(b3) {
to = pop_1st_bit(&b3);
mlist[n++].move = make_move(from, to);
}
}
}
// Queen moves
b1 = pos.queens(us);
if(b1) {
// Discovered queen checks are impossible!
// Direct queen checks:
checkSqs = pos.queen_attacks(ksq) & empty;
while(b1) {
from = pop_1st_bit(&b1);
b2 = pos.queen_attacks(from) & checkSqs;
while(b2) {
to = pop_1st_bit(&b2);
mlist[n++].move = make_move(from, to);
}
}
}
// King moves
from = pos.king_square(us);
if(bit_is_set(dc, from)) {
b1 = pos.king_attacks(from) & empty & ~QueenPseudoAttacks[ksq];
while(b1) {
to = pop_1st_bit(&b1);
mlist[n++].move = make_move(from, to);
}
}
// TODO: Castling moves!
return n;
}
/// generate_evasions() generates all check evasions when the side to move is
/// in check. Unlike the other move generation functions, this one generates
/// only legal moves. It returns the number of generated moves. This
/// function is very ugly, and needs cleaning up some time later. FIXME
int generate_evasions(const Position &pos, MoveStack *mlist) {
Color us, them;
Bitboard checkers = pos.checkers();
Bitboard pinned, b1, b2;
Square ksq, from, to;
int n = 0;
assert(pos.is_ok());
assert(pos.is_check());
us = pos.side_to_move();
them = opposite_color(us);
ksq = pos.king_square(us);
assert(pos.piece_on(ksq) == king_of_color(us));
// Generate evasions for king:
b1 = pos.king_attacks(ksq) & ~pos.pieces_of_color(us);
b2 = pos.occupied_squares();
clear_bit(&b2, ksq);
while(b1) {
to = pop_1st_bit(&b1);
// Make sure to is not attacked by the other side. This is a bit ugly,
// because we can't use Position::square_is_attacked. Instead we use
// the low-level bishop_attacks_bb and rook_attacks_bb with the bitboard
// b2 (the occupied squares with the king removed) in order to test whether
// the king will remain in check on the destination square.
if(((pos.pawn_attacks(us, to) & pos.pawns(them)) == EmptyBoardBB) &&
((pos.knight_attacks(to) & pos.knights(them)) == EmptyBoardBB) &&
((pos.king_attacks(to) & pos.kings(them)) == EmptyBoardBB) &&
((bishop_attacks_bb(to, b2) & pos.bishops_and_queens(them))
== EmptyBoardBB) &&
((rook_attacks_bb(to, b2) & pos.rooks_and_queens(them)) == EmptyBoardBB))
mlist[n++].move = make_move(ksq, to);
}
// Generate evasions for other pieces only if not double check. We use a
// simple bit twiddling hack here rather than calling count_1s in order to
// save some time (we know that pos.checkers() has at most two nonzero bits).
if(!(checkers & (checkers - 1))) {
Square checksq = first_1(checkers);
assert(pos.color_of_piece_on(checksq) == them);
// Find pinned pieces:
pinned = pos.pinned_pieces(us);
// Generate captures of the checking piece:
// Pawn captures:
b1 = pos.pawn_attacks(them, checksq) & pos.pawns(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
if(pawn_rank(us, checksq) == RANK_8) {
mlist[n++].move = make_promotion_move(from, checksq, QUEEN);
mlist[n++].move = make_promotion_move(from, checksq, ROOK);
mlist[n++].move = make_promotion_move(from, checksq, BISHOP);
mlist[n++].move = make_promotion_move(from, checksq, KNIGHT);
}
else
mlist[n++].move = make_move(from, checksq);
}
// Knight captures:
b1 = pos.knight_attacks(checksq) & pos.knights(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
mlist[n++].move = make_move(from, checksq);
}
// Bishop and queen captures:
b1 = pos.bishop_attacks(checksq) & pos.bishops_and_queens(us)
& ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
mlist[n++].move = make_move(from, checksq);
}
// Rook and queen captures:
b1 = pos.rook_attacks(checksq) & pos.rooks_and_queens(us)
& ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
mlist[n++].move = make_move(from, checksq);
}
// Blocking check evasions are possible only if the checking piece is
// a slider:
if(checkers & pos.sliders()) {
Bitboard blockSquares = squares_between(checksq, ksq);
assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB);
// Pawn moves. Because a blocking evasion can never be a capture, we
// only generate pawn pushes. As so often, the code for pawns is a bit
// ugly, and uses separate clauses for white and black pawns. :-(
if(us == WHITE) {
// Find non-pinned pawns:
b1 = pos.pawns(WHITE) & ~pinned;
// Single pawn pushes. We don't have to AND with empty squares here,
// because the blocking squares will always be empty.
b2 = (b1 << 8) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
assert(pos.piece_on(to) == EMPTY);
if(square_rank(to) == RANK_8) {
mlist[n++].move = make_promotion_move(to - DELTA_N, to, QUEEN);
mlist[n++].move = make_promotion_move(to - DELTA_N, to, ROOK);
mlist[n++].move = make_promotion_move(to - DELTA_N, to, BISHOP);
mlist[n++].move = make_promotion_move(to - DELTA_N, to, KNIGHT);
}
else
mlist[n++].move = make_move(to - DELTA_N, to);
}
// Double pawn pushes.
b2 = (((b1 << 8) & pos.empty_squares() & Rank3BB) << 8) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
assert(pos.piece_on(to) == EMPTY);
assert(square_rank(to) == RANK_4);
mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
}
}
else { // (us == BLACK)
// Find non-pinned pawns:
b1 = pos.pawns(BLACK) & ~pinned;
// Single pawn pushes. We don't have to AND with empty squares here,
// because the blocking squares will always be empty.
b2 = (b1 >> 8) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
assert(pos.piece_on(to) == EMPTY);
if(square_rank(to) == RANK_1) {
mlist[n++].move = make_promotion_move(to - DELTA_S, to, QUEEN);
mlist[n++].move = make_promotion_move(to - DELTA_S, to, ROOK);
mlist[n++].move = make_promotion_move(to - DELTA_S, to, BISHOP);
mlist[n++].move = make_promotion_move(to - DELTA_S, to, KNIGHT);
}
else
mlist[n++].move = make_move(to - DELTA_S, to);
}
// Double pawn pushes.
b2 = (((b1 >> 8) & pos.empty_squares() & Rank6BB) >> 8) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
assert(pos.piece_on(to) == EMPTY);
assert(square_rank(to) == RANK_5);
mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
}
}
// Knight moves
b1 = pos.knights(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
b2 = pos.knight_attacks(from) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
mlist[n++].move = make_move(from, to);
}
}
// Bishop moves
b1 = pos.bishops(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
b2 = pos.bishop_attacks(from) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
mlist[n++].move = make_move(from, to);
}
}
// Rook moves
b1 = pos.rooks(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
b2 = pos.rook_attacks(from) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
mlist[n++].move = make_move(from, to);
}
}
// Queen moves
b1 = pos.queens(us) & ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
b2 = pos.queen_attacks(from) & blockSquares;
while(b2) {
to = pop_1st_bit(&b2);
mlist[n++].move = make_move(from, to);
}
}
}
// Finally, the ugly special case of en passant captures. An en passant
// capture can only be a check evasion if the check is not a discovered
// check. If pos.ep_square() is set, the last move made must have been
// a double pawn push. If, furthermore, the checking piece is a pawn,
// an en passant check evasion may be possible.
if(pos.ep_square() != SQ_NONE && (checkers & pos.pawns(them))) {
to = pos.ep_square();
b1 = pos.pawn_attacks(them, to) & pos.pawns(us);
assert(b1 != EmptyBoardBB);
b1 &= ~pinned;
while(b1) {
from = pop_1st_bit(&b1);
// Before generating the move, we have to make sure it is legal.
// This is somewhat tricky, because the two disappearing pawns may
// cause new "discovered checks". We test this by removing the
// two relevant bits from the occupied squares bitboard, and using
// the low-level bitboard functions for bishop and rook attacks.
b2 = pos.occupied_squares();
clear_bit(&b2, from);
clear_bit(&b2, checksq);
if(((bishop_attacks_bb(ksq, b2) & pos.bishops_and_queens(them))
== EmptyBoardBB) &&
((rook_attacks_bb(ksq, b2) & pos.rooks_and_queens(them))
== EmptyBoardBB))
mlist[n++].move = make_ep_move(from, to);
}
}
}
return n;
}
/// generate_legal_moves() computes a complete list of legal moves in the
/// current position. This function is not very fast, and should be used
/// only in situations where performance is unimportant. It wouldn't be
/// very hard to write an efficient legal move generator, but for the moment
/// we don't need it.
int generate_legal_moves(const Position &pos, MoveStack *mlist) {
assert(pos.is_ok());
if(pos.is_check())
return generate_evasions(pos, mlist);
else {
int i, n;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
// Generate pseudo-legal moves:
n = generate_captures(pos, mlist);
n += generate_noncaptures(pos, mlist + n);
// Remove illegal moves from the list:
for(i = 0; i < n; i++) {
if(!pos.move_is_legal(mlist[i].move, pinned))
mlist[i--].move = mlist[--n].move;
}
return n;
}
}
/// generate_move_if_legal() takes a position and a (not necessarily
/// pseudo-legal) move and a pinned pieces bitboard as input, and tests
/// whether the move is legal. If the move is legal, the move itself is
/// returned. If not, the function returns MOVE_NONE. This function must
/// only be used when the side to move is not in check.
Move generate_move_if_legal(const Position &pos, Move m, Bitboard pinned) {
Color us, them;
Square from, to;
Piece pc;
assert(pos.is_ok());
assert(!pos.is_check());
assert(move_is_ok(m));
us = pos.side_to_move();
them = opposite_color(us);
from = move_from(m);
pc = pos.piece_on(from);
// If the from square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
if(color_of_piece(pc) != us )
return MOVE_NONE;
to = move_to(m);
// En passant moves:
if(move_is_ep(m)) {
// The piece must be a pawn:
if(type_of_piece(pc) != PAWN)
return MOVE_NONE;
// The destination square must be the en passant square:
if(to != pos.ep_square())
return MOVE_NONE;
assert(pos.square_is_empty(to));
assert(pos.piece_on(to - pawn_push(us)) == pawn_of_color(them));
// The move is pseudo-legal. If it is legal, return it.
if(pos.move_is_legal(m))
return m;
else
return MOVE_NONE;
}
// Castling moves:
else if(move_is_short_castle(m)) {
// The piece must be a king:
if(type_of_piece(pc) != KING)
return MOVE_NONE;
// The side to move must still have the right to castle kingside:
if(!pos.can_castle_kingside(us))
return MOVE_NONE;
assert(from == pos.king_square(us));
assert(to == pos.initial_kr_square(us));
assert(pos.piece_on(to) == rook_of_color(us));
Square g1 = relative_square(us, SQ_G1);
Square f1 = relative_square(us, SQ_F1);
Square s;
bool illegal = false;
for(s = Min(from, g1); s <= Max(from, g1); s++)
if((s != from && s != to && !pos.square_is_empty(s)) ||
pos.square_is_attacked(s, them))
illegal = true;
for(s = Min(to, f1); s <= Max(to, f1); s++)
if(s != from && s != to && !pos.square_is_empty(s))
illegal = true;
if(!illegal)
return m;
else
return MOVE_NONE;
}
else if(move_is_long_castle(m)) {
// The piece must be a king:
if(type_of_piece(pc) != KING)
return MOVE_NONE;
// The side to move must still have the right to castle kingside:
if(!pos.can_castle_queenside(us))
return MOVE_NONE;
assert(from == pos.king_square(us));
assert(to == pos.initial_qr_square(us));
assert(pos.piece_on(to) == rook_of_color(us));
Square c1 = relative_square(us, SQ_C1);
Square d1 = relative_square(us, SQ_D1);
Square s;
bool illegal = false;
for(s = Min(from, c1); s <= Max(from, c1); s++)
if((s != from && s != to && !pos.square_is_empty(s)) ||
pos.square_is_attacked(s, them))
illegal = true;
for(s = Min(to, d1); s <= Max(to, d1); s++)
if(s != from && s != to && !pos.square_is_empty(s))
illegal = true;
if(square_file(to) == FILE_B &&
(pos.piece_on(to + DELTA_W) == rook_of_color(them) ||
pos.piece_on(to + DELTA_W) == queen_of_color(them)))
illegal = true;
if(!illegal)
return m;
else
return MOVE_NONE;
}
// Normal moves
else {
// The destination square cannot be occupied by a friendly piece:
if(pos.color_of_piece_on(to) == us)
return MOVE_NONE;
// Proceed according to the type of the moving piece.
switch(type_of_piece(pc)) {
case PAWN:
// Pawn moves, as usual, are somewhat messy.
if(us == WHITE) {
// If the destination square is on the 8th rank, the move must be a
// promotion.
if(square_rank(to) == RANK_8 && !move_promotion(m))
return MOVE_NONE;
// Proceed according to the square delta between the source and
// destionation squares.
switch(to - from) {
case DELTA_NW: case DELTA_NE:
// Capture. The destination square must be occupied by an enemy piece
// (en passant captures was handled earlier).
if(pos.color_of_piece_on(to) != them)
return MOVE_NONE;
break;
case DELTA_N:
// Pawn push. The destination square must be empty.
if(!pos.square_is_empty(to))
return MOVE_NONE;
break;
case DELTA_NN:
// Double pawn push. The destination square must be on the fourth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if(square_rank(to) != RANK_4 || !pos.square_is_empty(to) ||
!pos.square_is_empty(from + DELTA_N))
return MOVE_NONE;
break;
default:
return MOVE_NONE;
}
}
else { // (us == BLACK)
// If the destination square is on the 1st rank, the move must be a
// promotion.
if(square_rank(to) == RANK_1 && !move_promotion(m))
return MOVE_NONE;
// Proceed according to the square delta between the source and
// destionation squares.
switch(to - from) {
case DELTA_SW: case DELTA_SE:
// Capture. The destination square must be occupied by an enemy piece
// (en passant captures was handled earlier).
if(pos.color_of_piece_on(to) != them)
return MOVE_NONE;
break;
case DELTA_S:
// Pawn push. The destination square must be empty.
if(!pos.square_is_empty(to))
return MOVE_NONE;
break;
case DELTA_SS:
// Double pawn push. The destination square must be on the fifth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if(square_rank(to) != RANK_5 || !pos.square_is_empty(to) ||
!pos.square_is_empty(from + DELTA_S))
return MOVE_NONE;
break;
default:
return MOVE_NONE;
}
}
// The move is pseudo-legal. Return it if it is legal.
if(pos.move_is_legal(m))
return m;
else
return MOVE_NONE;
break;
case KNIGHT:
if(pos.knight_attacks_square(from, to) && pos.move_is_legal(m) &&
!move_promotion(m))
return m;
else
return MOVE_NONE;
break;
case BISHOP:
if(pos.bishop_attacks_square(from, to) && pos.move_is_legal(m) &&
!move_promotion(m))
return m;
else
return MOVE_NONE;
break;
case ROOK:
if(pos.rook_attacks_square(from, to) && pos.move_is_legal(m) &&
!move_promotion(m))
return m;
else
return MOVE_NONE;
break;
case QUEEN:
if(pos.queen_attacks_square(from, to) && pos.move_is_legal(m) &&
!move_promotion(m))
return m;
else
return MOVE_NONE;
break;
case KING:
if(pos.king_attacks_square(from, to) && pos.move_is_legal(m) &&
!move_promotion(m))
return m;
else
return MOVE_NONE;
break;
default:
assert(false);
}
}
assert(false);
return MOVE_NONE;
}
namespace {
int generate_white_pawn_captures(const Position &pos, MoveStack *mlist) {
Bitboard pawns = pos.pawns(WHITE);
Bitboard enemyPieces = pos.pieces_of_color(BLACK);
Bitboard b1, b2;
Square sq;
int n = 0;
// Captures in the a1-h8 direction:
b1 = (pawns << 9) & ~FileABB & enemyPieces;
// Promotions:
b2 = b1 & Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, QUEEN);
}
// Non-promotions:
b2 = b1 & ~Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_NE, sq);
}
// Captures in the h1-a8 direction:
b1 = (pawns << 7) & ~FileHBB & enemyPieces;
// Promotions:
b2 = b1 & Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, QUEEN);
}
// Non-promotions:
b2 = b1 & ~Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_NW, sq);
}
// Non-capturing promotions:
b1 = (pawns << 8) & pos.empty_squares() & Rank8BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, QUEEN);
}
// En passant captures:
if(pos.ep_square() != SQ_NONE) {
assert(square_rank(pos.ep_square()) == RANK_6);
b1 = pawns & pos.black_pawn_attacks(pos.ep_square());
assert(b1 != EmptyBoardBB);
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_ep_move(sq, pos.ep_square());
}
}
return n;
}
int generate_black_pawn_captures(const Position &pos, MoveStack *mlist) {
Bitboard pawns = pos.pawns(BLACK);
Bitboard enemyPieces = pos.pieces_of_color(WHITE);
Bitboard b1, b2;
Square sq;
int n = 0;
// Captures in the a8-h1 direction:
b1 = (pawns >> 7) & ~FileABB & enemyPieces;
// Promotions:
b2 = b1 & Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, QUEEN);
}
// Non-promotions:
b2 = b1 & ~Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_SE, sq);
}
// Captures in the h8-a1 direction:
b1 = (pawns >> 9) & ~FileHBB & enemyPieces;
// Promotions:
b2 = b1 & Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, QUEEN);
}
// Non-promotions:
b2 = b1 & ~Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_SW, sq);
}
// Non-capturing promotions:
b1 = (pawns >> 8) & pos.empty_squares() & Rank1BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, QUEEN);
}
// En passant captures:
if(pos.ep_square() != SQ_NONE) {
assert(square_rank(pos.ep_square()) == RANK_3);
b1 = pawns & pos.white_pawn_attacks(pos.ep_square());
assert(b1 != EmptyBoardBB);
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_ep_move(sq, pos.ep_square());
}
}
return n;
}
int generate_white_pawn_noncaptures(const Position &pos, MoveStack *mlist) {
Bitboard pawns = pos.pawns(WHITE);
Bitboard enemyPieces = pos.pieces_of_color(BLACK);
Bitboard emptySquares = pos.empty_squares();
Bitboard b1, b2;
Square sq;
int n = 0;
// Underpromotion captures in the a1-h8 direction:
b1 = (pawns << 9) & ~FileABB & enemyPieces & Rank8BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, KNIGHT);
}
// Underpromotion captures in the h1-a8 direction:
b1 = (pawns << 7) & ~FileHBB & enemyPieces & Rank8BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, KNIGHT);
}
// Single pawn pushes:
b1 = (pawns << 8) & emptySquares;
b2 = b1 & Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, KNIGHT);
}
b2 = b1 & ~Rank8BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_N, sq);
}
// Double pawn pushes:
b2 = ((b1 & Rank3BB) << 8) & emptySquares;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_N - DELTA_N, sq);
}
return n;
}
int generate_black_pawn_noncaptures(const Position &pos, MoveStack *mlist) {
Bitboard pawns = pos.pawns(BLACK);
Bitboard enemyPieces = pos.pieces_of_color(WHITE);
Bitboard emptySquares = pos.empty_squares();
Bitboard b1, b2;
Square sq;
int n = 0;
// Underpromotion captures in the a8-h1 direction:
b1 = (pawns >> 7) & ~FileABB & enemyPieces & Rank1BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, KNIGHT);
}
// Underpromotion captures in the h8-a1 direction:
b1 = (pawns >> 9) & ~FileHBB & enemyPieces & Rank1BB;
while(b1) {
sq = pop_1st_bit(&b1);
mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, KNIGHT);
}
// Single pawn pushes:
b1 = (pawns >> 8) & emptySquares;
b2 = b1 & Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, ROOK);
mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, BISHOP);
mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, KNIGHT);
}
b2 = b1 & ~Rank1BB;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_S, sq);
}
// Double pawn pushes:
b2 = ((b1 & Rank6BB) >> 8) & emptySquares;
while(b2) {
sq = pop_1st_bit(&b2);
mlist[n++].move = make_move(sq - DELTA_S - DELTA_S, sq);
}
return n;
}
int generate_knight_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target) {
Square from, to;
Bitboard b;
int i, n = 0;
for(i = 0; i < pos.knight_count(side); i++) {
from = pos.knight_list(side, i);
b = pos.knight_attacks(from) & target;
while(b) {
to = pop_1st_bit(&b);
mlist[n++].move = make_move(from, to);
}
}
return n;
}
int generate_bishop_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target) {
Square from, to;
Bitboard b;
int i, n = 0;
for(i = 0; i < pos.bishop_count(side); i++) {
from = pos.bishop_list(side, i);
b = pos.bishop_attacks(from) & target;
while(b) {
to = pop_1st_bit(&b);
mlist[n++].move = make_move(from, to);
}
}
return n;
}
int generate_rook_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target) {
Square from, to;
Bitboard b;
int i, n = 0;
for(i = 0; i < pos.rook_count(side); i++) {
from = pos.rook_list(side, i);
b = pos.rook_attacks(from) & target;
while(b) {
to = pop_1st_bit(&b);
mlist[n++].move = make_move(from, to);
}
}
return n;
}
int generate_queen_moves(const Position &pos, MoveStack *mlist,
Color side, Bitboard target) {
Square from, to;
Bitboard b;
int i, n = 0;
for(i = 0; i < pos.queen_count(side); i++) {
from = pos.queen_list(side, i);
b = pos.queen_attacks(from) & target;
while(b) {
to = pop_1st_bit(&b);
mlist[n++].move = make_move(from, to);
}
}
return n;
}
int generate_king_moves(const Position &pos, MoveStack *mlist,
Square from, Bitboard target) {
Square to;
Bitboard b;
int n = 0;
b = pos.king_attacks(from) & target;
while(b) {
to = pop_1st_bit(&b);
mlist[n++].move = make_move(from, to);
}
return n;
}
int generate_castle_moves(const Position &pos, MoveStack *mlist, Color us) {
int n = 0;
if(pos.can_castle(us)) {
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
assert(pos.piece_on(ksq) == king_of_color(us));
if(pos.can_castle_kingside(us)) {
Square rsq = pos.initial_kr_square(us);
Square g1 = relative_square(us, SQ_G1);
Square f1 = relative_square(us, SQ_F1);
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == rook_of_color(us));
for(s = Min(ksq, g1); s <= Max(ksq, g1); s++)
if((s != ksq && s != rsq && pos.square_is_occupied(s))
|| pos.square_is_attacked(s, them))
illegal = true;
for(s = Min(rsq, f1); s <= Max(rsq, f1); s++)
if(s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if(!illegal)
mlist[n++].move = make_castle_move(ksq, rsq);
}
if(pos.can_castle_queenside(us)) {
Square rsq = pos.initial_qr_square(us);
Square c1 = relative_square(us, SQ_C1);
Square d1 = relative_square(us, SQ_D1);
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == rook_of_color(us));
for(s = Min(ksq, c1); s <= Max(ksq, c1); s++)
if((s != ksq && s != rsq && pos.square_is_occupied(s))
|| pos.square_is_attacked(s, them))
illegal = true;
for(s = Min(rsq, d1); s <= Max(rsq, d1); s++)
if(s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if(square_file(rsq) == FILE_B &&
(pos.piece_on(relative_square(us, SQ_A1)) == rook_of_color(them) ||
pos.piece_on(relative_square(us, SQ_A1)) == queen_of_color(them)))
illegal = true;
if(!illegal)
mlist[n++].move = make_castle_move(ksq, rsq);
}
}
return n;
}
}
glaurung-2.2/src/search.cpp 0000644 0001750 0001750 00000241000 11123214400 015304 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include
#include
#include
#include "book.h"
#include "evaluate.h"
#include "history.h"
#include "mersenne.h"
#include "misc.h"
#include "movepick.h"
#include "san.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
/// Types
// The RootMove class is used for moves at the root at the tree. For each
// root move, we store a score, a node count, and a PV (really a refutation
// in the case of moves which fail low).
class RootMove {
public:
RootMove();
Move move;
Value score;
int64_t nodes, cumulativeNodes;
Move pv[PLY_MAX_PLUS_2];
};
// The RootMoveList class is essentially an array of RootMove objects, with
// a handful of methods for accessing the data in the individual moves.
class RootMoveList {
public:
RootMoveList(Position &pos, Move searchMoves[]);
Move get_move(int moveNum) const;
Value get_move_score(int moveNum) const;
void set_move_score(int moveNum, Value score);
void set_move_nodes(int moveNum, int64_t nodes);
void set_move_pv(int moveNum, const Move pv[]);
Move get_move_pv(int moveNum, int i) const;
int64_t get_move_cumulative_nodes(int moveNum);
int move_count() const;
Move scan_for_easy_move() const;
void sort();
void sort_multipv(int n);
private:
static int compare_root_moves(const RootMove &rm1, const RootMove &rm2);
static const int MaxRootMoves = 500;
RootMove moves[MaxRootMoves];
int count;
};
/// Constants and variables
// Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV
// nodes:
int LMRPVMoves = 15;
int LMRNonPVMoves = 4;
// Depth limit for use of dynamic threat detection:
Depth ThreatDepth = 5*OnePly;
// Depth limit for selective search:
Depth SelectiveDepth = 7*OnePly;
// Use internal iterative deepening?
const bool UseIIDAtPVNodes = true;
const bool UseIIDAtNonPVNodes = false;
// Internal iterative deepening margin. At Non-PV moves, when
// UseIIDAtNonPVNodes is true, we do an internal iterative deepening search
// when the static evaluation is at most IIDMargin below beta.
const Value IIDMargin = Value(0x100);
// Easy move margin. An easy move candidate must be at least this much
// better than the second best move.
const Value EasyMoveMargin = Value(0x200);
// Problem margin. If the score of the first move at iteration N+1 has
// dropped by more than this since iteration N, the boolean variable
// "Problem" is set to true, which will make the program spend some extra
// time looking for a better move.
const Value ProblemMargin = Value(0x28);
// No problem margin. If the boolean "Problem" is true, and a new move
// is found at the root which is less than NoProblemMargin worse than the
// best move from the previous iteration, Problem is set back to false.
const Value NoProblemMargin = Value(0x14);
// Null move margin. A null move search will not be done if the approximate
// evaluation of the position is more than NullMoveMargin below beta.
const Value NullMoveMargin = Value(0x300);
// Pruning criterions. See the code and comments in ok_to_prune() to
// understand their precise meaning.
const bool PruneEscapeMoves = false;
const bool PruneDefendingMoves = false;
const bool PruneBlockingMoves = false;
// Use futility pruning?
bool UseQSearchFutilityPruning = true;
bool UseFutilityPruning = true;
// Margins for futility pruning in the quiescence search, at frontier
// nodes, and at pre-frontier nodes:
Value FutilityMargin0 = Value(0x80);
Value FutilityMargin1 = Value(0x100);
Value FutilityMargin2 = Value(0x200);
// Razoring
Depth RazorDepth = 4*OnePly;
Value RazorMargin1 = Value(0x100);
Value RazorMargin2 = Value(0x300);
// Extensions. Array index 0 is used at non-PV nodes, index 1 at PV nodes.
Depth CheckExtension[2] = {OnePly, OnePly};
Depth SingleReplyExtension[2] = {OnePly / 2, OnePly / 2};
Depth PawnPushTo7thExtension[2] = {OnePly / 2, OnePly / 2};
Depth PassedPawnExtension[2] = {Depth(0), Depth(0)};
Depth PawnEndgameExtension[2] = {OnePly, OnePly};
Depth MateThreatExtension[2] = {Depth(0), Depth(0)};
// Search depth at iteration 1:
const Depth InitialDepth = OnePly /*+ OnePly/2*/;
// Node counters
int NodesSincePoll;
int NodesBetweenPolls = 30000;
// Iteration counter:
int Iteration;
// Scores and number of times the best move changed for each iteration:
Value ValueByIteration[PLY_MAX_PLUS_2];
int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
// MultiPV mode:
int MultiPV = 1;
// Time managment variables
int SearchStartTime;
int MaxNodes, MaxDepth;
int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
Move BestRootMove, PonderMove, EasyMove;
int RootMoveNumber;
bool InfiniteSearch;
bool PonderSearch;
bool StopOnPonderhit;
bool AbortSearch;
bool Quit;
bool FailHigh;
bool Problem;
bool PonderingEnabled;
int ExactMaxTime;
// Show current line?
bool ShowCurrentLine = false;
// Log file
bool UseLogFile = false;
std::ofstream LogFile;
// MP related variables
Depth MinimumSplitDepth = 4*OnePly;
int MaxThreadsPerSplitPoint = 4;
Thread Threads[THREAD_MAX];
Lock MPLock;
bool AllThreadsShouldExit = false;
const int MaxActiveSplitPoints = 8;
SplitPoint SplitPointStack[THREAD_MAX][MaxActiveSplitPoints];
bool Idle = true;
#if !defined(_MSC_VER)
pthread_cond_t WaitCond;
pthread_mutex_t WaitLock;
#else
HANDLE SitIdleEvent[THREAD_MAX];
#endif
/// Functions
void id_loop(const Position &pos, Move searchMoves[]);
Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml);
Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta,
Depth depth, int ply, int threadID);
Value search(Position &pos, SearchStack ss[], Value beta,
Depth depth, int ply, bool allowNullmove, int threadID);
Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta,
Depth depth, int ply, int threadID);
void sp_search(SplitPoint *sp, int threadID);
void sp_search_pv(SplitPoint *sp, int threadID);
void init_search_stack(SearchStack ss[]);
void init_node(const Position &pos, SearchStack ss[], int ply, int threadID);
void update_pv(SearchStack ss[], int ply);
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
bool connected_moves(const Position &pos, Move m1, Move m2);
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
bool singleReply, bool mateThreat);
bool ok_to_do_nullmove(const Position &pos);
bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d);
bool fail_high_ply_1();
int current_search_time();
int nps();
void poll();
void ponderhit();
void print_current_line(SearchStack ss[], int ply, int threadID);
void wait_for_stop_or_ponderhit();
void idle_loop(int threadID, SplitPoint *waitSp);
void init_split_point_stack();
void destroy_split_point_stack();
bool thread_should_stop(int threadID);
bool thread_is_available(int slave, int master);
bool idle_thread_exists(int master);
bool split(const Position &pos, SearchStack *ss, int ply,
Value *alpha, Value *beta, Value *bestValue, Depth depth,
int *moves, MovePicker *mp, Bitboard dcCandidates, int master,
bool pvNode);
void wake_sleeping_threads();
#if !defined(_MSC_VER)
void *init_thread(void *threadID);
#else
DWORD WINAPI init_thread(LPVOID threadID);
#endif
}
////
//// Global variables
////
// The main transposition table
TranspositionTable TT = TranspositionTable(TTDefaultSize);
// Number of active threads:
int ActiveThreads = 1;
// Locks. In principle, there is no need for IOLock to be a global variable,
// but it could turn out to be useful for debugging.
Lock IOLock;
History H; // Should be made local?
////
//// Functions
////
/// think() is the external interface to Glaurung's search, and is called when
/// the program receives the UCI 'go' command. It initializes various
/// search-related global variables, and calls root_search()
void think(const Position &pos, bool infinite, bool ponder, int time,
int increment, int movesToGo, int maxDepth, int maxNodes,
int maxTime, Move searchMoves[]) {
// Look for a book move:
if(!infinite && !ponder && get_option_value_bool("OwnBook")) {
Move bookMove;
if(get_option_value_string("Book File") != OpeningBook.file_name()) {
OpeningBook.close();
OpeningBook.open("book.bin");
}
bookMove = OpeningBook.get_move(pos);
if(bookMove != MOVE_NONE) {
std::cout << "bestmove " << bookMove << std::endl;
return;
}
}
// Initialize global search variables:
Idle = false;
SearchStartTime = get_system_time();
BestRootMove = MOVE_NONE;
PonderMove = MOVE_NONE;
EasyMove = MOVE_NONE;
for(int i = 0; i < THREAD_MAX; i++) {
Threads[i].nodes = 0ULL;
Threads[i].failHighPly1 = false;
}
NodesSincePoll = 0;
InfiniteSearch = infinite;
PonderSearch = ponder;
StopOnPonderhit = false;
AbortSearch = false;
Quit = false;
FailHigh = false;
Problem = false;
ExactMaxTime = maxTime;
// Read UCI option values:
TT.set_size(get_option_value_int("Hash"));
if(button_was_pressed("Clear Hash"))
TT.clear();
PonderingEnabled = get_option_value_int("Ponder");
MultiPV = get_option_value_int("MultiPV");
CheckExtension[1] = Depth(get_option_value_int("Check Extension (PV nodes)"));
CheckExtension[0] =
Depth(get_option_value_int("Check Extension (non-PV nodes)"));
SingleReplyExtension[1] = Depth(get_option_value_int("Single Reply Extension (PV nodes)"));
SingleReplyExtension[0] =
Depth(get_option_value_int("Single Reply Extension (non-PV nodes)"));
PawnPushTo7thExtension[1] =
Depth(get_option_value_int("Pawn Push to 7th Extension (PV nodes)"));
PawnPushTo7thExtension[0] =
Depth(get_option_value_int("Pawn Push to 7th Extension (non-PV nodes)"));
PassedPawnExtension[1] =
Depth(get_option_value_int("Passed Pawn Extension (PV nodes)"));
PassedPawnExtension[0] =
Depth(get_option_value_int("Passed Pawn Extension (non-PV nodes)"));
PawnEndgameExtension[1] =
Depth(get_option_value_int("Pawn Endgame Extension (PV nodes)"));
PawnEndgameExtension[0] =
Depth(get_option_value_int("Pawn Endgame Extension (non-PV nodes)"));
MateThreatExtension[1] =
Depth(get_option_value_int("Mate Threat Extension (PV nodes)"));
MateThreatExtension[0] =
Depth(get_option_value_int("Mate Threat Extension (non-PV nodes)"));
LMRPVMoves = get_option_value_int("Full Depth Moves (PV nodes)") + 1;
LMRNonPVMoves = get_option_value_int("Full Depth Moves (non-PV nodes)") + 1;
ThreatDepth = get_option_value_int("Threat Depth") * OnePly;
SelectiveDepth = get_option_value_int("Selective Plies") * OnePly;
Chess960 = get_option_value_bool("UCI_Chess960");
ShowCurrentLine = get_option_value_bool("UCI_ShowCurrLine");
UseLogFile = get_option_value_bool("Use Search Log");
if(UseLogFile)
LogFile.open(get_option_value_string("Search Log Filename").c_str(),
std::ios::out | std::ios::app);
UseQSearchFutilityPruning =
get_option_value_bool("Futility Pruning (Quiescence Search)");
UseFutilityPruning =
get_option_value_bool("Futility Pruning (Main Search)");
FutilityMargin0 =
value_from_centipawns(get_option_value_int("Futility Margin 0"));
FutilityMargin1 =
value_from_centipawns(get_option_value_int("Futility Margin 1"));
FutilityMargin2 =
value_from_centipawns(get_option_value_int("Futility Margin 2"));
RazorDepth = (get_option_value_int("Maximum Razoring Depth") + 1) * OnePly;
RazorMargin2 = value_from_centipawns(get_option_value_int("Razoring Margin"));
RazorMargin1 = RazorMargin2 / 4;
MinimumSplitDepth = get_option_value_int("Minimum Split Depth") * OnePly;
MaxThreadsPerSplitPoint =
get_option_value_int("Maximum Number of Threads per Split Point");
read_weights(pos.side_to_move());
int newActiveThreads = get_option_value_int("Threads");
if(newActiveThreads != ActiveThreads) {
ActiveThreads = newActiveThreads;
init_eval(ActiveThreads);
}
// Write information to search log file:
if(UseLogFile) {
LogFile << "Searching: " << pos.to_fen() << '\n';
LogFile << "infinite: " << infinite << " ponder: " << ponder
<< " time: " << time << " increment: " << increment
<< " moves to go: " << movesToGo << '\n';
}
// Wake up sleeping threads:
wake_sleeping_threads();
for(int i = 1; i < ActiveThreads; i++)
assert(thread_is_available(i, 0));
// Set thinking time:
if(!movesToGo) { // Sudden death time control
if(increment) {
MaxSearchTime = time / 30 + increment;
AbsoluteMaxSearchTime = Max(time / 4, increment - 100);
}
else { // Blitz game without increment
MaxSearchTime = time / 40;
AbsoluteMaxSearchTime = time / 8;
}
}
else { // (x moves) / (y minutes)
if(movesToGo == 1) {
MaxSearchTime = time / 2;
AbsoluteMaxSearchTime = Min(time / 2, time - 500);
}
else {
MaxSearchTime = time / Min(movesToGo, 20);
AbsoluteMaxSearchTime = Min((4 * time) / movesToGo, time / 3);
}
}
if(PonderingEnabled) {
MaxSearchTime += MaxSearchTime / 4;
MaxSearchTime = Min(MaxSearchTime, AbsoluteMaxSearchTime);
}
// Fixed depth or fixed number of nodes?
MaxDepth = maxDepth;
if(MaxDepth)
InfiniteSearch = true; // HACK
MaxNodes = maxNodes;
if(MaxNodes) {
NodesBetweenPolls = Min(MaxNodes, 30000);
InfiniteSearch = true; // HACK
}
else
NodesBetweenPolls = 30000;
// We're ready to start thinking. Call the iterative deepening loop
// function:
id_loop(pos, searchMoves);
if(UseLogFile)
LogFile.close();
if(Quit) {
OpeningBook.close();
stop_threads();
quit_eval();
exit(0);
}
Idle = true;
}
/// init_threads() is called during startup. It launches all helper threads,
/// and initializes the split point stack and the global locks and condition
/// objects.
void init_threads() {
volatile int i;
#if !defined(_MSC_VER)
pthread_t pthread[1];
#endif
for(i = 0; i < THREAD_MAX; i++)
Threads[i].activeSplitPoints = 0;
// Initialize global locks:
lock_init(&MPLock, NULL);
lock_init(&IOLock, NULL);
init_split_point_stack();
#if !defined(_MSC_VER)
pthread_mutex_init(&WaitLock, NULL);
pthread_cond_init(&WaitCond, NULL);
#else
for(i = 0; i < THREAD_MAX; i++)
SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
#endif
// All threads except the main thread should be initialized to idle state:
for(i = 1; i < THREAD_MAX; i++) {
Threads[i].stop = false;
Threads[i].workIsWaiting = false;
Threads[i].idle = true;
Threads[i].running = false;
}
// Launch the helper threads:
for(i = 1; i < THREAD_MAX; i++) {
#if !defined(_MSC_VER)
pthread_create(pthread, NULL, init_thread, (void*)(&i));
#else
{
DWORD iID[1];
CreateThread(NULL, 0, init_thread, (LPVOID)(&i), 0, iID);
}
#endif
// Wait until the thread has finished launching:
while(!Threads[i].running);
}
}
/// stop_threads() is called when the program exits. It makes all the
/// helper threads exit cleanly.
void stop_threads() {
ActiveThreads = THREAD_MAX; // HACK
Idle = false; // HACK
wake_sleeping_threads();
AllThreadsShouldExit = true;
for(int i = 1; i < THREAD_MAX; i++) {
Threads[i].stop = true;
while(Threads[i].running);
}
destroy_split_point_stack();
}
/// nodes_searched() returns the total number of nodes searched so far in
/// the current search.
int64_t nodes_searched() {
int64_t result = 0ULL;
for(int i = 0; i < ActiveThreads; i++)
result += Threads[i].nodes;
return result;
}
namespace {
// id_loop() is the main iterative deepening loop. It calls root_search
// repeatedly with increasing depth until the allocated thinking time has
// been consumed, the user stops the search, or the maximum search depth is
// reached.
void id_loop(const Position &pos, Move searchMoves[]) {
Position p(pos);
RootMoveList rml(p, searchMoves);
SearchStack ss[PLY_MAX_PLUS_2];
// Initialize
TT.new_search();
H.clear();
init_search_stack(ss);
ValueByIteration[0] = Value(0);
ValueByIteration[1] = rml.get_move_score(0);
Iteration = 1;
EasyMove = rml.scan_for_easy_move();
// Iterative deepening loop
while(!AbortSearch && Iteration < PLY_MAX) {
// Initialize iteration
rml.sort();
Iteration++;
BestMoveChangesByIteration[Iteration] = 0;
if(Iteration <= 5)
ExtraSearchTime = 0;
std::cout << "info depth " << Iteration << std::endl;
// Search to the current depth
ValueByIteration[Iteration] = root_search(p, ss, rml);
// Erase the easy move if it differs from the new best move
if(ss[0].pv[0] != EasyMove)
EasyMove = MOVE_NONE;
Problem = false;
if(!InfiniteSearch) {
// Time to stop?
bool stopSearch = false;
// Stop search early if there is only a single legal move:
if(Iteration >= 6 && rml.move_count() == 1)
stopSearch = true;
// Stop search early when the last two iterations returned a mate
// score:
if(Iteration >= 6
&& abs(ValueByIteration[Iteration]) >= abs(VALUE_MATE) - 100
&& abs(ValueByIteration[Iteration-1]) >= abs(VALUE_MATE) - 100)
stopSearch = true;
// Stop search early if one move seems to be much better than the
// rest:
int64_t nodes = nodes_searched();
if(Iteration >= 8 && EasyMove == ss[0].pv[0] &&
((rml.get_move_cumulative_nodes(0) > (nodes * 85) / 100 &&
current_search_time() > MaxSearchTime / 16) ||
(rml.get_move_cumulative_nodes(0) > (nodes * 98) / 100 &&
current_search_time() > MaxSearchTime / 32)))
stopSearch = true;
// Add some extra time if the best move has changed during the last
// two iterations:
if(Iteration > 5 && Iteration <= 50)
ExtraSearchTime =
BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2) +
BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
// Stop search if most of MaxSearchTime is consumed at the end of the
// iteration. We probably don't have enough time to search the first
// move at the next iteration anyway.
if(current_search_time() > ((MaxSearchTime + ExtraSearchTime)*80) / 128)
stopSearch = true;
if(stopSearch) {
if(!PonderSearch)
break;
else
StopOnPonderhit = true;
}
}
// Write PV to transposition table, in case the relevant entries have
// been overwritten during the search:
TT.insert_pv(p, ss[0].pv);
if(MaxDepth && Iteration >= MaxDepth)
break;
}
rml.sort();
// If we are pondering, we shouldn't print the best move before we
// are told to do so
if(PonderSearch)
wait_for_stop_or_ponderhit();
else
// Print final search statistics
std::cout << "info nodes " << nodes_searched() << " nps " << nps()
<< " time " << current_search_time()
<< " hashfull " << TT.full() << std::endl;
// Print the best move and the ponder move to the standard output:
std::cout << "bestmove " << ss[0].pv[0];
if(ss[0].pv[1] != MOVE_NONE)
std::cout << " ponder " << ss[0].pv[1];
std::cout << std::endl;
if(UseLogFile) {
UndoInfo u;
LogFile << "Nodes: " << nodes_searched() << '\n';
LogFile << "Nodes/second: " << nps() << '\n';
LogFile << "Best move: " << move_to_san(p, ss[0].pv[0]) << '\n';
p.do_move(ss[0].pv[0], u);
LogFile << "Ponder move: " << move_to_san(p, ss[0].pv[1]) << '\n';
LogFile << std::endl;
}
}
// root_search() is the function which searches the root node. It is
// similar to search_pv except that it uses a different move ordering
// scheme (perhaps we should try to use this at internal PV nodes, too?)
// and prints some information to the standard output.
Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml) {
Value alpha = -VALUE_INFINITE, beta = VALUE_INFINITE, value;
Bitboard dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
// Loop through all the moves in the root move list:
for(int i = 0; i < rml.move_count() && !AbortSearch; i++) {
int64_t nodes;
Move move;
UndoInfo u;
Depth ext, newDepth;
RootMoveNumber = i + 1;
FailHigh = false;
// Remember the node count before the move is searched. The node counts
// are used to sort the root moves at the next iteration.
nodes = nodes_searched();
// Pick the next root move, and print the move and the move number to
// the standard output:
move = ss[0].currentMove = rml.get_move(i);
if(current_search_time() >= 1000)
std::cout << "info currmove " << move
<< " currmovenumber " << i + 1 << std::endl;
// Decide search depth for this move:
ext = extension(pos, move, true, pos.move_is_check(move), false, false);
newDepth = (Iteration-2)*OnePly + ext + InitialDepth;
// Make the move, and search it.
pos.do_move(move, u, dcCandidates);
if(i < MultiPV) {
value = -search_pv(pos, ss, -beta, VALUE_INFINITE, newDepth, 1, 0);
// If the value has dropped a lot compared to the last iteration,
// set the boolean variable Problem to true. This variable is used
// for time managment: When Problem is true, we try to complete the
// current iteration before playing a move.
Problem = (Iteration >= 2 &&
value <= ValueByIteration[Iteration-1] - ProblemMargin);
if(Problem && StopOnPonderhit)
StopOnPonderhit = false;
}
else {
value = -search(pos, ss, -alpha, newDepth, 1, true, 0);
if(value > alpha) {
// Fail high! Set the boolean variable FailHigh to true, and
// re-search the move with a big window. The variable FailHigh is
// used for time managment: We try to avoid aborting the search
// prematurely during a fail high research.
FailHigh = true;
value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0);
}
}
pos.undo_move(move, u);
// Finished searching the move. If AbortSearch is true, the search
// was aborted because the user interrupted the search or because we
// ran out of time. In this case, the return value of the search cannot
// be trusted, and we break out of the loop without updating the best
// move and/or PV:
if(AbortSearch)
break;
// Remember the node count for this move. The node counts are used to
// sort the root moves at the next iteration.
rml.set_move_nodes(i, nodes_searched() - nodes);
assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
if(value <= alpha && i >= MultiPV)
rml.set_move_score(i, -VALUE_INFINITE);
else {
// New best move!
// Update PV:
rml.set_move_score(i, value);
update_pv(ss, 0);
rml.set_move_pv(i, ss[0].pv);
if(MultiPV == 1) {
// We record how often the best move has been changed in each
// iteration. This information is used for time managment: When
// the best move changes frequently, we allocate some more time.
if(i > 0)
BestMoveChangesByIteration[Iteration]++;
// Print search information to the standard output:
std::cout << "info depth " << Iteration
<< " score " << value_to_string(value)
<< " time " << current_search_time()
<< " nodes " << nodes_searched()
<< " nps " << nps()
<< " pv ";
for(int j = 0; ss[0].pv[j] != MOVE_NONE && j < PLY_MAX; j++)
std::cout << ss[0].pv[j] << " ";
std::cout << std::endl;
if(UseLogFile)
LogFile << pretty_pv(pos, current_search_time(), Iteration,
nodes_searched(), value, ss[0].pv)
<< std::endl;
alpha = value;
// Reset the global variable Problem to false if the value isn't too
// far below the final value from the last iteration.
if(value > ValueByIteration[Iteration - 1] - NoProblemMargin)
Problem = false;
}
else { // MultiPV > 1
rml.sort_multipv(i);
for(int j = 0; j < Min(MultiPV, rml.move_count()); j++) {
int k;
std::cout << "info multipv " << j + 1
<< " score " << value_to_string(rml.get_move_score(j))
<< " depth " << ((j <= i)? Iteration : Iteration - 1)
<< " time " << current_search_time()
<< " nodes " << nodes_searched()
<< " nps " << nps()
<< " pv ";
for(k = 0; rml.get_move_pv(j, k) != MOVE_NONE && k < PLY_MAX; k++)
std::cout << rml.get_move_pv(j, k) << " ";
std::cout << std::endl;
}
alpha = rml.get_move_score(Min(i, MultiPV-1));
}
}
}
return alpha;
}
// search_pv() is the main search function for PV nodes.
Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta,
Depth depth, int ply, int threadID) {
assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
assert(beta > alpha && beta <= VALUE_INFINITE);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
EvalInfo ei;
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
Value oldAlpha = alpha;
if(AbortSearch || thread_should_stop(threadID))
return Value(0);
if(depth < OnePly)
return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID);
init_node(pos, ss, ply, threadID);
if(pos.is_draw())
return VALUE_DRAW;
if(ply >= PLY_MAX - 1)
return evaluate(pos, ei, threadID);
// Mate distance pruning
alpha = Max(value_mated_in(ply), alpha);
beta = Min(value_mate_in(ply+1), beta);
if(alpha >= beta)
return alpha;
// Transposition table lookup. At PV nodes, we don't use the TT for
// pruning, but only for move ordering.
Value ttValue;
Depth ttDepth;
Move ttMove = MOVE_NONE;
ValueType ttValueType;
TT.retrieve(pos, &ttValue, &ttDepth, &ttMove, &ttValueType);
// Internal iterative deepening.
if(UseIIDAtPVNodes && ttMove == MOVE_NONE && depth >= 5*OnePly) {
search_pv(pos, ss, alpha, beta, depth-2*OnePly, ply, threadID);
ttMove = ss[ply].pv[ply];
}
// Initialize a MovePicker object for the current position, and prepare
// to search all moves:
MovePicker mp = MovePicker(pos, true, ttMove, ss[ply].mateKiller,
ss[ply].killer1, ss[ply].killer2, depth);
Move move, movesSearched[256];
int moveCount = 0;
Value value, bestValue = -VALUE_INFINITE;
Bitboard dcCandidates = mp.discovered_check_candidates();
bool mateThreat =
MateThreatExtension[1] > Depth(0)
&& pos.has_mate_threat(opposite_color(pos.side_to_move()));
// Loop through all legal moves until no moves remain or a beta cutoff
// occurs.
while(alpha < beta && !thread_should_stop(threadID)
&& (move = mp.get_next_move()) != MOVE_NONE) {
UndoInfo u;
Depth ext, newDepth;
bool singleReply = (pos.is_check() && mp.number_of_moves() == 1);
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
movesSearched[moveCount++] = ss[ply].currentMove = move;
ss[ply].currentMoveCaptureValue = move_is_ep(move)?
PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
// Decide the new search depth.
ext = extension(pos, move, true, moveIsCheck, singleReply, mateThreat);
newDepth = depth - OnePly + ext;
// Make and search the move.
pos.do_move(move, u, dcCandidates);
if(moveCount == 1)
value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID);
else {
if(depth >= 2*OnePly && ext == Depth(0) && moveCount >= LMRPVMoves
&& !moveIsCapture && !move_promotion(move)
&& !moveIsPassedPawnPush && !move_is_castle(move)
&& move != ss[ply].killer1 && move != ss[ply].killer2) {
ss[ply].reduction = OnePly;
value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true,
threadID);
}
else value = alpha + 1;
if(value > alpha) {
ss[ply].reduction = Depth(0);
value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID);
if(value > alpha && value < beta) {
if(ply == 1 && RootMoveNumber == 1)
// When the search fails high at ply 1 while searching the first
// move at the root, set the flag failHighPly1. This is used for
// time managment: We don't want to stop the search early in
// such cases, because resolving the fail high at ply 1 could
// result in a big drop in score at the root.
Threads[threadID].failHighPly1 = true;
value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1,
threadID);
Threads[threadID].failHighPly1 = false;
}
}
}
pos.undo_move(move, u);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
// New best move?
if(value > bestValue) {
bestValue = value;
if(value > alpha) {
alpha = value;
update_pv(ss, ply);
if(value == value_mate_in(ply + 1))
ss[ply].mateKiller = move;
}
// If we are at ply 1, and we are searching the first root move at
// ply 0, set the 'Problem' variable if the score has dropped a lot
// (from the computer's point of view) since the previous iteration:
if(ply == 1 && Iteration >= 2 &&
-value <= ValueByIteration[Iteration-1] - ProblemMargin)
Problem = true;
}
// Split?
if(ActiveThreads > 1 && bestValue < beta && depth >= MinimumSplitDepth
&& Iteration <= 99 && idle_thread_exists(threadID)
&& !AbortSearch && !thread_should_stop(threadID)
&& split(pos, ss, ply, &alpha, &beta, &bestValue, depth,
&moveCount, &mp, dcCandidates, threadID, true))
break;
}
// All legal moves have been searched. A special case: If there were
// no legal moves, it must be mate or stalemate:
if(moveCount == 0) {
if(pos.is_check())
return value_mated_in(ply);
else
return VALUE_DRAW;
}
// If the search is not aborted, update the transposition table,
// history counters, and killer moves. This code is somewhat messy,
// and definitely needs to be cleaned up. FIXME
if(!AbortSearch && !thread_should_stop(threadID)) {
if(bestValue <= oldAlpha)
TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE,
VALUE_TYPE_UPPER);
else if(bestValue >= beta) {
Move m = ss[ply].pv[ply];
if(pos.square_is_empty(move_to(m)) && !move_promotion(m) &&
!move_is_ep(m)) {
for(int i = 0; i < moveCount - 1; i++)
if(pos.square_is_empty(move_to(movesSearched[i]))
&& !move_promotion(movesSearched[i])
&& !move_is_ep(movesSearched[i]))
H.failure(pos.piece_on(move_from(movesSearched[i])),
movesSearched[i]);
H.success(pos.piece_on(move_from(m)), m, depth);
if(m != ss[ply].killer1) {
ss[ply].killer2 = ss[ply].killer1;
ss[ply].killer1 = m;
}
}
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
}
else
TT.store(pos, value_to_tt(bestValue, ply), depth, ss[ply].pv[ply],
VALUE_TYPE_EXACT);
}
return bestValue;
}
// search() is the search function for zero-width nodes.
Value search(Position &pos, SearchStack ss[], Value beta, Depth depth,
int ply, bool allowNullmove, int threadID) {
assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
EvalInfo ei;
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
if(AbortSearch || thread_should_stop(threadID))
return Value(0);
if(depth < OnePly)
return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
init_node(pos, ss, ply, threadID);
if(pos.is_draw())
return VALUE_DRAW;
if(ply >= PLY_MAX - 1)
return evaluate(pos, ei, threadID);
// Mate distance pruning
if(value_mated_in(ply) >= beta)
return beta;
if(value_mate_in(ply+1) < beta)
return beta-1;
// Transposition table lookup
bool ttFound;
Value ttValue;
Depth ttDepth;
Move ttMove = MOVE_NONE;
ValueType ttValueType;
ttFound = TT.retrieve(pos, &ttValue, &ttDepth, &ttMove, &ttValueType);
if(ttFound) {
ttValue = value_from_tt(ttValue, ply);
if(ttDepth >= depth
|| ttValue >= Max(value_mate_in(100), beta)
|| ttValue < Min(value_mated_in(100), beta)) {
if((is_lower_bound(ttValueType) && ttValue >= beta) ||
(is_upper_bound(ttValueType) && ttValue < beta)) {
ss[ply].currentMove = ttMove;
return ttValue;
}
}
}
Value approximateEval = quick_evaluate(pos);
bool mateThreat = false;
// Null move search
if(!pos.is_check() && allowNullmove && ok_to_do_nullmove(pos)
&& approximateEval >= beta - NullMoveMargin) {
UndoInfo u;
Value nullValue;
ss[ply].currentMove = MOVE_NULL;
pos.do_null_move(u);
nullValue = -search(pos, ss, -(beta-1), depth-4*OnePly, ply+1, false,
threadID);
pos.undo_null_move(u);
if(nullValue >= beta) {
if(depth >= 6 * OnePly) { // Do zugzwang verification search
Value v = search(pos, ss, beta, depth-5*OnePly, ply, false, threadID);
if(v >= beta)
return beta;
}
else
return beta;
}
else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
if(nullValue == value_mated_in(ply+2))
mateThreat = true;
ss[ply].threatMove = ss[ply+1].currentMove;
if(depth < ThreatDepth && ss[ply-1].reduction &&
connected_moves(pos, ss[ply-1].currentMove, ss[ply].threatMove))
return beta - 1;
}
}
// Razoring:
else if((depth < RazorDepth && approximateEval < beta - RazorMargin2) ||
(depth < 2*OnePly && approximateEval < beta - RazorMargin1)) {
Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
if(v < beta)
return v;
}
// Internal iterative deepening
if(UseIIDAtNonPVNodes && ttMove == MOVE_NONE && depth >= 8*OnePly &&
evaluate(pos, ei, threadID) >= beta - IIDMargin) {
search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
ttMove = ss[ply].pv[ply];
}
// Initialize a MovePicker object for the current position, and prepare
// to search all moves:
MovePicker mp = MovePicker(pos, false, ttMove, ss[ply].mateKiller,
ss[ply].killer1, ss[ply].killer2, depth);
Move move, movesSearched[256];
int moveCount = 0;
Value value, bestValue = -VALUE_INFINITE, futilityValue = VALUE_NONE;
Bitboard dcCandidates = mp.discovered_check_candidates();
bool isCheck = pos.is_check();
bool useFutilityPruning =
UseFutilityPruning && depth < SelectiveDepth && !isCheck;
// Loop through all legal moves until no moves remain or a beta cutoff
// occurs.
while(bestValue < beta && !thread_should_stop(threadID)
&& (move = mp.get_next_move()) != MOVE_NONE) {
UndoInfo u;
Depth ext, newDepth;
bool singleReply = (isCheck && mp.number_of_moves() == 1);
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
movesSearched[moveCount++] = ss[ply].currentMove = move;
// Decide the new search depth.
ext = extension(pos, move, false, moveIsCheck, singleReply, mateThreat);
newDepth = depth - OnePly + ext;
// Futility pruning
if(useFutilityPruning && ext == Depth(0) && !moveIsCapture &&
!moveIsPassedPawnPush && !move_promotion(move)) {
if(moveCount >= 2 + int(depth)
&& ok_to_prune(pos, move, ss[ply].threatMove, depth))
continue;
if(depth < 6 * OnePly && approximateEval < beta) {
if(futilityValue == VALUE_NONE)
futilityValue = evaluate(pos, ei, threadID)
+ ((depth < 2 * OnePly)?
FutilityMargin1 :
FutilityMargin2 + (depth - 2*OnePly) * 32);
if(futilityValue < beta) {
if(futilityValue > bestValue)
bestValue = futilityValue;
continue;
}
}
}
// Make and search the move.
pos.do_move(move, u, dcCandidates);
if(depth >= 2*OnePly && ext == Depth(0) && moveCount >= LMRNonPVMoves
&& !moveIsCapture && !move_promotion(move) && !moveIsPassedPawnPush
&& !move_is_castle(move)
&& move != ss[ply].killer1 && move != ss[ply].killer2) {
ss[ply].reduction = OnePly;
value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true,
threadID);
}
else
value = beta;
if(value >= beta) {
ss[ply].reduction = Depth(0);
value = -search(pos, ss, -(beta-1), newDepth, ply+1, true, threadID);
}
pos.undo_move(move, u);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
// New best move?
if(value > bestValue) {
bestValue = value;
if(value >= beta)
update_pv(ss, ply);
if(value == value_mate_in(ply + 1))
ss[ply].mateKiller = move;
}
// Split?
if(ActiveThreads > 1 && bestValue < beta && depth >= MinimumSplitDepth
&& Iteration <= 99 && idle_thread_exists(threadID)
&& !AbortSearch && !thread_should_stop(threadID)
&& split(pos, ss, ply, &beta, &beta, &bestValue, depth, &moveCount,
&mp, dcCandidates, threadID, false))
break;
}
// All legal moves have been searched. A special case: If there were
// no legal moves, it must be mate or stalemate:
if(moveCount == 0) {
if(pos.is_check())
return value_mated_in(ply);
else
return VALUE_DRAW;
}
// If the search is not aborted, update the transposition table,
// history counters, and killer moves. This code is somewhat messy,
// and definitely needs to be cleaned up. FIXME
if(!AbortSearch && !thread_should_stop(threadID)) {
if(bestValue < beta)
TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE,
VALUE_TYPE_UPPER);
else {
Move m = ss[ply].pv[ply];
if(pos.square_is_empty(move_to(m)) && !move_promotion(m) &&
!move_is_ep(m)) {
for(int i = 0; i < moveCount - 1; i++)
if(pos.square_is_empty(move_to(movesSearched[i]))
&& !move_promotion(movesSearched[i])
&& !move_is_ep(movesSearched[i]))
H.failure(pos.piece_on(move_from(movesSearched[i])),
movesSearched[i]);
H.success(pos.piece_on(move_from(m)), m, depth);
if(m != ss[ply].killer1) {
ss[ply].killer2 = ss[ply].killer1;
ss[ply].killer1 = m;
}
}
TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
}
}
return bestValue;
}
// qsearch() is the quiescence search function, which is called by the main
// search function when the remaining depth is zero (or, to be more precise,
// less than OnePly).
Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta,
Depth depth, int ply, int threadID) {
Value staticValue, bestValue, value;
EvalInfo ei;
assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
assert(depth <= 0);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
if(AbortSearch || thread_should_stop(threadID))
return Value(0);
init_node(pos, ss, ply, threadID);
if(pos.is_draw())
return VALUE_DRAW;
// Evaluate the position statically:
staticValue = evaluate(pos, ei, threadID);
if(ply == PLY_MAX - 1) return staticValue;
// Initialize "stand pat score", and return it immediately if it is
// at least beta.
if(pos.is_check())
bestValue = -VALUE_INFINITE;
else {
bestValue = staticValue;
if(bestValue >= beta)
return bestValue;
if(bestValue > alpha)
alpha = bestValue;
}
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth == 0) will be generated.
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, depth);
Move move;
int moveCount = 0;
Bitboard dcCandidates = mp.discovered_check_candidates();
bool isCheck = pos.is_check();
// Loop through the moves until no moves remain or a beta cutoff
// occurs.
while(alpha < beta && ((move = mp.get_next_move()) != MOVE_NONE)) {
UndoInfo u;
bool moveIsCheck = pos.move_is_check(move, dcCandidates);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
moveCount++;
ss[ply].currentMove = move;
// Futility pruning
if(UseQSearchFutilityPruning && !isCheck && !moveIsCheck &&
!move_promotion(move) && !moveIsPassedPawnPush &&
beta - alpha == 1 &&
pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame) {
Value futilityValue =
staticValue
+ Max(pos.midgame_value_of_piece_on(move_to(move)),
pos.endgame_value_of_piece_on(move_to(move)))
+ FutilityMargin0
+ ei.futilityMargin;
if(futilityValue < alpha) {
if(futilityValue > bestValue)
bestValue = futilityValue;
continue;
}
}
// Don't search captures and checks with negative SEE values.
if(!isCheck && !move_promotion(move) &&
pos.midgame_value_of_piece_on(move_from(move)) >
pos.midgame_value_of_piece_on(move_to(move)) &&
pos.see(move) < 0)
continue;
// Make and search the move.
pos.do_move(move, u, dcCandidates);
value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID);
pos.undo_move(move, u);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
// New best move?
if(value > bestValue) {
bestValue = value;
if(value > alpha) {
alpha = value;
update_pv(ss, ply);
}
}
}
// All legal moves have been searched. A special case: If we're in check
// and no legal moves were found, it is checkmate:
if(pos.is_check() && moveCount == 0) // Mate!
return value_mated_in(ply);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
return bestValue;
}
// sp_search() is used to search from a split point. This function is called
// by each thread working at the split point. It is similar to the normal
// search() function, but simpler. Because we have already probed the hash
// table, done a null move search, and searched the first move before
// splitting, we don't have to repeat all this work in sp_search(). We
// also don't need to store anything to the hash table here: This is taken
// care of after we return from the split point.
void sp_search(SplitPoint *sp, int threadID) {
assert(threadID >= 0 && threadID < ActiveThreads);
assert(ActiveThreads > 1);
Position pos = Position(sp->pos);
SearchStack *ss = sp->sstack[threadID];
Value value;
Move move;
int moveCount = sp->moves;
bool isCheck = pos.is_check();
bool useFutilityPruning =
UseFutilityPruning && sp->depth < SelectiveDepth && !isCheck;
while(sp->bestValue < sp->beta && !thread_should_stop(threadID)
&& (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) {
UndoInfo u;
Depth ext, newDepth;
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
lock_grab(&(sp->lock));
sp->moves++;
moveCount = sp->moves;
lock_release(&(sp->lock));
ss[sp->ply].currentMove = move;
// Decide the new search depth.
ext = extension(pos, move, false, moveIsCheck, false, false);
newDepth = sp->depth - OnePly + ext;
// Prune?
if(useFutilityPruning && ext == Depth(0) && !moveIsCapture
&& !moveIsPassedPawnPush && !move_promotion(move)
&& moveCount >= 2 + int(sp->depth)
&& ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
continue;
// Make and search the move.
pos.do_move(move, u, sp->dcCandidates);
if(ext == Depth(0) && moveCount >= LMRNonPVMoves
&& !moveIsCapture && !move_promotion(move) && !moveIsPassedPawnPush
&& !move_is_castle(move)
&& move != ss[sp->ply].killer1 && move != ss[sp->ply].killer2) {
ss[sp->ply].reduction = OnePly;
value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1,
true, threadID);
}
else
value = sp->beta;
if(value >= sp->beta) {
ss[sp->ply].reduction = Depth(0);
value = -search(pos, ss, -(sp->beta - 1), newDepth, sp->ply+1, true,
threadID);
}
pos.undo_move(move, u);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
if(thread_should_stop(threadID))
break;
// New best move?
lock_grab(&(sp->lock));
if(value > sp->bestValue && !thread_should_stop(threadID)) {
sp->bestValue = value;
if(sp->bestValue >= sp->beta) {
sp_update_pv(sp->parentSstack, ss, sp->ply);
for(int i = 0; i < ActiveThreads; i++)
if(i != threadID && (i == sp->master || sp->slaves[i]))
Threads[i].stop = true;
sp->finished = true;
}
}
lock_release(&(sp->lock));
}
lock_grab(&(sp->lock));
// If this is the master thread and we have been asked to stop because of
// a beta cutoff higher up in the tree, stop all slave threads:
if(sp->master == threadID && thread_should_stop(threadID))
for(int i = 0; i < ActiveThreads; i++)
if(sp->slaves[i])
Threads[i].stop = true;
sp->cpus--;
sp->slaves[threadID] = 0;
lock_release(&(sp->lock));
}
// sp_search_pv() is used to search from a PV split point. This function
// is called by each thread working at the split point. It is similar to
// the normal search_pv() function, but simpler. Because we have already
// probed the hash table and searched the first move before splitting, we
// don't have to repeat all this work in sp_search_pv(). We also don't
// need to store anything to the hash table here: This is taken care of
// after we return from the split point.
void sp_search_pv(SplitPoint *sp, int threadID) {
assert(threadID >= 0 && threadID < ActiveThreads);
assert(ActiveThreads > 1);
Position pos = Position(sp->pos);
SearchStack *ss = sp->sstack[threadID];
Value value;
Move move;
int moveCount = sp->moves;
while(sp->alpha < sp->beta && !thread_should_stop(threadID)
&& (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) {
UndoInfo u;
Depth ext, newDepth;
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
bool moveIsCapture = pos.move_is_capture(move);
bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
assert(move_is_ok(move));
ss[sp->ply].currentMoveCaptureValue = move_is_ep(move)?
PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
lock_grab(&(sp->lock));
sp->moves++;
moveCount = sp->moves;
lock_release(&(sp->lock));
ss[sp->ply].currentMove = move;
// Decide the new search depth.
ext = extension(pos, move, true, moveIsCheck, false, false);
newDepth = sp->depth - OnePly + ext;
// Make and search the move.
pos.do_move(move, u, sp->dcCandidates);
if(ext == Depth(0) && moveCount >= LMRPVMoves && !moveIsCapture
&& !move_promotion(move) && !moveIsPassedPawnPush
&& !move_is_castle(move)
&& move != ss[sp->ply].killer1 && move != ss[sp->ply].killer2) {
ss[sp->ply].reduction = OnePly;
value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1,
true, threadID);
}
else
value = sp->alpha + 1;
if(value > sp->alpha) {
ss[sp->ply].reduction = Depth(0);
value = -search(pos, ss, -sp->alpha, newDepth, sp->ply+1, true,
threadID);
if(value > sp->alpha && value < sp->beta) {
if(sp->ply == 1 && RootMoveNumber == 1)
// When the search fails high at ply 1 while searching the first
// move at the root, set the flag failHighPly1. This is used for
// time managment: We don't want to stop the search early in
// such cases, because resolving the fail high at ply 1 could
// result in a big drop in score at the root.
Threads[threadID].failHighPly1 = true;
value = -search_pv(pos, ss, -sp->beta, -sp->alpha, newDepth,
sp->ply+1, threadID);
Threads[threadID].failHighPly1 = false;
}
}
pos.undo_move(move, u);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
if(thread_should_stop(threadID))
break;
// New best move?
lock_grab(&(sp->lock));
if(value > sp->bestValue && !thread_should_stop(threadID)) {
sp->bestValue = value;
if(value > sp->alpha) {
sp->alpha = value;
sp_update_pv(sp->parentSstack, ss, sp->ply);
if(value == value_mate_in(sp->ply + 1))
ss[sp->ply].mateKiller = move;
if(value >= sp->beta) {
for(int i = 0; i < ActiveThreads; i++)
if(i != threadID && (i == sp->master || sp->slaves[i]))
Threads[i].stop = true;
sp->finished = true;
}
}
// If we are at ply 1, and we are searching the first root move at
// ply 0, set the 'Problem' variable if the score has dropped a lot
// (from the computer's point of view) since the previous iteration:
if(Iteration >= 2 &&
-value <= ValueByIteration[Iteration-1] - ProblemMargin)
Problem = true;
}
lock_release(&(sp->lock));
}
lock_grab(&(sp->lock));
// If this is the master thread and we have been asked to stop because of
// a beta cutoff higher up in the tree, stop all slave threads:
if(sp->master == threadID && thread_should_stop(threadID))
for(int i = 0; i < ActiveThreads; i++)
if(sp->slaves[i])
Threads[i].stop = true;
sp->cpus--;
sp->slaves[threadID] = 0;
lock_release(&(sp->lock));
}
/// The RootMove class
// Constructor
RootMove::RootMove() {
nodes = cumulativeNodes = 0ULL;
}
/// The RootMoveList class
// Constructor
RootMoveList::RootMoveList(Position &pos, Move searchMoves[]) {
MoveStack mlist[MaxRootMoves];
bool includeAllMoves = (searchMoves[0] == MOVE_NONE);
int i, j = 0, k;
// Generate all legal moves
count = generate_legal_moves(pos, mlist);
// Add each move to the moves[] array
for(i = 0; i < count; i++) {
UndoInfo u;
SearchStack ss[PLY_MAX_PLUS_2];
bool includeMove;
if(includeAllMoves)
includeMove = true;
else {
includeMove = false;
for(k = 0; searchMoves[k] != MOVE_NONE; k++)
if(searchMoves[k] == mlist[i].move) {
includeMove = true;
break;
}
}
if(includeMove) {
moves[j].move = mlist[i].move;
moves[j].nodes = 0ULL;
pos.do_move(moves[j].move, u);
moves[j].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE,
Depth(0), 1, 0);
pos.undo_move(moves[j].move, u);
moves[j].pv[0] = moves[i].move;
moves[j].pv[1] = MOVE_NONE; // FIXME
j++;
}
}
count = j;
this->sort();
}
// Simple accessor methods for the RootMoveList class
Move RootMoveList::get_move(int moveNum) const {
return moves[moveNum].move;
}
Value RootMoveList::get_move_score(int moveNum) const {
return moves[moveNum].score;
}
void RootMoveList::set_move_score(int moveNum, Value score) {
moves[moveNum].score = score;
}
void RootMoveList::set_move_nodes(int moveNum, int64_t nodes) {
moves[moveNum].nodes = nodes;
moves[moveNum].cumulativeNodes += nodes;
}
void RootMoveList::set_move_pv(int moveNum, const Move pv[]) {
int j;
for(j = 0; pv[j] != MOVE_NONE; j++)
moves[moveNum].pv[j] = pv[j];
moves[moveNum].pv[j] = MOVE_NONE;
}
Move RootMoveList::get_move_pv(int moveNum, int i) const {
return moves[moveNum].pv[i];
}
int64_t RootMoveList::get_move_cumulative_nodes(int moveNum) {
return moves[moveNum].cumulativeNodes;
}
int RootMoveList::move_count() const {
return count;
}
// RootMoveList::scan_for_easy_move() is called at the end of the first
// iteration, and is used to detect an "easy move", i.e. a move which appears
// to be much bester than all the rest. If an easy move is found, the move
// is returned, otherwise the function returns MOVE_NONE. It is very
// important that this function is called at the right moment: The code
// assumes that the first iteration has been completed and the moves have
// been sorted.
Move RootMoveList::scan_for_easy_move() const {
Value bestMoveValue = this->get_move_score(0);
for(int i = 1; i < this->move_count(); i++)
if(this->get_move_score(i) >= bestMoveValue - EasyMoveMargin)
return MOVE_NONE;
return this->get_move(0);
}
// RootMoveList::sort() sorts the root move list at the beginning of a new
// iteration.
void RootMoveList::sort() {
for(int i = 1; i < count; i++) {
RootMove rm = moves[i];
int j;
for(j = i; j > 0 && compare_root_moves(moves[j-1], rm); j--)
moves[j] = moves[j-1];
moves[j] = rm;
}
}
// RootMoveList::sort_multipv() sorts the first few moves in the root move
// list by their scores and depths. It is used to order the different PVs
// correctly in MultiPV mode.
void RootMoveList::sort_multipv(int n) {
for(int i = 1; i <= n; i++) {
RootMove rm = moves[i];
int j;
for(j = i; j > 0 && moves[j-1].score < rm.score; j--)
moves[j] = moves[j-1];
moves[j] = rm;
}
}
// RootMoveList::compare_root_moves() is the comparison function used by
// RootMoveList::sort when sorting the moves. A move m1 is considered to
// be better than a move m2 if it has a higher score, or if the moves have
// equal score but m1 has the higher node count.
int RootMoveList::compare_root_moves(const RootMove &rm1,
const RootMove &rm2) {
if(rm1.score < rm2.score) return 1;
else if(rm1.score > rm2.score) return 0;
else if(rm1.nodes < rm2.nodes) return 1;
else if(rm1.nodes > rm2.nodes) return 0;
else return 1;
}
// init_search_stack() initializes a search stack at the beginning of a
// new search from the root.
void init_search_stack(SearchStack ss[]) {
for(int i = 0; i < 3; i++) {
ss[i].pv[i] = MOVE_NONE;
ss[i].pv[i+1] = MOVE_NONE;
ss[i].currentMove = MOVE_NONE;
ss[i].mateKiller = MOVE_NONE;
ss[i].killer1 = MOVE_NONE;
ss[i].killer2 = MOVE_NONE;
ss[i].threatMove = MOVE_NONE;
ss[i].reduction = Depth(0);
}
}
// init_node() is called at the beginning of all the search functions
// (search(), search_pv(), qsearch(), and so on) and initializes the search
// stack object corresponding to the current node. Once every
// NodesBetweenPolls nodes, init_node() also calls poll(), which polls
// for user input and checks whether it is time to stop the search.
void init_node(const Position &pos, SearchStack ss[], int ply, int threadID) {
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
Threads[threadID].nodes++;
if(threadID == 0) {
NodesSincePoll++;
if(NodesSincePoll >= NodesBetweenPolls) {
poll();
NodesSincePoll = 0;
}
}
ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE;
ss[ply+2].mateKiller = MOVE_NONE;
ss[ply+2].killer1 = ss[ply+2].killer2 = MOVE_NONE;
ss[ply].threatMove = MOVE_NONE;
ss[ply].reduction = Depth(0);
ss[ply].currentMoveCaptureValue = Value(0);
if(Threads[threadID].printCurrentLine)
print_current_line(ss, ply, threadID);
}
// update_pv() is called whenever a search returns a value > alpha. It
// updates the PV in the SearchStack object corresponding to the current
// node.
void update_pv(SearchStack ss[], int ply) {
assert(ply >= 0 && ply < PLY_MAX);
ss[ply].pv[ply] = ss[ply].currentMove;
int p;
for(p = ply + 1; ss[ply+1].pv[p] != MOVE_NONE; p++)
ss[ply].pv[p] = ss[ply+1].pv[p];
ss[ply].pv[p] = MOVE_NONE;
}
// sp_update_pv() is a variant of update_pv for use at split points. The
// difference between the two functions is that sp_update_pv also updates
// the PV at the parent node.
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply) {
assert(ply >= 0 && ply < PLY_MAX);
ss[ply].pv[ply] = pss[ply].pv[ply] = ss[ply].currentMove;
int p;
for(p = ply + 1; ss[ply+1].pv[p] != MOVE_NONE; p++)
ss[ply].pv[p] = pss[ply].pv[p] = ss[ply+1].pv[p];
ss[ply].pv[p] = pss[ply].pv[p] = MOVE_NONE;
}
// connected_moves() tests whether two moves are 'connected' in the sense
// that the first move somehow made the second move possible (for instance
// if the moving piece is the same in both moves). The first move is
// assumed to be the move that was made to reach the current position, while
// the second move is assumed to be a move from the current position.
bool connected_moves(const Position &pos, Move m1, Move m2) {
Square f1, t1, f2, t2;
assert(move_is_ok(m1));
assert(move_is_ok(m2));
if(m2 == MOVE_NONE)
return false;
// Case 1: The moving piece is the same in both moves.
f2 = move_from(m2);
t1 = move_to(m1);
if(f2 == t1)
return true;
// Case 2: The destination square for m2 was vacated by m1.
t2 = move_to(m2);
f1 = move_from(m1);
if(t2 == f1)
return true;
// Case 3: Moving through the vacated square:
if(piece_is_slider(pos.piece_on(f2)) &&
bit_is_set(squares_between(f2, t2), f1))
return true;
// Case 4: The destination square for m2 is attacked by the moving piece
// in m1:
if(pos.piece_attacks_square(t1, t2))
return true;
// Case 5: Discovered check, checking piece is the piece moved in m1:
if(piece_is_slider(pos.piece_on(t1)) &&
bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())),
f2) &&
!bit_is_set(squares_between(t2, pos.king_square(pos.side_to_move())),
t2)) {
Bitboard occ = pos.occupied_squares();
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
clear_bit(&occ, f2);
if(pos.type_of_piece_on(t1) == BISHOP) {
if(bit_is_set(bishop_attacks_bb(ksq, occ), t1))
return true;
}
else if(pos.type_of_piece_on(t1) == ROOK) {
if(bit_is_set(rook_attacks_bb(ksq, occ), t1))
return true;
}
else {
assert(pos.type_of_piece_on(t1) == QUEEN);
if(bit_is_set(queen_attacks_bb(ksq, occ), t1))
return true;
}
}
return false;
}
// extension() decides whether a move should be searched with normal depth,
// or with extended depth. Certain classes of moves (checking moves, in
// particular) are searched with bigger depth than ordinary moves.
Depth extension(const Position &pos, Move m, bool pvNode,
bool check, bool singleReply, bool mateThreat) {
Depth result = Depth(0);
if(check)
result += CheckExtension[pvNode];
if(singleReply)
result += SingleReplyExtension[pvNode];
if(pos.move_is_pawn_push_to_7th(m))
result += PawnPushTo7thExtension[pvNode];
if(pos.move_is_passed_pawn_push(m))
result += PassedPawnExtension[pvNode];
if(mateThreat)
result += MateThreatExtension[pvNode];
if(pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
&& (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
&& !move_promotion(m))
result += PawnEndgameExtension[pvNode];
if(pvNode && pos.move_is_capture(m)
&& pos.type_of_piece_on(move_to(m)) != PAWN && pos.see(m) >= 0)
result += OnePly/2;
return Min(result, OnePly);
}
// ok_to_do_nullmove() looks at the current position and decides whether
// doing a 'null move' should be allowed. In order to avoid zugzwang
// problems, null moves are not allowed when the side to move has very
// little material left. Currently, the test is a bit too simple: Null
// moves are avoided only when the side to move has only pawns left. It's
// probably a good idea to avoid null moves in at least some more
// complicated endgames, e.g. KQ vs KR. FIXME
bool ok_to_do_nullmove(const Position &pos) {
if(pos.non_pawn_material(pos.side_to_move()) == Value(0))
return false;
return true;
}
// ok_to_prune() tests whether it is safe to forward prune a move. Only
// non-tactical moves late in the move list close to the leaves are
// candidates for pruning.
bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d) {
Square mfrom, mto, tfrom, tto;
assert(move_is_ok(m));
assert(threat == MOVE_NONE || move_is_ok(threat));
assert(!move_promotion(m));
assert(!pos.move_is_check(m));
assert(!pos.move_is_capture(m));
assert(!pos.move_is_passed_pawn_push(m));
assert(d >= OnePly);
mfrom = move_from(m);
mto = move_to(m);
tfrom = move_from(threat);
tto = move_to(threat);
// Case 1: Castling moves are never pruned.
if(move_is_castle(m))
return false;
// Case 2: Don't prune moves which move the threatened piece
if(!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
return false;
// Case 3: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune move which defend it.
if(!PruneDefendingMoves && threat != MOVE_NONE
&& (piece_value_midgame(pos.piece_on(tfrom))
>= piece_value_midgame(pos.piece_on(tto)))
&& pos.move_attacks_square(m, tto))
return false;
// Case 4: Don't prune moves with good history.
if(!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
return false;
// Case 5: If the moving piece in the threatened move is a slider, don't
// prune safe moves which block its ray.
if(!PruneBlockingMoves && threat != MOVE_NONE
&& piece_is_slider(pos.piece_on(tfrom))
&& bit_is_set(squares_between(tfrom, tto), mto) && pos.see(m) >= 0)
return false;
return true;
}
// fail_high_ply_1() checks if some thread is currently resolving a fail
// high at ply 1 at the node below the first root node. This information
// is used for time managment.
bool fail_high_ply_1() {
for(int i = 0; i < ActiveThreads; i++)
if(Threads[i].failHighPly1)
return true;
return false;
}
// current_search_time() returns the number of milliseconds which have passed
// since the beginning of the current search.
int current_search_time() {
return get_system_time() - SearchStartTime;
}
// nps() computes the current nodes/second count.
int nps() {
int t = current_search_time();
return (t > 0)? int((nodes_searched() * 1000) / t) : 0;
}
// poll() performs two different functions: It polls for user input, and it
// looks at the time consumed so far and decides if it's time to abort the
// search.
void poll() {
int t, data;
static int lastInfoTime;
t = current_search_time();
// Poll for input
data = Bioskey();
if(data) {
char input[256];
if(fgets(input, 255, stdin) == NULL)
strcpy(input, "quit\n");
if(strncmp(input, "quit", 4) == 0) {
AbortSearch = true;
PonderSearch = false;
Quit = true;
}
else if(strncmp(input, "stop", 4) == 0) {
AbortSearch = true;
PonderSearch = false;
}
else if(strncmp(input, "ponderhit", 9) == 0)
ponderhit();
}
// Print search information
if(t < 1000)
lastInfoTime = 0;
else if(lastInfoTime > t)
// HACK: Must be a new search where we searched less than
// NodesBetweenPolls nodes during the first second of search.
lastInfoTime = 0;
else if(t - lastInfoTime >= 1000) {
lastInfoTime = t;
lock_grab(&IOLock);
std::cout << "info nodes " << nodes_searched() << " nps " << nps()
<< " time " << t << " hashfull " << TT.full() << std::endl;
lock_release(&IOLock);
if(ShowCurrentLine)
Threads[0].printCurrentLine = true;
}
// Should we stop the search?
if(!PonderSearch && Iteration >= 2 &&
(!InfiniteSearch && (t > AbsoluteMaxSearchTime ||
(RootMoveNumber == 1 &&
t > MaxSearchTime + ExtraSearchTime) ||
(!FailHigh && !fail_high_ply_1() && !Problem &&
t > 6*(MaxSearchTime + ExtraSearchTime)))))
AbortSearch = true;
if(!PonderSearch && ExactMaxTime && t >= ExactMaxTime)
AbortSearch = true;
if(!PonderSearch && Iteration >= 3 && MaxNodes
&& nodes_searched() >= MaxNodes)
AbortSearch = true;
}
// ponderhit() is called when the program is pondering (i.e. thinking while
// it's the opponent's turn to move) in order to let the engine know that
// it correctly predicted the opponent's move.
void ponderhit() {
int t = current_search_time();
PonderSearch = false;
if(Iteration >= 2 &&
(!InfiniteSearch && (StopOnPonderhit ||
t > AbsoluteMaxSearchTime ||
(RootMoveNumber == 1 &&
t > MaxSearchTime + ExtraSearchTime) ||
(!FailHigh && !fail_high_ply_1() && !Problem &&
t > 6*(MaxSearchTime + ExtraSearchTime)))))
AbortSearch = true;
}
// print_current_line() prints the current line of search for a given
// thread. Called when the UCI option UCI_ShowCurrLine is 'true'.
void print_current_line(SearchStack ss[], int ply, int threadID) {
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
if(!Threads[threadID].idle) {
lock_grab(&IOLock);
std::cout << "info currline " << (threadID + 1);
for(int p = 0; p < ply; p++)
std::cout << " " << ss[p].currentMove;
std::cout << std::endl;
lock_release(&IOLock);
}
Threads[threadID].printCurrentLine = false;
if(threadID + 1 < ActiveThreads)
Threads[threadID + 1].printCurrentLine = true;
}
// wait_for_stop_or_ponderhit() is called when the maximum depth is reached
// while the program is pondering. The point is to work around a wrinkle in
// the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
// We simply wait here until one of these commands is sent, and return,
// after which the bestmove and pondermove will be printed (in id_loop()).
void wait_for_stop_or_ponderhit() {
std::string command;
while(true) {
if(!std::getline(std::cin, command))
command = "quit";
if(command == "quit") {
OpeningBook.close();
stop_threads();
quit_eval();
exit(0);
}
else if(command == "ponderhit" || command == "stop")
break;
}
}
// idle_loop() is where the threads are parked when they have no work to do.
// The parameter "waitSp", if non-NULL, is a pointer to an active SplitPoint
// object for which the current thread is the master.
void idle_loop(int threadID, SplitPoint *waitSp) {
assert(threadID >= 0 && threadID < THREAD_MAX);
Threads[threadID].running = true;
while(true) {
if(AllThreadsShouldExit && threadID != 0)
break;
// If we are not thinking, wait for a condition to be signaled instead
// of wasting CPU time polling for work:
while(threadID != 0 && (Idle || threadID >= ActiveThreads)) {
#if !defined(_MSC_VER)
pthread_mutex_lock(&WaitLock);
if(Idle || threadID >= ActiveThreads)
pthread_cond_wait(&WaitCond, &WaitLock);
pthread_mutex_unlock(&WaitLock);
#else
WaitForSingleObject(SitIdleEvent[threadID], INFINITE);
#endif
}
// If this thread has been assigned work, launch a search:
if(Threads[threadID].workIsWaiting) {
Threads[threadID].workIsWaiting = false;
if(Threads[threadID].splitPoint->pvNode)
sp_search_pv(Threads[threadID].splitPoint, threadID);
else
sp_search(Threads[threadID].splitPoint, threadID);
Threads[threadID].idle = true;
}
// If this thread is the master of a split point and all threads have
// finished their work at this split point, return from the idle loop:
if(waitSp != NULL && waitSp->cpus == 0)
return;
}
Threads[threadID].running = false;
}
// init_split_point_stack() is called during program initialization, and
// initializes all split point objects.
void init_split_point_stack() {
for(int i = 0; i < THREAD_MAX; i++)
for(int j = 0; j < MaxActiveSplitPoints; j++) {
SplitPointStack[i][j].parent = NULL;
lock_init(&(SplitPointStack[i][j].lock), NULL);
}
}
// destroy_split_point_stack() is called when the program exits, and
// destroys all locks in the precomputed split point objects.
void destroy_split_point_stack() {
for(int i = 0; i < THREAD_MAX; i++)
for(int j = 0; j < MaxActiveSplitPoints; j++)
lock_destroy(&(SplitPointStack[i][j].lock));
}
// thread_should_stop() checks whether the thread with a given threadID has
// been asked to stop, directly or indirectly. This can happen if a beta
// cutoff has occured in thre thread's currently active split point, or in
// some ancestor of the current split point.
bool thread_should_stop(int threadID) {
assert(threadID >= 0 && threadID < ActiveThreads);
SplitPoint *sp;
if(Threads[threadID].stop)
return true;
if(ActiveThreads <= 2)
return false;
for(sp = Threads[threadID].splitPoint; sp != NULL; sp = sp->parent)
if(sp->finished) {
Threads[threadID].stop = true;
return true;
}
return false;
}
// thread_is_available() checks whether the thread with threadID "slave" is
// available to help the thread with threadID "master" at a split point. An
// obvious requirement is that "slave" must be idle. With more than two
// threads, this is not by itself sufficient: If "slave" is the master of
// some active split point, it is only available as a slave to the other
// threads which are busy searching the split point at the top of "slave"'s
// split point stack (the "helpful master concept" in YBWC terminology).
bool thread_is_available(int slave, int master) {
assert(slave >= 0 && slave < ActiveThreads);
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
if(!Threads[slave].idle || slave == master)
return false;
if(Threads[slave].activeSplitPoints == 0)
// No active split points means that the thread is available as a slave
// for any other thread.
return true;
if(ActiveThreads == 2)
return true;
// Apply the "helpful master" concept if possible.
if(SplitPointStack[slave][Threads[slave].activeSplitPoints-1].slaves[master])
return true;
return false;
}
// idle_thread_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
bool idle_thread_exists(int master) {
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
for(int i = 0; i < ActiveThreads; i++)
if(thread_is_available(i, master))
return true;
return false;
}
// split() does the actual work of distributing the work at a node between
// several threads at PV nodes. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
// split point objects), the function immediately returns false. If
// splitting is possible, a SplitPoint object is initialized with all the
// data that must be copied to the helper threads (the current position and
// search stack, alpha, beta, the search depth, etc.), and we tell our
// helper threads that they have been assigned work. This will cause them
// to instantly leave their idle loops and call sp_search_pv(). When all
// threads have returned from sp_search_pv (or, equivalently, when
// splitPoint->cpus becomes 0), split() returns true.
bool split(const Position &p, SearchStack *sstck, int ply,
Value *alpha, Value *beta, Value *bestValue,
Depth depth, int *moves,
MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode) {
assert(p.is_ok());
assert(sstck != NULL);
assert(ply >= 0 && ply < PLY_MAX);
assert(*bestValue >= -VALUE_INFINITE && *bestValue <= *alpha);
assert(!pvNode || *alpha < *beta);
assert(*beta <= VALUE_INFINITE);
assert(depth > Depth(0));
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
SplitPoint *splitPoint;
int i;
lock_grab(&MPLock);
// If no other thread is available to help us, or if we have too many
// active split points, don't split:
if(!idle_thread_exists(master) ||
Threads[master].activeSplitPoints >= MaxActiveSplitPoints) {
lock_release(&MPLock);
return false;
}
// Pick the next available split point object from the split point stack:
splitPoint = SplitPointStack[master] + Threads[master].activeSplitPoints;
Threads[master].activeSplitPoints++;
// Initialize the split point object:
splitPoint->parent = Threads[master].splitPoint;
splitPoint->finished = false;
splitPoint->ply = ply;
splitPoint->depth = depth;
splitPoint->alpha = pvNode? *alpha : (*beta - 1);
splitPoint->beta = *beta;
splitPoint->pvNode = pvNode;
splitPoint->dcCandidates = dcCandidates;
splitPoint->bestValue = *bestValue;
splitPoint->master = master;
splitPoint->mp = mp;
splitPoint->moves = *moves;
splitPoint->cpus = 1;
splitPoint->pos.copy(p);
splitPoint->parentSstack = sstck;
for(i = 0; i < ActiveThreads; i++)
splitPoint->slaves[i] = 0;
// Copy the current position and the search stack to the master thread:
memcpy(splitPoint->sstack[master], sstck, (ply+1)*sizeof(SearchStack));
Threads[master].splitPoint = splitPoint;
// Make copies of the current position and search stack for each thread:
for(i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint;
i++)
if(thread_is_available(i, master)) {
memcpy(splitPoint->sstack[i], sstck, (ply+1)*sizeof(SearchStack));
Threads[i].splitPoint = splitPoint;
splitPoint->slaves[i] = 1;
splitPoint->cpus++;
}
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for(i = 0; i < ActiveThreads; i++)
if(i == master || splitPoint->slaves[i]) {
Threads[i].workIsWaiting = true;
Threads[i].idle = false;
Threads[i].stop = false;
}
lock_release(&MPLock);
// Everything is set up. The master thread enters the idle loop, from
// which it will instantly launch a search, because its workIsWaiting
// slot is 'true'. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point
// (i.e. when // splitPoint->cpus == 0).
idle_loop(master, splitPoint);
// We have returned from the idle loop, which means that all threads are
// finished. Update alpha, beta and bestvalue, and return:
lock_grab(&MPLock);
if(pvNode) *alpha = splitPoint->alpha;
*beta = splitPoint->beta;
*bestValue = splitPoint->bestValue;
Threads[master].stop = false;
Threads[master].idle = false;
Threads[master].activeSplitPoints--;
Threads[master].splitPoint = splitPoint->parent;
lock_release(&MPLock);
return true;
}
// wake_sleeping_threads() wakes up all sleeping threads when it is time
// to start a new search from the root.
void wake_sleeping_threads() {
if(ActiveThreads > 1) {
for(int i = 1; i < ActiveThreads; i++) {
Threads[i].idle = true;
Threads[i].workIsWaiting = false;
}
#if !defined(_MSC_VER)
pthread_mutex_lock(&WaitLock);
pthread_cond_broadcast(&WaitCond);
pthread_mutex_unlock(&WaitLock);
#else
for(int i = 1; i < THREAD_MAX; i++)
SetEvent(SitIdleEvent[i]);
#endif
}
}
// init_thread() is the function which is called when a new thread is
// launched. It simply calls the idle_loop() function with the supplied
// threadID. There are two versions of this function; one for POSIX threads
// and one for Windows threads.
#if !defined(_MSC_VER)
void *init_thread(void *threadID) {
idle_loop(*(int *)threadID, NULL);
return NULL;
}
#else
DWORD WINAPI init_thread(LPVOID threadID) {
idle_loop(*(int *)threadID, NULL);
return NULL;
}
#endif
}
glaurung-2.2/src/Makefile 0000644 0001750 0001750 00000007210 11012547274 015015 0 ustar oliver oliver # Glaurung, a UCI chess playing engine.
# Copyright (C) 2004-2007 Tord Romstad
# This file is part of Glaurung.
#
# Glaurung is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glaurung is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
###
### Files
###
EXE = glaurung
OBJS = bitboard.o color.o pawns.o material.o endgame.o evaluate.o main.o \
misc.o move.o movegen.o history.o movepick.o search.o piece.o \
position.o square.o direction.o tt.o value.o uci.o ucioption.o \
mersenne.o book.o bitbase.o san.o benchmark.o
###
### Rules
###
all: $(EXE) .depend
clean:
$(RM) *.o .depend glaurung
###
### Compiler:
###
CXX = g++
# CXX = g++-4.2
# CXX = icpc
###
### Dependencies
###
$(EXE): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
.depend:
$(CXX) -MM $(OBJS:.o=.cpp) > $@
include .depend
###
### Compiler and linker switches
###
# Enable/disable debugging:
CXXFLAGS += -DNDEBUG
# Compile with full warnings, and symbol names
CXXFLAGS += -Wall -g
# General optimization flags. Note that -O2 might be faster than -O3 on some
# systems; this requires testing.
CXXFLAGS += -O3 -fno-exceptions -fomit-frame-pointer -fno-rtti -fstrict-aliasing
# Compiler optimization flags for the Intel C++ compiler in Mac OS X:
# CXXFLAGS += -mdynamic-no-pic -no-prec-div -ipo -static -xP
# Profiler guided optimization with the Intel C++ compiler. To use it, first
# create the directory ./profdata if it does not already exist, and delete its
# contents if it does exist. Then compile with -prof_gen, and run the
# resulting binary for a while (for instance, do ./glaurung bench 128 1, and
# wait 15 minutes for the benchmark to complete). Then do a 'make clean', and
# recompile with -prof_use.
# CXXFLAGS += -prof_gen -prof_dir ./profdata
# CXXFLAGS += -prof_use -prof_dir ./profdata
# Profiler guided optimization with GCC. I've never been able to make this
# work.
# CXXFLAGS += -fprofile-generate
# LDFLAGS += -fprofile-generate
# CXXFLAGS += -fprofile-use
# CXXFLAGS += -fprofile-use
# General linker flags
LDFLAGS += -lm -lpthread
# Compiler switches for generating binaries for various CPUs in Mac OS X.
# Note that 'arch ppc' and 'arch ppc64' only works with g++, and not with
# the intel compiler.
# CXXFLAGS += -arch ppc
# CXXFLAGS += -arch ppc64
# CXXFLAGS += -arch i386
# CXXFLAGS += -arch x86_64
# LDFLAGS += -arch ppc
# LDFLAGS += -arch ppc64
# LDFLAGS += -arch i386
# LDFLAGS += -arch x86_64
# Backwards compatibility with Mac OS X 10.4 when compiling under 10.5 with
# GCC 4.0. I haven't found a way to make it work with GCC 4.2.
# CXXFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk
# CXXFLAGS += -mmacosx-version-min=10.4
# LDFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk
# LDFLAGS += -Wl,-syslibroot /Developer/SDKs/MacOSX10.4u.sdk
# LDFLAGS += -mmacosx-version-min=10.4
# Backwards compatibility with Mac OS X 10.4 when compiling with ICC. Doesn't
# work yet. :-(
# CXXFLAGS += -DMAC_OS_X_VERSION_MIN_REQUIRED=1040
# CXXFLAGS += -DMAC_OS_X_VERSION_MAX_ALLOWED=1040
# CXXFLAGS += -D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1040
# CXXFLAGS += -F/Developer/SDKs/MacOSX10.4u.sdk/
# LDFLAGS += -Wl,-syslibroot -Wl,/Developer/SDKs/MacOSX10.4u.sdk
glaurung-2.2/src/psqtab.h 0000644 0001750 0001750 00000013031 11123021255 015003 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(PSQTAB_H_INCLUDED)
#define PSQTAB_H_INCLUDED
////
//// Includes
////
#include "position.h"
////
//// Variables
////
static const int MgPST[][64] = {
{ },
{ // Pawn
0, 0, 0, 0, 0, 0, 0, 0,
166, 192, 204, 216, 216, 204, 192, 166,
166, 192, 210, 242, 242, 210, 192, 166,
166, 192, 220, 268, 268, 220, 192, 166,
166, 192, 220, 242, 242, 220, 192, 166,
166, 192, 210, 216, 216, 210, 192, 166,
166, 192, 204, 216, 216, 204, 192, 166,
0, 0, 0, 0, 0, 0, 0, 0
},
{ // Knight
704, 730, 756, 768, 768, 756, 730, 704,
743, 768, 794, 807, 807, 794, 768, 743,
781, 807, 832, 844, 844, 832, 807, 781,
807, 832, 857, 870, 870, 857, 832, 807,
820, 844, 870, 883, 883, 870, 844, 820,
820, 844, 870, 883, 883, 870, 844, 820,
781, 807, 832, 844, 844, 832, 807, 781,
650, 768, 794, 807, 807, 794, 768, 650
},
{ // Bishop
786, 786, 792, 797, 797, 792, 786, 786,
812, 832, 827, 832, 832, 827, 832, 812,
817, 827, 842, 837, 837, 842, 827, 817,
822, 832, 837, 852, 852, 837, 832, 822,
822, 832, 837, 852, 852, 837, 832, 822,
817, 827, 842, 837, 837, 842, 827, 817,
812, 832, 827, 832, 832, 827, 832, 812,
812, 812, 817, 822, 822, 817, 812, 812
},
{ // Rook
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267
},
{ // Queen
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560
},
{ // King
302, 328, 276, 225, 225, 276, 328, 302,
276, 302, 251, 200, 200, 251, 302, 276,
225, 251, 200, 149, 149, 200, 251, 225,
200, 225, 175, 124, 124, 175, 225, 200,
175, 200, 149, 98, 98, 149, 200, 175,
149, 175, 124, 72, 72, 124, 175, 149,
124, 149, 98, 47, 47, 98, 149, 124,
98, 124, 72, 21, 21, 72, 124, 98,
}
};
static const int EgPST[][64] = {
{ },
{ // Pawn
0, 0, 0, 0, 0, 0, 0, 0,
256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256,
0, 0, 0, 0, 0, 0, 0, 0
},
{ // Knight
730, 756, 781, 794, 794, 781, 756, 730,
756, 781, 807, 820, 820, 807, 781, 756,
781, 807, 832, 844, 844, 832, 807, 781,
794, 820, 844, 857, 857, 844, 820, 794,
794, 820, 844, 857, 857, 844, 820, 794,
781, 807, 832, 844, 844, 832, 807, 781,
756, 781, 807, 820, 820, 807, 781, 756,
730, 756, 781, 794, 794, 781, 756, 730
},
{ // Bishop
786, 802, 809, 817, 817, 809, 802, 786,
802, 817, 825, 832, 832, 825, 817, 802,
809, 825, 832, 839, 839, 832, 825, 809,
817, 832, 839, 847, 847, 839, 832, 817,
817, 832, 839, 847, 847, 839, 832, 817,
809, 825, 832, 839, 839, 832, 825, 809,
802, 817, 825, 832, 832, 825, 817, 802,
786, 802, 809, 817, 817, 809, 802, 786
},
{ // Rook
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282
},
{ // Queen
2499, 2520, 2530, 2540, 2540, 2530, 2520, 2499,
2520, 2540, 2550, 2560, 2560, 2550, 2540, 2520,
2530, 2550, 2560, 2570, 2570, 2560, 2550, 2530,
2540, 2560, 2570, 2580, 2580, 2570, 2560, 2540,
2540, 2560, 2570, 2580, 2580, 2570, 2560, 2540,
2530, 2550, 2560, 2570, 2570, 2560, 2550, 2530,
2520, 2540, 2550, 2560, 2560, 2550, 2540, 2520,
2499, 2520, 2530, 2540, 2540, 2530, 2520, 2499
},
{ // King
16, 78, 108, 139, 139, 108, 78, 16,
78, 139, 170, 200, 200, 170, 139, 78,
108, 170, 200, 230, 230, 200, 170, 108,
139, 200, 230, 261, 261, 230, 200, 139,
139, 200, 230, 261, 261, 230, 200, 139,
108, 170, 200, 230, 230, 200, 170, 108,
78, 139, 170, 200, 200, 170, 139, 78,
16, 78, 108, 139, 139, 108, 78, 16
}
};
#endif // !defined(PSQTAB_H_INCLUDED)
glaurung-2.2/src/misc.cpp 0000644 0001750 0001750 00000007513 11123021252 015004 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#if !defined(_MSC_VER)
# include
# include
# include
#else
# include
# include
# include "dos.h"
int gettimeofday(struct timeval * tp, struct timezone * tzp);
#endif
#include
#include
#include
#include
#include
#include "misc.h"
////
//// Functions
////
/// engine_name() returns the full name of the current Glaurung version.
/// This will be either "Glaurung YYMMDD" (where YYMMDD is the date when the
/// program was compiled) or "Glaurung ", depending on whether
/// the constant EngineVersion (defined in misc.h) is empty.
const std::string engine_name() {
if(EngineVersion == "") {
static const char monthNames[12][4] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
const char *dateString = __DATE__;
std::stringstream s;
int month = 0, day = 0;
for(int i = 0; i < 12; i++)
if(strncmp(dateString, monthNames[i], 3) == 0)
month = i + 1;
day = atoi(dateString+4);
s << "Glaurung " << (dateString+9) << std::setfill('0') << std::setw(2)
<< month << std::setfill('0') << std::setw(2) << day;
return s.str();
}
else
return "Glaurung " + EngineVersion;
}
/// get_system_time() returns the current system time, measured in
/// milliseconds.
int get_system_time() {
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec*1000 + t.tv_usec/1000;
}
/// cpu_count() tries to detect the number of CPU cores.
#if !defined(_MSC_VER)
# if defined(_SC_NPROCESSORS_ONLN)
int cpu_count() {
return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
}
# else
int cpu_count() {
return 1;
}
# endif
#else
int cpu_count() {
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, 8);
}
#endif
/*
From Beowulf, from Olithink
*/
#ifndef _WIN32
/* Non-windows version */
int Bioskey()
{
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
/* Set to timeout immediately */
timeout.tv_sec = 0;
timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout);
return (FD_ISSET(fileno(stdin), &readfds));
}
#else
/* Windows-version */
#include
#include
int Bioskey()
{
static int init = 0,
pipe;
static HANDLE inh;
DWORD dw;
/* If we're running under XBoard then we can't use _kbhit() as the input
* commands are sent to us directly over the internal pipe */
#if defined(FILE_CNT)
if (stdin->_cnt > 0)
return stdin->_cnt;
#endif
if (!init) {
init = 1;
inh = GetStdHandle(STD_INPUT_HANDLE);
pipe = !GetConsoleMode(inh, &dw);
if (!pipe) {
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
}
}
if (pipe) {
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
return 1;
return dw;
} else {
GetNumberOfConsoleInputEvents(inh, &dw);
return dw <= 1 ? 0 : dw;
}
}
#endif
glaurung-2.2/src/move.h 0000644 0001750 0001750 00000004536 11123021255 014471 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
////
//// Includes
////
#include
#include "misc.h"
#include "piece.h"
#include "square.h"
////
//// Types
////
class Position;
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65,
MOVE_MAX = 0xFFFFFF
};
struct MoveStack {
Move move;
int score;
};
////
//// Inline functions
////
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 077);
}
inline Square move_to(Move m) {
return Square(m & 077);
}
inline PieceType move_promotion(Move m) {
return PieceType((int(m) >> 12) & 7);
}
inline bool move_is_ep(Move m) {
return bool((int(m) >> 15) & 1);
}
inline bool move_is_castle(Move m) {
return bool((int(m) >> 16) & 1);
}
inline bool move_is_short_castle(Move m) {
return move_is_castle(m) && (move_to(m) > move_from(m));
}
inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | (int(promotion) << 12));
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 16));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 15));
}
////
//// Prototypes
////
extern std::ostream &operator << (std::ostream &os, Move m);
extern Move move_from_string(const Position &pos, const std::string &str);
extern const std::string move_to_string(Move m);
extern bool move_is_ok(Move m);
#endif // !defined(MOVE_H_INCLUDED)
glaurung-2.2/src/bitbase.h 0000644 0001750 0001750 00000001660 11123021255 015127 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(BITBASE_H_INCLUDED)
#define BITBASE_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Prototypes
////
extern void generate_kpk_bitbase(uint8_t bitbase[]);
#endif // !defined(BITBASE_H_INCLUDED)
glaurung-2.2/src/color.cpp 0000644 0001750 0001750 00000001570 11123021252 015164 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include "color.h"
////
//// Functions
////
/// color_is_ok(), for debugging:
bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
glaurung-2.2/src/thread.h 0000644 0001750 0001750 00000003162 11123021255 014764 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED
////
//// Includes
////
#include "lock.h"
#include "movepick.h"
#include "position.h"
#include "search.h"
////
//// Constants and variables
////
const int THREAD_MAX = 8;
////
//// Types
////
struct SplitPoint {
SplitPoint *parent;
Position pos;
SearchStack sstack[THREAD_MAX][PLY_MAX];
SearchStack *parentSstack;
int ply;
Depth depth;
volatile Value alpha, beta, bestValue;
bool pvNode;
Bitboard dcCandidates;
int master, slaves[THREAD_MAX];
Lock lock;
MovePicker *mp;
volatile int moves;
volatile int cpus;
bool finished;
};
struct Thread {
SplitPoint *splitPoint;
int activeSplitPoints;
uint64_t nodes;
bool failHighPly1;
volatile bool stop;
volatile bool running;
volatile bool idle;
volatile bool workIsWaiting;
volatile bool printCurrentLine;
unsigned char pad[64];
};
#endif // !defined(THREAD_H_INCLUDED)
glaurung-2.2/src/pawns.h 0000644 0001750 0001750 00000006522 11123021255 014650 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED
////
//// Includes
////
#include "position.h"
////
//// Types
////
/// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object.
class PawnInfo {
friend class PawnInfoTable;
public:
Value mg_value() const;
Value eg_value() const;
Value kingside_storm_value(Color c) const;
Value queenside_storm_value(Color c) const;
Bitboard passed_pawns() const;
bool file_is_half_open(Color c, File f) const;
bool has_open_file_to_left(Color c, File f) const;
bool has_open_file_to_right(Color c, File f) const;
private:
void clear();
Key key;
Bitboard passedPawns;
int16_t mgValue, egValue;
int8_t ksStormValue[2], qsStormValue[2];
uint8_t halfOpenFiles[2];
};
/// The PawnInfoTable class represents a pawn hash table. It is basically
/// just an array of PawnInfo objects and a few methods for accessing these
/// objects. The most important method is get_pawn_info, which looks up a
/// position in the table and returns a pointer to a PawnInfo object.
class PawnInfoTable {
public:
PawnInfoTable(unsigned numOfEntries);
~PawnInfoTable();
void clear();
PawnInfo *get_pawn_info(const Position &pos);
private:
unsigned size;
PawnInfo *entries;
};
////
//// Inline functions
////
inline Value PawnInfo::mg_value() const {
return Value(mgValue);
}
inline Value PawnInfo::eg_value() const {
return Value(egValue);
}
inline Bitboard PawnInfo::passed_pawns() const {
return passedPawns;
}
inline Value PawnInfo::kingside_storm_value(Color c) const {
return Value(ksStormValue[c]);
}
inline Value PawnInfo::queenside_storm_value(Color c) const {
return Value(qsStormValue[c]);
}
inline bool PawnInfo::file_is_half_open(Color c, File f) const {
return (halfOpenFiles[c] & (1 << int(f)));
}
inline bool PawnInfo::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1);
}
inline bool PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
inline void PawnInfo::clear() {
mgValue = egValue = 0;
passedPawns = EmptyBoardBB;
ksStormValue[WHITE] = ksStormValue[BLACK] = 0;
qsStormValue[WHITE] = qsStormValue[BLACK] = 0;
halfOpenFiles[WHITE] = halfOpenFiles[BLACK] = 0xFF;
}
#endif // !defined(PAWNS_H_INCLUDED)
glaurung-2.2/src/move.cpp 0000644 0001750 0001750 00000010775 11123021252 015023 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "move.h"
#include "piece.h"
#include "position.h"
#include "ucioption.h"
////
//// Functions
////
/// move_from_string() takes a position and a string as input, and attempts to
/// convert the string to a move, using simple coordinate notation (g1f3,
/// a7a8q, etc.). In order to correctly parse en passant captures and castling
/// moves, we need the position. This function is not robust, and expects that
/// the input move is legal and correctly formatted.
Move move_from_string(const Position &pos, const std::string &str) {
Square from, to;
Piece piece;
Color us = pos.side_to_move();
if(str.length() < 4) return MOVE_NONE;
// Read the from and to squares:
from = square_from_string(str.substr(0, 2));
to = square_from_string(str.substr(2, 4));
// Find the moving piece:
piece = pos.piece_on(from);
// If the string has more than 4 characters, try to interpret the 5th
// character as a promotion:
if(type_of_piece(piece) == PAWN && str.length() >= 5) {
switch(str[4]) {
case 'n': case 'N':
return make_promotion_move(from, to, KNIGHT);
case 'b': case 'B':
return make_promotion_move(from, to, BISHOP);
case 'r': case 'R':
return make_promotion_move(from, to, ROOK);
case 'q': case 'Q':
return make_promotion_move(from, to, QUEEN);
}
}
if(piece == king_of_color(us)) {
// Is this a castling move? A king move is assumed to be a castling
// move if the destination square is occupied by a friendly rook, or
// if the distance between the source and destination squares is more
// than 1.
if(pos.piece_on(to) == rook_of_color(us))
return make_castle_move(from, to);
else if(square_distance(from, to) > 1) {
// This is a castling move, but we have to translate it to the
// internal "king captures rook" representation.
SquareDelta delta = (to > from)? DELTA_E : DELTA_W;
Square s;
for(s = from + delta;
pawn_rank(us, s) == RANK_1 && pos.piece_on(s) != rook_of_color(us);
s += delta);
if(pawn_rank(us, s) == RANK_1 && pos.piece_on(s) == rook_of_color(us))
return make_castle_move(from, s);
}
}
else if(piece == pawn_of_color(us)) {
// En passant move? We assume that a pawn move is an en passant move
// without further testing if the destination square is epSquare.
if(to == pos.ep_square())
return make_ep_move(from, to);
}
return make_move(from, to);
}
/// move_to_string() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode.
const std::string move_to_string(Move move) {
std::string str;
if(move == MOVE_NONE)
str = "(none)";
else if(move == MOVE_NULL)
str = "0000";
else {
if(!Chess960) {
if(move_from(move) == SQ_E1 && move_is_short_castle(move)) {
str = "e1g1"; return str;
}
else if(move_from(move) == SQ_E1 && move_is_long_castle(move)) {
str = "e1c1"; return str;
}
if(move_from(move) == SQ_E8 && move_is_short_castle(move)) {
str = "e8g8"; return str;
}
else if(move_from(move) == SQ_E8 && move_is_long_castle(move)) {
str = "e8c8"; return str;
}
}
str = square_to_string(move_from(move)) + square_to_string(move_to(move));
if(move_promotion(move))
str += piece_type_to_char(move_promotion(move), false);
}
return str;
}
/// Overload the << operator, to make it easier to print moves.
std::ostream &operator << (std::ostream &os, Move m) {
return os << move_to_string(m);
}
/// move_is_ok(), for debugging.
bool move_is_ok(Move m) {
return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
}
glaurung-2.2/src/mersenne.h 0000644 0001750 0001750 00000001751 11123021255 015333 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(MERSENNE_H_INCLUDED)
#define MERSENNE_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Prototypes
////
extern uint32_t genrand_int32(void);
extern uint64_t genrand_int64(void);
extern void init_mersenne(void);
#endif // !defined(MERSENNE_H_INCLUDED)
glaurung-2.2/src/pawns.cpp 0000644 0001750 0001750 00000026437 11123021251 015206 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "pawns.h"
////
//// Local definitions
////
namespace {
/// Constants and variables
// Doubled pawn penalty by file, middle game.
const Value DoubledPawnMidgamePenalty[8] = {
Value(20), Value(30), Value(34), Value(34),
Value(34), Value(34), Value(30), Value(20)
};
// Doubled pawn penalty by file, endgame.
const Value DoubledPawnEndgamePenalty[8] = {
Value(35), Value(40), Value(40), Value(40),
Value(40), Value(40), Value(40), Value(35)
};
// Isolated pawn penalty by file, middle game.
const Value IsolatedPawnMidgamePenalty[8] = {
Value(20), Value(30), Value(34), Value(34),
Value(34), Value(34), Value(30), Value(20)
};
// Isolated pawn penalty by file, endgame.
const Value IsolatedPawnEndgamePenalty[8] = {
Value(35), Value(40), Value(40), Value(40),
Value(40), Value(40), Value(40), Value(35)
};
// Backward pawn penalty by file, middle game.
const Value BackwardPawnMidgamePenalty[8] = {
Value(16), Value(24), Value(27), Value(27),
Value(27), Value(27), Value(24), Value(16)
};
// Backward pawn penalty by file, endgame.
const Value BackwardPawnEndgamePenalty[8] = {
Value(28), Value(32), Value(32), Value(32),
Value(32), Value(32), Value(32), Value(28)
};
// Pawn chain membership bonus by file, middle game.
const Value ChainMidgameBonus[8] = {
Value(14), Value(16), Value(17), Value(18),
Value(18), Value(17), Value(16), Value(14)
};
// Pawn chain membership bonus by file, endgame.
const Value ChainEndgameBonus[8] = {
Value(16), Value(16), Value(16), Value(16),
Value(16), Value(16), Value(16), Value(16)
};
// Candidate passed pawn bonus by rank, middle game.
const Value CandidateMidgameBonus[8] = {
Value(0), Value(12), Value(12), Value(20),
Value(40), Value(90), Value(0), Value(0)
};
// Candidate passed pawn bonus by rank, endgame.
const Value CandidateEndgameBonus[8] = {
Value(0), Value(24), Value(24), Value(40),
Value(80), Value(180), Value(0), Value(0)
};
// Pawn storm tables for positions with opposite castling:
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22, -22, -22, -13, -4, 0, 0, 0,
-4, -9, -9, -9, -4, 0, 0, 0,
9, 18, 22, 18, 9, 0, 0, 0,
22, 31, 31, 22, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -4, -13, -22, -27, -27,
0, 0, 0, -4, -9, -13, -18, -18,
0, 0, 0, 0, 9, 9, 9, 9,
0, 0, 0, 0, 9, 18, 27, 27,
0, 0, 0, 0, 9, 27, 40, 36,
0, 0, 0, 0, 0, 31, 40, 31,
0, 0, 0, 0, 0, 0, 0, 0
};
// Pawn storm open file bonuses by file:
const int KStormOpenFileBonus[8] = {
45, 45, 30, 0, 0, 0, 0, 0
};
const int QStormOpenFileBonus[8] = {
0, 0, 0, 0, 0, 30, 45, 30
};
}
////
//// Functions
////
/// Constructor
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
size = numOfEntries;
entries = new PawnInfo[size];
if(entries == NULL) {
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE);
}
this->clear();
}
/// Destructor
PawnInfoTable::~PawnInfoTable() {
delete [] entries;
}
/// PawnInfoTable::clear() clears the pawn hash table by setting all
/// entries to 0.
void PawnInfoTable::clear() {
memset(entries, 0, size * sizeof(PawnInfo));
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also
/// stored in a hash table, so we don't have to recompute everything when
/// the same pawn structure occurs again.
PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
int index = int(key & (size - 1));
PawnInfo *pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return the
// information we found the last time instead of recomputing it:
if(pi->key == key)
return pi;
// Clear the PawnInfo object, and set the key:
pi->clear();
pi->key = key;
Value mgValue[2] = {Value(0), Value(0)};
Value egValue[2] = {Value(0), Value(0)};
// Loop through the pawns for both colors:
for(Color us = WHITE; us <= BLACK; us++) {
Color them = opposite_color(us);
Bitboard ourPawns = pos.pawns(us);
Bitboard theirPawns = pos.pawns(them);
Bitboard pawns = ourPawns;
// Initialize pawn storm scores by giving bonuses for open files:
for(File f = FILE_A; f <= FILE_H; f++)
if(pos.file_is_half_open(us, f)) {
pi->ksStormValue[us] += KStormOpenFileBonus[f];
pi->qsStormValue[us] += QStormOpenFileBonus[f];
}
// Loop through all pawns of the current color and score each pawn:
while(pawns) {
Square s = pop_1st_bit(&pawns);
File f = square_file(s);
Rank r = square_rank(s);
bool passed, doubled, isolated, backward, chain, candidate;
int bonus;
assert(pos.piece_on(s) == pawn_of_color(us));
// The file containing the pawn is not half open:
pi->halfOpenFiles[us] &= ~(1 << f);
// Passed, isolated or doubled pawn?
passed = pos.pawn_is_passed(us, s);
isolated = pos.pawn_is_isolated(us, s);
doubled = pos.pawn_is_doubled(us, s);
// We calculate kingside and queenside pawn storm
// scores for both colors. These are used when evaluating
// middle game positions with opposite side castling.
//
// Each pawn is given a base score given by a piece square table
// (KStormTable[] or QStormTable[]). This score is increased if
// there are enemy pawns on adjacent files in front of the pawn.
// This is because we want to be able to open files against the
// enemy king, and to avoid blocking the pawn structure (e.g. white
// pawns on h6, g5, black pawns on h7, g6, f7).
// Kingside pawn storms:
bonus = KStormTable[relative_square(us, s)];
if(bonus > 0 && outpost_mask(us, s) & theirPawns) {
switch(f) {
case FILE_F:
bonus += bonus / 4;
break;
case FILE_G:
bonus += bonus / 2 + bonus / 4;
break;
case FILE_H:
bonus += bonus / 2;
break;
default:
break;
}
}
pi->ksStormValue[us] += bonus;
// Queenside pawn storms:
bonus = QStormTable[relative_square(us, s)];
if(bonus > 0 && passed_pawn_mask(us, s) & theirPawns) {
switch(f) {
case FILE_A:
bonus += bonus / 2;
break;
case FILE_B:
bonus += bonus / 2 + bonus / 4;
break;
case FILE_C:
bonus += bonus / 2;
break;
default:
break;
}
}
pi->qsStormValue[us] += bonus;
// Member of a pawn chain? We could speed up the test a little by
// introducing an array of masks indexed by color and square for doing
// the test, but because everything is hashed, it probably won't make
// any noticable difference.
chain = (us == WHITE)?
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r-1))) :
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r+1)));
// Test for backward pawn.
// If the pawn is isolated, passed, or member of a pawn chain, it cannot
// be backward:
if(passed || isolated || chain)
backward = false;
// If the pawn can capture an enemy pawn, it's not backward:
else if(pos.pawn_attacks(us, s) & theirPawns)
backward = false;
// Check for friendly pawns behind on neighboring files:
else if(ourPawns & in_front_bb(them, r) & neighboring_files_bb(f))
backward = false;
else {
// We now know that there is no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b;
if(us == WHITE) {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b<<=8);
backward = (b | (b << 8)) & theirPawns;
}
else {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b>>=8);
backward = (b | (b >> 8)) & theirPawns;
}
}
// Test for candidate passed pawn.
candidate =
(!passed && pos.file_is_half_open(them, f) &&
count_1s_max_15(neighboring_files_bb(f)
& (in_front_bb(them, r) | rank_bb(r))
& ourPawns)
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r)
& theirPawns)
>= 0);
// In order to prevent doubled passed pawns from receiving a too big
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if(passed && (ourPawns & squares_in_front_of(us, s))) {
// candidate = true;
passed = false;
}
// Score this pawn:
Value mv = Value(0), ev = Value(0);
if(isolated) {
mv -= IsolatedPawnMidgamePenalty[f];
ev -= IsolatedPawnEndgamePenalty[f];
if(pos.file_is_half_open(them, f)) {
mv -= IsolatedPawnMidgamePenalty[f] / 2;
ev -= IsolatedPawnEndgamePenalty[f] / 2;
}
}
if(doubled) {
mv -= DoubledPawnMidgamePenalty[f];
ev -= DoubledPawnEndgamePenalty[f];
}
if(backward) {
mv -= BackwardPawnMidgamePenalty[f];
ev -= BackwardPawnEndgamePenalty[f];
if(pos.file_is_half_open(them, f)) {
mv -= BackwardPawnMidgamePenalty[f] / 2;
ev -= BackwardPawnEndgamePenalty[f] / 2;
}
}
if(chain) {
mv += ChainMidgameBonus[f];
ev += ChainEndgameBonus[f];
}
if(candidate) {
mv += CandidateMidgameBonus[pawn_rank(us, s)];
ev += CandidateEndgameBonus[pawn_rank(us, s)];
}
mgValue[us] += mv;
egValue[us] += ev;
// If the pawn is passed, set the square of the pawn in the passedPawns
// bitboard:
if(passed)
set_bit(&(pi->passedPawns), s);
}
}
pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
return pi;
}
glaurung-2.2/src/movepick.cpp 0000644 0001750 0001750 00000034475 11123021252 015675 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "history.h"
#include "movegen.h"
#include "movepick.h"
#include "search.h"
#include "value.h"
////
//// Local definitions
////
namespace {
/// Types
enum MovegenPhase {
PH_TT_MOVE, // Transposition table move
PH_MATE_KILLER, // Mate killer from the current ply
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_BAD_CAPTURES, // Queen promotions and captures with SEE valuse <= 0
PH_KILLER_1, // Killer move 1 from the current ply (not used yet).
PH_KILLER_2, // Killer move 2 from the current ply (not used yet).
PH_NONCAPTURES, // Non-captures and underpromotions
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Checks in quiescence search
PH_STOP
};
/// Variables
MovegenPhase PhaseTable[32];
int MainSearchPhaseIndex;
int EvasionsPhaseIndex;
int QsearchWithChecksPhaseIndex;
int QsearchWithoutChecksPhaseIndex;
}
////
//// Functions
////
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(Position &p, bool pvnode, Move ttm, Move mk,
Move k1, Move k2, Depth dpth) {
pos = &p;
pvNode = pvnode;
ttMove = ttm;
mateKiller = (mk == ttm)? MOVE_NONE : mk;
killer1 = k1;
killer2 = k2;
depth = dpth;
movesPicked = 0;
numOfMoves = 0;
numOfBadCaptures = 0;
dc = p.discovered_check_candidates(p.side_to_move());
if(p.is_check())
phaseIndex = EvasionsPhaseIndex;
else if(depth > Depth(0))
phaseIndex = MainSearchPhaseIndex;
else if(depth == Depth(0))
phaseIndex = QsearchWithChecksPhaseIndex;
else
phaseIndex = QsearchWithoutChecksPhaseIndex;
pinned = p.pinned_pieces(p.side_to_move());
finished = false;
}
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// are no more moves left of the types we are interested in.
Move MovePicker::get_next_move() {
Move move;
while(true) {
// If we already have a list of generated moves, pick the best move from
// the list, and return it:
move = this->pick_move_from_list();
if(move != MOVE_NONE) {
assert(move_is_ok(move));
return move;
}
// Next phase:
phaseIndex++;
switch(PhaseTable[phaseIndex]) {
case PH_TT_MOVE:
if(ttMove != MOVE_NONE) {
assert(move_is_ok(ttMove));
Move m = generate_move_if_legal(*pos, ttMove, pinned);
if(m != MOVE_NONE) {
assert(m == ttMove);
return m;
}
}
break;
case PH_MATE_KILLER:
if(mateKiller != MOVE_NONE) {
assert(move_is_ok(mateKiller));
Move m = generate_move_if_legal(*pos, mateKiller, pinned);
if(m != MOVE_NONE) {
assert(m == mateKiller);
return m;
}
}
break;
case PH_GOOD_CAPTURES:
// pinned = pos->pinned_pieces(pos->side_to_move());
numOfMoves = generate_captures(*pos, moves);
this->score_captures();
movesPicked = 0;
break;
case PH_BAD_CAPTURES:
badCapturesPicked = 0;
break;
case PH_NONCAPTURES:
numOfMoves = generate_noncaptures(*pos, moves);
this->score_noncaptures();
movesPicked = 0;
break;
case PH_EVASIONS:
assert(pos->is_check());
// pinned = pos->pinned_pieces(pos->side_to_move());
numOfMoves = generate_evasions(*pos, moves);
this->score_evasions();
movesPicked = 0;
break;
case PH_QCAPTURES:
// pinned = pos->pinned_pieces(pos->side_to_move());
numOfMoves = generate_captures(*pos, moves);
this->score_qcaptures();
movesPicked = 0;
break;
case PH_QCHECKS:
numOfMoves = generate_checks(*pos, moves, dc);
movesPicked = 0;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
return MOVE_NONE;
}
}
assert(false);
return MOVE_NONE;
}
/// A variant of get_next_move() which takes a lock as a parameter, used to
/// prevent multiple threads from picking the same move at a split point.
Move MovePicker::get_next_move(Lock &lock) {
Move m;
lock_grab(&lock);
if(finished) {
lock_release(&lock);
return MOVE_NONE;
}
m = this->get_next_move();
if(m == MOVE_NONE)
finished = true;
lock_release(&lock);
return m;
}
/// MovePicker::number_of_moves() simply returns the numOfMoves member
/// variable. It is intended to be used in positions where the side to move
/// is in check, for detecting checkmates or situations where there is only
/// a single reply to check.
int MovePicker::number_of_moves() const {
return numOfMoves;
}
/// MovePicker::score_captures(), MovePicker::score_noncaptures(),
/// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a
/// numerical move ordering score to each move in a move list. The moves
/// with highest scores will be picked first by
/// MovePicker::pick_move_from_list().
void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA.
// Suprisingly, this appears to perform slightly better than SEE based
// move ordering. The reason is probably that in a position with a winning
// capture, capturing a more valuable (but sufficiently defended) piece
// first usually doesn't hurt. The opponent will have to recapture, and
// the hanging piece will still be hanging (except in the unusual cases
// where it is possible to recapture with the hanging piece). Exchanging
// big pieces before capturing a hanging piece probably helps to reduce
// the subtree size.
for(int i = 0; i < numOfMoves; i++) {
int seeValue = pos->see(moves[i].move);
if(seeValue >= 0) {
if(move_promotion(moves[i].move))
moves[i].score = QueenValueMidgame;
else
moves[i].score =
int(pos->midgame_value_of_piece_on(move_to(moves[i].move))) -
int(pos->type_of_piece_on(move_from(moves[i].move)));
}
else
moves[i].score = seeValue;
}
}
void MovePicker::score_noncaptures() {
for(int i = 0; i < numOfMoves; i++) {
Move m = moves[i].move;
if(m == killer1)
moves[i].score = HistoryMax + 2;
else if(m == killer2)
moves[i].score = HistoryMax + 1;
else
moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m);
}
}
void MovePicker::score_evasions() {
for(int i = 0; i < numOfMoves; i++) {
Move m = moves[i].move;
if(m == ttMove)
moves[i].score = 2*HistoryMax;
else if(!pos->square_is_empty(move_to(m))) {
int seeScore = pos->see(m);
moves[i].score = (seeScore >= 0)? seeScore + HistoryMax : seeScore;
}
else
moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m);
}
}
void MovePicker::score_qcaptures() {
// Use MVV/LVA ordering.
for(int i = 0; i < numOfMoves; i++) {
Move m = moves[i].move;
if(move_promotion(m))
moves[i].score = QueenValueMidgame;
else
moves[i].score =
int(pos->midgame_value_of_piece_on(move_to(m))) -
int(pos->midgame_value_of_piece_on(move_to(m))) / 64;
}
}
/// MovePicker::pick_move_from_list() picks the move with the biggest score
/// from a list of generated moves (moves[] or badCaptures[], depending on
/// the current move generation phase). It takes care not to return the
/// transposition table move if that has already been serched previously.
/// While picking captures in the PH_GOOD_CAPTURES phase (i.e. while picking
/// non-losing captures in the main search), it moves all captures with
/// negative SEE values to the badCaptures[] array.
Move MovePicker::pick_move_from_list() {
int bestScore = -10000000;
int bestIndex;
Move move;
switch(PhaseTable[phaseIndex]) {
case PH_GOOD_CAPTURES:
assert(!pos->is_check());
assert(movesPicked >= 0);
while(movesPicked < numOfMoves) {
bestScore = -10000000;
bestIndex = -1;
for(int i = movesPicked; i < numOfMoves; i++) {
if(moves[i].score < 0) {
// Losing capture, move it to the badCaptures[] array
assert(numOfBadCaptures < 63);
badCaptures[numOfBadCaptures++] = moves[i];
moves[i--] = moves[--numOfMoves];
}
else if(moves[i].score > bestScore) {
bestIndex = i;
bestScore = moves[i].score;
}
}
if(bestIndex != -1) { // Found a good capture
MoveStack tmp = moves[movesPicked];
moves[movesPicked] = moves[bestIndex];
moves[bestIndex] = tmp;
move = moves[movesPicked++].move;
if(move != ttMove && move != mateKiller &&
pos->move_is_legal(move, pinned))
return move;
}
}
break;
case PH_NONCAPTURES:
assert(!pos->is_check());
assert(movesPicked >= 0);
while(movesPicked < numOfMoves) {
bestScore = -10000000;
// If this is a PV node or we have only picked a few moves, scan
// the entire move list for the best move. If many moves have already
// been searched and it is not a PV node, we are probably failing low
// anyway, so we just pick the first move from the list.
if(pvNode || movesPicked < 12) {
bestIndex = -1;
for(int i = movesPicked; i < numOfMoves; i++)
if(moves[i].score > bestScore) {
bestIndex = i;
bestScore = moves[i].score;
}
}
else
bestIndex = movesPicked;
if(bestIndex != -1) {
MoveStack tmp = moves[movesPicked];
moves[movesPicked] = moves[bestIndex];
moves[bestIndex] = tmp;
move = moves[movesPicked++].move;
if(move != ttMove && move != mateKiller &&
pos->move_is_legal(move, pinned))
return move;
}
}
break;
case PH_EVASIONS:
assert(pos->is_check());
assert(movesPicked >= 0);
while(movesPicked < numOfMoves) {
bestScore = -10000000;
bestIndex = -1;
for(int i = movesPicked; i < numOfMoves; i++)
if(moves[i].score > bestScore) {
bestIndex = i;
bestScore = moves[i].score;
}
if(bestIndex != -1) {
MoveStack tmp = moves[movesPicked];
moves[movesPicked] = moves[bestIndex];
moves[bestIndex] = tmp;
move = moves[movesPicked++].move;
return move;
}
}
break;
case PH_BAD_CAPTURES:
assert(!pos->is_check());
assert(badCapturesPicked >= 0);
// It's probably a good idea to use SEE move ordering here, instead
// of just picking the first move. FIXME
while(badCapturesPicked < numOfBadCaptures) {
move = badCaptures[badCapturesPicked++].move;
if(move != ttMove && move != mateKiller &&
pos->move_is_legal(move, pinned))
return move;
}
break;
case PH_QCAPTURES:
assert(!pos->is_check());
assert(movesPicked >= 0);
while(movesPicked < numOfMoves) {
bestScore = -10000000;
if(movesPicked < 4) {
bestIndex = -1;
for(int i = movesPicked; i < numOfMoves; i++)
if(moves[i].score > bestScore) {
bestIndex = i;
bestScore = moves[i].score;
}
}
else
bestIndex = movesPicked;
if(bestIndex != -1) {
MoveStack tmp = moves[movesPicked];
moves[movesPicked] = moves[bestIndex];
moves[bestIndex] = tmp;
move = moves[movesPicked++].move;
// Remember to change the line below if we decide to hash the qsearch!
// Maybe also postpone the legality check until after futility pruning?
if(/* move != ttMove && */ pos->move_is_legal(move, pinned))
return move;
}
}
break;
case PH_QCHECKS:
assert(!pos->is_check());
assert(movesPicked >= 0);
// Perhaps we should do something better than just picking the first
// move here? FIXME
while(movesPicked < numOfMoves) {
move = moves[movesPicked++].move;
// Remember to change the line below if we decide to hash the qsearch!
if(/* move != ttMove && */ pos->move_is_legal(move, pinned))
return move;
}
break;
default:
break;
}
return MOVE_NONE;
}
/// MovePicker::init_phase_table() initializes the PhaseTable[],
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
/// and QsearchWithoutChecksPhaseIndex variables. It is only called once
/// during program startup, and never again while the program is running.
void MovePicker::init_phase_table() {
int i = 0;
// Main search
MainSearchPhaseIndex = i - 1;
PhaseTable[i++] = PH_TT_MOVE;
PhaseTable[i++] = PH_MATE_KILLER;
PhaseTable[i++] = PH_GOOD_CAPTURES;
// PH_KILLER_1 and PH_KILLER_2 are not yet used.
// PhaseTable[i++] = PH_KILLER_1;
// PhaseTable[i++] = PH_KILLER_2;
PhaseTable[i++] = PH_NONCAPTURES;
PhaseTable[i++] = PH_BAD_CAPTURES;
PhaseTable[i++] = PH_STOP;
// Check evasions
EvasionsPhaseIndex = i - 1;
PhaseTable[i++] = PH_EVASIONS;
PhaseTable[i++] = PH_STOP;
// Quiescence search with checks
QsearchWithChecksPhaseIndex = i - 1;
PhaseTable[i++] = PH_QCAPTURES;
PhaseTable[i++] = PH_QCHECKS;
PhaseTable[i++] = PH_STOP;
// Quiescence search without checks
QsearchWithoutChecksPhaseIndex = i - 1;
PhaseTable[i++] = PH_QCAPTURES;
PhaseTable[i++] = PH_STOP;
}
glaurung-2.2/src/tt.cpp 0000644 0001750 0001750 00000014015 11123021251 014472 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include "tt.h"
////
//// Functions
////
/// Constructor
TranspositionTable::TranspositionTable(unsigned mbSize) {
size = 0;
generation = 0;
writes = 0;
entries = 0;
this->set_size(mbSize);
}
/// Destructor
TranspositionTable::~TranspositionTable() {
delete [] entries;
}
/// TranspositionTable::set_size sets the size of the transposition table,
/// measured in megabytes.
void TranspositionTable::set_size(unsigned mbSize) {
unsigned newSize;
assert(mbSize >= 4 && mbSize <= 1024);
for(newSize = 1024; newSize * 4 * (sizeof(TTEntry)) <= (mbSize << 20);
newSize *= 2);
newSize /= 2;
if(newSize != size) {
size = newSize;
delete [] entries;
entries = new TTEntry[size * 4];
if(entries == NULL) {
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table."
<< std::endl;
exit(EXIT_FAILURE);
}
this->clear();
}
}
/// TranspositionTable::clear overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the
/// user asks the program to clear the table (from the UCI interface).
/// Perhaps we should also clear it when the "ucinewgame" command is recieved?
void TranspositionTable::clear() {
memset(entries, 0, size * 4 * sizeof(TTEntry));
}
/// TranspositionTable::store writes a new entry containing a position,
/// a value, a value type, a search depth, and a best move to the
/// transposition table. The transposition table is organized in clusters
/// of four TTEntry objects, and when a new entry is written, it replaces
/// the least valuable of the four entries in a cluster. A TTEntry t1 is
/// considered to be more valuable than a TTEntry t2 if t1 is from the
/// current search and t2 is from a previous search, or if the depth of t1
/// is bigger than the depth of t2.
void TranspositionTable::store(const Position &pos, Value v, Depth d,
Move m, ValueType type) {
TTEntry *tte, *replace;
tte = replace = entries + int(pos.get_key() & (size - 1)) * 4;
for(int i = 0; i < 4; i++) {
if((tte+i)->key() == pos.get_key()) {
if(m == MOVE_NONE)
m = (tte+i)->move();
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
return;
}
if(replace->generation() == generation) {
if((tte+i)->generation() != generation ||
(tte+i)->depth() < replace->depth())
replace = tte+i;
}
else if((tte+i)->generation() != generation &&
(tte+i)->depth() < replace->depth())
replace = tte+i;
}
*replace = TTEntry(pos.get_key(), v, type, d, m, generation);
writes++;
}
/// TranspositionTable::retrieve looks up the current position in the
/// transposition table, and extracts the value, value type, depth and
/// best move if the position is found. The return value is true if
/// the position is found, and false if it isn't.
bool TranspositionTable::retrieve(const Position &pos, Value *value,
Depth *d, Move *move,
ValueType *type) const {
TTEntry *tte;
bool found = false;
tte = entries + int(pos.get_key() & (size - 1)) * 4;
for(int i = 0; i < 4 && !found ; i++)
if((tte+i)->key() == pos.get_key()) {
tte = tte + i;
found = true;
}
if(!found) {
*move = MOVE_NONE;
return false;
}
*value = tte->value();
*type = tte->type();
*d = tte->depth();
*move = tte->move();
return true;
}
/// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from
/// entries from the current search.
void TranspositionTable::new_search() {
generation++;
writes = 0;
}
/// TranspositionTable::insert_pv() is called at the end of a search
/// iteration, and inserts the PV back into the PV. This makes sure the
/// old PV moves are searched first, even if the old TT entries have been
/// overwritten.
void TranspositionTable::insert_pv(const Position &pos, Move pv[]) {
UndoInfo u;
Position p(pos);
for(int i = 0; pv[i] != MOVE_NONE; i++) {
this->store(p, VALUE_NONE, Depth(0), pv[i], VALUE_TYPE_NONE);
p.do_move(pv[i], u);
}
}
/// TranspositionTable::full() returns the permill of all transposition table
/// entries which have received at least one write during the current search.
/// It is used to display the "info hashfull ..." information in UCI.
int TranspositionTable::full() {
double N = double(size) * 4.0;
return int(1000 * (1 - exp(writes * log(1.0 - 1.0/N))));
}
/// Constructors
TTEntry::TTEntry() {
}
TTEntry::TTEntry(Key k, Value v, ValueType t, Depth d, Move m,
int generation) {
key_ = k;
data = (m & 0x7FFFF) | (t << 20) | (generation << 23);
value_ = v;
depth_ = int16_t(d);
}
/// Functions for extracting data from TTEntry objects.
Key TTEntry::key() const {
return key_;
}
Depth TTEntry::depth() const {
return Depth(depth_);
}
Move TTEntry::move() const {
return Move(data & 0x7FFFF);
}
Value TTEntry::value() const {
return Value(value_);
}
ValueType TTEntry::type() const {
return ValueType((data >> 20) & 3);
}
int TTEntry::generation() const {
return (data >> 23);
}
glaurung-2.2/src/san.h 0000644 0001750 0001750 00000002521 11123021255 014274 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(SAN_H_INCLUDED)
#define SAN_H_INCLUDED
////
//// Includes
////
#include
#include "move.h"
#include "position.h"
#include "value.h"
////
//// Prototypes
////
extern const std::string move_to_san(Position &pos, Move m);
extern Move move_from_san(Position &pos, const std::string &str);
extern const std::string line_to_san(const Position &pos, Move line[],
int startColumn, bool breakLines);
extern const std::string pretty_pv(const Position &pos, int time, int depth,
uint64_t nodes, Value score, Move pv[]);
#endif // !defined(SAN_H_INCLUDED)
glaurung-2.2/src/color.h 0000644 0001750 0001750 00000002322 11123021255 014630 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(COLOR_H_INCLUDED)
#define COLOR_H_INCLUDED
////
//// Includes
////
#include "misc.h"
////
//// Types
////
enum Color {
WHITE,
BLACK,
COLOR_NONE
};
////
//// Inline functions
////
inline Color operator+ (Color c, int i) { return Color(int(c) + i); }
inline void operator++ (Color &c, int i) { c = Color(int(c) + 1); }
inline Color opposite_color(Color c) {
return Color(int(c) ^ 1);
}
////
//// Prototypes
////
extern bool color_is_ok(Color c);
#endif // !defined(COLOR_H_INCLUDED)
glaurung-2.2/src/square.cpp 0000644 0001750 0001750 00000003354 11123021251 015347 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include "square.h"
////
//// Functions
////
/// Translating files, ranks and squares to/from characters and strings:
File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
char file_to_char(File f) {
return char(f - FILE_A) + 'a';
}
Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
char rank_to_char(Rank r) {
return char(r - RANK_1) + '1';
}
Square square_from_string(const std::string &str) {
return make_square(file_from_char(str[0]), rank_from_char(str[1]));
}
const std::string square_to_string(Square s) {
std::string str;
str += file_to_char(square_file(s));
str += rank_to_char(square_rank(s));
return str;
}
/// file_is_ok(), rank_is_ok() and square_is_ok(), for debugging:
bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
}
bool square_is_ok(Square s) {
return file_is_ok(square_file(s)) && rank_is_ok(square_rank(s));
}
glaurung-2.2/src/evaluate.h 0000644 0001750 0001750 00000007263 11123021255 015331 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED
////
//// Includes
////
#include "material.h"
#include "pawns.h"
#include "position.h"
////
//// Types
////
/// The EvalInfo struct contains various information computed and collected
/// by the evaluation function. An EvalInfo object is passed as one of the
/// arguments to the evaluation function, and the search can make use of its
/// contents to make intelligent search decisions.
///
/// At the moment, this is not utilized very much: The only part of the
/// EvalInfo object which is used by the search is futilityMargin.
struct EvalInfo {
// Middle game and endgame evaluations:
Value mgValue, egValue;
// Pointers to material and pawn hash table entries:
MaterialInfo *mi;
PawnInfo *pi;
// attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type. attackedBy[color][0] contains
// all squares attacked by the given color.
Bitboard attackedBy[2][8];
Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
// attackZone[color] is the zone around the enemy king which is considered
// by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file)
// squares two ranks in front of the king. For instance, if black's king
// is on g8, attackZone[WHITE] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6.
Bitboard attackZone[2];
// attackCount[color] is the number of pieces of the given color which
// attack a square adjacent to the enemy king.
int attackCount[2];
// attackWeight[color] is the sum of the "weight" of the pieces of the given
// color which attack a square adjacent to the enemy king. The weights of
// the individual piece types are given by the variables QueenAttackWeight,
// RookAttackWeight, BishopAttackWeight and KnightAttackWeight in
// evaluate.cpp.
int attackWeight[2];
// attacked[color] is the number of enemy piece attacks to squares directly
// adjacent to the king of the given color. Pieces which attack more
// than one square are counted multiple times. For instance, if black's
// king is on g8 and there's a white knight on g5, this knight adds
// 2 to attacked[BLACK].
int attacked[2];
// mateThreat[color] is a move for the given side which gives a direct mate.
Move mateThreat[2];
// Middle game and endgame mobility scores.
Value mgMobility, egMobility;
// Extra futility margin. This is added to the standard futility margin
// in the quiescence search.
Value futilityMargin;
};
////
//// Prototypes
////
extern Value evaluate(const Position &pos, EvalInfo &ei, int threadID);
extern Value quick_evaluate(const Position &pos);
extern void init_eval(int threads);
extern void quit_eval();
extern void read_weights(Color sideToMove);
#endif // !defined(EVALUATE_H_INCLUDED)
glaurung-2.2/src/COPYING 0000644 0001750 0001750 00000105755 11012547274 014425 0 ustar oliver oliver GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
glaurung-2.2/src/material.cpp 0000644 0001750 0001750 00000033656 11123021252 015656 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "material.h"
////
//// Local definitions
////
namespace {
const Value BishopPairMidgameBonus = Value(100);
const Value BishopPairEndgameBonus = Value(100);
Key KPKMaterialKey, KKPMaterialKey;
Key KBNKMaterialKey, KKBNMaterialKey;
Key KRKPMaterialKey, KPKRMaterialKey;
Key KRKBMaterialKey, KBKRMaterialKey;
Key KRKNMaterialKey, KNKRMaterialKey;
Key KQKRMaterialKey, KRKQMaterialKey;
Key KBBKNMaterialKey, KNKBBMaterialKey;
Key KRPKRMaterialKey, KRKRPMaterialKey;
Key KRPPKRPMaterialKey, KRPKRPPMaterialKey;
Key KNNKMaterialKey, KKNNMaterialKey;
Key KBPKBMaterialKey, KBKBPMaterialKey;
Key KBPKNMaterialKey, KNKBPMaterialKey;
Key KNPKMaterialKey, KKNPMaterialKey;
Key KPKPMaterialKey;
};
////
//// Functions
////
/// MaterialInfo::init() is called during program initialization. It
/// precomputes material hash keys for a few basic endgames, in order
/// to make it easy to recognize such endgames when they occur.
void MaterialInfo::init() {
KPKMaterialKey = Position::zobMaterial[WHITE][PAWN][1];
KKPMaterialKey = Position::zobMaterial[BLACK][PAWN][1];
KBNKMaterialKey =
Position::zobMaterial[WHITE][BISHOP][1] ^
Position::zobMaterial[WHITE][KNIGHT][1];
KKBNMaterialKey =
Position::zobMaterial[BLACK][BISHOP][1] ^
Position::zobMaterial[BLACK][KNIGHT][1];
KRKPMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KPKRMaterialKey =
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][ROOK][1];
KRKBMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[BLACK][BISHOP][1];
KBKRMaterialKey =
Position::zobMaterial[WHITE][BISHOP][1] ^
Position::zobMaterial[BLACK][ROOK][1];
KRKNMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[BLACK][KNIGHT][1];
KNKRMaterialKey =
Position::zobMaterial[WHITE][KNIGHT][1] ^
Position::zobMaterial[BLACK][ROOK][1];
KQKRMaterialKey =
Position::zobMaterial[WHITE][QUEEN][1] ^
Position::zobMaterial[BLACK][ROOK][1];
KRKQMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[BLACK][QUEEN][1];
KRPKRMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][ROOK][1];
KRKRPMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[BLACK][ROOK][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KRPPKRPMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[WHITE][PAWN][2] ^
Position::zobMaterial[BLACK][ROOK][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KRPKRPPMaterialKey =
Position::zobMaterial[WHITE][ROOK][1] ^
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][ROOK][1] ^
Position::zobMaterial[BLACK][PAWN][1] ^
Position::zobMaterial[BLACK][PAWN][2];
KNNKMaterialKey =
Position::zobMaterial[WHITE][KNIGHT][1] ^
Position::zobMaterial[WHITE][KNIGHT][2];
KKNNMaterialKey =
Position::zobMaterial[BLACK][KNIGHT][1] ^
Position::zobMaterial[BLACK][KNIGHT][2];
KBPKBMaterialKey =
Position::zobMaterial[WHITE][BISHOP][1] ^
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][BISHOP][1];
KBKBPMaterialKey =
Position::zobMaterial[WHITE][BISHOP][1] ^
Position::zobMaterial[BLACK][BISHOP][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KBPKNMaterialKey =
Position::zobMaterial[WHITE][BISHOP][1] ^
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][KNIGHT][1];
KNKBPMaterialKey =
Position::zobMaterial[WHITE][KNIGHT][1] ^
Position::zobMaterial[BLACK][BISHOP][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KNPKMaterialKey =
Position::zobMaterial[WHITE][KNIGHT][1] ^
Position::zobMaterial[WHITE][PAWN][1];
KKNPMaterialKey =
Position::zobMaterial[BLACK][KNIGHT][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KPKPMaterialKey =
Position::zobMaterial[WHITE][PAWN][1] ^
Position::zobMaterial[BLACK][PAWN][1];
KBBKNMaterialKey =
Position::zobMaterial[WHITE][BISHOP][2] ^
Position::zobMaterial[BLACK][KNIGHT][1];
KNKBBMaterialKey =
Position::zobMaterial[WHITE][KNIGHT][1] ^
Position::zobMaterial[BLACK][BISHOP][2];
}
/// Constructor for the MaterialInfoTable class.
MaterialInfoTable::MaterialInfoTable(unsigned numOfEntries) {
size = numOfEntries;
entries = new MaterialInfo[size];
if(entries == NULL) {
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(MaterialInfo))
<< " bytes for material hash table." << std::endl;
exit(EXIT_FAILURE);
}
this->clear();
}
/// Destructor for the MaterialInfoTable class.
MaterialInfoTable::~MaterialInfoTable() {
delete [] entries;
}
/// MaterialInfoTable::clear() clears a material hash table by setting
/// all entries to 0.
void MaterialInfoTable::clear() {
memset(entries, 0, size * sizeof(MaterialInfo));
}
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo *MaterialInfoTable::get_material_info(const Position &pos) {
Key key = pos.get_material_key();
int index = key & (size - 1);
MaterialInfo *mi = entries + index;
// If mi->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it:
if(mi->key == key)
return mi;
// Clear the MaterialInfo object, and set its key:
mi->clear();
mi->key = key;
// A special case before looking for a specialized evaluation function:
// KNN vs K is a draw:
if(key == KNNKMaterialKey || key == KKNNMaterialKey) {
mi->factor[WHITE] = mi->factor[BLACK] = 0;
return mi;
}
// Let's look if we have a specialized evaluation function for this
// particular material configuration:
if(key == KPKMaterialKey) {
mi->evaluationFunction = &EvaluateKPK;
return mi;
}
else if(key == KKPMaterialKey) {
mi->evaluationFunction = &EvaluateKKP;
return mi;
}
else if(key == KBNKMaterialKey) {
mi->evaluationFunction = &EvaluateKBNK;
return mi;
}
else if(key == KKBNMaterialKey) {
mi->evaluationFunction = &EvaluateKKBN;
return mi;
}
else if(key == KRKPMaterialKey) {
mi->evaluationFunction = &EvaluateKRKP;
return mi;
}
else if(key == KPKRMaterialKey) {
mi->evaluationFunction = &EvaluateKPKR;
return mi;
}
else if(key == KRKBMaterialKey) {
mi->evaluationFunction = &EvaluateKRKB;
return mi;
}
else if(key == KBKRMaterialKey) {
mi->evaluationFunction = &EvaluateKBKR;
return mi;
}
else if(key == KRKNMaterialKey) {
mi->evaluationFunction = &EvaluateKRKN;
return mi;
}
else if(key == KNKRMaterialKey) {
mi->evaluationFunction = &EvaluateKNKR;
return mi;
}
else if(key == KQKRMaterialKey) {
mi->evaluationFunction = &EvaluateKQKR;
return mi;
}
else if(key == KRKQMaterialKey) {
mi->evaluationFunction = &EvaluateKRKQ;
return mi;
}
else if(key == KBBKNMaterialKey) {
mi->evaluationFunction = &EvaluateKBBKN;
return mi;
}
else if(key == KNKBBMaterialKey) {
mi->evaluationFunction = &EvaluateKNKBB;
return mi;
}
else if(pos.non_pawn_material(BLACK) == Value(0) &&
pos.pawn_count(BLACK) == 0 &&
pos.non_pawn_material(WHITE) >= RookValueEndgame) {
mi->evaluationFunction = &EvaluateKXK;
return mi;
}
else if(pos.non_pawn_material(WHITE) == Value(0) &&
pos.pawn_count(WHITE) == 0 &&
pos.non_pawn_material(BLACK) >= RookValueEndgame) {
mi->evaluationFunction = &EvaluateKKX;
return mi;
}
else if(pos.pawns() == EmptyBoardBB && pos.rooks() == EmptyBoardBB
&& pos.queens() == EmptyBoardBB) {
// Minor piece endgame with at least one minor piece per side,
// and no pawns.
assert(pos.knights(WHITE) | pos.bishops(WHITE));
assert(pos.knights(BLACK) | pos.bishops(BLACK));
if(pos.bishop_count(WHITE) + pos.knight_count(WHITE) <= 2
&& pos.bishop_count(BLACK) + pos.knight_count(BLACK) <= 2) {
mi->evaluationFunction = &EvaluateKmmKm;
return mi;
}
}
// OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function?
//
// The code below is rather messy, and it could easily get worse later,
// if we decide to add more special cases. We face problems when there
// are several conflicting applicable scaling functions and we need to
// decide which one to use.
if(key == KRPKRMaterialKey) {
mi->scalingFunction[WHITE] = &ScaleKRPKR;
return mi;
}
if(key == KRKRPMaterialKey) {
mi->scalingFunction[BLACK] = &ScaleKRKRP;
return mi;
}
if(key == KRPPKRPMaterialKey) {
mi->scalingFunction[WHITE] = &ScaleKRPPKRP;
return mi;
}
else if(key == KRPKRPPMaterialKey) {
mi->scalingFunction[BLACK] = &ScaleKRPKRPP;
return mi;
}
if(key == KBPKBMaterialKey) {
mi->scalingFunction[WHITE] = &ScaleKBPKB;
return mi;
}
if(key == KBKBPMaterialKey) {
mi->scalingFunction[BLACK] = &ScaleKBKBP;
return mi;
}
if(key == KBPKNMaterialKey) {
mi->scalingFunction[WHITE] = &ScaleKBPKN;
return mi;
}
if(key == KNKBPMaterialKey) {
mi->scalingFunction[BLACK] = &ScaleKNKBP;
return mi;
}
if(key == KNPKMaterialKey) {
mi->scalingFunction[WHITE] = &ScaleKNPK;
return mi;
}
if(key == KKNPMaterialKey) {
mi->scalingFunction[BLACK] = &ScaleKKNP;
return mi;
}
if(pos.non_pawn_material(WHITE) == BishopValueMidgame &&
pos.bishop_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
mi->scalingFunction[WHITE] = &ScaleKBPK;
if(pos.non_pawn_material(BLACK) == BishopValueMidgame &&
pos.bishop_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
mi->scalingFunction[BLACK] = &ScaleKKBP;
if(pos.pawn_count(WHITE) == 0 &&
pos.non_pawn_material(WHITE) == QueenValueMidgame &&
pos.queen_count(WHITE) == 1 &&
pos.rook_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
mi->scalingFunction[WHITE] = &ScaleKQKRP;
else if(pos.pawn_count(BLACK) == 0 &&
pos.non_pawn_material(BLACK) == QueenValueMidgame &&
pos.queen_count(BLACK) == 1 &&
pos.rook_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
mi->scalingFunction[BLACK] = &ScaleKRPKQ;
if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0)) {
if(pos.pawn_count(BLACK) == 0) {
assert(pos.pawn_count(WHITE) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK;
}
else if(pos.pawn_count(WHITE) == 0) {
assert(pos.pawn_count(BLACK) >= 2);
mi->scalingFunction[BLACK] = &ScaleKKPs;
}
else if(pos.pawn_count(WHITE) == 1 && pos.pawn_count(BLACK) == 1) {
mi->scalingFunction[WHITE] = &ScaleKPKPw;
mi->scalingFunction[BLACK] = &ScaleKPKPb;
}
}
// Compute the space weight
if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame) {
int minorPieceCount =
pos.knight_count(WHITE) + pos.knight_count(BLACK)
+ pos.bishop_count(WHITE) + pos.bishop_count(BLACK);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material balance.
Color c;
int sign;
Value egValue = Value(0), mgValue = Value(0);
for(c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign) {
// No pawns makes it difficult to win, even with a material advantage:
if(pos.pawn_count(c) == 0 &&
pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c))
<= BishopValueMidgame) {
if(pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c)))
mi->factor[c] = 0;
else if(pos.non_pawn_material(c) < RookValueMidgame)
mi->factor[c] = 0;
else {
switch(pos.bishop_count(c)) {
case 2:
mi->factor[c] = 32; break;
case 1:
mi->factor[c] = 12; break;
case 0:
mi->factor[c] = 6; break;
}
}
}
// Bishop pair:
if(pos.bishop_count(c) >= 2) {
mgValue += sign * BishopPairMidgameBonus;
egValue += sign * BishopPairEndgameBonus;
}
// Knights are stronger when there are many pawns on the board. The
// formula is taken from Larry Kaufman's paper "The Evaluation of Material
// Imbalances in Chess":
// http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
mgValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
egValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
// Redundancy of major pieces, again based on Kaufman's paper:
if(pos.rook_count(c) >= 1) {
Value v = Value((pos.rook_count(c) - 1) * 32 + pos.queen_count(c) * 16);
mgValue -= sign * v;
egValue -= sign * v;
}
}
mi->mgValue = int16_t(mgValue);
mi->egValue = int16_t(egValue);
return mi;
}
glaurung-2.2/src/main.cpp 0000644 0001750 0001750 00000004322 11123021252 014770 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "benchmark.h"
#include "bitboard.h"
#include "direction.h"
#include "endgame.h"
#include "evaluate.h"
#include "material.h"
#include "mersenne.h"
#include "misc.h"
#include "movepick.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "uci.h"
#include "ucioption.h"
////
//// Functions
////
int main(int argc, char *argv[]) {
// Disable IO buffering
setbuf(stdin, NULL);
setbuf(stdout, NULL);
std::cout.rdbuf()->pubsetbuf(NULL, 0);
std::cin.rdbuf()->pubsetbuf(NULL, 0);
// Initialization
init_mersenne();
init_direction_table();
init_bitboards();
init_uci_options();
Position::init_zobrist();
Position::init_piece_square_tables();
MaterialInfo::init();
MovePicker::init_phase_table();
init_eval(1);
init_bitbases();
init_threads();
// Make random number generation less deterministic, for book moves
int i = abs(get_system_time() % 10000);
for(int j = 0; j < i; j++)
genrand_int32();
// Process command line arguments
if(argc >= 2) {
if(std::string(argv[1]) == "bench") {
if(argc != 4) {
std::cout << "Usage: glaurung bench " << std::endl;
exit(0);
}
benchmark(std::string(argv[2]), std::string(argv[3]));
return 0;
}
}
// Print copyright notice
std::cout << engine_name() << ". "
<< "Copyright (C) 2004-2008 Tord Romstad."
<< std::endl;
// Enter UCI mode
uci_main_loop();
return 0;
}
glaurung-2.2/src/position.cpp 0000644 0001750 0001750 00000206354 11123021251 015720 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include
#include
#include "mersenne.h"
#include "movegen.h"
#include "movepick.h"
#include "position.h"
#include "psqtab.h"
#include "ucioption.h"
////
//// Variables
////
int Position::castleRightsMask[64];
Key Position::zobrist[2][8][64];
Key Position::zobEp[64];
Key Position::zobCastle[16];
Key Position::zobMaterial[2][8][16];
Key Position::zobSideToMove;
Value Position::MgPieceSquareTable[16][64];
Value Position::EgPieceSquareTable[16][64];
////
//// Functions
////
/// Constructors
Position::Position() { } // Do we really need this one?
Position::Position(const Position &pos) {
this->copy(pos);
}
Position::Position(const std::string &fen) {
this->from_fen(fen);
}
/// Position::from_fen() initializes the position object with the given FEN
/// string. This function is not very robust - make sure that input FENs are
/// correct (this is assumed to be the responsibility of the GUI).
void Position::from_fen(const std::string &fen) {
File file;
Rank rank;
int i;
this->clear();
// Board
rank = RANK_8;
file = FILE_A;
for(i = 0; fen[i] != ' '; i++) {
if(isdigit(fen[i]))
// Skip the given number of files
file += (fen[i] - '1' + 1);
else {
Square square = make_square(file, rank);
switch(fen[i]) {
case 'K': this->put_piece(WK, square); file++; break;
case 'Q': this->put_piece(WQ, square); file++; break;
case 'R': this->put_piece(WR, square); file++; break;
case 'B': this->put_piece(WB, square); file++; break;
case 'N': this->put_piece(WN, square); file++; break;
case 'P': this->put_piece(WP, square); file++; break;
case 'k': this->put_piece(BK, square); file++; break;
case 'q': this->put_piece(BQ, square); file++; break;
case 'r': this->put_piece(BR, square); file++; break;
case 'b': this->put_piece(BB, square); file++; break;
case 'n': this->put_piece(BN, square); file++; break;
case 'p': this->put_piece(BP, square); file++; break;
case '/': file = FILE_A; rank--; break;
case ' ': break;
default:
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
}
}
// Side to move
i++;
if(fen[i] == 'w')
sideToMove = WHITE;
else if(fen[i] == 'b')
sideToMove = BLACK;
else {
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
// Castling rights:
i++;
if(fen[i] != ' ') {
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
i++;
while(strchr("KQkqabcdefghABCDEFGH-", fen[i])) {
if(fen[i] == '-') {
i++; break;
}
else if(fen[i] == 'K') this->allow_oo(WHITE);
else if(fen[i] == 'Q') this->allow_ooo(WHITE);
else if(fen[i] == 'k') this->allow_oo(BLACK);
else if(fen[i] == 'q') this->allow_ooo(BLACK);
else if(fen[i] >= 'A' && fen[i] <= 'H') {
File rookFile, kingFile = FILE_NONE;
for(Square square = SQ_B1; square <= SQ_G1; square++)
if(this->piece_on(square) == WK)
kingFile = square_file(square);
if(kingFile == FILE_NONE) {
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
initialKFile = kingFile;
rookFile = File(fen[i] - 'A') + FILE_A;
if(rookFile < initialKFile) {
this->allow_ooo(WHITE);
initialQRFile = rookFile;
}
else {
this->allow_oo(WHITE);
initialKRFile = rookFile;
}
}
else if(fen[i] >= 'a' && fen[i] <= 'h') {
File rookFile, kingFile = FILE_NONE;
for(Square square = SQ_B8; square <= SQ_G8; square++)
if(this->piece_on(square) == BK)
kingFile = square_file(square);
if(kingFile == FILE_NONE) {
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
initialKFile = kingFile;
rookFile = File(fen[i] - 'a') + FILE_A;
if(rookFile < initialKFile) {
this->allow_ooo(BLACK);
initialQRFile = rookFile;
}
else {
this->allow_oo(BLACK);
initialKRFile = rookFile;
}
}
else {
std::cout << "Error in FEN at character " << i << std::endl;
return;
}
i++;
}
while(fen[i] == ' ')
i++;
// En passant square
if(i < int(fen.length()) - 2)
if(fen[i] >= 'a' && fen[i] <= 'h' && (fen[i+1] == '3' || fen[i+1] == '6'))
epSquare = square_from_string(fen.substr(i, 2));
// Various initialisation
for(Square sq = SQ_A1; sq <= SQ_H8; sq++)
castleRightsMask[sq] = ALL_CASTLES;
castleRightsMask[make_square(initialKFile, RANK_1)] ^=
(WHITE_OO|WHITE_OOO);
castleRightsMask[make_square(initialKFile, RANK_8)] ^=
(BLACK_OO|BLACK_OOO);
castleRightsMask[make_square(initialKRFile, RANK_1)] ^= WHITE_OO;
castleRightsMask[make_square(initialKRFile, RANK_8)] ^= BLACK_OO;
castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO;
castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO;
this->find_checkers();
key = this->compute_key();
pawnKey = this->compute_pawn_key();
materialKey = this->compute_material_key();
mgValue = this->compute_mg_value();
egValue = this->compute_eg_value();
npMaterial[WHITE] = this->compute_non_pawn_material(WHITE);
npMaterial[BLACK] = this->compute_non_pawn_material(BLACK);
}
/// Position::to_fen() converts the position object to a FEN string. This is
/// probably only useful for debugging.
const std::string Position::to_fen() const {
char pieceLetters[] = " PNBRQK pnbrqk";
std::string result;
int skip;
for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
skip = 0;
for(File file = FILE_A; file <= FILE_H; file++) {
Square square = make_square(file, rank);
if(this->square_is_occupied(square)) {
if(skip > 0) result += (char)skip + '0';
result += pieceLetters[this->piece_on(square)];
skip = 0;
}
else skip++;
}
if(skip > 0) result += (char)skip + '0';
result += (rank > RANK_1)? '/' : ' ';
}
result += (sideToMove == WHITE)? 'w' : 'b';
result += ' ';
if(castleRights == NO_CASTLES) result += '-';
else {
if(this->can_castle_kingside(WHITE)) result += 'K';
if(this->can_castle_queenside(WHITE)) result += 'Q';
if(this->can_castle_kingside(BLACK)) result += 'k';
if(this->can_castle_queenside(BLACK)) result += 'q';
}
result += ' ';
if(this->ep_square() == SQ_NONE) result += '-';
else result += square_to_string(this->ep_square());
return result;
}
/// Position::print() prints an ASCII representation of the position to
/// the standard output.
void Position::print() const {
char pieceStrings[][8] =
{"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
"| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
};
for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
std::cout << "+---+---+---+---+---+---+---+---+\n";
for(File file = FILE_A; file <= FILE_H; file++) {
Square sq = make_square(file, rank);
Piece piece = this->piece_on(sq);
if(piece == EMPTY)
std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
else
std::cout << pieceStrings[piece];
}
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+\n";
std::cout << this->to_fen() << std::endl;
std::cout << key << std::endl;
}
/// Position::copy() creates a copy of the input position.
void Position::copy(const Position &pos) {
memcpy(this, &pos, sizeof(Position));
}
/// Position:pinned_pieces() returns a bitboard of all pinned (against the
/// king) pieces for the given color.
Bitboard Position::pinned_pieces(Color c) const {
Bitboard b1, b2, pinned, pinners, sliders;
Square ksq = this->king_square(c), s;
Color them = opposite_color(c);
pinned = EmptyBoardBB;
b1 = this->occupied_squares();
sliders = this->rooks_and_queens(them) & ~this->checkers();
if(sliders & RookPseudoAttacks[ksq]) {
b2 = this->rook_attacks(ksq) & this->pieces_of_color(c);
pinners = rook_attacks_bb(ksq, b1 ^ b2) & sliders;
while(pinners) {
s = pop_1st_bit(&pinners);
pinned |= (squares_between(s, ksq) & b2);
}
}
sliders = this->bishops_and_queens(them) & ~this->checkers();
if(sliders & BishopPseudoAttacks[ksq]) {
b2 = this->bishop_attacks(ksq) & this->pieces_of_color(c);
pinners = bishop_attacks_bb(ksq, b1 ^ b2) & sliders;
while(pinners) {
s = pop_1st_bit(&pinners);
pinned |= (squares_between(s, ksq) & b2);
}
}
return pinned;
}
/// Position:discovered_check_candidates() returns a bitboard containing all
/// pieces for the given side which are candidates for giving a discovered
/// check. The code is almost the same as the function for finding pinned
/// pieces.
Bitboard Position::discovered_check_candidates(Color c) const {
Bitboard b1, b2, dc, checkers, sliders;
Square ksq = this->king_square(opposite_color(c)), s;
dc = EmptyBoardBB;
b1 = this->occupied_squares();
sliders = this->rooks_and_queens(c);
if(sliders & RookPseudoAttacks[ksq]) {
b2 = this->rook_attacks(ksq) & this->pieces_of_color(c);
checkers = rook_attacks_bb(ksq, b1 ^ b2) & sliders;
while(checkers) {
s = pop_1st_bit(&checkers);
dc |= (squares_between(s, ksq) & b2);
}
}
sliders = this->bishops_and_queens(c);
if(sliders & BishopPseudoAttacks[ksq]) {
b2 = this->bishop_attacks(ksq) & this->pieces_of_color(c);
checkers = bishop_attacks_bb(ksq, b1 ^ b2) & sliders;
while(checkers) {
s = pop_1st_bit(&checkers);
dc |= (squares_between(s, ksq) & b2);
}
}
return dc;
}
/// Position::square_is_attacked() checks whether the given side attacks the
/// given square.
bool Position::square_is_attacked(Square s, Color c) const {
return
(this->pawn_attacks(opposite_color(c), s) & this->pawns(c)) ||
(this->knight_attacks(s) & this->knights(c)) ||
(this->king_attacks(s) & this->kings(c)) ||
(this->rook_attacks(s) & this->rooks_and_queens(c)) ||
(this->bishop_attacks(s) & this->bishops_and_queens(c));
}
/// Position::attacks_to() computes a bitboard containing all pieces which
/// attacks a given square. There are two versions of this function: One
/// which finds attackers of both colors, and one which only finds the
/// attackers for one side.
Bitboard Position::attacks_to(Square s) const {
return
(this->black_pawn_attacks(s) & this->pawns(WHITE)) |
(this->white_pawn_attacks(s) & this->pawns(BLACK)) |
(this->knight_attacks(s) & this->pieces_of_type(KNIGHT)) |
(this->rook_attacks(s) & this->rooks_and_queens()) |
(this->bishop_attacks(s) & this->bishops_and_queens()) |
(this->king_attacks(s) & this->pieces_of_type(KING));
}
Bitboard Position::attacks_to(Square s, Color c) const {
return this->attacks_to(s) & this->pieces_of_color(c);
}
/// Position::piece_attacks_square() tests whether the piece on square f
/// attacks square t.
bool Position::piece_attacks_square(Square f, Square t) const {
assert(square_is_ok(f));
assert(square_is_ok(t));
switch(this->piece_on(f)) {
case WP: return this->white_pawn_attacks_square(f, t);
case BP: return this->black_pawn_attacks_square(f, t);
case WN: case BN: return this->knight_attacks_square(f, t);
case WB: case BB: return this->bishop_attacks_square(f, t);
case WR: case BR: return this->rook_attacks_square(f, t);
case WQ: case BQ: return this->queen_attacks_square(f, t);
case WK: case BK: return this->king_attacks_square(f, t);
default: return false;
}
return false;
}
/// Position::find_checkers() computes the checkersBB bitboard, which
/// contains a nonzero bit for each checking piece (0, 1 or 2). It
/// currently works by calling Position::attacks_to, which is probably
/// inefficient. Consider rewriting this function to use the last move
/// played, like in non-bitboard versions of Glaurung.
void Position::find_checkers() {
checkersBB = attacks_to(this->king_square(this->side_to_move()),
opposite_color(this->side_to_move()));
}
/// Position::move_is_legal() tests whether a pseudo-legal move is legal.
/// There are two versions of this function: One which takes only a
/// move as input, and one which takes a move and a bitboard of pinned
/// pieces. The latter function is faster, and should always be preferred
/// when a pinned piece bitboard has already been computed.
bool Position::move_is_legal(Move m) const {
return this->move_is_legal(m, this->pinned_pieces(this->side_to_move()));
}
bool Position::move_is_legal(Move m, Bitboard pinned) const {
Color us, them;
Square ksq, from;
assert(this->is_ok());
assert(move_is_ok(m));
assert(pinned == this->pinned_pieces(this->side_to_move()));
// If we're in check, all pseudo-legal moves are legal, because our
// check evasion generator only generates true legal moves.
if(this->is_check()) return true;
// Castling moves are checked for legality during move generation.
if(move_is_castle(m)) return true;
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
ksq = this->king_square(us);
assert(this->color_of_piece_on(from) == us);
assert(this->piece_on(ksq) == king_of_color(us));
// En passant captures are a tricky special case. Because they are
// rather uncommon, we do it simply by testing whether the king is attacked
// after the move is made:
if(move_is_ep(m)) {
Square to = move_to(m);
Square capsq = make_square(square_file(to), square_rank(from));
Bitboard b = this->occupied_squares();
assert(to == this->ep_square());
assert(this->piece_on(from) == pawn_of_color(us));
assert(this->piece_on(capsq) == pawn_of_color(them));
assert(this->piece_on(to) == EMPTY);
clear_bit(&b, from); clear_bit(&b, capsq); set_bit(&b, to);
return
(!(rook_attacks_bb(ksq, b) & this->rooks_and_queens(them)) &&
!(bishop_attacks_bb(ksq, b) & this->bishops_and_queens(them)));
}
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent.
if(from == ksq) return !(this->square_is_attacked(move_to(m), them));
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
if(!bit_is_set(pinned, from)) return true;
if(direction_between_squares(from, ksq) ==
direction_between_squares(move_to(m), ksq))
return true;
return false;
}
/// Position::move_is_check() tests whether a pseudo-legal move is a check.
/// There are two versions of this function: One which takes only a move as
/// input, and one which takes a move and a bitboard of discovered check
/// candidates. The latter function is faster, and should always be preferred
/// when a discovered check candidates bitboard has already been computed.
bool Position::move_is_check(Move m) const {
Bitboard dc = this->discovered_check_candidates(this->side_to_move());
return this->move_is_check(m, dc);
}
bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
Color us, them;
Square ksq, from, to;
assert(this->is_ok());
assert(move_is_ok(m));
assert(dcCandidates ==
this->discovered_check_candidates(this->side_to_move()));
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
to = move_to(m);
ksq = this->king_square(them);
assert(this->color_of_piece_on(from) == us);
assert(this->piece_on(ksq) == king_of_color(them));
// Proceed according to the type of the moving piece:
switch(this->type_of_piece_on(from)) {
case PAWN:
// Normal check?
if(bit_is_set(this->pawn_attacks(them, ksq), to))
return true;
// Discovered check?
else if(bit_is_set(dcCandidates, from) &&
direction_between_squares(from, ksq) !=
direction_between_squares(to, ksq))
return true;
// Promotion with check?
else if(move_promotion(m)) {
Bitboard b = this->occupied_squares();
clear_bit(&b, from);
switch(move_promotion(m)) {
case KNIGHT:
return this->knight_attacks_square(to, ksq);
case BISHOP:
return bit_is_set(bishop_attacks_bb(to, b), ksq);
case ROOK:
return bit_is_set(rook_attacks_bb(to, b), ksq);
case QUEEN:
return bit_is_set(queen_attacks_bb(to, b), ksq);
default:
assert(false);
}
}
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through the
// captured pawn.
else if(move_is_ep(m)) {
Square capsq = make_square(square_file(to), square_rank(from));
Bitboard b = this->occupied_squares();
clear_bit(&b, from); clear_bit(&b, capsq); set_bit(&b, to);
return
((rook_attacks_bb(ksq, b) & this->rooks_and_queens(us)) ||
(bishop_attacks_bb(ksq, b) & this->bishops_and_queens(us)));
}
return false;
case KNIGHT:
// Discovered check?
if(bit_is_set(dcCandidates, from))
return true;
// Normal check?
else
return bit_is_set(this->knight_attacks(ksq), to);
case BISHOP:
// Discovered check?
if(bit_is_set(dcCandidates, from))
return true;
// Normal check?
else
return bit_is_set(this->bishop_attacks(ksq), to);
case ROOK:
// Discovered check?
if(bit_is_set(dcCandidates, from))
return true;
// Normal check?
else
return bit_is_set(this->rook_attacks(ksq), to);
case QUEEN:
// Discovered checks are impossible!
assert(!bit_is_set(dcCandidates, from));
// Normal check?
return bit_is_set(this->queen_attacks(ksq), to);
case KING:
// Discovered check?
if(bit_is_set(dcCandidates, from) &&
direction_between_squares(from, ksq) !=
direction_between_squares(to, ksq))
return true;
// Castling with check?
if(move_is_castle(m)) {
Square kfrom, kto, rfrom, rto;
Bitboard b = this->occupied_squares();
kfrom = from;
rfrom = to;
if(rfrom > kfrom) {
kto = relative_square(us, SQ_G1);
rto = relative_square(us, SQ_F1);
}
else {
kto = relative_square(us, SQ_C1);
rto = relative_square(us, SQ_D1);
}
clear_bit(&b, kfrom); clear_bit(&b, rfrom);
set_bit(&b, rto); set_bit(&b, kto);
return bit_is_set(rook_attacks_bb(rto, b), ksq);
}
return false;
default:
assert(false);
return false;
}
assert(false);
return false;
}
/// Position::move_is_capture() tests whether a move from the current
/// position is a capture.
bool Position::move_is_capture(Move m) const {
return
this->color_of_piece_on(move_to(m)) == opposite_color(this->side_to_move())
|| move_is_ep(m);
}
/// Position::move_attacks_square() tests whether a move from the current
/// position attacks a given square. Only attacks by the moving piece are
/// considered; the function does not handle X-ray attacks.
bool Position::move_attacks_square(Move m, Square s) const {
assert(move_is_ok(m));
assert(square_is_ok(s));
Square f = move_from(m), t = move_to(m);
assert(this->square_is_occupied(f));
switch(this->piece_on(f)) {
case WP: return this->white_pawn_attacks_square(t, s);
case BP: return this->black_pawn_attacks_square(t, s);
case WN: case BN: return this->knight_attacks_square(t, s);
case WB: case BB: return this->bishop_attacks_square(t, s);
case WR: case BR: return this->rook_attacks_square(t, s);
case WQ: case BQ: return this->queen_attacks_square(t, s);
case WK: case BK: return this->king_attacks_square(t, s);
default: assert(false);
}
return false;
}
/// Position::backup() is called when making a move. All information
/// necessary to restore the position when the move is later unmade
/// is saved to an UndoInfo object. The function Position::restore
/// does the reverse operation: When one does a backup followed by
/// a restore with the same UndoInfo object, the position is restored
/// to the state before backup was called.
void Position::backup(UndoInfo &u) const {
u.castleRights = castleRights;
u.epSquare = epSquare;
u.checkersBB = checkersBB;
u.key = key;
u.pawnKey = pawnKey;
u.materialKey = materialKey;
u.rule50 = rule50;
u.lastMove = lastMove;
u.capture = NO_PIECE_TYPE;
u.mgValue = mgValue;
u.egValue = egValue;
}
/// Position::restore() is called when unmaking a move. It copies back
/// the information backed up during a previous call to Position::backup.
void Position::restore(const UndoInfo &u) {
castleRights = u.castleRights;
epSquare = u.epSquare;
checkersBB = u.checkersBB;
key = u.key;
pawnKey = u.pawnKey;
materialKey = u.materialKey;
rule50 = u.rule50;
lastMove = u.lastMove;
mgValue = u.mgValue;
egValue = u.egValue;
}
/// Position::do_move() makes a move, and backs up all information necessary
/// to undo the move to an UndoInfo object. The move is assumed to be legal.
/// Pseudo-legal moves should be filtered out before this function is called.
/// There are two versions of this function, one which takes only the move and
/// the UndoInfo as input, and one which takes a third parameter, a bitboard of
/// discovered check candidates. The second version is faster, because knowing
/// the discovered check candidates makes it easier to update the checkersBB
/// member variable in the position object.
void Position::do_move(Move m, UndoInfo &u) {
this->do_move(m, u, this->discovered_check_candidates(this->side_to_move()));
}
void Position::do_move(Move m, UndoInfo &u, Bitboard dcCandidates) {
assert(this->is_ok());
assert(move_is_ok(m));
// Back up the necessary information to our UndoInfo object (except the
// captured piece, which is taken care of later:
this->backup(u);
// Save the current key to the history[] array, in order to be able to
// detect repetition draws:
history[gamePly] = key;
// Increment the 50 moves rule draw counter. Resetting it to zero in the
// case of non-reversible moves is taken care of later.
rule50++;
if(move_is_castle(m))
this->do_castle_move(m);
else if(move_promotion(m))
this->do_promotion_move(m, u);
else if(move_is_ep(m))
this->do_ep_move(m);
else {
Color us, them;
Square from, to;
PieceType piece, capture;
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
to = move_to(m);
assert(this->color_of_piece_on(from) == us);
assert(this->color_of_piece_on(to) == them || this->piece_on(to) == EMPTY);
piece = this->type_of_piece_on(from);
capture = this->type_of_piece_on(to);
if(capture) {
assert(capture != KING);
// Remove captured piece:
clear_bit(&(byColorBB[them]), to);
clear_bit(&(byTypeBB[capture]), to);
// Update hash key:
key ^= zobrist[them][capture][to];
// If the captured piece was a pawn, update pawn hash key:
if(capture == PAWN)
pawnKey ^= zobrist[them][PAWN][to];
// Update incremental scores:
mgValue -= this->mg_pst(them, capture, to);
egValue -= this->eg_pst(them, capture, to);
// Update material:
if(capture != PAWN)
npMaterial[them] -= piece_value_midgame(capture);
// Update material hash key:
materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
// Update piece count:
pieceCount[them][capture]--;
// Update piece list:
pieceList[them][capture][index[to]] =
pieceList[them][capture][pieceCount[them][capture]];
index[pieceList[them][capture][index[to]]] = index[to];
// Remember the captured piece, in order to be able to undo the move
// correctly:
u.capture = capture;
// Reset rule 50 counter:
rule50 = 0;
}
// Move the piece:
clear_bit(&(byColorBB[us]), from);
clear_bit(&(byTypeBB[piece]), from);
clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
set_bit(&(byColorBB[us]), to);
set_bit(&(byTypeBB[piece]), to);
set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
board[to] = board[from];
board[from] = EMPTY;
// Update hash key:
key ^= zobrist[us][piece][from] ^ zobrist[us][piece][to];
// Update incremental scores:
mgValue -= this->mg_pst(us, piece, from);
mgValue += this->mg_pst(us, piece, to);
egValue -= this->eg_pst(us, piece, from);
egValue += this->eg_pst(us, piece, to);
// If the moving piece was a king, update the king square:
if(piece == KING)
kingSquare[us] = to;
// If the move was a double pawn push, set the en passant square.
// This code is a bit ugly right now, and should be cleaned up later.
// FIXME
if(epSquare != SQ_NONE) {
key ^= zobEp[epSquare];
epSquare = SQ_NONE;
}
if(piece == PAWN) {
if(abs(int(to) - int(from)) == 16) {
if((us == WHITE && (this->white_pawn_attacks(from + DELTA_N) &
this->pawns(BLACK))) ||
(us == BLACK && (this->black_pawn_attacks(from + DELTA_S) &
this->pawns(WHITE)))) {
epSquare = Square((int(from) + int(to)) / 2);
key ^= zobEp[epSquare];
}
}
// Reset rule 50 draw counter.
rule50 = 0;
// Update pawn hash key:
pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
}
// Update piece lists:
pieceList[us][piece][index[from]] = to;
index[to] = index[from];
// Update castle rights:
key ^= zobCastle[castleRights];
castleRights &= castleRightsMask[from];
castleRights &= castleRightsMask[to];
key ^= zobCastle[castleRights];
// Update checkers bitboard:
checkersBB = EmptyBoardBB;
Square ksq = this->king_square(them);
switch(piece) {
case PAWN:
if(bit_is_set(this->pawn_attacks(them, ksq), to))
set_bit(&checkersBB, to);
if(bit_is_set(dcCandidates, from))
checkersBB |=
((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
(this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
break;
case KNIGHT:
if(bit_is_set(this->knight_attacks(ksq), to))
set_bit(&checkersBB, to);
if(bit_is_set(dcCandidates, from))
checkersBB |=
((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
(this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
break;
case BISHOP:
if(bit_is_set(this->bishop_attacks(ksq), to))
set_bit(&checkersBB, to);
if(bit_is_set(dcCandidates, from))
checkersBB |=
(this->rook_attacks(ksq) & this->rooks_and_queens(us));
break;
case ROOK:
if(bit_is_set(this->rook_attacks(ksq), to))
set_bit(&checkersBB, to);
if(bit_is_set(dcCandidates, from))
checkersBB |=
(this->bishop_attacks(ksq) & this->bishops_and_queens(us));
break;
case QUEEN:
if(bit_is_set(this->queen_attacks(ksq), to))
set_bit(&checkersBB, to);
break;
case KING:
if(bit_is_set(dcCandidates, from))
checkersBB |=
((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
(this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
break;
default:
assert(false);
break;
}
}
// Finish
key ^= zobSideToMove;
sideToMove = opposite_color(sideToMove);
gamePly++;
mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
assert(this->is_ok());
}
/// Position::do_castle_move() is a private method used to make a castling
/// move. It is called from the main Position::do_move function. Note that
/// castling moves are encoded as "king captures friendly rook" moves, for
/// instance white short castling in a non-Chess960 game is encoded as e1h1.
void Position::do_castle_move(Move m) {
Color us, them;
Square kfrom, kto, rfrom, rto;
assert(this->is_ok());
assert(move_is_ok(m));
assert(move_is_castle(m));
us = this->side_to_move();
them = opposite_color(us);
// Find source squares for king and rook:
kfrom = move_from(m);
rfrom = move_to(m); // HACK: See comment at beginning of function.
assert(this->piece_on(kfrom) == king_of_color(us));
assert(this->piece_on(rfrom) == rook_of_color(us));
// Find destination squares for king and rook:
if(rfrom > kfrom) { // O-O
kto = relative_square(us, SQ_G1);
rto = relative_square(us, SQ_F1);
}
else { // O-O-O
kto = relative_square(us, SQ_C1);
rto = relative_square(us, SQ_D1);
}
// Remove pieces from source squares:
clear_bit(&(byColorBB[us]), kfrom);
clear_bit(&(byTypeBB[KING]), kfrom);
clear_bit(&(byTypeBB[0]), kfrom); // HACK: byTypeBB[0] == occupied squares
clear_bit(&(byColorBB[us]), rfrom);
clear_bit(&(byTypeBB[ROOK]), rfrom);
clear_bit(&(byTypeBB[0]), rfrom); // HACK: byTypeBB[0] == occupied squares
// Put pieces on destination squares:
set_bit(&(byColorBB[us]), kto);
set_bit(&(byTypeBB[KING]), kto);
set_bit(&(byTypeBB[0]), kto); // HACK: byTypeBB[0] == occupied squares
set_bit(&(byColorBB[us]), rto);
set_bit(&(byTypeBB[ROOK]), rto);
set_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
// Update board array:
board[kfrom] = board[rfrom] = EMPTY;
board[kto] = king_of_color(us);
board[rto] = rook_of_color(us);
// Update king square:
kingSquare[us] = kto;
// Update piece lists:
pieceList[us][KING][index[kfrom]] = kto;
pieceList[us][ROOK][index[rfrom]] = rto;
int tmp = index[rfrom];
index[kto] = index[kfrom];
index[rto] = tmp;
// Update incremental scores:
mgValue -= this->mg_pst(us, KING, kfrom);
mgValue += this->mg_pst(us, KING, kto);
egValue -= this->eg_pst(us, KING, kfrom);
egValue += this->eg_pst(us, KING, kto);
mgValue -= this->mg_pst(us, ROOK, rfrom);
mgValue += this->mg_pst(us, ROOK, rto);
egValue -= this->eg_pst(us, ROOK, rfrom);
egValue += this->eg_pst(us, ROOK, rto);
// Update hash key:
key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto];
// Clear en passant square:
if(epSquare != SQ_NONE) {
key ^= zobEp[epSquare];
epSquare = SQ_NONE;
}
// Update castling rights:
key ^= zobCastle[castleRights];
castleRights &= castleRightsMask[kfrom];
key ^= zobCastle[castleRights];
// Reset rule 50 counter:
rule50 = 0;
// Update checkers BB:
checkersBB = attacks_to(this->king_square(them), us);
}
/// Position::do_promotion_move() is a private method used to make a promotion
/// move. It is called from the main Position::do_move function. The
/// UndoInfo object, which has been initialized in Position::do_move, is
/// used to store the captured piece (if any).
void Position::do_promotion_move(Move m, UndoInfo &u) {
Color us, them;
Square from, to;
PieceType capture, promotion;
assert(this->is_ok());
assert(move_is_ok(m));
assert(move_promotion(m));
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
to = move_to(m);
assert(pawn_rank(us, to) == RANK_8);
assert(this->piece_on(from) == pawn_of_color(us));
assert(this->color_of_piece_on(to) == them || this->square_is_empty(to));
capture = this->type_of_piece_on(to);
if(capture) {
assert(capture != KING);
// Remove captured piece:
clear_bit(&(byColorBB[them]), to);
clear_bit(&(byTypeBB[capture]), to);
// Update hash key:
key ^= zobrist[them][capture][to];
// Update incremental scores:
mgValue -= this->mg_pst(them, capture, to);
egValue -= this->eg_pst(them, capture, to);
// Update material. Because our move is a promotion, we know that the
// captured piece is not a pawn.
assert(capture != PAWN);
npMaterial[them] -= piece_value_midgame(capture);
// Update material hash key:
materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
// Update piece count:
pieceCount[them][capture]--;
// Update piece list:
pieceList[them][capture][index[to]] =
pieceList[them][capture][pieceCount[them][capture]];
index[pieceList[them][capture][index[to]]] = index[to];
// Remember the captured piece, in order to be able to undo the move
// correctly:
u.capture = capture;
}
// Remove pawn:
clear_bit(&(byColorBB[us]), from);
clear_bit(&(byTypeBB[PAWN]), from);
clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
board[from] = EMPTY;
// Insert promoted piece:
promotion = move_promotion(m);
assert(promotion >= KNIGHT && promotion <= QUEEN);
set_bit(&(byColorBB[us]), to);
set_bit(&(byTypeBB[promotion]), to);
set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
board[to] = piece_of_color_and_type(us, promotion);
// Update hash key:
key ^= zobrist[us][PAWN][from] ^ zobrist[us][promotion][to];
// Update pawn hash key:
pawnKey ^= zobrist[us][PAWN][from];
// Update material key:
materialKey ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]];
materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1];
// Update piece counts:
pieceCount[us][PAWN]--;
pieceCount[us][promotion]++;
// Update piece lists:
pieceList[us][PAWN][index[from]] =
pieceList[us][PAWN][pieceCount[us][PAWN]];
index[pieceList[us][PAWN][index[from]]] = index[from];
pieceList[us][promotion][pieceCount[us][promotion] - 1] = to;
index[to] = pieceCount[us][promotion] - 1;
// Update incremental scores:
mgValue -= this->mg_pst(us, PAWN, from);
mgValue += this->mg_pst(us, promotion, to);
egValue -= this->eg_pst(us, PAWN, from);
egValue += this->eg_pst(us, promotion, to);
// Update material:
npMaterial[us] += piece_value_midgame(promotion);
// Clear the en passant square:
if(epSquare != SQ_NONE) {
key ^= zobEp[epSquare];
epSquare = SQ_NONE;
}
// Update castle rights:
key ^= zobCastle[castleRights];
castleRights &= castleRightsMask[to];
key ^= zobCastle[castleRights];
// Reset rule 50 counter:
rule50 = 0;
// Update checkers BB:
checkersBB = attacks_to(this->king_square(them), us);
}
/// Position::do_ep_move() is a private method used to make an en passant
/// capture. It is called from the main Position::do_move function. Because
/// the captured piece is always a pawn, we don't need to pass an UndoInfo
/// object in which to store the captured piece.
void Position::do_ep_move(Move m) {
Color us, them;
Square from, to, capsq;
assert(this->is_ok());
assert(move_is_ok(m));
assert(move_is_ep(m));
us = this->side_to_move();
them = opposite_color(us);
// Find from, to and capture squares:
from = move_from(m);
to = move_to(m);
capsq = (us == WHITE)? (to - DELTA_N) : (to - DELTA_S);
assert(to == epSquare);
assert(pawn_rank(us, to) == RANK_6);
assert(this->piece_on(to) == EMPTY);
assert(this->piece_on(from) == pawn_of_color(us));
assert(this->piece_on(capsq) == pawn_of_color(them));
// Remove captured piece:
clear_bit(&(byColorBB[them]), capsq);
clear_bit(&(byTypeBB[PAWN]), capsq);
clear_bit(&(byTypeBB[0]), capsq); // HACK: byTypeBB[0] == occupied squares
board[capsq] = EMPTY;
// Remove moving piece from source square:
clear_bit(&(byColorBB[us]), from);
clear_bit(&(byTypeBB[PAWN]), from);
clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
// Put moving piece on destination square:
set_bit(&(byColorBB[us]), to);
set_bit(&(byTypeBB[PAWN]), to);
set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
board[to] = board[from];
board[from] = EMPTY;
// Update material hash key:
materialKey ^= zobMaterial[them][PAWN][pieceCount[them][PAWN]];
// Update piece count:
pieceCount[them][PAWN]--;
// Update piece list:
pieceList[us][PAWN][index[from]] = to;
index[to] = index[from];
pieceList[them][PAWN][index[capsq]] =
pieceList[them][PAWN][pieceCount[them][PAWN]];
index[pieceList[them][PAWN][index[capsq]]] = index[capsq];
// Update hash key:
key ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
key ^= zobrist[them][PAWN][capsq];
key ^= zobEp[epSquare];
// Update pawn hash key:
pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
pawnKey ^= zobrist[them][PAWN][capsq];
// Update incremental scores:
mgValue -= this->mg_pst(them, PAWN, capsq);
mgValue -= this->mg_pst(us, PAWN, from);
mgValue += this->mg_pst(us, PAWN, to);
egValue -= this->eg_pst(them, PAWN, capsq);
egValue -= this->eg_pst(us, PAWN, from);
egValue += this->eg_pst(us, PAWN, to);
// Reset en passant square:
epSquare = SQ_NONE;
// Reset rule 50 counter:
rule50 = 0;
// Update checkers BB:
checkersBB = attacks_to(this->king_square(them), us);
}
/// Position::undo_move() unmakes a move. When it returns, the position should
/// be restored to exactly the same state as before the move was made. It is
/// important that Position::undo_move is called with the same move and UndoInfo
/// object as the earlier call to Position::do_move.
void Position::undo_move(Move m, const UndoInfo &u) {
assert(this->is_ok());
assert(move_is_ok(m));
gamePly--;
sideToMove = opposite_color(sideToMove);
// Restore information from our UndoInfo object (except the captured piece,
// which is taken care of later):
this->restore(u);
if(move_is_castle(m))
this->undo_castle_move(m);
else if(move_promotion(m))
this->undo_promotion_move(m, u);
else if(move_is_ep(m))
this->undo_ep_move(m);
else {
Color us, them;
Square from, to;
PieceType piece, capture;
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
to = move_to(m);
assert(this->piece_on(from) == EMPTY);
assert(color_of_piece_on(to) == us);
// Put the piece back at the source square:
piece = this->type_of_piece_on(to);
set_bit(&(byColorBB[us]), from);
set_bit(&(byTypeBB[piece]), from);
set_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
board[from] = piece_of_color_and_type(us, piece);
// Clear the destination square
clear_bit(&(byColorBB[us]), to);
clear_bit(&(byTypeBB[piece]), to);
clear_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
// If the moving piece was a king, update the king square:
if(piece == KING)
kingSquare[us] = from;
// Update piece list:
pieceList[us][piece][index[to]] = from;
index[from] = index[to];
capture = u.capture;
if(capture) {
assert(capture != KING);
// Replace the captured piece:
set_bit(&(byColorBB[them]), to);
set_bit(&(byTypeBB[capture]), to);
set_bit(&(byTypeBB[0]), to);
board[to] = piece_of_color_and_type(them, capture);
// Update material:
if(capture != PAWN)
npMaterial[them] += piece_value_midgame(capture);
// Update piece list:
pieceList[them][capture][pieceCount[them][capture]] = to;
index[to] = pieceCount[them][capture];
// Update piece count:
pieceCount[them][capture]++;
}
else
board[to] = EMPTY;
}
assert(this->is_ok());
}
/// Position::undo_castle_move() is a private method used to unmake a castling
/// move. It is called from the main Position::undo_move function. Note that
/// castling moves are encoded as "king captures friendly rook" moves, for
/// instance white short castling in a non-Chess960 game is encoded as e1h1.
void Position::undo_castle_move(Move m) {
Color us, them;
Square kfrom, kto, rfrom, rto;
assert(move_is_ok(m));
assert(move_is_castle(m));
// When we have arrived here, some work has already been done by
// Position::undo_move. In particular, the side to move has been switched,
// so the code below is correct.
us = this->side_to_move();
them = opposite_color(us);
// Find source squares for king and rook:
kfrom = move_from(m);
rfrom = move_to(m); // HACK: See comment at beginning of function.
// Find destination squares for king and rook:
if(rfrom > kfrom) { // O-O
kto = relative_square(us, SQ_G1);
rto = relative_square(us, SQ_F1);
}
else { // O-O-O
kto = relative_square(us, SQ_C1);
rto = relative_square(us, SQ_D1);
}
assert(this->piece_on(kto) == king_of_color(us));
assert(this->piece_on(rto) == rook_of_color(us));
// Remove pieces from destination squares:
clear_bit(&(byColorBB[us]), kto);
clear_bit(&(byTypeBB[KING]), kto);
clear_bit(&(byTypeBB[0]), kto); // HACK: byTypeBB[0] == occupied squares
clear_bit(&(byColorBB[us]), rto);
clear_bit(&(byTypeBB[ROOK]), rto);
clear_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
// Put pieces on source squares:
set_bit(&(byColorBB[us]), kfrom);
set_bit(&(byTypeBB[KING]), kfrom);
set_bit(&(byTypeBB[0]), kfrom); // HACK: byTypeBB[0] == occupied squares
set_bit(&(byColorBB[us]), rfrom);
set_bit(&(byTypeBB[ROOK]), rfrom);
set_bit(&(byTypeBB[0]), rfrom); // HACK: byTypeBB[0] == occupied squares
// Update board:
board[rto] = board[kto] = EMPTY;
board[rfrom] = rook_of_color(us);
board[kfrom] = king_of_color(us);
// Update king square:
kingSquare[us] = kfrom;
// Update piece lists:
pieceList[us][KING][index[kto]] = kfrom;
pieceList[us][ROOK][index[rto]] = rfrom;
int tmp = index[rto]; // Necessary because we may have rto == kfrom in FRC.
index[kfrom] = index[kto];
index[rfrom] = tmp;
}
/// Position::undo_promotion_move() is a private method used to unmake a
/// promotion move. It is called from the main Position::do_move
/// function. The UndoInfo object, which has been initialized in
/// Position::do_move, is used to put back the captured piece (if any).
void Position::undo_promotion_move(Move m, const UndoInfo &u) {
Color us, them;
Square from, to;
PieceType capture, promotion;
assert(move_is_ok(m));
assert(move_promotion(m));
// When we have arrived here, some work has already been done by
// Position::undo_move. In particular, the side to move has been switched,
// so the code below is correct.
us = this->side_to_move();
them = opposite_color(us);
from = move_from(m);
to = move_to(m);
assert(pawn_rank(us, to) == RANK_8);
assert(this->piece_on(from) == EMPTY);
// Remove promoted piece:
promotion = move_promotion(m);
assert(this->piece_on(to)==piece_of_color_and_type(us, promotion));
assert(promotion >= KNIGHT && promotion <= QUEEN);
clear_bit(&(byColorBB[us]), to);
clear_bit(&(byTypeBB[promotion]), to);
clear_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
// Insert pawn at source square:
set_bit(&(byColorBB[us]), from);
set_bit(&(byTypeBB[PAWN]), from);
set_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
board[from] = pawn_of_color(us);
// Update material:
npMaterial[us] -= piece_value_midgame(promotion);
// Update piece list:
pieceList[us][PAWN][pieceCount[us][PAWN]] = from;
index[from] = pieceCount[us][PAWN];
pieceList[us][promotion][index[to]] =
pieceList[us][promotion][pieceCount[us][promotion] - 1];
index[pieceList[us][promotion][index[to]]] = index[to];
// Update piece counts:
pieceCount[us][promotion]--;
pieceCount[us][PAWN]++;
capture = u.capture;
if(capture) {
assert(capture != KING);
// Insert captured piece:
set_bit(&(byColorBB[them]), to);
set_bit(&(byTypeBB[capture]), to);
set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
board[to] = piece_of_color_and_type(them, capture);
// Update material. Because the move is a promotion move, we know
// that the captured piece cannot be a pawn.
assert(capture != PAWN);
npMaterial[them] += piece_value_midgame(capture);
// Update piece list:
pieceList[them][capture][pieceCount[them][capture]] = to;
index[to] = pieceCount[them][capture];
// Update piece count:
pieceCount[them][capture]++;
}
else
board[to] = EMPTY;
}
/// Position::undo_ep_move() is a private method used to unmake an en passant
/// capture. It is called from the main Position::undo_move function. Because
/// the captured piece is always a pawn, we don't need to pass an UndoInfo
/// object from which to retrieve the captured piece.
void Position::undo_ep_move(Move m) {
Color us, them;
Square from, to, capsq;
assert(move_is_ok(m));
assert(move_is_ep(m));
// When we have arrived here, some work has already been done by
// Position::undo_move. In particular, the side to move has been switched,
// so the code below is correct.
us = this->side_to_move();
them = opposite_color(us);
// Find from, to and captures squares:
from = move_from(m);
to = move_to(m);
capsq = (us == WHITE)? (to - DELTA_N) : (to - DELTA_S);
assert(to == this->ep_square());
assert(pawn_rank(us, to) == RANK_6);
assert(this->piece_on(to) == pawn_of_color(us));
assert(this->piece_on(from) == EMPTY);
assert(this->piece_on(capsq) == EMPTY);
// Replace captured piece:
set_bit(&(byColorBB[them]), capsq);
set_bit(&(byTypeBB[PAWN]), capsq);
set_bit(&(byTypeBB[0]), capsq);
board[capsq] = pawn_of_color(them);
// Remove moving piece from destination square:
clear_bit(&(byColorBB[us]), to);
clear_bit(&(byTypeBB[PAWN]), to);
clear_bit(&(byTypeBB[0]), to);
board[to] = EMPTY;
// Replace moving piece at source square:
set_bit(&(byColorBB[us]), from);
set_bit(&(byTypeBB[PAWN]), from);
set_bit(&(byTypeBB[0]), from);
board[from] = pawn_of_color(us);
// Update piece list:
pieceList[us][PAWN][index[to]] = from;
index[from] = index[to];
pieceList[them][PAWN][pieceCount[them][PAWN]] = capsq;
index[capsq] = pieceCount[them][PAWN];
// Update piece count:
pieceCount[them][PAWN]++;
}
/// Position::do_null_move makes() a "null move": It switches the side to move
/// and updates the hash key without executing any move on the board.
void Position::do_null_move(UndoInfo &u) {
assert(this->is_ok());
assert(!this->is_check());
// Back up the information necessary to undo the null move to the supplied
// UndoInfo object. In the case of a null move, the only thing we need to
// remember is the last move made and the en passant square.
u.lastMove = lastMove;
u.epSquare = epSquare;
// Save the current key to the history[] array, in order to be able to
// detect repetition draws:
history[gamePly] = key;
// Update the necessary information.
sideToMove = opposite_color(sideToMove);
if(epSquare != SQ_NONE)
key ^= zobEp[epSquare];
epSquare = SQ_NONE;
rule50++;
gamePly++;
key ^= zobSideToMove;
mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
assert(this->is_ok());
}
/// Position::undo_null_move() unmakes a "null move".
void Position::undo_null_move(const UndoInfo &u) {
assert(this->is_ok());
assert(!this->is_check());
// Restore information from the supplied UndoInfo object:
lastMove = u.lastMove;
epSquare = u.epSquare;
if(epSquare != SQ_NONE)
key ^= zobEp[epSquare];
// Update the necessary information.
sideToMove = opposite_color(sideToMove);
rule50--;
gamePly--;
key ^= zobSideToMove;
mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
assert(this->is_ok());
}
/// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move. There are two versions of
/// this function: One which takes a move as input, and one which takes a
/// 'from' and a 'to' square. The function does not yet understand promotions
/// or en passant captures.
int Position::see(Square from, Square to) const {
// Approximate material values, with pawn = 1:
static const int seeValues[18] = {
0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0
};
Color us, them;
Piece piece, capture;
Bitboard attackers, occ, b;
assert(square_is_ok(from));
assert(square_is_ok(to));
// Initialize colors:
us = this->color_of_piece_on(from);
them = opposite_color(us);
// Initialize pieces:
piece = this->piece_on(from);
capture = this->piece_on(to);
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it:
occ = this->occupied_squares();
clear_bit(&occ, from);
attackers =
(rook_attacks_bb(to, occ) & this->rooks_and_queens()) |
(bishop_attacks_bb(to, occ) & this->bishops_and_queens()) |
(this->knight_attacks(to) & this->knights()) |
(this->king_attacks(to) & this->kings()) |
(this->white_pawn_attacks(to) & this->pawns(BLACK)) |
(this->black_pawn_attacks(to) & this->pawns(WHITE));
attackers &= occ;
// If the opponent has no attackers, we are finished:
if((attackers & this->pieces_of_color(them)) == EmptyBoardBB)
return seeValues[capture];
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
// the material gain or loss at each stop in a sequence of captures to the
// destianation square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece.
int lastCapturingPieceValue = seeValues[piece];
int swapList[32], n = 1;
Color c = them;
PieceType pt;
swapList[0] = seeValues[capture];
do {
// Locate the least valuable attacker for the side to move. The loop
// below looks like it is potentially infinite, but it isn't. We know
// that the side to move still has at least one attacker left.
for(pt = PAWN; !(attackers&this->pieces_of_color_and_type(c, pt)); pt++)
assert(pt < KING);
// Remove the attacker we just found from the 'attackers' bitboard,
// and scan for new X-ray attacks behind the attacker:
b = attackers & this->pieces_of_color_and_type(c, pt);
occ ^= (b & -b);
attackers |=
(rook_attacks_bb(to, occ) & this->rooks_and_queens()) |
(bishop_attacks_bb(to, occ) & this->bishops_and_queens());
attackers &= occ;
// Add the new entry to the swap list:
assert(n < 32);
swapList[n] = -swapList[n - 1] + lastCapturingPieceValue;
n++;
// Remember the value of the capturing piece, and change the side to move
// before beginning the next iteration:
lastCapturingPieceValue = seeValues[pt];
c = opposite_color(c);
// Stop after a king capture:
if(pt == KING && (attackers & this->pieces_of_color(c))) {
assert(n < 32);
swapList[n++] = 100;
break;
}
} while(attackers & this->pieces_of_color(c));
// Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move:
while(--n) swapList[n-1] = Min(-swapList[n], swapList[n-1]);
return swapList[0];
}
int Position::see(Move m) const {
assert(move_is_ok(m));
return this->see(move_from(m), move_to(m));
}
/// Position::clear() erases the position object to a pristine state, with an
/// empty board, white to move, and no castling rights.
void Position::clear() {
int i, j;
for(i = 0; i < 64; i++) {
board[i] = EMPTY;
index[i] = 0;
}
for(i = 0; i < 2; i++)
byColorBB[i] = EmptyBoardBB;
for(i = 0; i < 7; i++) {
byTypeBB[i] = EmptyBoardBB;
pieceCount[0][i] = pieceCount[1][i] = 0;
for(j = 0; j < 8; j++)
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
}
checkersBB = EmptyBoardBB;
lastMove = MOVE_NONE;
sideToMove = WHITE;
castleRights = NO_CASTLES;
initialKFile = FILE_E;
initialKRFile = FILE_H;
initialQRFile = FILE_A;
epSquare = SQ_NONE;
rule50 = 0;
gamePly = 0;
}
/// Position::reset_game_ply() simply sets gamePly to 0. It is used from the
/// UCI interface code, whenever a non-reversible move is made in a
/// 'position fen moves m1 m2 ...' command. This makes it possible
/// for the program to handle games of arbitrary length, as long as the GUI
/// handles draws by the 50 move rule correctly.
void Position::reset_game_ply() {
gamePly = 0;
}
/// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, bitboards, and piece counts.
void Position::put_piece(Piece p, Square s) {
Color c = color_of_piece(p);
PieceType pt = type_of_piece(p);
board[s] = p;
index[s] = pieceCount[c][pt];
pieceList[c][pt][index[s]] = s;
set_bit(&(byTypeBB[pt]), s);
set_bit(&(byColorBB[c]), s);
set_bit(&byTypeBB[0], s); // HACK: byTypeBB[0] contains all occupied squares.
pieceCount[c][pt]++;
if(pt == KING)
kingSquare[c] = s;
}
/// Position::allow_oo() gives the given side the right to castle kingside.
/// Used when setting castling rights during parsing of FEN strings.
void Position::allow_oo(Color c) {
castleRights |= (1 + int(c));
}
/// Position::allow_ooo() gives the given side the right to castle queenside.
/// Used when setting castling rights during parsing of FEN strings.
void Position::allow_ooo(Color c) {
castleRights |= (4 + 4*int(c));
}
/// Position::compute_key() computes the hash key of the position. The hash
/// key is usually updated incrementally as moves are made and unmade, the
/// compute_key() function is only used when a new position is set up, and
/// to verify the correctness of the hash key when running in debug mode.
Key Position::compute_key() const {
Key result = Key(0ULL);
for(Square s = SQ_A1; s <= SQ_H8; s++)
if(this->square_is_occupied(s))
result ^=
zobrist[this->color_of_piece_on(s)][this->type_of_piece_on(s)][s];
if(this->ep_square() != SQ_NONE)
result ^= zobEp[this->ep_square()];
result ^= zobCastle[castleRights];
if(this->side_to_move() == BLACK) result ^= zobSideToMove;
return result;
}
/// Position::compute_pawn_key() computes the hash key of the position. The
/// hash key is usually updated incrementally as moves are made and unmade,
/// the compute_pawn_key() function is only used when a new position is set
/// up, and to verify the correctness of the pawn hash key when running in
/// debug mode.
Key Position::compute_pawn_key() const {
Key result = Key(0ULL);
Bitboard b;
Square s;
for(Color c = WHITE; c <= BLACK; c++) {
b = this->pawns(c);
while(b) {
s = pop_1st_bit(&b);
result ^= zobrist[c][PAWN][s];
}
}
return result;
}
/// Position::compute_material_key() computes the hash key of the position.
/// The hash key is usually updated incrementally as moves are made and unmade,
/// the compute_material_key() function is only used when a new position is set
/// up, and to verify the correctness of the material hash key when running in
/// debug mode.
Key Position::compute_material_key() const {
Key result = Key(0ULL);
for(Color c = WHITE; c <= BLACK; c++)
for(PieceType pt = PAWN; pt <= QUEEN; pt++) {
int count = this->piece_count(c, pt);
for(int i = 0; i <= count; i++)
result ^= zobMaterial[c][pt][i];
}
return result;
}
/// Position::compute_mg_value() and Position::compute_eg_value() compute the
/// incremental scores for the middle game and the endgame. These functions
/// are used to initialize the incremental scores when a new position is set
/// up, and to verify that the scores are correctly updated by do_move
/// and undo_move when the program is running in debug mode.
Value Position::compute_mg_value() const {
Value result = Value(0);
Bitboard b;
Square s;
for(Color c = WHITE; c <= BLACK; c++)
for(PieceType pt = PAWN; pt <= KING; pt++) {
b = this->pieces_of_color_and_type(c, pt);
while(b) {
s = pop_1st_bit(&b);
assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
result += this->mg_pst(c, pt, s);
}
}
result += (this->side_to_move() == WHITE)?
(TempoValueMidgame / 2) : -(TempoValueMidgame / 2);
return result;
}
Value Position::compute_eg_value() const {
Value result = Value(0);
Bitboard b;
Square s;
for(Color c = WHITE; c <= BLACK; c++)
for(PieceType pt = PAWN; pt <= KING; pt++) {
b = this->pieces_of_color_and_type(c, pt);
while(b) {
s = pop_1st_bit(&b);
assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
result += this->eg_pst(c, pt, s);
}
}
result += (this->side_to_move() == WHITE)?
(TempoValueEndgame / 2) : -(TempoValueEndgame / 2);
return result;
}
/// Position::compute_non_pawn_material() computes the total non-pawn middle
/// game material score for the given side. Material scores are updated
/// incrementally during the search, this function is only used while
/// initializing a new Position object.
Value Position::compute_non_pawn_material(Color c) const {
Value result = Value(0);
Square s;
for(PieceType pt = KNIGHT; pt <= QUEEN; pt++) {
Bitboard b = this->pieces_of_color_and_type(c, pt);
while(b) {
s = pop_1st_bit(&b);
assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
result += piece_value_midgame(pt);
}
}
return result;
}
/// Position::is_mate() returns true or false depending on whether the
/// side to move is checkmated. Note that this function is currently very
/// slow, and shouldn't be used frequently inside the search.
bool Position::is_mate() {
if(this->is_check()) {
MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, Depth(0));
return mp.get_next_move() == MOVE_NONE;
}
else
return false;
}
/// Position::is_draw() tests whether the position is drawn by material,
/// repetition, or the 50 moves rule. It does not detect stalemates, this
/// must be done by the search.
bool Position::is_draw() const {
// Draw by material?
if(!this->pawns() &&
this->non_pawn_material(WHITE) + this->non_pawn_material(BLACK)
<= BishopValueMidgame)
return true;
// Draw by the 50 moves rule?
if(rule50 > 100 || (rule50 == 100 && !this->is_check()))
return true;
// Draw by repetition?
for(int i = 2; i < Min(gamePly, rule50); i += 2)
if(history[gamePly - i] == key)
return true;
return false;
}
/// Position::has_mate_threat() tests whether a given color has a mate in one
/// from the current position. This function is quite slow, but it doesn't
/// matter, because it is currently only called from PV nodes, which are rare.
bool Position::has_mate_threat(Color c) {
UndoInfo u1, u2;
Color stm = this->side_to_move();
// The following lines are useless and silly, but prevents gcc from
// emitting a stupid warning stating that u1.lastMove and u1.epSquare might
// be used uninitialized.
u1.lastMove = lastMove;
u1.epSquare = epSquare;
if(this->is_check())
return false;
// If the input color is not equal to the side to move, do a null move
if(c != stm) this->do_null_move(u1);
MoveStack mlist[120];
int count;
bool result = false;
// Generate legal moves
count = generate_legal_moves(*this, mlist);
// Loop through the moves, and see if one of them is mate.
for(int i = 0; i < count; i++) {
this->do_move(mlist[i].move, u2);
if(this->is_mate()) result = true;
this->undo_move(mlist[i].move, u2);
}
// Undo null move, if necessary
if(c != stm) this->undo_null_move(u1);
return result;
}
/// Position::init_zobrist() is a static member function which initializes the
/// various arrays used to compute hash keys.
void Position::init_zobrist() {
for(int i = 0; i < 2; i++)
for(int j = 0; j < 8; j++)
for(int k = 0; k < 64; k++)
zobrist[i][j][k] = Key(genrand_int64());
for(int i = 0; i < 64; i++)
zobEp[i] = Key(genrand_int64());
for(int i = 0; i < 16; i++)
zobCastle[i] = genrand_int64();
zobSideToMove = genrand_int64();
for(int i = 0; i < 2; i++)
for(int j = 0; j < 8; j++)
for(int k = 0; k < 16; k++)
zobMaterial[i][j][k] = (k > 0)? Key(genrand_int64()) : Key(0LL);
for(int i = 0; i < 16; i++)
zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
}
/// Position::init_piece_square_tables() initializes the piece square tables.
/// This is a two-step operation: First, the white halves of the tables are
/// copied from the MgPST[][] and EgPST[][] arrays, with a small random number
/// added to each entry if the "Randomness" UCI parameter is non-zero.
/// Second, the black halves of the tables are initialized by mirroring
/// and changing the sign of the corresponding white scores.
void Position::init_piece_square_tables() {
int r = get_option_value_int("Randomness"), i;
for(Square s = SQ_A1; s <= SQ_H8; s++) {
for(Piece p = WP; p <= WK; p++) {
i = (r == 0)? 0 : (genrand_int32() % (r*2) - r);
MgPieceSquareTable[p][s] = Value(MgPST[p][s] + i);
EgPieceSquareTable[p][s] = Value(EgPST[p][s] + i);
}
}
for(Square s = SQ_A1; s <= SQ_H8; s++)
for(Piece p = BP; p <= BK; p++) {
MgPieceSquareTable[p][s] = -MgPieceSquareTable[p-8][flip_square(s)];
EgPieceSquareTable[p][s] = -EgPieceSquareTable[p-8][flip_square(s)];
}
}
/// Position::flipped_copy() makes a copy of the input position, but with
/// the white and black sides reversed. This is only useful for debugging,
/// especially for finding evaluation symmetry bugs.
void Position::flipped_copy(const Position &pos) {
assert(pos.is_ok());
this->clear();
// Board
for(Square s = SQ_A1; s <= SQ_H8; s++)
if(!pos.square_is_empty(s))
this->put_piece(Piece(int(pos.piece_on(s)) ^ 8), flip_square(s));
// Side to move
sideToMove = opposite_color(pos.side_to_move());
// Castling rights
if(pos.can_castle_kingside(WHITE)) this->allow_oo(BLACK);
if(pos.can_castle_queenside(WHITE)) this->allow_ooo(BLACK);
if(pos.can_castle_kingside(BLACK)) this->allow_oo(WHITE);
if(pos.can_castle_queenside(BLACK)) this->allow_ooo(WHITE);
initialKFile = pos.initialKFile;
initialKRFile = pos.initialKRFile;
initialQRFile = pos.initialQRFile;
for(Square sq = SQ_A1; sq <= SQ_H8; sq++)
castleRightsMask[sq] = ALL_CASTLES;
castleRightsMask[make_square(initialKFile, RANK_1)] ^= (WHITE_OO|WHITE_OOO);
castleRightsMask[make_square(initialKFile, RANK_8)] ^= (BLACK_OO|BLACK_OOO);
castleRightsMask[make_square(initialKRFile, RANK_1)] ^= WHITE_OO;
castleRightsMask[make_square(initialKRFile, RANK_8)] ^= BLACK_OO;
castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO;
castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO;
// En passant square
if(pos.epSquare != SQ_NONE)
epSquare = flip_square(pos.epSquare);
// Checkers
this->find_checkers();
// Hash keys
key = this->compute_key();
pawnKey = this->compute_pawn_key();
materialKey = this->compute_material_key();
// Incremental scores
mgValue = this->compute_mg_value();
egValue = this->compute_eg_value();
// Material
npMaterial[WHITE] = this->compute_non_pawn_material(WHITE);
npMaterial[BLACK] = this->compute_non_pawn_material(BLACK);
assert(this->is_ok());
}
/// Position::is_ok() performs some consitency checks for the position object.
/// This is meant to be helpful when debugging.
bool Position::is_ok() const {
// What features of the position should be verified?
static const bool debugBitboards = false;
static const bool debugKingCount = false;
static const bool debugKingCapture = false;
static const bool debugCheckerCount = false;
static const bool debugKey = false;
static const bool debugMaterialKey = false;
static const bool debugPawnKey = false;
static const bool debugIncrementalEval = false;
static const bool debugNonPawnMaterial = false;
static const bool debugPieceCounts = false;
static const bool debugPieceList = false;
// Side to move OK?
if(!color_is_ok(this->side_to_move()))
return false;
// Are the king squares in the position correct?
if(this->piece_on(this->king_square(WHITE)) != WK)
return false;
if(this->piece_on(this->king_square(BLACK)) != BK)
return false;
// Castle files OK?
if(!file_is_ok(initialKRFile))
return false;
if(!file_is_ok(initialQRFile))
return false;
// Do both sides have exactly one king?
if(debugKingCount) {
int kingCount[2] = {0, 0};
for(Square s = SQ_A1; s <= SQ_H8; s++)
if(this->type_of_piece_on(s) == KING)
kingCount[this->color_of_piece_on(s)]++;
if(kingCount[0] != 1 || kingCount[1] != 1)
return false;
}
// Can the side to move capture the opponent's king?
if(debugKingCapture) {
Color us = this->side_to_move();
Color them = opposite_color(us);
Square ksq = this->king_square(them);
if(this->square_is_attacked(ksq, us))
return false;
}
// Is there more than 2 checkers?
if(debugCheckerCount && count_1s(checkersBB) > 2)
return false;
// Bitboards OK?
if(debugBitboards) {
// The intersection of the white and black pieces must be empty:
if((this->pieces_of_color(WHITE) & this->pieces_of_color(BLACK))
!= EmptyBoardBB)
return false;
// The union of the white and black pieces must be equal to all
// occupied squares:
if((this->pieces_of_color(WHITE) | this->pieces_of_color(BLACK))
!= this->occupied_squares())
return false;
// Separate piece type bitboards must have empty intersections:
for(PieceType p1 = PAWN; p1 <= KING; p1++)
for(PieceType p2 = PAWN; p2 <= KING; p2++)
if(p1 != p2 && (this->pieces_of_type(p1) & this->pieces_of_type(p2)))
return false;
}
// En passant square OK?
if(this->ep_square() != SQ_NONE) {
// The en passant square must be on rank 6, from the point of view of the
// side to move.
if(pawn_rank(this->side_to_move(), this->ep_square()) != RANK_6)
return false;
}
// Hash key OK?
if(debugKey && key != this->compute_key())
return false;
// Pawn hash key OK?
if(debugPawnKey && pawnKey != this->compute_pawn_key())
return false;
// Material hash key OK?
if(debugMaterialKey && materialKey != this->compute_material_key())
return false;
// Incremental eval OK?
if(debugIncrementalEval) {
if(mgValue != this->compute_mg_value())
return false;
if(egValue != this->compute_eg_value())
return false;
}
// Non-pawn material OK?
if(debugNonPawnMaterial) {
if(npMaterial[WHITE] != compute_non_pawn_material(WHITE))
return false;
if(npMaterial[BLACK] != compute_non_pawn_material(BLACK))
return false;
}
// Piece counts OK?
if(debugPieceCounts)
for(Color c = WHITE; c <= BLACK; c++)
for(PieceType pt = PAWN; pt <= KING; pt++)
if(pieceCount[c][pt] != count_1s(this->pieces_of_color_and_type(c, pt)))
return false;
if(debugPieceList) {
for(Color c = WHITE; c <= BLACK; c++)
for(PieceType pt = PAWN; pt <= KING; pt++)
for(int i = 0; i < pieceCount[c][pt]; i++) {
if(this->piece_on(this->piece_list(c, pt, i)) !=
piece_of_color_and_type(c, pt))
return false;
if(index[this->piece_list(c, pt, i)] != i)
return false;
}
}
return true;
}
glaurung-2.2/src/position.h 0000644 0001750 0001750 00000051460 11123021255 015365 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED
////
//// Includes
////
#include "bitboard.h"
#include "color.h"
#include "direction.h"
#include "move.h"
#include "piece.h"
#include "phase.h"
#include "square.h"
#include "value.h"
////
//// Constants
////
/// FEN string for the initial position:
const std::string StartPosition =
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move):
const int MaxGameLength = 220;
////
//// Types
////
/// Castle rights, encoded as bit fields:
enum CastleRights {
NO_CASTLES = 0,
WHITE_OO = 1, BLACK_OO = 2,
WHITE_OOO = 4, BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// The UndoInfo struct stores information we need to restore a Position
/// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), an UndoInfo object
/// must be passed as a parameter. When the move is unmade (by calling
/// Position::undo_move), the same UndoInfo object must be passed again.
struct UndoInfo {
int castleRights;
Square epSquare;
Bitboard checkersBB;
Key key, pawnKey, materialKey;
int rule50;
Move lastMove;
PieceType capture;
Value mgValue, egValue;
};
/// The position data structure. A position consists of the following data:
///
/// * For each piece type, a bitboard representing the squares occupied
/// by pieces of that type.
/// * For each color, a bitboard representing the squares occupiecd by
/// pieces of that color.
/// * A bitboard of all occupied squares.
/// * A bitboard of all checking pieces.
/// * A 64-entry array of pieces, indexed by the squares of the board.
/// * The current side to move.
/// * Information about the castling rights for both sides.
/// * The initial files of the kings and both pairs of rooks. This is
/// used to implement the Chess960 castling rules.
/// * The en passant square (which is SQ_NONE if no en passant capture is
/// possible).
/// * The squares of the kings for both sides.
/// * The last move played.
/// * Hash keys for the position itself, the current pawn structure, and
/// the current material situation.
/// * Hash keys for all previous positions in the game (for detecting
/// repetition draws.
/// * A counter for detecting 50 move rule draws.
class Position {
friend class MaterialInfo;
public:
// Constructors
Position();
Position(const Position &pos);
Position(const std::string &fen);
// Text input/output
void from_fen(const std::string &fen);
const std::string to_fen() const;
void print() const;
// Copying
void copy(const Position &pos);
void flipped_copy(const Position &pos);
// The piece on a given square
Piece piece_on(Square s) const;
PieceType type_of_piece_on(Square s) const;
Color color_of_piece_on(Square s) const;
bool square_is_empty(Square s) const;
bool square_is_occupied(Square s) const;
Value midgame_value_of_piece_on(Square s) const;
Value endgame_value_of_piece_on(Square s) const;
// Side to move
Color side_to_move() const;
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const;
Bitboard pieces_of_type(PieceType pt) const;
Bitboard pieces_of_color_and_type(Color c, PieceType pt) const;
Bitboard pawns() const;
Bitboard knights() const;
Bitboard bishops() const;
Bitboard rooks() const;
Bitboard queens() const;
Bitboard kings() const;
Bitboard rooks_and_queens() const;
Bitboard bishops_and_queens() const;
Bitboard sliders() const;
Bitboard pawns(Color c) const;
Bitboard knights(Color c) const;
Bitboard bishops(Color c) const;
Bitboard rooks(Color c) const;
Bitboard queens(Color c) const;
Bitboard kings(Color c) const;
Bitboard rooks_and_queens(Color c) const;
Bitboard bishops_and_queens(Color c) const;
Bitboard sliders_of_color(Color c) const;
// Number of pieces of each color and type
int piece_count(Color c, PieceType pt) const;
int pawn_count(Color c) const;
int knight_count(Color c) const;
int bishop_count(Color c) const;
int rook_count(Color c) const;
int queen_count(Color c) const;
// The en passant square:
Square ep_square() const;
// Current king position for each color
Square king_square(Color c) const;
// Castling rights.
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
// Attack bitboards
Bitboard sliding_attacks(Square s, Direction d) const;
Bitboard ray_attacks(Square s, SignedDirection d) const;
Bitboard pawn_attacks(Color c, Square s) const;
Bitboard white_pawn_attacks(Square s) const;
Bitboard black_pawn_attacks(Square s) const;
Bitboard knight_attacks(Square s) const;
Bitboard bishop_attacks(Square s) const;
Bitboard rook_attacks(Square s) const;
Bitboard queen_attacks(Square s) const;
Bitboard king_attacks(Square s) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
// Checking pieces
Bitboard checkers() const;
// Piece lists:
Square piece_list(Color c, PieceType pt, int index) const;
Square pawn_list(Color c, int index) const;
Square knight_list(Color c, int index) const;
Square bishop_list(Color c, int index) const;
Square rook_list(Color c, int index) const;
Square queen_list(Color c, int index) const;
// Attack information for a given square
bool square_is_attacked(Square s, Color c) const;
Bitboard attacks_to(Square s) const;
Bitboard attacks_to(Square s, Color c) const;
bool is_check() const;
bool piece_attacks_square(Square f, Square t) const;
bool white_pawn_attacks_square(Square f, Square t) const;
bool black_pawn_attacks_square(Square f, Square t) const;
bool knight_attacks_square(Square f, Square t) const;
bool bishop_attacks_square(Square f, Square t) const;
bool rook_attacks_square(Square f, Square t) const;
bool queen_attacks_square(Square f, Square t) const;
bool king_attacks_square(Square f, Square t) const;
// Properties of moves
bool move_is_legal(Move m) const;
bool move_is_legal(Move m, Bitboard pinned) const;
bool move_is_check(Move m) const;
bool move_is_check(Move m, Bitboard dcCandidates) const;
bool move_is_capture(Move m) const;
bool move_is_pawn_push_to_7th(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
bool move_was_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const;
// Information about pawns
bool pawn_is_passed(Color c, Square s) const;
bool pawn_is_isolated(Color c, Square s) const;
bool pawn_is_doubled(Color c, Square s) const;
// Open and half-open files
bool file_is_open(File f) const;
bool file_is_half_open(Color c, File f) const;
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves
void backup(UndoInfo &u) const;
void restore(const UndoInfo &u);
void do_move(Move m, UndoInfo &u);
void do_move(Move m, UndoInfo &u, Bitboard dcCandidates);
void undo_move(Move m, const UndoInfo &u);
void do_null_move(UndoInfo &u);
void undo_null_move(const UndoInfo &u);
// Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const;
// Accessing hash keys
Key get_key() const;
Key get_pawn_key() const;
Key get_material_key() const;
// Incremental evaluation
Value mg_value() const;
Value eg_value() const;
Value non_pawn_material(Color c) const;
Phase game_phase() const;
// Game termination checks
bool is_mate();
bool is_draw() const;
// Check if one side threatens a mate in one
bool has_mate_threat(Color c);
// Number of plies since the last non-reversible move
int rule_50_counter() const;
// Other properties of the position
bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const;
// Reset the gamePly variable to 0
void reset_game_ply();
// Position consistency check, for debugging
bool is_ok() const;
// Static member functions:
static void init_zobrist();
static void init_piece_square_tables();
private:
// Initialization helper functions (used while setting up a position)
void clear();
void put_piece(Piece p, Square s);
void allow_oo(Color c);
void allow_ooo(Color c);
// Helper functions for doing and undoing moves
void do_castle_move(Move m);
void do_promotion_move(Move m, UndoInfo &u);
void do_ep_move(Move m);
void undo_castle_move(Move m);
void undo_promotion_move(Move m, const UndoInfo &u);
void undo_ep_move(Move m);
void find_checkers();
// Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const;
Key compute_pawn_key() const;
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
Value mg_pst(Color c, PieceType pt, Square s) const;
Value eg_pst(Color c, PieceType pt, Square s) const;
Value compute_mg_value() const;
Value compute_eg_value() const;
Value compute_non_pawn_material(Color c) const;
// Bitboards
Bitboard byColorBB[2], byTypeBB[8];
Bitboard checkersBB;
// Board
Piece board[64];
// Piece counts
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64];
// Other info
Color sideToMove;
int castleRights;
File initialKFile, initialKRFile, initialQRFile;
Square epSquare;
Square kingSquare[2];
Move lastMove;
Key key, pawnKey, materialKey, history[MaxGameLength];
int rule50, gamePly;
Value mgValue, egValue;
Value npMaterial[2];
// Static variables
static int castleRightsMask[64];
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove;
static Value MgPieceSquareTable[16][64];
static Value EgPieceSquareTable[16][64];
};
////
//// Inline functions
////
inline Piece Position::piece_on(Square s) const {
return board[s];
}
inline Color Position::color_of_piece_on(Square s) const {
return color_of_piece(this->piece_on(s));
}
inline PieceType Position::type_of_piece_on(Square s) const {
return type_of_piece(this->piece_on(s));
}
inline bool Position::square_is_empty(Square s) const {
return this->piece_on(s) == EMPTY;
}
inline bool Position::square_is_occupied(Square s) const {
return !this->square_is_empty(s);
}
inline Value Position::midgame_value_of_piece_on(Square s) const {
return piece_value_midgame(this->piece_on(s));
}
inline Value Position::endgame_value_of_piece_on(Square s) const {
return piece_value_endgame(this->piece_on(s));
}
inline Color Position::side_to_move() const {
return sideToMove;
}
inline Bitboard Position::occupied_squares() const {
return byTypeBB[0];
}
inline Bitboard Position::empty_squares() const {
return ~(this->occupied_squares());
}
inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c];
}
inline Bitboard Position::pieces_of_type(PieceType pt) const {
return byTypeBB[pt];
}
inline Bitboard Position::pieces_of_color_and_type(Color c, PieceType pt)
const {
return this->pieces_of_color(c) & this->pieces_of_type(pt);
}
inline Bitboard Position::pawns() const {
return this->pieces_of_type(PAWN);
}
inline Bitboard Position::knights() const {
return this->pieces_of_type(KNIGHT);
}
inline Bitboard Position::bishops() const {
return this->pieces_of_type(BISHOP);
}
inline Bitboard Position::rooks() const {
return this->pieces_of_type(ROOK);
}
inline Bitboard Position::queens() const {
return this->pieces_of_type(QUEEN);
}
inline Bitboard Position::kings() const {
return this->pieces_of_type(KING);
}
inline Bitboard Position::rooks_and_queens() const {
return this->rooks() | this->queens();
}
inline Bitboard Position::bishops_and_queens() const {
return this->bishops() | this->queens();
}
inline Bitboard Position::sliders() const {
return this->bishops() | this->queens() | this->rooks();
}
inline Bitboard Position::pawns(Color c) const {
return this->pieces_of_color_and_type(c, PAWN);
}
inline Bitboard Position::knights(Color c) const {
return this->pieces_of_color_and_type(c, KNIGHT);
}
inline Bitboard Position::bishops(Color c) const {
return this->pieces_of_color_and_type(c, BISHOP);
}
inline Bitboard Position::rooks(Color c) const {
return this->pieces_of_color_and_type(c, ROOK);
}
inline Bitboard Position::queens(Color c) const {
return this->pieces_of_color_and_type(c, QUEEN);
}
inline Bitboard Position::kings(Color c) const {
return this->pieces_of_color_and_type(c, KING);
}
inline Bitboard Position::rooks_and_queens(Color c) const {
return this->rooks_and_queens() & this->pieces_of_color(c);
}
inline Bitboard Position::bishops_and_queens(Color c) const {
return this->bishops_and_queens() & this->pieces_of_color(c);
}
inline Bitboard Position::sliders_of_color(Color c) const {
return this->sliders() & this->pieces_of_color(c);
}
inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt];
}
inline int Position::pawn_count(Color c) const {
return this->piece_count(c, PAWN);
}
inline int Position::knight_count(Color c) const {
return this->piece_count(c, KNIGHT);
}
inline int Position::bishop_count(Color c) const {
return this->piece_count(c, BISHOP);
}
inline int Position::rook_count(Color c) const {
return this->piece_count(c, ROOK);
}
inline int Position::queen_count(Color c) const {
return this->piece_count(c, QUEEN);
}
inline Square Position::piece_list(Color c, PieceType pt, int index) const {
return pieceList[c][pt][index];
}
inline Square Position::pawn_list(Color c, int index) const {
return this->piece_list(c, PAWN, index);
}
inline Square Position::knight_list(Color c, int index) const {
return this->piece_list(c, KNIGHT, index);
}
inline Square Position::bishop_list(Color c, int index) const {
return this->piece_list(c, BISHOP, index);
}
inline Square Position::rook_list(Color c, int index) const {
return this->piece_list(c, ROOK, index);
}
inline Square Position::queen_list(Color c, int index) const {
return this->piece_list(c, QUEEN, index);
}
inline Square Position::ep_square() const {
return epSquare;
}
inline Square Position::king_square(Color c) const {
return kingSquare[c];
}
inline bool Position::can_castle_kingside(Color side) const {
return castleRights & (1+int(side));
}
inline bool Position::can_castle_queenside(Color side) const {
return castleRights & (4+4*int(side));
}
inline bool Position::can_castle(Color side) const {
return can_castle_kingside(side) || can_castle_queenside(side);
}
inline Square Position::initial_kr_square(Color c) const {
return relative_square(c, make_square(initialKRFile, RANK_1));
}
inline Square Position::initial_qr_square(Color c) const {
return relative_square(c, make_square(initialQRFile, RANK_1));
}
inline Bitboard Position::pawn_attacks(Color c, Square s) const {
return StepAttackBB[pawn_of_color(c)][s];
}
inline Bitboard Position::white_pawn_attacks(Square s) const {
return this->pawn_attacks(WHITE, s);
}
inline Bitboard Position::black_pawn_attacks(Square s) const {
return this->pawn_attacks(BLACK, s);
}
inline Bitboard Position::knight_attacks(Square s) const {
return StepAttackBB[KNIGHT][s];
}
inline Bitboard Position::rook_attacks(Square s) const {
return rook_attacks_bb(s, this->occupied_squares());
}
inline Bitboard Position::bishop_attacks(Square s) const {
return bishop_attacks_bb(s, this->occupied_squares());
}
inline Bitboard Position::queen_attacks(Square s) const {
return this->rook_attacks(s) | this->bishop_attacks(s);
}
inline Bitboard Position::king_attacks(Square s) const {
return StepAttackBB[KING][s];
}
inline Bitboard Position::checkers() const {
return checkersBB;
}
inline bool Position::is_check() const {
return this->checkers() != EmptyBoardBB;
}
inline bool Position::white_pawn_attacks_square(Square f, Square t) const {
return bit_is_set(this->white_pawn_attacks(f), t);
}
inline bool Position::black_pawn_attacks_square(Square f, Square t) const {
return bit_is_set(this->black_pawn_attacks(f), t);
}
inline bool Position::knight_attacks_square(Square f, Square t) const {
return bit_is_set(this->knight_attacks(f), t);
}
inline bool Position::bishop_attacks_square(Square f, Square t) const {
return bit_is_set(this->bishop_attacks(f), t);
}
inline bool Position::rook_attacks_square(Square f, Square t) const {
return bit_is_set(this->rook_attacks(f), t);
}
inline bool Position::queen_attacks_square(Square f, Square t) const {
return bit_is_set(this->queen_attacks(f), t);
}
inline bool Position::king_attacks_square(Square f, Square t) const {
return bit_is_set(this->king_attacks(f), t);
}
inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(this->pawns(opposite_color(c)) & passed_pawn_mask(c, s));
}
inline bool Position::pawn_is_isolated(Color c, Square s) const {
return !(this->pawns(c) & neighboring_files_bb(s));
}
inline bool Position::pawn_is_doubled(Color c, Square s) const {
return this->pawns(c) & squares_behind(c, s);
}
inline bool Position::file_is_open(File f) const {
return !(this->pawns() & file_bb(f));
}
inline bool Position::file_is_half_open(Color c, File f) const {
return !(this->pawns(c) & file_bb(f));
}
inline bool Position::square_is_weak(Square s, Color c) const {
return !(this->pawns(c) & outpost_mask(opposite_color(c), s));
}
inline Key Position::get_key() const {
return key;
}
inline Key Position::get_pawn_key() const {
return pawnKey;
}
inline Key Position::get_material_key() const {
return materialKey;
}
inline Value Position::mg_pst(Color c, PieceType pt, Square s) const {
return MgPieceSquareTable[piece_of_color_and_type(c, pt)][s];
}
inline Value Position::eg_pst(Color c, PieceType pt, Square s) const {
return EgPieceSquareTable[piece_of_color_and_type(c, pt)][s];
}
inline Value Position::mg_value() const {
return mgValue;
}
inline Value Position::eg_value() const {
return egValue;
}
inline Value Position::non_pawn_material(Color c) const {
return npMaterial[c];
}
inline Phase Position::game_phase() const {
// The purpose of the Value(325) terms below is to make sure the difference
// between MidgameLimit and EndgameLimit is a power of 2, which should make
// the division at the end of the function a bit faster.
static const Value MidgameLimit =
2*QueenValueMidgame+2*RookValueMidgame+6*BishopValueMidgame+Value(325);
static const Value EndgameLimit = 4*RookValueMidgame-Value(325);
Value npm = this->non_pawn_material(WHITE) + this->non_pawn_material(BLACK);
if(npm >= MidgameLimit)
return PHASE_MIDGAME;
else if(npm <= EndgameLimit)
return PHASE_ENDGAME;
else
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
inline bool Position::move_is_pawn_push_to_7th(Move m) const {
Color c = this->side_to_move();
return
this->piece_on(move_from(m)) == pawn_of_color(c) &&
pawn_rank(c, move_to(m)) == RANK_7;
}
inline bool Position::move_is_passed_pawn_push(Move m) const {
Color c = this->side_to_move();
return
this->piece_on(move_from(m)) == pawn_of_color(c) &&
this->pawn_is_passed(c, move_to(m));
}
inline bool Position::move_was_passed_pawn_push(Move m) const {
Color c = opposite_color(this->side_to_move());
return
this->piece_on(move_to(m)) == pawn_of_color(c) &&
this->pawn_is_passed(c, move_to(m));
}
inline int Position::rule_50_counter() const {
return rule50;
}
inline bool Position::opposite_colored_bishops() const {
return
this->bishop_count(WHITE) == 1 && this->bishop_count(BLACK) == 1 &&
square_color(this->bishop_list(WHITE, 0)) !=
square_color(this->bishop_list(BLACK, 0));
}
inline bool Position::has_pawn_on_7th(Color c) const {
return this->pawns(c) & relative_rank_bb(c, RANK_7);
}
#endif // !defined(POSITION_H_INCLUDED)
glaurung-2.2/src/san.cpp 0000644 0001750 0001750 00000023771 11123021251 014635 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include
#include
#include "movepick.h"
#include "san.h"
////
//// Local definitions
////
namespace {
/// Types
enum Ambiguity {
AMBIGUITY_NONE,
AMBIGUITY_FILE,
AMBIGUITY_RANK,
AMBIGUITY_BOTH
};
/// Functions
Ambiguity move_ambiguity(Position &pos, Move m);
const std::string time_string(int milliseconds);
const std::string score_string(Value v);
}
////
//// Functions
////
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move from the position. The return value is
/// a string containing the move in short algebraic notation.
const std::string move_to_san(Position &pos, Move m) {
std::string str;
assert(pos.is_ok());
assert(move_is_ok(m));
if(m == MOVE_NONE) {
str = "(none)";
return str;
}
else if(m == MOVE_NULL) {
str = "(null)";
return str;
}
else if(move_is_long_castle(m))
str = "O-O-O";
else if(move_is_short_castle(m))
str = "O-O";
else {
Square from, to;
Piece pc;
from = move_from(m);
to = move_to(m);
pc = pos.piece_on(move_from(m));
str = "";
if(type_of_piece(pc) == PAWN) {
if(pos.move_is_capture(m))
str += file_to_char(square_file(move_from(m)));
}
else {
str += piece_type_to_char(type_of_piece(pc), true);
Ambiguity amb = move_ambiguity(pos, m);
switch(amb) {
case AMBIGUITY_NONE:
break;
case AMBIGUITY_FILE:
str += file_to_char(square_file(from));
break;
case AMBIGUITY_RANK:
str += rank_to_char(square_rank(from));
break;
case AMBIGUITY_BOTH:
str += square_to_string(from);
break;
default:
assert(false);
}
}
if(pos.move_is_capture(m))
str += "x";
str += square_to_string(move_to(m));
if(move_promotion(m)) {
str += "=";
str += piece_type_to_char(move_promotion(m), true);
}
}
// Is the move check? We don't use pos.move_is_check(m) here, because
// Position::move_is_check doesn't detect all checks (not castling moves,
// promotions and en passant captures).
UndoInfo u;
pos.do_move(m, u);
if(pos.is_check())
str += pos.is_mate()? "#" : "+";
pos.undo_move(m, u);
return str;
}
/// move_from_san() takes a position and a string as input, and tries to
/// interpret the string as a move in short algebraic notation. On success,
/// the move is returned. On failure (i.e. if the string is unparsable, or
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
Move move_from_san(Position &pos, const std::string &movestr) {
assert(pos.is_ok());
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, OnePly);
// Castling moves
if(movestr == "O-O-O") {
Move m;
while((m = mp.get_next_move()) != MOVE_NONE)
if(move_is_long_castle(m) && pos.move_is_legal(m))
return m;
return MOVE_NONE;
}
else if(movestr == "O-O") {
Move m;
while((m = mp.get_next_move()) != MOVE_NONE)
if(move_is_short_castle(m) && pos.move_is_legal(m))
return m;
return MOVE_NONE;
}
// Normal moves
const char *cstr = movestr.c_str();
const char *c;
char *cc;
char str[10];
int i;
// Initialize str[] by making a copy of movestr with the characters
// 'x', '=', '+' and '#' removed.
cc = str;
for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
if(!strchr("x=+#", *c)) {
*cc = strchr("nrq", *c)? toupper(*c) : *c;
cc++;
}
*cc = '\0';
int left = 0, right = strlen(str) - 1;
PieceType pt = NO_PIECE_TYPE, promotion;
Square to;
File fromFile = FILE_NONE;
Rank fromRank = RANK_NONE;
// Promotion?
if(strchr("BNRQ", str[right])) {
promotion = piece_type_from_char(str[right]);
right--;
}
else
promotion = NO_PIECE_TYPE;
// Find the moving piece:
if(left < right) {
if(strchr("BNRQK", str[left])) {
pt = piece_type_from_char(str[left]);
left++;
}
else
pt = PAWN;
}
// Find the to square:
if(left < right) {
if(str[right] < '1' || str[right] > '8' ||
str[right-1] < 'a' || str[right-1] > 'h')
return MOVE_NONE;
to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
right -= 2;
}
else
return MOVE_NONE;
// Find the file and/or rank of the from square:
if(left <= right) {
if(strchr("abcdefgh", str[left])) {
fromFile = file_from_char(str[left]);
left++;
}
if(strchr("12345678", str[left]))
fromRank = rank_from_char(str[left]);
}
// Look for a matching move:
Move m, move = MOVE_NONE;
int matches = 0;
while((m = mp.get_next_move()) != MOVE_NONE) {
bool match = true;
if(pos.type_of_piece_on(move_from(m)) != pt)
match = false;
else if(move_to(m) != to)
match = false;
else if(move_promotion(m) != promotion)
match = false;
else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
match = false;
else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
match = false;
if(match) {
move = m;
matches++;
}
}
if(matches == 1)
return move;
else
return MOVE_NONE;
}
/// line_to_san() takes a position and a line (an array of moves representing
/// a sequence of legal moves from the position) as input, and returns a
/// string containing the line in short algebraic notation. If the boolean
/// parameter 'breakLines' is true, line breaks are inserted, with a line
/// length of 80 characters. After a line break, 'startColumn' spaces are
/// inserted at the beginning of the new line.
const std::string line_to_san(const Position &pos, Move line[], int startColumn,
bool breakLines) {
Position p = Position(pos);
UndoInfo u;
std::stringstream s;
std::string moveStr;
int length, maxLength;
length = 0;
maxLength = 80 - startColumn;
for(int i = 0; line[i] != MOVE_NONE; i++) {
moveStr = move_to_san(p, line[i]);
length += moveStr.length() + 1;
if(breakLines && length > maxLength) {
s << "\n";
for(int j = 0; j < startColumn; j++)
s << " ";
length = moveStr.length() + 1;
}
s << moveStr << " ";
if(line[i] == MOVE_NULL)
p.do_null_move(u);
else
p.do_move(line[i], u);
}
return s.str();
}
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
const std::string pretty_pv(const Position &pos, int time, int depth,
uint64_t nodes, Value score, Move pv[]) {
std::stringstream s;
// Depth
s << std::setw(2) << std::setfill(' ') << depth << " ";
// Score
s << std::setw(8) << score_string(score);
// Time
s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
// Nodes
if(nodes < 1000000ULL)
s << std::setw(8) << std::setfill(' ') << nodes << " ";
else if(nodes < 1000000000ULL)
s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
else
s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
// PV
s << line_to_san(pos, pv, 30, true);
return s.str();
}
namespace {
Ambiguity move_ambiguity(Position &pos, Move m) {
Square from, to;
Piece pc;
from = move_from(m);
to = move_to(m);
pc = pos.piece_on(from);
// King moves are never ambiguous, because there is never two kings of
// the same color.
if(type_of_piece(pc) == KING)
return AMBIGUITY_NONE;
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
MOVE_NONE, OnePly);
Move mv, moveList[8];
int i, j, n;
n = 0;
while((mv = mp.get_next_move()) != MOVE_NONE)
if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
&& pos.move_is_legal(mv))
moveList[n++] = mv;
if(n == 1)
return AMBIGUITY_NONE;
j = 0;
for(i = 0; i < n; i++)
if(square_file(move_from(moveList[i])) == square_file(from))
j++;
if(j == 1)
return AMBIGUITY_FILE;
j = 0;
for(i = 0; i < n; i++)
if(square_rank(move_from(moveList[i])) == square_rank(from))
j++;
if(j == 1)
return AMBIGUITY_RANK;
return AMBIGUITY_BOTH;
}
const std::string time_string(int milliseconds) {
std::stringstream s;
int hours = milliseconds / (1000 * 60 * 60);
int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
if(hours)
s << hours << ':';
s << std::setw(2) << std::setfill('0') << minutes << ':';
s << std::setw(2) << std::setfill('0') << seconds;
return s.str();
}
const std::string score_string(Value v) {
std::stringstream s;
if(abs(v) >= VALUE_MATE - 200) {
if(v < 0)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << "#" << (VALUE_MATE - v + 1) / 2;
}
else {
float floatScore = float(v) / float(PawnValueMidgame);
if(v >= 0)
s << '+';
s << std::setprecision(2) << std::fixed << floatScore;
}
return s.str();
}
}
glaurung-2.2/src/square.h 0000644 0001750 0001750 00000012373 11123214340 015020 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(SQUARE_H_INCLUDED)
#define SQUARE_H_INCLUDED
////
//// Includes
////
#include
#include
#include "color.h"
#include "misc.h"
////
//// Types
////
enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NONE
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NONE
};
enum SquareDelta {
DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
DELTA_SW = -011, DELTA_S = -010, DELTA_SE = -07, DELTA_SEE = -06,
DELTA_W = -01, DELTA_ZERO = 0, DELTA_E = 01, DELTA_NWW = 06, DELTA_NW = 07,
DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017,
DELTA_NN = 020, DELTA_NNE = 021
};
////
//// Constants
////
const int FlipMask = 070;
const int FlopMask = 07;
////
//// Inline functions
////
inline File operator+ (File x, int i) { return File(int(x) + i); }
inline File operator+ (File x, File y) { return x + int(y); }
inline void operator++ (File &x, int) { x = File(int(x) + 1); }
inline void operator+= (File &x, int i) { x = File(int(x) + i); }
inline File operator- (File x, int i) { return File(int(x) - i); }
inline void operator-- (File &x, int) { x = File(int(x) - 1); }
inline void operator-= (File &x, int i) { x = File(int(x) - i); }
inline Rank operator+ (Rank x, int i) { return Rank(int(x) + i); }
inline Rank operator+ (Rank x, Rank y) { return x + int(y); }
inline void operator++ (Rank &x, int) { x = Rank(int(x) + 1); }
inline void operator+= (Rank &x, int i) { x = Rank(int(x) + i); }
inline Rank operator- (Rank x, int i) { return Rank(int(x) - i); }
inline void operator-- (Rank &x, int) { x = Rank(int(x) - 1); }
inline void operator-= (Rank &x, int i) { x = Rank(int(x) - i); }
inline Square operator+ (Square x, int i) { return Square(int(x) + i); }
inline void operator++ (Square &x, int) { x = Square(int(x) + 1); }
inline void operator+= (Square &x, int i) { x = Square(int(x) + i); }
inline Square operator- (Square x, int i) { return Square(int(x) - i); }
inline void operator-- (Square &x, int) { x = Square(int(x) - 1); }
inline void operator-= (Square &x, int i) { x = Square(int(x) - i); }
inline Square operator+ (Square x, SquareDelta i) { return Square(int(x) + i); }
inline void operator+= (Square &x, SquareDelta i) { x = Square(int(x) + i); }
inline Square operator- (Square x, SquareDelta i) { return Square(int(x) - i); }
inline void operator-= (Square &x, SquareDelta i) { x = Square(int(x) - i); }
inline SquareDelta operator- (Square x, Square y) {
return SquareDelta(int(x) - int(y));
}
inline Square make_square(File f, Rank r) {
return Square(int(f) | (int(r) << 3));
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ FlipMask);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ FlopMask);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * FlipMask));
}
inline Rank pawn_rank(Color c, Square s) {
return square_rank(relative_square(c, s));
}
inline Color square_color(Square s) {
return Color((int(square_file(s)) + int(square_rank(s))) & 1);
}
inline int file_distance(File f1, File f2) {
return abs(int(f1) - int(f2));
}
inline int file_distance(Square s1, Square s2) {
return file_distance(square_file(s1), square_file(s2));
}
inline int rank_distance(Rank r1, Rank r2) {
return abs(int(r1) - int(r2));
}
inline int rank_distance(Square s1, Square s2) {
return rank_distance(square_rank(s1), square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
////
//// Prototypes
////
extern File file_from_char(char c);
extern char file_to_char(File f);
extern Rank rank_from_char(char c);
extern char rank_to_char(Rank r);
extern Square square_from_string(const std::string &str);
extern const std::string square_to_string(Square s);
extern bool file_is_ok(File f);
extern bool rank_is_ok(Rank r);
extern bool square_is_ok(Square s);
#endif // !defined(SQUARE_H_INCLUDED)
glaurung-2.2/src/lock.h 0000644 0001750 0001750 00000005041 11123021255 014443 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
// x86 assembly language locks or OS spin locks may perform faster than
// mutex locks on some platforms. On my machine, mutexes seem to be the
// best.
//#define ASM_LOCK
//#define OS_SPIN_LOCK
#if defined(ASM_LOCK)
typedef volatile int Lock;
static inline void LockX86(Lock *lock) {
int dummy;
asm __volatile__("1: movl $1, %0" "\n\t"
" xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jz 3f" "\n\t" "2: pause" "\n\t"
" movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
"\n\t":"=&q"(dummy)
:"q"(lock)
:"cc");
}
static inline void UnlockX86(Lock *lock) {
int dummy;
asm __volatile__("movl $0, (%1)":"=&q"(dummy)
:"q"(lock));
}
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) LockX86(x)
# define lock_release(x) UnlockX86(x)
# define lock_destroy(x)
#elif defined(OS_SPIN_LOCK)
# include
typedef OSSpinLock Lock;
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) OSSpinLockLock(x)
# define lock_release(x) OSSpinLockUnlock(x)
# define lock_destroy(x)
#elif !defined(_MSC_VER)
# include
typedef pthread_mutex_t Lock;
# define lock_init(x, y) pthread_mutex_init(x, y)
# define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x)
#else
# include
typedef CRITICAL_SECTION Lock;
# define lock_init(x, y) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x)
#endif
#endif // !defined(LOCK_H_INCLUDED)
glaurung-2.2/src/phase.h 0000644 0001750 0001750 00000001575 11123021255 014623 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(PHASE_H_INCLUDED)
#define PHASE_H_INCLUDED
////
//// Types
////
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
#endif // !defined(PHASE_H_INCLUDED)
glaurung-2.2/src/piece.cpp 0000644 0001750 0001750 00000005065 11123021251 015135 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "piece.h"
////
//// Constants and variables
////
const int SlidingArray[18] = {
0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
};
const SquareDelta Directions[16][16] = {
{DELTA_ZERO},
{DELTA_NW, DELTA_NE, DELTA_ZERO},
{DELTA_SSW, DELTA_SSE, DELTA_SWW, DELTA_SEE,
DELTA_NWW, DELTA_NEE, DELTA_NNW, DELTA_NNE, DELTA_ZERO},
{DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N,
DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N,
DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
{DELTA_ZERO},
{DELTA_ZERO},
{DELTA_SW, DELTA_SE, DELTA_ZERO},
{DELTA_SSW, DELTA_SSE, DELTA_SWW, DELTA_SEE,
DELTA_NWW, DELTA_NEE, DELTA_NNW, DELTA_NNE, DELTA_ZERO},
{DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N,
DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
{DELTA_S, DELTA_E, DELTA_W, DELTA_N,
DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
};
const SquareDelta PawnPush[2] = {
DELTA_N, DELTA_S
};
////
//// Functions
////
/// Translating piece types to/from English piece letters:
static const char PieceChars[] = " pnbrqk";
char piece_type_to_char(PieceType pt, bool upcase = false) {
return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
}
PieceType piece_type_from_char(char c) {
const char *ch = strchr(PieceChars, tolower(c));
return ch? PieceType(ch - PieceChars) : NO_PIECE_TYPE;
}
/// piece_is_ok() and piece_type_is_ok(), for debugging:
bool piece_is_ok(Piece pc) {
return
piece_type_is_ok(type_of_piece(pc)) &&
color_is_ok(color_of_piece(pc));
}
bool piece_type_is_ok(PieceType pc) {
return pc >= PAWN && pc <= KING;
}
glaurung-2.2/src/misc.h 0000644 0001750 0001750 00000002421 11123021255 014445 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED
////
//// Includes
////
#include
////
//// Constants
////
/// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
const std::string EngineVersion = "2.2";
////
//// Macros
////
#define Min(x, y) (((x) < (y))? (x) : (y))
#define Max(x, y) (((x) < (y))? (y) : (x))
////
//// Prototypes
////
extern const std::string engine_name();
extern int get_system_time();
extern int cpu_count();
extern int Bioskey();
#endif // !defined(MISC_H_INCLUDED)
glaurung-2.2/src/direction.h 0000644 0001750 0001750 00000003442 11123021255 015476 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(DIRECTION_H_INCLUDED)
#define DIRECTION_H_INCLUDED
////
//// Includes
////
#include "square.h"
#include "types.h"
////
//// Types
////
enum Direction {
DIR_E = 0, DIR_N = 1, DIR_NE = 2, DIR_NW = 3, DIR_NONE = 4
};
enum SignedDirection {
SIGNED_DIR_E = 0, SIGNED_DIR_W = 1,
SIGNED_DIR_N = 2, SIGNED_DIR_S = 3,
SIGNED_DIR_NE = 4, SIGNED_DIR_SW = 5,
SIGNED_DIR_NW = 6, SIGNED_DIR_SE = 7,
SIGNED_DIR_NONE = 8
};
////
//// Variables
////
extern uint8_t DirectionTable[64][64];
extern uint8_t SignedDirectionTable[64][64];
////
//// Inline functions
////
inline void operator++ (Direction &d, int) { d = Direction(int(d) + 1); }
inline void operator++ (SignedDirection &d, int) {
d = SignedDirection(int(d) + 1);
}
inline Direction direction_between_squares(Square s1, Square s2) {
return Direction(DirectionTable[s1][s2]);
}
inline SignedDirection signed_direction_between_squares(Square s1, Square s2) {
return SignedDirection(SignedDirectionTable[s1][s2]);
}
////
//// Prototypes
////
extern void init_direction_table();
#endif // !defined(DIRECTION_H_INCLUDED)
glaurung-2.2/src/book.cpp 0000644 0001750 0001750 00000063316 11123021252 015006 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
////
//// Includes
////
#include
#include
#include "book.h"
#include "mersenne.h"
#include "movegen.h"
////
//// Global variables
////
Book OpeningBook;
////
//// Local definitions
////
namespace {
/// Random numbers from PolyGlot, used to compute book hash keys.
const uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
};
/// Indices to the Random64[] array
const int RandomPiece = 0;
const int RandomCastle = 768;
const int RandomEnPassant = 772;
const int RandomTurn = 780;
/// Convert pieces to the range 0..1
const int PieceTo12[] = {
0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11
};
/// Prototypes
uint64_t book_key(const Position &pos);
uint64_t book_piece_key(Piece p, Square s);
uint64_t book_castle_key(const Position &pos);
uint64_t book_ep_key(const Position &pos);
uint64_t book_color_key(const Position &pos);
uint64_t read_integer(FILE *file, int size);
}
////
//// Functions
////
/// Constructor
Book::Book() {
bookFile = NULL;
bookSize = 0;
}
/// Book::open() opens a book file with a given file name.
void Book::open(const std::string &fName) {
fileName = fName;
bookFile = fopen(fileName.c_str(), "rb");
if(bookFile != NULL) {
if(fseek(bookFile, 0, SEEK_END) == -1) {
std::cerr << "Failed to open book file " << fileName << std::endl;
exit(EXIT_FAILURE);
}
bookSize = ftell(bookFile) / 16;
if(bookSize == -1) {
std::cerr << "Failed to open book file " << fileName << std::endl;
exit(EXIT_FAILURE);
}
}
}
/// Book::close() closes the currently open book file.
void Book::close() {
if(bookFile != NULL && fclose(bookFile) == EOF) {
std::cerr << "Failed to close book file" << std::endl;
exit(EXIT_FAILURE);
}
}
/// Book::is_open() tests whether a book file has been opened.
bool Book::is_open() const {
return bookFile != NULL && bookSize != 0;
}
/// Book::file_name() returns the file name of the currently active book,
/// or the empty string if no book is open.
const std::string Book::file_name() const {
return this->is_open()? fileName : "";
}
/// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found.
Move Book::get_move(const Position &pos) const {
if(this->is_open()) {
int bestMove = 0, bestScore = 0, move, score;
uint64_t key = book_key(pos);
BookEntry entry;
for(int i = this->find_key(key); i < bookSize; i++) {
this->read_entry(entry, i);
if(entry.key != key)
break;
move = entry.move;
score = entry.count;
assert(score > 0);
bestScore += score;
if(int(genrand_int32() % bestScore) < score)
bestMove = move;
}
if(bestMove != 0) {
MoveStack moves[256];
int n, j;
n = generate_legal_moves(pos, moves);
for(j = 0; j < n; j++)
if((int(moves[j].move) & 07777) == bestMove)
return moves[j].move;
}
}
return MOVE_NONE;
}
/// Book::find_key() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book
/// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
int Book::find_key(uint64_t key) const {
int left, right, mid;
BookEntry entry;
// Binary search (finds the leftmost entry)
left = 0;
right = bookSize - 1;
assert(left <= right);
while(left < right) {
mid = (left + right) / 2;
assert(mid >= left && mid < right);
this->read_entry(entry, mid);
if(key <= entry.key)
right = mid;
else
left = mid + 1;
}
assert(left == right);
this->read_entry(entry, left);
return (entry.key == key)? left : bookSize;
}
/// Book::read_entry() takes a BookEntry reference and an integer index as
/// input, and looks up the opening book entry at the given index in the book
/// file. The book entry is copied to the first input parameter.
void Book::read_entry(BookEntry& entry, int n) const {
assert(n >= 0 && n < bookSize);
assert(bookFile != NULL);
if(fseek(bookFile, n*16, SEEK_SET) == -1) {
std::cerr << "Failed to read book entry at index " << n << std::endl;
exit(EXIT_FAILURE);
}
entry.key = read_integer(bookFile, 8);
entry.move = read_integer(bookFile, 2);
entry.count = read_integer(bookFile, 2);
entry.n = read_integer(bookFile, 2);
entry.sum = read_integer(bookFile, 2);
}
////
//// Local definitions
////
namespace {
uint64_t book_key(const Position &pos) {
uint64_t result = 0ULL;
for(Color c = WHITE; c <= BLACK; c++) {
Bitboard b = pos.pieces_of_color(c);
Square s;
Piece p;
while(b != EmptyBoardBB) {
s = pop_1st_bit(&b);
p = pos.piece_on(s);
assert(piece_is_ok(p));
assert(color_of_piece(p) == c);
result ^= book_piece_key(p, s);
}
}
result ^= book_castle_key(pos);
result ^= book_ep_key(pos);
result ^= book_color_key(pos);
return result;
}
uint64_t book_piece_key(Piece p, Square s) {
return Random64[RandomPiece + (PieceTo12[int(p)]^1)*64 + int(s)];
}
uint64_t book_castle_key(const Position &pos) {
uint64_t result = 0ULL;
if(pos.can_castle_kingside(WHITE))
result ^= Random64[RandomCastle+0];
if(pos.can_castle_queenside(WHITE))
result ^= Random64[RandomCastle+1];
if(pos.can_castle_kingside(BLACK))
result ^= Random64[RandomCastle+2];
if(pos.can_castle_queenside(BLACK))
result ^= Random64[RandomCastle+3];
return result;
}
uint64_t book_ep_key(const Position &pos) {
return (pos.ep_square() == SQ_NONE)?
0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())];
}
uint64_t book_color_key(const Position &pos) {
return (pos.side_to_move() == WHITE)? Random64[RandomTurn] : 0ULL;
}
uint64_t read_integer(FILE *file, int size) {
uint64_t n = 0ULL;;
int i;
int b;
assert(file != NULL);
assert(size > 0 && size <= 8);
for(i = 0; i < size; i++) {
b = fgetc(file);
if(b == EOF) {
std::cerr << "Failed to read " << size << " bytes from book file"
<< std::endl;
exit(EXIT_FAILURE);
}
assert(b >= 0 && b < 256);
n = (n << 8) | b;
}
return n;
}
}
glaurung-2.2/src/piece.h 0000644 0001750 0001750 00000006261 11123021255 014605 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(PIECE_H_INCLUDED)
#define PIECE_H_INCLUDED
////
//// Includes
////
#include "color.h"
#include "misc.h"
#include "square.h"
////
//// Types
////
enum PieceType {
NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
NO_PIECE = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14,
EMPTY = 16, OUTSIDE = 17
};
////
//// Constants and variables
////
const PieceType PieceTypeMin = PAWN;
const PieceType PieceTypeMax = KING;
extern const int SlidingArray[18];
extern const SquareDelta Directions[16][16];
extern const SquareDelta PawnPush[2];
////
//// Inline functions
////
inline Piece operator+ (Piece p, int i) { return Piece(int(p) + i); }
inline void operator++ (Piece &p, int) { p = Piece(int(p) + 1); }
inline Piece operator- (Piece p, int i) { return Piece(int(p) - i); }
inline void operator-- (Piece &p, int) { p = Piece(int(p) - 1); }
inline PieceType operator+ (PieceType p, int i) {return PieceType(int(p) + i);}
inline void operator++ (PieceType &p, int) { p = PieceType(int(p) + 1); }
inline PieceType operator- (PieceType p, int i) {return PieceType(int(p) - i);}
inline void operator-- (PieceType &p, int) { p = PieceType(int(p) - 1); }
inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
}
inline Piece piece_of_color_and_type(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt));
}
inline Piece pawn_of_color(Color c) {
return piece_of_color_and_type(c, PAWN);
}
inline Piece knight_of_color(Color c) {
return piece_of_color_and_type(c, KNIGHT);
}
inline Piece bishop_of_color(Color c) {
return piece_of_color_and_type(c, BISHOP);
}
inline Piece rook_of_color(Color c) {
return piece_of_color_and_type(c, ROOK);
}
inline Piece queen_of_color(Color c) {
return piece_of_color_and_type(c, QUEEN);
}
inline Piece king_of_color(Color c) {
return piece_of_color_and_type(c, KING);
}
inline int piece_is_slider(Piece p) {
return SlidingArray[int(p)];
}
inline int piece_type_is_slider(PieceType pt) {
return SlidingArray[int(pt)];
}
inline SquareDelta pawn_push(Color c) {
return PawnPush[c];
}
////
//// Prototypes
////
extern char piece_type_to_char(PieceType pt, bool upcase);
extern PieceType piece_type_from_char(char c);
extern bool piece_is_ok(Piece pc);
extern bool piece_type_is_ok(PieceType pt);
#endif // !defined(PIECE_H_INCLUDED)
glaurung-2.2/src/scale.h 0000644 0001750 0001750 00000002202 11123021255 014576 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(SCALE_H_INCLUDED)
#define SCALE_H_INCLUDED
////
//// Includes
////
#include "value.h"
////
//// Types
////
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
////
//// Inline functions
////
inline Value apply_scale_factor(Value v, ScaleFactor f) {
return Value((v * f) / int(SCALE_FACTOR_NORMAL));
}
#endif // !defined(SCALE_H_INCLUDED)
glaurung-2.2/src/benchmark.cpp 0000644 0001750 0001750 00000005764 11123021251 016010 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include "benchmark.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
////
//// Variables
////
const std::string BenchmarkPositions[15] = {
"r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
"4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
"4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
"2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
};
////
//// Functions
////
/// benchmark() runs a simple benchmark by letting Glaurung analyze 15
/// positions for 60 seconds each. There are two parameters; the
/// transposition table size and the number of search threads that should
/// be used. The analysis is written to a file named bench.txt.
void benchmark(const std::string &ttSize, const std::string &threads) {
Position pos;
Move moves[1] = {MOVE_NONE};
int i;
i = atoi(ttSize.c_str());
if(i < 4 || i > 1024) {
std::cerr << "The hash table size must be between 4 and 1024" << std::endl;
exit(EXIT_FAILURE);
}
i = atoi(threads.c_str());
if(i < 1 || i > THREAD_MAX) {
std::cerr << "The number of threads must be between 1 and " << THREAD_MAX
<< std::endl;
exit(EXIT_FAILURE);
}
set_option_value("Hash", ttSize);
set_option_value("Threads", threads);
set_option_value("OwnBook", "false");
set_option_value("Use Search Log", "true");
set_option_value("Search Log Filename", "bench.txt");
for(i = 0; i < 15; i++) {
pos.from_fen(BenchmarkPositions[i]);
think(pos, true, false, 0, 0, 0, 0, 0, 60000, moves);
}
}
glaurung-2.2/src/endgame.cpp 0000644 0001750 0001750 00000101275 11123021252 015451 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "bitbase.h"
#include "endgame.h"
////
//// Constants and variables
////
/// Evaluation functions
// Generic "mate lone king" eval:
KXKEvaluationFunction EvaluateKXK = KXKEvaluationFunction(WHITE);
KXKEvaluationFunction EvaluateKKX = KXKEvaluationFunction(BLACK);
// KBN vs K:
KBNKEvaluationFunction EvaluateKBNK = KBNKEvaluationFunction(WHITE);
KBNKEvaluationFunction EvaluateKKBN = KBNKEvaluationFunction(BLACK);
// KP vs K:
KPKEvaluationFunction EvaluateKPK = KPKEvaluationFunction(WHITE);
KPKEvaluationFunction EvaluateKKP = KPKEvaluationFunction(BLACK);
// KR vs KP:
KRKPEvaluationFunction EvaluateKRKP = KRKPEvaluationFunction(WHITE);
KRKPEvaluationFunction EvaluateKPKR = KRKPEvaluationFunction(BLACK);
// KR vs KB:
KRKBEvaluationFunction EvaluateKRKB = KRKBEvaluationFunction(WHITE);
KRKBEvaluationFunction EvaluateKBKR = KRKBEvaluationFunction(BLACK);
// KR vs KN:
KRKNEvaluationFunction EvaluateKRKN = KRKNEvaluationFunction(WHITE);
KRKNEvaluationFunction EvaluateKNKR = KRKNEvaluationFunction(BLACK);
// KQ vs KR:
KQKREvaluationFunction EvaluateKQKR = KQKREvaluationFunction(WHITE);
KQKREvaluationFunction EvaluateKRKQ = KQKREvaluationFunction(BLACK);
// KBB vs KN:
KBBKNEvaluationFunction EvaluateKBBKN = KBBKNEvaluationFunction(WHITE);
KBBKNEvaluationFunction EvaluateKNKBB = KBBKNEvaluationFunction(BLACK);
// K and two minors vs K and one or two minors:
KmmKmEvaluationFunction EvaluateKmmKm = KmmKmEvaluationFunction(WHITE);
/// Scaling functions
// KBP vs K:
KBPKScalingFunction ScaleKBPK = KBPKScalingFunction(WHITE);
KBPKScalingFunction ScaleKKBP = KBPKScalingFunction(BLACK);
// KQ vs KRP:
KQKRPScalingFunction ScaleKQKRP = KQKRPScalingFunction(WHITE);
KQKRPScalingFunction ScaleKRPKQ = KQKRPScalingFunction(BLACK);
// KRP vs KR:
KRPKRScalingFunction ScaleKRPKR = KRPKRScalingFunction(WHITE);
KRPKRScalingFunction ScaleKRKRP = KRPKRScalingFunction(BLACK);
// KRPP vs KRP:
KRPPKRPScalingFunction ScaleKRPPKRP = KRPPKRPScalingFunction(WHITE);
KRPPKRPScalingFunction ScaleKRPKRPP = KRPPKRPScalingFunction(BLACK);
// King and pawns vs king:
KPsKScalingFunction ScaleKPsK = KPsKScalingFunction(WHITE);
KPsKScalingFunction ScaleKKPs = KPsKScalingFunction(BLACK);
// KBP vs KB:
KBPKBScalingFunction ScaleKBPKB = KBPKBScalingFunction(WHITE);
KBPKBScalingFunction ScaleKBKBP = KBPKBScalingFunction(BLACK);
// KBP vs KN:
KBPKNScalingFunction ScaleKBPKN = KBPKNScalingFunction(WHITE);
KBPKNScalingFunction ScaleKNKBP = KBPKNScalingFunction(BLACK);
// KNP vs K:
KNPKScalingFunction ScaleKNPK = KNPKScalingFunction(WHITE);
KNPKScalingFunction ScaleKKNP = KNPKScalingFunction(BLACK);
// KPKP
KPKPScalingFunction ScaleKPKPw = KPKPScalingFunction(WHITE);
KPKPScalingFunction ScaleKPKPb = KPKPScalingFunction(BLACK);
////
//// Local definitions
////
namespace {
// Table used to drive the defending king towards the edge of the board
// in KX vs K and KQ vs KR endgames:
const uint8_t MateTable[64] = {
100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80,
70, 50, 30, 20, 20, 30, 50, 70,
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,
};
// Table used to drive the defending king towards a corner square of the
// right color in KBN vs K endgames:
const uint8_t KBNKMateTable[64] = {
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
};
// The attacking side is given a descending bonus based on distance between
// the two kings in basic endgames:
const int DistanceBonus[8] = {0, 0, 100, 80, 60, 40, 20, 10};
// Bitbase for KP vs K:
uint8_t KPKBitbase[24576];
// Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames:
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Various inline functions for accessing the above arrays:
inline Value mate_table(Square s) {
return Value(MateTable[s]);
}
inline Value kbnk_mate_table(Square s) {
return Value(KBNKMateTable[s]);
}
inline Value distance_bonus(int d) {
return Value(DistanceBonus[d]);
}
inline Value krkn_king_knight_distance_penalty(int d) {
return Value(KRKNKingKnightDistancePenalty[d]);
}
// Function for probing the KP vs K bitbase:
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
}
////
//// Functions
////
/// Constructors
EndgameEvaluationFunction::EndgameEvaluationFunction(Color c) {
strongerSide = c;
weakerSide = opposite_color(strongerSide);
}
KXKEvaluationFunction::KXKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KBNKEvaluationFunction::KBNKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KPKEvaluationFunction::KPKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KRKPEvaluationFunction::KRKPEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KRKBEvaluationFunction::KRKBEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KRKNEvaluationFunction::KRKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KQKREvaluationFunction::KQKREvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KBBKNEvaluationFunction::KBBKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KmmKmEvaluationFunction::KmmKmEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
ScalingFunction::ScalingFunction(Color c) {
strongerSide = c;
weakerSide = opposite_color(c);
}
KBPKScalingFunction::KBPKScalingFunction(Color c) : ScalingFunction(c) { }
KQKRPScalingFunction::KQKRPScalingFunction(Color c) : ScalingFunction(c) { }
KRPKRScalingFunction::KRPKRScalingFunction(Color c) : ScalingFunction(c) { }
KRPPKRPScalingFunction::KRPPKRPScalingFunction(Color c) : ScalingFunction(c) { }
KPsKScalingFunction::KPsKScalingFunction(Color c) : ScalingFunction(c) { }
KBPKBScalingFunction::KBPKBScalingFunction(Color c) : ScalingFunction(c) { }
KBPKNScalingFunction::KBPKNScalingFunction(Color c) : ScalingFunction(c) { }
KNPKScalingFunction::KNPKScalingFunction(Color c) : ScalingFunction(c) { }
KPKPScalingFunction::KPKPScalingFunction(Color c) : ScalingFunction(c) { }
/// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small.
Value KXKEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(weakerSide) == Value(0));
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result =
pos.non_pawn_material(strongerSide) +
pos.pawn_count(strongerSide) * PawnValueEndgame +
mate_table(loserKSq) +
distance_bonus(square_distance(winnerKSq, loserKSq));
if(pos.queen_count(strongerSide) > 0 || pos.rook_count(strongerSide) > 0 ||
pos.bishop_count(strongerSide) > 1)
// TODO: check for two equal-colored bishops!
result += VALUE_KNOWN_WIN;
return (strongerSide == pos.side_to_move())? result : -result;
}
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color.
Value KBNKEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(weakerSide) == Value(0));
assert(pos.non_pawn_material(strongerSide) ==
KnightValueMidgame + BishopValueMidgame);
assert(pos.bishop_count(strongerSide) == 1);
assert(pos.knight_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.bishop_list(strongerSide, 0);
if(square_color(bishopSquare) == BLACK) {
winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq);
}
Value result =
VALUE_KNOWN_WIN + distance_bonus(square_distance(winnerKSq, loserKSq)) +
kbnk_mate_table(loserKSq);
return (strongerSide == pos.side_to_move())? result : -result;
}
/// KP vs K. This endgame is evaluated with the help of a bitbase.
Value KPKEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(strongerSide) == 1);
assert(pos.pawn_count(weakerSide) == 0);
Square wksq, bksq, wpsq;
Color stm;
if(strongerSide == WHITE) {
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.pawn_list(WHITE, 0);
stm = pos.side_to_move();
}
else {
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.pawn_list(BLACK, 0));
stm = opposite_color(pos.side_to_move());
}
if(square_file(wpsq) >= FILE_E) {
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
}
if(probe_kpk(wksq, wpsq, bksq, stm)) {
Value result =
VALUE_KNOWN_WIN + PawnValueEndgame + Value(square_rank(wpsq));
return (strongerSide == pos.side_to_move())? result : -result;
}
return VALUE_DRAW;
}
/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
/// a bitbase. The function below returns drawish scores when the pawn is
/// far advanced with support of the king, while the attacking king is far
/// away.
Value KRKPEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.pawn_count(strongerSide) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.pawn_count(weakerSide) == 1);
Square wksq, wrsq, bksq, bpsq;
int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide);
wrsq = pos.rook_list(strongerSide, 0);
bksq = pos.king_square(weakerSide);
bpsq = pos.pawn_list(weakerSide, 0);
if(strongerSide == BLACK) {
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
bksq = flip_square(bksq);
bpsq = flip_square(bpsq);
}
Square queeningSq = make_square(square_file(bpsq), RANK_1);
Value result;
// If the stronger side's king is in front of the pawn, it's a win:
if(wksq < bpsq && square_file(wksq) == square_file(bpsq))
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the weaker side's king is too far from the pawn and the rook,
// it's a win:
else if(square_distance(bksq, bpsq) - (tempo^1) >= 3 &&
square_distance(bksq, wrsq) >= 3)
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the pawn is far advanced and supported by the defending king,
// the position is drawish:
else if(square_rank(bksq) <= RANK_3 && square_distance(bksq, bpsq) == 1 &&
square_rank(wksq) >= RANK_4 &&
square_distance(wksq, bpsq) - tempo > 2)
result = Value(80 - square_distance(wksq, bpsq) * 8);
else
result = Value(200)
- Value(square_distance(wksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bpsq, queeningSq) * 8);
return (strongerSide == pos.side_to_move())? result : -result;
}
/// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge.
Value KRKBEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.pawn_count(strongerSide) == 0);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.pawn_count(weakerSide) == 0);
assert(pos.bishop_count(weakerSide) == 1);
Value result = mate_table(pos.king_square(weakerSide));
return (pos.side_to_move() == strongerSide)? result : -result;
}
/// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart.
Value KRKNEvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.pawn_count(strongerSide) == 0);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pawn_count(weakerSide) == 0);
assert(pos.knight_count(weakerSide) == 1);
Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.knight_list(weakerSide, 0);
Value result = Value(10) + mate_table(defendingKSq) +
krkn_king_knight_distance_penalty(square_distance(defendingKSq, nSq));
return (strongerSide == pos.side_to_move())? result : -result;
}
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking
/// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move
/// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR.
Value KQKREvaluationFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.pawn_count(strongerSide) == 0);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.pawn_count(weakerSide) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = QueenValueEndgame - RookValueEndgame +
mate_table(loserKSq) + distance_bonus(square_distance(winnerKSq, loserKSq));
return (strongerSide == pos.side_to_move())? result : -result;
}
Value KBBKNEvaluationFunction::apply(const Position &pos) {
assert(pos.bishop_count(strongerSide) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.knight_count(weakerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pawns() == EmptyBoardBB);
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.knight_list(weakerSide, 0);
// Bonus for attacking king close to defending king:
result += distance_bonus(square_distance(wksq, bksq));
// Bonus for driving the defending king and knight apart:
result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility:
result += Value((8 - count_1s_max_15(pos.knight_attacks(nsq))) * 8);
return (strongerSide == pos.side_to_move())? result : -result;
}
Value KmmKmEvaluationFunction::apply(const Position &pos) {
return Value(0);
}
/// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
ScaleFactor KBPKScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.bishop_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) >= 1);
// No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pawns(strongerSide);
File pawnFile = square_file(pos.pawn_list(strongerSide, 0));
if((pawnFile == FILE_A || pawnFile == FILE_H) &&
(pawns & ~file_bb(pawnFile)) == EmptyBoardBB) {
// All pawns are on a single rook file.
Square bishopSq = pos.bishop_list(strongerSide, 0);
Square queeningSq =
relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide);
if(square_color(queeningSq) != square_color(bishopSq) &&
file_distance(square_file(kingSq), pawnFile) <= 1) {
// The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the
// frontmost pawn:
Rank rank;
if(strongerSide == WHITE) {
for(rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--);
assert(rank >= RANK_2 && rank <= RANK_7);
}
else {
for(rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++);
rank = Rank(rank^7); // HACK
assert(rank >= RANK_2 && rank <= RANK_7);
}
// If the defending king has distance 1 to the promotion square or
// is placed somewhere in front of the pawn, it's a draw.
if(square_distance(kingSq, queeningSq) <= 1 ||
pawn_rank(strongerSide, kingSq) >= rank)
return ScaleFactor(0);
}
}
return SCALE_FACTOR_NONE;
}
/// KQKRPScalingFunction scales endgames where the stronger side has only
/// king and queen, while the weaker side has at least a rook and a pawn.
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
ScaleFactor KQKRPScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.queen_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) == 0);
assert(pos.rook_count(weakerSide) == 1);
assert(pos.pawn_count(weakerSide) >= 1);
Square kingSq = pos.king_square(weakerSide);
if(pawn_rank(weakerSide, kingSq) <= RANK_2 &&
pawn_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 &&
(pos.rooks(weakerSide) & relative_rank_bb(weakerSide, RANK_3)) &&
(pos.pawns(weakerSide) & relative_rank_bb(weakerSide, RANK_2)) &&
(pos.king_attacks(kingSq) & pos.pawns(weakerSide))) {
Square rsq = pos.rook_list(weakerSide, 0);
if(pos.pawn_attacks(strongerSide, rsq) & pos.pawns(weakerSide))
return ScaleFactor(0);
}
return SCALE_FACTOR_NONE;
}
/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a
/// handful of the most important classes of drawn positions, but is far
/// from perfect. It would probably be a good idea to add more knowledge
/// in the future.
///
/// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty.
ScaleFactor KRPKRScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.pawn_count(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.pawn_count(weakerSide) == 0);
Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.rook_list(strongerSide, 0);
Square wpsq = pos.pawn_list(strongerSide, 0);
Square bksq = pos.king_square(weakerSide);
Square brsq = pos.rook_list(weakerSide, 0);
// Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board:
if(strongerSide == BLACK) {
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
wpsq = flip_square(wpsq);
bksq = flip_square(bksq);
brsq = flip_square(brsq);
}
if(square_file(wpsq) > FILE_D) {
wksq = flop_square(wksq);
wrsq = flop_square(wrsq);
wpsq = flop_square(wpsq);
bksq = flop_square(bksq);
brsq = flop_square(brsq);
}
File f = square_file(wpsq);
Rank r = square_rank(wpsq);
Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongerSide);
// If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence:
if(r <= RANK_5 && square_distance(bksq, queeningSq) <= 1 && wksq <= SQ_H5 &&
(square_rank(brsq) == RANK_6 || (r <= RANK_3 &&
square_rank(wrsq) != RANK_6)))
return ScaleFactor(0);
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if(r == RANK_6 && square_distance(bksq, queeningSq) <= 1 &&
square_rank(wksq) + tempo <= RANK_6 &&
(square_rank(brsq) == RANK_1 ||
(!tempo && abs(square_file(brsq) - f) >= 3)))
return ScaleFactor(0);
if(r >= RANK_6 && bksq == queeningSq && square_rank(brsq) == RANK_1 &&
(!tempo || square_distance(wksq, wpsq) >= 2))
return ScaleFactor(0);
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn.
if(wpsq == SQ_A7 && wrsq == SQ_A8 && (bksq == SQ_H7 || bksq == SQ_G7) &&
square_file(brsq) == FILE_A &&
(square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D ||
square_rank(wksq) <= RANK_5))
return ScaleFactor(0);
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
if(r <= RANK_5 && bksq == wpsq + DELTA_N &&
square_distance(wksq, wpsq) - tempo >= 2 &&
square_distance(wksq, brsq) - tempo >= 2)
return ScaleFactor(0);
// Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking
// rook.
if(r == RANK_7 && f != FILE_A && square_file(wrsq) == f
&& wrsq != queeningSq
&& (square_distance(wksq, queeningSq) <
square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, queeningSq) <
square_distance(bksq, wrsq) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX
- 2 * square_distance(wksq, queeningSq));
// Similar to the above, but with the pawn further back:
if(f != FILE_A && square_file(wrsq) == f && wrsq < wpsq
&& (square_distance(wksq, queeningSq) <
square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, wpsq + DELTA_N) <
square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
&& (square_distance(bksq, wrsq) + tempo >= 3
|| (square_distance(wksq, queeningSq) <
square_distance(bksq, wrsq) + tempo
&& (square_distance(wksq, wpsq + DELTA_N) <
square_distance(bksq, wrsq) + tempo))))
return
ScaleFactor(SCALE_FACTOR_MAX
- (8 * square_distance(wpsq, queeningSq) +
2 * square_distance(wksq, queeningSq)));
// If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw:
if(r <= RANK_4 && bksq > wpsq) {
if(square_file(bksq) == square_file(wpsq))
return ScaleFactor(10);
if(abs(square_file(bksq) - square_file(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
}
return SCALE_FACTOR_NONE;
}
/// KRPPKRPScalingFunction scales KRPP vs KRP endgames. There is only a
/// single pattern: If the stronger side has no pawns and the defending king
/// is actively placed, the position is drawish.
ScaleFactor KRPPKRPScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.pawn_count(strongerSide) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.pawn_count(weakerSide) == 1);
Square wpsq1 = pos.pawn_list(strongerSide, 0);
Square wpsq2 = pos.pawn_list(strongerSide, 1);
Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn?
if(pos.pawn_is_passed(strongerSide, wpsq1) ||
pos.pawn_is_passed(strongerSide, wpsq2))
return SCALE_FACTOR_NONE;
Rank r = Max(pawn_rank(strongerSide, wpsq1), pawn_rank(strongerSide, wpsq2));
if(file_distance(bksq, wpsq1) <= 1 && file_distance(bksq, wpsq2) <= 1
&& pawn_rank(strongerSide, bksq) > r) {
switch(r) {
case RANK_2: return ScaleFactor(10);
case RANK_3: return ScaleFactor(10);
case RANK_4: return ScaleFactor(15);
case RANK_5: return ScaleFactor(20);
case RANK_6: return ScaleFactor(40);
default: assert(false);
}
}
return SCALE_FACTOR_NONE;
}
/// KPsKScalingFunction scales endgames with king and two or more pawns
/// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw.
ScaleFactor KPsKScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.pawn_count(strongerSide) >= 2);
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(weakerSide) == 0);
Bitboard pawns = pos.pawns(strongerSide);
// Are all pawns on the 'a' file?
if((pawns & ~FileABB) == EmptyBoardBB) {
// Does the defending king block the pawns?
Square ksq = pos.king_square(weakerSide);
if(square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1)
return ScaleFactor(0);
else if(square_file(ksq) == FILE_A &&
(in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
}
// Are all pawns on the 'h' file?
else if((pawns & ~FileHBB) == EmptyBoardBB) {
// Does the defending king block the pawns?
Square ksq = pos.king_square(weakerSide);
if(square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1)
return ScaleFactor(0);
else if(square_file(ksq) == FILE_H &&
(in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
}
else
return SCALE_FACTOR_NONE;
}
/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw.
ScaleFactor KBPKBScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.bishop_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.bishop_count(weakerSide) == 1);
assert(pos.pawn_count(weakerSide) == 0);
Square pawnSq = pos.pawn_list(strongerSide, 0);
Square strongerBishopSq = pos.bishop_list(strongerSide, 0);
Square weakerBishopSq = pos.bishop_list(weakerSide, 0);
Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away.
if(square_file(weakerKingSq) == square_file(pawnSq)
&& pawn_rank(strongerSide, pawnSq) < pawn_rank(strongerSide, weakerKingSq)
&& (square_color(weakerKingSq) != square_color(strongerBishopSq)
|| pawn_rank(strongerSide, weakerKingSq) <= RANK_6))
return ScaleFactor(0);
// Case 2: Opposite colored bishops.
if(square_color(strongerBishopSq) != square_color(weakerBishopSq)) {
// We assume that the position is drawn in the following three situations:
//
// a. The pawn is on rank 5 or further back.
// b. The defending king is somewhere in the pawn's path.
// c. The defending bishop attacks some square along the pawn's path,
// and is at least three squares away from the pawn.
//
// These rules are probably not perfect, but in practice they work
// reasonably well.
if(pawn_rank(strongerSide, pawnSq) <= RANK_5)
return ScaleFactor(0);
else {
Bitboard ray =
ray_bb(pawnSq, (strongerSide == WHITE)? SIGNED_DIR_N : SIGNED_DIR_S);
if(ray & pos.kings(weakerSide))
return ScaleFactor(0);
if((pos.bishop_attacks(weakerBishopSq) & ray)
&& square_distance(weakerBishopSq, pawnSq) >= 3)
return ScaleFactor(0);
}
}
return SCALE_FACTOR_NONE;
}
/// KBPKNScalingFunction scales KBP vs KN endgames. There is a single rule:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw.
ScaleFactor KBPKNScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.bishop_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.knight_count(weakerSide) == 1);
assert(pos.pawn_count(weakerSide) == 0);
Square pawnSq = pos.pawn_list(strongerSide, 0);
Square strongerBishopSq = pos.bishop_list(strongerSide, 0);
Square weakerKingSq = pos.king_square(weakerSide);
if(square_file(weakerKingSq) == square_file(pawnSq)
&& pawn_rank(strongerSide, pawnSq) < pawn_rank(strongerSide, weakerKingSq)
&& (square_color(weakerKingSq) != square_color(strongerBishopSq)
|| pawn_rank(strongerSide, weakerKingSq) <= RANK_6))
return ScaleFactor(0);
return SCALE_FACTOR_NONE;
}
/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule:
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn.
ScaleFactor KNPKScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.knight_count(strongerSide) == 1);
assert(pos.pawn_count(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(weakerSide) == 0);
Square pawnSq = pos.pawn_list(strongerSide, 0);
Square weakerKingSq = pos.king_square(weakerSide);
if(pawnSq == relative_square(strongerSide, SQ_A7) &&
square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return ScaleFactor(0);
if(pawnSq == relative_square(strongerSide, SQ_H7) &&
square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return ScaleFactor(0);
return SCALE_FACTOR_NONE;
}
/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing
/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest
/// side has a draw without the pawn, she probably has at least a draw with
/// the pawn as well. The exception is when the stronger side's pawn is far
/// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
ScaleFactor KPKPScalingFunction::apply(const Position &pos) {
assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.pawn_count(WHITE) == 1);
assert(pos.pawn_count(BLACK) == 1);
Square wksq, bksq, wpsq;
Color stm;
if(strongerSide == WHITE) {
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.pawn_list(WHITE, 0);
stm = pos.side_to_move();
}
else {
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.pawn_list(BLACK, 0));
stm = opposite_color(pos.side_to_move());
}
if(square_file(wpsq) >= FILE_E) {
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
}
// If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw.
if(square_rank(wpsq) >= RANK_5 && square_file(wpsq) != FILE_A)
return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, it's probably at least a draw even with the pawn.
if(probe_kpk(wksq, wpsq, bksq, stm))
return SCALE_FACTOR_NONE;
else
return ScaleFactor(0);
}
/// init_bitbases() is called during program initialization, and simply loads
/// bitbases from disk into memory. At the moment, there is only the bitbase
/// for KP vs K, but we may decide to add other bitbases later.
void init_bitbases() {
generate_kpk_bitbase(KPKBitbase);
}
namespace {
// Probe the KP vs K bitbase:
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
int wp = int(square_file(wpsq)) + (int(square_rank(wpsq)) - 1) * 4;
int index = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*wp;
assert(index >= 0 && index < 24576*8);
return KPKBitbase[index/8] & (1 << (index&7));
}
}
glaurung-2.2/src/history.cpp 0000644 0001750 0001750 00000005137 11123021252 015552 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "history.h"
////
//// Functions
////
/// Constructor
History::History() {
this->clear();
}
/// History::clear() clears the history tables.
void History::clear() {
memset(history, 0, 2 * 8 * 64 * sizeof(int));
memset(successCount, 0, 2 * 8 * 64 * sizeof(int));
memset(failureCount, 0, 2 * 8 * 64 * sizeof(int));
}
/// History::success() registers a move as being successful. This is done
/// whenever a non-capturing move causes a beta cutoff in the main search.
/// The three parameters are the moving piece, the move itself, and the
/// search depth.
void History::success(Piece p, Move m, Depth d) {
assert(piece_is_ok(p));
assert(move_is_ok(m));
history[p][move_to(m)] += int(d) * int(d);
successCount[p][move_to(m)]++;
// Prevent history overflow:
if(history[p][move_to(m)] >= HistoryMax)
for(int i = 0; i < 16; i++)
for(int j = 0; j < 64; j++)
history[i][j] /= 2;
}
/// History::failure() registers a move as being unsuccessful. The function is
/// called for each non-capturing move which failed to produce a beta cutoff
/// at a node where a beta cutoff was finally found.
void History::failure(Piece p, Move m) {
assert(piece_is_ok(p));
assert(move_is_ok(m));
failureCount[p][move_to(m)]++;
}
/// History::move_ordering_score() returns an integer value used to order the
/// non-capturing moves in the MovePicker class.
int History::move_ordering_score(Piece p, Move m) const {
assert(piece_is_ok(p));
assert(move_is_ok(m));
return history[p][move_to(m)];
}
/// History::ok_to_prune() decides whether a move has been sufficiently
/// unsuccessful that it makes sense to prune it entirely.
bool History::ok_to_prune(Piece p, Move m, Depth d) const {
assert(piece_is_ok(p));
assert(move_is_ok(m));
return (int(d) * successCount[p][move_to(m)] < failureCount[p][move_to(m)]);
}
glaurung-2.2/src/mersenne.cpp 0000644 0001750 0001750 00000012006 11123021252 015656 0 ustar oliver oliver /*
A C-program for MT19937, with initialization improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Any feedback is very welcome.
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/
#include "types.h"
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
static unsigned long mt[N]; /* the array for the state vector */
static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */
void init_genrand(unsigned long s)
{
mt[0]= s & 0xffffffffUL;
for (mti=1; mti> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt[mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
void init_by_array(unsigned long init_key[], int key_length)
{
int i, j, k;
init_genrand(19650218UL);
i=1; j=0;
k = (N>key_length ? N : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ init_key[j] + j; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++; j++;
if (i>=N) { mt[0] = mt[N-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=N-1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i>=N) { mt[0] = mt[N-1]; i=1; }
}
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
}
/* generates a random number on [0,0xffffffff]-interval */
uint32_t genrand_int32(void) {
unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */
int kk;
if (mti == N+1) /* if init_genrand() has not been called, */
init_genrand(5489UL); /* a default initial seed is used */
for (kk=0;kk> 1) ^ mag01[y & 0x1UL];
}
for (;kk> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
uint64_t genrand_int64(void) {
uint64_t x, y;
x = genrand_int32(); y = genrand_int32();
return (x<<32)|y;
}
void init_mersenne(void) {
unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
init_by_array(init, length);
}
glaurung-2.2/src/book.h 0000644 0001750 0001750 00000003442 11123021255 014450 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#if !defined(BOOK_H_INCLUDED)
#define BOOK_H_INCLUDED
////
//// Includes
////
#include
#include "move.h"
#include "position.h"
////
//// Types
////
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint16_t n;
uint16_t sum;
};
class Book {
public:
// Constructors
Book();
// Open and close book files
void open(const std::string &fName);
void close();
// Testing if a book is opened
bool is_open() const;
// The file name of the currently active book
const std::string file_name() const;
// Get a book move for a given position
Move get_move(const Position &pos) const;
private:
int find_key(uint64_t key) const;
void read_entry(BookEntry &entry, int n) const;
std::string fileName;
FILE *bookFile;
int bookSize;
};
////
//// Global variables
////
extern Book OpeningBook;
#endif // !defined(BOOK_H_INCLUDED)
glaurung-2.2/src/ucioption.h 0000644 0001750 0001750 00000002665 11123021255 015535 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED
////
//// Includes
////
#include
////
//// Variables
////
extern bool Chess960;
////
//// Prototypes
////
extern void init_uci_options();
extern void print_uci_options();
extern bool get_option_value_bool(const std::string &optionName);
extern int get_option_value_int(const std::string &optionName);
extern const std::string get_option_value_string(const std::string &optionName);
extern bool button_was_pressed(const std::string &buttonName);
extern void set_option_value(const std::string &optionName,
const std::string &newValue);
extern void push_button(const std::string &buttonName);
#endif // !defined(UCIOPTION_H_INCLUDED)
glaurung-2.2/src/uci.cpp 0000644 0001750 0001750 00000026332 11123021251 014630 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "book.h"
#include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "movegen.h"
#include "position.h"
#include "san.h"
#include "search.h"
#include "uci.h"
#include "ucioption.h"
////
//// Local definitions:
////
namespace {
// UCIInputParser is a class for parsing UCI input. The class is
// very simple, and basically just consist of a constant input
// string and a current location in the string. There are methods
// for checking if we are at the end of the line, for getting the
// next token (defined as any whitespace-delimited sequence of
// characters), and for getting the rest of the line as a single
// string.
class UCIInputParser {
public:
UCIInputParser(const std::string &line);
std::string get_next_token();
std::string get_rest_of_line();
bool at_end_of_line();
private:
const std::string &inputLine;
int length, currentIndex;
void skip_whitespace();
};
// The root position. This is set up when the user (or in practice, the GUI)
// sends the "position" UCI command. The root position is sent to the think()
// function when the program receives the "go" command.
Position RootPosition;
// Local functions
void wait_for_command();
void handle_command(const std::string &command);
void set_option(UCIInputParser &uip);
void set_position(UCIInputParser &uip);
void go(UCIInputParser &uip);
}
////
//// Functions
////
/// uci_main_loop() is the only global function in this file. It is
/// called immediately after the program has finished initializing.
/// The program remains in this loop until it receives the "quit" UCI
/// command.
void uci_main_loop() {
RootPosition.from_fen(StartPosition);
while(1) wait_for_command();
}
////
//// Local functions
////
namespace {
///
/// Implementation of the UCIInputParser class.
///
// Constructor for the UCIInputParser class. The constructor takes a
// text string containing a single UCI command as input.
UCIInputParser::UCIInputParser(const std::string &line) : inputLine(line) {
this->currentIndex = 0;
this->length = line.length();
}
// UCIInputParser::skip_whitspace() skips any number of whitespace
// characters from the current location in an input string.
void UCIInputParser::skip_whitespace() {
while(isspace((int)(unsigned char)this->inputLine[this->currentIndex]))
this->currentIndex++;
}
// UCIInputParser::get_next_token() gets the next token in an UCI
// command. A 'token' in an UCI command is simply any
// whitespace-delimited sequence of characters.
std::string UCIInputParser::get_next_token() {
int i, j;
this->skip_whitespace();
for(i = j = this->currentIndex;
j < this->length && !isspace(this->inputLine[j]);
j++);
this->currentIndex = j;
this->skip_whitespace();
std::string str = this->inputLine.substr(i, j - i);
return str;
}
// UCIInputParser::get_rest_of_line() returns the rest of the input
// line (from the current location) as a single string.
std::string UCIInputParser::get_rest_of_line() {
this->skip_whitespace();
return this->inputLine.substr(this->currentIndex, this->length);
}
// UCIInputParser::at_end_of_line() tests whether we have reached the
// end of the input string, i.e. if any more input remains to be
// parsed.
bool UCIInputParser::at_end_of_line() {
return this->currentIndex == this->length;
}
///
/// Other functions
///
// wait_for_command() waits for a command from the user, and passes
// this command to handle_command. wait_for_command also intercepts
// EOF from stdin, by translating EOF to the "quit" command. This
// ensures that Glaurung exits gracefully if the GUI dies
// unexpectedly.
void wait_for_command() {
std::string command;
if(!std::getline(std::cin, command)) command = "quit";
handle_command(command);
}
// handle_command() takes a text string as input, uses a
// UCIInputParser object to parse this text string as a UCI command,
// and calls the appropriate functions. In addition to the UCI
// commands, the function also supports a few debug commands.
void handle_command(const std::string &command) {
UCIInputParser uip(command);
std::string s = uip.get_next_token();
if(s == "quit") {
OpeningBook.close();
stop_threads();
quit_eval();
exit(0);
}
else if(s == "uci") {
std::cout << "id name " << engine_name() << std::endl;
std::cout << "id author Tord Romstad" << std::endl;
print_uci_options();
std::cout << "uciok" << std::endl;
}
else if(s == "ucinewgame") {
TT.clear();
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
else if(s == "isready")
std::cout << "readyok" << std::endl;
else if(s == "position")
set_position(uip);
else if(s == "setoption")
set_option(uip);
else if(s == "go")
go(uip);
// The remaining commands are for debugging purposes only.
// Perhaps they should be removed later in order to reduce the
// size of the program binary.
else if(s == "d")
RootPosition.print();
else if(s == "flip") {
Position p(RootPosition);
RootPosition.flipped_copy(p);
}
else if(s == "eval") {
EvalInfo ei;
std::cout << "Incremental mg: " << RootPosition.mg_value()
<< std::endl;
std::cout << "Incremental eg: " << RootPosition.eg_value()
<< std::endl;
std::cout << "Full eval: "
<< evaluate(RootPosition, ei, 0)
<< std::endl;
}
else if(s == "key") {
std::cout << "key: " << RootPosition.get_key()
<< " material key: " << RootPosition.get_material_key()
<< " pawn key: " << RootPosition.get_pawn_key()
<< std::endl;
}
else {
std::cout << "Unknown command: " << command << std::endl;
while(!uip.at_end_of_line()) {
std::cout << uip.get_next_token() << std::endl;
}
}
}
// set_position() is called when Glaurung receives the "position" UCI
// command. The input parameter is a UCIInputParser. It is assumed
// that this parser has consumed the first token of the UCI command
// ("position"), and is ready to read the second token ("startpos"
// or "fen", if the input is well-formed).
void set_position(UCIInputParser &uip) {
std::string token;
token = uip.get_next_token();
if(token == "startpos")
RootPosition.from_fen(StartPosition);
else if(token == "fen") {
std::string fen;
while(token != "moves" && !uip.at_end_of_line()) {
token = uip.get_next_token();
fen += token;
fen += ' ';
}
RootPosition.from_fen(fen);
}
if(!uip.at_end_of_line()) {
if(token != "moves")
token = uip.get_next_token();
if(token == "moves") {
Move move;
UndoInfo u;
while(!uip.at_end_of_line()) {
token = uip.get_next_token();
move = move_from_string(RootPosition, token);
RootPosition.do_move(move, u);
if(RootPosition.rule_50_counter() == 0)
RootPosition.reset_game_ply();
}
}
}
}
// set_option() is called when Glaurung receives the "setoption" UCI
// command. The input parameter is a UCIInputParser. It is assumed
// that this parser has consumed the first token of the UCI command
// ("setoption"), and is ready to read the second token ("name", if
// the input is well-formed).
void set_option(UCIInputParser &uip) {
std::string token;
if(!uip.at_end_of_line()) {
token = uip.get_next_token();
if(token == "name" && !uip.at_end_of_line()) {
std::string name = uip.get_next_token();
std::string nextToken;
while(!uip.at_end_of_line()
&& (nextToken = uip.get_next_token()) != "value")
name += (" " + nextToken);
if(nextToken == "value")
set_option_value(name, uip.get_rest_of_line());
else
push_button(name);
}
}
}
// go() is called when Glaurung receives the "go" UCI command. The
// input parameter is a UCIInputParser. It is assumed that this
// parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and
// calls think() (defined in search.cpp) with the appropriate
// parameters.
void go(UCIInputParser &uip) {
std::string token;
int time[2] = {0, 0}, inc[2] = {0, 0}, movesToGo = 0, depth = 0, nodes = 0;
int moveTime = 0;
bool infinite = false, ponder = false;
Move searchMoves[500];
searchMoves[0] = MOVE_NONE;
while(!uip.at_end_of_line()) {
token = uip.get_next_token();
if(token == "infinite")
infinite = true;
else if(token == "ponder")
ponder = true;
else if(token == "wtime") {
if(!uip.at_end_of_line())
time[0] = atoi(uip.get_next_token().c_str());
}
else if(token == "btime") {
if(!uip.at_end_of_line())
time[1] = atoi(uip.get_next_token().c_str());
}
else if(token == "winc") {
if(!uip.at_end_of_line())
inc[0] = atoi(uip.get_next_token().c_str());
}
else if(token == "binc") {
if(!uip.at_end_of_line())
inc[1] = atoi(uip.get_next_token().c_str());
}
else if(token == "movestogo") {
if(!uip.at_end_of_line())
movesToGo = atoi(uip.get_next_token().c_str());
}
else if(token == "depth") {
if(!uip.at_end_of_line())
depth = atoi(uip.get_next_token().c_str());
}
else if(token == "nodes") {
if(!uip.at_end_of_line())
nodes = atoi(uip.get_next_token().c_str());
}
else if(token == "movetime") {
if(!uip.at_end_of_line())
moveTime = atoi(uip.get_next_token().c_str());
}
else if(token == "searchmoves" && !uip.at_end_of_line()) {
int numOfMoves = 0;
while(!uip.at_end_of_line()) {
token = uip.get_next_token();
searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
}
searchMoves[numOfMoves] = MOVE_NONE;
}
}
if(moveTime)
infinite = true; // HACK
think(RootPosition, infinite, ponder, time[RootPosition.side_to_move()],
inc[RootPosition.side_to_move()], movesToGo, depth, nodes, moveTime,
searchMoves);
}
}
glaurung-2.2/src/endgame.h 0000644 0001750 0001750 00000013403 11123021255 015114 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED
////
//// Includes
////
#include "position.h"
#include "scale.h"
#include "value.h"
////
//// Types
////
/// Abstract base class for all special endgame evaluation functions:
class EndgameEvaluationFunction {
public:
EndgameEvaluationFunction(Color c);
virtual ~EndgameEvaluationFunction() { }
virtual Value apply(const Position &pos) =0;
protected:
Color strongerSide, weakerSide;
};
/// Subclasses for various concrete endgames:
// Generic "mate lone king" eval:
class KXKEvaluationFunction : public EndgameEvaluationFunction {
public:
KXKEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KBN vs K:
class KBNKEvaluationFunction : public EndgameEvaluationFunction {
public:
KBNKEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KP vs K:
class KPKEvaluationFunction : public EndgameEvaluationFunction {
public:
KPKEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KR vs KP:
class KRKPEvaluationFunction : public EndgameEvaluationFunction {
public:
KRKPEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KR vs KB:
class KRKBEvaluationFunction : public EndgameEvaluationFunction {
public:
KRKBEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KR vs KN:
class KRKNEvaluationFunction : public EndgameEvaluationFunction {
public:
KRKNEvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KQ vs KR:
class KQKREvaluationFunction : public EndgameEvaluationFunction {
public:
KQKREvaluationFunction(Color c);
Value apply(const Position &pos);
};
// KBB vs KN:
class KBBKNEvaluationFunction : public EndgameEvaluationFunction {
public:
KBBKNEvaluationFunction(Color C);
Value apply(const Position &pos);
};
// K and two minors vs K and one or two minors:
class KmmKmEvaluationFunction : public EndgameEvaluationFunction {
public:
KmmKmEvaluationFunction(Color c);
Value apply(const Position &pos);
};
/// Abstract base class for all evaluation scaling functions:
class ScalingFunction {
public:
ScalingFunction(Color c);
virtual ~ScalingFunction() { }
virtual ScaleFactor apply(const Position &pos) =0;
protected:
Color strongerSide, weakerSide;
};
/// Subclasses for various concrete endgames:
// KBP vs K:
class KBPKScalingFunction : public ScalingFunction {
public:
KBPKScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KQ vs KRP:
class KQKRPScalingFunction: public ScalingFunction {
public:
KQKRPScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KRP vs KR:
class KRPKRScalingFunction : public ScalingFunction {
public:
KRPKRScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KRPP vs KRP:
class KRPPKRPScalingFunction : public ScalingFunction {
public:
KRPPKRPScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// King and pawns vs king:
class KPsKScalingFunction : public ScalingFunction {
public:
KPsKScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KBP vs KB:
class KBPKBScalingFunction : public ScalingFunction {
public:
KBPKBScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KBP vs KN:
class KBPKNScalingFunction : public ScalingFunction {
public:
KBPKNScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KNP vs K:
class KNPKScalingFunction : public ScalingFunction {
public:
KNPKScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
// KP vs KP:
class KPKPScalingFunction : public ScalingFunction {
public:
KPKPScalingFunction(Color c);
ScaleFactor apply(const Position &pos);
};
////
//// Constants and variables
////
// Generic "mate lone king" eval:
extern KXKEvaluationFunction EvaluateKXK, EvaluateKKX;
// KBN vs K:
extern KBNKEvaluationFunction EvaluateKBNK, EvaluateKKBN;
// KP vs K:
extern KPKEvaluationFunction EvaluateKPK, EvaluateKKP;
// KR vs KP:
extern KRKPEvaluationFunction EvaluateKRKP, EvaluateKPKR;
// KR vs KB:
extern KRKBEvaluationFunction EvaluateKRKB, EvaluateKBKR;
// KR vs KN:
extern KRKNEvaluationFunction EvaluateKRKN, EvaluateKNKR;
// KQ vs KR:
extern KQKREvaluationFunction EvaluateKQKR, EvaluateKRKQ;
// KBB vs KN:
extern KBBKNEvaluationFunction EvaluateKBBKN, EvaluateKNKBB;
// K and two minors vs K and one or two minors:
extern KmmKmEvaluationFunction EvaluateKmmKm;
// KBP vs K:
extern KBPKScalingFunction ScaleKBPK, ScaleKKBP;
// KQ vs KRP:
extern KQKRPScalingFunction ScaleKQKRP, ScaleKRPKQ;
// KRP vs KR:
extern KRPKRScalingFunction ScaleKRPKR, ScaleKRKRP;
// KRPP vs KRP:
extern KRPPKRPScalingFunction ScaleKRPPKRP, ScaleKRPKRPP;
// King and pawns vs king:
extern KPsKScalingFunction ScaleKPsK, ScaleKKPs;
// KBP vs KB:
extern KBPKBScalingFunction ScaleKBPKB, ScaleKBKBP;
// KBP vs KN:
extern KBPKNScalingFunction ScaleKBPKN, ScaleKNKBP;
// KNP vs K:
extern KNPKScalingFunction ScaleKNPK, ScaleKKNP;
// KP vs KP:
extern KPKPScalingFunction ScaleKPKPw, ScaleKPKPb;
////
//// Prototypes
////
extern void init_bitbases();
#endif // !defined(ENDGAME_H_INCLUDED)
glaurung-2.2/src/value.cpp 0000644 0001750 0001750 00000004642 11123021252 015165 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "value.h"
////
//// Functions
////
/// value_to_tt() adjusts a mate score from "plies to mate from the root" to
/// "plies to mate from the current ply". Non-mate scores are unchanged.
/// The function is called before storing a value to the transposition table.
Value value_to_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v + ply;
else if(v <= value_mated_in(100))
return v - ply;
else
return v;
}
/// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
/// from the transposition table to a mate score corrected for the current
/// ply depth.
Value value_from_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v - ply;
else if(v <= value_mated_in(100))
return v + ply;
else
return v;
}
/// value_to_centipawns() converts a value from Glaurung's somewhat unusual
/// scale of pawn = 256 to the more conventional pawn = 100.
int value_to_centipawns(Value v) {
return (int(v) * 100) / int(PawnValueMidgame);
}
/// value_from_centipawns() converts a centipawn value to Glaurung's internal
/// evaluation scale. It's used when reading the values of UCI options
/// containing material values (e.g. futility pruning margins).
Value value_from_centipawns(int cp) {
return Value((cp * 256) / 100);
}
/// value_to_string() converts a value to a string suitable for use with the
/// UCI protocol.
const std::string value_to_string(Value v) {
std::stringstream s;
if(abs(v) < VALUE_MATE - 200)
s << "cp " << value_to_centipawns(v);
else {
s << "mate ";
if(v > 0)
s << (VALUE_MATE - v + 1) / 2;
else
s << -(VALUE_MATE + v) / 2;
}
return s.str();
}
glaurung-2.2/src/history.h 0000644 0001750 0001750 00000004330 11123021255 015214 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "move.h"
#include "piece.h"
////
//// Types
////
/// The History class stores statistics about how often different moves have
/// been successful or unsuccessful during the current search. These
/// statistics are used for reduction and move ordering decisions.
class History {
public:
History();
void clear();
void success(Piece p, Move m, Depth d);
void failure(Piece p, Move m);
int move_ordering_score(Piece p, Move m) const;
bool ok_to_prune(Piece p, Move m, Depth d) const;
private:
int history[16][64]; // [piece][square]
int successCount[16][64];
int failureCount[16][64];
};
////
//// Constants and variables
////
/// HistoryMax controls how often the history counters will be scaled down:
/// When the history score for a move gets bigger than HistoryMax, all
/// entries in the table are divided by 2. It is difficult to guess what
/// the ideal value of this constant is. Scaling down the scores often has
/// the effect that parts of the search tree which have been searched
/// recently have a bigger importance for move ordering than the moves which
/// have been searched a long time ago.
///
/// Note that HistoryMax should probably be changed whenever the constant
/// OnePly in depth.h is changed. This is somewhat annoying. Perhaps it
/// would be better to scale down the history table at regular intervals?
const int HistoryMax = 50000;
#endif // !defined(HISTORY_H_INCLUDED)
glaurung-2.2/src/tt.h 0000644 0001750 0001750 00000004106 11123021255 014143 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "position.h"
#include "value.h"
////
//// Types
////
/// The TTEntry class is the class of transposition table entries.
class TTEntry {
public:
TTEntry();
TTEntry(Key k, Value v, ValueType t, Depth d, Move m, int generation);
Key key() const;
Depth depth() const;
Move move() const;
Value value() const;
ValueType type() const;
int generation() const;
private:
Key key_;
uint32_t data;
int16_t value_;
int16_t depth_;
};
/// The transposition table class. This is basically just a huge array
/// containing TTEntry objects, and a few methods for writing new entries
/// and reading new ones.
class TranspositionTable {
public:
TranspositionTable(unsigned mbSize);
~TranspositionTable();
void set_size(unsigned mbSize);
void clear();
void store(const Position &pos, Value v, Depth d, Move m, ValueType type);
bool retrieve(const Position &pos, Value *value, Depth *d, Move *move,
ValueType *type) const;
void new_search();
void insert_pv(const Position &pos, Move pv[]);
int full();
private:
unsigned size;
int writes;
TTEntry* entries;
uint8_t generation;
};
////
//// Constants and variables
////
// Default transposition table size, in megabytes:
const int TTDefaultSize = 32;
#endif // !defined(TT_H_INCLUDED)
glaurung-2.2/src/depth.h 0000644 0001750 0001750 00000003642 11123021255 014624 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(DEPTH_H_INCLUDED)
#define DEPTH_H_INCLUDED
////
//// Types
////
enum Depth {
DEPTH_ZERO = 0,
DEPTH_MAX = 200 // 100 * OnePly;
};
////
//// Constants
////
/// Note: If OnePly is changed, the constant HistoryMax in history.h should
/// probably also be changed.
const Depth OnePly = Depth(2);
////
//// Inline functions
////
inline Depth operator+ (Depth d, int i) { return Depth(int(d) + i); }
inline Depth operator+ (Depth d1, Depth d2) { return Depth(int(d1) + int(d2)); }
inline void operator+= (Depth &d, int i) { d = Depth(int(d) + i); }
inline void operator+= (Depth &d1, Depth d2) { d1 += int(d2); }
inline Depth operator- (Depth d, int i) { return Depth(int(d) - i); }
inline Depth operator- (Depth d1, Depth d2) { return Depth(int(d1) - int(d2)); }
inline void operator-= (Depth & d, int i) { d = Depth(int(d) - i); }
inline Depth operator* (Depth d, int i) { return Depth(int(d) * i); }
inline Depth operator* (int i, Depth d) { return Depth(int(d) * i); }
inline void operator*= (Depth &d, int i) { d = Depth(int(d) * i); }
inline Depth operator/ (Depth d, int i) { return Depth(int(d) / i); }
inline void operator/= (Depth &d, int i) { d = Depth(int(d) / i); }
#endif // !defined(DEPTH_H_INCLUDED)
glaurung-2.2/src/value.h 0000644 0001750 0001750 00000010612 11123021255 014627 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(VALUE_H_INCLUDED)
#define VALUE_H_INCLUDED
////
//// Includes
////
#include "piece.h"
////
//// Types
////
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1, // Upper bound
VALUE_TYPE_LOWER = 2, // Lower bound
VALUE_TYPE_EXACT = 3 // Exact score
};
enum Value {
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000,
VALUE_INFINITE = 30001,
VALUE_NONE = 30002
};
////
//// Constants and variables
////
/// Piece values, middle game and endgame
/// Important: If the material values are changed, one must also
/// adjust the piece square tables, and the method game_phase() in the
/// Position class!
const Value PawnValueMidgame = Value(0xCC);
const Value PawnValueEndgame = Value(0x100);
const Value KnightValueMidgame = Value(0x340);
const Value KnightValueEndgame = Value(0x340);
const Value BishopValueMidgame = Value(0x340);
const Value BishopValueEndgame = Value(0x340);
const Value RookValueMidgame = Value(0x505);
const Value RookValueEndgame = Value(0x505);
const Value QueenValueMidgame = Value(0xA00);
const Value QueenValueEndgame = Value(0xA00);
const Value PieceValueMidgame[17] = {
Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0)
};
const Value PieceValueEndgame[17] = {
Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0)
};
/// Bonus for having the side to move
const Value TempoValueMidgame = Value(50);
const Value TempoValueEndgame = Value(20);
////
//// Inline functions
////
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator+ (Value v1, Value v2) { return Value(int(v1) + int(v2)); }
inline void operator+= (Value &v1, Value v2) {
v1 = Value(int(v1) + int(v2));
}
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
inline Value operator- (Value v) { return Value(-int(v)); }
inline Value operator- (Value v1, Value v2) { return Value(int(v1) - int(v2)); }
inline void operator-= (Value &v1, Value v2) {
v1 = Value(int(v1) - int(v2));
}
inline Value operator* (Value v, int i) { return Value(int(v) * i); }
inline void operator*= (Value &v, int i) { v = Value(int(v) * i); }
inline Value operator* (int i, Value v) { return Value(int(v) * i); }
inline Value operator/ (Value v, int i) { return Value(int(v) / i); }
inline void operator/= (Value &v, int i) { v = Value(int(v) / i); }
inline Value value_mate_in(int ply) {
return Value(VALUE_MATE - Value(ply));
}
inline Value value_mated_in(int ply) {
return Value(-VALUE_MATE + Value(ply));
}
inline bool is_upper_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_UPPER)) != 0;
}
inline bool is_lower_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_LOWER)) != 0;
}
inline Value piece_value_midgame(PieceType pt) {
return PieceValueMidgame[pt];
}
inline Value piece_value_endgame(PieceType pt) {
return PieceValueEndgame[pt];
}
inline Value piece_value_midgame(Piece p) {
return PieceValueMidgame[p];
}
inline Value piece_value_endgame(Piece p) {
return PieceValueEndgame[p];
}
////
//// Prototypes
////
extern Value value_to_tt(Value v, int ply);
extern Value value_from_tt(Value v, int ply);
extern int value_to_centipawns(Value v);
extern Value value_from_centipawns(int cp);
extern const std::string value_to_string(Value v);
#endif // !defined(VALUE_H_INCLUDED)
glaurung-2.2/src/material.h 0000644 0001750 0001750 00000011235 11123021255 015313 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(MATERIAL_H_INCLUDED)
#define MATERIAL_H_INCLUDED
////
//// Includes
////
#include "endgame.h"
#include "position.h"
#include "scale.h"
////
//// Types
////
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will
/// be used), and "scale factors" for black and white.
///
/// The scale factors are used to scale the evaluation score up or down.
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn.
class MaterialInfo {
friend class MaterialInfoTable;
public:
Value mg_value() const;
Value eg_value() const;
ScaleFactor scale_factor(const Position &pos, Color c) const;
int space_weight() const;
bool specialized_eval_exists() const;
Value evaluate(const Position &pos) const;
static void init();
private:
void clear();
Key key;
int16_t mgValue;
int16_t egValue;
uint8_t spaceWeight;
uint8_t factor[2];
EndgameEvaluationFunction *evaluationFunction;
ScalingFunction *scalingFunction[2];
};
/// The MaterialInfoTable class represents a pawn hash table. It is basically
/// just an array of MaterialInfo objects and a few methods for accessing these
/// objects. The most important method is get_material_info, which looks up a
/// position in the table and returns a pointer to a MaterialInfo object.
class MaterialInfoTable {
public:
MaterialInfoTable(unsigned numOfEntries);
~MaterialInfoTable();
void clear();
MaterialInfo *get_material_info(const Position &pos);
private:
unsigned size;
MaterialInfo *entries;
};
////
//// Inline functions
////
/// MaterialInfo::mg_value and MaterialInfo::eg_value simply returns the
/// material balance evaluation for the middle game and the endgame.
inline Value MaterialInfo::mg_value() const {
return Value(mgValue);
}
inline Value MaterialInfo::eg_value() const {
return Value(egValue);
}
/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
/// with all slots at their default values.
inline void MaterialInfo::clear() {
mgValue = egValue = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
spaceWeight = 0;
evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
}
/// MaterialInfo::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not
/// be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialInfo::scale_factor(const Position &pos, Color c)
const {
if(scalingFunction[c] != NULL) {
ScaleFactor sf = scalingFunction[c]->apply(pos);
if(sf != SCALE_FACTOR_NONE)
return sf;
}
return ScaleFactor(factor[c]);
}
/// MaterialInfo::space_weight() simply returns the weight for the space
/// evaluation for this material configuration.
inline int MaterialInfo::space_weight() const {
return int(spaceWeight);
}
/// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration,
/// or if the normal evaluation function should be used.
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
/// MaterialInfo::evaluate applies a specialized evaluation function to a
/// given position object. It should only be called when
/// this->specialized_eval_exists() returns 'true'.
inline Value MaterialInfo::evaluate(const Position &pos) const {
return evaluationFunction->apply(pos);
}
#endif // !defined(MATERIAL_H_INCLUDED)
glaurung-2.2/src/types.h 0000644 0001750 0001750 00000002242 11123021255 014657 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED
#if !defined(_MSC_VER)
#include
#else
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed long int32_t;
typedef unsigned long uint32_t;
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
#endif // !defined(_MSC_VER)
// Hash keys:
typedef uint64_t Key;
#endif // !defined(TYPES_H_INCLUDED)
glaurung-2.2/src/benchmark.h 0000644 0001750 0001750 00000001716 11123021255 015452 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(BENCHMARK_H_INCLUDED)
#define BENCHMARK_H_INCLUDED
////
//// Includes
////
#include
////
//// Prototypes
////
extern void benchmark(const std::string &ttSize, const std::string &threads);
#endif // !defined(BENCHMARK_H_INCLUDED)
glaurung-2.2/src/movepick.h 0000644 0001750 0001750 00000004734 11123021255 015340 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "lock.h"
#include "position.h"
////
//// Types
////
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::pick_next_move(), which returns a new legal move each time it
/// is called, until there are no legal 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 be strongest first.
class MovePicker {
public:
MovePicker(Position &p, bool pvnode, Move ttm, Move mk, Move k1, Move k2,
Depth dpth);
Move get_next_move();
Move get_next_move(Lock &lock);
int number_of_moves() const;
int current_move_score() const;
Bitboard discovered_check_candidates();
static void init_phase_table();
private:
void score_captures();
void score_noncaptures();
void score_evasions();
void score_qcaptures();
Move pick_move_from_list();
Position *pos;
Move ttMove, mateKiller, killer1, killer2;
Bitboard pinned, dc;
MoveStack moves[256], badCaptures[64];
bool pvNode;
Depth depth;
int phaseIndex;
int numOfMoves, numOfBadCaptures;
int movesPicked, badCapturesPicked;
bool finished;
};
////
//// Inline functions
////
/// MovePicker::discovered_check_candidates() returns a bitboard containing
/// all pieces which can possibly give discovered check. This bitboard is
/// computed by the constructor function.
inline Bitboard MovePicker::discovered_check_candidates() {
return dc;
}
#endif // !defined(MOVEPICK_H_INCLUDED)
glaurung-2.2/src/movegen.h 0000644 0001750 0001750 00000002534 11123021255 015157 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED
////
//// Includes
////
#include "position.h"
////
//// Prototypes
////
extern int generate_captures(const Position &pos, MoveStack *mlist);
extern int generate_noncaptures(const Position &pos, MoveStack *mlist);
extern int generate_checks(const Position &pos, MoveStack *mlist, Bitboard dc);
extern int generate_evasions(const Position &pos, MoveStack *mlist);
extern int generate_legal_moves(const Position &pos, MoveStack *mlist);
extern Move generate_move_if_legal(const Position &pos, Move m,
Bitboard pinned);
#endif // !defined(MOVEGEN_H_INCLUDED)
glaurung-2.2/src/bitboard.h 0000644 0001750 0001750 00000026565 11123021255 015317 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(BITBOARD_H_INCLUDED)
#define BITBOARD_H_INCLUDED
////
//// Defines
////
//#define USE_COMPACT_ROOK_ATTACKS
//#define USE_32BIT_ATTACKS
#define USE_FOLDED_BITSCAN
#define BITCOUNT_SWAR_64
//#define BITCOUNT_SWAR_32
//#define BITCOUNT_LOOP
////
//// Includes
////
#include "direction.h"
#include "piece.h"
#include "square.h"
#include "types.h"
////
//// Types
////
typedef uint64_t Bitboard;
////
//// Constants and variables
////
const Bitboard EmptyBoardBB = 0ULL;
const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
extern const Bitboard SquaresByColorBB[2];
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = 0x0202020202020202ULL;
const Bitboard FileCBB = 0x0404040404040404ULL;
const Bitboard FileDBB = 0x0808080808080808ULL;
const Bitboard FileEBB = 0x1010101010101010ULL;
const Bitboard FileFBB = 0x2020202020202020ULL;
const Bitboard FileGBB = 0x4040404040404040ULL;
const Bitboard FileHBB = 0x8080808080808080ULL;
extern const Bitboard FileBB[8];
extern const Bitboard NeighboringFilesBB[8];
extern const Bitboard ThisAndNeighboringFilesBB[8];
const Bitboard Rank1BB = 0xFFULL;
const Bitboard Rank2BB = 0xFF00ULL;
const Bitboard Rank3BB = 0xFF0000ULL;
const Bitboard Rank4BB = 0xFF000000ULL;
const Bitboard Rank5BB = 0xFF00000000ULL;
const Bitboard Rank6BB = 0xFF0000000000ULL;
const Bitboard Rank7BB = 0xFF000000000000ULL;
const Bitboard Rank8BB = 0xFF00000000000000ULL;
extern const Bitboard RankBB[8];
extern const Bitboard RelativeRankBB[2][8];
extern const Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[64];
extern Bitboard ClearMaskBB[64];
extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8];
extern Bitboard BetweenBB[64][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard OutpostMask[2][64];
#if defined(USE_COMPACT_ROOK_ATTACKS)
extern Bitboard RankAttacks[8][64], FileAttacks[8][64];
#else
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
#endif // defined(USE_COMPACT_ROOK_ATTACKS)
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
////
//// Inline functions
////
/// Functions for testing whether a given bit is set in a bitboard, and for
/// setting and clearing bits.
inline Bitboard set_mask_bb(Square s) {
// return 1ULL << s;
return SetMaskBB[s];
}
inline Bitboard clear_mask_bb(Square s) {
// return ~set_mask_bb(s);
return ClearMaskBB[s];
}
inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & set_mask_bb(s);
}
inline void set_bit(Bitboard *b, Square s) {
*b |= set_mask_bb(s);
}
inline void clear_bit(Bitboard *b, Square s) {
*b &= clear_mask_bb(s);
}
/// rank_bb() and file_bb() gives a bitboard containing all squares on a given
/// file or rank. It is also possible to pass a square as input to these
/// functions.
inline Bitboard rank_bb(Rank r) {
return RankBB[r];
}
inline Bitboard rank_bb(Square s) {
return rank_bb(square_rank(s));
}
inline Bitboard file_bb(File f) {
return FileBB[f];
}
inline Bitboard file_bb(Square s) {
return file_bb(square_file(s));
}
/// neighboring_files_bb takes a file or a square as input, and returns a
/// bitboard representing all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f];
}
inline Bitboard neighboring_files_bb(Square s) {
return neighboring_files_bb(square_file(s));
}
/// this_and_neighboring_files_bb takes a file or a square as input, and
/// returns a bitboard representing all squares on the given and neighboring
/// files.
inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f];
}
inline Bitboard this_and_neighboring_files_bb(Square s) {
return this_and_neighboring_files_bb(square_file(s));
}
/// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
/// representing all squares on the given rank from the given color's point of
/// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
/// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
/// rank.
inline Bitboard relative_rank_bb(Color c, Rank r) {
return RelativeRankBB[c][r];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r];
}
inline Bitboard in_front_bb(Color c, Square s) {
return in_front_bb(c, square_rank(s));
}
/// ray_bb() gives a bitboard representing all squares along the ray in a
/// given direction from a given square.
inline Bitboard ray_bb(Square s, SignedDirection d) {
return RayBB[s][d];
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.
#if defined(USE_COMPACT_ROOK_ATTACKS)
inline Bitboard file_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = (blockers >> square_file(s)) & 0x01010101010100ULL;
return
FileAttacks[square_rank(s)][(b*0xd6e8802041d0c441ULL)>>58] & file_bb(s);
}
inline Bitboard rank_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = (blockers >> ((s & 56) + 1)) & 63;
return RankAttacks[square_file(s)][b] & rank_bb(s);
}
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
return file_attacks_bb(s, blockers) | rank_attacks_bb(s, blockers);
}
#elif defined(USE_32BIT_ATTACKS)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^
int(b >> 32) * int(RMult[s] >> 32))
>> RShift[s])];
}
#else
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
}
#endif
#if defined(USE_32BIT_ATTACKS)
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^
int(b >> 32) * int(BMult[s] >> 32))
>> BShift[s])];
}
#else // defined(USE_32BIT_ATTACKS)
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
}
#endif // defined(USE_32BIT_ATTACKS)
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
/// squares_between returns a bitboard representing all squares between
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
/// on the same line, file or diagonal, EmptyBoardBB is returned.
inline Bitboard squares_between(Square s1, Square s2) {
return BetweenBB[s1][s2];
}
/// squares_in_front_of takes a color and a square as input, and returns a
/// bitboard representing all squares along the line in front of the square,
/// from the point of view of the given color. For instance,
/// squares_in_front_of(BLACK, SQ_E4) returns a bitboard with the squares
/// e3, e2 and e1 set.
inline Bitboard squares_in_front_of(Color c, Square s) {
return in_front_bb(c, s) & file_bb(s);
}
/// squares_behind is similar to squares_in_front, but returns the squares
/// behind the square instead of in front of the square.
inline Bitboard squares_behind(Color c, Square s) {
return in_front_bb(opposite_color(c), s) & file_bb(s);
}
/// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn.
inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
}
/// outpost_mask takes a color and a square as input, and returns a bitboard
/// mask which can be used to test whether a piece on the square can possibly
/// be driven away by an enemy pawn.
inline Bitboard outpost_mask(Color c, Square s) {
return OutpostMask[c][s];
}
/// isolated_pawn_mask takes a square as input, and returns a bitboard mask
/// which can be used to test whether a pawn on the given square is isolated.
inline Bitboard isolated_pawn_mask(Square s) {
return neighboring_files_bb(s);
}
/// count_1s() counts the number of nonzero bits in a bitboard.
#if defined(BITCOUNT_LOOP)
inline int count_1s(Bitboard b) {
int r;
for(r = 0; b; r++, b &= b - 1);
return r;
}
inline int count_1s_max_15(Bitboard b) {
return count_1s(b);
}
#elif defined(BITCOUNT_SWAR_32)
inline int count_1s(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v = v - ((v >> 1) & 0x55555555);
w = w - ((w >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
v = (v + (v >> 4)) & 0x0F0F0F0F;
w = (w + (w >> 4)) & 0x0F0F0F0F;
v = ((v+w) * 0x01010101) >> 24; // mul is fast on amd procs
return int(v);
}
inline int count_1s_max_15(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v = v - ((v >> 1) & 0x55555555);
w = w - ((w >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
v = ((v+w) * 0x11111111) >> 28;
return int(v);
}
#elif defined(BITCOUNT_SWAR_64)
inline int count_1s(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
b *= 0x0101010101010101ULL;
return int(b >> 56);
}
inline int count_1s_max_15(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL;
return int(b >> 60);
}
#endif // BITCOUNT
////
//// Prototypes
////
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
extern Square first_1(Bitboard b);
extern Square pop_1st_bit(Bitboard *b);
#endif // !defined(BITBOARD_H_INCLUDED)
glaurung-2.2/src/bitbase.cpp 0000644 0001750 0001750 00000020635 11123021252 015462 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include "bitbase.h"
#include "bitboard.h"
#include "move.h"
#include "square.h"
////
//// Local definitions
////
namespace {
enum Result {
RESULT_UNKNOWN,
RESULT_INVALID,
RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW
};
struct KPKPosition {
void from_index(int index);
int to_index() const;
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const;
Bitboard bk_attacks() const;
Bitboard pawn_attacks() const;
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
Result *Bitbase;
const int IndexMax = 2*24*64*64;
int UnknownCount = 0;
void initialize();
bool next_iteration();
Result classify_wtm(const KPKPosition &p);
Result classify_btm(const KPKPosition &p);
int compute_index(Square wksq, Square bksq, Square psq, Color stm);
int compress_result(Result r);
}
////
//// Functions
////
void generate_kpk_bitbase(uint8_t bitbase[]) {
// Allocate array and initialize:
Bitbase = new Result[IndexMax];
initialize();
// Iterate until all positions are classified:
while(next_iteration());
// Compress bitbase into the supplied parameter:
int i, j, b;
for(i = 0; i < 24576; i++) {
for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
bitbase[i] = b;
}
// Release allocated memory:
delete [] Bitbase;
}
namespace {
void KPKPosition::from_index(int index) {
int s;
sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64);
s = (index / 8192) % 24;
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
}
int KPKPosition::to_index() const {
return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
sideToMove);
}
bool KPKPosition::is_legal() const {
if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
pawnSquare == blackKingSquare)
return false;
if(sideToMove == WHITE) {
if(bit_is_set(this->wk_attacks(), blackKingSquare))
return false;
if(bit_is_set(this->pawn_attacks(), blackKingSquare))
return false;
}
else {
if(bit_is_set(this->bk_attacks(), whiteKingSquare))
return false;
}
return true;
}
bool KPKPosition::is_immediate_draw() const {
if(sideToMove == BLACK) {
Bitboard wka = this->wk_attacks();
Bitboard bka = this->bk_attacks();
// Case 1: Stalemate
if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
return true;
// Case 2: King can capture pawn
if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true;
}
else {
// Case 1: Stalemate
if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
(blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true;
}
return false;
}
bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the white
// pawn can be promoted without getting captured:
return
sideToMove == WHITE &&
square_rank(pawnSquare) == RANK_7 &&
(square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 ||
bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N));
}
Bitboard KPKPosition::wk_attacks() const {
return StepAttackBB[WK][whiteKingSquare];
}
Bitboard KPKPosition::bk_attacks() const {
return StepAttackBB[BK][blackKingSquare];
}
Bitboard KPKPosition::pawn_attacks() const {
return StepAttackBB[WP][pawnSquare];
}
void initialize() {
KPKPosition p;
for(int i = 0; i < IndexMax; i++) {
p.from_index(i);
if(!p.is_legal())
Bitbase[i] = RESULT_INVALID;
else if(p.is_immediate_draw())
Bitbase[i] = RESULT_DRAW;
else if(p.is_immediate_win())
Bitbase[i] = RESULT_WIN;
else {
Bitbase[i] = RESULT_UNKNOWN;
UnknownCount++;
}
}
}
bool next_iteration() {
KPKPosition p;
int previousUnknownCount = UnknownCount;
for(int i = 0; i < IndexMax; i++)
if(Bitbase[i] == RESULT_UNKNOWN) {
p.from_index(i);
Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
Bitbase[i] == RESULT_DRAW)
UnknownCount--;
}
return UnknownCount != previousUnknownCount;
}
Result classify_wtm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_LOSS, the result
// of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified as
// RESULT_DRAW. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
// King moves
b = p.wk_attacks();
while(b) {
s = pop_1st_bit(&b);
switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
}
// Pawn moves
if(square_rank(p.pawnSquare) < RANK_7) {
s = p.pawnSquare + DELTA_N;
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
if(square_rank(s) == RANK_3 &&
s != p.whiteKingSquare && s != p.blackKingSquare) {
s += DELTA_N;
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
}
}
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
}
Result classify_btm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the current position is classified as
// RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
// King moves
b = p.bk_attacks();
while(b) {
s = pop_1st_bit(&b);
switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
WHITE)]) {
case RESULT_DRAW:
return RESULT_DRAW;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_WIN: case RESULT_INVALID:
break;
default:
assert(false);
}
}
return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
}
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
assert(result >= 0 && result < IndexMax);
return result;
}
int compress_result(Result r) {
return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
}
}
glaurung-2.2/src/uci.h 0000644 0001750 0001750 00000001536 11123021255 014300 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(UCI_H_INCLUDED)
#define UCI_H_INCLUDED
////
//// Prototypes
////
extern void uci_main_loop();
#endif // !defined(UCI_H_INCLUDED)
glaurung-2.2/src/ucioption.cpp 0000644 0001750 0001750 00000022464 11123214415 016071 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include
#include
#include "misc.h"
#include "thread.h"
#include "ucioption.h"
////
//// Variables
////
bool Chess960 = false;
////
//// Local definitions
////
namespace {
///
/// Types
///
enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON, OPTION_TYPE_NONE};
struct Option {
char name[50], defaultValue[300], currentValue[300];
OptionType type;
int minValue, maxValue;
char comboValues[8][64];
};
///
/// Variables
///
Option Options[] = {
{ "Use Search Log", "false", "false", CHECK, 0, 0, {""} },
{ "Search Log Filename", "SearchLog.txt", "SearchLog.txt", STRING, 0, 0, {""} },
{ "Book File", "book.bin", "book.bin", STRING, 0, 0, {""} },
{ "Mobility (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
{ "Mobility (Endgame)", "100", "100", SPIN, 0, 200, {""} },
{ "Pawn Structure (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
{ "Pawn Structure (Endgame)", "100", "100", SPIN, 0, 200, {""} },
{ "Passed Pawns (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
{ "Passed Pawns (Endgame)", "100", "100", SPIN, 0, 200, {""} },
{ "Space", "100", "100", SPIN, 0, 200, {""} },
{ "Aggressiveness", "100", "100", SPIN, 0, 200, {""} },
{ "Cowardice", "100", "100", SPIN, 0, 200, {""} },
{ "King Safety Curve", "Quadratic", "Quadratic", COMBO, 0, 0,
{ "Quadratic", "Linear" /*, "From File"*/ } },
{ "King Safety Coefficient", "40", "40", SPIN, 1, 100 , {""} },
{ "King Safety X Intercept", "0", "0", SPIN, 0, 20, {""} },
{ "King Safety Max Slope", "30", "30", SPIN, 10, 100, {""} },
{ "King Safety Max Value", "500", "500", SPIN, 100, 1000, {""} },
{ "Queen Contact Check Bonus", "3", "3", SPIN, 0, 8, {""} },
{ "Queen Check Bonus", "2", "2", SPIN, 0, 4, {""} },
{ "Rook Check Bonus", "1", "1", SPIN, 0, 4, {""} },
{ "Bishop Check Bonus", "1", "1", SPIN, 0, 4, {""} },
{ "Knight Check Bonus", "1", "1", SPIN, 0, 4, {""} },
{ "Discovered Check Bonus", "3", "3", SPIN, 0, 8, {""} },
{ "Mate Threat Bonus", "3", "3", SPIN, 0, 8, {""} },
{ "Check Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
{ "Check Extension (non-PV nodes)", "1", "1", SPIN, 0, 2, {""} },
{ "Single Reply Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
{ "Single Reply Extension (non-PV nodes)", "2", "2", SPIN, 0, 2, {""} },
{ "Mate Threat Extension (PV nodes)", "0", "0", SPIN, 0, 2, {""} },
{ "Mate Threat Extension (non-PV nodes)", "0", "0", SPIN, 0, 2, {""} },
{ "Pawn Push to 7th Extension (PV nodes)", "1", "1", SPIN, 0, 2, {""} },
{ "Pawn Push to 7th Extension (non-PV nodes)", "1", "1", SPIN, 0, 2, {""} },
{ "Passed Pawn Extension (PV nodes)", "1", "1", SPIN, 0, 2, {""} },
{ "Passed Pawn Extension (non-PV nodes)", "0", "0", SPIN, 0, 2, {""} },
{ "Pawn Endgame Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
{ "Pawn Endgame Extension (non-PV nodes)", "2", "2", SPIN, 0, 2, {""} },
{ "Full Depth Moves (PV nodes)", "14", "14", SPIN, 1, 100, {""} },
{ "Full Depth Moves (non-PV nodes)", "3", "3", SPIN, 1, 100, {""} },
{ "Threat Depth", "5", "5", SPIN, 0, 100, {""} },
{ "Selective Plies", "7", "7", SPIN, 0, 10, {""} },
{ "Futility Pruning (Main Search)", "true", "true", CHECK, 0, 0, {""} },
{ "Futility Pruning (Quiescence Search)", "true", "true", CHECK, 0, 0, {""} },
{ "Futility Margin 0", "50", "50", SPIN, 0, 1000, {""} },
{ "Futility Margin 1", "100", "100", SPIN, 0, 1000, {""} },
{ "Futility Margin 2", "200", "200", SPIN, 0, 1000, {""} },
{ "Maximum Razoring Depth", "3", "3", SPIN, 0, 4, {""} },
{ "Razoring Margin", "300", "300", SPIN, 150, 600, {""} },
{ "Randomness", "0", "0", SPIN, 0, 10, {""} },
{ "Minimum Split Depth", "4", "4", SPIN, 4, 7, {""} },
{ "Maximum Number of Threads per Split Point", "5", "5", SPIN, 4, 8, {""} },
{ "Threads", "1", "1", SPIN, 1, 8, {""} },
{ "Hash", "32", "32", SPIN, 4, 4096, {""} },
{ "Clear Hash", "false", "false", BUTTON, 0, 0, {""} },
{ "Ponder", "true", "true", CHECK, 0, 0, {""} },
{ "OwnBook", "true", "true", CHECK, 0, 0, {""} },
{ "MultiPV", "1", "1", SPIN, 1, 500, {""} },
{ "UCI_ShowCurrLine", "false", "false", CHECK, 0, 0, {""} },
{ "UCI_Chess960", "false", "false", CHECK, 0, 0, {""} },
{ "", "", "", OPTION_TYPE_NONE, 0, 0, {""}}
};
///
/// Functions
///
Option *option_with_name(const char *optionName);
}
////
//// Functions
////
/// init_uci_options() initializes the UCI options. Currently, the only
/// thing this function does is to initialize the default value of the
/// "Threads" parameter to the number of available CPU cores.
void init_uci_options() {
Option *o;
o = option_with_name("Threads");
assert(o != NULL);
// Limit the default value of "Threads" to 7 even if we have 8 CPU cores.
// According to Ken Dail's tests, Glaurung plays much better with 7 than
// with 8 threads. This is weird, but it is probably difficult to find out
// why before I have a 8-core computer to experiment with myself.
sprintf(o->defaultValue, "%d", Min(cpu_count(), 7));
sprintf(o->currentValue, "%d", Min(cpu_count(), 7));
// Increase the minimum split depth when the number of CPUs is big.
// It would probably be better to let this depend on the number of threads
// instead.
o = option_with_name("Minimum Split Depth");
assert(o != NULL);
if(cpu_count() > 4) {
sprintf(o->defaultValue, "%d", 6);
sprintf(o->defaultValue, "%d", 6);
}
}
/// print_uci_options() prints all the UCI options to the standard output,
/// in the format defined by the UCI protocol.
void print_uci_options() {
static const char optionTypeName[][16] = {
"spin", "combo", "check", "string", "button"
};
for(Option *o = Options; o->type != OPTION_TYPE_NONE; o++) {
printf("option name %s type %s", o->name, optionTypeName[o->type]);
if(o->type != BUTTON) {
printf(" default %s", o->defaultValue);
if(o->type == SPIN)
printf(" min %d max %d", o->minValue, o->maxValue);
else if(o->type == COMBO)
for(int i = 0; strlen(o->comboValues[i]) > 0; i++)
printf(" var %s", o->comboValues[i]);
}
printf("\n");
}
}
/// get_option_value_bool() returns the current value of a UCI parameter of
/// type "check".
bool get_option_value_bool(const std::string &optionName) {
Option *o = option_with_name(optionName.c_str());
return o != NULL && strcmp(o->currentValue, "true") == 0;
}
/// get_option_value_int() returns the value of a UCI parameter as an integer.
/// Normally, this function will be used for a parameter of type "spin", but
/// it could also be used with a "combo" parameter, where all the available
/// values are integers.
int get_option_value_int(const std::string &optionName) {
Option *o = option_with_name(optionName.c_str());
return atoi(o->currentValue);
}
/// get_option_value_string() returns the current value of a UCI parameter as
/// a string. It is used with parameters of type "combo" and "string".
const std::string get_option_value_string(const std::string &optionName) {
Option *o = option_with_name(optionName.c_str());
return o->currentValue;
}
/// button_was_pressed() tests whether a UCI parameter of type "button" has
/// been selected since the last time the function was called.
bool button_was_pressed(const std::string &buttonName) {
if(get_option_value_bool(buttonName)) {
set_option_value(buttonName, "false");
return true;
}
else
return false;
}
/// set_option_value() inserts a new value for a UCI parameter. Note that
/// the function does not check that the new value is legal for the given
/// parameter: This is assumed to be the responsibility of the GUI.
void set_option_value(const std::string &optionName,
const std::string &newValue) {
Option *o = option_with_name(optionName.c_str());
if(o != NULL)
strcpy(o->currentValue, newValue.c_str());
else
std::cout << "No such option: " << optionName << std::endl;
}
/// push_button() is used to tell the engine that a UCI parameter of type
/// "button" has been selected:
void push_button(const std::string &buttonName) {
set_option_value(buttonName, "true");
}
namespace {
// option_with_name() tries to find a UCI option with a given
// name. It returns a pointer to the UCI option or the null pointer,
// depending on whether an option with the given name exists.
Option *option_with_name(const char *optionName) {
for(Option *o = Options; o->type != OPTION_TYPE_NONE; o++)
if(strcmp(o->name, optionName) == 0)
return o;
return NULL;
}
}
glaurung-2.2/src/evaluate.cpp 0000644 0001750 0001750 00000133124 11123021252 015655 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "evaluate.h"
#include "material.h"
#include "pawns.h"
#include "scale.h"
#include "thread.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
const int Sign[2] = {1, -1};
// Evaluation grain size, must be a power of 2.
const int GrainSize = 4;
// Evaluation weights
int WeightMobilityMidgame = 0x100;
int WeightMobilityEndgame = 0x100;
int WeightPawnStructureMidgame = 0x100;
int WeightPawnStructureEndgame = 0x100;
int WeightPassedPawnsMidgame = 0x100;
int WeightPassedPawnsEndgame = 0x100;
int WeightKingSafety[2] = { 0x100, 0x100 };
int WeightSpace;
// Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change
// the evaluation weights while keeping the default values of the UCI
// parameters at 100, which looks prettier.
const int WeightMobilityMidgameInternal = 0x100;
const int WeightMobilityEndgameInternal = 0x100;
const int WeightPawnStructureMidgameInternal = 0x100;
const int WeightPawnStructureEndgameInternal = 0x100;
const int WeightPassedPawnsMidgameInternal = 0x100;
const int WeightPassedPawnsEndgameInternal = 0x100;
const int WeightKingSafetyInternal = 0x110;
const int WeightSpaceInternal = 0x30;
// Knight mobility bonus in middle game and endgame, indexed by the number
// of attacked squares not occupied by friendly piecess.
const Value MidgameKnightMobilityBonus[] = {
Value(-30), Value(-20), Value(-10), Value(0), Value(10),
Value(20), Value(25), Value(30), Value(30)
};
const Value EndgameKnightMobilityBonus[] = {
Value(-30), Value(-20), Value(-10), Value(0), Value(10),
Value(20), Value(25), Value(30), Value(30)
};
// Bishop mobility bonus in middle game and endgame, indexed by the number
// of attacked squares not occupied by friendly pieces. X-ray attacks through
// queens are also included.
const Value MidgameBishopMobilityBonus[] = {
Value(-30), Value(-15), Value(0), Value(15), Value(30), Value(45),
Value(58), Value(66), Value(72), Value(76), Value(78), Value(80),
Value(81), Value(82), Value(83), Value(83)
};
const Value EndgameBishopMobilityBonus[] = {
Value(-30), Value(-15), Value(0), Value(15), Value(30), Value(45),
Value(58), Value(66), Value(72), Value(76), Value(78), Value(80),
Value(81), Value(82), Value(83), Value(83)
};
// Rook mobility bonus in middle game and endgame, indexed by the number
// of attacked squares not occupied by friendly pieces. X-ray attacks through
// queens and rooks are also included.
const Value MidgameRookMobilityBonus[] = {
Value(-18), Value(-12), Value(-6), Value(0), Value(6), Value(12),
Value(16), Value(21), Value(24), Value(27), Value(28), Value(29),
Value(30), Value(31), Value(32), Value(33)
};
const Value EndgameRookMobilityBonus[] = {
Value(-30), Value(-18), Value(-6), Value(6), Value(18), Value(30),
Value(42), Value(54), Value(66), Value(74), Value(78), Value(80),
Value(81), Value(82), Value(83), Value(83)
};
// Queen mobility bonus in middle game and endgame, indexed by the number
// of attacked squares not occupied by friendly pieces.
const Value MidgameQueenMobilityBonus[] = {
Value(-10), Value(-8), Value(-6), Value(-4), Value(-2), Value(0), Value(2),
Value(4), Value(6), Value(8), Value(10), Value(12), Value(13), Value(14),
Value(15), Value(16), Value(16), Value(16), Value(16), Value(16),
Value(16), Value(16), Value(16), Value(16), Value(16), Value(16),
Value(16), Value(16), Value(16), Value(16), Value(16), Value(16)
};
const Value EndgameQueenMobilityBonus[] = {
Value(-20), Value(-15), Value(-10), Value(-5), Value(0), Value(5),
Value(10), Value(15), Value(19), Value(23), Value(27), Value(29),
Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
Value(30), Value(30)
};
// Outpost bonuses for knights and bishops, indexed by square (from white's
// point of view).
const Value KnightOutpostBonus[64] = {
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(5),Value(10),Value(10),Value(5),Value(0),Value(0),
Value(0),Value(5),Value(20),Value(30),Value(30),Value(20),Value(5),Value(0),
Value(0),Value(10),Value(30),Value(40),Value(40),Value(30),Value(10),Value(0),
Value(0),Value(5),Value(20),Value(20),Value(20),Value(20),Value(5),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0)
};
const Value BishopOutpostBonus[64] = {
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(5),Value(5),Value(5),Value(5),Value(0),Value(0),
Value(0),Value(5),Value(10),Value(10),Value(10),Value(10),Value(5),Value(0),
Value(0),Value(10),Value(20),Value(20),Value(20),Value(20),Value(10),Value(0),
Value(0),Value(5),Value(8),Value(8),Value(8),Value(8),Value(5),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0)
};
// Bonus for unstoppable passed pawns:
const Value UnstoppablePawnValue = Value(0x500);
// Rooks and queens on the 7th rank:
const Value MidgameRookOn7thBonus = Value(50);
const Value EndgameRookOn7thBonus = Value(100);
const Value MidgameQueenOn7thBonus = Value(25);
const Value EndgameQueenOn7thBonus = Value(50);
// Rooks on open files:
const Value RookOpenFileBonus = Value(40);
const Value RookHalfOpenFileBonus = Value(20);
// Penalty for rooks trapped inside a friendly king which has lost the
// right to castle:
const Value TrappedRookPenalty = Value(180);
// Penalty for a bishop on a7/h7 (a2/h2 for black) which is trapped by
// enemy pawns:
const Value TrappedBishopA7H7Penalty = Value(300);
// Bitboard masks for detecting trapped bishops on a7/h7 (a2/h2 for black):
const Bitboard MaskA7H7[2] = {
((1ULL << SQ_A7) | (1ULL << SQ_H7)),
((1ULL << SQ_A2) | (1ULL << SQ_H2))
};
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games.
const Value TrappedBishopA1H1Penalty = Value(100);
// Bitboard masks for detecting trapped bishops on a1/h1 (a8/h8 for black):
const Bitboard MaskA1H1[2] = {
((1ULL << SQ_A1) | (1ULL << SQ_H1)),
((1ULL << SQ_A8) | (1ULL << SQ_H8))
};
// The SpaceMask[color] contains area of the board which is consdered by
// the space evaluation. In the middle game, each side is given a bonus
// based on how many squares inside this area are safe and available for
// friendly minor pieces.
const Bitboard SpaceMask[2] = {
(1ULL<= 0 && threadID < THREAD_MAX);
stm = pos.side_to_move();
// Initialize by reading the incrementally updated scores included in the
// position object (material + piece square tables):
ei.mgValue = pos.mg_value();
ei.egValue = pos.eg_value();
// Probe the material hash table:
ei.mi = MaterialTable[threadID]->get_material_info(pos);
ei.mgValue += ei.mi->mg_value();
ei.egValue += ei.mi->eg_value();
factor[WHITE] = ei.mi->scale_factor(pos, WHITE);
factor[BLACK] = ei.mi->scale_factor(pos, BLACK);
// If we have a specialized evaluation function for the current material
// configuration, call it and return:
if(ei.mi->specialized_eval_exists())
return ei.mi->evaluate(pos);
phase = pos.game_phase();
// Probe the pawn hash table:
ei.pi = PawnTable[threadID]->get_pawn_info(pos);
ei.mgValue += apply_weight(ei.pi->mg_value(), WeightPawnStructureMidgame);
ei.egValue += apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame);
// Initialize king attack bitboards and king attack zones for both sides:
ei.attackedBy[WHITE][KING] = pos.king_attacks(pos.king_square(WHITE));
ei.attackedBy[BLACK][KING] = pos.king_attacks(pos.king_square(BLACK));
ei.attackZone[WHITE] =
ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8);
ei.attackZone[BLACK] =
ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8);
// Initialize pawn attack bitboards for both sides:
ei.attackedBy[WHITE][PAWN] =
((pos.pawns(WHITE) << 9) & ~FileABB) | ((pos.pawns(WHITE) << 7) & ~FileHBB);
ei.attackCount[WHITE] +=
count_1s_max_15(ei.attackedBy[WHITE][PAWN] & ei.attackedBy[BLACK][KING])/2;
ei.attackedBy[BLACK][PAWN] =
((pos.pawns(BLACK) >> 7) & ~FileABB) | ((pos.pawns(BLACK) >> 9) & ~FileHBB);
ei.attackCount[BLACK] +=
count_1s_max_15(ei.attackedBy[BLACK][PAWN] & ei.attackedBy[WHITE][KING])/2;
// Evaluate pieces:
for(Color c = WHITE; c <= BLACK; c++) {
Bitboard b;
// Knights
for(int i = 0; i < pos.knight_count(c); i++)
evaluate_knight(pos, pos.knight_list(c, i), c, ei);
// Bishops
for(int i = 0; i < pos.bishop_count(c); i++)
evaluate_bishop(pos, pos.bishop_list(c, i), c, ei);
// Rooks
for(int i = 0; i < pos.rook_count(c); i++)
evaluate_rook(pos, pos.rook_list(c, i), c, ei);
// Queens
for(int i = 0; i < pos.queen_count(c); i++)
evaluate_queen(pos, pos.queen_list(c, i), c, ei);
// Some special patterns:
// Trapped bishops on a7/h7/a2/h2
b = pos.bishops(c) & MaskA7H7[c];
while(b)
evaluate_trapped_bishop_a7h7(pos, pop_1st_bit(&b), c, ei);
// Trapped bishops on a1/h1/a8/h8 in Chess960:
if(Chess960) {
b = pos.bishops(c) & MaskA1H1[c];
while(b)
evaluate_trapped_bishop_a1h1(pos, pop_1st_bit(&b), c, ei);
}
ei.attackedBy[c][0] =
ei.attackedBy[c][PAWN] | ei.attackedBy[c][KNIGHT]
| ei.attackedBy[c][BISHOP] | ei.attackedBy[c][ROOK]
| ei.attackedBy[c][QUEEN] | ei.attackedBy[c][KING];
}
// Kings. Kings are evaluated after all other pieces for both sides,
// because we need complete attack information for all pieces when computing
// the king safety evaluation.
for(Color c = WHITE; c <= BLACK; c++)
evaluate_king(pos, pos.king_square(c), c, ei);
// Evaluate passed pawns. We evaluate passed pawns for both sides at once,
// because we need to know which side promotes first in positions where
// both sides have an unstoppable passed pawn.
if(ei.pi->passed_pawns())
evaluate_passed_pawns(pos, ei);
// Middle-game specific evaluation terms
if(phase > PHASE_ENDGAME) {
// Pawn storms in positions with opposite castling.
if(square_file(pos.king_square(WHITE)) >= FILE_E &&
square_file(pos.king_square(BLACK)) <= FILE_D)
ei.mgValue +=
ei.pi->queenside_storm_value(WHITE) -
ei.pi->kingside_storm_value(BLACK);
else if(square_file(pos.king_square(WHITE)) <= FILE_D &&
square_file(pos.king_square(BLACK)) >= FILE_E)
ei.mgValue +=
ei.pi->kingside_storm_value(WHITE) -
ei.pi->queenside_storm_value(BLACK);
// Evaluate space for both sides.
if(ei.mi->space_weight() > 0) {
evaluate_space(pos, WHITE, ei);
evaluate_space(pos, BLACK, ei);
}
}
// Mobility
ei.mgValue += apply_weight(ei.mgMobility, WeightMobilityMidgame);
ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those:
if(phase < PHASE_MIDGAME && pos.opposite_colored_bishops()
&& ((factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0)) ||
(factor[BLACK] == SCALE_FACTOR_NORMAL && ei.egValue < Value(0)))) {
if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) ==
2*BishopValueMidgame) {
// Only the two bishops
if(pos.pawn_count(WHITE) + pos.pawn_count(BLACK) == 1) {
// KBP vs KB with only a single pawn; almost certainly a draw.
if(factor[WHITE] == SCALE_FACTOR_NORMAL)
factor[WHITE] = ScaleFactor(8);
if(factor[BLACK] == SCALE_FACTOR_NORMAL)
factor[BLACK] = ScaleFactor(8);
}
else {
// At least two pawns
if(factor[WHITE] == SCALE_FACTOR_NORMAL)
factor[WHITE] = ScaleFactor(32);
if(factor[BLACK] == SCALE_FACTOR_NORMAL)
factor[BLACK] = ScaleFactor(32);
}
}
else {
// Endgame with opposite-colored bishops, but also other pieces.
// Still a bit drawish, but not as drawish as with only the two
// bishops.
if(factor[WHITE] == SCALE_FACTOR_NORMAL)
factor[WHITE] = ScaleFactor(50);
if(factor[BLACK] == SCALE_FACTOR_NORMAL)
factor[BLACK] = ScaleFactor(50);
}
}
// Interpolate between the middle game and the endgame score, and
// return:
Value value = scale_by_game_phase(ei.mgValue, ei.egValue, phase, factor);
if(ei.mateThreat[stm] != MOVE_NONE)
return 8 * QueenValueMidgame - Sign[stm] * value;
else
return Sign[stm] * value;
}
/// quick_evaluate() does a very approximate evaluation of the current position.
/// It currently considers only material and piece square table scores. Perhaps
/// we should add scores from the pawn and material hash tables?
Value quick_evaluate(const Position &pos) {
Color stm;
Value mgValue, egValue;
ScaleFactor factor[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL};
Phase phase;
assert(pos.is_ok());
stm = pos.side_to_move();
mgValue = pos.mg_value();
egValue = pos.eg_value();
phase = pos.game_phase();
Value value = scale_by_game_phase(mgValue, egValue, phase, factor);
return Sign[stm] * value;
}
/// init_eval() initializes various tables used by the evaluation function.
void init_eval(int threads) {
assert(threads <= THREAD_MAX);
for(int i = 0; i < threads; i++) {
if(PawnTable[i] == NULL)
PawnTable[i] = new PawnInfoTable(PawnTableSize);
if(MaterialTable[i] == NULL)
MaterialTable[i] = new MaterialInfoTable(MaterialTableSize);
}
for(int i = threads; i < THREAD_MAX; i++) {
if(PawnTable[i] != NULL) {
delete PawnTable[i];
PawnTable[i] = NULL;
}
if(MaterialTable[i] != NULL) {
delete MaterialTable[i];
MaterialTable[i] = NULL;
}
}
for(Bitboard b = 0ULL; b < 256ULL; b++)
BitCount8Bit[b] = count_1s(b);
}
/// quit_eval() releases heap-allocated memory at program termination.
void quit_eval() {
for(int i = 0; i < THREAD_MAX; i++) {
delete PawnTable[i];
delete MaterialTable[i];
}
}
/// read_weights() reads evaluation weights from the corresponding UCI
/// parameters.
void read_weights(Color sideToMove) {
WeightMobilityMidgame =
compute_weight(get_option_value_int("Mobility (Middle Game)"),
WeightMobilityMidgameInternal);
WeightMobilityEndgame =
compute_weight(get_option_value_int("Mobility (Endgame)"),
WeightMobilityEndgameInternal);
WeightPawnStructureMidgame =
compute_weight(get_option_value_int("Pawn Structure (Middle Game)"),
WeightPawnStructureMidgameInternal);
WeightPawnStructureEndgame =
compute_weight(get_option_value_int("Pawn Structure (Endgame)"),
WeightPawnStructureEndgameInternal);
WeightPassedPawnsMidgame =
compute_weight(get_option_value_int("Passed Pawns (Middle Game)"),
WeightPassedPawnsMidgameInternal);
WeightPassedPawnsEndgame =
compute_weight(get_option_value_int("Passed Pawns (Endgame)"),
WeightPassedPawnsEndgameInternal);
WeightKingSafety[sideToMove] =
compute_weight(get_option_value_int("Cowardice"), WeightKingSafetyInternal);
WeightKingSafety[opposite_color(sideToMove)] =
compute_weight(get_option_value_int("Aggressiveness"),
WeightKingSafetyInternal);
WeightSpace =
compute_weight(get_option_value_int("Space"), WeightSpaceInternal);
init_safety();
}
namespace {
// evaluate_knight() assigns bonuses and penalties to a knight of a given
// color on a given square.
void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
Bitboard b = p.knight_attacks(s);
ei.attackedBy[us][KNIGHT] |= b;
// King attack
if(b & ei.attackZone[us]) {
ei.attackCount[us]++;
ei.attackWeight[us] += KnightAttackWeight;
Bitboard bb = (b & ei.attackedBy[them][KING]);
if(bb) ei.attacked[us] += count_1s_max_15(bb);
}
// Mobility
int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
ei.mgMobility += Sign[us] * MidgameKnightMobilityBonus[mob];
ei.egMobility += Sign[us] * EndgameKnightMobilityBonus[mob];
// Knight outposts:
if(p.square_is_weak(s, them)) {
Value v, bonus;
// Initial bonus based on square:
v = bonus = KnightOutpostBonus[relative_square(us, s)];
// Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can exchange the outpost piece:
if(v && p.pawn_attacks(them, s) & p.pawns(us)) {
bonus += v/2;
if(p.knight_count(them) == 0 &&
(SquaresByColorBB[square_color(s)] &
p.bishops(them)) == EmptyBoardBB) {
bonus += v;
}
}
ei.mgValue += Sign[us] * bonus;
ei.egValue += Sign[us] * bonus;
}
}
// evaluate_bishop() assigns bonuses and penalties to a bishop of a given
// color on a given square.
void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
Bitboard b =
bishop_attacks_bb(s, p.occupied_squares() & ~p.queens(us));
ei.attackedBy[us][BISHOP] |= b;
// King attack
if(b & ei.attackZone[us]) {
ei.attackCount[us]++;
ei.attackWeight[us] += BishopAttackWeight;
Bitboard bb = (b & ei.attackedBy[them][KING]);
if(bb) ei.attacked[us] += count_1s_max_15(bb);
}
// Mobility:
int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
ei.mgMobility += Sign[us] * MidgameBishopMobilityBonus[mob];
ei.egMobility += Sign[us] * EndgameBishopMobilityBonus[mob];
// Bishop outposts:
if(p.square_is_weak(s, them)) {
Value v, bonus;
// Initial bonus based on square:
v = bonus = BishopOutpostBonus[relative_square(us, s)];
// Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can exchange the outpost piece:
if(v && p.pawn_attacks(them, s) & p.pawns(us)) {
bonus += v/2;
if(p.knight_count(them) == 0 &&
(SquaresByColorBB[square_color(s)] &
p.bishops(them)) == EmptyBoardBB) {
bonus += v;
}
}
ei.mgValue += Sign[us] * bonus;
ei.egValue += Sign[us] * bonus;
}
}
// evaluate_rook() assigns bonuses and penalties to a rook of a given
// color on a given square.
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
// Open and half-open files:
File f = square_file(s);
if(ei.pi->file_is_half_open(us, f)) {
if(ei.pi->file_is_half_open(them, f)) {
ei.mgValue += Sign[us] * RookOpenFileBonus;
ei.egValue += Sign[us] * RookOpenFileBonus;
}
else {
ei.mgValue += Sign[us] * RookHalfOpenFileBonus;
ei.egValue += Sign[us] * RookHalfOpenFileBonus;
}
}
// Rook on 7th rank:
if(pawn_rank(us, s) == RANK_7 &&
pawn_rank(us, p.king_square(them)) == RANK_8) {
ei.mgValue += Sign[us] * MidgameRookOn7thBonus;
ei.egValue += Sign[us] * EndgameRookOn7thBonus;
}
Bitboard b =
rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
ei.attackedBy[us][ROOK] |= b;
// King attack
if(b & ei.attackZone[us]) {
ei.attackCount[us]++;
ei.attackWeight[us] += RookAttackWeight;
Bitboard bb = (b & ei.attackedBy[them][KING]);
if(bb) ei.attacked[us] += count_1s_max_15(bb);
}
// Mobility
int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
ei.mgMobility += Sign[us] * MidgameRookMobilityBonus[mob];
ei.egMobility += Sign[us] * EndgameRookMobilityBonus[mob];
// Penalize rooks which are trapped inside a king which has lost the
// right to castle:
if(mob <= 6 && !ei.pi->file_is_half_open(us, f)) {
Square ksq = p.king_square(us);
if(square_file(ksq) >= FILE_E && square_file(s) > square_file(ksq) &&
(pawn_rank(us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s))) {
// Is there a half-open file between the king and the edge of the
// board?
if(!(ei.pi->has_open_file_to_right(us, square_file(ksq)))) {
ei.mgValue -= p.can_castle(us)?
Sign[us] * ((TrappedRookPenalty - mob * 16) / 2) :
Sign[us] * (TrappedRookPenalty - mob * 16);
}
}
else if(square_file(ksq) <= FILE_D && square_file(s) < square_file(ksq)
&& (pawn_rank(us, ksq) == RANK_1 ||
square_rank(ksq) == square_rank(s))) {
// Is there a half-open file between the king and the edge of the
// board?
if(!(ei.pi->has_open_file_to_left(us, square_file(ksq)))) {
ei.mgValue -= p.can_castle(us)?
Sign[us] * ((TrappedRookPenalty - mob * 16) / 2) :
Sign[us] * (TrappedRookPenalty - mob * 16);
}
}
}
}
// evaluate_queen() assigns bonuses and penalties to a queen of a given
// color on a given square.
void evaluate_queen(const Position &p, Square s, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
// Queen on 7th rank:
if(pawn_rank(us, s) == RANK_7 &&
pawn_rank(us, p.king_square(them)) == RANK_8) {
ei.mgValue += Sign[us] * MidgameQueenOn7thBonus;
ei.egValue += Sign[us] * EndgameQueenOn7thBonus;
}
Bitboard b = p.queen_attacks(s);
ei.attackedBy[us][QUEEN] |= b;
// King attack
if(b & ei.attackZone[us]) {
ei.attackCount[us]++;
ei.attackWeight[us] += QueenAttackWeight;
Bitboard bb = (b & ei.attackedBy[them][KING]);
if(bb) ei.attacked[us] += count_1s_max_15(bb);
}
// Mobility
int mob = count_1s(b & ~p.pieces_of_color(us));
ei.mgMobility += Sign[us] * MidgameQueenMobilityBonus[mob];
ei.egMobility += Sign[us] * EndgameQueenMobilityBonus[mob];
}
// evaluate_king() assigns bonuses and penalties to a king of a given
// color on a given square.
void evaluate_king(const Position &p, Square s, Color us, EvalInfo &ei) {
int shelter = 0, sign = Sign[us];
// King shelter.
if(pawn_rank(us, s) <= RANK_4) {
Bitboard pawns = p.pawns(us) & this_and_neighboring_files_bb(s);
Rank r = square_rank(s);
for(int i = 0; i < 3; i++)
shelter += count_1s_8bit(pawns >> ((r+(i+1)*sign) * 8)) * (64>>i);
ei.mgValue += sign * Value(shelter);
}
// King safety. This is quite complicated, and is almost certainly far
// from optimally tuned.
Color them = opposite_color(us);
if(p.queen_count(them) >= 1 && ei.attackCount[them] >= 2
&& p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame
&& ei.attacked[them]) {
// Is it the attackers turn to move?
bool sente = (them == p.side_to_move());
// Find the attacked squares around the king which has no defenders
// apart from the king itself:
Bitboard undefended =
ei.attacked_by(them) & ~ei.attacked_by(us, PAWN)
& ~ei.attacked_by(us, KNIGHT) & ~ei.attacked_by(us, BISHOP)
& ~ei.attacked_by(us, ROOK) & ~ei.attacked_by(us, QUEEN)
& ei.attacked_by(us, KING);
Bitboard occ = p.occupied_squares(), b, b2;
// Initialize the 'attackUnits' variable, which is used later on as an
// index to the SafetyTable[] array. The initial value is based on the
// number and types of the attacking pieces, the number of attacked and
// undefended squares around the king, the square of the king, and the
// quality of the pawn shelter.
int attackUnits =
Min((ei.attackCount[them] * ei.attackWeight[them]) / 2, 25)
+ (ei.attacked[them] + count_1s_max_15(undefended)) * 3
+ InitKingDanger[relative_square(us, s)] - (shelter >> 5);
// Analyse safe queen contact checks:
b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
if(b) {
Bitboard attackedByOthers =
ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT)
| ei.attacked_by(them, BISHOP) | ei.attacked_by(them, ROOK);
b &= attackedByOthers;
if(b) {
// The bitboard b now contains the squares available for safe queen
// contact checks.
int count = count_1s_max_15(b);
attackUnits += QueenContactCheckBonus * count * (sente? 2 : 1);
// Is there a mate threat?
if(QueenContactMates && !p.is_check()) {
Bitboard escapeSquares =
p.king_attacks(s) & ~p.pieces_of_color(us) & ~attackedByOthers;
while(b) {
Square from, to = pop_1st_bit(&b);
if(!(escapeSquares
& ~queen_attacks_bb(to, occ & clear_mask_bb(s)))) {
// We have a mate, unless the queen is pinned or there
// is an X-ray attack through the queen.
for(int i = 0; i < p.queen_count(them); i++) {
from = p.queen_list(them, i);
if(bit_is_set(p.queen_attacks(from), to)
&& !bit_is_set(p.pinned_pieces(them), from)
&& !(rook_attacks_bb(to, occ & clear_mask_bb(from))
& p.rooks_and_queens(us))
&& !(rook_attacks_bb(to, occ & clear_mask_bb(from))
& p.rooks_and_queens(us)))
ei.mateThreat[them] = make_move(from, to);
}
}
}
}
}
}
// Analyse safe distance checks:
if(QueenCheckBonus > 0 || RookCheckBonus > 0) {
b = p.rook_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
// Queen checks
b2 = b & ei.attacked_by(them, QUEEN);
if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2);
// Rook checks
b2 = b & ei.attacked_by(them, ROOK);
if(b2) attackUnits += RookCheckBonus * count_1s_max_15(b2);
}
if(QueenCheckBonus > 0 || BishopCheckBonus > 0) {
b = p.bishop_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
// Queen checks
b2 = b & ei.attacked_by(them, QUEEN);
if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2);
// Bishop checks
b2 = b & ei.attacked_by(them, BISHOP);
if(b2) attackUnits += BishopCheckBonus * count_1s_max_15(b2);
}
if(KnightCheckBonus > 0) {
b = p.knight_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
// Knight checks
b2 = b & ei.attacked_by(them, KNIGHT);
if(b2) attackUnits += KnightCheckBonus * count_1s_max_15(b2);
}
// Analyse discovered checks (only for non-pawns right now, consider
// adding pawns later).
if(DiscoveredCheckBonus) {
b = p.discovered_check_candidates(them) & ~p.pawns();
if(b)
attackUnits +=
DiscoveredCheckBonus * count_1s_max_15(b) * (sente? 2 : 1);
}
// Has a mate threat been found? We don't do anything here if the
// side with the mating move is the side to move, because in that
// case the mating side will get a huge bonus at the end of the main
// evaluation function instead.
if(ei.mateThreat[them] != MOVE_NONE)
attackUnits += MateThreatBonus;
// Ensure that attackUnits is between 0 and 99, in order to avoid array
// out of bounds errors:
if(attackUnits < 0) attackUnits = 0;
if(attackUnits >= 100) attackUnits = 99;
// Finally, extract the king safety score from the SafetyTable[] array.
// Add the score to the evaluation, and also to ei.futilityMargin. The
// reason for adding the king safety score to the futility margin is
// that the king safety scores can sometimes be very big, and that
// capturing a single attacking piece can therefore result in a score
// change far bigger than the value of the captured piece.
Value v = apply_weight(SafetyTable[attackUnits], WeightKingSafety[us]);
ei.mgValue -= sign * v;
if(us == p.side_to_move())
ei.futilityMargin += v;
}
}
// evaluate_passed_pawns() evaluates the passed pawns for both sides.
void evaluate_passed_pawns(const Position &pos, EvalInfo &ei) {
bool hasUnstoppable[2] = {false, false};
int movesToGo[2] = {100, 100};
for(Color us = WHITE; us <= BLACK; us++) {
Color them = opposite_color(us);
Square ourKingSq = pos.king_square(us);
Square theirKingSq = pos.king_square(them);
Bitboard b = ei.pi->passed_pawns() & pos.pawns(us), b2, b3, b4;
while(b) {
Square s = pop_1st_bit(&b);
assert(pos.piece_on(s) == pawn_of_color(us));
assert(pos.pawn_is_passed(us, s));
int r = int(pawn_rank(us, s) - RANK_2);
int tr = Max(0, r * (r-1));
Square blockSq = s + pawn_push(us);
// Base bonus based on rank:
Value mbonus = Value(20 * tr);
Value ebonus = Value(10 + r * r * 10);
// Adjust bonus based on king proximity:
ebonus -= Value(square_distance(ourKingSq, blockSq) * 3 * tr);
ebonus -=
Value(square_distance(ourKingSq, blockSq + pawn_push(us)) * 1 * tr);
ebonus += Value(square_distance(theirKingSq, blockSq) * 6 * tr);
// If the pawn is free to advance, increase bonus:
if(pos.square_is_empty(blockSq)) {
b2 = squares_in_front_of(us, s);
b3 = b2 & ei.attacked_by(them);
b4 = b2 & ei.attacked_by(us);
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen:
if(bit_is_set(ei.attacked_by(them,ROOK)|ei.attacked_by(them,QUEEN),s)
&& squares_behind(us, s) & pos.rooks_and_queens(them))
b3 = b2;
if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) {
// There are no enemy pieces in the pawn's path! Are any of the
// squares in the pawn's path attacked by the enemy?
if(b3 == EmptyBoardBB)
// No enemy attacks, huge bonus!
ebonus += Value(tr * ((b2 == b4)? 17 : 15));
else
// OK, there are enemy attacks. Are those squares which are
// attacked by the enemy also attacked by us? If yes, big bonus
// (but smaller than when there are no enemy attacks), if no,
// somewhat smaller bonus.
ebonus += Value(tr * (((b3 & b4) == b3)? 13 : 8));
}
else {
// There are some enemy pieces in the pawn's path. While this is
// sad, we still assign a moderate bonus if all squares in the path
// which are either occupied by or attacked by enemy pieces are
// also attacked by us.
if(((b3 | (b2 & pos.pieces_of_color(them))) & ~b4) == EmptyBoardBB)
ebonus += Value(tr * 6);
}
// At last, add a small bonus when there are no *friendly* pieces
// in the pawn's path:
if((b2 & pos.pieces_of_color(us)) == EmptyBoardBB)
ebonus += Value(tr);
}
// If the pawn is supported by a friendly pawn, increase bonus.
b2 = pos.pawns(us) & neighboring_files_bb(s);
if(b2 & rank_bb(s))
ebonus += Value(r * 20);
else if(pos.pawn_attacks(them, s) & b2)
ebonus += Value(r * 12);
// If the other side has only a king, check whether the pawn is
// unstoppable:
if(pos.non_pawn_material(them) == Value(0)) {
Square qsq;
int d;
qsq = relative_square(us, make_square(square_file(s), RANK_8));
d = square_distance(s, qsq) - square_distance(theirKingSq, qsq)
+ ((us == pos.side_to_move())? 0 : 1);
if(d < 0) {
int mtg = RANK_8 - pawn_rank(us, s);
int blockerCount =
count_1s_max_15(squares_in_front_of(us,s)&pos.occupied_squares());
mtg += blockerCount;
d += blockerCount;
if(d < 0) {
hasUnstoppable[us] = true;
movesToGo[us] = Min(movesToGo[us], mtg);
}
}
}
// Rook pawns are a special case: They are sometimes worse, and
// sometimes better than other passed pawns. It is difficult to find
// good rules for determining whether they are good or bad. For now,
// we try the following: Increase the value for rook pawns if the
// other side has no pieces apart from a knight, and decrease the
// value if the other side has a rook or queen.
if(square_file(s) == FILE_A || square_file(s) == FILE_H) {
if(pos.non_pawn_material(them) == KnightValueMidgame
&& pos.knight_count(them) == 1)
ebonus += ebonus / 4;
else if(pos.rooks_and_queens(them))
ebonus -= ebonus / 4;
}
// Add the scores for this pawn to the middle game and endgame eval.
ei.mgValue += apply_weight(Sign[us] * mbonus, WeightPassedPawnsMidgame);
ei.egValue += apply_weight(Sign[us] * ebonus, WeightPassedPawnsEndgame);
}
}
// Does either side have an unstoppable passed pawn?
if(hasUnstoppable[WHITE] && !hasUnstoppable[BLACK])
ei.egValue += UnstoppablePawnValue - Value(0x40 * movesToGo[WHITE]);
else if(hasUnstoppable[BLACK] && !hasUnstoppable[WHITE])
ei.egValue -= UnstoppablePawnValue - Value(0x40 * movesToGo[BLACK]);
else if(hasUnstoppable[BLACK] && hasUnstoppable[WHITE]) {
// Both sides have unstoppable pawns! Try to find out who queens
// first. We begin by transforming 'movesToGo' to the number of
// plies until the pawn queens for both sides:
movesToGo[WHITE] *= 2;
movesToGo[BLACK] *= 2;
movesToGo[pos.side_to_move()]--;
// If one side queens at least three plies before the other, that
// side wins:
if(movesToGo[WHITE] <= movesToGo[BLACK] - 3)
ei.egValue += UnstoppablePawnValue - Value(0x40 * (movesToGo[WHITE]/2));
else if(movesToGo[BLACK] <= movesToGo[WHITE] - 3)
ei.egValue -= UnstoppablePawnValue - Value(0x40 * (movesToGo[BLACK]/2));
// We could also add some rules about the situation when one side
// queens exactly one ply before the other: Does the first queen
// check the opponent's king, or attack the opponent's queening square?
// This is slightly tricky to get right, because it is possible that
// the opponent's king has moved somewhere before the first pawn queens.
}
}
// evaluate_trapped_bishop_a7h7() determines whether a bishop on a7/h7
// (a2/h2 for black) is trapped by enemy pawns, and assigns a penalty
// if it is.
void evaluate_trapped_bishop_a7h7(const Position &pos, Square s, Color us,
EvalInfo &ei) {
Piece pawn = pawn_of_color(opposite_color(us));
Square b6, b8;
assert(square_is_ok(s));
assert(pos.piece_on(s) == bishop_of_color(us));
if(square_file(s) == FILE_A) {
b6 = relative_square(us, SQ_B6);
b8 = relative_square(us, SQ_B8);
}
else {
b6 = relative_square(us, SQ_G6);
b8 = relative_square(us, SQ_G8);
}
if(pos.piece_on(b6) == pawn && pos.see(s, b6) < 0 && pos.see(s, b8) < 0) {
ei.mgValue -= Sign[us] * TrappedBishopA7H7Penalty;
ei.egValue -= Sign[us] * TrappedBishopA7H7Penalty;
}
}
// evaluate_trapped_bishop_a1h1() determines whether a bishop on a1/h1
// (a8/h8 for black) is trapped by a friendly pawn on b2/g2 (b7/g7 for
// black), and assigns a penalty if it is. This pattern can obviously
// only occur in Chess960 games.
void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
EvalInfo &ei) {
Piece pawn = pawn_of_color(us);
Square b2, b3, c3;
assert(Chess960);
assert(square_is_ok(s));
assert(pos.piece_on(s) == bishop_of_color(us));
if(square_file(s) == FILE_A) {
b2 = relative_square(us, SQ_B2);
b3 = relative_square(us, SQ_B3);
c3 = relative_square(us, SQ_C3);
}
else {
b2 = relative_square(us, SQ_G2);
b3 = relative_square(us, SQ_G3);
c3 = relative_square(us, SQ_F3);
}
if(pos.piece_on(b2) == pawn) {
Value penalty;
if(!pos.square_is_empty(b3))
penalty = 2*TrappedBishopA1H1Penalty;
else if(pos.piece_on(c3) == pawn)
penalty = TrappedBishopA1H1Penalty;
else
penalty = TrappedBishopA1H1Penalty / 2;
ei.mgValue -= Sign[us] * penalty;
ei.egValue -= Sign[us] * penalty;
}
}
// evaluate_space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares
// available for minor pieces on the central four files on ranks 2--4. Safe
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table.
void evaluate_space(const Position &pos, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
// Find the safe squares for our pieces inside the area defined by
// SpaceMask[us]. A square is unsafe it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safeSquares =
SpaceMask[us] & ~pos.pawns(us) & ~ei.attacked_by(them, PAWN)
& ~(~ei.attacked_by(us) & ei.attacked_by(them));
// Find all squares which are at most three squares behind some friendly
// pawn.
Bitboard behindFriendlyPawns = pos.pawns(us);
if(us == WHITE) {
behindFriendlyPawns |= (behindFriendlyPawns >> 8);
behindFriendlyPawns |= (behindFriendlyPawns >> 16);
}
else {
behindFriendlyPawns |= (behindFriendlyPawns << 8);
behindFriendlyPawns |= (behindFriendlyPawns << 16);
}
int space =
count_1s_max_15(safeSquares)
+ count_1s_max_15(behindFriendlyPawns & safeSquares);
ei.mgValue += Sign[us] *
apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
}
// apply_weight applies() an evaluation weight to a value.
inline Value apply_weight(Value v, int w) {
return (v*w) / 0x100;
}
// scale_by_game_phase() interpolates between a middle game and an endgame
// score, based on game phase. It also scales the return value by a
// ScaleFactor array.
Value scale_by_game_phase(Value mv, Value ev, Phase ph, ScaleFactor sf[]) {
assert(mv > -VALUE_INFINITE && mv < VALUE_INFINITE);
assert(ev > -VALUE_INFINITE && ev < VALUE_INFINITE);
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
if(ev > Value(0))
ev = apply_scale_factor(ev, sf[WHITE]);
else
ev = apply_scale_factor(ev, sf[BLACK]);
Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
return Value(int(result) & ~(GrainSize - 1));
}
// count_1s_8bit() counts the number of nonzero bits in the 8 least
// significant bits of an integer. This function is used by the king
// shield evaluation.
int count_1s_8bit(int b) {
return int(BitCount8Bit[b & 0xFF]);
}
// compute_weight() computes the value of an evaluation weight, by combining
// an UCI-configurable weight with an internal weight.
int compute_weight(int uciWeight, int internalWeight) {
uciWeight = (uciWeight * 0x100) / 100;
return (uciWeight * internalWeight) / 0x100;
}
// init_safety() initizes the king safety evaluation, based on UCI
// parameters. It is called from read_weights().
void init_safety() {
double a, b;
int maxSlope, peak, i, j;
QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus");
QueenCheckBonus = get_option_value_int("Queen Check Bonus");
RookCheckBonus = get_option_value_int("Rook Check Bonus");
BishopCheckBonus = get_option_value_int("Bishop Check Bonus");
KnightCheckBonus = get_option_value_int("Knight Check Bonus");
DiscoveredCheckBonus = get_option_value_int("Discovered Check Bonus");
MateThreatBonus = get_option_value_int("Mate Threat Bonus");
a = get_option_value_int("King Safety Coefficient") / 100.0;
b = get_option_value_int("King Safety X Intercept") * 1.0;
maxSlope = get_option_value_int("King Safety Max Slope");
peak = (get_option_value_int("King Safety Max Value") * 256) / 100;
for(i = 0; i < 100; i++) {
if(i < b) SafetyTable[i] = Value(0);
else if(get_option_value_string("King Safety Curve") == "Quadratic")
SafetyTable[i] = Value((int)(a * (i - b) * (i - b)));
else if(get_option_value_string("King Safety Curve") == "Linear")
SafetyTable[i] = Value((int)(100 * a * (i - b)));
}
for(i = 0; i < 100; i++)
if(SafetyTable[i+1] - SafetyTable[i] > maxSlope) {
for(j = i + 1; j < 100; j++)
SafetyTable[j] = SafetyTable[j-1] + Value(maxSlope);
}
for(i = 0; i < 100; i++)
if(SafetyTable[i] > Value(peak))
SafetyTable[i] = Value(peak);
}
}
glaurung-2.2/src/direction.cpp 0000644 0001750 0001750 00000003346 11123021252 016031 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include "direction.h"
#include "square.h"
////
//// Variables
////
uint8_t DirectionTable[64][64];
uint8_t SignedDirectionTable[64][64];
////
//// Functions
////
void init_direction_table() {
SquareDelta deltas[8] = {
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
};
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
DirectionTable[s1][s2] = uint8_t(DIR_NONE);
SignedDirectionTable[s1][s2] = uint8_t(SIGNED_DIR_NONE);
if(s1 == s2) continue;
for(SignedDirection d = SIGNED_DIR_E; d <= SIGNED_DIR_SE; d++) {
SquareDelta delta = deltas[d];
Square s3, s4;
for(s4 = s1 + delta, s3 = s1;
square_distance(s4, s3) == 1 && s4 != s2 && square_is_ok(s4);
s3 = s4, s4 += delta);
if(s4 == s2 && square_distance(s4, s3) == 1) {
SignedDirectionTable[s1][s2] = uint8_t(d);
DirectionTable[s1][s2] = uint8_t(d/2);
break;
}
}
}
}
glaurung-2.2/src/timeoday.cpp 0000644 0001750 0001750 00000001163 11123021251 015656 0 ustar oliver oliver /*
(c) Copyright 1992 Eric Backus
This software may be used freely so long as this copyright notice is
left intact. There is no warrantee on this software.
*/
#include
#include
#include "dos.h"
int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
SYSTEMTIME systime;
if (tp) {
struct tm tmrec;
time_t theTime = time(NULL);
tmrec = *localtime(&theTime);
tp->tv_sec = mktime(&tmrec);
GetLocalTime(&systime); /* system time */
tp->tv_usec = systime.wMilliseconds * 1000;
}
return 0;
}
glaurung-2.2/src/search.h 0000644 0001750 0001750 00000003757 11123021255 014774 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#if !defined(SEARCH_H_INCLUDED)
#define SEARCH_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "history.h"
#include "lock.h"
#include "movegen.h"
#include "position.h"
#include "tt.h"
#include "value.h"
////
//// Constants
////
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = 102;
////
//// Types
////
/// The SearchStack struct keeps track of the information we need to remember
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
struct SearchStack {
Move pv[PLY_MAX];
Move currentMove;
Value currentMoveCaptureValue;
Move mateKiller, killer1, killer2;
Move threatMove;
Depth reduction;
};
////
//// Global variables
////
extern TranspositionTable TT;
extern int ActiveThreads;
extern Lock SMPLock;
// Perhaps better to make H local, and pass as parameter to MovePicker?
extern History H;
////
//// Prototypes
////
extern void init_threads();
extern void stop_threads();
extern void think(const Position &pos, bool infinite, bool ponder, int time,
int increment, int movesToGo, int maxDepth, int maxNodes,
int maxTime, Move searchMoves[]);
extern int64_t nodes_searched();
#endif // !defined(SEARCH_H_INCLUDED)
glaurung-2.2/src/bitboard.cpp 0000644 0001750 0001750 00000045214 11123214362 015645 0 ustar oliver oliver /*
Glaurung, a UCI chess playing engine.
Copyright (C) 2004-2008 Tord Romstad
Glaurung is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Glaurung is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
////
//// Includes
////
#include
#include
#include "bitboard.h"
#include "direction.h"
////
//// Constants and variables
////
const Bitboard SquaresByColorBB[2] = {BlackSquaresBB, WhiteSquaresBB};
const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
};
const Bitboard NeighboringFilesBB[8] = {
FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
};
const Bitboard ThisAndNeighboringFilesBB[8] = {
FileABB|FileBBB, FileABB|FileBBB|FileCBB,
FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
};
const Bitboard RankBB[8] = {
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
};
const Bitboard RelativeRankBB[2][8] = {
{
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
},
{
Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB
}
};
const Bitboard InFrontBB[2][8] = {
{
Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank6BB | Rank7BB | Rank8BB,
Rank7BB | Rank8BB,
Rank8BB,
EmptyBoardBB
},
{
EmptyBoardBB,
Rank1BB,
Rank2BB | Rank1BB,
Rank3BB | Rank2BB | Rank1BB,
Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
}
};
#if defined(USE_COMPACT_ROOK_ATTACKS)
Bitboard RankAttacks[8][64], FileAttacks[8][64];
#elif defined(USE_32BIT_ATTACKS)
const uint64_t RMult[64] = {
0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
0x5ac08a4735370479ULL,
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#else // if defined(USE_32BIT_ATTACKS)
const uint64_t RMult[64] = {
0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
0x410201ce5c030092ULL
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#endif // defined(USE_32BIT_ATTACKS)
#if !defined(USE_COMPACT_ROOK_ATTACKS)
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
#endif
#if defined(USE_32BIT_ATTACKS)
const uint64_t BMult[64] = {
0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
0xdc01f6dca4bebbdcULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
#else // if defined(USE_32BIT_ATTACKS)
const uint64_t BMult[64] = {
0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
0xa08520292120600ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
#endif // defined(USE_32BIT_ATTACKS)
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[64];
Bitboard ClearMaskBB[64];
Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8];
Bitboard BetweenBB[64][64];
Bitboard PassedPawnMask[2][64];
Bitboard OutpostMask[2][64];
Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
////
//// Local definitions
////
namespace {
void init_masks();
void init_ray_bitboards();
void init_attacks();
void init_between_bitboards();
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
Bitboard index_to_bitboard(int index, Bitboard mask);
void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]);
void init_pseudo_attacks();
#if defined(USE_COMPACT_ROOK_ATTACKS)
void init_file_and_rank_attacks();
#endif
};
////
//// Functions
////
/// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) {
for(Rank r = RANK_8; r >= RANK_1; r--) {
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
for(File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "|" << std::endl;
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
}
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks();
init_ray_bitboards();
init_attacks();
init_between_bitboards();
#if defined(USE_COMPACT_ROOK_ATTACKS)
init_file_and_rank_attacks();
#else
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift,
RMult, rookDeltas);
#endif
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift,
BMult, bishopDeltas);
init_pseudo_attacks();
}
#if defined(USE_FOLDED_BITSCAN)
static const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
58, 20, 37, 17, 36, 8
};
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
Square first_1(Bitboard b) {
b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
}
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
Square pop_1st_bit(Bitboard *b) {
Bitboard bb = *b ^ (*b - 1);
uint32_t fold = int(bb) ^ int(bb >> 32);
*b &= (*b - 1);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
}
#else
static const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
51, 60, 42, 59, 58
};
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
}
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
Square pop_1st_bit(Bitboard *b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
}
#endif // defined(USE_FOLDED_BITSCAN)
namespace {
// All functions below are used to precompute various bitboards during
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
void init_masks() {
for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];
}
for(Color c = WHITE; c <= BLACK; c++)
for(Square s = SQ_A1; s <= SQ_H8; s++) {
PassedPawnMask[c][s] =
in_front_bb(c, s) & this_and_neighboring_files_bb(s);
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
}
}
void init_ray_bitboards() {
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
for(int i = 0; i < 128; i = i + 9 & ~8) {
for(int j = 0; j < 8; j++) {
RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
}
}
}
void init_attacks() {
int i, j, k, l;
int step[16][8] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
};
for(i = 0; i < 64; i++) {
for(j = 0; j <= int(BK); j++) {
StepAttackBB[j][i] = EmptyBoardBB;
for(k = 0; k < 8 && step[j][k] != 0; k++) {
l = i + step[j][k];
if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
StepAttackBB[j][i] |= (1ULL << l);
}
}
}
}
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
Bitboard result = 0ULL;
int rk = sq / 8, fl = sq % 8, r, f, i;
for(i = 0; i < dirs; i++) {
int dx = deltas[i][0], dy = deltas[i][1];
for(f = fl+dx, r = rk+dy;
(dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
f += dx, r += dy) {
result |= (1ULL << (f + r*8));
if(block & (1ULL << (f + r*8))) break;
}
}
return result;
}
void init_between_bitboards() {
SquareDelta step[8] = {
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
};
SignedDirection d;
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
BetweenBB[s1][s2] = EmptyBoardBB;
d = signed_direction_between_squares(s1, s2);
if(d != SIGNED_DIR_NONE)
for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
int i, j, bits = count_1s(mask);
Bitboard result = 0ULL;
for(i = 0; i < bits; i++) {
j = pop_1st_bit(&mask);
if(index & (1 << i)) result |= (1ULL << j);
}
return result;
}
void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]) {
int i, j, k, index = 0;
Bitboard b;
for(i = 0; i < 64; i++) {
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
j = (1 << (64 - shift[i]));
for(k = 0; k < j; k++) {
#if defined(USE_32BIT_ATTACKS)
b = index_to_bitboard(k, mask[i]);
attacks[index +
(unsigned(int(b) * int(mult[i]) ^
int(b >> 32) * int(mult[i] >> 32))
>> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#else
b = index_to_bitboard(k, mask[i]);
attacks[index + ((b * mult[i]) >> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#endif
}
index += j;
}
}
void init_pseudo_attacks() {
Square s;
for(s = SQ_A1; s <= SQ_H8; s++) {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
}
}
#if defined(USE_COMPACT_ROOK_ATTACKS)
void init_file_and_rank_attacks() {
int i, j, k, l, m, s;
Bitboard b1, b2;
for(i = 0; i < 64; i++) {
for(m = 0; m <= 1; m++) {
b1 = 0ULL;
for(j = 0; j < 6; j++) if(i & (1<= 0 && l <= 7; l += s) {
b2 |= (m? RankBB[l] : FileBB[l]);
if(b1 & (1ULL << (l*(1+m*7)))) break;
}
}
if(m) FileAttacks[j][(b1*0xd6e8802041d0c441ULL) >> 58] = b2;
else RankAttacks[j][i] = b2;
}
}
}
}
#endif // defined(USE_COMPACT_ROOK_ATTACKS)
}
glaurung-2.2/Windows/ 0000755 0001750 0001750 00000000000 11123710607 014213 5 ustar oliver oliver glaurung-2.2/Copying.txt 0000644 0001750 0001750 00000105755 10720663707 014761 0 ustar oliver oliver GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
glaurung-2.2/polyglot.ini 0000644 0001750 0001750 00000000420 11123036336 015127 0 ustar oliver oliver
[PolyGlot]
EngineDir = .
EngineCommand = ./glaurung
Book = false
BookFile = book.bin
Log = true
LogFile = glaurung.log
Resign = true
ResignScore = 600
[Engine]
Hash = 128
Threads = 1
OwnBook = false
Book File = book.bin
Use Search Log = false