diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..86072ca4470ff03814b0dace044f13e305ceb4f2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +stockfish-4-win/Windows/stockfish_4_32bit.exe filter=lfs diff=lfs merge=lfs -text +stockfish-4-win/Windows/stockfish_4_x64_modern_sse42.exe filter=lfs diff=lfs merge=lfs -text +stockfish-4-win/Windows/stockfish_4_x64_modern.exe filter=lfs diff=lfs merge=lfs -text +stockfish-4-win/Windows/stockfish_4_x64.exe filter=lfs diff=lfs merge=lfs -text diff --git a/stockfish-4-win/.DS_Store b/stockfish-4-win/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..11c5d585b1893b91d81ef2f3111591f675ac19aa Binary files /dev/null and b/stockfish-4-win/.DS_Store differ diff --git a/stockfish-4-win/Copying.txt b/stockfish-4-win/Copying.txt new file mode 100644 index 0000000000000000000000000000000000000000..818433ecc0e094a4db1023c68b33f24344643ad8 --- /dev/null +++ b/stockfish-4-win/Copying.txt @@ -0,0 +1,674 @@ + 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 +. diff --git a/stockfish-4-win/Readme.md b/stockfish-4-win/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..5ea7a2c793ce1564293908ad811d764fba3eecf8 --- /dev/null +++ b/stockfish-4-win/Readme.md @@ -0,0 +1,70 @@ +### Overview + +Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is +not a complete chess program and requires some UCI-compatible GUI +(e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess +Partner or Fritz) in order to be used comfortably. Read the +documentation for your GUI of choice for information about how to use +Stockfish with it. + +This version of Stockfish supports up to 64 CPUs. The engine defaults +to one search thread 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. + + +### 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. For further + information about how to compile Stockfish yourself read section below. + + * polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot + adapter. + + +### Opening books + +This version of Stockfish has support for PolyGlot opening books. For +information about how to create such books, consult the PolyGlot +documentation. The book file can be selected by setting the *Book File* +UCI parameter. + + +### 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 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* diff --git a/stockfish-4-win/Windows/stockfish_4_32bit.exe b/stockfish-4-win/Windows/stockfish_4_32bit.exe new file mode 100644 index 0000000000000000000000000000000000000000..00a8bf0e58b8af58619f475b9af4be0d21594000 --- /dev/null +++ b/stockfish-4-win/Windows/stockfish_4_32bit.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48d7df295a019b0e84310e8b0264153e1b6e14808996191212115282ee38b9a6 +size 860672 diff --git a/stockfish-4-win/Windows/stockfish_4_x64.exe b/stockfish-4-win/Windows/stockfish_4_x64.exe new file mode 100644 index 0000000000000000000000000000000000000000..11e9c5d0573ea5d753a8bb8a75d4e5f029057cb2 --- /dev/null +++ b/stockfish-4-win/Windows/stockfish_4_x64.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41403082407022143f8b981d0e1b4e32f75101d7c7a9d440da9fbda9e3ae77bd +size 1051648 diff --git a/stockfish-4-win/Windows/stockfish_4_x64_modern.exe b/stockfish-4-win/Windows/stockfish_4_x64_modern.exe new file mode 100644 index 0000000000000000000000000000000000000000..c0d9ca0d450c81e1ca9b4c58cc3179da43768af6 --- /dev/null +++ b/stockfish-4-win/Windows/stockfish_4_x64_modern.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4a70a652171e2d55f3d0fcafe5bb1c88afd82645ad3b4cd03a589c71ebac85e +size 1046528 diff --git a/stockfish-4-win/Windows/stockfish_4_x64_modern_sse42.exe b/stockfish-4-win/Windows/stockfish_4_x64_modern_sse42.exe new file mode 100644 index 0000000000000000000000000000000000000000..63d66f1ec47df252e096b18aaeef7ff1b1e8da48 --- /dev/null +++ b/stockfish-4-win/Windows/stockfish_4_x64_modern_sse42.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3920c8b7e541c61e9eb4863b1599e47a4e4b01718835bcf8b79097632cd56de +size 1046528 diff --git a/stockfish-4-win/logo.bmp b/stockfish-4-win/logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a3c59a7022419ced13e52af5de517eb0f5bd95c0 Binary files /dev/null and b/stockfish-4-win/logo.bmp differ diff --git a/stockfish-4-win/polyglot.ini b/stockfish-4-win/polyglot.ini new file mode 100644 index 0000000000000000000000000000000000000000..26320f4d9ea5cabecb15156e64120c73f8fc8a70 --- /dev/null +++ b/stockfish-4-win/polyglot.ini @@ -0,0 +1,47 @@ +[PolyGlot] + +EngineDir = . +EngineCommand = ./stockfish + +Book = false +BookFile = book.bin + +Log = false +LogFile = stockfish.log + +Resign = true +ResignScore = 600 + +[Engine] + +Write Debug Log = false +Write Search Log = false +Search Log Filename = SearchLog.txt +Book File = book.bin +Best Book Move = false +Contempt Factor = 0 +Mobility (Midgame) = 100 +Mobility (Endgame) = 100 +Pawn Structure (Midgame) = 100 +Pawn Structure (Endgame) = 100 +Passed Pawns (Midgame) = 100 +Passed Pawns (Endgame) = 100 +Space = 100 +Aggressiveness = 100 +Cowardice = 100 +Min Split Depth = 0 +Max Threads per Split Point = 5 +Threads = 1 +Idle Threads Sleep = false +Hash = 128 +Ponder = true +OwnBook = false +MultiPV = 1 +Skill Level = 20 +Emergency Move Horizon = 40 +Emergency Base Time = 200 +Emergency Move Time = 70 +Minimum Thinking Time = 20 +Slow Mover = 100 +UCI_Chess960 = false +UCI_AnalyseMode = false diff --git a/stockfish-4-win/src/Makefile b/stockfish-4-win/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f8d65ccb32eddc33395199087984a3145b784aa0 --- /dev/null +++ b/stockfish-4-win/src/Makefile @@ -0,0 +1,565 @@ +# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 +# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) +# Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +UNAME = $(shell uname) + +### Executable name +EXE = stockfish + +### Installation dir definitions +PREFIX = /usr/local +# Haiku has a non-standard filesystem layout +ifeq ($(UNAME),Haiku) + PREFIX=/boot/common +endif +BINDIR = $(PREFIX)/bin + +### Built-in benchmark for pgo-builds and signature +PGOBENCH = ./$(EXE) bench 32 1 10 default depth +SIGNBENCH = ./$(EXE) bench + +### Object files +OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \ + material.o misc.o movegen.o movepick.o notation.o pawns.o position.o \ + search.o thread.o timeman.o tt.o uci.o ucioption.o + +### ========================================================================== +### Section 2. High-level Configuration +### ========================================================================== +# +# flag --- Comp switch --- Description +# ---------------------------------------------------------------------------- +# +# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode +# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations +# arch = (name) --- (-arch) --- Target architecture +# os = (name) --- --- Target operating system +# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system +# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction +# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only +# with GCC and ICC 64-bit) +# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction +# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# +# 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 +debug = no +optimize = yes + +### 2.2 Architecture specific + +# General-section +ifeq ($(ARCH),general-64) + arch = any + os = any + bits = 64 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),general-32) + arch = any + os = any + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +# x86-section +ifeq ($(ARCH),x86-64) + arch = x86_64 + os = any + bits = 64 + prefetch = yes + bsfq = yes + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),x86-64-modern) + arch = x86_64 + os = any + bits = 64 + prefetch = yes + bsfq = yes + popcnt = yes + sse = yes +endif + +ifeq ($(ARCH),x86-32) + arch = i386 + os = any + bits = 32 + prefetch = yes + bsfq = no + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),x86-32-old) + arch = i386 + os = any + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +#arm section +ifeq ($(ARCH),armv7) + arch = armv7 + os = any + bits = 32 + prefetch = yes + bsfq = yes + popcnt = no + sse = no +endif + +# osx-section +ifeq ($(ARCH),osx-ppc-64) + arch = ppc64 + os = osx + bits = 64 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),osx-ppc-32) + arch = ppc + os = osx + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),osx-x86-64) + arch = x86_64 + os = osx + bits = 64 + prefetch = yes + bsfq = yes + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),osx-x86-32) + arch = i386 + os = osx + bits = 32 + prefetch = yes + bsfq = no + popcnt = no + sse = yes +endif + + +### ========================================================================== +### Section 3. Low-level configuration +### ========================================================================== + +### 3.1 Selecting compiler (default = gcc) +ifeq ($(COMP),) + COMP=gcc +endif + +ifeq ($(COMP),mingw) + comp=mingw + CXX=g++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +ifeq ($(COMP),gcc) + comp=gcc + CXX=g++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +ifeq ($(COMP),icc) + comp=icc + CXX=icpc + profile_prepare = icc-profile-prepare + profile_make = icc-profile-make + profile_use = icc-profile-use + profile_clean = icc-profile-clean +endif + +ifeq ($(COMP),clang) + comp=clang + CXX=clang++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +### 3.2 General compiler settings +CXXFLAGS = -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS) + +ifeq ($(comp),gcc) + CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow +endif + +ifeq ($(comp),mingw) + CXXFLAGS += -Wextra -Wshadow +endif + +ifeq ($(comp),icc) + CXXFLAGS += -wd383,981,1418,1419,1476,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi +endif + +ifeq ($(comp),clang) + CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow +endif + +ifeq ($(os),osx) + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.0 +endif + +### 3.3 General linker settings +LDFLAGS = $(EXTRALDFLAGS) + +ifeq ($(comp),mingw) + LDFLAGS += -static-libstdc++ -static-libgcc +endif + +### On mingw use Windows threads, otherwise POSIX +ifneq ($(comp),mingw) + # Haiku has pthreads in its libroot, so only link it in on other platforms + ifneq ($(UNAME),Haiku) + LDFLAGS += -lpthread + endif +endif + +ifeq ($(os),osx) + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.0 +endif + +### 3.4 Debugging +ifeq ($(debug),no) + CXXFLAGS += -DNDEBUG +else + CXXFLAGS += -g +endif + +### 3.5 Optimization +ifeq ($(optimize),yes) + + ifeq ($(comp),gcc) + CXXFLAGS += -O3 + + ifeq ($(os),osx) + ifeq ($(arch),i386) + CXXFLAGS += -mdynamic-no-pic + endif + ifeq ($(arch),x86_64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + + ifeq ($(arch),armv7) + CXXFLAGS += -fno-gcse + endif + endif + + ifeq ($(comp),mingw) + CXXFLAGS += -O3 + endif + + ifeq ($(comp),icc) + ifeq ($(os),osx) + CXXFLAGS += -fast -mdynamic-no-pic + else + CXXFLAGS += -O3 + endif + endif + + ifeq ($(comp),clang) + ### -O4 requires a linker that supports LLVM's LTO + CXXFLAGS += -O3 + + ifeq ($(os),osx) + ifeq ($(arch),i386) + CXXFLAGS += -mdynamic-no-pic + endif + ifeq ($(arch),x86_64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + endif +endif + +### 3.6. Bits +ifeq ($(bits),64) + CXXFLAGS += -DIS_64BIT +endif + +### 3.7 prefetch +ifeq ($(prefetch),yes) + ifeq ($(sse),yes) + CXXFLAGS += -msse + DEPENDFLAGS += -msse + endif +else + CXXFLAGS += -DNO_PREFETCH +endif + +### 3.8 bsfq +ifeq ($(bsfq),yes) + CXXFLAGS += -DUSE_BSFQ +endif + +### 3.9 popcnt +ifeq ($(popcnt),yes) + CXXFLAGS += -msse3 -DUSE_POPCNT +endif + +### 3.10 Link Time Optimization, it works since gcc 4.5 but not on mingw. +### 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) + GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.` + GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.` + ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \))) + CXXFLAGS += -flto + LDFLAGS += $(CXXFLAGS) + endif + endif + endif +endif + +### ========================================================================== +### Section 4. Public targets +### ========================================================================== + +help: + @echo "" + @echo "To compile stockfish, type: " + @echo "" + @echo "make target ARCH=arch [COMP=comp]" + @echo "" + @echo "Supported targets:" + @echo "" + @echo "build > Standard build" + @echo "signature-build > Standard build with embedded signature" + @echo "profile-build > PGO build" + @echo "signature-profile-build > PGO build with embedded signature" + @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-32 > x86 32-bit with SSE support" + @echo "x86-32-old > x86 32-bit fall back for old hardware" + @echo "osx-ppc-64 > PPC-Mac OS X 64 bit" + @echo "osx-ppc-32 > PPC-Mac OS X 32 bit" + @echo "osx-x86-64 > x86-Mac OS X 64 bit" + @echo "osx-x86-32 > x86-Mac OS X 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 "Non-standard targets:" + @echo "" + @echo "make hpux > Compile for HP-UX. Compiler = aCC" + @echo "" + @echo "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 "" + +.PHONY: build profile-build embed-signature +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 + $(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 + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) + @echo "" + @echo "Step 4/4. Deleting profile data ..." + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) + +embed-signature: + @echo "Running benchmark for getting the signature ..." + @$(SIGNBENCH) 2>&1 | sed -n 's/Nodes searched : \(.*\)/\/string Version\/s\/"\\(.*\\)"\/"sig-\1"\//p' > sign.txt + @sed -f sign.txt misc.cpp > misc2.cpp + @mv misc2.cpp misc.cpp + @rm sign.txt + +signature-build: build embed-signature + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all + +signature-profile-build: build embed-signature profile-build + +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 + +default: + help + +### ========================================================================== +### Section 5. Private targets +### ========================================================================== + +all: $(EXE) .depend + +config-sanity: + @echo "" + @echo "Config:" + @echo "debug: '$(debug)'" + @echo "optimize: '$(optimize)'" + @echo "arch: '$(arch)'" + @echo "os: '$(os)'" + @echo "bits: '$(bits)'" + @echo "prefetch: '$(prefetch)'" + @echo "bsfq: '$(bsfq)'" + @echo "popcnt: '$(popcnt)'" + @echo "sse: '$(sse)'" + @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 "$(os)" = "any" || test "$(os)" = "osx" + @test "$(bits)" = "32" || test "$(bits)" = "64" + @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" + @test "$(bsfq)" = "yes" || test "$(bsfq)" = "no" + @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" + @test "$(sse)" = "yes" || test "$(sse)" = "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' \ + EXTRALDFLAGS='-lgcov' \ + all + +gcc-profile-clean: + @rm -rf *.gcda *.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 + + +### ========================================================================== +### Section 6. Non-standard targets +### ========================================================================== + +hpux: + $(MAKE) \ + CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -mt +O3 -DNDEBUG -DNO_PREFETCH' \ + CXXFLAGS="" \ + LDFLAGS="" \ + all + diff --git a/stockfish-4-win/src/benchmark.cpp b/stockfish-4-win/src/benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f22ea6dada5d2ca7246039d45454c9e5e7e01a63 --- /dev/null +++ b/stockfish-4-win/src/benchmark.cpp @@ -0,0 +1,145 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "tt.h" +#include "ucioption.h" + +using namespace std; + +static const char* 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" +}; + + +/// 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 12), 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 secs or number of nodes. + +void benchmark(const Position& current, istream& is) { + + string token; + Search::LimitsType limits; + vector fens; + + // Assign default values to missing arguments + string ttSize = (is >> token) ? token : "32"; + string threads = (is >> token) ? token : "1"; + string limit = (is >> token) ? token : "12"; + string fenFile = (is >> token) ? token : "default"; + string limitType = (is >> token) ? token : "depth"; + + Options["Hash"] = ttSize; + Options["Threads"] = threads; + TT.clear(); + + if (limitType == "time") + limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms + + else if (limitType == "nodes") + limits.nodes = atoi(limit.c_str()); + + else if (limitType == "mate") + limits.mate = atoi(limit.c_str()); + + else + limits.depth = atoi(limit.c_str()); + + if (fenFile == "default") + fens.assign(Defaults, Defaults + 16); + + else if (fenFile == "current") + fens.push_back(current.fen()); + + else + { + string fen; + ifstream file(fenFile.c_str()); + + 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(); + } + + int64_t nodes = 0; + Search::StateStackPtr st; + Time::point elapsed = Time::now(); + + for (size_t i = 0; i < fens.size(); i++) + { + Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); + + cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; + + if (limitType == "perft") + { + size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); + cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; + nodes += cnt; + } + else + { + Threads.start_thinking(pos, limits, vector(), st); + Threads.wait_for_think_finished(); + nodes += Search::RootPos.nodes_searched(); + } + } + + elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' + + cerr << "\n===========================" + << "\nTotal time (ms) : " << elapsed + << "\nNodes searched : " << nodes + << "\nNodes/second : " << 1000 * nodes / elapsed << endl; +} diff --git a/stockfish-4-win/src/bitbase.cpp b/stockfish-4-win/src/bitbase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..570a9771419099aec3bc929b469a1b0b598e61dd --- /dev/null +++ b/stockfish-4-win/src/bitbase.cpp @@ -0,0 +1,175 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +namespace { + + // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 + const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608 + + // Each uint32_t stores results of 32 positions, one per bit + uint32_t KPKBitbase[IndexMax / 32]; + + // A KPK bitbase index is an integer in [0, IndexMax] range + // + // Information is mapped in a way that minimizes 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 + }; + + inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); } + + struct KPKPosition { + + operator Result() const { return res; } + Result classify_leaf(unsigned idx); + Result classify(const std::vector& db) + { return us == WHITE ? classify(db) : classify(db); } + + private: + template Result classify(const std::vector& db); + + Color us; + Square bksq, wksq, psq; + Result res; + }; + +} // namespace + + +bool Bitbases::probe_kpk(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_kpk() { + + unsigned idx, repeat = 1; + std::vector db(IndexMax); + + // Initialize db with known win / draw positions + for (idx = 0; idx < IndexMax; idx++) + db[idx].classify_leaf(idx); + + // Iterate through the positions until no more of the unknown positions can be + // changed to either wins or draws (15 cycles needed). + while (repeat) + for (repeat = idx = 0; idx < IndexMax; idx++) + if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN) + repeat = 1; + + // Map 32 results into one KPKBitbase[] entry + for (idx = 0; idx < IndexMax; idx++) + if (db[idx] == WIN) + KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); +} + + +namespace { + + Result KPKPosition::classify_leaf(unsigned idx) { + + wksq = Square((idx >> 0) & 0x3F); + bksq = Square((idx >> 6) & 0x3F); + us = Color ((idx >> 12) & 0x01); + psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15)); + + // Check if two pieces are on the same square or if a king can be captured + if ( wksq == psq || wksq == bksq || bksq == psq + || (StepAttacksBB[KING][wksq] & bksq) + || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) + return res = INVALID; + + if (us == WHITE) + { + // Immediate win if pawn can be promoted without getting captured + if ( rank_of(psq) == RANK_7 + && wksq != psq + DELTA_N + && ( square_distance(bksq, psq + DELTA_N) > 1 + ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) + return res = WIN; + } + // Immediate draw if is stalemate or king captures undefended pawn + else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) + || (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) + return res = DRAW; + + return res = 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 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 WIN otherwise the current position is + // classified UNKNOWN. + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Result r = INVALID; + Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; + + while (b) + r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)] + : db[index(Them, pop_lsb(&b), wksq, psq)]; + + if (Us == WHITE && rank_of(psq) < RANK_7) + { + Square s = psq + DELTA_N; + r |= db[index(BLACK, bksq, wksq, s)]; // Single push + + if (rank_of(s) == RANK_3 && s != wksq && s != bksq) + r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push + } + + if (Us == WHITE) + return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW; + else + return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN; + } + +} // namespace diff --git a/stockfish-4-win/src/bitboard.cpp b/stockfish-4-win/src/bitboard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9598aa706552f5f38da88c50bc7b734dd6b35919 --- /dev/null +++ b/stockfish-4-win/src/bitboard.cpp @@ -0,0 +1,339 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitboard.h" +#include "bitcount.h" +#include "misc.h" +#include "rkiss.h" + +CACHE_LINE_ALIGNMENT + +Bitboard RMasks[SQUARE_NB]; +Bitboard RMagics[SQUARE_NB]; +Bitboard* RAttacks[SQUARE_NB]; +unsigned RShifts[SQUARE_NB]; + +Bitboard BMasks[SQUARE_NB]; +Bitboard BMagics[SQUARE_NB]; +Bitboard* BAttacks[SQUARE_NB]; +unsigned BShifts[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 DistanceRingsBB[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]; + +int SquareDistance[SQUARE_NB][SQUARE_NB]; + +namespace { + + // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan + const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL; + const uint32_t DeBruijn_32 = 0x783A9B23; + + CACHE_LINE_ALIGNMENT + + int MS1BTable[256]; + Square BSFTable[SQUARE_NB]; + Bitboard RTable[0x19000]; // Storage space for rook attacks + Bitboard BTable[0x1480]; // Storage space for bishop attacks + + typedef unsigned (Fn)(Square, Bitboard); + + void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], + Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); + + FORCE_INLINE unsigned bsf_index(Bitboard b) { + + // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch + b ^= (b - 1); + return Is64Bit ? (b * DeBruijn_64) >> 58 + : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; + } +} + +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. + +#ifndef USE_BSFQ + +Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } + +Square pop_lsb(Bitboard* b) { + + Bitboard bb = *b; + *b = bb & (bb - 1); + return BSFTable[bsf_index(bb)]; +} + +Square msb(Bitboard 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 + MS1BTable[b32]); +} + +#endif // ifndef USE_BSFQ + + +/// Bitboards::print() prints a bitboard in an easily readable format to the +/// standard output. This is sometimes useful for debugging. + +void Bitboards::print(Bitboard b) { + + sync_cout; + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) + { + std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; + + for (File file = FILE_A; file <= FILE_H; file++) + std::cout << "| " << (b & (file | rank) ? "X " : " "); + + std::cout << "|\n"; + } + std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl; +} + + +/// Bitboards::init() initializes various bitboard arrays. It is called during +/// program initialization. + +void Bitboards::init() { + + for (int k = 0, i = 0; i < 8; i++) + while (k < (2 << i)) + MS1BTable[k++] = i; + + for (int i = 0; i < 64; i++) + BSFTable[bsf_index(1ULL << i)] = Square(i); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + SquareBB[s] = 1ULL << s; + + FileBB[FILE_A] = FileABB; + RankBB[RANK_1] = Rank1BB; + + for (int i = 1; i < 8; i++) + { + FileBB[i] = FileBB[i - 1] << 1; + RankBB[i] = RankBB[i - 1] << 8; + } + + 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++) + { + SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); + if (s1 != s2) + DistanceRingsBB[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 k = 0; steps[pt][k]; k++) + { + Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]); + + if (is_ok(to) && square_distance(s, to) < 3) + StepAttacksBB[make_piece(c, pt)][s] |= to; + } + + Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; + Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; + + init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index); + init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + { + PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb(s, 0); + PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0); + } + + for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) + for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) + if (PseudoAttacks[QUEEN][s1] & s2) + { + Square delta = (s2 - s1) / square_distance(s1, s2); + + for (Square s = s1 + delta; s != s2; s += delta) + BetweenBB[s1][s2] |= s; + } +} + + +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) && square_distance(s, s - deltas[i]) == 1; + s += deltas[i]) + { + attack |= s; + + if (occupied & s) + break; + } + + return attack; + } + + + Bitboard pick_random(RKISS& rk, int booster) { + + // Values s1 and s2 are used to rotate the candidate magic of a + // quantity known to be the optimal to quickly find the magics. + int s1 = booster & 63, s2 = (booster >> 6) & 63; + + Bitboard m = rk.rand(); + m = (m >> s1) | (m << (64 - s1)); + m &= rk.rand(); + m = (m >> s2) | (m << (64 - s2)); + return m & rk.rand(); + } + + + // 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 MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 }, + { 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } }; + RKISS rk; + Bitboard occupancy[4096], reference[4096], edges, b; + int i, size, booster; + + // 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); + 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; + + booster = MagicBoosters[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] = pick_random(rk, booster); + while (popcount((magics[s] * masks[s]) >> 56) < 6); + + std::memset(attacks[s], 0, size * sizeof(Bitboard)); + + // A good magic must map every possible occupancy to an index that + // looks up the correct sliding attack in the attacks[s] database. + // Note that we build up the database for square 's' as a side + // effect of verifying the magic. + for (i = 0; i < size; i++) + { + Bitboard& attack = attacks[s][index(s, occupancy[i])]; + + if (attack && attack != reference[i]) + break; + + assert(reference[i] != 0); + + attack = reference[i]; + } + } while (i != size); + } + } +} diff --git a/stockfish-4-win/src/bitboard.h b/stockfish-4-win/src/bitboard.h new file mode 100644 index 0000000000000000000000000000000000000000..bc3ed87b623dab6ae9debb5d89a48df9e3ee68ee --- /dev/null +++ b/stockfish-4-win/src/bitboard.h @@ -0,0 +1,322 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +namespace Bitboards { + +void init(); +void print(Bitboard b); + +} + +namespace Bitbases { + +void init_kpk(); +bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us); + +} + +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); + +CACHE_LINE_ALIGNMENT + +extern Bitboard RMasks[SQUARE_NB]; +extern Bitboard RMagics[SQUARE_NB]; +extern Bitboard* RAttacks[SQUARE_NB]; +extern unsigned RShifts[SQUARE_NB]; + +extern Bitboard BMasks[SQUARE_NB]; +extern Bitboard BMagics[SQUARE_NB]; +extern Bitboard* BAttacks[SQUARE_NB]; +extern unsigned BShifts[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 DistanceRingsBB[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]; + +extern int SquareDistance[SQUARE_NB][SQUARE_NB]; + +const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; + +/// 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); +} + +inline int square_distance(Square s1, Square s2) { + return SquareDistance[s1][s2]; +} + +inline int file_distance(Square s1, Square s2) { + return abs(file_of(s1) - file_of(s2)); +} + +inline int rank_distance(Square s1, Square s2) { + return abs(rank_of(s1) - rank_of(s2)); +} + + +/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns. + +template +inline Bitboard shift_bb(Bitboard b) { + + return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 + : Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7 + : Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 + : 0; +} + + +/// rank_bb() and file_bb() take a file or a square as input and return +/// a bitboard representing all 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)]; +} + + +/// adjacent_files_bb() takes a file as input and returns a bitboard representing +/// all squares on the adjacent files. + +inline Bitboard adjacent_files_bb(File f) { + return AdjacentFilesBB[f]; +} + + +/// in_front_bb() takes a color and a rank as input, and returns a bitboard +/// representing all the squares on all ranks in front of the rank, from the +/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will +/// give all squares on ranks 1 and 2. + +inline Bitboard in_front_bb(Color c, Rank r) { + return InFrontBB[c][r]; +} + + +/// between_bb() returns a bitboard representing all squares between two squares. +/// 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 line, file or diagonal, +/// 0 is returned. + +inline Bitboard between_bb(Square s1, Square s2) { + return BetweenBB[s1][s2]; +} + + +/// forward_bb() takes a color and a square as input, and returns a bitboard +/// representing all squares along the line in front of the square, from the +/// point of view of the given color. Definition of the table is: +/// 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() takes a color and a square as input, and returns a bitboard +/// representing all squares that can be attacked by a pawn of the given color +/// when it moves along its file starting from the given square. Definition is: +/// 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() takes a color and a square as input, and returns a +/// bitboard mask which can be used to test if a pawn of the given color on +/// the given square is a passed pawn. Definition of the table is: +/// 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]; +} + + +/// squares_of_color() returns a bitboard representing all squares with the same +/// color of the given square. + +inline Bitboard squares_of_color(Square s) { + return DarkSquares & s ? DarkSquares : ~DarkSquares; +} + + +/// squares_aligned() returns true if the squares s1, s2 and s3 are aligned +/// either on a straight or on a diagonal line. + +inline bool squares_aligned(Square s1, Square s2, Square s3) { + return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) + & ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]); +} + + +/// Functions for computing sliding attack bitboards. Function attacks_bb() takes +/// a square and a bitboard of occupied squares as input, and returns a bitboard +/// representing all squares attacked by Pt (bishop or rook) on the given square. +template +FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) { + + Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks; + Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics; + unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts; + + if (Is64Bit) + return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]); + + unsigned lo = unsigned(occ) & unsigned(Masks[s]); + unsigned hi = unsigned(occ >> 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 occ) { + return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index(s, occ)]; +} + + +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. + +#ifdef USE_BSFQ + +# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + +FORCE_INLINE Square lsb(Bitboard b) { + unsigned long index; + _BitScanForward64(&index, b); + return (Square) index; +} + +FORCE_INLINE Square msb(Bitboard b) { + unsigned long index; + _BitScanReverse64(&index, b); + return (Square) index; +} + +# elif defined(__arm__) + +FORCE_INLINE int lsb32(uint32_t v) { + __asm__("rbit %0, %1" : "=r"(v) : "r"(v)); + return __builtin_clz(v); +} + +FORCE_INLINE Square msb(Bitboard b) { + return (Square) (63 - __builtin_clzll(b)); +} + +FORCE_INLINE Square lsb(Bitboard b) { + return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32))); +} + +# else + +FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen + Bitboard index; + __asm__("bsfq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; +} + +FORCE_INLINE Square msb(Bitboard b) { + Bitboard index; + __asm__("bsrq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; +} + +# endif + +FORCE_INLINE Square pop_lsb(Bitboard* b) { + const Square s = lsb(*b); + *b &= *b - 1; + return s; +} + +#else // if defined(USE_BSFQ) + +extern Square msb(Bitboard b); +extern Square lsb(Bitboard b); +extern Square pop_lsb(Bitboard* b); + +#endif + +#endif // #ifndef BITBOARD_H_INCLUDED diff --git a/stockfish-4-win/src/bitcount.h b/stockfish-4-win/src/bitcount.h new file mode 100644 index 0000000000000000000000000000000000000000..ad8df94ab078522066fdfa44da80c931fa732c88 --- /dev/null +++ b/stockfish-4-win/src/bitcount.h @@ -0,0 +1,105 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 BITCOUNT_H_INCLUDED +#define BITCOUNT_H_INCLUDED + +#include +#include "types.h" + +enum BitCountType { + CNT_64, + CNT_64_MAX15, + CNT_32, + CNT_32_MAX15, + CNT_HW_POPCNT +}; + +/// Determine at compile time the best popcount<> specialization according if +/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count +/// and if hardware popcnt instruction is available. +const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; +const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15; + + +/// popcount() counts the number of nonzero bits in a bitboard +template inline int popcount(Bitboard); + +template<> +inline int popcount(Bitboard b) { + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL; + return (b * 0x0101010101010101ULL) >> 56; +} + +template<> +inline int popcount(Bitboard b) { + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + return (b * 0x1111111111111111ULL) >> 60; +} + +template<> +inline int popcount(Bitboard b) { + unsigned w = unsigned(b >> 32), v = unsigned(b); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F; + return (v * 0x01010101) >> 24; +} + +template<> +inline int popcount(Bitboard b) { + unsigned w = unsigned(b >> 32), v = unsigned(b); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + return ((v + w) * 0x11111111) >> 28; +} + +template<> +inline int popcount(Bitboard b) { + +#ifndef USE_POPCNT + + assert(false); + return b != 0; // Avoid 'b not used' warning + +#elif defined(_MSC_VER) && defined(__INTEL_COMPILER) + + return _mm_popcnt_u64(b); + +#elif defined(_MSC_VER) + + return (int)__popcnt64(b); + +#else + + __asm__("popcnt %1, %0" : "=r" (b) : "r" (b)); + return b; + +#endif +} + +#endif // #ifndef BITCOUNT_H_INCLUDED diff --git a/stockfish-4-win/src/book.cpp b/stockfish-4-win/src/book.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caf2b0663ec76987720231e29360c6d434238d27 --- /dev/null +++ b/stockfish-4-win/src/book.cpp @@ -0,0 +1,478 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 . +*/ + +/* + The code in this file is based on the opening book code in PolyGlot + by Fabien Letouzey. PolyGlot is available under the GNU General + Public License, and can be downloaded from http://wbec-ridderkerk.nl +*/ + +#include +#include +#include + +#include "book.h" +#include "misc.h" +#include "movegen.h" + +using namespace std; + +namespace { + + // A Polyglot book is a series of "entries" of 16 bytes. All integers are + // stored in big-endian format, with highest byte first (regardless of size). + // The entries are ordered according to the key in ascending order. + struct Entry { + uint64_t key; + uint16_t move; + uint16_t count; + uint32_t learn; + }; + + // Random numbers from PolyGlot, used to compute book hash keys + const union { + Key PolyGlotRandoms[781]; + struct { + Key psq[12][64]; // [piece][square] + Key castle[4]; // [castle right] + Key enpassant[8]; // [file] + Key turn; + } Zobrist; + } PG = {{ + 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, + 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, + 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, + 0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL, + 0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL, + 0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL, + 0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL, + 0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL, + 0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL, + 0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL, + 0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL, + 0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL, + 0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL, + 0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL, + 0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL, + 0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL, + 0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL, + 0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL, + 0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL, + 0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL, + 0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL, + 0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL, + 0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL, + 0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL, + 0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL, + 0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL, + 0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL, + 0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL, + 0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL, + 0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL, + 0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL, + 0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL, + 0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL, + 0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL, + 0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL, + 0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL, + 0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL, + 0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL, + 0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL, + 0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL, + 0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL, + 0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL, + 0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL, + 0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL, + 0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL, + 0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL, + 0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL, + 0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL, + 0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL, + 0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL, + 0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL, + 0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL, + 0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL, + 0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL, + 0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL, + 0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL, + 0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL, + 0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL, + 0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL, + 0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL, + 0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL, + 0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL, + 0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL, + 0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL, + 0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL, + 0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL, + 0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL, + 0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL, + 0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL, + 0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL, + 0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL, + 0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL, + 0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL, + 0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL, + 0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL, + 0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL, + 0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL, + 0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL, + 0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL, + 0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL, + 0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL, + 0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL, + 0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL, + 0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL, + 0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL, + 0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL, + 0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL, + 0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL, + 0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL, + 0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL, + 0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL, + 0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL, + 0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL, + 0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL, + 0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL, + 0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL, + 0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL, + 0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL, + 0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL, + 0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL, + 0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL, + 0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL, + 0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL, + 0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL, + 0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL, + 0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL, + 0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL, + 0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL, + 0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL, + 0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL, + 0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL, + 0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL, + 0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL, + 0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL, + 0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL, + 0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL, + 0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL, + 0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL, + 0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL, + 0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL, + 0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL, + 0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL, + 0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL, + 0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL, + 0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL, + 0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL, + 0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL, + 0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL, + 0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL, + 0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL, + 0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL, + 0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL, + 0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL, + 0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL, + 0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL, + 0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL, + 0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL, + 0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL, + 0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL, + 0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL, + 0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL, + 0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL, + 0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL, + 0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL, + 0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL, + 0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL, + 0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL, + 0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL, + 0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL, + 0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL, + 0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL, + 0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL, + 0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL, + 0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL, + 0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL, + 0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL, + 0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL, + 0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL, + 0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL, + 0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL, + 0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL, + 0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL, + 0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL, + 0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL, + 0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL, + 0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL, + 0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL, + 0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL, + 0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL, + 0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL, + 0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL, + 0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL, + 0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL, + 0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL, + 0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL, + 0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL, + 0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL, + 0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL, + 0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL, + 0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL, + 0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL, + 0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL, + 0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL, + 0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL, + 0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL, + 0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL, + 0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL, + 0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL, + 0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL, + 0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL, + 0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL, + 0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL, + 0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL, + 0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL, + 0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL, + 0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL, + 0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL, + 0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL, + 0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL, + 0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL, + 0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL, + 0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL, + 0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL, + 0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL, + 0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL, + 0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL, + 0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL, + 0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL, + 0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL, + 0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL, + 0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL, + 0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL, + 0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL, + 0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL, + 0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL, + 0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL, + 0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL, + 0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL, + 0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL, + 0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL, + 0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL, + 0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL, + 0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL, + 0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL, + 0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL, + 0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL, + 0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL, + 0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL, + 0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL, + 0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL, + 0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL, + 0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL, + 0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL, + 0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL, + 0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL, + 0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL, + 0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL, + 0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL, + 0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL, + 0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL, + 0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL, + 0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL, + 0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL, + 0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL, + 0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL, + 0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL, + 0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL, + 0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL, + 0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL, + 0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL, + 0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL, + 0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL, + 0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL, + 0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL, + 0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL, + 0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL, + 0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL, + 0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL, + 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL, + 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL, + 0xF8D626AAAF278509ULL + }}; + + // polyglot_key() returns the PolyGlot hash key of the given position + Key polyglot_key(const Position& pos) { + + Key key = 0; + Bitboard b = pos.pieces(); + + while (b) + { + Square s = pop_lsb(&b); + Piece p = pos.piece_on(s); + + // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 + key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s]; + } + + b = pos.can_castle(ALL_CASTLES); + + while (b) + key ^= PG.Zobrist.castle[pop_lsb(&b)]; + + if (pos.ep_square() != SQ_NONE) + key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())]; + + if (pos.side_to_move() == WHITE) + key ^= PG.Zobrist.turn; + + return key; + } + +} // namespace + +PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {} + +PolyglotBook::~PolyglotBook() { if (is_open()) close(); } + + +/// operator>>() reads sizeof(T) chars from the file's binary byte stream and +/// converts them in a number of type T. A Polyglot book stores numbers in +/// big-endian format. + +template PolyglotBook& PolyglotBook::operator>>(T& n) { + + n = 0; + for (size_t i = 0; i < sizeof(T); i++) + n = T((n << 8) + ifstream::get()); + + return *this; +} + +template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) { + return *this >> e.key >> e.move >> e.count >> e.learn; +} + + +/// open() tries to open a book file with the given name after closing any +/// exsisting one. + +bool PolyglotBook::open(const char* fName) { + + if (is_open()) // Cannot close an already closed file + close(); + + ifstream::open(fName, ifstream::in | ifstream::binary); + + fileName = is_open() ? fName : ""; + ifstream::clear(); // Reset any error flag to allow retry ifstream::open() + return !fileName.empty(); +} + + +/// probe() tries to find a book move for the given position. If no move is +/// found returns MOVE_NONE. If pickBest is true returns always the highest +/// rated move, otherwise randomly chooses one, based on the move score. + +Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) { + + if (fileName != fName && !open(fName.c_str())) + return MOVE_NONE; + + Entry e; + uint16_t best = 0; + unsigned sum = 0; + Move move = MOVE_NONE; + Key key = polyglot_key(pos); + + seekg(find_first(key) * sizeof(Entry), ios_base::beg); + + while (*this >> e, e.key == key && good()) + { + best = max(best, e.count); + sum += e.count; + + // Choose book move according to its score. If a move has a very + // high score it has higher probability to be choosen than a move + // with lower score. Note that first entry is always chosen. + if ( (sum && rkiss.rand() % sum < e.count) + || (pickBest && e.count == best)) + move = Move(e.move); + } + + if (!move) + return MOVE_NONE; + + // A PolyGlot book move is encoded as follows: + // + // bit 0- 5: destination square (from 0 to 63) + // bit 6-11: origin square (from 0 to 63) + // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) + // + // Castling moves follow "king captures rook" representation. So in case book + // move is a promotion we have to convert to our representation, in all the + // other cases we can directly compare with a Move after having masked out + // the special Move's flags (bit 14-15) that are not supported by PolyGlot. + int pt = (move >> 12) & 7; + if (pt) + move = make(from_sq(move), to_sq(move), PieceType(pt + 1)); + + // Add 'special move' flags and verify it is legal + for (MoveList it(pos); *it; ++it) + if (move == (*it ^ type_of(*it))) + return *it; + + return MOVE_NONE; +} + + +/// find_first() takes a book key as input, and does a binary search through +/// the book file for the given key. Returns the index of the leftmost book +/// entry with the same key as the input. + +size_t PolyglotBook::find_first(Key key) { + + seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size + + size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1; + Entry e; + + assert(low <= high); + + while (low < high && good()) + { + mid = (low + high) / 2; + + assert(mid >= low && mid < high); + + seekg(mid * sizeof(Entry), ios_base::beg); + *this >> e; + + if (key <= e.key) + high = mid; + else + low = mid + 1; + } + + assert(low == high); + + return low; +} diff --git a/stockfish-4-win/src/book.h b/stockfish-4-win/src/book.h new file mode 100644 index 0000000000000000000000000000000000000000..4ce74f471fddddfdef74b710e6de15153ac89d93 --- /dev/null +++ b/stockfish-4-win/src/book.h @@ -0,0 +1,45 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 BOOK_H_INCLUDED +#define BOOK_H_INCLUDED + +#include +#include + +#include "position.h" +#include "rkiss.h" + +class PolyglotBook : private std::ifstream { +public: + PolyglotBook(); + ~PolyglotBook(); + Move probe(const Position& pos, const std::string& fName, bool pickBest); + +private: + template PolyglotBook& operator>>(T& n); + + bool open(const char* fName); + size_t find_first(Key key); + + RKISS rkiss; + std::string fileName; +}; + +#endif // #ifndef BOOK_H_INCLUDED diff --git a/stockfish-4-win/src/endgame.cpp b/stockfish-4-win/src/endgame.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc98de90d4f40f71f900c04c1bbc71bd2322ec11 --- /dev/null +++ b/stockfish-4-win/src/endgame.cpp @@ -0,0 +1,964 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "endgame.h" +#include "movegen.h" + +using std::string; + +namespace { + + // Table used to drive the defending king towards the edge of the board + // in KX vs K and KQ vs KR endgames. + const int MateTable[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 defending king towards a corner square of the + // right color in KBN vs K endgames. + const int KBNKMateTable[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 + }; + + // The attacking side is given a descending bonus based on distance between + // the two kings in basic endgames. + const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + + // Get the material key of a 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 to do the work for us. Note that the + // fen string could correspond to an illegal position. + 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)), // Weaker + code.substr(0, code.find('K', 1)) }; // Stronger + + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); + + string fen = sides[0] + char('0' + int(8 - code.length())) + + sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; + + return Position(fen, false, NULL).material_key(); + } + + template + void delete_endgame(const typename M::value_type& p) { delete p.second; } + +} // namespace + + +/// Endgames members definitions + +Endgames::Endgames() { + + add("KK"); + add("KPK"); + add("KBK"); + add("KNK"); + add("KNNK"); + add("KBNK"); + add("KRKP"); + add("KRKB"); + add("KRKN"); + add("KQKP"); + add("KQKR"); + add("KBBKN"); + + add("KNPK"); + add("KNPKB"); + add("KRPKR"); + add("KBPKB"); + add("KBPKN"); + add("KBPPKB"); + add("KRPPKRP"); +} + +Endgames::~Endgames() { + + for_each(m1.begin(), m1.end(), delete_endgame); + for_each(m2.begin(), m2.end(), delete_endgame); +} + +template +void Endgames::add(const string& code) { + + map((Endgame*)0)[key(code, WHITE)] = new Endgame(WHITE); + map((Endgame*)0)[key(code, BLACK)] = 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(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(!pos.count(weakerSide)); + assert(!pos.checkers()); // Eval is never called when in check + + // Stalemate detection with lone king + if (pos.side_to_move() == weakerSide && !MoveList(pos).size()) + return VALUE_DRAW; + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + + Value result = pos.non_pawn_material(strongerSide) + + pos.count(strongerSide) * PawnValueEg + + MateTable[loserKSq] + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( pos.count(strongerSide) + || pos.count(strongerSide) + || pos.bishop_pair(strongerSide)) + result += VALUE_KNOWN_WIN; + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the +/// defending king towards a corner square of the right color. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 0); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongerSide)[0]; + + // kbnk_mate_table() tries to drive toward corners A1 or H8, + // if we have a bishop that cannot reach the above squares we + // mirror the kings so to drive enemy toward corners A8 or H1. + if (opposite_colors(bishopSq, SQ_A1)) + { + winnerKSq = mirror(winnerKSq); + loserKSq = mirror(loserKSq); + } + + Value result = VALUE_KNOWN_WIN + + DistanceBonus[square_distance(winnerKSq, loserKSq)] + + KBNKMateTable[loserKSq]; + + return strongerSide == 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(pos.non_pawn_material(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); + + Square wksq, bksq, wpsq; + Color us; + + if (strongerSide == WHITE) + { + wksq = pos.king_square(WHITE); + bksq = pos.king_square(BLACK); + wpsq = pos.list(WHITE)[0]; + us = pos.side_to_move(); + } + else + { + wksq = ~pos.king_square(BLACK); + bksq = ~pos.king_square(WHITE); + wpsq = ~pos.list(BLACK)[0]; + us = ~pos.side_to_move(); + } + + if (file_of(wpsq) >= FILE_E) + { + wksq = mirror(wksq); + bksq = mirror(bksq); + wpsq = mirror(wpsq); + } + + if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us)) + return VALUE_DRAW; + + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); + + return strongerSide == 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == 0); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); + + Square wksq, wrsq, bksq, bpsq; + int tempo = (pos.side_to_move() == strongerSide); + + wksq = pos.king_square(strongerSide); + bksq = pos.king_square(weakerSide); + wrsq = pos.list(strongerSide)[0]; + bpsq = pos.list(weakerSide)[0]; + + if (strongerSide == BLACK) + { + wksq = ~wksq; + wrsq = ~wrsq; + bksq = ~bksq; + bpsq = ~bpsq; + } + + Square queeningSq = file_of(bpsq) | RANK_1; + Value result; + + // If the stronger side's king is in front of the pawn, it's a win + if (wksq < bpsq && file_of(wksq) == file_of(bpsq)) + result = RookValueEg - Value(square_distance(wksq, bpsq)); + + // If the weaker side's king is too far from the pawn and the rook, + // it's a win + else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3 + && square_distance(bksq, wrsq) >= 3) + result = RookValueEg - Value(square_distance(wksq, bpsq)); + + // If the pawn is far advanced and supported by the defending king, + // the position is drawish + else if ( rank_of(bksq) <= RANK_3 + && square_distance(bksq, bpsq) == 1 + && rank_of(wksq) >= RANK_4 + && square_distance(wksq, bpsq) - tempo > 2) + result = Value(80 - square_distance(wksq, bpsq) * 8); + + else + result = Value(200) + - Value(square_distance(wksq, bpsq + DELTA_S) * 8) + + Value(square_distance(bksq, bpsq + DELTA_S) * 8) + + Value(square_distance(bpsq, queeningSq) * 8); + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KR vs KB. This is very simple, and always returns drawish scores. The +/// score is slightly bigger when the defending king is close to the edge. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + assert(pos.count< PAWN>(strongerSide) == 0); + + Value result = Value(MateTable[pos.king_square(weakerSide)]); + return strongerSide == 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + assert(pos.count< PAWN>(strongerSide) == 0); + + const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; + + Square bksq = pos.king_square(weakerSide); + Square bnsq = pos.list(weakerSide)[0]; + Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KQ vs KP. In general, a win for the stronger side, however, there are a few +/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can +/// be a draw, so we scale down to distance between kings only. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(weakerSide)[0]; + + Value result = QueenValueEg + - PawnValueEg + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( square_distance(loserKSq, pawnSq) == 1 + && relative_rank(weakerSide, pawnSq) == RANK_7) + { + File f = file_of(pawnSq); + + if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H) + result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]); + } + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KQ vs KR. This is almost identical to KX vs K: We give the attacking +/// king a bonus for having the kings close together, and for forcing the +/// defending king towards the edge. If we also take care to avoid null move +/// for the defending side in the search, this is usually sufficient to be +/// able to win KQ vs KR. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.non_pawn_material(weakerSide ) == RookValueMg); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 0); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + + Value result = QueenValueEg + - RookValueEg + + MateTable[loserKSq] + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + return strongerSide == pos.side_to_move() ? result : -result; +} + +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(strongerSide) == 2); + assert(pos.count(weakerSide ) == 1); + assert(!pos.pieces(PAWN)); + + Value result = BishopValueEg; + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square nsq = pos.list(weakerSide)[0]; + + // Bonus for attacking king close to defending king + result += Value(DistanceBonus[square_distance(wksq, bksq)]); + + // Bonus for driving the defending king and knight apart + result += Value(square_distance(bksq, nsq) * 32); + + // Bonus for restricting the knight's mobility + result += Value((8 - popcount(pos.attacks_from(nsq))) * 8); + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// Some cases of trivial draws +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } + + +/// K, bishop 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(strongerSide) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) >= 1); + + // No assertions about the material of weakerSide, because we want draws to + // be detected even when the weaker side has some pawns. + + Bitboard pawns = pos.pieces(strongerSide, PAWN); + File pawnFile = file_of(pos.list(strongerSide)[0]); + + // All pawns are on a single rook file ? + if ( (pawnFile == FILE_A || pawnFile == FILE_H) + && !(pawns & ~file_bb(pawnFile))) + { + Square bishopSq = pos.list(strongerSide)[0]; + Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); + Square kingSq = pos.king_square(weakerSide); + + if ( opposite_colors(queeningSq, bishopSq) + && abs(file_of(kingSq) - pawnFile) <= 1) + { + // The bishop has the wrong color, and the defending king is on the + // file of the pawn(s) or the adjacent file. Find the rank of the + // frontmost pawn. + Rank rank; + if (strongerSide == WHITE) + { + for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {} + assert(rank >= RANK_2 && rank <= RANK_7); + } + else + { + for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {} + rank = Rank(rank ^ 7); // HACK to get the relative rank + assert(rank >= RANK_2 && rank <= RANK_7); + } + // If the defending king has distance 1 to the promotion square or + // is placed somewhere in front of the pawn, it's a draw. + if ( square_distance(kingSq, queeningSq) <= 1 + || relative_rank(strongerSide, kingSq) >= rank) + return SCALE_FACTOR_DRAW; + } + } + + // All pawns on same B or G file? Then potential draw + if ( (pawnFile == FILE_B || pawnFile == FILE_G) + && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) + && pos.non_pawn_material(weakerSide) == 0 + && pos.count(weakerSide) >= 1) + { + // Get weaker pawn closest to opponent's queening square + Bitboard wkPawns = pos.pieces(weakerSide, PAWN); + Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns); + + Square strongerKingSq = pos.king_square(strongerSide); + Square weakerKingSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongerSide)[0]; + + // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and + // weaker king can stop opposing opponent's king from penetrating. + if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 + && opposite_colors(bishopSq, weakerPawnSq) + && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) + return SCALE_FACTOR_DRAW; + } + + return SCALE_FACTOR_NONE; +} + + +/// K and queen vs K, rook 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(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 0); + assert(pos.count< ROOK>(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) >= 1); + + Square kingSq = pos.king_square(weakerSide); + if ( relative_rank(weakerSide, kingSq) <= RANK_2 + && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 + && (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3))) + && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) + && (pos.attacks_from(kingSq) & pos.pieces(weakerSide, PAWN))) + { + Square rsq = pos.list(weakerSide)[0]; + if (pos.attacks_from(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; +} + + +/// K, rook and one pawn vs K and a rook. This function knows a handful of the +/// most important classes of drawn positions, but is far from perfect. It would +/// probably be a good idea to add more knowledge in the future. +/// +/// It would also be nice to rewrite the actual code for this function, +/// which is mostly copied from Glaurung 1.x, and not very pretty. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); + + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square wrsq = pos.list(strongerSide)[0]; + Square wpsq = pos.list(strongerSide)[0]; + Square brsq = pos.list(weakerSide)[0]; + + // Orient the board in such a way that the stronger side is white, and the + // pawn is on the left half of the board. + if (strongerSide == BLACK) + { + wksq = ~wksq; + wrsq = ~wrsq; + wpsq = ~wpsq; + bksq = ~bksq; + brsq = ~brsq; + } + if (file_of(wpsq) > FILE_D) + { + wksq = mirror(wksq); + wrsq = mirror(wrsq); + wpsq = mirror(wpsq); + bksq = mirror(bksq); + brsq = mirror(brsq); + } + + File f = file_of(wpsq); + Rank r = rank_of(wpsq); + Square queeningSq = f | RANK_8; + int tempo = (pos.side_to_move() == strongerSide); + + // If the pawn is not too far advanced and the defending king defends the + // queening square, use the third-rank defence. + if ( r <= RANK_5 + && square_distance(bksq, queeningSq) <= 1 + && wksq <= SQ_H5 + && (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 + && square_distance(bksq, queeningSq) <= 1 + && rank_of(wksq) + tempo <= RANK_6 + && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3))) + return SCALE_FACTOR_DRAW; + + if ( r >= RANK_6 + && bksq == queeningSq + && rank_of(brsq) == RANK_1 + && (!tempo || square_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 + DELTA_N + && square_distance(wksq, wpsq) - tempo >= 2 + && square_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 + && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) + && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq)); + + // Similar to the above, but with the pawn further back + if ( f != FILE_A + && file_of(wrsq) == f + && wrsq < wpsq + && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) + && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) + && ( square_distance(bksq, wrsq) + tempo >= 3 + || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo + && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) + return ScaleFactor( SCALE_FACTOR_MAX + - 8 * square_distance(wpsq, queeningSq) + - 2 * square_distance(wksq, queeningSq)); + + // If the pawn is not far advanced, and the defending king is somewhere in + // the pawn's path, it's probably a draw. + if (r <= RANK_4 && bksq > wpsq) + { + if (file_of(bksq) == file_of(wpsq)) + return ScaleFactor(10); + if ( abs(file_of(bksq) - file_of(wpsq)) == 1 + && square_distance(wksq, bksq) > 2) + return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); + } + return SCALE_FACTOR_NONE; +} + + +/// K, rook and two pawns vs K, rook and one pawn. There is only a single +/// pattern: 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 2); + assert(pos.count(weakerSide ) == 1); + + Square wpsq1 = pos.list(strongerSide)[0]; + Square wpsq2 = pos.list(strongerSide)[1]; + Square bksq = pos.king_square(weakerSide); + + // Does the stronger side have a passed pawn? + if ( pos.pawn_is_passed(strongerSide, wpsq1) + || pos.pawn_is_passed(strongerSide, wpsq2)) + return SCALE_FACTOR_NONE; + + Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2)); + + if ( file_distance(bksq, wpsq1) <= 1 + && file_distance(bksq, wpsq2) <= 1 + && relative_rank(strongerSide, bksq) > r) + { + switch (r) { + case RANK_2: return ScaleFactor(10); + case RANK_3: return ScaleFactor(10); + case RANK_4: return ScaleFactor(15); + case RANK_5: return ScaleFactor(20); + case RANK_6: return ScaleFactor(40); + default: assert(false); + } + } + return SCALE_FACTOR_NONE; +} + + +/// 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(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) >= 2); + assert(pos.count(weakerSide ) == 0); + + Square ksq = pos.king_square(weakerSide); + Bitboard pawns = pos.pieces(strongerSide, PAWN); + + // Are all pawns on the 'a' file? + if (!(pawns & ~FileABB)) + { + // Does the defending king block the pawns? + if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 + || ( file_of(ksq) == FILE_A + && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + return SCALE_FACTOR_DRAW; + } + // Are all pawns on the 'h' file? + else if (!(pawns & ~FileHBB)) + { + // Does the defending king block the pawns? + if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 + || ( file_of(ksq) == FILE_H + && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; +} + + +/// K, bishop and a pawn vs K and a bishop. 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(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square strongerBishopSq = pos.list(strongerSide)[0]; + Square weakerBishopSq = pos.list(weakerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + // Case 1: Defending king blocks the pawn, and cannot be driven away + if ( file_of(weakerKingSq) == file_of(pawnSq) + && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) + && ( opposite_colors(weakerKingSq, strongerBishopSq) + || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + return SCALE_FACTOR_DRAW; + + // Case 2: Opposite colored bishops + if (opposite_colors(strongerBishopSq, weakerBishopSq)) + { + // We assume that the position is drawn in the following three situations: + // + // a. The pawn is on rank 5 or further back. + // b. The defending king is somewhere in the pawn's path. + // c. The defending bishop attacks some square along the pawn's path, + // and is at least three squares away from the pawn. + // + // These rules are probably not perfect, but in practice they work + // reasonably well. + + if (relative_rank(strongerSide, pawnSq) <= RANK_5) + return SCALE_FACTOR_DRAW; + else + { + Bitboard path = forward_bb(strongerSide, pawnSq); + + if (path & pos.pieces(weakerSide, KING)) + return SCALE_FACTOR_DRAW; + + if ( (pos.attacks_from(weakerBishopSq) & path) + && square_distance(weakerBishopSq, pawnSq) >= 3) + return SCALE_FACTOR_DRAW; + } + } + return SCALE_FACTOR_NONE; +} + + +/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with +/// opposite-colored bishops. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 2); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square wbsq = pos.list(strongerSide)[0]; + Square bbsq = pos.list(weakerSide)[0]; + + if (!opposite_colors(wbsq, bbsq)) + return SCALE_FACTOR_NONE; + + Square ksq = pos.king_square(weakerSide); + Square psq1 = pos.list(strongerSide)[0]; + Square psq2 = pos.list(strongerSide)[1]; + Rank r1 = rank_of(psq1); + Rank r2 = rank_of(psq2); + Square blockSq1, blockSq2; + + if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) + { + blockSq1 = psq1 + pawn_push(strongerSide); + blockSq2 = file_of(psq2) | rank_of(psq1); + } + else + { + blockSq1 = psq2 + pawn_push(strongerSide); + blockSq2 = file_of(psq1) | rank_of(psq2); + } + + switch (file_distance(psq1, psq2)) + { + case 0: + // Both pawns are on the same file. Easy draw if defender firmly controls + // some square in the frontmost pawn's path. + if ( file_of(ksq) == file_of(blockSq1) + && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) + && opposite_colors(ksq, wbsq)) + return SCALE_FACTOR_DRAW; + else + return SCALE_FACTOR_NONE; + + case 1: + // Pawns on adjacent files. Draw if 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(weakerSide, BISHOP)) + || abs(r1 - r2) >= 2)) + return SCALE_FACTOR_DRAW; + + else if ( ksq == blockSq2 + && opposite_colors(ksq, wbsq) + && ( bbsq == blockSq1 + || (pos.attacks_from(blockSq1) & pos.pieces(weakerSide, 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; + } +} + + +/// K, bisop and a pawn vs K and knight. 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(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square strongerBishopSq = pos.list(strongerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + if ( file_of(weakerKingSq) == file_of(pawnSq) + && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) + && ( opposite_colors(weakerKingSq, strongerBishopSq) + || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + return SCALE_FACTOR_DRAW; + + return SCALE_FACTOR_NONE; +} + + +/// K, knight and a pawn 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(pos.non_pawn_material(strongerSide) == KnightValueMg); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + if ( pawnSq == relative_square(strongerSide, SQ_A7) + && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) + return SCALE_FACTOR_DRAW; + + if ( pawnSq == relative_square(strongerSide, SQ_H7) + && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) + return SCALE_FACTOR_DRAW; + + return SCALE_FACTOR_NONE; +} + + +/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking +/// pawn, it's a win. Otherwise, drawn. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + Square pawnSq = pos.list(strongerSide)[0]; + Square bishopSq = pos.list(weakerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + // 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(strongerSide, pawnSq) & pos.attacks_from(bishopSq)) + return ScaleFactor(square_distance(weakerKingSq, pawnSq)); + + return SCALE_FACTOR_NONE; +} + + +/// K and a pawn vs K and a pawn. This is done by removing the weakest side's +/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without +/// the pawn, she probably has at least a draw with the pawn as well. The exception +/// is when the stronger side's pawn is far advanced and not on a rook file; in +/// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(WHITE) == 1); + assert(pos.count(BLACK) == 1); + + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square wpsq = pos.list(strongerSide)[0]; + Color us = pos.side_to_move(); + + if (strongerSide == BLACK) + { + wksq = ~wksq; + bksq = ~bksq; + wpsq = ~wpsq; + us = ~us; + } + + if (file_of(wpsq) >= FILE_E) + { + wksq = mirror(wksq); + bksq = mirror(bksq); + wpsq = mirror(wpsq); + } + + // If the pawn has advanced to the fifth rank or further, and is not a + // rook pawn, it's too dangerous to assume that it's at least a draw. + if ( rank_of(wpsq) >= RANK_5 + && file_of(wpsq) != FILE_A) + return SCALE_FACTOR_NONE; + + // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, + // it's probably at least a draw even with the pawn. + return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; +} diff --git a/stockfish-4-win/src/endgame.h b/stockfish-4-win/src/endgame.h new file mode 100644 index 0000000000000000000000000000000000000000..5529eae108429e35fba55f9a1c7ec362c68c3789 --- /dev/null +++ b/stockfish-4-win/src/endgame.h @@ -0,0 +1,125 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "position.h" +#include "types.h" + + +/// EndgameType lists all supported endgames + +enum EndgameType { + + // Evaluation functions + + KK, // K vs K + KBK, // KB vs K + KNK, // KN vs K + 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 + KBBKN, // KBB vs KN + KmmKm, // K and two minors vs K and one or two minors + + + // Scaling functions + SCALE_FUNS, + + KBPsK, // KB+pawns vs K + KQKRPs, // KQ vs KR+pawns + KRPKR, // KRP vs KR + KRPPKRP, // KRPP vs KRP + KPsK, // King and pawns vs king + 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 according if return a Value or a +/// ScaleFactor. Type eg_fun::type equals to either ScaleFactor or Value +/// depending if the template parameter is 0 or 1. + +template struct eg_fun { typedef Value type; }; +template<> struct eg_fun<1> { typedef ScaleFactor type; }; + + +/// Base and derived templates for endgame evaluation and scaling functions + +template +struct EndgameBase { + + virtual ~EndgameBase() {} + virtual Color color() const = 0; + virtual T operator()(const Position&) const = 0; +}; + + +template SCALE_FUNS)>::type> +struct Endgame : public EndgameBase { + + explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {} + Color color() const { return strongerSide; } + T operator()(const Position&) const; + +private: + Color strongerSide, weakerSide; +}; + + +/// Endgames class stores in two std::map the pointers to endgame evaluation +/// and scaling base objects. Then we use polymorphism to invoke the actual +/// endgame function calling its operator() that is virtual. + +class Endgames { + + typedef std::map::type>*> M1; + typedef std::map::type>*> M2; + + M1 m1; + M2 m2; + + M1& map(M1::mapped_type) { return m1; } + M2& map(M2::mapped_type) { return m2; } + + template void add(const std::string& code); + +public: + Endgames(); + ~Endgames(); + + template T probe(Key key, T& eg) + { return eg = map(eg).count(key) ? map(eg)[key] : NULL; } +}; + +#endif // #ifndef ENDGAME_H_INCLUDED diff --git a/stockfish-4-win/src/evaluate.cpp b/stockfish-4-win/src/evaluate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12634ddd56b0a41ec937d88e3073dd9e623c378d --- /dev/null +++ b/stockfish-4-win/src/evaluate.cpp @@ -0,0 +1,1185 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "evaluate.h" +#include "material.h" +#include "pawns.h" +#include "thread.h" +#include "ucioption.h" + +namespace { + + enum ExtendedPieceType { // Used for tracing + PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL + }; + + namespace Tracing { + + Score scores[COLOR_NB][TOTAL + 1]; + std::stringstream stream; + + void add(int idx, Score term_w, Score term_b = SCORE_ZERO); + void row(const char* name, int idx); + std::string do_trace(const Position& pos); + } + + // Struct EvalInfo contains various information computed and collected + // by the evaluation functions. + struct EvalInfo { + + // Pointers to material and pawn hash table entries + Material::Entry* mi; + Pawns::Entry* pi; + + // attackedBy[color][piece type] is a bitboard representing all squares + // attacked by a given color and piece type, attackedBy[color][ALL_PIECES] + // contains all squares attacked by the given color. + Bitboard attackedBy[COLOR_NB][PIECE_TYPE_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 "weight" 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 variables + // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and + // KnightAttackWeight in evaluate.cpp + int kingAttackersWeight[COLOR_NB]; + + // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares + // directly adjacent to the king of the given color. Pieces which attack + // more than one square are counted multiple times. For instance, if black's + // king is on g8 and there's a white knight on g5, this knight adds + // 2 to kingAdjacentZoneAttacksCount[BLACK]. + int kingAdjacentZoneAttacksCount[COLOR_NB]; + }; + + // Evaluation grain size, must be a power of 2 + const int GrainSize = 4; + + // Evaluation weights, initialized from UCI options + enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem }; + Score Weights[6]; + + typedef Value V; + #define S(mg, eg) make_score(mg, eg) + + // Internal evaluation weights. These are applied on top of the evaluation + // weights read from UCI parameters. The purpose is to be able to change + // the evaluation weights while keeping the default values of the UCI + // parameters at 100, which looks prettier. + // + // Values modified by Joona Kiiski + const Score WeightsInternal[] = { + S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0) + }; + + // MobilityBonus[PieceType][attacked] contains bonuses for middle and end + // game, indexed by piece type and number of attacked squares not occupied by + // friendly pieces. + const Score MobilityBonus[][32] = { + {}, {}, + { S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights + S( 37, 28), S( 42, 31), S(44, 33) }, + { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops + S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77), + S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) }, + { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks + S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119), + S( 35,122), S( 36,123), S(37,124), S(38,124) }, + { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens + S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40), + S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41) } + }; + + // Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed + // by piece type and square (from white's point of view). + const Value Outpost[][SQUARE_NB] = { + { + // A B C D E F G H + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(4), V(8), V(8), V(4), V(0), V(0), + V(0), V(4),V(17),V(26),V(26),V(17), V(4), V(0), + V(0), V(8),V(26),V(35),V(35),V(26), V(8), V(0), + V(0), V(4),V(17),V(17),V(17),V(17), V(4), V(0) }, + { + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Bishops + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(5), V(5), V(5), V(5), V(0), V(0), + V(0), V(5),V(10),V(10),V(10),V(10), V(5), V(0), + V(0),V(10),V(21),V(21),V(21),V(21),V(10), V(0), + V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) } + }; + + // Threat[attacking][attacked] contains bonuses according to which piece + // type attacks which one. + const Score Threat[][PIECE_TYPE_NB] = { + {}, {}, + { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT + { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP + { S(0, 0), S( 0, 22), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK + { S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN + }; + + // ThreatenedByPawn[PieceType] contains a penalty according to which piece + // type is attacked by an enemy pawn. + const Score ThreatenedByPawn[] = { + S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) + }; + + #undef S + + const Score Tempo = make_score(24, 11); + const Score BishopPin = make_score(66, 11); + const Score RookOn7th = make_score(11, 20); + const Score QueenOn7th = make_score( 3, 8); + const Score RookOnPawn = make_score(10, 28); + const Score QueenOnPawn = make_score( 4, 20); + const Score RookOpenFile = make_score(43, 21); + const Score RookSemiopenFile = make_score(19, 10); + const Score BishopPawns = make_score( 8, 12); + const Score MinorBehindPawn = make_score(16, 0); + const Score UndefendedMinor = make_score(25, 10); + const Score TrappedRook = make_score(90, 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 = make_score(50, 50); + + // The SpaceMask[Color] contains the area of the board which is considered + // by the space evaluation. In the middle game, each side is given a bonus + // based on how many squares inside this area are safe and available for + // friendly minor pieces. + const Bitboard SpaceMask[] = { + (1ULL << SQ_C2) | (1ULL << SQ_D2) | (1ULL << SQ_E2) | (1ULL << SQ_F2) | + (1ULL << SQ_C3) | (1ULL << SQ_D3) | (1ULL << SQ_E3) | (1ULL << SQ_F3) | + (1ULL << SQ_C4) | (1ULL << SQ_D4) | (1ULL << SQ_E4) | (1ULL << SQ_F4), + (1ULL << SQ_C7) | (1ULL << SQ_D7) | (1ULL << SQ_E7) | (1ULL << SQ_F7) | + (1ULL << SQ_C6) | (1ULL << SQ_D6) | (1ULL << SQ_E6) | (1ULL << SQ_F6) | + (1ULL << SQ_C5) | (1ULL << SQ_D5) | (1ULL << SQ_E5) | (1ULL << SQ_F5) + }; + + // King danger constants and variables. The king danger scores are taken + // from the KingDanger[]. Various little "meta-bonuses" measuring + // the strength of the enemy attack are added up into an integer, which + // is used as an index to KingDanger[]. + // + // KingAttackWeights[PieceType] contains king attack weights by piece type + const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; + + // Bonuses for enemy's safe checks + const int QueenContactCheck = 6; + const int RookContactCheck = 4; + const int QueenCheck = 3; + const int RookCheck = 2; + const int BishopCheck = 1; + const int KnightCheck = 1; + + // KingExposed[Square] contains penalties based on the position of the + // defending king, indexed by king's square (from white's point of view). + const int KingExposed[] = { + 2, 0, 2, 5, 5, 2, 0, 2, + 2, 2, 4, 8, 8, 4, 2, 2, + 7, 10, 12, 12, 12, 12, 10, 7, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15 + }; + + // KingDanger[Color][attackUnits] contains the actual king danger weighted + // scores, indexed by color and by a calculated integer number. + Score KingDanger[COLOR_NB][128]; + + // Function prototypes + template + Value do_evaluate(const Position& pos, Value& margin); + + template + void init_eval_info(const Position& pos, EvalInfo& ei); + + template + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); + + template + Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]); + + template + Score evaluate_threats(const Position& pos, const EvalInfo& ei); + + template + Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei); + + template + int evaluate_space(const Position& pos, const EvalInfo& ei); + + Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei); + + Value interpolate(const Score& v, Phase ph, ScaleFactor sf); + Score apply_weight(Score v, Score w); + Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); + double to_cp(Value v); +} + + +namespace Eval { + + /// evaluate() is the main evaluation function. It always computes two + /// values, an endgame score and a middle game score, and interpolates + /// between them based on the remaining material. + + Value evaluate(const Position& pos, Value& margin) { + return do_evaluate(pos, margin); + } + + + /// trace() is like evaluate() but instead of a value returns a string suitable + /// to be print on stdout with the detailed descriptions and values of each + /// evaluation term. Used mainly for debugging. + std::string trace(const Position& pos) { + return Tracing::do_trace(pos); + } + + + /// init() computes evaluation weights from the corresponding UCI parameters + /// and setup king tables. + + void init() { + + Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]); + Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]); + Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); + Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); + Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); + Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); + + const int MaxSlope = 30; + const int Peak = 1280; + + for (int t = 0, i = 1; i < 100; i++) + { + t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); + + KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); + KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); + } + } + +} // namespace Eval + + +namespace { + +template +Value do_evaluate(const Position& pos, Value& margin) { + + assert(!pos.checkers()); + + EvalInfo ei; + Value margins[COLOR_NB]; + Score score, mobilityWhite, mobilityBlack; + Thread* th = pos.this_thread(); + + // margins[] store the uncertainty estimation of position's evaluation + // that typically is used by the search for pruning decisions. + margins[WHITE] = margins[BLACK] = VALUE_ZERO; + + // Initialize score by reading the incrementally updated scores included + // in the position object (material + piece square tables) and adding + // Tempo bonus. Score is computed from the point of view of white. + score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); + + // Probe the material hash table + ei.mi = Material::probe(pos, th->materialTable, th->endgames); + score += ei.mi->material_value(); + + // If we have a specialized evaluation function for the current material + // configuration, call it and return. + if (ei.mi->specialized_eval_exists()) + { + margin = VALUE_ZERO; + return ei.mi->evaluate(pos); + } + + // Probe the pawn hash table + ei.pi = Pawns::probe(pos, th->pawnsTable); + score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); + + // Initialize attack and king safety bitboards + init_eval_info(pos, ei); + init_eval_info(pos, ei); + + // Evaluate pieces and mobility + score += evaluate_pieces_of_color(pos, ei, mobilityWhite) + - evaluate_pieces_of_color(pos, ei, mobilityBlack); + + score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); + + // Evaluate kings after all other pieces because we need complete attack + // information when computing the king safety evaluation. + score += evaluate_king(pos, ei, margins) + - evaluate_king(pos, ei, margins); + + // 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 one side has only a king, check whether exists any unstoppable passed pawn + if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) + score += evaluate_unstoppable_pawns(pos, ei); + + // Evaluate space for both sides, only in middle-game. + if (ei.mi->space_weight()) + { + int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); + score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); + } + + // Scale winning side if position is more drawish that what it appears + ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) + : ei.mi->scale_factor(pos, BLACK); + + // If we don't already have an unusual scale factor, check for opposite + // colored bishop endgames, and use a lower scale for those. + if ( ei.mi->game_phase() < PHASE_MIDGAME + && pos.opposite_bishops() + && sf == SCALE_FACTOR_NORMAL) + { + // Only the two bishops ? + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + { + // Check for KBP vs KB with only a single pawn that is almost + // certainly a draw or at least two pawns. + bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); + sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + } + else + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. + sf = ScaleFactor(50); + } + + margin = margins[pos.side_to_move()]; + Value v = interpolate(score, ei.mi->game_phase(), sf); + + // In case of tracing add all single evaluation contributions for both white and black + if (Trace) + { + Tracing::add(PST, pos.psq_score()); + Tracing::add(IMBALANCE, ei.mi->material_value()); + Tracing::add(PAWN, ei.pi->pawns_value()); + Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); + Score w = ei.mi->space_weight() * evaluate_space(pos, ei); + Score b = ei.mi->space_weight() * evaluate_space(pos, ei); + Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); + Tracing::add(TOTAL, score); + Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE]) + << ", Black: " << to_cp(margins[BLACK]) + << "\nScaling: " << std::noshowpos + << std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, " + << std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * " + << std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n" + << "Total evaluation: " << to_cp(v); + } + + return pos.side_to_move() == WHITE ? v : -v; +} + + + // init_eval_info() initializes king bitboards for given color adding + // pawn attacks. To be done at the beginning of the evaluation. + + template + void init_eval_info(const Position& pos, EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); + + Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from(pos.king_square(Them)); + ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); + + // Init king safety tables only if we are going to use them + if (pos.count(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + { + ei.kingRing[Them] = b | shift_bb(b); + b &= ei.attackedBy[Us][PAWN]; + ei.kingAttackersCount[Us] = b ? popcount(b) / 2 : 0; + ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; + } else + ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; + } + + + // evaluate_outposts() evaluates bishop and knight outposts squares + + template + Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + assert (Piece == BISHOP || Piece == KNIGHT); + + // Initial bonus based on square + Value bonus = Outpost[Piece == BISHOP][relative_square(Us, s)]; + + // Increase bonus if supported by pawn, especially if the opponent has + // no minor piece which can exchange the outpost piece. + if (bonus && (ei.attackedBy[Us][PAWN] & s)) + { + if ( !pos.pieces(Them, KNIGHT) + && !(squares_of_color(s) & pos.pieces(Them, BISHOP))) + bonus += bonus + bonus / 2; + else + bonus += bonus / 2; + } + return make_score(bonus, bonus); + } + + + // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color + + template + Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { + + Bitboard b; + Square s; + Score score = SCORE_ZERO; + + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square* pl = pos.list(Us); + + ei.attackedBy[Us][Piece] = 0; + + while ((s = *pl++) != SQ_NONE) + { + // Find attacked squares, including x-ray attacks for bishops and rooks + b = Piece == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(Us, QUEEN)) + : Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) + : pos.attacks_from(s); + + ei.attackedBy[Us][Piece] |= b; + + if (b & ei.kingRing[Them]) + { + ei.kingAttackersCount[Us]++; + ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; + Bitboard bb = (b & ei.attackedBy[Them][KING]); + if (bb) + ei.kingAdjacentZoneAttacksCount[Us] += popcount(bb); + } + + int mob = Piece != QUEEN ? popcount(b & mobilityArea) + : popcount(b & mobilityArea); + + mobility += MobilityBonus[Piece][mob]; + + // Decrease score if we are attacked by an enemy pawn. Remaining part + // of threat evaluation must be done later when we have full attack info. + if (ei.attackedBy[Them][PAWN] & s) + score -= ThreatenedByPawn[Piece]; + + // Otherwise give a bonus if we are a bishop and can pin a piece or can + // give a discovered check through an x-ray attack. + else if ( Piece == BISHOP + && (PseudoAttacks[Piece][pos.king_square(Them)] & s) + && !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces())) + score += BishopPin; + + // Penalty for bishop with same coloured pawns + if (Piece == BISHOP) + score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + + if (Piece == BISHOP || Piece == KNIGHT) + { + // Bishop and knight outposts squares + if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) + score += evaluate_outposts(pos, ei, s); + + // Bishop or knight behind a pawn + if ( relative_rank(Us, s) < RANK_5 + && (pos.pieces(PAWN) & (s + pawn_push(Us)))) + score += MinorBehindPawn; + } + + if ( (Piece == ROOK || Piece == QUEEN) + && relative_rank(Us, s) >= RANK_5) + { + // Major piece on 7th rank and enemy king trapped on 8th + if ( relative_rank(Us, s) == RANK_7 + && relative_rank(Us, pos.king_square(Them)) == RANK_8) + score += Piece == ROOK ? RookOn7th : QueenOn7th; + + // Major piece attacking enemy pawns on the same rank/file + Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; + if (pawns) + score += popcount(pawns) * (Piece == ROOK ? RookOnPawn : QueenOnPawn); + } + + // Special extra evaluation for rooks + if (Piece == ROOK) + { + // Give a bonus for a rook on a open or semi-open file + if (ei.pi->semiopen(Us, file_of(s))) + score += ei.pi->semiopen(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile; + + if (mob > 3 || ei.pi->semiopen(Us, file_of(s))) + continue; + + Square ksq = pos.king_square(Us); + + // Penalize rooks which are trapped inside a king. Penalize more if + // king has lost right to castle. + 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_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E)) + score -= (TrappedRook - make_score(mob * 8, 0)) * (pos.can_castle(Us) ? 1 : 2); + } + + // 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 ( Piece == BISHOP + && pos.is_chess960() + && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) + { + const enum Piece P = make_piece(Us, PAWN); + Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); + if (pos.piece_on(s + d) == P) + score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 + : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2 + : TrappedBishopA1H1; + } + } + + if (Trace) + Tracing::scores[Us][Piece] = score; + + return score; + } + + + // evaluate_threats<>() assigns bonuses according to the type of attacking piece + // and the type of attacked one. + + template + Score evaluate_threats(const Position& pos, const EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard b, undefendedMinors, weakEnemies; + Score score = SCORE_ZERO; + + // Undefended minors get penalized even if not under attack + undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT) + & ~ei.attackedBy[Them][ALL_PIECES]; + + if (undefendedMinors) + score += UndefendedMinor; + + // Enemy pieces not defended by a pawn and under our attack + weakEnemies = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][ALL_PIECES]; + + // Add bonus according to type of attacked enemy piece and to the + // type of attacking piece, from knights to queens. Kings are not + // considered because are already handled in king evaluation. + if (weakEnemies) + for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++) + { + b = ei.attackedBy[Us][pt1] & weakEnemies; + if (b) + for (PieceType pt2 = PAWN; pt2 < KING; pt2++) + if (b & pos.pieces(pt2)) + score += Threat[pt1][pt2]; + } + + if (Trace) + Tracing::scores[Us][THREAT] = score; + + return score; + } + + + // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the + // pieces of a given color. + + template + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Score score = mobility = SCORE_ZERO; + + // Do not include in mobility squares protected by enemy pawns or occupied by our pieces + const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING)); + + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + + // Sum up all attacked squares + ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; + if (Trace) + Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]); + + return score; + } + + + // evaluate_king<>() assigns bonuses and penalties to a king of a given color + + template + Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard undefended, b, b1, b2, safe; + int attackUnits; + const Square ksq = pos.king_square(Us); + + // King shelter and enemy pawns storm + Score score = ei.pi->king_safety(pos, ksq); + + // King safety. This is quite complicated, and is almost certainly far + // from optimally tuned. + if ( ei.kingAttackersCount[Them] >= 2 + && ei.kingAdjacentZoneAttacksCount[Them]) + { + // Find the attacked squares around the king which has no defenders + // apart from the king itself + undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING]; + undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN]); + + // Initialize the 'attackUnits' variable, which is used later on as an + // index to the KingDanger[] array. 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, the square of the + // king, and the quality of the pawn shelter. + attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + + KingExposed[relative_square(Us, ksq)] + - mg_value(score) / 32; + + // Analyse enemy's safe queen contact checks. First find undefended + // squares around the king attacked by enemy queen... + b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); + if (b) + { + // ...then remove squares not supported by another enemy piece + b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); + if (b) + attackUnits += QueenContactCheck + * popcount(b) + * (Them == pos.side_to_move() ? 2 : 1); + } + + // Analyse enemy's safe rook contact checks. First find undefended + // squares around the king attacked by enemy rooks... + b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); + + // Consider only squares where the enemy rook gives check + b &= PseudoAttacks[ROOK][ksq]; + + if (b) + { + // ...then remove squares not supported by another enemy piece + b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); + if (b) + attackUnits += RookContactCheck + * popcount(b) + * (Them == pos.side_to_move() ? 2 : 1); + } + + // Analyse enemy's safe distance checks for sliders and knights + safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); + + b1 = pos.attacks_from(ksq) & safe; + b2 = pos.attacks_from(ksq) & safe; + + // Enemy queen safe checks + b = (b1 | b2) & ei.attackedBy[Them][QUEEN]; + if (b) + attackUnits += QueenCheck * popcount(b); + + // Enemy rooks safe checks + b = b1 & ei.attackedBy[Them][ROOK]; + if (b) + attackUnits += RookCheck * popcount(b); + + // Enemy bishops safe checks + b = b2 & ei.attackedBy[Them][BISHOP]; + if (b) + attackUnits += BishopCheck * popcount(b); + + // Enemy knights safe checks + b = pos.attacks_from(ksq) & ei.attackedBy[Them][KNIGHT] & safe; + if (b) + attackUnits += KnightCheck * popcount(b); + + // To index KingDanger[] attackUnits must be in [0, 99] range + attackUnits = std::min(99, std::max(0, attackUnits)); + + // Finally, extract the king danger score from the KingDanger[] + // array and subtract the score from evaluation. Set also margins[] + // value that will be used for pruning because this value can sometimes + // be very big, and so capturing a single attacking piece can therefore + // result in a score change far bigger than the value of the captured piece. + score -= KingDanger[Us == Search::RootColor][attackUnits]; + margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]); + } + + if (Trace) + Tracing::scores[Us][KING] = 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, squaresToQueen, defendedSquares, unsafeSquares, supportingPawns; + Score score = SCORE_ZERO; + + b = ei.pi->passed_pawns(Us); + + while (b) + { + Square s = pop_lsb(&b); + + assert(pos.pawn_is_passed(Us, s)); + + int r = int(relative_rank(Us, s) - RANK_2); + int rr = r * (r - 1); + + // Base bonus based on rank + Value mbonus = Value(17 * rr); + Value ebonus = Value(7 * (rr + r + 1)); + + if (rr) + { + Square blockSq = s + pawn_push(Us); + + // Adjust bonus based on kings proximity + ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr); + ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr); + + // If blockSq is not the queening square then consider also a second push + if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1)) + ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); + + // If the pawn is free to advance, increase bonus + if (pos.is_empty(blockSq)) + { + squaresToQueen = forward_bb(Us, s); + defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; + + // If there is an enemy rook or queen attacking the pawn from behind, + // add all X-ray attacks by the rook or queen. Otherwise consider only + // the squares in the pawn's path attacked or occupied by the enemy. + if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN)) + && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from(s))) + unsafeSquares = squaresToQueen; + else + unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); + + // If there aren't enemy attacks huge bonus, a bit smaller if at + // least block square is not attacked, otherwise smallest bonus. + int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3; + + // Big bonus if the path to queen is fully defended, a bit less + // if at least block square is defended. + if (defendedSquares == squaresToQueen) + k += 6; + + else if (defendedSquares & blockSq) + k += (unsafeSquares & defendedSquares) == unsafeSquares ? 4 : 2; + + mbonus += Value(k * rr), ebonus += Value(k * rr); + } + } // rr != 0 + + // Increase the bonus if the passed pawn is supported by a friendly pawn + // on the same rank and a bit smaller if it's on the previous rank. + supportingPawns = pos.pieces(Us, PAWN) & adjacent_files_bb(file_of(s)); + if (supportingPawns & rank_bb(s)) + ebonus += Value(r * 20); + + else if (supportingPawns & rank_bb(s - pawn_push(Us))) + ebonus += Value(r * 12); + + // Rook pawns are a special case: They are sometimes worse, and + // sometimes better than other passed pawns. It is difficult to find + // good rules for determining whether they are good or bad. For now, + // we try the following: Increase the value for rook pawns if the + // other side has no pieces apart from a knight, and decrease the + // value if the other side has a rook or queen. + if (file_of(s) == FILE_A || file_of(s) == FILE_H) + { + if (pos.non_pawn_material(Them) <= KnightValueMg) + ebonus += ebonus / 4; + else if (pos.pieces(Them, ROOK, QUEEN)) + ebonus -= ebonus / 4; + } + score += make_score(mbonus, ebonus); + + } + + if (Trace) + Tracing::scores[Us][PASSED] = apply_weight(score, Weights[PassedPawns]); + + // Add the scores to the middle game and endgame eval + return apply_weight(score, Weights[PassedPawns]); + } + + + // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite + // conservative and returns a winning score only when we are very sure that the pawn is winning. + + Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) { + + Bitboard b, b2, blockers, supporters, queeningPath, candidates; + Square s, blockSq, queeningSquare; + Color c, winnerSide, loserSide; + bool pathDefended, opposed; + int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d; + int pliesToQueen[] = { 256, 256 }; + + // Step 1. Hunt for unstoppable passed pawns. If we find at least one, + // record how many plies are required for promotion. + for (c = WHITE; c <= BLACK; c++) + { + // Skip if other side has non-pawn pieces + if (pos.non_pawn_material(~c)) + continue; + + b = ei.pi->passed_pawns(c); + + while (b) + { + s = pop_lsb(&b); + queeningSquare = relative_square(c, file_of(s) | RANK_8); + queeningPath = forward_bb(c, s); + + // Compute plies to queening and check direct advancement + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); + oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); + pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath); + + if (movesToGo >= oppMovesToGo && !pathDefended) + continue; + + // Opponent king cannot block because path is defended and position + // is not in check. So only friendly pieces can be blockers. + assert(!pos.checkers()); + assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c))); + + // Add moves needed to free the path from friendly pieces and retest condition + movesToGo += popcount(queeningPath & pos.pieces(c)); + + if (movesToGo >= oppMovesToGo && !pathDefended) + continue; + + pliesToGo = 2 * movesToGo - int(c == pos.side_to_move()); + pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo); + } + } + + // Step 2. If either side cannot promote at least three plies before the other side then situation + // becomes too complex and we give up. Otherwise we determine the possibly "winning side" + if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3) + return SCORE_ZERO; + + winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); + loserSide = ~winnerSide; + + // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? + b = candidates = pos.pieces(loserSide, PAWN); + + while (b) + { + s = pop_lsb(&b); + + // Compute plies from queening + queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); + pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); + + // Check if (without even considering any obstacles) we're too far away or doubled + if ( pliesToQueen[winnerSide] + 3 <= pliesToGo + || (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN))) + candidates ^= s; + } + + // If any candidate is already a passed pawn it _may_ promote in time. We give up. + if (candidates & ei.pi->passed_pawns(loserSide)) + return SCORE_ZERO; + + // Step 4. Check new passed pawn creation through king capturing and pawn sacrifices + b = candidates; + + while (b) + { + s = pop_lsb(&b); + sacptg = blockersCount = 0; + minKingDist = kingptg = 256; + + // Compute plies from queening + queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); + pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); + + // Generate list of blocking pawns and supporters + supporters = adjacent_files_bb(file_of(s)) & candidates; + opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN); + blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN); + + assert(blockers); + + // How many plies does it take to remove all the blocking pawns? + while (blockers) + { + blockSq = pop_lsb(&blockers); + movesToGo = 256; + + // Check pawns that can give support to overcome obstacle, for instance + // black pawns: a4, b4 white: b2 then pawn in b4 is giving support. + if (!opposed) + { + b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide))); + + while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) + { + d = square_distance(blockSq, pop_lsb(&b2)) - 2; + movesToGo = std::min(movesToGo, d); + } + } + + // Check pawns that can be sacrificed against the blocking pawn + b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s); + + while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) + { + d = square_distance(blockSq, pop_lsb(&b2)) - 2; + movesToGo = std::min(movesToGo, d); + } + + // If obstacle can be destroyed with an immediate pawn exchange / sacrifice, + // it's not a real obstacle and we have nothing to add to pliesToGo. + if (movesToGo <= 0) + continue; + + // Plies needed to sacrifice against all the blocking pawns + sacptg += movesToGo * 2; + blockersCount++; + + // Plies needed for the king to capture all the blocking pawns + d = square_distance(pos.king_square(loserSide), blockSq); + minKingDist = std::min(minKingDist, d); + kingptg = (minKingDist + blockersCount) * 2; + } + + // Check if pawn sacrifice plan _may_ save the day + if (pliesToQueen[winnerSide] + 3 > pliesToGo + sacptg) + return SCORE_ZERO; + + // Check if king capture plan _may_ save the day (contains some false positives) + if (pliesToQueen[winnerSide] + 3 > pliesToGo + kingptg) + return SCORE_ZERO; + } + + // Winning pawn is unstoppable and will promote as first, return big score + Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]); + return winnerSide == WHITE ? score : -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 scaled by a weight taken from the + // material hash table. The aim is to improve play on game opening. + template + int evaluate_space(const Position& pos, const EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + // 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[Us] + & ~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 + return popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + } + + + // interpolate() interpolates between a middle game and an endgame score, + // based on game phase. It also scales the return value by a ScaleFactor array. + + Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { + + assert(mg_value(v) > -VALUE_INFINITE && mg_value(v) < VALUE_INFINITE); + assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE); + assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME); + + int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; + int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128; + return Value((result / GrainSize) * GrainSize); // Sign independent + } + + // apply_weight() weights score v by score w trying to prevent overflow + Score apply_weight(Score v, Score w) { + return make_score((int(mg_value(v)) * mg_value(w)) / 0x100, + (int(eg_value(v)) * eg_value(w)) / 0x100); + } + + // weight_option() computes the value of an evaluation weight, by combining + // two UCI-configurable weights (midgame and endgame) with an internal weight. + + Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { + + // Scale option value from 100 to 256 + int mg = Options[mgOpt] * 256 / 100; + int eg = Options[egOpt] * 256 / 100; + + return apply_weight(make_score(mg, eg), internalWeight); + } + + + // Tracing functions definitions + + double to_cp(Value v) { return double(v) / double(PawnValueMg); } + + void Tracing::add(int idx, Score wScore, Score bScore) { + + scores[WHITE][idx] = wScore; + scores[BLACK][idx] = bScore; + } + + void Tracing::row(const char* name, int idx) { + + Score wScore = scores[WHITE][idx]; + Score bScore = scores[BLACK][idx]; + + switch (idx) { + case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL: + stream << std::setw(20) << name << " | --- --- | --- --- | " + << std::setw(6) << to_cp(mg_value(wScore)) << " " + << std::setw(6) << to_cp(eg_value(wScore)) << " \n"; + break; + default: + stream << std::setw(20) << name << " | " << std::noshowpos + << std::setw(5) << to_cp(mg_value(wScore)) << " " + << std::setw(5) << to_cp(eg_value(wScore)) << " | " + << std::setw(5) << to_cp(mg_value(bScore)) << " " + << std::setw(5) << to_cp(eg_value(bScore)) << " | " + << std::showpos + << std::setw(6) << to_cp(mg_value(wScore - bScore)) << " " + << std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n"; + } + } + + std::string Tracing::do_trace(const Position& pos) { + + stream.str(""); + stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); + std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score)); + + Value margin; + do_evaluate(pos, margin); + + std::string totals = stream.str(); + stream.str(""); + + stream << std::setw(21) << "Eval term " << "| White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "---------------------+-------------+-------------+---------------\n"; + + row("Material, PST, Tempo", PST); + row("Material imbalance", IMBALANCE); + row("Pawns", PAWN); + row("Knights", KNIGHT); + row("Bishops", BISHOP); + row("Rooks", ROOK); + row("Queens", QUEEN); + row("Mobility", MOBILITY); + row("King safety", KING); + row("Threats", THREAT); + row("Passed pawns", PASSED); + row("Unstoppable pawns", UNSTOPPABLE); + row("Space", SPACE); + + stream << "---------------------+-------------+-------------+---------------\n"; + row("Total", TOTAL); + stream << totals; + + return stream.str(); + } +} diff --git a/stockfish-4-win/src/evaluate.h b/stockfish-4-win/src/evaluate.h new file mode 100644 index 0000000000000000000000000000000000000000..2234a4a0f9078e3e5abf1230825be03659040b75 --- /dev/null +++ b/stockfish-4-win/src/evaluate.h @@ -0,0 +1,35 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +class Position; + +namespace Eval { + +extern void init(); +extern Value evaluate(const Position& pos, Value& margin); +extern std::string trace(const Position& pos); + +} + +#endif // #ifndef EVALUATE_H_INCLUDED diff --git a/stockfish-4-win/src/main.cpp b/stockfish-4-win/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d26b63ac663e0f40e874c1cb75313d99ab191a4 --- /dev/null +++ b/stockfish-4-win/src/main.cpp @@ -0,0 +1,52 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "evaluate.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +int main(int argc, char* argv[]) { + + std::cout << engine_info() << std::endl; + + UCI::init(Options); + Bitboards::init(); + Position::init(); + Bitbases::init_kpk(); + Search::init(); + Eval::init(); + Threads.init(); + TT.set_size(Options["Hash"]); + + std::string args; + + for (int i = 1; i < argc; i++) + args += std::string(argv[i]) + " "; + + UCI::loop(args); + + Threads.exit(); +} diff --git a/stockfish-4-win/src/material.cpp b/stockfish-4-win/src/material.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abad2abb923e053291bb22e41d21585b8573e47d --- /dev/null +++ b/stockfish-4-win/src/material.cpp @@ -0,0 +1,292 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +#include "material.h" + +using namespace std; + +namespace { + + // Values modified by Joona Kiiski + const Value MidgameLimit = Value(15581); + const Value EndgameLimit = Value(3998); + + // Scale factors used when one side has no more pawns + const int NoPawnsSF[4] = { 6, 12, 32 }; + + // Polynomial material balance parameters + const Value RedundantQueen = Value(320); + const Value RedundantRook = Value(554); + + // pair pawn knight bishop rook queen + const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; + + const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { + // pair pawn knight bishop rook queen + { 7 }, // Bishop pair + { 39, 2 }, // Pawn + { 35, 271, -4 }, // Knight + { 7, 105, 4, 7 }, // Bishop + { -27, -2, 46, 100, 56 }, // Rook + { 58, 29, 83, 148, -3, -25 } // Queen + }; + + const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { + // THEIR PIECES + // pair pawn knight bishop rook queen + { 41 }, // Bishop pair + { 37, 41 }, // Pawn + { 10, 62, 41 }, // Knight OUR PIECES + { 57, 64, 39, 41 }, // Bishop + { 50, 40, 23, -22, 41 }, // Rook + { 106, 101, 3, 151, 171, 41 } // Queen + }; + + // Endgame evaluation and scaling functions accessed direcly and not through + // the function maps because correspond to more then one material hash key. + Endgame EvaluateKmmKm[] = { Endgame(WHITE), Endgame(BLACK) }; + 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 templates used to detect a given material distribution + template bool is_KXK(const Position& pos) { + const Color Them = (Us == WHITE ? BLACK : WHITE); + return !pos.count(Them) + && pos.non_pawn_material(Them) == VALUE_ZERO + && pos.non_pawn_material(Us) >= RookValueMg; + } + + template bool is_KBPsKs(const Position& pos) { + return pos.non_pawn_material(Us) == BishopValueMg + && pos.count(Us) == 1 + && pos.count(Us) >= 1; + } + + template bool is_KQKRPs(const Position& pos) { + const Color Them = (Us == WHITE ? BLACK : WHITE); + return !pos.count(Us) + && pos.non_pawn_material(Us) == QueenValueMg + && pos.count(Us) == 1 + && pos.count(Them) == 1 + && pos.count(Them) >= 1; + } + + /// imbalance() calculates imbalance comparing 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 pt1, pt2, pc, v; + int value = 0; + + // Redundancy of major pieces, formula based on Kaufman's paper + // "The Evaluation of Material Imbalances in Chess" + if (pieceCount[Us][ROOK] > 0) + value -= RedundantRook * (pieceCount[Us][ROOK] - 1) + + RedundantQueen * pieceCount[Us][QUEEN]; + + // Second-degree polynomial material imbalance by Tord Romstad + for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) + { + pc = pieceCount[Us][pt1]; + if (!pc) + continue; + + v = LinearCoefficients[pt1]; + + for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) + v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] + + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + + value += pc * v; + } + return value; + } + +} // namespace + +namespace Material { + +/// Material::probe() takes a position object as input, looks up a MaterialEntry +/// object, and returns a pointer to it. If the material configuration is not +/// already present in the table, it is computed and stored there, so we don't +/// have to recompute everything when the same material configuration occurs again. + +Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { + + Key key = pos.material_key(); + Entry* e = entries[key]; + + // If e->key matches the position's material hash key, it means that we + // have analysed this material configuration before, and we can simply + // return the information we found the last time instead of recomputing it. + if (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 = game_phase(pos); + + // Let's look if we have a specialized evaluation function for this + // particular material configuration. First we look for a fixed + // configuration one, then a generic one if previous search failed. + if (endgames.probe(key, e->evaluationFunction)) + return e; + + if (is_KXK(pos)) + { + e->evaluationFunction = &EvaluateKXK[WHITE]; + return e; + } + + if (is_KXK(pos)) + { + e->evaluationFunction = &EvaluateKXK[BLACK]; + return e; + } + + if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) + { + // Minor piece endgame with at least one minor piece per side and + // no pawns. Note that the case KmmK is already handled by KXK. + assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); + assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); + + if ( pos.count(WHITE) + pos.count(WHITE) <= 2 + && pos.count(BLACK) + pos.count(BLACK) <= 2) + { + e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; + return e; + } + } + + // OK, we didn't find any special evaluation function for the current + // material configuration. Is there a suitable scaling function? + // + // We face problems when there are several conflicting applicable + // scaling functions and we need to decide which one to use. + EndgameBase* sf; + + if (endgames.probe(key, sf)) + { + e->scalingFunction[sf->color()] = sf; + return e; + } + + // Generic scaling functions that refer to more then one material + // distribution. Should be probed after the specialized ones. + // Note that these ones don't return after setting the function. + if (is_KBPsKs(pos)) + e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; + + if (is_KBPsKs(pos)) + e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; + + if (is_KQKRPs(pos)) + e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; + + else if (is_KQKRPs(pos)) + e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; + + Value npm_w = pos.non_pawn_material(WHITE); + Value npm_b = pos.non_pawn_material(BLACK); + + if (npm_w + npm_b == VALUE_ZERO) + { + 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]; + } + } + + // No pawns makes it difficult to win, even with a material advantage + if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) + { + e->factor[WHITE] = (uint8_t) + (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count(WHITE), 2)]); + } + + if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) + { + e->factor[BLACK] = (uint8_t) + (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count(BLACK), 2)]); + } + + // Compute the space weight + if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) + { + int minorPieceCount = pos.count(WHITE) + pos.count(WHITE) + + pos.count(BLACK) + pos.count(BLACK); + + e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0); + } + + // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder + // for the bishop pair "extended piece", this allow 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; +} + + +/// Material::game_phase() calculates the phase given the current +/// position. Because the phase is strictly a function of the material, it +/// is stored in MaterialEntry. + +Phase game_phase(const Position& pos) { + + Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); + + return npm >= MidgameLimit ? PHASE_MIDGAME + : npm <= EndgameLimit ? PHASE_ENDGAME + : Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); +} + +} // namespace Material diff --git a/stockfish-4-win/src/material.h b/stockfish-4-win/src/material.h new file mode 100644 index 0000000000000000000000000000000000000000..cbf6e5550e2b6b1ca55fcd0784ef7074920576cf --- /dev/null +++ b/stockfish-4-win/src/material.h @@ -0,0 +1,77 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 balance evaluation, a function pointer to a special +/// endgame evaluation function (which in most cases is NULL, meaning that the +/// standard evaluation function will be used), and "scale factors". +/// +/// 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 material_value() const { return make_score(value, value); } + Score space_weight() const { return spaceWeight; } + Phase game_phase() const { return gamePhase; } + bool specialized_eval_exists() const { return evaluationFunction != NULL; } + Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } + ScaleFactor scale_factor(const Position& pos, Color c) const; + + Key key; + int16_t value; + uint8_t factor[COLOR_NB]; + EndgameBase* evaluationFunction; + EndgameBase* scalingFunction[COLOR_NB]; + Score spaceWeight; + Phase gamePhase; +}; + +typedef HashTable Table; + +Entry* probe(const Position& pos, Table& entries, Endgames& endgames); +Phase game_phase(const Position& pos); + +/// Material::scale_factor takes a position and a color as input, and +/// returns a scale factor for the given color. We have to provide the +/// position in addition to the color, because the scale factor need not +/// to be a constant: It can also be a function which should be applied to +/// the position. For instance, in KBP vs K endgames, a scaling function +/// which checks for draws with rook pawns and wrong-colored bishops. + +inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const { + + return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE + ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos); +} + +} + +#endif // #ifndef MATERIAL_H_INCLUDED diff --git a/stockfish-4-win/src/misc.cpp b/stockfish-4-win/src/misc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..495ad4a8cee778a2c75a4f8620ca3af63222f46a --- /dev/null +++ b/stockfish-4-win/src/misc.cpp @@ -0,0 +1,204 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "thread.h" + +using namespace std; + +/// Version number. If Version is left empty, then compile date, in the +/// format DD-MM-YY, is shown in engine_info. +static const string Version = "4"; + + +/// 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 s, date(__DATE__); // From compiler, format is "Sep 21 2008" + + s << "Stockfish " << Version << setfill('0'); + + if (Version.empty()) + { + date >> month >> day >> year; + s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); + } + + s << (Is64Bit ? " 64" : "") + << (HasPopCnt ? " SSE4.2" : "") + << (to_uci ? "\nid author ": " by ") + << "Tord Romstad, Marco Costalba and Joona Kiiski"; + + return s.str(); +} + + +/// Debug functions used mainly to collect run-time statistics + +static uint64_t hits[2], means[2]; + +void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; } +void dbg_hit_on_c(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 " + << (float)means[1] / means[0] << endl; +} + + +/// 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 while preserving +/// usual i/o functionality and 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 splitted streambuf for cin and cout + + Tie(streambuf* b, ofstream* f) : buf(b), file(f) {} + + int sync() { return file->rdbuf()->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; + ofstream* file; + + int log(int c, const char* prefix) { + + static int last = '\n'; + + if (last == '\n') + file->rdbuf()->sputn(prefix, 3); + + return last = file->rdbuf()->sputc((char)c); + } +}; + +class Logger { + + Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {} + ~Logger() { start(false); } + + ofstream file; + Tie in, out; + +public: + static void start(bool b) { + + static Logger l; + + if (b && !l.file.is_open()) + { + l.file.open("io_log.txt", ifstream::out | ifstream::app); + cin.rdbuf(&l.in); + cout.rdbuf(&l.out); + } + else if (!b && l.file.is_open()) + { + cout.rdbuf(l.out.buf); + cin.rdbuf(l.in.buf); + l.file.close(); + } + } +}; + + +/// Used to serialize access to std::cout to avoid multiple threads to write 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(bool b) { Logger::start(b); } + + +/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap +/// conversion from milliseconds to struct timespec, as used by pthreads. + +void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) { + +#ifdef _WIN32 + int tm = msec; +#else + timespec ts, *tm = &ts; + uint64_t ms = Time::now() + msec; + + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000LL; +#endif + + cond_timedwait(sleepCond, sleepLock, tm); +} + + +/// prefetch() preloads the given address in L1/L2 cache. This is a non +/// blocking function and do not stalls the CPU waiting for data to be +/// loaded from memory, that can be quite slow. +#ifdef NO_PREFETCH + +void prefetch(char*) {} + +#else + +void prefetch(char* addr) { + +# if defined(__INTEL_COMPILER) + // This hack prevents prefetches to be optimized away by + // Intel compiler. Both MSVC and gcc seems not affected. + __asm__ (""); +# endif + +# if defined(__INTEL_COMPILER) || defined(_MSC_VER) + _mm_prefetch(addr, _MM_HINT_T0); +# else + __builtin_prefetch(addr); +# endif +} + +#endif diff --git a/stockfish-4-win/src/misc.h b/stockfish-4-win/src/misc.h new file mode 100644 index 0000000000000000000000000000000000000000..82baa5807b88c2edac488cdee838b149754f8741 --- /dev/null +++ b/stockfish-4-win/src/misc.h @@ -0,0 +1,68 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +extern const std::string engine_info(bool to_uci = false); +extern void timed_wait(WaitCondition&, Lock&, int); +extern void prefetch(char* addr); +extern void start_logger(bool b); + +extern void dbg_hit_on(bool b); +extern void dbg_hit_on_c(bool c, bool b); +extern void dbg_mean_of(int v); +extern void dbg_print(); + + +struct Log : public std::ofstream { + Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {} + ~Log() { if (is_open()) close(); } +}; + + +namespace Time { + typedef int64_t point; + inline point now() { return system_time_to_msec(); } +} + + +template +struct HashTable { + HashTable() : e(Size, Entry()) {} + Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; } + +private: + std::vector e; +}; + + +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 + +#endif // #ifndef MISC_H_INCLUDED diff --git a/stockfish-4-win/src/movegen.cpp b/stockfish-4-win/src/movegen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fab5e46182f924118c63fe797ea9e8bb87b921a0 --- /dev/null +++ b/stockfish-4-win/src/movegen.cpp @@ -0,0 +1,423 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +/// Simple macro to wrap a very common while loop, no facny, no flexibility, +/// hardcoded names 'mlist' and 'from'. +#define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b)) + +/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square +#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \ + (mlist++)->move = make_move(to - (d), to); } +namespace { + + template + ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) { + + if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) + return mlist; + + // After castling, the rook and king final positions are the same in Chess960 + // as they would be in standard chess. + Square kfrom = pos.king_square(us); + Square rfrom = pos.castle_rook_square(us, Side); + Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); + Bitboard enemies = pos.pieces(~us); + + assert(!pos.checkers()); + + const int K = Chess960 ? kto > kfrom ? -1 : 1 + : Side == KING_SIDE ? -1 : 1; + + for (Square s = kto; s != kfrom; s += (Square)K) + if (pos.attackers_to(s) & enemies) + return mlist; + + // 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 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) + return mlist; + + (mlist++)->move = make(kfrom, rfrom); + + if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) + mlist--; + + return mlist; + } + + + template + inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7, + Bitboard target, const CheckInfo* ci) { + + Bitboard b = shift_bb(pawnsOn7) & target; + + while (b) + { + Square to = pop_lsb(&b); + + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + (mlist++)->move = make(to - Delta, to, QUEEN); + + if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) + { + (mlist++)->move = make(to - Delta, to, ROOK); + (mlist++)->move = make(to - Delta, to, BISHOP); + (mlist++)->move = make(to - Delta, to, KNIGHT); + } + + // Knight-promotion is the only one that can give a direct check not + // already included in the queen-promotion. + if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) + (mlist++)->move = make(to - Delta, to, KNIGHT); + else + (void)ci; // Silence a warning under MSVC + } + + return mlist; + } + + + template + ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist, + Bitboard target, const CheckInfo* ci) { + + // 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 ? DELTA_N : DELTA_S); + const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); + const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + + Bitboard b1, b2, dc1, dc2, 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()); + + b1 = shift_bb(pawnsNotOn7) & emptySquares; + b2 = shift_bb(b1 & TRank3BB) & emptySquares; + + if (Type == EVASIONS) // Consider only blocking squares + { + b1 &= target; + b2 &= target; + } + + if (Type == QUIET_CHECKS) + { + b1 &= pos.attacks_from(ci->ksq, Them); + b2 &= pos.attacks_from(ci->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 among captures. + if (pawnsNotOn7 & ci->dcCandidates) + { + dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); + dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; + + b1 |= dc1; + b2 |= dc2; + } + } + + SERIALIZE_PAWNS(b1, Up); + SERIALIZE_PAWNS(b2, Up + Up); + } + + // Promotions and underpromotions + if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) + { + if (Type == CAPTURES) + emptySquares = ~pos.pieces(); + + if (Type == EVASIONS) + emptySquares &= target; + + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ci); + } + + // Standard and en-passant captures + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + { + b1 = shift_bb(pawnsNotOn7) & enemies; + b2 = shift_bb(pawnsNotOn7) & enemies; + + SERIALIZE_PAWNS(b1, Right); + SERIALIZE_PAWNS(b2, Left); + + 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 mlist; + + b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); + + assert(b1); + + while (b1) + (mlist++)->move = make(pop_lsb(&b1), pos.ep_square()); + } + } + + return mlist; + } + + + template FORCE_INLINE + ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us, + Bitboard target, const CheckInfo* ci) { + + assert(Pt != KING && Pt != PAWN); + + const Square* pl = pos.list(us); + + for (Square from = *pl; from != SQ_NONE; from = *++pl) + { + if (Checks) + { + if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) + && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) + continue; + + if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from)) + continue; + } + + Bitboard b = pos.attacks_from(from) & target; + + if (Checks) + b &= ci->checkSq[Pt]; + + SERIALIZE(b); + } + + return mlist; + } + + + template FORCE_INLINE + ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target, + const CheckInfo* ci = NULL) { + + const bool Checks = Type == QUIET_CHECKS; + + mlist = generate_pawn_moves(pos, mlist, target, ci); + mlist = generate_moves(pos, mlist, Us, target, ci); + mlist = generate_moves(pos, mlist, Us, target, ci); + mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci); + mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci); + + if (Type != QUIET_CHECKS && Type != EVASIONS) + { + Square from = pos.king_square(Us); + Bitboard b = pos.attacks_from(from) & target; + SERIALIZE(b); + } + + if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) + { + if (pos.is_chess960()) + { + mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us); + mlist = generate_castle(pos, mlist, Us); + } + else + { + mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us); + mlist = generate_castle(pos, mlist, Us); + } + } + + return mlist; + } + + +} // 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* mlist) { + + 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, mlist, target) + : generate_all(pos, mlist, 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* mlist) { + + assert(!pos.checkers()); + + Color us = pos.side_to_move(); + CheckInfo ci(pos); + Bitboard dc = ci.dcCandidates; + + while (dc) + { + Square from = pop_lsb(&dc); + PieceType pt = type_of(pos.piece_on(from)); + + if (pt == PAWN) + continue; // Will be generated togheter with direct checks + + Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); + + if (pt == KING) + b &= ~PseudoAttacks[QUEEN][ci.ksq]; + + SERIALIZE(b); + } + + return us == WHITE ? generate_all(pos, mlist, ~pos.pieces(), &ci) + : generate_all(pos, mlist, ~pos.pieces(), &ci); +} + + +/// 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* mlist) { + + assert(pos.checkers()); + + int checkersCnt = 0; + Color us = pos.side_to_move(); + Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq; + Bitboard sliderAttacks = 0; + Bitboard b = pos.checkers(); + + assert(pos.checkers()); + + // Find squares attacked by slider checkers, we will remove them from the king + // evasions so to skip known illegal moves avoiding useless legality check later. + do + { + checkersCnt++; + checksq = pop_lsb(&b); + + assert(color_of(pos.piece_on(checksq)) == ~us); + + switch (type_of(pos.piece_on(checksq))) + { + case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break; + case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break; + case QUEEN: + // If queen and king are far or not on a diagonal line we can safely + // remove all the squares attacked in the other direction becuase are + // not reachable by the king anyway. + if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq)) + sliderAttacks |= PseudoAttacks[QUEEN][checksq]; + + // Otherwise we need to use real rook attacks to check if king is safe + // to move in the other direction. For example: king in B2, queen in A1 + // a knight in B1, and we can safely move to C1. + else + sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from(checksq); + + default: + break; + } + } while (b); + + // Generate evasions for king, capture and non capture moves + b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; + SERIALIZE(b); + + if (checkersCnt > 1) + return mlist; // Double check, only a king move can save the day + + // Generate blocking evasions or captures of the checking piece + Bitboard target = between_bb(checksq, ksq) | checksq; + + return us == WHITE ? generate_all(pos, mlist, target) + : generate_all(pos, mlist, target); +} + + +/// generate generates all the legal moves in the given position + +template<> +ExtMove* generate(const Position& pos, ExtMove* mlist) { + + ExtMove *end, *cur = mlist; + Bitboard pinned = pos.pinned_pieces(); + Square ksq = pos.king_square(pos.side_to_move()); + + end = pos.checkers() ? generate(pos, mlist) + : generate(pos, mlist); + while (cur != end) + if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) + && !pos.pl_move_is_legal(cur->move, pinned)) + cur->move = (--end)->move; + else + cur++; + + return end; +} diff --git a/stockfish-4-win/src/movegen.h b/stockfish-4-win/src/movegen.h new file mode 100644 index 0000000000000000000000000000000000000000..2a4cda90dffef0117e450fdbe1a7491103f30bb8 --- /dev/null +++ b/stockfish-4-win/src/movegen.h @@ -0,0 +1,58 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +enum GenType { + CAPTURES, + QUIETS, + QUIET_CHECKS, + EVASIONS, + NON_EVASIONS, + LEGAL +}; + +class Position; + +template +ExtMove* generate(const Position& pos, ExtMove* mlist); + +/// The MoveList struct is a simple wrapper around generate(), sometimes comes +/// handy to use this class instead of the low level generate() function. +template +struct MoveList { + + explicit MoveList(const Position& pos) : cur(mlist), last(generate(pos, mlist)) { last->move = MOVE_NONE; } + void operator++() { cur++; } + Move operator*() const { return cur->move; } + size_t size() const { return last - mlist; } + bool contains(Move m) const { + for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true; + return false; + } + +private: + ExtMove mlist[MAX_MOVES]; + ExtMove *cur, *last; +}; + +#endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/stockfish-4-win/src/movepick.cpp b/stockfish-4-win/src/movepick.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f2fa01eb231ba64e10feaba33d1af33977a35be --- /dev/null +++ b/stockfish-4-win/src/movepick.cpp @@ -0,0 +1,377 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, + EVASION, EVASIONS_S2, + QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, + QSEARCH_1, CAPTURES_S4, + PROBCUT, CAPTURES_S5, + RECAPTURE, CAPTURES_S6, + STOP + }; + + // Our insertion sort, guaranteed to be stable, as is needed + 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; + } + } + + // Unary predicate used by std::partition to split positive scores from remaining + // ones so to sort separately the two sets, and with the second sort delayed. + inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; } + + // Picks and moves to the front the best move in the range [begin, end), + // it is faster than sorting all the moves in advance when moves are few, as + // normally are the possible captures. + inline ExtMove* pick_best(ExtMove* begin, ExtMove* end) + { + std::swap(*begin, *std::max_element(begin, end)); + return begin; + } +} + + +/// 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 about how important good +/// move ordering is at the current node. + +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, + Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) { + + assert(d > DEPTH_ZERO); + + cur = end = moves; + endBadCaptures = moves + MAX_MOVES - 1; + countermoves = cm; + ss = s; + + if (p.checkers()) + stage = EVASION; + + else + stage = MAIN_SEARCH; + + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + end += (ttMove != MOVE_NONE); +} + +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, + Square sq) : pos(p), history(h), cur(moves), end(moves) { + + assert(d <= DEPTH_ZERO); + + if (p.checkers()) + stage = EVASION; + + else if (d > DEPTH_QS_NO_CHECKS) + stage = QSEARCH_0; + + else if (d > DEPTH_QS_RECAPTURES) + { + stage = QSEARCH_1; + + // Skip TT move if is not a capture or a promotion, this avoids qsearch + // tree explosion due to a possible perpetual check or similar rare cases + // when TT table is full. + if (ttm && !pos.is_capture_or_promotion(ttm)) + ttm = MOVE_NONE; + } + else + { + stage = RECAPTURE; + recaptureSquare = sq; + ttm = MOVE_NONE; + } + + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + end += (ttMove != MOVE_NONE); +} + +MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt) + : pos(p), history(h), cur(moves), end(moves) { + + assert(!pos.checkers()); + + stage = PROBCUT; + + // In ProbCut we generate only captures better than parent's captured piece + captureThreshold = PieceValue[MG][pt]; + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + + if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) + ttMove = MOVE_NONE; + + end += (ttMove != MOVE_NONE); +} + + +/// score() assign a numerical move ordering score to each move in a move list. +/// The moves with highest scores will be picked first. +template<> +void MovePicker::score() { + // Winning and equal captures in the main search are ordered by MVV/LVA. + // Suprisingly, this appears to perform slightly better than SEE based + // move ordering. The reason is probably that in a position with a winning + // capture, capturing a more valuable (but sufficiently defended) piece + // first usually doesn't hurt. The opponent will have to recapture, and + // the hanging piece will still be hanging (except in the unusual cases + // where it is possible to recapture with the hanging piece). Exchanging + // big pieces before capturing a hanging piece probably helps to reduce + // the subtree size. + // In main search we want to push captures with negative SEE values to + // badCaptures[] array, but instead of doing it now we delay till when + // the move has been picked up in pick_move_from_list(), this way we save + // some SEE calls in case we get a cutoff (idea from Pablo Vazquez). + Move m; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)); + + if (type_of(m) == PROMOTION) + it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; + + else if (type_of(m) == ENPASSANT) + it->score += PieceValue[MG][PAWN]; + } +} + +template<> +void MovePicker::score() { + + Move m; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + it->score = history[pos.piece_moved(m)][to_sq(m)]; + } +} + +template<> +void MovePicker::score() { + // Try good captures ordered by MVV/LVA, then non-captures if destination square + // is not under attack, ordered by history value, then bad-captures and quiet + // moves with a negative SEE. This last group is ordered by the SEE score. + Move m; + int seeScore; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + if ((seeScore = pos.see_sign(m)) < 0) + it->score = seeScore - HistoryStats::Max; // At the bottom + + else if (pos.is_capture(m)) + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)) + HistoryStats::Max; + else + it->score = history[pos.piece_moved(m)][to_sq(m)]; + } +} + + +/// generate_next() generates, scores and sorts the next bunch of moves, when +/// there are no more moves to try for the current phase. + +void MovePicker::generate_next() { + + cur = moves; + + switch (++stage) { + + case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: + end = generate(pos, moves); + score(); + return; + + case KILLERS_S1: + cur = killers; + end = cur + 2; + + killers[0].move = ss->killers[0]; + killers[1].move = ss->killers[1]; + killers[2].move = killers[3].move = MOVE_NONE; + + // Be sure countermoves are different from killers + for (int i = 0; i < 2; i++) + if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move) + (end++)->move = countermoves[i]; + + if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races + killers[3].move = MOVE_NONE; + + return; + + case QUIETS_1_S1: + endQuiets = end = generate(pos, moves); + score(); + end = std::partition(cur, end, has_positive_score); + insertion_sort(cur, end); + return; + + case QUIETS_2_S1: + cur = end; + end = endQuiets; + if (depth >= 3 * ONE_PLY) + insertion_sort(cur, end); + return; + + case BAD_CAPTURES_S1: + // Just pick them in reverse order to get MVV/LVA ordering + cur = moves + MAX_MOVES - 1; + end = endBadCaptures; + return; + + case EVASIONS_S2: + end = generate(pos, moves); + if (end > moves + 1) + score(); + return; + + case QUIET_CHECKS_S3: + end = generate(pos, moves); + return; + + case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: + stage = STOP; + case STOP: + end = cur + 1; // Avoid another next_phase() call + return; + + default: + assert(false); + } +} + + +/// next_move() is the most important method of the MovePicker class. It returns +/// a new pseudo legal move every time is called, until there are no more moves +/// left. It picks the move with the biggest score from a list of generated moves +/// taking care not returning the ttMove if has already been searched previously. +template<> +Move MovePicker::next_move() { + + Move move; + + while (true) + { + while (cur == end) + generate_next(); + + switch (stage) { + + case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: + cur++; + return ttMove; + + case CAPTURES_S1: + move = pick_best(cur++, end)->move; + if (move != ttMove) + { + if (pos.see_sign(move) >= 0) + return move; + + // Losing capture, move it to the tail of the array + (endBadCaptures--)->move = move; + } + break; + + case KILLERS_S1: + move = (cur++)->move; + if ( move != MOVE_NONE + && pos.is_pseudo_legal(move) + && move != ttMove + && !pos.is_capture(move)) + return move; + break; + + case QUIETS_1_S1: case QUIETS_2_S1: + move = (cur++)->move; + if ( move != ttMove + && move != killers[0].move + && move != killers[1].move + && move != killers[2].move + && move != killers[3].move) + return move; + break; + + case BAD_CAPTURES_S1: + return (cur--)->move; + + case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: + move = pick_best(cur++, end)->move; + if (move != ttMove) + return move; + break; + + case CAPTURES_S5: + move = pick_best(cur++, end)->move; + if (move != ttMove && pos.see(move) > captureThreshold) + return move; + break; + + case CAPTURES_S6: + move = pick_best(cur++, end)->move; + if (to_sq(move) == recaptureSquare) + return move; + break; + + case QUIET_CHECKS_S3: + move = (cur++)->move; + if (move != ttMove) + return move; + break; + + case STOP: + return MOVE_NONE; + + default: + assert(false); + } + } +} + + +/// Version of next_move() to use at split point nodes where the move is grabbed +/// from the split point's shared MovePicker object. This function is not thread +/// safe so must be lock protected by the caller. +template<> +Move MovePicker::next_move() { return ss->splitPoint->movePicker->next_move(); } diff --git a/stockfish-4-win/src/movepick.h b/stockfish-4-win/src/movepick.h new file mode 100644 index 0000000000000000000000000000000000000000..c444615f5b9f2e8ab4a0952ff47e3c97bf642079 --- /dev/null +++ b/stockfish-4-win/src/movepick.h @@ -0,0 +1,110 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "search.h" +#include "types.h" + + +/// The Stats struct stores moves statistics. According to the template parameter +/// the class can store History, Gains 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. Gains records the move's +/// best evaluation gain from one ply to the next and is used for pruning decisions. +/// Countermoves store the move that refute a previous one. Entries are stored +/// according only to 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(2000); + + const T* operator[](Piece p) const { return table[p]; } + void clear() { std::memset(table, 0, sizeof(table)); } + + void update(Piece p, Square to, Move m) { + + if (m == table[p][to].first) + return; + + table[p][to].second = table[p][to].first; + table[p][to].first = m; + } + + void update(Piece p, Square to, Value v) { + + if (Gain) + table[p][to] = std::max(v, table[p][to] - 1); + + else if (abs(table[p][to] + v) < Max) + table[p][to] += v; + } + +private: + T table[PIECE_NB][SQUARE_NB]; +}; + +typedef Stats< true, Value> GainsStats; +typedef Stats HistoryStats; +typedef Stats > CountermovesStats; + + +/// MovePicker class is used to pick one pseudo legal move at a time from the +/// current position. The most important method is next_move(), which returns a +/// new pseudo legal move each time it is called, until there are no moves left, +/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha +/// beta algorithm, MovePicker attempts to return the moves which are most likely +/// to get a cut-off first. + +class MovePicker { + + MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC + +public: + MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); + MovePicker(const Position&, Move, const HistoryStats&, PieceType); + MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*); + + template Move next_move(); + +private: + template void score(); + void generate_next(); + + const Position& pos; + const HistoryStats& history; + Search::Stack* ss; + Move* countermoves; + Depth depth; + Move ttMove; + ExtMove killers[4]; + Square recaptureSquare; + int captureThreshold, stage; + ExtMove *cur, *end, *endQuiets, *endBadCaptures; + ExtMove moves[MAX_MOVES]; +}; + +#endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/stockfish-4-win/src/notation.cpp b/stockfish-4-win/src/notation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c96b9dbe78c1fb93fe69d67639ce4efb11f366bc --- /dev/null +++ b/stockfish-4-win/src/notation.cpp @@ -0,0 +1,263 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "movegen.h" +#include "notation.h" +#include "position.h" + +using namespace std; + +static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" }; + + +/// score_to_uci() converts a value to a string suitable for use with the UCI +/// protocol specifications: +/// +/// 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 score_to_uci(Value v, Value alpha, Value beta) { + + stringstream s; + + if (abs(v) < VALUE_MATE_IN_MAX_PLY) + s << "cp " << v * 100 / int(PawnValueMg); + else + s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + + s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); + + return s.str(); +} + + +/// move_to_uci() converts a move to a string in coordinate notation +/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print +/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 +/// mode. Internally castle moves are always coded as "king captures rook". + +const string move_to_uci(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) == CASTLE && !chess960) + to = (to > from ? FILE_G : FILE_C) | rank_of(from); + + string move = square_to_string(from) + square_to_string(to); + + if (type_of(m) == PROMOTION) + move += PieceToChar[BLACK][promotion_type(m)]; // Lower case + + return move; +} + + +/// move_from_uci() takes a position and a string representing a move in +/// simple coordinate notation and returns an equivalent legal Move if any. + +Move move_from_uci(const Position& pos, string& str) { + + if (str.length() == 5) // Junior could send promotion piece in uppercase + str[4] = char(tolower(str[4])); + + for (MoveList it(pos); *it; ++it) + if (str == move_to_uci(*it, pos.is_chess960())) + return *it; + + return MOVE_NONE; +} + + +/// move_to_san() takes a position and a legal Move as input and returns its +/// short algebraic notation representation. + +const string move_to_san(Position& pos, Move m) { + + if (m == MOVE_NONE) + return "(none)"; + + if (m == MOVE_NULL) + return "(null)"; + + assert(MoveList(pos).contains(m)); + + Bitboard others, b; + string san; + Color us = pos.side_to_move(); + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = pos.piece_on(from); + PieceType pt = type_of(pc); + + if (type_of(m) == CASTLE) + san = to > from ? "O-O" : "O-O-O"; + else + { + if (pt != PAWN) + { + san = PieceToChar[WHITE][pt]; // Upper case + + // Disambiguation if we have more then one piece of type 'pt' that can + // reach 'to' with a legal move. + others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; + + while (b) + { + Move move = make_move(pop_lsb(&b), to); + if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) + others ^= from_sq(move); + } + + if (others) + { + if (!(others & file_bb(from))) + san += file_to_char(file_of(from)); + + else if (!(others & rank_bb(from))) + san += rank_to_char(rank_of(from)); + + else + san += square_to_string(from); + } + } + else if (pos.is_capture(m)) + san = file_to_char(file_of(from)); + + if (pos.is_capture(m)) + san += 'x'; + + san += square_to_string(to); + + if (type_of(m) == PROMOTION) + san += string("=") + PieceToChar[WHITE][promotion_type(m)]; + } + + if (pos.move_gives_check(m, CheckInfo(pos))) + { + StateInfo st; + pos.do_move(m, st); + san += MoveList(pos).size() ? "+" : "#"; + pos.undo_move(m); + } + + return san; +} + + +/// pretty_pv() formats human-readable search information, typically to be +/// appended to the search log file. It uses the two helpers below to pretty +/// format time and score respectively. + +static string time_to_string(int64_t msecs) { + + const int MSecMinute = 1000 * 60; + const int MSecHour = 1000 * 60 * 60; + + int64_t hours = msecs / MSecHour; + int64_t minutes = (msecs % MSecHour) / MSecMinute; + int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000; + + stringstream s; + + if (hours) + s << hours << ':'; + + s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds; + + return s.str(); +} + +static string score_to_string(Value v) { + + stringstream s; + + if (v >= VALUE_MATE_IN_MAX_PLY) + s << "#" << (VALUE_MATE - v + 1) / 2; + + else if (v <= VALUE_MATED_IN_MAX_PLY) + s << "-#" << (VALUE_MATE + v) / 2; + + else + s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg; + + return s.str(); +} + +string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) { + + const int64_t K = 1000; + const int64_t M = 1000000; + + std::stack st; + Move* m = pv; + string san, padding; + size_t length; + stringstream s; + + s << setw(2) << depth + << setw(8) << score_to_string(value) + << setw(8) << time_to_string(msecs); + + if (pos.nodes_searched() < M) + s << setw(8) << pos.nodes_searched() / 1 << " "; + + else if (pos.nodes_searched() < K * M) + s << setw(7) << pos.nodes_searched() / K << "K "; + + else + s << setw(7) << pos.nodes_searched() / M << "M "; + + padding = string(s.str().length(), ' '); + length = padding.length(); + + while (*m != MOVE_NONE) + { + san = move_to_san(pos, *m); + + if (length + san.length() > 80) + { + s << "\n" + padding; + length = padding.length(); + } + + s << san << ' '; + length += san.length() + 1; + + st.push(StateInfo()); + pos.do_move(*m++, st.top()); + } + + while (m != pv) + pos.undo_move(*--m); + + return s.str(); +} diff --git a/stockfish-4-win/src/notation.h b/stockfish-4-win/src/notation.h new file mode 100644 index 0000000000000000000000000000000000000000..3ede26074ac703bab48d2dee6612ab39c4be93be --- /dev/null +++ b/stockfish-4-win/src/notation.h @@ -0,0 +1,35 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 NOTATION_H_INCLUDED +#define NOTATION_H_INCLUDED + +#include + +#include "types.h" + +class Position; + +std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); +Move move_from_uci(const Position& pos, std::string& str); +const std::string move_to_uci(Move m, bool chess960); +const std::string move_to_san(Position& pos, Move m); +std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); + +#endif // #ifndef NOTATION_H_INCLUDED diff --git a/stockfish-4-win/src/pawns.cpp b/stockfish-4-win/src/pawns.cpp new file mode 100644 index 0000000000000000000000000000000000000000..718dd575957076d73e62b0bf47350619e826891c --- /dev/null +++ b/stockfish-4-win/src/pawns.cpp @@ -0,0 +1,281 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "pawns.h" +#include "position.h" + +namespace { + + #define V Value + #define S(mg, eg) make_score(mg, eg) + + // Doubled pawn penalty by opposed flag and file + const Score Doubled[2][FILE_NB] = { + { S(13, 43), S(20, 48), S(23, 48), S(23, 48), + S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, + { S(13, 43), S(20, 48), S(23, 48), S(23, 48), + S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; + + // Isolated pawn penalty by opposed flag and file + const Score Isolated[2][FILE_NB] = { + { S(37, 45), S(54, 52), S(60, 52), S(60, 52), + S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, + { S(25, 30), S(36, 35), S(40, 35), S(40, 35), + S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; + + // Backward pawn penalty by opposed flag and file + const Score Backward[2][FILE_NB] = { + { S(30, 42), S(43, 46), S(49, 46), S(49, 46), + S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, + { S(20, 28), S(29, 31), S(33, 31), S(33, 31), + S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; + + // Pawn chain membership bonus by file + const Score ChainMember[FILE_NB] = { + S(11,-1), S(13,-1), S(13,-1), S(14,-1), + S(14,-1), S(13,-1), S(13,-1), S(11,-1) + }; + + // Candidate passed pawn bonus by rank + const Score CandidatePassed[RANK_NB] = { + S( 0, 0), S( 6, 13), S(6,13), S(14,29), + S(34,68), S(83,166), S(0, 0), S( 0, 0) + }; + + // Weakness of our pawn shelter in front of the king indexed by [rank] + const Value ShelterWeakness[RANK_NB] = + { V(100), V(0), V(27), V(73), V(92), V(101), V(101) }; + + // Danger of enemy pawns moving toward our king indexed by + // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn] + const Value StormDanger[3][RANK_NB] = { + { V( 0), V(64), V(128), V(51), V(26) }, + { V(26), V(32), V( 96), V(38), V(20) }, + { V( 0), V( 0), V( 64), V(25), V(13) }}; + + // Max bonus for king safety. Corresponds to start position with all the pawns + // in front of the king and no enemy pawn on the horizont. + const Value MaxSafetyBonus = V(263); + + #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 ? DELTA_N : DELTA_S); + const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); + const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + + Bitboard b; + Square s; + File f; + Rank r; + bool passed, isolated, doubled, opposed, chain, backward, candidate; + Score value = SCORE_ZERO; + const Square* pl = pos.list(Us); + + Bitboard ourPawns = pos.pieces(Us, PAWN); + Bitboard theirPawns = pos.pieces(Them, PAWN); + + e->passedPawns[Us] = 0; + e->kingSquares[Us] = SQ_NONE; + e->semiopenFiles[Us] = 0xFF; + e->pawnAttacks[Us] = shift_bb(ourPawns) | shift_bb(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)); + + f = file_of(s); + r = rank_of(s); + + // This file cannot be semi-open + e->semiopenFiles[Us] &= ~(1 << f); + + // Our rank plus previous one. Used for chain detection + b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); + + // Flag the pawn as passed, isolated, doubled or member of a pawn + // chain (but not the backward one). + chain = ourPawns & adjacent_files_bb(f) & b; + isolated = !(ourPawns & adjacent_files_bb(f)); + doubled = ourPawns & forward_bb(Us, s); + opposed = theirPawns & forward_bb(Us, s); + passed = !(theirPawns & passed_pawn_mask(Us, s)); + + // Test for backward pawn + backward = false; + + // If the pawn is passed, isolated, or member of a pawn chain it cannot + // be backward. If there are friendly pawns behind on adjacent files + // or if can capture an enemy pawn it cannot be backward either. + if ( !(passed | isolated | chain) + && !(ourPawns & pawn_attack_span(Them, s)) + && !(pos.attacks_from(s, Us) & theirPawns)) + { + // We now know that there are no friendly pawns beside or behind this + // pawn on adjacent files. We now check whether the pawn is + // backward by looking in the forward direction on the adjacent + // files, and seeing whether we meet a friendly or an enemy pawn first. + b = pos.attacks_from(s, Us); + + // Note that we are sure to find something because pawn is not passed + // nor isolated, so loop is potentially infinite, but it isn't. + while (!(b & (ourPawns | theirPawns))) + b = shift_bb(b); + + // The friendly pawn needs to be at least two ranks closer than the + // enemy pawn in order to help the potentially backward pawn advance. + backward = (b | shift_bb(b)) & theirPawns; + } + + assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); + + // A not passed pawn is a candidate to become passed if it is free to + // advance and if the number of friendly pawns beside or behind this + // pawn on adjacent files is higher or equal than the number of + // enemy pawns in the forward direction on the adjacent files. + candidate = !(opposed | passed | backward | isolated) + && (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0 + && popcount(b) >= popcount(pawn_attack_span(Us, s) & theirPawns); + + // Passed pawns will be properly scored in evaluation because we need + // full attack info to evaluate passed pawns. Only the frontmost passed + // pawn on each file is considered a true passed pawn. + if (passed && !doubled) + e->passedPawns[Us] |= s; + + // Score this pawn + if (isolated) + value -= Isolated[opposed][f]; + + if (doubled) + value -= Doubled[opposed][f]; + + if (backward) + value -= Backward[opposed][f]; + + if (chain) + value += ChainMember[f]; + + if (candidate) + value += CandidatePassed[relative_rank(Us, s)]; + } + + return value; + } + +} // namespace + +namespace Pawns { + +/// probe() takes a position object as input, computes a Entry object, and returns +/// a pointer to it. The result is also stored in a hash table, so we don't have +/// to recompute everything when the same pawn structure occurs again. + +Entry* probe(const Position& pos, Table& entries) { + + Key key = pos.pawn_key(); + Entry* e = entries[key]; + + if (e->key == key) + return e; + + e->key = key; + e->value = evaluate(pos, e) - evaluate(pos, e); + 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); + + Value safety = MaxSafetyBonus; + 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); + Rank rkUs, rkThem; + File kf = file_of(ksq); + + kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf; + + for (int f = kf - 1; f <= kf + 1; f++) + { + b = ourPawns & FileBB[f]; + rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; + safety -= ShelterWeakness[rkUs]; + + b = theirPawns & FileBB[f]; + rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; + safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; + } + + return safety; +} + + +/// Entry::update_safety() calculates and caches a bonus for king safety. It is +/// called only when king square changes, about 20% of total king_safety() calls. + +template +Score Entry::update_safety(const Position& pos, Square ksq) { + + kingSquares[Us] = ksq; + castleRights[Us] = pos.can_castle(Us); + minKPdistance[Us] = 0; + + Bitboard pawns = pos.pieces(Us, PAWN); + if (pawns) + while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} + + if (relative_rank(Us, ksq) > RANK_4) + return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]); + + Value bonus = shelter_storm(pos, ksq); + + // If we can castle use the bonus after the castle if is bigger + if (pos.can_castle(make_castle_right(Us, KING_SIDE))) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); + + if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE))) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); + + return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]); +} + +// Explicit template instantiation +template Score Entry::update_safety(const Position& pos, Square ksq); +template Score Entry::update_safety(const Position& pos, Square ksq); + +} // namespace Pawns diff --git a/stockfish-4-win/src/pawns.h b/stockfish-4-win/src/pawns.h new file mode 100644 index 0000000000000000000000000000000000000000..9f2af30e4f5028472e493e1ea42ee5fbd630691b --- /dev/null +++ b/stockfish-4-win/src/pawns.h @@ -0,0 +1,78 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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. Currently, +/// it only includes a middle game and end game pawn structure evaluation, and a +/// bitboard of passed pawns. We may want to add further information in the future. +/// A lookup to the pawn hash table (performed by calling the probe function) +/// returns a pointer to an Entry object. + +struct Entry { + + Score pawns_value() const { return value; } + Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } + Bitboard passed_pawns(Color c) const { return passedPawns[c]; } + int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; } + int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); } + int semiopen_on_side(Color c, File f, bool left) const { + + return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1)); + } + + template + Score king_safety(const Position& pos, Square ksq) { + + return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us) + ? kingSafety[Us] : update_safety(pos, ksq); + } + + template + Score update_safety(const Position& pos, Square ksq); + + template + Value shelter_storm(const Position& pos, Square ksq); + + Key key; + Bitboard passedPawns[COLOR_NB]; + Bitboard pawnAttacks[COLOR_NB]; + Square kingSquares[COLOR_NB]; + int minKPdistance[COLOR_NB]; + int castleRights[COLOR_NB]; + Score value; + int semiopenFiles[COLOR_NB]; + Score kingSafety[COLOR_NB]; + int pawnsOnSquares[COLOR_NB][COLOR_NB]; +}; + +typedef HashTable Table; + +Entry* probe(const Position& pos, Table& entries); + +} + +#endif // #ifndef PAWNS_H_INCLUDED diff --git a/stockfish-4-win/src/platform.h b/stockfish-4-win/src/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..ee0389840ceac87113dd0a6e8b65f3a928374e1c --- /dev/null +++ b/stockfish-4-win/src/platform.h @@ -0,0 +1,116 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 PLATFORM_H_INCLUDED +#define PLATFORM_H_INCLUDED + +#ifdef _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' +#pragma warning(disable: 4996) // Function _ftime() may be unsafe + +// MSVC does not support +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else +# include +#endif + +#ifndef _WIN32 // Linux - Unix + +# include + +inline int64_t system_time_to_msec() { + timeval t; + gettimeofday(&t, NULL); + return t.tv_sec * 1000LL + t.tv_usec / 1000; +} + +# include +typedef pthread_mutex_t Lock; +typedef pthread_cond_t WaitCondition; +typedef pthread_t NativeHandle; +typedef void*(*pt_start_fn)(void*); + +# define lock_init(x) pthread_mutex_init(&(x), NULL) +# define lock_grab(x) pthread_mutex_lock(&(x)) +# define lock_release(x) pthread_mutex_unlock(&(x)) +# define lock_destroy(x) pthread_mutex_destroy(&(x)) +# define cond_destroy(x) pthread_cond_destroy(&(x)) +# define cond_init(x) pthread_cond_init(&(x), NULL) +# define cond_signal(x) pthread_cond_signal(&(x)) +# define cond_wait(x,y) pthread_cond_wait(&(x),&(y)) +# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z) +# define thread_create(x,f,t) pthread_create(&(x),NULL,(pt_start_fn)f,t) +# define thread_join(x) pthread_join(x, NULL) + +#else // Windows and MinGW + +# include + +inline int64_t system_time_to_msec() { + _timeb t; + _ftime(&t); + return t.time * 1000LL + t.millitm; +} + +#ifndef NOMINMAX +# define NOMINMAX // disable macros min() and max() +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +// We use critical sections on Windows to support Windows XP and older versions, +// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject() +// but apart from this they have the same speed performance of SRW locks. +typedef CRITICAL_SECTION Lock; +typedef HANDLE WaitCondition; +typedef HANDLE NativeHandle; + +// On Windows 95 and 98 parameter lpThreadId my not be null +inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; } + +# define lock_init(x) InitializeCriticalSection(&(x)) +# define lock_grab(x) EnterCriticalSection(&(x)) +# define lock_release(x) LeaveCriticalSection(&(x)) +# define lock_destroy(x) DeleteCriticalSection(&(x)) +# define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); } +# define cond_destroy(x) CloseHandle(x) +# define cond_signal(x) SetEvent(x) +# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } +# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } +# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge())) +# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); } + +#endif + +#endif // #ifndef PLATFORM_H_INCLUDED diff --git a/stockfish-4-win/src/position.cpp b/stockfish-4-win/src/position.cpp new file mode 100644 index 0000000000000000000000000000000000000000..615763cdcf93007a3e09b42d8a2f8727e31f19c7 --- /dev/null +++ b/stockfish-4-win/src/position.cpp @@ -0,0 +1,1453 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "bitcount.h" +#include "movegen.h" +#include "notation.h" +#include "position.h" +#include "psqtab.h" +#include "rkiss.h" +#include "thread.h" +#include "tt.h" + +using std::string; +using std::cout; +using std::endl; + +static const string PieceToChar(" PNBRQK pnbrqk"); + +CACHE_LINE_ALIGNMENT + +Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +Value PieceValue[PHASE_NB][PIECE_NB] = { +{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, +{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; + +namespace Zobrist { + + Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + Key enpassant[FILE_NB]; + Key castle[CASTLE_RIGHT_NB]; + Key side; + Key exclusion; +} + +Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} + +namespace { + +// min_attacker() is an 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 FORCE_INLINE +PieceType min_attacker(const Bitboard* bb, const Square& to, const 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<> FORCE_INLINE +PieceType min_attacker(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { + return KING; // No need to update bitboards, it is the last cycle +} + +} // namespace + + +/// CheckInfo c'tor + +CheckInfo::CheckInfo(const Position& pos) { + + Color them = ~pos.side_to_move(); + ksq = pos.king_square(them); + + pinned = pos.pinned_pieces(); + dcCandidates = pos.discovered_check_candidates(); + + checkSq[PAWN] = pos.attacks_from(ksq, them); + checkSq[KNIGHT] = pos.attacks_from(ksq); + checkSq[BISHOP] = pos.attacks_from(ksq); + checkSq[ROOK] = pos.attacks_from(ksq); + checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; + checkSq[KING] = 0; +} + + +/// Position::init() initializes at startup the various arrays used to compute +/// hash keys and the piece square tables. The latter is a two-step operation: +/// First, the white halves of the tables are copied from PSQT[] tables. Second, +/// the black halves of the tables are initialized by flipping and changing the +/// sign of the white scores. + +void Position::init() { + + RKISS rk; + + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + for (Square s = SQ_A1; s <= SQ_H8; s++) + Zobrist::psq[c][pt][s] = rk.rand(); + + for (File f = FILE_A; f <= FILE_H; f++) + Zobrist::enpassant[f] = rk.rand(); + + for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++) + { + Bitboard b = cr; + while (b) + { + Key k = Zobrist::castle[1ULL << pop_lsb(&b)]; + Zobrist::castle[cr] ^= k ? k : rk.rand(); + } + } + + Zobrist::side = rk.rand(); + Zobrist::exclusion = rk.rand(); + + for (PieceType pt = PAWN; pt <= KING; pt++) + { + PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; + PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; + + Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + { + psq[WHITE][pt][ s] = (v + PSQT[pt][s]); + psq[BLACK][pt][~s] = -(v + PSQT[pt][s]); + } + } +} + + +/// Position::operator=() creates a copy of 'pos'. We want the new born Position +/// object do not depend on any external data so we detach state pointer from +/// the source one. + +Position& Position::operator=(const Position& pos) { + + std::memcpy(this, &pos, sizeof(Position)); + startState = *st; + st = &startState; + nodes = 0; + + assert(pos_is_ok()); + + return *this; +} + + +/// 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. + +void Position::set(const string& fenStr, bool isChess960, 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") while Black take 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. +*/ + + char col, row, token; + size_t p; + Square sq = SQ_A8; + std::istringstream ss(fenStr); + + clear(); + 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 ((p = PieceToChar.find(token)) != string::npos) + { + put_piece(sq, color_of(Piece(p)), type_of(Piece(p))); + 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; + + token = char(toupper(token)); + + if (token == 'K') + for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {} + + else if (token == 'Q') + for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {} + + else if (token >= 'A' && token <= 'H') + rsq = File(token - 'A') | relative_rank(c, RANK_1); + + else + continue; + + set_castle_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 = File(col - 'a') | Rank(row - '1'); + + if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) + 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) + int(sideToMove == BLACK); + + st->key = compute_key(); + st->pawnKey = compute_pawn_key(); + st->materialKey = compute_material_key(); + st->psq = compute_psq_score(); + st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); + st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); + st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); + chess960 = isChess960; + thisThread = th; + + assert(pos_is_ok()); +} + + +/// Position::set_castle_right() is an helper function used to set castling +/// rights given the corresponding color and the rook starting square. + +void Position::set_castle_right(Color c, Square rfrom) { + + Square kfrom = king_square(c); + CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; + CastleRight cr = make_castle_right(c, cs); + + st->castleRights |= cr; + castleRightsMask[kfrom] |= cr; + castleRightsMask[rfrom] |= cr; + castleRookSquare[c][cs] = 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) + castlePath[c][cs] |= s; + + for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++) + if (s != kfrom && s != rfrom) + castlePath[c][cs] |= s; +} + + +/// Position::fen() returns a FEN representation of the position. In case +/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function. + +const string Position::fen() const { + + std::ostringstream ss; + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) + { + for (File file = FILE_A; file <= FILE_H; file++) + { + Square sq = file | rank; + + if (is_empty(sq)) + { + int emptyCnt = 1; + + for ( ; file < FILE_H && is_empty(sq++); file++) + emptyCnt++; + + ss << emptyCnt; + } + else + ss << PieceToChar[piece_on(sq)]; + } + + if (rank > RANK_1) + ss << '/'; + } + + ss << (sideToMove == WHITE ? " w " : " b "); + + if (can_castle(WHITE_OO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K'); + + if (can_castle(WHITE_OOO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q'); + + if (can_castle(BLACK_OO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k'); + + if (can_castle(BLACK_OOO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q'); + + if (st->castleRights == CASTLES_NONE) + ss << '-'; + + ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") + << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2; + + return ss.str(); +} + + +/// Position::pretty() returns an ASCII representation of the position to be +/// printed to the standard output together with the move's san notation. + +const string Position::pretty(Move move) const { + + const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; + const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" + + dottedLine + "\n| . | | . | | . | | . | |"; + + string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)]; + } + + std::ostringstream ss; + + if (move) + ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") + << move_to_san(*const_cast(this), move); + + ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase + << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; + + for (Bitboard b = checkers(); b; ) + ss << square_to_string(pop_lsb(&b)) << " "; + + ss << "\nLegal moves: "; + for (MoveList it(*this); *it; ++it) + ss << move_to_san(*const_cast(this), *it) << " "; + + return ss.str(); +} + + +/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check +/// pieces, according to the call parameters. Pinned pieces protect our king, +/// discovery check pieces attack the enemy king. + +Bitboard Position::hidden_checkers(Square ksq, Color c) const { + + Bitboard b, pinners, result = 0; + + // Pinners are sliders that give check when pinned piece is removed + pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq]) + | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c); + + while (pinners) + { + b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); + + if (!more_than_one(b)) + result |= b & pieces(sideToMove); + } + return result; +} + + +/// Position::attackers_to() computes a bitboard of all pieces which attack a +/// given square. Slider attacks use occ bitboard as occupancy. + +Bitboard Position::attackers_to(Square s, Bitboard occ) const { + + return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) + | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) + | (attacks_from(s) & pieces(KNIGHT)) + | (attacks_bb(s, occ) & pieces(ROOK, QUEEN)) + | (attacks_bb(s, occ) & pieces(BISHOP, QUEEN)) + | (attacks_from(s) & pieces(KING)); +} + + +/// Position::attacks_from() computes a bitboard of all attacks of a given piece +/// put in a given square. Slider attacks use occ bitboard as occupancy. + +Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) { + + assert(is_ok(s)); + + switch (type_of(p)) + { + case BISHOP: return attacks_bb(s, occ); + case ROOK : return attacks_bb(s, occ); + case QUEEN : return attacks_bb(s, occ) | attacks_bb(s, occ); + default : return StepAttacksBB[p][s]; + } +} + + +/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal + +bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { + + assert(is_ok(m)); + assert(pinned == pinned_pieces()); + + Color us = sideToMove; + Square from = from_sq(m); + + assert(color_of(piece_moved(m)) == us); + assert(piece_on(king_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) + { + Color them = ~us; + Square to = to_sq(m); + Square capsq = to + pawn_push(them); + Square ksq = king_square(us); + Bitboard b = (pieces() ^ from ^ capsq) | to; + + assert(to == ep_square()); + assert(piece_moved(m) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(them, PAWN)); + assert(piece_on(to) == NO_PIECE); + + return !(attacks_bb< ROOK>(ksq, b) & pieces(them, QUEEN, ROOK)) + && !(attacks_bb(ksq, b) & pieces(them, 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) == CASTLE || !(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 + || !(pinned & from) + || squares_aligned(from, to_sq(m), king_square(us)); +} + + +/// Position::is_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::is_pseudo_legal(const Move m) const { + + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_moved(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) - 2 != 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) + { + // Move direction must be compatible with pawn color + int direction = to - from; + if ((us == WHITE) != (direction > 0)) + return false; + + // We have already handled promotion moves, so destination + // cannot be on the 8/1th rank. + if (rank_of(to) == RANK_8 || rank_of(to) == RANK_1) + return false; + + // Proceed according to the square delta between the origin and + // destination squares. + switch (direction) + { + case DELTA_NW: + case DELTA_NE: + case DELTA_SW: + case DELTA_SE: + // Capture. The destination square must be occupied by an enemy + // piece (en passant captures was handled earlier). + if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us) + return false; + + // From and to files must be one file apart, avoids a7h5 + if (abs(file_of(from) - file_of(to)) != 1) + return false; + break; + + case DELTA_N: + case DELTA_S: + // Pawn push. The destination square must be empty. + if (!is_empty(to)) + return false; + break; + + case DELTA_NN: + // Double white pawn push. The destination square must be on the fourth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( rank_of(to) != RANK_4 + || !is_empty(to) + || !is_empty(from + DELTA_N)) + return false; + break; + + case DELTA_SS: + // Double black pawn push. The destination square must be on the fifth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( rank_of(to) != RANK_5 + || !is_empty(to) + || !is_empty(from + DELTA_S)) + return false; + break; + + default: + return false; + } + } + else if (!(attacks_from(pc, from) & to)) + return false; + + // Evasions generator already takes care to avoid some kind of illegal moves + // and pl_move_is_legal() relies on this. So we 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()), king_square(us)) | checkers()) & to)) + return false; + } + // In case of king moves under check we have to remove king so to catch + // as invalid moves like b1a1 when opposite queen is on c1. + else if (attackers_to(to, pieces() ^ from) & pieces(~us)) + return false; + } + + return true; +} + + +/// Position::move_gives_check() tests whether a pseudo-legal move gives a check + +bool Position::move_gives_check(Move m, const CheckInfo& ci) const { + + assert(is_ok(m)); + assert(ci.dcCandidates == discovered_check_candidates()); + assert(color_of(piece_moved(m)) == sideToMove); + + Square from = from_sq(m); + Square to = to_sq(m); + PieceType pt = type_of(piece_on(from)); + + // Direct check ? + if (ci.checkSq[pt] & to) + return true; + + // Discovery check ? + if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from)) + { + // For pawn and king moves we need to verify also direction + if ( (pt != PAWN && pt != KING) + || !squares_aligned(from, to, king_square(~sideToMove))) + return true; + } + + // Can we skip the ugly special cases ? + if (type_of(m) == NORMAL) + return false; + + Color us = sideToMove; + Square ksq = king_square(~us); + + switch (type_of(m)) + { + case PROMOTION: + return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; + + // En passant capture with check ? We have already handled the case + // of direct checks and ordinary discovered check, the only case we + // need to handle is the unusual case of a discovered check through + // the captured pawn. + case ENPASSANT: + { + Square capsq = file_of(to) | rank_of(from); + Bitboard b = (pieces() ^ from ^ capsq) | to; + + return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) + | (attacks_bb(ksq, b) & pieces(us, QUEEN, BISHOP)); + } + case CASTLE: + { + Square kfrom = from; + Square rfrom = to; // 'King captures the rook' notation + Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1); + Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1); + + return (PseudoAttacks[ROOK][rto] & ksq) + && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq); + } + 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) { + + CheckInfo ci(*this); + do_move(m, newSt, ci, move_gives_check(m, ci)); +} + +void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { + + assert(is_ok(m)); + assert(&newSt != st); + + nodes++; + Key k = st->key; + + // Copy some fields of old state to our new StateInfo object except the ones + // which are going to be recalculated from scratch anyway, then switch our state + // pointer to point to the new, ready to be updated, state. + std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); + + newSt.previous = st; + st = &newSt; + + // Update side to move + k ^= Zobrist::side; + + // Increment ply counters.In particular rule50 will be later reset it to zero + // 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); + PieceType pt = type_of(pc); + PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); + + assert(color_of(pc) == us); + assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); + assert(capture != KING); + + if (type_of(m) == CASTLE) + { + assert(pc == make_piece(us, KING)); + + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + + do_castle(from, to, rfrom, rto); + + st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom]; + k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + } + + if (capture) + { + Square capsq = to; + + // If the captured piece is a pawn, update pawn hash key, otherwise + // update non-pawn material. + if (capture == PAWN) + { + if (type_of(m) == ENPASSANT) + { + capsq += pawn_push(them); + + assert(pt == 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; + } + + st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; + } + else + st->npMaterial[them] -= PieceValue[MG][capture]; + + // Update board and piece lists + remove_piece(capsq, them, capture); + + // Update material hash key and prefetch access to materialTable + k ^= Zobrist::psq[them][capture][capsq]; + st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; + prefetch((char*)thisThread->materialTable[st->materialKey]); + + // Update incremental scores + st->psq -= psq[them][capture][capsq]; + + // Reset rule 50 counter + st->rule50 = 0; + } + + // Update hash key + k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; + + // Reset en passant square + if (st->epSquare != SQ_NONE) + { + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } + + // Update castle rights if needed + if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to])) + { + int cr = castleRightsMask[from] | castleRightsMask[to]; + k ^= Zobrist::castle[st->castleRights & cr]; + st->castleRights &= ~cr; + } + + // Prefetch TT access as soon as we know the new hash key + prefetch((char*)TT.first_entry(k)); + + // Move the piece. The tricky Chess960 castle is handled earlier + if (type_of(m) != CASTLE) + move_piece(from, to, us, pt); + + // If the moving piece is a pawn do some special extra work + if (pt == PAWN) + { + // Set en-passant square, only if moved pawn can be captured + if ( (int(to) ^ int(from)) == 16 + && (attacks_from(from + pawn_push(us), us) & pieces(them, PAWN))) + { + st->epSquare = Square((from + to) / 2); + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + } + + if (type_of(m) == PROMOTION) + { + PieceType promotion = promotion_type(m); + + assert(relative_rank(us, to) == RANK_8); + assert(promotion >= KNIGHT && promotion <= QUEEN); + + remove_piece(to, us, PAWN); + put_piece(to, us, promotion); + + // Update hash keys + k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; + st->pawnKey ^= Zobrist::psq[us][PAWN][to]; + st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] + ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; + + // Update incremental score + st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; + + // Update material + st->npMaterial[us] += PieceValue[MG][promotion]; + } + + // Update pawn hash key and prefetch access to pawnsTable + st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + prefetch((char*)thisThread->pawnsTable[st->pawnKey]); + + // Reset rule 50 draw counter + st->rule50 = 0; + } + + // Update incremental scores + st->psq += psq[us][pt][to] - psq[us][pt][from]; + + // Set capture piece + st->capturedType = capture; + + // Update the key with the final value + st->key = k; + + // Update checkers bitboard, piece must be already moved + st->checkersBB = 0; + + if (moveIsCheck) + { + if (type_of(m) != NORMAL) + st->checkersBB = attackers_to(king_square(them)) & pieces(us); + else + { + // Direct checks + if (ci.checkSq[pt] & to) + st->checkersBB |= to; + + // Discovery checks + if (ci.dcCandidates && (ci.dcCandidates & from)) + { + if (pt != ROOK) + st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, ROOK); + + if (pt != BISHOP) + st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, BISHOP); + } + } + } + + sideToMove = ~sideToMove; + + 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; + Color them = ~us; + Square from = from_sq(m); + Square to = to_sq(m); + PieceType pt = type_of(piece_on(to)); + PieceType capture = st->capturedType; + + assert(is_empty(from) || type_of(m) == CASTLE); + assert(capture != KING); + + if (type_of(m) == PROMOTION) + { + PieceType promotion = promotion_type(m); + + assert(promotion == pt); + assert(relative_rank(us, to) == RANK_8); + assert(promotion >= KNIGHT && promotion <= QUEEN); + + remove_piece(to, us, promotion); + put_piece(to, us, PAWN); + pt = PAWN; + } + + if (type_of(m) == CASTLE) + { + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + pt = KING; + do_castle(to, from, rto, rfrom); + } + else + move_piece(to, from, us, pt); // Put the piece back at the source square + + if (capture) + { + Square capsq = to; + + if (type_of(m) == ENPASSANT) + { + capsq -= pawn_push(us); + + assert(pt == PAWN); + assert(to == st->previous->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(capsq) == NO_PIECE); + } + + put_piece(capsq, them, capture); // Restore the captured piece + } + + // Finally point our state pointer back to the previous state + st = st->previous; + gamePly--; + + assert(pos_is_ok()); +} + + +/// Position::do_castle() is a helper used to do/undo a castling move. This +/// is a bit tricky, especially in Chess960. + +void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { + + // Remove both pieces first since squares could overlap in Chess960 + remove_piece(kfrom, sideToMove, KING); + remove_piece(rfrom, sideToMove, ROOK); + board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us + put_piece(kto, sideToMove, KING); + put_piece(rto, sideToMove, ROOK); +} + + +/// 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()); + + std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here + + 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((char*)TT.first_entry(st->key)); + + st->rule50++; + st->pliesFromNull = 0; + + sideToMove = ~sideToMove; + + assert(pos_is_ok()); +} + +void Position::undo_null_move() { + + assert(!checkers()); + + st = st->previous; + sideToMove = ~sideToMove; +} + + +/// Position::see() is a static exchange evaluator: It tries to estimate the +/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes +/// tempi into account. If the side who initiated the capturing sequence does the +/// last capture, he loses a tempo and if the result is below 'asymmThreshold' +/// the capturing sequence is considered bad. + +int Position::see_sign(Move m) const { + + assert(is_ok(m)); + + // Early return if SEE cannot be negative because captured piece value + // is not less then capturing one. Note that king moves always return + // here because king midgame value is set to 0. + if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))]) + return 1; + + return see(m); +} + +int Position::see(Move m, int asymmThreshold) const { + + Square from, to; + Bitboard occupied, attackers, stmAttackers; + int swapList[32], slIndex = 1; + PieceType captured; + Color stm; + + assert(is_ok(m)); + + from = from_sq(m); + to = to_sq(m); + swapList[0] = PieceValue[MG][type_of(piece_on(to))]; + stm = color_of(piece_on(from)); + occupied = pieces() ^ from; + + // Castle moves are implemented as king capturing the rook so cannot be + // handled correctly. Simply return 0 that is always the correct value + // unless in the rare case the rook ends up under attack. + if (type_of(m) == CASTLE) + return 0; + + if (type_of(m) == ENPASSANT) + { + occupied ^= to - pawn_push(stm); // Remove the captured pawn + swapList[0] = PieceValue[MG][PAWN]; + } + + // Find all attackers to the destination square, with the moving piece + // removed, but possibly an X-ray attacker added behind it. + attackers = attackers_to(to, occupied) & occupied; + + // If the opponent has no attackers we are finished + stm = ~stm; + stmAttackers = attackers & pieces(stm); + if (!stmAttackers) + return swapList[0]; + + // The destination square is defended, which makes things rather more + // difficult to compute. We proceed by building up a "swap list" containing + // the material gain or loss at each stop in a sequence of captures to the + // destination square, where the sides alternately capture, and always + // capture with the least valuable piece. After each capture, we look for + // new X-ray attacks from behind the capturing piece. + captured = type_of(piece_on(from)); + + do { + assert(slIndex < 32); + + // Add the new entry to the swap list + swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; + slIndex++; + + // Locate and remove the next least valuable attacker + captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + stm = ~stm; + stmAttackers = attackers & pieces(stm); + + // Stop before processing a king capture + if (captured == KING && stmAttackers) + { + swapList[slIndex++] = QueenValueMg * 16; + break; + } + + } while (stmAttackers); + + // If we are doing asymmetric SEE evaluation and the same side does the first + // and the last capture, he loses a tempo and gain must be at least worth + // 'asymmThreshold', otherwise we replace the score with a very low value, + // before negamaxing. + if (asymmThreshold) + for (int i = 0; i < slIndex; i += 2) + if (swapList[i] < asymmThreshold) + swapList[i] = - QueenValueMg * 16; + + // Having built the swap list, we negamax through it to find the best + // achievable score from the point of view of the side to move. + while (--slIndex) + swapList[slIndex-1] = std::min(-swapList[slIndex], swapList[slIndex-1]); + + return swapList[0]; +} + + +/// Position::clear() erases the position object to a pristine state, with an +/// empty board, white to move, and no castling rights. + +void Position::clear() { + + std::memset(this, 0, sizeof(Position)); + startState.epSquare = SQ_NONE; + st = &startState; + + for (int i = 0; i < 8; i++) + for (int j = 0; j < 16; j++) + pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; +} + + +/// Position::compute_key() computes the hash key of the position. The hash +/// key is usually updated incrementally as moves are made and unmade, the +/// compute_key() function is only used when a new position is set up, and +/// to verify the correctness of the hash key when running in debug mode. + +Key Position::compute_key() const { + + Key k = Zobrist::castle[st->castleRights]; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s]; + } + + if (ep_square() != SQ_NONE) + k ^= Zobrist::enpassant[file_of(ep_square())]; + + if (sideToMove == BLACK) + k ^= Zobrist::side; + + return k; +} + + +/// Position::compute_pawn_key() computes the hash key of the position. The +/// hash key is usually updated incrementally as moves are made and unmade, +/// the compute_pawn_key() function is only used when a new position is set +/// up, and to verify the correctness of the pawn hash key when running in +/// debug mode. + +Key Position::compute_pawn_key() const { + + Key k = 0; + + for (Bitboard b = pieces(PAWN); b; ) + { + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; + } + + return k; +} + + +/// Position::compute_material_key() computes the hash key of the position. +/// The hash key is usually updated incrementally as moves are made and unmade, +/// the compute_material_key() function is only used when a new position is set +/// up, and to verify the correctness of the material hash key when running in +/// debug mode. + +Key Position::compute_material_key() const { + + Key k = 0; + + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= QUEEN; pt++) + for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++) + k ^= Zobrist::psq[c][pt][cnt]; + + return k; +} + + +/// Position::compute_psq_score() computes the incremental scores for the middle +/// game and the endgame. These functions are used to initialize the incremental +/// scores when a new position is set up, and to verify that the scores are correctly +/// updated by do_move and undo_move when the program is running in debug mode. +Score Position::compute_psq_score() const { + + Score score = SCORE_ZERO; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + Piece pc = piece_on(s); + score += psq[color_of(pc)][type_of(pc)][s]; + } + + return score; +} + + +/// Position::compute_non_pawn_material() computes the total non-pawn middle +/// game material value for the given side. Material values are updated +/// incrementally during the search, this function is only used while +/// initializing a new Position object. + +Value Position::compute_non_pawn_material(Color c) const { + + Value value = VALUE_ZERO; + + for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) + value += pieceCount[c][pt] * PieceValue[MG][pt]; + + return value; +} + + +/// Position::is_draw() tests whether the position is drawn by material, +/// repetition, or the 50 moves rule. It does not detect stalemates, this +/// must be done by the search. +bool Position::is_draw() const { + + // Draw by material? + if ( !pieces(PAWN) + && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) + return true; + + // Draw by the 50 moves rule? + if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) + return true; + + // Draw by repetition? + int i = 4, e = std::min(st->rule50, st->pliesFromNull); + + if (i <= e) + { + StateInfo* stp = st->previous->previous; + + do { + stp = stp->previous->previous; + + if (stp->key == st->key) + return true; + + i += 2; + + } while (i <= e); + } + + return false; +} + + +/// Position::flip() flips position with the white and black sides reversed. This +/// is only useful for debugging especially for finding evaluation symmetry bugs. + +static char toggle_case(char c) { + return char(islower(c) ? toupper(c) : tolower(c)); +} + +void Position::flip() { + + string f, token; + std::stringstream ss(fen()); + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement + { + std::getline(ss, token, rank > 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(), toggle_case); + + 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(), this_thread()); + + assert(pos_is_ok()); +} + + +/// Position::pos_is_ok() performs some consitency checks for the position object. +/// This is meant to be helpful when debugging. + +bool Position::pos_is_ok(int* failedStep) const { + + int dummy, *step = failedStep ? failedStep : &dummy; + + // What features of the position should be verified? + const bool all = false; + + const bool debugBitboards = all || false; + const bool debugKingCount = all || false; + const bool debugKingCapture = all || false; + const bool debugCheckerCount = all || false; + const bool debugKey = all || false; + const bool debugMaterialKey = all || false; + const bool debugPawnKey = all || false; + const bool debugIncrementalEval = all || false; + const bool debugNonPawnMaterial = all || false; + const bool debugPieceCounts = all || false; + const bool debugPieceList = all || false; + const bool debugCastleSquares = all || false; + + *step = 1; + + if (sideToMove != WHITE && sideToMove != BLACK) + return false; + + if ((*step)++, piece_on(king_square(WHITE)) != W_KING) + return false; + + if ((*step)++, piece_on(king_square(BLACK)) != B_KING) + return false; + + if ((*step)++, debugKingCount) + { + int kingCount[COLOR_NB] = {}; + + for (Square s = SQ_A1; s <= SQ_H8; s++) + if (type_of(piece_on(s)) == KING) + kingCount[color_of(piece_on(s))]++; + + if (kingCount[0] != 1 || kingCount[1] != 1) + return false; + } + + if ((*step)++, debugKingCapture) + if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove)) + return false; + + if ((*step)++, debugCheckerCount && popcount(st->checkersBB) > 2) + return false; + + if ((*step)++, debugBitboards) + { + // The intersection of the white and black pieces must be empty + if (pieces(WHITE) & pieces(BLACK)) + return false; + + // The union of the white and black pieces must be equal to all + // occupied squares + if ((pieces(WHITE) | pieces(BLACK)) != pieces()) + return false; + + // Separate piece type bitboards must have empty intersections + for (PieceType p1 = PAWN; p1 <= KING; p1++) + for (PieceType p2 = PAWN; p2 <= KING; p2++) + if (p1 != p2 && (pieces(p1) & pieces(p2))) + return false; + } + + if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6) + return false; + + if ((*step)++, debugKey && st->key != compute_key()) + return false; + + if ((*step)++, debugPawnKey && st->pawnKey != compute_pawn_key()) + return false; + + if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key()) + return false; + + if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score()) + return false; + + if ((*step)++, debugNonPawnMaterial) + if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE) + || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK)) + return false; + + if ((*step)++, debugPieceCounts) + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + if (pieceCount[c][pt] != popcount(pieces(c, pt))) + return false; + + if ((*step)++, debugPieceList) + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + for (int i = 0; i < pieceCount[c][pt]; i++) + if ( board[pieceList[c][pt][i]] != make_piece(c, pt) + || index[pieceList[c][pt][i]] != i) + return false; + + if ((*step)++, debugCastleSquares) + for (Color c = WHITE; c <= BLACK; c++) + for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) + { + CastleRight cr = make_castle_right(c, s); + + if (!can_castle(cr)) + continue; + + if ( (castleRightsMask[king_square(c)] & cr) != cr + || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK) + || castleRightsMask[castleRookSquare[c][s]] != cr) + return false; + } + + *step = 0; + return true; +} diff --git a/stockfish-4-win/src/position.h b/stockfish-4-win/src/position.h new file mode 100644 index 0000000000000000000000000000000000000000..bf387dc6f1d848e84fff0eac5994c9b9c7adcff6 --- /dev/null +++ b/stockfish-4-win/src/position.h @@ -0,0 +1,459 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitboard.h" +#include "types.h" + + +/// The checkInfo struct is initialized at c'tor time and keeps info used +/// to detect if a move gives check. +class Position; +struct Thread; + +struct CheckInfo { + + explicit CheckInfo(const Position&); + + Bitboard dcCandidates; + Bitboard pinned; + Bitboard checkSq[PIECE_TYPE_NB]; + Square ksq; +}; + + +/// The StateInfo struct stores information we need to restore a Position +/// object to its previous state when we retract a move. Whenever a move +/// is made on the board (by calling Position::do_move), a StateInfo object +/// must be passed as a parameter. + +struct StateInfo { + Key pawnKey, materialKey; + Value npMaterial[COLOR_NB]; + int castleRights, rule50, pliesFromNull; + Score psq; + Square epSquare; + + Key key; + Bitboard checkersBB; + PieceType capturedType; + StateInfo* previous; +}; + + +/// When making a move the current StateInfo up to 'key' excluded is copied to +/// the new one. Here we calculate the quad words (64bits) needed to be copied. +const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; + + +/// The position data structure. A position consists of the following data: +/// +/// * For each piece type, a bitboard representing the squares occupied +/// by pieces of that type. +/// * For each color, a bitboard representing the squares occupied by +/// pieces of that color. +/// * A bitboard of all occupied squares. +/// * A bitboard of all checking pieces. +/// * A 64-entry array of pieces, indexed by the squares of the board. +/// * The current side to move. +/// * Information about the castling rights for both sides. +/// * The initial files of the kings and both pairs of rooks. This is +/// used to implement the Chess960 castling rules. +/// * The en passant square (which is SQ_NONE if no en passant capture is +/// possible). +/// * The squares of the kings for both sides. +/// * Hash keys for the position itself, the current pawn structure, and +/// the current material situation. +/// * Hash keys for all previous positions in the game for detecting +/// repetition draws. +/// * A counter for detecting 50 move rule draws. + +class Position { +public: + Position() {} + Position(const Position& p, Thread* t) { *this = p; thisThread = t; } + Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } + Position& operator=(const Position&); + static void init(); + + // Text input/output + void set(const std::string& fen, bool isChess960, Thread* th); + const std::string fen() const; + const std::string pretty(Move m = MOVE_NONE) 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 king_square(Color c) const; + Square ep_square() const; + bool is_empty(Square s) const; + template int count(Color c) const; + template const Square* list(Color c) const; + + // Castling + int can_castle(CastleRight f) const; + int can_castle(Color c) const; + bool castle_impeded(Color c, CastlingSide s) const; + Square castle_rook_square(Color c, CastlingSide s) const; + + // Checking + Bitboard checkers() const; + Bitboard discovered_check_candidates() const; + Bitboard pinned_pieces() const; + + // Attacks to/from a given square + Bitboard attackers_to(Square s) const; + Bitboard attackers_to(Square s, Bitboard occ) const; + Bitboard attacks_from(Piece p, Square s) const; + static Bitboard attacks_from(Piece p, Square s, Bitboard occ); + template Bitboard attacks_from(Square s) const; + template Bitboard attacks_from(Square s, Color c) const; + + // Properties of moves + bool move_gives_check(Move m, const CheckInfo& ci) const; + bool pl_move_is_legal(Move m, Bitboard pinned) const; + bool is_pseudo_legal(const Move m) const; + bool is_capture(Move m) const; + bool is_capture_or_promotion(Move m) const; + bool is_passed_pawn_push(Move m) const; + Piece piece_moved(Move m) const; + PieceType captured_piece_type() const; + + // Piece specific + bool pawn_is_passed(Color c, Square s) const; + bool pawn_on_7th(Color c) const; + bool opposite_bishops() const; + bool bishop_pair(Color c) const; + + // Doing and undoing moves + void do_move(Move m, StateInfo& st); + void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); + void undo_move(Move m); + void do_null_move(StateInfo& st); + void undo_null_move(); + + // Static exchange evaluation + int see(Move m, int asymmThreshold = 0) const; + int see_sign(Move m) const; + + // Accessing hash keys + Key key() const; + Key exclusion_key() const; + Key pawn_key() const; + Key material_key() const; + + // Incremental piece-square evaluation + Score psq_score() const; + Value non_pawn_material(Color c) const; + + // Other properties of the position + Color side_to_move() const; + int game_ply() const; + bool is_chess960() const; + Thread* this_thread() const; + int64_t nodes_searched() const; + void set_nodes_searched(int64_t n); + bool is_draw() const; + + // Position consistency check, for debugging + bool pos_is_ok(int* failedStep = NULL) const; + void flip(); + +private: + // Initialization helpers (used while setting up a position) + void clear(); + void set_castle_right(Color c, Square rfrom); + + // Helper functions + void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); + Bitboard hidden_checkers(Square ksq, Color c) const; + void put_piece(Square s, Color c, PieceType pt); + void remove_piece(Square s, Color c, PieceType pt); + void move_piece(Square from, Square to, Color c, PieceType pt); + + // Computing hash keys from scratch (for initialization and debugging) + Key compute_key() const; + Key compute_pawn_key() const; + Key compute_material_key() const; + + // Computing incremental evaluation scores and material counts + Score compute_psq_score() const; + Value compute_non_pawn_material(Color c) const; + + // Board and pieces + Piece board[SQUARE_NB]; + Bitboard byTypeBB[PIECE_TYPE_NB]; + Bitboard byColorBB[COLOR_NB]; + int pieceCount[COLOR_NB][PIECE_TYPE_NB]; + Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; + int index[SQUARE_NB]; + + // Other info + int castleRightsMask[SQUARE_NB]; + Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB]; + Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB]; + StateInfo startState; + int64_t nodes; + int gamePly; + Color sideToMove; + Thread* thisThread; + StateInfo* st; + int chess960; +}; + +inline int64_t Position::nodes_searched() const { + return nodes; +} + +inline void Position::set_nodes_searched(int64_t n) { + nodes = n; +} + +inline Piece Position::piece_on(Square s) const { + return board[s]; +} + +inline Piece Position::piece_moved(Move m) const { + return board[from_sq(m)]; +} + +inline bool Position::is_empty(Square s) const { + return board[s] == NO_PIECE; +} + +inline Color Position::side_to_move() const { + return sideToMove; +} + +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[c][Pt]; +} + +template inline const Square* Position::list(Color c) const { + return pieceList[c][Pt]; +} + +inline Square Position::ep_square() const { + return st->epSquare; +} + +inline Square Position::king_square(Color c) const { + return pieceList[c][KING][0]; +} + +inline int Position::can_castle(CastleRight f) const { + return st->castleRights & f; +} + +inline int Position::can_castle(Color c) const { + return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); +} + +inline bool Position::castle_impeded(Color c, CastlingSide s) const { + return byTypeBB[ALL_PIECES] & castlePath[c][s]; +} + +inline Square Position::castle_rook_square(Color c, CastlingSide s) const { + return castleRookSquare[c][s]; +} + +template +inline Bitboard Position::attacks_from(Square s) const { + + return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, 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 p, Square s) const { + return attacks_from(p, 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 hidden_checkers(king_square(~sideToMove), sideToMove); +} + +inline Bitboard Position::pinned_pieces() const { + return hidden_checkers(king_square(sideToMove), ~sideToMove); +} + +inline bool Position::pawn_is_passed(Color c, Square s) const { + return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); +} + +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->npMaterial[c]; +} + +inline bool Position::is_passed_pawn_push(Move m) const { + + return type_of(piece_moved(m)) == PAWN + && pawn_is_passed(sideToMove, to_sq(m)); +} + +inline int Position::game_ply() const { + return gamePly; +} + +inline bool Position::opposite_bishops() const { + + return pieceCount[WHITE][BISHOP] == 1 + && pieceCount[BLACK][BISHOP] == 1 + && opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); +} + +inline bool Position::bishop_pair(Color c) const { + + return pieceCount[c][BISHOP] >= 2 + && opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]); +} + +inline bool Position::pawn_on_7th(Color c) const { + return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); +} + +inline bool Position::is_chess960() const { + return chess960; +} + +inline bool Position::is_capture_or_promotion(Move m) const { + + assert(is_ok(m)); + return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m)); +} + +inline bool Position::is_capture(Move m) const { + + // Note that castle is coded as "king captures the rook" + assert(is_ok(m)); + return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; +} + +inline PieceType Position::captured_piece_type() const { + return st->capturedType; +} + +inline Thread* Position::this_thread() const { + return thisThread; +} + +inline void Position::put_piece(Square s, Color c, PieceType pt) { + + board[s] = make_piece(c, pt); + byTypeBB[ALL_PIECES] |= s; + byTypeBB[pt] |= s; + byColorBB[c] |= s; + index[s] = pieceCount[c][pt]++; + pieceList[c][pt][index[s]] = s; +} + +inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { + + // 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[pt] ^= from_to_bb; + byColorBB[c] ^= from_to_bb; + board[from] = NO_PIECE; + board[to] = make_piece(c, pt); + index[to] = index[from]; + pieceList[c][pt][index[to]] = to; +} + +inline void Position::remove_piece(Square s, Color c, PieceType pt) { + + // 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 guaranteed to be invariant to a do_move() + undo_move() sequence. + byTypeBB[ALL_PIECES] ^= s; + byTypeBB[pt] ^= s; + byColorBB[c] ^= s; + /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing + Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; + index[lastSquare] = index[s]; + pieceList[c][pt][index[lastSquare]] = lastSquare; + pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; +} + +#endif // #ifndef POSITION_H_INCLUDED diff --git a/stockfish-4-win/src/psqtab.h b/stockfish-4-win/src/psqtab.h new file mode 100644 index 0000000000000000000000000000000000000000..f45442dabb1d3e8a33ce9ee2352d14e8f00c740b --- /dev/null +++ b/stockfish-4-win/src/psqtab.h @@ -0,0 +1,98 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 PSQTAB_H_INCLUDED +#define PSQTAB_H_INCLUDED + +#include "types.h" + +#define S(mg, eg) make_score(mg, eg) + + +/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on +/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined +/// for white side, for black side the tables are symmetric. + +static const Score PSQT[][SQUARE_NB] = { + { }, + { // Pawn + S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), + S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), + S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) + }, + { // Knight + S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104), + S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79), + S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55), + S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42), + S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42), + S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55), + S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79), + S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104) + }, + { // Bishop + S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59), + S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), + S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), + S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), + S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), + S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), + S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), + S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59) + }, + { // Rook + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3) + }, + { // Queen + S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80), + S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), + S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), + S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), + S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), + S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), + S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), + S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80) + }, + { // King + S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18), + S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77), + S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105), + S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135), + S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135), + S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105), + S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77), + S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18) + } +}; + +#undef S + +#endif // #ifndef PSQTAB_H_INCLUDED diff --git a/stockfish-4-win/src/rkiss.h b/stockfish-4-win/src/rkiss.h new file mode 100644 index 0000000000000000000000000000000000000000..9564253b91a81301a70fd029163eac470248187c --- /dev/null +++ b/stockfish-4-win/src/rkiss.h @@ -0,0 +1,74 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 . + + This file is based on original code by Heinz van Saanen and is + available under 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. +*/ + +#ifndef RKISS_H_INCLUDED +#define RKISS_H_INCLUDED + +#include "types.h" + +/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys. +/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a +/// specific version that Heinz van Saanen derived from some public domain code +/// by Bob Jenkins. Following the feature list, as tested by Heinz. +/// +/// - Quite platform independent +/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-) +/// - ~12 times faster than my *nix sys-rand() +/// - ~4 times faster than SSE2-version of Mersenne twister +/// - Average cycle length: ~2^126 +/// - 64 bit seed +/// - Return doubles with a full 53 bit mantissa +/// - Thread safe + +class RKISS { + + struct S { uint64_t a, b, c, d; } s; // Keep variables always together + + uint64_t rotate(uint64_t x, uint64_t k) const { + return (x << k) | (x >> (64 - k)); + } + + uint64_t rand64() { + + const uint64_t + e = s.a - rotate(s.b, 7); + s.a = s.b ^ rotate(s.c, 13); + s.b = s.c + rotate(s.d, 37); + s.c = s.d + e; + return s.d = e + s.a; + } + +public: + RKISS(int seed = 73) { + + s.a = 0xf1ea5eed; + s.b = s.c = s.d = 0xd4e12c77; + for (int i = 0; i < seed; i++) // Scramble a few rounds + rand64(); + } + + template T rand() { return T(rand64()); } +}; + +#endif // #ifndef RKISS_H_INCLUDED diff --git a/stockfish-4-win/src/search.cpp b/stockfish-4-win/src/search.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a7cde2b5fe848b7be3c778c263fc5d730da9a5b --- /dev/null +++ b/stockfish-4-win/src/search.cpp @@ -0,0 +1,1810 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "book.h" +#include "evaluate.h" +#include "movegen.h" +#include "movepick.h" +#include "notation.h" +#include "search.h" +#include "timeman.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +namespace Search { + + volatile SignalsType Signals; + LimitsType Limits; + std::vector RootMoves; + Position RootPos; + Color RootColor; + Time::point SearchTime; + StateStackPtr SetupStates; +} + +using std::string; +using Eval::evaluate; +using namespace Search; + +namespace { + + // Set to true to force running with one thread. Used for debugging + const bool FakeSplit = false; + + // This is the minimum interval in msec between two check_time() calls + const int TimerResolution = 5; + + // Different node types, used as template parameter + enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; + + // Dynamic razoring margin based on depth + inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); } + + // Futility lookup tables (initialized at startup) and their access functions + Value FutilityMargins[16][64]; // [depth][moveNumber] + int FutilityMoveCounts[2][32]; // [improving][depth] + + inline Value futility_margin(Depth d, int mn) { + + return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)] + : 2 * VALUE_INFINITE; + } + + // Reduction lookup tables (initialized at startup) and their access function + int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] + + template inline Depth reduction(bool i, Depth d, int mn) { + + return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; + } + + size_t PVSize, PVIdx; + TimeManager TimeMgr; + int BestMoveChanges; + Value DrawValue[COLOR_NB]; + HistoryStats History; + GainsStats Gains; + CountermovesStats Countermoves; + + 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); + + void id_loop(Position& pos); + Value value_to_tt(Value v, int ply); + Value value_from_tt(Value v, int ply); + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); + bool allows(const Position& pos, Move first, Move second); + bool refutes(const Position& pos, Move first, Move second); + string uci_pv(const Position& pos, int depth, Value alpha, Value beta); + + struct Skill { + Skill(int l) : level(l), best(MOVE_NONE) {} + ~Skill() { + if (enabled()) // Swap best PV line with the sub-optimal one + std::swap(RootMoves[0], *std::find(RootMoves.begin(), + RootMoves.end(), best ? best : pick_move())); + } + + bool enabled() const { return level < 20; } + bool time_to_pick(int depth) const { return depth == 1 + level; } + Move pick_move(); + + int level; + Move best; + }; + +} // namespace + + +/// Search::init() is called during startup to initialize various lookup tables + +void Search::init() { + + int d; // depth (ONE_PLY == 2) + int hd; // half depth (ONE_PLY == 1) + int mc; // moveCount + + // Init reductions array + for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++) + { + double pvRed = log(double(hd)) * log(double(mc)) / 3.0; + double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; + Reductions[1][1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0); + Reductions[0][1][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0); + + Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc]; + Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc]; + + if (Reductions[0][0][hd][mc] > 2 * ONE_PLY) + Reductions[0][0][hd][mc] += ONE_PLY; + } + + // Init futility margins array + for (d = 1; d < 16; d++) for (mc = 0; mc < 64; mc++) + FutilityMargins[d][mc] = Value(112 * int(log(double(d * d) / 2) / log(2.0) + 1.001) - 8 * mc + 45); + + // Init futility move count array + for (d = 0; d < 32; d++) + { + FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4; + FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8)); + } +} + + +/// 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 returned. + +static size_t perft(Position& pos, Depth depth) { + + StateInfo st; + size_t cnt = 0; + CheckInfo ci(pos); + const bool leaf = depth == 2 * ONE_PLY; + + for (MoveList it(pos); *it; ++it) + { + pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci)); + cnt += leaf ? MoveList(pos).size() : ::perft(pos, depth - ONE_PLY); + pos.undo_move(*it); + } + return cnt; +} + +size_t Search::perft(Position& pos, Depth depth) { + return depth > ONE_PLY ? ::perft(pos, depth) : MoveList(pos).size(); +} + +/// Search::think() is the external interface to Stockfish's search, and is +/// called by the main thread when the program receives the UCI 'go' command. It +/// searches from RootPos and at the end prints the "bestmove" to output. + +void Search::think() { + + static PolyglotBook book; // Defined static to initialize the PRNG only once + + RootColor = RootPos.side_to_move(); + TimeMgr.init(Limits, RootPos.game_ply(), RootColor); + + if (RootMoves.empty()) + { + RootMoves.push_back(MOVE_NONE); + sync_cout << "info depth 0 score " + << score_to_uci(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) + << sync_endl; + + goto finalize; + } + + if (Options["OwnBook"] && !Limits.infinite && !Limits.mate) + { + Move bookMove = book.probe(RootPos, Options["Book File"], Options["Best Book Move"]); + + if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) + { + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove)); + goto finalize; + } + } + + if (Options["Contempt Factor"] && !Options["UCI_AnalyseMode"]) + { + int cf = Options["Contempt Factor"] * PawnValueMg / 100; // From centipawns + cf = cf * Material::game_phase(RootPos) / PHASE_MIDGAME; // Scale down with phase + DrawValue[ RootColor] = VALUE_DRAW - Value(cf); + DrawValue[~RootColor] = VALUE_DRAW + Value(cf); + } + else + DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW; + + if (Options["Write Search Log"]) + { + Log log(Options["Search Log Filename"]); + log << "\nSearching: " << RootPos.fen() + << "\ninfinite: " << Limits.infinite + << " ponder: " << Limits.ponder + << " time: " << Limits.time[RootColor] + << " increment: " << Limits.inc[RootColor] + << " moves to go: " << Limits.movestogo + << std::endl; + } + + // Reset the threads, still sleeping: will be wake up at split time + for (size_t i = 0; i < Threads.size(); i++) + Threads[i]->maxPly = 0; + + Threads.sleepWhileIdle = Options["Idle Threads Sleep"]; + + // Set best timer interval to avoid lagging under time pressure. Timer is + // used to check for remaining available thinking time. + Threads.timer->msec = + Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : + Limits.nodes ? 2 * TimerResolution + : 100; + + Threads.timer->notify_one(); // Wake up the recurring timer + + id_loop(RootPos); // Let's start searching ! + + Threads.timer->msec = 0; // Stop the timer + Threads.sleepWhileIdle = true; // Send idle threads to sleep + + if (Options["Write Search Log"]) + { + Time::point elapsed = Time::now() - SearchTime + 1; + + Log log(Options["Search Log Filename"]); + log << "Nodes: " << RootPos.nodes_searched() + << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed + << "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]); + + StateInfo st; + RootPos.do_move(RootMoves[0].pv[0], st); + log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl; + RootPos.undo_move(RootMoves[0].pv[0]); + } + +finalize: + + // When search is stopped this info is not printed + sync_cout << "info nodes " << RootPos.nodes_searched() + << " time " << Time::now() - SearchTime + 1 << sync_endl; + + // When we reach max depth we arrive here even without Signals.stop is raised, + // but if we are pondering or in infinite search, according to UCI protocol, + // we shouldn't print the best move before the GUI sends a "stop" or "ponderhit" + // command. We simply wait here until GUI sends one of those commands (that + // raise Signals.stop). + if (!Signals.stop && (Limits.ponder || Limits.infinite)) + { + Signals.stopOnPonderhit = true; + RootPos.this_thread()->wait_for(Signals.stop); + } + + // Best move could be MOVE_NONE when searching on a stalemate position + sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], RootPos.is_chess960()) + << " ponder " << move_to_uci(RootMoves[0].pv[1], RootPos.is_chess960()) + << sync_endl; +} + + +namespace { + + // id_loop() is the main iterative deepening loop. It calls search() repeatedly + // with increasing depth until the allocated thinking time has been consumed, + // user stops the search, or the maximum search depth is reached. + + void id_loop(Position& pos) { + + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + int depth, prevBestMoveChanges; + Value bestValue, alpha, beta, delta; + + std::memset(ss-2, 0, 5 * sizeof(Stack)); + (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains + + depth = BestMoveChanges = 0; + bestValue = delta = alpha = -VALUE_INFINITE; + beta = VALUE_INFINITE; + + TT.new_search(); + History.clear(); + Gains.clear(); + Countermoves.clear(); + + PVSize = Options["MultiPV"]; + Skill skill(Options["Skill Level"]); + + // Do we have to play with skill handicap? In this case enable MultiPV search + // that we will use behind the scenes to retrieve a set of possible moves. + if (skill.enabled() && PVSize < 4) + PVSize = 4; + + PVSize = std::min(PVSize, RootMoves.size()); + + // Iterative deepening loop until requested to stop or target depth reached + while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) + { + // Save last iteration's scores before first PV line is searched and all + // the move scores but the (new) PV are set to -VALUE_INFINITE. + for (size_t i = 0; i < RootMoves.size(); i++) + RootMoves[i].prevScore = RootMoves[i].score; + + prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1 + BestMoveChanges = 0; + + // MultiPV loop. We perform a full root search for each PV line + for (PVIdx = 0; PVIdx < PVSize; PVIdx++) + { + // Reset aspiration window starting size + if (depth >= 5) + { + delta = Value(16); + alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE); + beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE); + } + + // Start with a small aspiration window and, in case of fail high/low, + // research with bigger window until not failing high/low anymore. + while (true) + { + bestValue = search(pos, ss, alpha, beta, depth * ONE_PLY, false); + + // Bring to front the best move. 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 but 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()); + + // Write PV back to transposition table in case the relevant + // entries have been overwritten during the search. + for (size_t i = 0; i <= PVIdx; i++) + RootMoves[i].insert_pv_in_tt(pos); + + // If search has been stopped return immediately. Sorting and + // writing PV back to TT is safe becuase RootMoves is still + // valid, although refers to previous iteration. + if (Signals.stop) + return; + + // When failing high/low give some update (without cluttering + // the UI) before to research. + if ( (bestValue <= alpha || bestValue >= beta) + && Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + + // In case of failing low/high increase aspiration window and + // research, otherwise exit the loop. + if (bestValue <= alpha) + { + alpha = std::max(bestValue - delta, -VALUE_INFINITE); + + Signals.failedLowAtRoot = true; + Signals.stopOnPonderhit = false; + } + else if (bestValue >= beta) + beta = std::min(bestValue + delta, VALUE_INFINITE); + + else + break; + + delta += delta / 2; + + 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 (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + } + + // Do we need to pick now the sub-optimal best move ? + if (skill.enabled() && skill.time_to_pick(depth)) + skill.pick_move(); + + if (Options["Write Search Log"]) + { + RootMove& rm = RootMoves[0]; + if (skill.best != MOVE_NONE) + rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best); + + Log log(Options["Search Log Filename"]); + log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0]) + << std::endl; + } + + // Do we have 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() && !Signals.stopOnPonderhit) + { + bool stop = false; // Local variable, not the volatile Signals.stop + + // Take in account some extra time if the best move has changed + if (depth > 4 && depth < 50 && PVSize == 1) + TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges); + + // Stop search if most of available time is already consumed. We + // probably don't have enough time to search the first move at the + // next iteration anyway. + if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100) + stop = true; + + // Stop search early if one move seems to be much better than others + if ( depth >= 12 + && !stop + && PVSize == 1 + && bestValue > VALUE_MATED_IN_MAX_PLY + && ( RootMoves.size() == 1 + || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) + { + Value rBeta = bestValue - 2 * PawnValueMg; + ss->excludedMove = RootMoves[0].pv[0]; + ss->skipNullMove = true; + Value v = search(pos, ss, rBeta - 1, rBeta, (depth - 3) * ONE_PLY, true); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (v < rBeta) + stop = true; + } + + if (stop) + { + // If we are allowed to ponder do not stop the search now but + // keep pondering until GUI sends "ponderhit" or "stop". + if (Limits.ponder) + Signals.stopOnPonderhit = true; + else + Signals.stop = true; + } + } + } + } + + + // search<>() is the main search function for both PV and non-PV nodes and for + // normal and SplitPoint nodes. When called just after a split point the search + // is simpler because we have already probed the hash table, done a null move + // search, and searched the first move before splitting, we don't have to repeat + // all this work again. We also don't need to store anything to the hash table + // here: This is taken care of after we return from the split point. + + template + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + + const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot); + const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot); + const bool RootNode = (NT == Root || NT == SplitPointRoot); + + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert(PvNode || (alpha == beta - 1)); + assert(depth > DEPTH_ZERO); + + Move quietsSearched[64]; + StateInfo st; + const TTEntry *tte; + SplitPoint* splitPoint; + Key posKey; + Move ttMove, move, excludedMove, bestMove, threatMove; + Depth ext, newDepth; + Value bestValue, value, ttValue; + Value eval, nullValue, futilityValue; + bool inCheck, givesCheck, pvMove, singularExtensionNode, improving; + bool captureOrPromotion, dangerous, doFullDepthSearch; + int moveCount, quietCount; + + // Step 1. Initialize node + Thread* thisThread = pos.this_thread(); + inCheck = pos.checkers(); + + if (SpNode) + { + splitPoint = ss->splitPoint; + bestMove = splitPoint->bestMove; + threatMove = splitPoint->threatMove; + bestValue = splitPoint->bestValue; + tte = NULL; + ttMove = excludedMove = MOVE_NONE; + ttValue = VALUE_NONE; + + assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); + + goto moves_loop; + } + + moveCount = quietCount = 0; + bestValue = -VALUE_INFINITE; + ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + ss->ply = (ss-1)->ply + 1; + ss->futilityMoveCount = 0; + (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + + // 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 || pos.is_draw() || ss->ply > MAX_PLY) + return 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 + // further, we will never beat 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; + } + + // 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 = excludedMove ? pos.exclusion_key() : pos.key(); + tte = TT.probe(posKey); + ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + + // At PV nodes we check for exact scores, while at non-PV nodes we check for + // a fail high/low. Biggest advantage at probing at PV nodes is to have a + // smooth experience in analysis mode. We don't probe at Root nodes otherwise + // we should also update RootMoveList to avoid bogus output. + if ( !RootNode + && tte + && tte->depth() >= depth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) + { + TT.refresh(tte); + ss->currentMove = ttMove; // Can be MOVE_NONE + + if ( ttValue >= beta + && ttMove + && !pos.is_capture_or_promotion(ttMove) + && ttMove != ss->killers[0]) + { + ss->killers[1] = ss->killers[0]; + ss->killers[0] = ttMove; + } + return ttValue; + } + + // Step 5. Evaluate the position statically and update parent's gain statistics + if (inCheck) + { + ss->staticEval = ss->evalMargin = eval = VALUE_NONE; + goto moves_loop; + } + + else if (tte) + { + // Never assume anything on values stored in TT + if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + eval = ss->staticEval = evaluate(pos, ss->evalMargin); + + // Can ttValue be used as a better position evaluation? + if (ttValue != VALUE_NONE) + if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval) + || ((tte->bound() & BOUND_UPPER) && ttValue < eval)) + eval = ttValue; + } + else + { + eval = ss->staticEval = evaluate(pos, ss->evalMargin); + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + ss->staticEval, ss->evalMargin); + } + + // Update gain for the parent non-capture move given the static position + // evaluation before and after the move. + if ( !pos.captured_piece_type() + && ss->staticEval != VALUE_NONE + && (ss-1)->staticEval != VALUE_NONE + && (move = (ss-1)->currentMove) != MOVE_NULL + && type_of(move) == NORMAL) + { + Square to = to_sq(move); + Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); + } + + // Step 6. Razoring (skipped when in check) + if ( !PvNode + && depth < 4 * ONE_PLY + && eval + razor_margin(depth) < beta + && ttMove == MOVE_NONE + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && !pos.pawn_on_7th(pos.side_to_move())) + { + Value rbeta = beta - razor_margin(depth); + Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); + if (v < rbeta) + // Logically we should return (v + razor_margin(depth)), but + // surprisingly this did slightly weaker in tests. + return v; + } + + // Step 7. Static null move pruning (skipped when in check) + // We're betting that the opponent doesn't have a move that will reduce + // the score by more than futility_margin(depth) if we do a null move. + if ( !PvNode + && !ss->skipNullMove + && depth < 4 * ONE_PLY + && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && abs(eval) < VALUE_KNOWN_WIN + && pos.non_pawn_material(pos.side_to_move())) + return eval - futility_margin(depth, (ss-1)->futilityMoveCount); + + // Step 8. Null move search with verification search (is omitted in PV nodes) + if ( !PvNode + && !ss->skipNullMove + && depth > ONE_PLY + && eval >= beta + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && pos.non_pawn_material(pos.side_to_move())) + { + ss->currentMove = MOVE_NULL; + + // Null move dynamic reduction based on depth + Depth R = 3 * ONE_PLY + depth / 4; + + // Null move dynamic reduction based on value + if (eval - PawnValueMg > beta) + R += ONE_PLY; + + pos.do_null_move(st); + (ss+1)->skipNullMove = true; + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -alpha, depth-R, !cutNode); + (ss+1)->skipNullMove = 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) + return nullValue; + + // Do verification search at high depths + ss->skipNullMove = true; + Value v = search(pos, ss, alpha, beta, depth-R, false); + ss->skipNullMove = false; + + if (v >= beta) + return nullValue; + } + else + { + // The null move failed low, which means that we may be faced with + // some kind of threat. If the previous move was reduced, check if + // the move that refuted the null move was somehow connected to the + // move which was reduced. If a connection is found, return a fail + // low score (which will cause the reduced move to fail high in the + // parent node, which will trigger a re-search with full depth). + threatMove = (ss+1)->currentMove; + + if ( depth < 5 * ONE_PLY + && (ss-1)->reduction + && threatMove != MOVE_NONE + && allows(pos, (ss-1)->currentMove, threatMove)) + return alpha; + } + } + + // Step 9. ProbCut (skipped when in check) + // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) + // and a reduced search returns a value much above beta, we can (almost) safely + // prune the previous move. + if ( !PvNode + && depth >= 5 * ONE_PLY + && !ss->skipNullMove + && abs(beta) < VALUE_MATE_IN_MAX_PLY) + { + Value rbeta = beta + 200; + Depth rdepth = depth - ONE_PLY - 3 * ONE_PLY; + + assert(rdepth >= ONE_PLY); + assert((ss-1)->currentMove != MOVE_NONE); + assert((ss-1)->currentMove != MOVE_NULL); + + MovePicker mp(pos, ttMove, History, pos.captured_piece_type()); + CheckInfo ci(pos); + + while ((move = mp.next_move()) != MOVE_NONE) + if (pos.pl_move_is_legal(move, ci.pinned)) + { + ss->currentMove = move; + pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); + 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 >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) + && ttMove == MOVE_NONE + && (PvNode || ss->staticEval + Value(256) >= beta)) + { + Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); + + ss->skipNullMove = true; + search(pos, ss, alpha, beta, d, true); + ss->skipNullMove = false; + + tte = TT.probe(posKey); + ttMove = tte ? tte->move() : MOVE_NONE; + } + +moves_loop: // When in check and at SpNode search starts from here + + Square prevMoveSq = to_sq((ss-1)->currentMove); + Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first, + Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second }; + + MovePicker mp(pos, ttMove, depth, History, countermoves, ss); + CheckInfo ci(pos); + value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc + improving = ss->staticEval >= (ss-2)->staticEval + || ss->staticEval == VALUE_NONE + ||(ss-2)->staticEval == VALUE_NONE; + + singularExtensionNode = !RootNode + && !SpNode + && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) + && ttMove != MOVE_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(RootMoves.begin() + PVIdx, RootMoves.end(), move)) + continue; + + if (SpNode) + { + // Shared counter cannot be decremented later if move turns out to be illegal + if (!pos.pl_move_is_legal(move, ci.pinned)) + continue; + + moveCount = ++splitPoint->moveCount; + splitPoint->mutex.unlock(); + } + else + moveCount++; + + if (RootNode) + { + Signals.firstRootMove = (moveCount == 1); + + if (thisThread == Threads.main() && Time::now() - SearchTime > 3000) + sync_cout << "info depth " << depth / ONE_PLY + << " currmove " << move_to_uci(move, pos.is_chess960()) + << " currmovenumber " << moveCount + PVIdx << sync_endl; + } + + ext = DEPTH_ZERO; + captureOrPromotion = pos.is_capture_or_promotion(move); + givesCheck = pos.move_gives_check(move, ci); + dangerous = givesCheck + || pos.is_passed_pawn_push(move) + || type_of(move) == CASTLE; + + // Step 12. Extend checks and, in PV nodes, also dangerous moves + if (PvNode && dangerous) + ext = ONE_PLY; + + else if (givesCheck && pos.see_sign(move) >= 0) + ext = ONE_PLY / 2; + + // 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, if result is lower than ttValue minus + // a margin then we extend ttMove. + if ( singularExtensionNode + && move == ttMove + && !ext + && pos.pl_move_is_legal(move, ci.pinned) + && abs(ttValue) < VALUE_KNOWN_WIN) + { + assert(ttValue != VALUE_NONE); + + Value rBeta = ttValue - int(depth); + ss->excludedMove = move; + ss->skipNullMove = true; + value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (value < rBeta) + ext = ONE_PLY; + } + + // Update current move (this must be done after singular extension search) + newDepth = depth - ONE_PLY + ext; + + // Step 13. Futility pruning (is omitted in PV nodes) + if ( !PvNode + && !captureOrPromotion + && !inCheck + && !dangerous + /* && move != ttMove Already implicit in the next condition */ + && bestValue > VALUE_MATED_IN_MAX_PLY) + { + // Move count based pruning + if ( depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[improving][depth] + && (!threatMove || !refutes(pos, move, threatMove))) + { + if (SpNode) + splitPoint->mutex.lock(); + + continue; + } + + // Value based pruning + // We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth, + // but fixing this made program slightly weaker. + Depth predictedDepth = newDepth - reduction(improving, depth, moveCount); + futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) + + Gains[pos.piece_moved(move)][to_sq(move)]; + + if (futilityValue < beta) + { + bestValue = std::max(bestValue, futilityValue); + + if (SpNode) + { + splitPoint->mutex.lock(); + if (bestValue > splitPoint->bestValue) + splitPoint->bestValue = bestValue; + } + continue; + } + + // Prune moves with negative SEE at low depths + if ( predictedDepth < 4 * ONE_PLY + && pos.see_sign(move) < 0) + { + if (SpNode) + splitPoint->mutex.lock(); + + continue; + } + + // We have not pruned the move that will be searched, but remember how + // far in the move list we are to be more aggressive in the child node. + ss->futilityMoveCount = moveCount; + } + else + ss->futilityMoveCount = 0; + + // Check for legality only before to do the move + if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned)) + { + moveCount--; + continue; + } + + pvMove = PvNode && moveCount == 1; + ss->currentMove = move; + if (!SpNode && !captureOrPromotion && quietCount < 64) + quietsSearched[quietCount++] = move; + + // Step 14. Make the move + pos.do_move(move, st, ci, givesCheck); + + // Step 15. Reduced depth search (LMR). If the move fails high will be + // re-searched at full depth. + if ( depth > 3 * ONE_PLY + && !pvMove + && !captureOrPromotion + && !dangerous + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1]) + { + ss->reduction = reduction(improving, depth, moveCount); + + if (!PvNode && cutNode) + ss->reduction += ONE_PLY; + + if (move == countermoves[0] || move == countermoves[1]) + ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY); + + Depth d = std::max(newDepth - ss->reduction, ONE_PLY); + if (SpNode) + alpha = splitPoint->alpha; + + value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + + doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); + ss->reduction = DEPTH_ZERO; + } + else + doFullDepthSearch = !pvMove; + + // Step 16. Full depth search, when LMR is skipped or fails high + if (doFullDepthSearch) + { + if (SpNode) + alpha = splitPoint->alpha; + + 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); + } + + // Only for PV nodes 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 to fail low with value <= alpha and to try another move. + if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta)))) + 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 new best move + if (SpNode) + { + splitPoint->mutex.lock(); + bestValue = splitPoint->bestValue; + alpha = splitPoint->alpha; + } + + // Finished searching the move. If Signals.stop is true, the search + // was aborted because the user interrupted the search or because we + // ran out of time. In this case, the return value of the search cannot + // be trusted, and we don't update the best move and/or PV. + if (Signals.stop || thisThread->cutoff_occurred()) + return value; // To avoid returning VALUE_INFINITE + + if (RootNode) + { + RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); + + // PV move or new best move ? + if (pvMove || value > alpha) + { + rm.score = value; + rm.extract_pv_from_tt(pos); + + // 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 (!pvMove) + BestMoveChanges++; + } + else + // All other moves but the PV are set to the lowest value, this + // is not a problem when sorting becuase sort is stable and move + // position in the list is preserved, just the PV is pushed up. + rm.score = -VALUE_INFINITE; + } + + if (value > bestValue) + { + bestValue = SpNode ? splitPoint->bestValue = value : value; + + if (value > alpha) + { + bestMove = SpNode ? splitPoint->bestMove = move : move; + + if (PvNode && value < beta) // Update alpha! Always alpha < beta + alpha = SpNode ? splitPoint->alpha = value : value; + else + { + assert(value >= beta); // Fail high + + if (SpNode) + splitPoint->cutoff = true; + + break; + } + } + } + + // Step 19. Check for splitting the search + if ( !SpNode + && depth >= Threads.minimumSplitDepth + && Threads.available_slave(thisThread) + && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) + { + assert(bestValue < beta); + + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT, cutNode); + if (bestValue >= beta) + break; + } + } + + if (SpNode) + return bestValue; + + // Step 20. Check for mate and stalemate + // All legal moves have been searched and if there are no legal moves, it + // must be mate or stalemate. Note that we can have a false positive in + // case of Signals.stop or thread.cutoff_occurred() are set, but this is + // harmless because return value is discarded anyhow in the parent nodes. + // If we are in a singular extension search then return a fail low score. + // A split node has at least one move, the one tried before to be splitted. + if (!moveCount) + return excludedMove ? alpha + : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; + + // If we have pruned all the moves without searching return a fail-low score + if (bestValue == -VALUE_INFINITE) + bestValue = alpha; + + TT.store(posKey, value_to_tt(bestValue, ss->ply), + bestValue >= beta ? BOUND_LOWER : + PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, + depth, bestMove, ss->staticEval, ss->evalMargin); + + // Quiet best move: update killers, history and countermoves + if ( bestValue >= beta + && !pos.is_capture_or_promotion(bestMove) + && !inCheck) + { + if (ss->killers[0] != bestMove) + { + ss->killers[1] = ss->killers[0]; + ss->killers[0] = bestMove; + } + + // Increase history value of the cut-off move and decrease all the other + // played non-capture moves. + Value bonus = Value(int(depth) * int(depth)); + History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); + for (int i = 0; i < quietCount - 1; i++) + { + Move m = quietsSearched[i]; + History.update(pos.piece_moved(m), to_sq(m), -bonus); + } + + if (is_ok((ss-1)->currentMove)) + Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove); + } + + 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(NT == PV || NT == NonPV); + assert(InCheck == !!pos.checkers()); + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert(PvNode || (alpha == beta - 1)); + assert(depth <= DEPTH_ZERO); + + StateInfo st; + const TTEntry* tte; + Key posKey; + Move ttMove, move, bestMove; + Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + bool givesCheck, evasionPrunable; + Depth ttDepth; + + // To flag BOUND_EXACT a node with eval above alpha and no available moves + if (PvNode) + oldAlpha = alpha; + + ss->currentMove = bestMove = MOVE_NONE; + ss->ply = (ss-1)->ply + 1; + + // Check for an instant draw or maximum ply reached + if (pos.is_draw() || ss->ply > MAX_PLY) + return DrawValue[pos.side_to_move()]; + + // 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); + ttMove = tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_NONE; + + if ( tte + && tte->depth() >= ttDepth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) + { + ss->currentMove = ttMove; // Can be MOVE_NONE + return ttValue; + } + + // Evaluate the position statically + if (InCheck) + { + ss->staticEval = ss->evalMargin = VALUE_NONE; + bestValue = futilityBase = -VALUE_INFINITE; + } + else + { + if (tte) + { + // Never assume anything on values stored in TT + if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); + } + else + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); + + // Stand pat. Return immediately if static value is at least beta + if (bestValue >= beta) + { + if (!tte) + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, + DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin); + + return bestValue; + } + + if (PvNode && bestValue > alpha) + alpha = bestValue; + + futilityBase = ss->staticEval + ss->evalMargin + Value(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, History, to_sq((ss-1)->currentMove)); + CheckInfo ci(pos); + + // 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 = pos.move_gives_check(move, ci); + + // Futility pruning + if ( !PvNode + && !InCheck + && !givesCheck + && move != ttMove + && type_of(move) != PROMOTION + && !pos.is_passed_pawn_push(move)) + { + futilityValue = futilityBase + + PieceValue[EG][pos.piece_on(to_sq(move))] + + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO); + + if (futilityValue < beta) + { + bestValue = std::max(bestValue, futilityValue); + continue; + } + + // Prune moves with negative or equal SEE and also moves with positive + // SEE where capturing piece loses a tempo and SEE < beta - futilityBase. + if ( futilityBase < beta + && pos.see(move, beta - futilityBase) <= 0) + { + bestValue = std::max(bestValue, futilityBase); + continue; + } + } + + // Detect non-capture evasions that are candidate to be pruned + evasionPrunable = InCheck + && bestValue > VALUE_MATED_IN_MAX_PLY + && !pos.is_capture(move) + && !pos.can_castle(pos.side_to_move()); + + // Don't search moves with negative SEE values + if ( !PvNode + && (!InCheck || evasionPrunable) + && move != ttMove + && type_of(move) != PROMOTION + && pos.see_sign(move) < 0) + continue; + + // Don't search useless checks + if ( !PvNode + && !InCheck + && givesCheck + && move != ttMove + && !pos.is_capture_or_promotion(move) + && ss->staticEval + PawnValueMg / 4 < beta + && !check_is_dangerous(pos, move, futilityBase, beta)) + continue; + + // Check for legality only before to do the move + if (!pos.pl_move_is_legal(move, ci.pinned)) + continue; + + ss->currentMove = move; + + // Make and search the move + pos.do_move(move, st, ci, 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 new best move + if (value > bestValue) + { + bestValue = value; + + if (value > alpha) + { + if (PvNode && value < beta) // Update alpha here! Always alpha < beta + { + alpha = value; + bestMove = move; + } + else // Fail high + { + TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, + ttDepth, move, ss->staticEval, ss->evalMargin); + + 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 + + TT.store(posKey, value_to_tt(bestValue, ss->ply), + PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + ttDepth, bestMove, ss->staticEval, ss->evalMargin); + + 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 to 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 (where 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; + } + + + // check_is_dangerous() tests if a checking move can be pruned in qsearch() + + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) + { + Piece pc = pos.piece_moved(move); + Square from = from_sq(move); + Square to = to_sq(move); + Color them = ~pos.side_to_move(); + Square ksq = pos.king_square(them); + Bitboard enemies = pos.pieces(them); + Bitboard kingAtt = pos.attacks_from(ksq); + Bitboard occ = pos.pieces() ^ from ^ ksq; + Bitboard oldAtt = pos.attacks_from(pc, from, occ); + Bitboard newAtt = pos.attacks_from(pc, to, occ); + + // Checks which give opponent's king at most one escape square are dangerous + if (!more_than_one(kingAtt & ~(enemies | newAtt | to))) + return true; + + // Queen contact check is very dangerous + if (type_of(pc) == QUEEN && (kingAtt & to)) + return true; + + // Creating new double threats with checks is dangerous + Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt; + while (b) + { + // Note that here we generate illegal "double move"! + if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta) + return true; + } + + return false; + } + + + // allows() tests whether the 'first' move at previous ply somehow makes the + // 'second' move possible, for instance if the moving piece is the same in + // both moves. Normally the second move is the threat (the best move returned + // from a null search that fails low). + + bool allows(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); + assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // The piece is the same or second's destination was vacated by the first move + if (m1to == m2from || m2to == m1from) + return true; + + // Second one moves through the square vacated by first one + if (between_bb(m2from, m2to) & m1from) + return true; + + // Second's destination is defended by the first move's piece + Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from); + if (m1att & m2to) + return true; + + // Second move gives a discovered check through the first's checking piece + if (m1att & pos.king_square(pos.side_to_move())) + { + assert(between_bb(m1to, pos.king_square(pos.side_to_move())) & m2from); + return true; + } + + return false; + } + + + // refutes() tests whether a 'first' move is able to defend against a 'second' + // opponent's move. In this case will not be pruned. Normally the second move + // is the threat (the best move returned from a null search that fails low). + + bool refutes(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // Don't prune moves of the threatened piece + if (m1from == m2to) + return true; + + // If the threatened piece has value less than or equal to the value of the + // threat piece, don't prune moves which defend it. + if ( pos.is_capture(second) + && ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)] + || type_of(pos.piece_on(m2from)) == KING)) + { + // Update occupancy as if the piece and the threat are moving + Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from; + Piece pc = pos.piece_on(m1from); + + // The moved piece attacks the square 'tto' ? + if (pos.attacks_from(pc, m1to, occ) & m2to) + return true; + + // Scan for possible X-ray attackers behind the moved piece + Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK)) + | (attacks_bb(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP)); + + // Verify attackers are triggered by our move and not already existing + if (unlikely(xray) && (xray & ~pos.attacks_from(m2to))) + return true; + } + + // Don't prune safe moves which block the threat path + if ((between_bb(m2from, m2to) & m1to) && pos.see_sign(first) >= 0) + return true; + + return false; + } + + + // When playing with strength handicap choose best move among the MultiPV set + // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. + + Move Skill::pick_move() { + + static RKISS rk; + + // PRNG sequence should be not deterministic + for (int i = Time::now() % 50; i > 0; i--) + rk.rand(); + + // RootMoves are already sorted by score in descending order + int variance = std::min(RootMoves[0].score - RootMoves[PVSize - 1].score, PawnValueMg); + int weakness = 120 - 2 * level; + int max_s = -VALUE_INFINITE; + best = MOVE_NONE; + + // Choose best move. For each move score we add two terms both dependent on + // weakness, one deterministic and bigger for weaker moves, and one random, + // then we choose the move with the resulting highest score. + for (size_t i = 0; i < PVSize; i++) + { + int s = RootMoves[i].score; + + // Don't allow crazy blunders even at very low skills + if (i > 0 && RootMoves[i-1].score > s + 2 * PawnValueMg) + break; + + // This is our magic formula + s += ( weakness * int(RootMoves[0].score - s) + + variance * (rk.rand() % weakness)) / 128; + + if (s > max_s) + { + max_s = s; + best = RootMoves[i].pv[0]; + } + } + return best; + } + + + // uci_pv() formats PV information according to UCI protocol. UCI requires + // to send all the PV lines also if are still to be searched and so refer to + // the previous search score. + + string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { + + std::stringstream s; + Time::point elapsed = Time::now() - SearchTime + 1; + size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); + int selDepth = 0; + + for (size_t i = 0; i < Threads.size(); i++) + if (Threads[i]->maxPly > selDepth) + selDepth = Threads[i]->maxPly; + + for (size_t i = 0; i < uciPVSize; i++) + { + bool updated = (i <= PVIdx); + + if (depth == 1 && !updated) + continue; + + int d = updated ? depth : depth - 1; + Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; + + if (s.rdbuf()->in_avail()) // Not at first line + s << "\n"; + + s << "info depth " << d + << " seldepth " << selDepth + << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << pos.nodes_searched() * 1000 / elapsed + << " time " << elapsed + << " multipv " << i + 1 + << " pv"; + + for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) + s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960()); + } + + return s.str(); + } + +} // namespace + + +/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. +/// We consider also failing high nodes and not only BOUND_EXACT nodes so to +/// allow to always have a ponder move even when we fail high at root, and a +/// long PV to print that is important for position analysis. + +void RootMove::extract_pv_from_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_6], *st = state; + const TTEntry* tte; + int ply = 0; + Move m = pv[0]; + + pv.clear(); + + do { + pv.push_back(m); + + assert(MoveList(pos).contains(pv[ply])); + + pos.do_move(pv[ply++], *st++); + tte = TT.probe(pos.key()); + + } while ( tte + && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change + && pos.pl_move_is_legal(m, pos.pinned_pieces()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)); + + pv.push_back(MOVE_NONE); // Must be zero-terminating + + while (ply) pos.undo_move(pv[--ply]); +} + + +/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and +/// inserts the PV back into the TT. This makes sure the old PV moves are searched +/// first, even if the old TT entries have been overwritten. + +void RootMove::insert_pv_in_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_6], *st = state; + const TTEntry* tte; + int ply = 0; + + do { + tte = TT.probe(pos.key()); + + if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries + TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE); + + assert(MoveList(pos).contains(pv[ply])); + + pos.do_move(pv[ply++], *st++); + + } while (pv[ply] != MOVE_NONE); + + while (ply) pos.undo_move(pv[--ply]); +} + + +/// Thread::idle_loop() is where the thread is parked when it has no work to do + +void Thread::idle_loop() { + + // Pointer 'this_sp' is not null only if we are called from split(), and not + // at the thread creation. So it means we are the split point's master. + SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; + + assert(!this_sp || (this_sp->masterThread == this && searching)); + + while (true) + { + // If we are not searching, wait for a condition to be signaled instead of + // wasting CPU time polling for work. + while ((!searching && Threads.sleepWhileIdle) || exit) + { + if (exit) + { + assert(!this_sp); + return; + } + + // Grab the lock to avoid races with Thread::notify_one() + mutex.lock(); + + // If we are master and all slaves have finished then exit idle_loop + if (this_sp && !this_sp->slavesMask) + { + mutex.unlock(); + break; + } + + // Do sleep after retesting sleep conditions under lock protection, in + // particular we need to avoid a deadlock in case a master thread has, + // in the meanwhile, allocated us and sent the notify_one() call before + // we had the chance to grab the lock. + if (!searching && !exit) + sleepCondition.wait(mutex); + + mutex.unlock(); + } + + // If this thread has been assigned work, launch a search + if (searching) + { + assert(!exit); + + Threads.mutex.lock(); + + assert(searching); + assert(activeSplitPoint); + SplitPoint* sp = activeSplitPoint; + + Threads.mutex.unlock(); + + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + Position pos(*sp->pos, this); + + std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); + ss->splitPoint = sp; + + sp->mutex.lock(); + + assert(activePosition == NULL); + + activePosition = &pos; + + switch (sp->nodeType) { + case Root: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + case PV: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + case NonPV: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + default: + assert(false); + } + + assert(searching); + + searching = false; + activePosition = NULL; + sp->slavesMask &= ~(1ULL << idx); + sp->nodes += pos.nodes_searched(); + + // Wake up master thread so to allow it to return from the idle loop + // in case we are the last slave of the split point. + if ( Threads.sleepWhileIdle + && this != sp->masterThread + && !sp->slavesMask) + { + assert(!sp->masterThread->searching); + sp->masterThread->notify_one(); + } + + // After releasing the lock we cannot access anymore any SplitPoint + // related data in a safe way becuase it could have been released under + // our feet by the sp master. Also accessing other Thread objects is + // unsafe because if we are exiting there is a chance are already freed. + sp->mutex.unlock(); + } + + // If this thread is the master of a split point and all slaves have finished + // their work at this split point, return from the idle loop. + if (this_sp && !this_sp->slavesMask) + { + this_sp->mutex.lock(); + bool finished = !this_sp->slavesMask; // Retest under lock protection + this_sp->mutex.unlock(); + if (finished) + return; + } + } +} + + +/// check_time() is called by the timer thread when the timer triggers. It is +/// used to print debug info and, more important, to detect when we are out of +/// available time and so stop the search. + +void check_time() { + + static Time::point lastInfoTime = Time::now(); + int64_t nodes = 0; // Workaround silly 'uninitialized' gcc warning + + if (Time::now() - lastInfoTime >= 1000) + { + lastInfoTime = Time::now(); + dbg_print(); + } + + if (Limits.ponder) + return; + + if (Limits.nodes) + { + Threads.mutex.lock(); + + nodes = RootPos.nodes_searched(); + + // Loop across all split points and sum accumulated SplitPoint nodes plus + // all the currently active positions nodes. + for (size_t i = 0; i < Threads.size(); i++) + for (int j = 0; j < Threads[i]->splitPointsSize; j++) + { + SplitPoint& sp = Threads[i]->splitPoints[j]; + + sp.mutex.lock(); + + nodes += sp.nodes; + Bitboard sm = sp.slavesMask; + while (sm) + { + Position* pos = Threads[pop_lsb(&sm)]->activePosition; + if (pos) + nodes += pos->nodes_searched(); + } + + sp.mutex.unlock(); + } + + Threads.mutex.unlock(); + } + + Time::point elapsed = Time::now() - SearchTime; + bool stillAtFirstMove = Signals.firstRootMove + && !Signals.failedLowAtRoot + && elapsed > TimeMgr.available_time(); + + bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution + || stillAtFirstMove; + + if ( (Limits.use_time_management() && noMoreTime) + || (Limits.movetime && elapsed >= Limits.movetime) + || (Limits.nodes && nodes >= Limits.nodes)) + Signals.stop = true; +} diff --git a/stockfish-4-win/src/search.h b/stockfish-4-win/src/search.h new file mode 100644 index 0000000000000000000000000000000000000000..e2a136062c689a79695b4c725719f7c9cb562bdc --- /dev/null +++ b/stockfish-4-win/src/search.h @@ -0,0 +1,112 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "misc.h" +#include "position.h" +#include "types.h" + +struct SplitPoint; + +namespace Search { + +/// The 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 { + SplitPoint* splitPoint; + int ply; + Move currentMove; + Move excludedMove; + Move killers[2]; + Depth reduction; + Value staticEval; + Value evalMargin; + int skipNullMove; + int futilityMoveCount; +}; + + +/// RootMove struct is used for moves at the root of the tree. For each root +/// move we store a score, a node count, and a PV (really a refutation in the +/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for +/// all non-pv moves. +struct RootMove { + + RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { + pv.push_back(m); pv.push_back(MOVE_NONE); + } + + bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort + bool operator==(const Move& m) const { return pv[0] == m; } + + void extract_pv_from_tt(Position& pos); + void insert_pv_in_tt(Position& pos); + + Value score; + Value prevScore; + std::vector pv; +}; + + +/// The 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 is our opponent's side to move. + +struct LimitsType { + + LimitsType() { std::memset(this, 0, sizeof(LimitsType)); } + bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } + + int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; +}; + + +/// The SignalsType struct stores volatile flags updated during the search +/// typically in an async fashion, for instance to stop the search by the GUI. + +struct SignalsType { + bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot; +}; + +typedef std::auto_ptr > StateStackPtr; + +extern volatile SignalsType Signals; +extern LimitsType Limits; +extern std::vector RootMoves; +extern Position RootPos; +extern Color RootColor; +extern Time::point SearchTime; +extern StateStackPtr SetupStates; + +extern void init(); +extern size_t perft(Position& pos, Depth depth); +extern void think(); + +} // namespace Search + +#endif // #ifndef SEARCH_H_INCLUDED diff --git a/stockfish-4-win/src/thread.cpp b/stockfish-4-win/src/thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d85db8618ca982059b96466d963e29d3b1b5ba7 --- /dev/null +++ b/stockfish-4-win/src/thread.cpp @@ -0,0 +1,394 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "ucioption.h" + +using namespace Search; + +ThreadPool Threads; // Global object + +namespace { + + // start_routine() is the C function which is called when a new thread + // is launched. It is a wrapper to the virtual function idle_loop(). + + extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } } + + + // Helpers to launch a thread after creation and joining before delete. Must be + // outside Thread c'tor and d'tor because object shall be fully initialized + // when start_routine (and hence virtual idle_loop) is called and when joining. + + template T* new_thread() { + T* th = new T(); + thread_create(th->handle, start_routine, th); // Will go to sleep + return th; + } + + void delete_thread(ThreadBase* th) { + th->exit = true; // Search must be already finished + th->notify_one(); + thread_join(th->handle); // Wait for thread termination + delete th; + } + +} + + +// ThreadBase::notify_one() wakes up the thread when there is some work to do + +void ThreadBase::notify_one() { + + mutex.lock(); + sleepCondition.notify_one(); + mutex.unlock(); +} + + +// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true + +void ThreadBase::wait_for(volatile const bool& b) { + + mutex.lock(); + while (!b) sleepCondition.wait(mutex); + mutex.unlock(); +} + + +// Thread c'tor just inits data but does not launch any thread of execution that +// instead will be started only upon c'tor returns. + +Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC + + searching = false; + maxPly = splitPointsSize = 0; + activeSplitPoint = NULL; + activePosition = NULL; + idx = Threads.size(); +} + + +// TimerThread::idle_loop() is where the timer thread waits msec milliseconds +// and then calls check_time(). If msec is 0 thread sleeps until is woken up. +extern void check_time(); + +void TimerThread::idle_loop() { + + while (!exit) + { + mutex.lock(); + + if (!exit) + sleepCondition.wait_for(mutex, msec ? msec : INT_MAX); + + mutex.unlock(); + + if (msec) + check_time(); + } +} + + +// MainThread::idle_loop() is where the main thread is parked waiting to be started +// when there is a new search. Main thread will launch all the slave threads. + +void MainThread::idle_loop() { + + while (true) + { + mutex.lock(); + + thinking = false; + + while (!thinking && !exit) + { + Threads.sleepCondition.notify_one(); // Wake up UI thread if needed + sleepCondition.wait(mutex); + } + + mutex.unlock(); + + if (exit) + return; + + searching = true; + + Search::think(); + + assert(searching); + + searching = false; + } +} + + +// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the +// current active split point, or in some ancestor of the split point. + +bool Thread::cutoff_occurred() const { + + for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint) + if (sp->cutoff) + return true; + + return false; +} + + +// Thread::is_available_to() checks whether the thread is available to help the +// thread 'master' at a split point. An obvious requirement is that thread must +// be idle. With more than two threads, this is not sufficient: If the thread is +// the master of some split point, it is only available as a slave to the slaves +// which are busy searching the split point at the top of slaves split point +// stack (the "helpful master concept" in YBWC terminology). + +bool Thread::is_available_to(const Thread* master) const { + + if (searching) + return false; + + // Make a local copy to be sure doesn't become zero under our feet while + // testing next condition and so leading to an out of bound access. + int size = splitPointsSize; + + // No split points means that the thread is available as a slave for any + // other thread otherwise apply the "helpful master" concept if possible. + return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx)); +} + + +// init() is called at startup to create and launch requested threads, that will +// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use +// a c'tor becuase Threads is a static object and we need a fully initialized +// engine at this point due to allocation of Endgames in Thread c'tor. + +void ThreadPool::init() { + + sleepWhileIdle = true; + timer = new_thread(); + push_back(new_thread()); + read_uci_options(); +} + + +// exit() cleanly terminates the threads before the program exits + +void ThreadPool::exit() { + + delete_thread(timer); // As first because check_time() accesses threads data + + for (iterator it = begin(); it != end(); ++it) + delete_thread(*it); +} + + +// read_uci_options() updates internal threads parameters from the corresponding +// UCI options and creates/destroys threads to match the requested number. Thread +// objects are dynamically allocated to avoid creating in advance all possible +// threads, with included pawns and material tables, if only few are used. + +void ThreadPool::read_uci_options() { + + maxThreadsPerSplitPoint = Options["Max Threads per Split Point"]; + minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; + size_t requested = Options["Threads"]; + + assert(requested > 0); + + // Value 0 has a special meaning: We determine the optimal minimum split depth + // automatically. Anyhow the minimumSplitDepth should never be under 4 plies. + if (!minimumSplitDepth) + minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY; + else + minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth); + + while (size() < requested) + push_back(new_thread()); + + while (size() > requested) + { + delete_thread(back()); + pop_back(); + } +} + + +// slave_available() tries to find an idle thread which is available as a slave +// for the thread 'master'. + +Thread* ThreadPool::available_slave(const Thread* master) const { + + for (const_iterator it = begin(); it != end(); ++it) + if ((*it)->is_available_to(master)) + return *it; + + return NULL; +} + + +// split() does the actual work of distributing the work at a node between +// several available threads. If it does not succeed in splitting the node +// (because no idle threads are available), the function immediately returns. +// If splitting is possible, a SplitPoint object is initialized with all the +// data that must be copied to the helper threads and then helper threads are +// told that they have been assigned work. This will cause them to instantly +// leave their idle loops and call search(). When all threads have returned from +// search() then split() returns. + +template +void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, + Move* bestMove, Depth depth, Move threatMove, int moveCount, + MovePicker* movePicker, int nodeType, bool cutNode) { + + assert(pos.pos_is_ok()); + assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); + assert(*bestValue > -VALUE_INFINITE); + assert(depth >= Threads.minimumSplitDepth); + assert(searching); + assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); + + // Pick the next available split point from the split point stack + SplitPoint& sp = splitPoints[splitPointsSize]; + + sp.masterThread = this; + sp.parentSplitPoint = activeSplitPoint; + sp.slavesMask = 1ULL << idx; + sp.depth = depth; + sp.bestValue = *bestValue; + sp.bestMove = *bestMove; + sp.threatMove = threatMove; + sp.alpha = alpha; + sp.beta = beta; + sp.nodeType = nodeType; + sp.cutNode = cutNode; + sp.movePicker = movePicker; + sp.moveCount = moveCount; + sp.pos = &pos; + sp.nodes = 0; + sp.cutoff = false; + sp.ss = ss; + + // Try to allocate available threads and ask them to start searching setting + // 'searching' flag. This must be done under lock protection to avoid concurrent + // allocation of the same slave by another master. + Threads.mutex.lock(); + sp.mutex.lock(); + + splitPointsSize++; + activeSplitPoint = &sp; + activePosition = NULL; + + size_t slavesCnt = 1; // This thread is always included + Thread* slave; + + while ( (slave = Threads.available_slave(this)) != NULL + && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) + { + sp.slavesMask |= 1ULL << slave->idx; + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping + } + + // Everything is set up. The master thread enters the idle loop, from which + // it will instantly launch a search, because its 'searching' flag is set. + // The thread will return from the idle loop when all slaves have finished + // their work at this split point. + if (slavesCnt > 1 || Fake) + { + sp.mutex.unlock(); + Threads.mutex.unlock(); + + Thread::idle_loop(); // Force a call to base class idle_loop() + + // In helpful master concept a master can help only a sub-tree of its split + // point, and because here is all finished is not possible master is booked. + assert(!searching); + assert(!activePosition); + + // We have returned from the idle loop, which means that all threads are + // finished. Note that setting 'searching' and decreasing splitPointsSize is + // done under lock protection to avoid a race with Thread::is_available_to(). + Threads.mutex.lock(); + sp.mutex.lock(); + } + + searching = true; + splitPointsSize--; + activeSplitPoint = sp.parentSplitPoint; + activePosition = &pos; + pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); + *bestMove = sp.bestMove; + *bestValue = sp.bestValue; + + sp.mutex.unlock(); + Threads.mutex.unlock(); +} + +// Explicit template instantiations +template void Thread::split(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); +template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); + + +// wait_for_think_finished() waits for main thread to go to sleep then returns + +void ThreadPool::wait_for_think_finished() { + + MainThread* t = main(); + t->mutex.lock(); + while (t->thinking) sleepCondition.wait(t->mutex); + t->mutex.unlock(); +} + + +// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop() +// so to start a new search, then returns immediately. + +void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, + const std::vector& searchMoves, StateStackPtr& states) { + wait_for_think_finished(); + + SearchTime = Time::now(); // As early as possible + + Signals.stopOnPonderhit = Signals.firstRootMove = false; + Signals.stop = Signals.failedLowAtRoot = false; + + RootMoves.clear(); + RootPos = pos; + Limits = limits; + if (states.get()) // If we don't set a new position, preserve current state + { + SetupStates = states; // Ownership transfer here + assert(!states.get()); + } + + for (MoveList it(pos); *it; ++it) + if ( searchMoves.empty() + || std::count(searchMoves.begin(), searchMoves.end(), *it)) + RootMoves.push_back(RootMove(*it)); + + main()->thinking = true; + main()->notify_one(); // Starts main thread +} diff --git a/stockfish-4-win/src/thread.h b/stockfish-4-win/src/thread.h new file mode 100644 index 0000000000000000000000000000000000000000..56d000f3f94438275a83c88eb06ad7c2582fd921 --- /dev/null +++ b/stockfish-4-win/src/thread.h @@ -0,0 +1,178 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "material.h" +#include "movepick.h" +#include "pawns.h" +#include "position.h" +#include "search.h" + +const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t +const int MAX_SPLITPOINTS_PER_THREAD = 8; + +struct Mutex { + Mutex() { lock_init(l); } + ~Mutex() { lock_destroy(l); } + + void lock() { lock_grab(l); } + void unlock() { lock_release(l); } + +private: + friend struct ConditionVariable; + + Lock l; +}; + +struct ConditionVariable { + ConditionVariable() { cond_init(c); } + ~ConditionVariable() { cond_destroy(c); } + + void wait(Mutex& m) { cond_wait(c, m.l); } + void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); } + void notify_one() { cond_signal(c); } + +private: + WaitCondition c; +}; + +struct Thread; + +struct SplitPoint { + + // Const data after split point has been setup + const Position* pos; + const Search::Stack* ss; + Thread* masterThread; + Depth depth; + Value beta; + int nodeType; + Move threatMove; + bool cutNode; + + // Const pointers to shared data + MovePicker* movePicker; + SplitPoint* parentSplitPoint; + + // Shared data + Mutex mutex; + volatile uint64_t slavesMask; + volatile int64_t nodes; + volatile Value alpha; + volatile Value bestValue; + volatile Move bestMove; + volatile int moveCount; + volatile bool cutoff; +}; + + +/// ThreadBase struct is the base of the hierarchy from where we derive all the +/// specialized thread classes. + +struct ThreadBase { + + ThreadBase() : exit(false) {} + virtual ~ThreadBase() {} + virtual void idle_loop() = 0; + void notify_one(); + void wait_for(volatile const bool& b); + + Mutex mutex; + ConditionVariable sleepCondition; + NativeHandle handle; + volatile bool exit; +}; + + +/// Thread struct keeps together all the thread related stuff like locks, state +/// and especially split points. 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. + +struct Thread : public ThreadBase { + + Thread(); + virtual void idle_loop(); + bool cutoff_occurred() const; + bool is_available_to(const Thread* master) const; + + template + void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, + Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); + + SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; + Material::Table materialTable; + Endgames endgames; + Pawns::Table pawnsTable; + Position* activePosition; + size_t idx; + int maxPly; + SplitPoint* volatile activeSplitPoint; + volatile int splitPointsSize; + volatile bool searching; +}; + + +/// MainThread and TimerThread are derived classes used to characterize the two +/// special threads: the main one and the recurring timer. + +struct MainThread : public Thread { + MainThread() : thinking(true) {} // Avoid a race with start_thinking() + virtual void idle_loop(); + volatile bool thinking; +}; + +struct TimerThread : public ThreadBase { + TimerThread() : msec(0) {} + virtual void idle_loop(); + int msec; +}; + + +/// ThreadPool struct handles all the threads related stuff like init, starting, +/// parking and, the most important, launching a slave thread at a split point. +/// All the access to shared thread data is done through this class. + +struct ThreadPool : public std::vector { + + void init(); // No c'tor and d'tor, threads rely on globals that should + void exit(); // be initialized and valid during the whole thread lifetime. + + MainThread* main() { return static_cast((*this)[0]); } + void read_uci_options(); + Thread* available_slave(const Thread* master) const; + void wait_for_think_finished(); + void start_thinking(const Position&, const Search::LimitsType&, + const std::vector&, Search::StateStackPtr&); + + bool sleepWhileIdle; + Depth minimumSplitDepth; + size_t maxThreadsPerSplitPoint; + Mutex mutex; + ConditionVariable sleepCondition; + TimerThread* timer; +}; + +extern ThreadPool Threads; + +#endif // #ifndef THREAD_H_INCLUDED diff --git a/stockfish-4-win/src/timeman.cpp b/stockfish-4-win/src/timeman.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d2bacca3c10c4d20e73c8f59ddaaf0be85fe12c --- /dev/null +++ b/stockfish-4-win/src/timeman.cpp @@ -0,0 +1,162 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "search.h" +#include "timeman.h" +#include "ucioption.h" + +namespace { + + /// Constants + + const int MoveHorizon = 50; // Plan time management at most this many moves ahead + const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio + const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio + + + // MoveImportance[] is 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 CCRL game database with some simple filtering criteria. + const int MoveImportance[512] = { + 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, + 7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751, + 7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425, + 7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651, + 6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462, + 5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155, + 4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011, + 2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192, + 2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605, + 1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180, + 1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888, + 878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641, + 634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460, + 450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333, + 328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240, + 235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170, + 170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118, + 118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80, + 80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54, + 52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33, + 31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20, + 19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11, + 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1 }; + + int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; } + + + /// Function Prototypes + + enum TimeType { OptimumTime, MaxTime }; + + template + int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover); +} + + +void TimeManager::pv_instability(int curChanges, int prevChanges) { + + unstablePVExtraTime = curChanges * (optimumSearchTime / 2) + + prevChanges * (optimumSearchTime / 3); +} + + +void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us) +{ + /* We support four different kind of time controls: + + increment == 0 && movesToGo == 0 means: x basetime [sudden death!] + increment == 0 && movesToGo != 0 means: x moves in y minutes + increment > 0 && movesToGo == 0 means: x basetime + z increment + increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment + + Time management is adjusted by following UCI parameters: + + emergencyMoveHorizon: Be prepared to always play at least this many moves + emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock + emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move + minThinkingTime : No matter what, use at least this much thinking before doing the move + */ + + int hypMTG, hypMyTime, t1, t2; + + // Read uci parameters + int emergencyMoveHorizon = Options["Emergency Move Horizon"]; + int emergencyBaseTime = Options["Emergency Base Time"]; + int emergencyMoveTime = Options["Emergency Move Time"]; + int minThinkingTime = Options["Minimum Thinking Time"]; + int slowMover = Options["Slow Mover"]; + + // Initialize to maximum values but unstablePVExtraTime that is reset + unstablePVExtraTime = 0; + optimumSearchTime = maximumSearchTime = limits.time[us]; + + // We calculate optimum time usage for different hypothetic "moves to go"-values and choose the + // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. + for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++) + { + // Calculate thinking time for hypothetic "moves to go"-value + hypMyTime = limits.time[us] + + limits.inc[us] * (hypMTG - 1) + - emergencyBaseTime + - emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon); + + hypMyTime = std::max(hypMyTime, 0); + + t1 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); + t2 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); + + optimumSearchTime = std::min(optimumSearchTime, t1); + maximumSearchTime = std::min(maximumSearchTime, t2); + } + + if (Options["Ponder"]) + optimumSearchTime += optimumSearchTime / 4; + + // Make sure that maxSearchTime is not over absoluteMaxSearchTime + optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); +} + + +namespace { + + template + int remaining(int myTime, int movesToGo, int currentPly, int slowMover) + { + const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); + const float TStealRatio = (T == OptimumTime ? 0 : StealRatio); + + int thisMoveImportance = move_importance(currentPly) * slowMover / 100; + int otherMovesImportance = 0; + + for (int i = 1; i < movesToGo; i++) + otherMovesImportance += move_importance(currentPly + 2 * i); + + float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance); + float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance); + + return int(floor(myTime * std::min(ratio1, ratio2))); + } +} diff --git a/stockfish-4-win/src/timeman.h b/stockfish-4-win/src/timeman.h new file mode 100644 index 0000000000000000000000000000000000000000..e12b0007f302f37f07cb40ea8c5f6fce07a3f615 --- /dev/null +++ b/stockfish-4-win/src/timeman.h @@ -0,0 +1,39 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +/// The TimeManager class computes the optimal time to think depending on the +/// maximum available time, the move game number and other parameters. + +class TimeManager { +public: + void init(const Search::LimitsType& limits, int currentPly, Color us); + void pv_instability(int curChanges, int prevChanges); + int available_time() const { return optimumSearchTime + unstablePVExtraTime; } + int maximum_time() const { return maximumSearchTime; } + +private: + int optimumSearchTime; + int maximumSearchTime; + int unstablePVExtraTime; +}; + +#endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/stockfish-4-win/src/tt.cpp b/stockfish-4-win/src/tt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9774a2332fa8fd481983ee90b729195ec7112aeb --- /dev/null +++ b/stockfish-4-win/src/tt.cpp @@ -0,0 +1,121 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "tt.h" + +TranspositionTable TT; // Our global transposition table + + +/// TranspositionTable::set_size() 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::set_size(size_t mbSize) { + + assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); + + uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + + if (hashMask == size - ClusterSize) + return; + + hashMask = size - ClusterSize; + free(mem); + mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1); + + if (!mem) + { + std::cerr << "Failed to allocate " << mbSize + << "MB for transposition table." << std::endl; + exit(EXIT_FAILURE); + } + + table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); +} + + +/// TranspositionTable::clear() overwrites the entire transposition table +/// with zeroes. It is called whenever the table is resized, or when the +/// user asks the program to clear the table (from the UCI interface). + +void TranspositionTable::clear() { + + std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); +} + + +/// TranspositionTable::probe() looks up the current position in the +/// transposition table. Returns a pointer to the TTEntry or NULL if +/// position is not found. + +const TTEntry* TranspositionTable::probe(const Key key) const { + + const TTEntry* tte = first_entry(key); + uint32_t key32 = key >> 32; + + for (unsigned i = 0; i < ClusterSize; i++, tte++) + if (tte->key() == key32) + return tte; + + return NULL; +} + + +/// TranspositionTable::store() writes a new entry containing position key and +/// valuable information of current position. The lowest order bits of position +/// key are used to decide on which cluster the position will be placed. +/// When a new entry is written and there are no empty entries available in cluster, +/// it replaces the least valuable of entries. A TTEntry t1 is considered to be +/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from +/// a previous search, or if the depth of t1 is bigger than the depth of t2. + +void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) { + + int c1, c2, c3; + TTEntry *tte, *replace; + uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster + + tte = replace = first_entry(key); + + for (unsigned i = 0; i < ClusterSize; i++, tte++) + { + if (!tte->key() || tte->key() == key32) // Empty or overwrite old + { + if (!m) + m = tte->move(); // Preserve any existing ttMove + + replace = tte; + break; + } + + // Implement replace strategy + c1 = (replace->generation() == generation ? 2 : 0); + c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0); + c3 = (tte->depth() < replace->depth() ? 1 : 0); + + if (c1 + c2 + c3 > 0) + replace = tte; + } + + replace->save(key32, v, b, d, m, generation, statV, evalM); +} diff --git a/stockfish-4-win/src/tt.h b/stockfish-4-win/src/tt.h new file mode 100644 index 0000000000000000000000000000000000000000..3113751adb938e868616efe6a8eb78c15e946b43 --- /dev/null +++ b/stockfish-4-win/src/tt.h @@ -0,0 +1,118 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +/// The TTEntry is the 128 bit transposition table entry, defined as below: +/// +/// key: 32 bit +/// move: 16 bit +/// bound type: 8 bit +/// generation: 8 bit +/// value: 16 bit +/// depth: 16 bit +/// static value: 16 bit +/// static margin: 16 bit + +struct TTEntry { + + void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { + + key32 = (uint32_t)k; + move16 = (uint16_t)m; + bound8 = (uint8_t)b; + generation8 = (uint8_t)g; + value16 = (int16_t)v; + depth16 = (int16_t)d; + evalValue = (int16_t)ev; + evalMargin = (int16_t)em; + } + void set_generation(uint8_t g) { generation8 = g; } + + uint32_t key() const { return key32; } + Depth depth() const { return (Depth)depth16; } + Move move() const { return (Move)move16; } + Value value() const { return (Value)value16; } + Bound bound() const { return (Bound)bound8; } + int generation() const { return (int)generation8; } + Value eval_value() const { return (Value)evalValue; } + Value eval_margin() const { return (Value)evalMargin; } + +private: + uint32_t key32; + uint16_t move16; + uint8_t bound8, generation8; + int16_t value16, depth16, evalValue, evalMargin; +}; + + +/// 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. Size of a cluster shall not be +/// bigger than a cache line size. In case it is less, it should be padded to +/// guarantee always aligned accesses. + +class TranspositionTable { + + static const unsigned ClusterSize = 4; // A cluster is 64 Bytes + +public: + ~TranspositionTable() { free(mem); } + void new_search() { generation++; } + + const TTEntry* probe(const Key key) const; + TTEntry* first_entry(const Key key) const; + void refresh(const TTEntry* tte) const; + void set_size(size_t mbSize); + void clear(); + void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); + +private: + uint32_t hashMask; + TTEntry* table; + void* mem; + uint8_t generation; // Size must be not bigger than TTEntry::generation8 +}; + +extern TranspositionTable TT; + + +/// TranspositionTable::first_entry() returns a pointer to the first entry of +/// a cluster given a position. The lowest order bits of the key are used to +/// get the index of the cluster. + +inline TTEntry* TranspositionTable::first_entry(const Key key) const { + + return table + ((uint32_t)key & hashMask); +} + + +/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry +/// to avoid aging. Normally called after a TT hit. + +inline void TranspositionTable::refresh(const TTEntry* tte) const { + + const_cast(tte)->set_generation(generation); +} + +#endif // #ifndef TT_H_INCLUDED diff --git a/stockfish-4-win/src/types.h b/stockfish-4-win/src/types.h new file mode 100644 index 0000000000000000000000000000000000000000..86a86d195ccf25b4e6f1f5ce3e6295c856bafa5d --- /dev/null +++ b/stockfish-4-win/src/types.h @@ -0,0 +1,443 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +/// For Linux and OSX configuration is done automatically using Makefile. To get +/// started type 'make help'. +/// +/// For Windows, part of the configuration is detected automatically, but some +/// switches need to be set manually: +/// +/// -DNDEBUG | Disable debugging mode. Use always. +/// +/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want +/// | the executable to run on some very old machines. +/// +/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works +/// | only in 64-bit mode. For compiling requires hardware with +/// | popcnt support. + +#include +#include +#include +#include + +#include "platform.h" + +#define unlikely(x) (x) // For code annotation purposes + +#if defined(_WIN64) && !defined(IS_64BIT) +# include // MSVC popcnt and bsfq instrinsics +# define IS_64BIT +# define USE_BSFQ +#endif + +#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER) +# include // Intel header for _mm_popcnt_u64() intrinsic +#endif + +# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) +# include // Intel and Microsoft header for _mm_prefetch() +# endif + +#define CACHE_LINE_SIZE 64 +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) +# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE)) +#else +# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE))) +#endif + +#ifdef _MSC_VER +# define FORCE_INLINE __forceinline +#elif defined(__GNUC__) +# define FORCE_INLINE inline __attribute__((always_inline)) +#else +# define FORCE_INLINE inline +#endif + +#ifdef USE_POPCNT +const bool HasPopCnt = true; +#else +const bool HasPopCnt = 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 = 192; +const int MAX_PLY = 100; +const int MAX_PLY_PLUS_6 = MAX_PLY + 6; + +/// 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), castle (3) +/// +/// 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 { + MOVE_NONE, + MOVE_NULL = 65 +}; + +enum MoveType { + NORMAL, + PROMOTION = 1 << 14, + ENPASSANT = 2 << 14, + CASTLE = 3 << 14 +}; + +enum CastleRight { // Defined as in PolyGlot book hash key + CASTLES_NONE, + WHITE_OO, + WHITE_OOO = WHITE_OO << 1, + BLACK_OO = WHITE_OO << 2, + BLACK_OOO = WHITE_OO << 3, + ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, + CASTLE_RIGHT_NB = 16 +}; + +enum CastlingSide { + KING_SIDE, + QUEEN_SIDE, + CASTLING_SIDE_NB = 2 +}; + +enum Phase { + PHASE_ENDGAME, + PHASE_MIDGAME = 128, + MG = 0, EG = 1, PHASE_NB = 2 +}; + +enum ScaleFactor { + SCALE_FACTOR_DRAW = 0, + 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 { + VALUE_ZERO = 0, + VALUE_DRAW = 0, + VALUE_KNOWN_WIN = 15000, + VALUE_MATE = 30000, + VALUE_INFINITE = 30001, + VALUE_NONE = 30002, + + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, + + VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, + VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN, + + PawnValueMg = 198, PawnValueEg = 258, + KnightValueMg = 817, KnightValueEg = 846, + BishopValueMg = 836, BishopValueEg = 857, + RookValueMg = 1270, RookValueEg = 1278, + QueenValueMg = 2521, QueenValueEg = 2558 +}; + +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 +}; + +enum Color { + WHITE, BLACK, NO_COLOR, COLOR_NB = 2 +}; + +enum Depth { + + ONE_PLY = 2, + + DEPTH_ZERO = 0 * ONE_PLY, + DEPTH_QS_CHECKS = -1 * ONE_PLY, + DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, + DEPTH_QS_RECAPTURES = -7 * ONE_PLY, + + DEPTH_NONE = -127 * ONE_PLY +}; + +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, + + DELTA_N = 8, + DELTA_E = 1, + DELTA_S = -8, + DELTA_W = -1, + + DELTA_NN = DELTA_N + DELTA_N, + DELTA_NE = DELTA_N + DELTA_E, + DELTA_SE = DELTA_S + DELTA_E, + DELTA_SS = DELTA_S + DELTA_S, + DELTA_SW = DELTA_S + DELTA_W, + DELTA_NW = DELTA_N + DELTA_W +}; + +enum File { + FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB +}; + +enum Rank { + RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB +}; + + +/// Score enum keeps a midgame and an endgame value in a single integer (enum), +/// first LSB 16 bits are used to store endgame value, while upper bits are used +/// for midgame value. Compiler is free to choose the enum type as long as can +/// keep its data, so ensure Score to be an integer type. +enum Score { + SCORE_ZERO, + SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, + SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN +}; + +inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } + +/// Extracting the signed lower and upper 16 bits it 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 mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); } + +/// On Intel 64 bit we have a small speed regression with the standard conforming +/// version, so use a faster code in this case that, although not 100% standard +/// compliant it seems to work for Intel and MSVC. +#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER)) + +inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); } + +#else + +inline Value eg_value(Score s) { + return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); +} + +#endif + +#define ENABLE_SAFE_OPERATORS_ON(T) \ +inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \ +inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \ +inline T operator*(int i, const T d) { return T(i * int(d)); } \ +inline T operator*(const T d, int i) { return T(int(d) * i); } \ +inline T operator-(const T d) { return T(-int(d)); } \ +inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \ +inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \ +inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; } + +#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \ +inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \ +inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \ +inline T operator/(const T d, int i) { return T(int(d) / i); } \ +inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; } + +ENABLE_OPERATORS_ON(Value) +ENABLE_OPERATORS_ON(PieceType) +ENABLE_OPERATORS_ON(Piece) +ENABLE_OPERATORS_ON(Color) +ENABLE_OPERATORS_ON(Depth) +ENABLE_OPERATORS_ON(Square) +ENABLE_OPERATORS_ON(File) +ENABLE_OPERATORS_ON(Rank) + +/// Added operators for adding 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); } + +ENABLE_SAFE_OPERATORS_ON(Score) + +/// 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); +} + +#undef ENABLE_OPERATORS_ON +#undef ENABLE_SAFE_OPERATORS_ON + +extern Value PieceValue[PHASE_NB][PIECE_NB]; + +struct ExtMove { + Move move; + int score; +}; + +inline bool operator<(const ExtMove& f, const ExtMove& s) { + return f.score < s.score; +} + +inline Color operator~(Color c) { + return Color(c ^ 1); +} + +inline Square operator~(Square s) { + return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8 +} + +inline Square operator|(File f, Rank r) { + return Square((r << 3) | f); +} + +inline Value mate_in(int ply) { + return VALUE_MATE - ply; +} + +inline Value mated_in(int ply) { + return -VALUE_MATE + ply; +} + +inline Piece make_piece(Color c, PieceType pt) { + return Piece((c << 3) | pt); +} + +inline CastleRight make_castle_right(Color c, CastlingSide s) { + return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); +} + +inline PieceType type_of(Piece p) { + return PieceType(p & 7); +} + +inline Color color_of(Piece p) { + assert(p != NO_PIECE); + return Color(p >> 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 mirror(Square s) { + return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1 +} + +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 char file_to_char(File f, bool tolower = true) { + return char(f - FILE_A + (tolower ? 'a' : 'A')); +} + +inline char rank_to_char(Rank r) { + return char(r - RANK_1 + '1'); +} + +inline Square pawn_push(Color c) { + return c == WHITE ? DELTA_N : DELTA_S; +} + +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) + 2); +} + +inline Move make_move(Square from, Square to) { + return Move(to | (from << 6)); +} + +template +inline Move make(Square from, Square to, PieceType pt = KNIGHT) { + return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)); +} + +inline bool is_ok(Move m) { + return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE +} + +#include + +inline const std::string square_to_string(Square s) { + char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 }; + return ch; +} + +#endif // #ifndef TYPES_H_INCLUDED diff --git a/stockfish-4-win/src/uci.cpp b/stockfish-4-win/src/uci.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caf1b5c2fed08d67084b09c584ba1c7f4c7716c1 --- /dev/null +++ b/stockfish-4-win/src/uci.cpp @@ -0,0 +1,219 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "evaluate.h" +#include "notation.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "ucioption.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"; + + // Keep track of position keys along the setup moves (from start position to the + // position just before to start searching). Needed by repetition draw detection. + Search::StateStackPtr SetupStates; + + void setoption(istringstream& up); + void position(Position& pos, istringstream& up); + void go(const Position& pos, istringstream& up); +} + + +/// Wait for a command from the user, parse this text string as an UCI command, +/// and call the appropriate functions. Also intercepts EOF from stdin to ensure +/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI +/// commands, the function also supports a few debug commands. + +void UCI::loop(const string& args) { + + Position pos(StartFEN, false, Threads.main()); // The root position + string token, cmd = args; + + do { + if (args.empty() && !getline(cin, cmd)) // Block here waiting for input + cmd = "quit"; + + istringstream is(cmd); + + is >> skipws >> token; + + if (token == "quit" || token == "stop" || token == "ponderhit") + { + // 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 != "ponderhit" || Search::Signals.stopOnPonderhit) + { + Search::Signals.stop = true; + Threads.main()->notify_one(); // Could be sleeping + } + else + Search::Limits.ponder = false; + } + else if (token == "perft" && (is >> token)) // Read perft depth + { + stringstream ss; + + ss << Options["Hash"] << " " + << Options["Threads"] << " " << token << " current perft"; + + benchmark(pos, ss); + } + else if (token == "key") + sync_cout << hex << uppercase << setfill('0') + << "position key: " << setw(16) << pos.key() + << "\nmaterial key: " << setw(16) << pos.material_key() + << "\npawn key: " << setw(16) << pos.pawn_key() + << dec << sync_endl; + + else if (token == "uci") + sync_cout << "id name " << engine_info(true) + << "\n" << Options + << "\nuciok" << sync_endl; + + else if (token == "eval") + { + Search::RootColor = pos.side_to_move(); // Ensure it is set + sync_cout << Eval::trace(pos) << sync_endl; + } + else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } + else if (token == "go") go(pos, is); + else if (token == "position") position(pos, is); + else if (token == "setoption") setoption(is); + else if (token == "flip") pos.flip(); + else if (token == "bench") benchmark(pos, is); + else if (token == "d") sync_cout << pos.pretty() << sync_endl; + else if (token == "isready") sync_cout << "readyok" << sync_endl; + else + sync_cout << "Unknown command: " << cmd << sync_endl; + + } while (token != "quit" && args.empty()); // Args have one-shot behaviour + + Threads.wait_for_think_finished(); // Cannot quit while search is running +} + + +namespace { + + // 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; + + pos.set(fen, Options["UCI_Chess960"], Threads.main()); + SetupStates = Search::StateStackPtr(new std::stack()); + + // Parse move list (if any) + while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE) + { + SetupStates->push(StateInfo()); + pos.do_move(m, SetupStates->top()); + } + } + + + // 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()) + token; + + // Read option value (can contain spaces) + while (is >> token) + value += string(" ", !value.empty()) + 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, and starts + // the search. + + void go(const Position& pos, istringstream& is) { + + Search::LimitsType limits; + vector searchMoves; + string token; + + while (is >> token) + { + if (token == "searchmoves") + while (is >> token) + searchMoves.push_back(move_from_uci(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 = true; + else if (token == "ponder") limits.ponder = true; + } + + Threads.start_thinking(pos, limits, searchMoves, SetupStates); + } +} diff --git a/stockfish-4-win/src/ucioption.cpp b/stockfish-4-win/src/ucioption.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b18f09924a9085d2b36e00682f21450fe2fb2964 --- /dev/null +++ b/stockfish-4-win/src/ucioption.cpp @@ -0,0 +1,164 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "evaluate.h" +#include "misc.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +using std::string; + +UCI::OptionsMap Options; // Global object + +namespace UCI { + +/// 'On change' actions, triggered by an option's value change +void on_logger(const Option& o) { start_logger(o); } +void on_eval(const Option&) { Eval::init(); } +void on_threads(const Option&) { Threads.read_uci_options(); } +void on_hash_size(const Option& o) { TT.set_size(o); } +void on_clear_hash(const Option&) { TT.clear(); } + + +/// Our case insensitive less() function as required by UCI protocol +bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); } + +bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less); +} + + +/// init() initializes the UCI options to their hard coded default values + +void init(OptionsMap& o) { + + o["Write Debug Log"] = Option(false, on_logger); + o["Write Search Log"] = Option(false); + o["Search Log Filename"] = Option("SearchLog.txt"); + o["Book File"] = Option("book.bin"); + o["Best Book Move"] = Option(false); + o["Contempt Factor"] = Option(0, -50, 50); + o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval); + o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); + o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval); + o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); + o["Space"] = Option(100, 0, 200, on_eval); + o["Aggressiveness"] = Option(100, 0, 200, on_eval); + o["Cowardice"] = Option(100, 0, 200, on_eval); + o["Min Split Depth"] = Option(0, 0, 12, on_threads); + o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); + o["Threads"] = Option(1, 1, MAX_THREADS, on_threads); + o["Idle Threads Sleep"] = Option(false); + o["Hash"] = Option(32, 1, 8192, on_hash_size); + o["Clear Hash"] = Option(on_clear_hash); + o["Ponder"] = Option(true); + o["OwnBook"] = Option(false); + o["MultiPV"] = Option(1, 1, 500); + o["Skill Level"] = Option(20, 0, 20); + o["Emergency Move Horizon"] = Option(40, 0, 50); + o["Emergency Base Time"] = Option(200, 0, 30000); + o["Emergency Move Time"] = Option(70, 0, 5000); + o["Minimum Thinking Time"] = Option(20, 0, 5000); + o["Slow Mover"] = Option(100, 10, 1000); + o["UCI_Chess960"] = Option(false); + o["UCI_AnalyseMode"] = Option(false, on_eval); +} + + +/// 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 (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it) + 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 c'tors and conversion operators + +Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) +{ defaultValue = currentValue = v; } + +Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) +{ defaultValue = currentValue = (v ? "true" : "false"); } + +Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) +{} + +Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f) +{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); } + + +Option::operator int() const { + assert(type == "check" || type == "spin"); + return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true"); +} + +Option::operator std::string() const { + assert(type == "string"); + return currentValue; +} + + +/// 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" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max))) + return *this; + + if (type != "button") + currentValue = v; + + if (on_change) + (*on_change)(*this); + + return *this; +} + +} // namespace UCI diff --git a/stockfish-4-win/src/ucioption.h b/stockfish-4-win/src/ucioption.h new file mode 100644 index 0000000000000000000000000000000000000000..efdfce6607141be39c4e97950cd5354d6534db8c --- /dev/null +++ b/stockfish-4-win/src/ucioption.h @@ -0,0 +1,69 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 UCIOPTION_H_INCLUDED +#define UCIOPTION_H_INCLUDED + +#include +#include + +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 (Fn)(const Option&); + +public: + Option(Fn* = NULL); + Option(bool v, Fn* = NULL); + Option(const char* v, Fn* = NULL); + Option(int v, int min, int max, Fn* = NULL); + + Option& operator=(const std::string& v); + 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; + Fn* on_change; +}; + +void init(OptionsMap&); +void loop(const std::string&); + +} // namespace UCI + +extern UCI::OptionsMap Options; + +#endif // #ifndef UCIOPTION_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/Makefile b/stockfish-4-win/src_c++11/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..add677e425c53b6bbd9c9c53731163e9a5bae8e8 --- /dev/null +++ b/stockfish-4-win/src_c++11/Makefile @@ -0,0 +1,565 @@ +# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 +# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) +# Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +UNAME = $(shell uname) + +### Executable name +EXE = stockfish + +### Installation dir definitions +PREFIX = /usr/local +# Haiku has a non-standard filesystem layout +ifeq ($(UNAME),Haiku) + PREFIX=/boot/common +endif +BINDIR = $(PREFIX)/bin + +### Built-in benchmark for pgo-builds and signature +PGOBENCH = ./$(EXE) bench 32 1 10 default depth +SIGNBENCH = ./$(EXE) bench + +### Object files +OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \ + material.o misc.o movegen.o movepick.o notation.o pawns.o position.o \ + search.o thread.o timeman.o tt.o uci.o ucioption.o + +### ========================================================================== +### Section 2. High-level Configuration +### ========================================================================== +# +# flag --- Comp switch --- Description +# ---------------------------------------------------------------------------- +# +# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode +# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations +# arch = (name) --- (-arch) --- Target architecture +# os = (name) --- --- Target operating system +# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system +# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction +# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only +# with GCC and ICC 64-bit) +# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction +# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# +# 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 +debug = no +optimize = yes + +### 2.2 Architecture specific + +# General-section +ifeq ($(ARCH),general-64) + arch = any + os = any + bits = 64 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),general-32) + arch = any + os = any + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +# x86-section +ifeq ($(ARCH),x86-64) + arch = x86_64 + os = any + bits = 64 + prefetch = yes + bsfq = yes + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),x86-64-modern) + arch = x86_64 + os = any + bits = 64 + prefetch = yes + bsfq = yes + popcnt = yes + sse = yes +endif + +ifeq ($(ARCH),x86-32) + arch = i386 + os = any + bits = 32 + prefetch = yes + bsfq = no + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),x86-32-old) + arch = i386 + os = any + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +#arm section +ifeq ($(ARCH),armv7) + arch = armv7 + os = any + bits = 32 + prefetch = yes + bsfq = yes + popcnt = no + sse = no +endif + +# osx-section +ifeq ($(ARCH),osx-ppc-64) + arch = ppc64 + os = osx + bits = 64 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),osx-ppc-32) + arch = ppc + os = osx + bits = 32 + prefetch = no + bsfq = no + popcnt = no + sse = no +endif + +ifeq ($(ARCH),osx-x86-64) + arch = x86_64 + os = osx + bits = 64 + prefetch = yes + bsfq = yes + popcnt = no + sse = yes +endif + +ifeq ($(ARCH),osx-x86-32) + arch = i386 + os = osx + bits = 32 + prefetch = yes + bsfq = no + popcnt = no + sse = yes +endif + + +### ========================================================================== +### Section 3. Low-level configuration +### ========================================================================== + +### 3.1 Selecting compiler (default = gcc) +ifeq ($(COMP),) + COMP=gcc +endif + +ifeq ($(COMP),mingw) + comp=mingw + CXX=g++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +ifeq ($(COMP),gcc) + comp=gcc + CXX=g++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +ifeq ($(COMP),icc) + comp=icc + CXX=icpc + profile_prepare = icc-profile-prepare + profile_make = icc-profile-make + profile_use = icc-profile-use + profile_clean = icc-profile-clean +endif + +ifeq ($(COMP),clang) + comp=clang + CXX=clang++ + profile_prepare = gcc-profile-prepare + profile_make = gcc-profile-make + profile_use = gcc-profile-use + profile_clean = gcc-profile-clean +endif + +### 3.2 General compiler settings +CXXFLAGS = -Wall -Wcast-qual -fno-exceptions -fno-rtti -std=c++11 $(EXTRACXXFLAGS) + +ifeq ($(comp),gcc) + CXXFLAGS += -pedantic -Wno-long-long -Wextra -Wshadow +endif + +ifeq ($(comp),mingw) + CXXFLAGS += -Wextra -Wshadow +endif + +ifeq ($(comp),icc) + CXXFLAGS += -wd383,981,1418,1419,1476,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi +endif + +ifeq ($(comp),clang) + CXXFLAGS += -pedantic -Wno-long-long -Wextra -Wshadow +endif + +ifeq ($(os),osx) + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.0 +endif + +### 3.3 General linker settings +LDFLAGS = $(EXTRALDFLAGS) + +ifeq ($(comp),mingw) + LDFLAGS += -static-libstdc++ -static-libgcc +endif + +### On mingw use Windows threads, otherwise POSIX +ifneq ($(comp),mingw) + # Haiku has pthreads in its libroot, so only link it in on other platforms + ifneq ($(UNAME),Haiku) + LDFLAGS += -lpthread + endif +endif + +ifeq ($(os),osx) + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.0 +endif + +### 3.4 Debugging +ifeq ($(debug),no) + CXXFLAGS += -DNDEBUG +else + CXXFLAGS += -g +endif + +### 3.5 Optimization +ifeq ($(optimize),yes) + + ifeq ($(comp),gcc) + CXXFLAGS += -O3 + + ifeq ($(os),osx) + ifeq ($(arch),i386) + CXXFLAGS += -mdynamic-no-pic + endif + ifeq ($(arch),x86_64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + + ifeq ($(arch),armv7) + CXXFLAGS += -fno-gcse + endif + endif + + ifeq ($(comp),mingw) + CXXFLAGS += -O3 + endif + + ifeq ($(comp),icc) + ifeq ($(os),osx) + CXXFLAGS += -fast -mdynamic-no-pic + else + CXXFLAGS += -O3 + endif + endif + + ifeq ($(comp),clang) + ### -O4 requires a linker that supports LLVM's LTO + CXXFLAGS += -O3 + + ifeq ($(os),osx) + ifeq ($(arch),i386) + CXXFLAGS += -mdynamic-no-pic + endif + ifeq ($(arch),x86_64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + endif +endif + +### 3.6. Bits +ifeq ($(bits),64) + CXXFLAGS += -DIS_64BIT +endif + +### 3.7 prefetch +ifeq ($(prefetch),yes) + ifeq ($(sse),yes) + CXXFLAGS += -msse + DEPENDFLAGS += -msse + endif +else + CXXFLAGS += -DNO_PREFETCH +endif + +### 3.8 bsfq +ifeq ($(bsfq),yes) + CXXFLAGS += -DUSE_BSFQ +endif + +### 3.9 popcnt +ifeq ($(popcnt),yes) + CXXFLAGS += -msse3 -DUSE_POPCNT +endif + +### 3.10 Link Time Optimization, it works since gcc 4.5 but not on mingw. +### 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) + GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.` + GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.` + ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \))) + CXXFLAGS += -flto + LDFLAGS += $(CXXFLAGS) + endif + endif + endif +endif + +### ========================================================================== +### Section 4. Public targets +### ========================================================================== + +help: + @echo "" + @echo "To compile stockfish, type: " + @echo "" + @echo "make target ARCH=arch [COMP=comp]" + @echo "" + @echo "Supported targets:" + @echo "" + @echo "build > Standard build" + @echo "signature-build > Standard build with embedded signature" + @echo "profile-build > PGO build" + @echo "signature-profile-build > PGO build with embedded signature" + @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-32 > x86 32-bit with SSE support" + @echo "x86-32-old > x86 32-bit fall back for old hardware" + @echo "osx-ppc-64 > PPC-Mac OS X 64 bit" + @echo "osx-ppc-32 > PPC-Mac OS X 32 bit" + @echo "osx-x86-64 > x86-Mac OS X 64 bit" + @echo "osx-x86-32 > x86-Mac OS X 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 "Non-standard targets:" + @echo "" + @echo "make hpux > Compile for HP-UX. Compiler = aCC" + @echo "" + @echo "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 "" + +.PHONY: build profile-build embed-signature +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 + $(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 + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) + @echo "" + @echo "Step 4/4. Deleting profile data ..." + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) + +embed-signature: + @echo "Running benchmark for getting the signature ..." + @$(SIGNBENCH) 2>&1 | sed -n 's/Nodes searched : \(.*\)/\/string Version\/s\/"\\(.*\\)"\/"sig-\1"\//p' > sign.txt + @sed -f sign.txt misc.cpp > misc2.cpp + @mv misc2.cpp misc.cpp + @rm sign.txt + +signature-build: build embed-signature + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all + +signature-profile-build: build embed-signature profile-build + +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 + +default: + help + +### ========================================================================== +### Section 5. Private targets +### ========================================================================== + +all: $(EXE) .depend + +config-sanity: + @echo "" + @echo "Config:" + @echo "debug: '$(debug)'" + @echo "optimize: '$(optimize)'" + @echo "arch: '$(arch)'" + @echo "os: '$(os)'" + @echo "bits: '$(bits)'" + @echo "prefetch: '$(prefetch)'" + @echo "bsfq: '$(bsfq)'" + @echo "popcnt: '$(popcnt)'" + @echo "sse: '$(sse)'" + @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 "$(os)" = "any" || test "$(os)" = "osx" + @test "$(bits)" = "32" || test "$(bits)" = "64" + @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" + @test "$(bsfq)" = "yes" || test "$(bsfq)" = "no" + @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" + @test "$(sse)" = "yes" || test "$(sse)" = "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' \ + EXTRALDFLAGS='-lgcov' \ + all + +gcc-profile-clean: + @rm -rf *.gcda *.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 + + +### ========================================================================== +### Section 6. Non-standard targets +### ========================================================================== + +hpux: + $(MAKE) \ + CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -mt +O3 -DNDEBUG -DNO_PREFETCH' \ + CXXFLAGS="" \ + LDFLAGS="" \ + all + diff --git a/stockfish-4-win/src_c++11/benchmark.cpp b/stockfish-4-win/src_c++11/benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c18ec389f23453ce60c4ff003a4248aad0ed7e2c --- /dev/null +++ b/stockfish-4-win/src_c++11/benchmark.cpp @@ -0,0 +1,145 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "tt.h" +#include "ucioption.h" + +using namespace std; + +static 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" +}; + + +/// 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 12), 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 secs or number of nodes. + +void benchmark(const Position& current, istream& is) { + + string token; + Search::LimitsType limits; + vector fens; + + // Assign default values to missing arguments + string ttSize = (is >> token) ? token : "32"; + string threads = (is >> token) ? token : "1"; + string limit = (is >> token) ? token : "12"; + string fenFile = (is >> token) ? token : "default"; + string limitType = (is >> token) ? token : "depth"; + + Options["Hash"] = ttSize; + Options["Threads"] = threads; + TT.clear(); + + if (limitType == "time") + limits.movetime = 1000 * stoi(limit); // movetime is in ms + + 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(); + } + + int64_t nodes = 0; + Search::StateStackPtr st; + Time::point elapsed = Time::now(); + + for (size_t i = 0; i < fens.size(); i++) + { + Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); + + cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; + + if (limitType == "perft") + { + size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); + cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; + nodes += cnt; + } + else + { + Threads.start_thinking(pos, limits, vector(), st); + Threads.wait_for_think_finished(); + nodes += Search::RootPos.nodes_searched(); + } + } + + elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' + + cerr << "\n===========================" + << "\nTotal time (ms) : " << elapsed + << "\nNodes searched : " << nodes + << "\nNodes/second : " << 1000 * nodes / elapsed << endl; +} diff --git a/stockfish-4-win/src_c++11/bitbase.cpp b/stockfish-4-win/src_c++11/bitbase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..570a9771419099aec3bc929b469a1b0b598e61dd --- /dev/null +++ b/stockfish-4-win/src_c++11/bitbase.cpp @@ -0,0 +1,175 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +namespace { + + // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 + const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608 + + // Each uint32_t stores results of 32 positions, one per bit + uint32_t KPKBitbase[IndexMax / 32]; + + // A KPK bitbase index is an integer in [0, IndexMax] range + // + // Information is mapped in a way that minimizes 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 + }; + + inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); } + + struct KPKPosition { + + operator Result() const { return res; } + Result classify_leaf(unsigned idx); + Result classify(const std::vector& db) + { return us == WHITE ? classify(db) : classify(db); } + + private: + template Result classify(const std::vector& db); + + Color us; + Square bksq, wksq, psq; + Result res; + }; + +} // namespace + + +bool Bitbases::probe_kpk(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_kpk() { + + unsigned idx, repeat = 1; + std::vector db(IndexMax); + + // Initialize db with known win / draw positions + for (idx = 0; idx < IndexMax; idx++) + db[idx].classify_leaf(idx); + + // Iterate through the positions until no more of the unknown positions can be + // changed to either wins or draws (15 cycles needed). + while (repeat) + for (repeat = idx = 0; idx < IndexMax; idx++) + if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN) + repeat = 1; + + // Map 32 results into one KPKBitbase[] entry + for (idx = 0; idx < IndexMax; idx++) + if (db[idx] == WIN) + KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); +} + + +namespace { + + Result KPKPosition::classify_leaf(unsigned idx) { + + wksq = Square((idx >> 0) & 0x3F); + bksq = Square((idx >> 6) & 0x3F); + us = Color ((idx >> 12) & 0x01); + psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15)); + + // Check if two pieces are on the same square or if a king can be captured + if ( wksq == psq || wksq == bksq || bksq == psq + || (StepAttacksBB[KING][wksq] & bksq) + || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) + return res = INVALID; + + if (us == WHITE) + { + // Immediate win if pawn can be promoted without getting captured + if ( rank_of(psq) == RANK_7 + && wksq != psq + DELTA_N + && ( square_distance(bksq, psq + DELTA_N) > 1 + ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) + return res = WIN; + } + // Immediate draw if is stalemate or king captures undefended pawn + else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) + || (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) + return res = DRAW; + + return res = 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 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 WIN otherwise the current position is + // classified UNKNOWN. + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Result r = INVALID; + Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; + + while (b) + r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)] + : db[index(Them, pop_lsb(&b), wksq, psq)]; + + if (Us == WHITE && rank_of(psq) < RANK_7) + { + Square s = psq + DELTA_N; + r |= db[index(BLACK, bksq, wksq, s)]; // Single push + + if (rank_of(s) == RANK_3 && s != wksq && s != bksq) + r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push + } + + if (Us == WHITE) + return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW; + else + return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN; + } + +} // namespace diff --git a/stockfish-4-win/src_c++11/bitboard.cpp b/stockfish-4-win/src_c++11/bitboard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9598aa706552f5f38da88c50bc7b734dd6b35919 --- /dev/null +++ b/stockfish-4-win/src_c++11/bitboard.cpp @@ -0,0 +1,339 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitboard.h" +#include "bitcount.h" +#include "misc.h" +#include "rkiss.h" + +CACHE_LINE_ALIGNMENT + +Bitboard RMasks[SQUARE_NB]; +Bitboard RMagics[SQUARE_NB]; +Bitboard* RAttacks[SQUARE_NB]; +unsigned RShifts[SQUARE_NB]; + +Bitboard BMasks[SQUARE_NB]; +Bitboard BMagics[SQUARE_NB]; +Bitboard* BAttacks[SQUARE_NB]; +unsigned BShifts[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 DistanceRingsBB[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]; + +int SquareDistance[SQUARE_NB][SQUARE_NB]; + +namespace { + + // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan + const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL; + const uint32_t DeBruijn_32 = 0x783A9B23; + + CACHE_LINE_ALIGNMENT + + int MS1BTable[256]; + Square BSFTable[SQUARE_NB]; + Bitboard RTable[0x19000]; // Storage space for rook attacks + Bitboard BTable[0x1480]; // Storage space for bishop attacks + + typedef unsigned (Fn)(Square, Bitboard); + + void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], + Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); + + FORCE_INLINE unsigned bsf_index(Bitboard b) { + + // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch + b ^= (b - 1); + return Is64Bit ? (b * DeBruijn_64) >> 58 + : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; + } +} + +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. + +#ifndef USE_BSFQ + +Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } + +Square pop_lsb(Bitboard* b) { + + Bitboard bb = *b; + *b = bb & (bb - 1); + return BSFTable[bsf_index(bb)]; +} + +Square msb(Bitboard 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 + MS1BTable[b32]); +} + +#endif // ifndef USE_BSFQ + + +/// Bitboards::print() prints a bitboard in an easily readable format to the +/// standard output. This is sometimes useful for debugging. + +void Bitboards::print(Bitboard b) { + + sync_cout; + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) + { + std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; + + for (File file = FILE_A; file <= FILE_H; file++) + std::cout << "| " << (b & (file | rank) ? "X " : " "); + + std::cout << "|\n"; + } + std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl; +} + + +/// Bitboards::init() initializes various bitboard arrays. It is called during +/// program initialization. + +void Bitboards::init() { + + for (int k = 0, i = 0; i < 8; i++) + while (k < (2 << i)) + MS1BTable[k++] = i; + + for (int i = 0; i < 64; i++) + BSFTable[bsf_index(1ULL << i)] = Square(i); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + SquareBB[s] = 1ULL << s; + + FileBB[FILE_A] = FileABB; + RankBB[RANK_1] = Rank1BB; + + for (int i = 1; i < 8; i++) + { + FileBB[i] = FileBB[i - 1] << 1; + RankBB[i] = RankBB[i - 1] << 8; + } + + 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++) + { + SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); + if (s1 != s2) + DistanceRingsBB[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 k = 0; steps[pt][k]; k++) + { + Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]); + + if (is_ok(to) && square_distance(s, to) < 3) + StepAttacksBB[make_piece(c, pt)][s] |= to; + } + + Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; + Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; + + init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index); + init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + { + PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb(s, 0); + PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0); + } + + for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) + for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) + if (PseudoAttacks[QUEEN][s1] & s2) + { + Square delta = (s2 - s1) / square_distance(s1, s2); + + for (Square s = s1 + delta; s != s2; s += delta) + BetweenBB[s1][s2] |= s; + } +} + + +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) && square_distance(s, s - deltas[i]) == 1; + s += deltas[i]) + { + attack |= s; + + if (occupied & s) + break; + } + + return attack; + } + + + Bitboard pick_random(RKISS& rk, int booster) { + + // Values s1 and s2 are used to rotate the candidate magic of a + // quantity known to be the optimal to quickly find the magics. + int s1 = booster & 63, s2 = (booster >> 6) & 63; + + Bitboard m = rk.rand(); + m = (m >> s1) | (m << (64 - s1)); + m &= rk.rand(); + m = (m >> s2) | (m << (64 - s2)); + return m & rk.rand(); + } + + + // 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 MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 }, + { 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } }; + RKISS rk; + Bitboard occupancy[4096], reference[4096], edges, b; + int i, size, booster; + + // 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); + 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; + + booster = MagicBoosters[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] = pick_random(rk, booster); + while (popcount((magics[s] * masks[s]) >> 56) < 6); + + std::memset(attacks[s], 0, size * sizeof(Bitboard)); + + // A good magic must map every possible occupancy to an index that + // looks up the correct sliding attack in the attacks[s] database. + // Note that we build up the database for square 's' as a side + // effect of verifying the magic. + for (i = 0; i < size; i++) + { + Bitboard& attack = attacks[s][index(s, occupancy[i])]; + + if (attack && attack != reference[i]) + break; + + assert(reference[i] != 0); + + attack = reference[i]; + } + } while (i != size); + } + } +} diff --git a/stockfish-4-win/src_c++11/bitboard.h b/stockfish-4-win/src_c++11/bitboard.h new file mode 100644 index 0000000000000000000000000000000000000000..bc3ed87b623dab6ae9debb5d89a48df9e3ee68ee --- /dev/null +++ b/stockfish-4-win/src_c++11/bitboard.h @@ -0,0 +1,322 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +namespace Bitboards { + +void init(); +void print(Bitboard b); + +} + +namespace Bitbases { + +void init_kpk(); +bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us); + +} + +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); + +CACHE_LINE_ALIGNMENT + +extern Bitboard RMasks[SQUARE_NB]; +extern Bitboard RMagics[SQUARE_NB]; +extern Bitboard* RAttacks[SQUARE_NB]; +extern unsigned RShifts[SQUARE_NB]; + +extern Bitboard BMasks[SQUARE_NB]; +extern Bitboard BMagics[SQUARE_NB]; +extern Bitboard* BAttacks[SQUARE_NB]; +extern unsigned BShifts[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 DistanceRingsBB[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]; + +extern int SquareDistance[SQUARE_NB][SQUARE_NB]; + +const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; + +/// 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); +} + +inline int square_distance(Square s1, Square s2) { + return SquareDistance[s1][s2]; +} + +inline int file_distance(Square s1, Square s2) { + return abs(file_of(s1) - file_of(s2)); +} + +inline int rank_distance(Square s1, Square s2) { + return abs(rank_of(s1) - rank_of(s2)); +} + + +/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns. + +template +inline Bitboard shift_bb(Bitboard b) { + + return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 + : Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7 + : Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 + : 0; +} + + +/// rank_bb() and file_bb() take a file or a square as input and return +/// a bitboard representing all 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)]; +} + + +/// adjacent_files_bb() takes a file as input and returns a bitboard representing +/// all squares on the adjacent files. + +inline Bitboard adjacent_files_bb(File f) { + return AdjacentFilesBB[f]; +} + + +/// in_front_bb() takes a color and a rank as input, and returns a bitboard +/// representing all the squares on all ranks in front of the rank, from the +/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will +/// give all squares on ranks 1 and 2. + +inline Bitboard in_front_bb(Color c, Rank r) { + return InFrontBB[c][r]; +} + + +/// between_bb() returns a bitboard representing all squares between two squares. +/// 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 line, file or diagonal, +/// 0 is returned. + +inline Bitboard between_bb(Square s1, Square s2) { + return BetweenBB[s1][s2]; +} + + +/// forward_bb() takes a color and a square as input, and returns a bitboard +/// representing all squares along the line in front of the square, from the +/// point of view of the given color. Definition of the table is: +/// 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() takes a color and a square as input, and returns a bitboard +/// representing all squares that can be attacked by a pawn of the given color +/// when it moves along its file starting from the given square. Definition is: +/// 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() takes a color and a square as input, and returns a +/// bitboard mask which can be used to test if a pawn of the given color on +/// the given square is a passed pawn. Definition of the table is: +/// 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]; +} + + +/// squares_of_color() returns a bitboard representing all squares with the same +/// color of the given square. + +inline Bitboard squares_of_color(Square s) { + return DarkSquares & s ? DarkSquares : ~DarkSquares; +} + + +/// squares_aligned() returns true if the squares s1, s2 and s3 are aligned +/// either on a straight or on a diagonal line. + +inline bool squares_aligned(Square s1, Square s2, Square s3) { + return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) + & ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]); +} + + +/// Functions for computing sliding attack bitboards. Function attacks_bb() takes +/// a square and a bitboard of occupied squares as input, and returns a bitboard +/// representing all squares attacked by Pt (bishop or rook) on the given square. +template +FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) { + + Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks; + Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics; + unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts; + + if (Is64Bit) + return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]); + + unsigned lo = unsigned(occ) & unsigned(Masks[s]); + unsigned hi = unsigned(occ >> 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 occ) { + return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index(s, occ)]; +} + + +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. + +#ifdef USE_BSFQ + +# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + +FORCE_INLINE Square lsb(Bitboard b) { + unsigned long index; + _BitScanForward64(&index, b); + return (Square) index; +} + +FORCE_INLINE Square msb(Bitboard b) { + unsigned long index; + _BitScanReverse64(&index, b); + return (Square) index; +} + +# elif defined(__arm__) + +FORCE_INLINE int lsb32(uint32_t v) { + __asm__("rbit %0, %1" : "=r"(v) : "r"(v)); + return __builtin_clz(v); +} + +FORCE_INLINE Square msb(Bitboard b) { + return (Square) (63 - __builtin_clzll(b)); +} + +FORCE_INLINE Square lsb(Bitboard b) { + return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32))); +} + +# else + +FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen + Bitboard index; + __asm__("bsfq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; +} + +FORCE_INLINE Square msb(Bitboard b) { + Bitboard index; + __asm__("bsrq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; +} + +# endif + +FORCE_INLINE Square pop_lsb(Bitboard* b) { + const Square s = lsb(*b); + *b &= *b - 1; + return s; +} + +#else // if defined(USE_BSFQ) + +extern Square msb(Bitboard b); +extern Square lsb(Bitboard b); +extern Square pop_lsb(Bitboard* b); + +#endif + +#endif // #ifndef BITBOARD_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/bitcount.h b/stockfish-4-win/src_c++11/bitcount.h new file mode 100644 index 0000000000000000000000000000000000000000..ad8df94ab078522066fdfa44da80c931fa732c88 --- /dev/null +++ b/stockfish-4-win/src_c++11/bitcount.h @@ -0,0 +1,105 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 BITCOUNT_H_INCLUDED +#define BITCOUNT_H_INCLUDED + +#include +#include "types.h" + +enum BitCountType { + CNT_64, + CNT_64_MAX15, + CNT_32, + CNT_32_MAX15, + CNT_HW_POPCNT +}; + +/// Determine at compile time the best popcount<> specialization according if +/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count +/// and if hardware popcnt instruction is available. +const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; +const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15; + + +/// popcount() counts the number of nonzero bits in a bitboard +template inline int popcount(Bitboard); + +template<> +inline int popcount(Bitboard b) { + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL; + return (b * 0x0101010101010101ULL) >> 56; +} + +template<> +inline int popcount(Bitboard b) { + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + return (b * 0x1111111111111111ULL) >> 60; +} + +template<> +inline int popcount(Bitboard b) { + unsigned w = unsigned(b >> 32), v = unsigned(b); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F; + return (v * 0x01010101) >> 24; +} + +template<> +inline int popcount(Bitboard b) { + unsigned w = unsigned(b >> 32), v = unsigned(b); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + return ((v + w) * 0x11111111) >> 28; +} + +template<> +inline int popcount(Bitboard b) { + +#ifndef USE_POPCNT + + assert(false); + return b != 0; // Avoid 'b not used' warning + +#elif defined(_MSC_VER) && defined(__INTEL_COMPILER) + + return _mm_popcnt_u64(b); + +#elif defined(_MSC_VER) + + return (int)__popcnt64(b); + +#else + + __asm__("popcnt %1, %0" : "=r" (b) : "r" (b)); + return b; + +#endif +} + +#endif // #ifndef BITCOUNT_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/book.cpp b/stockfish-4-win/src_c++11/book.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c41022834abc0217bacfc802e23c3cbdfe9dfd20 --- /dev/null +++ b/stockfish-4-win/src_c++11/book.cpp @@ -0,0 +1,478 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 . +*/ + +/* + The code in this file is based on the opening book code in PolyGlot + by Fabien Letouzey. PolyGlot is available under the GNU General + Public License, and can be downloaded from http://wbec-ridderkerk.nl +*/ + +#include +#include +#include + +#include "book.h" +#include "misc.h" +#include "movegen.h" + +using namespace std; + +namespace { + + // A Polyglot book is a series of "entries" of 16 bytes. All integers are + // stored in big-endian format, with highest byte first (regardless of size). + // The entries are ordered according to the key in ascending order. + struct Entry { + uint64_t key; + uint16_t move; + uint16_t count; + uint32_t learn; + }; + + // Random numbers from PolyGlot, used to compute book hash keys + const union { + Key PolyGlotRandoms[781]; + struct { + Key psq[12][64]; // [piece][square] + Key castle[4]; // [castle right] + Key enpassant[8]; // [file] + Key turn; + } Zobrist; + } PG = {{ + 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, + 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, + 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, + 0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL, + 0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL, + 0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL, + 0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL, + 0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL, + 0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL, + 0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL, + 0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL, + 0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL, + 0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL, + 0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL, + 0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL, + 0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL, + 0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL, + 0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL, + 0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL, + 0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL, + 0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL, + 0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL, + 0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL, + 0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL, + 0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL, + 0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL, + 0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL, + 0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL, + 0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL, + 0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL, + 0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL, + 0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL, + 0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL, + 0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL, + 0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL, + 0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL, + 0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL, + 0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL, + 0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL, + 0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL, + 0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL, + 0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL, + 0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL, + 0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL, + 0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL, + 0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL, + 0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL, + 0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL, + 0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL, + 0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL, + 0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL, + 0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL, + 0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL, + 0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL, + 0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL, + 0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL, + 0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL, + 0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL, + 0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL, + 0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL, + 0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL, + 0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL, + 0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL, + 0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL, + 0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL, + 0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL, + 0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL, + 0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL, + 0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL, + 0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL, + 0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL, + 0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL, + 0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL, + 0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL, + 0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL, + 0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL, + 0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL, + 0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL, + 0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL, + 0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL, + 0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL, + 0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL, + 0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL, + 0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL, + 0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL, + 0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL, + 0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL, + 0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL, + 0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL, + 0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL, + 0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL, + 0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL, + 0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL, + 0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL, + 0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL, + 0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL, + 0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL, + 0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL, + 0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL, + 0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL, + 0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL, + 0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL, + 0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL, + 0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL, + 0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL, + 0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL, + 0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL, + 0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL, + 0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL, + 0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL, + 0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL, + 0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL, + 0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL, + 0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL, + 0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL, + 0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL, + 0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL, + 0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL, + 0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL, + 0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL, + 0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL, + 0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL, + 0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL, + 0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL, + 0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL, + 0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL, + 0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL, + 0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL, + 0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL, + 0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL, + 0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL, + 0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL, + 0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL, + 0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL, + 0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL, + 0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL, + 0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL, + 0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL, + 0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL, + 0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL, + 0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL, + 0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL, + 0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL, + 0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL, + 0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL, + 0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL, + 0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL, + 0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL, + 0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL, + 0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL, + 0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL, + 0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL, + 0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL, + 0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL, + 0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL, + 0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL, + 0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL, + 0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL, + 0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL, + 0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL, + 0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL, + 0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL, + 0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL, + 0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL, + 0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL, + 0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL, + 0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL, + 0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL, + 0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL, + 0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL, + 0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL, + 0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL, + 0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL, + 0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL, + 0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL, + 0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL, + 0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL, + 0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL, + 0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL, + 0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL, + 0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL, + 0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL, + 0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL, + 0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL, + 0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL, + 0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL, + 0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL, + 0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL, + 0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL, + 0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL, + 0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL, + 0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL, + 0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL, + 0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL, + 0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL, + 0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL, + 0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL, + 0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL, + 0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL, + 0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL, + 0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL, + 0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL, + 0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL, + 0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL, + 0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL, + 0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL, + 0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL, + 0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL, + 0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL, + 0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL, + 0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL, + 0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL, + 0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL, + 0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL, + 0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL, + 0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL, + 0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL, + 0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL, + 0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL, + 0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL, + 0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL, + 0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL, + 0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL, + 0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL, + 0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL, + 0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL, + 0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL, + 0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL, + 0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL, + 0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL, + 0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL, + 0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL, + 0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL, + 0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL, + 0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL, + 0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL, + 0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL, + 0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL, + 0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL, + 0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL, + 0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL, + 0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL, + 0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL, + 0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL, + 0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL, + 0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL, + 0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL, + 0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL, + 0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL, + 0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL, + 0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL, + 0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL, + 0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL, + 0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL, + 0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL, + 0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL, + 0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL, + 0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL, + 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL, + 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL, + 0xF8D626AAAF278509ULL + }}; + + // polyglot_key() returns the PolyGlot hash key of the given position + Key polyglot_key(const Position& pos) { + + Key key = 0; + Bitboard b = pos.pieces(); + + while (b) + { + Square s = pop_lsb(&b); + Piece p = pos.piece_on(s); + + // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 + key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s]; + } + + b = pos.can_castle(ALL_CASTLES); + + while (b) + key ^= PG.Zobrist.castle[pop_lsb(&b)]; + + if (pos.ep_square() != SQ_NONE) + key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())]; + + if (pos.side_to_move() == WHITE) + key ^= PG.Zobrist.turn; + + return key; + } + +} // namespace + +PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {} + +PolyglotBook::~PolyglotBook() { if (is_open()) close(); } + + +/// operator>>() reads sizeof(T) chars from the file's binary byte stream and +/// converts them in a number of type T. A Polyglot book stores numbers in +/// big-endian format. + +template PolyglotBook& PolyglotBook::operator>>(T& n) { + + n = 0; + for (size_t i = 0; i < sizeof(T); i++) + n = T((n << 8) + ifstream::get()); + + return *this; +} + +template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) { + return *this >> e.key >> e.move >> e.count >> e.learn; +} + + +/// open() tries to open a book file with the given name after closing any +/// exsisting one. + +bool PolyglotBook::open(const string& fName) { + + if (is_open()) // Cannot close an already closed file + close(); + + ifstream::open(fName, ifstream::in | ifstream::binary); + + fileName = is_open() ? fName : ""; + ifstream::clear(); // Reset any error flag to allow retry ifstream::open() + return !fileName.empty(); +} + + +/// probe() tries to find a book move for the given position. If no move is +/// found returns MOVE_NONE. If pickBest is true returns always the highest +/// rated move, otherwise randomly chooses one, based on the move score. + +Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) { + + if (fileName != fName && !open(fName)) + return MOVE_NONE; + + Entry e; + uint16_t best = 0; + unsigned sum = 0; + Move move = MOVE_NONE; + Key key = polyglot_key(pos); + + seekg(find_first(key) * sizeof(Entry), ios_base::beg); + + while (*this >> e, e.key == key && good()) + { + best = max(best, e.count); + sum += e.count; + + // Choose book move according to its score. If a move has a very + // high score it has higher probability to be choosen than a move + // with lower score. Note that first entry is always chosen. + if ( (sum && rkiss.rand() % sum < e.count) + || (pickBest && e.count == best)) + move = Move(e.move); + } + + if (!move) + return MOVE_NONE; + + // A PolyGlot book move is encoded as follows: + // + // bit 0- 5: destination square (from 0 to 63) + // bit 6-11: origin square (from 0 to 63) + // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) + // + // Castling moves follow "king captures rook" representation. So in case book + // move is a promotion we have to convert to our representation, in all the + // other cases we can directly compare with a Move after having masked out + // the special Move's flags (bit 14-15) that are not supported by PolyGlot. + int pt = (move >> 12) & 7; + if (pt) + move = make(from_sq(move), to_sq(move), PieceType(pt + 1)); + + // Add 'special move' flags and verify it is legal + for (const ExtMove& ms : MoveList(pos)) + if (move == (ms.move ^ type_of(ms.move))) + return ms.move; + + return MOVE_NONE; +} + + +/// find_first() takes a book key as input, and does a binary search through +/// the book file for the given key. Returns the index of the leftmost book +/// entry with the same key as the input. + +size_t PolyglotBook::find_first(Key key) { + + seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size + + size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1; + Entry e; + + assert(low <= high); + + while (low < high && good()) + { + mid = (low + high) / 2; + + assert(mid >= low && mid < high); + + seekg(mid * sizeof(Entry), ios_base::beg); + *this >> e; + + if (key <= e.key) + high = mid; + else + low = mid + 1; + } + + assert(low == high); + + return low; +} diff --git a/stockfish-4-win/src_c++11/book.h b/stockfish-4-win/src_c++11/book.h new file mode 100644 index 0000000000000000000000000000000000000000..68717b289484c6d479ac8c3d0092bdb7dc79f764 --- /dev/null +++ b/stockfish-4-win/src_c++11/book.h @@ -0,0 +1,45 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 BOOK_H_INCLUDED +#define BOOK_H_INCLUDED + +#include +#include + +#include "position.h" +#include "rkiss.h" + +class PolyglotBook : private std::ifstream { +public: + PolyglotBook(); + ~PolyglotBook(); + Move probe(const Position& pos, const std::string& fName, bool pickBest); + +private: + template PolyglotBook& operator>>(T& n); + + bool open(const std::string& fName); + size_t find_first(Key key); + + RKISS rkiss; + std::string fileName; +}; + +#endif // #ifndef BOOK_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/endgame.cpp b/stockfish-4-win/src_c++11/endgame.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa7f61fd0afa6cdc188c3fbbeb63f2b13075879b --- /dev/null +++ b/stockfish-4-win/src_c++11/endgame.cpp @@ -0,0 +1,956 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "endgame.h" +#include "movegen.h" + +using std::string; + +namespace { + + // Table used to drive the defending king towards the edge of the board + // in KX vs K and KQ vs KR endgames. + const int MateTable[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 defending king towards a corner square of the + // right color in KBN vs K endgames. + const int KBNKMateTable[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 + }; + + // The attacking side is given a descending bonus based on distance between + // the two kings in basic endgames. + const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + + // Get the material key of a 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 to do the work for us. Note that the + // fen string could correspond to an illegal position. + 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)), // Weaker + code.substr(0, code.find('K', 1)) }; // Stronger + + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); + + string fen = sides[0] + char('0' + int(8 - code.length())) + + sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; + + return Position(fen, false, nullptr).material_key(); + } + +} // namespace + + +/// Endgames members definitions + +Endgames::Endgames() { + + add("KK"); + add("KPK"); + add("KBK"); + add("KNK"); + add("KNNK"); + add("KBNK"); + add("KRKP"); + add("KRKB"); + add("KRKN"); + add("KQKP"); + add("KQKR"); + add("KBBKN"); + + add("KNPK"); + add("KNPKB"); + add("KRPKR"); + 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(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(!pos.count(weakerSide)); + assert(!pos.checkers()); // Eval is never called when in check + + // Stalemate detection with lone king + if (pos.side_to_move() == weakerSide && !MoveList(pos).size()) + return VALUE_DRAW; + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + + Value result = pos.non_pawn_material(strongerSide) + + pos.count(strongerSide) * PawnValueEg + + MateTable[loserKSq] + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( pos.count(strongerSide) + || pos.count(strongerSide) + || pos.bishop_pair(strongerSide)) + result += VALUE_KNOWN_WIN; + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the +/// defending king towards a corner square of the right color. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 0); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongerSide)[0]; + + // kbnk_mate_table() tries to drive toward corners A1 or H8, + // if we have a bishop that cannot reach the above squares we + // mirror the kings so to drive enemy toward corners A8 or H1. + if (opposite_colors(bishopSq, SQ_A1)) + { + winnerKSq = mirror(winnerKSq); + loserKSq = mirror(loserKSq); + } + + Value result = VALUE_KNOWN_WIN + + DistanceBonus[square_distance(winnerKSq, loserKSq)] + + KBNKMateTable[loserKSq]; + + return strongerSide == 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(pos.non_pawn_material(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); + + Square wksq, bksq, wpsq; + Color us; + + if (strongerSide == WHITE) + { + wksq = pos.king_square(WHITE); + bksq = pos.king_square(BLACK); + wpsq = pos.list(WHITE)[0]; + us = pos.side_to_move(); + } + else + { + wksq = ~pos.king_square(BLACK); + bksq = ~pos.king_square(WHITE); + wpsq = ~pos.list(BLACK)[0]; + us = ~pos.side_to_move(); + } + + if (file_of(wpsq) >= FILE_E) + { + wksq = mirror(wksq); + bksq = mirror(bksq); + wpsq = mirror(wpsq); + } + + if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us)) + return VALUE_DRAW; + + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); + + return strongerSide == 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == 0); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); + + Square wksq, wrsq, bksq, bpsq; + int tempo = (pos.side_to_move() == strongerSide); + + wksq = pos.king_square(strongerSide); + bksq = pos.king_square(weakerSide); + wrsq = pos.list(strongerSide)[0]; + bpsq = pos.list(weakerSide)[0]; + + if (strongerSide == BLACK) + { + wksq = ~wksq; + wrsq = ~wrsq; + bksq = ~bksq; + bpsq = ~bpsq; + } + + Square queeningSq = file_of(bpsq) | RANK_1; + Value result; + + // If the stronger side's king is in front of the pawn, it's a win + if (wksq < bpsq && file_of(wksq) == file_of(bpsq)) + result = RookValueEg - Value(square_distance(wksq, bpsq)); + + // If the weaker side's king is too far from the pawn and the rook, + // it's a win + else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3 + && square_distance(bksq, wrsq) >= 3) + result = RookValueEg - Value(square_distance(wksq, bpsq)); + + // If the pawn is far advanced and supported by the defending king, + // the position is drawish + else if ( rank_of(bksq) <= RANK_3 + && square_distance(bksq, bpsq) == 1 + && rank_of(wksq) >= RANK_4 + && square_distance(wksq, bpsq) - tempo > 2) + result = Value(80 - square_distance(wksq, bpsq) * 8); + + else + result = Value(200) + - Value(square_distance(wksq, bpsq + DELTA_S) * 8) + + Value(square_distance(bksq, bpsq + DELTA_S) * 8) + + Value(square_distance(bpsq, queeningSq) * 8); + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KR vs KB. This is very simple, and always returns drawish scores. The +/// score is slightly bigger when the defending king is close to the edge. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + assert(pos.count< PAWN>(strongerSide) == 0); + + Value result = Value(MateTable[pos.king_square(weakerSide)]); + return strongerSide == 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + assert(pos.count< PAWN>(strongerSide) == 0); + + const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; + + Square bksq = pos.king_square(weakerSide); + Square bnsq = pos.list(weakerSide)[0]; + Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KQ vs KP. In general, a win for the stronger side, however, there are a few +/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can +/// be a draw, so we scale down to distance between kings only. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(weakerSide)[0]; + + Value result = QueenValueEg + - PawnValueEg + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( square_distance(loserKSq, pawnSq) == 1 + && relative_rank(weakerSide, pawnSq) == RANK_7) + { + File f = file_of(pawnSq); + + if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H) + result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]); + } + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// KQ vs KR. This is almost identical to KX vs K: We give the attacking +/// king a bonus for having the kings close together, and for forcing the +/// defending king towards the edge. If we also take care to avoid null move +/// for the defending side in the search, this is usually sufficient to be +/// able to win KQ vs KR. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.non_pawn_material(weakerSide ) == RookValueMg); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 0); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + + Value result = QueenValueEg + - RookValueEg + + MateTable[loserKSq] + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + return strongerSide == pos.side_to_move() ? result : -result; +} + +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(strongerSide) == 2); + assert(pos.count(weakerSide ) == 1); + assert(!pos.pieces(PAWN)); + + Value result = BishopValueEg; + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square nsq = pos.list(weakerSide)[0]; + + // Bonus for attacking king close to defending king + result += Value(DistanceBonus[square_distance(wksq, bksq)]); + + // Bonus for driving the defending king and knight apart + result += Value(square_distance(bksq, nsq) * 32); + + // Bonus for restricting the knight's mobility + result += Value((8 - popcount(pos.attacks_from(nsq))) * 8); + + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// Some cases of trivial draws +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } +template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } + + +/// K, bishop 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(strongerSide) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) >= 1); + + // No assertions about the material of weakerSide, because we want draws to + // be detected even when the weaker side has some pawns. + + Bitboard pawns = pos.pieces(strongerSide, PAWN); + File pawnFile = file_of(pos.list(strongerSide)[0]); + + // All pawns are on a single rook file ? + if ( (pawnFile == FILE_A || pawnFile == FILE_H) + && !(pawns & ~file_bb(pawnFile))) + { + Square bishopSq = pos.list(strongerSide)[0]; + Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); + Square kingSq = pos.king_square(weakerSide); + + if ( opposite_colors(queeningSq, bishopSq) + && abs(file_of(kingSq) - pawnFile) <= 1) + { + // The bishop has the wrong color, and the defending king is on the + // file of the pawn(s) or the adjacent file. Find the rank of the + // frontmost pawn. + Rank rank; + if (strongerSide == WHITE) + { + for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {} + assert(rank >= RANK_2 && rank <= RANK_7); + } + else + { + for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {} + rank = Rank(rank ^ 7); // HACK to get the relative rank + assert(rank >= RANK_2 && rank <= RANK_7); + } + // If the defending king has distance 1 to the promotion square or + // is placed somewhere in front of the pawn, it's a draw. + if ( square_distance(kingSq, queeningSq) <= 1 + || relative_rank(strongerSide, kingSq) >= rank) + return SCALE_FACTOR_DRAW; + } + } + + // All pawns on same B or G file? Then potential draw + if ( (pawnFile == FILE_B || pawnFile == FILE_G) + && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) + && pos.non_pawn_material(weakerSide) == 0 + && pos.count(weakerSide) >= 1) + { + // Get weaker pawn closest to opponent's queening square + Bitboard wkPawns = pos.pieces(weakerSide, PAWN); + Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns); + + Square strongerKingSq = pos.king_square(strongerSide); + Square weakerKingSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongerSide)[0]; + + // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and + // weaker king can stop opposing opponent's king from penetrating. + if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 + && opposite_colors(bishopSq, weakerPawnSq) + && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) + return SCALE_FACTOR_DRAW; + } + + return SCALE_FACTOR_NONE; +} + + +/// K and queen vs K, rook 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(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 0); + assert(pos.count< ROOK>(weakerSide ) == 1); + assert(pos.count< PAWN>(weakerSide ) >= 1); + + Square kingSq = pos.king_square(weakerSide); + if ( relative_rank(weakerSide, kingSq) <= RANK_2 + && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 + && (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3))) + && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) + && (pos.attacks_from(kingSq) & pos.pieces(weakerSide, PAWN))) + { + Square rsq = pos.list(weakerSide)[0]; + if (pos.attacks_from(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; +} + + +/// K, rook and one pawn vs K and a rook. This function knows a handful of the +/// most important classes of drawn positions, but is far from perfect. It would +/// probably be a good idea to add more knowledge in the future. +/// +/// It would also be nice to rewrite the actual code for this function, +/// which is mostly copied from Glaurung 1.x, and not very pretty. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); + + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square wrsq = pos.list(strongerSide)[0]; + Square wpsq = pos.list(strongerSide)[0]; + Square brsq = pos.list(weakerSide)[0]; + + // Orient the board in such a way that the stronger side is white, and the + // pawn is on the left half of the board. + if (strongerSide == BLACK) + { + wksq = ~wksq; + wrsq = ~wrsq; + wpsq = ~wpsq; + bksq = ~bksq; + brsq = ~brsq; + } + if (file_of(wpsq) > FILE_D) + { + wksq = mirror(wksq); + wrsq = mirror(wrsq); + wpsq = mirror(wpsq); + bksq = mirror(bksq); + brsq = mirror(brsq); + } + + File f = file_of(wpsq); + Rank r = rank_of(wpsq); + Square queeningSq = f | RANK_8; + int tempo = (pos.side_to_move() == strongerSide); + + // If the pawn is not too far advanced and the defending king defends the + // queening square, use the third-rank defence. + if ( r <= RANK_5 + && square_distance(bksq, queeningSq) <= 1 + && wksq <= SQ_H5 + && (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 + && square_distance(bksq, queeningSq) <= 1 + && rank_of(wksq) + tempo <= RANK_6 + && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3))) + return SCALE_FACTOR_DRAW; + + if ( r >= RANK_6 + && bksq == queeningSq + && rank_of(brsq) == RANK_1 + && (!tempo || square_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 + DELTA_N + && square_distance(wksq, wpsq) - tempo >= 2 + && square_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 + && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) + && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq)); + + // Similar to the above, but with the pawn further back + if ( f != FILE_A + && file_of(wrsq) == f + && wrsq < wpsq + && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) + && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) + && ( square_distance(bksq, wrsq) + tempo >= 3 + || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo + && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) + return ScaleFactor( SCALE_FACTOR_MAX + - 8 * square_distance(wpsq, queeningSq) + - 2 * square_distance(wksq, queeningSq)); + + // If the pawn is not far advanced, and the defending king is somewhere in + // the pawn's path, it's probably a draw. + if (r <= RANK_4 && bksq > wpsq) + { + if (file_of(bksq) == file_of(wpsq)) + return ScaleFactor(10); + if ( abs(file_of(bksq) - file_of(wpsq)) == 1 + && square_distance(wksq, bksq) > 2) + return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); + } + return SCALE_FACTOR_NONE; +} + + +/// K, rook and two pawns vs K, rook and one pawn. There is only a single +/// pattern: 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(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 2); + assert(pos.count(weakerSide ) == 1); + + Square wpsq1 = pos.list(strongerSide)[0]; + Square wpsq2 = pos.list(strongerSide)[1]; + Square bksq = pos.king_square(weakerSide); + + // Does the stronger side have a passed pawn? + if ( pos.pawn_is_passed(strongerSide, wpsq1) + || pos.pawn_is_passed(strongerSide, wpsq2)) + return SCALE_FACTOR_NONE; + + Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2)); + + if ( file_distance(bksq, wpsq1) <= 1 + && file_distance(bksq, wpsq2) <= 1 + && relative_rank(strongerSide, bksq) > r) + { + switch (r) { + case RANK_2: return ScaleFactor(10); + case RANK_3: return ScaleFactor(10); + case RANK_4: return ScaleFactor(15); + case RANK_5: return ScaleFactor(20); + case RANK_6: return ScaleFactor(40); + default: assert(false); + } + } + return SCALE_FACTOR_NONE; +} + + +/// 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(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) >= 2); + assert(pos.count(weakerSide ) == 0); + + Square ksq = pos.king_square(weakerSide); + Bitboard pawns = pos.pieces(strongerSide, PAWN); + + // Are all pawns on the 'a' file? + if (!(pawns & ~FileABB)) + { + // Does the defending king block the pawns? + if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 + || ( file_of(ksq) == FILE_A + && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + return SCALE_FACTOR_DRAW; + } + // Are all pawns on the 'h' file? + else if (!(pawns & ~FileHBB)) + { + // Does the defending king block the pawns? + if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 + || ( file_of(ksq) == FILE_H + && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; +} + + +/// K, bishop and a pawn vs K and a bishop. 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(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square strongerBishopSq = pos.list(strongerSide)[0]; + Square weakerBishopSq = pos.list(weakerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + // Case 1: Defending king blocks the pawn, and cannot be driven away + if ( file_of(weakerKingSq) == file_of(pawnSq) + && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) + && ( opposite_colors(weakerKingSq, strongerBishopSq) + || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + return SCALE_FACTOR_DRAW; + + // Case 2: Opposite colored bishops + if (opposite_colors(strongerBishopSq, weakerBishopSq)) + { + // We assume that the position is drawn in the following three situations: + // + // a. The pawn is on rank 5 or further back. + // b. The defending king is somewhere in the pawn's path. + // c. The defending bishop attacks some square along the pawn's path, + // and is at least three squares away from the pawn. + // + // These rules are probably not perfect, but in practice they work + // reasonably well. + + if (relative_rank(strongerSide, pawnSq) <= RANK_5) + return SCALE_FACTOR_DRAW; + else + { + Bitboard path = forward_bb(strongerSide, pawnSq); + + if (path & pos.pieces(weakerSide, KING)) + return SCALE_FACTOR_DRAW; + + if ( (pos.attacks_from(weakerBishopSq) & path) + && square_distance(weakerBishopSq, pawnSq) >= 3) + return SCALE_FACTOR_DRAW; + } + } + return SCALE_FACTOR_NONE; +} + + +/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with +/// opposite-colored bishops. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 2); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square wbsq = pos.list(strongerSide)[0]; + Square bbsq = pos.list(weakerSide)[0]; + + if (!opposite_colors(wbsq, bbsq)) + return SCALE_FACTOR_NONE; + + Square ksq = pos.king_square(weakerSide); + Square psq1 = pos.list(strongerSide)[0]; + Square psq2 = pos.list(strongerSide)[1]; + Rank r1 = rank_of(psq1); + Rank r2 = rank_of(psq2); + Square blockSq1, blockSq2; + + if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) + { + blockSq1 = psq1 + pawn_push(strongerSide); + blockSq2 = file_of(psq2) | rank_of(psq1); + } + else + { + blockSq1 = psq2 + pawn_push(strongerSide); + blockSq2 = file_of(psq1) | rank_of(psq2); + } + + switch (file_distance(psq1, psq2)) + { + case 0: + // Both pawns are on the same file. Easy draw if defender firmly controls + // some square in the frontmost pawn's path. + if ( file_of(ksq) == file_of(blockSq1) + && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) + && opposite_colors(ksq, wbsq)) + return SCALE_FACTOR_DRAW; + else + return SCALE_FACTOR_NONE; + + case 1: + // Pawns on adjacent files. Draw if 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(weakerSide, BISHOP)) + || abs(r1 - r2) >= 2)) + return SCALE_FACTOR_DRAW; + + else if ( ksq == blockSq2 + && opposite_colors(ksq, wbsq) + && ( bbsq == blockSq1 + || (pos.attacks_from(blockSq1) & pos.pieces(weakerSide, 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; + } +} + + +/// K, bisop and a pawn vs K and knight. 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(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square strongerBishopSq = pos.list(strongerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + if ( file_of(weakerKingSq) == file_of(pawnSq) + && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) + && ( opposite_colors(weakerKingSq, strongerBishopSq) + || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + return SCALE_FACTOR_DRAW; + + return SCALE_FACTOR_NONE; +} + + +/// K, knight and a pawn 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(pos.non_pawn_material(strongerSide) == KnightValueMg); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 1); + assert(pos.count< PAWN>(weakerSide ) == 0); + + Square pawnSq = pos.list(strongerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + if ( pawnSq == relative_square(strongerSide, SQ_A7) + && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) + return SCALE_FACTOR_DRAW; + + if ( pawnSq == relative_square(strongerSide, SQ_H7) + && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) + return SCALE_FACTOR_DRAW; + + return SCALE_FACTOR_NONE; +} + + +/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking +/// pawn, it's a win. Otherwise, drawn. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + Square pawnSq = pos.list(strongerSide)[0]; + Square bishopSq = pos.list(weakerSide)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + // 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(strongerSide, pawnSq) & pos.attacks_from(bishopSq)) + return ScaleFactor(square_distance(weakerKingSq, pawnSq)); + + return SCALE_FACTOR_NONE; +} + + +/// K and a pawn vs K and a pawn. This is done by removing the weakest side's +/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without +/// the pawn, she probably has at least a draw with the pawn as well. The exception +/// is when the stronger side's pawn is far advanced and not on a rook file; in +/// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(WHITE) == 1); + assert(pos.count(BLACK) == 1); + + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square wpsq = pos.list(strongerSide)[0]; + Color us = pos.side_to_move(); + + if (strongerSide == BLACK) + { + wksq = ~wksq; + bksq = ~bksq; + wpsq = ~wpsq; + us = ~us; + } + + if (file_of(wpsq) >= FILE_E) + { + wksq = mirror(wksq); + bksq = mirror(bksq); + wpsq = mirror(wpsq); + } + + // If the pawn has advanced to the fifth rank or further, and is not a + // rook pawn, it's too dangerous to assume that it's at least a draw. + if ( rank_of(wpsq) >= RANK_5 + && file_of(wpsq) != FILE_A) + return SCALE_FACTOR_NONE; + + // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, + // it's probably at least a draw even with the pawn. + return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; +} diff --git a/stockfish-4-win/src_c++11/endgame.h b/stockfish-4-win/src_c++11/endgame.h new file mode 100644 index 0000000000000000000000000000000000000000..23b0f33e6425d7baafd48b8a840776fb486379ec --- /dev/null +++ b/stockfish-4-win/src_c++11/endgame.h @@ -0,0 +1,123 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + + KK, // K vs K + KBK, // KB vs K + KNK, // KN vs K + 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 + KBBKN, // KBB vs KN + KmmKm, // K and two minors vs K and one or two minors + + + // Scaling functions + SCALE_FUNS, + + KBPsK, // KB+pawns vs K + KQKRPs, // KQ vs KR+pawns + KRPKR, // KRP vs KR + KRPPKRP, // KRPP vs KRP + KPsK, // King and pawns vs king + 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 return a Value or a ScaleFactor, according to EndgameType +template +using eg_fun = std::conditional<(E < SCALE_FUNS), Value, ScaleFactor>; + + +/// Base and derived templates for endgame evaluation and scaling functions + +template +struct EndgameBase { + + virtual ~EndgameBase() {} + virtual Color color() const = 0; + virtual T operator()(const Position&) const = 0; +}; + + +template::type> +struct Endgame : public EndgameBase { + + explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {} + Color color() const { return strongerSide; } + T operator()(const Position&) const; + +private: + Color strongerSide, weakerSide; +}; + + +/// Endgames class stores in two std::map the std::unique_ptr to endgame +/// evaluation and scaling base objects. Then we use polymorphism to invoke +/// the actual endgame function calling its operator() that is virtual. + +class Endgames { + + template using Map = std::map>; + + template::type>> + void add(const std::string& code); + + template>::value> + Map& map() { return std::get(maps); } + + std::pair>, Map>> maps; + +public: + Endgames(); + + template T* probe(Key key, T** eg) + { return *eg = map().count(key) ? map()[key].get() : nullptr; } +}; + +#endif // #ifndef ENDGAME_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/evaluate.cpp b/stockfish-4-win/src_c++11/evaluate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12634ddd56b0a41ec937d88e3073dd9e623c378d --- /dev/null +++ b/stockfish-4-win/src_c++11/evaluate.cpp @@ -0,0 +1,1185 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "evaluate.h" +#include "material.h" +#include "pawns.h" +#include "thread.h" +#include "ucioption.h" + +namespace { + + enum ExtendedPieceType { // Used for tracing + PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL + }; + + namespace Tracing { + + Score scores[COLOR_NB][TOTAL + 1]; + std::stringstream stream; + + void add(int idx, Score term_w, Score term_b = SCORE_ZERO); + void row(const char* name, int idx); + std::string do_trace(const Position& pos); + } + + // Struct EvalInfo contains various information computed and collected + // by the evaluation functions. + struct EvalInfo { + + // Pointers to material and pawn hash table entries + Material::Entry* mi; + Pawns::Entry* pi; + + // attackedBy[color][piece type] is a bitboard representing all squares + // attacked by a given color and piece type, attackedBy[color][ALL_PIECES] + // contains all squares attacked by the given color. + Bitboard attackedBy[COLOR_NB][PIECE_TYPE_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 "weight" 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 variables + // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and + // KnightAttackWeight in evaluate.cpp + int kingAttackersWeight[COLOR_NB]; + + // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares + // directly adjacent to the king of the given color. Pieces which attack + // more than one square are counted multiple times. For instance, if black's + // king is on g8 and there's a white knight on g5, this knight adds + // 2 to kingAdjacentZoneAttacksCount[BLACK]. + int kingAdjacentZoneAttacksCount[COLOR_NB]; + }; + + // Evaluation grain size, must be a power of 2 + const int GrainSize = 4; + + // Evaluation weights, initialized from UCI options + enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem }; + Score Weights[6]; + + typedef Value V; + #define S(mg, eg) make_score(mg, eg) + + // Internal evaluation weights. These are applied on top of the evaluation + // weights read from UCI parameters. The purpose is to be able to change + // the evaluation weights while keeping the default values of the UCI + // parameters at 100, which looks prettier. + // + // Values modified by Joona Kiiski + const Score WeightsInternal[] = { + S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0) + }; + + // MobilityBonus[PieceType][attacked] contains bonuses for middle and end + // game, indexed by piece type and number of attacked squares not occupied by + // friendly pieces. + const Score MobilityBonus[][32] = { + {}, {}, + { S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights + S( 37, 28), S( 42, 31), S(44, 33) }, + { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops + S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77), + S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) }, + { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks + S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119), + S( 35,122), S( 36,123), S(37,124), S(38,124) }, + { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens + S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40), + S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), + S( 25, 41), S( 25, 41) } + }; + + // Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed + // by piece type and square (from white's point of view). + const Value Outpost[][SQUARE_NB] = { + { + // A B C D E F G H + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(4), V(8), V(8), V(4), V(0), V(0), + V(0), V(4),V(17),V(26),V(26),V(17), V(4), V(0), + V(0), V(8),V(26),V(35),V(35),V(26), V(8), V(0), + V(0), V(4),V(17),V(17),V(17),V(17), V(4), V(0) }, + { + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Bishops + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(5), V(5), V(5), V(5), V(0), V(0), + V(0), V(5),V(10),V(10),V(10),V(10), V(5), V(0), + V(0),V(10),V(21),V(21),V(21),V(21),V(10), V(0), + V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) } + }; + + // Threat[attacking][attacked] contains bonuses according to which piece + // type attacks which one. + const Score Threat[][PIECE_TYPE_NB] = { + {}, {}, + { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT + { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP + { S(0, 0), S( 0, 22), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK + { S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN + }; + + // ThreatenedByPawn[PieceType] contains a penalty according to which piece + // type is attacked by an enemy pawn. + const Score ThreatenedByPawn[] = { + S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) + }; + + #undef S + + const Score Tempo = make_score(24, 11); + const Score BishopPin = make_score(66, 11); + const Score RookOn7th = make_score(11, 20); + const Score QueenOn7th = make_score( 3, 8); + const Score RookOnPawn = make_score(10, 28); + const Score QueenOnPawn = make_score( 4, 20); + const Score RookOpenFile = make_score(43, 21); + const Score RookSemiopenFile = make_score(19, 10); + const Score BishopPawns = make_score( 8, 12); + const Score MinorBehindPawn = make_score(16, 0); + const Score UndefendedMinor = make_score(25, 10); + const Score TrappedRook = make_score(90, 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 = make_score(50, 50); + + // The SpaceMask[Color] contains the area of the board which is considered + // by the space evaluation. In the middle game, each side is given a bonus + // based on how many squares inside this area are safe and available for + // friendly minor pieces. + const Bitboard SpaceMask[] = { + (1ULL << SQ_C2) | (1ULL << SQ_D2) | (1ULL << SQ_E2) | (1ULL << SQ_F2) | + (1ULL << SQ_C3) | (1ULL << SQ_D3) | (1ULL << SQ_E3) | (1ULL << SQ_F3) | + (1ULL << SQ_C4) | (1ULL << SQ_D4) | (1ULL << SQ_E4) | (1ULL << SQ_F4), + (1ULL << SQ_C7) | (1ULL << SQ_D7) | (1ULL << SQ_E7) | (1ULL << SQ_F7) | + (1ULL << SQ_C6) | (1ULL << SQ_D6) | (1ULL << SQ_E6) | (1ULL << SQ_F6) | + (1ULL << SQ_C5) | (1ULL << SQ_D5) | (1ULL << SQ_E5) | (1ULL << SQ_F5) + }; + + // King danger constants and variables. The king danger scores are taken + // from the KingDanger[]. Various little "meta-bonuses" measuring + // the strength of the enemy attack are added up into an integer, which + // is used as an index to KingDanger[]. + // + // KingAttackWeights[PieceType] contains king attack weights by piece type + const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; + + // Bonuses for enemy's safe checks + const int QueenContactCheck = 6; + const int RookContactCheck = 4; + const int QueenCheck = 3; + const int RookCheck = 2; + const int BishopCheck = 1; + const int KnightCheck = 1; + + // KingExposed[Square] contains penalties based on the position of the + // defending king, indexed by king's square (from white's point of view). + const int KingExposed[] = { + 2, 0, 2, 5, 5, 2, 0, 2, + 2, 2, 4, 8, 8, 4, 2, 2, + 7, 10, 12, 12, 12, 12, 10, 7, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15 + }; + + // KingDanger[Color][attackUnits] contains the actual king danger weighted + // scores, indexed by color and by a calculated integer number. + Score KingDanger[COLOR_NB][128]; + + // Function prototypes + template + Value do_evaluate(const Position& pos, Value& margin); + + template + void init_eval_info(const Position& pos, EvalInfo& ei); + + template + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); + + template + Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]); + + template + Score evaluate_threats(const Position& pos, const EvalInfo& ei); + + template + Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei); + + template + int evaluate_space(const Position& pos, const EvalInfo& ei); + + Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei); + + Value interpolate(const Score& v, Phase ph, ScaleFactor sf); + Score apply_weight(Score v, Score w); + Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); + double to_cp(Value v); +} + + +namespace Eval { + + /// evaluate() is the main evaluation function. It always computes two + /// values, an endgame score and a middle game score, and interpolates + /// between them based on the remaining material. + + Value evaluate(const Position& pos, Value& margin) { + return do_evaluate(pos, margin); + } + + + /// trace() is like evaluate() but instead of a value returns a string suitable + /// to be print on stdout with the detailed descriptions and values of each + /// evaluation term. Used mainly for debugging. + std::string trace(const Position& pos) { + return Tracing::do_trace(pos); + } + + + /// init() computes evaluation weights from the corresponding UCI parameters + /// and setup king tables. + + void init() { + + Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]); + Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]); + Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); + Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); + Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); + Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); + + const int MaxSlope = 30; + const int Peak = 1280; + + for (int t = 0, i = 1; i < 100; i++) + { + t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); + + KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); + KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); + } + } + +} // namespace Eval + + +namespace { + +template +Value do_evaluate(const Position& pos, Value& margin) { + + assert(!pos.checkers()); + + EvalInfo ei; + Value margins[COLOR_NB]; + Score score, mobilityWhite, mobilityBlack; + Thread* th = pos.this_thread(); + + // margins[] store the uncertainty estimation of position's evaluation + // that typically is used by the search for pruning decisions. + margins[WHITE] = margins[BLACK] = VALUE_ZERO; + + // Initialize score by reading the incrementally updated scores included + // in the position object (material + piece square tables) and adding + // Tempo bonus. Score is computed from the point of view of white. + score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); + + // Probe the material hash table + ei.mi = Material::probe(pos, th->materialTable, th->endgames); + score += ei.mi->material_value(); + + // If we have a specialized evaluation function for the current material + // configuration, call it and return. + if (ei.mi->specialized_eval_exists()) + { + margin = VALUE_ZERO; + return ei.mi->evaluate(pos); + } + + // Probe the pawn hash table + ei.pi = Pawns::probe(pos, th->pawnsTable); + score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); + + // Initialize attack and king safety bitboards + init_eval_info(pos, ei); + init_eval_info(pos, ei); + + // Evaluate pieces and mobility + score += evaluate_pieces_of_color(pos, ei, mobilityWhite) + - evaluate_pieces_of_color(pos, ei, mobilityBlack); + + score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); + + // Evaluate kings after all other pieces because we need complete attack + // information when computing the king safety evaluation. + score += evaluate_king(pos, ei, margins) + - evaluate_king(pos, ei, margins); + + // 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 one side has only a king, check whether exists any unstoppable passed pawn + if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) + score += evaluate_unstoppable_pawns(pos, ei); + + // Evaluate space for both sides, only in middle-game. + if (ei.mi->space_weight()) + { + int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); + score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); + } + + // Scale winning side if position is more drawish that what it appears + ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) + : ei.mi->scale_factor(pos, BLACK); + + // If we don't already have an unusual scale factor, check for opposite + // colored bishop endgames, and use a lower scale for those. + if ( ei.mi->game_phase() < PHASE_MIDGAME + && pos.opposite_bishops() + && sf == SCALE_FACTOR_NORMAL) + { + // Only the two bishops ? + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + { + // Check for KBP vs KB with only a single pawn that is almost + // certainly a draw or at least two pawns. + bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); + sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + } + else + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. + sf = ScaleFactor(50); + } + + margin = margins[pos.side_to_move()]; + Value v = interpolate(score, ei.mi->game_phase(), sf); + + // In case of tracing add all single evaluation contributions for both white and black + if (Trace) + { + Tracing::add(PST, pos.psq_score()); + Tracing::add(IMBALANCE, ei.mi->material_value()); + Tracing::add(PAWN, ei.pi->pawns_value()); + Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); + Score w = ei.mi->space_weight() * evaluate_space(pos, ei); + Score b = ei.mi->space_weight() * evaluate_space(pos, ei); + Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); + Tracing::add(TOTAL, score); + Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE]) + << ", Black: " << to_cp(margins[BLACK]) + << "\nScaling: " << std::noshowpos + << std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, " + << std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * " + << std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n" + << "Total evaluation: " << to_cp(v); + } + + return pos.side_to_move() == WHITE ? v : -v; +} + + + // init_eval_info() initializes king bitboards for given color adding + // pawn attacks. To be done at the beginning of the evaluation. + + template + void init_eval_info(const Position& pos, EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); + + Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from(pos.king_square(Them)); + ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); + + // Init king safety tables only if we are going to use them + if (pos.count(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + { + ei.kingRing[Them] = b | shift_bb(b); + b &= ei.attackedBy[Us][PAWN]; + ei.kingAttackersCount[Us] = b ? popcount(b) / 2 : 0; + ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; + } else + ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; + } + + + // evaluate_outposts() evaluates bishop and knight outposts squares + + template + Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + assert (Piece == BISHOP || Piece == KNIGHT); + + // Initial bonus based on square + Value bonus = Outpost[Piece == BISHOP][relative_square(Us, s)]; + + // Increase bonus if supported by pawn, especially if the opponent has + // no minor piece which can exchange the outpost piece. + if (bonus && (ei.attackedBy[Us][PAWN] & s)) + { + if ( !pos.pieces(Them, KNIGHT) + && !(squares_of_color(s) & pos.pieces(Them, BISHOP))) + bonus += bonus + bonus / 2; + else + bonus += bonus / 2; + } + return make_score(bonus, bonus); + } + + + // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color + + template + Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { + + Bitboard b; + Square s; + Score score = SCORE_ZERO; + + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square* pl = pos.list(Us); + + ei.attackedBy[Us][Piece] = 0; + + while ((s = *pl++) != SQ_NONE) + { + // Find attacked squares, including x-ray attacks for bishops and rooks + b = Piece == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(Us, QUEEN)) + : Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) + : pos.attacks_from(s); + + ei.attackedBy[Us][Piece] |= b; + + if (b & ei.kingRing[Them]) + { + ei.kingAttackersCount[Us]++; + ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; + Bitboard bb = (b & ei.attackedBy[Them][KING]); + if (bb) + ei.kingAdjacentZoneAttacksCount[Us] += popcount(bb); + } + + int mob = Piece != QUEEN ? popcount(b & mobilityArea) + : popcount(b & mobilityArea); + + mobility += MobilityBonus[Piece][mob]; + + // Decrease score if we are attacked by an enemy pawn. Remaining part + // of threat evaluation must be done later when we have full attack info. + if (ei.attackedBy[Them][PAWN] & s) + score -= ThreatenedByPawn[Piece]; + + // Otherwise give a bonus if we are a bishop and can pin a piece or can + // give a discovered check through an x-ray attack. + else if ( Piece == BISHOP + && (PseudoAttacks[Piece][pos.king_square(Them)] & s) + && !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces())) + score += BishopPin; + + // Penalty for bishop with same coloured pawns + if (Piece == BISHOP) + score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + + if (Piece == BISHOP || Piece == KNIGHT) + { + // Bishop and knight outposts squares + if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) + score += evaluate_outposts(pos, ei, s); + + // Bishop or knight behind a pawn + if ( relative_rank(Us, s) < RANK_5 + && (pos.pieces(PAWN) & (s + pawn_push(Us)))) + score += MinorBehindPawn; + } + + if ( (Piece == ROOK || Piece == QUEEN) + && relative_rank(Us, s) >= RANK_5) + { + // Major piece on 7th rank and enemy king trapped on 8th + if ( relative_rank(Us, s) == RANK_7 + && relative_rank(Us, pos.king_square(Them)) == RANK_8) + score += Piece == ROOK ? RookOn7th : QueenOn7th; + + // Major piece attacking enemy pawns on the same rank/file + Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; + if (pawns) + score += popcount(pawns) * (Piece == ROOK ? RookOnPawn : QueenOnPawn); + } + + // Special extra evaluation for rooks + if (Piece == ROOK) + { + // Give a bonus for a rook on a open or semi-open file + if (ei.pi->semiopen(Us, file_of(s))) + score += ei.pi->semiopen(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile; + + if (mob > 3 || ei.pi->semiopen(Us, file_of(s))) + continue; + + Square ksq = pos.king_square(Us); + + // Penalize rooks which are trapped inside a king. Penalize more if + // king has lost right to castle. + 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_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E)) + score -= (TrappedRook - make_score(mob * 8, 0)) * (pos.can_castle(Us) ? 1 : 2); + } + + // 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 ( Piece == BISHOP + && pos.is_chess960() + && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) + { + const enum Piece P = make_piece(Us, PAWN); + Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); + if (pos.piece_on(s + d) == P) + score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 + : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2 + : TrappedBishopA1H1; + } + } + + if (Trace) + Tracing::scores[Us][Piece] = score; + + return score; + } + + + // evaluate_threats<>() assigns bonuses according to the type of attacking piece + // and the type of attacked one. + + template + Score evaluate_threats(const Position& pos, const EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard b, undefendedMinors, weakEnemies; + Score score = SCORE_ZERO; + + // Undefended minors get penalized even if not under attack + undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT) + & ~ei.attackedBy[Them][ALL_PIECES]; + + if (undefendedMinors) + score += UndefendedMinor; + + // Enemy pieces not defended by a pawn and under our attack + weakEnemies = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][ALL_PIECES]; + + // Add bonus according to type of attacked enemy piece and to the + // type of attacking piece, from knights to queens. Kings are not + // considered because are already handled in king evaluation. + if (weakEnemies) + for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++) + { + b = ei.attackedBy[Us][pt1] & weakEnemies; + if (b) + for (PieceType pt2 = PAWN; pt2 < KING; pt2++) + if (b & pos.pieces(pt2)) + score += Threat[pt1][pt2]; + } + + if (Trace) + Tracing::scores[Us][THREAT] = score; + + return score; + } + + + // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the + // pieces of a given color. + + template + Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Score score = mobility = SCORE_ZERO; + + // Do not include in mobility squares protected by enemy pawns or occupied by our pieces + const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING)); + + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + + // Sum up all attacked squares + ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; + if (Trace) + Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]); + + return score; + } + + + // evaluate_king<>() assigns bonuses and penalties to a king of a given color + + template + Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard undefended, b, b1, b2, safe; + int attackUnits; + const Square ksq = pos.king_square(Us); + + // King shelter and enemy pawns storm + Score score = ei.pi->king_safety(pos, ksq); + + // King safety. This is quite complicated, and is almost certainly far + // from optimally tuned. + if ( ei.kingAttackersCount[Them] >= 2 + && ei.kingAdjacentZoneAttacksCount[Them]) + { + // Find the attacked squares around the king which has no defenders + // apart from the king itself + undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING]; + undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN]); + + // Initialize the 'attackUnits' variable, which is used later on as an + // index to the KingDanger[] array. 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, the square of the + // king, and the quality of the pawn shelter. + attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + + KingExposed[relative_square(Us, ksq)] + - mg_value(score) / 32; + + // Analyse enemy's safe queen contact checks. First find undefended + // squares around the king attacked by enemy queen... + b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); + if (b) + { + // ...then remove squares not supported by another enemy piece + b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); + if (b) + attackUnits += QueenContactCheck + * popcount(b) + * (Them == pos.side_to_move() ? 2 : 1); + } + + // Analyse enemy's safe rook contact checks. First find undefended + // squares around the king attacked by enemy rooks... + b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); + + // Consider only squares where the enemy rook gives check + b &= PseudoAttacks[ROOK][ksq]; + + if (b) + { + // ...then remove squares not supported by another enemy piece + b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); + if (b) + attackUnits += RookContactCheck + * popcount(b) + * (Them == pos.side_to_move() ? 2 : 1); + } + + // Analyse enemy's safe distance checks for sliders and knights + safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); + + b1 = pos.attacks_from(ksq) & safe; + b2 = pos.attacks_from(ksq) & safe; + + // Enemy queen safe checks + b = (b1 | b2) & ei.attackedBy[Them][QUEEN]; + if (b) + attackUnits += QueenCheck * popcount(b); + + // Enemy rooks safe checks + b = b1 & ei.attackedBy[Them][ROOK]; + if (b) + attackUnits += RookCheck * popcount(b); + + // Enemy bishops safe checks + b = b2 & ei.attackedBy[Them][BISHOP]; + if (b) + attackUnits += BishopCheck * popcount(b); + + // Enemy knights safe checks + b = pos.attacks_from(ksq) & ei.attackedBy[Them][KNIGHT] & safe; + if (b) + attackUnits += KnightCheck * popcount(b); + + // To index KingDanger[] attackUnits must be in [0, 99] range + attackUnits = std::min(99, std::max(0, attackUnits)); + + // Finally, extract the king danger score from the KingDanger[] + // array and subtract the score from evaluation. Set also margins[] + // value that will be used for pruning because this value can sometimes + // be very big, and so capturing a single attacking piece can therefore + // result in a score change far bigger than the value of the captured piece. + score -= KingDanger[Us == Search::RootColor][attackUnits]; + margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]); + } + + if (Trace) + Tracing::scores[Us][KING] = 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, squaresToQueen, defendedSquares, unsafeSquares, supportingPawns; + Score score = SCORE_ZERO; + + b = ei.pi->passed_pawns(Us); + + while (b) + { + Square s = pop_lsb(&b); + + assert(pos.pawn_is_passed(Us, s)); + + int r = int(relative_rank(Us, s) - RANK_2); + int rr = r * (r - 1); + + // Base bonus based on rank + Value mbonus = Value(17 * rr); + Value ebonus = Value(7 * (rr + r + 1)); + + if (rr) + { + Square blockSq = s + pawn_push(Us); + + // Adjust bonus based on kings proximity + ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr); + ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr); + + // If blockSq is not the queening square then consider also a second push + if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1)) + ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); + + // If the pawn is free to advance, increase bonus + if (pos.is_empty(blockSq)) + { + squaresToQueen = forward_bb(Us, s); + defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; + + // If there is an enemy rook or queen attacking the pawn from behind, + // add all X-ray attacks by the rook or queen. Otherwise consider only + // the squares in the pawn's path attacked or occupied by the enemy. + if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN)) + && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from(s))) + unsafeSquares = squaresToQueen; + else + unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); + + // If there aren't enemy attacks huge bonus, a bit smaller if at + // least block square is not attacked, otherwise smallest bonus. + int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3; + + // Big bonus if the path to queen is fully defended, a bit less + // if at least block square is defended. + if (defendedSquares == squaresToQueen) + k += 6; + + else if (defendedSquares & blockSq) + k += (unsafeSquares & defendedSquares) == unsafeSquares ? 4 : 2; + + mbonus += Value(k * rr), ebonus += Value(k * rr); + } + } // rr != 0 + + // Increase the bonus if the passed pawn is supported by a friendly pawn + // on the same rank and a bit smaller if it's on the previous rank. + supportingPawns = pos.pieces(Us, PAWN) & adjacent_files_bb(file_of(s)); + if (supportingPawns & rank_bb(s)) + ebonus += Value(r * 20); + + else if (supportingPawns & rank_bb(s - pawn_push(Us))) + ebonus += Value(r * 12); + + // Rook pawns are a special case: They are sometimes worse, and + // sometimes better than other passed pawns. It is difficult to find + // good rules for determining whether they are good or bad. For now, + // we try the following: Increase the value for rook pawns if the + // other side has no pieces apart from a knight, and decrease the + // value if the other side has a rook or queen. + if (file_of(s) == FILE_A || file_of(s) == FILE_H) + { + if (pos.non_pawn_material(Them) <= KnightValueMg) + ebonus += ebonus / 4; + else if (pos.pieces(Them, ROOK, QUEEN)) + ebonus -= ebonus / 4; + } + score += make_score(mbonus, ebonus); + + } + + if (Trace) + Tracing::scores[Us][PASSED] = apply_weight(score, Weights[PassedPawns]); + + // Add the scores to the middle game and endgame eval + return apply_weight(score, Weights[PassedPawns]); + } + + + // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite + // conservative and returns a winning score only when we are very sure that the pawn is winning. + + Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) { + + Bitboard b, b2, blockers, supporters, queeningPath, candidates; + Square s, blockSq, queeningSquare; + Color c, winnerSide, loserSide; + bool pathDefended, opposed; + int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d; + int pliesToQueen[] = { 256, 256 }; + + // Step 1. Hunt for unstoppable passed pawns. If we find at least one, + // record how many plies are required for promotion. + for (c = WHITE; c <= BLACK; c++) + { + // Skip if other side has non-pawn pieces + if (pos.non_pawn_material(~c)) + continue; + + b = ei.pi->passed_pawns(c); + + while (b) + { + s = pop_lsb(&b); + queeningSquare = relative_square(c, file_of(s) | RANK_8); + queeningPath = forward_bb(c, s); + + // Compute plies to queening and check direct advancement + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); + oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); + pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath); + + if (movesToGo >= oppMovesToGo && !pathDefended) + continue; + + // Opponent king cannot block because path is defended and position + // is not in check. So only friendly pieces can be blockers. + assert(!pos.checkers()); + assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c))); + + // Add moves needed to free the path from friendly pieces and retest condition + movesToGo += popcount(queeningPath & pos.pieces(c)); + + if (movesToGo >= oppMovesToGo && !pathDefended) + continue; + + pliesToGo = 2 * movesToGo - int(c == pos.side_to_move()); + pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo); + } + } + + // Step 2. If either side cannot promote at least three plies before the other side then situation + // becomes too complex and we give up. Otherwise we determine the possibly "winning side" + if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3) + return SCORE_ZERO; + + winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); + loserSide = ~winnerSide; + + // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? + b = candidates = pos.pieces(loserSide, PAWN); + + while (b) + { + s = pop_lsb(&b); + + // Compute plies from queening + queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); + pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); + + // Check if (without even considering any obstacles) we're too far away or doubled + if ( pliesToQueen[winnerSide] + 3 <= pliesToGo + || (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN))) + candidates ^= s; + } + + // If any candidate is already a passed pawn it _may_ promote in time. We give up. + if (candidates & ei.pi->passed_pawns(loserSide)) + return SCORE_ZERO; + + // Step 4. Check new passed pawn creation through king capturing and pawn sacrifices + b = candidates; + + while (b) + { + s = pop_lsb(&b); + sacptg = blockersCount = 0; + minKingDist = kingptg = 256; + + // Compute plies from queening + queeningSquare = relative_square(loserSide, file_of(s) | RANK_8); + movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); + pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); + + // Generate list of blocking pawns and supporters + supporters = adjacent_files_bb(file_of(s)) & candidates; + opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN); + blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN); + + assert(blockers); + + // How many plies does it take to remove all the blocking pawns? + while (blockers) + { + blockSq = pop_lsb(&blockers); + movesToGo = 256; + + // Check pawns that can give support to overcome obstacle, for instance + // black pawns: a4, b4 white: b2 then pawn in b4 is giving support. + if (!opposed) + { + b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide))); + + while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) + { + d = square_distance(blockSq, pop_lsb(&b2)) - 2; + movesToGo = std::min(movesToGo, d); + } + } + + // Check pawns that can be sacrificed against the blocking pawn + b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s); + + while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) + { + d = square_distance(blockSq, pop_lsb(&b2)) - 2; + movesToGo = std::min(movesToGo, d); + } + + // If obstacle can be destroyed with an immediate pawn exchange / sacrifice, + // it's not a real obstacle and we have nothing to add to pliesToGo. + if (movesToGo <= 0) + continue; + + // Plies needed to sacrifice against all the blocking pawns + sacptg += movesToGo * 2; + blockersCount++; + + // Plies needed for the king to capture all the blocking pawns + d = square_distance(pos.king_square(loserSide), blockSq); + minKingDist = std::min(minKingDist, d); + kingptg = (minKingDist + blockersCount) * 2; + } + + // Check if pawn sacrifice plan _may_ save the day + if (pliesToQueen[winnerSide] + 3 > pliesToGo + sacptg) + return SCORE_ZERO; + + // Check if king capture plan _may_ save the day (contains some false positives) + if (pliesToQueen[winnerSide] + 3 > pliesToGo + kingptg) + return SCORE_ZERO; + } + + // Winning pawn is unstoppable and will promote as first, return big score + Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]); + return winnerSide == WHITE ? score : -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 scaled by a weight taken from the + // material hash table. The aim is to improve play on game opening. + template + int evaluate_space(const Position& pos, const EvalInfo& ei) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + // 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[Us] + & ~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 + return popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + } + + + // interpolate() interpolates between a middle game and an endgame score, + // based on game phase. It also scales the return value by a ScaleFactor array. + + Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { + + assert(mg_value(v) > -VALUE_INFINITE && mg_value(v) < VALUE_INFINITE); + assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE); + assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME); + + int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; + int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128; + return Value((result / GrainSize) * GrainSize); // Sign independent + } + + // apply_weight() weights score v by score w trying to prevent overflow + Score apply_weight(Score v, Score w) { + return make_score((int(mg_value(v)) * mg_value(w)) / 0x100, + (int(eg_value(v)) * eg_value(w)) / 0x100); + } + + // weight_option() computes the value of an evaluation weight, by combining + // two UCI-configurable weights (midgame and endgame) with an internal weight. + + Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { + + // Scale option value from 100 to 256 + int mg = Options[mgOpt] * 256 / 100; + int eg = Options[egOpt] * 256 / 100; + + return apply_weight(make_score(mg, eg), internalWeight); + } + + + // Tracing functions definitions + + double to_cp(Value v) { return double(v) / double(PawnValueMg); } + + void Tracing::add(int idx, Score wScore, Score bScore) { + + scores[WHITE][idx] = wScore; + scores[BLACK][idx] = bScore; + } + + void Tracing::row(const char* name, int idx) { + + Score wScore = scores[WHITE][idx]; + Score bScore = scores[BLACK][idx]; + + switch (idx) { + case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL: + stream << std::setw(20) << name << " | --- --- | --- --- | " + << std::setw(6) << to_cp(mg_value(wScore)) << " " + << std::setw(6) << to_cp(eg_value(wScore)) << " \n"; + break; + default: + stream << std::setw(20) << name << " | " << std::noshowpos + << std::setw(5) << to_cp(mg_value(wScore)) << " " + << std::setw(5) << to_cp(eg_value(wScore)) << " | " + << std::setw(5) << to_cp(mg_value(bScore)) << " " + << std::setw(5) << to_cp(eg_value(bScore)) << " | " + << std::showpos + << std::setw(6) << to_cp(mg_value(wScore - bScore)) << " " + << std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n"; + } + } + + std::string Tracing::do_trace(const Position& pos) { + + stream.str(""); + stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); + std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score)); + + Value margin; + do_evaluate(pos, margin); + + std::string totals = stream.str(); + stream.str(""); + + stream << std::setw(21) << "Eval term " << "| White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "---------------------+-------------+-------------+---------------\n"; + + row("Material, PST, Tempo", PST); + row("Material imbalance", IMBALANCE); + row("Pawns", PAWN); + row("Knights", KNIGHT); + row("Bishops", BISHOP); + row("Rooks", ROOK); + row("Queens", QUEEN); + row("Mobility", MOBILITY); + row("King safety", KING); + row("Threats", THREAT); + row("Passed pawns", PASSED); + row("Unstoppable pawns", UNSTOPPABLE); + row("Space", SPACE); + + stream << "---------------------+-------------+-------------+---------------\n"; + row("Total", TOTAL); + stream << totals; + + return stream.str(); + } +} diff --git a/stockfish-4-win/src_c++11/evaluate.h b/stockfish-4-win/src_c++11/evaluate.h new file mode 100644 index 0000000000000000000000000000000000000000..2234a4a0f9078e3e5abf1230825be03659040b75 --- /dev/null +++ b/stockfish-4-win/src_c++11/evaluate.h @@ -0,0 +1,35 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +class Position; + +namespace Eval { + +extern void init(); +extern Value evaluate(const Position& pos, Value& margin); +extern std::string trace(const Position& pos); + +} + +#endif // #ifndef EVALUATE_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/main.cpp b/stockfish-4-win/src_c++11/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d26b63ac663e0f40e874c1cb75313d99ab191a4 --- /dev/null +++ b/stockfish-4-win/src_c++11/main.cpp @@ -0,0 +1,52 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "evaluate.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +int main(int argc, char* argv[]) { + + std::cout << engine_info() << std::endl; + + UCI::init(Options); + Bitboards::init(); + Position::init(); + Bitbases::init_kpk(); + Search::init(); + Eval::init(); + Threads.init(); + TT.set_size(Options["Hash"]); + + std::string args; + + for (int i = 1; i < argc; i++) + args += std::string(argv[i]) + " "; + + UCI::loop(args); + + Threads.exit(); +} diff --git a/stockfish-4-win/src_c++11/material.cpp b/stockfish-4-win/src_c++11/material.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5245fdbccdcf39d7139a192b265d694f5b091462 --- /dev/null +++ b/stockfish-4-win/src_c++11/material.cpp @@ -0,0 +1,292 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +#include "material.h" + +using namespace std; + +namespace { + + // Values modified by Joona Kiiski + const Value MidgameLimit = Value(15581); + const Value EndgameLimit = Value(3998); + + // Scale factors used when one side has no more pawns + const int NoPawnsSF[4] = { 6, 12, 32 }; + + // Polynomial material balance parameters + const Value RedundantQueen = Value(320); + const Value RedundantRook = Value(554); + + // pair pawn knight bishop rook queen + const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; + + const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { + // pair pawn knight bishop rook queen + { 7 }, // Bishop pair + { 39, 2 }, // Pawn + { 35, 271, -4 }, // Knight + { 7, 105, 4, 7 }, // Bishop + { -27, -2, 46, 100, 56 }, // Rook + { 58, 29, 83, 148, -3, -25 } // Queen + }; + + const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { + // THEIR PIECES + // pair pawn knight bishop rook queen + { 41 }, // Bishop pair + { 37, 41 }, // Pawn + { 10, 62, 41 }, // Knight OUR PIECES + { 57, 64, 39, 41 }, // Bishop + { 50, 40, 23, -22, 41 }, // Rook + { 106, 101, 3, 151, 171, 41 } // Queen + }; + + // Endgame evaluation and scaling functions accessed direcly and not through + // the function maps because correspond to more then one material hash key. + Endgame EvaluateKmmKm[] = { Endgame(WHITE), Endgame(BLACK) }; + 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 templates used to detect a given material distribution + template bool is_KXK(const Position& pos) { + const Color Them = (Us == WHITE ? BLACK : WHITE); + return !pos.count(Them) + && pos.non_pawn_material(Them) == VALUE_ZERO + && pos.non_pawn_material(Us) >= RookValueMg; + } + + template bool is_KBPsKs(const Position& pos) { + return pos.non_pawn_material(Us) == BishopValueMg + && pos.count(Us) == 1 + && pos.count(Us) >= 1; + } + + template bool is_KQKRPs(const Position& pos) { + const Color Them = (Us == WHITE ? BLACK : WHITE); + return !pos.count(Us) + && pos.non_pawn_material(Us) == QueenValueMg + && pos.count(Us) == 1 + && pos.count(Them) == 1 + && pos.count(Them) >= 1; + } + + /// imbalance() calculates imbalance comparing 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 pt1, pt2, pc, v; + int value = 0; + + // Redundancy of major pieces, formula based on Kaufman's paper + // "The Evaluation of Material Imbalances in Chess" + if (pieceCount[Us][ROOK] > 0) + value -= RedundantRook * (pieceCount[Us][ROOK] - 1) + + RedundantQueen * pieceCount[Us][QUEEN]; + + // Second-degree polynomial material imbalance by Tord Romstad + for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) + { + pc = pieceCount[Us][pt1]; + if (!pc) + continue; + + v = LinearCoefficients[pt1]; + + for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) + v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] + + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + + value += pc * v; + } + return value; + } + +} // namespace + +namespace Material { + +/// Material::probe() takes a position object as input, looks up a MaterialEntry +/// object, and returns a pointer to it. If the material configuration is not +/// already present in the table, it is computed and stored there, so we don't +/// have to recompute everything when the same material configuration occurs again. + +Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { + + Key key = pos.material_key(); + Entry* e = entries[key]; + + // If e->key matches the position's material hash key, it means that we + // have analysed this material configuration before, and we can simply + // return the information we found the last time instead of recomputing it. + if (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 = game_phase(pos); + + // Let's look if we have a specialized evaluation function for this + // particular material configuration. First we look for a fixed + // configuration one, then a generic one if previous search failed. + if (endgames.probe(key, &e->evaluationFunction)) + return e; + + if (is_KXK(pos)) + { + e->evaluationFunction = &EvaluateKXK[WHITE]; + return e; + } + + if (is_KXK(pos)) + { + e->evaluationFunction = &EvaluateKXK[BLACK]; + return e; + } + + if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) + { + // Minor piece endgame with at least one minor piece per side and + // no pawns. Note that the case KmmK is already handled by KXK. + assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); + assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); + + if ( pos.count(WHITE) + pos.count(WHITE) <= 2 + && pos.count(BLACK) + pos.count(BLACK) <= 2) + { + e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; + return e; + } + } + + // OK, we didn't find any special evaluation function for the current + // material configuration. Is there a suitable scaling function? + // + // We face problems when there are several conflicting applicable + // scaling functions and we need to decide which one to use. + EndgameBase* sf; + + if (endgames.probe(key, &sf)) + { + e->scalingFunction[sf->color()] = sf; + return e; + } + + // Generic scaling functions that refer to more then one material + // distribution. Should be probed after the specialized ones. + // Note that these ones don't return after setting the function. + if (is_KBPsKs(pos)) + e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; + + if (is_KBPsKs(pos)) + e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; + + if (is_KQKRPs(pos)) + e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; + + else if (is_KQKRPs(pos)) + e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; + + Value npm_w = pos.non_pawn_material(WHITE); + Value npm_b = pos.non_pawn_material(BLACK); + + if (npm_w + npm_b == VALUE_ZERO) + { + 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]; + } + } + + // No pawns makes it difficult to win, even with a material advantage + if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) + { + e->factor[WHITE] = (uint8_t) + (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count(WHITE), 2)]); + } + + if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) + { + e->factor[BLACK] = (uint8_t) + (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count(BLACK), 2)]); + } + + // Compute the space weight + if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) + { + int minorPieceCount = pos.count(WHITE) + pos.count(WHITE) + + pos.count(BLACK) + pos.count(BLACK); + + e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0); + } + + // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder + // for the bishop pair "extended piece", this allow 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; +} + + +/// Material::game_phase() calculates the phase given the current +/// position. Because the phase is strictly a function of the material, it +/// is stored in MaterialEntry. + +Phase game_phase(const Position& pos) { + + Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); + + return npm >= MidgameLimit ? PHASE_MIDGAME + : npm <= EndgameLimit ? PHASE_ENDGAME + : Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); +} + +} // namespace Material diff --git a/stockfish-4-win/src_c++11/material.h b/stockfish-4-win/src_c++11/material.h new file mode 100644 index 0000000000000000000000000000000000000000..8df585e7df9483046ca076445cd5dabb94d4b6a8 --- /dev/null +++ b/stockfish-4-win/src_c++11/material.h @@ -0,0 +1,77 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 balance evaluation, a function pointer to a special +/// endgame evaluation function (which in most cases is NULL, meaning that the +/// standard evaluation function will be used), and "scale factors". +/// +/// 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 material_value() const { return make_score(value, value); } + Score space_weight() const { return spaceWeight; } + Phase game_phase() const { return gamePhase; } + bool specialized_eval_exists() const { return evaluationFunction != nullptr; } + Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } + ScaleFactor scale_factor(const Position& pos, Color c) const; + + Key key; + int16_t value; + uint8_t factor[COLOR_NB]; + EndgameBase* evaluationFunction; + EndgameBase* scalingFunction[COLOR_NB]; + Score spaceWeight; + Phase gamePhase; +}; + +typedef HashTable Table; + +Entry* probe(const Position& pos, Table& entries, Endgames& endgames); +Phase game_phase(const Position& pos); + +/// Material::scale_factor takes a position and a color as input, and +/// returns a scale factor for the given color. We have to provide the +/// position in addition to the color, because the scale factor need not +/// to be a constant: It can also be a function which should be applied to +/// the position. For instance, in KBP vs K endgames, a scaling function +/// which checks for draws with rook pawns and wrong-colored bishops. + +inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const { + + return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE + ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos); +} + +} + +#endif // #ifndef MATERIAL_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/misc.cpp b/stockfish-4-win/src_c++11/misc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09e65ec6cb73e565a8d728146d730670bc349f00 --- /dev/null +++ b/stockfish-4-win/src_c++11/misc.cpp @@ -0,0 +1,194 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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; +using namespace std::chrono; + +/// Version number. If Version is left empty, then compile date, in the +/// format DD-MM-YY, is shown in engine_info. +static const string Version = "4"; + + +/// 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 s, date(__DATE__); // From compiler, format is "Sep 21 2008" + + s << "Stockfish " << Version << setfill('0'); + + if (Version.empty()) + { + date >> month >> day >> year; + s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); + } + + s << (Is64Bit ? " 64" : "") + << (HasPopCnt ? " SSE4.2" : "") + << (to_uci ? "\nid author ": " by ") + << "Tord Romstad, Marco Costalba and Joona Kiiski"; + + return s.str(); +} + + +/// Convert system time to milliseconds. That's all we need. + +Time::point Time::now() { + return duration_cast(steady_clock::now().time_since_epoch()).count(); +} + + +/// Debug functions used mainly to collect run-time statistics + +static uint64_t hits[2], means[2]; + +void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; } +void dbg_hit_on_c(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 " + << (float)means[1] / means[0] << endl; +} + + +/// 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 while preserving +/// usual i/o functionality and 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 splitted streambuf for cin and cout + + Tie(streambuf* b, ofstream* f) : buf(b), file(f) {} + + int sync() { return file->rdbuf()->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; + ofstream* file; + + int log(int c, const char* prefix) { + + static int last = '\n'; + + if (last == '\n') + file->rdbuf()->sputn(prefix, 3); + + return last = file->rdbuf()->sputc((char)c); + } +}; + +class Logger { + + Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {} + ~Logger() { start(false); } + + ofstream file; + Tie in, out; + +public: + static void start(bool b) { + + static Logger l; + + if (b && !l.file.is_open()) + { + l.file.open("io_log.txt", ifstream::out | ifstream::app); + cin.rdbuf(&l.in); + cout.rdbuf(&l.out); + } + else if (!b && l.file.is_open()) + { + cout.rdbuf(l.out.buf); + cin.rdbuf(l.in.buf); + l.file.close(); + } + } +}; + + +/// Used to serialize access to std::cout to avoid multiple threads to write at +/// the same time. + +std::ostream& operator<<(std::ostream& os, SyncCout sc) { + + static std::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(bool b) { Logger::start(b); } + + +/// prefetch() preloads the given address in L1/L2 cache. This is a non +/// blocking function and do not stalls the CPU waiting for data to be +/// loaded from memory, that can be quite slow. +#ifdef NO_PREFETCH + +void prefetch(char*) {} + +#else + +void prefetch(char* addr) { + +# if defined(__INTEL_COMPILER) + // This hack prevents prefetches to be optimized away by + // Intel compiler. Both MSVC and gcc seems not affected. + __asm__ (""); +# endif + +# if defined(__INTEL_COMPILER) || defined(_MSC_VER) + _mm_prefetch(addr, _MM_HINT_T0); +# else + __builtin_prefetch(addr); +# endif +} + +#endif diff --git a/stockfish-4-win/src_c++11/misc.h b/stockfish-4-win/src_c++11/misc.h new file mode 100644 index 0000000000000000000000000000000000000000..718d39e4780c19a4152c635e2767e596aa452d41 --- /dev/null +++ b/stockfish-4-win/src_c++11/misc.h @@ -0,0 +1,67 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "types.h" + +extern const std::string engine_info(bool to_uci = false); +extern void prefetch(char* addr); +extern void start_logger(bool b); + +extern void dbg_hit_on(bool b); +extern void dbg_hit_on_c(bool c, bool b); +extern void dbg_mean_of(int v); +extern void dbg_print(); + + +struct Log : public std::ofstream { + Log(const std::string& f = "log.txt") : std::ofstream(f, std::ios::out | std::ios::app) {} + ~Log() { if (is_open()) close(); } +}; + + +namespace Time { + typedef int64_t point; + point now(); +} + + +template +struct HashTable { + HashTable() : e(Size, Entry()) {} + Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; } + +private: + std::vector e; +}; + + +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 + +#endif // #ifndef MISC_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/movegen.cpp b/stockfish-4-win/src_c++11/movegen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d2948bd438041a1f48369b459831bca83b05de0 --- /dev/null +++ b/stockfish-4-win/src_c++11/movegen.cpp @@ -0,0 +1,423 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +/// Simple macro to wrap a very common while loop, no facny, no flexibility, +/// hardcoded names 'mlist' and 'from'. +#define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b)) + +/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square +#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \ + (mlist++)->move = make_move(to - (d), to); } +namespace { + + template + ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) { + + if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) + return mlist; + + // After castling, the rook and king final positions are the same in Chess960 + // as they would be in standard chess. + Square kfrom = pos.king_square(us); + Square rfrom = pos.castle_rook_square(us, Side); + Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); + Bitboard enemies = pos.pieces(~us); + + assert(!pos.checkers()); + + const int K = Chess960 ? kto > kfrom ? -1 : 1 + : Side == KING_SIDE ? -1 : 1; + + for (Square s = kto; s != kfrom; s += (Square)K) + if (pos.attackers_to(s) & enemies) + return mlist; + + // 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 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) + return mlist; + + (mlist++)->move = make(kfrom, rfrom); + + if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) + mlist--; + + return mlist; + } + + + template + inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7, + Bitboard target, const CheckInfo* ci) { + + Bitboard b = shift_bb(pawnsOn7) & target; + + while (b) + { + Square to = pop_lsb(&b); + + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + (mlist++)->move = make(to - Delta, to, QUEEN); + + if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) + { + (mlist++)->move = make(to - Delta, to, ROOK); + (mlist++)->move = make(to - Delta, to, BISHOP); + (mlist++)->move = make(to - Delta, to, KNIGHT); + } + + // Knight-promotion is the only one that can give a direct check not + // already included in the queen-promotion. + if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) + (mlist++)->move = make(to - Delta, to, KNIGHT); + else + (void)ci; // Silence a warning under MSVC + } + + return mlist; + } + + + template + ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist, + Bitboard target, const CheckInfo* ci) { + + // 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 ? DELTA_N : DELTA_S); + const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); + const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + + Bitboard b1, b2, dc1, dc2, 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()); + + b1 = shift_bb(pawnsNotOn7) & emptySquares; + b2 = shift_bb(b1 & TRank3BB) & emptySquares; + + if (Type == EVASIONS) // Consider only blocking squares + { + b1 &= target; + b2 &= target; + } + + if (Type == QUIET_CHECKS) + { + b1 &= pos.attacks_from(ci->ksq, Them); + b2 &= pos.attacks_from(ci->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 among captures. + if (pawnsNotOn7 & ci->dcCandidates) + { + dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); + dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; + + b1 |= dc1; + b2 |= dc2; + } + } + + SERIALIZE_PAWNS(b1, Up); + SERIALIZE_PAWNS(b2, Up + Up); + } + + // Promotions and underpromotions + if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) + { + if (Type == CAPTURES) + emptySquares = ~pos.pieces(); + + if (Type == EVASIONS) + emptySquares &= target; + + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ci); + } + + // Standard and en-passant captures + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + { + b1 = shift_bb(pawnsNotOn7) & enemies; + b2 = shift_bb(pawnsNotOn7) & enemies; + + SERIALIZE_PAWNS(b1, Right); + SERIALIZE_PAWNS(b2, Left); + + 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 mlist; + + b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); + + assert(b1); + + while (b1) + (mlist++)->move = make(pop_lsb(&b1), pos.ep_square()); + } + } + + return mlist; + } + + + template FORCE_INLINE + ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us, + Bitboard target, const CheckInfo* ci) { + + assert(Pt != KING && Pt != PAWN); + + const Square* pl = pos.list(us); + + for (Square from = *pl; from != SQ_NONE; from = *++pl) + { + if (Checks) + { + if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) + && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) + continue; + + if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from)) + continue; + } + + Bitboard b = pos.attacks_from(from) & target; + + if (Checks) + b &= ci->checkSq[Pt]; + + SERIALIZE(b); + } + + return mlist; + } + + + template FORCE_INLINE + ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target, + const CheckInfo* ci = nullptr) { + + const bool Checks = Type == QUIET_CHECKS; + + mlist = generate_pawn_moves(pos, mlist, target, ci); + mlist = generate_moves(pos, mlist, Us, target, ci); + mlist = generate_moves(pos, mlist, Us, target, ci); + mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci); + mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci); + + if (Type != QUIET_CHECKS && Type != EVASIONS) + { + Square from = pos.king_square(Us); + Bitboard b = pos.attacks_from(from) & target; + SERIALIZE(b); + } + + if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) + { + if (pos.is_chess960()) + { + mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us); + mlist = generate_castle(pos, mlist, Us); + } + else + { + mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us); + mlist = generate_castle(pos, mlist, Us); + } + } + + return mlist; + } + + +} // 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* mlist) { + + 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, mlist, target) + : generate_all(pos, mlist, 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* mlist) { + + assert(!pos.checkers()); + + Color us = pos.side_to_move(); + CheckInfo ci(pos); + Bitboard dc = ci.dcCandidates; + + while (dc) + { + Square from = pop_lsb(&dc); + PieceType pt = type_of(pos.piece_on(from)); + + if (pt == PAWN) + continue; // Will be generated togheter with direct checks + + Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); + + if (pt == KING) + b &= ~PseudoAttacks[QUEEN][ci.ksq]; + + SERIALIZE(b); + } + + return us == WHITE ? generate_all(pos, mlist, ~pos.pieces(), &ci) + : generate_all(pos, mlist, ~pos.pieces(), &ci); +} + + +/// 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* mlist) { + + assert(pos.checkers()); + + int checkersCnt = 0; + Color us = pos.side_to_move(); + Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq; + Bitboard sliderAttacks = 0; + Bitboard b = pos.checkers(); + + assert(pos.checkers()); + + // Find squares attacked by slider checkers, we will remove them from the king + // evasions so to skip known illegal moves avoiding useless legality check later. + do + { + checkersCnt++; + checksq = pop_lsb(&b); + + assert(color_of(pos.piece_on(checksq)) == ~us); + + switch (type_of(pos.piece_on(checksq))) + { + case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break; + case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break; + case QUEEN: + // If queen and king are far or not on a diagonal line we can safely + // remove all the squares attacked in the other direction becuase are + // not reachable by the king anyway. + if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq)) + sliderAttacks |= PseudoAttacks[QUEEN][checksq]; + + // Otherwise we need to use real rook attacks to check if king is safe + // to move in the other direction. For example: king in B2, queen in A1 + // a knight in B1, and we can safely move to C1. + else + sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from(checksq); + + default: + break; + } + } while (b); + + // Generate evasions for king, capture and non capture moves + b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; + SERIALIZE(b); + + if (checkersCnt > 1) + return mlist; // Double check, only a king move can save the day + + // Generate blocking evasions or captures of the checking piece + Bitboard target = between_bb(checksq, ksq) | checksq; + + return us == WHITE ? generate_all(pos, mlist, target) + : generate_all(pos, mlist, target); +} + + +/// generate generates all the legal moves in the given position + +template<> +ExtMove* generate(const Position& pos, ExtMove* mlist) { + + ExtMove *end, *cur = mlist; + Bitboard pinned = pos.pinned_pieces(); + Square ksq = pos.king_square(pos.side_to_move()); + + end = pos.checkers() ? generate(pos, mlist) + : generate(pos, mlist); + while (cur != end) + if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) + && !pos.pl_move_is_legal(cur->move, pinned)) + cur->move = (--end)->move; + else + cur++; + + return end; +} diff --git a/stockfish-4-win/src_c++11/movegen.h b/stockfish-4-win/src_c++11/movegen.h new file mode 100644 index 0000000000000000000000000000000000000000..aa5310e2f8367047c2af459d7586b46ae1394c96 --- /dev/null +++ b/stockfish-4-win/src_c++11/movegen.h @@ -0,0 +1,57 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +enum GenType { + CAPTURES, + QUIETS, + QUIET_CHECKS, + EVASIONS, + NON_EVASIONS, + LEGAL +}; + +class Position; + +template +ExtMove* generate(const Position& pos, ExtMove* mlist); + +/// The MoveList struct is a simple wrapper around generate(), sometimes comes +/// handy to use this class instead of the low level generate() function. +template +struct MoveList { + + explicit MoveList(const Position& pos) : last(generate(pos, mlist)) {} + const ExtMove* begin() const { return mlist; } + const ExtMove* end() const { return last; } + size_t size() const { return last - mlist; } + bool contains(Move m) const { + for (const ExtMove& ms : *this) if (ms.move == m) return true; + return false; + } + +private: + ExtMove mlist[MAX_MOVES], *last; +}; + +#endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/movepick.cpp b/stockfish-4-win/src_c++11/movepick.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f2fa01eb231ba64e10feaba33d1af33977a35be --- /dev/null +++ b/stockfish-4-win/src_c++11/movepick.cpp @@ -0,0 +1,377 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, + EVASION, EVASIONS_S2, + QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, + QSEARCH_1, CAPTURES_S4, + PROBCUT, CAPTURES_S5, + RECAPTURE, CAPTURES_S6, + STOP + }; + + // Our insertion sort, guaranteed to be stable, as is needed + 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; + } + } + + // Unary predicate used by std::partition to split positive scores from remaining + // ones so to sort separately the two sets, and with the second sort delayed. + inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; } + + // Picks and moves to the front the best move in the range [begin, end), + // it is faster than sorting all the moves in advance when moves are few, as + // normally are the possible captures. + inline ExtMove* pick_best(ExtMove* begin, ExtMove* end) + { + std::swap(*begin, *std::max_element(begin, end)); + return begin; + } +} + + +/// 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 about how important good +/// move ordering is at the current node. + +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, + Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) { + + assert(d > DEPTH_ZERO); + + cur = end = moves; + endBadCaptures = moves + MAX_MOVES - 1; + countermoves = cm; + ss = s; + + if (p.checkers()) + stage = EVASION; + + else + stage = MAIN_SEARCH; + + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + end += (ttMove != MOVE_NONE); +} + +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, + Square sq) : pos(p), history(h), cur(moves), end(moves) { + + assert(d <= DEPTH_ZERO); + + if (p.checkers()) + stage = EVASION; + + else if (d > DEPTH_QS_NO_CHECKS) + stage = QSEARCH_0; + + else if (d > DEPTH_QS_RECAPTURES) + { + stage = QSEARCH_1; + + // Skip TT move if is not a capture or a promotion, this avoids qsearch + // tree explosion due to a possible perpetual check or similar rare cases + // when TT table is full. + if (ttm && !pos.is_capture_or_promotion(ttm)) + ttm = MOVE_NONE; + } + else + { + stage = RECAPTURE; + recaptureSquare = sq; + ttm = MOVE_NONE; + } + + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + end += (ttMove != MOVE_NONE); +} + +MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt) + : pos(p), history(h), cur(moves), end(moves) { + + assert(!pos.checkers()); + + stage = PROBCUT; + + // In ProbCut we generate only captures better than parent's captured piece + captureThreshold = PieceValue[MG][pt]; + ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); + + if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) + ttMove = MOVE_NONE; + + end += (ttMove != MOVE_NONE); +} + + +/// score() assign a numerical move ordering score to each move in a move list. +/// The moves with highest scores will be picked first. +template<> +void MovePicker::score() { + // Winning and equal captures in the main search are ordered by MVV/LVA. + // Suprisingly, this appears to perform slightly better than SEE based + // move ordering. The reason is probably that in a position with a winning + // capture, capturing a more valuable (but sufficiently defended) piece + // first usually doesn't hurt. The opponent will have to recapture, and + // the hanging piece will still be hanging (except in the unusual cases + // where it is possible to recapture with the hanging piece). Exchanging + // big pieces before capturing a hanging piece probably helps to reduce + // the subtree size. + // In main search we want to push captures with negative SEE values to + // badCaptures[] array, but instead of doing it now we delay till when + // the move has been picked up in pick_move_from_list(), this way we save + // some SEE calls in case we get a cutoff (idea from Pablo Vazquez). + Move m; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)); + + if (type_of(m) == PROMOTION) + it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; + + else if (type_of(m) == ENPASSANT) + it->score += PieceValue[MG][PAWN]; + } +} + +template<> +void MovePicker::score() { + + Move m; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + it->score = history[pos.piece_moved(m)][to_sq(m)]; + } +} + +template<> +void MovePicker::score() { + // Try good captures ordered by MVV/LVA, then non-captures if destination square + // is not under attack, ordered by history value, then bad-captures and quiet + // moves with a negative SEE. This last group is ordered by the SEE score. + Move m; + int seeScore; + + for (ExtMove* it = moves; it != end; ++it) + { + m = it->move; + if ((seeScore = pos.see_sign(m)) < 0) + it->score = seeScore - HistoryStats::Max; // At the bottom + + else if (pos.is_capture(m)) + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)) + HistoryStats::Max; + else + it->score = history[pos.piece_moved(m)][to_sq(m)]; + } +} + + +/// generate_next() generates, scores and sorts the next bunch of moves, when +/// there are no more moves to try for the current phase. + +void MovePicker::generate_next() { + + cur = moves; + + switch (++stage) { + + case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: + end = generate(pos, moves); + score(); + return; + + case KILLERS_S1: + cur = killers; + end = cur + 2; + + killers[0].move = ss->killers[0]; + killers[1].move = ss->killers[1]; + killers[2].move = killers[3].move = MOVE_NONE; + + // Be sure countermoves are different from killers + for (int i = 0; i < 2; i++) + if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move) + (end++)->move = countermoves[i]; + + if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races + killers[3].move = MOVE_NONE; + + return; + + case QUIETS_1_S1: + endQuiets = end = generate(pos, moves); + score(); + end = std::partition(cur, end, has_positive_score); + insertion_sort(cur, end); + return; + + case QUIETS_2_S1: + cur = end; + end = endQuiets; + if (depth >= 3 * ONE_PLY) + insertion_sort(cur, end); + return; + + case BAD_CAPTURES_S1: + // Just pick them in reverse order to get MVV/LVA ordering + cur = moves + MAX_MOVES - 1; + end = endBadCaptures; + return; + + case EVASIONS_S2: + end = generate(pos, moves); + if (end > moves + 1) + score(); + return; + + case QUIET_CHECKS_S3: + end = generate(pos, moves); + return; + + case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: + stage = STOP; + case STOP: + end = cur + 1; // Avoid another next_phase() call + return; + + default: + assert(false); + } +} + + +/// next_move() is the most important method of the MovePicker class. It returns +/// a new pseudo legal move every time is called, until there are no more moves +/// left. It picks the move with the biggest score from a list of generated moves +/// taking care not returning the ttMove if has already been searched previously. +template<> +Move MovePicker::next_move() { + + Move move; + + while (true) + { + while (cur == end) + generate_next(); + + switch (stage) { + + case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: + cur++; + return ttMove; + + case CAPTURES_S1: + move = pick_best(cur++, end)->move; + if (move != ttMove) + { + if (pos.see_sign(move) >= 0) + return move; + + // Losing capture, move it to the tail of the array + (endBadCaptures--)->move = move; + } + break; + + case KILLERS_S1: + move = (cur++)->move; + if ( move != MOVE_NONE + && pos.is_pseudo_legal(move) + && move != ttMove + && !pos.is_capture(move)) + return move; + break; + + case QUIETS_1_S1: case QUIETS_2_S1: + move = (cur++)->move; + if ( move != ttMove + && move != killers[0].move + && move != killers[1].move + && move != killers[2].move + && move != killers[3].move) + return move; + break; + + case BAD_CAPTURES_S1: + return (cur--)->move; + + case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: + move = pick_best(cur++, end)->move; + if (move != ttMove) + return move; + break; + + case CAPTURES_S5: + move = pick_best(cur++, end)->move; + if (move != ttMove && pos.see(move) > captureThreshold) + return move; + break; + + case CAPTURES_S6: + move = pick_best(cur++, end)->move; + if (to_sq(move) == recaptureSquare) + return move; + break; + + case QUIET_CHECKS_S3: + move = (cur++)->move; + if (move != ttMove) + return move; + break; + + case STOP: + return MOVE_NONE; + + default: + assert(false); + } + } +} + + +/// Version of next_move() to use at split point nodes where the move is grabbed +/// from the split point's shared MovePicker object. This function is not thread +/// safe so must be lock protected by the caller. +template<> +Move MovePicker::next_move() { return ss->splitPoint->movePicker->next_move(); } diff --git a/stockfish-4-win/src_c++11/movepick.h b/stockfish-4-win/src_c++11/movepick.h new file mode 100644 index 0000000000000000000000000000000000000000..c444615f5b9f2e8ab4a0952ff47e3c97bf642079 --- /dev/null +++ b/stockfish-4-win/src_c++11/movepick.h @@ -0,0 +1,110 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "search.h" +#include "types.h" + + +/// The Stats struct stores moves statistics. According to the template parameter +/// the class can store History, Gains 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. Gains records the move's +/// best evaluation gain from one ply to the next and is used for pruning decisions. +/// Countermoves store the move that refute a previous one. Entries are stored +/// according only to 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(2000); + + const T* operator[](Piece p) const { return table[p]; } + void clear() { std::memset(table, 0, sizeof(table)); } + + void update(Piece p, Square to, Move m) { + + if (m == table[p][to].first) + return; + + table[p][to].second = table[p][to].first; + table[p][to].first = m; + } + + void update(Piece p, Square to, Value v) { + + if (Gain) + table[p][to] = std::max(v, table[p][to] - 1); + + else if (abs(table[p][to] + v) < Max) + table[p][to] += v; + } + +private: + T table[PIECE_NB][SQUARE_NB]; +}; + +typedef Stats< true, Value> GainsStats; +typedef Stats HistoryStats; +typedef Stats > CountermovesStats; + + +/// MovePicker class is used to pick one pseudo legal move at a time from the +/// current position. The most important method is next_move(), which returns a +/// new pseudo legal move each time it is called, until there are no moves left, +/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha +/// beta algorithm, MovePicker attempts to return the moves which are most likely +/// to get a cut-off first. + +class MovePicker { + + MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC + +public: + MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); + MovePicker(const Position&, Move, const HistoryStats&, PieceType); + MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*); + + template Move next_move(); + +private: + template void score(); + void generate_next(); + + const Position& pos; + const HistoryStats& history; + Search::Stack* ss; + Move* countermoves; + Depth depth; + Move ttMove; + ExtMove killers[4]; + Square recaptureSquare; + int captureThreshold, stage; + ExtMove *cur, *end, *endQuiets, *endBadCaptures; + ExtMove moves[MAX_MOVES]; +}; + +#endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/notation.cpp b/stockfish-4-win/src_c++11/notation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e014e13bbb32c4259c2d6e5cb959907cd9fbc8a --- /dev/null +++ b/stockfish-4-win/src_c++11/notation.cpp @@ -0,0 +1,263 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "movegen.h" +#include "notation.h" +#include "position.h" + +using namespace std; + +static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" }; + + +/// score_to_uci() converts a value to a string suitable for use with the UCI +/// protocol specifications: +/// +/// 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 score_to_uci(Value v, Value alpha, Value beta) { + + stringstream s; + + if (abs(v) < VALUE_MATE_IN_MAX_PLY) + s << "cp " << v * 100 / int(PawnValueMg); + else + s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + + s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); + + return s.str(); +} + + +/// move_to_uci() converts a move to a string in coordinate notation +/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print +/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 +/// mode. Internally castle moves are always coded as "king captures rook". + +const string move_to_uci(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) == CASTLE && !chess960) + to = (to > from ? FILE_G : FILE_C) | rank_of(from); + + string move = square_to_string(from) + square_to_string(to); + + if (type_of(m) == PROMOTION) + move += PieceToChar[BLACK][promotion_type(m)]; // Lower case + + return move; +} + + +/// move_from_uci() takes a position and a string representing a move in +/// simple coordinate notation and returns an equivalent legal Move if any. + +Move move_from_uci(const Position& pos, string& str) { + + if (str.length() == 5) // Junior could send promotion piece in uppercase + str[4] = char(tolower(str[4])); + + for (const ExtMove& ms : MoveList(pos)) + if (str == move_to_uci(ms.move, pos.is_chess960())) + return ms.move; + + return MOVE_NONE; +} + + +/// move_to_san() takes a position and a legal Move as input and returns its +/// short algebraic notation representation. + +const string move_to_san(Position& pos, Move m) { + + if (m == MOVE_NONE) + return "(none)"; + + if (m == MOVE_NULL) + return "(null)"; + + assert(MoveList(pos).contains(m)); + + Bitboard others, b; + string san; + Color us = pos.side_to_move(); + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = pos.piece_on(from); + PieceType pt = type_of(pc); + + if (type_of(m) == CASTLE) + san = to > from ? "O-O" : "O-O-O"; + else + { + if (pt != PAWN) + { + san = PieceToChar[WHITE][pt]; // Upper case + + // Disambiguation if we have more then one piece of type 'pt' that can + // reach 'to' with a legal move. + others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; + + while (b) + { + Move move = make_move(pop_lsb(&b), to); + if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) + others ^= from_sq(move); + } + + if (others) + { + if (!(others & file_bb(from))) + san += file_to_char(file_of(from)); + + else if (!(others & rank_bb(from))) + san += rank_to_char(rank_of(from)); + + else + san += square_to_string(from); + } + } + else if (pos.is_capture(m)) + san = file_to_char(file_of(from)); + + if (pos.is_capture(m)) + san += 'x'; + + san += square_to_string(to); + + if (type_of(m) == PROMOTION) + san += string("=") + PieceToChar[WHITE][promotion_type(m)]; + } + + if (pos.move_gives_check(m, CheckInfo(pos))) + { + StateInfo st; + pos.do_move(m, st); + san += MoveList(pos).size() ? "+" : "#"; + pos.undo_move(m); + } + + return san; +} + + +/// pretty_pv() formats human-readable search information, typically to be +/// appended to the search log file. It uses the two helpers below to pretty +/// format time and score respectively. + +static string time_to_string(int64_t msecs) { + + const int MSecMinute = 1000 * 60; + const int MSecHour = 1000 * 60 * 60; + + int64_t hours = msecs / MSecHour; + int64_t minutes = (msecs % MSecHour) / MSecMinute; + int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000; + + stringstream s; + + if (hours) + s << hours << ':'; + + s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds; + + return s.str(); +} + +static string score_to_string(Value v) { + + stringstream s; + + if (v >= VALUE_MATE_IN_MAX_PLY) + s << "#" << (VALUE_MATE - v + 1) / 2; + + else if (v <= VALUE_MATED_IN_MAX_PLY) + s << "-#" << (VALUE_MATE + v) / 2; + + else + s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg; + + return s.str(); +} + +string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) { + + const int64_t K = 1000; + const int64_t M = 1000000; + + std::stack st; + Move* m = pv; + string san, padding; + size_t length; + stringstream s; + + s << setw(2) << depth + << setw(8) << score_to_string(value) + << setw(8) << time_to_string(msecs); + + if (pos.nodes_searched() < M) + s << setw(8) << pos.nodes_searched() / 1 << " "; + + else if (pos.nodes_searched() < K * M) + s << setw(7) << pos.nodes_searched() / K << "K "; + + else + s << setw(7) << pos.nodes_searched() / M << "M "; + + padding = string(s.str().length(), ' '); + length = padding.length(); + + while (*m != MOVE_NONE) + { + san = move_to_san(pos, *m); + + if (length + san.length() > 80) + { + s << "\n" + padding; + length = padding.length(); + } + + s << san << ' '; + length += san.length() + 1; + + st.push(StateInfo()); + pos.do_move(*m++, st.top()); + } + + while (m != pv) + pos.undo_move(*--m); + + return s.str(); +} diff --git a/stockfish-4-win/src_c++11/notation.h b/stockfish-4-win/src_c++11/notation.h new file mode 100644 index 0000000000000000000000000000000000000000..3ede26074ac703bab48d2dee6612ab39c4be93be --- /dev/null +++ b/stockfish-4-win/src_c++11/notation.h @@ -0,0 +1,35 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 NOTATION_H_INCLUDED +#define NOTATION_H_INCLUDED + +#include + +#include "types.h" + +class Position; + +std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); +Move move_from_uci(const Position& pos, std::string& str); +const std::string move_to_uci(Move m, bool chess960); +const std::string move_to_san(Position& pos, Move m); +std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); + +#endif // #ifndef NOTATION_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/pawns.cpp b/stockfish-4-win/src_c++11/pawns.cpp new file mode 100644 index 0000000000000000000000000000000000000000..718dd575957076d73e62b0bf47350619e826891c --- /dev/null +++ b/stockfish-4-win/src_c++11/pawns.cpp @@ -0,0 +1,281 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitcount.h" +#include "pawns.h" +#include "position.h" + +namespace { + + #define V Value + #define S(mg, eg) make_score(mg, eg) + + // Doubled pawn penalty by opposed flag and file + const Score Doubled[2][FILE_NB] = { + { S(13, 43), S(20, 48), S(23, 48), S(23, 48), + S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, + { S(13, 43), S(20, 48), S(23, 48), S(23, 48), + S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; + + // Isolated pawn penalty by opposed flag and file + const Score Isolated[2][FILE_NB] = { + { S(37, 45), S(54, 52), S(60, 52), S(60, 52), + S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, + { S(25, 30), S(36, 35), S(40, 35), S(40, 35), + S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; + + // Backward pawn penalty by opposed flag and file + const Score Backward[2][FILE_NB] = { + { S(30, 42), S(43, 46), S(49, 46), S(49, 46), + S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, + { S(20, 28), S(29, 31), S(33, 31), S(33, 31), + S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; + + // Pawn chain membership bonus by file + const Score ChainMember[FILE_NB] = { + S(11,-1), S(13,-1), S(13,-1), S(14,-1), + S(14,-1), S(13,-1), S(13,-1), S(11,-1) + }; + + // Candidate passed pawn bonus by rank + const Score CandidatePassed[RANK_NB] = { + S( 0, 0), S( 6, 13), S(6,13), S(14,29), + S(34,68), S(83,166), S(0, 0), S( 0, 0) + }; + + // Weakness of our pawn shelter in front of the king indexed by [rank] + const Value ShelterWeakness[RANK_NB] = + { V(100), V(0), V(27), V(73), V(92), V(101), V(101) }; + + // Danger of enemy pawns moving toward our king indexed by + // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn] + const Value StormDanger[3][RANK_NB] = { + { V( 0), V(64), V(128), V(51), V(26) }, + { V(26), V(32), V( 96), V(38), V(20) }, + { V( 0), V( 0), V( 64), V(25), V(13) }}; + + // Max bonus for king safety. Corresponds to start position with all the pawns + // in front of the king and no enemy pawn on the horizont. + const Value MaxSafetyBonus = V(263); + + #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 ? DELTA_N : DELTA_S); + const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); + const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); + + Bitboard b; + Square s; + File f; + Rank r; + bool passed, isolated, doubled, opposed, chain, backward, candidate; + Score value = SCORE_ZERO; + const Square* pl = pos.list(Us); + + Bitboard ourPawns = pos.pieces(Us, PAWN); + Bitboard theirPawns = pos.pieces(Them, PAWN); + + e->passedPawns[Us] = 0; + e->kingSquares[Us] = SQ_NONE; + e->semiopenFiles[Us] = 0xFF; + e->pawnAttacks[Us] = shift_bb(ourPawns) | shift_bb(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)); + + f = file_of(s); + r = rank_of(s); + + // This file cannot be semi-open + e->semiopenFiles[Us] &= ~(1 << f); + + // Our rank plus previous one. Used for chain detection + b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); + + // Flag the pawn as passed, isolated, doubled or member of a pawn + // chain (but not the backward one). + chain = ourPawns & adjacent_files_bb(f) & b; + isolated = !(ourPawns & adjacent_files_bb(f)); + doubled = ourPawns & forward_bb(Us, s); + opposed = theirPawns & forward_bb(Us, s); + passed = !(theirPawns & passed_pawn_mask(Us, s)); + + // Test for backward pawn + backward = false; + + // If the pawn is passed, isolated, or member of a pawn chain it cannot + // be backward. If there are friendly pawns behind on adjacent files + // or if can capture an enemy pawn it cannot be backward either. + if ( !(passed | isolated | chain) + && !(ourPawns & pawn_attack_span(Them, s)) + && !(pos.attacks_from(s, Us) & theirPawns)) + { + // We now know that there are no friendly pawns beside or behind this + // pawn on adjacent files. We now check whether the pawn is + // backward by looking in the forward direction on the adjacent + // files, and seeing whether we meet a friendly or an enemy pawn first. + b = pos.attacks_from(s, Us); + + // Note that we are sure to find something because pawn is not passed + // nor isolated, so loop is potentially infinite, but it isn't. + while (!(b & (ourPawns | theirPawns))) + b = shift_bb(b); + + // The friendly pawn needs to be at least two ranks closer than the + // enemy pawn in order to help the potentially backward pawn advance. + backward = (b | shift_bb(b)) & theirPawns; + } + + assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); + + // A not passed pawn is a candidate to become passed if it is free to + // advance and if the number of friendly pawns beside or behind this + // pawn on adjacent files is higher or equal than the number of + // enemy pawns in the forward direction on the adjacent files. + candidate = !(opposed | passed | backward | isolated) + && (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0 + && popcount(b) >= popcount(pawn_attack_span(Us, s) & theirPawns); + + // Passed pawns will be properly scored in evaluation because we need + // full attack info to evaluate passed pawns. Only the frontmost passed + // pawn on each file is considered a true passed pawn. + if (passed && !doubled) + e->passedPawns[Us] |= s; + + // Score this pawn + if (isolated) + value -= Isolated[opposed][f]; + + if (doubled) + value -= Doubled[opposed][f]; + + if (backward) + value -= Backward[opposed][f]; + + if (chain) + value += ChainMember[f]; + + if (candidate) + value += CandidatePassed[relative_rank(Us, s)]; + } + + return value; + } + +} // namespace + +namespace Pawns { + +/// probe() takes a position object as input, computes a Entry object, and returns +/// a pointer to it. The result is also stored in a hash table, so we don't have +/// to recompute everything when the same pawn structure occurs again. + +Entry* probe(const Position& pos, Table& entries) { + + Key key = pos.pawn_key(); + Entry* e = entries[key]; + + if (e->key == key) + return e; + + e->key = key; + e->value = evaluate(pos, e) - evaluate(pos, e); + 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); + + Value safety = MaxSafetyBonus; + 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); + Rank rkUs, rkThem; + File kf = file_of(ksq); + + kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf; + + for (int f = kf - 1; f <= kf + 1; f++) + { + b = ourPawns & FileBB[f]; + rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; + safety -= ShelterWeakness[rkUs]; + + b = theirPawns & FileBB[f]; + rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; + safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; + } + + return safety; +} + + +/// Entry::update_safety() calculates and caches a bonus for king safety. It is +/// called only when king square changes, about 20% of total king_safety() calls. + +template +Score Entry::update_safety(const Position& pos, Square ksq) { + + kingSquares[Us] = ksq; + castleRights[Us] = pos.can_castle(Us); + minKPdistance[Us] = 0; + + Bitboard pawns = pos.pieces(Us, PAWN); + if (pawns) + while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} + + if (relative_rank(Us, ksq) > RANK_4) + return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]); + + Value bonus = shelter_storm(pos, ksq); + + // If we can castle use the bonus after the castle if is bigger + if (pos.can_castle(make_castle_right(Us, KING_SIDE))) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); + + if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE))) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); + + return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]); +} + +// Explicit template instantiation +template Score Entry::update_safety(const Position& pos, Square ksq); +template Score Entry::update_safety(const Position& pos, Square ksq); + +} // namespace Pawns diff --git a/stockfish-4-win/src_c++11/pawns.h b/stockfish-4-win/src_c++11/pawns.h new file mode 100644 index 0000000000000000000000000000000000000000..9f2af30e4f5028472e493e1ea42ee5fbd630691b --- /dev/null +++ b/stockfish-4-win/src_c++11/pawns.h @@ -0,0 +1,78 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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. Currently, +/// it only includes a middle game and end game pawn structure evaluation, and a +/// bitboard of passed pawns. We may want to add further information in the future. +/// A lookup to the pawn hash table (performed by calling the probe function) +/// returns a pointer to an Entry object. + +struct Entry { + + Score pawns_value() const { return value; } + Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } + Bitboard passed_pawns(Color c) const { return passedPawns[c]; } + int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; } + int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); } + int semiopen_on_side(Color c, File f, bool left) const { + + return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1)); + } + + template + Score king_safety(const Position& pos, Square ksq) { + + return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us) + ? kingSafety[Us] : update_safety(pos, ksq); + } + + template + Score update_safety(const Position& pos, Square ksq); + + template + Value shelter_storm(const Position& pos, Square ksq); + + Key key; + Bitboard passedPawns[COLOR_NB]; + Bitboard pawnAttacks[COLOR_NB]; + Square kingSquares[COLOR_NB]; + int minKPdistance[COLOR_NB]; + int castleRights[COLOR_NB]; + Score value; + int semiopenFiles[COLOR_NB]; + Score kingSafety[COLOR_NB]; + int pawnsOnSquares[COLOR_NB][COLOR_NB]; +}; + +typedef HashTable Table; + +Entry* probe(const Position& pos, Table& entries); + +} + +#endif // #ifndef PAWNS_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/position.cpp b/stockfish-4-win/src_c++11/position.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ad7ac51df355fbb849721bbb63cbb81b65b5e9b --- /dev/null +++ b/stockfish-4-win/src_c++11/position.cpp @@ -0,0 +1,1453 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "bitcount.h" +#include "movegen.h" +#include "notation.h" +#include "position.h" +#include "psqtab.h" +#include "rkiss.h" +#include "thread.h" +#include "tt.h" + +using std::string; +using std::cout; +using std::endl; + +static const string PieceToChar(" PNBRQK pnbrqk"); + +CACHE_LINE_ALIGNMENT + +Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +Value PieceValue[PHASE_NB][PIECE_NB] = { +{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, +{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; + +namespace Zobrist { + + Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + Key enpassant[FILE_NB]; + Key castle[CASTLE_RIGHT_NB]; + Key side; + Key exclusion; +} + +Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} + +namespace { + +// min_attacker() is an 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 FORCE_INLINE +PieceType min_attacker(const Bitboard* bb, const Square& to, const 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<> FORCE_INLINE +PieceType min_attacker(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { + return KING; // No need to update bitboards, it is the last cycle +} + +} // namespace + + +/// CheckInfo c'tor + +CheckInfo::CheckInfo(const Position& pos) { + + Color them = ~pos.side_to_move(); + ksq = pos.king_square(them); + + pinned = pos.pinned_pieces(); + dcCandidates = pos.discovered_check_candidates(); + + checkSq[PAWN] = pos.attacks_from(ksq, them); + checkSq[KNIGHT] = pos.attacks_from(ksq); + checkSq[BISHOP] = pos.attacks_from(ksq); + checkSq[ROOK] = pos.attacks_from(ksq); + checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; + checkSq[KING] = 0; +} + + +/// Position::init() initializes at startup the various arrays used to compute +/// hash keys and the piece square tables. The latter is a two-step operation: +/// First, the white halves of the tables are copied from PSQT[] tables. Second, +/// the black halves of the tables are initialized by flipping and changing the +/// sign of the white scores. + +void Position::init() { + + RKISS rk; + + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + for (Square s = SQ_A1; s <= SQ_H8; s++) + Zobrist::psq[c][pt][s] = rk.rand(); + + for (File f = FILE_A; f <= FILE_H; f++) + Zobrist::enpassant[f] = rk.rand(); + + for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++) + { + Bitboard b = cr; + while (b) + { + Key k = Zobrist::castle[1ULL << pop_lsb(&b)]; + Zobrist::castle[cr] ^= k ? k : rk.rand(); + } + } + + Zobrist::side = rk.rand(); + Zobrist::exclusion = rk.rand(); + + for (PieceType pt = PAWN; pt <= KING; pt++) + { + PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; + PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; + + Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); + + for (Square s = SQ_A1; s <= SQ_H8; s++) + { + psq[WHITE][pt][ s] = (v + PSQT[pt][s]); + psq[BLACK][pt][~s] = -(v + PSQT[pt][s]); + } + } +} + + +/// Position::operator=() creates a copy of 'pos'. We want the new born Position +/// object do not depend on any external data so we detach state pointer from +/// the source one. + +Position& Position::operator=(const Position& pos) { + + std::memcpy(this, &pos, sizeof(Position)); + startState = *st; + st = &startState; + nodes = 0; + + assert(pos_is_ok()); + + return *this; +} + + +/// 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. + +void Position::set(const string& fenStr, bool isChess960, 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") while Black take 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. +*/ + + char col, row, token; + size_t p; + Square sq = SQ_A8; + std::istringstream ss(fenStr); + + clear(); + 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 ((p = PieceToChar.find(token)) != string::npos) + { + put_piece(sq, color_of(Piece(p)), type_of(Piece(p))); + 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; + + token = char(toupper(token)); + + if (token == 'K') + for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {} + + else if (token == 'Q') + for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {} + + else if (token >= 'A' && token <= 'H') + rsq = File(token - 'A') | relative_rank(c, RANK_1); + + else + continue; + + set_castle_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 = File(col - 'a') | Rank(row - '1'); + + if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) + 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) + int(sideToMove == BLACK); + + st->key = compute_key(); + st->pawnKey = compute_pawn_key(); + st->materialKey = compute_material_key(); + st->psq = compute_psq_score(); + st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); + st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); + st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); + chess960 = isChess960; + thisThread = th; + + assert(pos_is_ok()); +} + + +/// Position::set_castle_right() is an helper function used to set castling +/// rights given the corresponding color and the rook starting square. + +void Position::set_castle_right(Color c, Square rfrom) { + + Square kfrom = king_square(c); + CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; + CastleRight cr = make_castle_right(c, cs); + + st->castleRights |= cr; + castleRightsMask[kfrom] |= cr; + castleRightsMask[rfrom] |= cr; + castleRookSquare[c][cs] = 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) + castlePath[c][cs] |= s; + + for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++) + if (s != kfrom && s != rfrom) + castlePath[c][cs] |= s; +} + + +/// Position::fen() returns a FEN representation of the position. In case +/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function. + +const string Position::fen() const { + + std::ostringstream ss; + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) + { + for (File file = FILE_A; file <= FILE_H; file++) + { + Square sq = file | rank; + + if (is_empty(sq)) + { + int emptyCnt = 1; + + for ( ; file < FILE_H && is_empty(sq++); file++) + emptyCnt++; + + ss << emptyCnt; + } + else + ss << PieceToChar[piece_on(sq)]; + } + + if (rank > RANK_1) + ss << '/'; + } + + ss << (sideToMove == WHITE ? " w " : " b "); + + if (can_castle(WHITE_OO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K'); + + if (can_castle(WHITE_OOO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q'); + + if (can_castle(BLACK_OO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k'); + + if (can_castle(BLACK_OOO)) + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q'); + + if (st->castleRights == CASTLES_NONE) + ss << '-'; + + ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") + << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2; + + return ss.str(); +} + + +/// Position::pretty() returns an ASCII representation of the position to be +/// printed to the standard output together with the move's san notation. + +const string Position::pretty(Move move) const { + + const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; + const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" + + dottedLine + "\n| . | | . | | . | | . | |"; + + string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)]; + } + + std::ostringstream ss; + + if (move) + ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") + << move_to_san(*const_cast(this), move); + + ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase + << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; + + for (Bitboard b = checkers(); b; ) + ss << square_to_string(pop_lsb(&b)) << " "; + + ss << "\nLegal moves: "; + for (const ExtMove& ms : MoveList(*this)) + ss << move_to_san(*const_cast(this), ms.move) << " "; + + return ss.str(); +} + + +/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check +/// pieces, according to the call parameters. Pinned pieces protect our king, +/// discovery check pieces attack the enemy king. + +Bitboard Position::hidden_checkers(Square ksq, Color c) const { + + Bitboard b, pinners, result = 0; + + // Pinners are sliders that give check when pinned piece is removed + pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq]) + | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c); + + while (pinners) + { + b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); + + if (!more_than_one(b)) + result |= b & pieces(sideToMove); + } + return result; +} + + +/// Position::attackers_to() computes a bitboard of all pieces which attack a +/// given square. Slider attacks use occ bitboard as occupancy. + +Bitboard Position::attackers_to(Square s, Bitboard occ) const { + + return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) + | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) + | (attacks_from(s) & pieces(KNIGHT)) + | (attacks_bb(s, occ) & pieces(ROOK, QUEEN)) + | (attacks_bb(s, occ) & pieces(BISHOP, QUEEN)) + | (attacks_from(s) & pieces(KING)); +} + + +/// Position::attacks_from() computes a bitboard of all attacks of a given piece +/// put in a given square. Slider attacks use occ bitboard as occupancy. + +Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) { + + assert(is_ok(s)); + + switch (type_of(p)) + { + case BISHOP: return attacks_bb(s, occ); + case ROOK : return attacks_bb(s, occ); + case QUEEN : return attacks_bb(s, occ) | attacks_bb(s, occ); + default : return StepAttacksBB[p][s]; + } +} + + +/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal + +bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { + + assert(is_ok(m)); + assert(pinned == pinned_pieces()); + + Color us = sideToMove; + Square from = from_sq(m); + + assert(color_of(piece_moved(m)) == us); + assert(piece_on(king_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) + { + Color them = ~us; + Square to = to_sq(m); + Square capsq = to + pawn_push(them); + Square ksq = king_square(us); + Bitboard b = (pieces() ^ from ^ capsq) | to; + + assert(to == ep_square()); + assert(piece_moved(m) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(them, PAWN)); + assert(piece_on(to) == NO_PIECE); + + return !(attacks_bb< ROOK>(ksq, b) & pieces(them, QUEEN, ROOK)) + && !(attacks_bb(ksq, b) & pieces(them, 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) == CASTLE || !(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 + || !(pinned & from) + || squares_aligned(from, to_sq(m), king_square(us)); +} + + +/// Position::is_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::is_pseudo_legal(const Move m) const { + + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_moved(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) - 2 != 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) + { + // Move direction must be compatible with pawn color + int direction = to - from; + if ((us == WHITE) != (direction > 0)) + return false; + + // We have already handled promotion moves, so destination + // cannot be on the 8/1th rank. + if (rank_of(to) == RANK_8 || rank_of(to) == RANK_1) + return false; + + // Proceed according to the square delta between the origin and + // destination squares. + switch (direction) + { + case DELTA_NW: + case DELTA_NE: + case DELTA_SW: + case DELTA_SE: + // Capture. The destination square must be occupied by an enemy + // piece (en passant captures was handled earlier). + if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us) + return false; + + // From and to files must be one file apart, avoids a7h5 + if (abs(file_of(from) - file_of(to)) != 1) + return false; + break; + + case DELTA_N: + case DELTA_S: + // Pawn push. The destination square must be empty. + if (!is_empty(to)) + return false; + break; + + case DELTA_NN: + // Double white pawn push. The destination square must be on the fourth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( rank_of(to) != RANK_4 + || !is_empty(to) + || !is_empty(from + DELTA_N)) + return false; + break; + + case DELTA_SS: + // Double black pawn push. The destination square must be on the fifth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( rank_of(to) != RANK_5 + || !is_empty(to) + || !is_empty(from + DELTA_S)) + return false; + break; + + default: + return false; + } + } + else if (!(attacks_from(pc, from) & to)) + return false; + + // Evasions generator already takes care to avoid some kind of illegal moves + // and pl_move_is_legal() relies on this. So we 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()), king_square(us)) | checkers()) & to)) + return false; + } + // In case of king moves under check we have to remove king so to catch + // as invalid moves like b1a1 when opposite queen is on c1. + else if (attackers_to(to, pieces() ^ from) & pieces(~us)) + return false; + } + + return true; +} + + +/// Position::move_gives_check() tests whether a pseudo-legal move gives a check + +bool Position::move_gives_check(Move m, const CheckInfo& ci) const { + + assert(is_ok(m)); + assert(ci.dcCandidates == discovered_check_candidates()); + assert(color_of(piece_moved(m)) == sideToMove); + + Square from = from_sq(m); + Square to = to_sq(m); + PieceType pt = type_of(piece_on(from)); + + // Direct check ? + if (ci.checkSq[pt] & to) + return true; + + // Discovery check ? + if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from)) + { + // For pawn and king moves we need to verify also direction + if ( (pt != PAWN && pt != KING) + || !squares_aligned(from, to, king_square(~sideToMove))) + return true; + } + + // Can we skip the ugly special cases ? + if (type_of(m) == NORMAL) + return false; + + Color us = sideToMove; + Square ksq = king_square(~us); + + switch (type_of(m)) + { + case PROMOTION: + return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; + + // En passant capture with check ? We have already handled the case + // of direct checks and ordinary discovered check, the only case we + // need to handle is the unusual case of a discovered check through + // the captured pawn. + case ENPASSANT: + { + Square capsq = file_of(to) | rank_of(from); + Bitboard b = (pieces() ^ from ^ capsq) | to; + + return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) + | (attacks_bb(ksq, b) & pieces(us, QUEEN, BISHOP)); + } + case CASTLE: + { + Square kfrom = from; + Square rfrom = to; // 'King captures the rook' notation + Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1); + Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1); + + return (PseudoAttacks[ROOK][rto] & ksq) + && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq); + } + 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) { + + CheckInfo ci(*this); + do_move(m, newSt, ci, move_gives_check(m, ci)); +} + +void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { + + assert(is_ok(m)); + assert(&newSt != st); + + nodes++; + Key k = st->key; + + // Copy some fields of old state to our new StateInfo object except the ones + // which are going to be recalculated from scratch anyway, then switch our state + // pointer to point to the new, ready to be updated, state. + std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); + + newSt.previous = st; + st = &newSt; + + // Update side to move + k ^= Zobrist::side; + + // Increment ply counters.In particular rule50 will be later reset it to zero + // 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); + PieceType pt = type_of(pc); + PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); + + assert(color_of(pc) == us); + assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); + assert(capture != KING); + + if (type_of(m) == CASTLE) + { + assert(pc == make_piece(us, KING)); + + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + + do_castle(from, to, rfrom, rto); + + st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom]; + k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + } + + if (capture) + { + Square capsq = to; + + // If the captured piece is a pawn, update pawn hash key, otherwise + // update non-pawn material. + if (capture == PAWN) + { + if (type_of(m) == ENPASSANT) + { + capsq += pawn_push(them); + + assert(pt == 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; + } + + st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; + } + else + st->npMaterial[them] -= PieceValue[MG][capture]; + + // Update board and piece lists + remove_piece(capsq, them, capture); + + // Update material hash key and prefetch access to materialTable + k ^= Zobrist::psq[them][capture][capsq]; + st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; + prefetch((char*)thisThread->materialTable[st->materialKey]); + + // Update incremental scores + st->psq -= psq[them][capture][capsq]; + + // Reset rule 50 counter + st->rule50 = 0; + } + + // Update hash key + k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; + + // Reset en passant square + if (st->epSquare != SQ_NONE) + { + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } + + // Update castle rights if needed + if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to])) + { + int cr = castleRightsMask[from] | castleRightsMask[to]; + k ^= Zobrist::castle[st->castleRights & cr]; + st->castleRights &= ~cr; + } + + // Prefetch TT access as soon as we know the new hash key + prefetch((char*)TT.first_entry(k)); + + // Move the piece. The tricky Chess960 castle is handled earlier + if (type_of(m) != CASTLE) + move_piece(from, to, us, pt); + + // If the moving piece is a pawn do some special extra work + if (pt == PAWN) + { + // Set en-passant square, only if moved pawn can be captured + if ( (int(to) ^ int(from)) == 16 + && (attacks_from(from + pawn_push(us), us) & pieces(them, PAWN))) + { + st->epSquare = Square((from + to) / 2); + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + } + + if (type_of(m) == PROMOTION) + { + PieceType promotion = promotion_type(m); + + assert(relative_rank(us, to) == RANK_8); + assert(promotion >= KNIGHT && promotion <= QUEEN); + + remove_piece(to, us, PAWN); + put_piece(to, us, promotion); + + // Update hash keys + k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; + st->pawnKey ^= Zobrist::psq[us][PAWN][to]; + st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] + ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; + + // Update incremental score + st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; + + // Update material + st->npMaterial[us] += PieceValue[MG][promotion]; + } + + // Update pawn hash key and prefetch access to pawnsTable + st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + prefetch((char*)thisThread->pawnsTable[st->pawnKey]); + + // Reset rule 50 draw counter + st->rule50 = 0; + } + + // Update incremental scores + st->psq += psq[us][pt][to] - psq[us][pt][from]; + + // Set capture piece + st->capturedType = capture; + + // Update the key with the final value + st->key = k; + + // Update checkers bitboard, piece must be already moved + st->checkersBB = 0; + + if (moveIsCheck) + { + if (type_of(m) != NORMAL) + st->checkersBB = attackers_to(king_square(them)) & pieces(us); + else + { + // Direct checks + if (ci.checkSq[pt] & to) + st->checkersBB |= to; + + // Discovery checks + if (ci.dcCandidates && (ci.dcCandidates & from)) + { + if (pt != ROOK) + st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, ROOK); + + if (pt != BISHOP) + st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, BISHOP); + } + } + } + + sideToMove = ~sideToMove; + + 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; + Color them = ~us; + Square from = from_sq(m); + Square to = to_sq(m); + PieceType pt = type_of(piece_on(to)); + PieceType capture = st->capturedType; + + assert(is_empty(from) || type_of(m) == CASTLE); + assert(capture != KING); + + if (type_of(m) == PROMOTION) + { + PieceType promotion = promotion_type(m); + + assert(promotion == pt); + assert(relative_rank(us, to) == RANK_8); + assert(promotion >= KNIGHT && promotion <= QUEEN); + + remove_piece(to, us, promotion); + put_piece(to, us, PAWN); + pt = PAWN; + } + + if (type_of(m) == CASTLE) + { + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + pt = KING; + do_castle(to, from, rto, rfrom); + } + else + move_piece(to, from, us, pt); // Put the piece back at the source square + + if (capture) + { + Square capsq = to; + + if (type_of(m) == ENPASSANT) + { + capsq -= pawn_push(us); + + assert(pt == PAWN); + assert(to == st->previous->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(capsq) == NO_PIECE); + } + + put_piece(capsq, them, capture); // Restore the captured piece + } + + // Finally point our state pointer back to the previous state + st = st->previous; + gamePly--; + + assert(pos_is_ok()); +} + + +/// Position::do_castle() is a helper used to do/undo a castling move. This +/// is a bit tricky, especially in Chess960. + +void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { + + // Remove both pieces first since squares could overlap in Chess960 + remove_piece(kfrom, sideToMove, KING); + remove_piece(rfrom, sideToMove, ROOK); + board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us + put_piece(kto, sideToMove, KING); + put_piece(rto, sideToMove, ROOK); +} + + +/// 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()); + + std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here + + 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((char*)TT.first_entry(st->key)); + + st->rule50++; + st->pliesFromNull = 0; + + sideToMove = ~sideToMove; + + assert(pos_is_ok()); +} + +void Position::undo_null_move() { + + assert(!checkers()); + + st = st->previous; + sideToMove = ~sideToMove; +} + + +/// Position::see() is a static exchange evaluator: It tries to estimate the +/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes +/// tempi into account. If the side who initiated the capturing sequence does the +/// last capture, he loses a tempo and if the result is below 'asymmThreshold' +/// the capturing sequence is considered bad. + +int Position::see_sign(Move m) const { + + assert(is_ok(m)); + + // Early return if SEE cannot be negative because captured piece value + // is not less then capturing one. Note that king moves always return + // here because king midgame value is set to 0. + if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))]) + return 1; + + return see(m); +} + +int Position::see(Move m, int asymmThreshold) const { + + Square from, to; + Bitboard occupied, attackers, stmAttackers; + int swapList[32], slIndex = 1; + PieceType captured; + Color stm; + + assert(is_ok(m)); + + from = from_sq(m); + to = to_sq(m); + swapList[0] = PieceValue[MG][type_of(piece_on(to))]; + stm = color_of(piece_on(from)); + occupied = pieces() ^ from; + + // Castle moves are implemented as king capturing the rook so cannot be + // handled correctly. Simply return 0 that is always the correct value + // unless in the rare case the rook ends up under attack. + if (type_of(m) == CASTLE) + return 0; + + if (type_of(m) == ENPASSANT) + { + occupied ^= to - pawn_push(stm); // Remove the captured pawn + swapList[0] = PieceValue[MG][PAWN]; + } + + // Find all attackers to the destination square, with the moving piece + // removed, but possibly an X-ray attacker added behind it. + attackers = attackers_to(to, occupied) & occupied; + + // If the opponent has no attackers we are finished + stm = ~stm; + stmAttackers = attackers & pieces(stm); + if (!stmAttackers) + return swapList[0]; + + // The destination square is defended, which makes things rather more + // difficult to compute. We proceed by building up a "swap list" containing + // the material gain or loss at each stop in a sequence of captures to the + // destination square, where the sides alternately capture, and always + // capture with the least valuable piece. After each capture, we look for + // new X-ray attacks from behind the capturing piece. + captured = type_of(piece_on(from)); + + do { + assert(slIndex < 32); + + // Add the new entry to the swap list + swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; + slIndex++; + + // Locate and remove the next least valuable attacker + captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + stm = ~stm; + stmAttackers = attackers & pieces(stm); + + // Stop before processing a king capture + if (captured == KING && stmAttackers) + { + swapList[slIndex++] = QueenValueMg * 16; + break; + } + + } while (stmAttackers); + + // If we are doing asymmetric SEE evaluation and the same side does the first + // and the last capture, he loses a tempo and gain must be at least worth + // 'asymmThreshold', otherwise we replace the score with a very low value, + // before negamaxing. + if (asymmThreshold) + for (int i = 0; i < slIndex; i += 2) + if (swapList[i] < asymmThreshold) + swapList[i] = - QueenValueMg * 16; + + // Having built the swap list, we negamax through it to find the best + // achievable score from the point of view of the side to move. + while (--slIndex) + swapList[slIndex-1] = std::min(-swapList[slIndex], swapList[slIndex-1]); + + return swapList[0]; +} + + +/// Position::clear() erases the position object to a pristine state, with an +/// empty board, white to move, and no castling rights. + +void Position::clear() { + + std::memset(this, 0, sizeof(Position)); + startState.epSquare = SQ_NONE; + st = &startState; + + for (int i = 0; i < 8; i++) + for (int j = 0; j < 16; j++) + pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; +} + + +/// Position::compute_key() computes the hash key of the position. The hash +/// key is usually updated incrementally as moves are made and unmade, the +/// compute_key() function is only used when a new position is set up, and +/// to verify the correctness of the hash key when running in debug mode. + +Key Position::compute_key() const { + + Key k = Zobrist::castle[st->castleRights]; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s]; + } + + if (ep_square() != SQ_NONE) + k ^= Zobrist::enpassant[file_of(ep_square())]; + + if (sideToMove == BLACK) + k ^= Zobrist::side; + + return k; +} + + +/// Position::compute_pawn_key() computes the hash key of the position. The +/// hash key is usually updated incrementally as moves are made and unmade, +/// the compute_pawn_key() function is only used when a new position is set +/// up, and to verify the correctness of the pawn hash key when running in +/// debug mode. + +Key Position::compute_pawn_key() const { + + Key k = 0; + + for (Bitboard b = pieces(PAWN); b; ) + { + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; + } + + return k; +} + + +/// Position::compute_material_key() computes the hash key of the position. +/// The hash key is usually updated incrementally as moves are made and unmade, +/// the compute_material_key() function is only used when a new position is set +/// up, and to verify the correctness of the material hash key when running in +/// debug mode. + +Key Position::compute_material_key() const { + + Key k = 0; + + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= QUEEN; pt++) + for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++) + k ^= Zobrist::psq[c][pt][cnt]; + + return k; +} + + +/// Position::compute_psq_score() computes the incremental scores for the middle +/// game and the endgame. These functions are used to initialize the incremental +/// scores when a new position is set up, and to verify that the scores are correctly +/// updated by do_move and undo_move when the program is running in debug mode. +Score Position::compute_psq_score() const { + + Score score = SCORE_ZERO; + + for (Bitboard b = pieces(); b; ) + { + Square s = pop_lsb(&b); + Piece pc = piece_on(s); + score += psq[color_of(pc)][type_of(pc)][s]; + } + + return score; +} + + +/// Position::compute_non_pawn_material() computes the total non-pawn middle +/// game material value for the given side. Material values are updated +/// incrementally during the search, this function is only used while +/// initializing a new Position object. + +Value Position::compute_non_pawn_material(Color c) const { + + Value value = VALUE_ZERO; + + for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) + value += pieceCount[c][pt] * PieceValue[MG][pt]; + + return value; +} + + +/// Position::is_draw() tests whether the position is drawn by material, +/// repetition, or the 50 moves rule. It does not detect stalemates, this +/// must be done by the search. +bool Position::is_draw() const { + + // Draw by material? + if ( !pieces(PAWN) + && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) + return true; + + // Draw by the 50 moves rule? + if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) + return true; + + // Draw by repetition? + int i = 4, e = std::min(st->rule50, st->pliesFromNull); + + if (i <= e) + { + StateInfo* stp = st->previous->previous; + + do { + stp = stp->previous->previous; + + if (stp->key == st->key) + return true; + + i += 2; + + } while (i <= e); + } + + return false; +} + + +/// Position::flip() flips position with the white and black sides reversed. This +/// is only useful for debugging especially for finding evaluation symmetry bugs. + +static char toggle_case(char c) { + return char(islower(c) ? toupper(c) : tolower(c)); +} + +void Position::flip() { + + string f, token; + std::stringstream ss(fen()); + + for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement + { + std::getline(ss, token, rank > 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(), toggle_case); + + 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(), this_thread()); + + assert(pos_is_ok()); +} + + +/// Position::pos_is_ok() performs some consitency checks for the position object. +/// This is meant to be helpful when debugging. + +bool Position::pos_is_ok(int* failedStep) const { + + int dummy, *step = failedStep ? failedStep : &dummy; + + // What features of the position should be verified? + const bool all = false; + + const bool debugBitboards = all || false; + const bool debugKingCount = all || false; + const bool debugKingCapture = all || false; + const bool debugCheckerCount = all || false; + const bool debugKey = all || false; + const bool debugMaterialKey = all || false; + const bool debugPawnKey = all || false; + const bool debugIncrementalEval = all || false; + const bool debugNonPawnMaterial = all || false; + const bool debugPieceCounts = all || false; + const bool debugPieceList = all || false; + const bool debugCastleSquares = all || false; + + *step = 1; + + if (sideToMove != WHITE && sideToMove != BLACK) + return false; + + if ((*step)++, piece_on(king_square(WHITE)) != W_KING) + return false; + + if ((*step)++, piece_on(king_square(BLACK)) != B_KING) + return false; + + if ((*step)++, debugKingCount) + { + int kingCount[COLOR_NB] = {}; + + for (Square s = SQ_A1; s <= SQ_H8; s++) + if (type_of(piece_on(s)) == KING) + kingCount[color_of(piece_on(s))]++; + + if (kingCount[0] != 1 || kingCount[1] != 1) + return false; + } + + if ((*step)++, debugKingCapture) + if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove)) + return false; + + if ((*step)++, debugCheckerCount && popcount(st->checkersBB) > 2) + return false; + + if ((*step)++, debugBitboards) + { + // The intersection of the white and black pieces must be empty + if (pieces(WHITE) & pieces(BLACK)) + return false; + + // The union of the white and black pieces must be equal to all + // occupied squares + if ((pieces(WHITE) | pieces(BLACK)) != pieces()) + return false; + + // Separate piece type bitboards must have empty intersections + for (PieceType p1 = PAWN; p1 <= KING; p1++) + for (PieceType p2 = PAWN; p2 <= KING; p2++) + if (p1 != p2 && (pieces(p1) & pieces(p2))) + return false; + } + + if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6) + return false; + + if ((*step)++, debugKey && st->key != compute_key()) + return false; + + if ((*step)++, debugPawnKey && st->pawnKey != compute_pawn_key()) + return false; + + if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key()) + return false; + + if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score()) + return false; + + if ((*step)++, debugNonPawnMaterial) + if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE) + || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK)) + return false; + + if ((*step)++, debugPieceCounts) + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + if (pieceCount[c][pt] != popcount(pieces(c, pt))) + return false; + + if ((*step)++, debugPieceList) + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + for (int i = 0; i < pieceCount[c][pt]; i++) + if ( board[pieceList[c][pt][i]] != make_piece(c, pt) + || index[pieceList[c][pt][i]] != i) + return false; + + if ((*step)++, debugCastleSquares) + for (Color c = WHITE; c <= BLACK; c++) + for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) + { + CastleRight cr = make_castle_right(c, s); + + if (!can_castle(cr)) + continue; + + if ( (castleRightsMask[king_square(c)] & cr) != cr + || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK) + || castleRightsMask[castleRookSquare[c][s]] != cr) + return false; + } + + *step = 0; + return true; +} diff --git a/stockfish-4-win/src_c++11/position.h b/stockfish-4-win/src_c++11/position.h new file mode 100644 index 0000000000000000000000000000000000000000..5161f305778ddd686baaf2e817330d0d15823c9a --- /dev/null +++ b/stockfish-4-win/src_c++11/position.h @@ -0,0 +1,459 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "bitboard.h" +#include "types.h" + + +/// The checkInfo struct is initialized at c'tor time and keeps info used +/// to detect if a move gives check. +class Position; +struct Thread; + +struct CheckInfo { + + explicit CheckInfo(const Position&); + + Bitboard dcCandidates; + Bitboard pinned; + Bitboard checkSq[PIECE_TYPE_NB]; + Square ksq; +}; + + +/// The StateInfo struct stores information we need to restore a Position +/// object to its previous state when we retract a move. Whenever a move +/// is made on the board (by calling Position::do_move), a StateInfo object +/// must be passed as a parameter. + +struct StateInfo { + Key pawnKey, materialKey; + Value npMaterial[COLOR_NB]; + int castleRights, rule50, pliesFromNull; + Score psq; + Square epSquare; + + Key key; + Bitboard checkersBB; + PieceType capturedType; + StateInfo* previous; +}; + + +/// When making a move the current StateInfo up to 'key' excluded is copied to +/// the new one. Here we calculate the quad words (64bits) needed to be copied. +const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; + + +/// The position data structure. A position consists of the following data: +/// +/// * For each piece type, a bitboard representing the squares occupied +/// by pieces of that type. +/// * For each color, a bitboard representing the squares occupied by +/// pieces of that color. +/// * A bitboard of all occupied squares. +/// * A bitboard of all checking pieces. +/// * A 64-entry array of pieces, indexed by the squares of the board. +/// * The current side to move. +/// * Information about the castling rights for both sides. +/// * The initial files of the kings and both pairs of rooks. This is +/// used to implement the Chess960 castling rules. +/// * The en passant square (which is SQ_NONE if no en passant capture is +/// possible). +/// * The squares of the kings for both sides. +/// * Hash keys for the position itself, the current pawn structure, and +/// the current material situation. +/// * Hash keys for all previous positions in the game for detecting +/// repetition draws. +/// * A counter for detecting 50 move rule draws. + +class Position { +public: + Position() {} + Position(const Position& p, Thread* t) { *this = p; thisThread = t; } + Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } + Position& operator=(const Position&); + static void init(); + + // Text input/output + void set(const std::string& fen, bool isChess960, Thread* th); + const std::string fen() const; + const std::string pretty(Move m = MOVE_NONE) 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 king_square(Color c) const; + Square ep_square() const; + bool is_empty(Square s) const; + template int count(Color c) const; + template const Square* list(Color c) const; + + // Castling + int can_castle(CastleRight f) const; + int can_castle(Color c) const; + bool castle_impeded(Color c, CastlingSide s) const; + Square castle_rook_square(Color c, CastlingSide s) const; + + // Checking + Bitboard checkers() const; + Bitboard discovered_check_candidates() const; + Bitboard pinned_pieces() const; + + // Attacks to/from a given square + Bitboard attackers_to(Square s) const; + Bitboard attackers_to(Square s, Bitboard occ) const; + Bitboard attacks_from(Piece p, Square s) const; + static Bitboard attacks_from(Piece p, Square s, Bitboard occ); + template Bitboard attacks_from(Square s) const; + template Bitboard attacks_from(Square s, Color c) const; + + // Properties of moves + bool move_gives_check(Move m, const CheckInfo& ci) const; + bool pl_move_is_legal(Move m, Bitboard pinned) const; + bool is_pseudo_legal(const Move m) const; + bool is_capture(Move m) const; + bool is_capture_or_promotion(Move m) const; + bool is_passed_pawn_push(Move m) const; + Piece piece_moved(Move m) const; + PieceType captured_piece_type() const; + + // Piece specific + bool pawn_is_passed(Color c, Square s) const; + bool pawn_on_7th(Color c) const; + bool opposite_bishops() const; + bool bishop_pair(Color c) const; + + // Doing and undoing moves + void do_move(Move m, StateInfo& st); + void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); + void undo_move(Move m); + void do_null_move(StateInfo& st); + void undo_null_move(); + + // Static exchange evaluation + int see(Move m, int asymmThreshold = 0) const; + int see_sign(Move m) const; + + // Accessing hash keys + Key key() const; + Key exclusion_key() const; + Key pawn_key() const; + Key material_key() const; + + // Incremental piece-square evaluation + Score psq_score() const; + Value non_pawn_material(Color c) const; + + // Other properties of the position + Color side_to_move() const; + int game_ply() const; + bool is_chess960() const; + Thread* this_thread() const; + int64_t nodes_searched() const; + void set_nodes_searched(int64_t n); + bool is_draw() 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 clear(); + void set_castle_right(Color c, Square rfrom); + + // Helper functions + void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); + Bitboard hidden_checkers(Square ksq, Color c) const; + void put_piece(Square s, Color c, PieceType pt); + void remove_piece(Square s, Color c, PieceType pt); + void move_piece(Square from, Square to, Color c, PieceType pt); + + // Computing hash keys from scratch (for initialization and debugging) + Key compute_key() const; + Key compute_pawn_key() const; + Key compute_material_key() const; + + // Computing incremental evaluation scores and material counts + Score compute_psq_score() const; + Value compute_non_pawn_material(Color c) const; + + // Board and pieces + Piece board[SQUARE_NB]; + Bitboard byTypeBB[PIECE_TYPE_NB]; + Bitboard byColorBB[COLOR_NB]; + int pieceCount[COLOR_NB][PIECE_TYPE_NB]; + Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; + int index[SQUARE_NB]; + + // Other info + int castleRightsMask[SQUARE_NB]; + Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB]; + Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB]; + StateInfo startState; + int64_t nodes; + int gamePly; + Color sideToMove; + Thread* thisThread; + StateInfo* st; + int chess960; +}; + +inline int64_t Position::nodes_searched() const { + return nodes; +} + +inline void Position::set_nodes_searched(int64_t n) { + nodes = n; +} + +inline Piece Position::piece_on(Square s) const { + return board[s]; +} + +inline Piece Position::piece_moved(Move m) const { + return board[from_sq(m)]; +} + +inline bool Position::is_empty(Square s) const { + return board[s] == NO_PIECE; +} + +inline Color Position::side_to_move() const { + return sideToMove; +} + +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[c][Pt]; +} + +template inline const Square* Position::list(Color c) const { + return pieceList[c][Pt]; +} + +inline Square Position::ep_square() const { + return st->epSquare; +} + +inline Square Position::king_square(Color c) const { + return pieceList[c][KING][0]; +} + +inline int Position::can_castle(CastleRight f) const { + return st->castleRights & f; +} + +inline int Position::can_castle(Color c) const { + return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); +} + +inline bool Position::castle_impeded(Color c, CastlingSide s) const { + return byTypeBB[ALL_PIECES] & castlePath[c][s]; +} + +inline Square Position::castle_rook_square(Color c, CastlingSide s) const { + return castleRookSquare[c][s]; +} + +template +inline Bitboard Position::attacks_from(Square s) const { + + return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, 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 p, Square s) const { + return attacks_from(p, 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 hidden_checkers(king_square(~sideToMove), sideToMove); +} + +inline Bitboard Position::pinned_pieces() const { + return hidden_checkers(king_square(sideToMove), ~sideToMove); +} + +inline bool Position::pawn_is_passed(Color c, Square s) const { + return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); +} + +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->npMaterial[c]; +} + +inline bool Position::is_passed_pawn_push(Move m) const { + + return type_of(piece_moved(m)) == PAWN + && pawn_is_passed(sideToMove, to_sq(m)); +} + +inline int Position::game_ply() const { + return gamePly; +} + +inline bool Position::opposite_bishops() const { + + return pieceCount[WHITE][BISHOP] == 1 + && pieceCount[BLACK][BISHOP] == 1 + && opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); +} + +inline bool Position::bishop_pair(Color c) const { + + return pieceCount[c][BISHOP] >= 2 + && opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]); +} + +inline bool Position::pawn_on_7th(Color c) const { + return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); +} + +inline bool Position::is_chess960() const { + return chess960; +} + +inline bool Position::is_capture_or_promotion(Move m) const { + + assert(is_ok(m)); + return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m)); +} + +inline bool Position::is_capture(Move m) const { + + // Note that castle is coded as "king captures the rook" + assert(is_ok(m)); + return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; +} + +inline PieceType Position::captured_piece_type() const { + return st->capturedType; +} + +inline Thread* Position::this_thread() const { + return thisThread; +} + +inline void Position::put_piece(Square s, Color c, PieceType pt) { + + board[s] = make_piece(c, pt); + byTypeBB[ALL_PIECES] |= s; + byTypeBB[pt] |= s; + byColorBB[c] |= s; + index[s] = pieceCount[c][pt]++; + pieceList[c][pt][index[s]] = s; +} + +inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { + + // 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[pt] ^= from_to_bb; + byColorBB[c] ^= from_to_bb; + board[from] = NO_PIECE; + board[to] = make_piece(c, pt); + index[to] = index[from]; + pieceList[c][pt][index[to]] = to; +} + +inline void Position::remove_piece(Square s, Color c, PieceType pt) { + + // 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 guaranteed to be invariant to a do_move() + undo_move() sequence. + byTypeBB[ALL_PIECES] ^= s; + byTypeBB[pt] ^= s; + byColorBB[c] ^= s; + /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing + Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; + index[lastSquare] = index[s]; + pieceList[c][pt][index[lastSquare]] = lastSquare; + pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; +} + +#endif // #ifndef POSITION_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/psqtab.h b/stockfish-4-win/src_c++11/psqtab.h new file mode 100644 index 0000000000000000000000000000000000000000..f45442dabb1d3e8a33ce9ee2352d14e8f00c740b --- /dev/null +++ b/stockfish-4-win/src_c++11/psqtab.h @@ -0,0 +1,98 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 PSQTAB_H_INCLUDED +#define PSQTAB_H_INCLUDED + +#include "types.h" + +#define S(mg, eg) make_score(mg, eg) + + +/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on +/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined +/// for white side, for black side the tables are symmetric. + +static const Score PSQT[][SQUARE_NB] = { + { }, + { // Pawn + S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), + S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8), + S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8), + S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) + }, + { // Knight + S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104), + S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79), + S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55), + S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42), + S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42), + S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55), + S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79), + S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104) + }, + { // Bishop + S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59), + S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), + S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), + S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), + S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), + S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), + S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), + S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59) + }, + { // Rook + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), + S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3) + }, + { // Queen + S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80), + S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), + S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), + S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), + S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), + S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), + S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), + S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80) + }, + { // King + S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18), + S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77), + S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105), + S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135), + S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135), + S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105), + S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77), + S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18) + } +}; + +#undef S + +#endif // #ifndef PSQTAB_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/rkiss.h b/stockfish-4-win/src_c++11/rkiss.h new file mode 100644 index 0000000000000000000000000000000000000000..9564253b91a81301a70fd029163eac470248187c --- /dev/null +++ b/stockfish-4-win/src_c++11/rkiss.h @@ -0,0 +1,74 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 . + + This file is based on original code by Heinz van Saanen and is + available under 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. +*/ + +#ifndef RKISS_H_INCLUDED +#define RKISS_H_INCLUDED + +#include "types.h" + +/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys. +/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a +/// specific version that Heinz van Saanen derived from some public domain code +/// by Bob Jenkins. Following the feature list, as tested by Heinz. +/// +/// - Quite platform independent +/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-) +/// - ~12 times faster than my *nix sys-rand() +/// - ~4 times faster than SSE2-version of Mersenne twister +/// - Average cycle length: ~2^126 +/// - 64 bit seed +/// - Return doubles with a full 53 bit mantissa +/// - Thread safe + +class RKISS { + + struct S { uint64_t a, b, c, d; } s; // Keep variables always together + + uint64_t rotate(uint64_t x, uint64_t k) const { + return (x << k) | (x >> (64 - k)); + } + + uint64_t rand64() { + + const uint64_t + e = s.a - rotate(s.b, 7); + s.a = s.b ^ rotate(s.c, 13); + s.b = s.c + rotate(s.d, 37); + s.c = s.d + e; + return s.d = e + s.a; + } + +public: + RKISS(int seed = 73) { + + s.a = 0xf1ea5eed; + s.b = s.c = s.d = 0xd4e12c77; + for (int i = 0; i < seed; i++) // Scramble a few rounds + rand64(); + } + + template T rand() { return T(rand64()); } +}; + +#endif // #ifndef RKISS_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/search.cpp b/stockfish-4-win/src_c++11/search.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7199d37ba3a9a439e51f636ebcd23acd2f4bf3a --- /dev/null +++ b/stockfish-4-win/src_c++11/search.cpp @@ -0,0 +1,1805 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "book.h" +#include "evaluate.h" +#include "movegen.h" +#include "movepick.h" +#include "notation.h" +#include "search.h" +#include "timeman.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +namespace Search { + + volatile SignalsType Signals; + LimitsType Limits; + std::vector RootMoves; + Position RootPos; + Color RootColor; + Time::point SearchTime; + StateStackPtr SetupStates; +} + +using std::string; +using Eval::evaluate; +using namespace Search; + +namespace { + + // Set to true to force running with one thread. Used for debugging + const bool FakeSplit = false; + + // This is the minimum interval in msec between two check_time() calls + const int TimerResolution = 5; + + // Different node types, used as template parameter + enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; + + // Dynamic razoring margin based on depth + inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); } + + // Futility lookup tables (initialized at startup) and their access functions + Value FutilityMargins[16][64]; // [depth][moveNumber] + int FutilityMoveCounts[2][32]; // [improving][depth] + + inline Value futility_margin(Depth d, int mn) { + + return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)] + : 2 * VALUE_INFINITE; + } + + // Reduction lookup tables (initialized at startup) and their access function + int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] + + template inline Depth reduction(bool i, Depth d, int mn) { + + return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; + } + + size_t PVSize, PVIdx; + TimeManager TimeMgr; + int BestMoveChanges; + Value DrawValue[COLOR_NB]; + HistoryStats History; + GainsStats Gains; + CountermovesStats Countermoves; + + 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); + + void id_loop(Position& pos); + Value value_to_tt(Value v, int ply); + Value value_from_tt(Value v, int ply); + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); + bool allows(const Position& pos, Move first, Move second); + bool refutes(const Position& pos, Move first, Move second); + string uci_pv(const Position& pos, int depth, Value alpha, Value beta); + + struct Skill { + Skill(int l) : level(l), best(MOVE_NONE) {} + ~Skill() { + if (enabled()) // Swap best PV line with the sub-optimal one + std::swap(RootMoves[0], *std::find(RootMoves.begin(), + RootMoves.end(), best ? best : pick_move())); + } + + bool enabled() const { return level < 20; } + bool time_to_pick(int depth) const { return depth == 1 + level; } + Move pick_move(); + + int level; + Move best; + }; + +} // namespace + + +/// Search::init() is called during startup to initialize various lookup tables + +void Search::init() { + + int d; // depth (ONE_PLY == 2) + int hd; // half depth (ONE_PLY == 1) + int mc; // moveCount + + // Init reductions array + for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++) + { + double pvRed = log(double(hd)) * log(double(mc)) / 3.0; + double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; + Reductions[1][1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0); + Reductions[0][1][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0); + + Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc]; + Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc]; + + if (Reductions[0][0][hd][mc] > 2 * ONE_PLY) + Reductions[0][0][hd][mc] += ONE_PLY; + } + + // Init futility margins array + for (d = 1; d < 16; d++) for (mc = 0; mc < 64; mc++) + FutilityMargins[d][mc] = Value(112 * int(log(double(d * d) / 2) / log(2.0) + 1.001) - 8 * mc + 45); + + // Init futility move count array + for (d = 0; d < 32; d++) + { + FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4; + FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8)); + } +} + + +/// 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 returned. + +static size_t perft(Position& pos, Depth depth) { + + StateInfo st; + size_t cnt = 0; + CheckInfo ci(pos); + const bool leaf = depth == 2 * ONE_PLY; + + for (const ExtMove& ms : MoveList(pos)) + { + pos.do_move(ms.move, st, ci, pos.move_gives_check(ms.move, ci)); + cnt += leaf ? MoveList(pos).size() : ::perft(pos, depth - ONE_PLY); + pos.undo_move(ms.move); + } + return cnt; +} + +size_t Search::perft(Position& pos, Depth depth) { + return depth > ONE_PLY ? ::perft(pos, depth) : MoveList(pos).size(); +} + +/// Search::think() is the external interface to Stockfish's search, and is +/// called by the main thread when the program receives the UCI 'go' command. It +/// searches from RootPos and at the end prints the "bestmove" to output. + +void Search::think() { + + static PolyglotBook book; // Defined static to initialize the PRNG only once + + RootColor = RootPos.side_to_move(); + TimeMgr.init(Limits, RootPos.game_ply(), RootColor); + + if (RootMoves.empty()) + { + RootMoves.push_back(MOVE_NONE); + sync_cout << "info depth 0 score " + << score_to_uci(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) + << sync_endl; + + goto finalize; + } + + if (Options["OwnBook"] && !Limits.infinite && !Limits.mate) + { + Move bookMove = book.probe(RootPos, Options["Book File"], Options["Best Book Move"]); + + if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) + { + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove)); + goto finalize; + } + } + + if (Options["Contempt Factor"] && !Options["UCI_AnalyseMode"]) + { + int cf = Options["Contempt Factor"] * PawnValueMg / 100; // From centipawns + cf = cf * Material::game_phase(RootPos) / PHASE_MIDGAME; // Scale down with phase + DrawValue[ RootColor] = VALUE_DRAW - Value(cf); + DrawValue[~RootColor] = VALUE_DRAW + Value(cf); + } + else + DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW; + + if (Options["Write Search Log"]) + { + Log log(Options["Search Log Filename"]); + log << "\nSearching: " << RootPos.fen() + << "\ninfinite: " << Limits.infinite + << " ponder: " << Limits.ponder + << " time: " << Limits.time[RootColor] + << " increment: " << Limits.inc[RootColor] + << " moves to go: " << Limits.movestogo + << std::endl; + } + + // Reset the threads, still sleeping: will be wake up at split time + for (Thread* th : Threads) + th->maxPly = 0; + + Threads.sleepWhileIdle = Options["Idle Threads Sleep"]; + + // Set best timer interval to avoid lagging under time pressure. Timer is + // used to check for remaining available thinking time. + Threads.timer->msec = + Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : + Limits.nodes ? 2 * TimerResolution + : 100; + + Threads.timer->notify_one(); // Wake up the recurring timer + + id_loop(RootPos); // Let's start searching ! + + Threads.timer->msec = 0; // Stop the timer + Threads.sleepWhileIdle = true; // Send idle threads to sleep + + if (Options["Write Search Log"]) + { + Time::point elapsed = Time::now() - SearchTime + 1; + + Log log(Options["Search Log Filename"]); + log << "Nodes: " << RootPos.nodes_searched() + << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed + << "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]); + + StateInfo st; + RootPos.do_move(RootMoves[0].pv[0], st); + log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl; + RootPos.undo_move(RootMoves[0].pv[0]); + } + +finalize: + + // When search is stopped this info is not printed + sync_cout << "info nodes " << RootPos.nodes_searched() + << " time " << Time::now() - SearchTime + 1 << sync_endl; + + // When we reach max depth we arrive here even without Signals.stop is raised, + // but if we are pondering or in infinite search, according to UCI protocol, + // we shouldn't print the best move before the GUI sends a "stop" or "ponderhit" + // command. We simply wait here until GUI sends one of those commands (that + // raise Signals.stop). + if (!Signals.stop && (Limits.ponder || Limits.infinite)) + { + Signals.stopOnPonderhit = true; + RootPos.this_thread()->wait_for(Signals.stop); + } + + // Best move could be MOVE_NONE when searching on a stalemate position + sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], RootPos.is_chess960()) + << " ponder " << move_to_uci(RootMoves[0].pv[1], RootPos.is_chess960()) + << sync_endl; +} + + +namespace { + + // id_loop() is the main iterative deepening loop. It calls search() repeatedly + // with increasing depth until the allocated thinking time has been consumed, + // user stops the search, or the maximum search depth is reached. + + void id_loop(Position& pos) { + + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + int depth, prevBestMoveChanges; + Value bestValue, alpha, beta, delta; + + std::memset(ss-2, 0, 5 * sizeof(Stack)); + (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains + + depth = BestMoveChanges = 0; + bestValue = delta = alpha = -VALUE_INFINITE; + beta = VALUE_INFINITE; + + TT.new_search(); + History.clear(); + Gains.clear(); + Countermoves.clear(); + + PVSize = Options["MultiPV"]; + Skill skill(Options["Skill Level"]); + + // Do we have to play with skill handicap? In this case enable MultiPV search + // that we will use behind the scenes to retrieve a set of possible moves. + if (skill.enabled() && PVSize < 4) + PVSize = 4; + + PVSize = std::min(PVSize, RootMoves.size()); + + // Iterative deepening loop until requested to stop or target depth reached + while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) + { + // Save last iteration's scores before first PV line is searched and all + // the move scores but the (new) PV are set to -VALUE_INFINITE. + for (RootMove& rm : RootMoves) + rm.prevScore = rm.score; + + prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1 + BestMoveChanges = 0; + + // MultiPV loop. We perform a full root search for each PV line + for (PVIdx = 0; PVIdx < PVSize; PVIdx++) + { + // Reset aspiration window starting size + if (depth >= 5) + { + delta = Value(16); + alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE); + beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE); + } + + // Start with a small aspiration window and, in case of fail high/low, + // research with bigger window until not failing high/low anymore. + while (true) + { + bestValue = search(pos, ss, alpha, beta, depth * ONE_PLY, false); + + // Bring to front the best move. 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 but 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()); + + // Write PV back to transposition table in case the relevant + // entries have been overwritten during the search. + for (size_t i = 0; i <= PVIdx; i++) + RootMoves[i].insert_pv_in_tt(pos); + + // If search has been stopped return immediately. Sorting and + // writing PV back to TT is safe becuase RootMoves is still + // valid, although refers to previous iteration. + if (Signals.stop) + return; + + // When failing high/low give some update (without cluttering + // the UI) before to research. + if ( (bestValue <= alpha || bestValue >= beta) + && Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + + // In case of failing low/high increase aspiration window and + // research, otherwise exit the loop. + if (bestValue <= alpha) + { + alpha = std::max(bestValue - delta, -VALUE_INFINITE); + + Signals.failedLowAtRoot = true; + Signals.stopOnPonderhit = false; + } + else if (bestValue >= beta) + beta = std::min(bestValue + delta, VALUE_INFINITE); + + else + break; + + delta += delta / 2; + + 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 (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + } + + // Do we need to pick now the sub-optimal best move ? + if (skill.enabled() && skill.time_to_pick(depth)) + skill.pick_move(); + + if (Options["Write Search Log"]) + { + RootMove& rm = RootMoves[0]; + if (skill.best != MOVE_NONE) + rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best); + + Log log(Options["Search Log Filename"]); + log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0]) + << std::endl; + } + + // Do we have 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() && !Signals.stopOnPonderhit) + { + bool stop = false; // Local variable, not the volatile Signals.stop + + // Take in account some extra time if the best move has changed + if (depth > 4 && depth < 50 && PVSize == 1) + TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges); + + // Stop search if most of available time is already consumed. We + // probably don't have enough time to search the first move at the + // next iteration anyway. + if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100) + stop = true; + + // Stop search early if one move seems to be much better than others + if ( depth >= 12 + && !stop + && PVSize == 1 + && bestValue > VALUE_MATED_IN_MAX_PLY + && ( RootMoves.size() == 1 + || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) + { + Value rBeta = bestValue - 2 * PawnValueMg; + ss->excludedMove = RootMoves[0].pv[0]; + ss->skipNullMove = true; + Value v = search(pos, ss, rBeta - 1, rBeta, (depth - 3) * ONE_PLY, true); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (v < rBeta) + stop = true; + } + + if (stop) + { + // If we are allowed to ponder do not stop the search now but + // keep pondering until GUI sends "ponderhit" or "stop". + if (Limits.ponder) + Signals.stopOnPonderhit = true; + else + Signals.stop = true; + } + } + } + } + + + // search<>() is the main search function for both PV and non-PV nodes and for + // normal and SplitPoint nodes. When called just after a split point the search + // is simpler because we have already probed the hash table, done a null move + // search, and searched the first move before splitting, we don't have to repeat + // all this work again. We also don't need to store anything to the hash table + // here: This is taken care of after we return from the split point. + + template + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + + const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot); + const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot); + const bool RootNode = (NT == Root || NT == SplitPointRoot); + + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert(PvNode || (alpha == beta - 1)); + assert(depth > DEPTH_ZERO); + + Move quietsSearched[64]; + StateInfo st; + const TTEntry *tte; + SplitPoint* splitPoint; + Key posKey; + Move ttMove, move, excludedMove, bestMove, threatMove; + Depth ext, newDepth; + Value bestValue, value, ttValue; + Value eval, nullValue, futilityValue; + bool inCheck, givesCheck, pvMove, singularExtensionNode, improving; + bool captureOrPromotion, dangerous, doFullDepthSearch; + int moveCount, quietCount; + + // Step 1. Initialize node + Thread* thisThread = pos.this_thread(); + inCheck = pos.checkers(); + + if (SpNode) + { + splitPoint = ss->splitPoint; + bestMove = splitPoint->bestMove; + threatMove = splitPoint->threatMove; + bestValue = splitPoint->bestValue; + tte = nullptr; + ttMove = excludedMove = MOVE_NONE; + ttValue = VALUE_NONE; + + assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); + + goto moves_loop; + } + + moveCount = quietCount = 0; + bestValue = -VALUE_INFINITE; + ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + ss->ply = (ss-1)->ply + 1; + ss->futilityMoveCount = 0; + (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + + // 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 || pos.is_draw() || ss->ply > MAX_PLY) + return 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 + // further, we will never beat 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; + } + + // 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 = excludedMove ? pos.exclusion_key() : pos.key(); + tte = TT.probe(posKey); + ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + + // At PV nodes we check for exact scores, while at non-PV nodes we check for + // a fail high/low. Biggest advantage at probing at PV nodes is to have a + // smooth experience in analysis mode. We don't probe at Root nodes otherwise + // we should also update RootMoveList to avoid bogus output. + if ( !RootNode + && tte + && tte->depth() >= depth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) + { + TT.refresh(tte); + ss->currentMove = ttMove; // Can be MOVE_NONE + + if ( ttValue >= beta + && ttMove + && !pos.is_capture_or_promotion(ttMove) + && ttMove != ss->killers[0]) + { + ss->killers[1] = ss->killers[0]; + ss->killers[0] = ttMove; + } + return ttValue; + } + + // Step 5. Evaluate the position statically and update parent's gain statistics + if (inCheck) + { + ss->staticEval = ss->evalMargin = eval = VALUE_NONE; + goto moves_loop; + } + + else if (tte) + { + // Never assume anything on values stored in TT + if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + eval = ss->staticEval = evaluate(pos, ss->evalMargin); + + // Can ttValue be used as a better position evaluation? + if (ttValue != VALUE_NONE) + if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval) + || ((tte->bound() & BOUND_UPPER) && ttValue < eval)) + eval = ttValue; + } + else + { + eval = ss->staticEval = evaluate(pos, ss->evalMargin); + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + ss->staticEval, ss->evalMargin); + } + + // Update gain for the parent non-capture move given the static position + // evaluation before and after the move. + if ( !pos.captured_piece_type() + && ss->staticEval != VALUE_NONE + && (ss-1)->staticEval != VALUE_NONE + && (move = (ss-1)->currentMove) != MOVE_NULL + && type_of(move) == NORMAL) + { + Square to = to_sq(move); + Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); + } + + // Step 6. Razoring (skipped when in check) + if ( !PvNode + && depth < 4 * ONE_PLY + && eval + razor_margin(depth) < beta + && ttMove == MOVE_NONE + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && !pos.pawn_on_7th(pos.side_to_move())) + { + Value rbeta = beta - razor_margin(depth); + Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); + if (v < rbeta) + // Logically we should return (v + razor_margin(depth)), but + // surprisingly this did slightly weaker in tests. + return v; + } + + // Step 7. Static null move pruning (skipped when in check) + // We're betting that the opponent doesn't have a move that will reduce + // the score by more than futility_margin(depth) if we do a null move. + if ( !PvNode + && !ss->skipNullMove + && depth < 4 * ONE_PLY + && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && abs(eval) < VALUE_KNOWN_WIN + && pos.non_pawn_material(pos.side_to_move())) + return eval - futility_margin(depth, (ss-1)->futilityMoveCount); + + // Step 8. Null move search with verification search (is omitted in PV nodes) + if ( !PvNode + && !ss->skipNullMove + && depth > ONE_PLY + && eval >= beta + && abs(beta) < VALUE_MATE_IN_MAX_PLY + && pos.non_pawn_material(pos.side_to_move())) + { + ss->currentMove = MOVE_NULL; + + // Null move dynamic reduction based on depth + Depth R = 3 * ONE_PLY + depth / 4; + + // Null move dynamic reduction based on value + if (eval - PawnValueMg > beta) + R += ONE_PLY; + + pos.do_null_move(st); + (ss+1)->skipNullMove = true; + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -alpha, depth-R, !cutNode); + (ss+1)->skipNullMove = 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) + return nullValue; + + // Do verification search at high depths + ss->skipNullMove = true; + Value v = search(pos, ss, alpha, beta, depth-R, false); + ss->skipNullMove = false; + + if (v >= beta) + return nullValue; + } + else + { + // The null move failed low, which means that we may be faced with + // some kind of threat. If the previous move was reduced, check if + // the move that refuted the null move was somehow connected to the + // move which was reduced. If a connection is found, return a fail + // low score (which will cause the reduced move to fail high in the + // parent node, which will trigger a re-search with full depth). + threatMove = (ss+1)->currentMove; + + if ( depth < 5 * ONE_PLY + && (ss-1)->reduction + && threatMove != MOVE_NONE + && allows(pos, (ss-1)->currentMove, threatMove)) + return alpha; + } + } + + // Step 9. ProbCut (skipped when in check) + // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) + // and a reduced search returns a value much above beta, we can (almost) safely + // prune the previous move. + if ( !PvNode + && depth >= 5 * ONE_PLY + && !ss->skipNullMove + && abs(beta) < VALUE_MATE_IN_MAX_PLY) + { + Value rbeta = beta + 200; + Depth rdepth = depth - ONE_PLY - 3 * ONE_PLY; + + assert(rdepth >= ONE_PLY); + assert((ss-1)->currentMove != MOVE_NONE); + assert((ss-1)->currentMove != MOVE_NULL); + + MovePicker mp(pos, ttMove, History, pos.captured_piece_type()); + CheckInfo ci(pos); + + while ((move = mp.next_move()) != MOVE_NONE) + if (pos.pl_move_is_legal(move, ci.pinned)) + { + ss->currentMove = move; + pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); + 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 >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) + && ttMove == MOVE_NONE + && (PvNode || ss->staticEval + Value(256) >= beta)) + { + Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); + + ss->skipNullMove = true; + search(pos, ss, alpha, beta, d, true); + ss->skipNullMove = false; + + tte = TT.probe(posKey); + ttMove = tte ? tte->move() : MOVE_NONE; + } + +moves_loop: // When in check and at SpNode search starts from here + + Square prevMoveSq = to_sq((ss-1)->currentMove); + Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first, + Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second }; + + MovePicker mp(pos, ttMove, depth, History, countermoves, ss); + CheckInfo ci(pos); + value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc + improving = ss->staticEval >= (ss-2)->staticEval + || ss->staticEval == VALUE_NONE + ||(ss-2)->staticEval == VALUE_NONE; + + singularExtensionNode = !RootNode + && !SpNode + && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) + && ttMove != MOVE_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(RootMoves.begin() + PVIdx, RootMoves.end(), move)) + continue; + + if (SpNode) + { + // Shared counter cannot be decremented later if move turns out to be illegal + if (!pos.pl_move_is_legal(move, ci.pinned)) + continue; + + moveCount = ++splitPoint->moveCount; + splitPoint->mutex.unlock(); + } + else + moveCount++; + + if (RootNode) + { + Signals.firstRootMove = (moveCount == 1); + + if (thisThread == Threads.main() && Time::now() - SearchTime > 3000) + sync_cout << "info depth " << depth / ONE_PLY + << " currmove " << move_to_uci(move, pos.is_chess960()) + << " currmovenumber " << moveCount + PVIdx << sync_endl; + } + + ext = DEPTH_ZERO; + captureOrPromotion = pos.is_capture_or_promotion(move); + givesCheck = pos.move_gives_check(move, ci); + dangerous = givesCheck + || pos.is_passed_pawn_push(move) + || type_of(move) == CASTLE; + + // Step 12. Extend checks and, in PV nodes, also dangerous moves + if (PvNode && dangerous) + ext = ONE_PLY; + + else if (givesCheck && pos.see_sign(move) >= 0) + ext = ONE_PLY / 2; + + // 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, if result is lower than ttValue minus + // a margin then we extend ttMove. + if ( singularExtensionNode + && move == ttMove + && !ext + && pos.pl_move_is_legal(move, ci.pinned) + && abs(ttValue) < VALUE_KNOWN_WIN) + { + assert(ttValue != VALUE_NONE); + + Value rBeta = ttValue - int(depth); + ss->excludedMove = move; + ss->skipNullMove = true; + value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (value < rBeta) + ext = ONE_PLY; + } + + // Update current move (this must be done after singular extension search) + newDepth = depth - ONE_PLY + ext; + + // Step 13. Futility pruning (is omitted in PV nodes) + if ( !PvNode + && !captureOrPromotion + && !inCheck + && !dangerous + /* && move != ttMove Already implicit in the next condition */ + && bestValue > VALUE_MATED_IN_MAX_PLY) + { + // Move count based pruning + if ( depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[improving][depth] + && (!threatMove || !refutes(pos, move, threatMove))) + { + if (SpNode) + splitPoint->mutex.lock(); + + continue; + } + + // Value based pruning + // We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth, + // but fixing this made program slightly weaker. + Depth predictedDepth = newDepth - reduction(improving, depth, moveCount); + futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) + + Gains[pos.piece_moved(move)][to_sq(move)]; + + if (futilityValue < beta) + { + bestValue = std::max(bestValue, futilityValue); + + if (SpNode) + { + splitPoint->mutex.lock(); + if (bestValue > splitPoint->bestValue) + splitPoint->bestValue = bestValue; + } + continue; + } + + // Prune moves with negative SEE at low depths + if ( predictedDepth < 4 * ONE_PLY + && pos.see_sign(move) < 0) + { + if (SpNode) + splitPoint->mutex.lock(); + + continue; + } + + // We have not pruned the move that will be searched, but remember how + // far in the move list we are to be more aggressive in the child node. + ss->futilityMoveCount = moveCount; + } + else + ss->futilityMoveCount = 0; + + // Check for legality only before to do the move + if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned)) + { + moveCount--; + continue; + } + + pvMove = PvNode && moveCount == 1; + ss->currentMove = move; + if (!SpNode && !captureOrPromotion && quietCount < 64) + quietsSearched[quietCount++] = move; + + // Step 14. Make the move + pos.do_move(move, st, ci, givesCheck); + + // Step 15. Reduced depth search (LMR). If the move fails high will be + // re-searched at full depth. + if ( depth > 3 * ONE_PLY + && !pvMove + && !captureOrPromotion + && !dangerous + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1]) + { + ss->reduction = reduction(improving, depth, moveCount); + + if (!PvNode && cutNode) + ss->reduction += ONE_PLY; + + if (move == countermoves[0] || move == countermoves[1]) + ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY); + + Depth d = std::max(newDepth - ss->reduction, ONE_PLY); + if (SpNode) + alpha = splitPoint->alpha; + + value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + + doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); + ss->reduction = DEPTH_ZERO; + } + else + doFullDepthSearch = !pvMove; + + // Step 16. Full depth search, when LMR is skipped or fails high + if (doFullDepthSearch) + { + if (SpNode) + alpha = splitPoint->alpha; + + 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); + } + + // Only for PV nodes 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 to fail low with value <= alpha and to try another move. + if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta)))) + 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 new best move + if (SpNode) + { + splitPoint->mutex.lock(); + bestValue = splitPoint->bestValue; + alpha = splitPoint->alpha; + } + + // Finished searching the move. If Signals.stop is true, the search + // was aborted because the user interrupted the search or because we + // ran out of time. In this case, the return value of the search cannot + // be trusted, and we don't update the best move and/or PV. + if (Signals.stop || thisThread->cutoff_occurred()) + return value; // To avoid returning VALUE_INFINITE + + if (RootNode) + { + RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); + + // PV move or new best move ? + if (pvMove || value > alpha) + { + rm.score = value; + rm.extract_pv_from_tt(pos); + + // 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 (!pvMove) + BestMoveChanges++; + } + else + // All other moves but the PV are set to the lowest value, this + // is not a problem when sorting becuase sort is stable and move + // position in the list is preserved, just the PV is pushed up. + rm.score = -VALUE_INFINITE; + } + + if (value > bestValue) + { + bestValue = SpNode ? splitPoint->bestValue = value : value; + + if (value > alpha) + { + bestMove = SpNode ? splitPoint->bestMove = move : move; + + if (PvNode && value < beta) // Update alpha! Always alpha < beta + alpha = SpNode ? splitPoint->alpha = value : value; + else + { + assert(value >= beta); // Fail high + + if (SpNode) + splitPoint->cutoff = true; + + break; + } + } + } + + // Step 19. Check for splitting the search + if ( !SpNode + && depth >= Threads.minimumSplitDepth + && Threads.available_slave(thisThread) + && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) + { + assert(bestValue < beta); + + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT, cutNode); + if (bestValue >= beta) + break; + } + } + + if (SpNode) + return bestValue; + + // Step 20. Check for mate and stalemate + // All legal moves have been searched and if there are no legal moves, it + // must be mate or stalemate. Note that we can have a false positive in + // case of Signals.stop or thread.cutoff_occurred() are set, but this is + // harmless because return value is discarded anyhow in the parent nodes. + // If we are in a singular extension search then return a fail low score. + // A split node has at least one move, the one tried before to be splitted. + if (!moveCount) + return excludedMove ? alpha + : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; + + // If we have pruned all the moves without searching return a fail-low score + if (bestValue == -VALUE_INFINITE) + bestValue = alpha; + + TT.store(posKey, value_to_tt(bestValue, ss->ply), + bestValue >= beta ? BOUND_LOWER : + PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, + depth, bestMove, ss->staticEval, ss->evalMargin); + + // Quiet best move: update killers, history and countermoves + if ( bestValue >= beta + && !pos.is_capture_or_promotion(bestMove) + && !inCheck) + { + if (ss->killers[0] != bestMove) + { + ss->killers[1] = ss->killers[0]; + ss->killers[0] = bestMove; + } + + // Increase history value of the cut-off move and decrease all the other + // played non-capture moves. + Value bonus = Value(int(depth) * int(depth)); + History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); + for (int i = 0; i < quietCount - 1; i++) + { + Move m = quietsSearched[i]; + History.update(pos.piece_moved(m), to_sq(m), -bonus); + } + + if (is_ok((ss-1)->currentMove)) + Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove); + } + + 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(NT == PV || NT == NonPV); + assert(InCheck == !!pos.checkers()); + assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); + assert(PvNode || (alpha == beta - 1)); + assert(depth <= DEPTH_ZERO); + + StateInfo st; + const TTEntry* tte; + Key posKey; + Move ttMove, move, bestMove; + Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + bool givesCheck, evasionPrunable; + Depth ttDepth; + + // To flag BOUND_EXACT a node with eval above alpha and no available moves + if (PvNode) + oldAlpha = alpha; + + ss->currentMove = bestMove = MOVE_NONE; + ss->ply = (ss-1)->ply + 1; + + // Check for an instant draw or maximum ply reached + if (pos.is_draw() || ss->ply > MAX_PLY) + return DrawValue[pos.side_to_move()]; + + // 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); + ttMove = tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_NONE; + + if ( tte + && tte->depth() >= ttDepth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->bound() == BOUND_EXACT + : ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) + { + ss->currentMove = ttMove; // Can be MOVE_NONE + return ttValue; + } + + // Evaluate the position statically + if (InCheck) + { + ss->staticEval = ss->evalMargin = VALUE_NONE; + bestValue = futilityBase = -VALUE_INFINITE; + } + else + { + if (tte) + { + // Never assume anything on values stored in TT + if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); + } + else + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); + + // Stand pat. Return immediately if static value is at least beta + if (bestValue >= beta) + { + if (!tte) + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, + DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin); + + return bestValue; + } + + if (PvNode && bestValue > alpha) + alpha = bestValue; + + futilityBase = ss->staticEval + ss->evalMargin + Value(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, History, to_sq((ss-1)->currentMove)); + CheckInfo ci(pos); + + // 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 = pos.move_gives_check(move, ci); + + // Futility pruning + if ( !PvNode + && !InCheck + && !givesCheck + && move != ttMove + && type_of(move) != PROMOTION + && !pos.is_passed_pawn_push(move)) + { + futilityValue = futilityBase + + PieceValue[EG][pos.piece_on(to_sq(move))] + + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO); + + if (futilityValue < beta) + { + bestValue = std::max(bestValue, futilityValue); + continue; + } + + // Prune moves with negative or equal SEE and also moves with positive + // SEE where capturing piece loses a tempo and SEE < beta - futilityBase. + if ( futilityBase < beta + && pos.see(move, beta - futilityBase) <= 0) + { + bestValue = std::max(bestValue, futilityBase); + continue; + } + } + + // Detect non-capture evasions that are candidate to be pruned + evasionPrunable = InCheck + && bestValue > VALUE_MATED_IN_MAX_PLY + && !pos.is_capture(move) + && !pos.can_castle(pos.side_to_move()); + + // Don't search moves with negative SEE values + if ( !PvNode + && (!InCheck || evasionPrunable) + && move != ttMove + && type_of(move) != PROMOTION + && pos.see_sign(move) < 0) + continue; + + // Don't search useless checks + if ( !PvNode + && !InCheck + && givesCheck + && move != ttMove + && !pos.is_capture_or_promotion(move) + && ss->staticEval + PawnValueMg / 4 < beta + && !check_is_dangerous(pos, move, futilityBase, beta)) + continue; + + // Check for legality only before to do the move + if (!pos.pl_move_is_legal(move, ci.pinned)) + continue; + + ss->currentMove = move; + + // Make and search the move + pos.do_move(move, st, ci, 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 new best move + if (value > bestValue) + { + bestValue = value; + + if (value > alpha) + { + if (PvNode && value < beta) // Update alpha here! Always alpha < beta + { + alpha = value; + bestMove = move; + } + else // Fail high + { + TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, + ttDepth, move, ss->staticEval, ss->evalMargin); + + 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 + + TT.store(posKey, value_to_tt(bestValue, ss->ply), + PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + ttDepth, bestMove, ss->staticEval, ss->evalMargin); + + 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 to 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 (where 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; + } + + + // check_is_dangerous() tests if a checking move can be pruned in qsearch() + + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) + { + Piece pc = pos.piece_moved(move); + Square from = from_sq(move); + Square to = to_sq(move); + Color them = ~pos.side_to_move(); + Square ksq = pos.king_square(them); + Bitboard enemies = pos.pieces(them); + Bitboard kingAtt = pos.attacks_from(ksq); + Bitboard occ = pos.pieces() ^ from ^ ksq; + Bitboard oldAtt = pos.attacks_from(pc, from, occ); + Bitboard newAtt = pos.attacks_from(pc, to, occ); + + // Checks which give opponent's king at most one escape square are dangerous + if (!more_than_one(kingAtt & ~(enemies | newAtt | to))) + return true; + + // Queen contact check is very dangerous + if (type_of(pc) == QUEEN && (kingAtt & to)) + return true; + + // Creating new double threats with checks is dangerous + Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt; + while (b) + { + // Note that here we generate illegal "double move"! + if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta) + return true; + } + + return false; + } + + + // allows() tests whether the 'first' move at previous ply somehow makes the + // 'second' move possible, for instance if the moving piece is the same in + // both moves. Normally the second move is the threat (the best move returned + // from a null search that fails low). + + bool allows(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); + assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // The piece is the same or second's destination was vacated by the first move + if (m1to == m2from || m2to == m1from) + return true; + + // Second one moves through the square vacated by first one + if (between_bb(m2from, m2to) & m1from) + return true; + + // Second's destination is defended by the first move's piece + Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from); + if (m1att & m2to) + return true; + + // Second move gives a discovered check through the first's checking piece + if (m1att & pos.king_square(pos.side_to_move())) + { + assert(between_bb(m1to, pos.king_square(pos.side_to_move())) & m2from); + return true; + } + + return false; + } + + + // refutes() tests whether a 'first' move is able to defend against a 'second' + // opponent's move. In this case will not be pruned. Normally the second move + // is the threat (the best move returned from a null search that fails low). + + bool refutes(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // Don't prune moves of the threatened piece + if (m1from == m2to) + return true; + + // If the threatened piece has value less than or equal to the value of the + // threat piece, don't prune moves which defend it. + if ( pos.is_capture(second) + && ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)] + || type_of(pos.piece_on(m2from)) == KING)) + { + // Update occupancy as if the piece and the threat are moving + Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from; + Piece pc = pos.piece_on(m1from); + + // The moved piece attacks the square 'tto' ? + if (pos.attacks_from(pc, m1to, occ) & m2to) + return true; + + // Scan for possible X-ray attackers behind the moved piece + Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK)) + | (attacks_bb(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP)); + + // Verify attackers are triggered by our move and not already existing + if (unlikely(xray) && (xray & ~pos.attacks_from(m2to))) + return true; + } + + // Don't prune safe moves which block the threat path + if ((between_bb(m2from, m2to) & m1to) && pos.see_sign(first) >= 0) + return true; + + return false; + } + + + // When playing with strength handicap choose best move among the MultiPV set + // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. + + Move Skill::pick_move() { + + static RKISS rk; + + // PRNG sequence should be not deterministic + for (int i = Time::now() % 50; i > 0; i--) + rk.rand(); + + // RootMoves are already sorted by score in descending order + int variance = std::min(RootMoves[0].score - RootMoves[PVSize - 1].score, PawnValueMg); + int weakness = 120 - 2 * level; + int max_s = -VALUE_INFINITE; + best = MOVE_NONE; + + // Choose best move. For each move score we add two terms both dependent on + // weakness, one deterministic and bigger for weaker moves, and one random, + // then we choose the move with the resulting highest score. + for (size_t i = 0; i < PVSize; i++) + { + int s = RootMoves[i].score; + + // Don't allow crazy blunders even at very low skills + if (i > 0 && RootMoves[i-1].score > s + 2 * PawnValueMg) + break; + + // This is our magic formula + s += ( weakness * int(RootMoves[0].score - s) + + variance * (rk.rand() % weakness)) / 128; + + if (s > max_s) + { + max_s = s; + best = RootMoves[i].pv[0]; + } + } + return best; + } + + + // uci_pv() formats PV information according to UCI protocol. UCI requires + // to send all the PV lines also if are still to be searched and so refer to + // the previous search score. + + string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { + + std::stringstream s; + Time::point elapsed = Time::now() - SearchTime + 1; + size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); + int selDepth = 0; + + for (Thread* th : Threads) + if (th->maxPly > selDepth) + selDepth = th->maxPly; + + for (size_t i = 0; i < uciPVSize; i++) + { + bool updated = (i <= PVIdx); + + if (depth == 1 && !updated) + continue; + + int d = updated ? depth : depth - 1; + Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; + + if (s.rdbuf()->in_avail()) // Not at first line + s << "\n"; + + s << "info depth " << d + << " seldepth " << selDepth + << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << pos.nodes_searched() * 1000 / elapsed + << " time " << elapsed + << " multipv " << i + 1 + << " pv"; + + for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) + s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960()); + } + + return s.str(); + } + +} // namespace + + +/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. +/// We consider also failing high nodes and not only BOUND_EXACT nodes so to +/// allow to always have a ponder move even when we fail high at root, and a +/// long PV to print that is important for position analysis. + +void RootMove::extract_pv_from_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_6], *st = state; + const TTEntry* tte; + int ply = 0; + Move m = pv[0]; + + pv.clear(); + + do { + pv.push_back(m); + + assert(MoveList(pos).contains(pv[ply])); + + pos.do_move(pv[ply++], *st++); + tte = TT.probe(pos.key()); + + } while ( tte + && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change + && pos.pl_move_is_legal(m, pos.pinned_pieces()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)); + + pv.push_back(MOVE_NONE); // Must be zero-terminating + + while (ply) pos.undo_move(pv[--ply]); +} + + +/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and +/// inserts the PV back into the TT. This makes sure the old PV moves are searched +/// first, even if the old TT entries have been overwritten. + +void RootMove::insert_pv_in_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_6], *st = state; + const TTEntry* tte; + int ply = 0; + + do { + tte = TT.probe(pos.key()); + + if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries + TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE); + + assert(MoveList(pos).contains(pv[ply])); + + pos.do_move(pv[ply++], *st++); + + } while (pv[ply] != MOVE_NONE); + + while (ply) pos.undo_move(pv[--ply]); +} + + +/// Thread::idle_loop() is where the thread is parked when it has no work to do + +void Thread::idle_loop() { + + // Pointer 'this_sp' is not null only if we are called from split(), and not + // at the thread creation. So it means we are the split point's master. + SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : nullptr; + + assert(!this_sp || (this_sp->masterThread == this && searching)); + + while (true) + { + // If we are not searching, wait for a condition to be signaled instead of + // wasting CPU time polling for work. + while ((!searching && Threads.sleepWhileIdle) || exit) + { + if (exit) + { + assert(!this_sp); + return; + } + + // Grab the lock to avoid races with Thread::notify_one() + std::unique_lock lk(mutex); + + // If we are master and all slaves have finished then exit idle_loop + if (this_sp && !this_sp->slavesMask) + break; + + // Do sleep after retesting sleep conditions under lock protection, in + // particular we need to avoid a deadlock in case a master thread has, + // in the meanwhile, allocated us and sent the notify_one() call before + // we had the chance to grab the lock. + if (!searching && !exit) + sleepCondition.wait(lk); + } + + // If this thread has been assigned work, launch a search + if (searching) + { + assert(!exit); + + Threads.mutex.lock(); + + assert(searching); + assert(activeSplitPoint); + SplitPoint* sp = activeSplitPoint; + + Threads.mutex.unlock(); + + Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + Position pos(*sp->pos, this); + + std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); + ss->splitPoint = sp; + + sp->mutex.lock(); + + assert(activePosition == nullptr); + + activePosition = &pos; + + switch (sp->nodeType) { + case Root: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + case PV: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + case NonPV: + search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); + break; + default: + assert(false); + } + + assert(searching); + + searching = false; + activePosition = nullptr; + sp->slavesMask &= ~(1ULL << idx); + sp->nodes += pos.nodes_searched(); + + // Wake up master thread so to allow it to return from the idle loop + // in case we are the last slave of the split point. + if ( Threads.sleepWhileIdle + && this != sp->masterThread + && !sp->slavesMask) + { + assert(!sp->masterThread->searching); + sp->masterThread->notify_one(); + } + + // After releasing the lock we cannot access anymore any SplitPoint + // related data in a safe way becuase it could have been released under + // our feet by the sp master. Also accessing other Thread objects is + // unsafe because if we are exiting there is a chance are already freed. + sp->mutex.unlock(); + } + + // If this thread is the master of a split point and all slaves have finished + // their work at this split point, return from the idle loop. + if (this_sp && !this_sp->slavesMask) + { + this_sp->mutex.lock(); + bool finished = !this_sp->slavesMask; // Retest under lock protection + this_sp->mutex.unlock(); + if (finished) + return; + } + } +} + + +/// check_time() is called by the timer thread when the timer triggers. It is +/// used to print debug info and, more important, to detect when we are out of +/// available time and so stop the search. + +void check_time() { + + static Time::point lastInfoTime = Time::now(); + int64_t nodes = 0; // Workaround silly 'uninitialized' gcc warning + + if (Time::now() - lastInfoTime >= 1000) + { + lastInfoTime = Time::now(); + dbg_print(); + } + + if (Limits.ponder) + return; + + if (Limits.nodes) + { + Threads.mutex.lock(); + + nodes = RootPos.nodes_searched(); + + // Loop across all split points and sum accumulated SplitPoint nodes plus + // all the currently active positions nodes. + for (Thread* th : Threads) + for (int i = 0; i < th->splitPointsSize; i++) + { + SplitPoint& sp = th->splitPoints[i]; + + sp.mutex.lock(); + + nodes += sp.nodes; + Bitboard sm = sp.slavesMask; + while (sm) + { + Position* pos = Threads[pop_lsb(&sm)]->activePosition; + if (pos) + nodes += pos->nodes_searched(); + } + + sp.mutex.unlock(); + } + + Threads.mutex.unlock(); + } + + Time::point elapsed = Time::now() - SearchTime; + bool stillAtFirstMove = Signals.firstRootMove + && !Signals.failedLowAtRoot + && elapsed > TimeMgr.available_time(); + + bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution + || stillAtFirstMove; + + if ( (Limits.use_time_management() && noMoreTime) + || (Limits.movetime && elapsed >= Limits.movetime) + || (Limits.nodes && nodes >= Limits.nodes)) + Signals.stop = true; +} diff --git a/stockfish-4-win/src_c++11/search.h b/stockfish-4-win/src_c++11/search.h new file mode 100644 index 0000000000000000000000000000000000000000..bcf75780c0481a9e8266eb73cc35835897ef7504 --- /dev/null +++ b/stockfish-4-win/src_c++11/search.h @@ -0,0 +1,112 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 +#include + +#include "misc.h" +#include "position.h" +#include "types.h" + +struct SplitPoint; + +namespace Search { + +/// The 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 { + SplitPoint* splitPoint; + int ply; + Move currentMove; + Move excludedMove; + Move killers[2]; + Depth reduction; + Value staticEval; + Value evalMargin; + int skipNullMove; + int futilityMoveCount; +}; + + +/// RootMove struct is used for moves at the root of the tree. For each root +/// move we store a score, a node count, and a PV (really a refutation in the +/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for +/// all non-pv moves. +struct RootMove { + + RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { + pv.push_back(m); pv.push_back(MOVE_NONE); + } + + bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort + bool operator==(const Move& m) const { return pv[0] == m; } + + void extract_pv_from_tt(Position& pos); + void insert_pv_in_tt(Position& pos); + + Value score; + Value prevScore; + std::vector pv; +}; + + +/// The 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 is our opponent's side to move. + +struct LimitsType { + + LimitsType() { std::memset(this, 0, sizeof(LimitsType)); } + bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } + + int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; +}; + + +/// The SignalsType struct stores volatile flags updated during the search +/// typically in an async fashion, for instance to stop the search by the GUI. + +struct SignalsType { + bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot; +}; + +typedef std::unique_ptr> StateStackPtr; + +extern volatile SignalsType Signals; +extern LimitsType Limits; +extern std::vector RootMoves; +extern Position RootPos; +extern Color RootColor; +extern Time::point SearchTime; +extern StateStackPtr SetupStates; + +extern void init(); +extern size_t perft(Position& pos, Depth depth); +extern void think(); + +} // namespace Search + +#endif // #ifndef SEARCH_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/thread.cpp b/stockfish-4-win/src_c++11/thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7782031e2e820c6c02d64aa56bfd42a273026d7d --- /dev/null +++ b/stockfish-4-win/src_c++11/thread.cpp @@ -0,0 +1,382 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "ucioption.h" + +using namespace Search; + +ThreadPool Threads; // Global object + +namespace { + + // Helpers to launch a thread after creation and joining before delete. Must be + // outside Thread c'tor and d'tor because object shall be fully initialized + // when virtual idle_loop() is called and when joining. + template T* new_thread() { + T* th = new T(); + th->nativeThread = std::thread(&ThreadBase::idle_loop, th); // Will go to sleep + return th; + } + + void delete_thread(ThreadBase* th) { + th->exit = true; // Search must be already finished + th->notify_one(); + th->nativeThread.join(); // Wait for thread termination + delete th; + } + +} + +// ThreadBase::notify_one() wakes up the thread when there is some search to do + +void ThreadBase::notify_one() { + + std::unique_lock(this->mutex); + sleepCondition.notify_one(); +} + + +// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true + +void ThreadBase::wait_for(volatile const bool& b) { + + std::unique_lock lk(mutex); + sleepCondition.wait(lk, [&]{ return b; }); +} + + +// Thread c'tor just inits data but does not launch any thread of execution that +// instead will be started only upon c'tor returns. + +Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC + + searching = false; + maxPly = splitPointsSize = 0; + activeSplitPoint = nullptr; + activePosition = nullptr; + idx = Threads.size(); +} + + +// TimerThread::idle_loop() is where the timer thread waits msec milliseconds +// and then calls check_time(). If msec is 0 thread sleeps until is woken up. +extern void check_time(); + +void TimerThread::idle_loop() { + + while (!exit) + { + std::unique_lock lk(mutex); + + if (!exit) + sleepCondition.wait_for(lk, std::chrono::milliseconds(msec ? msec : INT_MAX)); + + lk.unlock(); + + if (msec) + check_time(); + } +} + + +// MainThread::idle_loop() is where the main thread is parked waiting to be started +// when there is a new search. Main thread will launch all the slave threads. + +void MainThread::idle_loop() { + + while (true) + { + std::unique_lock lk(mutex); + + thinking = false; + + while (!thinking && !exit) + { + Threads.sleepCondition.notify_one(); // Wake up UI thread if needed + sleepCondition.wait(lk); + } + + lk.unlock(); + + if (exit) + return; + + searching = true; + + Search::think(); + + assert(searching); + + searching = false; + } +} + + +// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the +// current active split point, or in some ancestor of the split point. + +bool Thread::cutoff_occurred() const { + + for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint) + if (sp->cutoff) + return true; + + return false; +} + + +// Thread::is_available_to() checks whether the thread is available to help the +// thread 'master' at a split point. An obvious requirement is that thread must +// be idle. With more than two threads, this is not sufficient: If the thread is +// the master of some split point, it is only available as a slave to the slaves +// which are busy searching the split point at the top of slaves split point +// stack (the "helpful master concept" in YBWC terminology). + +bool Thread::is_available_to(const Thread* master) const { + + if (searching) + return false; + + // Make a local copy to be sure doesn't become zero under our feet while + // testing next condition and so leading to an out of bound access. + int size = splitPointsSize; + + // No split points means that the thread is available as a slave for any + // other thread otherwise apply the "helpful master" concept if possible. + return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx)); +} + + +// init() is called at startup to create and launch requested threads, that will +// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use +// a c'tor becuase Threads is a static object and we need a fully initialized +// engine at this point due to allocation of Endgames in Thread c'tor. + +void ThreadPool::init() { + + sleepWhileIdle = true; + timer = new_thread(); + push_back(new_thread()); + read_uci_options(); +} + + +// exit() cleanly terminates the threads before the program exits + +void ThreadPool::exit() { + + delete_thread(timer); // As first because check_time() accesses threads data + + for (Thread* th : *this) + delete_thread(th); +} + + +// read_uci_options() updates internal threads parameters from the corresponding +// UCI options and creates/destroys threads to match the requested number. Thread +// objects are dynamically allocated to avoid creating in advance all possible +// threads, with included pawns and material tables, if only few are used. + +void ThreadPool::read_uci_options() { + + maxThreadsPerSplitPoint = Options["Max Threads per Split Point"]; + minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; + size_t requested = Options["Threads"]; + + assert(requested > 0); + + // Value 0 has a special meaning: We determine the optimal minimum split depth + // automatically. Anyhow the minimumSplitDepth should never be under 4 plies. + if (!minimumSplitDepth) + minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY; + else + minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth); + + while (size() < requested) + push_back(new_thread()); + + while (size() > requested) + { + delete_thread(back()); + pop_back(); + } +} + + +// slave_available() tries to find an idle thread which is available as a slave +// for the thread 'master'. + +Thread* ThreadPool::available_slave(const Thread* master) const { + + for (Thread* th : *this) + if (th->is_available_to(master)) + return th; + + return nullptr; +} + + +// split() does the actual work of distributing the work at a node between +// several available threads. If it does not succeed in splitting the node +// (because no idle threads are available), the function immediately returns. +// If splitting is possible, a SplitPoint object is initialized with all the +// data that must be copied to the helper threads and then helper threads are +// told that they have been assigned work. This will cause them to instantly +// leave their idle loops and call search(). When all threads have returned from +// search() then split() returns. + +template +void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, + Move* bestMove, Depth depth, Move threatMove, int moveCount, + MovePicker* movePicker, int nodeType, bool cutNode) { + + assert(pos.pos_is_ok()); + assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); + assert(*bestValue > -VALUE_INFINITE); + assert(depth >= Threads.minimumSplitDepth); + assert(searching); + assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); + + // Pick the next available split point from the split point stack + SplitPoint& sp = splitPoints[splitPointsSize]; + + sp.masterThread = this; + sp.parentSplitPoint = activeSplitPoint; + sp.slavesMask = 1ULL << idx; + sp.depth = depth; + sp.bestValue = *bestValue; + sp.bestMove = *bestMove; + sp.threatMove = threatMove; + sp.alpha = alpha; + sp.beta = beta; + sp.nodeType = nodeType; + sp.cutNode = cutNode; + sp.movePicker = movePicker; + sp.moveCount = moveCount; + sp.pos = &pos; + sp.nodes = 0; + sp.cutoff = false; + sp.ss = ss; + + // Try to allocate available threads and ask them to start searching setting + // 'searching' flag. This must be done under lock protection to avoid concurrent + // allocation of the same slave by another master. + Threads.mutex.lock(); + sp.mutex.lock(); + + splitPointsSize++; + activeSplitPoint = &sp; + activePosition = nullptr; + + size_t slavesCnt = 1; // This thread is always included + Thread* slave; + + while ( (slave = Threads.available_slave(this)) != nullptr + && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) + { + sp.slavesMask |= 1ULL << slave->idx; + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping + } + + // Everything is set up. The master thread enters the idle loop, from which + // it will instantly launch a search, because its 'searching' flag is set. + // The thread will return from the idle loop when all slaves have finished + // their work at this split point. + if (slavesCnt > 1 || Fake) + { + sp.mutex.unlock(); + Threads.mutex.unlock(); + + Thread::idle_loop(); // Force a call to base class idle_loop() + + // In helpful master concept a master can help only a sub-tree of its split + // point, and because here is all finished is not possible master is booked. + assert(!searching); + assert(!activePosition); + + // We have returned from the idle loop, which means that all threads are + // finished. Note that setting 'searching' and decreasing splitPointsSize is + // done under lock protection to avoid a race with Thread::is_available_to(). + Threads.mutex.lock(); + sp.mutex.lock(); + } + + searching = true; + splitPointsSize--; + activeSplitPoint = sp.parentSplitPoint; + activePosition = &pos; + pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); + *bestMove = sp.bestMove; + *bestValue = sp.bestValue; + + sp.mutex.unlock(); + Threads.mutex.unlock(); +} + +// Explicit template instantiations +template void Thread::split(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); +template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool); + + +// wait_for_think_finished() waits for main thread to go to sleep then returns + +void ThreadPool::wait_for_think_finished() { + + std::unique_lock lk(main()->mutex); + sleepCondition.wait(lk, [&]{ return !main()->thinking; }); +} + + +// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop() +// so to start a new search, then returns immediately. + +void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, + const std::vector& searchMoves, StateStackPtr& states) { + wait_for_think_finished(); + + SearchTime = Time::now(); // As early as possible + + Signals.stopOnPonderhit = Signals.firstRootMove = false; + Signals.stop = Signals.failedLowAtRoot = false; + + RootMoves.clear(); + RootPos = pos; + Limits = limits; + if (states.get()) // If we don't set a new position, preserve current state + { + SetupStates = std::move(states); // Ownership transfer here + assert(!states.get()); + } + + for (const ExtMove& ms : MoveList(pos)) + if ( searchMoves.empty() + || std::count(searchMoves.begin(), searchMoves.end(), ms.move)) + RootMoves.push_back(RootMove(ms.move)); + + main()->thinking = true; + main()->notify_one(); // Starts main thread +} diff --git a/stockfish-4-win/src_c++11/thread.h b/stockfish-4-win/src_c++11/thread.h new file mode 100644 index 0000000000000000000000000000000000000000..e24cd2764fc6fd41cf479deb2611ec257e009ebb --- /dev/null +++ b/stockfish-4-win/src_c++11/thread.h @@ -0,0 +1,156 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "material.h" +#include "movepick.h" +#include "pawns.h" +#include "position.h" +#include "search.h" + +const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t +const int MAX_SPLITPOINTS_PER_THREAD = 8; + +struct Thread; + +struct SplitPoint { + + // Const data after split point has been setup + const Position* pos; + const Search::Stack* ss; + Thread* masterThread; + Depth depth; + Value beta; + int nodeType; + Move threatMove; + bool cutNode; + + // Const pointers to shared data + MovePicker* movePicker; + SplitPoint* parentSplitPoint; + + // Shared data + std::mutex mutex; + volatile uint64_t slavesMask; + volatile int64_t nodes; + volatile Value alpha; + volatile Value bestValue; + volatile Move bestMove; + volatile int moveCount; + volatile bool cutoff; +}; + + +/// ThreadBase struct is the base of the hierarchy from where we derive all the +/// specialized thread classes. + +struct ThreadBase { + + ThreadBase() : exit(false) {} + virtual ~ThreadBase() {} + virtual void idle_loop() = 0; + void notify_one(); + void wait_for(volatile const bool& b); + + std::thread nativeThread; + std::mutex mutex; + std::condition_variable sleepCondition; + volatile bool exit; +}; + + +/// Thread struct keeps together all the thread related stuff like locks, state +/// and especially split points. 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. + +struct Thread : public ThreadBase { + + Thread(); + virtual void idle_loop(); + bool cutoff_occurred() const; + bool is_available_to(const Thread* master) const; + + template + void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, + Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); + + SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; + Material::Table materialTable; + Endgames endgames; + Pawns::Table pawnsTable; + Position* activePosition; + size_t idx; + int maxPly; + SplitPoint* volatile activeSplitPoint; + volatile int splitPointsSize; + volatile bool searching; +}; + + +/// MainThread and TimerThread are derived classes used to characterize the two +/// special threads: the main one and the recurring timer. + +struct MainThread : public Thread { + MainThread() : thinking(true) {} // Avoid a race with start_thinking() + virtual void idle_loop(); + volatile bool thinking; +}; + +struct TimerThread : public ThreadBase { + TimerThread() : msec(0) {} + virtual void idle_loop(); + int msec; +}; + + +/// ThreadPool struct handles all the threads related stuff like init, starting, +/// parking and, the most important, launching a slave thread at a split point. +/// All the access to shared thread data is done through this class. + +struct ThreadPool : public std::vector { + + void init(); // No c'tor and d'tor, threads rely on globals that should + void exit(); // be initialized and valid during the whole thread lifetime. + + MainThread* main() { return static_cast((*this)[0]); } + void read_uci_options(); + Thread* available_slave(const Thread* master) const; + void wait_for_think_finished(); + void start_thinking(const Position&, const Search::LimitsType&, + const std::vector&, Search::StateStackPtr&); + + bool sleepWhileIdle; + Depth minimumSplitDepth; + size_t maxThreadsPerSplitPoint; + std::mutex mutex; + std::condition_variable sleepCondition; + TimerThread* timer; +}; + +extern ThreadPool Threads; + +#endif // #ifndef THREAD_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/timeman.cpp b/stockfish-4-win/src_c++11/timeman.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d2bacca3c10c4d20e73c8f59ddaaf0be85fe12c --- /dev/null +++ b/stockfish-4-win/src_c++11/timeman.cpp @@ -0,0 +1,162 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "search.h" +#include "timeman.h" +#include "ucioption.h" + +namespace { + + /// Constants + + const int MoveHorizon = 50; // Plan time management at most this many moves ahead + const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio + const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio + + + // MoveImportance[] is 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 CCRL game database with some simple filtering criteria. + const int MoveImportance[512] = { + 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, + 7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751, + 7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425, + 7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651, + 6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462, + 5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155, + 4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011, + 2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192, + 2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605, + 1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180, + 1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888, + 878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641, + 634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460, + 450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333, + 328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240, + 235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170, + 170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118, + 118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80, + 80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54, + 52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33, + 31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20, + 19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11, + 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1 }; + + int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; } + + + /// Function Prototypes + + enum TimeType { OptimumTime, MaxTime }; + + template + int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover); +} + + +void TimeManager::pv_instability(int curChanges, int prevChanges) { + + unstablePVExtraTime = curChanges * (optimumSearchTime / 2) + + prevChanges * (optimumSearchTime / 3); +} + + +void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us) +{ + /* We support four different kind of time controls: + + increment == 0 && movesToGo == 0 means: x basetime [sudden death!] + increment == 0 && movesToGo != 0 means: x moves in y minutes + increment > 0 && movesToGo == 0 means: x basetime + z increment + increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment + + Time management is adjusted by following UCI parameters: + + emergencyMoveHorizon: Be prepared to always play at least this many moves + emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock + emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move + minThinkingTime : No matter what, use at least this much thinking before doing the move + */ + + int hypMTG, hypMyTime, t1, t2; + + // Read uci parameters + int emergencyMoveHorizon = Options["Emergency Move Horizon"]; + int emergencyBaseTime = Options["Emergency Base Time"]; + int emergencyMoveTime = Options["Emergency Move Time"]; + int minThinkingTime = Options["Minimum Thinking Time"]; + int slowMover = Options["Slow Mover"]; + + // Initialize to maximum values but unstablePVExtraTime that is reset + unstablePVExtraTime = 0; + optimumSearchTime = maximumSearchTime = limits.time[us]; + + // We calculate optimum time usage for different hypothetic "moves to go"-values and choose the + // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. + for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++) + { + // Calculate thinking time for hypothetic "moves to go"-value + hypMyTime = limits.time[us] + + limits.inc[us] * (hypMTG - 1) + - emergencyBaseTime + - emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon); + + hypMyTime = std::max(hypMyTime, 0); + + t1 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); + t2 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); + + optimumSearchTime = std::min(optimumSearchTime, t1); + maximumSearchTime = std::min(maximumSearchTime, t2); + } + + if (Options["Ponder"]) + optimumSearchTime += optimumSearchTime / 4; + + // Make sure that maxSearchTime is not over absoluteMaxSearchTime + optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); +} + + +namespace { + + template + int remaining(int myTime, int movesToGo, int currentPly, int slowMover) + { + const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); + const float TStealRatio = (T == OptimumTime ? 0 : StealRatio); + + int thisMoveImportance = move_importance(currentPly) * slowMover / 100; + int otherMovesImportance = 0; + + for (int i = 1; i < movesToGo; i++) + otherMovesImportance += move_importance(currentPly + 2 * i); + + float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance); + float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance); + + return int(floor(myTime * std::min(ratio1, ratio2))); + } +} diff --git a/stockfish-4-win/src_c++11/timeman.h b/stockfish-4-win/src_c++11/timeman.h new file mode 100644 index 0000000000000000000000000000000000000000..e12b0007f302f37f07cb40ea8c5f6fce07a3f615 --- /dev/null +++ b/stockfish-4-win/src_c++11/timeman.h @@ -0,0 +1,39 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +/// The TimeManager class computes the optimal time to think depending on the +/// maximum available time, the move game number and other parameters. + +class TimeManager { +public: + void init(const Search::LimitsType& limits, int currentPly, Color us); + void pv_instability(int curChanges, int prevChanges); + int available_time() const { return optimumSearchTime + unstablePVExtraTime; } + int maximum_time() const { return maximumSearchTime; } + +private: + int optimumSearchTime; + int maximumSearchTime; + int unstablePVExtraTime; +}; + +#endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/tt.cpp b/stockfish-4-win/src_c++11/tt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b447230fbdf0ee533c9da9fcf754ee7f13e62c08 --- /dev/null +++ b/stockfish-4-win/src_c++11/tt.cpp @@ -0,0 +1,121 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "tt.h" + +TranspositionTable TT; // Our global transposition table + + +/// TranspositionTable::set_size() 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::set_size(size_t mbSize) { + + assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); + + uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + + if (hashMask == size - ClusterSize) + return; + + hashMask = size - ClusterSize; + free(mem); + mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1); + + if (!mem) + { + std::cerr << "Failed to allocate " << mbSize + << "MB for transposition table." << std::endl; + exit(EXIT_FAILURE); + } + + table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); +} + + +/// TranspositionTable::clear() overwrites the entire transposition table +/// with zeroes. It is called whenever the table is resized, or when the +/// user asks the program to clear the table (from the UCI interface). + +void TranspositionTable::clear() { + + std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); +} + + +/// TranspositionTable::probe() looks up the current position in the +/// transposition table. Returns a pointer to the TTEntry or NULL if +/// position is not found. + +const TTEntry* TranspositionTable::probe(const Key key) const { + + const TTEntry* tte = first_entry(key); + uint32_t key32 = key >> 32; + + for (unsigned i = 0; i < ClusterSize; i++, tte++) + if (tte->key() == key32) + return tte; + + return nullptr; +} + + +/// TranspositionTable::store() writes a new entry containing position key and +/// valuable information of current position. The lowest order bits of position +/// key are used to decide on which cluster the position will be placed. +/// When a new entry is written and there are no empty entries available in cluster, +/// it replaces the least valuable of entries. A TTEntry t1 is considered to be +/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from +/// a previous search, or if the depth of t1 is bigger than the depth of t2. + +void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) { + + int c1, c2, c3; + TTEntry *tte, *replace; + uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster + + tte = replace = first_entry(key); + + for (unsigned i = 0; i < ClusterSize; i++, tte++) + { + if (!tte->key() || tte->key() == key32) // Empty or overwrite old + { + if (!m) + m = tte->move(); // Preserve any existing ttMove + + replace = tte; + break; + } + + // Implement replace strategy + c1 = (replace->generation() == generation ? 2 : 0); + c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0); + c3 = (tte->depth() < replace->depth() ? 1 : 0); + + if (c1 + c2 + c3 > 0) + replace = tte; + } + + replace->save(key32, v, b, d, m, generation, statV, evalM); +} diff --git a/stockfish-4-win/src_c++11/tt.h b/stockfish-4-win/src_c++11/tt.h new file mode 100644 index 0000000000000000000000000000000000000000..3113751adb938e868616efe6a8eb78c15e946b43 --- /dev/null +++ b/stockfish-4-win/src_c++11/tt.h @@ -0,0 +1,118 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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" + +/// The TTEntry is the 128 bit transposition table entry, defined as below: +/// +/// key: 32 bit +/// move: 16 bit +/// bound type: 8 bit +/// generation: 8 bit +/// value: 16 bit +/// depth: 16 bit +/// static value: 16 bit +/// static margin: 16 bit + +struct TTEntry { + + void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { + + key32 = (uint32_t)k; + move16 = (uint16_t)m; + bound8 = (uint8_t)b; + generation8 = (uint8_t)g; + value16 = (int16_t)v; + depth16 = (int16_t)d; + evalValue = (int16_t)ev; + evalMargin = (int16_t)em; + } + void set_generation(uint8_t g) { generation8 = g; } + + uint32_t key() const { return key32; } + Depth depth() const { return (Depth)depth16; } + Move move() const { return (Move)move16; } + Value value() const { return (Value)value16; } + Bound bound() const { return (Bound)bound8; } + int generation() const { return (int)generation8; } + Value eval_value() const { return (Value)evalValue; } + Value eval_margin() const { return (Value)evalMargin; } + +private: + uint32_t key32; + uint16_t move16; + uint8_t bound8, generation8; + int16_t value16, depth16, evalValue, evalMargin; +}; + + +/// 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. Size of a cluster shall not be +/// bigger than a cache line size. In case it is less, it should be padded to +/// guarantee always aligned accesses. + +class TranspositionTable { + + static const unsigned ClusterSize = 4; // A cluster is 64 Bytes + +public: + ~TranspositionTable() { free(mem); } + void new_search() { generation++; } + + const TTEntry* probe(const Key key) const; + TTEntry* first_entry(const Key key) const; + void refresh(const TTEntry* tte) const; + void set_size(size_t mbSize); + void clear(); + void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); + +private: + uint32_t hashMask; + TTEntry* table; + void* mem; + uint8_t generation; // Size must be not bigger than TTEntry::generation8 +}; + +extern TranspositionTable TT; + + +/// TranspositionTable::first_entry() returns a pointer to the first entry of +/// a cluster given a position. The lowest order bits of the key are used to +/// get the index of the cluster. + +inline TTEntry* TranspositionTable::first_entry(const Key key) const { + + return table + ((uint32_t)key & hashMask); +} + + +/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry +/// to avoid aging. Normally called after a TT hit. + +inline void TranspositionTable::refresh(const TTEntry* tte) const { + + const_cast(tte)->set_generation(generation); +} + +#endif // #ifndef TT_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/types.h b/stockfish-4-win/src_c++11/types.h new file mode 100644 index 0000000000000000000000000000000000000000..e92e8459463345a6c120f81c83a42a4d91a9c2de --- /dev/null +++ b/stockfish-4-win/src_c++11/types.h @@ -0,0 +1,442 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 + +/// For Linux and OSX configuration is done automatically using Makefile. To get +/// started type 'make help'. +/// +/// For Windows, part of the configuration is detected automatically, but some +/// switches need to be set manually: +/// +/// -DNDEBUG | Disable debugging mode. Use always. +/// +/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want +/// | the executable to run on some very old machines. +/// +/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works +/// | only in 64-bit mode. For compiling requires hardware with +/// | popcnt 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 + +#define unlikely(x) (x) // For code annotation purposes + +#if defined(_WIN64) && !defined(IS_64BIT) +# include // MSVC popcnt and bsfq instrinsics +# define IS_64BIT +# define USE_BSFQ +#endif + +#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER) +# include // Intel header for _mm_popcnt_u64() intrinsic +#endif + +# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) +# include // Intel and Microsoft header for _mm_prefetch() +# endif + +#define CACHE_LINE_SIZE 64 +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) +# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE)) +#else +# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE))) +#endif + +#ifdef _MSC_VER +# define FORCE_INLINE __forceinline +#elif defined(__GNUC__) +# define FORCE_INLINE inline __attribute__((always_inline)) +#else +# define FORCE_INLINE inline +#endif + +#ifdef USE_POPCNT +const bool HasPopCnt = true; +#else +const bool HasPopCnt = 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 = 192; +const int MAX_PLY = 100; +const int MAX_PLY_PLUS_6 = MAX_PLY + 6; + +/// 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), castle (3) +/// +/// 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 { + MOVE_NONE, + MOVE_NULL = 65 +}; + +enum MoveType { + NORMAL, + PROMOTION = 1 << 14, + ENPASSANT = 2 << 14, + CASTLE = 3 << 14 +}; + +enum CastleRight { // Defined as in PolyGlot book hash key + CASTLES_NONE, + WHITE_OO, + WHITE_OOO = WHITE_OO << 1, + BLACK_OO = WHITE_OO << 2, + BLACK_OOO = WHITE_OO << 3, + ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, + CASTLE_RIGHT_NB = 16 +}; + +enum CastlingSide { + KING_SIDE, + QUEEN_SIDE, + CASTLING_SIDE_NB = 2 +}; + +enum Phase { + PHASE_ENDGAME, + PHASE_MIDGAME = 128, + MG = 0, EG = 1, PHASE_NB = 2 +}; + +enum ScaleFactor { + SCALE_FACTOR_DRAW = 0, + 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 = 15000, + VALUE_MATE = 30000, + VALUE_INFINITE = 30001, + VALUE_NONE = 30002, + + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, + + Mg = 0, Eg = 1, + + PawnValueMg = 198, PawnValueEg = 258, + KnightValueMg = 817, KnightValueEg = 846, + BishopValueMg = 836, BishopValueEg = 857, + RookValueMg = 1270, RookValueEg = 1278, + QueenValueMg = 2521, QueenValueEg = 2558 +}; + +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 +}; + +enum Color { + WHITE, BLACK, NO_COLOR, COLOR_NB = 2 +}; + +enum Depth { + + ONE_PLY = 2, + + DEPTH_ZERO = 0 * ONE_PLY, + DEPTH_QS_CHECKS = -1 * ONE_PLY, + DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, + DEPTH_QS_RECAPTURES = -7 * ONE_PLY, + + DEPTH_NONE = -127 * ONE_PLY +}; + +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, + + DELTA_N = 8, + DELTA_E = 1, + DELTA_S = -8, + DELTA_W = -1, + + DELTA_NN = DELTA_N + DELTA_N, + DELTA_NE = DELTA_N + DELTA_E, + DELTA_SE = DELTA_S + DELTA_E, + DELTA_SS = DELTA_S + DELTA_S, + DELTA_SW = DELTA_S + DELTA_W, + DELTA_NW = DELTA_N + DELTA_W +}; + +enum File { + FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB +}; + +enum Rank { + RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB +}; + + +/// Score enum keeps a midgame and an endgame value in a single integer, first +/// LSB 16 bits are used to store endgame value, while upper bits are used for +/// midgame value. +enum Score : int { SCORE_ZERO }; + +inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } + +/// Extracting the signed lower and upper 16 bits it 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 mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); } + +/// On Intel 64 bit we have a small speed regression with the standard conforming +/// version, so use a faster code in this case that, although not 100% standard +/// compliant it seems to work for Intel and MSVC. +#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER)) + +inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); } + +#else + +inline Value eg_value(Score s) { + return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); +} + +#endif + +#define ENABLE_SAFE_OPERATORS_ON(T) \ +inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \ +inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \ +inline T operator*(int i, const T d) { return T(i * int(d)); } \ +inline T operator*(const T d, int i) { return T(int(d) * i); } \ +inline T operator-(const T d) { return T(-int(d)); } \ +inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \ +inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \ +inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; } + +#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \ +inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \ +inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \ +inline T operator/(const T d, int i) { return T(int(d) / i); } \ +inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; } + +ENABLE_OPERATORS_ON(Value) +ENABLE_OPERATORS_ON(PieceType) +ENABLE_OPERATORS_ON(Piece) +ENABLE_OPERATORS_ON(Color) +ENABLE_OPERATORS_ON(Depth) +ENABLE_OPERATORS_ON(Square) +ENABLE_OPERATORS_ON(File) +ENABLE_OPERATORS_ON(Rank) + +/// Added operators for adding 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); } + +ENABLE_SAFE_OPERATORS_ON(Score) + +/// 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); +} + +#undef ENABLE_OPERATORS_ON +#undef ENABLE_SAFE_OPERATORS_ON + +extern Value PieceValue[PHASE_NB][PIECE_NB]; + +struct ExtMove { + Move move; + int score; +}; + +inline bool operator<(const ExtMove& f, const ExtMove& s) { + return f.score < s.score; +} + +inline Color operator~(Color c) { + return Color(c ^ 1); +} + +inline Square operator~(Square s) { + return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8 +} + +inline Square operator|(File f, Rank r) { + return Square((r << 3) | f); +} + +inline Value mate_in(int ply) { + return VALUE_MATE - ply; +} + +inline Value mated_in(int ply) { + return -VALUE_MATE + ply; +} + +inline Piece make_piece(Color c, PieceType pt) { + return Piece((c << 3) | pt); +} + +inline CastleRight make_castle_right(Color c, CastlingSide s) { + return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); +} + +inline PieceType type_of(Piece p) { + return PieceType(p & 7); +} + +inline Color color_of(Piece p) { + assert(p != NO_PIECE); + return Color(p >> 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 mirror(Square s) { + return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1 +} + +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 char file_to_char(File f, bool tolower = true) { + return char(f - FILE_A + (tolower ? 'a' : 'A')); +} + +inline char rank_to_char(Rank r) { + return char(r - RANK_1 + '1'); +} + +inline Square pawn_push(Color c) { + return c == WHITE ? DELTA_N : DELTA_S; +} + +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) + 2); +} + +inline Move make_move(Square from, Square to) { + return Move(to | (from << 6)); +} + +template +inline Move make(Square from, Square to, PieceType pt = KNIGHT) { + return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)); +} + +inline bool is_ok(Move m) { + return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE +} + +#include + +inline const std::string square_to_string(Square s) { + return { file_to_char(file_of(s)), rank_to_char(rank_of(s)) }; +} + +#endif // #ifndef TYPES_H_INCLUDED diff --git a/stockfish-4-win/src_c++11/uci.cpp b/stockfish-4-win/src_c++11/uci.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caf1b5c2fed08d67084b09c584ba1c7f4c7716c1 --- /dev/null +++ b/stockfish-4-win/src_c++11/uci.cpp @@ -0,0 +1,219 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "evaluate.h" +#include "notation.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "ucioption.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"; + + // Keep track of position keys along the setup moves (from start position to the + // position just before to start searching). Needed by repetition draw detection. + Search::StateStackPtr SetupStates; + + void setoption(istringstream& up); + void position(Position& pos, istringstream& up); + void go(const Position& pos, istringstream& up); +} + + +/// Wait for a command from the user, parse this text string as an UCI command, +/// and call the appropriate functions. Also intercepts EOF from stdin to ensure +/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI +/// commands, the function also supports a few debug commands. + +void UCI::loop(const string& args) { + + Position pos(StartFEN, false, Threads.main()); // The root position + string token, cmd = args; + + do { + if (args.empty() && !getline(cin, cmd)) // Block here waiting for input + cmd = "quit"; + + istringstream is(cmd); + + is >> skipws >> token; + + if (token == "quit" || token == "stop" || token == "ponderhit") + { + // 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 != "ponderhit" || Search::Signals.stopOnPonderhit) + { + Search::Signals.stop = true; + Threads.main()->notify_one(); // Could be sleeping + } + else + Search::Limits.ponder = false; + } + else if (token == "perft" && (is >> token)) // Read perft depth + { + stringstream ss; + + ss << Options["Hash"] << " " + << Options["Threads"] << " " << token << " current perft"; + + benchmark(pos, ss); + } + else if (token == "key") + sync_cout << hex << uppercase << setfill('0') + << "position key: " << setw(16) << pos.key() + << "\nmaterial key: " << setw(16) << pos.material_key() + << "\npawn key: " << setw(16) << pos.pawn_key() + << dec << sync_endl; + + else if (token == "uci") + sync_cout << "id name " << engine_info(true) + << "\n" << Options + << "\nuciok" << sync_endl; + + else if (token == "eval") + { + Search::RootColor = pos.side_to_move(); // Ensure it is set + sync_cout << Eval::trace(pos) << sync_endl; + } + else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } + else if (token == "go") go(pos, is); + else if (token == "position") position(pos, is); + else if (token == "setoption") setoption(is); + else if (token == "flip") pos.flip(); + else if (token == "bench") benchmark(pos, is); + else if (token == "d") sync_cout << pos.pretty() << sync_endl; + else if (token == "isready") sync_cout << "readyok" << sync_endl; + else + sync_cout << "Unknown command: " << cmd << sync_endl; + + } while (token != "quit" && args.empty()); // Args have one-shot behaviour + + Threads.wait_for_think_finished(); // Cannot quit while search is running +} + + +namespace { + + // 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; + + pos.set(fen, Options["UCI_Chess960"], Threads.main()); + SetupStates = Search::StateStackPtr(new std::stack()); + + // Parse move list (if any) + while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE) + { + SetupStates->push(StateInfo()); + pos.do_move(m, SetupStates->top()); + } + } + + + // 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()) + token; + + // Read option value (can contain spaces) + while (is >> token) + value += string(" ", !value.empty()) + 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, and starts + // the search. + + void go(const Position& pos, istringstream& is) { + + Search::LimitsType limits; + vector searchMoves; + string token; + + while (is >> token) + { + if (token == "searchmoves") + while (is >> token) + searchMoves.push_back(move_from_uci(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 = true; + else if (token == "ponder") limits.ponder = true; + } + + Threads.start_thinking(pos, limits, searchMoves, SetupStates); + } +} diff --git a/stockfish-4-win/src_c++11/ucioption.cpp b/stockfish-4-win/src_c++11/ucioption.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0dde92fd7db78b3b6111ef8cbe5789db776e1491 --- /dev/null +++ b/stockfish-4-win/src_c++11/ucioption.cpp @@ -0,0 +1,161 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 "misc.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +using std::string; + +UCI::OptionsMap Options; // Global object + +namespace UCI { + +/// 'On change' actions, triggered by an option's value change +void on_logger(const Option& o) { start_logger(o); } +void on_eval(const Option&) { Eval::init(); } +void on_threads(const Option&) { Threads.read_uci_options(); } +void on_hash_size(const Option& o) { TT.set_size(o); } +void on_clear_hash(const Option&) { TT.clear(); } + + +/// 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) { + + o["Write Debug Log"] = Option(false, on_logger); + o["Write Search Log"] = Option(false); + o["Search Log Filename"] = Option("SearchLog.txt"); + o["Book File"] = Option("book.bin"); + o["Best Book Move"] = Option(false); + o["Contempt Factor"] = Option(0, -50, 50); + o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval); + o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); + o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval); + o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); + o["Space"] = Option(100, 0, 200, on_eval); + o["Aggressiveness"] = Option(100, 0, 200, on_eval); + o["Cowardice"] = Option(100, 0, 200, on_eval); + o["Min Split Depth"] = Option(0, 0, 12, on_threads); + o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); + o["Threads"] = Option(1, 1, MAX_THREADS, on_threads); + o["Idle Threads Sleep"] = Option(false); + o["Hash"] = Option(32, 1, 8192, on_hash_size); + o["Clear Hash"] = Option(on_clear_hash); + o["Ponder"] = Option(true); + o["OwnBook"] = Option(false); + o["MultiPV"] = Option(1, 1, 500); + o["Skill Level"] = Option(20, 0, 20); + o["Emergency Move Horizon"] = Option(40, 0, 50); + o["Emergency Base Time"] = Option(200, 0, 30000); + o["Emergency Move Time"] = Option(70, 0, 5000); + o["Minimum Thinking Time"] = Option(20, 0, 5000); + o["Slow Mover"] = Option(100, 10, 1000); + o["UCI_Chess960"] = Option(false); + o["UCI_AnalyseMode"] = Option(false, on_eval); +} + + +/// 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++) + { + auto it = std::find_if(om.begin(), om.end(), [idx](const OptionsMap::value_type& p) + { return p.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; + } + return os; +} + + +/// Option c'tors and conversion operators + +Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) +{ defaultValue = currentValue = v; } + +Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) +{ defaultValue = currentValue = (v ? "true" : "false"); } + +Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) +{} + +Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), 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=() 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 diff --git a/stockfish-4-win/src_c++11/ucioption.h b/stockfish-4-win/src_c++11/ucioption.h new file mode 100644 index 0000000000000000000000000000000000000000..3a2e152fd9e0596ff438b8ad27ad3b95561fe3ae --- /dev/null +++ b/stockfish-4-win/src_c++11/ucioption.h @@ -0,0 +1,69 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, 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 UCIOPTION_H_INCLUDED +#define UCIOPTION_H_INCLUDED + +#include +#include + +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 (Fn)(const Option&); + +public: + Option(Fn* = nullptr); + Option(bool v, Fn* = nullptr); + Option(const char* v, Fn* = nullptr); + Option(int v, int min, int max, Fn* = nullptr); + + Option& operator=(const std::string& v); + 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; + Fn* on_change; +}; + +void init(OptionsMap&); +void loop(const std::string&); + +} // namespace UCI + +extern UCI::OptionsMap Options; + +#endif // #ifndef UCIOPTION_H_INCLUDED