glaurung-2.2/0000755000175000017500000000000011123154606012562 5ustar oliveroliverglaurung-2.2/Readme.txt0000644000175000017500000003511611123154606014526 0ustar oliveroliver1. 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/0000755000175000017500000000000011123710547014171 5ustar oliveroliverglaurung-2.2/MacOSX/0000755000175000017500000000000011123710560013651 5ustar oliveroliverglaurung-2.2/src/0000755000175000017500000000000011123214517013347 5ustar oliveroliverglaurung-2.2/src/movegen.cpp0000644000175000017500000011047111123021252015507 0ustar oliveroliver/* 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.cpp0000644000175000017500000024100011123214400015304 0ustar oliveroliver/* 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/Makefile0000644000175000017500000000721011012547274015015 0ustar oliveroliver# 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.h0000644000175000017500000001303111123021255015003 0ustar oliveroliver/* 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.cpp0000644000175000017500000000751311123021252015004 0ustar oliveroliver/* 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.h0000644000175000017500000000453611123021255014471 0ustar oliveroliver/* 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.h0000644000175000017500000000166011123021255015127 0ustar oliveroliver/* 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.cpp0000644000175000017500000000157011123021252015164 0ustar oliveroliver/* 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.h0000644000175000017500000000316211123021255014764 0ustar oliveroliver/* 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.h0000644000175000017500000000652211123021255014650 0ustar oliveroliver/* 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.cpp0000644000175000017500000001077511123021252015023 0ustar oliveroliver/* 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.h0000644000175000017500000000175111123021255015333 0ustar oliveroliver/* 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.cpp0000644000175000017500000002643711123021251015206 0ustar oliveroliver/* 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.cpp0000644000175000017500000003447511123021252015675 0ustar oliveroliver/* 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.cpp0000644000175000017500000001401511123021251014472 0ustar oliveroliver/* 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.h0000644000175000017500000000252111123021255014274 0ustar oliveroliver/* 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.h0000644000175000017500000000232211123021255014630 0ustar oliveroliver/* 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.cpp0000644000175000017500000000335411123021251015347 0ustar oliveroliver/* 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.h0000644000175000017500000000726311123021255015331 0ustar oliveroliver/* 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/COPYING0000644000175000017500000010575511012547274014425 0ustar oliveroliver 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.cpp0000644000175000017500000003365611123021252015656 0ustar oliveroliver/* 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.cpp0000644000175000017500000000432211123021252014770 0ustar oliveroliver/* 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.cpp0000644000175000017500000020635411123021251015720 0ustar oliveroliver/* 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.h0000644000175000017500000005146011123021255015365 0ustar oliveroliver/* 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.cpp0000644000175000017500000002377111123021251014635 0ustar oliveroliver/* 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.h0000644000175000017500000001237311123214340015020 0ustar oliveroliver/* 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.h0000644000175000017500000000504111123021255014443 0ustar oliveroliver/* 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.h0000644000175000017500000000157511123021255014623 0ustar oliveroliver/* 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.cpp0000644000175000017500000000506511123021251015135 0ustar oliveroliver/* 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.h0000644000175000017500000000242111123021255014445 0ustar oliveroliver/* 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.h0000644000175000017500000000344211123021255015476 0ustar oliveroliver/* 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.cpp0000644000175000017500000006331611123021252015006 0ustar oliveroliver/* 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.h0000644000175000017500000000626111123021255014605 0ustar oliveroliver/* 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.h0000644000175000017500000000220211123021255014576 0ustar oliveroliver/* 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.cpp0000644000175000017500000000576411123021251016010 0ustar oliveroliver/* 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.cpp0000644000175000017500000010127511123021252015451 0ustar oliveroliver/* 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.cpp0000644000175000017500000000513711123021252015552 0ustar oliveroliver/* 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.cpp0000644000175000017500000001200611123021252015656 0ustar oliveroliver/* 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.h0000644000175000017500000000344211123021255014450 0ustar oliveroliver/* 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.h0000644000175000017500000000266511123021255015535 0ustar oliveroliver/* 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.cpp0000644000175000017500000002633211123021251014630 0ustar oliveroliver/* 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.h0000644000175000017500000001340311123021255015114 0ustar oliveroliver/* 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.cpp0000644000175000017500000000464211123021252015165 0ustar oliveroliver/* 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.h0000644000175000017500000000433011123021255015214 0ustar oliveroliver/* 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.h0000644000175000017500000000410611123021255014143 0ustar oliveroliver/* 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.h0000644000175000017500000000364211123021255014624 0ustar oliveroliver/* 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.h0000644000175000017500000001061211123021255014627 0ustar oliveroliver/* 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.h0000644000175000017500000001123511123021255015313 0ustar oliveroliver/* 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.h0000644000175000017500000000224211123021255014657 0ustar oliveroliver/* 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.h0000644000175000017500000000171611123021255015452 0ustar oliveroliver/* 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.h0000644000175000017500000000473411123021255015340 0ustar oliveroliver/* 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.h0000644000175000017500000000253411123021255015157 0ustar oliveroliver/* 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.h0000644000175000017500000002656511123021255015317 0ustar oliveroliver/* 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.cpp0000644000175000017500000002063511123021252015462 0ustar oliveroliver/* 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.h0000644000175000017500000000153611123021255014300 0ustar oliveroliver/* 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.cpp0000644000175000017500000002246411123214415016071 0ustar oliveroliver/* 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.cpp0000644000175000017500000013312411123021252015655 0ustar oliveroliver/* 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.cpp0000644000175000017500000000334611123021252016031 0ustar oliveroliver/* 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.cpp0000644000175000017500000000116311123021251015656 0ustar oliveroliver/* (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.h0000644000175000017500000000375711123021255014774 0ustar oliveroliver/* 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.cpp0000644000175000017500000004521411123214362015645 0ustar oliveroliver/* 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/0000755000175000017500000000000011123710607014213 5ustar oliveroliverglaurung-2.2/Copying.txt0000644000175000017500000010575510720663707014761 0ustar oliveroliver 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.ini0000644000175000017500000000042011123036336015127 0ustar oliveroliver [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