pax_global_header00006660000000000000000000000064130060570250014510gustar00rootroot0000000000000052 comment=369eff437cc081eb4b8ab5f519cf3f86b79e87d0 Stockfish-sf_8/000077500000000000000000000000001300605702500136465ustar00rootroot00000000000000Stockfish-sf_8/.travis.yml000066400000000000000000000034361300605702500157650ustar00rootroot00000000000000language: 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'] env: - COMPILER=g++-6 - COMP=gcc - os: linux compiler: clang addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['clang', 'g++-multilib', 'valgrind'] 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: - make clean && make build ARCH=x86-64 && ./stockfish bench 2>&1 >/dev/null | grep 'Nodes searched' | tee bench1 - make clean && make build ARCH=x86-32 && ./stockfish bench 2>&1 >/dev/null | grep 'Nodes searched' | tee bench2 - echo "Checking for same bench numbers..." - diff bench1 bench2 > result - test ! -s result # if valgrind is available check the build is without error, reduce depth to speedup testing, but not too shallow to catch more cases. - if [ -x "$(command -v valgrind )" ] ; then make clean && make ARCH=x86-64 debug=yes build && valgrind --error-exitcode=42 ./stockfish bench 128 1 10 default depth 1>/dev/null ; fi # use g++-6 as a proxy for having sanitizers ... might need revision as they become available for more recent versions of clang/gcc than trusty provides - if [[ "$COMPILER" == "g++-6" ]]; then make clean && make ARCH=x86-64 sanitize=yes build && ! ./stockfish bench 2>&1 | grep "runtime error:" ; fi Stockfish-sf_8/AUTHORS000066400000000000000000000025121300605702500147160ustar00rootroot00000000000000# Generated with git shortlog -sn | cut -c8-', which sorts by commits (manually ordered the first four authors) Tord Romstad Marco Costalba Joona Kiiski Gary Linscott lucasart mstembera Lucas Braesch Stefan Geschwentner Reuven Peleg Chris Caino joergoster VoyagerOne Jean-Francois Romang homoSapiensSapiens Alain SAVARD Arjun Temurnikar Stéphane Nicolet Uri Blass jundery Ralph Stößer Ajith Leonid Pechenik Stefano80 Tom Vijlbrief hxim snicolet Daylen Yang Henri Wiechers Jonathan Calovski mbootsector David Zar Eelco de Groot Jerry Donald Joerg Oster Jörg Oster Ryan Schmitt mcostalba Alexander Kure Dan Schmidt H. Felix Wittmann Joseph R. Prostko Justin Blanchard Linus Arver NicklasPersson Rodrigo Exterckötter Tjäder Ron Britvich Ronald de Man RyanTaker Vince Negri ceebo jhellis3 ppigazzini shane31 Andy Duplain Auguste Pop Balint Pfliegel Chris Cain DU-jdto Dariusz Orzechowski DiscanX Ernesto Gatti Gregor Cramer Guenther Demetz Hiraoka Takuya Hongzhi Cheng Joseph Hellis Kelly Wilson Ken T Takusagawa Kojirion Luca Brivio Matt Sullivan Matthew Lai Matthew Sullivan Michel Van den Bergh Mysseno Oskar Werkelin Ahlin Pablo Vazquez Pascal Romaret Ralph Stoesser Ralph Stößer Raminder Singh Richard Lloyd Ryan Takker Stephane Nicolet Thanar2 absimaldata braich gguliash kinderchocolate loco-loco pellanda renouve sf-x thaspel unknown uriblass Stockfish-sf_8/Copying.txt000066400000000000000000001057551300605702500160340ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . Stockfish-sf_8/Readme.md000066400000000000000000000121521300605702500153660ustar00rootroot00000000000000### Overview [![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) 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 128 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. ### 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*. Stockfish-sf_8/appveyor.yml000066400000000000000000000023251300605702500162400ustar00rootroot00000000000000version: 1.0.{build} clone_depth: 5 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 - Any CPU # build Configuration, i.e. Debug, Release, etc. configuration: Debug 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: - cd src - echo project (Stockfish) >> CMakeLists.txt - echo add_executable(stockfish benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp >> CMakeLists.txt - echo main.cpp material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp >> CMakeLists.txt - echo search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp syzygy/tbprobe.cpp) >> CMakeLists.txt - echo set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src) >> CMakeLists.txt # - echo target_compile_options(stockfish PUBLIC "/Ox") >> CMakeLists.txt build_script: - cmake -G "Visual Studio 14 2015 Win64" . - cmake --build . before_test: - cd Debug - stockfish.exe bench > null Stockfish-sf_8/src/000077500000000000000000000000001300605702500144355ustar00rootroot00000000000000Stockfish-sf_8/src/Makefile000066400000000000000000000324671300605702500161110ustar00rootroot00000000000000# 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-2016 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 . ### ========================================================================== ### 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 = yes/no --- (-fsanitize ) --- enable undefined behavior 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 -fno-rtti -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) endif else CXXFLAGS += -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 ifeq ($(ARCH),armv7) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif ifeq ($(KERNEL),Darwin) CXXFLAGS += -stdlib=libc++ DEPENDFLAGS += -stdlib=libc++ endif endif ifeq ($(comp),icc) profile_prepare = icc-profile-prepare profile_make = icc-profile-make profile_use = icc-profile-use profile_clean = icc-profile-clean else profile_prepare = gcc-profile-prepare profile_make = gcc-profile-make profile_use = gcc-profile-use profile_clean = gcc-profile-clean 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 ifeq ($(sanitize),yes) CXXFLAGS += -g3 -fsanitize=undefined 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) ifeq ($(pext),no) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif 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: build profile-build build: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all profile-build: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity @echo "" @echo "Step 0/4. Preparing for profile build." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) @echo "" @echo "Step 1/4. Building executable for benchmark ..." @touch *.cpp *.h syzygy/*.cpp syzygy/*.h $(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 final executable ..." @touch *.cpp *.h syzygy/*.cpp syzygy/*.h $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) @echo "" @echo "Step 4/4. Deleting profile data ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) strip: strip $(EXE) install: -mkdir -p -m 755 $(BINDIR) -cp $(EXE) $(BINDIR) -strip $(BINDIR)/$(EXE) clean: $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda ./syzygy/*.o ./syzygy/*.gcda default: help ### ========================================================================== ### Section 5. Private targets ### ========================================================================== all: $(EXE) .depend config-sanity: @echo "" @echo "Config:" @echo "debug: '$(debug)'" @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 "$(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) gcc-profile-prepare: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean 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 gcc-profile-clean: @rm -rf *.gcda *.gcno syzygy/*.gcda syzygy/*.gcno bench.txt icc-profile-prepare: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean @mkdir profdir icc-profile-make: $(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 icc-profile-clean: @rm -rf profdir bench.txt .depend: -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null -include .depend Stockfish-sf_8/src/benchmark.cpp000066400000000000000000000142251300605702500170770ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #include #include "misc.h" #include "position.h" #include "search.h" #include "thread.h" #include "uci.h" using namespace std; namespace { const vector Defaults = { "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", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "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", "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 "8/8/8/8/8/6k1/6p1/6K1 w - -", "5k2/5P2/5K2/8/8/8/8/8 b - -", "8/8/8/8/8/4k3/4p3/4K3 w - -", "8/8/8/8/8/5K2/8/3Q1k2 b - -", "7k/7P/6K1/8/3B4/8/8/8 b - -" }; } // namespace /// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// of positions for a given limit each. There are five parameters: the /// transposition table size, the number of search threads that should /// be used, the limit value spent for each position (optional, default is /// depth 13), an optional file name where to look for positions in FEN /// format (defaults are the positions defined above) and the type of the /// limit value: depth (default), time in millisecs or number of nodes. void benchmark(const Position& current, istream& is) { string token; vector fens; Search::LimitsType limits; // 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"; Options["Hash"] = ttSize; Options["Threads"] = threads; Search::clear(); if (limitType == "time") limits.movetime = stoi(limit); // movetime is in millisecs else if (limitType == "nodes") limits.nodes = stoi(limit); else if (limitType == "mate") limits.mate = stoi(limit); else limits.depth = stoi(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; return; } while (getline(file, fen)) if (!fen.empty()) fens.push_back(fen); file.close(); } uint64_t nodes = 0; TimePoint elapsed = now(); Position pos; for (size_t i = 0; i < fens.size(); ++i) { StateListPtr states(new std::deque(1)); pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main()); cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; if (limitType == "perft") nodes += Search::perft(pos, limits.depth * ONE_PLY); else { limits.startTime = now(); Threads.start_thinking(pos, states, limits); Threads.main()->wait_for_search_finished(); nodes += Threads.nodes_searched(); } } 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; } Stockfish-sf_8/src/bitbase.cpp000066400000000000000000000141341300605702500165550ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #include #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& db) { return us == WHITE ? classify(db) : classify(db); } template Result classify(const std::vector& 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 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_7 - Rank((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 && (StepAttacksBB[PAWN][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 || (StepAttacksBB[KING][ksq[us]] & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn else if ( us == BLACK && ( !(StepAttacksBB[KING][ksq[us]] & ~(StepAttacksBB[KING][ksq[~us]] | StepAttacksBB[PAWN][psq])) || (StepAttacksBB[KING][ksq[us]] & psq & ~StepAttacksBB[KING][ksq[~us]]))) result = DRAW; // Position will be classified later else result = UNKNOWN; } template Result KPKPosition::classify(const std::vector& 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 = StepAttacksBB[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 Stockfish-sf_8/src/bitboard.cpp000066400000000000000000000253231300605702500167340ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include "bitboard.h" #include "misc.h" uint8_t PopCnt16[1 << 16]; int SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard RookMasks [SQUARE_NB]; Bitboard RookMagics [SQUARE_NB]; Bitboard* RookAttacks[SQUARE_NB]; unsigned RookShifts [SQUARE_NB]; Bitboard BishopMasks [SQUARE_NB]; Bitboard BishopMagics [SQUARE_NB]; Bitboard* BishopAttacks[SQUARE_NB]; unsigned BishopShifts [SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard FileBB[FILE_NB]; Bitboard RankBB[RANK_NB]; Bitboard AdjacentFilesBB[FILE_NB]; Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][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 typedef unsigned (Fn)(Square, Bitboard); void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); // 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) InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); for (Color c = WHITE; c <= BLACK; ++c) for (Square s = SQ_A1; s <= SQ_H8; ++s) { ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; PassedPawnMask[c][s] = ForwardBB[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(s1, s2), distance(s1, s2)); DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2; } int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) for (Square s = SQ_A1; s <= SQ_H8; ++s) for (int i = 0; steps[pt][i]; ++i) { Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]); if (is_ok(to) && distance(s, to) < 3) StepAttacksBB[make_piece(c, pt)][s] |= to; } Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST }; Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index); init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); for (Piece pc = W_BISHOP; pc <= W_ROOK; ++pc) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) { if (!(PseudoAttacks[pc][s1] & s2)) continue; LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2; BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[s1]); } } } namespace { Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) { Bitboard attack = 0; for (int i = 0; i < 4; ++i) for (Square s = sq + deltas[i]; is_ok(s) && distance(s, s - deltas[i]) == 1; s += deltas[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[], Bitboard* attacks[], Bitboard magics[], Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { 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 age[4096] = {0}, current = 0, i, size; // attacks[s] is a pointer to the beginning of the attacks table for square 's' attacks[SQ_A1] = table; 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. masks[s] = sliding_attack(deltas, s, 0) & ~edges; shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]); // 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(deltas, s, b); if (HasPext) attacks[s][pext(b, masks[s])] = reference[size]; size++; b = (b - masks[s]) & masks[s]; } while (b); // Set the offset for the table of the next square. We have individual // table sizes for each square with "Fancy Magic Bitboards". if (s < SQ_H8) attacks[s + 1] = attacks[s] + size; 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. do { do magics[s] = rng.sparse_rand(); while (popcount((magics[s] * masks[s]) >> 56) < 6); // 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. for (++current, i = 0; i < size; ++i) { unsigned idx = index(s, occupancy[i]); if (age[idx] < current) { age[idx] = current; attacks[s][idx] = reference[i]; } else if (attacks[s][idx] != reference[i]) break; } } while (i < size); } } } Stockfish-sf_8/src/bitboard.h000066400000000000000000000234611300605702500164020ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED #include #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 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 InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard ForwardBB[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]; /// 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]; } inline 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 inline 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]; } /// in_front_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, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2. inline Bitboard in_front_bb(Color c, Rank r) { return InFrontBB[c][r]; } /// forward_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: /// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) inline Bitboard forward_bb(Color c, Square s) { return ForwardBB[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] = in_front_bb(c, s) & adjacent_files_bb(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_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 inline int distance(T x, T y) { return x < y ? y - x : x - y; } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } template inline int distance(T2 x, T2 y); template<> inline int distance(Square x, Square y) { return distance(file_of(x), file_of(y)); } template<> inline int distance(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'. The helper magic_index() /// looks up the index using the 'magic bitboards' approach. template inline unsigned magic_index(Square s, Bitboard occupied) { extern Bitboard RookMasks[SQUARE_NB]; extern Bitboard RookMagics[SQUARE_NB]; extern unsigned RookShifts[SQUARE_NB]; extern Bitboard BishopMasks[SQUARE_NB]; extern Bitboard BishopMagics[SQUARE_NB]; extern unsigned BishopShifts[SQUARE_NB]; Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks; Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics; unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts; if (HasPext) return unsigned(pext(occupied, Masks[s])); if (Is64Bit) return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]); unsigned lo = unsigned(occupied) & unsigned(Masks[s]); unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32); return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s]; } template inline Bitboard attacks_bb(Square s, Bitboard occupied) { extern Bitboard* RookAttacks[SQUARE_NB]; extern Bitboard* BishopAttacks[SQUARE_NB]; return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index(s, occupied)]; } inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { switch (type_of(pc)) { case BISHOP: return attacks_bb(s, occupied); case ROOK : return attacks_bb(s, occupied); case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); default : return StepAttacksBB[pc][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 Stockfish-sf_8/src/endgame.cpp000066400000000000000000000761271300605702500165560ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #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(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(strongSide) == 1); if (file_of(pos.square(strongSide)) >= FILE_E) sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 if (strongSide == BLACK) sq = ~sq; return sq; } // Get the material key of Position out of the given endgame key code // like "KBPKN". The trick here is to first forge an ad-hoc FEN string // and then let a Position object do the work for us. Key key(const string& code, Color c) { 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 fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10"; StateInfo st; return Position().set(fen, false, &st, nullptr).material_key(); } } // namespace /// Endgames members definitions Endgames::Endgames() { add("KPK"); add("KNNK"); add("KBNK"); add("KRKP"); add("KRKB"); add("KRKN"); add("KQKP"); add("KQKR"); add("KNPK"); add("KNPKB"); add("KRPKR"); add("KRPKB"); add("KBPKB"); add("KBPKN"); add("KBPPKB"); add("KRPPKRP"); } template void Endgames::add(const string& code) { map()[key(code, WHITE)] = std::unique_ptr>(new Endgame(WHITE)); map()[key(code, BLACK)] = std::unique_ptr>(new Endgame(BLACK)); } /// 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::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(pos).size()) return VALUE_DRAW; Square winnerKSq = pos.square(strongSide); Square loserKSq = pos.square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg + PushToEdges[loserKSq] + PushClose[distance(winnerKSq, loserKSq)]; if ( pos.count(strongSide) || pos.count(strongSide) ||(pos.count(strongSide) && pos.count(strongSide)) ||(pos.count(strongSide) > 1 && opposite_colors(pos.squares(strongSide)[0], pos.squares(strongSide)[1]))) 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::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(strongSide); Square loserKSq = pos.square(weakSide); Square bishopSq = pos.square(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::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(strongSide)); Square bksq = normalize(pos, strongSide, pos.square(weakSide)); Square psq = normalize(pos, strongSide, pos.square(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::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(strongSide)); Square bksq = relative_square(strongSide, pos.square(weakSide)); Square rsq = relative_square(strongSide, pos.square(strongSide)); Square psq = relative_square(strongSide, pos.square(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::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(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); Square bksq = pos.square(weakSide); Square bnsq = pos.square(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); Square winnerKSq = pos.square(strongSide); Square loserKSq = pos.square(weakSide); Square pawnSq = pos.square(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0)); Square winnerKSq = pos.square(strongSide); Square loserKSq = pos.square(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::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::operator()(const Position& pos) const { assert(pos.non_pawn_material(strongSide) == BishopValueMg); assert(pos.count(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(strongSide); Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8)); Square kingSq = pos.square(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(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(strongSide); Square weakKingSq = pos.square(weakSide); Square bishopSq = pos.square(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(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(pos.count(weakSide) == 1); assert(pos.count(weakSide) >= 1); Square kingSq = pos.square(weakSide); Square rsq = pos.square(weakSide); if ( relative_rank(weakSide, kingSq) <= RANK_2 && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 && relative_rank(weakSide, rsq) == RANK_3 && ( pos.pieces(weakSide, PAWN) & pos.attacks_from(kingSq) & pos.attacks_from(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::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(strongSide)); Square bksq = normalize(pos, strongSide, pos.square(weakSide)); Square wrsq = normalize(pos, strongSide, pos.square(strongSide)); Square wpsq = normalize(pos, strongSide, pos.square(strongSide)); Square brsq = normalize(pos, strongSide, pos.square(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(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(bksq, wpsq) == 1 && distance(wksq, bksq) > 2) return ScaleFactor(24 - 2 * distance(wksq, bksq)); } return SCALE_FACTOR_NONE; } template<> ScaleFactor Endgame::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(weakSide); Square bsq = pos.square(weakSide); Square psq = pos.square(strongSide); Rank rk = relative_rank(strongSide, psq); Square 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(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(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, weakSide, RookValueMg, 1)); Square wpsq1 = pos.squares(strongSide)[0]; Square wpsq2 = pos.squares(strongSide)[1]; Square bksq = pos.square(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(bksq, wpsq1) <= 1 && distance(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::operator()(const Position& pos) const { assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); assert(pos.count(strongSide) >= 2); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); Square ksq = pos.square(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 & ~in_front_bb(weakSide, rank_of(ksq))) && !((pawns & ~FileABB) && (pawns & ~FileHBB)) && distance(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Square pawnSq = pos.square(strongSide); Square strongBishopSq = pos.square(strongSide); Square weakBishopSq = pos.square(weakSide); Square weakKingSq = pos.square(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; else { Bitboard path = forward_bb(strongSide, pawnSq); if (path & pos.pieces(weakSide, KING)) return SCALE_FACTOR_DRAW; if ( (pos.attacks_from(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Square wbsq = pos.square(strongSide); Square bbsq = pos.square(weakSide); if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; Square ksq = pos.square(weakSide); Square psq1 = pos.squares(strongSide)[0]; Square psq2 = pos.squares(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(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(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(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::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); Square pawnSq = pos.square(strongSide); Square strongBishopSq = pos.square(strongSide); Square weakKingSq = pos.square(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::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(strongSide)); Square weakKingSq = normalize(pos, strongSide, pos.square(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::operator()(const Position& pos) const { Square pawnSq = pos.square(strongSide); Square bishopSq = pos.square(weakSide); Square weakKingSq = pos.square(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_bb(strongSide, pawnSq) & pos.attacks_from(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::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(strongSide)); Square bksq = normalize(pos, strongSide, pos.square(weakSide)); Square psq = normalize(pos, strongSide, pos.square(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; } Stockfish-sf_8/src/endgame.h000066400000000000000000000063231300605702500162120ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED #include #include #include #include #include #include "position.h" #include "types.h" /// EndgameType lists all supported endgames enum EndgameType { // 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 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 using eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; /// Base and derived templates for endgame evaluation and scaling functions template struct EndgameBase { virtual ~EndgameBase() = default; virtual Color strong_side() const = 0; virtual T operator()(const Position&) const = 0; }; template> struct Endgame : public EndgameBase { explicit Endgame(Color c) : strongSide(c), weakSide(~c) {} Color strong_side() const { return strongSide; } T operator()(const Position&) const; private: Color strongSide, weakSide; }; /// 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 using Map = std::map>>; template> void add(const std::string& code); template Map& map() { return std::get::value>(maps); } std::pair, Map> maps; public: Endgames(); template EndgameBase* probe(Key key) { return map().count(key) ? map()[key].get() : nullptr; } }; #endif // #ifndef ENDGAME_H_INCLUDED Stockfish-sf_8/src/evaluate.cpp000066400000000000000000001115241300605702500167530ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include // For std::memset #include #include #include "bitboard.h" #include "evaluate.h" #include "material.h" #include "pawns.h" namespace { namespace Trace { enum Term { // The first 8 entries are for PieceType MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, 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 == 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; // Struct EvalInfo contains various information computed and collected // by the evaluation functions. struct EvalInfo { // attackedBy[color][piece type] is a bitboard representing all squares // attacked by a given color and piece type (can be also 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 the three (or two, for a king on an edge file) // squares two ranks in front of the king. For instance, if black's king // is on g8, 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]; Bitboard pinnedPieces[COLOR_NB]; Material::Entry* me; Pawns::Entry* pi; }; #define V(v) Value(v) #define S(mg, eg) make_score(mg, eg) // MobilityBonus[PieceType][attacked] contains bonuses for middle and end // game, indexed by piece type and number of attacked squares in the MobilityArea. const Score MobilityBonus[][32] = { {}, {}, { S(-75,-76), S(-56,-54), S( -9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights S( 22, 26), S( 30, 28), S( 36, 29) }, { S(-48,-58), S(-21,-19), S( 16, -2), S( 26, 12), S( 37, 22), S( 51, 42), // Bishops S( 54, 54), S( 63, 58), S( 65, 63), S( 71, 70), S( 79, 74), S( 81, 86), S( 92, 90), S( 97, 94) }, { S(-56,-78), S(-25,-18), S(-11, 26), S( -5, 55), S( -4, 70), S( -1, 81), // Rooks S( 8,109), S( 14,120), S( 21,128), S( 23,143), S( 31,154), S( 32,160), S( 43,165), S( 49,168), S( 59,169) }, { S(-40,-35), S(-25,-12), S( 2, 7), S( 4, 19), S( 14, 37), S( 24, 55), // Queens S( 25, 62), S( 40, 76), S( 43, 79), S( 47, 87), S( 54, 94), S( 56,102), S( 60,111), S( 70,116), S( 72,118), S( 73,122), S( 75,128), S( 77,130), S( 85,133), S( 94,136), S( 99,140), S(108,157), S(112,158), S(113,161), S(118,174), S(119,177), S(123,191), S(128,199) } }; // Outpost[knight/bishop][supported by pawn] contains bonuses for knights and // bishops outposts, bigger if outpost piece is supported by a pawn. const Score Outpost[][2] = { { S(43,11), S(65,20) }, // Knights { S(20, 3), S(29, 8) } // Bishops }; // ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for // knights and bishops which can reach an outpost square in one move, bigger // if outpost square is supported by a pawn. const Score ReachableOutpost[][2] = { { S(21, 5), S(35, 8) }, // Knights { S( 8, 0), S(14, 4) } // Bishops }; // RookOnFile[semiopen/open] contains bonuses for each rook when there is no // friendly pawn on the rook file. const Score RookOnFile[2] = { S(20, 7), S(45, 20) }; // ThreatBySafePawn[PieceType] contains bonuses according to which piece // type is attacked by a pawn which is protected or is not attacked. const Score ThreatBySafePawn[PIECE_TYPE_NB] = { S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) }; // Threat[by minor/by rook][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 Threat[][PIECE_TYPE_NB] = { { S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72,107), S(48,118) }, // by Minor { S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // by Rook }; // ThreatByKing[on one/on many] contains bonuses for King attacks on // pawns or pieces which are not pawn-defended. const Score ThreatByKing[2] = { 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(5), V( 5), V(31), V(73), V(166), V(252) }, { 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) }; // Assorted bonuses and penalties used by evaluation const Score MinorBehindPawn = S(16, 0); const Score BishopPawns = S( 8, 12); const Score RookOnPawn = S( 8, 24); const Score TrappedRook = S(92, 0); const Score CloseEnemies = S( 7, 0); const Score SafeCheck = S(20, 20); const Score OtherCheck = S(10, 10); const Score ThreatByHangingPawn = S(71, 61); const Score LooseEnemies = S( 0, 25); const Score WeakQueen = S(35, 0); const Score Hanging = S(48, 27); const Score ThreatByPawnPush = S(38, 22); const Score Unstoppable = S( 0, 20); const Score PawnlessFlank = S(20, 80); const Score HinderPassedPawn = S( 7, 0); // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // happen in Chess960 games. const 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 QueenContactCheck = 997; const int QueenCheck = 695; const int RookCheck = 638; const int BishopCheck = 538; const int KnightCheck = 874; // eval_init() initializes king and attack bitboards for a given color // adding pawn attacks. To be done at the beginning of the evaluation. template void eval_init(const Position& pos, EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Square Down = (Us == WHITE ? SOUTH : NORTH); ei.pinnedPieces[Us] = pos.pinned_pieces(Us); Bitboard b = ei.attackedBy[Them][KING]; ei.attackedBy[Them][ALL_PIECES] |= b; ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy2[Us] = ei.attackedBy[Us][PAWN] & ei.attackedBy[Us][KING]; // Init king safety tables only if we are going to use them if (pos.non_pawn_material(Us) >= QueenValueMg) { ei.kingRing[Them] = b | shift(b); b &= ei.attackedBy[Us][PAWN]; ei.kingAttackersCount[Us] = popcount(b); ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; } else ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; } // evaluate_pieces() assigns bonuses and penalties to the pieces of a given // color and type. template Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, const Bitboard* mobilityArea) { Bitboard b, bb; Square s; Score score = SCORE_ZERO; const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1)); const Color Them = (Us == WHITE ? BLACK : WHITE); const Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB : Rank5BB | Rank4BB | Rank3BB); const Square* pl = pos.squares(Us); ei.attackedBy[Us][Pt] = 0; while ((s = *pl++) != SQ_NONE) { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(Us, QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) : pos.attacks_from(s); if (ei.pinnedPieces[Us] & s) b &= LineBB[pos.square(Us)][s]; ei.attackedBy2[Us] |= ei.attackedBy[Us][ALL_PIECES] & b; ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b; if (b & ei.kingRing[Them]) { ei.kingAttackersCount[Us]++; ei.kingAttackersWeight[Us] += KingAttackWeights[Pt]; ei.kingAdjacentZoneAttacksCount[Us] += popcount(b & ei.attackedBy[Them][KING]); } if (Pt == QUEEN) b &= ~( ei.attackedBy[Them][KNIGHT] | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt][mob]; if (Pt == BISHOP || Pt == KNIGHT) { // Bonus for outpost squares bb = OutpostRanks & ~ei.pi->pawn_attacks_span(Them); if (bb & s) score += Outpost[Pt == BISHOP][!!(ei.attackedBy[Us][PAWN] & s)]; else { bb &= b & ~pos.pieces(Us); if (bb) score += ReachableOutpost[Pt == BISHOP][!!(ei.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; // Penalty for pawns on the same color square as the bishop if (Pt == BISHOP) score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); // 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))) { Square 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 (ei.pi->semiopen_file(Us, file_of(s))) score += RookOnFile[!!ei.pi->semiopen_file(Them, file_of(s))]; // Penalize when trapped by the king, even more if the king cannot castle else if (mob <= 3) { Square ksq = pos.square(Us); if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) && !ei.pi->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 (DoTrace) Trace::add(Pt, Us, score); // Recursively call evaluate_pieces() of next piece type until KING is excluded return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } template<> Score evaluate_pieces(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; } template<> Score evaluate_pieces< true, WHITE, KING>(const Position&, EvalInfo&, Score*, const Bitboard*) { return SCORE_ZERO; } // evaluate_king() assigns bonuses and penalties to a king of a given color const Bitboard WhiteCamp = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB; const Bitboard BlackCamp = Rank8BB | Rank7BB | Rank6BB | Rank5BB | Rank4BB; const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; const Bitboard KingFlank[COLOR_NB][FILE_NB] = { { QueenSide & WhiteCamp, QueenSide & WhiteCamp, QueenSide & WhiteCamp, CenterFiles & WhiteCamp, CenterFiles & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp }, { QueenSide & BlackCamp, QueenSide & BlackCamp, QueenSide & BlackCamp, CenterFiles & BlackCamp, CenterFiles & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp }, }; template Score evaluate_king(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Square Up = (Us == WHITE ? NORTH : SOUTH); Bitboard undefended, b, b1, b2, safe, other; int kingDanger; const Square ksq = pos.square(Us); // King shelter and enemy pawns storm Score score = ei.pi->king_safety(pos, ksq); // Main king safety evaluation if (ei.kingAttackersCount[Them]) { // Find the attacked squares which are defended only by the king... undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING] & ~ei.attackedBy2[Us]; // ... and those which are not defended at all in the larger king ring b = ei.attackedBy[Them][ALL_PIECES] & ~ei.attackedBy[Us][ALL_PIECES] & ei.kingRing[Us] & ~pos.pieces(Them); // Initialize the 'kingDanger' variable, which will be transformed // later into a king danger score. The initial value is based on the // number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king and the quality of // the pawn shelter (current 'score' value). kingDanger = std::min(807, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + 101 * ei.kingAdjacentZoneAttacksCount[Them] + 235 * popcount(undefended) + 134 * (popcount(b) + !!ei.pinnedPieces[Us]) - 717 * !pos.count(Them) - 7 * mg_value(score) / 5 - 5; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king reachable by the enemy queen... b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); // ...and keep squares supported by another enemy piece kingDanger += QueenContactCheck * popcount(b & ei.attackedBy2[Them]); // Analyse the safe enemy's checks which are possible on next move... safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); // ... and some other potential checks, only requiring the square to be // safe from pawn-attacks, and not being occupied by a blocked pawn. other = ~( ei.attackedBy[Us][PAWN] | (pos.pieces(Them, PAWN) & shift(pos.pieces(PAWN)))); b1 = pos.attacks_from(ksq); b2 = pos.attacks_from(ksq); // Enemy queen safe checks if ((b1 | b2) & ei.attackedBy[Them][QUEEN] & safe) kingDanger += QueenCheck, score -= SafeCheck; // For other pieces, also consider the square safe if attacked twice, // and only defended by a queen. safe |= ei.attackedBy2[Them] & ~(ei.attackedBy2[Us] | pos.pieces(Them)) & ei.attackedBy[Us][QUEEN]; // Enemy rooks safe and other checks if (b1 & ei.attackedBy[Them][ROOK] & safe) kingDanger += RookCheck, score -= SafeCheck; else if (b1 & ei.attackedBy[Them][ROOK] & other) score -= OtherCheck; // Enemy bishops safe and other checks if (b2 & ei.attackedBy[Them][BISHOP] & safe) kingDanger += BishopCheck, score -= SafeCheck; else if (b2 & ei.attackedBy[Them][BISHOP] & other) score -= OtherCheck; // Enemy knights safe and other checks b = pos.attacks_from(ksq) & ei.attackedBy[Them][KNIGHT]; if (b & safe) kingDanger += KnightCheck, score -= SafeCheck; else if (b & other) score -= OtherCheck; // Compute the king danger score and subtract it from the evaluation if (kingDanger > 0) score -= make_score(std::min(kingDanger * kingDanger / 4096, 2 * int(BishopValueMg)), 0); } // King tropism: firstly, find squares that opponent attacks in our king flank File kf = file_of(ksq); b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][kf]; 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 & ei.attackedBy2[Them] & ~ei.attackedBy[Us][PAWN]); score -= CloseEnemies * popcount(b); // Penalty when our king is on a pawnless flank if (!(pos.pieces(PAWN) & (KingFlank[WHITE][kf] | KingFlank[BLACK][kf]))) score -= PawnlessFlank; if (DoTrace) Trace::add(KING, Us, score); return score; } // evaluate_threats() assigns bonuses according to the types of the attacking // and the attacked pieces. template Score evaluate_threats(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Square Up = (Us == WHITE ? NORTH : SOUTH); const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); enum { Minor, Rook }; Bitboard b, weak, defended, safeThreats; Score score = SCORE_ZERO; // Small bonus if the opponent has loose pawns or pieces if ( (pos.pieces(Them) ^ pos.pieces(Them, QUEEN, KING)) & ~(ei.attackedBy[Us][ALL_PIECES] | ei.attackedBy[Them][ALL_PIECES])) score += LooseEnemies; // Non-pawn enemies attacked by a pawn weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Us][PAWN]; if (weak) { b = pos.pieces(Us, PAWN) & ( ~ei.attackedBy[Them][ALL_PIECES] | ei.attackedBy[Us][ALL_PIECES]); safeThreats = (shift(b) | shift(b)) & weak; if (weak ^ safeThreats) score += ThreatByHangingPawn; while (safeThreats) score += ThreatBySafePawn[type_of(pos.piece_on(pop_lsb(&safeThreats)))]; } // Non-pawn enemies defended by a pawn defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Them][PAWN]; // Enemies not defended by a pawn and under our attack weak = pos.pieces(Them) & ~ei.attackedBy[Them][PAWN] & ei.attackedBy[Us][ALL_PIECES]; // Add a bonus according to the kind of attacking pieces if (defended | weak) { b = (defended | weak) & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); while (b) score += Threat[Minor][type_of(pos.piece_on(pop_lsb(&b)))]; b = (pos.pieces(Them, QUEEN) | weak) & ei.attackedBy[Us][ROOK]; while (b) score += Threat[Rook ][type_of(pos.piece_on(pop_lsb(&b)))]; score += Hanging * popcount(weak & ~ei.attackedBy[Them][ALL_PIECES]); b = weak & ei.attackedBy[Us][KING]; if (b) score += ThreatByKing[more_than_one(b)]; } // Bonus if some pawns can safely push and attack an enemy piece b = pos.pieces(Us, PAWN) & ~TRank7BB; b = shift(b | (shift(b & TRank2BB) & ~pos.pieces())); b &= ~pos.pieces() & ~ei.attackedBy[Them][PAWN] & (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]); b = (shift(b) | shift(b)) & pos.pieces(Them) & ~ei.attackedBy[Us][PAWN]; score += ThreatByPawnPush * popcount(b); if (DoTrace) Trace::add(THREAT, Us, score); return score; } // evaluate_passed_pawns() evaluates the passed pawns of the given color template Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; Score score = SCORE_ZERO; b = ei.pi->passed_pawns(Us); while (b) { Square s = pop_lsb(&b); assert(pos.pawn_passed(Us, s)); assert(!(pos.pieces(PAWN) & forward_bb(Us, s))); bb = forward_bb(Us, s) & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); score -= HinderPassedPawn * popcount(bb); int r = relative_rank(Us, s) - RANK_2; int rr = r * (r - 1); Value mbonus = Passed[MG][r], ebonus = Passed[EG][r]; if (rr) { Square blockSq = s + pawn_push(Us); // Adjust bonus based on the king's proximity ebonus += distance(pos.square(Them), blockSq) * 5 * rr - distance(pos.square(Us ), blockSq) * 2 * rr; // If blockSq is not the queening square then consider also a second push if (relative_rank(Us, blockSq) != RANK_8) ebonus -= distance(pos.square(Us), blockSq + pawn_push(Us)) * 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_bb(Us, s); bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); if (!(pos.pieces(Us) & bb)) defendedSquares &= ei.attackedBy[Us][ALL_PIECES]; if (!(pos.pieces(Them) & bb)) unsafeSquares &= ei.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 score += make_score(mbonus, ebonus) + PassedFile[file_of(s)]; } if (DoTrace) Trace::add(PASSED, Us, score); // Add the scores to the middlegame and endgame eval 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 Score evaluate_space(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Bitboard SpaceMask = Us == WHITE ? (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB) : (FileCBB | FileDBB | FileEBB | FileFBB) & (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) & ~ei.attackedBy[Them][PAWN] & (ei.attackedBy[Us][ALL_PIECES] | ~ei.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)); bonus = std::min(16, bonus); int weight = pos.count(Us) - 2 * ei.pi->open_files(); return make_score(bonus * weight * weight / 18, 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. Score evaluate_initiative(const Position& pos, int asymmetry, Value eg) { int kingDistance = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); int pawns = pos.count(WHITE) + pos.count(BLACK); // Compute the initiative bonus for the attacking side int initiative = 8 * (asymmetry + kingDistance - 15) + 12 * pawns; // 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 be divided by more than two. int value = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg / 2)); return make_score(0, value); } // evaluate_scale_factor() computes the scale factor for the winning side ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Value eg) { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; ScaleFactor sf = ei.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 ( ei.me->game_phase() < PHASE_MIDGAME && (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) sf = 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. else sf = 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(strongSide) <= 2 && !pos.pawn_passed(~strongSide, pos.square(~strongSide))) sf = ScaleFactor(37 + 7 * pos.count(strongSide)); } return sf; } } // namespace /// evaluate() is the main evaluation function. It returns a static evaluation /// of the position from the point of view of the side to move. template Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; EvalInfo ei; // Probe the material hash table ei.me = Material::probe(pos); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.me->specialized_eval_exists()) return ei.me->evaluate(pos); // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. Score score = pos.psq_score() + ei.me->imbalance(); // Probe the pawn hash table ei.pi = Pawns::probe(pos); score += ei.pi->pawns_score(); // Initialize attack and king safety bitboards ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0; ei.attackedBy[WHITE][KING] = pos.attacks_from(pos.square(WHITE)); ei.attackedBy[BLACK][KING] = pos.attacks_from(pos.square(BLACK)); eval_init(pos, ei); eval_init(pos, ei); // Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area Bitboard blockedPawns[] = { pos.pieces(WHITE, PAWN) & (shift(pos.pieces()) | Rank2BB | Rank3BB), pos.pieces(BLACK, PAWN) & (shift(pos.pieces()) | Rank7BB | Rank6BB) }; // Do not include in mobility area squares protected by enemy pawns, or occupied // by our blocked pawns or king. Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square(WHITE)), ~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square(BLACK)) }; // Evaluate all pieces but king and pawns score += evaluate_pieces(pos, ei, mobility, mobilityArea); score += mobility[WHITE] - mobility[BLACK]; // Evaluate kings after all other pieces because we need full attack // information when computing the king safety evaluation. score += evaluate_king(pos, ei) - evaluate_king(pos, ei); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(pos, ei) - evaluate_threats(pos, ei); // Evaluate passed pawns, we need full attack information including king score += evaluate_passed_pawns(pos, ei) - evaluate_passed_pawns(pos, ei); // If both sides have only pawns, score for potential unstoppable pawns if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) { Bitboard b; if ((b = ei.pi->passed_pawns(WHITE)) != 0) score += Unstoppable * int(relative_rank(WHITE, frontmost_sq(WHITE, b))); if ((b = ei.pi->passed_pawns(BLACK)) != 0) score -= Unstoppable * int(relative_rank(BLACK, frontmost_sq(BLACK, b))); } // Evaluate space for both sides, only during opening if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222) score += evaluate_space(pos, ei) - evaluate_space(pos, ei); // Evaluate position potential for the winning side score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score)); // Evaluate scale factor for the winning side ScaleFactor sf = evaluate_scale_factor(pos, ei, eg_value(score)); // Interpolate between a middlegame and a (scaled by 'sf') endgame score Value v = mg_value(score) * int(ei.me->game_phase()) + eg_value(score) * int(PHASE_MIDGAME - ei.me->game_phase()) * sf / SCALE_FACTOR_NORMAL; v /= int(PHASE_MIDGAME); // In case of tracing add all remaining individual evaluation terms if (DoTrace) { Trace::add(MATERIAL, pos.psq_score()); Trace::add(IMBALANCE, ei.me->imbalance()); Trace::add(PAWN, ei.pi->pawns_score()); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); Trace::add(SPACE, evaluate_space(pos, ei) , evaluate_space(pos, ei)); Trace::add(TOTAL, score); } return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view } // Explicit template instantiations template Value Eval::evaluate(const Position&); template Value Eval::evaluate(const Position&); /// 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 = evaluate(pos); 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) << " Bishop | " << 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) << "----------------+-------------+-------------+-------------\n" << " Total | " << Term(TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); } Stockfish-sf_8/src/evaluate.h000066400000000000000000000023441300605702500164170ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED #include #include "types.h" class Position; namespace Eval { const Value Tempo = Value(20); // Must be visible to search std::string trace(const Position& pos); template Value evaluate(const Position& pos); } #endif // #ifndef EVALUATE_H_INCLUDED Stockfish-sf_8/src/main.cpp000066400000000000000000000027041300605702500160700ustar00rootroot00000000000000/* 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-2016 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 . */ #include #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(); Threads.init(); Tablebases::init(Options["SyzygyPath"]); TT.resize(Options["Hash"]); UCI::loop(argc, argv); Threads.exit(); return 0; } Stockfish-sf_8/src/material.cpp000066400000000000000000000203641300605702500167440ustar00rootroot00000000000000/* 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-2016 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 . */ #include // For std::min #include #include // 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, 2 }, // Pawn { 32, 255, -3 }, // Knight OUR PIECES { 0, 104, 4, 0 }, // Bishop { -26, -2, 47, 105, -149 }, // Rook {-185, 24, 122, 137, -134, 0 } // 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 { 101, 100, -37, 141, 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 EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKPsK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKPKP[] = { Endgame(WHITE), Endgame(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(us) == 1 && pos.count(us) >= 1; } bool is_KQKRPs(const Position& pos, Color us) { return !pos.count(us) && pos.non_pawn_material(us) == QueenValueMg && pos.count(us) == 1 && pos.count(~us) == 1 && pos.count(~us) >= 1; } /// imbalance() calculates the imbalance by comparing the piece count of each /// piece type for both colors. template 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; e->gamePhase = pos.game_phase(); // 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(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* sf; if ((sf = pos.this_thread()->endgames.probe(key)) != nullptr) { e->scalingFunction[sf->strong_side()] = 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]; } Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board { if (!pos.count(BLACK)) { assert(pos.count(WHITE) >= 2); e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; } else if (!pos.count(WHITE)) { assert(pos.count(BLACK) >= 2); e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; } else if (pos.count(WHITE) == 1 && pos.count(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(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(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(WHITE) == 1 && npm_w - npm_b <= BishopValueMg) e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN; if (pos.count(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(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; e->value = int16_t((imbalance(PieceCount) - imbalance(PieceCount)) / 16); return e; } } // namespace Material Stockfish-sf_8/src/material.h000066400000000000000000000056271300605702500164160ustar00rootroot00000000000000/* 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-2016 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 . */ #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; int16_t value; uint8_t factor[COLOR_NB]; EndgameBase* evaluationFunction; EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each // side (e.g. KPKP, KBPsKs) Phase gamePhase; }; typedef HashTable Table; Entry* probe(const Position& pos); } // namespace Material #endif // #ifndef MATERIAL_H_INCLUDED Stockfish-sf_8/src/misc.cpp000066400000000000000000000121271300605702500160770ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #include #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 = "8"; /// 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() { return logBuf->pubsync(), buf->pubsync(); } int overflow(int c) { return log(buf->sputc((char)c), "<< "); } int underflow() { return buf->sgetc(); } int uflow() { 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 DD-MM-YY" (where DD-MM-YY is the date when /// the program was compiled) or "Stockfish ", 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 Stockfish-sf_8/src/misc.h000066400000000000000000000056461300605702500155540ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED #include #include #include #include #include #include "types.h" const std::string engine_info(bool to_uci = false); void prefetch(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::steady_clock::now().time_since_epoch()).count(); } template struct HashTable { Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } private: std::vector table = std::vector(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 /// 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 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 T sparse_rand() { return T(rand64() & rand64() & rand64()); } }; #endif // #ifndef MISC_H_INCLUDED Stockfish-sf_8/src/movegen.cpp000066400000000000000000000336721300605702500166140ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include "movegen.h" #include "position.h" namespace { template 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(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 Square 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(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) return moveList; Move m = make(kfrom, rfrom); if (Checks && !pos.gives_check(m)) return moveList; *moveList++ = m; return moveList; } template ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) *moveList++ = make(to - D, to, QUEEN); if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); *moveList++ = make(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 && (StepAttacksBB[W_KNIGHT][to] & ksq)) *moveList++ = make(to - D, to, KNIGHT); else (void)ksq; // Silence a warning under MSVC return moveList; } template 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 Square Up = (Us == WHITE ? NORTH : SOUTH); const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); const Square 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(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; if (Type == EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } if (Type == QUIET_CHECKS) { Square ksq = pos.square(Them); b1 &= pos.attacks_from(ksq, Them); b2 &= pos.attacks_from(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(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); Bitboard dc2 = shift(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(pawnsOn7) & enemies; Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; Square ksq = pos.square(Them); while (b1) moveList = make_promotions(moveList, pop_lsb(&b1), ksq); while (b2) moveList = make_promotions(moveList, pop_lsb(&b2), ksq); while (b3) moveList = make_promotions(moveList, pop_lsb(&b3), ksq); } // Standard and en-passant captures if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { Bitboard b1 = shift(pawnsNotOn7) & enemies; Bitboard b2 = shift(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(pos.ep_square(), Them); assert(b1); while (b1) *moveList++ = make(pop_lsb(&b1), pos.ep_square()); } } return moveList; } template ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, Bitboard target) { assert(Pt != KING && Pt != PAWN); const Square* pl = pos.squares(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(from) & target; if (Checks) b &= pos.check_squares(Pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); } return moveList; } template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { const bool Checks = Type == QUIET_CHECKS; moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, Us, target); moveList = generate_moves(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(Us); Bitboard b = pos.attacks_from(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::right, Checks, true>(pos, moveList, Us); moveList = generate_castling::right, Checks, true>(pos, moveList, Us); } else { moveList = generate_castling::right, Checks, false>(pos, moveList, Us); moveList = generate_castling::right, Checks, false>(pos, moveList, Us); } } return moveList; } } // namespace /// generate generates all pseudo-legal captures and queen /// promotions. Returns a pointer to the end of the move list. /// /// generate generates all pseudo-legal non-captures and /// underpromotions. Returns a pointer to the end of the move list. /// /// generate generates all pseudo-legal captures and /// non-captures. Returns a pointer to the end of the move list. template 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(pos, moveList, target) : generate_all(pos, moveList, target); } // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); /// generate 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(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(Piece(pt), from) & ~pos.pieces(); if (pt == KING) b &= ~PseudoAttacks[QUEEN][pos.square(~us)]; while (b) *moveList++ = make_move(from, pop_lsb(&b)); } return us == WHITE ? generate_all(pos, moveList, ~pos.pieces()) : generate_all(pos, moveList, ~pos.pieces()); } /// generate 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(const Position& pos, ExtMove* moveList) { assert(pos.checkers()); Color us = pos.side_to_move(); Square ksq = pos.square(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(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(pos, moveList, target) : generate_all(pos, moveList, target); } /// generate generates all the legal moves in the given position template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Square ksq = pos.square(pos.side_to_move()); ExtMove* cur = moveList; moveList = pos.checkers() ? generate(pos, moveList) : generate(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; } Stockfish-sf_8/src/movegen.h000066400000000000000000000037511300605702500162540ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED #include "types.h" class Position; enum GenType { CAPTURES, QUIETS, QUIET_CHECKS, EVASIONS, NON_EVASIONS, LEGAL }; struct ExtMove { Move move; Value value; operator Move() const { return move; } void operator=(Move m) { move = m; } }; inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } template 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 struct MoveList { explicit MoveList(const Position& pos) : last(generate(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 { for (const auto& m : *this) if (m == move) return true; return false; } private: ExtMove moveList[MAX_MOVES], *last; }; #endif // #ifndef MOVEGEN_H_INCLUDED Stockfish-sf_8/src/movepick.cpp000066400000000000000000000236571300605702500167730ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include "movepick.h" #include "thread.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 }; // Our insertion sort, which is guaranteed to be stable, as it should be void insertion_sort(ExtMove* begin, ExtMove* end) { ExtMove tmp, *p, *q; for (p = begin + 1; p < end; ++p) { tmp = *p; for (q = p; 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::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s) : pos(p), ss(s), depth(d) { assert(d > DEPTH_ZERO); Square prevSq = to_sq((ss-1)->currentMove); countermove = pos.this_thread()->counterMoves[pos.piece_on(prevSq)][prevSq]; stage = pos.checkers() ? EVASION : MAIN_SEARCH; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s) : pos(p) { 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::MovePicker(const Position& p, Move ttm, Value th) : pos(p), threshold(th) { assert(!pos.checkers()); stage = PROBCUT; // In ProbCut we generate captures with SEE higher than the given threshold ttMove = ttm && pos.pseudo_legal(ttm) && pos.capture(ttm) && pos.see_ge(ttm, threshold + 1)? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } /// score() assigns a numerical value to each move in a move list. The moves with /// highest values will be picked first. template<> void MovePicker::score() { // Winning and equal captures in the main search are ordered by MVV, preferring // captures near our home rank. Surprisingly, this appears to perform slightly // better than SEE-based move ordering: exchanging big pieces before capturing // a hanging piece probably helps to reduce the subtree size. // In the main search we want to push captures with negative SEE values to the // badCaptures[] array, but instead of doing it now we delay until the move // has been picked up, saving some SEE calls in case we get a cutoff. for (auto& m : *this) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(200 * relative_rank(pos.side_to_move(), to_sq(m))); } template<> void MovePicker::score() { const HistoryStats& history = pos.this_thread()->history; const FromToStats& fromTo = pos.this_thread()->fromTo; const CounterMoveStats* cm = (ss-1)->counterMoves; const CounterMoveStats* fm = (ss-2)->counterMoves; const CounterMoveStats* f2 = (ss-4)->counterMoves; Color c = pos.side_to_move(); for (auto& m : *this) m.value = history[pos.moved_piece(m)][to_sq(m)] + (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + fromTo.get(c, m); } template<> void MovePicker::score() { // Try captures ordered by MVV/LVA, then non-captures ordered by history value const HistoryStats& history = pos.this_thread()->history; const FromToStats& fromTo = pos.this_thread()->fromTo; Color c = pos.side_to_move(); for (auto& m : *this) if (pos.capture(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; else m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m); } /// 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() { 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(pos, cur); score(); ++stage; case GOOD_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) { if (pos.see_ge(move, VALUE_ZERO)) return move; // Losing capture, move it to the beginning of the array *endBadCaptures++ = move; } } ++stage; move = ss->killers[0]; // First killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; case KILLERS: ++stage; move = ss->killers[1]; // Second killer move if ( move != MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) return move; case COUNTERMOVE: ++stage; move = countermove; if ( move != MOVE_NONE && move != ttMove && move != ss->killers[0] && move != ss->killers[1] && pos.pseudo_legal(move) && !pos.capture(move)) return move; case QUIET_INIT: cur = endBadCaptures; endMoves = generate(pos, cur); score(); if (depth < 3 * ONE_PLY) { ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; }); insertion_sort(cur, goodQuiet); } else insertion_sort(cur, endMoves); ++stage; case QUIET: while (cur < endMoves) { move = *cur++; if ( move != ttMove && move != ss->killers[0] && move != ss->killers[1] && move != countermove) return move; } ++stage; cur = moves; // Point to beginning of bad captures case BAD_CAPTURES: if (cur < endBadCaptures) return *cur++; break; case EVASIONS_INIT: cur = moves; endMoves = generate(pos, cur); score(); ++stage; case ALL_EVASIONS: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } break; case PROBCUT_INIT: cur = moves; endMoves = generate(pos, cur); score(); ++stage; case PROBCUT_CAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if ( move != ttMove && pos.see_ge(move, threshold + 1)) return move; } break; case QCAPTURES_1_INIT: case QCAPTURES_2_INIT: cur = moves; endMoves = generate(pos, cur); score(); ++stage; case QCAPTURES_1: case QCAPTURES_2: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (move != ttMove) return move; } if (stage == QCAPTURES_2) break; cur = moves; endMoves = generate(pos, cur); ++stage; case QCHECKS: while (cur < endMoves) { move = cur++->move; if (move != ttMove) return move; } break; case QSEARCH_RECAPTURES: cur = moves; endMoves = generate(pos, cur); score(); ++stage; case QRECAPTURES: while (cur < endMoves) { move = pick_best(cur++, endMoves); if (to_sq(move) == recaptureSquare) return move; } break; default: assert(false); } return MOVE_NONE; } Stockfish-sf_8/src/movepick.h000066400000000000000000000076721300605702500164370ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED #include // For std::max #include // For std::memset #include "movegen.h" #include "position.h" #include "types.h" /// The Stats struct stores moves statistics. According to the template parameter /// the class can store History and Countermoves. History records how often /// different moves have been successful or unsuccessful during the current search /// and is used for reduction and move ordering decisions. /// Countermoves store the move that refute a previous one. Entries are stored /// using only the moving piece and destination square, hence two moves with /// different origin but same destination and piece will be considered identical. template struct Stats { static const Value Max = Value(1 << 28); const T* operator[](Piece pc) const { return table[pc]; } T* operator[](Piece pc) { return table[pc]; } void clear() { std::memset(table, 0, sizeof(table)); } void update(Piece pc, Square to, Move m) { table[pc][to] = m; } void update(Piece pc, Square to, Value v) { if (abs(int(v)) >= 324) return; table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324); table[pc][to] += int(v) * 32; } private: T table[PIECE_NB][SQUARE_NB]; }; typedef Stats MoveStats; typedef Stats HistoryStats; typedef Stats CounterMoveStats; typedef Stats CounterMoveHistoryStats; struct FromToStats { Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } void clear() { std::memset(table, 0, sizeof(table)); } void update(Color c, Move m, Value v) { if (abs(int(v)) >= 324) return; Square from = from_sq(m); Square to = to_sq(m); table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324; table[c][from][to] += int(v) * 32; } private: Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; }; /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo legal move each time it is called, until there are no moves left, /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// beta algorithm, MovePicker attempts to return the moves which are most likely /// to get a cut-off first. namespace Search { struct Stack; } class MovePicker { public: MovePicker(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete; MovePicker(const Position&, Move, Value); MovePicker(const Position&, Move, Depth, Square); MovePicker(const Position&, Move, Depth, Search::Stack*); Move next_move(); private: template void score(); ExtMove* begin() { return cur; } ExtMove* end() { return endMoves; } const Position& pos; const Search::Stack* ss; Move countermove; Depth depth; Move ttMove; Square recaptureSquare; Value threshold; int stage; ExtMove *cur, *endMoves, *endBadCaptures; ExtMove moves[MAX_MOVES]; }; #endif // #ifndef MOVEPICK_H_INCLUDED Stockfish-sf_8/src/pawns.cpp000066400000000000000000000245101300605702500162730ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #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 by opposed flag const Score Isolated[2] = { S(45, 40), S(30, 27) }; // Backward pawn penalty by opposed flag const Score Backward[2] = { S(56, 33), S(41, 19) }; // Unsupported pawn penalty for pawns which are neither isolated or backward const Score Unsupported = S(17, 8); // Connected pawn bonus by opposed, phalanx, twice supported and rank Score Connected[2][2][2][RANK_NB]; // Doubled pawn penalty const Score Doubled = S(18,38); // Lever bonus by rank const Score Lever[RANK_NB] = { S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), S(17, 16), S(33, 32), S(0, 0), S(0, 0) }; // Weakness of our pawn shelter in front of the king by [distance from edge][rank] const Value ShelterWeakness[][RANK_NB] = { { V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) }, { V(120), V( 0), V(28), V(76), V(88), V(103), V(104) }, { V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) }, { V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } }; // Danger of enemy pawns moving toward our king by [type][distance from edge][rank] const Value StormDanger[][4][RANK_NB] = { { { V( 0), V( 67), V( 134), V(38), V(32) }, { V( 0), V( 57), V( 139), V(37), V(22) }, { V( 0), V( 43), V( 115), V(43), V(27) }, { V( 0), V( 68), V( 124), V(57), V(32) } }, { { V(20), V( 43), V( 100), V(56), V(20) }, { V(23), V( 20), V( 98), V(40), V(15) }, { V(23), V( 39), V( 103), V(36), V(18) }, { V(28), V( 19), V( 108), V(42), V(26) } }, { { V( 0), V( 0), V( 75), V(14), V( 2) }, { V( 0), V( 0), V( 150), V(30), V( 4) }, { V( 0), V( 0), V( 160), V(22), V( 5) }, { V( 0), V( 0), V( 166), V(24), V(13) } }, { { V( 0), V(-283), V(-281), V(57), V(31) }, { V( 0), V( 58), V( 141), V(39), V(18) }, { V( 0), V( 65), V( 142), V(48), V(32) }, { V( 0), V( 60), V( 126), V(51), V(19) } } }; // 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 Score evaluate(const Position& pos, Pawns::Entry* e) { const Color Them = (Us == WHITE ? BLACK : WHITE); const Square Up = (Us == WHITE ? NORTH : SOUTH); const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); Bitboard b, neighbours, stoppers, doubled, supported, phalanx; Square s; bool opposed, lever, connected, backward; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; Bitboard ourPawns = pos.pieces(Us , PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; e->semiopenFiles[Us] = 0xFF; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = shift(ourPawns) | shift(ourPawns); e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares); e->pawnsOnSquares[Us][WHITE] = pos.count(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_bb(Us, s); stoppers = theirPawns & passed_pawn_mask(Us, s); lever = theirPawns & pawnAttacksBB[s]; doubled = ourPawns & (s + Up); neighbours = ourPawns & adjacent_files_bb(f); phalanx = neighbours & rank_bb(s); supported = neighbours & rank_bb(s - Up); connected = supported | phalanx; // 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(b & adjacent_files_bb(f))) & stoppers; assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours)); } // Passed pawns will be properly scored in evaluation because we need // full attack info to evaluate them. if (!stoppers && !(ourPawns & forward_bb(Us, s))) e->passedPawns[Us] |= s; // Score this pawn if (!neighbours) score -= Isolated[opposed]; else if (backward) score -= Backward[opposed]; else if (!supported) score -= Unsupported; if (connected) score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)]; if (doubled) score -= Doubled; if (lever) score += Lever[relative_rank(Us, s)]; } 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, 8, 19, 13, 71, 94, 169, 324 }; for (int opposed = 0; opposed <= 1; ++opposed) for (int phalanx = 0; phalanx <= 1; ++phalanx) for (int apex = 0; apex <= 1; ++apex) for (Rank r = RANK_2; r < RANK_8; ++r) { int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; v += (apex ? v / 2 : 0); Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8); } } /// 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(pos, e) - evaluate(pos, e); e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]); e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); return e; } /// Entry::shelter_storm() calculates shelter and storm penalties for the file /// the king is on, as well as the two adjacent files. template Value Entry::shelter_storm(const Position& pos, Square ksq) { const Color Them = (Us == WHITE ? BLACK : WHITE); enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(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 = center - File(1); f <= center + File(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; safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] + StormDanger [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : rkUs == RANK_1 ? NoFriendlyPawn : rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] [std::min(f, FILE_H - f)][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 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(pos, ksq); // If we can castle use the bonus after the castling if it is bigger if (pos.can_castle(MakeCastling::right)) bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); if (pos.can_castle(MakeCastling::right)) bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); return make_score(bonus, -16 * minKingPawnDistance); } // Explicit template instantiation template Score Entry::do_king_safety(const Position& pos, Square ksq); template Score Entry::do_king_safety(const Position& pos, Square ksq); } // namespace Pawns Stockfish-sf_8/src/pawns.h000066400000000000000000000054551300605702500157470ustar00rootroot00000000000000/* 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-2016 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 . */ #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 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][!!(DarkSquares & s)]; } template 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(pos, ksq)); } template Score do_king_safety(const Position& pos, Square ksq); template 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 castlingRights[COLOR_NB]; int semiopenFiles[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int asymmetry; int openFiles; }; typedef HashTable Table; void init(); Entry* probe(const Position& pos); } // namespace Pawns #endif // #ifndef PAWNS_H_INCLUDED Stockfish-sf_8/src/position.cpp000066400000000000000000001071301300605702500170070ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include // For offsetof() #include // For std::memset, std::memcmp #include #include #include "bitboard.h" #include "misc.h" #include "movegen.h" #include "position.h" #include "thread.h" #include "tt.h" #include "uci.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; } namespace { const string PieceToChar(" PNBRQK pnbrqk"); // min_attacker() is a helper function used by see() 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 PieceType min_attacker(const Bitboard* bb, Square to, Bitboard stmAttackers, Bitboard& occupied, Bitboard& attackers) { Bitboard b = stmAttackers & bb[Pt]; if (!b) return min_attacker(bb, to, stmAttackers, occupied, attackers); occupied ^= b & ~(b - 1); if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) attackers |= attacks_bb(to, occupied) & (bb[BISHOP] | bb[QUEEN]); if (Pt == ROOK || Pt == QUEEN) attackers |= attacks_bb(to, occupied) & (bb[ROOK] | bb[QUEEN]); attackers &= occupied; // After X-ray that may add already processed pieces return (PieceType)Pt; } template<> PieceType min_attacker(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::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b; ) os << UCI::square(pop_lsb(&b)) << " "; 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(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); 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(); } } Zobrist::side = rng.rand(); } /// 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 regardless of whether there is a pawn in position to make an en passant capture. 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 += Square(token - '0'); // Advance the given number of files else if (token == '/') sq -= Square(16); 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))) 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 ply 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(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(WHITE), si->pinnersForKing[WHITE]); si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinnersForKing[BLACK]); Square ksq = square(~sideToMove); si->checkSquares[PAWN] = attacks_from(ksq, ~sideToMove); si->checkSquares[KNIGHT] = attacks_from(ksq); si->checkSquares[BISHOP] = attacks_from(ksq); si->checkSquares[ROOK] = attacks_from(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->pawnKey = si->materialKey = 0; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->psq = SCORE_ZERO; si->checkersBB = attackers_to(square(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::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::game_phase() calculates the game phase interpolating total non-pawn /// material between endgame and midgame limits. Phase Position::game_phase() const { Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; npm = std::max(EndgameLimit, std::min(npm, MidgameLimit)); return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); } /// 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(s, BLACK) & pieces(WHITE, PAWN)) | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) | (attacks_from(s) & pieces(KNIGHT)) | (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) | (attacks_from(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(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(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(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(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(*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(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(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(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(~sideToMove))) return true; switch (type_of(m)) { case NORMAL: return false; case PROMOTION: return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & square(~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(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) | (attacks_bb(square(~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(~sideToMove)) && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~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); ++nodes; 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(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(to - pawn_push(us), us) & pieces(them, PAWN))) { st->epSquare = (from + to) / 2; 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]; prefetch(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(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(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 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 value. We'll use an /// algorithm similar to alpha-beta pruning with a null window. bool Position::see_ge(Move m, Value v) const { assert(is_ok(m)); // Castling moves are implemented as king capturing the rook so cannot be // handled correctly. Simply assume the SEE value is VALUE_ZERO that is always // correct unless in the rare case the rook ends up under attack. if (type_of(m) == CASTLING) return VALUE_ZERO >= v; 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; if (type_of(m) == ENPASSANT) { occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn balance = PieceValue[MG][PAWN]; } else { balance = PieceValue[MG][piece_on(to)]; occupied = 0; } if (balance < v) return false; if (nextVictim == KING) return true; balance -= PieceValue[MG][nextVictim]; if (balance >= v) return true; bool relativeStm = true; // True if the opponent is to move 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) { 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 (!stmAttackers) return relativeStm; // Locate and remove the next least valuable attacker nextVictim = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); if (nextVictim == KING) return relativeStm == bool(attackers & pieces(~stm)); balance += relativeStm ? PieceValue[MG][nextVictim] : -PieceValue[MG][nextVictim]; relativeStm = !relativeStm; if (relativeStm == (balance >= v)) return relativeStm; stm = ~stm; } } /// 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() const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; StateInfo* stp = st; for (int i = 2, e = std::min(st->rule50, st->pliesFromNull); i <= e; i += 2) { stp = stp->previous->previous; if (stp->key == st->key) return true; // Draw at first repetition } 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. /// This is meant to be helpful when debugging. bool Position::pos_is_ok(int* failedStep) const { const bool Fast = true; // Quick (default) or full check? enum { Default, King, Bitboards, State, Lists, Castling }; for (int step = Default; step <= (Fast ? Default : Castling); step++) { if (failedStep) *failedStep = step; if (step == Default) if ( (sideToMove != WHITE && sideToMove != BLACK) || piece_on(square(WHITE)) != W_KING || piece_on(square(BLACK)) != B_KING || ( ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) return false; if (step == King) if ( std::count(board, board + SQUARE_NB, W_KING) != 1 || std::count(board, board + SQUARE_NB, B_KING) != 1 || attackers_to(square(~sideToMove)) & pieces(sideToMove)) return false; if (step == Bitboards) { if ( (pieces(WHITE) & pieces(BLACK)) ||(pieces(WHITE) | pieces(BLACK)) != pieces()) return false; for (PieceType p1 = PAWN; p1 <= KING; ++p1) for (PieceType p2 = PAWN; p2 <= KING; ++p2) if (p1 != p2 && (pieces(p1) & pieces(p2))) return false; } if (step == State) { StateInfo si = *st; set_state(&si); if (std::memcmp(&si, st, sizeof(StateInfo))) return false; } if (step == Lists) for (Piece pc : Pieces) { if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))) return false; for (int i = 0; i < pieceCount[pc]; ++i) if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) return false; } if (step == Castling) for (Color c = WHITE; c <= BLACK; ++c) 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(c)] & (c | s)) != (c | s)) return false; } } return true; } Stockfish-sf_8/src/position.h000066400000000000000000000275101300605702500164570ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef POSITION_H_INCLUDED #define POSITION_H_INCLUDED #include #include #include // For std::unique_ptr #include #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]; }; // In a std::deque references to elements are unaffected upon resizing typedef std::unique_ptr> 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); 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 int count(Color c) const; template const Square* squares(Color c) const; template 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(Piece pc, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; Bitboard slider_blockers(Bitboard sliders, Square s, 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& st, bool givesCheck); void undo_move(Move m); void do_null_move(StateInfo& st); void undo_null_move(); // Static Exchange Evaluation bool see_ge(Move m, Value value) 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; Phase game_phase() const; int game_ply() const; bool is_chess960() const; Thread* this_thread() const; uint64_t nodes_searched() const; bool is_draw() const; int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; // Position consistency check, for debugging bool pos_is_ok(int* failedStep = nullptr) 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 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]; uint64_t nodes; 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 inline int Position::count(Color c) const { return pieceCount[make_piece(c, Pt)]; } template inline const Square* Position::squares(Color c) const { return pieceList[make_piece(c, Pt)]; } template 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 inline Bitboard Position::attacks_from(Square s) const { return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) : Pt == QUEEN ? attacks_from(s) | attacks_from(s) : StepAttacksBB[Pt][s]; } template<> inline Bitboard Position::attacks_from(Square s, Color c) const { return StepAttacksBB[make_piece(c, PAWN)][s]; } inline Bitboard Position::attacks_from(Piece pc, Square s) const { return attacks_bb(pc, 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 int Position::game_ply() const { return gamePly; } inline int Position::rule50_count() const { return st->rule50; } inline uint64_t Position::nodes_searched() const { return nodes; } inline bool Position::opposite_bishops() const { return pieceCount[W_BISHOP] == 1 && pieceCount[B_BISHOP] == 1 && opposite_colors(square(WHITE), square(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; } #endif // #ifndef POSITION_H_INCLUDED Stockfish-sf_8/src/psqt.cpp000066400000000000000000000114471300605702500161370ustar00rootroot00000000000000/* 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-2016 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 . */ #include #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(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) }, { S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) }, { S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) }, { S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) }, { S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) }, { S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) } }, { // Knight { S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) }, { S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) }, { S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) }, { S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) }, { S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) }, { S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) }, { S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) }, { S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) } }, { // Bishop { S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) }, { S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) }, { S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) }, { S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) }, { S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) }, { S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) }, { S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) }, { S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) } }, { // 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,-70), S(-3,-57), S(-4,-41), S(-1,-29) }, { S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) }, { S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) }, { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) }, { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) }, { S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) }, { S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) }, { S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) } }, { // King { S(291, 28), S(344, 76), S(294,103), S(219,112) }, { S(289, 70), S(329,119), S(263,170), S(205,159) }, { S(226,109), S(271,164), S(202,195), S(136,191) }, { S(204,131), S(212,194), S(175,194), S(137,204) }, { S(177,132), S(205,187), S(143,224), S( 94,227) }, { S(147,118), S(188,178), S(113,199), S( 70,197) }, { S(116, 72), S(158,121), S( 93,142), S( 48,161) }, { S( 94, 30), S(120, 76), S( 78,101), S( 31,111) } } }; #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_H - file_of(s)); psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f]; psq[~pc][~s] = -psq[pc][s]; } } } } // namespace PSQT Stockfish-sf_8/src/search.cpp000066400000000000000000001620611300605702500164140ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #include // For std::memset #include #include #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 { SignalsType Signals; 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 }; // Razoring and futility margin based on depth const int razor_margin[4] = { 483, 570, 603, 554 }; 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 Depth reduction(bool i, Depth d, int mn) { return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY; } // Skill structure is used to implement strength limit struct Skill { 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 best_move(size_t multiPV) { return best ? best : pick_best(multiPV); } Move pick_best(size_t multiPV); int level; Move best = MOVE_NONE; }; // EasyMoveManager structure is used to detect an 'easy move'. When the PV is // stable across multiple search iterations, we can quickly return the best move. struct EasyMoveManager { void clear() { stableCnt = 0; expectedPosKey = 0; pv[0] = pv[1] = pv[2] = MOVE_NONE; } Move get(Key key) const { return expectedPosKey == key ? pv[2] : MOVE_NONE; } void update(Position& pos, const std::vector& newPv) { assert(newPv.size() >= 3); // Keep track of how many times in a row the 3rd ply remains stable stableCnt = (newPv[2] == pv[2]) ? stableCnt + 1 : 0; if (!std::equal(newPv.begin(), newPv.begin() + 3, pv)) { std::copy(newPv.begin(), newPv.begin() + 3, pv); StateInfo st[2]; pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0])); pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1])); expectedPosKey = pos.key(); pos.undo_move(newPv[1]); pos.undo_move(newPv[0]); } } int stableCnt; Key expectedPosKey; Move pv[3]; }; // Set of rows with half bits set to 1 and half to 0. It is used to allocate // the search depths across the threads. typedef std::vector Row; const Row HalfDensity[] = { {0, 1}, {1, 0}, {0, 0, 1, 1}, {0, 1, 1, 0}, {1, 1, 0, 0}, {1, 0, 0, 1}, {0, 0, 0, 1, 1, 1}, {0, 0, 1, 1, 1, 0}, {0, 1, 1, 1, 0, 0}, {1, 1, 1, 0, 0, 0}, {1, 1, 0, 0, 0, 1}, {1, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 0, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0 ,0}, {0, 1, 1, 1, 1, 0, 0 ,0}, {1, 1, 1, 1, 0, 0, 0 ,0}, {1, 1, 1, 0, 0, 0, 0 ,1}, {1, 1, 0, 0, 0, 0, 1 ,1}, {1, 0, 0, 0, 0, 1, 1 ,1}, }; const size_t HalfDensitySize = std::extent::value; EasyMoveManager EasyMove; Value DrawValue[COLOR_NB]; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); 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_cm_stats(Stack* ss, Piece pc, Square s, Value bonus); void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, Value bonus); void check_time(); } // 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) / 2; if (r < 0.80) continue; 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.773 * pow(d + 0.00, 1.8)); FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8)); } } /// Search::clear() resets search state to zero, to obtain reproducible results void Search::clear() { TT.clear(); for (Thread* th : Threads) { th->history.clear(); th->counterMoves.clear(); th->fromTo.clear(); th->counterMoveHistory.clear(); } Threads.main()->previousScore = VALUE_INFINITE; } /// Search::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 uint64_t Search::perft(Position& pos, Depth depth) { StateInfo st; uint64_t cnt, nodes = 0; const bool leaf = (depth == 2 * ONE_PLY); for (const auto& m : MoveList(pos)) { if (Root && depth <= ONE_PLY) cnt = 1, nodes++; else { pos.do_move(m, st, pos.gives_check(m)); cnt = leaf ? MoveList(pos).size() : perft(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; } template uint64_t Search::perft(Position&, Depth); /// 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() { Color us = rootPos.side_to_move(); Time.init(Limits, us, rootPos.game_ply()); int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns DrawValue[ us] = VALUE_DRAW - Value(contempt); DrawValue[~us] = VALUE_DRAW + Value(contempt); if (rootMoves.empty()) { rootMoves.push_back(RootMove(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 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(); // When we reach the maximum depth, we can arrive here without a raise of // Signals.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 Signals.stop). if (!Signals.stop && (Limits.ponder || Limits.infinite)) { Signals.stopOnPonderhit = true; wait(Signals.stop); } // Stop the threads if not already stopped Signals.stop = true; // Wait until all threads have finished for (Thread* th : Threads) if (th != this) th->wait_for_search_finished(); // Check if there are threads with a better score than main thread Thread* bestThread = this; if ( !this->easyMovePlayed && Options["MultiPV"] == 1 && !Limits.depth && !Skill(Options["Skill Level"]).enabled() && rootMoves[0].pv[0] != MOVE_NONE) { for (Thread* th : Threads) if ( th->completedDepth > bestThread->completedDepth && th->rootMoves[0].score > bestThread->rootMoves[0].score) 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+5; // To allow referencing (ss-5) and (ss+2) Value bestValue, alpha, beta, delta; Move easyMove = MOVE_NONE; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); std::memset(ss-5, 0, 8 * sizeof(Stack)); bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; completedDepth = DEPTH_ZERO; if (mainThread) { easyMove = EasyMove.get(rootPos.key()); EasyMove.clear(); mainThread->easyMovePlayed = mainThread->failedLow = false; mainThread->bestMoveChanges = 0; TT.new_search(); } 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 && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth / ONE_PLY <= Limits.depth)) { // Set up the new depths for the helper threads skipping on average every // 2nd ply (using a half-density matrix). if (!mainThread) { const Row& row = HalfDensity[(idx - 1) % HalfDensitySize]; if (row[(rootDepth / ONE_PLY + rootPos.game_ply()) % row.size()]) 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 && !Signals.stop; ++PVIdx) { // 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(rootPos, ss, alpha, beta, rootDepth, 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, break immediately. Sorting and // writing PV back to TT is safe because RootMoves is still // valid, although it refers to the previous iteration. if (Signals.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; Signals.stopOnPonderhit = false; } } else if (bestValue >= beta) { alpha = (alpha + beta) / 2; 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) continue; if (Signals.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000) sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; } if (!Signals.stop) completedDepth = rootDepth; 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); // Have we found a "mate in x"? if ( Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - bestValue <= 2 * Limits.mate) Signals.stop = true; // Do we have time for the next iteration? Can we stop searching now? if (Limits.use_time_management()) { if (!Signals.stop && !Signals.stopOnPonderhit) { // Stop the search if only one legal move is available, or if all // of the available time has been used, or if we matched an easyMove // from the previous search and just did a fast verification. const int F[] = { mainThread->failedLow, bestValue - mainThread->previousScore }; int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1])); double unstablePvFactor = 1 + mainThread->bestMoveChanges; bool doEasyMove = rootMoves[0].pv[0] == easyMove && mainThread->bestMoveChanges < 0.03 && Time.elapsed() > Time.optimum() * 5 / 42; if ( rootMoves.size() == 1 || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628 || (mainThread->easyMovePlayed = doEasyMove, doEasyMove)) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". if (Limits.ponder) Signals.stopOnPonderhit = true; else Signals.stop = true; } } if (rootMoves[0].pv.size() >= 3) EasyMove.update(rootPos, rootMoves[0].pv); else EasyMove.clear(); } } if (!mainThread) return; // Clear any candidate easy move that wasn't stable for the last search // iterations; the second condition prevents consecutive fast moves. if (EasyMove.stableCnt < 6 || mainThread->easyMovePlayed) EasyMove.clear(); // 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_move(multiPV))); } namespace { // search<>() is the main search function for both PV and non-PV nodes template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { const bool PvNode = NT == PV; const bool rootNode = PvNode && (ss-1)->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], quietsSearched[64]; StateInfo st; TTEntry* tte; Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, nullValue; bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning; Piece moved_piece; int moveCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); moveCount = quietCount = ss->moveCount = 0; ss->history = VALUE_ZERO; bestValue = -VALUE_INFINITE; ss->ply = (ss-1)->ply + 1; // Check for the available remaining time if (thisThread->resetCalls.load(std::memory_order_relaxed)) { thisThread->resetCalls = false; thisThread->callsCnt = 0; } if (++thisThread->callsCnt > 4096) { for (Thread* th : Threads) th->resetCalls = true; check_time(); } // Used to send selDepth info to GUI if (PvNode && thisThread->maxPly < ss->ply) thisThread->maxPly = ss->ply; if (!rootNode) { // Step 2. Check for aborted search and immediate draw if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; // 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->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->counterMoves = nullptr; (ss+1)->skipEarlyPruning = false; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; // 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); 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 killers, history, counter move on TT hit if (ttValue >= beta && ttMove) { int d = depth / ONE_PLY; if (!pos.capture_or_promotion(ttMove)) { Value bonus = Value(d * d + 2 * d - 2); update_stats(pos, ss, ttMove, nullptr, 0, bonus); } // Extra penalty for a quiet TT move in previous ply when it gets refuted if ((ss-1)->moveCount == 1 && !pos.captured_piece()) { Value penalty = Value(d * d + 4 * d + 1); Square prevSq = to_sq((ss-1)->currentMove); update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); } } return ttValue; } // Step 4a. Tablebase probe if (!rootNode && TB::Cardinality) { int piecesCnt = pos.count(WHITE) + pos.count(BLACK); if ( piecesCnt <= TB::Cardinality && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { int found, v = Tablebases::probe_wdl(pos, &found); if (found) { thisThread->tbHits++; int drawScore = TB::UseRule50 ? 1 : 0; value = v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply : v > drawScore ? VALUE_MATE - MAX_PLY - ss->ply : VALUE_DRAW + 2 * v * drawScore; tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), MOVE_NONE, VALUE_NONE, TT.generation()); return 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) if (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 (ss->skipEarlyPruning) goto moves_loop; // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY && ttMove == MOVE_NONE && eval + razor_margin[depth / ONE_PLY] <= alpha) { if (depth <= ONE_PLY) return qsearch(pos, ss, alpha, beta, DEPTH_ZERO); Value ralpha = alpha - razor_margin[depth / ONE_PLY]; Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); 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 && pos.non_pawn_material(pos.side_to_move())) return eval; // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode && eval >= beta && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) && pos.non_pawn_material(pos.side_to_move())) { ss->currentMove = MOVE_NULL; ss->counterMoves = nullptr; 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; pos.do_null_move(st); (ss+1)->skipEarlyPruning = true; nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); (ss+1)->skipEarlyPruning = false; pos.undo_null_move(); if (nullValue >= beta) { // Do not return unproven mate scores if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) return nullValue; // Do verification search at high depths ss->skipEarlyPruning = true; Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta, DEPTH_ZERO) : search(pos, ss, beta-1, beta, depth-R, false); ss->skipEarlyPruning = false; 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); Depth rdepth = depth - 4 * ONE_PLY; assert(rdepth >= ONE_PLY); assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NULL); MovePicker mp(pos, ttMove, rbeta - ss->staticEval); while ((move = mp.next_move()) != MOVE_NONE) if (pos.legal(move)) { ss->currentMove = move; ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)]; pos.do_move(move, st, pos.gives_check(move)); value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); pos.undo_move(move); 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; ss->skipEarlyPruning = true; search(pos, ss, alpha, beta, d, cutNode); ss->skipEarlyPruning = false; tte = TT.probe(posKey, ttHit); ttMove = ttHit ? tte->move() : MOVE_NONE; } moves_loop: // When in check search starts from here const CounterMoveStats* cmh = (ss-1)->counterMoves; const CounterMoveStats* fmh = (ss-2)->counterMoves; const CounterMoveStats* fmh2 = (ss-4)->counterMoves; MovePicker mp(pos, ttMove, depth, ss); 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; // 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()) != 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); moved_piece = pos.moved_piece(move); givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates() ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move) : pos.gives_check(move); moveCountPruning = depth < 16 * ONE_PLY && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; // Step 12. Extend checks if ( givesCheck && !moveCountPruning && pos.see_ge(move, VALUE_ZERO)) extension = ONE_PLY; // 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 extend the ttMove. if ( singularExtensionNode && move == ttMove && !extension && 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; ss->skipEarlyPruning = true; value = search(pos, ss, rBeta - 1, rBeta, d, cutNode); ss->skipEarlyPruning = false; ss->excludedMove = MOVE_NONE; if (value < rBeta) extension = ONE_PLY; } // Update the current move (this must be done after singular extension search) newDepth = depth - ONE_PLY + extension; // Step 13. Pruning at shallow depth if ( !rootNode && bestValue > VALUE_MATED_IN_MAX_PLY) { if ( !captureOrPromotion && !givesCheck && !pos.advanced_pawn_push(move)) { // Move count based pruning if (moveCountPruning) continue; // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; // Countermoves based pruning if ( lmrDepth < 3 && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO) && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh))) continue; // Futility pruning: parent node if ( lmrDepth < 7 && !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, Value(-35 * depth / ONE_PLY * 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; } ss->currentMove = move; ss->counterMoves = &thisThread->counterMoveHistory[moved_piece][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(improving, depth, moveCount); if (captureOrPromotion) r -= r ? ONE_PLY : DEPTH_ZERO; else { // 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(). Also use see() instead of see_sign(), // because the destination square is empty. else if ( type_of(move) == NORMAL && type_of(pos.piece_on(to_sq(move))) != PAWN && !pos.see_ge(make_move(to_sq(move), from_sq(move)), VALUE_ZERO)) r -= 2 * ONE_PLY; ss->history = thisThread->history[moved_piece][to_sq(move)] + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO) + thisThread->fromTo.get(~pos.side_to_move(), move) - 8000; // Correction factor // Decrease/increase reduction by comparing opponent's stat score if (ss->history > VALUE_ZERO && (ss-1)->history < VALUE_ZERO) r -= ONE_PLY; else if (ss->history < VALUE_ZERO && (ss-1)->history > VALUE_ZERO) r += ONE_PLY; // Decrease/increase reduction for moves with a good/bad history r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY); } Depth d = std::max(newDepth - r, ONE_PLY); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); 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(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); // 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(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : - search(pos, ss+1, -beta, -alpha, newDepth, 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 (Signals.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.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(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) { // If there is an easy move for this position, clear it if unstable if ( PvNode && thisThread == Threads.main() && EasyMove.get(pos.key()) && (move != EasyMove.get(pos.key()) || moveCount > 1)) EasyMove.clear(); 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; } // 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 (Signals.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(pos).size()); if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; else if (bestMove) { int d = depth / ONE_PLY; // Quiet best move: update killers, history and countermoves if (!pos.capture_or_promotion(bestMove)) { Value bonus = Value(d * d + 2 * d - 2); update_stats(pos, ss, bestMove, quietsSearched, quietCount, bonus); } // Extra penalty for a quiet TT move in previous ply when it gets refuted if ((ss-1)->moveCount == 1 && !pos.captured_piece()) { Value penalty = Value(d * d + 4 * d + 1); Square prevSq = to_sq((ss-1)->currentMove); update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); } } // Bonus for prior countermove that caused the fail low else if ( depth >= 3 * ONE_PLY && !pos.captured_piece() && is_ok((ss-1)->currentMove)) { int d = depth / ONE_PLY; Value bonus = Value(d * d + 2 * d - 2); Square prevSq = to_sq((ss-1)->currentMove); update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus); } 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 when the remaining depth is zero (or, to be more precise, // less than ONE_PLY). template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { const bool PvNode = NT == PV; assert(InCheck == !!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; 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->ply = (ss-1)->ply + 1; // Check for an instant draw or if the maximum ply has been reached if (pos.is_draw() || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; 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) if (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(pos.key(), 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, 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.piece_on(from_sq(move)))) & to_sq(move) : pos.gives_check(move); // 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 && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.capture(move); // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) && type_of(move) != PROMOTION && !pos.see_ge(move, VALUE_ZERO)) 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)) continue; ss->currentMove = move; // Make and search the move pos.do_move(move, st, givesCheck); value = givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY) : -qsearch(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_cm_stats() updates countermove and follow-up move history void update_cm_stats(Stack* ss, Piece pc, Square s, Value bonus) { CounterMoveStats* cmh = (ss-1)->counterMoves; CounterMoveStats* fmh1 = (ss-2)->counterMoves; CounterMoveStats* fmh2 = (ss-4)->counterMoves; if (cmh) cmh->update(pc, s, bonus); if (fmh1) fmh1->update(pc, s, bonus); if (fmh2) fmh2->update(pc, s, bonus); } // update_stats() updates killers, history, countermove and countermove plus // follow-up move history when a new quiet best move is found. void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, Value 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->fromTo.update(c, move, bonus); thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus); update_cm_stats(ss, pos.moved_piece(move), to_sq(move), bonus); if ((ss-1)->counterMoves) { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves.update(pos.piece_on(prevSq), prevSq, move); } // Decrease all the other played quiet moves for (int i = 0; i < quietsCnt; ++i) { thisThread->fromTo.update(c, quiets[i], -bonus); thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); update_cm_stats(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); } } // 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() % weakness)) / 128; if (rootMoves[i].score + push > maxScore) { maxScore = rootMoves[i].score + push; best = rootMoves[i].pv[0]; } } return best; } // 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 check_time() { 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 (Limits.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)) Signals.stop = true; } } // namespace /// 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); 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 " << pos.this_thread()->maxPly << " 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, pos.gives_check(pv[0])); TTEntry* tte = TT.probe(pos.key(), ttHit); if (ttHit) { Move m = tte->move(); // Local copy to be SMP safe if (MoveList(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; // 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; } Stockfish-sf_8/src/search.h000066400000000000000000000064321300605702500160600ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED #include #include #include "misc.h" #include "movepick.h" #include "types.h" class Position; namespace Search { /// 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; int ply; Move currentMove; Move excludedMove; Move killers[2]; Value staticEval; Value history; bool skipEarlyPruning; int moveCount; CounterMoveStats* counterMoves; }; /// RootMove struct is used for moves at the root of the tree. For each root move /// we store a score and a PV (really a refutation in the case of moves which /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. struct RootMove { explicit RootMove(Move m) : pv(1, m) {} bool operator<(const RootMove& m) const { return m.score < score; } // Descending sort bool operator==(const Move& m) const { return pv[0] == m; } bool extract_ponder_from_tt(Position& pos); Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; std::vector pv; }; typedef std::vector RootMoves; /// LimitsType struct stores information sent by GUI about available time to /// search the current move, maximum depth/time, if we are in analysis mode or /// if we have to ponder while it's our opponent's turn to move. 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 = infinite = ponder = 0; } bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } std::vector searchmoves; int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder; int64_t nodes; TimePoint startTime; }; /// SignalsType struct stores atomic flags updated during the search, typically /// in an async fashion e.g. to stop the search by the GUI. struct SignalsType { std::atomic_bool stop, stopOnPonderhit; }; extern SignalsType Signals; extern LimitsType Limits; void init(); void clear(); template uint64_t perft(Position& pos, Depth depth); } // namespace Search #endif // #ifndef SEARCH_H_INCLUDED Stockfish-sf_8/src/syzygy/000077500000000000000000000000001300605702500160135ustar00rootroot00000000000000Stockfish-sf_8/src/syzygy/tbcore.cpp000066400000000000000000001056761300605702500200140ustar00rootroot00000000000000/* Copyright (c) 2011-2013 Ronald de Man This file may be redistributed and/or modified without restrictions. tbcore.c contains engine-independent routines of the tablebase probing code. This file should not need too much adaptation to add tablebase probing to a particular engine, provided the engine is written in C or C++. */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #endif #include "tbcore.h" #define TBMAX_PIECE 254 #define TBMAX_PAWN 256 #define HSHMAX 5 #define Swap(a,b) {int tmp=a;a=b;b=tmp;} #define TB_PAWN 1 #define TB_KNIGHT 2 #define TB_BISHOP 3 #define TB_ROOK 4 #define TB_QUEEN 5 #define TB_KING 6 #define TB_WPAWN TB_PAWN #define TB_BPAWN (TB_PAWN | 8) static LOCK_T TB_mutex; static bool initialized = false; static int num_paths = 0; static char *path_string = NULL; static char **paths = NULL; static int TBnum_piece, TBnum_pawn; static struct TBEntry_piece TB_piece[TBMAX_PIECE]; static struct TBEntry_pawn TB_pawn[TBMAX_PAWN]; static struct TBHashEntry TB_hash[1 << TBHASHBITS][HSHMAX]; #define DTZ_ENTRIES 64 static struct DTZTableEntry DTZ_table[DTZ_ENTRIES]; static void init_indices(void); static uint64 calc_key_from_pcs(int *pcs, int mirror); static void free_wdl_entry(struct TBEntry *entry); static void free_dtz_entry(struct TBEntry *entry); static FD open_tb(const char *str, const char *suffix) { int i; FD fd; char file[256]; for (i = 0; i < num_paths; i++) { strcpy(file, paths[i]); strcat(file, "/"); strcat(file, str); strcat(file, suffix); #ifndef _WIN32 fd = open(file, O_RDONLY); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif if (fd != FD_ERR) return fd; } return FD_ERR; } static void close_tb(FD fd) { #ifndef _WIN32 close(fd); #else CloseHandle(fd); #endif } static char *map_file(const char *name, const char *suffix, uint64 *mapping) { FD fd = open_tb(name, suffix); if (fd == FD_ERR) return NULL; #ifndef _WIN32 struct stat statbuf; fstat(fd, &statbuf); *mapping = statbuf.st_size; char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == (char *)(-1)) { printf("Could not mmap() %s.\n", name); exit(1); } #else DWORD size_low, size_high; size_low = GetFileSize(fd, &size_high); // *size = ((uint64)size_high) << 32 | ((uint64)size_low); HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, NULL); if (map == NULL) { printf("CreateFileMapping() failed.\n"); exit(1); } *mapping = (uint64)map; char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { printf("MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); exit(1); } #endif close_tb(fd); return data; } #ifndef _WIN32 static void unmap_file(char *data, uint64 size) { if (!data) return; munmap(data, size); } #else static void unmap_file(char *data, uint64 mapping) { if (!data) return; UnmapViewOfFile(data); CloseHandle((HANDLE)mapping); } #endif static void add_to_hash(struct TBEntry *ptr, uint64 key) { int i, hshidx; hshidx = key >> (64 - TBHASHBITS); i = 0; while (i < HSHMAX && TB_hash[hshidx][i].ptr) i++; if (i == HSHMAX) { printf("HSHMAX too low!\n"); exit(1); } else { TB_hash[hshidx][i].key = key; TB_hash[hshidx][i].ptr = ptr; } } static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'}; static void init_tb(char *str) { FD fd; struct TBEntry *entry; int i, j, pcs[16]; uint64 key, key2; int color; char *s; fd = open_tb(str, WDLSUFFIX); if (fd == FD_ERR) return; close_tb(fd); for (i = 0; i < 16; i++) pcs[i] = 0; color = 0; for (s = str; *s; s++) switch (*s) { case 'P': pcs[TB_PAWN | color]++; break; case 'N': pcs[TB_KNIGHT | color]++; break; case 'B': pcs[TB_BISHOP | color]++; break; case 'R': pcs[TB_ROOK | color]++; break; case 'Q': pcs[TB_QUEEN | color]++; break; case 'K': pcs[TB_KING | color]++; break; case 'v': color = 0x08; break; } for (i = 0; i < 8; i++) if (pcs[i] != pcs[i+8]) break; key = calc_key_from_pcs(pcs, 0); key2 = calc_key_from_pcs(pcs, 1); if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { if (TBnum_piece == TBMAX_PIECE) { printf("TBMAX_PIECE limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; } else { if (TBnum_pawn == TBMAX_PAWN) { printf("TBMAX_PAWN limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; } entry->key = key; entry->ready = 0; entry->num = 0; for (i = 0; i < 16; i++) entry->num += (ubyte)pcs[i]; entry->symmetric = (key == key2); entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); if (entry->num > Tablebases::MaxCardinality) Tablebases::MaxCardinality = entry->num; if (entry->has_pawns) { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; ptr->pawns[0] = (ubyte)pcs[TB_WPAWN]; ptr->pawns[1] = (ubyte)pcs[TB_BPAWN]; if (pcs[TB_BPAWN] > 0 && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { ptr->pawns[0] = (ubyte)pcs[TB_BPAWN]; ptr->pawns[1] = (ubyte)pcs[TB_WPAWN]; } } else { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; for (i = 0, j = 0; i < 16; i++) if (pcs[i] == 1) j++; if (j >= 3) ptr->enc_type = 0; else if (j == 2) ptr->enc_type = 2; else { /* only for suicide */ j = 16; for (i = 0; i < 16; i++) { if (pcs[i] < j && pcs[i] > 1) j = pcs[i]; ptr->enc_type = ubyte(1 + j); } } } add_to_hash(entry, key); if (key2 != key) add_to_hash(entry, key2); } void Tablebases::init(const std::string& path) { char str[16]; int i, j, k, l; if (initialized) { free(path_string); free(paths); struct TBEntry *entry; for (i = 0; i < TBnum_piece; i++) { entry = (struct TBEntry *)&TB_piece[i]; free_wdl_entry(entry); } for (i = 0; i < TBnum_pawn; i++) { entry = (struct TBEntry *)&TB_pawn[i]; free_wdl_entry(entry); } for (i = 0; i < DTZ_ENTRIES; i++) if (DTZ_table[i].entry) free_dtz_entry(DTZ_table[i].entry); } else { init_indices(); initialized = true; } const char *p = path.c_str(); if (strlen(p) == 0 || !strcmp(p, "")) return; path_string = (char *)malloc(strlen(p) + 1); strcpy(path_string, p); num_paths = 0; for (i = 0;; i++) { if (path_string[i] != SEP_CHAR) num_paths++; while (path_string[i] && path_string[i] != SEP_CHAR) i++; if (!path_string[i]) break; path_string[i] = 0; } paths = (char **)malloc(num_paths * sizeof(char *)); for (i = j = 0; i < num_paths; i++) { while (!path_string[j]) j++; paths[i] = &path_string[j]; while (path_string[j]) j++; } LOCK_INIT(TB_mutex); TBnum_piece = TBnum_pawn = 0; MaxCardinality = 0; for (i = 0; i < (1 << TBHASHBITS); i++) for (j = 0; j < HSHMAX; j++) { TB_hash[i][j].key = 0ULL; TB_hash[i][j].ptr = NULL; } for (i = 0; i < DTZ_ENTRIES; i++) DTZ_table[i].entry = NULL; for (i = 1; i < 6; i++) { sprintf(str, "K%cvK", pchr[i]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) { sprintf(str, "K%cvK%c", pchr[i], pchr[j]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) { sprintf(str, "K%c%cvK", pchr[i], pchr[j]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = 1; k < 6; k++) { sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) { sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = i; k < 6; k++) for (l = (i == k) ? j : k; l < 6; l++) { sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) for (l = 1; l < 6; l++) { sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) for (l = k; l < 6; l++) { sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } printf("info string Found %d tablebases.\n", TBnum_piece + TBnum_pawn); } static const signed char offdiag[] = { 0,-1,-1,-1,-1,-1,-1,-1, 1, 0,-1,-1,-1,-1,-1,-1, 1, 1, 0,-1,-1,-1,-1,-1, 1, 1, 1, 0,-1,-1,-1,-1, 1, 1, 1, 1, 0,-1,-1,-1, 1, 1, 1, 1, 1, 0,-1,-1, 1, 1, 1, 1, 1, 1, 0,-1, 1, 1, 1, 1, 1, 1, 1, 0 }; static const ubyte triangle[] = { 6, 0, 1, 2, 2, 1, 0, 6, 0, 7, 3, 4, 4, 3, 7, 0, 1, 3, 8, 5, 5, 8, 3, 1, 2, 4, 5, 9, 9, 5, 4, 2, 2, 4, 5, 9, 9, 5, 4, 2, 1, 3, 8, 5, 5, 8, 3, 1, 0, 7, 3, 4, 4, 3, 7, 0, 6, 0, 1, 2, 2, 1, 0, 6 }; static const ubyte invtriangle[] = { 1, 2, 3, 10, 11, 19, 0, 9, 18, 27 }; static const ubyte invdiag[] = { 0, 9, 18, 27, 36, 45, 54, 63, 7, 14, 21, 28, 35, 42, 49, 56 }; static const ubyte flipdiag[] = { 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 4, 12, 20, 28, 36, 44, 52, 60, 5, 13, 21, 29, 37, 45, 53, 61, 6, 14, 22, 30, 38, 46, 54, 62, 7, 15, 23, 31, 39, 47, 55, 63 }; static const ubyte lower[] = { 28, 0, 1, 2, 3, 4, 5, 6, 0, 29, 7, 8, 9, 10, 11, 12, 1, 7, 30, 13, 14, 15, 16, 17, 2, 8, 13, 31, 18, 19, 20, 21, 3, 9, 14, 18, 32, 22, 23, 24, 4, 10, 15, 19, 22, 33, 25, 26, 5, 11, 16, 20, 23, 25, 34, 27, 6, 12, 17, 21, 24, 26, 27, 35 }; static const ubyte diag[] = { 0, 0, 0, 0, 0, 0, 0, 8, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 10, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 0, 0, 0, 12, 4, 0, 0, 0, 0, 0, 13, 0, 0, 5, 0, 0, 0, 14, 0, 0, 0, 0, 6, 0, 15, 0, 0, 0, 0, 0, 0, 7 }; static const ubyte flap[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 12, 18, 18, 12, 6, 0, 1, 7, 13, 19, 19, 13, 7, 1, 2, 8, 14, 20, 20, 14, 8, 2, 3, 9, 15, 21, 21, 15, 9, 3, 4, 10, 16, 22, 22, 16, 10, 4, 5, 11, 17, 23, 23, 17, 11, 5, 0, 0, 0, 0, 0, 0, 0, 0 }; static const ubyte ptwist[] = { 0, 0, 0, 0, 0, 0, 0, 0, 47, 35, 23, 11, 10, 22, 34, 46, 45, 33, 21, 9, 8, 20, 32, 44, 43, 31, 19, 7, 6, 18, 30, 42, 41, 29, 17, 5, 4, 16, 28, 40, 39, 27, 15, 3, 2, 14, 26, 38, 37, 25, 13, 1, 0, 12, 24, 36, 0, 0, 0, 0, 0, 0, 0, 0 }; static const ubyte invflap[] = { 8, 16, 24, 32, 40, 48, 9, 17, 25, 33, 41, 49, 10, 18, 26, 34, 42, 50, 11, 19, 27, 35, 43, 51 }; static const ubyte invptwist[] = { 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11, 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10, 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9, 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8 }; static const ubyte file_to_file[] = { 0, 1, 2, 3, 3, 2, 1, 0 }; static const short KK_idx[10][64] = { { -1, -1, -1, 0, 1, 2, 3, 4, -1, -1, -1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }, { 58, -1, -1, -1, 59, 60, 61, 62, 63, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115}, {116,117, -1, -1, -1,118,119,120, 121,122, -1, -1, -1,123,124,125, 126,127,128,129,130,131,132,133, 134,135,136,137,138,139,140,141, 142,143,144,145,146,147,148,149, 150,151,152,153,154,155,156,157, 158,159,160,161,162,163,164,165, 166,167,168,169,170,171,172,173 }, {174, -1, -1, -1,175,176,177,178, 179, -1, -1, -1,180,181,182,183, 184, -1, -1, -1,185,186,187,188, 189,190,191,192,193,194,195,196, 197,198,199,200,201,202,203,204, 205,206,207,208,209,210,211,212, 213,214,215,216,217,218,219,220, 221,222,223,224,225,226,227,228 }, {229,230, -1, -1, -1,231,232,233, 234,235, -1, -1, -1,236,237,238, 239,240, -1, -1, -1,241,242,243, 244,245,246,247,248,249,250,251, 252,253,254,255,256,257,258,259, 260,261,262,263,264,265,266,267, 268,269,270,271,272,273,274,275, 276,277,278,279,280,281,282,283 }, {284,285,286,287,288,289,290,291, 292,293, -1, -1, -1,294,295,296, 297,298, -1, -1, -1,299,300,301, 302,303, -1, -1, -1,304,305,306, 307,308,309,310,311,312,313,314, 315,316,317,318,319,320,321,322, 323,324,325,326,327,328,329,330, 331,332,333,334,335,336,337,338 }, { -1, -1,339,340,341,342,343,344, -1, -1,345,346,347,348,349,350, -1, -1,441,351,352,353,354,355, -1, -1, -1,442,356,357,358,359, -1, -1, -1, -1,443,360,361,362, -1, -1, -1, -1, -1,444,363,364, -1, -1, -1, -1, -1, -1,445,365, -1, -1, -1, -1, -1, -1, -1,446 }, { -1, -1, -1,366,367,368,369,370, -1, -1, -1,371,372,373,374,375, -1, -1, -1,376,377,378,379,380, -1, -1, -1,447,381,382,383,384, -1, -1, -1, -1,448,385,386,387, -1, -1, -1, -1, -1,449,388,389, -1, -1, -1, -1, -1, -1,450,390, -1, -1, -1, -1, -1, -1, -1,451 }, {452,391,392,393,394,395,396,397, -1, -1, -1, -1,398,399,400,401, -1, -1, -1, -1,402,403,404,405, -1, -1, -1, -1,406,407,408,409, -1, -1, -1, -1,453,410,411,412, -1, -1, -1, -1, -1,454,413,414, -1, -1, -1, -1, -1, -1,455,415, -1, -1, -1, -1, -1, -1, -1,456 }, {457,416,417,418,419,420,421,422, -1,458,423,424,425,426,427,428, -1, -1, -1, -1, -1,429,430,431, -1, -1, -1, -1, -1,432,433,434, -1, -1, -1, -1, -1,435,436,437, -1, -1, -1, -1, -1,459,438,439, -1, -1, -1, -1, -1, -1,460,440, -1, -1, -1, -1, -1, -1, -1,461 } }; static int binomial[5][64]; static int pawnidx[5][24]; static int pfactor[5][4]; static void init_indices(void) { int i, j, k; // binomial[k-1][n] = Bin(n, k) for (i = 0; i < 5; i++) for (j = 0; j < 64; j++) { int f = j; int l = 1; for (k = 1; k <= i; k++) { f *= (j - k); l *= (k + 1); } binomial[i][j] = f / l; } for (i = 0; i < 5; i++) { int s = 0; for (j = 0; j < 6; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][0] = s; s = 0; for (; j < 12; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][1] = s; s = 0; for (; j < 18; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][2] = s; s = 0; for (; j < 24; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][3] = s; } } static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) { uint64 idx; int i, j, k, m, l, p; int n = ptr->num; if (pos[0] & 0x04) { for (i = 0; i < n; i++) pos[i] ^= 0x07; } if (pos[0] & 0x20) { for (i = 0; i < n; i++) pos[i] ^= 0x38; } for (i = 0; i < n; i++) if (offdiag[pos[i]]) break; if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i]]; switch (ptr->enc_type) { case 0: /* 111 */ i = (pos[1] > pos[0]); j = (pos[2] > pos[0]) + (pos[2] > pos[1]); if (offdiag[pos[0]]) idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); else if (offdiag[pos[1]]) idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; else if (offdiag[pos[2]]) idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; else idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); i = 3; break; case 1: /* K3 */ j = (pos[2] > pos[0]) + (pos[2] > pos[1]); idx = KK_idx[triangle[pos[0]]][pos[1]]; if (idx < 441) idx = idx + 441 * (pos[2] - j); else { idx = 441*62 + (idx - 441) + 21 * lower[pos[2]]; if (!offdiag[pos[2]]) idx -= j * 21; } i = 3; break; default: /* K2 */ idx = KK_idx[triangle[pos[0]]][pos[1]]; i = 2; break; } idx *= factor[0]; for (; i < n;) { int t = norm[i]; for (j = i; j < i + t; j++) for (k = j + 1; k < i + t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); int s = 0; for (m = i; m < i + t; m++) { p = pos[m]; for (l = 0, j = 0; l < i; l++) j += (p > pos[l]); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); i += t; } return idx; } // determine file of leftmost pawn and sort pawns static int pawn_file(struct TBEntry_pawn *ptr, int *pos) { int i; for (i = 1; i < ptr->pawns[0]; i++) if (flap[pos[0]] > flap[pos[i]]) Swap(pos[0], pos[i]); return file_to_file[pos[0] & 0x07]; } static uint64 encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) { uint64 idx; int i, j, k, m, s, t; int n = ptr->num; if (pos[0] & 0x04) for (i = 0; i < n; i++) pos[i] ^= 0x07; for (i = 1; i < ptr->pawns[0]; i++) for (j = i + 1; j < ptr->pawns[0]; j++) if (ptwist[pos[i]] < ptwist[pos[j]]) Swap(pos[i], pos[j]); t = ptr->pawns[0] - 1; idx = pawnidx[t][flap[pos[0]]]; for (i = t; i > 0; i--) idx += binomial[t - i][ptwist[pos[i]]]; idx *= factor[0]; // remaining pawns i = ptr->pawns[0]; t = i + ptr->pawns[1]; if (t > i) { for (j = i; j < t; j++) for (k = j + 1; k < t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); s = 0; for (m = i; m < t; m++) { int p = pos[m]; for (k = 0, j = 0; k < i; k++) j += (p > pos[k]); s += binomial[m - i][p - j - 8]; } idx += ((uint64)s) * ((uint64)factor[i]); i = t; } for (; i < n;) { t = norm[i]; for (j = i; j < i + t; j++) for (k = j + 1; k < i + t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); s = 0; for (m = i; m < i + t; m++) { int p = pos[m]; for (k = 0, j = 0; k < i; k++) j += (p > pos[k]); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); i += t; } return idx; } // place k like pieces on n squares static int subfactor(int k, int n) { int i, f, l; f = n; l = 1; for (i = 1; i < k; i++) { f *= n - i; l *= i + 1; } return f / l; } static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) { int i, k, n; uint64 f; static int pivfac[] = { 31332, 28056, 462 }; n = 64 - norm[0]; f = 1; for (i = norm[0], k = 0; i < num || k == order; k++) { if (k == order) { factor[0] = static_cast(f); f *= pivfac[enc_type]; } else { factor[i] = static_cast(f); f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; } } return f; } static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) { int i, k, n; uint64 f; i = norm[0]; if (order2 < 0x0f) i += norm[i]; n = 64 - i; f = 1; for (k = 0; i < num || k == order || k == order2; k++) { if (k == order) { factor[0] = static_cast(f); f *= pfactor[norm[0] - 1][file]; } else if (k == order2) { factor[norm[0]] = static_cast(f); f *= subfactor(norm[norm[0]], 48 - norm[0]); } else { factor[i] = static_cast(f); f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; } } return f; } static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces) { int i, j; for (i = 0; i < ptr->num; i++) norm[i] = 0; switch (ptr->enc_type) { case 0: norm[0] = 3; break; case 2: norm[0] = 2; break; default: norm[0] = ubyte(ptr->enc_type - 1); break; } for (i = norm[0]; i < ptr->num; i += norm[i]) for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) norm[i]++; } static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces) { int i, j; for (i = 0; i < ptr->num; i++) norm[i] = 0; norm[0] = ptr->pawns[0]; if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1]; for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i]) for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) norm[i]++; } static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64 *tb_size) { int i; int order; for (i = 0; i < ptr->num; i++) ptr->pieces[0][i] = ubyte(data[i + 1] & 0x0f); order = data[0] & 0x0f; set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]); tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type); for (i = 0; i < ptr->num; i++) ptr->pieces[1][i] = ubyte(data[i + 1] >> 4); order = data[0] >> 4; set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]); tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type); } static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64 *tb_size) { int i; int order; for (i = 0; i < ptr->num; i++) ptr->pieces[i] = ubyte(data[i + 1] & 0x0f); order = data[0] & 0x0f; set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces); tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type); } static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) { int i, j; int order, order2; j = 1 + (ptr->pawns[1] > 0); order = data[0] & 0x0f; order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[0][i] = ubyte(data[i + j] & 0x0f); set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]); tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f); order = data[0] >> 4; order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[1][i] = ubyte(data[i + j] >> 4); set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]); tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f); } static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) { int i, j; int order, order2; j = 1 + (ptr->pawns[1] > 0); order = data[0] & 0x0f; order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[i] = ubyte(data[i + j] & 0x0f); set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces); tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f); } static void calc_symlen(struct PairsData *d, int s, char *tmp) { int s1, s2; ubyte* w = d->sympat + 3 * s; s2 = (w[2] << 4) | (w[1] >> 4); if (s2 == 0x0fff) d->symlen[s] = 0; else { s1 = ((w[1] & 0xf) << 8) | w[0]; if (!tmp[s1]) calc_symlen(d, s1, tmp); if (!tmp[s2]) calc_symlen(d, s2, tmp); d->symlen[s] = ubyte(d->symlen[s1] + d->symlen[s2] + 1); } tmp[s] = 1; } ushort ReadUshort(ubyte* d) { return ushort(d[0] | (d[1] << 8)); } uint32 ReadUint32(ubyte* d) { return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); } static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 *size, unsigned char **next, ubyte *flags, int wdl) { struct PairsData *d; int i; *flags = data[0]; if (data[0] & 0x80) { d = (struct PairsData *)malloc(sizeof(struct PairsData)); d->idxbits = 0; if (wdl) d->min_len = data[1]; else d->min_len = 0; *next = data + 2; size[0] = size[1] = size[2] = 0; return d; } int blocksize = data[1]; int idxbits = data[2]; int real_num_blocks = ReadUint32(&data[4]); int num_blocks = real_num_blocks + *(ubyte *)(&data[3]); int max_len = data[8]; int min_len = data[9]; int h = max_len - min_len + 1; int num_syms = ReadUshort(&data[10 + 2 * h]); d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms); d->blocksize = blocksize; d->idxbits = idxbits; d->offset = (ushort*)(&data[10]); d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t); d->sympat = &data[12 + 2 * h]; d->min_len = min_len; *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; uint64 num_indices = (tb_size + (1ULL << idxbits) - 1) >> idxbits; size[0] = 6ULL * num_indices; size[1] = 2ULL * num_blocks; size[2] = (1ULL << blocksize) * real_num_blocks; // char tmp[num_syms]; char tmp[4096]; for (i = 0; i < num_syms; i++) tmp[i] = 0; for (i = 0; i < num_syms; i++) if (!tmp[i]) calc_symlen(d, i, tmp); d->base[h - 1] = 0; for (i = h - 2; i >= 0; i--) d->base[i] = (d->base[i + 1] + ReadUshort((ubyte*)(d->offset + i)) - ReadUshort((ubyte*)(d->offset + i + 1))) / 2; for (i = 0; i < h; i++) d->base[i] <<= 64 - (min_len + i); d->offset -= d->min_len; return d; } static int init_table_wdl(struct TBEntry *entry, char *str) { ubyte *next; int f, s; uint64 tb_size[8]; uint64 size[8 * 3]; ubyte flags; // first mmap the table into memory entry->data = map_file(str, WDLSUFFIX, &entry->mapping); if (!entry->data) { printf("Could not find %s" WDLSUFFIX, str); return 0; } ubyte *data = (ubyte *)entry->data; if (data[0] != WDL_MAGIC[0] || data[1] != WDL_MAGIC[1] || data[2] != WDL_MAGIC[2] || data[3] != WDL_MAGIC[3]) { printf("Corrupted table.\n"); unmap_file(entry->data, entry->mapping); entry->data = 0; return 0; } int split = data[4] & 0x01; int files = data[4] & 0x02 ? 4 : 1; data += 5; if (!entry->has_pawns) { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; setup_pieces_piece(ptr, data, &tb_size[0]); data += ptr->num + 1; data += ((uintptr_t)data) & 0x01; ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1); data = next; if (split) { ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1); data = next; } else ptr->precomp[1] = NULL; ptr->precomp[0]->indextable = (char *)data; data += size[0]; if (split) { ptr->precomp[1]->indextable = (char *)data; data += size[3]; } ptr->precomp[0]->sizetable = (ushort *)data; data += size[1]; if (split) { ptr->precomp[1]->sizetable = (ushort *)data; data += size[4]; } data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp[0]->data = data; data += size[2]; if (split) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp[1]->data = data; } } else { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; s = 1 + (ptr->pawns[1] > 0); for (f = 0; f < 4; f++) { setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f); data += ptr->num + s; } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1); data = next; if (split) { ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1); data = next; } else ptr->file[f].precomp[1] = NULL; } for (f = 0; f < files; f++) { ptr->file[f].precomp[0]->indextable = (char *)data; data += size[6 * f]; if (split) { ptr->file[f].precomp[1]->indextable = (char *)data; data += size[6 * f + 3]; } } for (f = 0; f < files; f++) { ptr->file[f].precomp[0]->sizetable = (ushort *)data; data += size[6 * f + 1]; if (split) { ptr->file[f].precomp[1]->sizetable = (ushort *)data; data += size[6 * f + 4]; } } for (f = 0; f < files; f++) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp[0]->data = data; data += size[6 * f + 2]; if (split) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp[1]->data = data; data += size[6 * f + 5]; } } } return 1; } static int init_table_dtz(struct TBEntry *entry) { ubyte *data = (ubyte *)entry->data; ubyte *next; int f, s; uint64 tb_size[4]; uint64 size[4 * 3]; if (!data) return 0; if (data[0] != DTZ_MAGIC[0] || data[1] != DTZ_MAGIC[1] || data[2] != DTZ_MAGIC[2] || data[3] != DTZ_MAGIC[3]) { printf("Corrupted table.\n"); return 0; } int files = data[4] & 0x02 ? 4 : 1; data += 5; if (!entry->has_pawns) { struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; setup_pieces_piece_dtz(ptr, data, &tb_size[0]); data += ptr->num + 1; data += ((uintptr_t)data) & 0x01; ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0); data = next; ptr->map = data; if (ptr->flags & 2) { int i; for (i = 0; i < 4; i++) { ptr->map_idx[i] = static_cast(data + 1 - ptr->map); data += 1 + data[0]; } data += ((uintptr_t)data) & 0x01; } ptr->precomp->indextable = (char *)data; data += size[0]; ptr->precomp->sizetable = (ushort *)data; data += size[1]; data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp->data = data; data += size[2]; } else { struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; s = 1 + (ptr->pawns[1] > 0); for (f = 0; f < 4; f++) { setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f); data += ptr->num + s; } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0); data = next; } ptr->map = data; for (f = 0; f < files; f++) { if (ptr->flags[f] & 2) { int i; for (i = 0; i < 4; i++) { ptr->map_idx[f][i] = static_cast(data + 1 - ptr->map); data += 1 + data[0]; } } } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp->indextable = (char *)data; data += size[3 * f]; } for (f = 0; f < files; f++) { ptr->file[f].precomp->sizetable = (ushort *)data; data += size[3 * f + 1]; } for (f = 0; f < files; f++) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp->data = data; data += size[3 * f + 2]; } } return 1; } template static ubyte decompress_pairs(struct PairsData *d, uint64 idx) { if (!d->idxbits) return ubyte(d->min_len); uint32 mainidx = static_cast(idx >> d->idxbits); int litidx = (idx & ((1ULL << d->idxbits) - 1)) - (1ULL << (d->idxbits - 1)); uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); if (!LittleEndian) block = BSWAP32(block); ushort idxOffset = *(ushort *)(d->indextable + 6 * mainidx + 4); if (!LittleEndian) idxOffset = ushort((idxOffset << 8) | (idxOffset >> 8)); litidx += idxOffset; if (litidx < 0) { do { litidx += d->sizetable[--block] + 1; } while (litidx < 0); } else { while (litidx > d->sizetable[block]) litidx -= d->sizetable[block++] + 1; } uint32 *ptr = (uint32 *)(d->data + (block << d->blocksize)); int m = d->min_len; ushort *offset = d->offset; base_t *base = d->base - m; ubyte *symlen = d->symlen; int sym, bitcnt; uint64 code = *((uint64 *)ptr); if (LittleEndian) code = BSWAP64(code); ptr += 2; bitcnt = 0; // number of "empty bits" in code for (;;) { int l = m; while (code < base[l]) l++; sym = offset[l]; if (!LittleEndian) sym = ((sym & 0xff) << 8) | (sym >> 8); sym += static_cast((code - base[l]) >> (64 - l)); if (litidx < (int)symlen[sym] + 1) break; litidx -= (int)symlen[sym] + 1; code <<= l; bitcnt += l; if (bitcnt >= 32) { bitcnt -= 32; uint32 tmp = *ptr++; if (LittleEndian) tmp = BSWAP32(tmp); code |= ((uint64)tmp) << bitcnt; } } ubyte *sympat = d->sympat; while (symlen[sym] != 0) { ubyte* w = sympat + (3 * sym); int s1 = ((w[1] & 0xf) << 8) | w[0]; if (litidx < (int)symlen[s1] + 1) sym = s1; else { litidx -= (int)symlen[s1] + 1; sym = (w[2] << 4) | (w[1] >> 4); } } return sympat[3 * sym]; } void load_dtz_table(char *str, uint64 key1, uint64 key2) { int i; struct TBEntry *ptr, *ptr3; struct TBHashEntry *ptr2; DTZ_table[0].key1 = key1; DTZ_table[0].key2 = key2; DTZ_table[0].entry = NULL; // find corresponding WDL entry ptr2 = TB_hash[key1 >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) if (ptr2[i].key == key1) break; if (i == HSHMAX) return; ptr = ptr2[i].ptr; ptr3 = (struct TBEntry *)malloc(ptr->has_pawns ? sizeof(struct DTZEntry_pawn) : sizeof(struct DTZEntry_piece)); ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping); ptr3->key = ptr->key; ptr3->num = ptr->num; ptr3->symmetric = ptr->symmetric; ptr3->has_pawns = ptr->has_pawns; if (ptr3->has_pawns) { struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3; entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0]; entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1]; } else { struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3; entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type; } if (!init_table_dtz(ptr3)) free(ptr3); else DTZ_table[0].entry = ptr3; } static void free_wdl_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); if (!entry->has_pawns) { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; free(ptr->precomp[0]); if (ptr->precomp[1]) free(ptr->precomp[1]); } else { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; int f; for (f = 0; f < 4; f++) { free(ptr->file[f].precomp[0]); if (ptr->file[f].precomp[1]) free(ptr->file[f].precomp[1]); } } } static void free_dtz_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); if (!entry->has_pawns) { struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; free(ptr->precomp); } else { struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; int f; for (f = 0; f < 4; f++) free(ptr->file[f].precomp); } free(entry); } static int wdl_to_map[5] = { 1, 3, 0, 2, 0 }; static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 }; Stockfish-sf_8/src/syzygy/tbcore.h000066400000000000000000000060351300605702500174460ustar00rootroot00000000000000/* Copyright (c) 2011-2013 Ronald de Man */ #ifndef TBCORE_H #define TBCORE_H #ifndef _WIN32 #include #define SEP_CHAR ':' #define FD int #define FD_ERR -1 #else #include #define SEP_CHAR ';' #define FD HANDLE #define FD_ERR INVALID_HANDLE_VALUE #endif #ifndef _WIN32 #define LOCK_T pthread_mutex_t #define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) #define LOCK(x) pthread_mutex_lock(&(x)) #define UNLOCK(x) pthread_mutex_unlock(&(x)) #else #define LOCK_T HANDLE #define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) #define LOCK(x) WaitForSingleObject(x, INFINITE) #define UNLOCK(x) ReleaseMutex(x) #endif #ifndef _MSC_VER #define BSWAP32(v) __builtin_bswap32(v) #define BSWAP64(v) __builtin_bswap64(v) #else #define BSWAP32(v) _byteswap_ulong(v) #define BSWAP64(v) _byteswap_uint64(v) #endif #define WDLSUFFIX ".rtbw" #define DTZSUFFIX ".rtbz" #define WDLDIR "RTBWDIR" #define DTZDIR "RTBZDIR" #define TBPIECES 6 typedef unsigned long long uint64; typedef unsigned int uint32; typedef unsigned char ubyte; typedef unsigned short ushort; const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d }; const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 }; #define TBHASHBITS 10 struct TBHashEntry; typedef uint64 base_t; struct PairsData { char *indextable; ushort *sizetable; ubyte *data; ushort *offset; ubyte *symlen; ubyte *sympat; int blocksize; int idxbits; int min_len; base_t base[1]; // C++ complains about base[]... }; struct TBEntry { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; } #ifndef _WIN32 __attribute__((__may_alias__)) #endif ; struct TBEntry_piece { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte enc_type; struct PairsData *precomp[2]; int factor[2][TBPIECES]; ubyte pieces[2][TBPIECES]; ubyte norm[2][TBPIECES]; }; struct TBEntry_pawn { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte pawns[2]; struct { struct PairsData *precomp[2]; int factor[2][TBPIECES]; ubyte pieces[2][TBPIECES]; ubyte norm[2][TBPIECES]; } file[4]; }; struct DTZEntry_piece { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte enc_type; struct PairsData *precomp; int factor[TBPIECES]; ubyte pieces[TBPIECES]; ubyte norm[TBPIECES]; ubyte flags; // accurate, mapped, side ushort map_idx[4]; ubyte *map; }; struct DTZEntry_pawn { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte pawns[2]; struct { struct PairsData *precomp; int factor[TBPIECES]; ubyte pieces[TBPIECES]; ubyte norm[TBPIECES]; } file[4]; ubyte flags[4]; ushort map_idx[4][4]; ubyte *map; }; struct TBHashEntry { uint64 key; struct TBEntry *ptr; }; struct DTZTableEntry { uint64 key1; uint64 key2; struct TBEntry *entry; }; #endif Stockfish-sf_8/src/syzygy/tbprobe.cpp000066400000000000000000000552201300605702500201600ustar00rootroot00000000000000/* Copyright (c) 2013 Ronald de Man This file may be redistributed and/or modified without restrictions. tbprobe.cpp contains the Stockfish-specific routines of the tablebase probing code. It should be relatively easy to adapt this code to other chess engines. */ #define NOMINMAX #include #include "../position.h" #include "../movegen.h" #include "../bitboard.h" #include "../search.h" #include "tbprobe.h" #include "tbcore.h" #include "tbcore.cpp" namespace Zobrist { extern Key psq[PIECE_NB][SQUARE_NB]; } int Tablebases::MaxCardinality = 0; // Given a position with 6 or fewer pieces, produce a text string // of the form KQPvKRP, where "KQP" represents the white pieces if // mirror == 0 and the black pieces if mirror == 1. static void prt_str(Position& pos, char *str, int mirror) { Color color; PieceType pt; int i; color = !mirror ? WHITE : BLACK; for (pt = KING; pt >= PAWN; --pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) *str++ = pchr[6 - pt]; *str++ = 'v'; color = ~color; for (pt = KING; pt >= PAWN; --pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) *str++ = pchr[6 - pt]; *str++ = 0; } // Given a position, produce a 64-bit material signature key. // If the engine supports such a key, it should equal the engine's key. static uint64 calc_key(Position& pos, int mirror) { Color color; PieceType pt; int i; uint64 key = 0; color = !mirror ? WHITE : BLACK; for (pt = PAWN; pt <= KING; ++pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) key ^= Zobrist::psq[make_piece(WHITE, pt)][i - 1]; color = ~color; for (pt = PAWN; pt <= KING; ++pt) for (i = popcount(pos.pieces(color, pt)); i > 0; i--) key ^= Zobrist::psq[make_piece(BLACK, pt)][i - 1]; return key; } // Produce a 64-bit material key corresponding to the material combination // defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white // pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black // pawns, ..., kings. static uint64 calc_key_from_pcs(int *pcs, int mirror) { int color; PieceType pt; int i; uint64 key = 0; color = !mirror ? 0 : 8; for (pt = PAWN; pt <= KING; ++pt) for (i = 0; i < pcs[color + pt]; i++) key ^= Zobrist::psq[make_piece(WHITE, pt)][i]; color ^= 8; for (pt = PAWN; pt <= KING; ++pt) for (i = 0; i < pcs[color + pt]; i++) key ^= Zobrist::psq[make_piece(BLACK, pt)][i]; return key; } bool is_little_endian() { union { int i; char c[sizeof(int)]; } x; x.i = 1; return x.c[0] == 1; } static ubyte decompress_pairs(struct PairsData *d, uint64 idx) { static const bool isLittleEndian = is_little_endian(); return isLittleEndian ? decompress_pairs(d, idx) : decompress_pairs(d, idx); } // probe_wdl_table and probe_dtz_table require similar adaptations. static int probe_wdl_table(Position& pos, int *success) { struct TBEntry *ptr; struct TBHashEntry *ptr2; uint64 idx; uint64 key; int i; ubyte res; int p[TBPIECES]; // Obtain the position's material signature key. key = pos.material_key(); // Test for KvK. if (key == (Zobrist::psq[W_KING][0] ^ Zobrist::psq[B_KING][0])) return 0; ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) if (ptr2[i].key == key) break; if (i == HSHMAX) { *success = 0; return 0; } ptr = ptr2[i].ptr; if (!ptr->ready) { LOCK(TB_mutex); if (!ptr->ready) { char str[16]; prt_str(pos, str, ptr->key != key); if (!init_table_wdl(ptr, str)) { ptr2[i].key = 0ULL; *success = 0; UNLOCK(TB_mutex); return 0; } // Memory barrier to ensure ptr->ready = 1 is not reordered. #ifdef _MSC_VER _ReadWriteBarrier(); #else __asm__ __volatile__ ("" ::: "memory"); #endif ptr->ready = 1; } UNLOCK(TB_mutex); } int bside, mirror, cmirror; if (!ptr->symmetric) { if (key != ptr->key) { cmirror = 8; mirror = 0x38; bside = (pos.side_to_move() == WHITE); } else { cmirror = mirror = 0; bside = !(pos.side_to_move() == WHITE); } } else { cmirror = pos.side_to_move() == WHITE ? 0 : 8; mirror = pos.side_to_move() == WHITE ? 0 : 0x38; bside = 0; } // p[i] is to contain the square 0-63 (A1-H8) for a piece of type // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. // Pieces of the same type are guaranteed to be consecutive. if (!ptr->has_pawns) { struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; ubyte *pc = entry->pieces[bside]; for (i = 0; i < entry->num;) { Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), (PieceType)(pc[i] & 0x07)); do { p[i++] = pop_lsb(&bb); } while (bb); } idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); res = decompress_pairs(entry->precomp[bside], idx); } else { struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; int k = entry->file[0].pieces[0][0] ^ cmirror; Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); i = 0; do { p[i++] = pop_lsb(&bb) ^ mirror; } while (bb); int f = pawn_file(entry, p); ubyte *pc = entry->file[f].pieces[bside]; for (; i < entry->num;) { bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), (PieceType)(pc[i] & 0x07)); do { p[i++] = pop_lsb(&bb) ^ mirror; } while (bb); } idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]); res = decompress_pairs(entry->file[f].precomp[bside], idx); } return ((int)res) - 2; } static int probe_dtz_table(Position& pos, int wdl, int *success) { struct TBEntry *ptr; uint64 idx; int i, res; int p[TBPIECES]; // Obtain the position's material signature key. uint64 key = pos.material_key(); if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { for (i = 1; i < DTZ_ENTRIES; i++) if (DTZ_table[i].key1 == key) break; if (i < DTZ_ENTRIES) { struct DTZTableEntry table_entry = DTZ_table[i]; for (; i > 0; i--) DTZ_table[i] = DTZ_table[i - 1]; DTZ_table[0] = table_entry; } else { struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) if (ptr2[i].key == key) break; if (i == HSHMAX) { *success = 0; return 0; } ptr = ptr2[i].ptr; char str[16]; int mirror = (ptr->key != key); prt_str(pos, str, mirror); if (DTZ_table[DTZ_ENTRIES - 1].entry) free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); for (i = DTZ_ENTRIES - 1; i > 0; i--) DTZ_table[i] = DTZ_table[i - 1]; load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror)); } } ptr = DTZ_table[0].entry; if (!ptr) { *success = 0; return 0; } int bside, mirror, cmirror; if (!ptr->symmetric) { if (key != ptr->key) { cmirror = 8; mirror = 0x38; bside = (pos.side_to_move() == WHITE); } else { cmirror = mirror = 0; bside = !(pos.side_to_move() == WHITE); } } else { cmirror = pos.side_to_move() == WHITE ? 0 : 8; mirror = pos.side_to_move() == WHITE ? 0 : 0x38; bside = 0; } if (!ptr->has_pawns) { struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; if ((entry->flags & 1) != bside && !entry->symmetric) { *success = -1; return 0; } ubyte *pc = entry->pieces; for (i = 0; i < entry->num;) { Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), (PieceType)(pc[i] & 0x07)); do { p[i++] = pop_lsb(&bb); } while (bb); } idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor); res = decompress_pairs(entry->precomp, idx); if (entry->flags & 2) res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res]; if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) res *= 2; } else { struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; int k = entry->file[0].pieces[0] ^ cmirror; Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); i = 0; do { p[i++] = pop_lsb(&bb) ^ mirror; } while (bb); int f = pawn_file((struct TBEntry_pawn *)entry, p); if ((entry->flags[f] & 1) != bside) { *success = -1; return 0; } ubyte *pc = entry->file[f].pieces; for (; i < entry->num;) { bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), (PieceType)(pc[i] & 0x07)); do { p[i++] = pop_lsb(&bb) ^ mirror; } while (bb); } idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor); res = decompress_pairs(entry->file[f].precomp, idx); if (entry->flags[f] & 2) res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res]; if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1)) res *= 2; } return res; } // Add underpromotion captures to list of captures. static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end) { ExtMove *moves, *extra = end; for (moves = stack; moves < end; moves++) { Move move = moves->move; if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) { (*extra++).move = (Move)(move - (1 << 12)); (*extra++).move = (Move)(move - (2 << 12)); (*extra++).move = (Move)(move - (3 << 12)); } } return extra; } static int probe_ab(Position& pos, int alpha, int beta, int *success) { int v; ExtMove stack[64]; ExtMove *moves, *end; StateInfo st; // Generate (at least) all legal non-ep captures including (under)promotions. // It is OK to generate more, as long as they are filtered out below. if (!pos.checkers()) { end = generate(pos, stack); // Since underpromotion captures are not included, we need to add them. end = add_underprom_caps(pos, stack, end); } else end = generate(pos, stack); for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (!pos.capture(capture) || type_of(capture) == ENPASSANT || !pos.legal(capture)) continue; pos.do_move(capture, st, pos.gives_check(capture)); v = -probe_ab(pos, -beta, -alpha, success); pos.undo_move(capture); if (*success == 0) return 0; if (v > alpha) { if (v >= beta) { *success = 2; return v; } alpha = v; } } v = probe_wdl_table(pos, success); if (*success == 0) return 0; if (alpha >= v) { *success = 1 + (alpha > 0); return alpha; } else { *success = 1; return v; } } // Probe the WDL table for a particular position. // If *success != 0, 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 int Tablebases::probe_wdl(Position& pos, int *success) { int v; *success = 1; v = probe_ab(pos, -2, 2, success); // If en passant is not possible, we are done. if (pos.ep_square() == SQ_NONE) return v; if (!(*success)) return 0; // Now handle en passant. int v1 = -3; // Generate (at least) all legal en passant captures. ExtMove stack[192]; ExtMove *moves, *end; StateInfo st; if (!pos.checkers()) end = generate(pos, stack); else end = generate(pos, stack); for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) != ENPASSANT || !pos.legal(capture)) continue; pos.do_move(capture, st, pos.gives_check(capture)); int v0 = -probe_ab(pos, -2, 2, success); pos.undo_move(capture); if (*success == 0) return 0; if (v0 > v1) v1 = v0; } if (v1 > -3) { if (v1 >= v) v = v1; else if (v == 0) { // Check whether there is at least one legal non-ep move. for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) == ENPASSANT) continue; if (pos.legal(capture)) break; } if (moves == end && !pos.checkers()) { end = generate(pos, end); for (; moves < end; moves++) { Move move = moves->move; if (pos.legal(move)) break; } } // If not, then we are forced to play the losing ep capture. if (moves == end) v = v1; } } return v; } // This routine treats a position with en passant captures as one without. static int probe_dtz_no_ep(Position& pos, int *success) { int wdl, dtz; wdl = probe_ab(pos, -2, 2, success); if (*success == 0) return 0; if (wdl == 0) return 0; if (*success == 2) return wdl == 2 ? 1 : 101; ExtMove stack[192]; ExtMove *moves, *end = NULL; StateInfo st; if (wdl > 0) { // Generate at least all legal non-capturing pawn moves // including non-capturing promotions. if (!pos.checkers()) end = generate(pos, stack); else end = generate(pos, stack); for (moves = stack; moves < end; moves++) { Move move = moves->move; if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) || !pos.legal(move)) continue; pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_wdl(pos, success); pos.undo_move(move); if (*success == 0) return 0; if (v == wdl) return v == 2 ? 1 : 101; } } dtz = 1 + probe_dtz_table(pos, wdl, success); if (*success >= 0) { if (wdl & 1) dtz += 100; return wdl >= 0 ? dtz : -dtz; } if (wdl > 0) { int best = 0xffff; for (moves = stack; moves < end; moves++) { Move move = moves->move; if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN || !pos.legal(move)) continue; pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_dtz(pos, success); pos.undo_move(move); if (*success == 0) return 0; if (v > 0 && v + 1 < best) best = v + 1; } return best; } else { int best = -1; if (!pos.checkers()) end = generate(pos, stack); else end = generate(pos, stack); for (moves = stack; moves < end; moves++) { int v; Move move = moves->move; if (!pos.legal(move)) continue; pos.do_move(move, st, pos.gives_check(move)); if (st.rule50 == 0) { if (wdl == -2) v = -1; else { v = probe_ab(pos, 1, 2, success); v = (v == 2) ? 0 : -101; } } else { v = -Tablebases::probe_dtz(pos, success) - 1; } pos.undo_move(move); if (*success == 0) return 0; if (v < best) best = v; } return best; } } static int wdl_to_dtz[] = { -1, -101, 0, 101, 1 }; // Probe the DTZ table for a particular position. // If *success != 0, 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, int *success) { *success = 1; int v = probe_dtz_no_ep(pos, success); if (pos.ep_square() == SQ_NONE) return v; if (*success == 0) return 0; // Now handle en passant. int v1 = -3; ExtMove stack[192]; ExtMove *moves, *end; StateInfo st; if (!pos.checkers()) end = generate(pos, stack); else end = generate(pos, stack); for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) != ENPASSANT || !pos.legal(capture)) continue; pos.do_move(capture, st, pos.gives_check(capture)); int v0 = -probe_ab(pos, -2, 2, success); pos.undo_move(capture); if (*success == 0) return 0; if (v0 > v1) v1 = v0; } if (v1 > -3) { v1 = wdl_to_dtz[v1 + 2]; if (v < -100) { if (v1 >= 0) v = v1; } else if (v < 0) { if (v1 >= 0 || v1 < -100) v = v1; } else if (v > 100) { if (v1 > 0) v = v1; } else if (v > 0) { if (v1 == 1) v = v1; } else if (v1 >= 0) { v = v1; } else { for (moves = stack; moves < end; moves++) { Move move = moves->move; if (type_of(move) == ENPASSANT) continue; if (pos.legal(move)) break; } if (moves == end && !pos.checkers()) { end = generate(pos, end); for (; moves < end; moves++) { Move move = moves->move; if (pos.legal(move)) break; } } if (moves == end) v = v1; } } return v; } // 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; } } static Value wdl_to_Value[5] = { -VALUE_MATE + MAX_PLY + 1, VALUE_DRAW - 2, VALUE_DRAW, VALUE_DRAW + 2, VALUE_MATE - MAX_PLY - 1 }; // 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) { int success; int dtz = probe_dtz(pos, &success); if (!success) 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, pos.gives_check(move)); int v = 0; if (pos.checkers() && dtz > 0) { ExtMove s[192]; if (generate(pos, s) == s) v = 1; } if (!v) { if (st.rule50 != 0) { v = -Tablebases::probe_dtz(pos, &success); if (v > 0) v++; else if (v < 0) v--; } else { v = -Tablebases::probe_wdl(pos, &success); v = wdl_to_dtz[v + 2]; } } pos.undo_move(move); if (!success) 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->rule50; // Use 50-move counter to determine whether the root position is // won, lost or drawn. int wdl = 0; if (dtz > 0) wdl = (dtz + cnt50 <= 100) ? 2 : 1; else if (dtz < 0) wdl = (-dtz + cnt50 <= 100) ? -2 : -1; // 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 == 1 && dtz <= 100) score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); else if (wdl == -1 && 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) { int success; int wdl = Tablebases::probe_wdl(pos, &success); if (!success) return false; score = wdl_to_Value[wdl + 2]; StateInfo st; int best = -2; // Probe each move. for (size_t i = 0; i < rootMoves.size(); i++) { Move move = rootMoves[i].pv[0]; pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_wdl(pos, &success); pos.undo_move(move); if (!success) 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; } Stockfish-sf_8/src/syzygy/tbprobe.h000066400000000000000000000007311300605702500176220ustar00rootroot00000000000000#ifndef TBPROBE_H #define TBPROBE_H #include "../search.h" namespace Tablebases { extern int MaxCardinality; void init(const std::string& path); int probe_wdl(Position& pos, int *success); int probe_dtz(Position& pos, int *success); 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); } #endif Stockfish-sf_8/src/thread.cpp000066400000000000000000000132421300605702500164120ustar00rootroot00000000000000/* 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-2016 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 . */ #include // For std::count #include #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 then waits until it goes to sleep /// in idle_loop(). Thread::Thread() { resetCalls = exit = false; maxPly = callsCnt = 0; tbHits = 0; history.clear(); counterMoves.clear(); idx = Threads.size(); // Start from 0 std::unique_lock lk(mutex); searching = true; nativeThread = std::thread(&Thread::idle_loop, this); sleepCondition.wait(lk, [&]{ return !searching; }); } /// Thread destructor waits for thread termination before returning Thread::~Thread() { mutex.lock(); exit = true; sleepCondition.notify_one(); mutex.unlock(); nativeThread.join(); } /// Thread::wait_for_search_finished() waits on sleep condition /// until not searching void Thread::wait_for_search_finished() { std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return !searching; }); } /// Thread::wait() waits on sleep condition until condition is true void Thread::wait(std::atomic_bool& condition) { std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return bool(condition); }); } /// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching(bool resume) { std::unique_lock lk(mutex); if (!resume) searching = true; sleepCondition.notify_one(); } /// Thread::idle_loop() is where the thread is parked when it has no work to do void Thread::idle_loop() { while (!exit) { std::unique_lock lk(mutex); searching = false; while (!searching && !exit) { sleepCondition.notify_one(); // Wake up any waiting thread sleepCondition.wait(lk); } lk.unlock(); if (!exit) search(); } } /// ThreadPool::init() creates and launches requested threads that will go /// immediately to sleep. We cannot use a constructor because Threads is a /// static object and we need a fully initialized engine at this point due to /// allocation of Endgames in the Thread constructor. void ThreadPool::init() { push_back(new MainThread); read_uci_options(); } /// ThreadPool::exit() terminates threads before the program exits. Cannot be /// done in destructor because threads must be terminated before deleting any /// static objects while still in main(). void ThreadPool::exit() { while (size()) delete back(), pop_back(); } /// ThreadPool::read_uci_options() updates internal threads parameters from the /// corresponding UCI options and creates/destroys threads to match requested /// number. Thread objects are dynamically allocated. void ThreadPool::read_uci_options() { size_t requested = Options["Threads"]; assert(requested > 0); while (size() < requested) push_back(new Thread); while (size() > requested) delete back(), pop_back(); } /// ThreadPool::nodes_searched() returns the number of nodes searched uint64_t ThreadPool::nodes_searched() const { uint64_t nodes = 0; for (Thread* th : *this) nodes += th->rootPos.nodes_searched(); return nodes; } /// ThreadPool::tb_hits() returns the number of TB hits uint64_t ThreadPool::tb_hits() const { uint64_t hits = 0; for (Thread* th : *this) hits += th->tbHits; return hits; } /// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop() /// and starts a new search, then returns immediately. void ThreadPool::start_thinking(Position& pos, StateListPtr& states, const Search::LimitsType& limits) { main()->wait_for_search_finished(); Search::Signals.stopOnPonderhit = Search::Signals.stop = false; Search::Limits = limits; Search::RootMoves rootMoves; for (const auto& m : MoveList(pos)) if ( limits.searchmoves.empty() || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) rootMoves.push_back(Search::RootMove(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 StateInfo tmp = setupStates->back(); for (Thread* th : Threads) { th->maxPly = 0; th->tbHits = 0; th->rootDepth = DEPTH_ZERO; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } setupStates->back() = tmp; // Restore st->previous, cleared by Position::set() main()->start_searching(); } Stockfish-sf_8/src/thread.h000066400000000000000000000061611300605702500160610ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef THREAD_H_INCLUDED #define THREAD_H_INCLUDED #include #include #include #include #include #include #include "material.h" #include "movepick.h" #include "pawns.h" #include "position.h" #include "search.h" #include "thread_win32.h" /// Thread struct keeps together all the thread-related stuff. We also 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 { std::thread nativeThread; Mutex mutex; ConditionVariable sleepCondition; bool exit, searching; public: Thread(); virtual ~Thread(); virtual void search(); void idle_loop(); void start_searching(bool resume = false); void wait_for_search_finished(); void wait(std::atomic_bool& b); Pawns::Table pawnsTable; Material::Table materialTable; Endgames endgames; size_t idx, PVIdx; int maxPly, callsCnt; uint64_t tbHits; Position rootPos; Search::RootMoves rootMoves; Depth rootDepth; Depth completedDepth; std::atomic_bool resetCalls; HistoryStats history; MoveStats counterMoves; FromToStats fromTo; CounterMoveHistoryStats counterMoveHistory; }; /// MainThread is a derived class with a specific overload for the main thread struct MainThread : public Thread { virtual void search(); bool easyMovePlayed, failedLow; double bestMoveChanges; Value previousScore; }; /// ThreadPool struct handles all the threads-related stuff like init, starting, /// parking and, most importantly, launching a thread. All the access to threads /// data is done through this class. struct ThreadPool : public std::vector { void init(); // No constructor and destructor, threads rely on globals that should void exit(); // be initialized and valid during the whole thread lifetime. MainThread* main() { return static_cast(at(0)); } void start_thinking(Position&, StateListPtr&, const Search::LimitsType&); void read_uci_options(); uint64_t nodes_searched() const; uint64_t tb_hits() const; private: StateListPtr setupStates; }; extern ThreadPool Threads; #endif // #ifndef THREAD_H_INCLUDED Stockfish-sf_8/src/thread_win32.h000066400000000000000000000047331300605702500171060ustar00rootroot00000000000000/* 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-2016 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 . */ #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 #include #if defined(_WIN32) && !defined(_MSC_VER) #ifndef NOMINMAX # define NOMINMAX // Disable macros min() and max() #endif #define WIN32_LEAN_AND_MEAN #include #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 Stockfish-sf_8/src/timeman.cpp000066400000000000000000000120651300605702500165770ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #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 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(hypMyTime, hypMTG, ply, slowMover); int t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); optimumTime = std::min(t1, optimumTime); maximumTime = std::min(t2, maximumTime); } if (Options["Ponder"]) optimumTime += optimumTime / 4; } Stockfish-sf_8/src/timeman.h000066400000000000000000000032041300605702500162370ustar00rootroot00000000000000/* 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-2016 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 . */ #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 Stockfish-sf_8/src/tt.cpp000066400000000000000000000103111300605702500155640ustar00rootroot00000000000000/* 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-2016 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 . */ #include // For std::memset #include #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 = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster)); if (newClusterCount == clusterCount) return; clusterCount = newClusterCount; free(mem); mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 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)); } /// 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; } Stockfish-sf_8/src/tt.h000066400000000000000000000073201300605702500152370ustar00rootroot00000000000000/* 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-2016 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 . */ #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 lowest order bits of the key are used to get the index of the cluster TTEntry* first_entry(const Key key) const { return &table[(size_t)key & (clusterCount - 1)].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 Stockfish-sf_8/src/types.h000066400000000000000000000303461300605702500157600ustar00rootroot00000000000000/* 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-2016 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 . */ #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 #include #include #include #include #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 // Microsoft header for _BitScanForward64() # define IS_64BIT #endif #if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include // Intel and Microsoft header for _mm_popcnt_u64() #endif #if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include // Intel and Microsoft header for _mm_prefetch() #endif #if defined(USE_PEXT) # include // 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, NO_COLOR, 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 struct MakeCastling { static const 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 = 188, PawnValueEg = 248, KnightValueMg = 753, KnightValueEg = 832, BishopValueMg = 826, BishopValueEg = 897, RookValueMg = 1285, RookValueEg = 1371, QueenValueMg = 2513, QueenValueEg = 2650, MidgameLimit = 15258, EndgameLimit = 3915 }; enum PieceType { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES = 0, 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 }; const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; extern Value PieceValue[PHASE_NB][PIECE_NB]; enum Depth { ONE_PLY = 1, 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 { 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, NORTH = 8, EAST = 1, SOUTH = -8, WEST = -1, 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 }; inline 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) \ inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ inline T operator*(int i, T d) { return T(i * int(d)); } \ inline T operator*(T d, int i) { return T(int(d) * i); } \ inline 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; } \ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_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); } \ inline T operator/(T d, int i) { return T(int(d) / i); } \ inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(PieceType) ENABLE_FULL_OPERATORS_ON(Piece) ENABLE_FULL_OPERATORS_ON(Color) ENABLE_FULL_OPERATORS_ON(Depth) ENABLE_FULL_OPERATORS_ON(Square) ENABLE_FULL_OPERATORS_ON(File) ENABLE_FULL_OPERATORS_ON(Rank) ENABLE_BASE_OPERATORS_ON(Score) #undef ENABLE_FULL_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON /// Additional operators to add integers to a Value inline Value operator+(Value v, int i) { return Value(int(v) + i); } inline 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; } /// 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. inline Score operator*(Score s1, Score s2); /// 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); } inline Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } inline Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } inline Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } inline CastlingRight operator|(Color c, CastlingSide s) { return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); } inline Value mate_in(int ply) { return VALUE_MATE - ply; } inline Value mated_in(int ply) { return -VALUE_MATE + ply; } inline Square make_square(File f, Rank r) { return Square((r << 3) + f); } inline Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt); } inline PieceType type_of(Piece pc) { return PieceType(pc & 7); } inline Color color_of(Piece pc) { assert(pc != NO_PIECE); return Color(pc >> 3); } inline bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } inline File file_of(Square s) { return File(s & 7); } inline Rank rank_of(Square s) { return Rank(s >> 3); } inline Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); } inline Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); } inline 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; } inline Square pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } inline Square from_sq(Move m) { return Square((m >> 6) & 0x3F); } inline Square to_sq(Move m) { return Square(m & 0x3F); } inline MoveType type_of(Move m) { return MoveType(m & (3 << 14)); } inline 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 inline Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } inline bool is_ok(Move m) { return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE } #endif // #ifndef TYPES_H_INCLUDED Stockfish-sf_8/src/uci.cpp000066400000000000000000000222231300605702500157220ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #include "evaluate.h" #include "movegen.h" #include "position.h" #include "search.h" #include "thread.h" #include "timeman.h" #include "uci.h" using namespace std; extern void benchmark(const Position& pos, istream& is); namespace { // FEN string of the initial position, normal chess const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // 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. StateListPtr States(new std::deque(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) { 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(1)); 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->push_back(StateInfo()); pos.do_move(m, States->back(), pos.gives_check(m)); } } // 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) { Search::LimitsType limits; string token; 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 == "infinite") limits.infinite = 1; else if (token == "ponder") limits.ponder = 1; Threads.start_thinking(pos, States, limits); } } // 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; pos.set(StartFEN, false, &States->back(), Threads.main()); 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(); // getline() could return empty or blank line is >> skipws >> token; // The GUI sends 'ponderhit' to tell us to ponder on the same move the // opponent has played. In case Signals.stopOnPonderhit is set we are // waiting for 'ponderhit' to stop the search (for instance because we // already ran out of time), otherwise we should continue searching but // switching from pondering to normal search. if ( token == "quit" || token == "stop" || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) { Search::Signals.stop = true; Threads.main()->start_searching(true); // Could be sleeping } else if (token == "ponderhit") Search::Limits.ponder = 0; // Switch to normal search else if (token == "uci") sync_cout << "id name " << engine_info(true) << "\n" << Options << "\nuciok" << sync_endl; else if (token == "ucinewgame") { Search::clear(); Time.availableNodes = 0; } else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "go") go(pos, is); else if (token == "position") position(pos, is); else if (token == "setoption") setoption(is); // Additional custom non-UCI commands, useful for debugging else if (token == "flip") pos.flip(); else if (token == "bench") benchmark(pos, is); else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "perft") { int depth; stringstream ss; is >> depth; ss << Options["Hash"] << " " << Options["Threads"] << " " << depth << " current perft"; benchmark(pos, ss); } else sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour Threads.main()->wait_for_search_finished(); } /// UCI::value() converts a Value to a string suitable for use with the UCI /// protocol specification: /// /// cp The score from the engine's point of view in centipawns. /// mate Mate in y moves, not plies. If the engine is getting mated /// use negative values for y. string UCI::value(Value v) { 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(pos)) if (str == UCI::move(m, pos.is_chess960())) return m; return MOVE_NONE; } Stockfish-sf_8/src/uci.h000066400000000000000000000044431300605702500153730ustar00rootroot00000000000000/* 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-2016 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 . */ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED #include #include #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 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 min, int max, 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 Stockfish-sf_8/src/ucioption.cpp000066400000000000000000000121731300605702500171560ustar00rootroot00000000000000/* 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-2016 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 . */ #include #include #include #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&) { Threads.read_uci_options(); } 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) { const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048; o["Debug Log File"] << Option("", on_logger); o["Contempt"] << Option(0, -100, 100); o["Threads"] << Option(1, 1, 128, 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("", 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