--- /dev/null
+language: cpp
+sudo: required
+dist: trusty
+
+matrix:
+ include:
+ - os: linux
+ compiler: gcc
+ addons:
+ apt:
+ sources: ['ubuntu-toolchain-r-test']
+ packages: ['g++-6', 'g++-6-multilib', 'g++-multilib', 'valgrind', 'expect']
+ env:
+ - COMPILER=g++-6
+ - COMP=gcc
+
+ - os: linux
+ compiler: clang
+ addons:
+ apt:
+ sources: ['ubuntu-toolchain-r-test']
+ packages: ['clang', 'g++-multilib', 'valgrind', 'expect']
+ env:
+ - COMPILER=clang++
+ - COMP=clang
+
+ - os: osx
+ compiler: gcc
+ env:
+ - COMPILER=g++
+ - COMP=gcc
+
+ - os: osx
+ compiler: clang
+ env:
+ - COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
+ - COMP=clang
+
+branches:
+ only:
+ - master
+
+before_script:
+ - cd src
+
+script:
+ # Obtain bench reference from git log
+ - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9][0-9]*\)/\1/g" > git_sig
+ - export benchref=$(cat git_sig)
+ - echo "Reference bench:" $benchref
+ #
+ # Verify bench number against various builds
+ - export CXXFLAGS=-Werror
+ - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
+ - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
+ - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
+ - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
+ #
+ # Check perft and reproducible search
+ - ../tests/perft.sh
+ - ../tests/reprosearch.sh
+ #
+ # Valgrind
+ #
+ - export CXXFLAGS=-O1
+ - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
+ - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
+ #
+ # Sanitizer
+ #
+ # Use g++-6 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc
+ - if [[ "$COMPILER" == "g++-6" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
+ - if [[ "$COMPILER" == "g++-6" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
--- /dev/null
+# Generated with 'git shortlog -sn | cut -c8-', which sorts by commits, manually ordered the first four authors, merged duplicates
+
+Tord Romstad
+Marco Costalba (mcostalba)
+Joona Kiiski (zamar)
+Gary Linscott (glinscott)
+Lucas Braesch (lucasart)
+Bill Henry (VoyagerOne)
+mstembera
+Stéphane Nicolet (Stephane Nicolet, snicolet)
+Stefan Geschwentner
+Alain SAVARD (Rocky640)
+Jörg Oster (Joerg Oster, joergoster)
+Reuven Peleg
+Chris Caino (Chris Cain, ceebo)
+Jean-Francois Romang
+homoSapiensSapiens
+Leonid Pechenik
+Stefano Cardanobile (Stefano80)
+Arjun Temurnikar
+Uri Blass (uriblass)
+jundery
+Ajith (ajithcj)
+hxim
+Ralph Stößer (Ralph Stoesser)
+Guenther Demetz
+Jonathan Calovski (Mysseno)
+Tom Vijlbrief
+mbootsector
+Daylen Yang
+ElbertoOne
+Henri Wiechers
+loco-loco
+Joost VandeVondele (Joost Vandevondele)
+Ronald de Man (syzygy)
+DU-jdto
+David Zar
+Eelco de Groot
+Jerry Donald
+NicklasPersson
+Ryan Schmitt
+Alexander Kure
+Dan Schmidt
+H. Felix Wittmann
+Jacques
+Joseph R. Prostko
+Justin Blanchard
+Linus Arver
+Luca Brivio
+Lyudmil Antonov
+Rodrigo Exterckötter Tjäder
+Ron Britvich
+RyanTaker
+Vince Negri
+erbsenzaehler
+Joseph Hellis (jhellis3)
+shane31
+Andrew Grant
+Andy Duplain
+Auguste Pop
+Balint Pfliegel
+Dariusz Orzechowski
+DiscanX
+Ernesto Gatti
+Gregor Cramer
+Hiraoka Takuya (HiraokaTakuya)
+Hongzhi Cheng
+IIvec
+Kelly Wilson
+Ken T Takusagawa
+Kojirion
+Krgp
+Matt Sullivan
+Matthew Lai
+Matthew Sullivan
+Michel Van den Bergh
+Niklas Fiekas
+Oskar Werkelin Ahlin
+Pablo Vazquez
+Pascal Romaret
+Raminder Singh
+Richard Lloyd
+Ryan Takker
+Thanar2
+absimaldata
+atumanian
+braich
+fanon
+gamander
+gguliash
+kinderchocolate
+pellanda
+ppigazzini
+renouve
+sf-x
+thaspel
+unknown
+
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE\r
+ Version 3, 29 June 2007\r
+\r
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+ Preamble\r
+\r
+ The GNU General Public License is a free, copyleft license for\r
+software and other kinds of works.\r
+\r
+ The licenses for most software and other practical works are designed\r
+to take away your freedom to share and change the works. By contrast,\r
+the GNU General Public License is intended to guarantee your freedom to\r
+share and change all versions of a program--to make sure it remains free\r
+software for all its users. We, the Free Software Foundation, use the\r
+GNU General Public License for most of our software; it applies also to\r
+any other work released this way by its authors. You can apply it to\r
+your programs, too.\r
+\r
+ When we speak of free software, we are referring to freedom, not\r
+price. Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+them if you wish), that you receive source code or can get it if you\r
+want it, that you can change the software or use pieces of it in new\r
+free programs, and that you know you can do these things.\r
+\r
+ To protect your rights, we need to prevent others from denying you\r
+these rights or asking you to surrender the rights. Therefore, you have\r
+certain responsibilities if you distribute copies of the software, or if\r
+you modify it: responsibilities to respect the freedom of others.\r
+\r
+ For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must pass on to the recipients the same\r
+freedoms that you received. You must make sure that they, too, receive\r
+or can get the source code. And you must show them these terms so they\r
+know their rights.\r
+\r
+ Developers that use the GNU GPL protect your rights with two steps:\r
+(1) assert copyright on the software, and (2) offer you this License\r
+giving you legal permission to copy, distribute and/or modify it.\r
+\r
+ For the developers' and authors' protection, the GPL clearly explains\r
+that there is no warranty for this free software. For both users' and\r
+authors' sake, the GPL requires that modified versions be marked as\r
+changed, so that their problems will not be attributed erroneously to\r
+authors of previous versions.\r
+\r
+ Some devices are designed to deny users access to install or run\r
+modified versions of the software inside them, although the manufacturer\r
+can do so. This is fundamentally incompatible with the aim of\r
+protecting users' freedom to change the software. The systematic\r
+pattern of such abuse occurs in the area of products for individuals to\r
+use, which is precisely where it is most unacceptable. Therefore, we\r
+have designed this version of the GPL to prohibit the practice for those\r
+products. If such problems arise substantially in other domains, we\r
+stand ready to extend this provision to those domains in future versions\r
+of the GPL, as needed to protect the freedom of users.\r
+\r
+ Finally, every program is threatened constantly by software patents.\r
+States should not allow patents to restrict development and use of\r
+software on general-purpose computers, but in those that do, we wish to\r
+avoid the special danger that patents applied to a free program could\r
+make it effectively proprietary. To prevent this, the GPL assures that\r
+patents cannot be used to render the program non-free.\r
+\r
+ The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+ TERMS AND CONDITIONS\r
+\r
+ 0. Definitions.\r
+\r
+ "This License" refers to version 3 of the GNU General Public License.\r
+\r
+ "Copyright" also means copyright-like laws that apply to other kinds of\r
+works, such as semiconductor masks.\r
+\r
+ "The Program" refers to any copyrightable work licensed under this\r
+License. Each licensee is addressed as "you". "Licensees" and\r
+"recipients" may be individuals or organizations.\r
+\r
+ To "modify" a work means to copy from or adapt all or part of the work\r
+in a fashion requiring copyright permission, other than the making of an\r
+exact copy. The resulting work is called a "modified version" of the\r
+earlier work or a work "based on" the earlier work.\r
+\r
+ A "covered work" means either the unmodified Program or a work based\r
+on the Program.\r
+\r
+ To "propagate" a work means to do anything with it that, without\r
+permission, would make you directly or secondarily liable for\r
+infringement under applicable copyright law, except executing it on a\r
+computer or modifying a private copy. Propagation includes copying,\r
+distribution (with or without modification), making available to the\r
+public, and in some countries other activities as well.\r
+\r
+ To "convey" a work means any kind of propagation that enables other\r
+parties to make or receive copies. Mere interaction with a user through\r
+a computer network, with no transfer of a copy, is not conveying.\r
+\r
+ An interactive user interface displays "Appropriate Legal Notices"\r
+to the extent that it includes a convenient and prominently visible\r
+feature that (1) displays an appropriate copyright notice, and (2)\r
+tells the user that there is no warranty for the work (except to the\r
+extent that warranties are provided), that licensees may convey the\r
+work under this License, and how to view a copy of this License. If\r
+the interface presents a list of user commands or options, such as a\r
+menu, a prominent item in the list meets this criterion.\r
+\r
+ 1. Source Code.\r
+\r
+ The "source code" for a work means the preferred form of the work\r
+for making modifications to it. "Object code" means any non-source\r
+form of a work.\r
+\r
+ A "Standard Interface" means an interface that either is an official\r
+standard defined by a recognized standards body, or, in the case of\r
+interfaces specified for a particular programming language, one that\r
+is widely used among developers working in that language.\r
+\r
+ The "System Libraries" of an executable work include anything, other\r
+than the work as a whole, that (a) is included in the normal form of\r
+packaging a Major Component, but which is not part of that Major\r
+Component, and (b) serves only to enable use of the work with that\r
+Major Component, or to implement a Standard Interface for which an\r
+implementation is available to the public in source code form. A\r
+"Major Component", in this context, means a major essential component\r
+(kernel, window system, and so on) of the specific operating system\r
+(if any) on which the executable work runs, or a compiler used to\r
+produce the work, or an object code interpreter used to run it.\r
+\r
+ The "Corresponding Source" for a work in object code form means all\r
+the source code needed to generate, install, and (for an executable\r
+work) run the object code and to modify the work, including scripts to\r
+control those activities. However, it does not include the work's\r
+System Libraries, or general-purpose tools or generally available free\r
+programs which are used unmodified in performing those activities but\r
+which are not part of the work. For example, Corresponding Source\r
+includes interface definition files associated with source files for\r
+the work, and the source code for shared libraries and dynamically\r
+linked subprograms that the work is specifically designed to require,\r
+such as by intimate data communication or control flow between those\r
+subprograms and other parts of the work.\r
+\r
+ The Corresponding Source need not include anything that users\r
+can regenerate automatically from other parts of the Corresponding\r
+Source.\r
+\r
+ The Corresponding Source for a work in source code form is that\r
+same work.\r
+\r
+ 2. Basic Permissions.\r
+\r
+ All rights granted under this License are granted for the term of\r
+copyright on the Program, and are irrevocable provided the stated\r
+conditions are met. This License explicitly affirms your unlimited\r
+permission to run the unmodified Program. The output from running a\r
+covered work is covered by this License only if the output, given its\r
+content, constitutes a covered work. This License acknowledges your\r
+rights of fair use or other equivalent, as provided by copyright law.\r
+\r
+ You may make, run and propagate covered works that you do not\r
+convey, without conditions so long as your license otherwise remains\r
+in force. You may convey covered works to others for the sole purpose\r
+of having them make modifications exclusively for you, or provide you\r
+with facilities for running those works, provided that you comply with\r
+the terms of this License in conveying all material for which you do\r
+not control copyright. Those thus making or running the covered works\r
+for you must do so exclusively on your behalf, under your direction\r
+and control, on terms that prohibit them from making any copies of\r
+your copyrighted material outside their relationship with you.\r
+\r
+ Conveying under any other circumstances is permitted solely under\r
+the conditions stated below. Sublicensing is not allowed; section 10\r
+makes it unnecessary.\r
+\r
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\r
+\r
+ No covered work shall be deemed part of an effective technological\r
+measure under any applicable law fulfilling obligations under article\r
+11 of the WIPO copyright treaty adopted on 20 December 1996, or\r
+similar laws prohibiting or restricting circumvention of such\r
+measures.\r
+\r
+ When you convey a covered work, you waive any legal power to forbid\r
+circumvention of technological measures to the extent such circumvention\r
+is effected by exercising rights under this License with respect to\r
+the covered work, and you disclaim any intention to limit operation or\r
+modification of the work as a means of enforcing, against the work's\r
+users, your or third parties' legal rights to forbid circumvention of\r
+technological measures.\r
+\r
+ 4. Conveying Verbatim Copies.\r
+\r
+ You may convey verbatim copies of the Program's source code as you\r
+receive it, in any medium, provided that you conspicuously and\r
+appropriately publish on each copy an appropriate copyright notice;\r
+keep intact all notices stating that this License and any\r
+non-permissive terms added in accord with section 7 apply to the code;\r
+keep intact all notices of the absence of any warranty; and give all\r
+recipients a copy of this License along with the Program.\r
+\r
+ You may charge any price or no price for each copy that you convey,\r
+and you may offer support or warranty protection for a fee.\r
+\r
+ 5. Conveying Modified Source Versions.\r
+\r
+ You may convey a work based on the Program, or the modifications to\r
+produce it from the Program, in the form of source code under the\r
+terms of section 4, provided that you also meet all of these conditions:\r
+\r
+ a) The work must carry prominent notices stating that you modified\r
+ it, and giving a relevant date.\r
+\r
+ b) The work must carry prominent notices stating that it is\r
+ released under this License and any conditions added under section\r
+ 7. This requirement modifies the requirement in section 4 to\r
+ "keep intact all notices".\r
+\r
+ c) You must license the entire work, as a whole, under this\r
+ License to anyone who comes into possession of a copy. This\r
+ License will therefore apply, along with any applicable section 7\r
+ additional terms, to the whole of the work, and all its parts,\r
+ regardless of how they are packaged. This License gives no\r
+ permission to license the work in any other way, but it does not\r
+ invalidate such permission if you have separately received it.\r
+\r
+ d) If the work has interactive user interfaces, each must display\r
+ Appropriate Legal Notices; however, if the Program has interactive\r
+ interfaces that do not display Appropriate Legal Notices, your\r
+ work need not make them do so.\r
+\r
+ A compilation of a covered work with other separate and independent\r
+works, which are not by their nature extensions of the covered work,\r
+and which are not combined with it such as to form a larger program,\r
+in or on a volume of a storage or distribution medium, is called an\r
+"aggregate" if the compilation and its resulting copyright are not\r
+used to limit the access or legal rights of the compilation's users\r
+beyond what the individual works permit. Inclusion of a covered work\r
+in an aggregate does not cause this License to apply to the other\r
+parts of the aggregate.\r
+\r
+ 6. Conveying Non-Source Forms.\r
+\r
+ You may convey a covered work in object code form under the terms\r
+of sections 4 and 5, provided that you also convey the\r
+machine-readable Corresponding Source under the terms of this License,\r
+in one of these ways:\r
+\r
+ a) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by the\r
+ Corresponding Source fixed on a durable physical medium\r
+ customarily used for software interchange.\r
+\r
+ b) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by a\r
+ written offer, valid for at least three years and valid for as\r
+ long as you offer spare parts or customer support for that product\r
+ model, to give anyone who possesses the object code either (1) a\r
+ copy of the Corresponding Source for all the software in the\r
+ product that is covered by this License, on a durable physical\r
+ medium customarily used for software interchange, for a price no\r
+ more than your reasonable cost of physically performing this\r
+ conveying of source, or (2) access to copy the\r
+ Corresponding Source from a network server at no charge.\r
+\r
+ c) Convey individual copies of the object code with a copy of the\r
+ written offer to provide the Corresponding Source. This\r
+ alternative is allowed only occasionally and noncommercially, and\r
+ only if you received the object code with such an offer, in accord\r
+ with subsection 6b.\r
+\r
+ d) Convey the object code by offering access from a designated\r
+ place (gratis or for a charge), and offer equivalent access to the\r
+ Corresponding Source in the same way through the same place at no\r
+ further charge. You need not require recipients to copy the\r
+ Corresponding Source along with the object code. If the place to\r
+ copy the object code is a network server, the Corresponding Source\r
+ may be on a different server (operated by you or a third party)\r
+ that supports equivalent copying facilities, provided you maintain\r
+ clear directions next to the object code saying where to find the\r
+ Corresponding Source. Regardless of what server hosts the\r
+ Corresponding Source, you remain obligated to ensure that it is\r
+ available for as long as needed to satisfy these requirements.\r
+\r
+ e) Convey the object code using peer-to-peer transmission, provided\r
+ you inform other peers where the object code and Corresponding\r
+ Source of the work are being offered to the general public at no\r
+ charge under subsection 6d.\r
+\r
+ A separable portion of the object code, whose source code is excluded\r
+from the Corresponding Source as a System Library, need not be\r
+included in conveying the object code work.\r
+\r
+ A "User Product" is either (1) a "consumer product", which means any\r
+tangible personal property which is normally used for personal, family,\r
+or household purposes, or (2) anything designed or sold for incorporation\r
+into a dwelling. In determining whether a product is a consumer product,\r
+doubtful cases shall be resolved in favor of coverage. For a particular\r
+product received by a particular user, "normally used" refers to a\r
+typical or common use of that class of product, regardless of the status\r
+of the particular user or of the way in which the particular user\r
+actually uses, or expects or is expected to use, the product. A product\r
+is a consumer product regardless of whether the product has substantial\r
+commercial, industrial or non-consumer uses, unless such uses represent\r
+the only significant mode of use of the product.\r
+\r
+ "Installation Information" for a User Product means any methods,\r
+procedures, authorization keys, or other information required to install\r
+and execute modified versions of a covered work in that User Product from\r
+a modified version of its Corresponding Source. The information must\r
+suffice to ensure that the continued functioning of the modified object\r
+code is in no case prevented or interfered with solely because\r
+modification has been made.\r
+\r
+ If you convey an object code work under this section in, or with, or\r
+specifically for use in, a User Product, and the conveying occurs as\r
+part of a transaction in which the right of possession and use of the\r
+User Product is transferred to the recipient in perpetuity or for a\r
+fixed term (regardless of how the transaction is characterized), the\r
+Corresponding Source conveyed under this section must be accompanied\r
+by the Installation Information. But this requirement does not apply\r
+if neither you nor any third party retains the ability to install\r
+modified object code on the User Product (for example, the work has\r
+been installed in ROM).\r
+\r
+ The requirement to provide Installation Information does not include a\r
+requirement to continue to provide support service, warranty, or updates\r
+for a work that has been modified or installed by the recipient, or for\r
+the User Product in which it has been modified or installed. Access to a\r
+network may be denied when the modification itself materially and\r
+adversely affects the operation of the network or violates the rules and\r
+protocols for communication across the network.\r
+\r
+ Corresponding Source conveyed, and Installation Information provided,\r
+in accord with this section must be in a format that is publicly\r
+documented (and with an implementation available to the public in\r
+source code form), and must require no special password or key for\r
+unpacking, reading or copying.\r
+\r
+ 7. Additional Terms.\r
+\r
+ "Additional permissions" are terms that supplement the terms of this\r
+License by making exceptions from one or more of its conditions.\r
+Additional permissions that are applicable to the entire Program shall\r
+be treated as though they were included in this License, to the extent\r
+that they are valid under applicable law. If additional permissions\r
+apply only to part of the Program, that part may be used separately\r
+under those permissions, but the entire Program remains governed by\r
+this License without regard to the additional permissions.\r
+\r
+ When you convey a copy of a covered work, you may at your option\r
+remove any additional permissions from that copy, or from any part of\r
+it. (Additional permissions may be written to require their own\r
+removal in certain cases when you modify the work.) You may place\r
+additional permissions on material, added by you to a covered work,\r
+for which you have or can give appropriate copyright permission.\r
+\r
+ Notwithstanding any other provision of this License, for material you\r
+add to a covered work, you may (if authorized by the copyright holders of\r
+that material) supplement the terms of this License with terms:\r
+\r
+ a) Disclaiming warranty or limiting liability differently from the\r
+ terms of sections 15 and 16 of this License; or\r
+\r
+ b) Requiring preservation of specified reasonable legal notices or\r
+ author attributions in that material or in the Appropriate Legal\r
+ Notices displayed by works containing it; or\r
+\r
+ c) Prohibiting misrepresentation of the origin of that material, or\r
+ requiring that modified versions of such material be marked in\r
+ reasonable ways as different from the original version; or\r
+\r
+ d) Limiting the use for publicity purposes of names of licensors or\r
+ authors of the material; or\r
+\r
+ e) Declining to grant rights under trademark law for use of some\r
+ trade names, trademarks, or service marks; or\r
+\r
+ f) Requiring indemnification of licensors and authors of that\r
+ material by anyone who conveys the material (or modified versions of\r
+ it) with contractual assumptions of liability to the recipient, for\r
+ any liability that these contractual assumptions directly impose on\r
+ those licensors and authors.\r
+\r
+ All other non-permissive additional terms are considered "further\r
+restrictions" within the meaning of section 10. If the Program as you\r
+received it, or any part of it, contains a notice stating that it is\r
+governed by this License along with a term that is a further\r
+restriction, you may remove that term. If a license document contains\r
+a further restriction but permits relicensing or conveying under this\r
+License, you may add to a covered work material governed by the terms\r
+of that license document, provided that the further restriction does\r
+not survive such relicensing or conveying.\r
+\r
+ If you add terms to a covered work in accord with this section, you\r
+must place, in the relevant source files, a statement of the\r
+additional terms that apply to those files, or a notice indicating\r
+where to find the applicable terms.\r
+\r
+ Additional terms, permissive or non-permissive, may be stated in the\r
+form of a separately written license, or stated as exceptions;\r
+the above requirements apply either way.\r
+\r
+ 8. Termination.\r
+\r
+ You may not propagate or modify a covered work except as expressly\r
+provided under this License. Any attempt otherwise to propagate or\r
+modify it is void, and will automatically terminate your rights under\r
+this License (including any patent licenses granted under the third\r
+paragraph of section 11).\r
+\r
+ However, if you cease all violation of this License, then your\r
+license from a particular copyright holder is reinstated (a)\r
+provisionally, unless and until the copyright holder explicitly and\r
+finally terminates your license, and (b) permanently, if the copyright\r
+holder fails to notify you of the violation by some reasonable means\r
+prior to 60 days after the cessation.\r
+\r
+ Moreover, your license from a particular copyright holder is\r
+reinstated permanently if the copyright holder notifies you of the\r
+violation by some reasonable means, this is the first time you have\r
+received notice of violation of this License (for any work) from that\r
+copyright holder, and you cure the violation prior to 30 days after\r
+your receipt of the notice.\r
+\r
+ Termination of your rights under this section does not terminate the\r
+licenses of parties who have received copies or rights from you under\r
+this License. If your rights have been terminated and not permanently\r
+reinstated, you do not qualify to receive new licenses for the same\r
+material under section 10.\r
+\r
+ 9. Acceptance Not Required for Having Copies.\r
+\r
+ You are not required to accept this License in order to receive or\r
+run a copy of the Program. Ancillary propagation of a covered work\r
+occurring solely as a consequence of using peer-to-peer transmission\r
+to receive a copy likewise does not require acceptance. However,\r
+nothing other than this License grants you permission to propagate or\r
+modify any covered work. These actions infringe copyright if you do\r
+not accept this License. Therefore, by modifying or propagating a\r
+covered work, you indicate your acceptance of this License to do so.\r
+\r
+ 10. Automatic Licensing of Downstream Recipients.\r
+\r
+ Each time you convey a covered work, the recipient automatically\r
+receives a license from the original licensors, to run, modify and\r
+propagate that work, subject to this License. You are not responsible\r
+for enforcing compliance by third parties with this License.\r
+\r
+ An "entity transaction" is a transaction transferring control of an\r
+organization, or substantially all assets of one, or subdividing an\r
+organization, or merging organizations. If propagation of a covered\r
+work results from an entity transaction, each party to that\r
+transaction who receives a copy of the work also receives whatever\r
+licenses to the work the party's predecessor in interest had or could\r
+give under the previous paragraph, plus a right to possession of the\r
+Corresponding Source of the work from the predecessor in interest, if\r
+the predecessor has it or can get it with reasonable efforts.\r
+\r
+ You may not impose any further restrictions on the exercise of the\r
+rights granted or affirmed under this License. For example, you may\r
+not impose a license fee, royalty, or other charge for exercise of\r
+rights granted under this License, and you may not initiate litigation\r
+(including a cross-claim or counterclaim in a lawsuit) alleging that\r
+any patent claim is infringed by making, using, selling, offering for\r
+sale, or importing the Program or any portion of it.\r
+\r
+ 11. Patents.\r
+\r
+ A "contributor" is a copyright holder who authorizes use under this\r
+License of the Program or a work on which the Program is based. The\r
+work thus licensed is called the contributor's "contributor version".\r
+\r
+ A contributor's "essential patent claims" are all patent claims\r
+owned or controlled by the contributor, whether already acquired or\r
+hereafter acquired, that would be infringed by some manner, permitted\r
+by this License, of making, using, or selling its contributor version,\r
+but do not include claims that would be infringed only as a\r
+consequence of further modification of the contributor version. For\r
+purposes of this definition, "control" includes the right to grant\r
+patent sublicenses in a manner consistent with the requirements of\r
+this License.\r
+\r
+ Each contributor grants you a non-exclusive, worldwide, royalty-free\r
+patent license under the contributor's essential patent claims, to\r
+make, use, sell, offer for sale, import and otherwise run, modify and\r
+propagate the contents of its contributor version.\r
+\r
+ In the following three paragraphs, a "patent license" is any express\r
+agreement or commitment, however denominated, not to enforce a patent\r
+(such as an express permission to practice a patent or covenant not to\r
+sue for patent infringement). To "grant" such a patent license to a\r
+party means to make such an agreement or commitment not to enforce a\r
+patent against the party.\r
+\r
+ If you convey a covered work, knowingly relying on a patent license,\r
+and the Corresponding Source of the work is not available for anyone\r
+to copy, free of charge and under the terms of this License, through a\r
+publicly available network server or other readily accessible means,\r
+then you must either (1) cause the Corresponding Source to be so\r
+available, or (2) arrange to deprive yourself of the benefit of the\r
+patent license for this particular work, or (3) arrange, in a manner\r
+consistent with the requirements of this License, to extend the patent\r
+license to downstream recipients. "Knowingly relying" means you have\r
+actual knowledge that, but for the patent license, your conveying the\r
+covered work in a country, or your recipient's use of the covered work\r
+in a country, would infringe one or more identifiable patents in that\r
+country that you have reason to believe are valid.\r
+\r
+ If, pursuant to or in connection with a single transaction or\r
+arrangement, you convey, or propagate by procuring conveyance of, a\r
+covered work, and grant a patent license to some of the parties\r
+receiving the covered work authorizing them to use, propagate, modify\r
+or convey a specific copy of the covered work, then the patent license\r
+you grant is automatically extended to all recipients of the covered\r
+work and works based on it.\r
+\r
+ A patent license is "discriminatory" if it does not include within\r
+the scope of its coverage, prohibits the exercise of, or is\r
+conditioned on the non-exercise of one or more of the rights that are\r
+specifically granted under this License. You may not convey a covered\r
+work if you are a party to an arrangement with a third party that is\r
+in the business of distributing software, under which you make payment\r
+to the third party based on the extent of your activity of conveying\r
+the work, and under which the third party grants, to any of the\r
+parties who would receive the covered work from you, a discriminatory\r
+patent license (a) in connection with copies of the covered work\r
+conveyed by you (or copies made from those copies), or (b) primarily\r
+for and in connection with specific products or compilations that\r
+contain the covered work, unless you entered into that arrangement,\r
+or that patent license was granted, prior to 28 March 2007.\r
+\r
+ Nothing in this License shall be construed as excluding or limiting\r
+any implied license or other defenses to infringement that may\r
+otherwise be available to you under applicable patent law.\r
+\r
+ 12. No Surrender of Others' Freedom.\r
+\r
+ If conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License. If you cannot convey a\r
+covered work so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you may\r
+not convey it at all. For example, if you agree to terms that obligate you\r
+to collect a royalty for further conveying from those to whom you convey\r
+the Program, the only way you could satisfy both those terms and this\r
+License would be to refrain entirely from conveying the Program.\r
+\r
+ 13. Use with the GNU Affero General Public License.\r
+\r
+ Notwithstanding any other provision of this License, you have\r
+permission to link or combine any covered work with a work licensed\r
+under version 3 of the GNU Affero General Public License into a single\r
+combined work, and to convey the resulting work. The terms of this\r
+License will continue to apply to the part which is the covered work,\r
+but the special requirements of the GNU Affero General Public License,\r
+section 13, concerning interaction through a network will apply to the\r
+combination as such.\r
+\r
+ 14. Revised Versions of this License.\r
+\r
+ The Free Software Foundation may publish revised and/or new versions of\r
+the GNU General Public License from time to time. Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+ Each version is given a distinguishing version number. If the\r
+Program specifies that a certain numbered version of the GNU General\r
+Public License "or any later version" applies to it, you have the\r
+option of following the terms and conditions either of that numbered\r
+version or of any later version published by the Free Software\r
+Foundation. If the Program does not specify a version number of the\r
+GNU General Public License, you may choose any version ever published\r
+by the Free Software Foundation.\r
+\r
+ If the Program specifies that a proxy can decide which future\r
+versions of the GNU General Public License can be used, that proxy's\r
+public statement of acceptance of a version permanently authorizes you\r
+to choose that version for the Program.\r
+\r
+ Later license versions may give you additional or different\r
+permissions. However, no additional obligations are imposed on any\r
+author or copyright holder as a result of your choosing to follow a\r
+later version.\r
+\r
+ 15. Disclaimer of Warranty.\r
+\r
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\r
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\r
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\r
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\r
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\r
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\r
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r
+\r
+ 16. Limitation of Liability.\r
+\r
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\r
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\r
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\r
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\r
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\r
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\r
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\r
+SUCH DAMAGES.\r
+\r
+ 17. Interpretation of Sections 15 and 16.\r
+\r
+ If the disclaimer of warranty and limitation of liability provided\r
+above cannot be given local legal effect according to their terms,\r
+reviewing courts shall apply local law that most closely approximates\r
+an absolute waiver of all civil liability in connection with the\r
+Program, unless a warranty or assumption of liability accompanies a\r
+copy of the Program in return for a fee.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ How to Apply These Terms to Your New Programs\r
+\r
+ If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+ To do so, attach the following notices to the program. It is safest\r
+to attach them to the start of each source file to most effectively\r
+state the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+ <one line to give the program's name and a brief idea of what it does.>\r
+ Copyright (C) <year> <name of author>\r
+\r
+ This program is free software: you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation, either version 3 of the License, or\r
+ (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+ If the program does terminal interaction, make it output a short\r
+notice like this when it starts in an interactive mode:\r
+\r
+ <program> Copyright (C) <year> <name of author>\r
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+ This is free software, and you are welcome to redistribute it\r
+ under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License. Of course, your program's commands\r
+might be different; for a GUI interface, you would use an "about box".\r
+\r
+ You should also get your employer (if you work as a programmer) or school,\r
+if any, to sign a "copyright disclaimer" for the program, if necessary.\r
+For more information on this, and how to apply and follow the GNU GPL, see\r
+<http://www.gnu.org/licenses/>.\r
+\r
+ The GNU General Public License does not permit incorporating your program\r
+into proprietary programs. If your program is a subroutine library, you\r
+may consider it more useful to permit linking proprietary applications with\r
+the library. If this is what you want to do, use the GNU Lesser General\r
+Public License instead of this License. But first, please read\r
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.\r
--- /dev/null
+### Overview
+
+[](https://travis-ci.org/official-stockfish/Stockfish)
+[](https://ci.appveyor.com/project/mcostalba/stockfish)
+
+Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
+not a complete chess program and requires some UCI-compatible GUI
+(e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess
+Partner or Fritz) in order to be used comfortably. Read the
+documentation for your GUI of choice for information about how to use
+Stockfish with it.
+
+This version of Stockfish supports up to 512 cores. The engine defaults
+to one search thread, so it is therefore recommended to inspect the value of
+the *Threads* UCI parameter, and to make sure it equals the number of CPU
+cores on your computer.
+
+This version of Stockfish has support for Syzygybases.
+
+
+### Files
+
+This distribution of Stockfish consists of the following files:
+
+ * Readme.md, 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 Stockfish on Unix-like systems.
+
+
+### Syzygybases
+
+**Configuration**
+
+Syzygybases are configured using the UCI options "SyzygyPath",
+"SyzygyProbeDepth", "Syzygy50MoveRule" and "SyzygyProbeLimit".
+
+The option "SyzygyPath" should be set to the directory or directories that
+contain the .rtbw and .rtbz files. Multiple directories should be
+separated by ";" on Windows and by ":" on Unix-based operating systems.
+**Do not use spaces around the ";" or ":".**
+
+Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
+
+It is recommended to store .rtbw files on an SSD. There is no loss in
+storing the .rtbz files on a regular HD.
+
+Increasing the "SyzygyProbeDepth" option lets the engine probe less
+aggressively. Set this option to a higher value if you experience too much
+slowdown (in terms of nps) due to TB probing.
+
+Set the "Syzygy50MoveRule" option to false if you want tablebase positions
+that are drawn by the 50-move rule to count as win or loss. This may be useful
+for correspondence games (because of tablebase adjudication).
+
+The "SyzygyProbeLimit" option should normally be left at its default value.
+
+**What to expect**
+If the engine is searching a position that is not in the tablebases (e.g.
+a position with 7 pieces), it will access the tablebases during the search.
+If the engine reports a very large score (typically 123.xx), this means
+that it has found a winning line into a tablebase position.
+
+If the engine is given a position to search that is in the tablebases, it
+will use the tablebases at the beginning of the search to preselect all
+good moves, i.e. all moves that preserve the win or preserve the draw while
+taking into account the 50-move rule.
+It will then perform a search only on those moves. **The engine will not move
+immediately**, unless there is only a single good move. **The engine likely
+will not report a mate score even if the position is known to be won.**
+
+It is therefore clear that behaviour is not identical to what one might
+be used to with Nalimov tablebases. There are technical reasons for this
+difference, the main technical reason being that Nalimov tablebases use the
+DTM metric (distance-to-mate), while Syzygybases use a variation of the
+DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
+counter). This special metric is one of the reasons that Syzygybases are
+more compact than Nalimov tablebases, while still storing all information
+needed for optimal play and in addition being able to take into account
+the 50-move rule.
+
+
+### Compiling it yourself
+
+On Unix-like systems, it should be possible to compile Stockfish
+directly from the source code with the included Makefile.
+
+Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
+instruction, big-endian machines such as Power PC, and other platforms.
+
+In general it is recommended to run `make help` to see a list of make
+targets with corresponding descriptions. When not using the Makefile to
+compile (for instance with Microsoft MSVC) you need to manually
+set/unset some switches in the compiler command line; see file *types.h*
+for a quick reference.
+
+### Resource For Understanding the Code Base
+
+* [Chessprogramingwiki](https://chessprogramming.wikispaces.com) has good overall chess engines explanations
+(techniques used here are well explained like hash maps etc), it was
+also recommended by the [support at stockfish.](http://support.stockfishchess.org/discussions/questions/1132-how-to-understand-stockfish-sources)
+
+* [Here](https://chessprogramming.wikispaces.com/Stockfish) you can find a set of features and techniques used by stockfish and each of them is explained at the wiki, however, it's a generic way rather than focusing on stockfish's own implementation, but it will still help you.
+
+
+### Terms of use
+
+Stockfish 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 Stockfish 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*.
--- /dev/null
+Contributors with >10,000 CPU hours as of January 23, 2018
+Thank you!
+
+Username CPU Hours Games played
+mibere 518300 41835669
+crunchy 375564 29121434
+cw 371664 28748719
+fastgm 299773 20765374
+JojoM 220590 15299913
+glinscott 204517 13932027
+bking_US 187568 12233168
+ctoks 169342 13475495
+spams 149531 10940322
+Thanar 137015 11714855
+velislav 127305 10047749
+vdbergh 121741 9056874
+malala 117291 8126488
+vdv 117218 8289983
+leszek 114825 8331897
+dsmith 114010 7622414
+CSU_Dynasty 113516 9582758
+sqrt2 112407 8782694
+marrco 111143 8222921
+drabel 108168 9061580
+BrunoBanani 104938 7448565
+Data 94621 8433010
+CoffeeOne 90394 3964243
+BRAVONE 80811 5341681
+psk 77195 6156031
+brabos 70284 5685893
+Fisherman 66650 5572406
+nssy 64587 5369140
+Pking_cda 64499 5704075
+sterni1971 63488 5070004
+mgrabiak 62385 5420812
+tvijlbrief 58957 4154234
+jromang 58854 4704502
+dv8silencer 57421 3961325
+sunu 56620 4609155
+tinker 56039 4204914
+biffhero 55743 4810039
+teddybaer 52982 4740444
+bcross 50548 5071599
+renouve 50318 3544864
+Freja 50296 3805120
+robnjr 47504 4131742
+eva42 46542 4044694
+davar 46538 4030604
+finfish 46244 3481661
+rap 46201 3219490
+ttruscott 45037 3645430
+solarlight 44155 4074841
+TueRens 41372 3891510
+ElbertoOne 41321 3920894
+Antihistamine 39218 2792761
+mhunt 38991 2697512
+bigpen0r 37820 3149955
+homyur 35569 3009637
+VoyagerOne 35137 3302650
+mhoram 34770 2684128
+racerschmacer 33022 3231055
+speedycpu 32043 2531964
+EthanOConnor 31638 2143255
+oryx 29574 2767730
+Pyafue 28885 1986098
+jkiiski 28014 1923255
+Garf 27579 2770144
+slakovv 27017 2031279
+Bobo1239 27000 2488707
+pb00067 26817 2306694
+robal 26337 2316795
+hyperbolic.tom 26248 2200777
+rkl 24898 2236013
+SC 23988 2126825
+nabildanial 23524 1586321
+achambord 23495 1942546
+Sharaf_DG 22975 1790697
+chriswk 22876 1947731
+anst 22568 2013953
+Patrick_G 22435 1682293
+cuistot 22201 1383031
+gri 21901 1820968
+Prcuvu 21182 1890546
+Zirie 21171 1493227
+JanErik 20596 1791991
+Isidor 20560 1730290
+xor12 20535 1819280
+team-oh 20364 1653708
+nesoneg 20264 1493435
+rstoesser 19802 1335177
+grandphish2 19402 1834196
+sg4032 18427 1671742
+dew 18263 1423326
+ianh2105 18133 1668562
+MazeOfGalious 18022 1644593
+ville 17900 1539130
+j3corre 17607 975954
+eudhan 17502 1424648
+iisiraider 17175 1118788
+jundery 17172 1115855
+SFTUser 16635 1363975
+purplefishies 16621 1106850
+DragonLord 16599 1252348
+chris 15274 1575333
+xoto 14900 1486261
+dju 14861 901552
+dex 14647 1228763
+nordlandia 14551 1369718
+ronaldjerum 14361 1210607
+OssumOpossum 14149 1029265
+IgorLeMasson 13844 1228391
+enedene 13762 935618
+ako027ako 13442 1250249
+AdrianSA 13324 924980
+bpfliegel 13318 886523
+ncfish1 13056 932344
+wei 12863 1369596
+jpulman 12776 854815
+horst.prack 12436 1151505
+joster 12424 986622
+cisco2015 12265 1205019
+fatmurphy 12015 901134
+modolief 11228 926456
+Dark_wizzie 11214 1017910
+mschmidt 10973 818594
+eastorwest 10970 1117836
+infinity 10762 746397
+SapphireBrand 10692 1024604
+Thomas A. 10553 736094
+pgontarz 10294 878746
+Andrew Grant 10195 922933
+stocky 10083 718114
--- /dev/null
+version: 1.0.{build}
+clone_depth: 50
+
+branches:
+ only:
+ - master
+ - appveyor
+
+# Operating system (build VM template)
+os: Visual Studio 2015
+
+# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
+platform:
+ - x86
+ - x64
+
+# build Configuration, i.e. Debug, Release, etc.
+configuration:
+ - Debug
+ - Release
+
+matrix:
+ # The build fail immediately once one of the job fails
+ fast_finish: true
+
+# Scripts that are called at very beginning, before repo cloning
+init:
+ - cmake --version
+ - msbuild /version
+
+before_build:
+ - ps: |
+ # Get sources
+ $src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName
+ $src = $src -join ' '
+ $src = $src.Replace("\", "/")
+
+ # Build CMakeLists.txt
+ $t = 'cmake_minimum_required(VERSION 3.8)',
+ 'project(Stockfish)',
+ 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)',
+ 'set(source_files', $src, ')',
+ 'add_executable(stockfish ${source_files})'
+
+ # Write CMakeLists.txt withouth BOM
+ $MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt'
+ $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
+ [System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding)
+
+ # Obtain bench reference from git log
+ $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
+ $bench = $b -match '\D+(\d+)' | % { $matches[1] }
+ Write-Host "Reference bench:" $bench
+ $g = "Visual Studio 14 2015"
+ If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' }
+ cmake -G "${g}" .
+ Write-Host "Generated files for: " $g
+
+build_script:
+ - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal
+
+before_test:
+ - cd src/%CONFIGURATION%
+ - ps: |
+ # Verify bench number
+ ./stockfish bench 2> out.txt 1> null
+ $s = (gc "./out.txt" | out-string)
+ $r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] })
+ Write-Host "Engine bench:" $r
+ Write-Host "Reference bench:" $bench
+ If ($r -ne $bench) { exit 1 }
--- /dev/null
+# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+# Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+# Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+# Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+#
+# Stockfish 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.
+#
+# Stockfish 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 <http://www.gnu.org/licenses/>.
+
+
+### ==========================================================================
+### Section 1. General Configuration
+### ==========================================================================
+
+### Establish the operating system name
+KERNEL = $(shell uname -s)
+ifeq ($(KERNEL),Linux)
+ OS = $(shell uname -o)
+endif
+
+### Executable name
+EXE = stockfish
+
+### Installation dir definitions
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+
+### Built-in benchmark for pgo-builds
+PGOBENCH = ./$(EXE) bench
+
+### Object files
+OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
+ material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \
+ search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o
+
+### ==========================================================================
+### Section 2. High-level Configuration
+### ==========================================================================
+#
+# flag --- Comp switch --- Description
+# ----------------------------------------------------------------------------
+#
+# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
+# sanitize = undefined/thread/no (-fsanitize )
+# --- ( undefined ) --- enable undefined behavior checks
+# --- ( thread ) --- enable threading error checks
+# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
+# arch = (name) --- (-arch) --- Target architecture
+# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
+# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
+# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
+# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
+# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
+#
+# Note that Makefile is space sensitive, so when adding new architectures
+# or modifying existing flags, you have to make sure there are no extra spaces
+# at the end of the line for flag values.
+
+### 2.1. General and architecture defaults
+optimize = yes
+debug = no
+sanitize = no
+bits = 32
+prefetch = no
+popcnt = no
+sse = no
+pext = no
+
+### 2.2 Architecture specific
+
+ifeq ($(ARCH),general-32)
+ arch = any
+endif
+
+ifeq ($(ARCH),x86-32-old)
+ arch = i386
+endif
+
+ifeq ($(ARCH),x86-32)
+ arch = i386
+ prefetch = yes
+ sse = yes
+endif
+
+ifeq ($(ARCH),general-64)
+ arch = any
+ bits = 64
+endif
+
+ifeq ($(ARCH),x86-64)
+ arch = x86_64
+ bits = 64
+ prefetch = yes
+ sse = yes
+endif
+
+ifeq ($(ARCH),x86-64-modern)
+ arch = x86_64
+ bits = 64
+ prefetch = yes
+ popcnt = yes
+ sse = yes
+endif
+
+ifeq ($(ARCH),x86-64-bmi2)
+ arch = x86_64
+ bits = 64
+ prefetch = yes
+ popcnt = yes
+ sse = yes
+ pext = yes
+endif
+
+ifeq ($(ARCH),armv7)
+ arch = armv7
+ prefetch = yes
+endif
+
+ifeq ($(ARCH),ppc-32)
+ arch = ppc
+endif
+
+ifeq ($(ARCH),ppc-64)
+ arch = ppc64
+ bits = 64
+endif
+
+
+### ==========================================================================
+### Section 3. Low-level configuration
+### ==========================================================================
+
+### 3.1 Selecting compiler (default = gcc)
+
+CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++11 $(EXTRACXXFLAGS)
+DEPENDFLAGS += -std=c++11
+LDFLAGS += $(EXTRALDFLAGS)
+
+ifeq ($(COMP),)
+ COMP=gcc
+endif
+
+ifeq ($(COMP),gcc)
+ comp=gcc
+ CXX=g++
+ CXXFLAGS += -pedantic -Wextra -Wshadow
+
+ ifeq ($(ARCH),armv7)
+ ifeq ($(OS),Android)
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+ else
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+
+ ifneq ($(KERNEL),Darwin)
+ LDFLAGS += -Wl,--no-as-needed
+ endif
+endif
+
+ifeq ($(COMP),mingw)
+ comp=mingw
+
+ ifeq ($(KERNEL),Linux)
+ ifeq ($(bits),64)
+ ifeq ($(shell which x86_64-w64-mingw32-c++-posix),)
+ CXX=x86_64-w64-mingw32-c++
+ else
+ CXX=x86_64-w64-mingw32-c++-posix
+ endif
+ else
+ ifeq ($(shell which i686-w64-mingw32-c++-posix),)
+ CXX=i686-w64-mingw32-c++
+ else
+ CXX=i686-w64-mingw32-c++-posix
+ endif
+ endif
+ else
+ CXX=g++
+ endif
+
+ CXXFLAGS += -Wextra -Wshadow
+ LDFLAGS += -static
+endif
+
+ifeq ($(COMP),icc)
+ comp=icc
+ CXX=icpc
+ CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi
+endif
+
+ifeq ($(COMP),clang)
+ comp=clang
+ CXX=clang++
+ CXXFLAGS += -pedantic -Wextra -Wshadow
+ifneq ($(KERNEL),Darwin)
+ifneq ($(KERNEL),OpenBSD)
+ LDFLAGS += -latomic
+endif
+endif
+
+ ifeq ($(ARCH),armv7)
+ ifeq ($(OS),Android)
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+ else
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+endif
+
+ifeq ($(comp),icc)
+ profile_make = icc-profile-make
+ profile_use = icc-profile-use
+else
+ifeq ($(comp),clang)
+ profile_make = clang-profile-make
+ profile_use = clang-profile-use
+else
+ profile_make = gcc-profile-make
+ profile_use = gcc-profile-use
+endif
+endif
+
+ifeq ($(KERNEL),Darwin)
+ CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9
+ LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9
+endif
+
+### Travis CI script uses COMPILER to overwrite CXX
+ifdef COMPILER
+ COMPCXX=$(COMPILER)
+endif
+
+### Allow overwriting CXX from command line
+ifdef COMPCXX
+ CXX=$(COMPCXX)
+endif
+
+### On mingw use Windows threads, otherwise POSIX
+ifneq ($(comp),mingw)
+ # On Android Bionic's C library comes with its own pthread implementation bundled in
+ ifneq ($(OS),Android)
+ # Haiku has pthreads in its libroot, so only link it in on other platforms
+ ifneq ($(KERNEL),Haiku)
+ LDFLAGS += -lpthread
+ endif
+ endif
+endif
+
+### 3.2.1 Debugging
+ifeq ($(debug),no)
+ CXXFLAGS += -DNDEBUG
+else
+ CXXFLAGS += -g
+endif
+
+### 3.2.2 Debugging with undefined behavior sanitizers
+ifneq ($(sanitize),no)
+ CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold
+ LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold
+endif
+
+### 3.3 Optimization
+ifeq ($(optimize),yes)
+
+ CXXFLAGS += -O3
+
+ ifeq ($(comp),gcc)
+
+ ifeq ($(KERNEL),Darwin)
+ ifeq ($(arch),i386)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ ifeq ($(arch),x86_64)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ endif
+
+ ifeq ($(OS), Android)
+ CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
+ endif
+ endif
+
+ ifeq ($(comp),icc)
+ ifeq ($(KERNEL),Darwin)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ endif
+
+ ifeq ($(comp),clang)
+ ifeq ($(KERNEL),Darwin)
+ CXXFLAGS += -flto
+ LDFLAGS += $(CXXFLAGS)
+ ifeq ($(arch),i386)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ ifeq ($(arch),x86_64)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ endif
+ endif
+endif
+
+### 3.4 Bits
+ifeq ($(bits),64)
+ CXXFLAGS += -DIS_64BIT
+endif
+
+### 3.5 prefetch
+ifeq ($(prefetch),yes)
+ ifeq ($(sse),yes)
+ CXXFLAGS += -msse
+ DEPENDFLAGS += -msse
+ endif
+else
+ CXXFLAGS += -DNO_PREFETCH
+endif
+
+### 3.6 popcnt
+ifeq ($(popcnt),yes)
+ ifeq ($(comp),icc)
+ CXXFLAGS += -msse3 -DUSE_POPCNT
+ else
+ CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
+ endif
+endif
+
+### 3.7 pext
+ifeq ($(pext),yes)
+ CXXFLAGS += -DUSE_PEXT
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
+ CXXFLAGS += -mbmi2
+ endif
+endif
+
+### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows.
+### This is a mix of compile and link time options because the lto link phase
+### needs access to the optimization flags.
+ifeq ($(comp),gcc)
+ ifeq ($(optimize),yes)
+ ifeq ($(debug),no)
+ CXXFLAGS += -flto
+ LDFLAGS += $(CXXFLAGS)
+ endif
+ endif
+endif
+
+ifeq ($(comp),mingw)
+ ifeq ($(KERNEL),Linux)
+ ifeq ($(optimize),yes)
+ ifeq ($(debug),no)
+ CXXFLAGS += -flto
+ LDFLAGS += $(CXXFLAGS)
+ endif
+ endif
+ endif
+endif
+
+### 3.9 Android 5 can only run position independent executables. Note that this
+### breaks Android 4.0 and earlier.
+ifeq ($(OS), Android)
+ CXXFLAGS += -fPIE
+ LDFLAGS += -fPIE -pie
+endif
+
+
+### ==========================================================================
+### Section 4. Public targets
+### ==========================================================================
+
+help:
+ @echo ""
+ @echo "To compile stockfish, type: "
+ @echo ""
+ @echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]"
+ @echo ""
+ @echo "Supported targets:"
+ @echo ""
+ @echo "build > Standard build"
+ @echo "profile-build > PGO build"
+ @echo "strip > Strip executable"
+ @echo "install > Install executable"
+ @echo "clean > Clean up"
+ @echo ""
+ @echo "Supported archs:"
+ @echo ""
+ @echo "x86-64 > x86 64-bit"
+ @echo "x86-64-modern > x86 64-bit with popcnt support"
+ @echo "x86-64-bmi2 > x86 64-bit with pext support"
+ @echo "x86-32 > x86 32-bit with SSE support"
+ @echo "x86-32-old > x86 32-bit fall back for old hardware"
+ @echo "ppc-64 > PPC 64-bit"
+ @echo "ppc-32 > PPC 32-bit"
+ @echo "armv7 > ARMv7 32-bit"
+ @echo "general-64 > unspecified 64-bit"
+ @echo "general-32 > unspecified 32-bit"
+ @echo ""
+ @echo "Supported compilers:"
+ @echo ""
+ @echo "gcc > Gnu compiler (default)"
+ @echo "mingw > Gnu compiler with MinGW under Windows"
+ @echo "clang > LLVM Clang compiler"
+ @echo "icc > Intel compiler"
+ @echo ""
+ @echo "Simple examples. If you don't know what to do, you likely want to run: "
+ @echo ""
+ @echo "make build ARCH=x86-64 (This is for 64-bit systems)"
+ @echo "make build ARCH=x86-32 (This is for 32-bit systems)"
+ @echo ""
+ @echo "Advanced examples, for experienced users: "
+ @echo ""
+ @echo "make build ARCH=x86-64 COMP=clang"
+ @echo "make profile-build ARCH=x86-64-modern COMP=gcc COMPCXX=g++-4.8"
+ @echo ""
+
+
+.PHONY: help build profile-build strip install clean objclean profileclean help \
+ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
+ clang-profile-use clang-profile-make
+
+build: config-sanity
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
+
+profile-build: config-sanity objclean profileclean
+ @echo ""
+ @echo "Step 1/4. Building instrumented executable ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
+ @echo ""
+ @echo "Step 2/4. Running benchmark for pgo-build ..."
+ $(PGOBENCH) > /dev/null
+ @echo ""
+ @echo "Step 3/4. Building optimized executable ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
+ @echo ""
+ @echo "Step 4/4. Deleting profile data ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean
+
+strip:
+ strip $(EXE)
+
+install:
+ -mkdir -p -m 755 $(BINDIR)
+ -cp $(EXE) $(BINDIR)
+ -strip $(BINDIR)/$(EXE)
+
+#clean all
+clean: objclean profileclean
+ @rm -f .depend *~ core
+
+# clean binaries and objects
+objclean:
+ @rm -f $(EXE) $(EXE).exe *.o ./syzygy/*.o
+
+# clean auxiliary profiling files
+profileclean:
+ @rm -rf profdir
+ @rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno
+ @rm -f stockfish.profdata *.profraw
+
+default:
+ help
+
+### ==========================================================================
+### Section 5. Private targets
+### ==========================================================================
+
+all: $(EXE) .depend
+
+config-sanity:
+ @echo ""
+ @echo "Config:"
+ @echo "debug: '$(debug)'"
+ @echo "sanitize: '$(sanitize)'"
+ @echo "optimize: '$(optimize)'"
+ @echo "arch: '$(arch)'"
+ @echo "bits: '$(bits)'"
+ @echo "kernel: '$(KERNEL)'"
+ @echo "os: '$(OS)'"
+ @echo "prefetch: '$(prefetch)'"
+ @echo "popcnt: '$(popcnt)'"
+ @echo "sse: '$(sse)'"
+ @echo "pext: '$(pext)'"
+ @echo ""
+ @echo "Flags:"
+ @echo "CXX: $(CXX)"
+ @echo "CXXFLAGS: $(CXXFLAGS)"
+ @echo "LDFLAGS: $(LDFLAGS)"
+ @echo ""
+ @echo "Testing config sanity. If this fails, try 'make help' ..."
+ @echo ""
+ @test "$(debug)" = "yes" || test "$(debug)" = "no"
+ @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "no"
+ @test "$(optimize)" = "yes" || test "$(optimize)" = "no"
+ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
+ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
+ @test "$(bits)" = "32" || test "$(bits)" = "64"
+ @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
+ @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
+ @test "$(sse)" = "yes" || test "$(sse)" = "no"
+ @test "$(pext)" = "yes" || test "$(pext)" = "no"
+ @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
+
+$(EXE): $(OBJS)
+ $(CXX) -o $@ $(OBJS) $(LDFLAGS)
+
+clang-profile-make:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-instr-generate ' \
+ EXTRALDFLAGS=' -fprofile-instr-generate' \
+ all
+
+clang-profile-use:
+ llvm-profdata merge -output=stockfish.profdata *.profraw
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
+ EXTRALDFLAGS='-fprofile-use ' \
+ all
+
+gcc-profile-make:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-generate' \
+ EXTRALDFLAGS='-lgcov' \
+ all
+
+gcc-profile-use:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \
+ EXTRALDFLAGS='-lgcov' \
+ all
+
+icc-profile-make:
+ @mkdir -p profdir
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
+ all
+
+icc-profile-use:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
+ all
+
+.depend:
+ -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null
+
+-include .depend
+
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <fstream>
+#include <iostream>
+#include <istream>
+#include <vector>
+
+#include "position.h"
+
+using namespace std;
+
+namespace {
+
+const vector<string> Defaults = {
+ "setoption name UCI_Chess960 value false",
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
+ "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
+ "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
+ "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
+ "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
+ "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
+ "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",
+ "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
+ "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
+ "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
+ "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
+ "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
+ "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
+ "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
+ "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
+ "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
+ "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
+ "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
+ "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
+ "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
+ "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
+
+ // 5-man positions
+ "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
+ "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
+ "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
+
+ // 6-man positions
+ "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
+ "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
+ "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
+
+ // 7-man positions
+ "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
+
+ // Mate and stalemate positions
+ "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
+ "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
+ "8/8/8/8/8/6k1/6p1/6K1 w - -",
+ "7k/7P/6K1/8/3B4/8/8/8 b - -",
+
+ // Chess 960
+ "setoption name UCI_Chess960 value true",
+ "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
+ "setoption name UCI_Chess960 value false"
+};
+
+} // namespace
+
+/// setup_bench() builds a list of UCI commands to be run by bench. There
+/// are five parameters: TT size in MB, number of search threads that
+/// should be used, the limit value spent for each position, a file name
+/// where to look for positions in FEN format and the type of the limit:
+/// depth, perft, nodes and movetime (in millisecs).
+///
+/// bench -> search default positions up to depth 13
+/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
+/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
+/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
+/// bench 16 1 5 default perft -> run a perft 5 on default positions
+
+vector<string> setup_bench(const Position& current, istream& is) {
+
+ vector<string> fens, list;
+ string go, token;
+
+ // Assign default values to missing arguments
+ string ttSize = (is >> token) ? token : "16";
+ string threads = (is >> token) ? token : "1";
+ string limit = (is >> token) ? token : "13";
+ string fenFile = (is >> token) ? token : "default";
+ string limitType = (is >> token) ? token : "depth";
+
+ go = "go " + limitType + " " + limit;
+
+ if (fenFile == "default")
+ fens = Defaults;
+
+ else if (fenFile == "current")
+ fens.push_back(current.fen());
+
+ else
+ {
+ string fen;
+ ifstream file(fenFile);
+
+ if (!file.is_open())
+ {
+ cerr << "Unable to open file " << fenFile << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ while (getline(file, fen))
+ if (!fen.empty())
+ fens.push_back(fen);
+
+ file.close();
+ }
+
+ list.emplace_back("ucinewgame");
+ list.emplace_back("setoption name Threads value " + threads);
+ list.emplace_back("setoption name Hash value " + ttSize);
+
+ for (const string& fen : fens)
+ if (fen.find("setoption") != string::npos)
+ list.emplace_back(fen);
+ else
+ {
+ list.emplace_back("position fen " + fen);
+ list.emplace_back(go);
+ }
+
+ return list;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+#include <vector>
+
+#include "bitboard.h"
+#include "types.h"
+
+namespace {
+
+ // There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7
+ const unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
+
+ // Each uint32_t stores results of 32 positions, one per bit
+ uint32_t KPKBitbase[MAX_INDEX / 32];
+
+ // A KPK bitbase index is an integer in [0, IndexMax] range
+ //
+ // Information is mapped in a way that minimizes the number of iterations:
+ //
+ // bit 0- 5: white king square (from SQ_A1 to SQ_H8)
+ // bit 6-11: black king square (from SQ_A1 to SQ_H8)
+ // bit 12: side to move (WHITE or BLACK)
+ // bit 13-14: white pawn file (from FILE_A to FILE_D)
+ // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
+ unsigned index(Color us, Square bksq, Square wksq, Square psq) {
+ return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
+ }
+
+ enum Result {
+ INVALID = 0,
+ UNKNOWN = 1,
+ DRAW = 2,
+ WIN = 4
+ };
+
+ Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
+
+ struct KPKPosition {
+ KPKPosition() = default;
+ explicit KPKPosition(unsigned idx);
+ operator Result() const { return result; }
+ Result classify(const std::vector<KPKPosition>& db)
+ { return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
+
+ template<Color Us> Result classify(const std::vector<KPKPosition>& db);
+
+ Color us;
+ Square ksq[COLOR_NB], psq;
+ Result result;
+ };
+
+} // namespace
+
+
+bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) {
+
+ assert(file_of(wpsq) <= FILE_D);
+
+ unsigned idx = index(us, bksq, wksq, wpsq);
+ return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
+}
+
+
+void Bitbases::init() {
+
+ std::vector<KPKPosition> db(MAX_INDEX);
+ unsigned idx, repeat = 1;
+
+ // Initialize db with known win / draw positions
+ for (idx = 0; idx < MAX_INDEX; ++idx)
+ db[idx] = KPKPosition(idx);
+
+ // Iterate through the positions until none of the unknown positions can be
+ // changed to either wins or draws (15 cycles needed).
+ while (repeat)
+ for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
+ repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
+
+ // Map 32 results into one KPKBitbase[] entry
+ for (idx = 0; idx < MAX_INDEX; ++idx)
+ if (db[idx] == WIN)
+ KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
+}
+
+
+namespace {
+
+ KPKPosition::KPKPosition(unsigned idx) {
+
+ ksq[WHITE] = Square((idx >> 0) & 0x3F);
+ ksq[BLACK] = Square((idx >> 6) & 0x3F);
+ us = Color ((idx >> 12) & 0x01);
+ psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
+
+ // Check if two pieces are on the same square or if a king can be captured
+ if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
+ || ksq[WHITE] == psq
+ || ksq[BLACK] == psq
+ || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK])))
+ result = INVALID;
+
+ // Immediate win if a pawn can be promoted without getting captured
+ else if ( us == WHITE
+ && rank_of(psq) == RANK_7
+ && ksq[us] != psq + NORTH
+ && ( distance(ksq[~us], psq + NORTH) > 1
+ || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH))))
+ result = WIN;
+
+ // Immediate draw if it is a stalemate or a king captures undefended pawn
+ else if ( us == BLACK
+ && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq]))
+ || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]])))
+ result = DRAW;
+
+ // Position will be classified later
+ else
+ result = UNKNOWN;
+ }
+
+ template<Color Us>
+ Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
+
+ // White to move: If one move leads to a position classified as WIN, the result
+ // of the current position is WIN. If all moves lead to positions classified
+ // as DRAW, the current position is classified as DRAW, otherwise the current
+ // position is classified as UNKNOWN.
+ //
+ // Black to move: If one move leads to a position classified as DRAW, the result
+ // of the current position is DRAW. If all moves lead to positions classified
+ // as WIN, the position is classified as WIN, otherwise the current position is
+ // classified as UNKNOWN.
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Result Good = (Us == WHITE ? WIN : DRAW);
+ const Result Bad = (Us == WHITE ? DRAW : WIN);
+
+ Result r = INVALID;
+ Bitboard b = PseudoAttacks[KING][ksq[Us]];
+
+ while (b)
+ r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]
+ : db[index(Them, pop_lsb(&b), ksq[Them] , psq)];
+
+ if (Us == WHITE)
+ {
+ if (rank_of(psq) < RANK_7) // Single push
+ r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)];
+
+ if ( rank_of(psq) == RANK_2 // Double push
+ && psq + NORTH != ksq[Us]
+ && psq + NORTH != ksq[Them])
+ r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)];
+ }
+
+ return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
+ }
+
+} // namespace
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+
+#include "bitboard.h"
+#include "misc.h"
+
+uint8_t PopCnt16[1 << 16];
+int SquareDistance[SQUARE_NB][SQUARE_NB];
+
+Bitboard SquareBB[SQUARE_NB];
+Bitboard FileBB[FILE_NB];
+Bitboard RankBB[RANK_NB];
+Bitboard AdjacentFilesBB[FILE_NB];
+Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
+Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
+Bitboard LineBB[SQUARE_NB][SQUARE_NB];
+Bitboard DistanceRingBB[SQUARE_NB][8];
+Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
+Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
+Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
+Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
+Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
+
+Magic RookMagics[SQUARE_NB];
+Magic BishopMagics[SQUARE_NB];
+
+namespace {
+
+ // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
+ const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL;
+ const uint32_t DeBruijn32 = 0x783A9B23;
+
+ int MSBTable[256]; // To implement software msb()
+ Square BSFTable[SQUARE_NB]; // To implement software bitscan
+ Bitboard RookTable[0x19000]; // To store rook attacks
+ Bitboard BishopTable[0x1480]; // To store bishop attacks
+
+ void init_magics(Bitboard table[], Magic magics[], Direction directions[]);
+
+ // bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
+ // Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
+
+ unsigned bsf_index(Bitboard b) {
+ b ^= b - 1;
+ return Is64Bit ? (b * DeBruijn64) >> 58
+ : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
+ }
+
+
+ // popcount16() counts the non-zero bits using SWAR-Popcount algorithm
+
+ unsigned popcount16(unsigned u) {
+ u -= (u >> 1) & 0x5555U;
+ u = ((u >> 2) & 0x3333U) + (u & 0x3333U);
+ u = ((u >> 4) + u) & 0x0F0FU;
+ return (u * 0x0101U) >> 8;
+ }
+}
+
+#ifdef NO_BSF
+
+/// Software fall-back of lsb() and msb() for CPU lacking hardware support
+
+Square lsb(Bitboard b) {
+ assert(b);
+ return BSFTable[bsf_index(b)];
+}
+
+Square msb(Bitboard b) {
+
+ assert(b);
+ unsigned b32;
+ int result = 0;
+
+ if (b > 0xFFFFFFFF)
+ {
+ b >>= 32;
+ result = 32;
+ }
+
+ b32 = unsigned(b);
+
+ if (b32 > 0xFFFF)
+ {
+ b32 >>= 16;
+ result += 16;
+ }
+
+ if (b32 > 0xFF)
+ {
+ b32 >>= 8;
+ result += 8;
+ }
+
+ return Square(result + MSBTable[b32]);
+}
+
+#endif // ifdef NO_BSF
+
+
+/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
+/// to be printed to standard output. Useful for debugging.
+
+const std::string Bitboards::pretty(Bitboard b) {
+
+ std::string s = "+---+---+---+---+---+---+---+---+\n";
+
+ for (Rank r = RANK_8; r >= RANK_1; --r)
+ {
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ s += b & make_square(f, r) ? "| X " : "| ";
+
+ s += "|\n+---+---+---+---+---+---+---+---+\n";
+ }
+
+ return s;
+}
+
+
+/// Bitboards::init() initializes various bitboard tables. It is called at
+/// startup and relies on global objects to be already zero-initialized.
+
+void Bitboards::init() {
+
+ for (unsigned i = 0; i < (1 << 16); ++i)
+ PopCnt16[i] = (uint8_t) popcount16(i);
+
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ SquareBB[s] = 1ULL << s;
+ BSFTable[bsf_index(SquareBB[s])] = s;
+ }
+
+ for (Bitboard b = 2; b < 256; ++b)
+ MSBTable[b] = MSBTable[b - 1] + !more_than_one(b);
+
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;
+
+ for (Rank r = RANK_1; r <= RANK_8; ++r)
+ RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB;
+
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
+
+ for (Rank r = RANK_1; r < RANK_8; ++r)
+ ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | RankBB[r]);
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ ForwardFileBB [c][s] = ForwardRanksBB[c][rank_of(s)] & FileBB[file_of(s)];
+ PawnAttackSpan[c][s] = ForwardRanksBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
+ PassedPawnMask[c][s] = ForwardFileBB [c][s] | PawnAttackSpan[c][s];
+ }
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ if (s1 != s2)
+ {
+ SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
+ DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
+ }
+
+ int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt : { PAWN, KNIGHT, KING })
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ for (int i = 0; steps[pt][i]; ++i)
+ {
+ Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
+
+ if (is_ok(to) && distance(s, to) < 3)
+ {
+ if (pt == PAWN)
+ PawnAttacks[c][s] |= to;
+ else
+ PseudoAttacks[pt][s] |= to;
+ }
+ }
+
+ Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
+ Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
+
+ init_magics(RookTable, RookMagics, RookDirections);
+ init_magics(BishopTable, BishopMagics, BishopDirections);
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ {
+ PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
+ PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
+
+ for (PieceType pt : { BISHOP, ROOK })
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ {
+ if (!(PseudoAttacks[pt][s1] & s2))
+ continue;
+
+ LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
+ BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
+ }
+ }
+}
+
+
+namespace {
+
+ Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
+
+ Bitboard attack = 0;
+
+ for (int i = 0; i < 4; ++i)
+ for (Square s = sq + directions[i];
+ is_ok(s) && distance(s, s - directions[i]) == 1;
+ s += directions[i])
+ {
+ attack |= s;
+
+ if (occupied & s)
+ break;
+ }
+
+ return attack;
+ }
+
+
+ // init_magics() computes all rook and bishop attacks at startup. Magic
+ // bitboards are used to look up attacks of sliding pieces. As a reference see
+ // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
+ // use the so called "fancy" approach.
+
+ void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {
+
+ // Optimal PRNG seeds to pick the correct magics in the shortest time
+ int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
+ { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
+
+ Bitboard occupancy[4096], reference[4096], edges, b;
+ int epoch[4096] = {}, cnt = 0, size = 0;
+
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ // Board edges are not considered in the relevant occupancies
+ edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
+
+ // Given a square 's', the mask is the bitboard of sliding attacks from
+ // 's' computed on an empty board. The index must be big enough to contain
+ // all the attacks for each possible subset of the mask and so is 2 power
+ // the number of 1s of the mask. Hence we deduce the size of the shift to
+ // apply to the 64 or 32 bits word to get the index.
+ Magic& m = magics[s];
+ m.mask = sliding_attack(directions, s, 0) & ~edges;
+ m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
+
+ // Set the offset for the attacks table of the square. We have individual
+ // table sizes for each square with "Fancy Magic Bitboards".
+ m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
+
+ // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
+ // store the corresponding sliding attack bitboard in reference[].
+ b = size = 0;
+ do {
+ occupancy[size] = b;
+ reference[size] = sliding_attack(directions, s, b);
+
+ if (HasPext)
+ m.attacks[pext(b, m.mask)] = reference[size];
+
+ size++;
+ b = (b - m.mask) & m.mask;
+ } while (b);
+
+ if (HasPext)
+ continue;
+
+ PRNG rng(seeds[Is64Bit][rank_of(s)]);
+
+ // Find a magic for square 's' picking up an (almost) random number
+ // until we find the one that passes the verification test.
+ for (int i = 0; i < size; )
+ {
+ for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
+ m.magic = rng.sparse_rand<Bitboard>();
+
+ // A good magic must map every possible occupancy to an index that
+ // looks up the correct sliding attack in the attacks[s] database.
+ // Note that we build up the database for square 's' as a side
+ // effect of verifying the magic. Keep track of the attempt count
+ // and save it in epoch[], little speed-up trick to avoid resetting
+ // m.attacks[] after every failed attempt.
+ for (++cnt, i = 0; i < size; ++i)
+ {
+ unsigned idx = m.index(occupancy[i]);
+
+ if (epoch[idx] < cnt)
+ {
+ epoch[idx] = cnt;
+ m.attacks[idx] = reference[i];
+ }
+ else if (m.attacks[idx] != reference[i])
+ break;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef BITBOARD_H_INCLUDED
+#define BITBOARD_H_INCLUDED
+
+#include <string>
+
+#include "types.h"
+
+namespace Bitbases {
+
+void init();
+bool probe(Square wksq, Square wpsq, Square bksq, Color us);
+
+}
+
+namespace Bitboards {
+
+void init();
+const std::string pretty(Bitboard b);
+
+}
+
+const Bitboard AllSquares = ~Bitboard(0);
+const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
+
+const Bitboard FileABB = 0x0101010101010101ULL;
+const Bitboard FileBBB = FileABB << 1;
+const Bitboard FileCBB = FileABB << 2;
+const Bitboard FileDBB = FileABB << 3;
+const Bitboard FileEBB = FileABB << 4;
+const Bitboard FileFBB = FileABB << 5;
+const Bitboard FileGBB = FileABB << 6;
+const Bitboard FileHBB = FileABB << 7;
+
+const Bitboard Rank1BB = 0xFF;
+const Bitboard Rank2BB = Rank1BB << (8 * 1);
+const Bitboard Rank3BB = Rank1BB << (8 * 2);
+const Bitboard Rank4BB = Rank1BB << (8 * 3);
+const Bitboard Rank5BB = Rank1BB << (8 * 4);
+const Bitboard Rank6BB = Rank1BB << (8 * 5);
+const Bitboard Rank7BB = Rank1BB << (8 * 6);
+const Bitboard Rank8BB = Rank1BB << (8 * 7);
+
+extern int SquareDistance[SQUARE_NB][SQUARE_NB];
+
+extern Bitboard SquareBB[SQUARE_NB];
+extern Bitboard FileBB[FILE_NB];
+extern Bitboard RankBB[RANK_NB];
+extern Bitboard AdjacentFilesBB[FILE_NB];
+extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
+extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
+extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
+extern Bitboard DistanceRingBB[SQUARE_NB][8];
+extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
+extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
+extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
+extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
+extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
+
+
+/// Magic holds all magic bitboards relevant data for a single square
+struct Magic {
+ Bitboard mask;
+ Bitboard magic;
+ Bitboard* attacks;
+ unsigned shift;
+
+ // Compute the attack's index using the 'magic bitboards' approach
+ unsigned index(Bitboard occupied) const {
+
+ if (HasPext)
+ return unsigned(pext(occupied, mask));
+
+ if (Is64Bit)
+ return unsigned(((occupied & mask) * magic) >> shift);
+
+ unsigned lo = unsigned(occupied) & unsigned(mask);
+ unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
+ return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
+ }
+};
+
+extern Magic RookMagics[SQUARE_NB];
+extern Magic BishopMagics[SQUARE_NB];
+
+
+/// Overloads of bitwise operators between a Bitboard and a Square for testing
+/// whether a given bit is set in a bitboard, and for setting and clearing bits.
+
+inline Bitboard operator&(Bitboard b, Square s) {
+ return b & SquareBB[s];
+}
+
+inline Bitboard operator|(Bitboard b, Square s) {
+ return b | SquareBB[s];
+}
+
+inline Bitboard operator^(Bitboard b, Square s) {
+ return b ^ SquareBB[s];
+}
+
+inline Bitboard& operator|=(Bitboard& b, Square s) {
+ return b |= SquareBB[s];
+}
+
+inline Bitboard& operator^=(Bitboard& b, Square s) {
+ return b ^= SquareBB[s];
+}
+
+constexpr bool more_than_one(Bitboard b) {
+ return b & (b - 1);
+}
+
+/// rank_bb() and file_bb() return a bitboard representing all the squares on
+/// the given file or rank.
+
+inline Bitboard rank_bb(Rank r) {
+ return RankBB[r];
+}
+
+inline Bitboard rank_bb(Square s) {
+ return RankBB[rank_of(s)];
+}
+
+inline Bitboard file_bb(File f) {
+ return FileBB[f];
+}
+
+inline Bitboard file_bb(Square s) {
+ return FileBB[file_of(s)];
+}
+
+
+/// shift() moves a bitboard one step along direction D. Mainly for pawns
+
+template<Direction D>
+constexpr Bitboard shift(Bitboard b) {
+ return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
+ : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7
+ : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
+ : 0;
+}
+
+
+/// adjacent_files_bb() returns a bitboard representing all the squares on the
+/// adjacent files of the given one.
+
+inline Bitboard adjacent_files_bb(File f) {
+ return AdjacentFilesBB[f];
+}
+
+
+/// between_bb() returns a bitboard representing all the squares between the two
+/// given ones. For instance, between_bb(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 rank, file
+/// or diagonal, 0 is returned.
+
+inline Bitboard between_bb(Square s1, Square s2) {
+ return BetweenBB[s1][s2];
+}
+
+
+/// forward_ranks_bb() returns a bitboard representing all the squares on all the ranks
+/// in front of the given one, from the point of view of the given color. For
+/// instance, forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
+
+inline Bitboard forward_ranks_bb(Color c, Square s) {
+ return ForwardRanksBB[c][rank_of(s)];
+}
+
+
+/// forward_file_bb() returns a bitboard representing all the squares along the line
+/// in front of the given one, from the point of view of the given color:
+/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s)
+
+inline Bitboard forward_file_bb(Color c, Square s) {
+ return ForwardFileBB[c][s];
+}
+
+
+/// pawn_attack_span() returns a bitboard representing all the squares that can be
+/// attacked by a pawn of the given color when it moves along its file, starting
+/// from the given square:
+/// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
+
+inline Bitboard pawn_attack_span(Color c, Square s) {
+ return PawnAttackSpan[c][s];
+}
+
+
+/// passed_pawn_mask() returns a bitboard mask which can be used to test if a
+/// pawn of the given color and on the given square is a passed pawn:
+/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_file_bb(c, s)
+
+inline Bitboard passed_pawn_mask(Color c, Square s) {
+ return PassedPawnMask[c][s];
+}
+
+
+/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
+/// straight or on a diagonal line.
+
+inline bool aligned(Square s1, Square s2, Square s3) {
+ return LineBB[s1][s2] & s3;
+}
+
+
+/// distance() functions return the distance between x and y, defined as the
+/// number of steps for a king in x to reach y. Works with squares, ranks, files.
+
+template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; }
+template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
+
+template<typename T1, typename T2> inline int distance(T2 x, T2 y);
+template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
+template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
+
+
+/// attacks_bb() returns a bitboard representing all the squares attacked by a
+/// piece of type Pt (bishop or rook) placed on 's'.
+
+template<PieceType Pt>
+inline Bitboard attacks_bb(Square s, Bitboard occupied) {
+
+ const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
+ return m.attacks[m.index(occupied)];
+}
+
+inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
+
+ assert(pt != PAWN);
+
+ switch (pt)
+ {
+ case BISHOP: return attacks_bb<BISHOP>(s, occupied);
+ case ROOK : return attacks_bb< ROOK>(s, occupied);
+ case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
+ default : return PseudoAttacks[pt][s];
+ }
+}
+
+
+/// popcount() counts the number of non-zero bits in a bitboard
+
+inline int popcount(Bitboard b) {
+
+#ifndef USE_POPCNT
+
+ extern uint8_t PopCnt16[1 << 16];
+ union { Bitboard bb; uint16_t u[4]; } v = { b };
+ return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
+
+#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
+
+ return (int)_mm_popcnt_u64(b);
+
+#else // Assumed gcc or compatible compiler
+
+ return __builtin_popcountll(b);
+
+#endif
+}
+
+
+/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
+
+#if defined(__GNUC__)
+
+inline Square lsb(Bitboard b) {
+ assert(b);
+ return Square(__builtin_ctzll(b));
+}
+
+inline Square msb(Bitboard b) {
+ assert(b);
+ return Square(63 ^ __builtin_clzll(b));
+}
+
+#elif defined(_WIN64) && defined(_MSC_VER)
+
+inline Square lsb(Bitboard b) {
+ assert(b);
+ unsigned long idx;
+ _BitScanForward64(&idx, b);
+ return (Square) idx;
+}
+
+inline Square msb(Bitboard b) {
+ assert(b);
+ unsigned long idx;
+ _BitScanReverse64(&idx, b);
+ return (Square) idx;
+}
+
+#else
+
+#define NO_BSF // Fallback on software implementation for other cases
+
+Square lsb(Bitboard b);
+Square msb(Bitboard b);
+
+#endif
+
+
+/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
+
+inline Square pop_lsb(Bitboard* b) {
+ const Square s = lsb(*b);
+ *b &= *b - 1;
+ return s;
+}
+
+
+/// frontmost_sq() and backmost_sq() return the square corresponding to the
+/// most/least advanced bit relative to the given color.
+
+inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
+inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
+
+#endif // #ifndef BITBOARD_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+
+#include "bitboard.h"
+#include "endgame.h"
+#include "movegen.h"
+
+using std::string;
+
+namespace {
+
+ // Table used to drive the king towards the edge of the board
+ // in KX vs K and KQ vs KR endgames.
+ const int PushToEdges[SQUARE_NB] = {
+ 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 king towards a corner square of the
+ // right color in KBN vs K endgames.
+ const int PushToCorners[SQUARE_NB] = {
+ 200, 190, 180, 170, 160, 150, 140, 130,
+ 190, 180, 170, 160, 150, 140, 130, 140,
+ 180, 170, 155, 140, 140, 125, 140, 150,
+ 170, 160, 140, 120, 110, 140, 150, 160,
+ 160, 150, 140, 110, 120, 140, 160, 170,
+ 150, 140, 125, 140, 140, 155, 170, 180,
+ 140, 130, 140, 150, 160, 170, 180, 190,
+ 130, 140, 150, 160, 170, 180, 190, 200
+ };
+
+ // Tables used to drive a piece towards or away from another piece
+ const int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
+ const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
+
+ // Pawn Rank based scaling factors used in KRPPKRP endgame
+ const int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
+
+#ifndef NDEBUG
+ bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
+ return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
+ }
+#endif
+
+ // Map the square as if strongSide is white and strongSide's only pawn
+ // is on the left half of the board.
+ Square normalize(const Position& pos, Color strongSide, Square sq) {
+
+ assert(pos.count<PAWN>(strongSide) == 1);
+
+ if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
+ sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
+
+ if (strongSide == BLACK)
+ sq = ~sq;
+
+ return sq;
+ }
+
+} // namespace
+
+
+/// Endgames members definitions
+
+Endgames::Endgames() {
+
+ add<KPK>("KPK");
+ add<KNNK>("KNNK");
+ add<KBNK>("KBNK");
+ add<KRKP>("KRKP");
+ add<KRKB>("KRKB");
+ add<KRKN>("KRKN");
+ add<KQKP>("KQKP");
+ add<KQKR>("KQKR");
+
+ add<KNPK>("KNPK");
+ add<KNPKB>("KNPKB");
+ add<KRPKR>("KRPKR");
+ add<KRPKB>("KRPKB");
+ add<KBPKB>("KBPKB");
+ add<KBPKN>("KBPKN");
+ add<KBPPKB>("KBPPKB");
+ add<KRPPKRP>("KRPPKRP");
+}
+
+
+/// 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.
+template<>
+Value Endgame<KXK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+ assert(!pos.checkers()); // Eval is never called when in check
+
+ // Stalemate detection with lone king
+ if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
+ return VALUE_DRAW;
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = pos.non_pawn_material(strongSide)
+ + pos.count<PAWN>(strongSide) * PawnValueEg
+ + PushToEdges[loserKSq]
+ + PushClose[distance(winnerKSq, loserKSq)];
+
+ if ( pos.count<QUEEN>(strongSide)
+ || pos.count<ROOK>(strongSide)
+ ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
+ || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
+ && (pos.pieces(strongSide, BISHOP) & DarkSquares)))
+ result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1);
+
+ return strongSide == 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.
+template<>
+Value Endgame<KBNK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+ Square bishopSq = pos.square<BISHOP>(strongSide);
+
+ // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
+ // bishop that cannot reach the above squares, we flip the kings in order
+ // to drive the enemy toward corners A8 or H1.
+ if (opposite_colors(bishopSq, SQ_A1))
+ {
+ winnerKSq = ~winnerKSq;
+ loserKSq = ~loserKSq;
+ }
+
+ Value result = VALUE_KNOWN_WIN
+ + PushClose[distance(winnerKSq, loserKSq)]
+ + PushToCorners[loserKSq];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// KP vs K. This endgame is evaluated with the help of a bitbase.
+template<>
+Value Endgame<KPK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ // Assume strongSide is white and the pawn is on files A-D
+ Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
+ Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
+ Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
+
+ Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
+
+ if (!Bitbases::probe(wksq, psq, bksq, us))
+ return VALUE_DRAW;
+
+ Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq));
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// 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.
+template<>
+Value Endgame<KRKP>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
+
+ Square wksq = relative_square(strongSide, pos.square<KING>(strongSide));
+ Square bksq = relative_square(strongSide, pos.square<KING>(weakSide));
+ Square rsq = relative_square(strongSide, pos.square<ROOK>(strongSide));
+ Square psq = relative_square(strongSide, pos.square<PAWN>(weakSide));
+
+ Square queeningSq = make_square(file_of(psq), RANK_1);
+ Value result;
+
+ // If the stronger side's king is in front of the pawn, it's a win
+ if (wksq < psq && file_of(wksq) == file_of(psq))
+ result = RookValueEg - distance(wksq, psq);
+
+ // If the weaker side's king is too far from the pawn and the rook,
+ // it's a win.
+ else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
+ && distance(bksq, rsq) >= 3)
+ result = RookValueEg - distance(wksq, psq);
+
+ // If the pawn is far advanced and supported by the defending king,
+ // the position is drawish
+ else if ( rank_of(bksq) <= RANK_3
+ && distance(bksq, psq) == 1
+ && rank_of(wksq) >= RANK_4
+ && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
+ result = Value(80) - 8 * distance(wksq, psq);
+
+ else
+ result = Value(200) - 8 * ( distance(wksq, psq + SOUTH)
+ - distance(bksq, psq + SOUTH)
+ - distance(psq, queeningSq));
+
+ return strongSide == 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.
+template<>
+Value Endgame<KRKB>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
+
+ Value result = Value(PushToEdges[pos.square<KING>(weakSide)]);
+ return strongSide == pos.side_to_move() ? 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.
+template<>
+Value Endgame<KRKN>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
+ assert(verify_material(pos, weakSide, KnightValueMg, 0));
+
+ Square bksq = pos.square<KING>(weakSide);
+ Square bnsq = pos.square<KNIGHT>(weakSide);
+ Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// KQ vs KP. In general, this is a win for the stronger side, but there are a
+/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
+/// with a king positioned next to it can be a draw, so in that case, we only
+/// use the distance between the kings.
+template<>
+Value Endgame<KQKP>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+ Square pawnSq = pos.square<PAWN>(weakSide);
+
+ Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
+
+ if ( relative_rank(weakSide, pawnSq) != RANK_7
+ || distance(loserKSq, pawnSq) != 1
+ || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq))
+ result += QueenValueEg - PawnValueEg;
+
+ return strongSide == 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 win KQ vs KR.
+template<>
+Value Endgame<KQKR>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
+ assert(verify_material(pos, weakSide, RookValueMg, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = QueenValueEg
+ - RookValueEg
+ + PushToEdges[loserKSq]
+ + PushClose[distance(winnerKSq, loserKSq)];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// Some cases of trivial draws
+template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
+
+
+/// KB and one or more pawns vs K. It checks for draws with rook pawns and
+/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
+/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
+/// will be used.
+template<>
+ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
+
+ assert(pos.non_pawn_material(strongSide) == BishopValueMg);
+ assert(pos.count<PAWN>(strongSide) >= 1);
+
+ // No assertions about the material of weakSide, because we want draws to
+ // be detected even when the weaker side has some pawns.
+
+ Bitboard pawns = pos.pieces(strongSide, PAWN);
+ File pawnsFile = file_of(lsb(pawns));
+
+ // All pawns are on a single rook file?
+ if ( (pawnsFile == FILE_A || pawnsFile == FILE_H)
+ && !(pawns & ~file_bb(pawnsFile)))
+ {
+ Square bishopSq = pos.square<BISHOP>(strongSide);
+ Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8));
+ Square kingSq = pos.square<KING>(weakSide);
+
+ if ( opposite_colors(queeningSq, bishopSq)
+ && distance(queeningSq, kingSq) <= 1)
+ return SCALE_FACTOR_DRAW;
+ }
+
+ // If all the pawns are on the same B or G file, then it's potentially a draw
+ if ( (pawnsFile == FILE_B || pawnsFile == FILE_G)
+ && !(pos.pieces(PAWN) & ~file_bb(pawnsFile))
+ && pos.non_pawn_material(weakSide) == 0
+ && pos.count<PAWN>(weakSide) >= 1)
+ {
+ // Get weakSide pawn that is closest to the home rank
+ Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
+
+ Square strongKingSq = pos.square<KING>(strongSide);
+ Square weakKingSq = pos.square<KING>(weakSide);
+ Square bishopSq = pos.square<BISHOP>(strongSide);
+
+ // There's potential for a draw if our pawn is blocked on the 7th rank,
+ // the bishop cannot attack it or they only have one pawn left
+ if ( relative_rank(strongSide, weakPawnSq) == RANK_7
+ && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
+ && (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1))
+ {
+ int strongKingDist = distance(weakPawnSq, strongKingSq);
+ int weakKingDist = distance(weakPawnSq, weakKingSq);
+
+ // It's a draw if the weak king is on its back two ranks, within 2
+ // squares of the blocking pawn and the strong king is not
+ // closer. (I think this rule only fails in practically
+ // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
+ // and positions where qsearch will immediately correct the
+ // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
+ if ( relative_rank(strongSide, weakKingSq) >= RANK_7
+ && weakKingDist <= 2
+ && weakKingDist <= strongKingDist)
+ return SCALE_FACTOR_DRAW;
+ }
+ }
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
+/// the third rank defended by a pawn.
+template<>
+ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
+ assert(pos.count<ROOK>(weakSide) == 1);
+ assert(pos.count<PAWN>(weakSide) >= 1);
+
+ Square kingSq = pos.square<KING>(weakSide);
+ Square rsq = pos.square<ROOK>(weakSide);
+
+ if ( relative_rank(weakSide, kingSq) <= RANK_2
+ && relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
+ && relative_rank(weakSide, rsq) == RANK_3
+ && ( pos.pieces(weakSide, PAWN)
+ & pos.attacks_from<KING>(kingSq)
+ & pos.attacks_from<PAWN>(rsq, strongSide)))
+ return SCALE_FACTOR_DRAW;
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KRP vs KR. 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 isn't very pretty.
+template<>
+ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 1));
+ assert(verify_material(pos, weakSide, RookValueMg, 0));
+
+ // Assume strongSide is white and the pawn is on files A-D
+ Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
+ Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
+ Square wrsq = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
+ Square wpsq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
+ Square brsq = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
+
+ File f = file_of(wpsq);
+ Rank r = rank_of(wpsq);
+ Square queeningSq = make_square(f, RANK_8);
+ int tempo = (pos.side_to_move() == strongSide);
+
+ // 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
+ && distance(bksq, queeningSq) <= 1
+ && wksq <= SQ_H5
+ && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
+ return SCALE_FACTOR_DRAW;
+
+ // 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
+ && distance(bksq, queeningSq) <= 1
+ && rank_of(wksq) + tempo <= RANK_6
+ && (rank_of(brsq) == RANK_1 || (!tempo && distance<File>(brsq, wpsq) >= 3)))
+ return SCALE_FACTOR_DRAW;
+
+ if ( r >= RANK_6
+ && bksq == queeningSq
+ && rank_of(brsq) == RANK_1
+ && (!tempo || distance(wksq, wpsq) >= 2))
+ return SCALE_FACTOR_DRAW;
+
+ // 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)
+ && file_of(brsq) == FILE_A
+ && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5))
+ return SCALE_FACTOR_DRAW;
+
+ // 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 + NORTH
+ && distance(wksq, wpsq) - tempo >= 2
+ && distance(wksq, brsq) - tempo >= 2)
+ return SCALE_FACTOR_DRAW;
+
+ // 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
+ && file_of(wrsq) == f
+ && wrsq != queeningSq
+ && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
+ && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo))
+ return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq));
+
+ // Similar to the above, but with the pawn further back
+ if ( f != FILE_A
+ && file_of(wrsq) == f
+ && wrsq < wpsq
+ && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
+ && (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo)
+ && ( distance(bksq, wrsq) + tempo >= 3
+ || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
+ && (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo))))
+ return ScaleFactor( SCALE_FACTOR_MAX
+ - 8 * distance(wpsq, queeningSq)
+ - 2 * 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 (file_of(bksq) == file_of(wpsq))
+ return ScaleFactor(10);
+ if ( distance<File>(bksq, wpsq) == 1
+ && distance(wksq, bksq) > 2)
+ return ScaleFactor(24 - 2 * distance(wksq, bksq));
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+template<>
+ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 1));
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
+
+ // Test for a rook pawn
+ if (pos.pieces(PAWN) & (FileABB | FileHBB))
+ {
+ Square ksq = pos.square<KING>(weakSide);
+ Square bsq = pos.square<BISHOP>(weakSide);
+ Square psq = pos.square<PAWN>(strongSide);
+ Rank rk = relative_rank(strongSide, psq);
+ Direction push = pawn_push(strongSide);
+
+ // If the pawn is on the 5th rank and the pawn (currently) is on
+ // the same color square as the bishop then there is a chance of
+ // a fortress. Depending on the king position give a moderate
+ // reduction or a stronger one if the defending king is near the
+ // corner but not trapped there.
+ if (rk == RANK_5 && !opposite_colors(bsq, psq))
+ {
+ int d = distance(psq + 3 * push, ksq);
+
+ if (d <= 2 && !(d == 0 && ksq == pos.square<KING>(strongSide) + 2 * push))
+ return ScaleFactor(24);
+ else
+ return ScaleFactor(48);
+ }
+
+ // When the pawn has moved to the 6th rank we can be fairly sure
+ // it's drawn if the bishop attacks the square in front of the
+ // pawn from a reasonable distance and the defending king is near
+ // the corner
+ if ( rk == RANK_6
+ && distance(psq + 2 * push, ksq) <= 1
+ && (PseudoAttacks[BISHOP][bsq] & (psq + push))
+ && distance<File>(bsq, psq) >= 2)
+ return ScaleFactor(8);
+ }
+
+ return SCALE_FACTOR_NONE;
+}
+
+/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
+/// pawns and the defending king is actively placed, the position is drawish.
+template<>
+ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 2));
+ assert(verify_material(pos, weakSide, RookValueMg, 1));
+
+ Square wpsq1 = pos.squares<PAWN>(strongSide)[0];
+ Square wpsq2 = pos.squares<PAWN>(strongSide)[1];
+ Square bksq = pos.square<KING>(weakSide);
+
+ // Does the stronger side have a passed pawn?
+ if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2))
+ return SCALE_FACTOR_NONE;
+
+ Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2));
+
+ if ( distance<File>(bksq, wpsq1) <= 1
+ && distance<File>(bksq, wpsq2) <= 1
+ && relative_rank(strongSide, bksq) > r)
+ {
+ assert(r > RANK_1 && r < RANK_7);
+ return ScaleFactor(KRPPKRPScaleFactors[r]);
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// K and two or more pawns vs K. 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.
+template<>
+ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
+
+ assert(pos.non_pawn_material(strongSide) == VALUE_ZERO);
+ assert(pos.count<PAWN>(strongSide) >= 2);
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square ksq = pos.square<KING>(weakSide);
+ Bitboard pawns = pos.pieces(strongSide, PAWN);
+
+ // If all pawns are ahead of the king, on a single rook file and
+ // the king is within one file of the pawns, it's a draw.
+ if ( !(pawns & ~forward_ranks_bb(weakSide, ksq))
+ && !((pawns & ~FileABB) && (pawns & ~FileHBB))
+ && distance<File>(ksq, lsb(pawns)) <= 1)
+ return SCALE_FACTOR_DRAW;
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KBP vs KB. 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.
+template<>
+ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, BishopValueMg, 1));
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
+
+ Square pawnSq = pos.square<PAWN>(strongSide);
+ Square strongBishopSq = pos.square<BISHOP>(strongSide);
+ Square weakBishopSq = pos.square<BISHOP>(weakSide);
+ Square weakKingSq = pos.square<KING>(weakSide);
+
+ // Case 1: Defending king blocks the pawn, and cannot be driven away
+ if ( file_of(weakKingSq) == file_of(pawnSq)
+ && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
+ && ( opposite_colors(weakKingSq, strongBishopSq)
+ || relative_rank(strongSide, weakKingSq) <= RANK_6))
+ return SCALE_FACTOR_DRAW;
+
+ // Case 2: Opposite colored bishops
+ if (opposite_colors(strongBishopSq, weakBishopSq))
+ {
+ // 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 (relative_rank(strongSide, pawnSq) <= RANK_5)
+ return SCALE_FACTOR_DRAW;
+
+ Bitboard path = forward_file_bb(strongSide, pawnSq);
+
+ if (path & pos.pieces(weakSide, KING))
+ return SCALE_FACTOR_DRAW;
+
+ if ( (pos.attacks_from<BISHOP>(weakBishopSq) & path)
+ && distance(weakBishopSq, pawnSq) >= 3)
+ return SCALE_FACTOR_DRAW;
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
+template<>
+ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, BishopValueMg, 2));
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
+
+ Square wbsq = pos.square<BISHOP>(strongSide);
+ Square bbsq = pos.square<BISHOP>(weakSide);
+
+ if (!opposite_colors(wbsq, bbsq))
+ return SCALE_FACTOR_NONE;
+
+ Square ksq = pos.square<KING>(weakSide);
+ Square psq1 = pos.squares<PAWN>(strongSide)[0];
+ Square psq2 = pos.squares<PAWN>(strongSide)[1];
+ Rank r1 = rank_of(psq1);
+ Rank r2 = rank_of(psq2);
+ Square blockSq1, blockSq2;
+
+ if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
+ {
+ blockSq1 = psq1 + pawn_push(strongSide);
+ blockSq2 = make_square(file_of(psq2), rank_of(psq1));
+ }
+ else
+ {
+ blockSq1 = psq2 + pawn_push(strongSide);
+ blockSq2 = make_square(file_of(psq1), rank_of(psq2));
+ }
+
+ switch (distance<File>(psq1, psq2))
+ {
+ case 0:
+ // Both pawns are on the same file. It's an easy draw if the defender firmly
+ // controls some square in the frontmost pawn's path.
+ if ( file_of(ksq) == file_of(blockSq1)
+ && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1)
+ && opposite_colors(ksq, wbsq))
+ return SCALE_FACTOR_DRAW;
+ else
+ return SCALE_FACTOR_NONE;
+
+ case 1:
+ // Pawns on adjacent files. It's a draw if the defender firmly controls the
+ // square in front of the frontmost pawn's path, and the square diagonally
+ // behind this square on the file of the other pawn.
+ if ( ksq == blockSq1
+ && opposite_colors(ksq, wbsq)
+ && ( bbsq == blockSq2
+ || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
+ || distance(r1, r2) >= 2))
+ return SCALE_FACTOR_DRAW;
+
+ else if ( ksq == blockSq2
+ && opposite_colors(ksq, wbsq)
+ && ( bbsq == blockSq1
+ || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
+ return SCALE_FACTOR_DRAW;
+ else
+ return SCALE_FACTOR_NONE;
+
+ default:
+ // The pawns are not on the same file or adjacent files. No scaling.
+ return SCALE_FACTOR_NONE;
+ }
+}
+
+
+/// KBP vs KN. 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.
+template<>
+ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, BishopValueMg, 1));
+ assert(verify_material(pos, weakSide, KnightValueMg, 0));
+
+ Square pawnSq = pos.square<PAWN>(strongSide);
+ Square strongBishopSq = pos.square<BISHOP>(strongSide);
+ Square weakKingSq = pos.square<KING>(weakSide);
+
+ if ( file_of(weakKingSq) == file_of(pawnSq)
+ && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
+ && ( opposite_colors(weakKingSq, strongBishopSq)
+ || relative_rank(strongSide, weakKingSq) <= RANK_6))
+ return SCALE_FACTOR_DRAW;
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KNP vs K. 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.
+template<>
+ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, KnightValueMg, 1));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ // Assume strongSide is white and the pawn is on files A-D
+ Square pawnSq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
+ Square weakKingSq = normalize(pos, strongSide, pos.square<KING>(weakSide));
+
+ if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
+ return SCALE_FACTOR_DRAW;
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
+/// Otherwise the position is drawn.
+template<>
+ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
+
+ Square pawnSq = pos.square<PAWN>(strongSide);
+ Square bishopSq = pos.square<BISHOP>(weakSide);
+ Square weakKingSq = pos.square<KING>(weakSide);
+
+ // King needs to get close to promoting pawn to prevent knight from blocking.
+ // Rules for this are very tricky, so just approximate.
+ if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
+ return ScaleFactor(distance(weakKingSq, pawnSq));
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KP vs KP. 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, it 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).
+template<>
+ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
+
+ // Assume strongSide is white and the pawn is on files A-D
+ Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
+ Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
+ Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
+
+ Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
+
+ // 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 (rank_of(psq) >= RANK_5 && file_of(psq) != 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.
+ return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ENDGAME_H_INCLUDED
+#define ENDGAME_H_INCLUDED
+
+#include <map>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "position.h"
+#include "types.h"
+
+
+/// EndgameCode lists all supported endgame functions by corresponding codes
+
+enum EndgameCode {
+
+ EVALUATION_FUNCTIONS,
+ KNNK, // KNN vs K
+ KXK, // Generic "mate lone king" eval
+ KBNK, // KBN vs K
+ KPK, // KP vs K
+ KRKP, // KR vs KP
+ KRKB, // KR vs KB
+ KRKN, // KR vs KN
+ KQKP, // KQ vs KP
+ KQKR, // KQ vs KR
+
+ SCALING_FUNCTIONS,
+ KBPsK, // KB and pawns vs K
+ KQKRPs, // KQ vs KR and pawns
+ KRPKR, // KRP vs KR
+ KRPKB, // KRP vs KB
+ KRPPKRP, // KRPP vs KRP
+ KPsK, // K and pawns vs K
+ KBPKB, // KBP vs KB
+ KBPPKB, // KBPP vs KB
+ KBPKN, // KBP vs KN
+ KNPK, // KNP vs K
+ KNPKB, // KNP vs KB
+ KPKP // KP vs KP
+};
+
+
+/// Endgame functions can be of two types depending on whether they return a
+/// Value or a ScaleFactor.
+template<EndgameCode E> using
+eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
+
+
+/// Base and derived functors for endgame evaluation and scaling functions
+
+template<typename T>
+struct EndgameBase {
+
+ explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
+ virtual ~EndgameBase() = default;
+ virtual T operator()(const Position&) const = 0;
+
+ const Color strongSide, weakSide;
+};
+
+
+template<EndgameCode E, typename T = eg_type<E>>
+struct Endgame : public EndgameBase<T> {
+
+ explicit Endgame(Color c) : EndgameBase<T>(c) {}
+ T operator()(const Position&) const override;
+};
+
+
+/// The Endgames class stores the pointers to endgame evaluation and scaling
+/// base objects in two std::map. We use polymorphism to invoke the actual
+/// endgame function by calling its virtual operator().
+
+class Endgames {
+
+ template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
+ template<typename T> using Map = std::map<Key, Ptr<T>>;
+
+ template<typename T>
+ Map<T>& map() {
+ return std::get<std::is_same<T, ScaleFactor>::value>(maps);
+ }
+
+ template<EndgameCode E, typename T = eg_type<E>, typename P = Ptr<T>>
+ void add(const std::string& code) {
+
+ StateInfo st;
+ map<T>()[Position().set(code, WHITE, &st).material_key()] = P(new Endgame<E>(WHITE));
+ map<T>()[Position().set(code, BLACK, &st).material_key()] = P(new Endgame<E>(BLACK));
+ }
+
+ std::pair<Map<Value>, Map<ScaleFactor>> maps;
+
+public:
+ Endgames();
+
+ template<typename T>
+ EndgameBase<T>* probe(Key key) {
+ return map<T>().count(key) ? map<T>()[key].get() : nullptr;
+ }
+};
+
+#endif // #ifndef ENDGAME_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <cstring> // For std::memset
+#include <iomanip>
+#include <sstream>
+
+#include "bitboard.h"
+#include "evaluate.h"
+#include "material.h"
+#include "pawns.h"
+
+namespace {
+
+ const Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
+ const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
+ const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
+ const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
+
+ const Bitboard KingFlank[FILE_NB] = {
+ QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide
+ };
+
+ namespace Trace {
+
+ enum Tracing {NO_TRACE, TRACE};
+
+ enum Term { // The first 8 entries are for PieceType
+ MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB
+ };
+
+ double scores[TERM_NB][COLOR_NB][PHASE_NB];
+
+ double to_cp(Value v) { return double(v) / PawnValueEg; }
+
+ void add(int idx, Color c, Score s) {
+ scores[idx][c][MG] = to_cp(mg_value(s));
+ scores[idx][c][EG] = to_cp(eg_value(s));
+ }
+
+ void add(int idx, Score w, Score b = SCORE_ZERO) {
+ add(idx, WHITE, w); add(idx, BLACK, b);
+ }
+
+ std::ostream& operator<<(std::ostream& os, Term t) {
+
+ if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == INITIATIVE || t == TOTAL)
+ os << " --- --- | --- --- | ";
+ else
+ os << std::setw(5) << scores[t][WHITE][MG] << " "
+ << std::setw(5) << scores[t][WHITE][EG] << " | "
+ << std::setw(5) << scores[t][BLACK][MG] << " "
+ << std::setw(5) << scores[t][BLACK][EG] << " | ";
+
+ os << std::setw(5) << scores[t][WHITE][MG] - scores[t][BLACK][MG] << " "
+ << std::setw(5) << scores[t][WHITE][EG] - scores[t][BLACK][EG] << " \n";
+
+ return os;
+ }
+ }
+
+ using namespace Trace;
+
+ // Evaluation class contains various information computed and collected
+ // by the evaluation functions.
+ template<Tracing T = NO_TRACE>
+ class Evaluation {
+
+ public:
+ Evaluation() = delete;
+ Evaluation(const Position& p) : pos(p) {}
+ Evaluation& operator=(const Evaluation&) = delete;
+
+ Value value();
+
+ private:
+ // Evaluation helpers (used when calling value())
+ template<Color Us> void initialize();
+ template<Color Us> Score evaluate_king();
+ template<Color Us> Score evaluate_threats();
+ int king_distance(Color c, Square s);
+ template<Color Us> Score evaluate_passed_pawns();
+ template<Color Us> Score evaluate_space();
+ template<Color Us, PieceType Pt> Score evaluate_pieces();
+ ScaleFactor evaluate_scale_factor(Value eg);
+ Score evaluate_initiative(Value eg);
+
+ // Data members
+ const Position& pos;
+ Material::Entry* me;
+ Pawns::Entry* pe;
+ Bitboard mobilityArea[COLOR_NB];
+ Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
+
+ // attackedBy[color][piece type] is a bitboard representing all squares
+ // attacked by a given color and piece type. Special "piece types" which are
+ // also calculated are QUEEN_DIAGONAL and ALL_PIECES.
+ Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
+
+ // attackedBy2[color] are the squares attacked by 2 pieces of a given color,
+ // possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
+ // pawn or squares attacked by 2 pawns are not explicitly added.
+ Bitboard attackedBy2[COLOR_NB];
+
+ // kingRing[color] is the zone around the king which is considered
+ // by the king safety evaluation. This consists of the squares directly
+ // adjacent to the king, and (only for a king on its first rank) the
+ // squares two ranks in front of the king. For instance, if black's king
+ // is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
+ // f7, g7, h7, f6, g6 and h6.
+ Bitboard kingRing[COLOR_NB];
+
+ // kingAttackersCount[color] is the number of pieces of the given color
+ // which attack a square in the kingRing of the enemy king.
+ int kingAttackersCount[COLOR_NB];
+
+ // kingAttackersWeight[color] is the sum of the "weights" of the pieces of the
+ // given color which attack a square in the kingRing of the enemy king. The
+ // weights of the individual piece types are given by the elements in the
+ // KingAttackWeights array.
+ int kingAttackersWeight[COLOR_NB];
+
+ // kingAdjacentZoneAttacksCount[color] is the number of attacks by the given
+ // color to squares directly adjacent to the enemy king. Pieces which attack
+ // more than one square are counted multiple times. For instance, if there is
+ // a white knight on g5 and black's king is on g8, this white knight adds 2
+ // to kingAdjacentZoneAttacksCount[WHITE].
+ int kingAdjacentZoneAttacksCount[COLOR_NB];
+ };
+
+ #define V(v) Value(v)
+ #define S(mg, eg) make_score(mg, eg)
+
+ // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
+ // indexed by piece type and number of attacked squares in the mobility area.
+ const Score MobilityBonus[][32] = {
+ { S(-75,-76), S(-57,-54), S( -9,-28), S( -2,-10), S( 6, 5), S( 14, 12), // Knights
+ S( 22, 26), S( 29, 29), S( 36, 29) },
+ { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops
+ S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
+ S( 91, 88), S( 98, 97) },
+ { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rooks
+ S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165),
+ S( 46,166), S( 48,169), S( 58,171) },
+ { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens
+ S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104),
+ S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136),
+ S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175),
+ S(106,184), S(109,191), S(113,206), S(116,212) }
+ };
+
+ // Outpost[knight/bishop][supported by pawn] contains bonuses for minor
+ // pieces if they can reach an outpost square, bigger if that square is
+ // supported by a pawn. If the minor piece occupies an outpost square
+ // then score is doubled.
+ const Score Outpost[][2] = {
+ { S(22, 6), S(36,12) }, // Knight
+ { S( 9, 2), S(15, 5) } // Bishop
+ };
+
+ // RookOnFile[semiopen/open] contains bonuses for each rook when there is no
+ // friendly pawn on the rook file.
+ const Score RookOnFile[] = { S(20, 7), S(45, 20) };
+
+ // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
+ // which piece type attacks which one. Attacks on lesser pieces which are
+ // pawn-defended are not considered.
+ const Score ThreatByMinor[PIECE_TYPE_NB] = {
+ S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72, 107), S(48, 118)
+ };
+
+ const Score ThreatByRook[PIECE_TYPE_NB] = {
+ S(0, 0), S(0, 25), S(40, 62), S(40, 59), S(0, 34), S(35, 48)
+ };
+
+ // ThreatByKing[on one/on many] contains bonuses for king attacks on
+ // pawns or pieces which are not pawn-defended.
+ const Score ThreatByKing[] = { S(3, 62), S(9, 138) };
+
+ // Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
+ // We don't use a Score because we process the two components independently.
+ const Value Passed[][RANK_NB] = {
+ { V(0), V(5), V( 5), V(31), V(73), V(166), V(252) },
+ { V(0), V(7), V(14), V(38), V(73), V(166), V(252) }
+ };
+
+ // PassedFile[File] contains a bonus according to the file of a passed pawn
+ const Score PassedFile[FILE_NB] = {
+ S( 9, 10), S( 2, 10), S( 1, -8), S(-20,-12),
+ S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10)
+ };
+
+ // Rank factor applied on some bonus for passed pawn on rank 4 or beyond
+ const int RankFactor[RANK_NB] = {0, 0, 0, 2, 6, 11, 16};
+
+ // KingProtector[PieceType-2] contains a bonus according to distance from king
+ const Score KingProtector[] = { S(-3, -5), S(-4, -3), S(-3, 0), S(-1, 1) };
+
+ // Assorted bonuses and penalties used by evaluation
+ const Score MinorBehindPawn = S( 16, 0);
+ const Score BishopPawns = S( 8, 12);
+ const Score LongRangedBishop = S( 22, 0);
+ const Score RookOnPawn = S( 8, 24);
+ const Score TrappedRook = S( 92, 0);
+ const Score WeakQueen = S( 50, 10);
+ const Score CloseEnemies = S( 7, 0);
+ const Score PawnlessFlank = S( 20, 80);
+ const Score ThreatBySafePawn = S(192,175);
+ const Score ThreatByRank = S( 16, 3);
+ const Score Hanging = S( 48, 27);
+ const Score WeakUnopposedPawn = S( 5, 25);
+ const Score ThreatByPawnPush = S( 38, 22);
+ const Score ThreatByAttackOnQueen = S( 38, 22);
+ const Score HinderPassedPawn = S( 7, 0);
+ const Score TrappedBishopA1H1 = S( 50, 50);
+
+ #undef S
+ #undef V
+
+ // KingAttackWeights[PieceType] contains king attack weights by piece type
+ const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 };
+
+ // Penalties for enemy's safe checks
+ const int QueenSafeCheck = 780;
+ const int RookSafeCheck = 880;
+ const int BishopSafeCheck = 435;
+ const int KnightSafeCheck = 790;
+
+ // Threshold for lazy and space evaluation
+ const Value LazyThreshold = Value(1500);
+ const Value SpaceThreshold = Value(12222);
+
+
+ // initialize() computes king and pawn attacks, and the king ring bitboard
+ // for a given color. This is done at the beginning of the evaluation.
+
+ template<Tracing T> template<Color Us>
+ void Evaluation<T>::initialize() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ const Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ const Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
+
+ // Find our pawns on the first two ranks, and those which are blocked
+ Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
+
+ // Squares occupied by those pawns, by our king, or controlled by enemy pawns
+ // are excluded from the mobility area.
+ mobilityArea[Us] = ~(b | pos.square<KING>(Us) | pe->pawn_attacks(Them));
+
+ // Initialise the attack bitboards with the king and pawn information
+ b = attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us));
+ attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
+
+ attackedBy2[Us] = b & attackedBy[Us][PAWN];
+ attackedBy[Us][ALL_PIECES] = b | attackedBy[Us][PAWN];
+
+ // Init our king safety tables only if we are going to use them
+ if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg)
+ {
+ kingRing[Us] = b;
+ if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
+ kingRing[Us] |= shift<Up>(b);
+
+ kingAttackersCount[Them] = popcount(b & pe->pawn_attacks(Them));
+ kingAdjacentZoneAttacksCount[Them] = kingAttackersWeight[Them] = 0;
+ }
+ else
+ kingRing[Us] = kingAttackersCount[Them] = 0;
+ }
+
+
+ // evaluate_pieces() assigns bonuses and penalties to the pieces of a given
+ // color and type.
+
+ template<Tracing T> template<Color Us, PieceType Pt>
+ Score Evaluation<T>::evaluate_pieces() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
+ : Rank5BB | Rank4BB | Rank3BB);
+ const Square* pl = pos.squares<Pt>(Us);
+
+ Bitboard b, bb;
+ Square s;
+ Score score = SCORE_ZERO;
+
+ attackedBy[Us][Pt] = 0;
+
+ if (Pt == QUEEN)
+ attackedBy[Us][QUEEN_DIAGONAL] = 0;
+
+ while ((s = *pl++) != SQ_NONE)
+ {
+ // Find attacked squares, including x-ray attacks for bishops and rooks
+ b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
+ : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
+ : pos.attacks_from<Pt>(s);
+
+ if (pos.pinned_pieces(Us) & s)
+ b &= LineBB[pos.square<KING>(Us)][s];
+
+ attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b;
+ attackedBy[Us][ALL_PIECES] |= attackedBy[Us][Pt] |= b;
+
+ if (Pt == QUEEN)
+ attackedBy[Us][QUEEN_DIAGONAL] |= b & PseudoAttacks[BISHOP][s];
+
+ if (b & kingRing[Them])
+ {
+ kingAttackersCount[Us]++;
+ kingAttackersWeight[Us] += KingAttackWeights[Pt];
+ kingAdjacentZoneAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
+ }
+
+ int mob = popcount(b & mobilityArea[Us]);
+
+ mobility[Us] += MobilityBonus[Pt - 2][mob];
+
+ // Bonus for this piece as a king protector
+ score += KingProtector[Pt - 2] * distance(s, pos.square<KING>(Us));
+
+ if (Pt == BISHOP || Pt == KNIGHT)
+ {
+ // Bonus for outpost squares
+ bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
+ if (bb & s)
+ score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2;
+ else
+ {
+ bb &= b & ~pos.pieces(Us);
+ if (bb)
+ score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)];
+ }
+
+ // Bonus when behind a pawn
+ if ( relative_rank(Us, s) < RANK_5
+ && (pos.pieces(PAWN) & (s + pawn_push(Us))))
+ score += MinorBehindPawn;
+
+ if (Pt == BISHOP)
+ {
+ // Penalty for pawns on the same color square as the bishop
+ score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s);
+
+ // Bonus for bishop on a long diagonal which can "see" both center squares
+ if (more_than_one(Center & (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) | s)))
+ score += LongRangedBishop;
+ }
+
+ // An important Chess960 pattern: A cornered bishop blocked by a friendly
+ // pawn diagonally in front of it is a very serious problem, especially
+ // when that pawn is also blocked.
+ if ( Pt == BISHOP
+ && pos.is_chess960()
+ && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
+ {
+ Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
+ if (pos.piece_on(s + d) == make_piece(Us, PAWN))
+ score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
+ : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
+ : TrappedBishopA1H1;
+ }
+ }
+
+ if (Pt == ROOK)
+ {
+ // Bonus for aligning with enemy pawns on the same rank/file
+ if (relative_rank(Us, s) >= RANK_5)
+ score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
+
+ // Bonus when on an open or semi-open file
+ if (pe->semiopen_file(Us, file_of(s)))
+ score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
+
+ // Penalty when trapped by the king, even more if the king cannot castle
+ else if (mob <= 3)
+ {
+ Square ksq = pos.square<KING>(Us);
+
+ if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
+ && !pe->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
+ score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
+ }
+ }
+
+ if (Pt == QUEEN)
+ {
+ // Penalty if any relative pin or discovered attack against the queen
+ Bitboard pinners;
+ if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, pinners))
+ score -= WeakQueen;
+ }
+ }
+
+ if (T)
+ Trace::add(Pt, Us, score);
+
+ return score;
+ }
+
+
+ // evaluate_king() assigns bonuses and penalties to a king of a given color
+
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::evaluate_king() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
+ : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
+
+ const Square ksq = pos.square<KING>(Us);
+ Bitboard weak, b, b1, b2, safe, unsafeChecks;
+
+ // King shelter and enemy pawns storm
+ Score score = pe->king_safety<Us>(pos, ksq);
+
+ // Main king safety evaluation
+ if (kingAttackersCount[Them] > (1 - pos.count<QUEEN>(Them)))
+ {
+ // Attacked squares defended at most once by our queen or king
+ weak = attackedBy[Them][ALL_PIECES]
+ & ~attackedBy2[Us]
+ & (attackedBy[Us][KING] | attackedBy[Us][QUEEN] | ~attackedBy[Us][ALL_PIECES]);
+
+ int kingDanger = unsafeChecks = 0;
+
+ // Analyse the safe enemy's checks which are possible on next move
+ safe = ~pos.pieces(Them);
+ safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
+
+ b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
+ b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
+
+ // Enemy queen safe checks
+ if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
+ kingDanger += QueenSafeCheck;
+
+ b1 &= attackedBy[Them][ROOK];
+ b2 &= attackedBy[Them][BISHOP];
+
+ // Enemy rooks checks
+ if (b1 & safe)
+ kingDanger += RookSafeCheck;
+ else
+ unsafeChecks |= b1;
+
+ // Enemy bishops checks
+ if (b2 & safe)
+ kingDanger += BishopSafeCheck;
+ else
+ unsafeChecks |= b2;
+
+ // Enemy knights checks
+ b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
+ if (b & safe)
+ kingDanger += KnightSafeCheck;
+ else
+ unsafeChecks |= b;
+
+ // Unsafe or occupied checking squares will also be considered, as long as
+ // the square is in the attacker's mobility area.
+ unsafeChecks &= mobilityArea[Them];
+
+ kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ + 102 * kingAdjacentZoneAttacksCount[Them]
+ + 191 * popcount(kingRing[Us] & weak)
+ + 143 * popcount(pos.pinned_pieces(Us) | unsafeChecks)
+ - 848 * !pos.count<QUEEN>(Them)
+ - 9 * mg_value(score) / 8
+ + 40;
+
+ // Transform the kingDanger units into a Score, and subtract it from the evaluation
+ if (kingDanger > 0)
+ {
+ int mobilityDanger = mg_value(mobility[Them] - mobility[Us]);
+ kingDanger = std::max(0, kingDanger + mobilityDanger);
+ score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
+ }
+ }
+
+ // King tropism: firstly, find squares that opponent attacks in our king flank
+ File kf = file_of(ksq);
+ b = attackedBy[Them][ALL_PIECES] & KingFlank[kf] & Camp;
+
+ assert(((Us == WHITE ? b << 4 : b >> 4) & b) == 0);
+ assert(popcount(Us == WHITE ? b << 4 : b >> 4) == popcount(b));
+
+ // Secondly, add the squares which are attacked twice in that flank and
+ // which are not defended by our pawns.
+ b = (Us == WHITE ? b << 4 : b >> 4)
+ | (b & attackedBy2[Them] & ~attackedBy[Us][PAWN]);
+
+ score -= CloseEnemies * popcount(b);
+
+ // Penalty when our king is on a pawnless flank
+ if (!(pos.pieces(PAWN) & KingFlank[kf]))
+ score -= PawnlessFlank;
+
+ if (T)
+ Trace::add(KING, Us, score);
+
+ return score;
+ }
+
+
+ // evaluate_threats() assigns bonuses according to the types of the attacking
+ // and the attacked pieces.
+
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::evaluate_threats() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
+ const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
+ const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
+
+ Bitboard b, weak, defended, stronglyProtected, safeThreats;
+ Score score = SCORE_ZERO;
+
+ // Non-pawn enemies attacked by a pawn
+ weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & attackedBy[Us][PAWN];
+
+ if (weak)
+ {
+ b = pos.pieces(Us, PAWN) & ( ~attackedBy[Them][ALL_PIECES]
+ | attackedBy[Us][ALL_PIECES]);
+
+ safeThreats = (shift<Right>(b) | shift<Left>(b)) & weak;
+
+ score += ThreatBySafePawn * popcount(safeThreats);
+ }
+
+ // Squares strongly protected by the opponent, either because they attack the
+ // square with a pawn, or because they attack the square twice and we don't.
+ stronglyProtected = attackedBy[Them][PAWN]
+ | (attackedBy2[Them] & ~attackedBy2[Us]);
+
+ // Non-pawn enemies, strongly protected
+ defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN))
+ & stronglyProtected;
+
+ // Enemies not strongly protected and under our attack
+ weak = pos.pieces(Them)
+ & ~stronglyProtected
+ & attackedBy[Us][ALL_PIECES];
+
+ // Add a bonus according to the kind of attacking pieces
+ if (defended | weak)
+ {
+ b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
+ while (b)
+ {
+ Square s = pop_lsb(&b);
+ score += ThreatByMinor[type_of(pos.piece_on(s))];
+ if (type_of(pos.piece_on(s)) != PAWN)
+ score += ThreatByRank * (int)relative_rank(Them, s);
+ }
+
+ b = (pos.pieces(Them, QUEEN) | weak) & attackedBy[Us][ROOK];
+ while (b)
+ {
+ Square s = pop_lsb(&b);
+ score += ThreatByRook[type_of(pos.piece_on(s))];
+ if (type_of(pos.piece_on(s)) != PAWN)
+ score += ThreatByRank * (int)relative_rank(Them, s);
+ }
+
+ score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]);
+
+ b = weak & attackedBy[Us][KING];
+ if (b)
+ score += ThreatByKing[more_than_one(b)];
+ }
+
+ // Bonus for opponent unopposed weak pawns
+ if (pos.pieces(Us, ROOK, QUEEN))
+ score += WeakUnopposedPawn * pe->weak_unopposed(Them);
+
+ // Find squares where our pawns can push on the next move
+ b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
+ b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
+
+ // Keep only the squares which are not completely unsafe
+ b &= ~attackedBy[Them][PAWN]
+ & (attackedBy[Us][ALL_PIECES] | ~attackedBy[Them][ALL_PIECES]);
+
+ // Add a bonus for each new pawn threats from those squares
+ b = (shift<Left>(b) | shift<Right>(b))
+ & pos.pieces(Them)
+ & ~attackedBy[Us][PAWN];
+
+ score += ThreatByPawnPush * popcount(b);
+
+ // Add a bonus for safe slider attack threats on opponent queen
+ safeThreats = ~pos.pieces(Us) & ~attackedBy2[Them] & attackedBy2[Us];
+ b = (attackedBy[Us][BISHOP] & attackedBy[Them][QUEEN_DIAGONAL])
+ | (attackedBy[Us][ROOK ] & attackedBy[Them][QUEEN] & ~attackedBy[Them][QUEEN_DIAGONAL]);
+
+ score += ThreatByAttackOnQueen * popcount(b & safeThreats);
+
+ if (T)
+ Trace::add(THREAT, Us, score);
+
+ return score;
+ }
+
+ // helper used by evaluate_passed_pawns to cap the distance
+ template<Tracing T>
+ int Evaluation<T>::king_distance(Color c, Square s) {
+ return std::min(distance(pos.square<KING>(c), s), 5);
+ }
+
+ // evaluate_passed_pawns() evaluates the passed pawns and candidate passed
+ // pawns of the given color.
+
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::evaluate_passed_pawns() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Direction Up = (Us == WHITE ? NORTH : SOUTH);
+
+ Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
+ Score score = SCORE_ZERO;
+
+ b = pe->passed_pawns(Us);
+
+ while (b)
+ {
+ Square s = pop_lsb(&b);
+
+ assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
+
+ bb = forward_file_bb(Us, s) & (attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
+ score -= HinderPassedPawn * popcount(bb);
+
+ int r = relative_rank(Us, s);
+ int rr = RankFactor[r];
+
+ Value mbonus = Passed[MG][r], ebonus = Passed[EG][r];
+
+ if (rr)
+ {
+ Square blockSq = s + Up;
+
+ // Adjust bonus based on the king's proximity
+ ebonus += (king_distance(Them, blockSq) * 5 - king_distance(Us, blockSq) * 2) * rr;
+
+ // If blockSq is not the queening square then consider also a second push
+ if (r != RANK_7)
+ ebonus -= king_distance(Us, blockSq + Up) * rr;
+
+ // If the pawn is free to advance, then increase the bonus
+ if (pos.empty(blockSq))
+ {
+ // If there is a rook or queen attacking/defending the pawn from behind,
+ // consider all the squaresToQueen. Otherwise consider only the squares
+ // in the pawn's path attacked or occupied by the enemy.
+ defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
+
+ bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
+
+ if (!(pos.pieces(Us) & bb))
+ defendedSquares &= attackedBy[Us][ALL_PIECES];
+
+ if (!(pos.pieces(Them) & bb))
+ unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
+
+ // If there aren't any enemy attacks, assign a big bonus. Otherwise
+ // assign a smaller bonus if the block square isn't attacked.
+ int k = !unsafeSquares ? 18 : !(unsafeSquares & blockSq) ? 8 : 0;
+
+ // If the path to the queen is fully defended, assign a big bonus.
+ // Otherwise assign a smaller bonus if the block square is defended.
+ if (defendedSquares == squaresToQueen)
+ k += 6;
+
+ else if (defendedSquares & blockSq)
+ k += 4;
+
+ mbonus += k * rr, ebonus += k * rr;
+ }
+ else if (pos.pieces(Us) & blockSq)
+ mbonus += rr + r * 2, ebonus += rr + r * 2;
+ } // rr != 0
+
+ // Scale down bonus for candidate passers which need more than one
+ // pawn push to become passed or have a pawn in front of them.
+ if (!pos.pawn_passed(Us, s + Up) || (pos.pieces(PAWN) & forward_file_bb(Us, s)))
+ mbonus /= 2, ebonus /= 2;
+
+ score += make_score(mbonus, ebonus) + PassedFile[file_of(s)];
+ }
+
+ if (T)
+ Trace::add(PASSED, Us, score);
+
+ return score;
+ }
+
+
+ // 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 multiplied by a weight. The aim is to
+ // improve play on game opening.
+
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::evaluate_space() {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard SpaceMask =
+ Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
+ : CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
+
+ // Find the safe squares for our pieces inside the area defined by
+ // SpaceMask. A square is unsafe if it is attacked by an enemy
+ // pawn, or if it is undefended and attacked by an enemy piece.
+ Bitboard safe = SpaceMask
+ & ~pos.pieces(Us, PAWN)
+ & ~attackedBy[Them][PAWN]
+ & (attackedBy[Us][ALL_PIECES] | ~attackedBy[Them][ALL_PIECES]);
+
+ // Find all squares which are at most three squares behind some friendly pawn
+ Bitboard behind = pos.pieces(Us, PAWN);
+ behind |= (Us == WHITE ? behind >> 8 : behind << 8);
+ behind |= (Us == WHITE ? behind >> 16 : behind << 16);
+
+ // Since SpaceMask[Us] is fully on our half of the board...
+ assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
+
+ // ...count safe + (behind & safe) with a single popcount.
+ int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
+ int weight = pos.count<ALL_PIECES>(Us) - 2 * pe->open_files();
+
+ return make_score(bonus * weight * weight / 16, 0);
+ }
+
+
+ // evaluate_initiative() computes the initiative correction value for the
+ // position, i.e., second order bonus/malus based on the known attacking/defending
+ // status of the players.
+
+ template<Tracing T>
+ Score Evaluation<T>::evaluate_initiative(Value eg) {
+
+ int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
+ - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
+ bool bothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide);
+
+ // Compute the initiative bonus for the attacking side
+ int initiative = 8 * (pe->pawn_asymmetry() + kingDistance - 17) + 12 * pos.count<PAWN>() + 16 * bothFlanks;
+
+ // Now apply the bonus: note that we find the attacking side by extracting
+ // the sign of the endgame value, and that we carefully cap the bonus so
+ // that the endgame score will never change sign after the bonus.
+ int v = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg));
+
+ if (T)
+ Trace::add(INITIATIVE, make_score(0, v));
+
+ return make_score(0, v);
+ }
+
+
+ // evaluate_scale_factor() computes the scale factor for the winning side
+
+ template<Tracing T>
+ ScaleFactor Evaluation<T>::evaluate_scale_factor(Value eg) {
+
+ Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
+ ScaleFactor sf = me->scale_factor(pos, strongSide);
+
+ // If we don't already have an unusual scale factor, check for certain
+ // types of endgames, and use a lower scale for those.
+ if (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)
+ {
+ if (pos.opposite_bishops())
+ {
+ // Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
+ // is almost a draw, in case of KBP vs KB, it is even more a draw.
+ if ( pos.non_pawn_material(WHITE) == BishopValueMg
+ && pos.non_pawn_material(BLACK) == BishopValueMg)
+ return more_than_one(pos.pieces(PAWN)) ? ScaleFactor(31) : ScaleFactor(9);
+
+ // Endgame with opposite-colored bishops, but also other pieces. Still
+ // a bit drawish, but not as drawish as with only the two bishops.
+ return ScaleFactor(46);
+ }
+ // Endings where weaker side can place his king in front of the opponent's
+ // pawns are drawish.
+ else if ( abs(eg) <= BishopValueEg
+ && pos.count<PAWN>(strongSide) <= 2
+ && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
+ return ScaleFactor(37 + 7 * pos.count<PAWN>(strongSide));
+ }
+
+ return sf;
+ }
+
+
+ // value() is the main function of the class. It computes the various parts of
+ // the evaluation and returns the value of the position from the point of view
+ // of the side to move.
+
+ template<Tracing T>
+ Value Evaluation<T>::value() {
+
+ assert(!pos.checkers());
+
+ // Probe the material hash table
+ me = Material::probe(pos);
+
+ // If we have a specialized evaluation function for the current material
+ // configuration, call it and return.
+ if (me->specialized_eval_exists())
+ return me->evaluate(pos);
+
+ // Initialize score by reading the incrementally updated scores included in
+ // the position object (material + piece square tables) and the material
+ // imbalance. Score is computed internally from the white point of view.
+ Score score = pos.psq_score() + me->imbalance() + Eval::Contempt;
+
+ // Probe the pawn hash table
+ pe = Pawns::probe(pos);
+ score += pe->pawns_score();
+
+ // Early exit if score is high
+ Value v = (mg_value(score) + eg_value(score)) / 2;
+ if (abs(v) > LazyThreshold)
+ return pos.side_to_move() == WHITE ? v : -v;
+
+ // Main evaluation begins here
+
+ initialize<WHITE>();
+ initialize<BLACK>();
+
+ score += evaluate_pieces<WHITE, KNIGHT>() - evaluate_pieces<BLACK, KNIGHT>();
+ score += evaluate_pieces<WHITE, BISHOP>() - evaluate_pieces<BLACK, BISHOP>();
+ score += evaluate_pieces<WHITE, ROOK >() - evaluate_pieces<BLACK, ROOK >();
+ score += evaluate_pieces<WHITE, QUEEN >() - evaluate_pieces<BLACK, QUEEN >();
+
+ score += mobility[WHITE] - mobility[BLACK];
+
+ score += evaluate_king<WHITE>()
+ - evaluate_king<BLACK>();
+
+ score += evaluate_threats<WHITE>()
+ - evaluate_threats<BLACK>();
+
+ score += evaluate_passed_pawns<WHITE>()
+ - evaluate_passed_pawns<BLACK>();
+
+ if (pos.non_pawn_material() >= SpaceThreshold)
+ score += evaluate_space<WHITE>()
+ - evaluate_space<BLACK>();
+
+ score += evaluate_initiative(eg_value(score));
+
+ // Interpolate between a middlegame and a (scaled by 'sf') endgame score
+ ScaleFactor sf = evaluate_scale_factor(eg_value(score));
+ v = mg_value(score) * int(me->game_phase())
+ + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
+
+ v /= int(PHASE_MIDGAME);
+
+ // In case of tracing add all remaining individual evaluation terms
+ if (T)
+ {
+ Trace::add(MATERIAL, pos.psq_score());
+ Trace::add(IMBALANCE, me->imbalance());
+ Trace::add(PAWN, pe->pawns_score());
+ Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
+ if (pos.non_pawn_material() >= SpaceThreshold)
+ Trace::add(SPACE, evaluate_space<WHITE>()
+ , evaluate_space<BLACK>());
+ Trace::add(TOTAL, score);
+ }
+
+ return pos.side_to_move() == WHITE ? v : -v; // Side to move point of view
+ }
+
+} // namespace
+
+Score Eval::Contempt = SCORE_ZERO;
+
+/// evaluate() is the evaluator for the outer world. It returns a static evaluation
+/// of the position from the point of view of the side to move.
+
+Value Eval::evaluate(const Position& pos)
+{
+ return Evaluation<>(pos).value() + Eval::Tempo;
+}
+
+/// trace() is like evaluate(), but instead of returning a value, it returns
+/// a string (suitable for outputting to stdout) that contains the detailed
+/// descriptions and values of each evaluation term. Useful for debugging.
+
+std::string Eval::trace(const Position& pos) {
+
+ std::memset(scores, 0, sizeof(scores));
+
+ Value v = Evaluation<TRACE>(pos).value() + Eval::Tempo;
+ v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
+
+ std::stringstream ss;
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
+ << " Eval term | White | Black | Total \n"
+ << " | MG EG | MG EG | MG EG \n"
+ << "----------------+-------------+-------------+-------------\n"
+ << " Material | " << Term(MATERIAL)
+ << " Imbalance | " << Term(IMBALANCE)
+ << " Pawns | " << Term(PAWN)
+ << " Knights | " << Term(KNIGHT)
+ << " Bishops | " << Term(BISHOP)
+ << " Rooks | " << Term(ROOK)
+ << " Queens | " << Term(QUEEN)
+ << " Mobility | " << Term(MOBILITY)
+ << " King safety | " << Term(KING)
+ << " Threats | " << Term(THREAT)
+ << " Passed pawns | " << Term(PASSED)
+ << " Space | " << Term(SPACE)
+ << " Initiative | " << Term(INITIATIVE)
+ << "----------------+-------------+-------------+-------------\n"
+ << " Total | " << Term(TOTAL);
+
+ ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
+
+ return ss.str();
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef EVALUATE_H_INCLUDED
+#define EVALUATE_H_INCLUDED
+
+#include <string>
+
+#include "types.h"
+
+class Position;
+
+namespace Eval {
+
+const Value Tempo = Value(20); // Must be visible to search
+
+extern Score Contempt;
+
+std::string trace(const Position& pos);
+
+Value evaluate(const Position& pos);
+}
+
+#endif // #ifndef EVALUATE_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include "bitboard.h"
+#include "position.h"
+#include "search.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+namespace PSQT {
+ void init();
+}
+
+int main(int argc, char* argv[]) {
+
+ std::cout << engine_info() << std::endl;
+
+ UCI::init(Options);
+ PSQT::init();
+ Bitboards::init();
+ Position::init();
+ Bitbases::init();
+ Search::init();
+ Pawns::init();
+ Tablebases::init(Options["SyzygyPath"]);
+ TT.resize(Options["Hash"]);
+ Threads.set(Options["Threads"]);
+ Search::clear(); // After threads are up
+
+ UCI::loop(argc, argv);
+
+ Threads.set(0);
+ return 0;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm> // For std::min
+#include <cassert>
+#include <cstring> // For std::memset
+
+#include "material.h"
+#include "thread.h"
+
+using namespace std;
+
+namespace {
+
+ // Polynomial material imbalance parameters
+
+ const int QuadraticOurs[][PIECE_TYPE_NB] = {
+ // OUR PIECES
+ // pair pawn knight bishop rook queen
+ {1667 }, // Bishop pair
+ { 40, 0 }, // Pawn
+ { 32, 255, -3 }, // Knight OUR PIECES
+ { 0, 104, 4, 0 }, // Bishop
+ { -26, -2, 47, 105, -149 }, // Rook
+ {-189, 24, 117, 133, -134, -10 } // Queen
+ };
+
+ const int QuadraticTheirs[][PIECE_TYPE_NB] = {
+ // THEIR PIECES
+ // pair pawn knight bishop rook queen
+ { 0 }, // Bishop pair
+ { 36, 0 }, // Pawn
+ { 9, 63, 0 }, // Knight OUR PIECES
+ { 59, 65, 42, 0 }, // Bishop
+ { 46, 39, 24, -24, 0 }, // Rook
+ { 97, 100, -42, 137, 268, 0 } // Queen
+ };
+
+ // Endgame evaluation and scaling functions are accessed directly and not through
+ // the function maps because they correspond to more than one material hash key.
+ Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
+
+ Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
+ Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
+ Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
+ Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
+
+ // Helper used to detect a given material distribution
+ bool is_KXK(const Position& pos, Color us) {
+ return !more_than_one(pos.pieces(~us))
+ && pos.non_pawn_material(us) >= RookValueMg;
+ }
+
+ bool is_KBPsKs(const Position& pos, Color us) {
+ return pos.non_pawn_material(us) == BishopValueMg
+ && pos.count<BISHOP>(us) == 1
+ && pos.count<PAWN >(us) >= 1;
+ }
+
+ bool is_KQKRPs(const Position& pos, Color us) {
+ return !pos.count<PAWN>(us)
+ && pos.non_pawn_material(us) == QueenValueMg
+ && pos.count<QUEEN>(us) == 1
+ && pos.count<ROOK>(~us) == 1
+ && pos.count<PAWN>(~us) >= 1;
+ }
+
+ /// imbalance() calculates the imbalance by comparing the piece count of each
+ /// piece type for both colors.
+ template<Color Us>
+ int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+
+ int bonus = 0;
+
+ // Second-degree polynomial material imbalance, by Tord Romstad
+ for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
+ {
+ if (!pieceCount[Us][pt1])
+ continue;
+
+ int v = 0;
+
+ for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
+ v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
+ + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
+
+ bonus += pieceCount[Us][pt1] * v;
+ }
+
+ return bonus;
+ }
+
+} // namespace
+
+namespace Material {
+
+/// Material::probe() looks up the current position's material configuration in
+/// the material hash table. It returns a pointer to the Entry if the position
+/// is found. Otherwise a new Entry is computed and stored there, so we don't
+/// have to recompute all when the same material configuration occurs again.
+
+Entry* probe(const Position& pos) {
+
+ Key key = pos.material_key();
+ Entry* e = pos.this_thread()->materialTable[key];
+
+ if (e->key == key)
+ return e;
+
+ std::memset(e, 0, sizeof(Entry));
+ e->key = key;
+ e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
+
+ Value npm_w = pos.non_pawn_material(WHITE);
+ Value npm_b = pos.non_pawn_material(BLACK);
+ Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit));
+
+ // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
+ e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
+
+ // Let's look if we have a specialized evaluation function for this particular
+ // material configuration. Firstly we look for a fixed configuration one, then
+ // for a generic one if the previous search failed.
+ if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr)
+ return e;
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ if (is_KXK(pos, c))
+ {
+ e->evaluationFunction = &EvaluateKXK[c];
+ return e;
+ }
+
+ // OK, we didn't find any special evaluation function for the current material
+ // configuration. Is there a suitable specialized scaling function?
+ EndgameBase<ScaleFactor>* sf;
+
+ if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
+ {
+ e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
+ return e;
+ }
+
+ // We didn't find any specialized scaling function, so fall back on generic
+ // ones that refer to more than one material distribution. Note that in this
+ // case we don't return after setting the function.
+ for (Color c = WHITE; c <= BLACK; ++c)
+ {
+ if (is_KBPsKs(pos, c))
+ e->scalingFunction[c] = &ScaleKBPsK[c];
+
+ else if (is_KQKRPs(pos, c))
+ e->scalingFunction[c] = &ScaleKQKRPs[c];
+ }
+
+ if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
+ {
+ if (!pos.count<PAWN>(BLACK))
+ {
+ assert(pos.count<PAWN>(WHITE) >= 2);
+
+ e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
+ }
+ else if (!pos.count<PAWN>(WHITE))
+ {
+ assert(pos.count<PAWN>(BLACK) >= 2);
+
+ e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
+ }
+ else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
+ {
+ // This is a special case because we set scaling functions
+ // for both colors instead of only one.
+ e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
+ e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
+ }
+ }
+
+ // Zero or just one pawn makes it difficult to win, even with a small material
+ // advantage. This catches some trivial draws like KK, KBK and KNK and gives a
+ // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
+ if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
+ e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
+ npm_b <= BishopValueMg ? 4 : 14);
+
+ if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
+ e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
+ npm_w <= BishopValueMg ? 4 : 14);
+
+ if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg)
+ e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN;
+
+ if (pos.count<PAWN>(BLACK) == 1 && npm_b - npm_w <= BishopValueMg)
+ e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN;
+
+ // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
+ // for the bishop pair "extended piece", which allows us to be more flexible
+ // in defining bishop pair bonuses.
+ const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = {
+ { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
+ pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
+ { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
+ pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
+
+ e->value = int16_t((imbalance<WHITE>(PieceCount) - imbalance<BLACK>(PieceCount)) / 16);
+ return e;
+}
+
+} // namespace Material
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MATERIAL_H_INCLUDED
+#define MATERIAL_H_INCLUDED
+
+#include "endgame.h"
+#include "misc.h"
+#include "position.h"
+#include "types.h"
+
+namespace Material {
+
+/// Material::Entry contains various information about a material configuration.
+/// It contains a material imbalance 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.
+///
+/// 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.
+
+struct Entry {
+
+ Score imbalance() const { return make_score(value, value); }
+ Phase game_phase() const { return gamePhase; }
+ bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
+ Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
+
+ // 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 may also be a function which should be applied to
+ // the position. For instance, in KBP vs K endgames, the scaling function looks
+ // for rook pawns and wrong-colored bishops.
+ ScaleFactor scale_factor(const Position& pos, Color c) const {
+ ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
+ : SCALE_FACTOR_NONE;
+ return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
+ }
+
+ Key key;
+ EndgameBase<Value>* evaluationFunction;
+ EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
+ // side (e.g. KPKP, KBPsKs)
+ int16_t value;
+ uint8_t factor[COLOR_NB];
+ Phase gamePhase;
+};
+
+typedef HashTable<Entry, 8192> Table;
+
+Entry* probe(const Position& pos);
+
+} // namespace Material
+
+#endif // #ifndef MATERIAL_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef _WIN32
+#if _WIN32_WINNT < 0x0601
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
+#endif
+#include <windows.h>
+// The needed Windows API for processor groups could be missed from old Windows
+// versions, so instead of calling them directly (forcing the linker to resolve
+// the calls at compile time), try to load them at runtime. To do this we need
+// first to define the corresponding function pointers.
+extern "C" {
+typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
+typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
+typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
+}
+#endif
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include "misc.h"
+#include "thread.h"
+
+using namespace std;
+
+namespace {
+
+/// Version number. If Version is left empty, then compile date in the format
+/// DD-MM-YY and show in engine_info.
+const string Version = "9";
+
+/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
+/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
+/// can toggle the logging of std::cout and std:cin at runtime whilst preserving
+/// usual I/O functionality, all without changing a single line of code!
+/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
+
+struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
+
+ Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
+
+ int sync() override { return logBuf->pubsync(), buf->pubsync(); }
+ int overflow(int c) override { return log(buf->sputc((char)c), "<< "); }
+ int underflow() override { return buf->sgetc(); }
+ int uflow() override { return log(buf->sbumpc(), ">> "); }
+
+ streambuf *buf, *logBuf;
+
+ int log(int c, const char* prefix) {
+
+ static int last = '\n'; // Single log file
+
+ if (last == '\n')
+ logBuf->sputn(prefix, 3);
+
+ return last = logBuf->sputc((char)c);
+ }
+};
+
+class Logger {
+
+ Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
+ ~Logger() { start(""); }
+
+ ofstream file;
+ Tie in, out;
+
+public:
+ static void start(const std::string& fname) {
+
+ static Logger l;
+
+ if (!fname.empty() && !l.file.is_open())
+ {
+ l.file.open(fname, ifstream::out);
+ cin.rdbuf(&l.in);
+ cout.rdbuf(&l.out);
+ }
+ else if (fname.empty() && l.file.is_open())
+ {
+ cout.rdbuf(l.out.buf);
+ cin.rdbuf(l.in.buf);
+ l.file.close();
+ }
+ }
+};
+
+} // namespace
+
+/// engine_info() returns the full name of the current Stockfish version. This
+/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
+/// the program was compiled) or "Stockfish <Version>", depending on whether
+/// Version is empty.
+
+const string engine_info(bool to_uci) {
+
+ const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
+ string month, day, year;
+ stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
+
+ ss << "Stockfish " << Version << setfill('0');
+
+ if (Version.empty())
+ {
+ date >> month >> day >> year;
+ ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
+ }
+
+ ss << (Is64Bit ? " 64" : "")
+ << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
+ << (to_uci ? "\nid author ": " by ")
+ << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
+
+ return ss.str();
+}
+
+
+/// Debug functions used mainly to collect run-time statistics
+static int64_t hits[2], means[2];
+
+void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
+void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
+void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
+
+void dbg_print() {
+
+ if (hits[0])
+ cerr << "Total " << hits[0] << " Hits " << hits[1]
+ << " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
+
+ if (means[0])
+ cerr << "Total " << means[0] << " Mean "
+ << (double)means[1] / means[0] << endl;
+}
+
+
+/// Used to serialize access to std::cout to avoid multiple threads writing at
+/// the same time.
+
+std::ostream& operator<<(std::ostream& os, SyncCout sc) {
+
+ static Mutex m;
+
+ if (sc == IO_LOCK)
+ m.lock();
+
+ if (sc == IO_UNLOCK)
+ m.unlock();
+
+ return os;
+}
+
+
+/// Trampoline helper to avoid moving Logger to misc.h
+void start_logger(const std::string& fname) { Logger::start(fname); }
+
+
+/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
+/// function that doesn't stall the CPU waiting for data to be loaded from memory,
+/// which can be quite slow.
+#ifdef NO_PREFETCH
+
+void prefetch(void*) {}
+
+#else
+
+void prefetch(void* addr) {
+
+# if defined(__INTEL_COMPILER)
+ // This hack prevents prefetches from being optimized away by
+ // Intel compiler. Both MSVC and gcc seem not be affected by this.
+ __asm__ ("");
+# endif
+
+# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
+ _mm_prefetch((char*)addr, _MM_HINT_T0);
+# else
+ __builtin_prefetch(addr);
+# endif
+}
+
+#endif
+
+void prefetch2(void* addr) {
+
+ prefetch(addr);
+ prefetch((uint8_t*)addr + 64);
+}
+
+namespace WinProcGroup {
+
+#ifndef _WIN32
+
+void bindThisThread(size_t) {}
+
+#else
+
+/// get_group() retrieves logical processor information using Windows specific
+/// API and returns the best group id for the thread with index idx. Original
+/// code from Texel by Peter Österlund.
+
+int get_group(size_t idx) {
+
+ int threads = 0;
+ int nodes = 0;
+ int cores = 0;
+ DWORD returnLength = 0;
+ DWORD byteOffset = 0;
+
+ // Early exit if the needed API is not available at runtime
+ HMODULE k32 = GetModuleHandle("Kernel32.dll");
+ auto fun1 = (fun1_t)GetProcAddress(k32, "GetLogicalProcessorInformationEx");
+ if (!fun1)
+ return -1;
+
+ // First call to get returnLength. We expect it to fail due to null buffer
+ if (fun1(RelationAll, nullptr, &returnLength))
+ return -1;
+
+ // Once we know returnLength, allocate the buffer
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
+ ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
+
+ // Second call, now we expect to succeed
+ if (!fun1(RelationAll, buffer, &returnLength))
+ {
+ free(buffer);
+ return -1;
+ }
+
+ while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
+ {
+ if (ptr->Relationship == RelationNumaNode)
+ nodes++;
+
+ else if (ptr->Relationship == RelationProcessorCore)
+ {
+ cores++;
+ threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
+ }
+
+ byteOffset += ptr->Size;
+ ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
+ }
+
+ free(buffer);
+
+ std::vector<int> groups;
+
+ // Run as many threads as possible on the same node until core limit is
+ // reached, then move on filling the next node.
+ for (int n = 0; n < nodes; n++)
+ for (int i = 0; i < cores / nodes; i++)
+ groups.push_back(n);
+
+ // In case a core has more than one logical processor (we assume 2) and we
+ // have still threads to allocate, then spread them evenly across available
+ // nodes.
+ for (int t = 0; t < threads - cores; t++)
+ groups.push_back(t % nodes);
+
+ // If we still have more threads than the total number of logical processors
+ // then return -1 and let the OS to decide what to do.
+ return idx < groups.size() ? groups[idx] : -1;
+}
+
+
+/// bindThisThread() set the group affinity of the current thread
+
+void bindThisThread(size_t idx) {
+
+ // Use only local variables to be thread-safe
+ int group = get_group(idx);
+
+ if (group == -1)
+ return;
+
+ // Early exit if the needed API are not available at runtime
+ HMODULE k32 = GetModuleHandle("Kernel32.dll");
+ auto fun2 = (fun2_t)GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
+ auto fun3 = (fun3_t)GetProcAddress(k32, "SetThreadGroupAffinity");
+
+ if (!fun2 || !fun3)
+ return;
+
+ GROUP_AFFINITY affinity;
+ if (fun2(group, &affinity))
+ fun3(GetCurrentThread(), &affinity, nullptr);
+}
+
+#endif
+
+} // namespace WinProcGroup
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MISC_H_INCLUDED
+#define MISC_H_INCLUDED
+
+#include <cassert>
+#include <chrono>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "types.h"
+
+const std::string engine_info(bool to_uci = false);
+void prefetch(void* addr);
+void prefetch2(void* addr);
+void start_logger(const std::string& fname);
+
+void dbg_hit_on(bool b);
+void dbg_hit_on(bool c, bool b);
+void dbg_mean_of(int v);
+void dbg_print();
+
+typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
+
+inline TimePoint now() {
+ return std::chrono::duration_cast<std::chrono::milliseconds>
+ (std::chrono::steady_clock::now().time_since_epoch()).count();
+}
+
+template<class Entry, int Size>
+struct HashTable {
+ Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
+
+private:
+ std::vector<Entry> table = std::vector<Entry>(Size);
+};
+
+
+enum SyncCout { IO_LOCK, IO_UNLOCK };
+std::ostream& operator<<(std::ostream&, SyncCout);
+
+#define sync_cout std::cout << IO_LOCK
+#define sync_endl std::endl << IO_UNLOCK
+
+
+/// xorshift64star Pseudo-Random Number Generator
+/// This class is based on original code written and dedicated
+/// to the public domain by Sebastiano Vigna (2014).
+/// It has the following characteristics:
+///
+/// - Outputs 64-bit numbers
+/// - Passes Dieharder and SmallCrush test batteries
+/// - Does not require warm-up, no zeroland to escape
+/// - Internal state is a single 64-bit integer
+/// - Period is 2^64 - 1
+/// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
+///
+/// For further analysis see
+/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
+
+class PRNG {
+
+ uint64_t s;
+
+ uint64_t rand64() {
+
+ s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
+ return s * 2685821657736338717LL;
+ }
+
+public:
+ PRNG(uint64_t seed) : s(seed) { assert(seed); }
+
+ template<typename T> T rand() { return T(rand64()); }
+
+ /// Special generator used to fast init magic numbers.
+ /// Output values only have 1/8th of their bits set on average.
+ template<typename T> T sparse_rand()
+ { return T(rand64() & rand64() & rand64()); }
+};
+
+
+/// Under Windows it is not possible for a process to run on more than one
+/// logical processor group. This usually means to be limited to use max 64
+/// cores. To overcome this, some special platform specific API should be
+/// called to set group affinity for each thread. Original code from Texel by
+/// Peter Österlund.
+
+namespace WinProcGroup {
+ void bindThisThread(size_t idx);
+}
+
+#endif // #ifndef MISC_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+
+#include "movegen.h"
+#include "position.h"
+
+namespace {
+
+ template<CastlingRight Cr, bool Checks, bool Chess960>
+ ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us) {
+
+ static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
+
+ if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
+ return moveList;
+
+ // After castling, the rook and king final positions are the same in Chess960
+ // as they would be in standard chess.
+ Square kfrom = pos.square<KING>(us);
+ Square rfrom = pos.castling_rook_square(Cr);
+ Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1);
+ Bitboard enemies = pos.pieces(~us);
+
+ assert(!pos.checkers());
+
+ const Direction K = Chess960 ? kto > kfrom ? WEST : EAST
+ : KingSide ? WEST : EAST;
+
+ for (Square s = kto; s != kfrom; s += K)
+ if (pos.attackers_to(s) & enemies)
+ return moveList;
+
+ // Because we generate only legal castling moves we need to verify that
+ // when moving the castling rook we do not discover some hidden checker.
+ // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
+ if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
+ return moveList;
+
+ Move m = make<CASTLING>(kfrom, rfrom);
+
+ if (Checks && !pos.gives_check(m))
+ return moveList;
+
+ *moveList++ = m;
+ return moveList;
+ }
+
+
+ template<GenType Type, Direction D>
+ ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
+
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
+ *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
+
+ if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
+ {
+ *moveList++ = make<PROMOTION>(to - D, to, ROOK);
+ *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
+ *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
+ }
+
+ // Knight promotion is the only promotion that can give a direct check
+ // that's not already included in the queen promotion.
+ if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq))
+ *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
+ else
+ (void)ksq; // Silence a warning under MSVC
+
+ return moveList;
+ }
+
+
+ template<Color Us, GenType Type>
+ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
+
+ // Compute our parametrized parameters at compile time, named according to
+ // the point of view of white side.
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
+ const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
+ const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
+ const Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
+ const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
+
+ Bitboard emptySquares;
+
+ Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
+ Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
+
+ Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
+ Type == CAPTURES ? target : pos.pieces(Them));
+
+ // Single and double pawn pushes, no promotions
+ if (Type != CAPTURES)
+ {
+ emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
+
+ Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
+ Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
+
+ if (Type == EVASIONS) // Consider only blocking squares
+ {
+ b1 &= target;
+ b2 &= target;
+ }
+
+ if (Type == QUIET_CHECKS)
+ {
+ Square ksq = pos.square<KING>(Them);
+
+ b1 &= pos.attacks_from<PAWN>(ksq, Them);
+ b2 &= pos.attacks_from<PAWN>(ksq, Them);
+
+ // Add pawn pushes 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. Note that a possible discovery check
+ // promotion has been already generated amongst the captures.
+ Bitboard dcCandidates = pos.discovered_check_candidates();
+ if (pawnsNotOn7 & dcCandidates)
+ {
+ Bitboard dc1 = shift<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
+ Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;
+
+ b1 |= dc1;
+ b2 |= dc2;
+ }
+ }
+
+ while (b1)
+ {
+ Square to = pop_lsb(&b1);
+ *moveList++ = make_move(to - Up, to);
+ }
+
+ while (b2)
+ {
+ Square to = pop_lsb(&b2);
+ *moveList++ = make_move(to - Up - Up, to);
+ }
+ }
+
+ // Promotions and underpromotions
+ if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
+ {
+ if (Type == CAPTURES)
+ emptySquares = ~pos.pieces();
+
+ if (Type == EVASIONS)
+ emptySquares &= target;
+
+ Bitboard b1 = shift<Right>(pawnsOn7) & enemies;
+ Bitboard b2 = shift<Left >(pawnsOn7) & enemies;
+ Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
+
+ Square ksq = pos.square<KING>(Them);
+
+ while (b1)
+ moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ksq);
+
+ while (b2)
+ moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ksq);
+
+ while (b3)
+ moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
+ }
+
+ // Standard and en-passant captures
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
+ {
+ Bitboard b1 = shift<Right>(pawnsNotOn7) & enemies;
+ Bitboard b2 = shift<Left >(pawnsNotOn7) & enemies;
+
+ while (b1)
+ {
+ Square to = pop_lsb(&b1);
+ *moveList++ = make_move(to - Right, to);
+ }
+
+ while (b2)
+ {
+ Square to = pop_lsb(&b2);
+ *moveList++ = make_move(to - Left, to);
+ }
+
+ if (pos.ep_square() != SQ_NONE)
+ {
+ assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
+
+ // An en passant capture can be an evasion only if the checking piece
+ // is the double pushed pawn and so is in the target. Otherwise this
+ // is a discovery check and we are forced to do otherwise.
+ if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
+ return moveList;
+
+ b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
+
+ assert(b1);
+
+ while (b1)
+ *moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
+ }
+ }
+
+ return moveList;
+ }
+
+
+ template<PieceType Pt, bool Checks>
+ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
+ Bitboard target) {
+
+ assert(Pt != KING && Pt != PAWN);
+
+ const Square* pl = pos.squares<Pt>(us);
+
+ for (Square from = *pl; from != SQ_NONE; from = *++pl)
+ {
+ if (Checks)
+ {
+ if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
+ && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt)))
+ continue;
+
+ if (pos.discovered_check_candidates() & from)
+ continue;
+ }
+
+ Bitboard b = pos.attacks_from<Pt>(from) & target;
+
+ if (Checks)
+ b &= pos.check_squares(Pt);
+
+ while (b)
+ *moveList++ = make_move(from, pop_lsb(&b));
+ }
+
+ return moveList;
+ }
+
+
+ template<Color Us, GenType Type>
+ ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
+
+ const bool Checks = Type == QUIET_CHECKS;
+
+ moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
+ moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
+ moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
+ moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
+ moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
+
+ if (Type != QUIET_CHECKS && Type != EVASIONS)
+ {
+ Square ksq = pos.square<KING>(Us);
+ Bitboard b = pos.attacks_from<KING>(ksq) & target;
+ while (b)
+ *moveList++ = make_move(ksq, pop_lsb(&b));
+ }
+
+ if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
+ {
+ if (pos.is_chess960())
+ {
+ moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us);
+ moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us);
+ }
+ else
+ {
+ moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us);
+ moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us);
+ }
+ }
+
+ return moveList;
+ }
+
+} // namespace
+
+
+/// generate<CAPTURES> generates all pseudo-legal captures and queen
+/// promotions. Returns a pointer to the end of the move list.
+///
+/// generate<QUIETS> generates all pseudo-legal non-captures and
+/// underpromotions. Returns a pointer to the end of the move list.
+///
+/// generate<NON_EVASIONS> generates all pseudo-legal captures and
+/// non-captures. Returns a pointer to the end of the move list.
+
+template<GenType Type>
+ExtMove* generate(const Position& pos, ExtMove* moveList) {
+
+ assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
+ assert(!pos.checkers());
+
+ Color us = pos.side_to_move();
+
+ Bitboard target = Type == CAPTURES ? pos.pieces(~us)
+ : Type == QUIETS ? ~pos.pieces()
+ : Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
+
+ return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
+ : generate_all<BLACK, Type>(pos, moveList, target);
+}
+
+// Explicit template instantiations
+template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
+template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
+template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
+
+
+/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
+/// underpromotions that give check. Returns a pointer to the end of the move list.
+template<>
+ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
+
+ assert(!pos.checkers());
+
+ Color us = pos.side_to_move();
+ Bitboard dc = pos.discovered_check_candidates();
+
+ while (dc)
+ {
+ Square from = pop_lsb(&dc);
+ PieceType pt = type_of(pos.piece_on(from));
+
+ if (pt == PAWN)
+ continue; // Will be generated together with direct checks
+
+ Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces();
+
+ if (pt == KING)
+ b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];
+
+ while (b)
+ *moveList++ = make_move(from, pop_lsb(&b));
+ }
+
+ return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces())
+ : generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces());
+}
+
+
+/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
+/// to move is in check. Returns a pointer to the end of the move list.
+template<>
+ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
+
+ assert(pos.checkers());
+
+ Color us = pos.side_to_move();
+ Square ksq = pos.square<KING>(us);
+ Bitboard sliderAttacks = 0;
+ Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
+
+ // Find all the squares attacked by slider checkers. We will remove them from
+ // the king evasions in order to skip known illegal moves, which avoids any
+ // useless legality checks later on.
+ while (sliders)
+ {
+ Square checksq = pop_lsb(&sliders);
+ sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
+ }
+
+ // Generate evasions for king, capture and non capture moves
+ Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
+ while (b)
+ *moveList++ = make_move(ksq, pop_lsb(&b));
+
+ if (more_than_one(pos.checkers()))
+ return moveList; // Double check, only a king move can save the day
+
+ // Generate blocking evasions or captures of the checking piece
+ Square checksq = lsb(pos.checkers());
+ Bitboard target = between_bb(checksq, ksq) | checksq;
+
+ return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
+ : generate_all<BLACK, EVASIONS>(pos, moveList, target);
+}
+
+
+/// generate<LEGAL> generates all the legal moves in the given position
+
+template<>
+ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
+
+ Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
+ Square ksq = pos.square<KING>(pos.side_to_move());
+ ExtMove* cur = moveList;
+
+ moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
+ : generate<NON_EVASIONS>(pos, moveList);
+ while (cur != moveList)
+ if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
+ && !pos.legal(*cur))
+ *cur = (--moveList)->move;
+ else
+ ++cur;
+
+ return moveList;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MOVEGEN_H_INCLUDED
+#define MOVEGEN_H_INCLUDED
+
+#include <algorithm>
+
+#include "types.h"
+
+class Position;
+
+enum GenType {
+ CAPTURES,
+ QUIETS,
+ QUIET_CHECKS,
+ EVASIONS,
+ NON_EVASIONS,
+ LEGAL
+};
+
+struct ExtMove {
+ Move move;
+ int value;
+
+ operator Move() const { return move; }
+ void operator=(Move m) { move = m; }
+
+ // Inhibit unwanted implicit conversions to Move
+ // with an ambiguity that yields to a compile error.
+ operator float() const = delete;
+};
+
+inline bool operator<(const ExtMove& f, const ExtMove& s) {
+ return f.value < s.value;
+}
+
+template<GenType>
+ExtMove* generate(const Position& pos, ExtMove* moveList);
+
+/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
+/// in handy to use this class instead of the low level generate() function.
+template<GenType T>
+struct MoveList {
+
+ explicit MoveList(const Position& pos) : last(generate<T>(pos, moveList)) {}
+ const ExtMove* begin() const { return moveList; }
+ const ExtMove* end() const { return last; }
+ size_t size() const { return last - moveList; }
+ bool contains(Move move) const {
+ return std::find(begin(), end(), move) != end();
+ }
+
+private:
+ ExtMove moveList[MAX_MOVES], *last;
+};
+
+#endif // #ifndef MOVEGEN_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+
+#include "movepick.h"
+
+namespace {
+
+ enum Stages {
+ MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLERS, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES,
+ EVASION, EVASIONS_INIT, ALL_EVASIONS,
+ PROBCUT, PROBCUT_INIT, PROBCUT_CAPTURES,
+ QSEARCH_WITH_CHECKS, QCAPTURES_1_INIT, QCAPTURES_1, QCHECKS,
+ QSEARCH_NO_CHECKS, QCAPTURES_2_INIT, QCAPTURES_2,
+ QSEARCH_RECAPTURES, QRECAPTURES
+ };
+
+ // partial_insertion_sort() sorts moves in descending order up to and including
+ // a given limit. The order of moves smaller than the limit is left unspecified.
+ void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
+
+ for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
+ if (p->value >= limit)
+ {
+ ExtMove tmp = *p, *q;
+ *p = *++sortedEnd;
+ for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
+ *q = *(q - 1);
+ *q = tmp;
+ }
+ }
+
+ // pick_best() finds the best move in the range (begin, end) and moves it to
+ // the front. It's faster than sorting all the moves in advance when there
+ // are few moves, e.g., the possible captures.
+ Move pick_best(ExtMove* begin, ExtMove* end) {
+
+ std::swap(*begin, *std::max_element(begin, end));
+ return *begin;
+ }
+
+} // namespace
+
+
+/// Constructors of the MovePicker class. As arguments we pass 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 how important good move
+/// ordering is at the current node.
+
+/// MovePicker constructor for the main search
+MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
+ const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers_p)
+ : pos(p), mainHistory(mh), captureHistory(cph), contHistory(ch), countermove(cm),
+ killers{killers_p[0], killers_p[1]}, depth(d){
+
+ assert(d > DEPTH_ZERO);
+
+ stage = pos.checkers() ? EVASION : MAIN_SEARCH;
+ ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
+ stage += (ttMove == MOVE_NONE);
+}
+
+/// MovePicker constructor for quiescence search
+MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, Square s)
+ : pos(p), mainHistory(mh), captureHistory(cph) {
+
+ assert(d <= DEPTH_ZERO);
+
+ if (pos.checkers())
+ stage = EVASION;
+
+ else if (d > DEPTH_QS_NO_CHECKS)
+ stage = QSEARCH_WITH_CHECKS;
+
+ else if (d > DEPTH_QS_RECAPTURES)
+ stage = QSEARCH_NO_CHECKS;
+
+ else
+ {
+ stage = QSEARCH_RECAPTURES;
+ recaptureSquare = s;
+ return;
+ }
+
+ ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
+ stage += (ttMove == MOVE_NONE);
+}
+
+/// MovePicker constructor for ProbCut: we generate captures with SEE higher
+/// than or equal to the given threshold.
+MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
+ : pos(p), captureHistory(cph), threshold(th) {
+
+ assert(!pos.checkers());
+
+ stage = PROBCUT;
+ ttMove = ttm
+ && pos.pseudo_legal(ttm)
+ && pos.capture(ttm)
+ && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
+
+ stage += (ttMove == MOVE_NONE);
+}
+
+/// score() assigns a numerical value to each move in a list, used for sorting.
+/// Captures are ordered by Most Valuable Victim (MVV), preferring captures
+/// with a good history. Quiets are ordered using the histories.
+template<GenType Type>
+void MovePicker::score() {
+
+ static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
+
+ for (auto& m : *this)
+ if (Type == CAPTURES)
+ m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
+ + Value((*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]);
+
+ else if (Type == QUIETS)
+ m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ + (*contHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ + (*contHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ + (*contHistory[3])[pos.moved_piece(m)][to_sq(m)];
+
+ else // Type == EVASIONS
+ {
+ if (pos.capture(m))
+ m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
+ - Value(type_of(pos.moved_piece(m)));
+ else
+ m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - (1 << 28);
+ }
+}
+
+/// next_move() is the most important method of the MovePicker class. It returns
+/// a new pseudo legal move every time it is called, until there are no more moves
+/// left. It picks the move with the biggest value from a list of generated moves
+/// taking care not to return the ttMove if it has already been searched.
+
+Move MovePicker::next_move(bool skipQuiets) {
+
+ Move move;
+
+ switch (stage) {
+
+ case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS:
+ case QSEARCH_NO_CHECKS: case PROBCUT:
+ ++stage;
+ return ttMove;
+
+ case CAPTURES_INIT:
+ endBadCaptures = cur = moves;
+ endMoves = generate<CAPTURES>(pos, cur);
+ score<CAPTURES>();
+ ++stage;
+ /* fallthrough */
+
+ case GOOD_CAPTURES:
+ while (cur < endMoves)
+ {
+ move = pick_best(cur++, endMoves);
+ if (move != ttMove)
+ {
+ if (pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)))
+ return move;
+
+ // Losing capture, move it to the beginning of the array
+ *endBadCaptures++ = move;
+ }
+ }
+
+ ++stage;
+ move = killers[0]; // First killer move
+ if ( move != MOVE_NONE
+ && move != ttMove
+ && pos.pseudo_legal(move)
+ && !pos.capture(move))
+ return move;
+ /* fallthrough */
+
+ case KILLERS:
+ ++stage;
+ move = killers[1]; // Second killer move
+ if ( move != MOVE_NONE
+ && move != ttMove
+ && pos.pseudo_legal(move)
+ && !pos.capture(move))
+ return move;
+ /* fallthrough */
+
+ case COUNTERMOVE:
+ ++stage;
+ move = countermove;
+ if ( move != MOVE_NONE
+ && move != ttMove
+ && move != killers[0]
+ && move != killers[1]
+ && pos.pseudo_legal(move)
+ && !pos.capture(move))
+ return move;
+ /* fallthrough */
+
+ case QUIET_INIT:
+ cur = endBadCaptures;
+ endMoves = generate<QUIETS>(pos, cur);
+ score<QUIETS>();
+ partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
+ ++stage;
+ /* fallthrough */
+
+ case QUIET:
+ while ( cur < endMoves
+ && (!skipQuiets || cur->value >= VALUE_ZERO))
+ {
+ move = *cur++;
+
+ if ( move != ttMove
+ && move != killers[0]
+ && move != killers[1]
+ && move != countermove)
+ return move;
+ }
+ ++stage;
+ cur = moves; // Point to beginning of bad captures
+ /* fallthrough */
+
+ case BAD_CAPTURES:
+ if (cur < endBadCaptures)
+ return *cur++;
+ break;
+
+ case EVASIONS_INIT:
+ cur = moves;
+ endMoves = generate<EVASIONS>(pos, cur);
+ score<EVASIONS>();
+ ++stage;
+ /* fallthrough */
+
+ case ALL_EVASIONS:
+ while (cur < endMoves)
+ {
+ move = pick_best(cur++, endMoves);
+ if (move != ttMove)
+ return move;
+ }
+ break;
+
+ case PROBCUT_INIT:
+ cur = moves;
+ endMoves = generate<CAPTURES>(pos, cur);
+ score<CAPTURES>();
+ ++stage;
+ /* fallthrough */
+
+ case PROBCUT_CAPTURES:
+ while (cur < endMoves)
+ {
+ move = pick_best(cur++, endMoves);
+ if ( move != ttMove
+ && pos.see_ge(move, threshold))
+ return move;
+ }
+ break;
+
+ case QCAPTURES_1_INIT: case QCAPTURES_2_INIT:
+ cur = moves;
+ endMoves = generate<CAPTURES>(pos, cur);
+ score<CAPTURES>();
+ ++stage;
+ /* fallthrough */
+
+ case QCAPTURES_1: case QCAPTURES_2:
+ while (cur < endMoves)
+ {
+ move = pick_best(cur++, endMoves);
+ if (move != ttMove)
+ return move;
+ }
+ if (stage == QCAPTURES_2)
+ break;
+ cur = moves;
+ endMoves = generate<QUIET_CHECKS>(pos, cur);
+ ++stage;
+ /* fallthrough */
+
+ case QCHECKS:
+ while (cur < endMoves)
+ {
+ move = cur++->move;
+ if (move != ttMove)
+ return move;
+ }
+ break;
+
+ case QSEARCH_RECAPTURES:
+ cur = moves;
+ endMoves = generate<CAPTURES>(pos, cur);
+ score<CAPTURES>();
+ ++stage;
+ /* fallthrough */
+
+ case QRECAPTURES:
+ while (cur < endMoves)
+ {
+ move = pick_best(cur++, endMoves);
+ if (to_sq(move) == recaptureSquare)
+ return move;
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+
+ return MOVE_NONE;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MOVEPICK_H_INCLUDED
+#define MOVEPICK_H_INCLUDED
+
+#include <array>
+#include <limits>
+
+#include "movegen.h"
+#include "position.h"
+#include "types.h"
+
+/// StatBoards is a generic 2-dimensional array used to store various statistics
+template<int Size1, int Size2, typename T = int16_t>
+struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
+
+ void fill(const T& v) {
+ T* p = &(*this)[0][0];
+ std::fill(p, p + sizeof(*this) / sizeof(*p), v);
+ }
+
+ void update(T& entry, int bonus, const int D) {
+
+ assert(abs(bonus) <= D); // Ensure range is [-32 * D, 32 * D]
+ assert(abs(32 * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow
+
+ entry += bonus * 32 - entry * abs(bonus) / D;
+
+ assert(abs(entry) <= 32 * D);
+ }
+};
+
+/// StatCubes is a generic 3-dimensional array used to store various statistics
+template<int Size1, int Size2, int Size3, typename T = int16_t>
+struct StatCubes : public std::array<std::array<std::array<T, Size3>, Size2>, Size1> {
+
+ void fill(const T& v) {
+ T* p = &(*this)[0][0][0];
+ std::fill(p, p + sizeof(*this) / sizeof(*p), v);
+ }
+
+ void update(T& entry, int bonus, const int D, const int W) {
+
+ assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
+ assert(abs(W * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow
+
+ entry += bonus * W - entry * abs(bonus) / D;
+
+ assert(abs(entry) <= W * D);
+ }
+};
+
+/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from
+/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
+typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
+
+/// PieceToBoards are addressed by a move's [piece][to] information
+typedef StatBoards<PIECE_NB, SQUARE_NB> PieceToBoards;
+
+/// CapturePieceToBoards are addressed by a move's [piece][to][captured piece type] information
+typedef StatCubes<PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToBoards;
+
+/// ButterflyHistory records how often quiet moves have been successful or
+/// unsuccessful during the current search, and is used for reduction and move
+/// ordering decisions. It uses ButterflyBoards as backing store.
+struct ButterflyHistory : public ButterflyBoards {
+
+ void update(Color c, Move m, int bonus) {
+ StatBoards::update((*this)[c][from_to(m)], bonus, 324);
+ }
+};
+
+/// PieceToHistory is like ButterflyHistory, but is based on PieceToBoards
+struct PieceToHistory : public PieceToBoards {
+
+ void update(Piece pc, Square to, int bonus) {
+ StatBoards::update((*this)[pc][to], bonus, 936);
+ }
+};
+
+/// CapturePieceToHistory is like PieceToHistory, but is based on CapturePieceToBoards
+struct CapturePieceToHistory : public CapturePieceToBoards {
+
+ void update(Piece pc, Square to, PieceType captured, int bonus) {
+ StatCubes::update((*this)[pc][to][captured], bonus, 324, 2);
+ }
+};
+
+/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
+/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
+typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveHistory;
+
+/// ContinuationHistory is the history of a given pair of moves, usually the
+/// current one given a previous one. History table is based on PieceToBoards
+/// instead of ButterflyBoards.
+typedef StatBoards<PIECE_NB, SQUARE_NB, PieceToHistory> ContinuationHistory;
+
+
+/// MovePicker class is used to pick one pseudo legal move at a time from the
+/// current position. The most important method is next_move(), which returns a
+/// new pseudo legal move each time it is called, until there are no moves left,
+/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
+/// beta algorithm, MovePicker attempts to return the moves which are most likely
+/// to get a cut-off first.
+
+class MovePicker {
+public:
+ MovePicker(const MovePicker&) = delete;
+ MovePicker& operator=(const MovePicker&) = delete;
+ MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
+ MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, Square);
+ MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, Move*);
+ Move next_move(bool skipQuiets = false);
+
+private:
+ template<GenType> void score();
+ ExtMove* begin() { return cur; }
+ ExtMove* end() { return endMoves; }
+
+ const Position& pos;
+ const ButterflyHistory* mainHistory;
+ const CapturePieceToHistory* captureHistory;
+ const PieceToHistory** contHistory;
+ Move ttMove, countermove, killers[2];
+ ExtMove *cur, *endMoves, *endBadCaptures;
+ int stage;
+ Square recaptureSquare;
+ Value threshold;
+ Depth depth;
+ ExtMove moves[MAX_MOVES];
+};
+
+#endif // #ifndef MOVEPICK_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+
+#include "bitboard.h"
+#include "pawns.h"
+#include "position.h"
+#include "thread.h"
+
+namespace {
+
+ #define V Value
+ #define S(mg, eg) make_score(mg, eg)
+
+ // Isolated pawn penalty
+ const Score Isolated = S(13, 18);
+
+ // Backward pawn penalty
+ const Score Backward = S(24, 12);
+
+ // Connected pawn bonus by opposed, phalanx, #support and rank
+ Score Connected[2][2][3][RANK_NB];
+
+ // Doubled pawn penalty
+ const Score Doubled = S(18, 38);
+
+ // Weakness of our pawn shelter in front of the king by [isKingFile][distance from edge][rank].
+ // RANK_1 = 0 is used for files where we have no pawns or our pawn is behind our king.
+ const Value ShelterWeakness[][int(FILE_NB) / 2][RANK_NB] = {
+ { { V( 97), V(17), V( 9), V(44), V( 84), V( 87), V( 99) }, // Not On King file
+ { V(106), V( 6), V(33), V(86), V( 87), V(104), V(112) },
+ { V(101), V( 2), V(65), V(98), V( 58), V( 89), V(115) },
+ { V( 73), V( 7), V(54), V(73), V( 84), V( 83), V(111) } },
+ { { V(104), V(20), V( 6), V(27), V( 86), V( 93), V( 82) }, // On King file
+ { V(123), V( 9), V(34), V(96), V(112), V( 88), V( 75) },
+ { V(120), V(25), V(65), V(91), V( 66), V( 78), V(117) },
+ { V( 81), V( 2), V(47), V(63), V( 94), V( 93), V(104) } }
+ };
+
+ // Danger of enemy pawns moving toward our king by [type][distance from edge][rank].
+ // For the unopposed and unblocked cases, RANK_1 = 0 is used when opponent has
+ // no pawn on the given file, or their pawn is behind our king.
+ const Value StormDanger[][4][RANK_NB] = {
+ { { V( 0), V(-290), V(-274), V(57), V(41) }, // BlockedByKing
+ { V( 0), V( 60), V( 144), V(39), V(13) },
+ { V( 0), V( 65), V( 141), V(41), V(34) },
+ { V( 0), V( 53), V( 127), V(56), V(14) } },
+ { { V( 4), V( 73), V( 132), V(46), V(31) }, // Unopposed
+ { V( 1), V( 64), V( 143), V(26), V(13) },
+ { V( 1), V( 47), V( 110), V(44), V(24) },
+ { V( 0), V( 72), V( 127), V(50), V(31) } },
+ { { V( 0), V( 0), V( 79), V(23), V( 1) }, // BlockedByPawn
+ { V( 0), V( 0), V( 148), V(27), V( 2) },
+ { V( 0), V( 0), V( 161), V(16), V( 1) },
+ { V( 0), V( 0), V( 171), V(22), V(15) } },
+ { { V(22), V( 45), V( 104), V(62), V( 6) }, // Unblocked
+ { V(31), V( 30), V( 99), V(39), V(19) },
+ { V(23), V( 29), V( 96), V(41), V(15) },
+ { V(21), V( 23), V( 116), V(41), V(15) } }
+ };
+
+ // Max bonus for king safety. Corresponds to start position with all the pawns
+ // in front of the king and no enemy pawn on the horizon.
+ const Value MaxSafetyBonus = V(258);
+
+ #undef S
+ #undef V
+
+ template<Color Us>
+ Score evaluate(const Position& pos, Pawns::Entry* e) {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+ const Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
+ const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
+
+ Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
+ Bitboard lever, leverPush;
+ Square s;
+ bool opposed, backward;
+ Score score = SCORE_ZERO;
+ const Square* pl = pos.squares<PAWN>(Us);
+
+ Bitboard ourPawns = pos.pieces( Us, PAWN);
+ Bitboard theirPawns = pos.pieces(Them, PAWN);
+
+ e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
+ e->semiopenFiles[Us] = 0xFF;
+ e->kingSquares[Us] = SQ_NONE;
+ e->pawnAttacks[Us] = shift<Right>(ourPawns) | shift<Left>(ourPawns);
+ e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
+ e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
+
+ // Loop through all pawns of the current color and score each pawn
+ while ((s = *pl++) != SQ_NONE)
+ {
+ assert(pos.piece_on(s) == make_piece(Us, PAWN));
+
+ File f = file_of(s);
+
+ e->semiopenFiles[Us] &= ~(1 << f);
+ e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
+
+ // Flag the pawn
+ opposed = theirPawns & forward_file_bb(Us, s);
+ stoppers = theirPawns & passed_pawn_mask(Us, s);
+ lever = theirPawns & PawnAttacks[Us][s];
+ leverPush = theirPawns & PawnAttacks[Us][s + Up];
+ doubled = ourPawns & (s - Up);
+ neighbours = ourPawns & adjacent_files_bb(f);
+ phalanx = neighbours & rank_bb(s);
+ supported = neighbours & rank_bb(s - Up);
+
+ // A pawn is backward when it is behind all pawns of the same color on the
+ // adjacent files and cannot be safely advanced.
+ if (!neighbours || lever || relative_rank(Us, s) >= RANK_5)
+ backward = false;
+ else
+ {
+ // Find the backmost rank with neighbours or stoppers
+ b = rank_bb(backmost_sq(Us, neighbours | stoppers));
+
+ // The pawn is backward when it cannot safely progress to that rank:
+ // either there is a stopper in the way on this rank, or there is a
+ // stopper on adjacent file which controls the way to that rank.
+ backward = (b | shift<Up>(b & adjacent_files_bb(f))) & stoppers;
+
+ assert(!(backward && (forward_ranks_bb(Them, s + Up) & neighbours)));
+ }
+
+ // Passed pawns will be properly scored in evaluation because we need
+ // full attack info to evaluate them. Include also not passed pawns
+ // which could become passed after one or two pawn pushes when are
+ // not attacked more times than defended.
+ if ( !(stoppers ^ lever ^ leverPush)
+ && !(ourPawns & forward_file_bb(Us, s))
+ && popcount(supported) >= popcount(lever)
+ && popcount(phalanx) >= popcount(leverPush))
+ e->passedPawns[Us] |= s;
+
+ else if ( stoppers == SquareBB[s + Up]
+ && relative_rank(Us, s) >= RANK_5)
+ {
+ b = shift<Up>(supported) & ~theirPawns;
+ while (b)
+ if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
+ e->passedPawns[Us] |= s;
+ }
+
+ // Score this pawn
+ if (supported | phalanx)
+ score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)];
+
+ else if (!neighbours)
+ score -= Isolated, e->weakUnopposed[Us] += !opposed;
+
+ else if (backward)
+ score -= Backward, e->weakUnopposed[Us] += !opposed;
+
+ if (doubled && !supported)
+ score -= Doubled;
+ }
+
+ return score;
+ }
+
+} // namespace
+
+namespace Pawns {
+
+/// Pawns::init() initializes some tables needed by evaluation. Instead of using
+/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
+/// to reduce independent parameters and to allow easier tuning and better insight.
+
+void init() {
+
+ static const int Seed[RANK_NB] = { 0, 13, 24, 18, 76, 100, 175, 330 };
+
+ for (int opposed = 0; opposed <= 1; ++opposed)
+ for (int phalanx = 0; phalanx <= 1; ++phalanx)
+ for (int support = 0; support <= 2; ++support)
+ for (Rank r = RANK_2; r < RANK_8; ++r)
+ {
+ int v = 17 * support;
+ v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
+
+ Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
+ }
+}
+
+
+/// Pawns::probe() looks up the current position's pawns configuration in
+/// the pawns hash table. It returns a pointer to the Entry if the position
+/// is found. Otherwise a new Entry is computed and stored there, so we don't
+/// have to recompute all when the same pawns configuration occurs again.
+
+Entry* probe(const Position& pos) {
+
+ Key key = pos.pawn_key();
+ Entry* e = pos.this_thread()->pawnsTable[key];
+
+ if (e->key == key)
+ return e;
+
+ e->key = key;
+ e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
+ e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
+ e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]);
+ return e;
+}
+
+
+/// Entry::shelter_storm() calculates shelter and storm penalties for the file
+/// the king is on, as well as the two closest files.
+
+template<Color Us>
+Value Entry::shelter_storm(const Position& pos, Square ksq) {
+
+ const Color Them = (Us == WHITE ? BLACK : WHITE);
+
+ enum { BlockedByKing, Unopposed, BlockedByPawn, Unblocked };
+
+ Bitboard b = pos.pieces(PAWN) & (forward_ranks_bb(Us, ksq) | rank_bb(ksq));
+ Bitboard ourPawns = b & pos.pieces(Us);
+ Bitboard theirPawns = b & pos.pieces(Them);
+ Value safety = MaxSafetyBonus;
+ File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
+
+ for (File f = File(center - 1); f <= File(center + 1); ++f)
+ {
+ b = ourPawns & file_bb(f);
+ Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
+
+ b = theirPawns & file_bb(f);
+ Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
+
+ int d = std::min(f, ~f);
+ safety -= ShelterWeakness[f == file_of(ksq)][d][rkUs]
+ + StormDanger
+ [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :
+ rkUs == RANK_1 ? Unopposed :
+ rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
+ [d][rkThem];
+ }
+
+ return safety;
+}
+
+
+/// Entry::do_king_safety() calculates a bonus for king safety. It is called only
+/// when king square changes, which is about 20% of total king_safety() calls.
+
+template<Color Us>
+Score Entry::do_king_safety(const Position& pos, Square ksq) {
+
+ kingSquares[Us] = ksq;
+ castlingRights[Us] = pos.can_castle(Us);
+ int minKingPawnDistance = 0;
+
+ Bitboard pawns = pos.pieces(Us, PAWN);
+ if (pawns)
+ while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {}
+
+ Value bonus = shelter_storm<Us>(pos, ksq);
+
+ // If we can castle use the bonus after the castling if it is bigger
+ if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
+ bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
+
+ if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
+ bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
+
+ return make_score(bonus, -16 * minKingPawnDistance);
+}
+
+// Explicit template instantiation
+template Score Entry::do_king_safety<WHITE>(const Position& pos, Square ksq);
+template Score Entry::do_king_safety<BLACK>(const Position& pos, Square ksq);
+
+} // namespace Pawns
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PAWNS_H_INCLUDED
+#define PAWNS_H_INCLUDED
+
+#include "misc.h"
+#include "position.h"
+#include "types.h"
+
+namespace Pawns {
+
+/// Pawns::Entry contains various information about a pawn structure. A lookup
+/// to the pawn hash table (performed by calling the probe function) returns a
+/// pointer to an Entry object.
+
+struct Entry {
+
+ Score pawns_score() const { return score; }
+ Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
+ Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
+ Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
+ int weak_unopposed(Color c) const { return weakUnopposed[c]; }
+ int pawn_asymmetry() const { return asymmetry; }
+ int open_files() const { return openFiles; }
+
+ int semiopen_file(Color c, File f) const {
+ return semiopenFiles[c] & (1 << f);
+ }
+
+ int semiopen_side(Color c, File f, bool leftSide) const {
+ return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1));
+ }
+
+ int pawns_on_same_color_squares(Color c, Square s) const {
+ return pawnsOnSquares[c][bool(DarkSquares & s)];
+ }
+
+ template<Color Us>
+ Score king_safety(const Position& pos, Square ksq) {
+ return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us)
+ ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
+ }
+
+ template<Color Us>
+ Score do_king_safety(const Position& pos, Square ksq);
+
+ template<Color Us>
+ Value shelter_storm(const Position& pos, Square ksq);
+
+ Key key;
+ Score score;
+ Bitboard passedPawns[COLOR_NB];
+ Bitboard pawnAttacks[COLOR_NB];
+ Bitboard pawnAttacksSpan[COLOR_NB];
+ Square kingSquares[COLOR_NB];
+ Score kingSafety[COLOR_NB];
+ int weakUnopposed[COLOR_NB];
+ int castlingRights[COLOR_NB];
+ int semiopenFiles[COLOR_NB];
+ int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
+ int asymmetry;
+ int openFiles;
+};
+
+typedef HashTable<Entry, 16384> Table;
+
+void init();
+Entry* probe(const Position& pos);
+
+} // namespace Pawns
+
+#endif // #ifndef PAWNS_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef> // For offsetof()
+#include <cstring> // For std::memset, std::memcmp
+#include <iomanip>
+#include <sstream>
+
+#include "bitboard.h"
+#include "misc.h"
+#include "movegen.h"
+#include "position.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+using std::string;
+
+namespace PSQT {
+ extern Score psq[PIECE_NB][SQUARE_NB];
+}
+
+namespace Zobrist {
+
+ Key psq[PIECE_NB][SQUARE_NB];
+ Key enpassant[FILE_NB];
+ Key castling[CASTLING_RIGHT_NB];
+ Key side, noPawns;
+}
+
+namespace {
+
+const string PieceToChar(" PNBRQK pnbrqk");
+
+const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
+ B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
+
+// min_attacker() is a helper function used by see_ge() to locate the least
+// valuable attacker for the side to move, remove the attacker we just found
+// from the bitboards and scan for new X-ray attacks behind it.
+
+template<int Pt>
+PieceType min_attacker(const Bitboard* bb, Square to, Bitboard stmAttackers,
+ Bitboard& occupied, Bitboard& attackers) {
+
+ Bitboard b = stmAttackers & bb[Pt];
+ if (!b)
+ return min_attacker<Pt + 1>(bb, to, stmAttackers, occupied, attackers);
+
+ occupied ^= b & ~(b - 1);
+
+ if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
+ attackers |= attacks_bb<BISHOP>(to, occupied) & (bb[BISHOP] | bb[QUEEN]);
+
+ if (Pt == ROOK || Pt == QUEEN)
+ attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]);
+
+ attackers &= occupied; // After X-ray that may add already processed pieces
+ return (PieceType)Pt;
+}
+
+template<>
+PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
+ return KING; // No need to update bitboards: it is the last cycle
+}
+
+} // namespace
+
+
+/// operator<<(Position) returns an ASCII representation of the position
+
+std::ostream& operator<<(std::ostream& os, const Position& pos) {
+
+ os << "\n +---+---+---+---+---+---+---+---+\n";
+
+ for (Rank r = RANK_8; r >= RANK_1; --r)
+ {
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
+
+ os << " |\n +---+---+---+---+---+---+---+---+\n";
+ }
+
+ os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
+ << std::setfill('0') << std::setw(16) << pos.key()
+ << std::setfill(' ') << std::dec << "\nCheckers: ";
+
+ for (Bitboard b = pos.checkers(); b; )
+ os << UCI::square(pop_lsb(&b)) << " ";
+
+ if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
+ && !pos.can_castle(ANY_CASTLING))
+ {
+ StateInfo st;
+ Position p;
+ p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
+ Tablebases::ProbeState s1, s2;
+ Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
+ int dtz = Tablebases::probe_dtz(p, &s2);
+ os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
+ << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
+ }
+
+ return os;
+}
+
+
+/// Position::init() initializes at startup the various arrays used to compute
+/// hash keys.
+
+void Position::init() {
+
+ PRNG rng(1070372);
+
+ for (Piece pc : Pieces)
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ Zobrist::psq[pc][s] = rng.rand<Key>();
+
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ Zobrist::enpassant[f] = rng.rand<Key>();
+
+ for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
+ {
+ Zobrist::castling[cr] = 0;
+ Bitboard b = cr;
+ while (b)
+ {
+ Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
+ Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
+ }
+ }
+
+ Zobrist::side = rng.rand<Key>();
+ Zobrist::noPawns = rng.rand<Key>();
+}
+
+
+/// Position::set() 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.
+
+Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
+/*
+ A FEN string defines a particular position using only the ASCII character set.
+
+ A FEN string contains six fields separated by a space. The fields are:
+
+ 1) Piece placement (from white's perspective). Each rank is described, starting
+ with rank 8 and ending with rank 1. Within each rank, the contents of each
+ square are described from file A through file H. Following the Standard
+ Algebraic Notation (SAN), each piece is identified by a single letter taken
+ from the standard English names. White pieces are designated using upper-case
+ letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are
+ noted using digits 1 through 8 (the number of blank squares), and "/"
+ separates ranks.
+
+ 2) Active color. "w" means white moves next, "b" means black.
+
+ 3) Castling availability. If neither side can castle, this is "-". Otherwise,
+ this has one or more letters: "K" (White can castle kingside), "Q" (White
+ can castle queenside), "k" (Black can castle kingside), and/or "q" (Black
+ can castle queenside).
+
+ 4) En passant target square (in algebraic notation). If there's no en passant
+ target square, this is "-". If a pawn has just made a 2-square move, this
+ is the position "behind" the pawn. This is recorded only if there is a pawn
+ in position to make an en passant capture, and if there really is a pawn
+ that might have advanced two squares.
+
+ 5) Halfmove clock. This is the number of halfmoves since the last pawn advance
+ or capture. This is used to determine if a draw can be claimed under the
+ fifty-move rule.
+
+ 6) Fullmove number. The number of the full move. It starts at 1, and is
+ incremented after Black's move.
+*/
+
+ unsigned char col, row, token;
+ size_t idx;
+ Square sq = SQ_A8;
+ std::istringstream ss(fenStr);
+
+ std::memset(this, 0, sizeof(Position));
+ std::memset(si, 0, sizeof(StateInfo));
+ std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
+ st = si;
+
+ ss >> std::noskipws;
+
+ // 1. Piece placement
+ while ((ss >> token) && !isspace(token))
+ {
+ if (isdigit(token))
+ sq += (token - '0') * EAST; // Advance the given number of files
+
+ else if (token == '/')
+ sq += 2 * SOUTH;
+
+ else if ((idx = PieceToChar.find(token)) != string::npos)
+ {
+ put_piece(Piece(idx), sq);
+ ++sq;
+ }
+ }
+
+ // 2. Active color
+ ss >> token;
+ sideToMove = (token == 'w' ? WHITE : BLACK);
+ ss >> token;
+
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
+ // if an inner rook is associated with the castling right, the castling tag is
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
+ while ((ss >> token) && !isspace(token))
+ {
+ Square rsq;
+ Color c = islower(token) ? BLACK : WHITE;
+ Piece rook = make_piece(c, ROOK);
+
+ token = char(toupper(token));
+
+ if (token == 'K')
+ for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
+
+ else if (token == 'Q')
+ for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
+
+ else if (token >= 'A' && token <= 'H')
+ rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
+
+ else
+ continue;
+
+ set_castling_right(c, rsq);
+ }
+
+ // 4. En passant square. Ignore if no pawn capture is possible
+ if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
+ && ((ss >> row) && (row == '3' || row == '6')))
+ {
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+
+ if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
+ || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
+ st->epSquare = SQ_NONE;
+ }
+ else
+ st->epSquare = SQ_NONE;
+
+ // 5-6. Halfmove clock and fullmove number
+ ss >> std::skipws >> st->rule50 >> gamePly;
+
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
+ // handle also common incorrect FEN with fullmove = 0.
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+
+ chess960 = isChess960;
+ thisThread = th;
+ set_state(st);
+
+ assert(pos_is_ok());
+
+ return *this;
+}
+
+
+/// Position::set_castling_right() is a helper function used to set castling
+/// rights given the corresponding color and the rook starting square.
+
+void Position::set_castling_right(Color c, Square rfrom) {
+
+ Square kfrom = square<KING>(c);
+ CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
+ CastlingRight cr = (c | cs);
+
+ st->castlingRights |= cr;
+ castlingRightsMask[kfrom] |= cr;
+ castlingRightsMask[rfrom] |= cr;
+ castlingRookSquare[cr] = rfrom;
+
+ Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
+ Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
+
+ for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
+ if (s != kfrom && s != rfrom)
+ castlingPath[cr] |= s;
+
+ for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
+ if (s != kfrom && s != rfrom)
+ castlingPath[cr] |= s;
+}
+
+
+/// Position::set_check_info() sets king attacks to detect if a move gives check
+
+void Position::set_check_info(StateInfo* si) const {
+
+ si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinnersForKing[WHITE]);
+ si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinnersForKing[BLACK]);
+
+ Square ksq = square<KING>(~sideToMove);
+
+ si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
+ si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
+ si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
+ si->checkSquares[ROOK] = attacks_from<ROOK>(ksq);
+ si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
+ si->checkSquares[KING] = 0;
+}
+
+
+/// Position::set_state() computes the hash keys of the position, and other
+/// data that once computed is updated incrementally as moves are made.
+/// The function is only used when a new position is set up, and to verify
+/// the correctness of the StateInfo data when running in debug mode.
+
+void Position::set_state(StateInfo* si) const {
+
+ si->key = si->materialKey = 0;
+ si->pawnKey = Zobrist::noPawns;
+ si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
+ si->psq = SCORE_ZERO;
+ si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
+
+ set_check_info(si);
+
+ for (Bitboard b = pieces(); b; )
+ {
+ Square s = pop_lsb(&b);
+ Piece pc = piece_on(s);
+ si->key ^= Zobrist::psq[pc][s];
+ si->psq += PSQT::psq[pc][s];
+ }
+
+ if (si->epSquare != SQ_NONE)
+ si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
+
+ if (sideToMove == BLACK)
+ si->key ^= Zobrist::side;
+
+ si->key ^= Zobrist::castling[si->castlingRights];
+
+ for (Bitboard b = pieces(PAWN); b; )
+ {
+ Square s = pop_lsb(&b);
+ si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
+ }
+
+ for (Piece pc : Pieces)
+ {
+ if (type_of(pc) != PAWN && type_of(pc) != KING)
+ si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
+
+ for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
+ si->materialKey ^= Zobrist::psq[pc][cnt];
+ }
+}
+
+
+/// Position::set() is an overload to initialize the position object with
+/// the given endgame code string like "KBPKN". It is mainly a helper to
+/// get the material key out of an endgame code.
+
+Position& Position::set(const string& code, Color c, StateInfo* si) {
+
+ assert(code.length() > 0 && code.length() < 8);
+ assert(code[0] == 'K');
+
+ string sides[] = { code.substr(code.find('K', 1)), // Weak
+ code.substr(0, code.find('K', 1)) }; // Strong
+
+ std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
+
+ string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
+ + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
+
+ return set(fenStr, false, si, nullptr);
+}
+
+
+/// Position::fen() returns a FEN representation of the position. In case of
+/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
+
+const string Position::fen() const {
+
+ int emptyCnt;
+ std::ostringstream ss;
+
+ for (Rank r = RANK_8; r >= RANK_1; --r)
+ {
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ {
+ for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f)
+ ++emptyCnt;
+
+ if (emptyCnt)
+ ss << emptyCnt;
+
+ if (f <= FILE_H)
+ ss << PieceToChar[piece_on(make_square(f, r))];
+ }
+
+ if (r > RANK_1)
+ ss << '/';
+ }
+
+ ss << (sideToMove == WHITE ? " w " : " b ");
+
+ if (can_castle(WHITE_OO))
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K');
+
+ if (can_castle(WHITE_OOO))
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
+
+ if (can_castle(BLACK_OO))
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k');
+
+ if (can_castle(BLACK_OOO))
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
+
+ if (!can_castle(WHITE) && !can_castle(BLACK))
+ ss << '-';
+
+ ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
+ << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
+
+ return ss.str();
+}
+
+
+/// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
+/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
+/// slider if removing that piece from the board would result in a position where
+/// square 's' is attacked. For example, a king-attack blocking piece can be either
+/// a pinned or a discovered check piece, according if its color is the opposite
+/// or the same of the color of the slider.
+
+Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const {
+
+ Bitboard result = 0;
+ pinners = 0;
+
+ // Snipers are sliders that attack 's' when a piece is removed
+ Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
+ | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
+
+ while (snipers)
+ {
+ Square sniperSq = pop_lsb(&snipers);
+ Bitboard b = between_bb(s, sniperSq) & pieces();
+
+ if (!more_than_one(b))
+ {
+ result |= b;
+ if (b & pieces(color_of(piece_on(s))))
+ pinners |= sniperSq;
+ }
+ }
+ return result;
+}
+
+
+/// Position::attackers_to() computes a bitboard of all pieces which attack a
+/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
+
+Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
+
+ return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
+ | (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
+ | (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
+ | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
+ | (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
+ | (attacks_from<KING>(s) & pieces(KING));
+}
+
+
+/// Position::legal() tests whether a pseudo-legal move is legal
+
+bool Position::legal(Move m) const {
+
+ assert(is_ok(m));
+
+ Color us = sideToMove;
+ Square from = from_sq(m);
+
+ assert(color_of(moved_piece(m)) == us);
+ assert(piece_on(square<KING>(us)) == make_piece(us, KING));
+
+ // 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 (type_of(m) == ENPASSANT)
+ {
+ Square ksq = square<KING>(us);
+ Square to = to_sq(m);
+ Square capsq = to - pawn_push(us);
+ Bitboard occupied = (pieces() ^ from ^ capsq) | to;
+
+ assert(to == ep_square());
+ assert(moved_piece(m) == make_piece(us, PAWN));
+ assert(piece_on(capsq) == make_piece(~us, PAWN));
+ assert(piece_on(to) == NO_PIECE);
+
+ return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
+ && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
+ }
+
+ // If the moving piece is a king, check whether the destination
+ // square is attacked by the opponent. Castling moves are checked
+ // for legality during move generation.
+ if (type_of(piece_on(from)) == KING)
+ return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
+
+ // 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.
+ return !(pinned_pieces(us) & from)
+ || aligned(from, to_sq(m), square<KING>(us));
+}
+
+
+/// Position::pseudo_legal() takes a random move and tests whether the move is
+/// pseudo legal. It is used to validate moves from TT that can be corrupted
+/// due to SMP concurrent access or hash position key aliasing.
+
+bool Position::pseudo_legal(const Move m) const {
+
+ Color us = sideToMove;
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+ Piece pc = moved_piece(m);
+
+ // Use a slower but simpler function for uncommon cases
+ if (type_of(m) != NORMAL)
+ return MoveList<LEGAL>(*this).contains(m);
+
+ // Is not a promotion, so promotion piece must be empty
+ if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
+ return false;
+
+ // If the 'from' square is not occupied by a piece belonging to the side to
+ // move, the move is obviously not legal.
+ if (pc == NO_PIECE || color_of(pc) != us)
+ return false;
+
+ // The destination square cannot be occupied by a friendly piece
+ if (pieces(us) & to)
+ return false;
+
+ // Handle the special case of a pawn move
+ if (type_of(pc) == PAWN)
+ {
+ // We have already handled promotion moves, so destination
+ // cannot be on the 8th/1st rank.
+ if (rank_of(to) == relative_rank(us, RANK_8))
+ return false;
+
+ if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
+ && !((from + pawn_push(us) == to) && empty(to)) // Not a single push
+ && !( (from + 2 * pawn_push(us) == to) // Not a double push
+ && (rank_of(from) == relative_rank(us, RANK_2))
+ && empty(to)
+ && empty(to - pawn_push(us))))
+ return false;
+ }
+ else if (!(attacks_from(type_of(pc), from) & to))
+ return false;
+
+ // Evasions generator already takes care to avoid some kind of illegal moves
+ // and legal() relies on this. We therefore have to take care that the same
+ // kind of moves are filtered out here.
+ if (checkers())
+ {
+ if (type_of(pc) != KING)
+ {
+ // Double check? In this case a king move is required
+ if (more_than_one(checkers()))
+ return false;
+
+ // Our move must be a blocking evasion or a capture of the checking piece
+ if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
+ return false;
+ }
+ // In case of king moves under check we have to remove king so as to catch
+ // invalid moves like b1a1 when opposite queen is on c1.
+ else if (attackers_to(to, pieces() ^ from) & pieces(~us))
+ return false;
+ }
+
+ return true;
+}
+
+
+/// Position::gives_check() tests whether a pseudo-legal move gives a check
+
+bool Position::gives_check(Move m) const {
+
+ assert(is_ok(m));
+ assert(color_of(moved_piece(m)) == sideToMove);
+
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+
+ // Is there a direct check?
+ if (st->checkSquares[type_of(piece_on(from))] & to)
+ return true;
+
+ // Is there a discovered check?
+ if ( (discovered_check_candidates() & from)
+ && !aligned(from, to, square<KING>(~sideToMove)))
+ return true;
+
+ switch (type_of(m))
+ {
+ case NORMAL:
+ return false;
+
+ case PROMOTION:
+ return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
+
+ // En passant capture with check? We have already handled the case
+ // of direct checks and ordinary discovered check, so the only case we
+ // need to handle is the unusual case of a discovered check through
+ // the captured pawn.
+ case ENPASSANT:
+ {
+ Square capsq = make_square(file_of(to), rank_of(from));
+ Bitboard b = (pieces() ^ from ^ capsq) | to;
+
+ return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
+ | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
+ }
+ case CASTLING:
+ {
+ Square kfrom = from;
+ Square rfrom = to; // Castling is encoded as 'King captures the rook'
+ Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
+ Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
+
+ return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
+ && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
+ }
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+
+/// Position::do_move() makes a move, and saves all information necessary
+/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
+/// moves should be filtered out before this function is called.
+
+void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
+
+ assert(is_ok(m));
+ assert(&newSt != st);
+
+ thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
+ Key k = st->key ^ Zobrist::side;
+
+ // Copy some fields of the old state to our new StateInfo object except the
+ // ones which are going to be recalculated from scratch anyway and then switch
+ // our state pointer to point to the new (ready to be updated) state.
+ std::memcpy(&newSt, st, offsetof(StateInfo, key));
+ newSt.previous = st;
+ st = &newSt;
+
+ // Increment ply counters. In particular, rule50 will be reset to zero later on
+ // in case of a capture or a pawn move.
+ ++gamePly;
+ ++st->rule50;
+ ++st->pliesFromNull;
+
+ Color us = sideToMove;
+ Color them = ~us;
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+ Piece pc = piece_on(from);
+ Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
+
+ assert(color_of(pc) == us);
+ assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
+ assert(type_of(captured) != KING);
+
+ if (type_of(m) == CASTLING)
+ {
+ assert(pc == make_piece(us, KING));
+ assert(captured == make_piece(us, ROOK));
+
+ Square rfrom, rto;
+ do_castling<true>(us, from, to, rfrom, rto);
+
+ st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom];
+ k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
+ captured = NO_PIECE;
+ }
+
+ if (captured)
+ {
+ Square capsq = to;
+
+ // If the captured piece is a pawn, update pawn hash key, otherwise
+ // update non-pawn material.
+ if (type_of(captured) == PAWN)
+ {
+ if (type_of(m) == ENPASSANT)
+ {
+ capsq -= pawn_push(us);
+
+ assert(pc == make_piece(us, PAWN));
+ assert(to == st->epSquare);
+ assert(relative_rank(us, to) == RANK_6);
+ assert(piece_on(to) == NO_PIECE);
+ assert(piece_on(capsq) == make_piece(them, PAWN));
+
+ board[capsq] = NO_PIECE; // Not done by remove_piece()
+ }
+
+ st->pawnKey ^= Zobrist::psq[captured][capsq];
+ }
+ else
+ st->nonPawnMaterial[them] -= PieceValue[MG][captured];
+
+ // Update board and piece lists
+ remove_piece(captured, capsq);
+
+ // Update material hash key and prefetch access to materialTable
+ k ^= Zobrist::psq[captured][capsq];
+ st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
+ prefetch(thisThread->materialTable[st->materialKey]);
+
+ // Update incremental scores
+ st->psq -= PSQT::psq[captured][capsq];
+
+ // Reset rule 50 counter
+ st->rule50 = 0;
+ }
+
+ // Update hash key
+ k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
+
+ // Reset en passant square
+ if (st->epSquare != SQ_NONE)
+ {
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
+ st->epSquare = SQ_NONE;
+ }
+
+ // Update castling rights if needed
+ if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
+ {
+ int cr = castlingRightsMask[from] | castlingRightsMask[to];
+ k ^= Zobrist::castling[st->castlingRights & cr];
+ st->castlingRights &= ~cr;
+ }
+
+ // Move the piece. The tricky Chess960 castling is handled earlier
+ if (type_of(m) != CASTLING)
+ move_piece(pc, from, to);
+
+ // If the moving piece is a pawn do some special extra work
+ if (type_of(pc) == PAWN)
+ {
+ // Set en-passant square if the moved pawn can be captured
+ if ( (int(to) ^ int(from)) == 16
+ && (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
+ {
+ st->epSquare = to - pawn_push(us);
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
+ }
+
+ else if (type_of(m) == PROMOTION)
+ {
+ Piece promotion = make_piece(us, promotion_type(m));
+
+ assert(relative_rank(us, to) == RANK_8);
+ assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
+
+ remove_piece(pc, to);
+ put_piece(promotion, to);
+
+ // Update hash keys
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
+ st->pawnKey ^= Zobrist::psq[pc][to];
+ st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
+ ^ Zobrist::psq[pc][pieceCount[pc]];
+
+ // Update incremental score
+ st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to];
+
+ // Update material
+ st->nonPawnMaterial[us] += PieceValue[MG][promotion];
+ }
+
+ // Update pawn hash key and prefetch access to pawnsTable
+ st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
+ prefetch2(thisThread->pawnsTable[st->pawnKey]);
+
+ // Reset rule 50 draw counter
+ st->rule50 = 0;
+ }
+
+ // Update incremental scores
+ st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
+
+ // Set capture piece
+ st->capturedPiece = captured;
+
+ // Update the key with the final value
+ st->key = k;
+
+ // Calculate checkers bitboard (if move gives check)
+ st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
+
+ sideToMove = ~sideToMove;
+
+ // Update king attacks used for fast check detection
+ set_check_info(st);
+
+ assert(pos_is_ok());
+}
+
+
+/// 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.
+
+void Position::undo_move(Move m) {
+
+ assert(is_ok(m));
+
+ sideToMove = ~sideToMove;
+
+ Color us = sideToMove;
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+ Piece pc = piece_on(to);
+
+ assert(empty(from) || type_of(m) == CASTLING);
+ assert(type_of(st->capturedPiece) != KING);
+
+ if (type_of(m) == PROMOTION)
+ {
+ assert(relative_rank(us, to) == RANK_8);
+ assert(type_of(pc) == promotion_type(m));
+ assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
+
+ remove_piece(pc, to);
+ pc = make_piece(us, PAWN);
+ put_piece(pc, to);
+ }
+
+ if (type_of(m) == CASTLING)
+ {
+ Square rfrom, rto;
+ do_castling<false>(us, from, to, rfrom, rto);
+ }
+ else
+ {
+ move_piece(pc, to, from); // Put the piece back at the source square
+
+ if (st->capturedPiece)
+ {
+ Square capsq = to;
+
+ if (type_of(m) == ENPASSANT)
+ {
+ capsq -= pawn_push(us);
+
+ assert(type_of(pc) == PAWN);
+ assert(to == st->previous->epSquare);
+ assert(relative_rank(us, to) == RANK_6);
+ assert(piece_on(capsq) == NO_PIECE);
+ assert(st->capturedPiece == make_piece(~us, PAWN));
+ }
+
+ put_piece(st->capturedPiece, capsq); // Restore the captured piece
+ }
+ }
+
+ // Finally point our state pointer back to the previous state
+ st = st->previous;
+ --gamePly;
+
+ assert(pos_is_ok());
+}
+
+
+/// Position::do_castling() is a helper used to do/undo a castling move. This
+/// is a bit tricky in Chess960 where from/to squares can overlap.
+template<bool Do>
+void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
+
+ bool kingSide = to > from;
+ rfrom = to; // Castling is encoded as "king captures friendly rook"
+ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
+ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
+
+ // Remove both pieces first since squares could overlap in Chess960
+ remove_piece(make_piece(us, KING), Do ? from : to);
+ remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
+ board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
+ put_piece(make_piece(us, KING), Do ? to : from);
+ put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
+}
+
+
+/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
+/// the side to move without executing any move on the board.
+
+void Position::do_null_move(StateInfo& newSt) {
+
+ assert(!checkers());
+ assert(&newSt != st);
+
+ std::memcpy(&newSt, st, sizeof(StateInfo));
+ newSt.previous = st;
+ st = &newSt;
+
+ if (st->epSquare != SQ_NONE)
+ {
+ st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
+ st->epSquare = SQ_NONE;
+ }
+
+ st->key ^= Zobrist::side;
+ prefetch(TT.first_entry(st->key));
+
+ ++st->rule50;
+ st->pliesFromNull = 0;
+
+ sideToMove = ~sideToMove;
+
+ set_check_info(st);
+
+ assert(pos_is_ok());
+}
+
+void Position::undo_null_move() {
+
+ assert(!checkers());
+
+ st = st->previous;
+ sideToMove = ~sideToMove;
+}
+
+
+/// Position::key_after() computes the new hash key after the given move. Needed
+/// for speculative prefetch. It doesn't recognize special moves like castling,
+/// en-passant and promotions.
+
+Key Position::key_after(Move m) const {
+
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+ Piece pc = piece_on(from);
+ Piece captured = piece_on(to);
+ Key k = st->key ^ Zobrist::side;
+
+ if (captured)
+ k ^= Zobrist::psq[captured][to];
+
+ return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
+}
+
+
+/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
+/// SEE value of move is greater or equal to the given threshold. We'll use an
+/// algorithm similar to alpha-beta pruning with a null window.
+
+bool Position::see_ge(Move m, Value threshold) const {
+
+ assert(is_ok(m));
+
+ // Only deal with normal moves, assume others pass a simple see
+ if (type_of(m) != NORMAL)
+ return VALUE_ZERO >= threshold;
+
+ Square from = from_sq(m), to = to_sq(m);
+ PieceType nextVictim = type_of(piece_on(from));
+ Color stm = ~color_of(piece_on(from)); // First consider opponent's move
+ Value balance; // Values of the pieces taken by us minus opponent's ones
+ Bitboard occupied, stmAttackers;
+
+ // The opponent may be able to recapture so this is the best result
+ // we can hope for.
+ balance = PieceValue[MG][piece_on(to)] - threshold;
+
+ if (balance < VALUE_ZERO)
+ return false;
+
+ // Now assume the worst possible result: that the opponent can
+ // capture our piece for free.
+ balance -= PieceValue[MG][nextVictim];
+
+ if (balance >= VALUE_ZERO) // Always true if nextVictim == KING
+ return true;
+
+ bool opponentToMove = true;
+ occupied = pieces() ^ from ^ to;
+
+ // Find all attackers to the destination square, with the moving piece removed,
+ // but possibly an X-ray attacker added behind it.
+ Bitboard attackers = attackers_to(to, occupied) & occupied;
+
+ while (true)
+ {
+ // The balance is negative only because we assumed we could win
+ // the last piece for free. We are truly winning only if we can
+ // win the last piece _cheaply enough_. Test if we can actually
+ // do this otherwise "give up".
+ assert(balance < VALUE_ZERO);
+
+ stmAttackers = attackers & pieces(stm);
+
+ // Don't allow pinned pieces to attack pieces except the king as long all
+ // pinners are on their original square.
+ if (!(st->pinnersForKing[stm] & ~occupied))
+ stmAttackers &= ~st->blockersForKing[stm];
+
+ // If we have no more attackers we must give up
+ if (!stmAttackers)
+ break;
+
+ // Locate and remove the next least valuable attacker
+ nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
+
+ if (nextVictim == KING)
+ {
+ // Our only attacker is the king. If the opponent still has
+ // attackers we must give up. Otherwise we make the move and
+ // (having no more attackers) the opponent must give up.
+ if (!(attackers & pieces(~stm)))
+ opponentToMove = !opponentToMove;
+ break;
+ }
+
+ // Assume the opponent can win the next piece for free and switch sides
+ balance += PieceValue[MG][nextVictim];
+ opponentToMove = !opponentToMove;
+
+ // If balance is negative after receiving a free piece then give up
+ if (balance < VALUE_ZERO)
+ break;
+
+ // Complete the process of switching sides. The first line swaps
+ // all negative numbers with non-negative numbers. The compiler
+ // probably knows that it is just the bitwise negation ~balance.
+ balance = -balance-1;
+ stm = ~stm;
+ }
+
+ // If the opponent gave up we win, otherwise we lose.
+ return opponentToMove;
+}
+
+
+/// Position::is_draw() tests whether the position is drawn by 50-move rule
+/// or by repetition. It does not detect stalemates.
+
+bool Position::is_draw(int ply) const {
+
+ if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
+ return true;
+
+ int end = std::min(st->rule50, st->pliesFromNull);
+
+ if (end < 4)
+ return false;
+
+ StateInfo* stp = st->previous->previous;
+ int cnt = 0;
+
+ for (int i = 4; i <= end; i += 2)
+ {
+ stp = stp->previous->previous;
+
+ // Return a draw score if a position repeats once earlier but strictly
+ // after the root, or repeats twice before or at the root.
+ if ( stp->key == st->key
+ && ++cnt + (ply > i) == 2)
+ return true;
+ }
+
+ return false;
+}
+
+
+/// Position::flip() flips position with the white and black sides reversed. This
+/// is only useful for debugging e.g. for finding evaluation symmetry bugs.
+
+void Position::flip() {
+
+ string f, token;
+ std::stringstream ss(fen());
+
+ for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
+ {
+ std::getline(ss, token, r > RANK_1 ? '/' : ' ');
+ f.insert(0, token + (f.empty() ? " " : "/"));
+ }
+
+ ss >> token; // Active color
+ f += (token == "w" ? "B " : "W "); // Will be lowercased later
+
+ ss >> token; // Castling availability
+ f += token + " ";
+
+ std::transform(f.begin(), f.end(), f.begin(),
+ [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); });
+
+ ss >> token; // En passant square
+ f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
+
+ std::getline(ss, token); // Half and full moves
+ f += token;
+
+ set(f, is_chess960(), st, this_thread());
+
+ assert(pos_is_ok());
+}
+
+
+/// Position::pos_is_ok() performs some consistency checks for the
+/// position object and raises an asserts if something wrong is detected.
+/// This is meant to be helpful when debugging.
+
+bool Position::pos_is_ok() const {
+
+ const bool Fast = true; // Quick (default) or full check?
+
+ if ( (sideToMove != WHITE && sideToMove != BLACK)
+ || piece_on(square<KING>(WHITE)) != W_KING
+ || piece_on(square<KING>(BLACK)) != B_KING
+ || ( ep_square() != SQ_NONE
+ && relative_rank(sideToMove, ep_square()) != RANK_6))
+ assert(0 && "pos_is_ok: Default");
+
+ if (Fast)
+ return true;
+
+ if ( pieceCount[W_KING] != 1
+ || pieceCount[B_KING] != 1
+ || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
+ assert(0 && "pos_is_ok: Kings");
+
+ if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
+ || pieceCount[W_PAWN] > 8
+ || pieceCount[B_PAWN] > 8)
+ assert(0 && "pos_is_ok: Pawns");
+
+ if ( (pieces(WHITE) & pieces(BLACK))
+ || (pieces(WHITE) | pieces(BLACK)) != pieces()
+ || popcount(pieces(WHITE)) > 16
+ || popcount(pieces(BLACK)) > 16)
+ assert(0 && "pos_is_ok: Bitboards");
+
+ for (PieceType p1 = PAWN; p1 <= KING; ++p1)
+ for (PieceType p2 = PAWN; p2 <= KING; ++p2)
+ if (p1 != p2 && (pieces(p1) & pieces(p2)))
+ assert(0 && "pos_is_ok: Bitboards");
+
+ StateInfo si = *st;
+ set_state(&si);
+ if (std::memcmp(&si, st, sizeof(StateInfo)))
+ assert(0 && "pos_is_ok: State");
+
+ for (Piece pc : Pieces)
+ {
+ if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
+ || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
+ assert(0 && "pos_is_ok: Pieces");
+
+ for (int i = 0; i < pieceCount[pc]; ++i)
+ if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
+ assert(0 && "pos_is_ok: Index");
+ }
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
+ {
+ if (!can_castle(c | s))
+ continue;
+
+ if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
+ || castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
+ || (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
+ assert(0 && "pos_is_ok: Castling");
+ }
+
+ return true;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef POSITION_H_INCLUDED
+#define POSITION_H_INCLUDED
+
+#include <cassert>
+#include <deque>
+#include <memory> // For std::unique_ptr
+#include <string>
+
+#include "bitboard.h"
+#include "types.h"
+
+
+/// StateInfo struct stores information needed to restore a Position object to
+/// its previous state when we retract a move. Whenever a move is made on the
+/// board (by calling Position::do_move), a StateInfo object must be passed.
+
+struct StateInfo {
+
+ // Copied when making a move
+ Key pawnKey;
+ Key materialKey;
+ Value nonPawnMaterial[COLOR_NB];
+ int castlingRights;
+ int rule50;
+ int pliesFromNull;
+ Score psq;
+ Square epSquare;
+
+ // Not copied when making a move (will be recomputed anyhow)
+ Key key;
+ Bitboard checkersBB;
+ Piece capturedPiece;
+ StateInfo* previous;
+ Bitboard blockersForKing[COLOR_NB];
+ Bitboard pinnersForKing[COLOR_NB];
+ Bitboard checkSquares[PIECE_TYPE_NB];
+};
+
+/// A list to keep track of the position states along the setup moves (from the
+/// start position to the position just before the search starts). Needed by
+/// 'draw by repetition' detection. Use a std::deque because pointers to
+/// elements are not invalidated upon list resizing.
+typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
+
+
+/// Position class stores information regarding the board representation as
+/// pieces, side to move, hash keys, castling info, etc. Important methods are
+/// do_move() and undo_move(), used by the search to update node info when
+/// traversing the search tree.
+class Thread;
+
+class Position {
+public:
+ static void init();
+
+ Position() = default;
+ Position(const Position&) = delete;
+ Position& operator=(const Position&) = delete;
+
+ // FEN string input/output
+ Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
+ Position& set(const std::string& code, Color c, StateInfo* si);
+ const std::string fen() const;
+
+ // Position representation
+ Bitboard pieces() const;
+ Bitboard pieces(PieceType pt) const;
+ Bitboard pieces(PieceType pt1, PieceType pt2) const;
+ Bitboard pieces(Color c) const;
+ Bitboard pieces(Color c, PieceType pt) const;
+ Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
+ Piece piece_on(Square s) const;
+ Square ep_square() const;
+ bool empty(Square s) const;
+ template<PieceType Pt> int count(Color c) const;
+ template<PieceType Pt> int count() const;
+ template<PieceType Pt> const Square* squares(Color c) const;
+ template<PieceType Pt> Square square(Color c) const;
+
+ // Castling
+ int can_castle(Color c) const;
+ int can_castle(CastlingRight cr) const;
+ bool castling_impeded(CastlingRight cr) const;
+ Square castling_rook_square(CastlingRight cr) const;
+
+ // Checking
+ Bitboard checkers() const;
+ Bitboard discovered_check_candidates() const;
+ Bitboard pinned_pieces(Color c) const;
+ Bitboard check_squares(PieceType pt) const;
+
+ // Attacks to/from a given square
+ Bitboard attackers_to(Square s) const;
+ Bitboard attackers_to(Square s, Bitboard occupied) const;
+ Bitboard attacks_from(PieceType pt, Square s) const;
+ template<PieceType> Bitboard attacks_from(Square s) const;
+ template<PieceType> Bitboard attacks_from(Square s, Color c) const;
+ Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
+
+ // Properties of moves
+ bool legal(Move m) const;
+ bool pseudo_legal(const Move m) const;
+ bool capture(Move m) const;
+ bool capture_or_promotion(Move m) const;
+ bool gives_check(Move m) const;
+ bool advanced_pawn_push(Move m) const;
+ Piece moved_piece(Move m) const;
+ Piece captured_piece() const;
+
+ // Piece specific
+ bool pawn_passed(Color c, Square s) const;
+ bool opposite_bishops() const;
+
+ // Doing and undoing moves
+ void do_move(Move m, StateInfo& newSt);
+ void do_move(Move m, StateInfo& newSt, bool givesCheck);
+ void undo_move(Move m);
+ void do_null_move(StateInfo& newSt);
+ void undo_null_move();
+
+ // Static Exchange Evaluation
+ bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
+
+ // Accessing hash keys
+ Key key() const;
+ Key key_after(Move m) const;
+ Key material_key() const;
+ Key pawn_key() const;
+
+ // Other properties of the position
+ Color side_to_move() const;
+ int game_ply() const;
+ bool is_chess960() const;
+ Thread* this_thread() const;
+ bool is_draw(int ply) const;
+ int rule50_count() const;
+ Score psq_score() const;
+ Value non_pawn_material(Color c) const;
+ Value non_pawn_material() const;
+
+ // Position consistency check, for debugging
+ bool pos_is_ok() const;
+ void flip();
+
+private:
+ // Initialization helpers (used while setting up a position)
+ void set_castling_right(Color c, Square rfrom);
+ void set_state(StateInfo* si) const;
+ void set_check_info(StateInfo* si) const;
+
+ // Other helpers
+ void put_piece(Piece pc, Square s);
+ void remove_piece(Piece pc, Square s);
+ void move_piece(Piece pc, Square from, Square to);
+ template<bool Do>
+ void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
+
+ // Data members
+ Piece board[SQUARE_NB];
+ Bitboard byTypeBB[PIECE_TYPE_NB];
+ Bitboard byColorBB[COLOR_NB];
+ int pieceCount[PIECE_NB];
+ Square pieceList[PIECE_NB][16];
+ int index[SQUARE_NB];
+ int castlingRightsMask[SQUARE_NB];
+ Square castlingRookSquare[CASTLING_RIGHT_NB];
+ Bitboard castlingPath[CASTLING_RIGHT_NB];
+ int gamePly;
+ Color sideToMove;
+ Thread* thisThread;
+ StateInfo* st;
+ bool chess960;
+};
+
+extern std::ostream& operator<<(std::ostream& os, const Position& pos);
+
+inline Color Position::side_to_move() const {
+ return sideToMove;
+}
+
+inline bool Position::empty(Square s) const {
+ return board[s] == NO_PIECE;
+}
+
+inline Piece Position::piece_on(Square s) const {
+ return board[s];
+}
+
+inline Piece Position::moved_piece(Move m) const {
+ return board[from_sq(m)];
+}
+
+inline Bitboard Position::pieces() const {
+ return byTypeBB[ALL_PIECES];
+}
+
+inline Bitboard Position::pieces(PieceType pt) const {
+ return byTypeBB[pt];
+}
+
+inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
+ return byTypeBB[pt1] | byTypeBB[pt2];
+}
+
+inline Bitboard Position::pieces(Color c) const {
+ return byColorBB[c];
+}
+
+inline Bitboard Position::pieces(Color c, PieceType pt) const {
+ return byColorBB[c] & byTypeBB[pt];
+}
+
+inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
+ return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
+}
+
+template<PieceType Pt> inline int Position::count(Color c) const {
+ return pieceCount[make_piece(c, Pt)];
+}
+
+template<PieceType Pt> inline int Position::count() const {
+ return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)];
+}
+
+template<PieceType Pt> inline const Square* Position::squares(Color c) const {
+ return pieceList[make_piece(c, Pt)];
+}
+
+template<PieceType Pt> inline Square Position::square(Color c) const {
+ assert(pieceCount[make_piece(c, Pt)] == 1);
+ return pieceList[make_piece(c, Pt)][0];
+}
+
+inline Square Position::ep_square() const {
+ return st->epSquare;
+}
+
+inline int Position::can_castle(CastlingRight cr) const {
+ return st->castlingRights & cr;
+}
+
+inline int Position::can_castle(Color c) const {
+ return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
+}
+
+inline bool Position::castling_impeded(CastlingRight cr) const {
+ return byTypeBB[ALL_PIECES] & castlingPath[cr];
+}
+
+inline Square Position::castling_rook_square(CastlingRight cr) const {
+ return castlingRookSquare[cr];
+}
+
+template<PieceType Pt>
+inline Bitboard Position::attacks_from(Square s) const {
+ assert(Pt != PAWN);
+ return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
+ : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
+ : PseudoAttacks[Pt][s];
+}
+
+template<>
+inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
+ return PawnAttacks[c][s];
+}
+
+inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
+ return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
+}
+
+inline Bitboard Position::attackers_to(Square s) const {
+ return attackers_to(s, byTypeBB[ALL_PIECES]);
+}
+
+inline Bitboard Position::checkers() const {
+ return st->checkersBB;
+}
+
+inline Bitboard Position::discovered_check_candidates() const {
+ return st->blockersForKing[~sideToMove] & pieces(sideToMove);
+}
+
+inline Bitboard Position::pinned_pieces(Color c) const {
+ return st->blockersForKing[c] & pieces(c);
+}
+
+inline Bitboard Position::check_squares(PieceType pt) const {
+ return st->checkSquares[pt];
+}
+
+inline bool Position::pawn_passed(Color c, Square s) const {
+ return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
+}
+
+inline bool Position::advanced_pawn_push(Move m) const {
+ return type_of(moved_piece(m)) == PAWN
+ && relative_rank(sideToMove, from_sq(m)) > RANK_4;
+}
+
+inline Key Position::key() const {
+ return st->key;
+}
+
+inline Key Position::pawn_key() const {
+ return st->pawnKey;
+}
+
+inline Key Position::material_key() const {
+ return st->materialKey;
+}
+
+inline Score Position::psq_score() const {
+ return st->psq;
+}
+
+inline Value Position::non_pawn_material(Color c) const {
+ return st->nonPawnMaterial[c];
+}
+
+inline Value Position::non_pawn_material() const {
+ return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
+}
+
+inline int Position::game_ply() const {
+ return gamePly;
+}
+
+inline int Position::rule50_count() const {
+ return st->rule50;
+}
+
+inline bool Position::opposite_bishops() const {
+ return pieceCount[W_BISHOP] == 1
+ && pieceCount[B_BISHOP] == 1
+ && opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
+}
+
+inline bool Position::is_chess960() const {
+ return chess960;
+}
+
+inline bool Position::capture_or_promotion(Move m) const {
+ assert(is_ok(m));
+ return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
+}
+
+inline bool Position::capture(Move m) const {
+ assert(is_ok(m));
+ // Castling is encoded as "king captures rook"
+ return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
+}
+
+inline Piece Position::captured_piece() const {
+ return st->capturedPiece;
+}
+
+inline Thread* Position::this_thread() const {
+ return thisThread;
+}
+
+inline void Position::put_piece(Piece pc, Square s) {
+
+ board[s] = pc;
+ byTypeBB[ALL_PIECES] |= s;
+ byTypeBB[type_of(pc)] |= s;
+ byColorBB[color_of(pc)] |= s;
+ index[s] = pieceCount[pc]++;
+ pieceList[pc][index[s]] = s;
+ pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
+}
+
+inline void Position::remove_piece(Piece pc, Square s) {
+
+ // WARNING: This is not a reversible operation. If we remove a piece in
+ // do_move() and then replace it in undo_move() we will put it at the end of
+ // the list and not in its original place, it means index[] and pieceList[]
+ // are not invariant to a do_move() + undo_move() sequence.
+ byTypeBB[ALL_PIECES] ^= s;
+ byTypeBB[type_of(pc)] ^= s;
+ byColorBB[color_of(pc)] ^= s;
+ /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
+ Square lastSquare = pieceList[pc][--pieceCount[pc]];
+ index[lastSquare] = index[s];
+ pieceList[pc][index[lastSquare]] = lastSquare;
+ pieceList[pc][pieceCount[pc]] = SQ_NONE;
+ pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
+}
+
+inline void Position::move_piece(Piece pc, Square from, Square to) {
+
+ // index[from] is not updated and becomes stale. This works as long as index[]
+ // is accessed just by known occupied squares.
+ Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
+ byTypeBB[ALL_PIECES] ^= from_to_bb;
+ byTypeBB[type_of(pc)] ^= from_to_bb;
+ byColorBB[color_of(pc)] ^= from_to_bb;
+ board[from] = NO_PIECE;
+ board[to] = pc;
+ index[to] = index[from];
+ pieceList[pc][index[to]] = to;
+}
+
+inline void Position::do_move(Move m, StateInfo& newSt) {
+ do_move(m, newSt, gives_check(m));
+}
+
+#endif // #ifndef POSITION_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+
+#include "types.h"
+
+Value PieceValue[PHASE_NB][PIECE_NB] = {
+ { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
+ { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
+};
+
+namespace PSQT {
+
+#define S(mg, eg) make_score(mg, eg)
+
+// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
+// type on a given square a (middlegame, endgame) score pair is assigned. Table
+// is defined for files A..D and white side: it is symmetric for black side and
+// second half of the files.
+const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
+ { },
+ { // Pawn
+ { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
+ { S(-11, 7), S( 6,-4), S( 7, 8), S( 3,-2) },
+ { S(-18,-4), S( -2,-5), S( 19, 5), S(24, 4) },
+ { S(-17, 3), S( -9, 3), S( 20,-8), S(35,-3) },
+ { S( -6, 8), S( 5, 9), S( 3, 7), S(21,-6) },
+ { S( -6, 8), S( -8,-5), S( -6, 2), S(-2, 4) },
+ { S( -4, 3), S( 20,-9), S( -8, 1), S(-4,18) }
+ },
+ { // Knight
+ { S(-161,-105), S(-96,-82), S(-80,-46), S(-73,-14) },
+ { S( -83, -69), S(-43,-54), S(-21,-17), S(-10, 9) },
+ { S( -71, -50), S(-22,-39), S( 0, -7), S( 9, 28) },
+ { S( -25, -41), S( 18,-25), S( 43, 6), S( 47, 38) },
+ { S( -26, -46), S( 16,-25), S( 38, 3), S( 50, 40) },
+ { S( -11, -54), S( 37,-38), S( 56, -7), S( 65, 27) },
+ { S( -63, -65), S(-19,-50), S( 5,-24), S( 14, 13) },
+ { S(-195,-109), S(-67,-89), S(-42,-50), S(-29,-13) }
+ },
+ { // Bishop
+ { S(-44,-58), S(-13,-31), S(-25,-37), S(-34,-19) },
+ { S(-20,-34), S( 20, -9), S( 12,-14), S( 1, 4) },
+ { S( -9,-23), S( 27, 0), S( 21, -3), S( 11, 16) },
+ { S(-11,-26), S( 28, -3), S( 21, -5), S( 10, 16) },
+ { S(-11,-26), S( 27, -4), S( 16, -7), S( 9, 14) },
+ { S(-17,-24), S( 16, -2), S( 12, 0), S( 2, 13) },
+ { S(-23,-34), S( 17,-10), S( 6,-12), S( -2, 6) },
+ { S(-35,-55), S(-11,-32), S(-19,-36), S(-29,-17) }
+ },
+ { // Rook
+ { S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) },
+ { S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) },
+ { S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) },
+ { S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) },
+ { S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) },
+ { S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) },
+ { S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) },
+ { S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) }
+ },
+ { // Queen
+ { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) },
+ { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) },
+ { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) },
+ { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) },
+ { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) },
+ { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) },
+ { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) },
+ { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) }
+ },
+ { // King
+ { S(267, 0), S(320, 48), S(270, 75), S(195, 84) },
+ { S(264, 43), S(304, 92), S(238,143), S(180,132) },
+ { S(200, 83), S(245,138), S(176,167), S(110,165) },
+ { S(177,106), S(185,169), S(148,169), S(110,179) },
+ { S(149,108), S(177,163), S(115,200), S( 66,203) },
+ { S(118, 95), S(159,155), S( 84,176), S( 41,174) },
+ { S( 87, 50), S(128, 99), S( 63,122), S( 20,139) },
+ { S( 63, 9), S( 88, 55), S( 47, 80), S( 0, 90) }
+ }
+};
+
+#undef S
+
+Score psq[PIECE_NB][SQUARE_NB];
+
+// init() initializes piece-square tables: the white halves of the tables are
+// copied from Bonus[] adding the piece value, then the black halves of the
+// tables are initialized by flipping and changing the sign of the white scores.
+void init() {
+
+ for (Piece pc = W_PAWN; pc <= W_KING; ++pc)
+ {
+ PieceValue[MG][~pc] = PieceValue[MG][pc];
+ PieceValue[EG][~pc] = PieceValue[EG][pc];
+
+ Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
+
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ File f = std::min(file_of(s), ~file_of(s));
+ psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f];
+ psq[~pc][~s] = -psq[pc][s];
+ }
+ }
+}
+
+} // namespace PSQT
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstring> // For std::memset
+#include <iostream>
+#include <sstream>
+
+#include "evaluate.h"
+#include "misc.h"
+#include "movegen.h"
+#include "movepick.h"
+#include "position.h"
+#include "search.h"
+#include "timeman.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+namespace Search {
+
+ LimitsType Limits;
+}
+
+namespace Tablebases {
+
+ int Cardinality;
+ bool RootInTB;
+ bool UseRule50;
+ Depth ProbeDepth;
+ Value Score;
+}
+
+namespace TB = Tablebases;
+
+using std::string;
+using Eval::evaluate;
+using namespace Search;
+
+namespace {
+
+ // Different node types, used as a template parameter
+ enum NodeType { NonPV, PV };
+
+ // Sizes and phases of the skip-blocks, used for distributing search depths across the threads
+ const int skipSize[] = { 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 };
+ const int skipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 };
+
+ // Razoring and futility margin based on depth
+ const int razor_margin = 600;
+ Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); }
+
+ // Futility and reductions lookup tables, initialized at startup
+ int FutilityMoveCounts[2][16]; // [improving][depth]
+ int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
+
+ template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
+ return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY;
+ }
+
+ // History and stats update bonus, based on depth
+ int stat_bonus(Depth depth) {
+ int d = depth / ONE_PLY;
+ return d > 17 ? 0 : d * d + 2 * d - 2;
+ }
+
+ // Skill structure is used to implement strength limit
+ struct Skill {
+ explicit Skill(int l) : level(l) {}
+ bool enabled() const { return level < 20; }
+ bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; }
+ Move pick_best(size_t multiPV);
+
+ int level;
+ Move best = MOVE_NONE;
+ };
+
+ template <NodeType NT>
+ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning);
+
+ template <NodeType NT, bool InCheck>
+ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO);
+
+ Value value_to_tt(Value v, int ply);
+ Value value_from_tt(Value v, int ply);
+ void update_pv(Move* pv, Move move, Move* childPv);
+ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
+ void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
+ void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus);
+ bool pv_is_draw(Position& pos);
+
+ // perft() is our utility to verify move generation. All the leaf nodes up
+ // to the given depth are generated and counted, and the sum is returned.
+ template<bool Root>
+ uint64_t perft(Position& pos, Depth depth) {
+
+ StateInfo st;
+ uint64_t cnt, nodes = 0;
+ const bool leaf = (depth == 2 * ONE_PLY);
+
+ for (const auto& m : MoveList<LEGAL>(pos))
+ {
+ if (Root && depth <= ONE_PLY)
+ cnt = 1, nodes++;
+ else
+ {
+ pos.do_move(m, st);
+ cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
+ nodes += cnt;
+ pos.undo_move(m);
+ }
+ if (Root)
+ sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
+ }
+ return nodes;
+ }
+
+} // namespace
+
+
+/// Search::init() is called during startup to initialize various lookup tables
+
+void Search::init() {
+
+ for (int imp = 0; imp <= 1; ++imp)
+ for (int d = 1; d < 64; ++d)
+ for (int mc = 1; mc < 64; ++mc)
+ {
+ double r = log(d) * log(mc) / 1.95;
+
+ Reductions[NonPV][imp][d][mc] = int(std::round(r));
+ Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0);
+
+ // Increase reduction for non-PV nodes when eval is not improving
+ if (!imp && Reductions[NonPV][imp][d][mc] >= 2)
+ Reductions[NonPV][imp][d][mc]++;
+ }
+
+ for (int d = 0; d < 16; ++d)
+ {
+ FutilityMoveCounts[0][d] = int(2.4 + 0.74 * pow(d, 1.78));
+ FutilityMoveCounts[1][d] = int(5.0 + 1.00 * pow(d, 2.00));
+ }
+}
+
+
+/// Search::clear() resets search state to its initial value
+
+void Search::clear() {
+
+ Threads.main()->wait_for_search_finished();
+
+ Time.availableNodes = 0;
+ TT.clear();
+ Threads.clear();
+}
+
+
+/// MainThread::search() is called by the main thread when the program receives
+/// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
+
+void MainThread::search() {
+
+ if (Limits.perft)
+ {
+ nodes = perft<true>(rootPos, Limits.perft * ONE_PLY);
+ sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
+ return;
+ }
+
+ Color us = rootPos.side_to_move();
+ Time.init(Limits, us, rootPos.game_ply());
+ TT.new_search();
+
+ int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns
+
+ Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2)
+ : -make_score(contempt, contempt / 2));
+
+ if (rootMoves.empty())
+ {
+ rootMoves.emplace_back(MOVE_NONE);
+ sync_cout << "info depth 0 score "
+ << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW)
+ << sync_endl;
+ }
+ else
+ {
+ for (Thread* th : Threads)
+ if (th != this)
+ th->start_searching();
+
+ Thread::search(); // Let's start searching!
+ }
+
+ // When we reach the maximum depth, we can arrive here without a raise of
+ // Threads.stop. However, if we are pondering or in an infinite search,
+ // the UCI protocol states that we shouldn't print the best move before the
+ // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here
+ // until the GUI sends one of those commands (which also raises Threads.stop).
+ Threads.stopOnPonderhit = true;
+
+ while (!Threads.stop && (Threads.ponder || Limits.infinite))
+ {} // Busy wait for a stop or a ponder reset
+
+ // Stop the threads if not already stopped (also raise the stop if
+ // "ponderhit" just reset Threads.ponder).
+ Threads.stop = true;
+
+ // Wait until all threads have finished
+ for (Thread* th : Threads)
+ if (th != this)
+ th->wait_for_search_finished();
+
+ // When playing in 'nodes as time' mode, subtract the searched nodes from
+ // the available ones before exiting.
+ if (Limits.npmsec)
+ Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
+
+ // Check if there are threads with a better score than main thread
+ Thread* bestThread = this;
+ if ( Options["MultiPV"] == 1
+ && !Limits.depth
+ && !Skill(Options["Skill Level"]).enabled()
+ && rootMoves[0].pv[0] != MOVE_NONE)
+ {
+ for (Thread* th : Threads)
+ {
+ Depth depthDiff = th->completedDepth - bestThread->completedDepth;
+ Value scoreDiff = th->rootMoves[0].score - bestThread->rootMoves[0].score;
+
+ // Select the thread with the best score, always if it is a mate
+ if ( scoreDiff > 0
+ && (depthDiff >= 0 || th->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY))
+ bestThread = th;
+ }
+ }
+
+ previousScore = bestThread->rootMoves[0].score;
+
+ // Send new PV when needed
+ if (bestThread != this)
+ sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl;
+
+ sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
+
+ if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos))
+ std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960());
+
+ std::cout << sync_endl;
+}
+
+
+/// Thread::search() is the main iterative deepening loop. It calls 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 Thread::search() {
+
+ Stack stack[MAX_PLY+7], *ss = stack+4; // To reference from (ss-4) to (ss+2)
+ Value bestValue, alpha, beta, delta;
+ Move lastBestMove = MOVE_NONE;
+ Depth lastBestMoveDepth = DEPTH_ZERO;
+ MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
+ double timeReduction = 1.0;
+
+ std::memset(ss-4, 0, 7 * sizeof(Stack));
+ for (int i = 4; i > 0; i--)
+ (ss-i)->contHistory = &this->contHistory[NO_PIECE][0]; // Use as sentinel
+
+ bestValue = delta = alpha = -VALUE_INFINITE;
+ beta = VALUE_INFINITE;
+
+ if (mainThread)
+ {
+ mainThread->failedLow = false;
+ mainThread->bestMoveChanges = 0;
+ }
+
+ size_t multiPV = Options["MultiPV"];
+ Skill skill(Options["Skill Level"]);
+
+ // When playing with strength handicap enable MultiPV search that we will
+ // use behind the scenes to retrieve a set of possible moves.
+ if (skill.enabled())
+ multiPV = std::max(multiPV, (size_t)4);
+
+ multiPV = std::min(multiPV, rootMoves.size());
+
+ // Iterative deepening loop until requested to stop or the target depth is reached
+ while ( (rootDepth += ONE_PLY) < DEPTH_MAX
+ && !Threads.stop
+ && !(Limits.depth && mainThread && rootDepth / ONE_PLY > Limits.depth))
+ {
+ // Distribute search depths across the threads
+ if (idx)
+ {
+ int i = (idx - 1) % 20;
+ if (((rootDepth / ONE_PLY + rootPos.game_ply() + skipPhase[i]) / skipSize[i]) % 2)
+ continue;
+ }
+
+ // Age out PV variability metric
+ if (mainThread)
+ mainThread->bestMoveChanges *= 0.505, mainThread->failedLow = false;
+
+ // Save the last iteration's scores before first PV line is searched and
+ // all the move scores except the (new) PV are set to -VALUE_INFINITE.
+ for (RootMove& rm : rootMoves)
+ rm.previousScore = rm.score;
+
+ // MultiPV loop. We perform a full root search for each PV line
+ for (PVIdx = 0; PVIdx < multiPV && !Threads.stop; ++PVIdx)
+ {
+ // Reset UCI info selDepth for each depth and each PV line
+ selDepth = 0;
+
+ // Reset aspiration window starting size
+ if (rootDepth >= 5 * ONE_PLY)
+ {
+ delta = Value(18);
+ alpha = std::max(rootMoves[PVIdx].previousScore - delta,-VALUE_INFINITE);
+ beta = std::min(rootMoves[PVIdx].previousScore + delta, VALUE_INFINITE);
+ }
+
+ // Start with a small aspiration window and, in the case of a fail
+ // high/low, re-search with a bigger window until we're not failing
+ // high/low anymore.
+ while (true)
+ {
+ bestValue = ::search<PV>(rootPos, ss, alpha, beta, rootDepth, false, false);
+
+ // Bring the best move to the front. It is critical that sorting
+ // is done with a stable algorithm because all the values but the
+ // first and eventually the new best one are set to -VALUE_INFINITE
+ // and we want to keep the same order for all the moves except the
+ // new PV that goes to the front. Note that in case of MultiPV
+ // search the already searched PV lines are preserved.
+ std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
+
+ // If search has been stopped, we break immediately. Sorting and
+ // writing PV back to TT is safe because RootMoves is still
+ // valid, although it refers to the previous iteration.
+ if (Threads.stop)
+ break;
+
+ // When failing high/low give some update (without cluttering
+ // the UI) before a re-search.
+ if ( mainThread
+ && multiPV == 1
+ && (bestValue <= alpha || bestValue >= beta)
+ && Time.elapsed() > 3000)
+ sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl;
+
+ // In case of failing low/high increase aspiration window and
+ // re-search, otherwise exit the loop.
+ if (bestValue <= alpha)
+ {
+ beta = (alpha + beta) / 2;
+ alpha = std::max(bestValue - delta, -VALUE_INFINITE);
+
+ if (mainThread)
+ {
+ mainThread->failedLow = true;
+ Threads.stopOnPonderhit = false;
+ }
+ }
+ else if (bestValue >= beta)
+ beta = std::min(bestValue + delta, VALUE_INFINITE);
+ else
+ break;
+
+ delta += delta / 4 + 5;
+
+ assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+ }
+
+ // Sort the PV lines searched so far and update the GUI
+ std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1);
+
+ if ( mainThread
+ && (Threads.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000))
+ sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl;
+ }
+
+ if (!Threads.stop)
+ completedDepth = rootDepth;
+
+ if (rootMoves[0].pv[0] != lastBestMove) {
+ lastBestMove = rootMoves[0].pv[0];
+ lastBestMoveDepth = rootDepth;
+ }
+
+ // Have we found a "mate in x"?
+ if ( Limits.mate
+ && bestValue >= VALUE_MATE_IN_MAX_PLY
+ && VALUE_MATE - bestValue <= 2 * Limits.mate)
+ Threads.stop = true;
+
+ if (!mainThread)
+ continue;
+
+ // If skill level is enabled and time is up, pick a sub-optimal best move
+ if (skill.enabled() && skill.time_to_pick(rootDepth))
+ skill.pick_best(multiPV);
+
+ // Do we have time for the next iteration? Can we stop searching now?
+ if (Limits.use_time_management())
+ {
+ if (!Threads.stop && !Threads.stopOnPonderhit)
+ {
+ // Stop the search if only one legal move is available, or if all
+ // of the available time has been used
+ const int F[] = { mainThread->failedLow,
+ bestValue - mainThread->previousScore };
+ int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1]));
+
+ Color us = rootPos.side_to_move();
+ bool thinkHard = bestValue == VALUE_DRAW
+ && Limits.time[us] - Time.elapsed() > Limits.time[~us]
+ && ::pv_is_draw(rootPos);
+
+ double unstablePvFactor = 1 + mainThread->bestMoveChanges + thinkHard;
+
+ // if the bestMove is stable over several iterations, reduce time for this move,
+ // the longer the move has been stable, the more.
+ // Use part of the gained time from a previous stable move for the current move.
+ timeReduction = 1;
+ for (int i : {3, 4, 5})
+ if (lastBestMoveDepth * i < completedDepth && !thinkHard)
+ timeReduction *= 1.3;
+ unstablePvFactor *= std::pow(mainThread->previousTimeReduction, 0.51) / timeReduction;
+
+ if ( rootMoves.size() == 1
+ || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628)
+ {
+ // If we are allowed to ponder do not stop the search now but
+ // keep pondering until the GUI sends "ponderhit" or "stop".
+ if (Threads.ponder)
+ Threads.stopOnPonderhit = true;
+ else
+ Threads.stop = true;
+ }
+ }
+ }
+ }
+
+ if (!mainThread)
+ return;
+
+ mainThread->previousTimeReduction = timeReduction;
+
+ // If skill level is enabled, swap best PV line with the sub-optimal one
+ if (skill.enabled())
+ std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(),
+ skill.best ? skill.best : skill.pick_best(multiPV)));
+}
+
+
+namespace {
+
+ // search<>() is the main search function for both PV and non-PV nodes
+
+ template <NodeType NT>
+ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning) {
+
+ const bool PvNode = NT == PV;
+ const bool rootNode = PvNode && ss->ply == 0;
+
+ assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
+ assert(PvNode || (alpha == beta - 1));
+ assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
+ assert(!(PvNode && cutNode));
+ assert(depth / ONE_PLY * ONE_PLY == depth);
+
+ Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
+ StateInfo st;
+ TTEntry* tte;
+ Key posKey;
+ Move ttMove, move, excludedMove, bestMove;
+ Depth extension, newDepth;
+ Value bestValue, value, ttValue, eval, maxValue;
+ bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
+ bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
+ Piece movedPiece;
+ int moveCount, captureCount, quietCount;
+
+ // Step 1. Initialize node
+ Thread* thisThread = pos.this_thread();
+ inCheck = pos.checkers();
+ moveCount = captureCount = quietCount = ss->moveCount = 0;
+ ss->statScore = 0;
+ bestValue = -VALUE_INFINITE;
+ maxValue = VALUE_INFINITE;
+
+ // Check for the available remaining time
+ if (thisThread == Threads.main())
+ static_cast<MainThread*>(thisThread)->check_time();
+
+ // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0)
+ if (PvNode && thisThread->selDepth < ss->ply + 1)
+ thisThread->selDepth = ss->ply + 1;
+
+ if (!rootNode)
+ {
+ // Step 2. Check for aborted search and immediate draw
+ if (Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY)
+ return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : VALUE_DRAW;
+
+ // Step 3. Mate distance pruning. Even if we mate at the next move our score
+ // would be at best mate_in(ss->ply+1), but if alpha is already bigger because
+ // a shorter mate was found upward in the tree then there is no need to search
+ // because we will never beat the current alpha. Same logic but with reversed
+ // signs applies also in the opposite condition of being mated instead of giving
+ // mate. In this case return a fail-high score.
+ alpha = std::max(mated_in(ss->ply), alpha);
+ beta = std::min(mate_in(ss->ply+1), beta);
+ if (alpha >= beta)
+ return alpha;
+ }
+
+ assert(0 <= ss->ply && ss->ply < MAX_PLY);
+
+ (ss+1)->ply = ss->ply + 1;
+ ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
+ ss->contHistory = &thisThread->contHistory[NO_PIECE][0];
+ (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
+ Square prevSq = to_sq((ss-1)->currentMove);
+
+ // Step 4. Transposition table lookup. We don't want the score of a partial
+ // search to overwrite a previous full search TT value, so we use a different
+ // position key in case of an excluded move.
+ excludedMove = ss->excludedMove;
+ posKey = pos.key() ^ Key(excludedMove << 16); // isn't a very good hash
+ tte = TT.probe(posKey, ttHit);
+ ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+ ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0]
+ : ttHit ? tte->move() : MOVE_NONE;
+
+ // At non-PV nodes we check for an early TT cutoff
+ if ( !PvNode
+ && ttHit
+ && tte->depth() >= depth
+ && ttValue != VALUE_NONE // Possible in case of TT access race
+ && (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
+ : (tte->bound() & BOUND_UPPER)))
+ {
+ // If ttMove is quiet, update move sorting heuristics on TT hit
+ if (ttMove)
+ {
+ if (ttValue >= beta)
+ {
+ if (!pos.capture_or_promotion(ttMove))
+ update_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
+
+ // Extra penalty for a quiet TT move in previous ply when it gets refuted
+ if ((ss-1)->moveCount == 1 && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+ }
+ // Penalty for a quiet ttMove that fails low
+ else if (!pos.capture_or_promotion(ttMove))
+ {
+ int penalty = -stat_bonus(depth);
+ thisThread->mainHistory.update(pos.side_to_move(), ttMove, penalty);
+ update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty);
+ }
+ }
+ return ttValue;
+ }
+
+ // Step 4a. Tablebase probe
+ if (!rootNode && TB::Cardinality)
+ {
+ int piecesCount = pos.count<ALL_PIECES>();
+
+ if ( piecesCount <= TB::Cardinality
+ && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth)
+ && pos.rule50_count() == 0
+ && !pos.can_castle(ANY_CASTLING))
+ {
+ TB::ProbeState err;
+ TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err);
+
+ if (err != TB::ProbeState::FAIL)
+ {
+ thisThread->tbHits.fetch_add(1, std::memory_order_relaxed);
+
+ int drawScore = TB::UseRule50 ? 1 : 0;
+
+ value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1
+ : wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1
+ : VALUE_DRAW + 2 * wdl * drawScore;
+
+ Bound b = wdl < -drawScore ? BOUND_UPPER
+ : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT;
+
+ if ( b == BOUND_EXACT
+ || (b == BOUND_LOWER ? value >= beta : value <= alpha))
+ {
+ tte->save(posKey, value_to_tt(value, ss->ply), b,
+ std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY),
+ MOVE_NONE, VALUE_NONE, TT.generation());
+
+ return value;
+ }
+
+ if (PvNode)
+ {
+ if (b == BOUND_LOWER)
+ bestValue = value, alpha = std::max(alpha, bestValue);
+ else
+ maxValue = value;
+ }
+ }
+ }
+ }
+
+ // Step 5. Evaluate the position statically
+ if (inCheck)
+ {
+ ss->staticEval = eval = VALUE_NONE;
+ goto moves_loop;
+ }
+
+ else if (ttHit)
+ {
+ // Never assume anything on values stored in TT
+ if ((ss->staticEval = eval = tte->eval()) == VALUE_NONE)
+ eval = ss->staticEval = evaluate(pos);
+
+ // Can ttValue be used as a better position evaluation?
+ if ( ttValue != VALUE_NONE
+ && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER)))
+ eval = ttValue;
+ }
+ else
+ {
+ eval = ss->staticEval =
+ (ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
+ : -(ss-1)->staticEval + 2 * Eval::Tempo;
+
+ tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
+ ss->staticEval, TT.generation());
+ }
+
+ if (skipEarlyPruning || !pos.non_pawn_material(pos.side_to_move()))
+ goto moves_loop;
+
+ // Step 6. Razoring (skipped when in check)
+ if ( !PvNode
+ && depth < 4 * ONE_PLY
+ && eval + razor_margin <= alpha)
+ {
+ if (depth <= ONE_PLY)
+ return qsearch<NonPV, false>(pos, ss, alpha, alpha+1);
+
+ Value ralpha = alpha - razor_margin;
+ Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1);
+ if (v <= ralpha)
+ return v;
+ }
+
+ // Step 7. Futility pruning: child node (skipped when in check)
+ if ( !rootNode
+ && depth < 7 * ONE_PLY
+ && eval - futility_margin(depth) >= beta
+ && eval < VALUE_KNOWN_WIN) // Do not return unproven wins
+ return eval;
+
+ // Step 8. Null move search with verification search (is omitted in PV nodes)
+ if ( !PvNode
+ && eval >= beta
+ && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
+ && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 != thisThread->nmp_odd))
+ {
+
+ assert(eval - beta >= 0);
+
+ // Null move dynamic reduction based on depth and value
+ Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY;
+
+ ss->currentMove = MOVE_NULL;
+ ss->contHistory = &thisThread->contHistory[NO_PIECE][0];
+
+ pos.do_null_move(st);
+ Value nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -beta+1)
+ : - search<NonPV>(pos, ss+1, -beta, -beta+1, depth-R, !cutNode, true);
+ pos.undo_null_move();
+
+ if (nullValue >= beta)
+ {
+ // Do not return unproven mate scores
+ if (nullValue >= VALUE_MATE_IN_MAX_PLY)
+ nullValue = beta;
+
+ if (abs(beta) < VALUE_KNOWN_WIN && (depth < 12 * ONE_PLY || thisThread->nmp_ply))
+ return nullValue;
+
+ // Do verification search at high depths
+ // disable null move pruning for side to move for the first part of the remaining search tree
+ thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4;
+ thisThread->nmp_odd = ss->ply % 2;
+
+ Value v = depth-R < ONE_PLY ? qsearch<NonPV, false>(pos, ss, beta-1, beta)
+ : search<NonPV>(pos, ss, beta-1, beta, depth-R, false, true);
+
+ thisThread->nmp_odd = thisThread->nmp_ply = 0;
+
+ if (v >= beta)
+ return nullValue;
+ }
+ }
+
+ // Step 9. ProbCut (skipped when in check)
+ // If we have a good enough capture and a reduced search returns a value
+ // much above beta, we can (almost) safely prune the previous move.
+ if ( !PvNode
+ && depth >= 5 * ONE_PLY
+ && abs(beta) < VALUE_MATE_IN_MAX_PLY)
+ {
+ Value rbeta = std::min(beta + 200, VALUE_INFINITE);
+
+ assert(is_ok((ss-1)->currentMove));
+
+ MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);
+
+ while ((move = mp.next_move()) != MOVE_NONE)
+ if (pos.legal(move))
+ {
+ ss->currentMove = move;
+ ss->contHistory = &thisThread->contHistory[pos.moved_piece(move)][to_sq(move)];
+
+ assert(depth >= 5 * ONE_PLY);
+ pos.do_move(move, st);
+ value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, depth - 4 * ONE_PLY, !cutNode, false);
+ pos.undo_move(move);
+ if (value >= rbeta)
+ return value;
+ }
+ }
+
+ // Step 10. Internal iterative deepening (skipped when in check)
+ if ( depth >= 6 * ONE_PLY
+ && !ttMove
+ && (PvNode || ss->staticEval + 256 >= beta))
+ {
+ Depth d = (3 * depth / (4 * ONE_PLY) - 2) * ONE_PLY;
+ search<NT>(pos, ss, alpha, beta, d, cutNode, true);
+
+ tte = TT.probe(posKey, ttHit);
+ ttMove = ttHit ? tte->move() : MOVE_NONE;
+ }
+
+moves_loop: // When in check search starts from here
+
+ const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory };
+ Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
+
+ MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers);
+ value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
+ improving = ss->staticEval >= (ss-2)->staticEval
+ /* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */
+ ||(ss-2)->staticEval == VALUE_NONE;
+
+ singularExtensionNode = !rootNode
+ && depth >= 8 * ONE_PLY
+ && ttMove != MOVE_NONE
+ && ttValue != VALUE_NONE
+ && !excludedMove // Recursive singular search is not allowed
+ && (tte->bound() & BOUND_LOWER)
+ && tte->depth() >= depth - 3 * ONE_PLY;
+ skipQuiets = false;
+ ttCapture = false;
+ pvExact = PvNode && ttHit && tte->bound() == BOUND_EXACT;
+
+ // Step 11. Loop through moves
+ // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
+ while ((move = mp.next_move(skipQuiets)) != MOVE_NONE)
+ {
+ assert(is_ok(move));
+
+ if (move == excludedMove)
+ continue;
+
+ // At root obey the "searchmoves" option and skip moves not listed in Root
+ // Move List. As a consequence any illegal move is also skipped. In MultiPV
+ // mode we also skip PV moves which have been already searched.
+ if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx,
+ thisThread->rootMoves.end(), move))
+ continue;
+
+ ss->moveCount = ++moveCount;
+
+ if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
+ sync_cout << "info depth " << depth / ONE_PLY
+ << " currmove " << UCI::move(move, pos.is_chess960())
+ << " currmovenumber " << moveCount + thisThread->PVIdx << sync_endl;
+
+ if (PvNode)
+ (ss+1)->pv = nullptr;
+
+ extension = DEPTH_ZERO;
+ captureOrPromotion = pos.capture_or_promotion(move);
+ movedPiece = pos.moved_piece(move);
+
+ givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
+ ? pos.check_squares(type_of(movedPiece)) & to_sq(move)
+ : pos.gives_check(move);
+
+ moveCountPruning = depth < 16 * ONE_PLY
+ && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY];
+
+ // Step 12. Singular and Gives Check Extensions
+
+ // Singular extension search. If all moves but one fail low on a search of
+ // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
+ // is singular and should be extended. To verify this we do a reduced search
+ // on all the other moves but the ttMove and if the result is lower than
+ // ttValue minus a margin then we will extend the ttMove.
+ if ( singularExtensionNode
+ && move == ttMove
+ && pos.legal(move))
+ {
+ Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
+ Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY;
+ ss->excludedMove = move;
+ value = search<NonPV>(pos, ss, rBeta - 1, rBeta, d, cutNode, true);
+ ss->excludedMove = MOVE_NONE;
+
+ if (value < rBeta)
+ extension = ONE_PLY;
+ }
+ else if ( givesCheck
+ && !moveCountPruning
+ && pos.see_ge(move))
+ extension = ONE_PLY;
+
+ // Calculate new depth for this move
+ newDepth = depth - ONE_PLY + extension;
+
+ // Step 13. Pruning at shallow depth
+ if ( !rootNode
+ && pos.non_pawn_material(pos.side_to_move())
+ && bestValue > VALUE_MATED_IN_MAX_PLY)
+ {
+ if ( !captureOrPromotion
+ && !givesCheck
+ && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= Value(5000)))
+ {
+ // Move count based pruning
+ if (moveCountPruning)
+ {
+ skipQuiets = true;
+ continue;
+ }
+
+ // Reduced depth of the next LMR search
+ int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY;
+
+ // Countermoves based pruning
+ if ( lmrDepth < 3
+ && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold
+ && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold)
+ continue;
+
+ // Futility pruning: parent node
+ if ( lmrDepth < 7
+ && !inCheck
+ && ss->staticEval + 256 + 200 * lmrDepth <= alpha)
+ continue;
+
+ // Prune moves with negative SEE
+ if ( lmrDepth < 8
+ && !pos.see_ge(move, Value(-35 * lmrDepth * lmrDepth)))
+ continue;
+ }
+ else if ( depth < 7 * ONE_PLY
+ && !extension
+ && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY)))
+ continue;
+ }
+
+ // Speculative prefetch as early as possible
+ prefetch(TT.first_entry(pos.key_after(move)));
+
+ // Check for legality just before making the move
+ if (!rootNode && !pos.legal(move))
+ {
+ ss->moveCount = --moveCount;
+ continue;
+ }
+
+ if (move == ttMove && captureOrPromotion)
+ ttCapture = true;
+
+ // Update the current move (this must be done after singular extension search)
+ ss->currentMove = move;
+ ss->contHistory = &thisThread->contHistory[movedPiece][to_sq(move)];
+
+ // Step 14. Make the move
+ pos.do_move(move, st, givesCheck);
+
+ // Step 15. Reduced depth search (LMR). If the move fails high it will be
+ // re-searched at full depth.
+ if ( depth >= 3 * ONE_PLY
+ && moveCount > 1
+ && (!captureOrPromotion || moveCountPruning))
+ {
+ Depth r = reduction<PvNode>(improving, depth, moveCount);
+
+ if (captureOrPromotion)
+ r -= r ? ONE_PLY : DEPTH_ZERO;
+ else
+ {
+ // Decrease reduction if opponent's move count is high
+ if ((ss-1)->moveCount > 15)
+ r -= ONE_PLY;
+
+ // Decrease reduction for exact PV nodes
+ if (pvExact)
+ r -= ONE_PLY;
+
+ // Increase reduction if ttMove is a capture
+ if (ttCapture)
+ r += ONE_PLY;
+
+ // Increase reduction for cut nodes
+ if (cutNode)
+ r += 2 * ONE_PLY;
+
+ // Decrease reduction for moves that escape a capture. Filter out
+ // castling moves, because they are coded as "king captures rook" and
+ // hence break make_move().
+ else if ( type_of(move) == NORMAL
+ && !pos.see_ge(make_move(to_sq(move), from_sq(move))))
+ r -= 2 * ONE_PLY;
+
+ ss->statScore = thisThread->mainHistory[~pos.side_to_move()][from_to(move)]
+ + (*contHist[0])[movedPiece][to_sq(move)]
+ + (*contHist[1])[movedPiece][to_sq(move)]
+ + (*contHist[3])[movedPiece][to_sq(move)]
+ - 4000;
+
+ // Decrease/increase reduction by comparing opponent's stat score
+ if (ss->statScore >= 0 && (ss-1)->statScore < 0)
+ r -= ONE_PLY;
+
+ else if ((ss-1)->statScore >= 0 && ss->statScore < 0)
+ r += ONE_PLY;
+
+ // Decrease/increase reduction for moves with a good/bad history
+ r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->statScore / 20000) * ONE_PLY);
+ }
+
+ Depth d = std::max(newDepth - r, ONE_PLY);
+
+ value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true, false);
+
+ doFullDepthSearch = (value > alpha && d != newDepth);
+ }
+ else
+ doFullDepthSearch = !PvNode || moveCount > 1;
+
+ // Step 16. Full depth search when LMR is skipped or fails high
+ if (doFullDepthSearch)
+ value = newDepth < ONE_PLY ?
+ givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha)
+ : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha)
+ : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode, false);
+
+ // For PV nodes only, do a full PV search on the first move or after a fail
+ // high (in the latter case search only if value < beta), otherwise let the
+ // parent node fail low with value <= alpha and try another move.
+ if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta))))
+ {
+ (ss+1)->pv = pv;
+ (ss+1)->pv[0] = MOVE_NONE;
+
+ value = newDepth < ONE_PLY ?
+ givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha)
+ : -qsearch<PV, false>(pos, ss+1, -beta, -alpha)
+ : - search<PV>(pos, ss+1, -beta, -alpha, newDepth, false, false);
+ }
+
+ // Step 17. Undo move
+ pos.undo_move(move);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ // Step 18. Check for a new best move
+ // Finished searching the move. If a stop occurred, the return value of
+ // the search cannot be trusted, and we return immediately without
+ // updating best move, PV and TT.
+ if (Threads.stop.load(std::memory_order_relaxed))
+ return VALUE_ZERO;
+
+ if (rootNode)
+ {
+ RootMove& rm = *std::find(thisThread->rootMoves.begin(),
+ thisThread->rootMoves.end(), move);
+
+ // PV move or new best move ?
+ if (moveCount == 1 || value > alpha)
+ {
+ rm.score = value;
+ rm.selDepth = thisThread->selDepth;
+ rm.pv.resize(1);
+
+ assert((ss+1)->pv);
+
+ for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m)
+ rm.pv.push_back(*m);
+
+ // We record how often the best move has been changed in each
+ // iteration. This information is used for time management: When
+ // the best move changes frequently, we allocate some more time.
+ if (moveCount > 1 && thisThread == Threads.main())
+ ++static_cast<MainThread*>(thisThread)->bestMoveChanges;
+ }
+ else
+ // All other moves but the PV are set to the lowest value: this
+ // is not a problem when sorting because the sort is stable and the
+ // move position in the list is preserved - just the PV is pushed up.
+ rm.score = -VALUE_INFINITE;
+ }
+
+ if (value > bestValue)
+ {
+ bestValue = value;
+
+ if (value > alpha)
+ {
+ bestMove = move;
+
+ if (PvNode && !rootNode) // Update pv even in fail-high case
+ update_pv(ss->pv, move, (ss+1)->pv);
+
+ if (PvNode && value < beta) // Update alpha! Always alpha < beta
+ alpha = value;
+ else
+ {
+ assert(value >= beta); // Fail high
+ break;
+ }
+ }
+ }
+
+ if (!captureOrPromotion && move != bestMove && quietCount < 64)
+ quietsSearched[quietCount++] = move;
+ else if (captureOrPromotion && move != bestMove && captureCount < 32)
+ capturesSearched[captureCount++] = move;
+ }
+
+ // The following condition would detect a stop only after move loop has been
+ // completed. But in this case bestValue is valid because we have fully
+ // searched our subtree, and we can anyhow save the result in TT.
+ /*
+ if (Threads.stop)
+ return VALUE_DRAW;
+ */
+
+ // Step 20. Check for mate and stalemate
+ // All legal moves have been searched and if there are no legal moves, it
+ // must be a mate or a stalemate. If we are in a singular extension search then
+ // return a fail low score.
+
+ assert(moveCount || !inCheck || excludedMove || !MoveList<LEGAL>(pos).size());
+
+ if (!moveCount)
+ bestValue = excludedMove ? alpha
+ : inCheck ? mated_in(ss->ply) : VALUE_DRAW;
+ else if (bestMove)
+ {
+ // Quiet best move: update move sorting heuristics
+ if (!pos.capture_or_promotion(bestMove))
+ update_stats(pos, ss, bestMove, quietsSearched, quietCount, stat_bonus(depth));
+ else
+ update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth));
+
+ // Extra penalty for a quiet TT move in previous ply when it gets refuted
+ if ((ss-1)->moveCount == 1 && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+ }
+ // Bonus for prior countermove that caused the fail low
+ else if ( depth >= 3 * ONE_PLY
+ && !pos.captured_piece()
+ && is_ok((ss-1)->currentMove))
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth));
+
+ if (PvNode)
+ bestValue = std::min(bestValue, maxValue);
+
+ if (!excludedMove)
+ tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ bestValue >= beta ? BOUND_LOWER :
+ PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
+ depth, bestMove, ss->staticEval, TT.generation());
+
+ assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
+
+ return bestValue;
+ }
+
+
+ // qsearch() is the quiescence search function, which is called by the main
+ // search function with depth zero, or recursively with depth less than ONE_PLY.
+
+ template <NodeType NT, bool InCheck>
+ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
+
+ const bool PvNode = NT == PV;
+
+ assert(InCheck == bool(pos.checkers()));
+ assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
+ assert(PvNode || (alpha == beta - 1));
+ assert(depth <= DEPTH_ZERO);
+ assert(depth / ONE_PLY * ONE_PLY == depth);
+
+ Move pv[MAX_PLY+1];
+ StateInfo st;
+ TTEntry* tte;
+ Key posKey;
+ Move ttMove, move, bestMove;
+ Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
+ bool ttHit, givesCheck, evasionPrunable;
+ Depth ttDepth;
+ int moveCount;
+
+ if (PvNode)
+ {
+ oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves
+ (ss+1)->pv = pv;
+ ss->pv[0] = MOVE_NONE;
+ }
+
+ ss->currentMove = bestMove = MOVE_NONE;
+ (ss+1)->ply = ss->ply + 1;
+ moveCount = 0;
+
+ // Check for an instant draw or if the maximum ply has been reached
+ if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY)
+ return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW;
+
+ assert(0 <= ss->ply && ss->ply < MAX_PLY);
+
+ // Decide whether or not to include checks: this fixes also the type of
+ // TT entry depth that we are going to use. Note that in qsearch we use
+ // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
+ ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
+ : DEPTH_QS_NO_CHECKS;
+ // Transposition table lookup
+ posKey = pos.key();
+ tte = TT.probe(posKey, ttHit);
+ ttMove = ttHit ? tte->move() : MOVE_NONE;
+ ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+
+ if ( !PvNode
+ && ttHit
+ && tte->depth() >= ttDepth
+ && ttValue != VALUE_NONE // Only in case of TT access race
+ && (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
+ : (tte->bound() & BOUND_UPPER)))
+ return ttValue;
+
+ // Evaluate the position statically
+ if (InCheck)
+ {
+ ss->staticEval = VALUE_NONE;
+ bestValue = futilityBase = -VALUE_INFINITE;
+ }
+ else
+ {
+ if (ttHit)
+ {
+ // Never assume anything on values stored in TT
+ if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE)
+ ss->staticEval = bestValue = evaluate(pos);
+
+ // Can ttValue be used as a better position evaluation?
+ if ( ttValue != VALUE_NONE
+ && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)))
+ bestValue = ttValue;
+ }
+ else
+ ss->staticEval = bestValue =
+ (ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
+ : -(ss-1)->staticEval + 2 * Eval::Tempo;
+
+ // Stand pat. Return immediately if static value is at least beta
+ if (bestValue >= beta)
+ {
+ if (!ttHit)
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER,
+ DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation());
+
+ return bestValue;
+ }
+
+ if (PvNode && bestValue > alpha)
+ alpha = bestValue;
+
+ futilityBase = bestValue + 128;
+ }
+
+ // 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 >= DEPTH_QS_CHECKS) will
+ // be generated.
+ MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove));
+
+ // Loop through the moves until no moves remain or a beta cutoff occurs
+ while ((move = mp.next_move()) != MOVE_NONE)
+ {
+ assert(is_ok(move));
+
+ givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
+ ? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move)
+ : pos.gives_check(move);
+
+ moveCount++;
+
+ // Futility pruning
+ if ( !InCheck
+ && !givesCheck
+ && futilityBase > -VALUE_KNOWN_WIN
+ && !pos.advanced_pawn_push(move))
+ {
+ assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push
+
+ futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))];
+
+ if (futilityValue <= alpha)
+ {
+ bestValue = std::max(bestValue, futilityValue);
+ continue;
+ }
+
+ if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1))
+ {
+ bestValue = std::max(bestValue, futilityBase);
+ continue;
+ }
+ }
+
+ // Detect non-capture evasions that are candidates to be pruned
+ evasionPrunable = InCheck
+ && (depth != DEPTH_ZERO || moveCount > 2)
+ && bestValue > VALUE_MATED_IN_MAX_PLY
+ && !pos.capture(move);
+
+ // Don't search moves with negative SEE values
+ if ( (!InCheck || evasionPrunable)
+ && !pos.see_ge(move))
+ continue;
+
+ // Speculative prefetch as early as possible
+ prefetch(TT.first_entry(pos.key_after(move)));
+
+ // Check for legality just before making the move
+ if (!pos.legal(move))
+ {
+ moveCount--;
+ continue;
+ }
+
+ ss->currentMove = move;
+
+ // Make and search the move
+ pos.do_move(move, st, givesCheck);
+ value = givesCheck ? -qsearch<NT, true>(pos, ss+1, -beta, -alpha, depth - ONE_PLY)
+ : -qsearch<NT, false>(pos, ss+1, -beta, -alpha, depth - ONE_PLY);
+ pos.undo_move(move);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ // Check for a new best move
+ if (value > bestValue)
+ {
+ bestValue = value;
+
+ if (value > alpha)
+ {
+ if (PvNode) // Update pv even in fail-high case
+ update_pv(ss->pv, move, (ss+1)->pv);
+
+ if (PvNode && value < beta) // Update alpha here!
+ {
+ alpha = value;
+ bestMove = move;
+ }
+ else // Fail high
+ {
+ tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
+ ttDepth, move, ss->staticEval, TT.generation());
+
+ return value;
+ }
+ }
+ }
+ }
+
+ // All legal moves have been searched. A special case: If we're in check
+ // and no legal moves were found, it is checkmate.
+ if (InCheck && bestValue == -VALUE_INFINITE)
+ return mated_in(ss->ply); // Plies to mate from the root
+
+ tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
+ ttDepth, bestMove, ss->staticEval, TT.generation());
+
+ assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
+
+ return bestValue;
+ }
+
+
+ // value_to_tt() adjusts a mate score from "plies to mate from the root" to
+ // "plies to mate from the current position". Non-mate scores are unchanged.
+ // The function is called before storing a value in the transposition table.
+
+ Value value_to_tt(Value v, int ply) {
+
+ assert(v != VALUE_NONE);
+
+ return v >= VALUE_MATE_IN_MAX_PLY ? v + ply
+ : v <= VALUE_MATED_IN_MAX_PLY ? v - ply : v;
+ }
+
+
+ // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
+ // from the transposition table (which refers to the plies to mate/be mated
+ // from current position) to "plies to mate/be mated from the root".
+
+ Value value_from_tt(Value v, int ply) {
+
+ return v == VALUE_NONE ? VALUE_NONE
+ : v >= VALUE_MATE_IN_MAX_PLY ? v - ply
+ : v <= VALUE_MATED_IN_MAX_PLY ? v + ply : v;
+ }
+
+
+ // update_pv() adds current move and appends child pv[]
+
+ void update_pv(Move* pv, Move move, Move* childPv) {
+
+ for (*pv++ = move; childPv && *childPv != MOVE_NONE; )
+ *pv++ = *childPv++;
+ *pv = MOVE_NONE;
+ }
+
+
+ // update_continuation_histories() updates histories of the move pairs formed
+ // by moves at ply -1, -2, and -4 with current move.
+
+ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
+
+ for (int i : {1, 2, 4})
+ if (is_ok((ss-i)->currentMove))
+ (ss-i)->contHistory->update(pc, to, bonus);
+ }
+
+
+ // update_capture_stats() updates move sorting heuristics when a new capture best move is found
+
+ void update_capture_stats(const Position& pos, Move move,
+ Move* captures, int captureCnt, int bonus) {
+
+ CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
+ Piece moved_piece = pos.moved_piece(move);
+ PieceType captured = type_of(pos.piece_on(to_sq(move)));
+ captureHistory.update(moved_piece, to_sq(move), captured, bonus);
+
+ // Decrease all the other played capture moves
+ for (int i = 0; i < captureCnt; ++i)
+ {
+ moved_piece = pos.moved_piece(captures[i]);
+ captured = type_of(pos.piece_on(to_sq(captures[i])));
+ captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus);
+ }
+ }
+
+
+ // update_stats() updates move sorting heuristics when a new quiet best move is found
+
+ void update_stats(const Position& pos, Stack* ss, Move move,
+ Move* quiets, int quietsCnt, int bonus) {
+
+ if (ss->killers[0] != move)
+ {
+ ss->killers[1] = ss->killers[0];
+ ss->killers[0] = move;
+ }
+
+ Color c = pos.side_to_move();
+ Thread* thisThread = pos.this_thread();
+ thisThread->mainHistory.update(c, move, bonus);
+ update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus);
+
+ if (is_ok((ss-1)->currentMove))
+ {
+ Square prevSq = to_sq((ss-1)->currentMove);
+ thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
+ }
+
+ // Decrease all the other played quiet moves
+ for (int i = 0; i < quietsCnt; ++i)
+ {
+ thisThread->mainHistory.update(c, quiets[i], -bonus);
+ update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
+ }
+ }
+
+
+ // Is the PV leading to a draw position? Assumes all pv moves are legal
+ bool pv_is_draw(Position& pos) {
+
+ StateInfo st[MAX_PLY];
+ auto& pv = pos.this_thread()->rootMoves[0].pv;
+
+ for (size_t i = 0; i < pv.size(); ++i)
+ pos.do_move(pv[i], st[i]);
+
+ bool isDraw = pos.is_draw(pv.size());
+
+ for (size_t i = pv.size(); i > 0; --i)
+ pos.undo_move(pv[i-1]);
+
+ return isDraw;
+ }
+
+
+ // When playing with strength handicap, choose best move among a set of RootMoves
+ // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
+
+ Move Skill::pick_best(size_t multiPV) {
+
+ const RootMoves& rootMoves = Threads.main()->rootMoves;
+ static PRNG rng(now()); // PRNG sequence should be non-deterministic
+
+ // RootMoves are already sorted by score in descending order
+ Value topScore = rootMoves[0].score;
+ int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg);
+ int weakness = 120 - 2 * level;
+ int maxScore = -VALUE_INFINITE;
+
+ // Choose best move. For each move score we add two terms, both dependent on
+ // weakness. One is deterministic and bigger for weaker levels, and one is
+ // random. Then we choose the move with the resulting highest score.
+ for (size_t i = 0; i < multiPV; ++i)
+ {
+ // This is our magic formula
+ int push = ( weakness * int(topScore - rootMoves[i].score)
+ + delta * (rng.rand<unsigned>() % weakness)) / 128;
+
+ if (rootMoves[i].score + push >= maxScore)
+ {
+ maxScore = rootMoves[i].score + push;
+ best = rootMoves[i].pv[0];
+ }
+ }
+
+ return best;
+ }
+
+} // namespace
+
+ // check_time() is used to print debug info and, more importantly, to detect
+ // when we are out of available time and thus stop the search.
+
+ void MainThread::check_time() {
+
+ if (--callsCnt > 0)
+ return;
+
+ // At low node count increase the checking rate to about 0.1% of nodes
+ // otherwise use a default value.
+ callsCnt = Limits.nodes ? std::min(4096, int(Limits.nodes / 1024)) : 4096;
+
+ static TimePoint lastInfoTime = now();
+
+ int elapsed = Time.elapsed();
+ TimePoint tick = Limits.startTime + elapsed;
+
+ if (tick - lastInfoTime >= 1000)
+ {
+ lastInfoTime = tick;
+ dbg_print();
+ }
+
+ // An engine may not stop pondering until told so by the GUI
+ if (Threads.ponder)
+ return;
+
+ if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10)
+ || (Limits.movetime && elapsed >= Limits.movetime)
+ || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
+ Threads.stop = true;
+ }
+
+
+/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
+/// that all (if any) unsearched PV lines are sent using a previous search score.
+
+string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
+
+ std::stringstream ss;
+ int elapsed = Time.elapsed() + 1;
+ const RootMoves& rootMoves = pos.this_thread()->rootMoves;
+ size_t PVIdx = pos.this_thread()->PVIdx;
+ size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size());
+ uint64_t nodesSearched = Threads.nodes_searched();
+ uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0);
+
+ for (size_t i = 0; i < multiPV; ++i)
+ {
+ bool updated = (i <= PVIdx && rootMoves[i].score != -VALUE_INFINITE);
+
+ if (depth == ONE_PLY && !updated)
+ continue;
+
+ Depth d = updated ? depth : depth - ONE_PLY;
+ Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore;
+
+ bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY;
+ v = tb ? TB::Score : v;
+
+ if (ss.rdbuf()->in_avail()) // Not at first line
+ ss << "\n";
+
+ ss << "info"
+ << " depth " << d / ONE_PLY
+ << " seldepth " << rootMoves[i].selDepth
+ << " multipv " << i + 1
+ << " score " << UCI::value(v);
+
+ if (!tb && i == PVIdx)
+ ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
+
+ ss << " nodes " << nodesSearched
+ << " nps " << nodesSearched * 1000 / elapsed;
+
+ if (elapsed > 1000) // Earlier makes little sense
+ ss << " hashfull " << TT.hashfull();
+
+ ss << " tbhits " << tbHits
+ << " time " << elapsed
+ << " pv";
+
+ for (Move m : rootMoves[i].pv)
+ ss << " " << UCI::move(m, pos.is_chess960());
+ }
+
+ return ss.str();
+}
+
+
+/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
+/// before exiting the search, for instance, in case we stop the search during a
+/// fail high at root. We try hard to have a ponder move to return to the GUI,
+/// otherwise in case of 'ponder on' we have nothing to think on.
+
+bool RootMove::extract_ponder_from_tt(Position& pos) {
+
+ StateInfo st;
+ bool ttHit;
+
+ assert(pv.size() == 1);
+
+ if (!pv[0])
+ return false;
+
+ pos.do_move(pv[0], st);
+ TTEntry* tte = TT.probe(pos.key(), ttHit);
+
+ if (ttHit)
+ {
+ Move m = tte->move(); // Local copy to be SMP safe
+ if (MoveList<LEGAL>(pos).contains(m))
+ pv.push_back(m);
+ }
+
+ pos.undo_move(pv[0]);
+ return pv.size() > 1;
+}
+
+void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) {
+
+ RootInTB = false;
+ UseRule50 = Options["Syzygy50MoveRule"];
+ ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
+ Cardinality = Options["SyzygyProbeLimit"];
+
+ // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality
+ if (Cardinality > MaxCardinality)
+ {
+ Cardinality = MaxCardinality;
+ ProbeDepth = DEPTH_ZERO;
+ }
+
+ if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING))
+ return;
+
+ // Don't filter any moves if the user requested analysis on multiple
+ if (Options["MultiPV"] != 1)
+ return;
+
+ // If the current root position is in the tablebases, then RootMoves
+ // contains only moves that preserve the draw or the win.
+ RootInTB = root_probe(pos, rootMoves, TB::Score);
+
+ if (RootInTB)
+ Cardinality = 0; // Do not probe tablebases during the search
+
+ else // If DTZ tables are missing, use WDL tables as a fallback
+ {
+ // Filter out moves that do not preserve the draw or the win.
+ RootInTB = root_probe_wdl(pos, rootMoves, TB::Score);
+
+ // Only probe during search if winning
+ if (RootInTB && TB::Score <= VALUE_DRAW)
+ Cardinality = 0;
+ }
+
+ if (RootInTB && !UseRule50)
+ TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
+ : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
+ : VALUE_DRAW;
+
+ // Since root_probe() and root_probe_wdl() dirty the root move scores,
+ // we reset them to -VALUE_INFINITE
+ for (RootMove& rm : rootMoves)
+ rm.score = -VALUE_INFINITE;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SEARCH_H_INCLUDED
+#define SEARCH_H_INCLUDED
+
+#include <vector>
+
+#include "misc.h"
+#include "movepick.h"
+#include "types.h"
+
+class Position;
+
+namespace Search {
+
+/// Threshold used for countermoves based pruning
+const int CounterMovePruneThreshold = 0;
+
+
+/// Stack 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 Stack objects, indexed by the current ply.
+
+struct Stack {
+ Move* pv;
+ PieceToHistory* contHistory;
+ int ply;
+ Move currentMove;
+ Move excludedMove;
+ Move killers[2];
+ Value staticEval;
+ int statScore;
+ int moveCount;
+};
+
+
+/// RootMove struct is used for moves at the root of the tree. For each root move
+/// we store a score and a PV (really a refutation in the case of moves which
+/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
+
+struct RootMove {
+
+ explicit RootMove(Move m) : pv(1, m) {}
+ bool extract_ponder_from_tt(Position& pos);
+ bool operator==(const Move& m) const { return pv[0] == m; }
+ bool operator<(const RootMove& m) const { // Sort in descending order
+ return m.score != score ? m.score < score
+ : m.previousScore < previousScore;
+ }
+
+ Value score = -VALUE_INFINITE;
+ Value previousScore = -VALUE_INFINITE;
+ int selDepth = 0;
+ std::vector<Move> pv;
+};
+
+typedef std::vector<RootMove> RootMoves;
+
+
+/// LimitsType struct stores information sent by GUI about available time to
+/// search the current move, maximum depth/time, or if we are in analysis mode.
+
+struct LimitsType {
+
+ LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
+ nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] =
+ npmsec = movestogo = depth = movetime = mate = perft = infinite = 0;
+ }
+
+ bool use_time_management() const {
+ return !(mate | movetime | depth | nodes | perft | infinite);
+ }
+
+ std::vector<Move> searchmoves;
+ int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth,
+ movetime, mate, perft, infinite;
+ int64_t nodes;
+ TimePoint startTime;
+};
+
+extern LimitsType Limits;
+
+void init();
+void clear();
+
+} // namespace Search
+
+#endif // #ifndef SEARCH_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (c) 2013 Ronald de Man
+ Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <atomic>
+#include <cstdint>
+#include <cstring> // For std::memset
+#include <deque>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <sstream>
+#include <type_traits>
+
+#include "../bitboard.h"
+#include "../movegen.h"
+#include "../position.h"
+#include "../search.h"
+#include "../thread_win32.h"
+#include "../types.h"
+
+#include "tbprobe.h"
+
+#ifndef _WIN32
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#else
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+#endif
+
+using namespace Tablebases;
+
+int Tablebases::MaxCardinality;
+
+namespace {
+
+// Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables
+enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, SingleValue = 128 };
+
+inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); }
+inline Square operator^=(Square& s, int i) { return s = Square(int(s) ^ i); }
+inline Square operator^(Square s, int i) { return Square(int(s) ^ i); }
+
+// DTZ tables don't store valid scores for moves that reset the rule50 counter
+// like captures and pawn moves but we can easily recover the correct dtz of the
+// previous move if we know the position's WDL score.
+int dtz_before_zeroing(WDLScore wdl) {
+ return wdl == WDLWin ? 1 :
+ wdl == WDLCursedWin ? 101 :
+ wdl == WDLBlessedLoss ? -101 :
+ wdl == WDLLoss ? -1 : 0;
+}
+
+// Return the sign of a number (-1, 0, 1)
+template <typename T> int sign_of(T val) {
+ return (T(0) < val) - (val < T(0));
+}
+
+// Numbers in little endian used by sparseIndex[] to point into blockLength[]
+struct SparseEntry {
+ char block[4]; // Number of block
+ char offset[2]; // Offset within the block
+};
+
+static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes");
+
+typedef uint16_t Sym; // Huffman symbol
+
+struct LR {
+ enum Side { Left, Right, Value };
+
+ uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12
+ // bits is the right-hand symbol. If symbol has length 1,
+ // then the first byte is the stored value.
+ template<Side S>
+ Sym get() {
+ return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] :
+ S == Right ? (lr[2] << 4) | (lr[1] >> 4) :
+ S == Value ? lr[0] : (assert(false), Sym(-1));
+ }
+};
+
+static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes");
+
+const int TBPIECES = 6;
+
+struct PairsData {
+ int flags;
+ size_t sizeofBlock; // Block size in bytes
+ size_t span; // About every span values there is a SparseIndex[] entry
+ int blocksNum; // Number of blocks in the TB file
+ int maxSymLen; // Maximum length in bits of the Huffman symbols
+ int minSymLen; // Minimum length in bits of the Huffman symbols
+ Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value
+ LR* btree; // btree[sym] stores the left and right symbols that expand sym
+ uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536
+ int blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum
+ SparseEntry* sparseIndex; // Partial indices into blockLength[]
+ size_t sparseIndexSize; // Size of SparseIndex[] table
+ uint8_t* data; // Start of Huffman compressed data
+ std::vector<uint64_t> base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l
+ std::vector<uint8_t> symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256
+ Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups
+ uint64_t groupIdx[TBPIECES+1]; // Start index used for the encoding of the group's pieces
+ int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1)
+};
+
+// Helper struct to avoid manually defining entry copy constructor as we
+// should because the default one is not compatible with std::atomic_bool.
+struct Atomic {
+ Atomic() = default;
+ Atomic(const Atomic& e) { ready = e.ready.load(); } // MSVC 2013 wants assignment within body
+ std::atomic_bool ready;
+};
+
+// We define types for the different parts of the WDLEntry and DTZEntry with
+// corresponding specializations for pieces or pawns.
+
+struct WDLEntryPiece {
+ PairsData* precomp;
+};
+
+struct WDLEntryPawn {
+ uint8_t pawnCount[2]; // [Lead color / other color]
+ WDLEntryPiece file[2][4]; // [wtm / btm][FILE_A..FILE_D]
+};
+
+struct DTZEntryPiece {
+ PairsData* precomp;
+ uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss
+ uint8_t* map;
+};
+
+struct DTZEntryPawn {
+ uint8_t pawnCount[2];
+ DTZEntryPiece file[4];
+ uint8_t* map;
+};
+
+struct TBEntry : public Atomic {
+ void* baseAddress;
+ uint64_t mapping;
+ Key key;
+ Key key2;
+ int pieceCount;
+ bool hasPawns;
+ bool hasUniquePieces;
+};
+
+// Now the main types: WDLEntry and DTZEntry
+struct WDLEntry : public TBEntry {
+ WDLEntry(const std::string& code);
+ ~WDLEntry();
+ union {
+ WDLEntryPiece pieceTable[2]; // [wtm / btm]
+ WDLEntryPawn pawnTable;
+ };
+};
+
+struct DTZEntry : public TBEntry {
+ DTZEntry(const WDLEntry& wdl);
+ ~DTZEntry();
+ union {
+ DTZEntryPiece pieceTable;
+ DTZEntryPawn pawnTable;
+ };
+};
+
+typedef decltype(WDLEntry::pieceTable) WDLPieceTable;
+typedef decltype(DTZEntry::pieceTable) DTZPieceTable;
+typedef decltype(WDLEntry::pawnTable ) WDLPawnTable;
+typedef decltype(DTZEntry::pawnTable ) DTZPawnTable;
+
+auto item(WDLPieceTable& e, int stm, int ) -> decltype(e[stm])& { return e[stm]; }
+auto item(DTZPieceTable& e, int , int ) -> decltype(e)& { return e; }
+auto item(WDLPawnTable& e, int stm, int f) -> decltype(e.file[stm][f])& { return e.file[stm][f]; }
+auto item(DTZPawnTable& e, int , int f) -> decltype(e.file[f])& { return e.file[f]; }
+
+template<typename E> struct Ret { typedef int type; };
+template<> struct Ret<WDLEntry> { typedef WDLScore type; };
+
+int MapPawns[SQUARE_NB];
+int MapB1H1H7[SQUARE_NB];
+int MapA1D1D4[SQUARE_NB];
+int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB]
+
+// Comparison function to sort leading pawns in ascending MapPawns[] order
+bool pawns_comp(Square i, Square j) { return MapPawns[i] < MapPawns[j]; }
+int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); }
+
+const Value WDL_to_value[] = {
+ -VALUE_MATE + MAX_PLY + 1,
+ VALUE_DRAW - 2,
+ VALUE_DRAW,
+ VALUE_DRAW + 2,
+ VALUE_MATE - MAX_PLY - 1
+};
+
+const std::string PieceToChar = " PNBRQK pnbrqk";
+
+int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements
+int LeadPawnIdx[5][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB]
+int LeadPawnsSize[5][4]; // [leadPawnsCnt][FILE_A..FILE_D]
+
+enum { BigEndian, LittleEndian };
+
+template<typename T, int Half = sizeof(T) / 2, int End = sizeof(T) - 1>
+inline void swap_byte(T& x)
+{
+ char tmp, *c = (char*)&x;
+ for (int i = 0; i < Half; ++i)
+ tmp = c[i], c[i] = c[End - i], c[End - i] = tmp;
+}
+template<> inline void swap_byte<uint8_t, 0, 0>(uint8_t&) {}
+
+template<typename T, int LE> T number(void* addr)
+{
+ const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
+ const bool IsLittleEndian = (Le.c[0] == 4);
+
+ T v;
+
+ if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare)
+ std::memcpy(&v, addr, sizeof(T));
+ else
+ v = *((T*)addr);
+
+ if (LE != IsLittleEndian)
+ swap_byte(v);
+ return v;
+}
+
+class HashTable {
+
+ typedef std::pair<WDLEntry*, DTZEntry*> EntryPair;
+ typedef std::pair<Key, EntryPair> Entry;
+
+ static const int TBHASHBITS = 10;
+ static const int HSHMAX = 5;
+
+ Entry hashTable[1 << TBHASHBITS][HSHMAX];
+
+ std::deque<WDLEntry> wdlTable;
+ std::deque<DTZEntry> dtzTable;
+
+ void insert(Key key, WDLEntry* wdl, DTZEntry* dtz) {
+ Entry* entry = hashTable[key >> (64 - TBHASHBITS)];
+
+ for (int i = 0; i < HSHMAX; ++i, ++entry)
+ if (!entry->second.first || entry->first == key) {
+ *entry = std::make_pair(key, std::make_pair(wdl, dtz));
+ return;
+ }
+
+ std::cerr << "HSHMAX too low!" << std::endl;
+ exit(1);
+ }
+
+public:
+ template<typename E, int I = std::is_same<E, WDLEntry>::value ? 0 : 1>
+ E* get(Key key) {
+ Entry* entry = hashTable[key >> (64 - TBHASHBITS)];
+
+ for (int i = 0; i < HSHMAX; ++i, ++entry)
+ if (entry->first == key)
+ return std::get<I>(entry->second);
+
+ return nullptr;
+ }
+
+ void clear() {
+ std::memset(hashTable, 0, sizeof(hashTable));
+ wdlTable.clear();
+ dtzTable.clear();
+ }
+ size_t size() const { return wdlTable.size(); }
+ void insert(const std::vector<PieceType>& pieces);
+};
+
+HashTable EntryTable;
+
+class TBFile : public std::ifstream {
+
+ std::string fname;
+
+public:
+ // Look for and open the file among the Paths directories where the .rtbw
+ // and .rtbz files can be found. Multiple directories are separated by ";"
+ // on Windows and by ":" on Unix-based operating systems.
+ //
+ // Example:
+ // C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6
+ static std::string Paths;
+
+ TBFile(const std::string& f) {
+
+#ifndef _WIN32
+ const char SepChar = ':';
+#else
+ const char SepChar = ';';
+#endif
+ std::stringstream ss(Paths);
+ std::string path;
+
+ while (std::getline(ss, path, SepChar)) {
+ fname = path + "/" + f;
+ std::ifstream::open(fname);
+ if (is_open())
+ return;
+ }
+ }
+
+ // Memory map the file and check it. File should be already open and will be
+ // closed after mapping.
+ uint8_t* map(void** baseAddress, uint64_t* mapping, const uint8_t* TB_MAGIC) {
+
+ assert(is_open());
+
+ close(); // Need to re-open to get native file descriptor
+
+#ifndef _WIN32
+ struct stat statbuf;
+ int fd = ::open(fname.c_str(), O_RDONLY);
+
+ if (fd == -1)
+ return *baseAddress = nullptr, nullptr;
+
+ fstat(fd, &statbuf);
+ *mapping = statbuf.st_size;
+ *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ ::close(fd);
+
+ if (*baseAddress == MAP_FAILED) {
+ std::cerr << "Could not mmap() " << fname << std::endl;
+ exit(1);
+ }
+#else
+ HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (fd == INVALID_HANDLE_VALUE)
+ return *baseAddress = nullptr, nullptr;
+
+ DWORD size_high;
+ DWORD size_low = GetFileSize(fd, &size_high);
+ HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
+ CloseHandle(fd);
+
+ if (!mmap) {
+ std::cerr << "CreateFileMapping() failed" << std::endl;
+ exit(1);
+ }
+
+ *mapping = (uint64_t)mmap;
+ *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
+
+ if (!*baseAddress) {
+ std::cerr << "MapViewOfFile() failed, name = " << fname
+ << ", error = " << GetLastError() << std::endl;
+ exit(1);
+ }
+#endif
+ uint8_t* data = (uint8_t*)*baseAddress;
+
+ if ( *data++ != *TB_MAGIC++
+ || *data++ != *TB_MAGIC++
+ || *data++ != *TB_MAGIC++
+ || *data++ != *TB_MAGIC) {
+ std::cerr << "Corrupted table in file " << fname << std::endl;
+ unmap(*baseAddress, *mapping);
+ return *baseAddress = nullptr, nullptr;
+ }
+
+ return data;
+ }
+
+ static void unmap(void* baseAddress, uint64_t mapping) {
+
+#ifndef _WIN32
+ munmap(baseAddress, mapping);
+#else
+ UnmapViewOfFile(baseAddress);
+ CloseHandle((HANDLE)mapping);
+#endif
+ }
+};
+
+std::string TBFile::Paths;
+
+WDLEntry::WDLEntry(const std::string& code) {
+
+ StateInfo st;
+ Position pos;
+
+ memset(this, 0, sizeof(WDLEntry));
+
+ ready = false;
+ key = pos.set(code, WHITE, &st).material_key();
+ pieceCount = popcount(pos.pieces());
+ hasPawns = pos.pieces(PAWN);
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = PAWN; pt < KING; ++pt)
+ if (popcount(pos.pieces(c, pt)) == 1)
+ hasUniquePieces = true;
+
+ if (hasPawns) {
+ // Set the leading color. In case both sides have pawns the leading color
+ // is the side with less pawns because this leads to better compression.
+ bool c = !pos.count<PAWN>(BLACK)
+ || ( pos.count<PAWN>(WHITE)
+ && pos.count<PAWN>(BLACK) >= pos.count<PAWN>(WHITE));
+
+ pawnTable.pawnCount[0] = pos.count<PAWN>(c ? WHITE : BLACK);
+ pawnTable.pawnCount[1] = pos.count<PAWN>(c ? BLACK : WHITE);
+ }
+
+ key2 = pos.set(code, BLACK, &st).material_key();
+}
+
+WDLEntry::~WDLEntry() {
+
+ if (baseAddress)
+ TBFile::unmap(baseAddress, mapping);
+
+ for (int i = 0; i < 2; ++i)
+ if (hasPawns)
+ for (File f = FILE_A; f <= FILE_D; ++f)
+ delete pawnTable.file[i][f].precomp;
+ else
+ delete pieceTable[i].precomp;
+}
+
+DTZEntry::DTZEntry(const WDLEntry& wdl) {
+
+ memset(this, 0, sizeof(DTZEntry));
+
+ ready = false;
+ key = wdl.key;
+ key2 = wdl.key2;
+ pieceCount = wdl.pieceCount;
+ hasPawns = wdl.hasPawns;
+ hasUniquePieces = wdl.hasUniquePieces;
+
+ if (hasPawns) {
+ pawnTable.pawnCount[0] = wdl.pawnTable.pawnCount[0];
+ pawnTable.pawnCount[1] = wdl.pawnTable.pawnCount[1];
+ }
+}
+
+DTZEntry::~DTZEntry() {
+
+ if (baseAddress)
+ TBFile::unmap(baseAddress, mapping);
+
+ if (hasPawns)
+ for (File f = FILE_A; f <= FILE_D; ++f)
+ delete pawnTable.file[f].precomp;
+ else
+ delete pieceTable.precomp;
+}
+
+void HashTable::insert(const std::vector<PieceType>& pieces) {
+
+ std::string code;
+
+ for (PieceType pt : pieces)
+ code += PieceToChar[pt];
+
+ TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK
+
+ if (!file.is_open()) // Only WDL file is checked
+ return;
+
+ file.close();
+
+ MaxCardinality = std::max((int)pieces.size(), MaxCardinality);
+
+ wdlTable.emplace_back(code);
+ dtzTable.emplace_back(wdlTable.back());
+
+ insert(wdlTable.back().key , &wdlTable.back(), &dtzTable.back());
+ insert(wdlTable.back().key2, &wdlTable.back(), &dtzTable.back());
+}
+
+// TB tables are compressed with canonical Huffman code. The compressed data is divided into
+// blocks of size d->sizeofBlock, and each block stores a variable number of symbols.
+// Each symbol represents either a WDL or a (remapped) DTZ value, or a pair of other symbols
+// (recursively). If you keep expanding the symbols in a block, you end up with up to 65536
+// WDL or DTZ values. Each symbol represents up to 256 values and will correspond after
+// Huffman coding to at least 1 bit. So a block of 32 bytes corresponds to at most
+// 32 x 8 x 256 = 65536 values. This maximum is only reached for tables that consist mostly
+// of draws or mostly of wins, but such tables are actually quite common. In principle, the
+// blocks in WDL tables are 64 bytes long (and will be aligned on cache lines). But for
+// mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so
+// in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long.
+// The generator picks the size that leads to the smallest table. The "book" of symbols and
+// Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file
+// will have one table for wtm and one for btm, a TB file with pawns will have tables per
+// file a,b,c,d also in this case one set for wtm and one for btm.
+int decompress_pairs(PairsData* d, uint64_t idx) {
+
+ // Special case where all table positions store the same value
+ if (d->flags & TBFlag::SingleValue)
+ return d->minSymLen;
+
+ // First we need to locate the right block that stores the value at index "idx".
+ // Because each block n stores blockLength[n] + 1 values, the index i of the block
+ // that contains the value at position idx is:
+ //
+ // for (i = -1, sum = 0; sum <= idx; i++)
+ // sum += blockLength[i + 1] + 1;
+ //
+ // This can be slow, so we use SparseIndex[] populated with a set of SparseEntry that
+ // point to known indices into blockLength[]. Namely SparseIndex[k] is a SparseEntry
+ // that stores the blockLength[] index and the offset within that block of the value
+ // with index I(k), where:
+ //
+ // I(k) = k * d->span + d->span / 2 (1)
+
+ // First step is to get the 'k' of the I(k) nearest to our idx, using definition (1)
+ uint32_t k = idx / d->span;
+
+ // Then we read the corresponding SparseIndex[] entry
+ uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
+ int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
+
+ // Now compute the difference idx - I(k). From definition of k we know that
+ //
+ // idx = k * d->span + idx % d->span (2)
+ //
+ // So from (1) and (2) we can compute idx - I(K):
+ int diff = idx % d->span - d->span / 2;
+
+ // Sum the above to offset to find the offset corresponding to our idx
+ offset += diff;
+
+ // Move to previous/next block, until we reach the correct block that contains idx,
+ // that is when 0 <= offset <= d->blockLength[block]
+ while (offset < 0)
+ offset += d->blockLength[--block] + 1;
+
+ while (offset > d->blockLength[block])
+ offset -= d->blockLength[block++] + 1;
+
+ // Finally, we find the start address of our block of canonical Huffman symbols
+ uint32_t* ptr = (uint32_t*)(d->data + block * d->sizeofBlock);
+
+ // Read the first 64 bits in our block, this is a (truncated) sequence of
+ // unknown number of symbols of unknown length but we know the first one
+ // is at the beginning of this 64 bits sequence.
+ uint64_t buf64 = number<uint64_t, BigEndian>(ptr); ptr += 2;
+ int buf64Size = 64;
+ Sym sym;
+
+ while (true) {
+ int len = 0; // This is the symbol length - d->min_sym_len
+
+ // Now get the symbol length. For any symbol s64 of length l right-padded
+ // to 64 bits we know that d->base64[l-1] >= s64 >= d->base64[l] so we
+ // can find the symbol length iterating through base64[].
+ while (buf64 < d->base64[len])
+ ++len;
+
+ // All the symbols of a given length are consecutive integers (numerical
+ // sequence property), so we can compute the offset of our symbol of
+ // length len, stored at the beginning of buf64.
+ sym = (buf64 - d->base64[len]) >> (64 - len - d->minSymLen);
+
+ // Now add the value of the lowest symbol of length len to get our symbol
+ sym += number<Sym, LittleEndian>(&d->lowestSym[len]);
+
+ // If our offset is within the number of values represented by symbol sym
+ // we are done...
+ if (offset < d->symlen[sym] + 1)
+ break;
+
+ // ...otherwise update the offset and continue to iterate
+ offset -= d->symlen[sym] + 1;
+ len += d->minSymLen; // Get the real length
+ buf64 <<= len; // Consume the just processed symbol
+ buf64Size -= len;
+
+ if (buf64Size <= 32) { // Refill the buffer
+ buf64Size += 32;
+ buf64 |= (uint64_t)number<uint32_t, BigEndian>(ptr++) << (64 - buf64Size);
+ }
+ }
+
+ // Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols.
+ // We binary-search for our value recursively expanding into the left and
+ // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
+ // that will store the value we need.
+ while (d->symlen[sym]) {
+
+ Sym left = d->btree[sym].get<LR::Left>();
+
+ // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
+ // expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then
+ // we know that, for instance the ten-th value (offset = 10) will be on
+ // the left side because in Recursive Pairing child symbols are adjacent.
+ if (offset < d->symlen[left] + 1)
+ sym = left;
+ else {
+ offset -= d->symlen[left] + 1;
+ sym = d->btree[sym].get<LR::Right>();
+ }
+ }
+
+ return d->btree[sym].get<LR::Value>();
+}
+
+bool check_dtz_stm(WDLEntry*, int, File) { return true; }
+
+bool check_dtz_stm(DTZEntry* entry, int stm, File f) {
+
+ int flags = entry->hasPawns ? entry->pawnTable.file[f].precomp->flags
+ : entry->pieceTable.precomp->flags;
+
+ return (flags & TBFlag::STM) == stm
+ || ((entry->key == entry->key2) && !entry->hasPawns);
+}
+
+// DTZ scores are sorted by frequency of occurrence and then assigned the
+// values 0, 1, 2, ... in order of decreasing frequency. This is done for each
+// of the four WDLScore values. The mapping information necessary to reconstruct
+// the original values is stored in the TB file and read during map[] init.
+WDLScore map_score(WDLEntry*, File, int value, WDLScore) { return WDLScore(value - 2); }
+
+int map_score(DTZEntry* entry, File f, int value, WDLScore wdl) {
+
+ const int WDLMap[] = { 1, 3, 0, 2, 0 };
+
+ int flags = entry->hasPawns ? entry->pawnTable.file[f].precomp->flags
+ : entry->pieceTable.precomp->flags;
+
+ uint8_t* map = entry->hasPawns ? entry->pawnTable.map
+ : entry->pieceTable.map;
+
+ uint16_t* idx = entry->hasPawns ? entry->pawnTable.file[f].map_idx
+ : entry->pieceTable.map_idx;
+ if (flags & TBFlag::Mapped)
+ value = map[idx[WDLMap[wdl + 2]] + value];
+
+ // DTZ tables store distance to zero in number of moves or plies. We
+ // want to return plies, so we have convert to plies when needed.
+ if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies))
+ || (wdl == WDLLoss && !(flags & TBFlag::LossPlies))
+ || wdl == WDLCursedWin
+ || wdl == WDLBlessedLoss)
+ value *= 2;
+
+ return value + 1;
+}
+
+// Compute a unique index out of a position and use it to probe the TB file. To
+// encode k pieces of same type and color, first sort the pieces by square in
+// ascending order s1 <= s2 <= ... <= sk then compute the unique index as:
+//
+// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
+//
+template<typename Entry, typename T = typename Ret<Entry>::type>
+T do_probe_table(const Position& pos, Entry* entry, WDLScore wdl, ProbeState* result) {
+
+ const bool IsWDL = std::is_same<Entry, WDLEntry>::value;
+
+ Square squares[TBPIECES];
+ Piece pieces[TBPIECES];
+ uint64_t idx;
+ int next = 0, size = 0, leadPawnsCnt = 0;
+ PairsData* d;
+ Bitboard b, leadPawns = 0;
+ File tbFile = FILE_A;
+
+ // A given TB entry like KRK has associated two material keys: KRvk and Kvkr.
+ // If both sides have the same pieces keys are equal. In this case TB tables
+ // only store the 'white to move' case, so if the position to lookup has black
+ // to move, we need to switch the color and flip the squares before to lookup.
+ bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move());
+
+ // TB files are calculated for white as stronger side. For instance we have
+ // KRvK, not KvKR. A position where stronger side is white will have its
+ // material key == entry->key, otherwise we have to switch the color and
+ // flip the squares before to lookup.
+ bool blackStronger = (pos.material_key() != entry->key);
+
+ int flipColor = (symmetricBlackToMove || blackStronger) * 8;
+ int flipSquares = (symmetricBlackToMove || blackStronger) * 070;
+ int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
+
+ // For pawns, TB files store 4 separate tables according if leading pawn is on
+ // file a, b, c or d after reordering. The leading pawn is the one with maximum
+ // MapPawns[] value, that is the one most toward the edges and with lowest rank.
+ if (entry->hasPawns) {
+
+ // In all the 4 tables, pawns are at the beginning of the piece sequence and
+ // their color is the reference one. So we just pick the first one.
+ Piece pc = Piece(item(entry->pawnTable, 0, 0).precomp->pieces[0] ^ flipColor);
+
+ assert(type_of(pc) == PAWN);
+
+ leadPawns = b = pos.pieces(color_of(pc), PAWN);
+ do
+ squares[size++] = pop_lsb(&b) ^ flipSquares;
+ while (b);
+
+ leadPawnsCnt = size;
+
+ std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
+
+ tbFile = file_of(squares[0]);
+ if (tbFile > FILE_D)
+ tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
+
+ d = item(entry->pawnTable , stm, tbFile).precomp;
+ } else
+ d = item(entry->pieceTable, stm, tbFile).precomp;
+
+ // DTZ tables are one-sided, i.e. they store positions only for white to
+ // move or only for black to move, so check for side to move to be stm,
+ // early exit otherwise.
+ if (!IsWDL && !check_dtz_stm(entry, stm, tbFile))
+ return *result = CHANGE_STM, T();
+
+ // Now we are ready to get all the position pieces (but the lead pawns) and
+ // directly map them to the correct color and square.
+ b = pos.pieces() ^ leadPawns;
+ do {
+ Square s = pop_lsb(&b);
+ squares[size] = s ^ flipSquares;
+ pieces[size++] = Piece(pos.piece_on(s) ^ flipColor);
+ } while (b);
+
+ assert(size >= 2);
+
+ // Then we reorder the pieces to have the same sequence as the one stored
+ // in precomp->pieces[i]: the sequence that ensures the best compression.
+ for (int i = leadPawnsCnt; i < size; ++i)
+ for (int j = i; j < size; ++j)
+ if (d->pieces[i] == pieces[j])
+ {
+ std::swap(pieces[i], pieces[j]);
+ std::swap(squares[i], squares[j]);
+ break;
+ }
+
+ // Now we map again the squares so that the square of the lead piece is in
+ // the triangle A1-D1-D4.
+ if (file_of(squares[0]) > FILE_D)
+ for (int i = 0; i < size; ++i)
+ squares[i] ^= 7; // Horizontal flip: SQ_H1 -> SQ_A1
+
+ // Encode leading pawns starting with the one with minimum MapPawns[] and
+ // proceeding in ascending order.
+ if (entry->hasPawns) {
+ idx = LeadPawnIdx[leadPawnsCnt][squares[0]];
+
+ std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp);
+
+ for (int i = 1; i < leadPawnsCnt; ++i)
+ idx += Binomial[i][MapPawns[squares[i]]];
+
+ goto encode_remaining; // With pawns we have finished special treatments
+ }
+
+ // In positions withouth pawns, we further flip the squares to ensure leading
+ // piece is below RANK_5.
+ if (rank_of(squares[0]) > RANK_4)
+ for (int i = 0; i < size; ++i)
+ squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1
+
+ // Look for the first piece of the leading group not on the A1-D4 diagonal
+ // and ensure it is mapped below the diagonal.
+ for (int i = 0; i < d->groupLen[0]; ++i) {
+ if (!off_A1H8(squares[i]))
+ continue;
+
+ if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C3
+ for (int j = i; j < size; ++j)
+ squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
+ break;
+ }
+
+ // Encode the leading group.
+ //
+ // Suppose we have KRvK. Let's say the pieces are on square numbers wK, wR
+ // and bK (each 0...63). The simplest way to map this position to an index
+ // is like this:
+ //
+ // index = wK * 64 * 64 + wR * 64 + bK;
+ //
+ // But this way the TB is going to have 64*64*64 = 262144 positions, with
+ // lots of positions being equivalent (because they are mirrors of each
+ // other) and lots of positions being invalid (two pieces on one square,
+ // adjacent kings, etc.).
+ // Usually the first step is to take the wK and bK together. There are just
+ // 462 ways legal and not-mirrored ways to place the wK and bK on the board.
+ // Once we have placed the wK and bK, there are 62 squares left for the wR
+ // Mapping its square from 0..63 to available squares 0..61 can be done like:
+ //
+ // wR -= (wR > wK) + (wR > bK);
+ //
+ // In words: if wR "comes later" than wK, we deduct 1, and the same if wR
+ // "comes later" than bK. In case of two same pieces like KRRvK we want to
+ // place the two Rs "together". If we have 62 squares left, we can place two
+ // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be
+ // swapped and still get the same position.)
+ //
+ // In case we have at least 3 unique pieces (inlcuded kings) we encode them
+ // together.
+ if (entry->hasUniquePieces) {
+
+ int adjust1 = squares[1] > squares[0];
+ int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]);
+
+ // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3
+ // triangle to 0...5. There are 63 squares for second piece and and 62
+ // (mapped to 0...61) for the third.
+ if (off_A1H8(squares[0]))
+ idx = ( MapA1D1D4[squares[0]] * 63
+ + (squares[1] - adjust1)) * 62
+ + squares[2] - adjust2;
+
+ // First piece is on a1-h8 diagonal, second below: map this occurence to
+ // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal
+ // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27.
+ else if (off_A1H8(squares[1]))
+ idx = ( 6 * 63 + rank_of(squares[0]) * 28
+ + MapB1H1H7[squares[1]]) * 62
+ + squares[2] - adjust2;
+
+ // First two pieces are on a1-h8 diagonal, third below
+ else if (off_A1H8(squares[2]))
+ idx = 6 * 63 * 62 + 4 * 28 * 62
+ + rank_of(squares[0]) * 7 * 28
+ + (rank_of(squares[1]) - adjust1) * 28
+ + MapB1H1H7[squares[2]];
+
+ // All 3 pieces on the diagonal a1-h8
+ else
+ idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28
+ + rank_of(squares[0]) * 7 * 6
+ + (rank_of(squares[1]) - adjust1) * 6
+ + (rank_of(squares[2]) - adjust2);
+ } else
+ // We don't have at least 3 unique pieces, like in KRRvKBB, just map
+ // the kings.
+ idx = MapKK[MapA1D1D4[squares[0]]][squares[1]];
+
+encode_remaining:
+ idx *= d->groupIdx[0];
+ Square* groupSq = squares + d->groupLen[0];
+
+ // Encode remainig pawns then pieces according to square, in ascending order
+ bool remainingPawns = entry->hasPawns && entry->pawnTable.pawnCount[1];
+
+ while (d->groupLen[++next])
+ {
+ std::sort(groupSq, groupSq + d->groupLen[next]);
+ uint64_t n = 0;
+
+ // Map down a square if "comes later" than a square in the previous
+ // groups (similar to what done earlier for leading group pieces).
+ for (int i = 0; i < d->groupLen[next]; ++i)
+ {
+ auto f = [&](Square s) { return groupSq[i] > s; };
+ auto adjust = std::count_if(squares, groupSq, f);
+ n += Binomial[i + 1][groupSq[i] - adjust - 8 * remainingPawns];
+ }
+
+ remainingPawns = false;
+ idx += n * d->groupIdx[next];
+ groupSq += d->groupLen[next];
+ }
+
+ // Now that we have the index, decompress the pair and get the score
+ return map_score(entry, tbFile, decompress_pairs(d, idx), wdl);
+}
+
+// Group together pieces that will be encoded together. The general rule is that
+// a group contains pieces of same type and color. The exception is the leading
+// group that, in case of positions withouth pawns, can be formed by 3 different
+// pieces (default) or by the king pair when there is not a unique piece apart
+// from the kings. When there are pawns, pawns are always first in pieces[].
+//
+// As example KRKN -> KRK + N, KNNK -> KK + NN, KPPKP -> P + PP + K + K
+//
+// The actual grouping depends on the TB generator and can be inferred from the
+// sequence of pieces in piece[] array.
+template<typename T>
+void set_groups(T& e, PairsData* d, int order[], File f) {
+
+ int n = 0, firstLen = e.hasPawns ? 0 : e.hasUniquePieces ? 3 : 2;
+ d->groupLen[n] = 1;
+
+ // Number of pieces per group is stored in groupLen[], for instance in KRKN
+ // the encoder will default on '111', so groupLen[] will be (3, 1).
+ for (int i = 1; i < e.pieceCount; ++i)
+ if (--firstLen > 0 || d->pieces[i] == d->pieces[i - 1])
+ d->groupLen[n]++;
+ else
+ d->groupLen[++n] = 1;
+
+ d->groupLen[++n] = 0; // Zero-terminated
+
+ // The sequence in pieces[] defines the groups, but not the order in which
+ // they are encoded. If the pieces in a group g can be combined on the board
+ // in N(g) different ways, then the position encoding will be of the form:
+ //
+ // g1 * N(g2) * N(g3) + g2 * N(g3) + g3
+ //
+ // This ensures unique encoding for the whole position. The order of the
+ // groups is a per-table parameter and could not follow the canonical leading
+ // pawns/pieces -> remainig pawns -> remaining pieces. In particular the
+ // first group is at order[0] position and the remaining pawns, when present,
+ // are at order[1] position.
+ bool pp = e.hasPawns && e.pawnTable.pawnCount[1]; // Pawns on both sides
+ int next = pp ? 2 : 1;
+ int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0);
+ uint64_t idx = 1;
+
+ for (int k = 0; next < n || k == order[0] || k == order[1]; ++k)
+ if (k == order[0]) // Leading pawns or pieces
+ {
+ d->groupIdx[0] = idx;
+ idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f]
+ : e.hasUniquePieces ? 31332 : 462;
+ }
+ else if (k == order[1]) // Remaining pawns
+ {
+ d->groupIdx[1] = idx;
+ idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]];
+ }
+ else // Remainig pieces
+ {
+ d->groupIdx[next] = idx;
+ idx *= Binomial[d->groupLen[next]][freeSquares];
+ freeSquares -= d->groupLen[next++];
+ }
+
+ d->groupIdx[n] = idx;
+}
+
+// In Recursive Pairing each symbol represents a pair of childern symbols. So
+// read d->btree[] symbols data and expand each one in his left and right child
+// symbol until reaching the leafs that represent the symbol value.
+uint8_t set_symlen(PairsData* d, Sym s, std::vector<bool>& visited) {
+
+ visited[s] = true; // We can set it now because tree is acyclic
+ Sym sr = d->btree[s].get<LR::Right>();
+
+ if (sr == 0xFFF)
+ return 0;
+
+ Sym sl = d->btree[s].get<LR::Left>();
+
+ if (!visited[sl])
+ d->symlen[sl] = set_symlen(d, sl, visited);
+
+ if (!visited[sr])
+ d->symlen[sr] = set_symlen(d, sr, visited);
+
+ return d->symlen[sl] + d->symlen[sr] + 1;
+}
+
+uint8_t* set_sizes(PairsData* d, uint8_t* data) {
+
+ d->flags = *data++;
+
+ if (d->flags & TBFlag::SingleValue) {
+ d->blocksNum = d->blockLengthSize = 0;
+ d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init
+ d->minSymLen = *data++; // Here we store the single value
+ return data;
+ }
+
+ // groupLen[] is a zero-terminated list of group lengths, the last groupIdx[]
+ // element stores the biggest index that is the tb size.
+ uint64_t tbSize = d->groupIdx[std::find(d->groupLen, d->groupLen + 7, 0) - d->groupLen];
+
+ d->sizeofBlock = 1ULL << *data++;
+ d->span = 1ULL << *data++;
+ d->sparseIndexSize = (tbSize + d->span - 1) / d->span; // Round up
+ int padding = number<uint8_t, LittleEndian>(data++);
+ d->blocksNum = number<uint32_t, LittleEndian>(data); data += sizeof(uint32_t);
+ d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[]
+ // does not point out of range.
+ d->maxSymLen = *data++;
+ d->minSymLen = *data++;
+ d->lowestSym = (Sym*)data;
+ d->base64.resize(d->maxSymLen - d->minSymLen + 1);
+
+ // The canonical code is ordered such that longer symbols (in terms of
+ // the number of bits of their Huffman code) have lower numeric value,
+ // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
+ // Starting from this we compute a base64[] table indexed by symbol length
+ // and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
+ // See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf
+ for (int i = d->base64.size() - 2; i >= 0; --i) {
+ d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i])
+ - number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2;
+
+ assert(d->base64[i] * 2 >= d->base64[i+1]);
+ }
+
+ // Now left-shift by an amount so that d->base64[i] gets shifted 1 bit more
+ // than d->base64[i+1] and given the above assert condition, we ensure that
+ // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i
+ // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i].
+ for (size_t i = 0; i < d->base64.size(); ++i)
+ d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits
+
+ data += d->base64.size() * sizeof(Sym);
+ d->symlen.resize(number<uint16_t, LittleEndian>(data)); data += sizeof(uint16_t);
+ d->btree = (LR*)data;
+
+ // The comrpession scheme used is "Recursive Pairing", that replaces the most
+ // frequent adjacent pair of symbols in the source message by a new symbol,
+ // reevaluating the frequencies of all of the symbol pairs with respect to
+ // the extended alphabet, and then repeating the process.
+ // See http://www.larsson.dogma.net/dcc99.pdf
+ std::vector<bool> visited(d->symlen.size());
+
+ for (Sym sym = 0; sym < d->symlen.size(); ++sym)
+ if (!visited[sym])
+ d->symlen[sym] = set_symlen(d, sym, visited);
+
+ return data + d->symlen.size() * sizeof(LR) + (d->symlen.size() & 1);
+}
+
+template<typename T>
+uint8_t* set_dtz_map(WDLEntry&, T&, uint8_t*, File) { return nullptr; }
+
+template<typename T>
+uint8_t* set_dtz_map(DTZEntry&, T& p, uint8_t* data, File maxFile) {
+
+ p.map = data;
+
+ for (File f = FILE_A; f <= maxFile; ++f) {
+ if (item(p, 0, f).precomp->flags & TBFlag::Mapped)
+ for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x
+ item(p, 0, f).map_idx[i] = (uint16_t)(data - p.map + 1);
+ data += *data + 1;
+ }
+ }
+
+ return data += (uintptr_t)data & 1; // Word alignment
+}
+
+template<typename Entry, typename T>
+void do_init(Entry& e, T& p, uint8_t* data) {
+
+ const bool IsWDL = std::is_same<Entry, WDLEntry>::value;
+
+ PairsData* d;
+
+ enum { Split = 1, HasPawns = 2 };
+
+ assert(e.hasPawns == !!(*data & HasPawns));
+ assert((e.key != e.key2) == !!(*data & Split));
+
+ data++; // First byte stores flags
+
+ const int Sides = IsWDL && (e.key != e.key2) ? 2 : 1;
+ const File MaxFile = e.hasPawns ? FILE_D : FILE_A;
+
+ bool pp = e.hasPawns && e.pawnTable.pawnCount[1]; // Pawns on both sides
+
+ assert(!pp || e.pawnTable.pawnCount[0]);
+
+ for (File f = FILE_A; f <= MaxFile; ++f) {
+
+ for (int i = 0; i < Sides; i++)
+ item(p, i, f).precomp = new PairsData();
+
+ int order[][2] = { { *data & 0xF, pp ? *(data + 1) & 0xF : 0xF },
+ { *data >> 4, pp ? *(data + 1) >> 4 : 0xF } };
+ data += 1 + pp;
+
+ for (int k = 0; k < e.pieceCount; ++k, ++data)
+ for (int i = 0; i < Sides; i++)
+ item(p, i, f).precomp->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF);
+
+ for (int i = 0; i < Sides; ++i)
+ set_groups(e, item(p, i, f).precomp, order[i], f);
+ }
+
+ data += (uintptr_t)data & 1; // Word alignment
+
+ for (File f = FILE_A; f <= MaxFile; ++f)
+ for (int i = 0; i < Sides; i++)
+ data = set_sizes(item(p, i, f).precomp, data);
+
+ if (!IsWDL)
+ data = set_dtz_map(e, p, data, MaxFile);
+
+ for (File f = FILE_A; f <= MaxFile; ++f)
+ for (int i = 0; i < Sides; i++) {
+ (d = item(p, i, f).precomp)->sparseIndex = (SparseEntry*)data;
+ data += d->sparseIndexSize * sizeof(SparseEntry);
+ }
+
+ for (File f = FILE_A; f <= MaxFile; ++f)
+ for (int i = 0; i < Sides; i++) {
+ (d = item(p, i, f).precomp)->blockLength = (uint16_t*)data;
+ data += d->blockLengthSize * sizeof(uint16_t);
+ }
+
+ for (File f = FILE_A; f <= MaxFile; ++f)
+ for (int i = 0; i < Sides; i++) {
+ data = (uint8_t*)(((uintptr_t)data + 0x3F) & ~0x3F); // 64 byte alignment
+ (d = item(p, i, f).precomp)->data = data;
+ data += d->blocksNum * d->sizeofBlock;
+ }
+}
+
+template<typename Entry>
+void* init(Entry& e, const Position& pos) {
+
+ const bool IsWDL = std::is_same<Entry, WDLEntry>::value;
+
+ static Mutex mutex;
+
+ // Avoid a thread reads 'ready' == true while another is still in do_init(),
+ // this could happen due to compiler reordering.
+ if (e.ready.load(std::memory_order_acquire))
+ return e.baseAddress;
+
+ std::unique_lock<Mutex> lk(mutex);
+
+ if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
+ return e.baseAddress;
+
+ // Pieces strings in decreasing order for each color, like ("KPP","KR")
+ std::string fname, w, b;
+ for (PieceType pt = KING; pt >= PAWN; --pt) {
+ w += std::string(popcount(pos.pieces(WHITE, pt)), PieceToChar[pt]);
+ b += std::string(popcount(pos.pieces(BLACK, pt)), PieceToChar[pt]);
+ }
+
+ const uint8_t TB_MAGIC[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
+ { 0x71, 0xE8, 0x23, 0x5D } };
+
+ fname = (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w)
+ + (IsWDL ? ".rtbw" : ".rtbz");
+
+ uint8_t* data = TBFile(fname).map(&e.baseAddress, &e.mapping, TB_MAGIC[IsWDL]);
+ if (data)
+ e.hasPawns ? do_init(e, e.pawnTable, data) : do_init(e, e.pieceTable, data);
+
+ e.ready.store(true, std::memory_order_release);
+ return e.baseAddress;
+}
+
+template<typename E, typename T = typename Ret<E>::type>
+T probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) {
+
+ if (!(pos.pieces() ^ pos.pieces(KING)))
+ return T(WDLDraw); // KvK
+
+ E* entry = EntryTable.get<E>(pos.material_key());
+
+ if (!entry || !init(*entry, pos))
+ return *result = FAIL, T();
+
+ return do_probe_table(pos, entry, wdl, result);
+}
+
+// For a position where the side to move has a winning capture it is not necessary
+// to store a winning value so the generator treats such positions as "don't cares"
+// and tries to assign to it a value that improves the compression ratio. Similarly,
+// if the side to move has a drawing capture, then the position is at least drawn.
+// If the position is won, then the TB needs to store a win value. But if the
+// position is drawn, the TB may store a loss value if that is better for compression.
+// All of this means that during probing, the engine must look at captures and probe
+// their results and must probe the position itself. The "best" result of these
+// probes is the correct result for the position.
+// DTZ table don't store values when a following move is a zeroing winning move
+// (winning capture or winning pawn move). Also DTZ store wrong values for positions
+// where the best move is an ep-move (even if losing). So in all these cases set
+// the state to ZEROING_BEST_MOVE.
+template<bool CheckZeroingMoves = false>
+WDLScore search(Position& pos, ProbeState* result) {
+
+ WDLScore value, bestValue = WDLLoss;
+ StateInfo st;
+
+ auto moveList = MoveList<LEGAL>(pos);
+ size_t totalCount = moveList.size(), moveCount = 0;
+
+ for (const Move& move : moveList)
+ {
+ if ( !pos.capture(move)
+ && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN))
+ continue;
+
+ moveCount++;
+
+ pos.do_move(move, st);
+ value = -search(pos, result);
+ pos.undo_move(move);
+
+ if (*result == FAIL)
+ return WDLDraw;
+
+ if (value > bestValue)
+ {
+ bestValue = value;
+
+ if (value >= WDLWin)
+ {
+ *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move
+ return value;
+ }
+ }
+ }
+
+ // In case we have already searched all the legal moves we don't have to probe
+ // the TB because the stored score could be wrong. For instance TB tables
+ // do not contain information on position with ep rights, so in this case
+ // the result of probe_wdl_table is wrong. Also in case of only capture
+ // moves, for instance here 4K3/4q3/6p1/2k5/6p1/8/8/8 w - - 0 7, we have to
+ // return with ZEROING_BEST_MOVE set.
+ bool noMoreMoves = (moveCount && moveCount == totalCount);
+
+ if (noMoreMoves)
+ value = bestValue;
+ else
+ {
+ value = probe_table<WDLEntry>(pos, result);
+
+ if (*result == FAIL)
+ return WDLDraw;
+ }
+
+ // DTZ stores a "don't care" value if bestValue is a win
+ if (bestValue >= value)
+ return *result = ( bestValue > WDLDraw
+ || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue;
+
+ return *result = OK, value;
+}
+
+} // namespace
+
+void Tablebases::init(const std::string& paths) {
+
+ EntryTable.clear();
+ MaxCardinality = 0;
+ TBFile::Paths = paths;
+
+ if (paths.empty() || paths == "<empty>")
+ return;
+
+ // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27
+ int code = 0;
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ if (off_A1H8(s) < 0)
+ MapB1H1H7[s] = code++;
+
+ // MapA1D1D4[] encodes a square in the a1-d1-d4 triangle to 0..9
+ std::vector<Square> diagonal;
+ code = 0;
+ for (Square s = SQ_A1; s <= SQ_D4; ++s)
+ if (off_A1H8(s) < 0 && file_of(s) <= FILE_D)
+ MapA1D1D4[s] = code++;
+
+ else if (!off_A1H8(s) && file_of(s) <= FILE_D)
+ diagonal.push_back(s);
+
+ // Diagonal squares are encoded as last ones
+ for (auto s : diagonal)
+ MapA1D1D4[s] = code++;
+
+ // MapKK[] encodes all the 461 possible legal positions of two kings where
+ // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4
+ // diagonal, the other one shall not to be above the a1-h8 diagonal.
+ std::vector<std::pair<int, Square>> bothOnDiagonal;
+ code = 0;
+ for (int idx = 0; idx < 10; idx++)
+ for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1)
+ if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0
+ {
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ if ((PseudoAttacks[KING][s1] | s1) & s2)
+ continue; // Illegal position
+
+ else if (!off_A1H8(s1) && off_A1H8(s2) > 0)
+ continue; // First on diagonal, second above
+
+ else if (!off_A1H8(s1) && !off_A1H8(s2))
+ bothOnDiagonal.push_back(std::make_pair(idx, s2));
+
+ else
+ MapKK[idx][s2] = code++;
+ }
+
+ // Legal positions with both kings on diagonal are encoded as last ones
+ for (auto p : bothOnDiagonal)
+ MapKK[p.first][p.second] = code++;
+
+ // Binomial[] stores the Binomial Coefficents using Pascal rule. There
+ // are Binomial[k][n] ways to choose k elements from a set of n elements.
+ Binomial[0][0] = 1;
+
+ for (int n = 1; n < 64; n++) // Squares
+ for (int k = 0; k < 6 && k <= n; ++k) // Pieces
+ Binomial[k][n] = (k > 0 ? Binomial[k - 1][n - 1] : 0)
+ + (k < n ? Binomial[k ][n - 1] : 0);
+
+ // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible
+ // available squares when the leading one is in 's'. Moreover the pawn with
+ // highest MapPawns[] is the leading pawn, the one nearest the edge and,
+ // among pawns with same file, the one with lowest rank.
+ int availableSquares = 47; // Available squares when lead pawn is in a2
+
+ // Init the tables for the encoding of leading pawns group: with 6-men TB we
+ // can have up to 4 leading pawns (KPPPPK).
+ for (int leadPawnsCnt = 1; leadPawnsCnt <= 4; ++leadPawnsCnt)
+ for (File f = FILE_A; f <= FILE_D; ++f)
+ {
+ // Restart the index at every file because TB table is splitted
+ // by file, so we can reuse the same index for different files.
+ int idx = 0;
+
+ // Sum all possible combinations for a given file, starting with
+ // the leading pawn on rank 2 and increasing the rank.
+ for (Rank r = RANK_2; r <= RANK_7; ++r)
+ {
+ Square sq = make_square(f, r);
+
+ // Compute MapPawns[] at first pass.
+ // If sq is the leading pawn square, any other pawn cannot be
+ // below or more toward the edge of sq. There are 47 available
+ // squares when sq = a2 and reduced by 2 for any rank increase
+ // due to mirroring: sq == a3 -> no a2, h2, so MapPawns[a3] = 45
+ if (leadPawnsCnt == 1)
+ {
+ MapPawns[sq] = availableSquares--;
+ MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip
+ }
+ LeadPawnIdx[leadPawnsCnt][sq] = idx;
+ idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]];
+ }
+ // After a file is traversed, store the cumulated per-file index
+ LeadPawnsSize[leadPawnsCnt][f] = idx;
+ }
+
+ for (PieceType p1 = PAWN; p1 < KING; ++p1) {
+ EntryTable.insert({KING, p1, KING});
+
+ for (PieceType p2 = PAWN; p2 <= p1; ++p2) {
+ EntryTable.insert({KING, p1, p2, KING});
+ EntryTable.insert({KING, p1, KING, p2});
+
+ for (PieceType p3 = PAWN; p3 < KING; ++p3)
+ EntryTable.insert({KING, p1, p2, KING, p3});
+
+ for (PieceType p3 = PAWN; p3 <= p2; ++p3) {
+ EntryTable.insert({KING, p1, p2, p3, KING});
+
+ for (PieceType p4 = PAWN; p4 <= p3; ++p4)
+ EntryTable.insert({KING, p1, p2, p3, p4, KING});
+
+ for (PieceType p4 = PAWN; p4 < KING; ++p4)
+ EntryTable.insert({KING, p1, p2, p3, KING, p4});
+ }
+
+ for (PieceType p3 = PAWN; p3 <= p1; ++p3)
+ for (PieceType p4 = PAWN; p4 <= (p1 == p3 ? p2 : p3); ++p4)
+ EntryTable.insert({KING, p1, p2, KING, p3, p4});
+ }
+ }
+
+ sync_cout << "info string Found " << EntryTable.size() << " tablebases" << sync_endl;
+}
+
+// Probe the WDL table for a particular position.
+// If *result != FAIL, the probe was successful.
+// The return value is from the point of view of the side to move:
+// -2 : loss
+// -1 : loss, but draw under 50-move rule
+// 0 : draw
+// 1 : win, but draw under 50-move rule
+// 2 : win
+WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
+
+ *result = OK;
+ return search(pos, result);
+}
+
+// Probe the DTZ table for a particular position.
+// If *result != FAIL, the probe was successful.
+// The return value is from the point of view of the side to move:
+// n < -100 : loss, but draw under 50-move rule
+// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
+// 0 : draw
+// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
+// 100 < n : win, but draw under 50-move rule
+//
+// The return value n can be off by 1: a return value -n can mean a loss
+// in n+1 ply and a return value +n can mean a win in n+1 ply. This
+// cannot happen for tables with positions exactly on the "edge" of
+// the 50-move rule.
+//
+// This implies that if dtz > 0 is returned, the position is certainly
+// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
+// picks moves that preserve dtz + 50-move-counter <= 99.
+//
+// If n = 100 immediately after a capture or pawn move, then the position
+// is also certainly a win, and during the whole phase until the next
+// capture or pawn move, the inequality to be preserved is
+// dtz + 50-movecounter <= 100.
+//
+// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
+// then do not accept moves leading to dtz + 50-move-counter == 100.
+int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
+
+ *result = OK;
+ WDLScore wdl = search<true>(pos, result);
+
+ if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws
+ return 0;
+
+ // DTZ stores a 'don't care' value in this case, or even a plain wrong
+ // one as in case the best move is a losing ep, so it cannot be probed.
+ if (*result == ZEROING_BEST_MOVE)
+ return dtz_before_zeroing(wdl);
+
+ int dtz = probe_table<DTZEntry>(pos, result, wdl);
+
+ if (*result == FAIL)
+ return 0;
+
+ if (*result != CHANGE_STM)
+ return (dtz + 100 * (wdl == WDLBlessedLoss || wdl == WDLCursedWin)) * sign_of(wdl);
+
+ // DTZ stores results for the other side, so we need to do a 1-ply search and
+ // find the winning move that minimizes DTZ.
+ StateInfo st;
+ int minDTZ = 0xFFFF;
+
+ for (const Move& move : MoveList<LEGAL>(pos))
+ {
+ bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN;
+
+ pos.do_move(move, st);
+
+ // For zeroing moves we want the dtz of the move _before_ doing it,
+ // otherwise we will get the dtz of the next move sequence. Search the
+ // position after the move to get the score sign (because even in a
+ // winning position we could make a losing capture or going for a draw).
+ dtz = zeroing ? -dtz_before_zeroing(search(pos, result))
+ : -probe_dtz(pos, result);
+
+ pos.undo_move(move);
+
+ if (*result == FAIL)
+ return 0;
+
+ // Convert result from 1-ply search. Zeroing moves are already accounted
+ // by dtz_before_zeroing() that returns the DTZ of the previous move.
+ if (!zeroing)
+ dtz += sign_of(dtz);
+
+ // Skip the draws and if we are winning only pick positive dtz
+ if (dtz < minDTZ && sign_of(dtz) == sign_of(wdl))
+ minDTZ = dtz;
+ }
+
+ // Special handle a mate position, when there are no legal moves, in this
+ // case return value is somewhat arbitrary, so stick to the original TB code
+ // that returns -1 in this case.
+ return minDTZ == 0xFFFF ? -1 : minDTZ;
+}
+
+// Check whether there has been at least one repetition of positions
+// since the last capture or pawn move.
+static int has_repeated(StateInfo *st)
+{
+ while (1) {
+ int i = 4, e = std::min(st->rule50, st->pliesFromNull);
+
+ if (e < i)
+ return 0;
+
+ StateInfo *stp = st->previous->previous;
+
+ do {
+ stp = stp->previous->previous;
+
+ if (stp->key == st->key)
+ return 1;
+
+ i += 2;
+ } while (i <= e);
+
+ st = st->previous;
+ }
+}
+
+// Use the DTZ tables to filter out moves that don't preserve the win or draw.
+// If the position is lost, but DTZ is fairly high, only keep moves that
+// maximise DTZ.
+//
+// A return value false indicates that not all probes were successful and that
+// no moves were filtered out.
+bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score)
+{
+ assert(rootMoves.size());
+
+ ProbeState result;
+ int dtz = probe_dtz(pos, &result);
+
+ if (result == FAIL)
+ return false;
+
+ StateInfo st;
+
+ // Probe each move
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ Move move = rootMoves[i].pv[0];
+ pos.do_move(move, st);
+ int v = 0;
+
+ if (pos.checkers() && dtz > 0) {
+ ExtMove s[MAX_MOVES];
+
+ if (generate<LEGAL>(pos, s) == s)
+ v = 1;
+ }
+
+ if (!v) {
+ if (st.rule50 != 0) {
+ v = -probe_dtz(pos, &result);
+
+ if (v > 0)
+ ++v;
+ else if (v < 0)
+ --v;
+ } else {
+ v = -probe_wdl(pos, &result);
+ v = dtz_before_zeroing(WDLScore(v));
+ }
+ }
+
+ pos.undo_move(move);
+
+ if (result == FAIL)
+ return false;
+
+ rootMoves[i].score = (Value)v;
+ }
+
+ // Obtain 50-move counter for the root position.
+ // In Stockfish there seems to be no clean way, so we do it like this:
+ int cnt50 = st.previous ? st.previous->rule50 : 0;
+
+ // Use 50-move counter to determine whether the root position is
+ // won, lost or drawn.
+ WDLScore wdl = WDLDraw;
+
+ if (dtz > 0)
+ wdl = (dtz + cnt50 <= 100) ? WDLWin : WDLCursedWin;
+ else if (dtz < 0)
+ wdl = (-dtz + cnt50 <= 100) ? WDLLoss : WDLBlessedLoss;
+
+ // Determine the score to report to the user.
+ score = WDL_to_value[wdl + 2];
+
+ // If the position is winning or losing, but too few moves left, adjust the
+ // score to show how close it is to winning or losing.
+ // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
+ if (wdl == WDLCursedWin && dtz <= 100)
+ score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
+ else if (wdl == WDLBlessedLoss && dtz >= -100)
+ score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
+
+ // Now be a bit smart about filtering out moves.
+ size_t j = 0;
+
+ if (dtz > 0) { // winning (or 50-move rule draw)
+ int best = 0xffff;
+
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ int v = rootMoves[i].score;
+
+ if (v > 0 && v < best)
+ best = v;
+ }
+
+ int max = best;
+
+ // If the current phase has not seen repetitions, then try all moves
+ // that stay safely within the 50-move budget, if there are any.
+ if (!has_repeated(st.previous) && best + cnt50 <= 99)
+ max = 99 - cnt50;
+
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ int v = rootMoves[i].score;
+
+ if (v > 0 && v <= max)
+ rootMoves[j++] = rootMoves[i];
+ }
+ } else if (dtz < 0) { // losing (or 50-move rule draw)
+ int best = 0;
+
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ int v = rootMoves[i].score;
+
+ if (v < best)
+ best = v;
+ }
+
+ // Try all moves, unless we approach or have a 50-move rule draw.
+ if (-best * 2 + cnt50 < 100)
+ return true;
+
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ if (rootMoves[i].score == best)
+ rootMoves[j++] = rootMoves[i];
+ }
+ } else { // drawing
+ // Try all moves that preserve the draw.
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ if (rootMoves[i].score == 0)
+ rootMoves[j++] = rootMoves[i];
+ }
+ }
+
+ rootMoves.resize(j, Search::RootMove(MOVE_NONE));
+
+ return true;
+}
+
+// Use the WDL tables to filter out moves that don't preserve the win or draw.
+// This is a fallback for the case that some or all DTZ tables are missing.
+//
+// A return value false indicates that not all probes were successful and that
+// no moves were filtered out.
+bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score)
+{
+ ProbeState result;
+
+ WDLScore wdl = Tablebases::probe_wdl(pos, &result);
+
+ if (result == FAIL)
+ return false;
+
+ score = WDL_to_value[wdl + 2];
+
+ StateInfo st;
+
+ int best = WDLLoss;
+
+ // Probe each move
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ Move move = rootMoves[i].pv[0];
+ pos.do_move(move, st);
+ WDLScore v = -Tablebases::probe_wdl(pos, &result);
+ pos.undo_move(move);
+
+ if (result == FAIL)
+ return false;
+
+ rootMoves[i].score = (Value)v;
+
+ if (v > best)
+ best = v;
+ }
+
+ size_t j = 0;
+
+ for (size_t i = 0; i < rootMoves.size(); ++i) {
+ if (rootMoves[i].score == best)
+ rootMoves[j++] = rootMoves[i];
+ }
+
+ rootMoves.resize(j, Search::RootMove(MOVE_NONE));
+
+ return true;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (c) 2013 Ronald de Man
+ Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TBPROBE_H
+#define TBPROBE_H
+
+#include <ostream>
+
+#include "../search.h"
+
+namespace Tablebases {
+
+enum WDLScore {
+ WDLLoss = -2, // Loss
+ WDLBlessedLoss = -1, // Loss, but draw under 50-move rule
+ WDLDraw = 0, // Draw
+ WDLCursedWin = 1, // Win, but draw under 50-move rule
+ WDLWin = 2, // Win
+
+ WDLScoreNone = -1000
+};
+
+// Possible states after a probing operation
+enum ProbeState {
+ FAIL = 0, // Probe failed (missing file table)
+ OK = 1, // Probe succesful
+ CHANGE_STM = -1, // DTZ should check the other side
+ ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move)
+};
+
+extern int MaxCardinality;
+
+void init(const std::string& paths);
+WDLScore probe_wdl(Position& pos, ProbeState* result);
+int probe_dtz(Position& pos, ProbeState* result);
+bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score);
+bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);
+void filter_root_moves(Position& pos, Search::RootMoves& rootMoves);
+
+inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
+
+ os << (v == WDLLoss ? "Loss" :
+ v == WDLBlessedLoss ? "Blessed loss" :
+ v == WDLDraw ? "Draw" :
+ v == WDLCursedWin ? "Cursed win" :
+ v == WDLWin ? "Win" : "None");
+
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
+
+ os << (v == FAIL ? "Failed" :
+ v == OK ? "Success" :
+ v == CHANGE_STM ? "Probed opponent side" :
+ v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None");
+
+ return os;
+}
+
+}
+
+#endif
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm> // For std::count
+#include <cassert>
+
+#include "movegen.h"
+#include "search.h"
+#include "thread.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+ThreadPool Threads; // Global object
+
+
+/// Thread constructor launches the thread and waits until it goes to sleep
+/// in idle_loop(). Note that 'searching' and 'exit' should be alredy set.
+
+Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
+
+ wait_for_search_finished();
+}
+
+
+/// Thread destructor wakes up the thread in idle_loop() and waits
+/// for its termination. Thread should be already waiting.
+
+Thread::~Thread() {
+
+ assert(!searching);
+
+ exit = true;
+ start_searching();
+ stdThread.join();
+}
+
+
+/// Thread::clear() reset histories, usually before a new game
+
+void Thread::clear() {
+
+ counterMoves.fill(MOVE_NONE);
+ mainHistory.fill(0);
+ captureHistory.fill(0);
+
+ for (auto& to : contHistory)
+ for (auto& h : to)
+ h.fill(0);
+
+ contHistory[NO_PIECE][0].fill(Search::CounterMovePruneThreshold - 1);
+}
+
+/// Thread::start_searching() wakes up the thread that will start the search
+
+void Thread::start_searching() {
+
+ std::lock_guard<Mutex> lk(mutex);
+ searching = true;
+ cv.notify_one(); // Wake up the thread in idle_loop()
+}
+
+
+/// Thread::wait_for_search_finished() blocks on the condition variable
+/// until the thread has finished searching.
+
+void Thread::wait_for_search_finished() {
+
+ std::unique_lock<Mutex> lk(mutex);
+ cv.wait(lk, [&]{ return !searching; });
+}
+
+
+/// Thread::idle_loop() is where the thread is parked, blocked on the
+/// condition variable, when it has no work to do.
+
+void Thread::idle_loop() {
+
+ // If OS already scheduled us on a different group than 0 then don't overwrite
+ // the choice, eventually we are one of many one-threaded processes running on
+ // some Windows NUMA hardware, for instance in fishtest. To make it simple,
+ // just check if running threads are below a threshold, in this case all this
+ // NUMA machinery is not needed.
+ if (Options["Threads"] >= 8)
+ WinProcGroup::bindThisThread(idx);
+
+ while (true)
+ {
+ std::unique_lock<Mutex> lk(mutex);
+ searching = false;
+ cv.notify_one(); // Wake up anyone waiting for search finished
+ cv.wait(lk, [&]{ return searching; });
+
+ if (exit)
+ return;
+
+ lk.unlock();
+
+ search();
+ }
+}
+
+/// ThreadPool::set() creates/destroys threads to match the requested number.
+/// Created and launced threads wil go immediately to sleep in idle_loop.
+/// Upon resizing, threads are recreated to allow for binding if necessary.
+
+void ThreadPool::set(size_t requested) {
+
+ if (size() > 0) { // destroy any existing thread(s)
+ main()->wait_for_search_finished();
+
+ while (size() > 0)
+ delete back(), pop_back();
+ }
+
+ if (requested > 0) { // create new thread(s)
+ push_back(new MainThread(0));
+
+ while (size() < requested)
+ push_back(new Thread(size()));
+ clear();
+ }
+}
+
+/// ThreadPool::clear() sets threadPool data to initial values.
+
+void ThreadPool::clear() {
+
+ for (Thread* th : *this)
+ th->clear();
+
+ main()->callsCnt = 0;
+ main()->previousScore = VALUE_INFINITE;
+ main()->previousTimeReduction = 1;
+}
+
+/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
+/// returns immediately. Main thread will wake up other threads and start the search.
+
+void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
+ const Search::LimitsType& limits, bool ponderMode) {
+
+ main()->wait_for_search_finished();
+
+ stopOnPonderhit = stop = false;
+ ponder = ponderMode;
+ Search::Limits = limits;
+ Search::RootMoves rootMoves;
+
+ for (const auto& m : MoveList<LEGAL>(pos))
+ if ( limits.searchmoves.empty()
+ || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
+ rootMoves.emplace_back(m);
+
+ if (!rootMoves.empty())
+ Tablebases::filter_root_moves(pos, rootMoves);
+
+ // After ownership transfer 'states' becomes empty, so if we stop the search
+ // and call 'go' again without setting a new position states.get() == NULL.
+ assert(states.get() || setupStates.get());
+
+ if (states.get())
+ setupStates = std::move(states); // Ownership transfer, states is now empty
+
+ // We use Position::set() to set root position across threads. But there are
+ // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
+ // be deduced from a fen string, so set() clears them and to not lose the info
+ // we need to backup and later restore setupStates->back(). Note that setupStates
+ // is shared by threads but is accessed in read-only mode.
+ StateInfo tmp = setupStates->back();
+
+ for (Thread* th : *this)
+ {
+ th->nodes = th->tbHits = th->nmp_ply = th->nmp_odd = 0;
+ th->rootDepth = th->completedDepth = DEPTH_ZERO;
+ th->rootMoves = rootMoves;
+ th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
+ }
+
+ setupStates->back() = tmp;
+
+ main()->start_searching();
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef THREAD_H_INCLUDED
+#define THREAD_H_INCLUDED
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "material.h"
+#include "movepick.h"
+#include "pawns.h"
+#include "position.h"
+#include "search.h"
+#include "thread_win32.h"
+
+
+/// Thread class keeps together all the thread-related stuff. We use
+/// per-thread pawn and material hash tables so that once we get a
+/// pointer to an entry its life time is unlimited and we don't have
+/// to care about someone changing the entry under our feet.
+
+class Thread {
+
+ Mutex mutex;
+ ConditionVariable cv;
+ size_t idx;
+ bool exit = false, searching = true; // Set before starting std::thread
+ std::thread stdThread;
+
+public:
+ explicit Thread(size_t);
+ virtual ~Thread();
+ virtual void search();
+ void clear();
+ void idle_loop();
+ void start_searching();
+ void wait_for_search_finished();
+
+ Pawns::Table pawnsTable;
+ Material::Table materialTable;
+ Endgames endgames;
+ size_t PVIdx;
+ int selDepth, nmp_ply, nmp_odd;
+ std::atomic<uint64_t> nodes, tbHits;
+
+ Position rootPos;
+ Search::RootMoves rootMoves;
+ Depth rootDepth, completedDepth;
+ CounterMoveHistory counterMoves;
+ ButterflyHistory mainHistory;
+ CapturePieceToHistory captureHistory;
+ ContinuationHistory contHistory;
+};
+
+
+/// MainThread is a derived class specific for main thread
+
+struct MainThread : public Thread {
+
+ using Thread::Thread;
+
+ void search() override;
+ void check_time();
+
+ bool failedLow;
+ double bestMoveChanges, previousTimeReduction;
+ Value previousScore;
+ int callsCnt;
+};
+
+
+/// ThreadPool struct handles all the threads-related stuff like init, starting,
+/// parking and, most importantly, launching a thread. All the access to threads
+/// is done through this class.
+
+struct ThreadPool : public std::vector<Thread*> {
+
+ void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
+ void clear();
+ void set(size_t);
+
+ MainThread* main() const { return static_cast<MainThread*>(front()); }
+ uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
+ uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
+
+ std::atomic_bool stop, ponder, stopOnPonderhit;
+
+private:
+ StateListPtr setupStates;
+
+ uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
+
+ uint64_t sum = 0;
+ for (Thread* th : *this)
+ sum += (th->*member).load(std::memory_order_relaxed);
+ return sum;
+ }
+};
+
+extern ThreadPool Threads;
+
+#endif // #ifndef THREAD_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef THREAD_WIN32_H_INCLUDED
+#define THREAD_WIN32_H_INCLUDED
+
+/// STL thread library used by mingw and gcc when cross compiling for Windows
+/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
+/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
+/// mode transition in order to lock or unlock, which is very slow compared to
+/// interlocked operations (about 30% slower on bench test). To work around this
+/// issue, we define our wrappers to the low level Win32 calls. We use critical
+/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
+/// is racy between unlock() and WaitForSingleObject() but they have the same
+/// speed performance as the SRW locks.
+
+#include <condition_variable>
+#include <mutex>
+
+#if defined(_WIN32) && !defined(_MSC_VER)
+
+#ifndef NOMINMAX
+# define NOMINMAX // Disable macros min() and max()
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+/// Mutex and ConditionVariable struct are wrappers of the low level locking
+/// machinery and are modeled after the corresponding C++11 classes.
+
+struct Mutex {
+ Mutex() { InitializeCriticalSection(&cs); }
+ ~Mutex() { DeleteCriticalSection(&cs); }
+ void lock() { EnterCriticalSection(&cs); }
+ void unlock() { LeaveCriticalSection(&cs); }
+
+private:
+ CRITICAL_SECTION cs;
+};
+
+typedef std::condition_variable_any ConditionVariable;
+
+#else // Default case: use STL classes
+
+typedef std::mutex Mutex;
+typedef std::condition_variable ConditionVariable;
+
+#endif
+
+#endif // #ifndef THREAD_WIN32_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+
+#include "search.h"
+#include "timeman.h"
+#include "uci.h"
+
+TimeManagement Time; // Our global time management object
+
+namespace {
+
+ enum TimeType { OptimumTime, MaxTime };
+
+ const int MoveHorizon = 50; // Plan time management at most this many moves ahead
+ const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio
+ const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
+
+
+ // move_importance() is a skew-logistic function based on naive statistical
+ // analysis of "how many games are still undecided after n half-moves". Game
+ // is considered "undecided" as long as neither side has >275cp advantage.
+ // Data was extracted from the CCRL game database with some simple filtering criteria.
+
+ double move_importance(int ply) {
+
+ const double XScale = 7.64;
+ const double XShift = 58.4;
+ const double Skew = 0.183;
+
+ return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
+ }
+
+ template<TimeType T>
+ int remaining(int myTime, int movesToGo, int ply, int slowMover) {
+
+ const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
+ const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
+
+ double moveImportance = (move_importance(ply) * slowMover) / 100;
+ double otherMovesImportance = 0;
+
+ for (int i = 1; i < movesToGo; ++i)
+ otherMovesImportance += move_importance(ply + 2 * i);
+
+ double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
+ double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
+
+ return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
+ }
+
+} // namespace
+
+
+/// init() is called at the beginning of the search and calculates the allowed
+/// thinking time out of the time control and current game ply. We support four
+/// different kinds of time controls, passed in 'limits':
+///
+/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
+/// inc == 0 && movestogo != 0 means: x moves in y minutes
+/// inc > 0 && movestogo == 0 means: x basetime + z increment
+/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
+
+void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
+
+ int minThinkingTime = Options["Minimum Thinking Time"];
+ int moveOverhead = Options["Move Overhead"];
+ int slowMover = Options["Slow Mover"];
+ int npmsec = Options["nodestime"];
+
+ // If we have to play in 'nodes as time' mode, then convert from time
+ // to nodes, and use resulting values in time management formulas.
+ // WARNING: Given npms (nodes per millisecond) must be much lower then
+ // the real engine speed to avoid time losses.
+ if (npmsec)
+ {
+ if (!availableNodes) // Only once at game start
+ availableNodes = npmsec * limits.time[us]; // Time is in msec
+
+ // Convert from millisecs to nodes
+ limits.time[us] = (int)availableNodes;
+ limits.inc[us] *= npmsec;
+ limits.npmsec = npmsec;
+ }
+
+ startTime = limits.startTime;
+ optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
+
+ const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
+
+ // We calculate optimum time usage for different hypothetical "moves to go"-values
+ // and choose the minimum of calculated search time values. Usually the greatest
+ // hypMTG gives the minimum values.
+ for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
+ {
+ // Calculate thinking time for hypothetical "moves to go"-value
+ int hypMyTime = limits.time[us]
+ + limits.inc[us] * (hypMTG - 1)
+ - moveOverhead * (2 + std::min(hypMTG, 40));
+
+ hypMyTime = std::max(hypMyTime, 0);
+
+ int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
+ int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
+
+ optimumTime = std::min(t1, optimumTime);
+ maximumTime = std::min(t2, maximumTime);
+ }
+
+ if (Options["Ponder"])
+ optimumTime += optimumTime / 4;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TIMEMAN_H_INCLUDED
+#define TIMEMAN_H_INCLUDED
+
+#include "misc.h"
+#include "search.h"
+#include "thread.h"
+
+/// The TimeManagement class computes the optimal time to think depending on
+/// the maximum available time, the game move number and other parameters.
+
+class TimeManagement {
+public:
+ void init(Search::LimitsType& limits, Color us, int ply);
+ int optimum() const { return optimumTime; }
+ int maximum() const { return maximumTime; }
+ int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); }
+
+ int64_t availableNodes; // When in 'nodes as time' mode
+
+private:
+ TimePoint startTime;
+ int optimumTime;
+ int maximumTime;
+};
+
+extern TimeManagement Time;
+
+#endif // #ifndef TIMEMAN_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstring> // For std::memset
+#include <iostream>
+
+#include "bitboard.h"
+#include "tt.h"
+
+TranspositionTable TT; // Our global transposition table
+
+
+/// TranspositionTable::resize() sets the size of the transposition table,
+/// measured in megabytes. Transposition table consists of a power of 2 number
+/// of clusters and each cluster consists of ClusterSize number of TTEntry.
+
+void TranspositionTable::resize(size_t mbSize) {
+
+ size_t newClusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
+
+ if (newClusterCount == clusterCount)
+ return;
+
+ clusterCount = newClusterCount;
+
+ free(mem);
+ mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1);
+
+ if (!mem)
+ {
+ std::cerr << "Failed to allocate " << mbSize
+ << "MB for transposition table." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
+ clear();
+}
+
+
+/// TranspositionTable::clear() overwrites the entire transposition table
+/// with zeros. It is called whenever the table is resized, or when the
+/// user asks the program to clear the table (from the UCI interface).
+
+void TranspositionTable::clear() {
+
+ std::memset(table, 0, clusterCount * sizeof(Cluster));
+}
+
+
+/// TranspositionTable::probe() looks up the current position in the transposition
+/// table. It returns true and a pointer to the TTEntry if the position is found.
+/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
+/// to be replaced later. The replace value of an entry is calculated as its depth
+/// minus 8 times its relative age. TTEntry t1 is considered more valuable than
+/// TTEntry t2 if its replace value is greater than that of t2.
+
+TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
+
+ TTEntry* const tte = first_entry(key);
+ const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
+
+ for (int i = 0; i < ClusterSize; ++i)
+ if (!tte[i].key16 || tte[i].key16 == key16)
+ {
+ if ((tte[i].genBound8 & 0xFC) != generation8 && tte[i].key16)
+ tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
+
+ return found = (bool)tte[i].key16, &tte[i];
+ }
+
+ // Find an entry to be replaced according to the replacement strategy
+ TTEntry* replace = tte;
+ for (int i = 1; i < ClusterSize; ++i)
+ // Due to our packed storage format for generation and its cyclic
+ // nature we add 259 (256 is the modulus plus 3 to keep the lowest
+ // two bound bits from affecting the result) to calculate the entry
+ // age correctly even after generation8 overflows into the next cycle.
+ if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
+ > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
+ replace = &tte[i];
+
+ return found = false, replace;
+}
+
+
+/// TranspositionTable::hashfull() returns an approximation of the hashtable
+/// occupation during a search. The hash is x permill full, as per UCI protocol.
+
+int TranspositionTable::hashfull() const {
+
+ int cnt = 0;
+ for (int i = 0; i < 1000 / ClusterSize; i++)
+ {
+ const TTEntry* tte = &table[i].entry[0];
+ for (int j = 0; j < ClusterSize; j++)
+ if ((tte[j].genBound8 & 0xFC) == generation8)
+ cnt++;
+ }
+ return cnt;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TT_H_INCLUDED
+#define TT_H_INCLUDED
+
+#include "misc.h"
+#include "types.h"
+
+/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
+///
+/// key 16 bit
+/// move 16 bit
+/// value 16 bit
+/// eval value 16 bit
+/// generation 6 bit
+/// bound type 2 bit
+/// depth 8 bit
+
+struct TTEntry {
+
+ Move move() const { return (Move )move16; }
+ Value value() const { return (Value)value16; }
+ Value eval() const { return (Value)eval16; }
+ Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); }
+ Bound bound() const { return (Bound)(genBound8 & 0x3); }
+
+ void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
+
+ assert(d / ONE_PLY * ONE_PLY == d);
+
+ // Preserve any existing move for the same position
+ if (m || (k >> 48) != key16)
+ move16 = (uint16_t)m;
+
+ // Don't overwrite more valuable entries
+ if ( (k >> 48) != key16
+ || d / ONE_PLY > depth8 - 4
+ /* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
+ || b == BOUND_EXACT)
+ {
+ key16 = (uint16_t)(k >> 48);
+ value16 = (int16_t)v;
+ eval16 = (int16_t)ev;
+ genBound8 = (uint8_t)(g | b);
+ depth8 = (int8_t)(d / ONE_PLY);
+ }
+ }
+
+private:
+ friend class TranspositionTable;
+
+ uint16_t key16;
+ uint16_t move16;
+ int16_t value16;
+ int16_t eval16;
+ uint8_t genBound8;
+ int8_t depth8;
+};
+
+
+/// A TranspositionTable consists of a power of 2 number of clusters and each
+/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
+/// contains information of exactly one position. The size of a cluster should
+/// divide the size of a cache line size, to ensure that clusters never cross
+/// cache lines. This ensures best cache performance, as the cacheline is
+/// prefetched, as soon as possible.
+
+class TranspositionTable {
+
+ static const int CacheLineSize = 64;
+ static const int ClusterSize = 3;
+
+ struct Cluster {
+ TTEntry entry[ClusterSize];
+ char padding[2]; // Align to a divisor of the cache line size
+ };
+
+ static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect");
+
+public:
+ ~TranspositionTable() { free(mem); }
+ void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
+ uint8_t generation() const { return generation8; }
+ TTEntry* probe(const Key key, bool& found) const;
+ int hashfull() const;
+ void resize(size_t mbSize);
+ void clear();
+
+ // The 32 lowest order bits of the key are used to get the index of the cluster
+ TTEntry* first_entry(const Key key) const {
+ return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0];
+ }
+
+private:
+ size_t clusterCount;
+ Cluster* table;
+ void* mem;
+ uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
+};
+
+extern TranspositionTable TT;
+
+#endif // #ifndef TT_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TYPES_H_INCLUDED
+#define TYPES_H_INCLUDED
+
+/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
+/// is done automatically. To get started type 'make help'.
+///
+/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
+/// need to be set manually:
+///
+/// -DNDEBUG | Disable debugging mode. Always use this for release.
+///
+/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
+/// | run on some very old machines.
+///
+/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
+/// | only in 64-bit mode and requires hardware with popcnt support.
+///
+/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
+/// | only in 64-bit mode and requires hardware with pext support.
+
+#include <cassert>
+#include <cctype>
+#include <climits>
+#include <cstdint>
+#include <cstdlib>
+
+#if defined(_MSC_VER)
+// Disable some silly and noisy warning from MSVC compiler
+#pragma warning(disable: 4127) // Conditional expression is constant
+#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
+#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
+#endif
+
+/// Predefined macros hell:
+///
+/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
+/// __INTEL_COMPILER Compiler is Intel
+/// _MSC_VER Compiler is MSVC or Intel on Windows
+/// _WIN32 Building on Windows (any)
+/// _WIN64 Building on Windows 64 bit
+
+#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
+# include <intrin.h> // Microsoft header for _BitScanForward64()
+# define IS_64BIT
+#endif
+
+#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
+# include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64()
+#endif
+
+#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
+# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
+#endif
+
+#if defined(USE_PEXT)
+# include <immintrin.h> // Header for _pext_u64() intrinsic
+# define pext(b, m) _pext_u64(b, m)
+#else
+# define pext(b, m) 0
+#endif
+
+#ifdef USE_POPCNT
+const bool HasPopCnt = true;
+#else
+const bool HasPopCnt = false;
+#endif
+
+#ifdef USE_PEXT
+const bool HasPext = true;
+#else
+const bool HasPext = false;
+#endif
+
+#ifdef IS_64BIT
+const bool Is64Bit = true;
+#else
+const bool Is64Bit = false;
+#endif
+
+typedef uint64_t Key;
+typedef uint64_t Bitboard;
+
+const int MAX_MOVES = 256;
+const int MAX_PLY = 128;
+
+/// A move needs 16 bits to be stored
+///
+/// bit 0- 5: destination square (from 0 to 63)
+/// bit 6-11: origin square (from 0 to 63)
+/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
+/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
+/// NOTE: EN-PASSANT bit is set only when a pawn can be captured
+///
+/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
+/// any normal move destination square is always different from origin square
+/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
+
+enum Move : int {
+ MOVE_NONE,
+ MOVE_NULL = 65
+};
+
+enum MoveType {
+ NORMAL,
+ PROMOTION = 1 << 14,
+ ENPASSANT = 2 << 14,
+ CASTLING = 3 << 14
+};
+
+enum Color {
+ WHITE, BLACK, COLOR_NB = 2
+};
+
+enum CastlingSide {
+ KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
+};
+
+enum CastlingRight {
+ NO_CASTLING,
+ WHITE_OO,
+ WHITE_OOO = WHITE_OO << 1,
+ BLACK_OO = WHITE_OO << 2,
+ BLACK_OOO = WHITE_OO << 3,
+ ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
+ CASTLING_RIGHT_NB = 16
+};
+
+template<Color C, CastlingSide S> struct MakeCastling {
+ static constexpr CastlingRight
+ right = C == WHITE ? S == QUEEN_SIDE ? WHITE_OOO : WHITE_OO
+ : S == QUEEN_SIDE ? BLACK_OOO : BLACK_OO;
+};
+
+enum Phase {
+ PHASE_ENDGAME,
+ PHASE_MIDGAME = 128,
+ MG = 0, EG = 1, PHASE_NB = 2
+};
+
+enum ScaleFactor {
+ SCALE_FACTOR_DRAW = 0,
+ SCALE_FACTOR_ONEPAWN = 48,
+ SCALE_FACTOR_NORMAL = 64,
+ SCALE_FACTOR_MAX = 128,
+ SCALE_FACTOR_NONE = 255
+};
+
+enum Bound {
+ BOUND_NONE,
+ BOUND_UPPER,
+ BOUND_LOWER,
+ BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
+};
+
+enum Value : int {
+ VALUE_ZERO = 0,
+ VALUE_DRAW = 0,
+ VALUE_KNOWN_WIN = 10000,
+ VALUE_MATE = 32000,
+ VALUE_INFINITE = 32001,
+ VALUE_NONE = 32002,
+
+ VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
+ VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
+
+ PawnValueMg = 171, PawnValueEg = 240,
+ KnightValueMg = 764, KnightValueEg = 848,
+ BishopValueMg = 826, BishopValueEg = 891,
+ RookValueMg = 1282, RookValueEg = 1373,
+ QueenValueMg = 2526, QueenValueEg = 2646,
+
+ MidgameLimit = 15258, EndgameLimit = 3915
+};
+
+enum PieceType {
+ NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
+ ALL_PIECES = 0,
+ QUEEN_DIAGONAL = 7,
+ PIECE_TYPE_NB = 8
+};
+
+enum Piece {
+ NO_PIECE,
+ W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
+ B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
+ PIECE_NB = 16
+};
+
+extern Value PieceValue[PHASE_NB][PIECE_NB];
+
+enum Depth : int {
+
+ ONE_PLY = 1,
+
+ DEPTH_ZERO = 0 * ONE_PLY,
+ DEPTH_QS_CHECKS = 0 * ONE_PLY,
+ DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
+ DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
+
+ DEPTH_NONE = -6 * ONE_PLY,
+ DEPTH_MAX = MAX_PLY * ONE_PLY
+};
+
+static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
+
+enum Square : int {
+ 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,
+
+ SQUARE_NB = 64
+};
+
+enum Direction : int {
+ NORTH = 8,
+ EAST = 1,
+ SOUTH = -NORTH,
+ WEST = -EAST,
+
+ NORTH_EAST = NORTH + EAST,
+ SOUTH_EAST = SOUTH + EAST,
+ SOUTH_WEST = SOUTH + WEST,
+ NORTH_WEST = NORTH + WEST
+};
+
+enum File : int {
+ FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
+};
+
+enum Rank : int {
+ RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
+};
+
+
+/// Score enum stores a middlegame and an endgame value in a single integer
+/// (enum). The least significant 16 bits are used to store the endgame value
+/// and the upper 16 bits are used to store the middlegame value. Take some
+/// care to avoid left-shifting a signed int to avoid undefined behavior.
+enum Score : int { SCORE_ZERO };
+
+constexpr Score make_score(int mg, int eg) {
+ return Score((int)((unsigned int)eg << 16) + mg);
+}
+
+/// Extracting the signed lower and upper 16 bits is not so trivial because
+/// according to the standard a simple cast to short is implementation defined
+/// and so is a right shift of a signed integer.
+inline Value eg_value(Score s) {
+ union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
+ return Value(eg.s);
+}
+
+inline Value mg_value(Score s) {
+ union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
+ return Value(mg.s);
+}
+
+#define ENABLE_BASE_OPERATORS_ON(T) \
+constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
+constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
+constexpr T operator-(T d) { return T(-int(d)); } \
+inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
+inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; }
+
+#define ENABLE_INCR_OPERATORS_ON(T) \
+inline T& operator++(T& d) { return d = T(int(d) + 1); } \
+inline T& operator--(T& d) { return d = T(int(d) - 1); }
+
+#define ENABLE_FULL_OPERATORS_ON(T) \
+ENABLE_BASE_OPERATORS_ON(T) \
+ENABLE_INCR_OPERATORS_ON(T) \
+constexpr T operator*(int i, T d) { return T(i * int(d)); } \
+constexpr T operator*(T d, int i) { return T(int(d) * i); } \
+constexpr T operator/(T d, int i) { return T(int(d) / i); } \
+constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \
+inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
+inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
+
+ENABLE_FULL_OPERATORS_ON(Value)
+ENABLE_FULL_OPERATORS_ON(Depth)
+ENABLE_FULL_OPERATORS_ON(Direction)
+
+ENABLE_INCR_OPERATORS_ON(PieceType)
+ENABLE_INCR_OPERATORS_ON(Piece)
+ENABLE_INCR_OPERATORS_ON(Color)
+ENABLE_INCR_OPERATORS_ON(Square)
+ENABLE_INCR_OPERATORS_ON(File)
+ENABLE_INCR_OPERATORS_ON(Rank)
+
+ENABLE_BASE_OPERATORS_ON(Score)
+
+#undef ENABLE_FULL_OPERATORS_ON
+#undef ENABLE_INCR_OPERATORS_ON
+#undef ENABLE_BASE_OPERATORS_ON
+
+/// Additional operators to add integers to a Value
+constexpr Value operator+(Value v, int i) { return Value(int(v) + i); }
+constexpr Value operator-(Value v, int i) { return Value(int(v) - i); }
+inline Value& operator+=(Value& v, int i) { return v = v + i; }
+inline Value& operator-=(Value& v, int i) { return v = v - i; }
+
+/// Additional operators to add a Direction to a Square
+inline Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
+inline Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
+inline Square& operator+=(Square &s, Direction d) { return s = s + d; }
+inline Square& operator-=(Square &s, Direction d) { return s = s - d; }
+
+/// Only declared but not defined. We don't want to multiply two scores due to
+/// a very high risk of overflow. So user should explicitly convert to integer.
+Score operator*(Score, Score) = delete;
+
+/// Division of a Score must be handled separately for each term
+inline Score operator/(Score s, int i) {
+ return make_score(mg_value(s) / i, eg_value(s) / i);
+}
+
+/// Multiplication of a Score by an integer. We check for overflow in debug mode.
+inline Score operator*(Score s, int i) {
+
+ Score result = Score(int(s) * i);
+
+ assert(eg_value(result) == (i * eg_value(s)));
+ assert(mg_value(result) == (i * mg_value(s)));
+ assert((i == 0) || (result / i) == s );
+
+ return result;
+}
+
+constexpr Color operator~(Color c) {
+ return Color(c ^ BLACK); // Toggle color
+}
+
+constexpr Square operator~(Square s) {
+ return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
+}
+
+constexpr File operator~(File f) {
+ return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
+}
+
+constexpr Piece operator~(Piece pc) {
+ return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
+}
+
+constexpr CastlingRight operator|(Color c, CastlingSide s) {
+ return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
+}
+
+constexpr Value mate_in(int ply) {
+ return VALUE_MATE - ply;
+}
+
+constexpr Value mated_in(int ply) {
+ return -VALUE_MATE + ply;
+}
+
+constexpr Square make_square(File f, Rank r) {
+ return Square((r << 3) + f);
+}
+
+constexpr Piece make_piece(Color c, PieceType pt) {
+ return Piece((c << 3) + pt);
+}
+
+constexpr PieceType type_of(Piece pc) {
+ return PieceType(pc & 7);
+}
+
+inline Color color_of(Piece pc) {
+ assert(pc != NO_PIECE);
+ return Color(pc >> 3);
+}
+
+constexpr bool is_ok(Square s) {
+ return s >= SQ_A1 && s <= SQ_H8;
+}
+
+constexpr File file_of(Square s) {
+ return File(s & 7);
+}
+
+constexpr Rank rank_of(Square s) {
+ return Rank(s >> 3);
+}
+
+constexpr Square relative_square(Color c, Square s) {
+ return Square(s ^ (c * 56));
+}
+
+constexpr Rank relative_rank(Color c, Rank r) {
+ return Rank(r ^ (c * 7));
+}
+
+constexpr Rank relative_rank(Color c, Square s) {
+ return relative_rank(c, rank_of(s));
+}
+
+inline bool opposite_colors(Square s1, Square s2) {
+ int s = int(s1) ^ int(s2);
+ return ((s >> 3) ^ s) & 1;
+}
+
+constexpr Direction pawn_push(Color c) {
+ return c == WHITE ? NORTH : SOUTH;
+}
+
+constexpr Square from_sq(Move m) {
+ return Square((m >> 6) & 0x3F);
+}
+
+constexpr Square to_sq(Move m) {
+ return Square(m & 0x3F);
+}
+
+constexpr int from_to(Move m) {
+ return m & 0xFFF;
+}
+
+constexpr MoveType type_of(Move m) {
+ return MoveType(m & (3 << 14));
+}
+
+constexpr PieceType promotion_type(Move m) {
+ return PieceType(((m >> 12) & 3) + KNIGHT);
+}
+
+inline Move make_move(Square from, Square to) {
+ return Move((from << 6) + to);
+}
+
+template<MoveType T>
+constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
+ return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
+}
+
+constexpr bool is_ok(Move m) {
+ return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
+}
+
+#endif // #ifndef TYPES_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "evaluate.h"
+#include "movegen.h"
+#include "position.h"
+#include "search.h"
+#include "thread.h"
+#include "tt.h"
+#include "timeman.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+using namespace std;
+
+extern vector<string> setup_bench(const Position&, istream&);
+
+namespace {
+
+ // FEN string of the initial position, normal chess
+ const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+
+
+ // position() is called when engine receives the "position" UCI command.
+ // The function sets up the position described in the given FEN string ("fen")
+ // or the starting position ("startpos") and then makes the moves given in the
+ // following move list ("moves").
+
+ void position(Position& pos, istringstream& is, StateListPtr& states) {
+
+ Move m;
+ string token, fen;
+
+ is >> token;
+
+ if (token == "startpos")
+ {
+ fen = StartFEN;
+ is >> token; // Consume "moves" token if any
+ }
+ else if (token == "fen")
+ while (is >> token && token != "moves")
+ fen += token + " ";
+ else
+ return;
+
+ states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
+ pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
+
+ // Parse move list (if any)
+ while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
+ {
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+ }
+
+
+ // setoption() is called when engine receives the "setoption" UCI command. The
+ // function updates the UCI option ("name") to the given value ("value").
+
+ void setoption(istringstream& is) {
+
+ string token, name, value;
+
+ is >> token; // Consume "name" token
+
+ // Read option name (can contain spaces)
+ while (is >> token && token != "value")
+ name += string(" ", name.empty() ? 0 : 1) + token;
+
+ // Read option value (can contain spaces)
+ while (is >> token)
+ value += string(" ", value.empty() ? 0 : 1) + token;
+
+ if (Options.count(name))
+ Options[name] = value;
+ else
+ sync_cout << "No such option: " << name << sync_endl;
+ }
+
+
+ // go() is called when engine receives the "go" UCI command. The function sets
+ // the thinking time and other parameters from the input string, then starts
+ // the search.
+
+ void go(Position& pos, istringstream& is, StateListPtr& states) {
+
+ Search::LimitsType limits;
+ string token;
+ bool ponderMode = false;
+
+ limits.startTime = now(); // As early as possible!
+
+ while (is >> token)
+ if (token == "searchmoves")
+ while (is >> token)
+ limits.searchmoves.push_back(UCI::to_move(pos, token));
+
+ else if (token == "wtime") is >> limits.time[WHITE];
+ else if (token == "btime") is >> limits.time[BLACK];
+ else if (token == "winc") is >> limits.inc[WHITE];
+ else if (token == "binc") is >> limits.inc[BLACK];
+ else if (token == "movestogo") is >> limits.movestogo;
+ else if (token == "depth") is >> limits.depth;
+ else if (token == "nodes") is >> limits.nodes;
+ else if (token == "movetime") is >> limits.movetime;
+ else if (token == "mate") is >> limits.mate;
+ else if (token == "perft") is >> limits.perft;
+ else if (token == "infinite") limits.infinite = 1;
+ else if (token == "ponder") ponderMode = true;
+
+ Threads.start_thinking(pos, states, limits, ponderMode);
+ }
+
+
+ // bench() is called when engine receives the "bench" command. Firstly
+ // a list of UCI commands is setup according to bench parameters, then
+ // it is run one by one printing a summary at the end.
+
+ void bench(Position& pos, istream& args, StateListPtr& states) {
+
+ string token;
+ uint64_t num, nodes = 0, cnt = 1;
+
+ vector<string> list = setup_bench(pos, args);
+ num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
+
+ TimePoint elapsed = now();
+
+ for (const auto& cmd : list)
+ {
+ istringstream is(cmd);
+ is >> skipws >> token;
+
+ if (token == "go")
+ {
+ cerr << "\nPosition: " << cnt++ << '/' << num << endl;
+ go(pos, is, states);
+ Threads.main()->wait_for_search_finished();
+ nodes += Threads.nodes_searched();
+ }
+ else if (token == "setoption") setoption(is);
+ else if (token == "position") position(pos, is, states);
+ else if (token == "ucinewgame") Search::clear();
+ }
+
+ elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
+
+ dbg_print(); // Just before exiting
+
+ cerr << "\n==========================="
+ << "\nTotal time (ms) : " << elapsed
+ << "\nNodes searched : " << nodes
+ << "\nNodes/second : " << 1000 * nodes / elapsed << endl;
+ }
+
+} // namespace
+
+
+/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
+/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
+/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
+/// run 'bench', once the command is executed the function returns immediately.
+/// In addition to the UCI ones, also some additional debug commands are supported.
+
+void UCI::loop(int argc, char* argv[]) {
+
+ Position pos;
+ string token, cmd;
+ StateListPtr states(new std::deque<StateInfo>(1));
+ auto uiThread = std::make_shared<Thread>(0);
+
+ pos.set(StartFEN, false, &states->back(), uiThread.get());
+
+ for (int i = 1; i < argc; ++i)
+ cmd += std::string(argv[i]) + " ";
+
+ do {
+ if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
+ cmd = "quit";
+
+ istringstream is(cmd);
+
+ token.clear(); // Avoid a stale if getline() returns empty or blank line
+ is >> skipws >> token;
+
+ // The GUI sends 'ponderhit' to tell us the user has played the expected move.
+ // So 'ponderhit' will be sent if we were told to ponder on the same move the
+ // user has played. We should continue searching but switch from pondering to
+ // normal search. In case Threads.stopOnPonderhit is set we are waiting for
+ // 'ponderhit' to stop the search, for instance if max search depth is reached.
+ if ( token == "quit"
+ || token == "stop"
+ || (token == "ponderhit" && Threads.stopOnPonderhit))
+ Threads.stop = true;
+
+ else if (token == "ponderhit")
+ Threads.ponder = false; // Switch to normal search
+
+ else if (token == "uci")
+ sync_cout << "id name " << engine_info(true)
+ << "\n" << Options
+ << "\nuciok" << sync_endl;
+
+ else if (token == "setoption") setoption(is);
+ else if (token == "go") go(pos, is, states);
+ else if (token == "position") position(pos, is, states);
+ else if (token == "ucinewgame") Search::clear();
+ else if (token == "isready") sync_cout << "readyok" << sync_endl;
+
+ // Additional custom non-UCI commands, mainly for debugging
+ else if (token == "flip") pos.flip();
+ else if (token == "bench") bench(pos, is, states);
+ else if (token == "d") sync_cout << pos << sync_endl;
+ else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
+ else
+ sync_cout << "Unknown command: " << cmd << sync_endl;
+
+ } while (token != "quit" && argc == 1); // Command line args are one-shot
+}
+
+
+/// UCI::value() converts a Value to a string suitable for use with the UCI
+/// protocol specification:
+///
+/// cp <x> The score from the engine's point of view in centipawns.
+/// mate <y> Mate in y moves, not plies. If the engine is getting mated
+/// use negative values for y.
+
+string UCI::value(Value v) {
+
+ assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
+
+ stringstream ss;
+
+ if (abs(v) < VALUE_MATE - MAX_PLY)
+ ss << "cp " << v * 100 / PawnValueEg;
+ else
+ ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
+
+ return ss.str();
+}
+
+
+/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
+
+std::string UCI::square(Square s) {
+ return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
+}
+
+
+/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
+/// The only special case is castling, where we print in the e1g1 notation in
+/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
+/// castling moves are always encoded as 'king captures rook'.
+
+string UCI::move(Move m, bool chess960) {
+
+ Square from = from_sq(m);
+ Square to = to_sq(m);
+
+ if (m == MOVE_NONE)
+ return "(none)";
+
+ if (m == MOVE_NULL)
+ return "0000";
+
+ if (type_of(m) == CASTLING && !chess960)
+ to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
+
+ string move = UCI::square(from) + UCI::square(to);
+
+ if (type_of(m) == PROMOTION)
+ move += " pnbrqk"[promotion_type(m)];
+
+ return move;
+}
+
+
+/// UCI::to_move() converts a string representing a move in coordinate notation
+/// (g1f3, a7a8q) to the corresponding legal Move, if any.
+
+Move UCI::to_move(const Position& pos, string& str) {
+
+ if (str.length() == 5) // Junior could send promotion piece in uppercase
+ str[4] = char(tolower(str[4]));
+
+ for (const auto& m : MoveList<LEGAL>(pos))
+ if (str == UCI::move(m, pos.is_chess960()))
+ return m;
+
+ return MOVE_NONE;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef UCI_H_INCLUDED
+#define UCI_H_INCLUDED
+
+#include <map>
+#include <string>
+
+#include "types.h"
+
+class Position;
+
+namespace UCI {
+
+class Option;
+
+/// Custom comparator because UCI options should be case insensitive
+struct CaseInsensitiveLess {
+ bool operator() (const std::string&, const std::string&) const;
+};
+
+/// Our options container is actually a std::map
+typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
+
+/// Option class implements an option as defined by UCI protocol
+class Option {
+
+ typedef void (*OnChange)(const Option&);
+
+public:
+ Option(OnChange = nullptr);
+ Option(bool v, OnChange = nullptr);
+ Option(const char* v, OnChange = nullptr);
+ Option(int v, int minv, int maxv, OnChange = nullptr);
+
+ Option& operator=(const std::string&);
+ void operator<<(const Option&);
+ operator int() const;
+ operator std::string() const;
+
+private:
+ friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
+
+ std::string defaultValue, currentValue, type;
+ int min, max;
+ size_t idx;
+ OnChange on_change;
+};
+
+void init(OptionsMap&);
+void loop(int argc, char* argv[]);
+std::string value(Value v);
+std::string square(Square s);
+std::string move(Move m, bool chess960);
+std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
+Move to_move(const Position& pos, std::string& str);
+
+} // namespace UCI
+
+extern UCI::OptionsMap Options;
+
+#endif // #ifndef UCI_H_INCLUDED
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+
+ Stockfish 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.
+
+ Stockfish 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <ostream>
+
+#include "misc.h"
+#include "search.h"
+#include "thread.h"
+#include "tt.h"
+#include "uci.h"
+#include "syzygy/tbprobe.h"
+
+using std::string;
+
+UCI::OptionsMap Options; // Global object
+
+namespace UCI {
+
+/// 'On change' actions, triggered by an option's value change
+void on_clear_hash(const Option&) { Search::clear(); }
+void on_hash_size(const Option& o) { TT.resize(o); }
+void on_logger(const Option& o) { start_logger(o); }
+void on_threads(const Option& o) { Threads.set(o); }
+void on_tb_path(const Option& o) { Tablebases::init(o); }
+
+
+/// Our case insensitive less() function as required by UCI protocol
+bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
+
+ return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
+ [](char c1, char c2) { return tolower(c1) < tolower(c2); });
+}
+
+
+/// init() initializes the UCI options to their hard-coded default values
+
+void init(OptionsMap& o) {
+
+ // at most 2^32 clusters.
+ const int MaxHashMB = Is64Bit ? 131072 : 2048;
+
+ o["Debug Log File"] << Option("", on_logger);
+ o["Contempt"] << Option(20, -100, 100);
+ o["Threads"] << Option(1, 1, 512, on_threads);
+ o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
+ o["Clear Hash"] << Option(on_clear_hash);
+ o["Ponder"] << Option(false);
+ o["MultiPV"] << Option(1, 1, 500);
+ o["Skill Level"] << Option(20, 0, 20);
+ o["Move Overhead"] << Option(30, 0, 5000);
+ o["Minimum Thinking Time"] << Option(20, 0, 5000);
+ o["Slow Mover"] << Option(89, 10, 1000);
+ o["nodestime"] << Option(0, 0, 10000);
+ o["UCI_Chess960"] << Option(false);
+ o["SyzygyPath"] << Option("<empty>", on_tb_path);
+ o["SyzygyProbeDepth"] << Option(1, 1, 100);
+ o["Syzygy50MoveRule"] << Option(true);
+ o["SyzygyProbeLimit"] << Option(6, 0, 6);
+}
+
+
+/// operator<<() is used to print all the options default values in chronological
+/// insertion order (the idx field) and in the format defined by the UCI protocol.
+
+std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
+
+ for (size_t idx = 0; idx < om.size(); ++idx)
+ for (const auto& it : om)
+ if (it.second.idx == idx)
+ {
+ const Option& o = it.second;
+ os << "\noption name " << it.first << " type " << o.type;
+
+ if (o.type != "button")
+ os << " default " << o.defaultValue;
+
+ if (o.type == "spin")
+ os << " min " << o.min << " max " << o.max;
+
+ break;
+ }
+
+ return os;
+}
+
+
+/// Option class constructors and conversion operators
+
+Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
+{ defaultValue = currentValue = v; }
+
+Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f)
+{ defaultValue = currentValue = (v ? "true" : "false"); }
+
+Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
+{}
+
+Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
+{ defaultValue = currentValue = std::to_string(v); }
+
+Option::operator int() const {
+ assert(type == "check" || type == "spin");
+ return (type == "spin" ? stoi(currentValue) : currentValue == "true");
+}
+
+Option::operator std::string() const {
+ assert(type == "string");
+ return currentValue;
+}
+
+
+/// operator<<() inits options and assigns idx in the correct printing order
+
+void Option::operator<<(const Option& o) {
+
+ static size_t insert_order = 0;
+
+ *this = o;
+ idx = insert_order++;
+}
+
+
+/// operator=() updates currentValue and triggers on_change() action. It's up to
+/// the GUI to check for option's limits, but we could receive the new value from
+/// the user by console window, so let's check the bounds anyway.
+
+Option& Option::operator=(const string& v) {
+
+ assert(!type.empty());
+
+ if ( (type != "button" && v.empty())
+ || (type == "check" && v != "true" && v != "false")
+ || (type == "spin" && (stoi(v) < min || stoi(v) > max)))
+ return *this;
+
+ if (type != "button")
+ currentValue = v;
+
+ if (on_change)
+ on_change(*this);
+
+ return *this;
+}
+
+} // namespace UCI
--- /dev/null
+#!/bin/bash
+# check for errors under valgrind or sanitizers.
+
+error()
+{
+ echo "instrumented testing failed on line $1"
+ exit 1
+}
+trap 'error ${LINENO}' ERR
+
+# define suitable post and prefixes for testing options
+case $1 in
+ --valgrind)
+ echo "valgrind testing started"
+ prefix=''
+ exeprefix='valgrind --error-exitcode=42'
+ postfix='1>/dev/null'
+ threads="1"
+ ;;
+ --valgrind-thread)
+ echo "valgrind-thread testing started"
+ prefix=''
+ exeprefix='valgrind --error-exitcode=42'
+ postfix='1>/dev/null'
+ threads="2"
+ ;;
+ --sanitizer-undefined)
+ echo "sanitizer-undefined testing started"
+ prefix='!'
+ exeprefix=''
+ postfix='2>&1 | grep "runtime error:"'
+ threads="1"
+ ;;
+ --sanitizer-thread)
+ echo "sanitizer-thread testing started"
+ prefix='!'
+ exeprefix=''
+ postfix='2>&1 | grep "WARNING: ThreadSanitizer:"'
+ threads="2"
+
+cat << EOF > tsan.supp
+race:TTEntry::move
+race:TTEntry::depth
+race:TTEntry::bound
+race:TTEntry::save
+race:TTEntry::value
+race:TTEntry::eval
+
+race:TranspositionTable::probe
+race:TranspositionTable::hashfull
+
+EOF
+
+ export TSAN_OPTIONS="suppressions=./tsan.supp"
+
+ ;;
+ *)
+ echo "unknown testing started"
+ prefix=''
+ exeprefix=''
+ postfix=''
+ threads="1"
+ ;;
+esac
+
+# simple command line testing
+for args in "eval" \
+ "go nodes 1000" \
+ "go depth 10" \
+ "go movetime 1000" \
+ "go wtime 8000 btime 8000 winc 500 binc 500" \
+ "bench 128 $threads 10 default depth"
+do
+
+ echo "$prefix $exeprefix ./stockfish $args $postfix"
+ eval "$prefix $exeprefix ./stockfish $args $postfix"
+
+done
+
+# more general testing, following an uci protocol exchange
+cat << EOF > game.exp
+ set timeout 10
+ spawn $exeprefix ./stockfish
+
+ send "uci\n"
+ expect "uciok"
+
+ send "setoption name Threads value $threads\n"
+
+ send "ucinewgame\n"
+ send "position startpos\n"
+ send "go nodes 1000\n"
+ expect "bestmove"
+
+ send "position startpos moves e2e4 e7e6\n"
+ send "go nodes 1000\n"
+ expect "bestmove"
+
+ send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
+ send "go depth 30\n"
+ expect "bestmove"
+
+ send "quit\n"
+ expect eof
+
+ # return error code of the spawned program, useful for valgrind
+ lassign [wait] pid spawnid os_error_flag value
+ exit \$value
+EOF
+
+for exps in game.exp
+do
+
+ echo "$prefix expect $exps $postfix"
+ eval "$prefix expect $exps $postfix"
+
+ rm $exps
+
+done
+
+rm -f tsan.supp
+
+echo "instrumented testing OK"
--- /dev/null
+#!/bin/bash
+# verify perft numbers (positions from https://chessprogramming.wikispaces.com/Perft+Results)
+
+error()
+{
+ echo "perft testing failed on line $1"
+ exit 1
+}
+trap 'error ${LINENO}' ERR
+
+echo "perft testing started"
+
+cat << EOF > perft.exp
+ set timeout 10
+ lassign \$argv pos depth result
+ spawn ./stockfish
+ send "position \$pos\\ngo perft \$depth\\n"
+ expect "Nodes searched? \$result" {} timeout {exit 1}
+ send "quit\\n"
+ expect eof
+EOF
+
+expect perft.exp startpos 5 4865609 > /dev/null
+expect perft.exp "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 > /dev/null
+expect perft.exp "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 6 11030083 > /dev/null
+expect perft.exp "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 5 15833292 > /dev/null
+expect perft.exp "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 > /dev/null
+expect perft.exp "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 > /dev/null
+
+rm perft.exp
+
+echo "perft testing OK"
--- /dev/null
+#!/bin/bash
+# verify reproducible search
+
+error()
+{
+ echo "reprosearch testing failed on line $1"
+ exit 1
+}
+trap 'error ${LINENO}' ERR
+
+echo "reprosearch testing started"
+
+# repeat two short games, separated by ucinewgame.
+# with go nodes $nodes they should result in exactly
+# the same node count for each iteration.
+cat << EOF > repeat.exp
+ set timeout 10
+ spawn ./stockfish
+ lassign \$argv nodes
+
+ send "uci\n"
+ expect "uciok"
+
+ send "ucinewgame\n"
+ send "position startpos\n"
+ send "go nodes \$nodes\n"
+ expect "bestmove"
+
+ send "position startpos moves e2e4 e7e6\n"
+ send "go nodes \$nodes\n"
+ expect "bestmove"
+
+ send "ucinewgame\n"
+ send "position startpos\n"
+ send "go nodes \$nodes\n"
+ expect "bestmove"
+
+ send "position startpos moves e2e4 e7e6\n"
+ send "go nodes \$nodes\n"
+ expect "bestmove"
+
+ send "quit\n"
+ expect eof
+EOF
+
+# to increase the likelyhood of finding a non-reproducible case,
+# the allowed number of nodes are varied systematically
+for i in `seq 1 20`
+do
+
+ nodes=$((100*3**i/2**i))
+ echo "reprosearch testing with $nodes nodes"
+
+ # each line should appear exactly an even number of times
+ expect repeat.exp $nodes 2>&1 | grep -o "nodes [0-9]*" | sort | uniq -c | awk '{if ($1%2!=0) exit(1)}'
+
+done
+
+rm repeat.exp
+
+echo "reprosearch testing OK"
--- /dev/null
+#!/bin/bash
+# obtain and optionally verify Bench / signature
+# if no reference is given, the output is deliberately limited to just the signature
+
+error()
+{
+ echo "running bench for signature failed on line $1"
+ exit 1
+}
+trap 'error ${LINENO}' ERR
+
+# obtain
+
+signature=`./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'`
+
+if [ $# -gt 0 ]; then
+ # compare to given reference
+ if [ "$1" != "$signature" ]; then
+ echo "signature mismatch: reference $1 obtained $signature"
+ exit 1
+ else
+ echo "signature OK: $signature"
+ fi
+else
+ # just report signature
+ echo $signature
+fi