--- /dev/null
+ GNU GENERAL PUBLIC LICENSE\r
+ Version 3, 29 June 2007\r
+\r
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+ Preamble\r
+\r
+ The GNU General Public License is a free, copyleft license for\r
+software and other kinds of works.\r
+\r
+ The licenses for most software and other practical works are designed\r
+to take away your freedom to share and change the works. By contrast,\r
+the GNU General Public License is intended to guarantee your freedom to\r
+share and change all versions of a program--to make sure it remains free\r
+software for all its users. We, the Free Software Foundation, use the\r
+GNU General Public License for most of our software; it applies also to\r
+any other work released this way by its authors. You can apply it to\r
+your programs, too.\r
+\r
+ When we speak of free software, we are referring to freedom, not\r
+price. Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+them if you wish), that you receive source code or can get it if you\r
+want it, that you can change the software or use pieces of it in new\r
+free programs, and that you know you can do these things.\r
+\r
+ To protect your rights, we need to prevent others from denying you\r
+these rights or asking you to surrender the rights. Therefore, you have\r
+certain responsibilities if you distribute copies of the software, or if\r
+you modify it: responsibilities to respect the freedom of others.\r
+\r
+ For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must pass on to the recipients the same\r
+freedoms that you received. You must make sure that they, too, receive\r
+or can get the source code. And you must show them these terms so they\r
+know their rights.\r
+\r
+ Developers that use the GNU GPL protect your rights with two steps:\r
+(1) assert copyright on the software, and (2) offer you this License\r
+giving you legal permission to copy, distribute and/or modify it.\r
+\r
+ For the developers' and authors' protection, the GPL clearly explains\r
+that there is no warranty for this free software. For both users' and\r
+authors' sake, the GPL requires that modified versions be marked as\r
+changed, so that their problems will not be attributed erroneously to\r
+authors of previous versions.\r
+\r
+ Some devices are designed to deny users access to install or run\r
+modified versions of the software inside them, although the manufacturer\r
+can do so. This is fundamentally incompatible with the aim of\r
+protecting users' freedom to change the software. The systematic\r
+pattern of such abuse occurs in the area of products for individuals to\r
+use, which is precisely where it is most unacceptable. Therefore, we\r
+have designed this version of the GPL to prohibit the practice for those\r
+products. If such problems arise substantially in other domains, we\r
+stand ready to extend this provision to those domains in future versions\r
+of the GPL, as needed to protect the freedom of users.\r
+\r
+ Finally, every program is threatened constantly by software patents.\r
+States should not allow patents to restrict development and use of\r
+software on general-purpose computers, but in those that do, we wish to\r
+avoid the special danger that patents applied to a free program could\r
+make it effectively proprietary. To prevent this, the GPL assures that\r
+patents cannot be used to render the program non-free.\r
+\r
+ The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+ TERMS AND CONDITIONS\r
+\r
+ 0. Definitions.\r
+\r
+ "This License" refers to version 3 of the GNU General Public License.\r
+\r
+ "Copyright" also means copyright-like laws that apply to other kinds of\r
+works, such as semiconductor masks.\r
+\r
+ "The Program" refers to any copyrightable work licensed under this\r
+License. Each licensee is addressed as "you". "Licensees" and\r
+"recipients" may be individuals or organizations.\r
+\r
+ To "modify" a work means to copy from or adapt all or part of the work\r
+in a fashion requiring copyright permission, other than the making of an\r
+exact copy. The resulting work is called a "modified version" of the\r
+earlier work or a work "based on" the earlier work.\r
+\r
+ A "covered work" means either the unmodified Program or a work based\r
+on the Program.\r
+\r
+ To "propagate" a work means to do anything with it that, without\r
+permission, would make you directly or secondarily liable for\r
+infringement under applicable copyright law, except executing it on a\r
+computer or modifying a private copy. Propagation includes copying,\r
+distribution (with or without modification), making available to the\r
+public, and in some countries other activities as well.\r
+\r
+ To "convey" a work means any kind of propagation that enables other\r
+parties to make or receive copies. Mere interaction with a user through\r
+a computer network, with no transfer of a copy, is not conveying.\r
+\r
+ An interactive user interface displays "Appropriate Legal Notices"\r
+to the extent that it includes a convenient and prominently visible\r
+feature that (1) displays an appropriate copyright notice, and (2)\r
+tells the user that there is no warranty for the work (except to the\r
+extent that warranties are provided), that licensees may convey the\r
+work under this License, and how to view a copy of this License. If\r
+the interface presents a list of user commands or options, such as a\r
+menu, a prominent item in the list meets this criterion.\r
+\r
+ 1. Source Code.\r
+\r
+ The "source code" for a work means the preferred form of the work\r
+for making modifications to it. "Object code" means any non-source\r
+form of a work.\r
+\r
+ A "Standard Interface" means an interface that either is an official\r
+standard defined by a recognized standards body, or, in the case of\r
+interfaces specified for a particular programming language, one that\r
+is widely used among developers working in that language.\r
+\r
+ The "System Libraries" of an executable work include anything, other\r
+than the work as a whole, that (a) is included in the normal form of\r
+packaging a Major Component, but which is not part of that Major\r
+Component, and (b) serves only to enable use of the work with that\r
+Major Component, or to implement a Standard Interface for which an\r
+implementation is available to the public in source code form. A\r
+"Major Component", in this context, means a major essential component\r
+(kernel, window system, and so on) of the specific operating system\r
+(if any) on which the executable work runs, or a compiler used to\r
+produce the work, or an object code interpreter used to run it.\r
+\r
+ The "Corresponding Source" for a work in object code form means all\r
+the source code needed to generate, install, and (for an executable\r
+work) run the object code and to modify the work, including scripts to\r
+control those activities. However, it does not include the work's\r
+System Libraries, or general-purpose tools or generally available free\r
+programs which are used unmodified in performing those activities but\r
+which are not part of the work. For example, Corresponding Source\r
+includes interface definition files associated with source files for\r
+the work, and the source code for shared libraries and dynamically\r
+linked subprograms that the work is specifically designed to require,\r
+such as by intimate data communication or control flow between those\r
+subprograms and other parts of the work.\r
+\r
+ The Corresponding Source need not include anything that users\r
+can regenerate automatically from other parts of the Corresponding\r
+Source.\r
+\r
+ The Corresponding Source for a work in source code form is that\r
+same work.\r
+\r
+ 2. Basic Permissions.\r
+\r
+ All rights granted under this License are granted for the term of\r
+copyright on the Program, and are irrevocable provided the stated\r
+conditions are met. This License explicitly affirms your unlimited\r
+permission to run the unmodified Program. The output from running a\r
+covered work is covered by this License only if the output, given its\r
+content, constitutes a covered work. This License acknowledges your\r
+rights of fair use or other equivalent, as provided by copyright law.\r
+\r
+ You may make, run and propagate covered works that you do not\r
+convey, without conditions so long as your license otherwise remains\r
+in force. You may convey covered works to others for the sole purpose\r
+of having them make modifications exclusively for you, or provide you\r
+with facilities for running those works, provided that you comply with\r
+the terms of this License in conveying all material for which you do\r
+not control copyright. Those thus making or running the covered works\r
+for you must do so exclusively on your behalf, under your direction\r
+and control, on terms that prohibit them from making any copies of\r
+your copyrighted material outside their relationship with you.\r
+\r
+ Conveying under any other circumstances is permitted solely under\r
+the conditions stated below. Sublicensing is not allowed; section 10\r
+makes it unnecessary.\r
+\r
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\r
+\r
+ No covered work shall be deemed part of an effective technological\r
+measure under any applicable law fulfilling obligations under article\r
+11 of the WIPO copyright treaty adopted on 20 December 1996, or\r
+similar laws prohibiting or restricting circumvention of such\r
+measures.\r
+\r
+ When you convey a covered work, you waive any legal power to forbid\r
+circumvention of technological measures to the extent such circumvention\r
+is effected by exercising rights under this License with respect to\r
+the covered work, and you disclaim any intention to limit operation or\r
+modification of the work as a means of enforcing, against the work's\r
+users, your or third parties' legal rights to forbid circumvention of\r
+technological measures.\r
+\r
+ 4. Conveying Verbatim Copies.\r
+\r
+ You may convey verbatim copies of the Program's source code as you\r
+receive it, in any medium, provided that you conspicuously and\r
+appropriately publish on each copy an appropriate copyright notice;\r
+keep intact all notices stating that this License and any\r
+non-permissive terms added in accord with section 7 apply to the code;\r
+keep intact all notices of the absence of any warranty; and give all\r
+recipients a copy of this License along with the Program.\r
+\r
+ You may charge any price or no price for each copy that you convey,\r
+and you may offer support or warranty protection for a fee.\r
+\r
+ 5. Conveying Modified Source Versions.\r
+\r
+ You may convey a work based on the Program, or the modifications to\r
+produce it from the Program, in the form of source code under the\r
+terms of section 4, provided that you also meet all of these conditions:\r
+\r
+ a) The work must carry prominent notices stating that you modified\r
+ it, and giving a relevant date.\r
+\r
+ b) The work must carry prominent notices stating that it is\r
+ released under this License and any conditions added under section\r
+ 7. This requirement modifies the requirement in section 4 to\r
+ "keep intact all notices".\r
+\r
+ c) You must license the entire work, as a whole, under this\r
+ License to anyone who comes into possession of a copy. This\r
+ License will therefore apply, along with any applicable section 7\r
+ additional terms, to the whole of the work, and all its parts,\r
+ regardless of how they are packaged. This License gives no\r
+ permission to license the work in any other way, but it does not\r
+ invalidate such permission if you have separately received it.\r
+\r
+ d) If the work has interactive user interfaces, each must display\r
+ Appropriate Legal Notices; however, if the Program has interactive\r
+ interfaces that do not display Appropriate Legal Notices, your\r
+ work need not make them do so.\r
+\r
+ A compilation of a covered work with other separate and independent\r
+works, which are not by their nature extensions of the covered work,\r
+and which are not combined with it such as to form a larger program,\r
+in or on a volume of a storage or distribution medium, is called an\r
+"aggregate" if the compilation and its resulting copyright are not\r
+used to limit the access or legal rights of the compilation's users\r
+beyond what the individual works permit. Inclusion of a covered work\r
+in an aggregate does not cause this License to apply to the other\r
+parts of the aggregate.\r
+\r
+ 6. Conveying Non-Source Forms.\r
+\r
+ You may convey a covered work in object code form under the terms\r
+of sections 4 and 5, provided that you also convey the\r
+machine-readable Corresponding Source under the terms of this License,\r
+in one of these ways:\r
+\r
+ a) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by the\r
+ Corresponding Source fixed on a durable physical medium\r
+ customarily used for software interchange.\r
+\r
+ b) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by a\r
+ written offer, valid for at least three years and valid for as\r
+ long as you offer spare parts or customer support for that product\r
+ model, to give anyone who possesses the object code either (1) a\r
+ copy of the Corresponding Source for all the software in the\r
+ product that is covered by this License, on a durable physical\r
+ medium customarily used for software interchange, for a price no\r
+ more than your reasonable cost of physically performing this\r
+ conveying of source, or (2) access to copy the\r
+ Corresponding Source from a network server at no charge.\r
+\r
+ c) Convey individual copies of the object code with a copy of the\r
+ written offer to provide the Corresponding Source. This\r
+ alternative is allowed only occasionally and noncommercially, and\r
+ only if you received the object code with such an offer, in accord\r
+ with subsection 6b.\r
+\r
+ d) Convey the object code by offering access from a designated\r
+ place (gratis or for a charge), and offer equivalent access to the\r
+ Corresponding Source in the same way through the same place at no\r
+ further charge. You need not require recipients to copy the\r
+ Corresponding Source along with the object code. If the place to\r
+ copy the object code is a network server, the Corresponding Source\r
+ may be on a different server (operated by you or a third party)\r
+ that supports equivalent copying facilities, provided you maintain\r
+ clear directions next to the object code saying where to find the\r
+ Corresponding Source. Regardless of what server hosts the\r
+ Corresponding Source, you remain obligated to ensure that it is\r
+ available for as long as needed to satisfy these requirements.\r
+\r
+ e) Convey the object code using peer-to-peer transmission, provided\r
+ you inform other peers where the object code and Corresponding\r
+ Source of the work are being offered to the general public at no\r
+ charge under subsection 6d.\r
+\r
+ A separable portion of the object code, whose source code is excluded\r
+from the Corresponding Source as a System Library, need not be\r
+included in conveying the object code work.\r
+\r
+ A "User Product" is either (1) a "consumer product", which means any\r
+tangible personal property which is normally used for personal, family,\r
+or household purposes, or (2) anything designed or sold for incorporation\r
+into a dwelling. In determining whether a product is a consumer product,\r
+doubtful cases shall be resolved in favor of coverage. For a particular\r
+product received by a particular user, "normally used" refers to a\r
+typical or common use of that class of product, regardless of the status\r
+of the particular user or of the way in which the particular user\r
+actually uses, or expects or is expected to use, the product. A product\r
+is a consumer product regardless of whether the product has substantial\r
+commercial, industrial or non-consumer uses, unless such uses represent\r
+the only significant mode of use of the product.\r
+\r
+ "Installation Information" for a User Product means any methods,\r
+procedures, authorization keys, or other information required to install\r
+and execute modified versions of a covered work in that User Product from\r
+a modified version of its Corresponding Source. The information must\r
+suffice to ensure that the continued functioning of the modified object\r
+code is in no case prevented or interfered with solely because\r
+modification has been made.\r
+\r
+ If you convey an object code work under this section in, or with, or\r
+specifically for use in, a User Product, and the conveying occurs as\r
+part of a transaction in which the right of possession and use of the\r
+User Product is transferred to the recipient in perpetuity or for a\r
+fixed term (regardless of how the transaction is characterized), the\r
+Corresponding Source conveyed under this section must be accompanied\r
+by the Installation Information. But this requirement does not apply\r
+if neither you nor any third party retains the ability to install\r
+modified object code on the User Product (for example, the work has\r
+been installed in ROM).\r
+\r
+ The requirement to provide Installation Information does not include a\r
+requirement to continue to provide support service, warranty, or updates\r
+for a work that has been modified or installed by the recipient, or for\r
+the User Product in which it has been modified or installed. Access to a\r
+network may be denied when the modification itself materially and\r
+adversely affects the operation of the network or violates the rules and\r
+protocols for communication across the network.\r
+\r
+ Corresponding Source conveyed, and Installation Information provided,\r
+in accord with this section must be in a format that is publicly\r
+documented (and with an implementation available to the public in\r
+source code form), and must require no special password or key for\r
+unpacking, reading or copying.\r
+\r
+ 7. Additional Terms.\r
+\r
+ "Additional permissions" are terms that supplement the terms of this\r
+License by making exceptions from one or more of its conditions.\r
+Additional permissions that are applicable to the entire Program shall\r
+be treated as though they were included in this License, to the extent\r
+that they are valid under applicable law. If additional permissions\r
+apply only to part of the Program, that part may be used separately\r
+under those permissions, but the entire Program remains governed by\r
+this License without regard to the additional permissions.\r
+\r
+ When you convey a copy of a covered work, you may at your option\r
+remove any additional permissions from that copy, or from any part of\r
+it. (Additional permissions may be written to require their own\r
+removal in certain cases when you modify the work.) You may place\r
+additional permissions on material, added by you to a covered work,\r
+for which you have or can give appropriate copyright permission.\r
+\r
+ Notwithstanding any other provision of this License, for material you\r
+add to a covered work, you may (if authorized by the copyright holders of\r
+that material) supplement the terms of this License with terms:\r
+\r
+ a) Disclaiming warranty or limiting liability differently from the\r
+ terms of sections 15 and 16 of this License; or\r
+\r
+ b) Requiring preservation of specified reasonable legal notices or\r
+ author attributions in that material or in the Appropriate Legal\r
+ Notices displayed by works containing it; or\r
+\r
+ c) Prohibiting misrepresentation of the origin of that material, or\r
+ requiring that modified versions of such material be marked in\r
+ reasonable ways as different from the original version; or\r
+\r
+ d) Limiting the use for publicity purposes of names of licensors or\r
+ authors of the material; or\r
+\r
+ e) Declining to grant rights under trademark law for use of some\r
+ trade names, trademarks, or service marks; or\r
+\r
+ f) Requiring indemnification of licensors and authors of that\r
+ material by anyone who conveys the material (or modified versions of\r
+ it) with contractual assumptions of liability to the recipient, for\r
+ any liability that these contractual assumptions directly impose on\r
+ those licensors and authors.\r
+\r
+ All other non-permissive additional terms are considered "further\r
+restrictions" within the meaning of section 10. If the Program as you\r
+received it, or any part of it, contains a notice stating that it is\r
+governed by this License along with a term that is a further\r
+restriction, you may remove that term. If a license document contains\r
+a further restriction but permits relicensing or conveying under this\r
+License, you may add to a covered work material governed by the terms\r
+of that license document, provided that the further restriction does\r
+not survive such relicensing or conveying.\r
+\r
+ If you add terms to a covered work in accord with this section, you\r
+must place, in the relevant source files, a statement of the\r
+additional terms that apply to those files, or a notice indicating\r
+where to find the applicable terms.\r
+\r
+ Additional terms, permissive or non-permissive, may be stated in the\r
+form of a separately written license, or stated as exceptions;\r
+the above requirements apply either way.\r
+\r
+ 8. Termination.\r
+\r
+ You may not propagate or modify a covered work except as expressly\r
+provided under this License. Any attempt otherwise to propagate or\r
+modify it is void, and will automatically terminate your rights under\r
+this License (including any patent licenses granted under the third\r
+paragraph of section 11).\r
+\r
+ However, if you cease all violation of this License, then your\r
+license from a particular copyright holder is reinstated (a)\r
+provisionally, unless and until the copyright holder explicitly and\r
+finally terminates your license, and (b) permanently, if the copyright\r
+holder fails to notify you of the violation by some reasonable means\r
+prior to 60 days after the cessation.\r
+\r
+ Moreover, your license from a particular copyright holder is\r
+reinstated permanently if the copyright holder notifies you of the\r
+violation by some reasonable means, this is the first time you have\r
+received notice of violation of this License (for any work) from that\r
+copyright holder, and you cure the violation prior to 30 days after\r
+your receipt of the notice.\r
+\r
+ Termination of your rights under this section does not terminate the\r
+licenses of parties who have received copies or rights from you under\r
+this License. If your rights have been terminated and not permanently\r
+reinstated, you do not qualify to receive new licenses for the same\r
+material under section 10.\r
+\r
+ 9. Acceptance Not Required for Having Copies.\r
+\r
+ You are not required to accept this License in order to receive or\r
+run a copy of the Program. Ancillary propagation of a covered work\r
+occurring solely as a consequence of using peer-to-peer transmission\r
+to receive a copy likewise does not require acceptance. However,\r
+nothing other than this License grants you permission to propagate or\r
+modify any covered work. These actions infringe copyright if you do\r
+not accept this License. Therefore, by modifying or propagating a\r
+covered work, you indicate your acceptance of this License to do so.\r
+\r
+ 10. Automatic Licensing of Downstream Recipients.\r
+\r
+ Each time you convey a covered work, the recipient automatically\r
+receives a license from the original licensors, to run, modify and\r
+propagate that work, subject to this License. You are not responsible\r
+for enforcing compliance by third parties with this License.\r
+\r
+ An "entity transaction" is a transaction transferring control of an\r
+organization, or substantially all assets of one, or subdividing an\r
+organization, or merging organizations. If propagation of a covered\r
+work results from an entity transaction, each party to that\r
+transaction who receives a copy of the work also receives whatever\r
+licenses to the work the party's predecessor in interest had or could\r
+give under the previous paragraph, plus a right to possession of the\r
+Corresponding Source of the work from the predecessor in interest, if\r
+the predecessor has it or can get it with reasonable efforts.\r
+\r
+ You may not impose any further restrictions on the exercise of the\r
+rights granted or affirmed under this License. For example, you may\r
+not impose a license fee, royalty, or other charge for exercise of\r
+rights granted under this License, and you may not initiate litigation\r
+(including a cross-claim or counterclaim in a lawsuit) alleging that\r
+any patent claim is infringed by making, using, selling, offering for\r
+sale, or importing the Program or any portion of it.\r
+\r
+ 11. Patents.\r
+\r
+ A "contributor" is a copyright holder who authorizes use under this\r
+License of the Program or a work on which the Program is based. The\r
+work thus licensed is called the contributor's "contributor version".\r
+\r
+ A contributor's "essential patent claims" are all patent claims\r
+owned or controlled by the contributor, whether already acquired or\r
+hereafter acquired, that would be infringed by some manner, permitted\r
+by this License, of making, using, or selling its contributor version,\r
+but do not include claims that would be infringed only as a\r
+consequence of further modification of the contributor version. For\r
+purposes of this definition, "control" includes the right to grant\r
+patent sublicenses in a manner consistent with the requirements of\r
+this License.\r
+\r
+ Each contributor grants you a non-exclusive, worldwide, royalty-free\r
+patent license under the contributor's essential patent claims, to\r
+make, use, sell, offer for sale, import and otherwise run, modify and\r
+propagate the contents of its contributor version.\r
+\r
+ In the following three paragraphs, a "patent license" is any express\r
+agreement or commitment, however denominated, not to enforce a patent\r
+(such as an express permission to practice a patent or covenant not to\r
+sue for patent infringement). To "grant" such a patent license to a\r
+party means to make such an agreement or commitment not to enforce a\r
+patent against the party.\r
+\r
+ If you convey a covered work, knowingly relying on a patent license,\r
+and the Corresponding Source of the work is not available for anyone\r
+to copy, free of charge and under the terms of this License, through a\r
+publicly available network server or other readily accessible means,\r
+then you must either (1) cause the Corresponding Source to be so\r
+available, or (2) arrange to deprive yourself of the benefit of the\r
+patent license for this particular work, or (3) arrange, in a manner\r
+consistent with the requirements of this License, to extend the patent\r
+license to downstream recipients. "Knowingly relying" means you have\r
+actual knowledge that, but for the patent license, your conveying the\r
+covered work in a country, or your recipient's use of the covered work\r
+in a country, would infringe one or more identifiable patents in that\r
+country that you have reason to believe are valid.\r
+\r
+ If, pursuant to or in connection with a single transaction or\r
+arrangement, you convey, or propagate by procuring conveyance of, a\r
+covered work, and grant a patent license to some of the parties\r
+receiving the covered work authorizing them to use, propagate, modify\r
+or convey a specific copy of the covered work, then the patent license\r
+you grant is automatically extended to all recipients of the covered\r
+work and works based on it.\r
+\r
+ A patent license is "discriminatory" if it does not include within\r
+the scope of its coverage, prohibits the exercise of, or is\r
+conditioned on the non-exercise of one or more of the rights that are\r
+specifically granted under this License. You may not convey a covered\r
+work if you are a party to an arrangement with a third party that is\r
+in the business of distributing software, under which you make payment\r
+to the third party based on the extent of your activity of conveying\r
+the work, and under which the third party grants, to any of the\r
+parties who would receive the covered work from you, a discriminatory\r
+patent license (a) in connection with copies of the covered work\r
+conveyed by you (or copies made from those copies), or (b) primarily\r
+for and in connection with specific products or compilations that\r
+contain the covered work, unless you entered into that arrangement,\r
+or that patent license was granted, prior to 28 March 2007.\r
+\r
+ Nothing in this License shall be construed as excluding or limiting\r
+any implied license or other defenses to infringement that may\r
+otherwise be available to you under applicable patent law.\r
+\r
+ 12. No Surrender of Others' Freedom.\r
+\r
+ If conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License. If you cannot convey a\r
+covered work so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you may\r
+not convey it at all. For example, if you agree to terms that obligate you\r
+to collect a royalty for further conveying from those to whom you convey\r
+the Program, the only way you could satisfy both those terms and this\r
+License would be to refrain entirely from conveying the Program.\r
+\r
+ 13. Use with the GNU Affero General Public License.\r
+\r
+ Notwithstanding any other provision of this License, you have\r
+permission to link or combine any covered work with a work licensed\r
+under version 3 of the GNU Affero General Public License into a single\r
+combined work, and to convey the resulting work. The terms of this\r
+License will continue to apply to the part which is the covered work,\r
+but the special requirements of the GNU Affero General Public License,\r
+section 13, concerning interaction through a network will apply to the\r
+combination as such.\r
+\r
+ 14. Revised Versions of this License.\r
+\r
+ The Free Software Foundation may publish revised and/or new versions of\r
+the GNU General Public License from time to time. Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+ Each version is given a distinguishing version number. If the\r
+Program specifies that a certain numbered version of the GNU General\r
+Public License "or any later version" applies to it, you have the\r
+option of following the terms and conditions either of that numbered\r
+version or of any later version published by the Free Software\r
+Foundation. If the Program does not specify a version number of the\r
+GNU General Public License, you may choose any version ever published\r
+by the Free Software Foundation.\r
+\r
+ If the Program specifies that a proxy can decide which future\r
+versions of the GNU General Public License can be used, that proxy's\r
+public statement of acceptance of a version permanently authorizes you\r
+to choose that version for the Program.\r
+\r
+ Later license versions may give you additional or different\r
+permissions. However, no additional obligations are imposed on any\r
+author or copyright holder as a result of your choosing to follow a\r
+later version.\r
+\r
+ 15. Disclaimer of Warranty.\r
+\r
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\r
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\r
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\r
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\r
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\r
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\r
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r
+\r
+ 16. Limitation of Liability.\r
+\r
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\r
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\r
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\r
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\r
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\r
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\r
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\r
+SUCH DAMAGES.\r
+\r
+ 17. Interpretation of Sections 15 and 16.\r
+\r
+ If the disclaimer of warranty and limitation of liability provided\r
+above cannot be given local legal effect according to their terms,\r
+reviewing courts shall apply local law that most closely approximates\r
+an absolute waiver of all civil liability in connection with the\r
+Program, unless a warranty or assumption of liability accompanies a\r
+copy of the Program in return for a fee.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ How to Apply These Terms to Your New Programs\r
+\r
+ If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+ To do so, attach the following notices to the program. It is safest\r
+to attach them to the start of each source file to most effectively\r
+state the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+ <one line to give the program's name and a brief idea of what it does.>\r
+ Copyright (C) <year> <name of author>\r
+\r
+ This program is free software: you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation, either version 3 of the License, or\r
+ (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+ If the program does terminal interaction, make it output a short\r
+notice like this when it starts in an interactive mode:\r
+\r
+ <program> Copyright (C) <year> <name of author>\r
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+ This is free software, and you are welcome to redistribute it\r
+ under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License. Of course, your program's commands\r
+might be different; for a GUI interface, you would use an "about box".\r
+\r
+ You should also get your employer (if you work as a programmer) or school,\r
+if any, to sign a "copyright disclaimer" for the program, if necessary.\r
+For more information on this, and how to apply and follow the GNU GPL, see\r
+<http://www.gnu.org/licenses/>.\r
+\r
+ The GNU General Public License does not permit incorporating your program\r
+into proprietary programs. If your program is a subroutine library, you\r
+may consider it more useful to permit linking proprietary applications with\r
+the library. If this is what you want to do, use the GNU Lesser General\r
+Public License instead of this License. But first, please read\r
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.\r
--- /dev/null
+1. Introduction\r
+---------------\r
+\r
+Glaurung is a free UCI chess engine. It is not a complete chess\r
+program, but requires some UCI compatible GUI (like XBoard with\r
+PolyGlot, eboard, José, Arena, Sigma Chess, Shredder, Chess Partner,\r
+or Fritz) in order to be used comfortably. Read the documentation for\r
+your GUI of choice for information about how to use Glaurung with your\r
+GUI.\r
+\r
+Glaurung 2 is a completely rewritten version of Glaurung. Apart from\r
+the parallel search code, almost no code is shared with Glaurung\r
+1.2.1, the previous stable version. The new program is clearly\r
+stronger than the old, but has a less attractive style of play,\r
+because there are still a few major holes in its evaluation function\r
+(most notably space and development).\r
+\r
+This version of Glaurung supports up to 8 CPUs, but has not been\r
+tested thoroughly with more than 2. The program tries to detect the\r
+number of CPUs on your computer and set the number of search threads\r
+accordingly, but please be aware that the detection is not always\r
+correct. It is therefore recommended to inspect the value of the\r
+"Threads" UCI parameter, and to make sure it equals the number of CPU\r
+cores on your computer.\r
+\r
+\r
+2. Files\r
+--------\r
+\r
+This distribution of Glaurung consists of the following files:\r
+\r
+ * Readme.txt, the file you are currently reading.\r
+\r
+ * Copying.txt, a text file containing the GNU General Public\r
+ License.\r
+\r
+ * src/, a subdirectory containing the full source code, including a\r
+ Makefile that can be used to compile Glaurung on Unix-like\r
+ systems. For further information about how to compile Glaurung\r
+ yourself, read section 4 below.\r
+\r
+ * MacOSX/, a subdirectory containing excutables for Apple Macintosh\r
+ computers running Mac OS X 10.4 (Tiger) and newer. There are two\r
+ executables, one for OS X 10.4, and one for OS X 10.5. The\r
+ executable for OS X 10.4 will work in 10.5 as well, but the one\r
+ for 10.5 is faster.\r
+\r
+ * LinuxX86/, a subdirectory containing 32-bit and 64-bit x86 GNU/Linux\r
+ executables.\r
+\r
+ * Windows/, a subdirectory containing 32-bit and 64-bit Windows\r
+ executables.\r
+\r
+ * polyglot.ini, for using Glaurung with Fabien Letouzey's PolyGlot\r
+ adapter.\r
+\r
+\r
+3. Opening books\r
+----------------\r
+\r
+This version of Glaurung has experimental support for PolyGlot opening\r
+books. For information about how to create such books, consult the\r
+PolyGlot documentation. The book file can be selected by setting the\r
+UCI parameter "Book File".\r
+\r
+A book file contributed by Salvo Spitaleri can be found on the\r
+Glaurung web page.\r
+\r
+\r
+4. Compiling it yourself\r
+------------------------\r
+\r
+On Unix-like systems, it should usually be possible to compile\r
+Glaurung directly from the source code with the included Makefile.\r
+The exception is computer with big-endian CPUs, like PowerPC\r
+Macintoshes. Some of the bitboard routines in the current version of\r
+Glaurung are endianness-sensitive, and won't work on a big-endian CPU.\r
+Ensuring that the line with #define USE_32BIT_ATTACKS" near the top\r
+of bitboard.h is commented out should solve this problem.\r
+Commenting out the line with "#define USE_32BIT_ATTACKS" near the\r
+\r
+There is also a problem with compiling Glaurung on certain 64-bit \r
+systems, regardless of the endianness. If Glaurung segfaults \r
+immediately after startup, try to comment out the line with \r
+"#define USE_FOLDED_BITSCAN" near the beginning of bitboard.h and\r
+recompile.\r
+\r
+Finally, even if Glaurung does work without any changes on your\r
+computer, it might be possible to improve the performance by changing\r
+some of the #define directives in bitboard.h. The default settings\r
+are optimized for 64-bit CPUs. On 32-bit CPUs, it is probably better\r
+to switch on USE_32BIT_ATTACKS, and to use BITCOUNT_SWAR_32 instead of\r
+BITCOUNT_SWAR_64. For computers with very little memory (like\r
+handheld devices), it is possible to conserve memory by defining\r
+USE_COMPACT_ROOK_ATTACKS. \r
+\r
+\r
+\r
+5. History\r
+----------\r
+\r
+2007-05-06: Glaurung 2 - epsilon\r
+--------------------------------\r
+\r
+The first public release, and the first version of my new program\r
+which is able to match the old Glaurung 1.2.1 on a single CPU. Lots\r
+of features and chess knowledge is still missing.\r
+\r
+2007-05-10: Glaurung 2 - epsilon/2\r
+----------------------------------\r
+\r
+This version is very close to 2 - epsilon. The major changes are:\r
+\r
+ * A number of compatibility problems which appeared when trying to\r
+ compile Glaurung 2 - epsilon on various operating systems and CPUs\r
+ have been solved.\r
+\r
+ * Fixed a major bug in the detection of rooks trapped inside a\r
+ friendly king.\r
+\r
+ * Added knowledge about several types of drawn endgames.\r
+\r
+ * Fixed a few FRC related bugs. FRC now works, but because of\r
+ serious holes in the evaluation function the program plays very\r
+ badly.\r
+\r
+ * A slightly more sophisticated king safety evaluation.\r
+\r
+2007-06-07: Glaurung 2 - epsilon/3\r
+----------------------------------\r
+\r
+The first public version with support for multiple CPUs. Unless you\r
+have a dual-core (or better) computer, use Glaurung with a PolyGlot\r
+book, or runs games with ponder on, you may want to skip this version,\r
+which is almost certainly no stronger than 2 - epsilon/2 when running\r
+on a single CPU. The main changes compared to the previous version\r
+are:\r
+\r
+ * Parallel search, with support for 1-4 CPUs. The program currently\r
+ always allocates a separate pawn hash table and material hash\r
+ table for four threads, which is a pure waste of RAM if your\r
+ computer has just a single CPU. This will be fixed in a future\r
+ version.\r
+\r
+ * Fixed a bug in book randomization. When using Polyglot books, the\r
+ previous version would always select exactly the same move in the\r
+ same position after a restart of the program. Thanks to Pavel\r
+ Háse for pointing this out.\r
+\r
+ * Fixed a UCI pondering bug: Glaurung no longer instantly prints its\r
+ best move when the maximum depth is reached during a ponder\r
+ search, as the previous version did. According to the UCI\r
+ protocol, it is not allowed to print the best move before the\r
+ engine has received the "stop" or "quit" command.\r
+\r
+ * Additional search information: The new version displays hash\r
+ saturation and the current line(s) of search.\r
+\r
+ * Several minor bug fixes and optimizations in the search and\r
+ evaluation. \r
+\r
+2007-06-08: Glaurung 2 - epsilon/4\r
+----------------------------------\r
+\r
+A bugfix release, with only a single important change:\r
+\r
+ * Fixed a very serious pondering bug. As pointed out by Marc\r
+ Lacrosse, the previous version would lose on time in almost every\r
+ single game with pondering enabled. The new version handles\r
+ pondering correctly (or so I hope). When playing with ponder\r
+ off, the new version is identical to version 2 - epsilon/3.\r
+\r
+2007-06-25: Glaurung 2 - epsilon/5\r
+----------------------------------\r
+\r
+Another minor update, including the following improvements and bug\r
+fixes:\r
+\r
+ * As Werner Schüle discovered, the previous version would sometimes\r
+ stop thinking and lose on time right before delivering checkmate\r
+ (which is of course a very unfortunate moment to lose on time).\r
+ I haven't been able to reproduce Werner's problem on my computer\r
+ (probably because I run a different OS), but I have fixed the bug\r
+ which I suspect caused the time losses. I hope the time losses\r
+ will no longer occur with 2 - epsilon/5.\r
+\r
+ * The program is now slightly less resource-hungry on computers\r
+ with less than 4 CPU cores: The previous version would always\r
+ allocated separate pawn and material hash tables for four\r
+ threads, even when running on a single-core CPU. The new version\r
+ only allocates pawn and material hash tables for the threads\r
+ which are actually used.\r
+\r
+ * A minor reorganization of the memory layout has made the parallel\r
+ search about 10% more efficient (at least on my computer, but the\r
+ results are likely to vary considerably on different systems).\r
+\r
+ * The Intel Mac OS X binary is much faster than before, thanks to\r
+ the Intel C++ compiler (previous versions were compiled with\r
+ GCC).\r
+\r
+ * A few other very minor bug fixes and enhancements.\r
+\r
+2007-11-21: Glaurung 2.0\r
+------------------------\r
+\r
+The first stable (or so I hope) and feature-complete version of\r
+Glaurung 2. The following are the main changes compared to the\r
+previous version:\r
+\r
+ * The license has been changed from GPL version 2 to GPL version 3.\r
+\r
+ * MultiPV mode.\r
+\r
+ * Support for the "searchmoves" option in the UCI "go" command.\r
+ This means that it is possible to ask Glaurung to exclude some\r
+ moves from its analysis, or to restrict its analysis to just a\r
+ handful of moves selected by the user. This feature must also be\r
+ supported by the GUI under which Glaurung is run. Glaurung's own\r
+ GUI does currently not support this feature.\r
+\r
+ * Chess960 support now works. The program still plays this game \r
+ very badly, because of lack of opening knowledge.\r
+\r
+ * Much more aggressive pruning in the last few plies of the main\r
+ search.\r
+\r
+ * Somewhat better scaling on multi-CPU systems, and support for up\r
+ to 8 CPUs.\r
+\r
+ * Lots of new UCI parameters.\r
+\r
+ * Improved time managment, especially in games with pondering on \r
+ (i.e. when the engine is allowed to think when it's the\r
+ opponent's turn to move).\r
+\r
+ * Some evaluation improvements, and some new basic endgame\r
+ patterns.\r
+\r
+ * The program should no longer crash if the game lasts longer than\r
+ 1000 plies.\r
+ \r
+ * Many minor bug fixes and other tiny improvements throughout the\r
+ code. \r
+ \r
+ * More generously commented code, and numerous cosmetic changes in\r
+ coding style.\r
+\r
+2007-11-22: Glaurung 2.0.1\r
+--------------------------\r
+\r
+ * Fixed (or so I hope) a bug which would occasionally cause one of\r
+ the search threads to get stuck forever in its idle loop.\r
+\r
+2008-05-14: Glaurung 2.1\r
+------------------------\r
+\r
+This version contains far too many changes to list them all, but most\r
+of them are minor and cosmetic. The most important and noticable\r
+changes are a lot of new UCI parameters, and many improvements in the\r
+evaluation function. The highlights are:\r
+\r
+ * Extensive changes in the evaluation function. The addition of\r
+ king safety is the most important improvement, but there are also\r
+ numerous little improvements elsewhere in the evaluation. There\r
+ is still much work left to do in the evaluation function, though.\r
+ Space and development are still missing, and the tuning is likely\r
+ to be very poor. Currently, the program is optimized for an\r
+ entertaining style rather than maximum strength.\r
+\r
+ * More accurate forward pruning. The previous version used the\r
+ null move refutation move to improve the pruning accuracy by\r
+ means of a very simple trick: It did not allow pruning of any\r
+ moves with the piece captured by the null move refutation move.\r
+ In Glaurung 2.1, this has been enhanced: It does not allow\r
+ pruning of moves which defend the destination square of the null\r
+ move refutation move, nor of moves which block the ray of the\r
+ piece in the case that the moving piece in the null move\r
+ refutation move is a slider.\r
+\r
+ * More conservative use of LMR at PV nodes. The previous version\r
+ searched the first 6 moves with full depth, 2.1 by default\r
+ searches the first 14 moves with full depth (but there is a new\r
+ UCI parameter for configuring this). I am not at all sure\r
+ whether this is an improvement. More thorough testing is\r
+ required. \r
+\r
+ * Feedback from the evaluation to the search. The search passes an\r
+ object of type 'EvalInfo' to the eval, and the eval fills this\r
+ struct with various potentially useful information (like the sets\r
+ of squares attacked by each piece type, the middle game and\r
+ endgame components of the eval, etc.). At the moment, almost\r
+ none of this information is actually used by the search. The\r
+ only exception is that the evaluation function is now used to\r
+ adjust the futility pruning margin in the quiescence search.\r
+\r
+ * Less extensions. This hurts the programs performance a lot in most\r
+ test suites, but I hope it improves the branching factor in deep\r
+ searches.\r
+\r
+ * A very long list of new UCI parameters, especially for tuning the\r
+ evaluation. \r
+\r
+\r
+6. Terms of use\r
+---------------\r
+\r
+Glaurung is free, and distributed under the GNU General Public License\r
+(GPL). Essentially, this means that you are free to do almost exactly\r
+what you want with the program, including distributing it among your\r
+friends, making it available for download from your web site, selling\r
+it (either by itself or as part of some bigger software package), or\r
+using it as the starting point for a software project of your own.\r
+\r
+The only real limitation is that whenever you distribute Glaurung in\r
+some way, you must always include the full source code, or a pointer\r
+to where the source code can be found. If you make any changes to the\r
+source code, these changes must also be made available under the GPL.\r
+\r
+For full details, read the copy of the GPL found in the file named\r
+Copying.txt.\r
+\r
+\r
+7. Feedback\r
+-----------\r
+\r
+The author's e-mail address is tord@glaurungchess.com\r
+\r
--- /dev/null
+\r
+[PolyGlot]\r
+\r
+EngineDir = .\r
+EngineCommand = ./glaurung\r
+\r
+Book = false\r
+BookFile = book.bin\r
+\r
+Log = true\r
+LogFile = glaurung.log\r
+\r
+Resign = true\r
+ResignScore = 600\r
+\r
+[Engine]\r
+\r
+Hash = 128\r
+Threads = 1\r
+OwnBook = false\r
+Book File = book.bin\r
+Use Search Log = false\r
+Mobility (Middle Game) = 100\r
+Mobility (Endgame) = 100\r
+Pawn Structure (Middle Game) = 100\r
+Pawn Structure (Endgame) = 100\r
+Passed Pawns (Middle Game) = 100\r
+Passed Pawns (Endgame) = 100\r
+Aggressiveness = 100\r
+Cowardice = 100\r
+King Safety Curve = Quadratic\r
+Quadratic = Linear\r
+King Safety Coefficient = 40\r
+King Safety X Intercept = 0\r
+King Safety Max Slope = 30\r
+King Safety Max Value = 500\r
+Queen Contact Check Bonus = 4\r
+Rook Contact Check Bonus = 2\r
+Queen Check Bonus = 2\r
+Rook Check Bonus = 1\r
+Bishop Check Bonus = 1\r
+Knight Check Bonus = 1\r
+Discovered Check Bonus = 3\r
+Mate Threat Bonus = 3\r
+Check Extension (PV nodes) = 2\r
+Check Extension (non-PV nodes) = 1\r
+Single Reply Extension (PV nodes) = 2\r
+Single Reply Extension (non-PV nodes) = 2\r
+Mate Threat Extension (PV nodes) = 0\r
+Mate Threat Extension (non-PV nodes) = 0\r
+Pawn Push to 7th Extension (PV nodes) = 1\r
+Pawn Push to 7th Extension (non-PV nodes) = 1\r
+Passed Pawn Extension (PV nodes) = 1\r
+Passed Pawn Extension (non-PV nodes) = 0\r
+Pawn Endgame Extension (PV nodes) = 2\r
+Pawn Endgame Extension (non-PV nodes) = 2\r
+Full Depth Moves (PV nodes) = 14\r
+Full Depth Moves (non-PV nodes) = 3\r
+Threat Depth = 5\r
+Selective Plies = 7\r
+Futility Pruning (Main Search) = true\r
+Futility Pruning (Quiescence Search) = true\r
+Futility Margin 0 = 50\r
+Futility Margin 1 = 100\r
+Futility Margin 2 = 300\r
+Maximum Razoring Depth = 3\r
+Razoring Margin = 300\r
+Randomness = 0\r
+Minimum Split Depth = 4\r
+Maximum Number of Threads per Split Point = 5\r
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE\r
+ Version 3, 29 June 2007\r
+\r
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+ Preamble\r
+\r
+ The GNU General Public License is a free, copyleft license for\r
+software and other kinds of works.\r
+\r
+ The licenses for most software and other practical works are designed\r
+to take away your freedom to share and change the works. By contrast,\r
+the GNU General Public License is intended to guarantee your freedom to\r
+share and change all versions of a program--to make sure it remains free\r
+software for all its users. We, the Free Software Foundation, use the\r
+GNU General Public License for most of our software; it applies also to\r
+any other work released this way by its authors. You can apply it to\r
+your programs, too.\r
+\r
+ When we speak of free software, we are referring to freedom, not\r
+price. Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+them if you wish), that you receive source code or can get it if you\r
+want it, that you can change the software or use pieces of it in new\r
+free programs, and that you know you can do these things.\r
+\r
+ To protect your rights, we need to prevent others from denying you\r
+these rights or asking you to surrender the rights. Therefore, you have\r
+certain responsibilities if you distribute copies of the software, or if\r
+you modify it: responsibilities to respect the freedom of others.\r
+\r
+ For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must pass on to the recipients the same\r
+freedoms that you received. You must make sure that they, too, receive\r
+or can get the source code. And you must show them these terms so they\r
+know their rights.\r
+\r
+ Developers that use the GNU GPL protect your rights with two steps:\r
+(1) assert copyright on the software, and (2) offer you this License\r
+giving you legal permission to copy, distribute and/or modify it.\r
+\r
+ For the developers' and authors' protection, the GPL clearly explains\r
+that there is no warranty for this free software. For both users' and\r
+authors' sake, the GPL requires that modified versions be marked as\r
+changed, so that their problems will not be attributed erroneously to\r
+authors of previous versions.\r
+\r
+ Some devices are designed to deny users access to install or run\r
+modified versions of the software inside them, although the manufacturer\r
+can do so. This is fundamentally incompatible with the aim of\r
+protecting users' freedom to change the software. The systematic\r
+pattern of such abuse occurs in the area of products for individuals to\r
+use, which is precisely where it is most unacceptable. Therefore, we\r
+have designed this version of the GPL to prohibit the practice for those\r
+products. If such problems arise substantially in other domains, we\r
+stand ready to extend this provision to those domains in future versions\r
+of the GPL, as needed to protect the freedom of users.\r
+\r
+ Finally, every program is threatened constantly by software patents.\r
+States should not allow patents to restrict development and use of\r
+software on general-purpose computers, but in those that do, we wish to\r
+avoid the special danger that patents applied to a free program could\r
+make it effectively proprietary. To prevent this, the GPL assures that\r
+patents cannot be used to render the program non-free.\r
+\r
+ The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+ TERMS AND CONDITIONS\r
+\r
+ 0. Definitions.\r
+\r
+ "This License" refers to version 3 of the GNU General Public License.\r
+\r
+ "Copyright" also means copyright-like laws that apply to other kinds of\r
+works, such as semiconductor masks.\r
+\r
+ "The Program" refers to any copyrightable work licensed under this\r
+License. Each licensee is addressed as "you". "Licensees" and\r
+"recipients" may be individuals or organizations.\r
+\r
+ To "modify" a work means to copy from or adapt all or part of the work\r
+in a fashion requiring copyright permission, other than the making of an\r
+exact copy. The resulting work is called a "modified version" of the\r
+earlier work or a work "based on" the earlier work.\r
+\r
+ A "covered work" means either the unmodified Program or a work based\r
+on the Program.\r
+\r
+ To "propagate" a work means to do anything with it that, without\r
+permission, would make you directly or secondarily liable for\r
+infringement under applicable copyright law, except executing it on a\r
+computer or modifying a private copy. Propagation includes copying,\r
+distribution (with or without modification), making available to the\r
+public, and in some countries other activities as well.\r
+\r
+ To "convey" a work means any kind of propagation that enables other\r
+parties to make or receive copies. Mere interaction with a user through\r
+a computer network, with no transfer of a copy, is not conveying.\r
+\r
+ An interactive user interface displays "Appropriate Legal Notices"\r
+to the extent that it includes a convenient and prominently visible\r
+feature that (1) displays an appropriate copyright notice, and (2)\r
+tells the user that there is no warranty for the work (except to the\r
+extent that warranties are provided), that licensees may convey the\r
+work under this License, and how to view a copy of this License. If\r
+the interface presents a list of user commands or options, such as a\r
+menu, a prominent item in the list meets this criterion.\r
+\r
+ 1. Source Code.\r
+\r
+ The "source code" for a work means the preferred form of the work\r
+for making modifications to it. "Object code" means any non-source\r
+form of a work.\r
+\r
+ A "Standard Interface" means an interface that either is an official\r
+standard defined by a recognized standards body, or, in the case of\r
+interfaces specified for a particular programming language, one that\r
+is widely used among developers working in that language.\r
+\r
+ The "System Libraries" of an executable work include anything, other\r
+than the work as a whole, that (a) is included in the normal form of\r
+packaging a Major Component, but which is not part of that Major\r
+Component, and (b) serves only to enable use of the work with that\r
+Major Component, or to implement a Standard Interface for which an\r
+implementation is available to the public in source code form. A\r
+"Major Component", in this context, means a major essential component\r
+(kernel, window system, and so on) of the specific operating system\r
+(if any) on which the executable work runs, or a compiler used to\r
+produce the work, or an object code interpreter used to run it.\r
+\r
+ The "Corresponding Source" for a work in object code form means all\r
+the source code needed to generate, install, and (for an executable\r
+work) run the object code and to modify the work, including scripts to\r
+control those activities. However, it does not include the work's\r
+System Libraries, or general-purpose tools or generally available free\r
+programs which are used unmodified in performing those activities but\r
+which are not part of the work. For example, Corresponding Source\r
+includes interface definition files associated with source files for\r
+the work, and the source code for shared libraries and dynamically\r
+linked subprograms that the work is specifically designed to require,\r
+such as by intimate data communication or control flow between those\r
+subprograms and other parts of the work.\r
+\r
+ The Corresponding Source need not include anything that users\r
+can regenerate automatically from other parts of the Corresponding\r
+Source.\r
+\r
+ The Corresponding Source for a work in source code form is that\r
+same work.\r
+\r
+ 2. Basic Permissions.\r
+\r
+ All rights granted under this License are granted for the term of\r
+copyright on the Program, and are irrevocable provided the stated\r
+conditions are met. This License explicitly affirms your unlimited\r
+permission to run the unmodified Program. The output from running a\r
+covered work is covered by this License only if the output, given its\r
+content, constitutes a covered work. This License acknowledges your\r
+rights of fair use or other equivalent, as provided by copyright law.\r
+\r
+ You may make, run and propagate covered works that you do not\r
+convey, without conditions so long as your license otherwise remains\r
+in force. You may convey covered works to others for the sole purpose\r
+of having them make modifications exclusively for you, or provide you\r
+with facilities for running those works, provided that you comply with\r
+the terms of this License in conveying all material for which you do\r
+not control copyright. Those thus making or running the covered works\r
+for you must do so exclusively on your behalf, under your direction\r
+and control, on terms that prohibit them from making any copies of\r
+your copyrighted material outside their relationship with you.\r
+\r
+ Conveying under any other circumstances is permitted solely under\r
+the conditions stated below. Sublicensing is not allowed; section 10\r
+makes it unnecessary.\r
+\r
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\r
+\r
+ No covered work shall be deemed part of an effective technological\r
+measure under any applicable law fulfilling obligations under article\r
+11 of the WIPO copyright treaty adopted on 20 December 1996, or\r
+similar laws prohibiting or restricting circumvention of such\r
+measures.\r
+\r
+ When you convey a covered work, you waive any legal power to forbid\r
+circumvention of technological measures to the extent such circumvention\r
+is effected by exercising rights under this License with respect to\r
+the covered work, and you disclaim any intention to limit operation or\r
+modification of the work as a means of enforcing, against the work's\r
+users, your or third parties' legal rights to forbid circumvention of\r
+technological measures.\r
+\r
+ 4. Conveying Verbatim Copies.\r
+\r
+ You may convey verbatim copies of the Program's source code as you\r
+receive it, in any medium, provided that you conspicuously and\r
+appropriately publish on each copy an appropriate copyright notice;\r
+keep intact all notices stating that this License and any\r
+non-permissive terms added in accord with section 7 apply to the code;\r
+keep intact all notices of the absence of any warranty; and give all\r
+recipients a copy of this License along with the Program.\r
+\r
+ You may charge any price or no price for each copy that you convey,\r
+and you may offer support or warranty protection for a fee.\r
+\r
+ 5. Conveying Modified Source Versions.\r
+\r
+ You may convey a work based on the Program, or the modifications to\r
+produce it from the Program, in the form of source code under the\r
+terms of section 4, provided that you also meet all of these conditions:\r
+\r
+ a) The work must carry prominent notices stating that you modified\r
+ it, and giving a relevant date.\r
+\r
+ b) The work must carry prominent notices stating that it is\r
+ released under this License and any conditions added under section\r
+ 7. This requirement modifies the requirement in section 4 to\r
+ "keep intact all notices".\r
+\r
+ c) You must license the entire work, as a whole, under this\r
+ License to anyone who comes into possession of a copy. This\r
+ License will therefore apply, along with any applicable section 7\r
+ additional terms, to the whole of the work, and all its parts,\r
+ regardless of how they are packaged. This License gives no\r
+ permission to license the work in any other way, but it does not\r
+ invalidate such permission if you have separately received it.\r
+\r
+ d) If the work has interactive user interfaces, each must display\r
+ Appropriate Legal Notices; however, if the Program has interactive\r
+ interfaces that do not display Appropriate Legal Notices, your\r
+ work need not make them do so.\r
+\r
+ A compilation of a covered work with other separate and independent\r
+works, which are not by their nature extensions of the covered work,\r
+and which are not combined with it such as to form a larger program,\r
+in or on a volume of a storage or distribution medium, is called an\r
+"aggregate" if the compilation and its resulting copyright are not\r
+used to limit the access or legal rights of the compilation's users\r
+beyond what the individual works permit. Inclusion of a covered work\r
+in an aggregate does not cause this License to apply to the other\r
+parts of the aggregate.\r
+\r
+ 6. Conveying Non-Source Forms.\r
+\r
+ You may convey a covered work in object code form under the terms\r
+of sections 4 and 5, provided that you also convey the\r
+machine-readable Corresponding Source under the terms of this License,\r
+in one of these ways:\r
+\r
+ a) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by the\r
+ Corresponding Source fixed on a durable physical medium\r
+ customarily used for software interchange.\r
+\r
+ b) Convey the object code in, or embodied in, a physical product\r
+ (including a physical distribution medium), accompanied by a\r
+ written offer, valid for at least three years and valid for as\r
+ long as you offer spare parts or customer support for that product\r
+ model, to give anyone who possesses the object code either (1) a\r
+ copy of the Corresponding Source for all the software in the\r
+ product that is covered by this License, on a durable physical\r
+ medium customarily used for software interchange, for a price no\r
+ more than your reasonable cost of physically performing this\r
+ conveying of source, or (2) access to copy the\r
+ Corresponding Source from a network server at no charge.\r
+\r
+ c) Convey individual copies of the object code with a copy of the\r
+ written offer to provide the Corresponding Source. This\r
+ alternative is allowed only occasionally and noncommercially, and\r
+ only if you received the object code with such an offer, in accord\r
+ with subsection 6b.\r
+\r
+ d) Convey the object code by offering access from a designated\r
+ place (gratis or for a charge), and offer equivalent access to the\r
+ Corresponding Source in the same way through the same place at no\r
+ further charge. You need not require recipients to copy the\r
+ Corresponding Source along with the object code. If the place to\r
+ copy the object code is a network server, the Corresponding Source\r
+ may be on a different server (operated by you or a third party)\r
+ that supports equivalent copying facilities, provided you maintain\r
+ clear directions next to the object code saying where to find the\r
+ Corresponding Source. Regardless of what server hosts the\r
+ Corresponding Source, you remain obligated to ensure that it is\r
+ available for as long as needed to satisfy these requirements.\r
+\r
+ e) Convey the object code using peer-to-peer transmission, provided\r
+ you inform other peers where the object code and Corresponding\r
+ Source of the work are being offered to the general public at no\r
+ charge under subsection 6d.\r
+\r
+ A separable portion of the object code, whose source code is excluded\r
+from the Corresponding Source as a System Library, need not be\r
+included in conveying the object code work.\r
+\r
+ A "User Product" is either (1) a "consumer product", which means any\r
+tangible personal property which is normally used for personal, family,\r
+or household purposes, or (2) anything designed or sold for incorporation\r
+into a dwelling. In determining whether a product is a consumer product,\r
+doubtful cases shall be resolved in favor of coverage. For a particular\r
+product received by a particular user, "normally used" refers to a\r
+typical or common use of that class of product, regardless of the status\r
+of the particular user or of the way in which the particular user\r
+actually uses, or expects or is expected to use, the product. A product\r
+is a consumer product regardless of whether the product has substantial\r
+commercial, industrial or non-consumer uses, unless such uses represent\r
+the only significant mode of use of the product.\r
+\r
+ "Installation Information" for a User Product means any methods,\r
+procedures, authorization keys, or other information required to install\r
+and execute modified versions of a covered work in that User Product from\r
+a modified version of its Corresponding Source. The information must\r
+suffice to ensure that the continued functioning of the modified object\r
+code is in no case prevented or interfered with solely because\r
+modification has been made.\r
+\r
+ If you convey an object code work under this section in, or with, or\r
+specifically for use in, a User Product, and the conveying occurs as\r
+part of a transaction in which the right of possession and use of the\r
+User Product is transferred to the recipient in perpetuity or for a\r
+fixed term (regardless of how the transaction is characterized), the\r
+Corresponding Source conveyed under this section must be accompanied\r
+by the Installation Information. But this requirement does not apply\r
+if neither you nor any third party retains the ability to install\r
+modified object code on the User Product (for example, the work has\r
+been installed in ROM).\r
+\r
+ The requirement to provide Installation Information does not include a\r
+requirement to continue to provide support service, warranty, or updates\r
+for a work that has been modified or installed by the recipient, or for\r
+the User Product in which it has been modified or installed. Access to a\r
+network may be denied when the modification itself materially and\r
+adversely affects the operation of the network or violates the rules and\r
+protocols for communication across the network.\r
+\r
+ Corresponding Source conveyed, and Installation Information provided,\r
+in accord with this section must be in a format that is publicly\r
+documented (and with an implementation available to the public in\r
+source code form), and must require no special password or key for\r
+unpacking, reading or copying.\r
+\r
+ 7. Additional Terms.\r
+\r
+ "Additional permissions" are terms that supplement the terms of this\r
+License by making exceptions from one or more of its conditions.\r
+Additional permissions that are applicable to the entire Program shall\r
+be treated as though they were included in this License, to the extent\r
+that they are valid under applicable law. If additional permissions\r
+apply only to part of the Program, that part may be used separately\r
+under those permissions, but the entire Program remains governed by\r
+this License without regard to the additional permissions.\r
+\r
+ When you convey a copy of a covered work, you may at your option\r
+remove any additional permissions from that copy, or from any part of\r
+it. (Additional permissions may be written to require their own\r
+removal in certain cases when you modify the work.) You may place\r
+additional permissions on material, added by you to a covered work,\r
+for which you have or can give appropriate copyright permission.\r
+\r
+ Notwithstanding any other provision of this License, for material you\r
+add to a covered work, you may (if authorized by the copyright holders of\r
+that material) supplement the terms of this License with terms:\r
+\r
+ a) Disclaiming warranty or limiting liability differently from the\r
+ terms of sections 15 and 16 of this License; or\r
+\r
+ b) Requiring preservation of specified reasonable legal notices or\r
+ author attributions in that material or in the Appropriate Legal\r
+ Notices displayed by works containing it; or\r
+\r
+ c) Prohibiting misrepresentation of the origin of that material, or\r
+ requiring that modified versions of such material be marked in\r
+ reasonable ways as different from the original version; or\r
+\r
+ d) Limiting the use for publicity purposes of names of licensors or\r
+ authors of the material; or\r
+\r
+ e) Declining to grant rights under trademark law for use of some\r
+ trade names, trademarks, or service marks; or\r
+\r
+ f) Requiring indemnification of licensors and authors of that\r
+ material by anyone who conveys the material (or modified versions of\r
+ it) with contractual assumptions of liability to the recipient, for\r
+ any liability that these contractual assumptions directly impose on\r
+ those licensors and authors.\r
+\r
+ All other non-permissive additional terms are considered "further\r
+restrictions" within the meaning of section 10. If the Program as you\r
+received it, or any part of it, contains a notice stating that it is\r
+governed by this License along with a term that is a further\r
+restriction, you may remove that term. If a license document contains\r
+a further restriction but permits relicensing or conveying under this\r
+License, you may add to a covered work material governed by the terms\r
+of that license document, provided that the further restriction does\r
+not survive such relicensing or conveying.\r
+\r
+ If you add terms to a covered work in accord with this section, you\r
+must place, in the relevant source files, a statement of the\r
+additional terms that apply to those files, or a notice indicating\r
+where to find the applicable terms.\r
+\r
+ Additional terms, permissive or non-permissive, may be stated in the\r
+form of a separately written license, or stated as exceptions;\r
+the above requirements apply either way.\r
+\r
+ 8. Termination.\r
+\r
+ You may not propagate or modify a covered work except as expressly\r
+provided under this License. Any attempt otherwise to propagate or\r
+modify it is void, and will automatically terminate your rights under\r
+this License (including any patent licenses granted under the third\r
+paragraph of section 11).\r
+\r
+ However, if you cease all violation of this License, then your\r
+license from a particular copyright holder is reinstated (a)\r
+provisionally, unless and until the copyright holder explicitly and\r
+finally terminates your license, and (b) permanently, if the copyright\r
+holder fails to notify you of the violation by some reasonable means\r
+prior to 60 days after the cessation.\r
+\r
+ Moreover, your license from a particular copyright holder is\r
+reinstated permanently if the copyright holder notifies you of the\r
+violation by some reasonable means, this is the first time you have\r
+received notice of violation of this License (for any work) from that\r
+copyright holder, and you cure the violation prior to 30 days after\r
+your receipt of the notice.\r
+\r
+ Termination of your rights under this section does not terminate the\r
+licenses of parties who have received copies or rights from you under\r
+this License. If your rights have been terminated and not permanently\r
+reinstated, you do not qualify to receive new licenses for the same\r
+material under section 10.\r
+\r
+ 9. Acceptance Not Required for Having Copies.\r
+\r
+ You are not required to accept this License in order to receive or\r
+run a copy of the Program. Ancillary propagation of a covered work\r
+occurring solely as a consequence of using peer-to-peer transmission\r
+to receive a copy likewise does not require acceptance. However,\r
+nothing other than this License grants you permission to propagate or\r
+modify any covered work. These actions infringe copyright if you do\r
+not accept this License. Therefore, by modifying or propagating a\r
+covered work, you indicate your acceptance of this License to do so.\r
+\r
+ 10. Automatic Licensing of Downstream Recipients.\r
+\r
+ Each time you convey a covered work, the recipient automatically\r
+receives a license from the original licensors, to run, modify and\r
+propagate that work, subject to this License. You are not responsible\r
+for enforcing compliance by third parties with this License.\r
+\r
+ An "entity transaction" is a transaction transferring control of an\r
+organization, or substantially all assets of one, or subdividing an\r
+organization, or merging organizations. If propagation of a covered\r
+work results from an entity transaction, each party to that\r
+transaction who receives a copy of the work also receives whatever\r
+licenses to the work the party's predecessor in interest had or could\r
+give under the previous paragraph, plus a right to possession of the\r
+Corresponding Source of the work from the predecessor in interest, if\r
+the predecessor has it or can get it with reasonable efforts.\r
+\r
+ You may not impose any further restrictions on the exercise of the\r
+rights granted or affirmed under this License. For example, you may\r
+not impose a license fee, royalty, or other charge for exercise of\r
+rights granted under this License, and you may not initiate litigation\r
+(including a cross-claim or counterclaim in a lawsuit) alleging that\r
+any patent claim is infringed by making, using, selling, offering for\r
+sale, or importing the Program or any portion of it.\r
+\r
+ 11. Patents.\r
+\r
+ A "contributor" is a copyright holder who authorizes use under this\r
+License of the Program or a work on which the Program is based. The\r
+work thus licensed is called the contributor's "contributor version".\r
+\r
+ A contributor's "essential patent claims" are all patent claims\r
+owned or controlled by the contributor, whether already acquired or\r
+hereafter acquired, that would be infringed by some manner, permitted\r
+by this License, of making, using, or selling its contributor version,\r
+but do not include claims that would be infringed only as a\r
+consequence of further modification of the contributor version. For\r
+purposes of this definition, "control" includes the right to grant\r
+patent sublicenses in a manner consistent with the requirements of\r
+this License.\r
+\r
+ Each contributor grants you a non-exclusive, worldwide, royalty-free\r
+patent license under the contributor's essential patent claims, to\r
+make, use, sell, offer for sale, import and otherwise run, modify and\r
+propagate the contents of its contributor version.\r
+\r
+ In the following three paragraphs, a "patent license" is any express\r
+agreement or commitment, however denominated, not to enforce a patent\r
+(such as an express permission to practice a patent or covenant not to\r
+sue for patent infringement). To "grant" such a patent license to a\r
+party means to make such an agreement or commitment not to enforce a\r
+patent against the party.\r
+\r
+ If you convey a covered work, knowingly relying on a patent license,\r
+and the Corresponding Source of the work is not available for anyone\r
+to copy, free of charge and under the terms of this License, through a\r
+publicly available network server or other readily accessible means,\r
+then you must either (1) cause the Corresponding Source to be so\r
+available, or (2) arrange to deprive yourself of the benefit of the\r
+patent license for this particular work, or (3) arrange, in a manner\r
+consistent with the requirements of this License, to extend the patent\r
+license to downstream recipients. "Knowingly relying" means you have\r
+actual knowledge that, but for the patent license, your conveying the\r
+covered work in a country, or your recipient's use of the covered work\r
+in a country, would infringe one or more identifiable patents in that\r
+country that you have reason to believe are valid.\r
+\r
+ If, pursuant to or in connection with a single transaction or\r
+arrangement, you convey, or propagate by procuring conveyance of, a\r
+covered work, and grant a patent license to some of the parties\r
+receiving the covered work authorizing them to use, propagate, modify\r
+or convey a specific copy of the covered work, then the patent license\r
+you grant is automatically extended to all recipients of the covered\r
+work and works based on it.\r
+\r
+ A patent license is "discriminatory" if it does not include within\r
+the scope of its coverage, prohibits the exercise of, or is\r
+conditioned on the non-exercise of one or more of the rights that are\r
+specifically granted under this License. You may not convey a covered\r
+work if you are a party to an arrangement with a third party that is\r
+in the business of distributing software, under which you make payment\r
+to the third party based on the extent of your activity of conveying\r
+the work, and under which the third party grants, to any of the\r
+parties who would receive the covered work from you, a discriminatory\r
+patent license (a) in connection with copies of the covered work\r
+conveyed by you (or copies made from those copies), or (b) primarily\r
+for and in connection with specific products or compilations that\r
+contain the covered work, unless you entered into that arrangement,\r
+or that patent license was granted, prior to 28 March 2007.\r
+\r
+ Nothing in this License shall be construed as excluding or limiting\r
+any implied license or other defenses to infringement that may\r
+otherwise be available to you under applicable patent law.\r
+\r
+ 12. No Surrender of Others' Freedom.\r
+\r
+ If conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License. If you cannot convey a\r
+covered work so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you may\r
+not convey it at all. For example, if you agree to terms that obligate you\r
+to collect a royalty for further conveying from those to whom you convey\r
+the Program, the only way you could satisfy both those terms and this\r
+License would be to refrain entirely from conveying the Program.\r
+\r
+ 13. Use with the GNU Affero General Public License.\r
+\r
+ Notwithstanding any other provision of this License, you have\r
+permission to link or combine any covered work with a work licensed\r
+under version 3 of the GNU Affero General Public License into a single\r
+combined work, and to convey the resulting work. The terms of this\r
+License will continue to apply to the part which is the covered work,\r
+but the special requirements of the GNU Affero General Public License,\r
+section 13, concerning interaction through a network will apply to the\r
+combination as such.\r
+\r
+ 14. Revised Versions of this License.\r
+\r
+ The Free Software Foundation may publish revised and/or new versions of\r
+the GNU General Public License from time to time. Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+ Each version is given a distinguishing version number. If the\r
+Program specifies that a certain numbered version of the GNU General\r
+Public License "or any later version" applies to it, you have the\r
+option of following the terms and conditions either of that numbered\r
+version or of any later version published by the Free Software\r
+Foundation. If the Program does not specify a version number of the\r
+GNU General Public License, you may choose any version ever published\r
+by the Free Software Foundation.\r
+\r
+ If the Program specifies that a proxy can decide which future\r
+versions of the GNU General Public License can be used, that proxy's\r
+public statement of acceptance of a version permanently authorizes you\r
+to choose that version for the Program.\r
+\r
+ Later license versions may give you additional or different\r
+permissions. However, no additional obligations are imposed on any\r
+author or copyright holder as a result of your choosing to follow a\r
+later version.\r
+\r
+ 15. Disclaimer of Warranty.\r
+\r
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\r
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\r
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\r
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\r
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\r
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\r
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\r
+\r
+ 16. Limitation of Liability.\r
+\r
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\r
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\r
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\r
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\r
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\r
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\r
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\r
+SUCH DAMAGES.\r
+\r
+ 17. Interpretation of Sections 15 and 16.\r
+\r
+ If the disclaimer of warranty and limitation of liability provided\r
+above cannot be given local legal effect according to their terms,\r
+reviewing courts shall apply local law that most closely approximates\r
+an absolute waiver of all civil liability in connection with the\r
+Program, unless a warranty or assumption of liability accompanies a\r
+copy of the Program in return for a fee.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ How to Apply These Terms to Your New Programs\r
+\r
+ If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+ To do so, attach the following notices to the program. It is safest\r
+to attach them to the start of each source file to most effectively\r
+state the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+ <one line to give the program's name and a brief idea of what it does.>\r
+ Copyright (C) <year> <name of author>\r
+\r
+ This program is free software: you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation, either version 3 of the License, or\r
+ (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+ If the program does terminal interaction, make it output a short\r
+notice like this when it starts in an interactive mode:\r
+\r
+ <program> Copyright (C) <year> <name of author>\r
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+ This is free software, and you are welcome to redistribute it\r
+ under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License. Of course, your program's commands\r
+might be different; for a GUI interface, you would use an "about box".\r
+\r
+ You should also get your employer (if you work as a programmer) or school,\r
+if any, to sign a "copyright disclaimer" for the program, if necessary.\r
+For more information on this, and how to apply and follow the GNU GPL, see\r
+<http://www.gnu.org/licenses/>.\r
+\r
+ The GNU General Public License does not permit incorporating your program\r
+into proprietary programs. If your program is a subroutine library, you\r
+may consider it more useful to permit linking proprietary applications with\r
+the library. If this is what you want to do, use the GNU Lesser General\r
+Public License instead of this License. But first, please read\r
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.\r
--- /dev/null
+# Glaurung, a UCI chess playing engine.
+# Copyright (C) 2004-2007 Tord Romstad
+
+# This file is part of Glaurung.
+#
+# Glaurung is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Glaurung is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+###
+### Files
+###
+
+EXE = glaurung
+
+OBJS = bitboard.o color.o pawns.o material.o endgame.o evaluate.o main.o \
+ misc.o move.o movegen.o history.o movepick.o search.o piece.o \
+ position.o square.o direction.o tt.o value.o uci.o ucioption.o \
+ mersenne.o book.o bitbase.o san.o benchmark.o
+
+
+###
+### Rules
+###
+
+all: $(EXE) .depend
+
+clean:
+ $(RM) *.o .depend glaurung
+
+
+###
+### Compiler:
+###
+
+CXX = g++
+# CXX = g++-4.2
+# CXX = icpc
+
+
+###
+### Dependencies
+###
+
+$(EXE): $(OBJS)
+ $(CXX) $(LDFLAGS) -o $@ $(OBJS)
+
+.depend:
+ $(CXX) -MM $(OBJS:.o=.cpp) > $@
+
+include .depend
+
+
+###
+### Compiler and linker switches
+###
+
+# Enable/disable debugging:
+
+CXXFLAGS += -DNDEBUG
+
+
+# Compile with full warnings, and symbol names
+
+CXXFLAGS += -Wall -g
+
+
+# General optimization flags. Note that -O2 might be faster than -O3 on some
+# systems; this requires testing.
+
+CXXFLAGS += -O3 -fno-exceptions -fomit-frame-pointer -fno-rtti -fstrict-aliasing
+
+
+# Compiler optimization flags for the Intel C++ compiler in Mac OS X:
+
+# CXXFLAGS += -mdynamic-no-pic -no-prec-div -ipo -static -xP
+
+
+# Profiler guided optimization with the Intel C++ compiler. To use it, first
+# create the directory ./profdata if it does not already exist, and delete its
+# contents if it does exist. Then compile with -prof_gen, and run the
+# resulting binary for a while (for instance, do ./glaurung bench 128 1, and
+# wait 15 minutes for the benchmark to complete). Then do a 'make clean', and
+# recompile with -prof_use.
+
+# CXXFLAGS += -prof_gen -prof_dir ./profdata
+# CXXFLAGS += -prof_use -prof_dir ./profdata
+
+
+# Profiler guided optimization with GCC. I've never been able to make this
+# work.
+
+# CXXFLAGS += -fprofile-generate
+# LDFLAGS += -fprofile-generate
+# CXXFLAGS += -fprofile-use
+# CXXFLAGS += -fprofile-use
+
+
+# General linker flags
+
+LDFLAGS += -lm -lpthread
+
+
+# Compiler switches for generating binaries for various CPUs in Mac OS X.
+# Note that 'arch ppc' and 'arch ppc64' only works with g++, and not with
+# the intel compiler.
+
+# CXXFLAGS += -arch ppc
+# CXXFLAGS += -arch ppc64
+# CXXFLAGS += -arch i386
+# CXXFLAGS += -arch x86_64
+# LDFLAGS += -arch ppc
+# LDFLAGS += -arch ppc64
+# LDFLAGS += -arch i386
+# LDFLAGS += -arch x86_64
+
+
+# Backwards compatibility with Mac OS X 10.4 when compiling under 10.5 with
+# GCC 4.0. I haven't found a way to make it work with GCC 4.2.
+
+# CXXFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk
+# CXXFLAGS += -mmacosx-version-min=10.4
+# LDFLAGS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk
+# LDFLAGS += -Wl,-syslibroot /Developer/SDKs/MacOSX10.4u.sdk
+# LDFLAGS += -mmacosx-version-min=10.4
+
+
+# Backwards compatibility with Mac OS X 10.4 when compiling with ICC. Doesn't
+# work yet. :-(
+
+# CXXFLAGS += -DMAC_OS_X_VERSION_MIN_REQUIRED=1040
+# CXXFLAGS += -DMAC_OS_X_VERSION_MAX_ALLOWED=1040
+# CXXFLAGS += -D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1040
+# CXXFLAGS += -F/Developer/SDKs/MacOSX10.4u.sdk/
+# LDFLAGS += -Wl,-syslibroot -Wl,/Developer/SDKs/MacOSX10.4u.sdk
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include "benchmark.h"
+#include "search.h"
+#include "thread.h"
+#include "ucioption.h"
+
+
+////
+//// Variables
+////
+
+const std::string BenchmarkPositions[15] = {
+ "r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
+ "4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
+ "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
+ "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
+ "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
+ "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
+ "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
+ "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
+ "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
+ "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
+ "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
+ "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
+ "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
+ "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - 3 22",
+ "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
+};
+
+
+////
+//// Functions
+////
+
+/// benchmark() runs a simple benchmark by letting Glaurung analyze 15
+/// positions for 60 seconds each. There are two parameters; the
+/// transposition table size and the number of search threads that should
+/// be used. The analysis is written to a file named bench.txt.
+
+void benchmark(const std::string &ttSize, const std::string &threads) {
+ Position pos;
+ Move moves[1] = {MOVE_NONE};
+ int i;
+
+ i = atoi(ttSize.c_str());
+ if(i < 4 || i > 1024) {
+ std::cerr << "The hash table size must be between 4 and 1024" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ i = atoi(threads.c_str());
+ if(i < 1 || i > THREAD_MAX) {
+ std::cerr << "The number of threads must be between 1 and " << THREAD_MAX
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ set_option_value("Hash", ttSize);
+ set_option_value("Threads", threads);
+ set_option_value("OwnBook", "false");
+ set_option_value("Use Search Log", "true");
+ set_option_value("Search Log Filename", "bench.txt");
+
+ for(i = 0; i < 15; i++) {
+ pos.from_fen(BenchmarkPositions[i]);
+ think(pos, true, false, 0, 0, 0, 0, 0, 60000, moves);
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(BENCHMARK_H_INCLUDED)
+#define BENCHMARK_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include <string>
+
+
+////
+//// Prototypes
+////
+
+extern void benchmark(const std::string &ttSize, const std::string &threads);
+
+
+#endif // !defined(BENCHMARK_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "bitbase.h"
+#include "bitboard.h"
+#include "move.h"
+#include "square.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ enum Result {
+ RESULT_UNKNOWN,
+ RESULT_INVALID,
+ RESULT_WIN,
+ RESULT_LOSS,
+ RESULT_DRAW
+ };
+
+ struct KPKPosition {
+ void from_index(int index);
+ int to_index() const;
+ bool is_legal() const;
+ bool is_immediate_draw() const;
+ bool is_immediate_win() const;
+ Bitboard wk_attacks() const;
+ Bitboard bk_attacks() const;
+ Bitboard pawn_attacks() const;
+
+ Square whiteKingSquare, blackKingSquare, pawnSquare;
+ Color sideToMove;
+ };
+
+
+ Result *Bitbase;
+ const int IndexMax = 2*24*64*64;
+ int UnknownCount = 0;
+
+ void initialize();
+ bool next_iteration();
+ Result classify_wtm(const KPKPosition &p);
+ Result classify_btm(const KPKPosition &p);
+ int compute_index(Square wksq, Square bksq, Square psq, Color stm);
+ int compress_result(Result r);
+
+}
+
+
+////
+//// Functions
+////
+
+void generate_kpk_bitbase(uint8_t bitbase[]) {
+ // Allocate array and initialize:
+ Bitbase = new Result[IndexMax];
+ initialize();
+
+ // Iterate until all positions are classified:
+ while(next_iteration());
+
+ // Compress bitbase into the supplied parameter:
+ int i, j, b;
+ for(i = 0; i < 24576; i++) {
+ for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
+ bitbase[i] = b;
+ }
+
+ // Release allocated memory:
+ delete [] Bitbase;
+}
+
+
+namespace {
+
+ void KPKPosition::from_index(int index) {
+ int s;
+ sideToMove = Color(index % 2);
+ blackKingSquare = Square((index / 2) % 64);
+ whiteKingSquare = Square((index / 128) % 64);
+ s = (index / 8192) % 24;
+ pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
+ }
+
+
+ int KPKPosition::to_index() const {
+ return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
+ sideToMove);
+ }
+
+
+ bool KPKPosition::is_legal() const {
+ if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
+ pawnSquare == blackKingSquare)
+ return false;
+ if(sideToMove == WHITE) {
+ if(bit_is_set(this->wk_attacks(), blackKingSquare))
+ return false;
+ if(bit_is_set(this->pawn_attacks(), blackKingSquare))
+ return false;
+ }
+ else {
+ if(bit_is_set(this->bk_attacks(), whiteKingSquare))
+ return false;
+ }
+ return true;
+ }
+
+
+ bool KPKPosition::is_immediate_draw() const {
+ if(sideToMove == BLACK) {
+ Bitboard wka = this->wk_attacks();
+ Bitboard bka = this->bk_attacks();
+
+ // Case 1: Stalemate
+ if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
+ return true;
+
+ // Case 2: King can capture pawn
+ if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
+ return true;
+ }
+ else {
+ // Case 1: Stalemate
+ if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
+ (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool KPKPosition::is_immediate_win() const {
+ // The position is an immediate win if it is white to move and the white
+ // pawn can be promoted without getting captured:
+ return
+ sideToMove == WHITE &&
+ square_rank(pawnSquare) == RANK_7 &&
+ (square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 ||
+ bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N));
+ }
+
+
+ Bitboard KPKPosition::wk_attacks() const {
+ return StepAttackBB[WK][whiteKingSquare];
+ }
+
+
+ Bitboard KPKPosition::bk_attacks() const {
+ return StepAttackBB[BK][blackKingSquare];
+ }
+
+
+ Bitboard KPKPosition::pawn_attacks() const {
+ return StepAttackBB[WP][pawnSquare];
+ }
+
+
+ void initialize() {
+ KPKPosition p;
+ for(int i = 0; i < IndexMax; i++) {
+ p.from_index(i);
+ if(!p.is_legal())
+ Bitbase[i] = RESULT_INVALID;
+ else if(p.is_immediate_draw())
+ Bitbase[i] = RESULT_DRAW;
+ else if(p.is_immediate_win())
+ Bitbase[i] = RESULT_WIN;
+ else {
+ Bitbase[i] = RESULT_UNKNOWN;
+ UnknownCount++;
+ }
+ }
+ }
+
+
+ bool next_iteration() {
+ KPKPosition p;
+ int previousUnknownCount = UnknownCount;
+
+ for(int i = 0; i < IndexMax; i++)
+ if(Bitbase[i] == RESULT_UNKNOWN) {
+ p.from_index(i);
+
+ Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
+
+ if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
+ Bitbase[i] == RESULT_DRAW)
+ UnknownCount--;
+ }
+
+ return UnknownCount != previousUnknownCount;
+ }
+
+
+ Result classify_wtm(const KPKPosition &p) {
+
+ // If one move leads to a position classified as RESULT_LOSS, the result
+ // of the current position is RESULT_WIN. If all moves lead to positions
+ // classified as RESULT_DRAW, the current position is classified as
+ // RESULT_DRAW. Otherwise, the current position is classified as
+ // RESULT_UNKNOWN.
+
+ bool unknownFound = false;
+ Bitboard b;
+ Square s;
+
+ // King moves
+ b = p.wk_attacks();
+ while(b) {
+ s = pop_1st_bit(&b);
+ switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
+ BLACK)]) {
+ case RESULT_LOSS:
+ return RESULT_WIN;
+
+ case RESULT_UNKNOWN:
+ unknownFound = true;
+ break;
+
+ case RESULT_DRAW: case RESULT_INVALID:
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ // Pawn moves
+ if(square_rank(p.pawnSquare) < RANK_7) {
+ s = p.pawnSquare + DELTA_N;
+ switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
+ BLACK)]) {
+ case RESULT_LOSS:
+ return RESULT_WIN;
+
+ case RESULT_UNKNOWN:
+ unknownFound = true;
+ break;
+
+ case RESULT_DRAW: case RESULT_INVALID:
+ break;
+
+ default:
+ assert(false);
+ }
+
+ if(square_rank(s) == RANK_3 &&
+ s != p.whiteKingSquare && s != p.blackKingSquare) {
+ s += DELTA_N;
+ switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
+ BLACK)]) {
+ case RESULT_LOSS:
+ return RESULT_WIN;
+
+ case RESULT_UNKNOWN:
+ unknownFound = true;
+ break;
+
+ case RESULT_DRAW: case RESULT_INVALID:
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
+ }
+
+
+ Result classify_btm(const KPKPosition &p) {
+
+ // If one move leads to a position classified as RESULT_DRAW, the result
+ // of the current position is RESULT_DRAW. If all moves lead to positions
+ // classified as RESULT_WIN, the current position is classified as
+ // RESULT_LOSS. Otherwise, the current position is classified as
+ // RESULT_UNKNOWN.
+
+ bool unknownFound = false;
+ Bitboard b;
+ Square s;
+
+ // King moves
+ b = p.bk_attacks();
+ while(b) {
+ s = pop_1st_bit(&b);
+ switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
+ WHITE)]) {
+ case RESULT_DRAW:
+ return RESULT_DRAW;
+
+ case RESULT_UNKNOWN:
+ unknownFound = true;
+ break;
+
+ case RESULT_WIN: case RESULT_INVALID:
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
+ }
+
+
+ int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
+ int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
+ int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
+ assert(result >= 0 && result < IndexMax);
+ return result;
+ }
+
+
+ int compress_result(Result r) {
+ return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(BITBASE_H_INCLUDED)
+#define BITBASE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "types.h"
+
+
+////
+//// Prototypes
+////
+
+extern void generate_kpk_bitbase(uint8_t bitbase[]);
+
+
+#endif // !defined(BITBASE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <iostream>
+
+#include "bitboard.h"
+#include "direction.h"
+
+
+////
+//// Constants and variables
+////
+
+const Bitboard SquaresByColorBB[2] = {BlackSquaresBB, WhiteSquaresBB};
+
+const Bitboard FileBB[8] = {
+ FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
+};
+
+const Bitboard NeighboringFilesBB[8] = {
+ FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
+ FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
+};
+
+const Bitboard ThisAndNeighboringFilesBB[8] = {
+ FileABB|FileBBB, FileABB|FileBBB|FileCBB,
+ FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
+ FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
+ FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
+};
+
+const Bitboard RankBB[8] = {
+ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
+};
+
+const Bitboard RelativeRankBB[2][8] = {
+ {
+ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
+ },
+ {
+ Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB
+ }
+};
+
+const Bitboard InFrontBB[2][8] = {
+ {
+ Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
+ Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
+ Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
+ Rank5BB | Rank6BB | Rank7BB | Rank8BB,
+ Rank6BB | Rank7BB | Rank8BB,
+ Rank7BB | Rank8BB,
+ Rank8BB,
+ EmptyBoardBB
+ },
+ {
+ EmptyBoardBB,
+ Rank1BB,
+ Rank2BB | Rank1BB,
+ Rank3BB | Rank2BB | Rank1BB,
+ Rank4BB | Rank3BB | Rank2BB | Rank1BB,
+ Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
+ Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
+ Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
+ }
+};
+
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+
+Bitboard RankAttacks[8][64], FileAttacks[8][64];
+
+#elif defined(USE_32BIT_ATTACKS)
+
+const uint64_t RMult[64] = {
+ 0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
+ 0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
+ 0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
+ 0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
+ 0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
+ 0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
+ 0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
+ 0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
+ 0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
+ 0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
+ 0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
+ 0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
+ 0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
+ 0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
+ 0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
+ 0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
+ 0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
+ 0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
+ 0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
+ 0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
+ 0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
+ 0x5ac08a4735370479ULL,
+};
+
+const int RShift[64] = {
+ 20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
+ 21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
+ 21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
+ 21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
+};
+
+#else // if defined(USE_32BIT_ATTACKS)
+
+const uint64_t RMult[64] = {
+ 0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
+ 0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
+ 0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
+ 0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
+ 0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
+ 0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
+ 0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
+ 0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
+ 0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
+ 0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
+ 0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
+ 0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
+ 0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
+ 0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
+ 0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
+ 0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
+ 0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
+ 0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
+ 0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
+ 0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
+ 0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
+ 0x410201ce5c030092ULL
+};
+
+const int RShift[64] = {
+ 52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
+ 53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
+ 53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
+ 53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
+};
+
+#endif // defined(USE_32BIT_ATTACKS)
+
+#if !defined(USE_COMPACT_ROOK_ATTACKS)
+Bitboard RMask[64];
+int RAttackIndex[64];
+Bitboard RAttacks[0x19000];
+#endif
+
+#if defined(USE_32BIT_ATTACKS)
+
+const uint64_t BMult[64] = {
+ 0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
+ 0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
+ 0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
+ 0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
+ 0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
+ 0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
+ 0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
+ 0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
+ 0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
+ 0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
+ 0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
+ 0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
+ 0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
+ 0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
+ 0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
+ 0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
+ 0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
+ 0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
+ 0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
+ 0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
+ 0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
+ 0xdc01f6dca4bebbdcULL,
+};
+
+const int BShift[64] = {
+ 26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
+ 27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
+};
+
+#else // if defined(USE_32BIT_ATTACKS)
+
+const uint64_t BMult[64] = {
+ 0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
+ 0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
+ 0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
+ 0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
+ 0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
+ 0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
+ 0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
+ 0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
+ 0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
+ 0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
+ 0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
+ 0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
+ 0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
+ 0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
+ 0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
+ 0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
+ 0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
+ 0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
+ 0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
+ 0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
+ 0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
+ 0xa08520292120600ULL
+};
+
+const int BShift[64] = {
+ 58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
+ 59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
+};
+
+#endif // defined(USE_32BIT_ATTACKS)
+
+Bitboard BMask[64];
+int BAttackIndex[64];
+Bitboard BAttacks[0x1480];
+
+Bitboard SetMaskBB[64];
+Bitboard ClearMaskBB[64];
+
+Bitboard StepAttackBB[16][64];
+Bitboard RayBB[64][8];
+Bitboard BetweenBB[64][64];
+
+Bitboard PassedPawnMask[2][64];
+Bitboard OutpostMask[2][64];
+
+Bitboard BishopPseudoAttacks[64];
+Bitboard RookPseudoAttacks[64];
+Bitboard QueenPseudoAttacks[64];
+
+
+////
+//// Local definitions
+////
+
+namespace {
+ void init_masks();
+ void init_ray_bitboards();
+ void init_attacks();
+ void init_between_bitboards();
+ Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
+ int fmin, int fmax, int rmin, int rmax);
+ Bitboard index_to_bitboard(int index, Bitboard mask);
+ void init_sliding_attacks(Bitboard attacks[],
+ int attackIndex[], Bitboard mask[],
+ const int shift[2], const Bitboard mult[],
+ int deltas[][2]);
+ void init_pseudo_attacks();
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+ void init_file_and_rank_attacks();
+#endif
+};
+
+
+////
+//// Functions
+////
+
+/// print_bitboard() prints a bitboard in an easily readable format to the
+/// standard output. This is sometimes useful for debugging.
+
+void print_bitboard(Bitboard b) {
+ for(Rank r = RANK_8; r >= RANK_1; r--) {
+ std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
+ for(File f = FILE_A; f <= FILE_H; f++)
+ std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
+ std::cout << "|" << std::endl;
+ }
+ std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
+}
+
+
+/// init_bitboards() initializes various bitboard arrays. It is called during
+/// program initialization.
+
+void init_bitboards() {
+ int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
+ int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
+ init_masks();
+ init_ray_bitboards();
+ init_attacks();
+ init_between_bitboards();
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+ init_file_and_rank_attacks();
+#else
+ init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift,
+ RMult, rookDeltas);
+#endif
+ init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift,
+ BMult, bishopDeltas);
+ init_pseudo_attacks();
+}
+
+
+#if defined(USE_FOLDED_BITSCAN)
+
+static const int BitTable[64] = {
+ 63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
+ 51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
+ 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
+ 58, 20, 37, 17, 36, 8
+};
+
+
+/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
+
+Square first_1(Bitboard b) {
+ b ^= (b - 1);
+ uint32_t fold = int(b) ^ int(b >> 32);
+ return Square(BitTable[(fold * 0x783a9b23) >> 26]);
+}
+
+
+/// pop_1st_bit() finds and clears the least significant nonzero bit in a
+/// nonzero bitboard.
+
+Square pop_1st_bit(Bitboard *b) {
+ Bitboard bb = *b ^ (*b - 1);
+ uint32_t fold = int(bb) ^ int(bb >> 32);
+ *b &= (*b - 1);
+ return Square(BitTable[(fold * 0x783a9b23) >> 26]);
+}
+
+#else
+
+static const int BitTable[64] = {
+ 0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
+ 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
+ 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
+ 51, 60, 42, 59, 58
+};
+
+
+/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
+
+Square first_1(Bitboard b) {
+ return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
+}
+
+
+/// pop_1st_bit() finds and clears the least significant nonzero bit in a
+/// nonzero bitboard.
+
+Square pop_1st_bit(Bitboard *b) {
+ Bitboard bb = *b;
+ *b &= (*b - 1);
+ return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
+}
+
+#endif // defined(USE_FOLDED_BITSCAN)
+
+
+namespace {
+
+ // All functions below are used to precompute various bitboards during
+ // program initialization. Some of the functions may be difficult to
+ // understand, but they all seem to work correctly, and it should never
+ // be necessary to touch any of them.
+
+ void init_masks() {
+ for(Square s = SQ_A1; s <= SQ_H8; s++) {
+ SetMaskBB[s] = (1ULL << s);
+ ClearMaskBB[s] = ~SetMaskBB[s];
+ }
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(Square s = SQ_A1; s <= SQ_H8; s++) {
+ PassedPawnMask[c][s] =
+ in_front_bb(c, s) & this_and_neighboring_files_bb(s);
+ OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
+ }
+ }
+
+
+ void init_ray_bitboards() {
+ int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
+ for(int i = 0; i < 128; i = i + 9 & ~8) {
+ for(int j = 0; j < 8; j++) {
+ RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
+ for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
+ set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
+ }
+ }
+ }
+
+
+ void init_attacks() {
+ int i, j, k, l;
+ int step[16][8] = {
+ {0},
+ {7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
+ {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
+ {-7,-9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
+ {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
+ };
+
+ for(i = 0; i < 64; i++) {
+ for(j = 0; j <= int(BK); j++) {
+ StepAttackBB[j][i] = EmptyBoardBB;
+ for(k = 0; k < 8 && step[j][k] != 0; k++) {
+ l = i + step[j][k];
+ if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
+ StepAttackBB[j][i] |= (1ULL << l);
+ }
+ }
+ }
+ }
+
+
+ Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
+ int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
+ Bitboard result = 0ULL;
+ int rk = sq / 8, fl = sq % 8, r, f, i;
+ for(i = 0; i < dirs; i++) {
+ int dx = deltas[i][0], dy = deltas[i][1];
+ for(f = fl+dx, r = rk+dy;
+ (dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
+ f += dx, r += dy) {
+ result |= (1ULL << (f + r*8));
+ if(block & (1ULL << (f + r*8))) break;
+ }
+ }
+ return result;
+ }
+
+
+ void init_between_bitboards() {
+ SquareDelta step[8] = {
+ DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
+ };
+ SignedDirection d;
+ for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
+ for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
+ BetweenBB[s1][s2] = EmptyBoardBB;
+ d = signed_direction_between_squares(s1, s2);
+ if(d != SIGNED_DIR_NONE)
+ for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
+ set_bit(&(BetweenBB[s1][s2]), s3);
+ }
+ }
+
+
+ Bitboard index_to_bitboard(int index, Bitboard mask) {
+ int i, j, bits = count_1s(mask);
+ Bitboard result = 0ULL;
+ for(i = 0; i < bits; i++) {
+ j = pop_1st_bit(&mask);
+ if(index & (1 << i)) result |= (1ULL << j);
+ }
+ return result;
+ }
+
+
+ void init_sliding_attacks(Bitboard attacks[],
+ int attackIndex[], Bitboard mask[],
+ const int shift[2], const Bitboard mult[],
+ int deltas[][2]) {
+ int i, j, k, index = 0;
+ Bitboard b;
+ for(i = 0; i < 64; i++) {
+ attackIndex[i] = index;
+ mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
+ j = (1 << (64 - shift[i]));
+ for(k = 0; k < j; k++) {
+#if defined(USE_32BIT_ATTACKS)
+ b = index_to_bitboard(k, mask[i]);
+ attacks[index +
+ (unsigned(int(b) * int(mult[i]) ^
+ int(b >> 32) * int(mult[i] >> 32))
+ >> shift[i])] =
+ sliding_attacks(i, b, 4, deltas);
+#else
+ b = index_to_bitboard(k, mask[i]);
+ attacks[index + ((b * mult[i]) >> shift[i])] =
+ sliding_attacks(i, b, 4, deltas);
+#endif
+ }
+ index += j;
+ }
+ }
+
+
+ void init_pseudo_attacks() {
+ Square s;
+ for(s = SQ_A1; s <= SQ_H8; s++) {
+ BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
+ RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
+ QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
+ }
+ }
+
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+ void init_file_and_rank_attacks() {
+ int i, j, k, l, m, s;
+ Bitboard b1, b2;
+ for(i = 0; i < 64; i++) {
+
+ for(m = 0; m <= 1; m++) {
+ b1 = 0ULL;
+ for(j = 0; j < 6; j++) if(i & (1<<j)) b1 |= (1ULL << ((j+1)*(1+m*7)));
+ for(j = 0; j < 8; j++) {
+ b2 = 0ULL;
+ for(k = 0, s = 1; k < 2; k++, s *= -1) {
+ for(l = j+s; l >= 0 && l <= 7; l += s) {
+ b2 |= (m? RankBB[l] : FileBB[l]);
+ if(b1 & (1ULL << (l*(1+m*7)))) break;
+ }
+ }
+ if(m) FileAttacks[j][(b1*0xd6e8802041d0c441ULL) >> 58] = b2;
+ else RankAttacks[j][i] = b2;
+ }
+ }
+ }
+ }
+#endif // defined(USE_COMPACT_ROOK_ATTACKS)
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(BITBOARD_H_INCLUDED)
+#define BITBOARD_H_INCLUDED
+
+
+////
+//// Defines
+////
+
+//#define USE_COMPACT_ROOK_ATTACKS
+//#define USE_32BIT_ATTACKS
+#define USE_FOLDED_BITSCAN
+
+#define BITCOUNT_SWAR_64
+//#define BITCOUNT_SWAR_32
+//#define BITCOUNT_LOOP
+
+
+
+////
+//// Includes
+////
+
+#include "direction.h"
+#include "piece.h"
+#include "square.h"
+#include "types.h"
+
+
+////
+//// Types
+////
+
+typedef uint64_t Bitboard;
+
+
+////
+//// Constants and variables
+////
+
+const Bitboard EmptyBoardBB = 0ULL;
+
+const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
+const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
+
+extern const Bitboard SquaresByColorBB[2];
+
+const Bitboard FileABB = 0x0101010101010101ULL;
+const Bitboard FileBBB = 0x0202020202020202ULL;
+const Bitboard FileCBB = 0x0404040404040404ULL;
+const Bitboard FileDBB = 0x0808080808080808ULL;
+const Bitboard FileEBB = 0x1010101010101010ULL;
+const Bitboard FileFBB = 0x2020202020202020ULL;
+const Bitboard FileGBB = 0x4040404040404040ULL;
+const Bitboard FileHBB = 0x8080808080808080ULL;
+
+extern const Bitboard FileBB[8];
+extern const Bitboard NeighboringFilesBB[8];
+extern const Bitboard ThisAndNeighboringFilesBB[8];
+
+const Bitboard Rank1BB = 0xFFULL;
+const Bitboard Rank2BB = 0xFF00ULL;
+const Bitboard Rank3BB = 0xFF0000ULL;
+const Bitboard Rank4BB = 0xFF000000ULL;
+const Bitboard Rank5BB = 0xFF00000000ULL;
+const Bitboard Rank6BB = 0xFF0000000000ULL;
+const Bitboard Rank7BB = 0xFF000000000000ULL;
+const Bitboard Rank8BB = 0xFF00000000000000ULL;
+
+extern const Bitboard RankBB[8];
+extern const Bitboard RelativeRankBB[2][8];
+extern const Bitboard InFrontBB[2][8];
+
+extern Bitboard SetMaskBB[64];
+extern Bitboard ClearMaskBB[64];
+
+extern Bitboard StepAttackBB[16][64];
+extern Bitboard RayBB[64][8];
+extern Bitboard BetweenBB[64][64];
+
+extern Bitboard PassedPawnMask[2][64];
+extern Bitboard OutpostMask[2][64];
+
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+extern Bitboard RankAttacks[8][64], FileAttacks[8][64];
+#else
+extern const uint64_t RMult[64];
+extern const int RShift[64];
+extern Bitboard RMask[64];
+extern int RAttackIndex[64];
+extern Bitboard RAttacks[0x19000];
+#endif // defined(USE_COMPACT_ROOK_ATTACKS)
+
+extern const uint64_t BMult[64];
+extern const int BShift[64];
+extern Bitboard BMask[64];
+extern int BAttackIndex[64];
+extern Bitboard BAttacks[0x1480];
+
+extern Bitboard BishopPseudoAttacks[64];
+extern Bitboard RookPseudoAttacks[64];
+extern Bitboard QueenPseudoAttacks[64];
+
+
+////
+//// Inline functions
+////
+
+/// Functions for testing whether a given bit is set in a bitboard, and for
+/// setting and clearing bits.
+
+inline Bitboard set_mask_bb(Square s) {
+ // return 1ULL << s;
+ return SetMaskBB[s];
+}
+
+inline Bitboard clear_mask_bb(Square s) {
+ // return ~set_mask_bb(s);
+ return ClearMaskBB[s];
+}
+
+inline Bitboard bit_is_set(Bitboard b, Square s) {
+ return b & set_mask_bb(s);
+}
+
+inline void set_bit(Bitboard *b, Square s) {
+ *b |= set_mask_bb(s);
+}
+
+inline void clear_bit(Bitboard *b, Square s) {
+ *b &= clear_mask_bb(s);
+}
+
+
+/// rank_bb() and file_bb() gives a bitboard containing all squares on a given
+/// file or rank. It is also possible to pass a square as input to these
+/// functions.
+
+inline Bitboard rank_bb(Rank r) {
+ return RankBB[r];
+}
+
+inline Bitboard rank_bb(Square s) {
+ return rank_bb(square_rank(s));
+}
+
+inline Bitboard file_bb(File f) {
+ return FileBB[f];
+}
+
+inline Bitboard file_bb(Square s) {
+ return file_bb(square_file(s));
+}
+
+
+/// neighboring_files_bb takes a file or a square as input, and returns a
+/// bitboard representing all squares on the neighboring files.
+
+inline Bitboard neighboring_files_bb(File f) {
+ return NeighboringFilesBB[f];
+}
+
+inline Bitboard neighboring_files_bb(Square s) {
+ return neighboring_files_bb(square_file(s));
+}
+
+
+/// this_and_neighboring_files_bb takes a file or a square as input, and
+/// returns a bitboard representing all squares on the given and neighboring
+/// files.
+
+inline Bitboard this_and_neighboring_files_bb(File f) {
+ return ThisAndNeighboringFilesBB[f];
+}
+
+inline Bitboard this_and_neighboring_files_bb(Square s) {
+ return this_and_neighboring_files_bb(square_file(s));
+}
+
+
+/// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
+/// representing all squares on the given rank from the given color's point of
+/// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
+/// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
+/// rank.
+
+inline Bitboard relative_rank_bb(Color c, Rank r) {
+ return RelativeRankBB[c][r];
+}
+
+
+/// in_front_bb() takes a color and a rank or square as input, and returns a
+/// bitboard representing all the squares on all ranks in front of the rank
+/// (or square), from the given color's point of view. For instance,
+/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
+/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
+
+inline Bitboard in_front_bb(Color c, Rank r) {
+ return InFrontBB[c][r];
+}
+
+inline Bitboard in_front_bb(Color c, Square s) {
+ return in_front_bb(c, square_rank(s));
+}
+
+
+/// ray_bb() gives a bitboard representing all squares along the ray in a
+/// given direction from a given square.
+
+inline Bitboard ray_bb(Square s, SignedDirection d) {
+ return RayBB[s][d];
+}
+
+
+/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
+/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
+/// bitboard of occupied squares as input, and return a bitboard representing
+/// all squares attacked by a rook, bishop or queen on the given square.
+
+#if defined(USE_COMPACT_ROOK_ATTACKS)
+
+inline Bitboard file_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = (blockers >> square_file(s)) & 0x01010101010100ULL;
+ return
+ FileAttacks[square_rank(s)][(b*0xd6e8802041d0c441ULL)>>58] & file_bb(s);
+}
+
+inline Bitboard rank_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = (blockers >> ((s & 56) + 1)) & 63;
+ return RankAttacks[square_file(s)][b] & rank_bb(s);
+}
+
+inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
+ return file_attacks_bb(s, blockers) | rank_attacks_bb(s, blockers);
+}
+
+#elif defined(USE_32BIT_ATTACKS)
+
+inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = blockers & RMask[s];
+ return RAttacks[RAttackIndex[s] +
+ (unsigned(int(b) * int(RMult[s]) ^
+ int(b >> 32) * int(RMult[s] >> 32))
+ >> RShift[s])];
+}
+
+#else
+
+inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = blockers & RMask[s];
+ return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
+}
+
+#endif
+
+#if defined(USE_32BIT_ATTACKS)
+
+inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = blockers & BMask[s];
+ return BAttacks[BAttackIndex[s] +
+ (unsigned(int(b) * int(BMult[s]) ^
+ int(b >> 32) * int(BMult[s] >> 32))
+ >> BShift[s])];
+}
+
+#else // defined(USE_32BIT_ATTACKS)
+
+inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
+ Bitboard b = blockers & BMask[s];
+ return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
+}
+
+#endif // defined(USE_32BIT_ATTACKS)
+
+inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
+ return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
+}
+
+
+/// squares_between returns a bitboard representing all squares between
+/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
+/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
+/// on the same line, file or diagonal, EmptyBoardBB is returned.
+
+inline Bitboard squares_between(Square s1, Square s2) {
+ return BetweenBB[s1][s2];
+}
+
+
+/// squares_in_front_of takes a color and a square as input, and returns a
+/// bitboard representing all squares along the line in front of the square,
+/// from the point of view of the given color. For instance,
+/// squares_in_front_of(BLACK, SQ_E4) returns a bitboard with the squares
+/// e3, e2 and e1 set.
+
+inline Bitboard squares_in_front_of(Color c, Square s) {
+ return in_front_bb(c, s) & file_bb(s);
+}
+
+
+/// squares_behind is similar to squares_in_front, but returns the squares
+/// behind the square instead of in front of the square.
+
+inline Bitboard squares_behind(Color c, Square s) {
+ return in_front_bb(opposite_color(c), s) & file_bb(s);
+}
+
+
+/// passed_pawn_mask takes a color and a square as input, and returns a
+/// bitboard mask which can be used to test if a pawn of the given color on
+/// the given square is a passed pawn.
+
+inline Bitboard passed_pawn_mask(Color c, Square s) {
+ return PassedPawnMask[c][s];
+}
+
+
+/// outpost_mask takes a color and a square as input, and returns a bitboard
+/// mask which can be used to test whether a piece on the square can possibly
+/// be driven away by an enemy pawn.
+
+inline Bitboard outpost_mask(Color c, Square s) {
+ return OutpostMask[c][s];
+}
+
+
+/// isolated_pawn_mask takes a square as input, and returns a bitboard mask
+/// which can be used to test whether a pawn on the given square is isolated.
+
+inline Bitboard isolated_pawn_mask(Square s) {
+ return neighboring_files_bb(s);
+}
+
+
+/// count_1s() counts the number of nonzero bits in a bitboard.
+
+#if defined(BITCOUNT_LOOP)
+
+inline int count_1s(Bitboard b) {
+ int r;
+ for(r = 0; b; r++, b &= b - 1);
+ return r;
+}
+
+inline int count_1s_max_15(Bitboard b) {
+ return count_1s(b);
+}
+
+#elif defined(BITCOUNT_SWAR_32)
+
+inline int count_1s(Bitboard b) {
+ unsigned w = unsigned(b >> 32), v = unsigned(b);
+ v = v - ((v >> 1) & 0x55555555);
+ w = w - ((w >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
+ v = (v + (v >> 4)) & 0x0F0F0F0F;
+ w = (w + (w >> 4)) & 0x0F0F0F0F;
+ v = ((v+w) * 0x01010101) >> 24; // mul is fast on amd procs
+ return int(v);
+}
+
+inline int count_1s_max_15(Bitboard b) {
+ unsigned w = unsigned(b >> 32), v = unsigned(b);
+ v = v - ((v >> 1) & 0x55555555);
+ w = w - ((w >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
+ v = ((v+w) * 0x11111111) >> 28;
+ return int(v);
+}
+
+#elif defined(BITCOUNT_SWAR_64)
+
+inline int count_1s(Bitboard b) {
+ b -= ((b>>1) & 0x5555555555555555ULL);
+ b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
+ b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
+ b *= 0x0101010101010101ULL;
+ return int(b >> 56);
+}
+
+inline int count_1s_max_15(Bitboard b) {
+ b -= (b>>1) & 0x5555555555555555ULL;
+ b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
+ b *= 0x1111111111111111ULL;
+ return int(b >> 60);
+}
+
+#endif // BITCOUNT
+
+
+////
+//// Prototypes
+////
+
+extern void print_bitboard(Bitboard b);
+extern void init_bitboards();
+extern Square first_1(Bitboard b);
+extern Square pop_1st_bit(Bitboard *b);
+
+
+#endif // !defined(BITBOARD_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ The code in this file is based on the opening book code in PolyGlot
+ by Fabien Letouzey. PolyGlot is available under the GNU General
+ Public License, and can be downloaded from http://wbec-ridderkerk.nl
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstdio>
+
+#include "book.h"
+#include "mersenne.h"
+#include "movegen.h"
+
+
+////
+//// Global variables
+////
+
+Book OpeningBook;
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ /// Random numbers from PolyGlot, used to compute book hash keys.
+
+ const uint64_t Random64[781] = {
+ 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
+ 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
+ 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
+ 0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
+ 0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
+ 0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
+ 0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
+ 0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
+ 0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
+ 0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
+ 0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
+ 0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
+ 0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
+ 0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
+ 0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
+ 0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
+ 0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
+ 0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
+ 0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
+ 0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
+ 0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
+ 0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
+ 0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
+ 0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
+ 0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
+ 0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
+ 0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
+ 0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
+ 0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
+ 0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
+ 0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
+ 0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
+ 0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
+ 0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
+ 0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
+ 0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
+ 0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
+ 0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
+ 0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
+ 0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
+ 0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
+ 0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
+ 0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
+ 0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
+ 0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
+ 0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
+ 0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
+ 0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
+ 0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
+ 0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
+ 0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
+ 0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
+ 0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
+ 0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
+ 0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
+ 0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
+ 0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
+ 0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
+ 0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
+ 0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
+ 0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
+ 0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
+ 0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
+ 0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
+ 0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
+ 0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
+ 0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
+ 0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
+ 0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
+ 0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
+ 0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
+ 0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
+ 0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
+ 0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
+ 0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
+ 0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
+ 0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
+ 0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
+ 0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
+ 0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
+ 0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
+ 0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
+ 0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
+ 0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
+ 0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
+ 0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
+ 0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
+ 0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
+ 0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
+ 0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
+ 0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
+ 0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
+ 0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
+ 0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
+ 0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
+ 0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
+ 0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
+ 0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
+ 0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
+ 0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
+ 0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
+ 0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
+ 0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
+ 0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
+ 0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
+ 0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
+ 0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
+ 0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
+ 0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
+ 0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
+ 0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
+ 0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
+ 0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
+ 0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
+ 0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
+ 0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
+ 0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
+ 0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
+ 0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
+ 0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
+ 0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
+ 0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
+ 0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
+ 0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
+ 0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
+ 0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
+ 0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
+ 0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
+ 0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
+ 0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
+ 0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
+ 0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
+ 0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
+ 0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
+ 0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
+ 0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
+ 0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
+ 0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
+ 0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
+ 0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
+ 0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
+ 0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
+ 0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
+ 0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
+ 0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
+ 0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
+ 0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
+ 0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
+ 0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
+ 0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
+ 0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
+ 0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
+ 0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
+ 0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
+ 0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
+ 0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
+ 0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
+ 0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
+ 0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
+ 0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
+ 0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
+ 0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
+ 0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
+ 0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
+ 0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
+ 0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
+ 0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
+ 0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
+ 0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
+ 0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
+ 0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
+ 0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
+ 0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
+ 0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
+ 0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
+ 0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
+ 0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
+ 0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
+ 0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
+ 0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
+ 0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
+ 0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
+ 0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
+ 0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
+ 0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
+ 0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
+ 0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
+ 0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
+ 0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
+ 0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
+ 0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
+ 0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
+ 0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
+ 0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
+ 0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
+ 0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
+ 0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
+ 0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
+ 0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
+ 0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
+ 0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
+ 0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
+ 0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
+ 0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
+ 0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
+ 0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
+ 0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
+ 0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
+ 0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
+ 0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
+ 0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
+ 0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
+ 0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
+ 0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
+ 0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
+ 0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
+ 0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
+ 0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
+ 0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
+ 0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
+ 0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
+ 0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
+ 0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
+ 0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
+ 0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
+ 0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
+ 0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
+ 0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
+ 0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
+ 0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
+ 0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
+ 0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
+ 0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
+ 0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
+ 0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
+ 0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
+ 0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
+ 0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
+ 0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
+ 0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
+ 0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
+ 0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
+ 0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
+ 0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
+ 0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
+ 0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
+ 0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
+ 0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
+ 0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
+ 0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
+ 0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
+ 0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
+ 0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
+ 0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
+ 0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
+ 0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
+ 0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
+ 0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
+ 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
+ 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
+ 0xF8D626AAAF278509ULL
+ };
+
+
+ /// Indices to the Random64[] array
+
+ const int RandomPiece = 0;
+ const int RandomCastle = 768;
+ const int RandomEnPassant = 772;
+ const int RandomTurn = 780;
+
+
+ /// Convert pieces to the range 0..1
+
+ const int PieceTo12[] = {
+ 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11
+ };
+
+
+ /// Prototypes
+
+ uint64_t book_key(const Position &pos);
+ uint64_t book_piece_key(Piece p, Square s);
+ uint64_t book_castle_key(const Position &pos);
+ uint64_t book_ep_key(const Position &pos);
+ uint64_t book_color_key(const Position &pos);
+
+ uint64_t read_integer(FILE *file, int size);
+
+}
+
+
+////
+//// Functions
+////
+
+
+/// Constructor
+
+Book::Book() {
+ bookFile = NULL;
+ bookSize = 0;
+}
+
+
+/// Book::open() opens a book file with a given file name.
+
+void Book::open(const std::string &fName) {
+ fileName = fName;
+ bookFile = fopen(fileName.c_str(), "rb");
+ if(bookFile != NULL) {
+ if(fseek(bookFile, 0, SEEK_END) == -1) {
+ std::cerr << "Failed to open book file " << fileName << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ bookSize = ftell(bookFile) / 16;
+ if(bookSize == -1) {
+ std::cerr << "Failed to open book file " << fileName << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+
+/// Book::close() closes the currently open book file.
+
+void Book::close() {
+ if(bookFile != NULL && fclose(bookFile) == EOF) {
+ std::cerr << "Failed to close book file" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+/// Book::is_open() tests whether a book file has been opened.
+
+bool Book::is_open() const {
+ return bookFile != NULL && bookSize != 0;
+}
+
+
+/// Book::file_name() returns the file name of the currently active book,
+/// or the empty string if no book is open.
+
+const std::string Book::file_name() const {
+ return this->is_open()? fileName : "";
+}
+
+
+/// Book::get_move() gets a book move for a given position. Returns
+/// MOVE_NONE if no book move is found.
+
+Move Book::get_move(const Position &pos) const {
+ if(this->is_open()) {
+ int bestMove = 0, bestScore = 0, move, score;
+ uint64_t key = book_key(pos);
+ BookEntry entry;
+
+ for(int i = this->find_key(key); i < bookSize; i++) {
+ this->read_entry(entry, i);
+ if(entry.key != key)
+ break;
+ move = entry.move;
+ score = entry.count;
+ assert(score > 0);
+
+ bestScore += score;
+ if(int(genrand_int32() % bestScore) < score)
+ bestMove = move;
+ }
+
+ if(bestMove != 0) {
+ MoveStack moves[256];
+ int n, j;
+ n = generate_legal_moves(pos, moves);
+ for(j = 0; j < n; j++)
+ if((int(moves[j].move) & 07777) == bestMove)
+ return moves[j].move;
+ }
+ }
+ return MOVE_NONE;
+}
+
+
+/// Book::find_key() takes a book key as input, and does a binary search
+/// through the book file for the given key. The index to the first book
+/// entry with the same key as the input is returned. When the key is not
+/// found in the book file, bookSize is returned.
+
+int Book::find_key(uint64_t key) const {
+ int left, right, mid;
+ BookEntry entry;
+
+ // Binary search (finds the leftmost entry)
+ left = 0;
+ right = bookSize - 1;
+
+ assert(left <= right);
+
+ while(left < right) {
+ mid = (left + right) / 2;
+ assert(mid >= left && mid < right);
+
+ this->read_entry(entry, mid);
+
+ if(key <= entry.key)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+
+ assert(left == right);
+
+ this->read_entry(entry, left);
+
+ return (entry.key == key)? left : bookSize;
+}
+
+
+/// Book::read_entry() takes a BookEntry reference and an integer index as
+/// input, and looks up the opening book entry at the given index in the book
+/// file. The book entry is copied to the first input parameter.
+
+void Book::read_entry(BookEntry& entry, int n) const {
+ assert(n >= 0 && n < bookSize);
+ assert(bookFile != NULL);
+
+ if(fseek(bookFile, n*16, SEEK_SET) == -1) {
+ std::cerr << "Failed to read book entry at index " << n << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ entry.key = read_integer(bookFile, 8);
+ entry.move = read_integer(bookFile, 2);
+ entry.count = read_integer(bookFile, 2);
+ entry.n = read_integer(bookFile, 2);
+ entry.sum = read_integer(bookFile, 2);
+}
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ uint64_t book_key(const Position &pos) {
+ uint64_t result = 0ULL;
+
+ for(Color c = WHITE; c <= BLACK; c++) {
+ Bitboard b = pos.pieces_of_color(c);
+ Square s;
+ Piece p;
+ while(b != EmptyBoardBB) {
+ s = pop_1st_bit(&b);
+ p = pos.piece_on(s);
+ assert(piece_is_ok(p));
+ assert(color_of_piece(p) == c);
+
+ result ^= book_piece_key(p, s);
+ }
+ }
+
+ result ^= book_castle_key(pos);
+ result ^= book_ep_key(pos);
+ result ^= book_color_key(pos);
+
+ return result;
+ }
+
+
+ uint64_t book_piece_key(Piece p, Square s) {
+ return Random64[RandomPiece + (PieceTo12[int(p)]^1)*64 + int(s)];
+ }
+
+
+ uint64_t book_castle_key(const Position &pos) {
+ uint64_t result = 0ULL;
+
+ if(pos.can_castle_kingside(WHITE))
+ result ^= Random64[RandomCastle+0];
+ if(pos.can_castle_queenside(WHITE))
+ result ^= Random64[RandomCastle+1];
+ if(pos.can_castle_kingside(BLACK))
+ result ^= Random64[RandomCastle+2];
+ if(pos.can_castle_queenside(BLACK))
+ result ^= Random64[RandomCastle+3];
+ return result;
+ }
+
+
+ uint64_t book_ep_key(const Position &pos) {
+ return (pos.ep_square() == SQ_NONE)?
+ 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())];
+ }
+
+
+ uint64_t book_color_key(const Position &pos) {
+ return (pos.side_to_move() == WHITE)? Random64[RandomTurn] : 0ULL;
+ }
+
+
+ uint64_t read_integer(FILE *file, int size) {
+ uint64_t n = 0ULL;;
+ int i;
+ int b;
+
+ assert(file != NULL);
+ assert(size > 0 && size <= 8);
+
+ for(i = 0; i < size; i++) {
+ b = fgetc(file);
+ if(b == EOF) {
+ std::cerr << "Failed to read " << size << " bytes from book file"
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ assert(b >= 0 && b < 256);
+ n = (n << 8) | b;
+ }
+ return n;
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ The code in this file is based on the opening book code in PolyGlot
+ by Fabien Letouzey. PolyGlot is available under the GNU General
+ Public License, and can be downloaded from http://wbec-ridderkerk.nl
+*/
+
+
+#if !defined(BOOK_H_INCLUDED)
+#define BOOK_H_INCLUDED
+
+
+////
+//// Includes
+////
+
+#include <string>
+
+#include "move.h"
+#include "position.h"
+
+
+////
+//// Types
+////
+
+struct BookEntry {
+ uint64_t key;
+ uint16_t move;
+ uint16_t count;
+ uint16_t n;
+ uint16_t sum;
+};
+
+class Book {
+
+public:
+ // Constructors
+ Book();
+
+ // Open and close book files
+ void open(const std::string &fName);
+ void close();
+
+ // Testing if a book is opened
+ bool is_open() const;
+
+ // The file name of the currently active book
+ const std::string file_name() const;
+
+ // Get a book move for a given position
+ Move get_move(const Position &pos) const;
+
+private:
+ int find_key(uint64_t key) const;
+ void read_entry(BookEntry &entry, int n) const;
+
+ std::string fileName;
+ FILE *bookFile;
+ int bookSize;
+};
+
+
+////
+//// Global variables
+////
+
+extern Book OpeningBook;
+
+
+#endif // !defined(BOOK_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include "color.h"
+
+
+////
+//// Functions
+////
+
+/// color_is_ok(), for debugging:
+
+bool color_is_ok(Color c) {
+ return c == WHITE || c == BLACK;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(COLOR_H_INCLUDED)
+#define COLOR_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "misc.h"
+
+
+////
+//// Types
+////
+
+enum Color {
+ WHITE,
+ BLACK,
+ COLOR_NONE
+};
+
+
+////
+//// Inline functions
+////
+
+inline Color operator+ (Color c, int i) { return Color(int(c) + i); }
+inline void operator++ (Color &c, int i) { c = Color(int(c) + 1); }
+
+inline Color opposite_color(Color c) {
+ return Color(int(c) ^ 1);
+}
+
+
+////
+//// Prototypes
+////
+
+extern bool color_is_ok(Color c);
+
+
+#endif // !defined(COLOR_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(DEPTH_H_INCLUDED)
+#define DEPTH_H_INCLUDED
+
+////
+//// Types
+////
+
+enum Depth {
+ DEPTH_ZERO = 0,
+ DEPTH_MAX = 200 // 100 * OnePly;
+};
+
+
+////
+//// Constants
+////
+
+/// Note: If OnePly is changed, the constant HistoryMax in history.h should
+/// probably also be changed.
+
+const Depth OnePly = Depth(2);
+
+
+////
+//// Inline functions
+////
+
+inline Depth operator+ (Depth d, int i) { return Depth(int(d) + i); }
+inline Depth operator+ (Depth d1, Depth d2) { return Depth(int(d1) + int(d2)); }
+inline void operator+= (Depth &d, int i) { d = Depth(int(d) + i); }
+inline void operator+= (Depth &d1, Depth d2) { d1 += int(d2); }
+inline Depth operator- (Depth d, int i) { return Depth(int(d) - i); }
+inline Depth operator- (Depth d1, Depth d2) { return Depth(int(d1) - int(d2)); }
+inline void operator-= (Depth & d, int i) { d = Depth(int(d) - i); }
+inline Depth operator* (Depth d, int i) { return Depth(int(d) * i); }
+inline Depth operator* (int i, Depth d) { return Depth(int(d) * i); }
+inline void operator*= (Depth &d, int i) { d = Depth(int(d) * i); }
+inline Depth operator/ (Depth d, int i) { return Depth(int(d) / i); }
+inline void operator/= (Depth &d, int i) { d = Depth(int(d) / i); }
+
+
+#endif // !defined(DEPTH_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include "direction.h"
+#include "square.h"
+
+
+////
+//// Variables
+////
+
+uint8_t DirectionTable[64][64];
+uint8_t SignedDirectionTable[64][64];
+
+
+////
+//// Functions
+////
+
+void init_direction_table() {
+ SquareDelta deltas[8] = {
+ DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
+ };
+ for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
+ for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
+ DirectionTable[s1][s2] = uint8_t(DIR_NONE);
+ SignedDirectionTable[s1][s2] = uint8_t(SIGNED_DIR_NONE);
+ if(s1 == s2) continue;
+ for(SignedDirection d = SIGNED_DIR_E; d <= SIGNED_DIR_SE; d++) {
+ SquareDelta delta = deltas[d];
+ Square s3, s4;
+ for(s4 = s1 + delta, s3 = s1;
+ square_distance(s4, s3) == 1 && s4 != s2 && square_is_ok(s4);
+ s3 = s4, s4 += delta);
+ if(s4 == s2 && square_distance(s4, s3) == 1) {
+ SignedDirectionTable[s1][s2] = uint8_t(d);
+ DirectionTable[s1][s2] = uint8_t(d/2);
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(DIRECTION_H_INCLUDED)
+#define DIRECTION_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "square.h"
+#include "types.h"
+
+
+////
+//// Types
+////
+
+enum Direction {
+ DIR_E = 0, DIR_N = 1, DIR_NE = 2, DIR_NW = 3, DIR_NONE = 4
+};
+
+enum SignedDirection {
+ SIGNED_DIR_E = 0, SIGNED_DIR_W = 1,
+ SIGNED_DIR_N = 2, SIGNED_DIR_S = 3,
+ SIGNED_DIR_NE = 4, SIGNED_DIR_SW = 5,
+ SIGNED_DIR_NW = 6, SIGNED_DIR_SE = 7,
+ SIGNED_DIR_NONE = 8
+};
+
+
+////
+//// Variables
+////
+
+extern uint8_t DirectionTable[64][64];
+extern uint8_t SignedDirectionTable[64][64];
+
+
+////
+//// Inline functions
+////
+
+inline void operator++ (Direction &d, int) { d = Direction(int(d) + 1); }
+
+inline void operator++ (SignedDirection &d, int) {
+ d = SignedDirection(int(d) + 1);
+}
+
+inline Direction direction_between_squares(Square s1, Square s2) {
+ return Direction(DirectionTable[s1][s2]);
+}
+
+inline SignedDirection signed_direction_between_squares(Square s1, Square s2) {
+ return SignedDirection(SignedDirectionTable[s1][s2]);
+}
+
+
+////
+//// Prototypes
+////
+
+extern void init_direction_table();
+
+
+#endif // !defined(DIRECTION_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "bitbase.h"
+#include "endgame.h"
+
+
+////
+//// Constants and variables
+////
+
+/// Evaluation functions
+
+// Generic "mate lone king" eval:
+KXKEvaluationFunction EvaluateKXK = KXKEvaluationFunction(WHITE);
+KXKEvaluationFunction EvaluateKKX = KXKEvaluationFunction(BLACK);
+
+// KBN vs K:
+KBNKEvaluationFunction EvaluateKBNK = KBNKEvaluationFunction(WHITE);
+KBNKEvaluationFunction EvaluateKKBN = KBNKEvaluationFunction(BLACK);
+
+// KP vs K:
+KPKEvaluationFunction EvaluateKPK = KPKEvaluationFunction(WHITE);
+KPKEvaluationFunction EvaluateKKP = KPKEvaluationFunction(BLACK);
+
+// KR vs KP:
+KRKPEvaluationFunction EvaluateKRKP = KRKPEvaluationFunction(WHITE);
+KRKPEvaluationFunction EvaluateKPKR = KRKPEvaluationFunction(BLACK);
+
+// KR vs KB:
+KRKBEvaluationFunction EvaluateKRKB = KRKBEvaluationFunction(WHITE);
+KRKBEvaluationFunction EvaluateKBKR = KRKBEvaluationFunction(BLACK);
+
+// KR vs KN:
+KRKNEvaluationFunction EvaluateKRKN = KRKNEvaluationFunction(WHITE);
+KRKNEvaluationFunction EvaluateKNKR = KRKNEvaluationFunction(BLACK);
+
+// KQ vs KR:
+KQKREvaluationFunction EvaluateKQKR = KQKREvaluationFunction(WHITE);
+KQKREvaluationFunction EvaluateKRKQ = KQKREvaluationFunction(BLACK);
+
+
+/// Scaling functions
+
+// KBP vs K:
+KBPKScalingFunction ScaleKBPK = KBPKScalingFunction(WHITE);
+KBPKScalingFunction ScaleKKBP = KBPKScalingFunction(BLACK);
+
+// KQ vs KRP:
+KQKRPScalingFunction ScaleKQKRP = KQKRPScalingFunction(WHITE);
+KQKRPScalingFunction ScaleKRPKQ = KQKRPScalingFunction(BLACK);
+
+// KRP vs KR:
+KRPKRScalingFunction ScaleKRPKR = KRPKRScalingFunction(WHITE);
+KRPKRScalingFunction ScaleKRKRP = KRPKRScalingFunction(BLACK);
+
+// KRPP vs KRP:
+KRPPKRPScalingFunction ScaleKRPPKRP = KRPPKRPScalingFunction(WHITE);
+KRPPKRPScalingFunction ScaleKRPKRPP = KRPPKRPScalingFunction(BLACK);
+
+// King and pawns vs king:
+KPsKScalingFunction ScaleKPsK = KPsKScalingFunction(WHITE);
+KPsKScalingFunction ScaleKKPs = KPsKScalingFunction(BLACK);
+
+// KBP vs KB:
+KBPKBScalingFunction ScaleKBPKB = KBPKBScalingFunction(WHITE);
+KBPKBScalingFunction ScaleKBKBP = KBPKBScalingFunction(BLACK);
+
+// KBP vs KN:
+KBPKNScalingFunction ScaleKBPKN = KBPKNScalingFunction(WHITE);
+KBPKNScalingFunction ScaleKNKBP = KBPKNScalingFunction(BLACK);
+
+// KNP vs K:
+KNPKScalingFunction ScaleKNPK = KNPKScalingFunction(WHITE);
+KNPKScalingFunction ScaleKKNP = KNPKScalingFunction(BLACK);
+
+// KPKP
+KPKPScalingFunction ScaleKPKPw = KPKPScalingFunction(WHITE);
+KPKPScalingFunction ScaleKPKPb = KPKPScalingFunction(BLACK);
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ // Table used to drive the defending king towards the edge of the board
+ // in KX vs K and KQ vs KR endgames:
+ const uint8_t MateTable[64] = {
+ 100, 90, 80, 70, 70, 80, 90, 100,
+ 90, 70, 60, 50, 50, 60, 70, 90,
+ 80, 60, 40, 30, 30, 40, 60, 80,
+ 70, 50, 30, 20, 20, 30, 50, 70,
+ 70, 50, 30, 20, 20, 30, 50, 70,
+ 80, 60, 40, 30, 30, 40, 60, 80,
+ 90, 70, 60, 50, 50, 60, 70, 90,
+ 100, 90, 80, 70, 70, 80, 90, 100,
+ };
+
+ // Table used to drive the defending king towards a corner square of the
+ // right color in KBN vs K endgames:
+ const uint8_t KBNKMateTable[64] = {
+ 200, 190, 180, 170, 160, 150, 140, 130,
+ 190, 180, 170, 160, 150, 140, 130, 140,
+ 180, 170, 155, 140, 140, 125, 140, 150,
+ 170, 160, 140, 120, 110, 140, 150, 160,
+ 160, 150, 140, 110, 120, 140, 160, 170,
+ 150, 140, 125, 140, 140, 155, 170, 180,
+ 140, 130, 140, 150, 160, 170, 180, 190,
+ 130, 140, 150, 160, 170, 180, 190, 200
+ };
+
+ // The attacking side is given a descending bonus based on distance between
+ // the two kings in basic endgames:
+ const int DistanceBonus[8] = {0, 0, 100, 80, 60, 40, 20, 10};
+
+ // Bitbase for KP vs K:
+ uint8_t KPKBitbase[24576];
+
+ // Penalty for big distance between king and knight for the defending king
+ // and knight in KR vs KN endgames:
+ const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
+
+ // Various inline functions for accessing the above arrays:
+
+ inline Value mate_table(Square s) {
+ return Value(MateTable[s]);
+ }
+
+ inline Value kbnk_mate_table(Square s) {
+ return Value(KBNKMateTable[s]);
+ }
+
+ inline Value distance_bonus(int d) {
+ return Value(DistanceBonus[d]);
+ }
+
+ inline Value krkn_king_knight_distance_penalty(int d) {
+ return Value(KRKNKingKnightDistancePenalty[d]);
+ }
+
+ // Function for probing the KP vs K bitbase:
+ int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
+
+}
+
+
+////
+//// Functions
+////
+
+/// Constructors
+
+EndgameEvaluationFunction::EndgameEvaluationFunction(Color c) {
+ strongerSide = c;
+ weakerSide = opposite_color(strongerSide);
+}
+
+KXKEvaluationFunction::KXKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KBNKEvaluationFunction::KBNKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KPKEvaluationFunction::KPKEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KRKPEvaluationFunction::KRKPEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KRKBEvaluationFunction::KRKBEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KRKNEvaluationFunction::KRKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+KQKREvaluationFunction::KQKREvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
+
+
+ScalingFunction::ScalingFunction(Color c) {
+ strongerSide = c;
+ weakerSide = opposite_color(c);
+}
+
+KBPKScalingFunction::KBPKScalingFunction(Color c) : ScalingFunction(c) { }
+KQKRPScalingFunction::KQKRPScalingFunction(Color c) : ScalingFunction(c) { }
+KRPKRScalingFunction::KRPKRScalingFunction(Color c) : ScalingFunction(c) { }
+KRPPKRPScalingFunction::KRPPKRPScalingFunction(Color c) : ScalingFunction(c) { }
+KPsKScalingFunction::KPsKScalingFunction(Color c) : ScalingFunction(c) { }
+KBPKBScalingFunction::KBPKBScalingFunction(Color c) : ScalingFunction(c) { }
+KBPKNScalingFunction::KBPKNScalingFunction(Color c) : ScalingFunction(c) { }
+KNPKScalingFunction::KNPKScalingFunction(Color c) : ScalingFunction(c) { }
+KPKPScalingFunction::KPKPScalingFunction(Color c) : ScalingFunction(c) { }
+
+
+/// Mate with KX vs K. This function is used to evaluate positions with
+/// King and plenty of material vs a lone king. It simply gives the
+/// attacking side a bonus for driving the defending king towards the edge
+/// of the board, and for keeping the distance between the two kings small.
+
+Value KXKEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(weakerSide) == Value(0));
+
+ Square winnerKSq = pos.king_square(strongerSide);
+ Square loserKSq = pos.king_square(weakerSide);
+
+ Value result =
+ pos.non_pawn_material(strongerSide) +
+ pos.pawn_count(strongerSide) * PawnValueEndgame +
+ mate_table(loserKSq) +
+ distance_bonus(square_distance(winnerKSq, loserKSq));
+
+ if(pos.queen_count(strongerSide) > 0 || pos.rook_count(strongerSide) > 0 ||
+ pos.bishop_count(strongerSide) > 1)
+ // TODO: check for two equal-colored bishops!
+ result += VALUE_KNOWN_WIN;
+
+ return (strongerSide == pos.side_to_move())? result : -result;
+}
+
+
+/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
+/// defending king towards a corner square of the right color.
+
+Value KBNKEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(weakerSide) == Value(0));
+ assert(pos.non_pawn_material(strongerSide) ==
+ KnightValueMidgame + BishopValueMidgame);
+ assert(pos.bishop_count(strongerSide) == 1);
+ assert(pos.knight_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) == 0);
+
+ Square winnerKSq = pos.king_square(strongerSide);
+ Square loserKSq = pos.king_square(weakerSide);
+ Square bishopSquare = pos.bishop_list(strongerSide, 0);
+
+ if(square_color(bishopSquare) == BLACK) {
+ winnerKSq = flop_square(winnerKSq);
+ loserKSq = flop_square(loserKSq);
+ }
+
+ Value result =
+ VALUE_KNOWN_WIN + distance_bonus(square_distance(winnerKSq, loserKSq)) +
+ kbnk_mate_table(loserKSq);
+
+ return (strongerSide == pos.side_to_move())? result : -result;
+}
+
+
+/// KP vs K. This endgame is evaluated with the help of a bitbase.
+
+Value KPKEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(strongerSide) == Value(0));
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(strongerSide) == 1);
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square wksq, bksq, wpsq;
+ Color stm;
+
+ if(strongerSide == WHITE) {
+ wksq = pos.king_square(WHITE);
+ bksq = pos.king_square(BLACK);
+ wpsq = pos.pawn_list(WHITE, 0);
+ stm = pos.side_to_move();
+ }
+ else {
+ wksq = flip_square(pos.king_square(BLACK));
+ bksq = flip_square(pos.king_square(WHITE));
+ wpsq = flip_square(pos.pawn_list(BLACK, 0));
+ stm = opposite_color(pos.side_to_move());
+ }
+
+ if(square_file(wpsq) >= FILE_E) {
+ wksq = flop_square(wksq);
+ bksq = flop_square(bksq);
+ wpsq = flop_square(wpsq);
+ }
+
+ if(probe_kpk(wksq, wpsq, bksq, stm)) {
+ Value result =
+ VALUE_KNOWN_WIN + PawnValueEndgame + Value(square_rank(wpsq));
+ return (strongerSide == pos.side_to_move())? result : -result;
+ }
+
+ return VALUE_DRAW;
+}
+
+
+/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
+/// a bitbase. The function below returns drawish scores when the pawn is
+/// far advanced with support of the king, while the attacking king is far
+/// away.
+
+Value KRKPEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 0);
+ assert(pos.non_pawn_material(weakerSide) == 0);
+ assert(pos.pawn_count(weakerSide) == 1);
+
+ Square wksq, wrsq, bksq, bpsq;
+ int tempo = (pos.side_to_move() == strongerSide);
+
+ wksq = pos.king_square(strongerSide);
+ wrsq = pos.rook_list(strongerSide, 0);
+ bksq = pos.king_square(weakerSide);
+ bpsq = pos.pawn_list(weakerSide, 0);
+
+ if(strongerSide == BLACK) {
+ wksq = flip_square(wksq);
+ wrsq = flip_square(wrsq);
+ bksq = flip_square(bksq);
+ bpsq = flip_square(bpsq);
+ }
+
+ Square queeningSq = make_square(square_file(bpsq), RANK_1);
+ Value result;
+
+ // If the stronger side's king is in front of the pawn, it's a win:
+ if(wksq < bpsq && square_file(wksq) == square_file(bpsq))
+ result = RookValueEndgame - Value(square_distance(wksq, bpsq));
+
+ // If the weaker side's king is too far from the pawn and the rook,
+ // it's a win:
+ else if(square_distance(bksq, bpsq) - (tempo^1) >= 3 &&
+ square_distance(bksq, wrsq) >= 3)
+ result = RookValueEndgame - Value(square_distance(wksq, bpsq));
+
+ // If the pawn is far advanced and supported by the defending king,
+ // the position is drawish:
+ else if(square_rank(bksq) <= RANK_3 && square_distance(bksq, bpsq) == 1 &&
+ square_rank(wksq) >= RANK_4 &&
+ square_distance(wksq, bpsq) - tempo > 2)
+ result = Value(80 - square_distance(wksq, bpsq) * 8);
+
+ else
+ result = Value(200)
+ - Value(square_distance(wksq, bpsq + DELTA_S) * 8)
+ + Value(square_distance(bksq, bpsq + DELTA_S) * 8)
+ + Value(square_distance(bpsq, queeningSq) * 8);
+
+ return (strongerSide == pos.side_to_move())? result : -result;
+}
+
+
+/// KR vs KB. This is very simple, and always returns drawish scores. The
+/// score is slightly bigger when the defending king is close to the edge.
+
+Value KRKBEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 0);
+ assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
+ assert(pos.pawn_count(weakerSide) == 0);
+ assert(pos.bishop_count(weakerSide) == 1);
+
+ Value result = mate_table(pos.king_square(weakerSide));
+ return (pos.side_to_move() == strongerSide)? result : -result;
+}
+
+
+/// KR vs KN. The attacking side has slightly better winning chances than
+/// in KR vs KB, particularly if the king and the knight are far apart.
+
+Value KRKNEvaluationFunction::apply(const Position &pos) {
+
+ assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 0);
+ assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
+ assert(pos.pawn_count(weakerSide) == 0);
+ assert(pos.knight_count(weakerSide) == 1);
+
+ Square defendingKSq = pos.king_square(weakerSide);
+ Square nSq = pos.knight_list(weakerSide, 0);
+
+ Value result = Value(10) + mate_table(defendingKSq) +
+ krkn_king_knight_distance_penalty(square_distance(defendingKSq, nSq));
+
+ return (strongerSide == pos.side_to_move())? result : -result;
+}
+
+
+/// KQ vs KR. This is almost identical to KX vs K: We give the attacking
+/// king a bonus for having the kings close together, and for forcing the
+/// defending king towards the edge. If we also take care to avoid null move
+/// for the defending side in the search, this is usually sufficient to be
+/// able to win KQ vs KR.
+
+Value KQKREvaluationFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 0);
+ assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square winnerKSq = pos.king_square(strongerSide);
+ Square loserKSq = pos.king_square(weakerSide);
+
+ Value result = QueenValueEndgame - RookValueEndgame +
+ mate_table(loserKSq) + distance_bonus(square_distance(winnerKSq, loserKSq));
+
+ return (strongerSide == pos.side_to_move())? result : -result;
+}
+
+
+/// KBPKScalingFunction scales endgames where the stronger side has king,
+/// bishop and one or more pawns. It checks for draws with rook pawns and a
+/// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is
+/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
+/// will be used.
+
+ScaleFactor KBPKScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
+ assert(pos.bishop_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) >= 1);
+
+ // No assertions about the material of weakerSide, because we want draws to
+ // be detected even when the weaker side has some pawns.
+
+ Bitboard pawns = pos.pawns(strongerSide);
+ File pawnFile = square_file(pos.pawn_list(strongerSide, 0));
+
+ if((pawnFile == FILE_A || pawnFile == FILE_H) &&
+ (pawns & ~file_bb(pawnFile)) == EmptyBoardBB) {
+ // All pawns are on a single rook file.
+
+ Square bishopSq = pos.bishop_list(strongerSide, 0);
+ Square queeningSq =
+ relative_square(strongerSide, make_square(pawnFile, RANK_8));
+ Square kingSq = pos.king_square(weakerSide);
+
+ if(square_color(queeningSq) != square_color(bishopSq) &&
+ file_distance(square_file(kingSq), pawnFile) <= 1) {
+ // The bishop has the wrong color, and the defending king is on the
+ // file of the pawn(s) or the neighboring file. Find the rank of the
+ // frontmost pawn:
+
+ Rank rank;
+ if(strongerSide == WHITE) {
+ for(rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--);
+ assert(rank >= RANK_2 && rank <= RANK_7);
+ }
+ else {
+ for(rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++);
+ rank = Rank(rank^7); // HACK
+ assert(rank >= RANK_2 && rank <= RANK_7);
+ }
+ // If the defending king has distance 1 to the promotion square or
+ // is placed somewhere in front of the pawn, it's a draw.
+ if(square_distance(kingSq, queeningSq) <= 1 ||
+ pawn_rank(strongerSide, kingSq) >= rank)
+ return ScaleFactor(0);
+ }
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KQKRPScalingFunction scales endgames where the stronger side has only
+/// king and queen, while the weaker side has at least a rook and a pawn.
+/// It tests for fortress draws with a rook on the third rank defended by
+/// a pawn.
+
+ScaleFactor KQKRPScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
+ assert(pos.queen_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) == 0);
+ assert(pos.rook_count(weakerSide) == 1);
+ assert(pos.pawn_count(weakerSide) >= 1);
+
+ Square kingSq = pos.king_square(weakerSide);
+ if(pawn_rank(weakerSide, kingSq) <= RANK_2 &&
+ pawn_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 &&
+ (pos.rooks(weakerSide) & relative_rank_bb(weakerSide, RANK_3)) &&
+ (pos.pawns(weakerSide) & relative_rank_bb(weakerSide, RANK_2)) &&
+ (pos.king_attacks(kingSq) & pos.pawns(weakerSide))) {
+ Square rsq = pos.rook_list(weakerSide, 0);
+ if(pos.pawn_attacks(strongerSide, rsq) & pos.pawns(weakerSide))
+ return ScaleFactor(0);
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a
+/// handful of the most important classes of drawn positions, but is far
+/// from perfect. It would probably be a good idea to add more knowledge
+/// in the future.
+///
+/// It would also be nice to rewrite the actual code for this function,
+/// which is mostly copied from Glaurung 1.x, and not very pretty.
+
+ScaleFactor KRPKRScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 1);
+ assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square wksq = pos.king_square(strongerSide);
+ Square wrsq = pos.rook_list(strongerSide, 0);
+ Square wpsq = pos.pawn_list(strongerSide, 0);
+ Square bksq = pos.king_square(weakerSide);
+ Square brsq = pos.rook_list(weakerSide, 0);
+
+ // Orient the board in such a way that the stronger side is white, and the
+ // pawn is on the left half of the board:
+ if(strongerSide == BLACK) {
+ wksq = flip_square(wksq);
+ wrsq = flip_square(wrsq);
+ wpsq = flip_square(wpsq);
+ bksq = flip_square(bksq);
+ brsq = flip_square(brsq);
+ }
+ if(square_file(wpsq) > FILE_D) {
+ wksq = flop_square(wksq);
+ wrsq = flop_square(wrsq);
+ wpsq = flop_square(wpsq);
+ bksq = flop_square(bksq);
+ brsq = flop_square(brsq);
+ }
+
+ File f = square_file(wpsq);
+ Rank r = square_rank(wpsq);
+ Square queeningSq = make_square(f, RANK_8);
+ int tempo = (pos.side_to_move() == strongerSide);
+
+ // If the pawn is not too far advanced and the defending king defends the
+ // queening square, use the third-rank defence:
+ if(r <= RANK_5 && square_distance(bksq, queeningSq) <= 1 && wksq <= SQ_H5 &&
+ (square_rank(brsq) == RANK_6 || (r <= RANK_3 &&
+ square_rank(wrsq) != RANK_6)))
+ return ScaleFactor(0);
+
+ // The defending side saves a draw by checking from behind in case the pawn
+ // has advanced to the 6th rank with the king behind.
+ if(r == RANK_6 && square_distance(bksq, queeningSq) <= 1 &&
+ square_rank(wksq) + tempo <= RANK_6 &&
+ (square_rank(brsq) == RANK_1 ||
+ (!tempo && abs(square_file(brsq) - f) >= 3)))
+ return ScaleFactor(0);
+
+ if(r >= RANK_6 && bksq == queeningSq && square_rank(brsq) == RANK_1 &&
+ (!tempo || square_distance(wksq, wpsq) >= 2))
+ return ScaleFactor(0);
+
+ // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
+ // and the black rook is behind the pawn.
+ if(wpsq == SQ_A7 && wrsq == SQ_A8 && (bksq == SQ_H7 || bksq == SQ_G7) &&
+ square_file(brsq) == FILE_A &&
+ (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D ||
+ square_rank(wksq) <= RANK_5))
+ return ScaleFactor(0);
+
+ // If the defending king blocks the pawn and the attacking king is too far
+ // away, it's a draw.
+ if(r <= RANK_5 && bksq == wpsq + DELTA_N &&
+ square_distance(wksq, wpsq) - tempo >= 2 &&
+ square_distance(wksq, brsq) - tempo >= 2)
+ return ScaleFactor(0);
+
+ // Pawn on the 7th rank supported by the rook from behind usually wins if the
+ // attacking king is closer to the queening square than the defending king,
+ // and the defending king cannot gain tempi by threatening the attacking
+ // rook.
+ if(r == RANK_7 && f != FILE_A && square_file(wrsq) == f
+ && wrsq != queeningSq
+ && (square_distance(wksq, queeningSq) <
+ square_distance(bksq, queeningSq) - 2 + tempo)
+ && (square_distance(wksq, queeningSq) <
+ square_distance(bksq, wrsq) + tempo))
+ return ScaleFactor(SCALE_FACTOR_MAX
+ - 2 * square_distance(wksq, queeningSq));
+
+ // Similar to the above, but with the pawn further back:
+ if(f != FILE_A && square_file(wrsq) == f && wrsq < wpsq
+ && (square_distance(wksq, queeningSq) <
+ square_distance(bksq, queeningSq) - 2 + tempo)
+ && (square_distance(wksq, wpsq + DELTA_N) <
+ square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
+ && (square_distance(bksq, wrsq) + tempo >= 3
+ || (square_distance(wksq, queeningSq) <
+ square_distance(bksq, wrsq) + tempo
+ && (square_distance(wksq, wpsq + DELTA_N) <
+ square_distance(bksq, wrsq) + tempo))))
+ return
+ ScaleFactor(SCALE_FACTOR_MAX
+ - (8 * square_distance(wpsq, queeningSq) +
+ 2 * square_distance(wksq, queeningSq)));
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KRPPKRPScalingFunction scales KRPP vs KRP endgames. There is only a
+/// single pattern: If the stronger side has no pawns and the defending king
+/// is actively placed, the position is drawish.
+
+ScaleFactor KRPPKRPScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
+ assert(pos.pawn_count(strongerSide) == 2);
+ assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
+ assert(pos.pawn_count(weakerSide) == 1);
+
+ Square wpsq1 = pos.pawn_list(strongerSide, 0);
+ Square wpsq2 = pos.pawn_list(strongerSide, 1);
+ Square bksq = pos.king_square(weakerSide);
+
+ // Does the stronger side have a passed pawn?
+ if(pos.pawn_is_passed(strongerSide, wpsq1) ||
+ pos.pawn_is_passed(strongerSide, wpsq2))
+ return SCALE_FACTOR_NONE;
+
+ Rank r = Max(pawn_rank(strongerSide, wpsq1), pawn_rank(strongerSide, wpsq2));
+
+ if(file_distance(bksq, wpsq1) <= 1 && file_distance(bksq, wpsq2) <= 1
+ && pawn_rank(strongerSide, bksq) > r) {
+ switch(r) {
+
+ case RANK_2: return ScaleFactor(10);
+ case RANK_3: return ScaleFactor(10);
+ case RANK_4: return ScaleFactor(15);
+ case RANK_5: return ScaleFactor(20);
+ case RANK_6: return ScaleFactor(40);
+ default: assert(false);
+
+ }
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KPsKScalingFunction scales endgames with king and two or more pawns
+/// against king. There is just a single rule here: If all pawns are on
+/// the same rook file and are blocked by the defending king, it's a draw.
+
+ScaleFactor KPsKScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == Value(0));
+ assert(pos.pawn_count(strongerSide) >= 2);
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Bitboard pawns = pos.pawns(strongerSide);
+
+ // Are all pawns on the 'a' file?
+ if((pawns & ~FileABB) == EmptyBoardBB) {
+ // Does the defending king block the pawns?
+ Square ksq = pos.king_square(weakerSide);
+ if(square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1)
+ return ScaleFactor(0);
+ else if(square_file(ksq) == FILE_A &&
+ (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
+ return ScaleFactor(0);
+ else
+ return SCALE_FACTOR_NONE;
+ }
+ // Are all pawns on the 'h' file?
+ else if((pawns & ~FileHBB) == EmptyBoardBB) {
+ // Does the defending king block the pawns?
+ Square ksq = pos.king_square(weakerSide);
+ if(square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1)
+ return ScaleFactor(0);
+ else if(square_file(ksq) == FILE_H &&
+ (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
+ return ScaleFactor(0);
+ else
+ return SCALE_FACTOR_NONE;
+ }
+ else
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules:
+/// If the defending king is somewhere along the path of the pawn, and the
+/// square of the king is not of the same color as the stronger side's bishop,
+/// it's a draw. If the two bishops have opposite color, it's almost always
+/// a draw.
+
+ScaleFactor KBPKBScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
+ assert(pos.bishop_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) == 1);
+ assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
+ assert(pos.bishop_count(weakerSide) == 1);
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square pawnSq = pos.pawn_list(strongerSide, 0);
+ Square strongerBishopSq = pos.bishop_list(strongerSide, 0);
+ Square weakerBishopSq = pos.bishop_list(weakerSide, 0);
+ Square weakerKingSq = pos.king_square(weakerSide);
+
+ // Case 1: Defending king blocks the pawn, and cannot be driven away.
+ if(square_file(weakerKingSq) == square_file(pawnSq)
+ && pawn_rank(strongerSide, pawnSq) < pawn_rank(strongerSide, weakerKingSq)
+ && (square_color(weakerKingSq) != square_color(strongerBishopSq)
+ || pawn_rank(strongerSide, weakerKingSq) <= RANK_6))
+ return ScaleFactor(0);
+
+ // Case 2: Opposite colored bishops.
+ if(square_color(strongerBishopSq) != square_color(weakerBishopSq)) {
+
+ // We assume that the position is drawn in the following three situations:
+ //
+ // a. The pawn is on rank 5 or further back.
+ // b. The defending king is somewhere in the pawn's path.
+ // c. The defending bishop attacks some square along the pawn's path,
+ // and is at least three squares away from the pawn.
+ //
+ // These rules are probably not perfect, but in practice they work
+ // reasonably well.
+
+ if(pawn_rank(strongerSide, pawnSq) <= RANK_5)
+ return ScaleFactor(0);
+ else {
+ Bitboard ray =
+ ray_bb(pawnSq, (strongerSide == WHITE)? SIGNED_DIR_N : SIGNED_DIR_S);
+ if(ray & pos.kings(weakerSide))
+ return ScaleFactor(0);
+ if((pos.bishop_attacks(weakerBishopSq) & ray)
+ && square_distance(weakerBishopSq, pawnSq) >= 3)
+ return ScaleFactor(0);
+ }
+ }
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KBPKNScalingFunction scales KBP vs KN endgames. There is a single rule:
+/// If the defending king is somewhere along the path of the pawn, and the
+/// square of the king is not of the same color as the stronger side's bishop,
+/// it's a draw.
+
+ScaleFactor KBPKNScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
+ assert(pos.bishop_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) == 1);
+ assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
+ assert(pos.knight_count(weakerSide) == 1);
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square pawnSq = pos.pawn_list(strongerSide, 0);
+ Square strongerBishopSq = pos.bishop_list(strongerSide, 0);
+ Square weakerKingSq = pos.king_square(weakerSide);
+
+ if(square_file(weakerKingSq) == square_file(pawnSq)
+ && pawn_rank(strongerSide, pawnSq) < pawn_rank(strongerSide, weakerKingSq)
+ && (square_color(weakerKingSq) != square_color(strongerBishopSq)
+ || pawn_rank(strongerSide, weakerKingSq) <= RANK_6))
+ return ScaleFactor(0);
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule:
+/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
+/// the pawn from advancing, the position is drawn.
+
+ScaleFactor KNPKScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
+ assert(pos.knight_count(strongerSide) == 1);
+ assert(pos.pawn_count(strongerSide) == 1);
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(weakerSide) == 0);
+
+ Square pawnSq = pos.pawn_list(strongerSide, 0);
+ Square weakerKingSq = pos.king_square(weakerSide);
+
+ if(pawnSq == relative_square(strongerSide, SQ_A7) &&
+ square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
+ return ScaleFactor(0);
+
+ if(pawnSq == relative_square(strongerSide, SQ_H7) &&
+ square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
+ return ScaleFactor(0);
+
+ return SCALE_FACTOR_NONE;
+}
+
+
+/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing
+/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest
+/// side has a draw without the pawn, she probably has at least a draw with
+/// the pawn as well. The exception is when the stronger side's pawn is far
+/// advanced and not on a rook file; in this case it is often possible to win
+/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
+
+ScaleFactor KPKPScalingFunction::apply(const Position &pos) {
+ assert(pos.non_pawn_material(strongerSide) == Value(0));
+ assert(pos.non_pawn_material(weakerSide) == Value(0));
+ assert(pos.pawn_count(WHITE) == 1);
+ assert(pos.pawn_count(BLACK) == 1);
+
+ Square wksq, bksq, wpsq;
+ Color stm;
+
+ if(strongerSide == WHITE) {
+ wksq = pos.king_square(WHITE);
+ bksq = pos.king_square(BLACK);
+ wpsq = pos.pawn_list(WHITE, 0);
+ stm = pos.side_to_move();
+ }
+ else {
+ wksq = flip_square(pos.king_square(BLACK));
+ bksq = flip_square(pos.king_square(WHITE));
+ wpsq = flip_square(pos.pawn_list(BLACK, 0));
+ stm = opposite_color(pos.side_to_move());
+ }
+
+ if(square_file(wpsq) >= FILE_E) {
+ wksq = flop_square(wksq);
+ bksq = flop_square(bksq);
+ wpsq = flop_square(wpsq);
+ }
+
+ // If the pawn has advanced to the fifth rank or further, and is not a
+ // rook pawn, it's too dangerous to assume that it's at least a draw.
+ if(square_rank(wpsq) >= RANK_5 && square_file(wpsq) != FILE_A)
+ return SCALE_FACTOR_NONE;
+
+ // Probe the KPK bitbase with the weakest side's pawn removed. If it's a
+ // draw, it's probably at least a draw even with the pawn.
+ if(probe_kpk(wksq, wpsq, bksq, stm))
+ return SCALE_FACTOR_NONE;
+ else
+ return ScaleFactor(0);
+}
+
+
+/// init_bitbases() is called during program initialization, and simply loads
+/// bitbases from disk into memory. At the moment, there is only the bitbase
+/// for KP vs K, but we may decide to add other bitbases later.
+
+void init_bitbases() {
+ generate_kpk_bitbase(KPKBitbase);
+}
+
+
+namespace {
+
+ // Probe the KP vs K bitbase:
+
+ int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
+ int wp = int(square_file(wpsq)) + (int(square_rank(wpsq)) - 1) * 4;
+ int index = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*wp;
+
+ assert(index >= 0 && index < 24576*8);
+ return KPKBitbase[index/8] & (1 << (index&7));
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(ENDGAME_H_INCLUDED)
+#define ENDGAME_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "position.h"
+#include "scale.h"
+#include "value.h"
+
+
+////
+//// Types
+////
+
+/// Abstract base class for all special endgame evaluation functions:
+
+class EndgameEvaluationFunction {
+public:
+ EndgameEvaluationFunction(Color c);
+ virtual ~EndgameEvaluationFunction() { }
+
+ virtual Value apply(const Position &pos) =0;
+
+protected:
+ Color strongerSide, weakerSide;
+};
+
+
+/// Subclasses for various concrete endgames:
+
+// Generic "mate lone king" eval:
+class KXKEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KXKEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KBN vs K:
+class KBNKEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KBNKEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KP vs K:
+class KPKEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KPKEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KR vs KP:
+class KRKPEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KRKPEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KR vs KB:
+class KRKBEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KRKBEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KR vs KN:
+class KRKNEvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KRKNEvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+// KQ vs KR:
+class KQKREvaluationFunction : public EndgameEvaluationFunction {
+public:
+ KQKREvaluationFunction(Color c);
+ Value apply(const Position &pos);
+};
+
+
+/// Abstract base class for all evaluation scaling functions:
+
+class ScalingFunction {
+public:
+ ScalingFunction(Color c);
+ virtual ~ScalingFunction() { }
+
+ virtual ScaleFactor apply(const Position &pos) =0;
+
+protected:
+ Color strongerSide, weakerSide;
+};
+
+
+/// Subclasses for various concrete endgames:
+
+// KBP vs K:
+class KBPKScalingFunction : public ScalingFunction {
+public:
+ KBPKScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KQ vs KRP:
+class KQKRPScalingFunction: public ScalingFunction {
+public:
+ KQKRPScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KRP vs KR:
+class KRPKRScalingFunction : public ScalingFunction {
+public:
+ KRPKRScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KRPP vs KRP:
+class KRPPKRPScalingFunction : public ScalingFunction {
+public:
+ KRPPKRPScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// King and pawns vs king:
+class KPsKScalingFunction : public ScalingFunction {
+public:
+ KPsKScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KBP vs KB:
+class KBPKBScalingFunction : public ScalingFunction {
+public:
+ KBPKBScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KBP vs KN:
+class KBPKNScalingFunction : public ScalingFunction {
+public:
+ KBPKNScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KNP vs K:
+class KNPKScalingFunction : public ScalingFunction {
+public:
+ KNPKScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+// KP vs KP:
+class KPKPScalingFunction : public ScalingFunction {
+public:
+ KPKPScalingFunction(Color c);
+ ScaleFactor apply(const Position &pos);
+};
+
+
+////
+//// Constants and variables
+////
+
+// Generic "mate lone king" eval:
+extern KXKEvaluationFunction EvaluateKXK, EvaluateKKX;
+
+// KBN vs K:
+extern KBNKEvaluationFunction EvaluateKBNK, EvaluateKKBN;
+
+// KP vs K:
+extern KPKEvaluationFunction EvaluateKPK, EvaluateKKP;
+
+// KR vs KP:
+extern KRKPEvaluationFunction EvaluateKRKP, EvaluateKPKR;
+
+// KR vs KB:
+extern KRKBEvaluationFunction EvaluateKRKB, EvaluateKBKR;
+
+// KR vs KN:
+extern KRKNEvaluationFunction EvaluateKRKN, EvaluateKNKR;
+
+// KQ vs KR:
+extern KQKREvaluationFunction EvaluateKQKR, EvaluateKRKQ;
+
+// KBP vs K:
+extern KBPKScalingFunction ScaleKBPK, ScaleKKBP;
+
+// KQ vs KRP:
+extern KQKRPScalingFunction ScaleKQKRP, ScaleKRPKQ;
+
+// KRP vs KR:
+extern KRPKRScalingFunction ScaleKRPKR, ScaleKRKRP;
+
+// KRPP vs KRP:
+extern KRPPKRPScalingFunction ScaleKRPPKRP, ScaleKRPKRPP;
+
+// King and pawns vs king:
+extern KPsKScalingFunction ScaleKPsK, ScaleKKPs;
+
+// KBP vs KB:
+extern KBPKBScalingFunction ScaleKBPKB, ScaleKBKBP;
+
+// KBP vs KN:
+extern KBPKNScalingFunction ScaleKBPKN, ScaleKNKBP;
+
+// KNP vs K:
+extern KNPKScalingFunction ScaleKNPK, ScaleKKNP;
+
+// KP vs KP:
+extern KPKPScalingFunction ScaleKPKPw, ScaleKPKPb;
+
+
+////
+//// Prototypes
+////
+
+extern void init_bitbases();
+
+
+#endif // !defined(ENDGAME_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstring>
+
+#include "evaluate.h"
+#include "material.h"
+#include "pawns.h"
+#include "scale.h"
+#include "thread.h"
+#include "ucioption.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ const int Sign[2] = {1, -1};
+
+ // Evaluation grain size, must be a power of 2.
+ const int GrainSize = 4;
+
+ // Evaluation weights
+ int WeightMobilityMidgame = 0x100;
+ int WeightMobilityEndgame = 0x100;
+ int WeightPawnStructureMidgame = 0x100;
+ int WeightPawnStructureEndgame = 0x100;
+ int WeightPassedPawnsMidgame = 0x100;
+ int WeightPassedPawnsEndgame = 0x100;
+ int WeightKingSafety[2] = { 0x100, 0x100 };
+
+ // Internal evaluation weights. These are applied on top of the evaluation
+ // weights read from UCI parameters. The purpose is to be able to change
+ // the evaluation weights while keeping the default values of the UCI
+ // parameters at 100, which looks prettier.
+ const int WeightMobilityMidgameInternal = 0x100;
+ const int WeightMobilityEndgameInternal = 0x100;
+ const int WeightPawnStructureMidgameInternal = 0x100;
+ const int WeightPawnStructureEndgameInternal = 0x100;
+ const int WeightPassedPawnsMidgameInternal = 0x100;
+ const int WeightPassedPawnsEndgameInternal = 0x100;
+ const int WeightKingSafetyInternal = 0x100;
+
+ // Knight mobility bonus in middle game and endgame, indexed by the number
+ // of attacked squares not occupied by friendly piecess.
+ const Value MidgameKnightMobilityBonus[] = {
+ Value(-30), Value(-20), Value(-10), Value(0), Value(10),
+ Value(20), Value(25), Value(30), Value(30)
+ };
+
+ const Value EndgameKnightMobilityBonus[] = {
+ Value(-30), Value(-20), Value(-10), Value(0), Value(10),
+ Value(20), Value(25), Value(30), Value(30)
+ };
+
+ // Bishop mobility bonus in middle game and endgame, indexed by the number
+ // of attacked squares not occupied by friendly pieces. X-ray attacks through
+ // queens are also included.
+ const Value MidgameBishopMobilityBonus[] = {
+ Value(-30), Value(-15), Value(0), Value(15), Value(30), Value(45),
+ Value(58), Value(66), Value(72), Value(76), Value(78), Value(80),
+ Value(81), Value(82), Value(83), Value(83)
+ };
+
+ const Value EndgameBishopMobilityBonus[] = {
+ Value(-30), Value(-15), Value(0), Value(15), Value(30), Value(45),
+ Value(58), Value(66), Value(72), Value(76), Value(78), Value(80),
+ Value(81), Value(82), Value(83), Value(83)
+ };
+
+ // Rook mobility bonus in middle game and endgame, indexed by the number
+ // of attacked squares not occupied by friendly pieces. X-ray attacks through
+ // queens and rooks are also included.
+ const Value MidgameRookMobilityBonus[] = {
+ Value(-18), Value(-12), Value(-6), Value(0), Value(6), Value(12),
+ Value(16), Value(21), Value(24), Value(27), Value(28), Value(29),
+ Value(30), Value(31), Value(32), Value(33)
+ };
+
+ const Value EndgameRookMobilityBonus[] = {
+ Value(-30), Value(-18), Value(-6), Value(6), Value(18), Value(30),
+ Value(42), Value(54), Value(66), Value(74), Value(78), Value(80),
+ Value(81), Value(82), Value(83), Value(83)
+ };
+
+ // Queen mobility bonus in middle game and endgame, indexed by the number
+ // of attacked squares not occupied by friendly pieces.
+ const Value MidgameQueenMobilityBonus[] = {
+ Value(-10), Value(-8), Value(-6), Value(-4), Value(-2), Value(0), Value(2),
+ Value(4), Value(6), Value(8), Value(10), Value(12), Value(13), Value(14),
+ Value(15), Value(16), Value(16), Value(16), Value(16), Value(16),
+ Value(16), Value(16), Value(16), Value(16), Value(16), Value(16),
+ Value(16), Value(16), Value(16), Value(16), Value(16), Value(16)
+ };
+
+ const Value EndgameQueenMobilityBonus[] = {
+ Value(-20), Value(-15), Value(-10), Value(-5), Value(0), Value(5),
+ Value(10), Value(15), Value(19), Value(23), Value(27), Value(29),
+ Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
+ Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
+ Value(30), Value(30), Value(30), Value(30), Value(30), Value(30),
+ Value(30), Value(30)
+ };
+
+
+ // Outpost bonuses for knights and bishops, indexed by square (from white's
+ // point of view).
+ const Value KnightOutpostBonus[64] = {
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(5),Value(10),Value(10),Value(5),Value(0),Value(0),
+ Value(0),Value(5),Value(20),Value(30),Value(30),Value(20),Value(5),Value(0),
+ Value(0),Value(10),Value(30),Value(40),Value(40),Value(30),Value(10),Value(0),
+ Value(0),Value(5),Value(20),Value(20),Value(20),Value(20),Value(5),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0)
+ };
+
+ const Value BishopOutpostBonus[64] = {
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(5),Value(5),Value(5),Value(5),Value(0),Value(0),
+ Value(0),Value(5),Value(10),Value(10),Value(10),Value(10),Value(5),Value(0),
+ Value(0),Value(10),Value(20),Value(20),Value(20),Value(20),Value(10),Value(0),
+ Value(0),Value(5),Value(8),Value(8),Value(8),Value(8),Value(5),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),
+ Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0),Value(0)
+ };
+
+ // Bonus for unstoppable passed pawns:
+ const Value UnstoppablePawnValue = Value(0x500);
+
+ // Rooks and queens on the 7th rank:
+ const Value MidgameRookOn7thBonus = Value(50);
+ const Value EndgameRookOn7thBonus = Value(100);
+ const Value MidgameQueenOn7thBonus = Value(25);
+ const Value EndgameQueenOn7thBonus = Value(50);
+
+ // Rooks on open files:
+ const Value RookOpenFileBonus = Value(40);
+ const Value RookHalfOpenFileBonus = Value(20);
+
+ // Penalty for rooks trapped inside a friendly king which has lost the
+ // right to castle:
+ const Value TrappedRookPenalty = Value(180);
+
+ // Penalty for a bishop on a7/h7 (a2/h2 for black) which is trapped by
+ // enemy pawns:
+ const Value TrappedBishopA7H7Penalty = Value(300);
+
+ // Bitboard masks for detecting trapped bishops on a7/h7 (a2/h2 for black):
+ const Bitboard MaskA7H7[2] = {
+ ((1ULL << SQ_A7) | (1ULL << SQ_H7)),
+ ((1ULL << SQ_A2) | (1ULL << SQ_H2))
+ };
+
+ // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
+ // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
+ // happen in Chess960 games.
+ const Value TrappedBishopA1H1Penalty = Value(100);
+
+ // Bitboard masks for detecting trapped bishops on a1/h1 (a8/h8 for black):
+ const Bitboard MaskA1H1[2] = {
+ ((1ULL << SQ_A1) | (1ULL << SQ_H1)),
+ ((1ULL << SQ_A8) | (1ULL << SQ_H8))
+ };
+
+
+ /// King safety constants and variables. The king safety scores are taken
+ /// from the array SafetyTable[]. Various little "meta-bonuses" measuring
+ /// the strength of the attack are added up into an integer, which is used
+ /// as an index to SafetyTable[].
+
+ // Attack weights for each piece type.
+ const int QueenAttackWeight = 5;
+ const int RookAttackWeight = 3;
+ const int BishopAttackWeight = 2;
+ const int KnightAttackWeight = 2;
+
+ // Bonuses for safe checks for each piece type.
+ int QueenContactCheckBonus = 4;
+ int RookContactCheckBonus = 2;
+ int QueenCheckBonus = 2;
+ int RookCheckBonus = 1;
+ int BishopCheckBonus = 1;
+ int KnightCheckBonus = 1;
+ int DiscoveredCheckBonus = 3;
+
+ // Scan for queen contact mates?
+ const bool QueenContactMates = true;
+
+ // Bonus for having a mate threat.
+ int MateThreatBonus = 3;
+
+ // InitKingDanger[] contains bonuses based on the position of the defending
+ // king.
+ const int InitKingDanger[64] = {
+ 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,
+ };
+
+ // SafetyTable[] contains the actual king safety scores. It is initialized
+ // in init_safety().
+ Value SafetyTable[100];
+
+
+ // Pawn and material hash tables, indexed by the current thread id:
+ PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ // Sizes of pawn and material hash tables:
+ const int PawnTableSize = 16384;
+ const int MaterialTableSize = 1024;
+
+ // Array which gives the number of nonzero bits in an 8-bit integer:
+ uint8_t BitCount8Bit[256];
+
+ // Function prototypes:
+ void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei);
+ void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei);
+ void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei);
+ void evaluate_queen(const Position &p, Square s, Color us, EvalInfo &ei);
+ void evaluate_king(const Position &p, Square s, Color us, EvalInfo &ei);
+
+ void evaluate_passed_pawns(const Position &pos, EvalInfo &ei);
+ void evaluate_trapped_bishop_a7h7(const Position &pos, Square s, Color us,
+ EvalInfo &ei);
+ void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
+ EvalInfo &ei);
+
+ Value apply_weight(Value v, int w);
+ Value scale_by_game_phase(Value mv, Value ev, Phase ph, ScaleFactor sf[]);
+
+ int count_1s_8bit(int b);
+
+ int compute_weight(int uciWeight, int internalWeight);
+ void init_safety();
+
+}
+
+
+////
+//// Functions
+////
+
+/// 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, EvalInfo &ei, int threadID) {
+ Color stm;
+ Square s;
+ ScaleFactor factor[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL};
+ Phase phase;
+
+ memset(&ei, 0, sizeof(EvalInfo));
+
+ assert(pos.is_ok());
+ assert(threadID >= 0 && threadID < THREAD_MAX);
+
+ stm = pos.side_to_move();
+
+ // Initialize by reading the incrementally updated scores included in the
+ // position object (material + piece square tables):
+ ei.mgValue = pos.mg_value();
+ ei.egValue = pos.eg_value();
+
+ // Probe the material hash table:
+ ei.mi = MaterialTable[threadID]->get_material_info(pos);
+ ei.mgValue += ei.mi->mg_value();
+ ei.egValue += ei.mi->eg_value();
+
+ factor[WHITE] = ei.mi->scale_factor(pos, WHITE);
+ factor[BLACK] = ei.mi->scale_factor(pos, BLACK);
+
+ // If we have a specialized evaluation function for the current material
+ // configuration, call it and return:
+ if(ei.mi->specialized_eval_exists())
+ return ei.mi->evaluate(pos);
+
+ phase = pos.game_phase();
+
+ // Probe the pawn hash table:
+ ei.pi = PawnTable[threadID]->get_pawn_info(pos);
+ ei.mgValue += apply_weight(ei.pi->mg_value(), WeightPawnStructureMidgame);
+ ei.egValue += apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame);
+
+ // Initialize king attack bitboards and king attack zones for both sides:
+ ei.attackedBy[WHITE][KING] = pos.king_attacks(pos.king_square(WHITE));
+ ei.attackedBy[BLACK][KING] = pos.king_attacks(pos.king_square(BLACK));
+ ei.attackZone[WHITE] =
+ ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8);
+ ei.attackZone[BLACK] =
+ ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8);
+
+ // Initialize pawn attack bitboards for both sides:
+ ei.attackedBy[WHITE][PAWN] =
+ ((pos.pawns(WHITE) << 9) & ~FileABB) | ((pos.pawns(WHITE) << 7) & ~FileHBB);
+ ei.attackCount[WHITE] +=
+ count_1s_max_15(ei.attackedBy[WHITE][PAWN] & ei.attackedBy[BLACK][KING])/2;
+ ei.attackedBy[BLACK][PAWN] =
+ ((pos.pawns(BLACK) >> 7) & ~FileABB) | ((pos.pawns(BLACK) >> 9) & ~FileHBB);
+ ei.attackCount[BLACK] +=
+ count_1s_max_15(ei.attackedBy[BLACK][PAWN] & ei.attackedBy[WHITE][KING])/2;
+
+ // Evaluate pieces:
+ for(Color c = WHITE; c <= BLACK; c++) {
+ Bitboard b;
+
+ // Knights
+ for(int i = 0; i < pos.knight_count(c); i++) {
+ s = pos.knight_list(c, i);
+ evaluate_knight(pos, s, c, ei);
+ }
+
+ // Bishops
+ for(int i = 0; i < pos.bishop_count(c); i++) {
+ s = pos.bishop_list(c, i);
+ evaluate_bishop(pos, s, c, ei);
+ }
+
+ // Rooks
+ for(int i = 0; i < pos.rook_count(c); i++) {
+ s = pos.rook_list(c, i);
+ evaluate_rook(pos, s, c, ei);
+ }
+
+ // Queens
+ for(int i = 0; i < pos.queen_count(c); i++) {
+ s = pos.queen_list(c, i);
+ evaluate_queen(pos, s, c, ei);
+ }
+
+ // Some special patterns:
+
+ // Trapped bishops on a7/h7/a2/h2
+ b = pos.bishops(c) & MaskA7H7[c];
+ while(b) {
+ s = pop_1st_bit(&b);
+ evaluate_trapped_bishop_a7h7(pos, s, c, ei);
+ }
+
+ // Trapped bishops on a1/h1/a8/h8 in Chess960:
+ if(Chess960) {
+ b = pos.bishops(c) & MaskA1H1[c];
+ while(b) {
+ s = pop_1st_bit(&b);
+ evaluate_trapped_bishop_a1h1(pos, s, c, ei);
+ }
+ }
+
+ ei.attackedBy[c][0] =
+ ei.attackedBy[c][PAWN] | ei.attackedBy[c][KNIGHT]
+ | ei.attackedBy[c][BISHOP] | ei.attackedBy[c][ROOK]
+ | ei.attackedBy[c][QUEEN] | ei.attackedBy[c][KING];
+ }
+
+ // Kings. Kings are evaluated after all other pieces for both sides,
+ // because we need complete attack information for all pieces when computing
+ // the king safety evaluation.
+ for(Color c = WHITE; c <= BLACK; c++) {
+ s = pos.king_square(c);
+ evaluate_king(pos, s, c, ei);
+ }
+
+ // Evaluate passed pawns. We evaluate passed pawns for both sides at once,
+ // because we need to know which side promotes first in positions where
+ // both sides have an unstoppable passed pawn.
+ if(ei.pi->passed_pawns())
+ evaluate_passed_pawns(pos, ei);
+
+ // Middle-game specific evaluation terms
+ if(phase > PHASE_ENDGAME) {
+
+ // Pawn storms in positions with opposite castling.
+ if(square_file(pos.king_square(WHITE)) >= FILE_E &&
+ square_file(pos.king_square(BLACK)) <= FILE_D)
+ ei.mgValue +=
+ ei.pi->queenside_storm_value(WHITE) -
+ ei.pi->kingside_storm_value(BLACK);
+ else if(square_file(pos.king_square(WHITE)) <= FILE_D &&
+ square_file(pos.king_square(BLACK)) >= FILE_E)
+ ei.mgValue +=
+ ei.pi->kingside_storm_value(WHITE) -
+ ei.pi->queenside_storm_value(BLACK);
+ }
+
+ // Mobility
+ ei.mgValue += apply_weight(ei.mgMobility, WeightMobilityMidgame);
+ ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
+
+ // If we don't already have an unusual scale factor, check for opposite
+ // colored bishop endgames, and use a lower scale for those:
+ if(phase < PHASE_MIDGAME && pos.opposite_colored_bishops()
+ && ((factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0)) ||
+ (factor[BLACK] == SCALE_FACTOR_NORMAL && ei.egValue < Value(0)))) {
+ if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) ==
+ 2*BishopValueMidgame) {
+ // Only the two bishops
+ if(pos.pawn_count(WHITE) + pos.pawn_count(BLACK) == 1) {
+ // KBP vs KB with only a single pawn; almost certainly a draw.
+ if(factor[WHITE] == SCALE_FACTOR_NORMAL)
+ factor[WHITE] = ScaleFactor(8);
+ if(factor[BLACK] == SCALE_FACTOR_NORMAL)
+ factor[BLACK] = ScaleFactor(8);
+ }
+ else {
+ // At least two pawns
+ if(factor[WHITE] == SCALE_FACTOR_NORMAL)
+ factor[WHITE] = ScaleFactor(32);
+ if(factor[BLACK] == SCALE_FACTOR_NORMAL)
+ factor[BLACK] = ScaleFactor(32);
+ }
+ }
+ else {
+ // Endgame with opposite-colored bishops, but also other pieces.
+ // Still a bit drawish, but not as drawish as with only the two
+ // bishops.
+ if(factor[WHITE] == SCALE_FACTOR_NORMAL)
+ factor[WHITE] = ScaleFactor(50);
+ if(factor[BLACK] == SCALE_FACTOR_NORMAL)
+ factor[BLACK] = ScaleFactor(50);
+ }
+ }
+
+ // Interpolate between the middle game and the endgame score, and
+ // return:
+ Value value = scale_by_game_phase(ei.mgValue, ei.egValue, phase, factor);
+
+ if(ei.mateThreat[stm] != MOVE_NONE)
+ return 8 * QueenValueMidgame - Sign[stm] * value;
+ else
+ return Sign[stm] * value;
+}
+
+
+/// quick_evaluate() does a very approximate evaluation of the current position.
+/// It currently considers only material and piece square table scores. Perhaps
+/// we should add scores from the pawn and material hash tables?
+
+Value quick_evaluate(const Position &pos) {
+ Color stm;
+ Value mgValue, egValue;
+ ScaleFactor factor[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL};
+ Phase phase;
+
+ assert(pos.is_ok());
+
+ stm = pos.side_to_move();
+
+ mgValue = pos.mg_value();
+ egValue = pos.eg_value();
+ phase = pos.game_phase();
+
+ Value value = scale_by_game_phase(mgValue, egValue, phase, factor);
+
+ return Sign[stm] * value;
+}
+
+
+/// init_eval() initializes various tables used by the evaluation function.
+
+void init_eval(int threads) {
+ assert(threads <= THREAD_MAX);
+
+ for(int i = 0; i < threads; i++) {
+ if(PawnTable[i] == NULL)
+ PawnTable[i] = new PawnInfoTable(PawnTableSize);
+ if(MaterialTable[i] == NULL)
+ MaterialTable[i] = new MaterialInfoTable(MaterialTableSize);
+ }
+ for(int i = threads; i < THREAD_MAX; i++) {
+ if(PawnTable[i] != NULL) {
+ delete PawnTable[i];
+ PawnTable[i] = NULL;
+ }
+ if(MaterialTable[i] != NULL) {
+ delete MaterialTable[i];
+ MaterialTable[i] = NULL;
+ }
+ }
+
+ for(Bitboard b = 0ULL; b < 256ULL; b++)
+ BitCount8Bit[b] = count_1s(b);
+}
+
+
+/// quit_eval() releases heap-allocated memory at program termination.
+
+void quit_eval() {
+ for(int i = 0; i < THREAD_MAX; i++) {
+ delete PawnTable[i];
+ delete MaterialTable[i];
+ }
+}
+
+
+/// read_weights() reads evaluation weights from the corresponding UCI
+/// parameters.
+
+void read_weights(Color sideToMove) {
+ WeightMobilityMidgame =
+ compute_weight(get_option_value_int("Mobility (Middle Game)"),
+ WeightMobilityMidgameInternal);
+ WeightMobilityEndgame =
+ compute_weight(get_option_value_int("Mobility (Endgame)"),
+ WeightMobilityEndgameInternal);
+ WeightPawnStructureMidgame =
+ compute_weight(get_option_value_int("Pawn Structure (Middle Game)"),
+ WeightPawnStructureMidgameInternal);
+ WeightPawnStructureEndgame =
+ compute_weight(get_option_value_int("Pawn Structure (Endgame)"),
+ WeightPawnStructureEndgameInternal);
+ WeightPassedPawnsMidgame =
+ compute_weight(get_option_value_int("Passed Pawns (Middle Game)"),
+ WeightPassedPawnsMidgameInternal);
+ WeightPassedPawnsEndgame =
+ compute_weight(get_option_value_int("Passed Pawns (Endgame)"),
+ WeightPassedPawnsEndgameInternal);
+ WeightKingSafety[sideToMove] =
+ compute_weight(get_option_value_int("Cowardice"), WeightKingSafetyInternal);
+ WeightKingSafety[opposite_color(sideToMove)] =
+ compute_weight(get_option_value_int("Aggressiveness"),
+ WeightKingSafetyInternal);
+ WeightKingSafety[opposite_color(sideToMove)] =
+ (get_option_value_int("Aggressiveness") * 0x100) / 100;
+
+ init_safety();
+}
+
+
+namespace {
+
+ // evaluate_knight() assigns bonuses and penalties to a knight of a given
+ // color on a given square.
+
+ void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei) {
+
+ Color them = opposite_color(us);
+ Bitboard b = p.knight_attacks(s);
+ ei.attackedBy[us][KNIGHT] |= b;
+
+ // King attack
+ if(b & ei.attackZone[us]) {
+ ei.attackCount[us]++;
+ ei.attackWeight[us] += KnightAttackWeight;
+ Bitboard bb = (b & ei.attackedBy[them][KING]);
+ if(bb) ei.attacked[us] += count_1s_max_15(bb);
+ }
+
+ // Mobility
+ int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
+ ei.mgMobility += Sign[us] * MidgameKnightMobilityBonus[mob];
+ ei.egMobility += Sign[us] * EndgameKnightMobilityBonus[mob];
+
+ // Knight outposts:
+ if(p.square_is_weak(s, them)) {
+ Value v, bonus;
+
+ // Initial bonus based on square:
+ v = bonus = KnightOutpostBonus[relative_square(us, s)];
+
+ // Increase bonus if supported by pawn, especially if the opponent has
+ // no minor piece which can exchange the outpost piece:
+ if(v && p.pawn_attacks(them, s) & p.pawns(us)) {
+ bonus += v/2;
+ if(p.knight_count(them) == 0 &&
+ (SquaresByColorBB[square_color(s)] &
+ p.bishops(them)) == EmptyBoardBB) {
+ bonus += v;
+ }
+ }
+
+ ei.mgValue += Sign[us] * bonus;
+ ei.egValue += Sign[us] * bonus;
+ }
+ }
+
+
+ // evaluate_bishop() assigns bonuses and penalties to a bishop of a given
+ // color on a given square.
+
+ void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei) {
+
+ Color them = opposite_color(us);
+ Bitboard b =
+ bishop_attacks_bb(s, p.occupied_squares() & ~p.queens(us));
+
+ ei.attackedBy[us][BISHOP] |= b;
+
+ // King attack
+ if(b & ei.attackZone[us]) {
+ ei.attackCount[us]++;
+ ei.attackWeight[us] += BishopAttackWeight;
+ Bitboard bb = (b & ei.attackedBy[them][KING]);
+ if(bb) ei.attacked[us] += count_1s_max_15(bb);
+ }
+
+ // Mobility:
+ int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
+ ei.mgMobility += Sign[us] * MidgameBishopMobilityBonus[mob];
+ ei.egMobility += Sign[us] * EndgameBishopMobilityBonus[mob];
+
+ // Bishop outposts:
+ if(p.square_is_weak(s, them)) {
+ Value v, bonus;
+
+ // Initial bonus based on square:
+ v = bonus = BishopOutpostBonus[relative_square(us, s)];
+
+ // Increase bonus if supported by pawn, especially if the opponent has
+ // no minor piece which can exchange the outpost piece:
+ if(v && p.pawn_attacks(them, s) & p.pawns(us)) {
+ bonus += v/2;
+ if(p.knight_count(them) == 0 &&
+ (SquaresByColorBB[square_color(s)] &
+ p.bishops(them)) == EmptyBoardBB) {
+ bonus += v;
+ }
+ }
+
+ ei.mgValue += Sign[us] * bonus;
+ ei.egValue += Sign[us] * bonus;
+ }
+ }
+
+
+ // evaluate_rook() assigns bonuses and penalties to a rook of a given
+ // color on a given square.
+
+ void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
+
+ Color them = opposite_color(us);
+
+ // Open and half-open files:
+ File f = square_file(s);
+ if(ei.pi->file_is_half_open(us, f)) {
+ if(ei.pi->file_is_half_open(them, f)) {
+ ei.mgValue += Sign[us] * RookOpenFileBonus;
+ ei.egValue += Sign[us] * RookOpenFileBonus;
+ }
+ else {
+ ei.mgValue += Sign[us] * RookHalfOpenFileBonus;
+ ei.egValue += Sign[us] * RookHalfOpenFileBonus;
+ }
+ }
+
+ // Rook on 7th rank:
+ if(pawn_rank(us, s) == RANK_7 &&
+ pawn_rank(us, p.king_square(them)) == RANK_8) {
+ ei.mgValue += Sign[us] * MidgameRookOn7thBonus;
+ ei.egValue += Sign[us] * EndgameRookOn7thBonus;
+ }
+
+ //Bitboard b = p.rook_attacks(s);
+ Bitboard b =
+ rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
+ ei.attackedBy[us][ROOK] |= b;
+
+ // King attack
+ if(b & ei.attackZone[us]) {
+ ei.attackCount[us]++;
+ ei.attackWeight[us] += RookAttackWeight;
+ Bitboard bb = (b & ei.attackedBy[them][KING]);
+ if(bb) ei.attacked[us] += count_1s_max_15(bb);
+ }
+
+ // Mobility
+ int mob = count_1s_max_15(b & ~p.pieces_of_color(us));
+ ei.mgMobility += Sign[us] * MidgameRookMobilityBonus[mob];
+ ei.egMobility += Sign[us] * EndgameRookMobilityBonus[mob];
+
+ // Penalize rooks which are trapped inside a king which has lost the
+ // right to castle:
+ if(mob <= 6 && !ei.pi->file_is_half_open(us, f)) {
+ Square ksq = p.king_square(us);
+ if(square_file(ksq) >= FILE_E && square_file(s) > square_file(ksq) &&
+ (pawn_rank(us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s))) {
+ // Is there a half-open file between the king and the edge of the
+ // board?
+ if(!(ei.pi->has_open_file_to_right(us, square_file(ksq)))) {
+ ei.mgValue -= p.can_castle(us)?
+ Sign[us] * ((TrappedRookPenalty - mob * 16) / 2) :
+ Sign[us] * (TrappedRookPenalty - mob * 16);
+ }
+ }
+ else if(square_file(ksq) <= FILE_D && square_file(s) < square_file(ksq)
+ && (pawn_rank(us, ksq) == RANK_1 ||
+ square_rank(ksq) == square_rank(s))) {
+ // Is there a half-open file between the king and the edge of the
+ // board?
+ if(!(ei.pi->has_open_file_to_left(us, square_file(ksq)))) {
+ ei.mgValue -= p.can_castle(us)?
+ Sign[us] * ((TrappedRookPenalty - mob * 16) / 2) :
+ Sign[us] * (TrappedRookPenalty - mob * 16);
+ }
+ }
+ }
+ }
+
+
+ // evaluate_queen() assigns bonuses and penalties to a queen of a given
+ // color on a given square.
+
+ void evaluate_queen(const Position &p, Square s, Color us, EvalInfo &ei) {
+
+ Color them = opposite_color(us);
+
+ // Queen on 7th rank:
+ if(pawn_rank(us, s) == RANK_7 &&
+ pawn_rank(us, p.king_square(them)) == RANK_8) {
+ ei.mgValue += Sign[us] * MidgameQueenOn7thBonus;
+ ei.egValue += Sign[us] * EndgameQueenOn7thBonus;
+ }
+
+ Bitboard b = p.queen_attacks(s);
+ ei.attackedBy[us][QUEEN] |= b;
+
+ // King attack
+ if(b & ei.attackZone[us]) {
+ ei.attackCount[us]++;
+ ei.attackWeight[us] += QueenAttackWeight;
+ Bitboard bb = (b & ei.attackedBy[them][KING]);
+ if(bb) ei.attacked[us] += count_1s_max_15(bb);
+ }
+
+ // Mobility
+ int mob = count_1s(b & ~p.pieces_of_color(us));
+ ei.mgMobility += Sign[us] * MidgameQueenMobilityBonus[mob];
+ ei.egMobility += Sign[us] * EndgameQueenMobilityBonus[mob];
+ }
+
+
+ // evaluate_king() assigns bonuses and penalties to a king of a given
+ // color on a given square.
+
+ void evaluate_king(const Position &p, Square s, Color us, EvalInfo &ei) {
+
+ int shelter = 0, sign = Sign[us];
+
+ // King shelter.
+ if(pawn_rank(us, s) <= RANK_4) {
+ Bitboard pawns = p.pawns(us) & this_and_neighboring_files_bb(s);
+ Rank r = square_rank(s);
+ for(int i = 0; i < 3; i++)
+ shelter += count_1s_8bit(pawns >> ((r+(i+1)*sign) * 8)) * (64>>i);
+ ei.mgValue += sign * Value(shelter);
+ }
+
+ // King safety. This is quite complicated, and is almost certainly far
+ // from optimally tuned.
+ Color them = opposite_color(us);
+ if(p.queen_count(them) >= 1 && ei.attackCount[them] >= 2
+ && p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame
+ && ei.attacked[them]) {
+
+ // Is it the attackers turn to move?
+ bool sente = (them == p.side_to_move());
+
+ // Find the attacked squares around the king which has no defenders
+ // apart from the king itself:
+ Bitboard undefended =
+ ei.attacked_by(them) & ~ei.attacked_by(us, PAWN)
+ & ~ei.attacked_by(us, KNIGHT) & ~ei.attacked_by(us, BISHOP)
+ & ~ei.attacked_by(us, ROOK) & ~ei.attacked_by(us, QUEEN)
+ & ei.attacked_by(us, KING);
+ Bitboard occ = p.occupied_squares(), b, b2;
+
+ // Initialize the 'attackUnits' variable, which is used later on as an
+ // index to the SafetyTable[] array. The initial is based on the number
+ // and types of the attacking pieces, the number of attacked and
+ // undefended squares around the king, the square of the king, and the
+ // quality of the pawn shelter.
+ int attackUnits =
+ Min((ei.attackCount[them] * ei.attackWeight[them]) / 2, 25)
+ + (ei.attacked[them] + count_1s_max_15(undefended)) * 3
+ + InitKingDanger[relative_square(us, s)] - shelter / 32;
+
+ // Analyse safe queen contact checks:
+ b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
+ if(b) {
+ Bitboard attackedByOthers =
+ ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT)
+ | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, ROOK);
+ b &= attackedByOthers;
+ if(b) {
+ // The bitboard b now contains the squares available for safe queen
+ // contact checks.
+ int count = count_1s_max_15(b);
+ attackUnits += QueenContactCheckBonus * count * (sente? 2 : 1);
+
+ // Is there a mate threat?
+ if(QueenContactMates && !p.is_check()) {
+ Bitboard escapeSquares =
+ p.king_attacks(s) & ~p.pieces_of_color(us) & ~attackedByOthers;
+ while(b) {
+ Square from, to = pop_1st_bit(&b);
+ if(!(escapeSquares
+ & ~queen_attacks_bb(to, occ & clear_mask_bb(s)))) {
+ // We have a mate, unless the queen is pinned or there
+ // is an X-ray attack through the queen.
+ for(int i = 0; i < p.queen_count(them); i++) {
+ from = p.queen_list(them, i);
+ if(bit_is_set(p.queen_attacks(from), to)
+ && !bit_is_set(p.pinned_pieces(them), from)
+ && !(rook_attacks_bb(to, occ & clear_mask_bb(from))
+ & p.rooks_and_queens(us))
+ && !(rook_attacks_bb(to, occ & clear_mask_bb(from))
+ & p.rooks_and_queens(us)))
+ ei.mateThreat[them] = make_move(from, to);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Analyse safe rook contact checks:
+ if(RookContactCheckBonus) {
+ b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them);
+ if(b) {
+ Bitboard attackedByOthers =
+ ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT)
+ | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN);
+ b &= attackedByOthers;
+ if(b) {
+ int count = count_1s_max_15(b);
+ attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1));
+ }
+ }
+ }
+
+ // Analyse safe distance checks:
+ if(QueenCheckBonus > 0 || RookCheckBonus > 0) {
+ b = p.rook_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+
+ // Queen checks
+ b2 = b & ei.attacked_by(them, QUEEN);
+ if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2);
+
+ // Rook checks
+ b2 = b & ei.attacked_by(them, ROOK);
+ if(b2) attackUnits += RookCheckBonus * count_1s_max_15(b2);
+ }
+ if(QueenCheckBonus > 0 || BishopCheckBonus > 0) {
+ b = p.bishop_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+ // Queen checks
+ b2 = b & ei.attacked_by(them, QUEEN);
+ if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2);
+
+ // Bishop checks
+ b2 = b & ei.attacked_by(them, BISHOP);
+ if(b2) attackUnits += BishopCheckBonus * count_1s_max_15(b2);
+ }
+ if(KnightCheckBonus > 0) {
+ b = p.knight_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
+ // Knight checks
+ b2 = b & ei.attacked_by(them, KNIGHT);
+ if(b2) attackUnits += KnightCheckBonus * count_1s_max_15(b2);
+ }
+
+ // Analyse discovered checks (only for non-pawns right now, consider
+ // adding pawns later).
+ if(DiscoveredCheckBonus) {
+ b = p.discovered_check_candidates(them) & ~p.pawns();
+ if(b)
+ attackUnits +=
+ DiscoveredCheckBonus * count_1s_max_15(b) * (sente? 2 : 1);
+ }
+
+ // Has a mate threat been found? We don't do anything here if the
+ // side with the mating move is the side to move, because in that
+ // case the mating side will get a huge bonus at the end of the main
+ // evaluation function instead.
+ if(ei.mateThreat[them] != MOVE_NONE)
+ attackUnits += MateThreatBonus;
+
+ // Ensure that attackUnits is between 0 and 99, in order to avoid array
+ // out of bounds errors:
+ if(attackUnits < 0) attackUnits = 0;
+ if(attackUnits >= 100) attackUnits = 99;
+
+ // Finally, extract the king safety score from the SafetyTable[] array.
+ // Add the score to the evaluation, and also to ei.futilityMargin. The
+ // reason for adding the king safety score to the futility margin is
+ // that the king safety scores can sometimes be very big, and that
+ // capturing a single attacking piece can therefore result in a score
+ // change far bigger than the value of the captured piece.
+ Value v = apply_weight(SafetyTable[attackUnits], WeightKingSafety[us]);
+ ei.mgValue -= sign * v;
+ if(us == p.side_to_move())
+ ei.futilityMargin += v;
+ }
+ }
+
+
+ // evaluate_passed_pawns() evaluates the passed pawns for both sides.
+
+ void evaluate_passed_pawns(const Position &pos, EvalInfo &ei) {
+ bool hasUnstoppable[2] = {false, false};
+ int movesToGo[2] = {100, 100};
+
+ for(Color us = WHITE; us <= BLACK; us++) {
+ Color them = opposite_color(us);
+ Square ourKingSq = pos.king_square(us);
+ Square theirKingSq = pos.king_square(them);
+ Bitboard b = ei.pi->passed_pawns() & pos.pawns(us), b2, b3, b4;
+
+ while(b) {
+ Square s = pop_1st_bit(&b);
+ assert(pos.piece_on(s) == pawn_of_color(us));
+ assert(pos.pawn_is_passed(us, s));
+
+ int r = int(pawn_rank(us, s) - RANK_2);
+ int tr = Max(0, r * (r-1));
+ Square blockSq = s + pawn_push(us);
+
+ // Base bonus based on rank:
+ Value mbonus = Value(20 * tr);
+ Value ebonus = Value(10 + r * r * 10);
+
+ // Adjust bonus based on king proximity:
+ ebonus -= Value(square_distance(ourKingSq, blockSq) * 3 * tr);
+ ebonus -=
+ Value(square_distance(ourKingSq, blockSq + pawn_push(us)) * 1 * tr);
+ ebonus += Value(square_distance(theirKingSq, blockSq) * 6 * tr);
+
+ // If the pawn is free to advance, increase bonus:
+ if(pos.square_is_empty(blockSq)) {
+
+ b2 = squares_in_front_of(us, s);
+ b3 = b2 & ei.attacked_by(them);
+ b4 = b2 & ei.attacked_by(us);
+ if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) {
+ // There are no enemy pieces in the pawn's path! Are any of the
+ // squares in the pawn's path attacked by the enemy?
+ if(b3 == EmptyBoardBB)
+ // No enemy attacks, huge bonus!
+ ebonus += Value(tr * ((b2 == b4)? 17 : 15));
+ else
+ // OK, there are enemy attacks. Are those squares which are
+ // attacked by the enemy also attacked by us? If yes, big bonus
+ // (but smaller than when there are no enemy attacks), if no,
+ // somewhat smaller bonus.
+ ebonus += Value(tr * (((b3 & b4) == b3)? 13 : 8));
+ }
+ else {
+ // There are some enemy pieces in the pawn's path. While this is
+ // sad, we still assign a moderate bonus if all squares in the path
+ // which are either occupied by or attacked by enemy pieces are
+ // also attacked by us.
+ if(((b3 | (b2 & pos.pieces_of_color(them))) & ~b4) == EmptyBoardBB)
+ ebonus += Value(tr * 6);
+ }
+ // At last, add a small bonus when there are no *friendly* pieces
+ // in the pawn's path:
+ if((b2 & pos.pieces_of_color(us)) == EmptyBoardBB)
+ ebonus += Value(tr);
+ }
+
+ // If the pawn is supported by a friendly pawn, increase bonus.
+ b2 = pos.pawns(us) & neighboring_files_bb(s);
+ if(b2 & rank_bb(s))
+ ebonus += Value(r * 20);
+ else if(pos.pawn_attacks(them, s) & b2)
+ ebonus += Value(r * 12);
+
+ // If the other side has only a king, check whether the pawn is
+ // unstoppable:
+ if(pos.non_pawn_material(them) == Value(0)) {
+ Square qsq;
+ int d;
+
+ qsq = relative_square(us, make_square(square_file(s), RANK_8));
+ d = square_distance(s, qsq) - square_distance(theirKingSq, qsq)
+ + ((us == pos.side_to_move())? 0 : 1);
+
+ if(d < 0) {
+ int mtg = RANK_8 - pawn_rank(us, s);
+ int blockerCount =
+ count_1s_max_15(squares_in_front_of(us,s)&pos.occupied_squares());
+ mtg += blockerCount;
+ d += blockerCount;
+ if(d < 0) {
+ hasUnstoppable[us] = true;
+ movesToGo[us] = Min(movesToGo[us], mtg);
+ }
+ }
+ }
+ // Rook pawns are a special case: They are sometimes worse, and
+ // sometimes better than other passed pawns. It is difficult to find
+ // good rules for determining whether they are good or bad. For now,
+ // we try the following: Increase the value for rook pawns if the
+ // other side has no pieces apart from a knight, and decrease the
+ // value if the other side has a rook or queen.
+ if(square_file(s) == FILE_A || square_file(s) == FILE_H) {
+ if(pos.non_pawn_material(them) == KnightValueMidgame
+ && pos.knight_count(them) == 1)
+ ebonus += ebonus / 4;
+ else if(pos.rooks_and_queens(them))
+ ebonus -= ebonus / 4;
+ }
+
+ // Add the scores for this pawn to the middle game and endgame eval.
+ ei.mgValue += apply_weight(Sign[us] * mbonus, WeightPassedPawnsMidgame);
+ ei.egValue += apply_weight(Sign[us] * ebonus, WeightPassedPawnsEndgame);
+ }
+ }
+
+ // Does either side have an unstoppable passed pawn?
+ if(hasUnstoppable[WHITE] && !hasUnstoppable[BLACK])
+ ei.egValue += UnstoppablePawnValue - Value(0x40 * movesToGo[WHITE]);
+ else if(hasUnstoppable[BLACK] && !hasUnstoppable[WHITE])
+ ei.egValue -= UnstoppablePawnValue - Value(0x40 * movesToGo[BLACK]);
+ else if(hasUnstoppable[BLACK] && hasUnstoppable[WHITE]) {
+ // Both sides have unstoppable pawns! Try to find out who queens
+ // first. We begin by transforming 'movesToGo' to the number of
+ // plies until the pawn queens for both sides:
+ movesToGo[WHITE] *= 2;
+ movesToGo[BLACK] *= 2;
+ movesToGo[pos.side_to_move()]--;
+
+ // If one side queens at least three plies before the other, that
+ // side wins:
+ if(movesToGo[WHITE] <= movesToGo[BLACK] - 3)
+ ei.egValue += UnstoppablePawnValue - Value(0x40 * (movesToGo[WHITE]/2));
+ else if(movesToGo[BLACK] <= movesToGo[WHITE] - 3)
+ ei.egValue -= UnstoppablePawnValue - Value(0x40 * (movesToGo[BLACK]/2));
+
+ // We could also add some rules about the situation when one side
+ // queens exactly one ply before the other: Does the first queen
+ // check the opponent's king, or attack the opponent's queening square?
+ // This is slightly tricky to get right, because it is possible that
+ // the opponent's king has moved somewhere before the first pawn queens.
+ }
+ }
+
+
+ // evaluate_trapped_bishop_a7h7() determines whether a bishop on a7/h7
+ // (a2/h2 for black) is trapped by enemy pawns, and assigns a penalty
+ // if it is.
+
+ void evaluate_trapped_bishop_a7h7(const Position &pos, Square s, Color us,
+ EvalInfo &ei) {
+ Piece pawn = pawn_of_color(opposite_color(us));
+ Square b6, b8;
+
+ assert(square_is_ok(s));
+ assert(pos.piece_on(s) == bishop_of_color(us));
+
+ if(square_file(s) == FILE_A) {
+ b6 = relative_square(us, SQ_B6);
+ b8 = relative_square(us, SQ_B8);
+ }
+ else {
+ b6 = relative_square(us, SQ_G6);
+ b8 = relative_square(us, SQ_G8);
+ }
+
+ if(pos.piece_on(b6) == pawn && pos.see(s, b6) < 0 && pos.see(s, b8) < 0) {
+ ei.mgValue -= Sign[us] * TrappedBishopA7H7Penalty;
+ ei.egValue -= Sign[us] * TrappedBishopA7H7Penalty;
+ }
+
+ }
+
+
+ // evaluate_trapped_bishop_a1h1() determines whether a bishop on a1/h1
+ // (a8/h8 for black) is trapped by a friendly pawn on b2/g2 (b7/g7 for
+ // black), and assigns a penalty if it is. This pattern can obviously
+ // only occur in Chess960 games.
+
+ void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
+ EvalInfo &ei) {
+ Piece pawn = pawn_of_color(us);
+ Square b2, b3, c3;
+
+ assert(Chess960);
+ assert(square_is_ok(s));
+ assert(pos.piece_on(s) == bishop_of_color(us));
+
+ if(square_file(s) == FILE_A) {
+ b2 = relative_square(us, SQ_B2);
+ b3 = relative_square(us, SQ_B3);
+ c3 = relative_square(us, SQ_C3);
+ }
+ else {
+ b2 = relative_square(us, SQ_G2);
+ b3 = relative_square(us, SQ_G3);
+ c3 = relative_square(us, SQ_F3);
+ }
+
+ if(pos.piece_on(b2) == pawn) {
+ Value penalty;
+
+ if(!pos.square_is_empty(b3))
+ penalty = 2*TrappedBishopA1H1Penalty;
+ else if(pos.piece_on(c3) == pawn)
+ penalty = TrappedBishopA1H1Penalty;
+ else
+ penalty = TrappedBishopA1H1Penalty / 2;
+
+ ei.mgValue -= Sign[us] * penalty;
+ ei.egValue -= Sign[us] * penalty;
+ }
+
+ }
+
+
+ // apply_weight applies an evaluation weight to a value.
+
+ inline Value apply_weight(Value v, int w) {
+ return (v*w) / 0x100;
+ }
+
+
+ // scale_by_game_phase interpolates between a middle game and an endgame
+ // score, based on game phase. It also scales the return value by a
+ // ScaleFactor array.
+
+ Value scale_by_game_phase(Value mv, Value ev, Phase ph, ScaleFactor sf[]) {
+ assert(mv > -VALUE_INFINITE && mv < VALUE_INFINITE);
+ assert(ev > -VALUE_INFINITE && ev < VALUE_INFINITE);
+ assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
+
+ if(ev > Value(0))
+ ev = apply_scale_factor(ev, sf[WHITE]);
+ else
+ ev = apply_scale_factor(ev, sf[BLACK]);
+
+ Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
+ return Value(int(result) & ~(GrainSize - 1));
+ }
+
+
+ // count_1s_8bit() counts the number of nonzero bits in the 8 least
+ // significant bits of an integer. This function is used by the king
+ // shield evaluation.
+
+ int count_1s_8bit(int b) {
+ return int(BitCount8Bit[b & 0xFF]);
+ }
+
+
+ // compute_weight() computes the value of an evaluation weight, by combining
+ // an UCI-configurable weight with an internal weight.
+
+ int compute_weight(int uciWeight, int internalWeight) {
+ uciWeight = (uciWeight * 0x100) / 100;
+ return (uciWeight * internalWeight) / 0x100;
+ }
+
+
+ // init_safety() initizes the king safety evaluation, based on UCI
+ // parameters. It is called from read_weights().
+
+ void init_safety() {
+ double a, b;
+ int maxSlope, peak, i, j;
+
+ QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus");
+ RookContactCheckBonus = get_option_value_int("Rook Contact Check Bonus");
+ QueenCheckBonus = get_option_value_int("Queen Check Bonus");
+ RookCheckBonus = get_option_value_int("Rook Check Bonus");
+ BishopCheckBonus = get_option_value_int("Bishop Check Bonus");
+ KnightCheckBonus = get_option_value_int("Knight Check Bonus");
+ DiscoveredCheckBonus = get_option_value_int("Discovered Check Bonus");
+ MateThreatBonus = get_option_value_int("Mate Threat Bonus");
+
+ a = get_option_value_int("King Safety Coefficient") / 100.0;
+ b = get_option_value_int("King Safety X Intercept") * 1.0;
+ maxSlope = get_option_value_int("King Safety Max Slope");
+ peak = (get_option_value_int("King Safety Max Value") * 256) / 100;
+
+ for(i = 0; i < 100; i++) {
+ if(i < b) SafetyTable[i] = Value(0);
+ else if(get_option_value_string("King Safety Curve") == "Quadratic")
+ SafetyTable[i] = Value((int)(a * (i - b) * (i - b)));
+ else if(get_option_value_string("King Safety Curve") == "Linear")
+ SafetyTable[i] = Value((int)(100 * a * (i - b)));
+ }
+
+ for(i = 0; i < 100; i++)
+ if(SafetyTable[i+1] - SafetyTable[i] > maxSlope) {
+ for(j = i + 1; j < 100; j++)
+ SafetyTable[j] = SafetyTable[j-1] + Value(maxSlope);
+ }
+ for(i = 0; i < 100; i++)
+ if(SafetyTable[i] > Value(peak))
+ SafetyTable[i] = Value(peak);
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(EVALUATE_H_INCLUDED)
+#define EVALUATE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "material.h"
+#include "pawns.h"
+#include "position.h"
+
+
+////
+//// Types
+////
+
+/// The EvalInfo struct contains various information computed and collected
+/// by the evaluation function. An EvalInfo object is passed as one of the
+/// arguments to the evaluation function, and the search can make use of its
+/// contents to make intelligent search decisions.
+///
+/// At the moment, this is not utilized very much: The only part of the
+/// EvalInfo object which is used by the search is futilityMargin.
+
+struct EvalInfo {
+
+ // Middle game and endgame evaluations:
+ Value mgValue, egValue;
+
+ // Pointers to material and pawn hash table entries:
+ MaterialInfo *mi;
+ PawnInfo *pi;
+
+ // attackedBy[color][piece type] is a bitboard representing all squares
+ // attacked by a given color and piece type. attackedBy[color][0] contains
+ // all squares attacked by the given color.
+ Bitboard attackedBy[2][8];
+ Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
+ Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
+ // attackZone[color] is the zone around the enemy king which is considered
+ // by the king safety evaluation. This consists of the squares directly
+ // adjacent to the king, and the three (or two, for a king on an edge file)
+ // squares two ranks in front of the king. For instance, if black's king
+ // is on g8, attackZone[WHITE] is a bitboard containing the squares f8, h8,
+ // f7, g7, h7, f6, g6 and h6.
+ Bitboard attackZone[2];
+
+ // attackCount[color] is the number of pieces of the given color which
+ // attack a square adjacent to the enemy king.
+ int attackCount[2];
+
+ // attackWeight[color] is the sum of the "weight" of the pieces of the given
+ // color which attack a square adjacent to the enemy king. The weights of
+ // the individual piece types are given by the variables QueenAttackWeight,
+ // RookAttackWeight, BishopAttackWeight and KnightAttackWeight in
+ // evaluate.cpp.
+ int attackWeight[2];
+
+ // attacked[color] is the number of enemy piece attacks to squares directly
+ // adjacent to the king of the given color. Pieces which attack more
+ // than one square are counted multiple times. For instance, if black's
+ // king is on g8 and there's a white knight on g5, this knight adds
+ // 2 to attacked[BLACK].
+ int attacked[2];
+
+ // mateThreat[color] is a move for the given side which gives a direct mate.
+ Move mateThreat[2];
+
+ // Middle game and endgame mobility scores.
+ Value mgMobility, egMobility;
+
+ // Extra futility margin. This is added to the standard futility margin
+ // in the quiescence search.
+ Value futilityMargin;
+};
+
+
+////
+//// Prototypes
+////
+
+extern Value evaluate(const Position &pos, EvalInfo &ei, int threadID);
+extern Value quick_evaluate(const Position &pos);
+extern void init_eval(int threads);
+extern void quit_eval();
+extern void read_weights(Color sideToMove);
+
+
+#endif // !defined(EVALUATE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "history.h"
+
+
+////
+//// Functions
+////
+
+/// Constructor
+
+History::History() {
+ this->clear();
+}
+
+
+/// History::clear() clears the history tables.
+
+void History::clear() {
+ memset(history, 0, 2 * 8 * 64 * sizeof(int));
+ memset(successCount, 0, 2 * 8 * 64 * sizeof(int));
+ memset(failureCount, 0, 2 * 8 * 64 * sizeof(int));
+}
+
+
+/// History::success() registers a move as being successful. This is done
+/// whenever a non-capturing move causes a beta cutoff in the main search.
+/// The three parameters are the moving piece, the move itself, and the
+/// search depth.
+
+void History::success(Piece p, Move m, Depth d) {
+ assert(piece_is_ok(p));
+ assert(move_is_ok(m));
+
+ history[p][move_to(m)] += int(d) * int(d);
+ successCount[p][move_to(m)]++;
+
+ // Prevent history overflow:
+ if(history[p][move_to(m)] >= HistoryMax)
+ for(int i = 0; i < 16; i++)
+ for(int j = 0; j < 64; j++)
+ history[i][j] /= 2;
+}
+
+
+/// History::failure() registers a move as being unsuccessful. The function is
+/// called for each non-capturing move which failed to produce a beta cutoff
+/// at a node where a beta cutoff was finally found.
+
+void History::failure(Piece p, Move m) {
+ assert(piece_is_ok(p));
+ assert(move_is_ok(m));
+
+ failureCount[p][move_to(m)]++;
+}
+
+
+/// History::move_ordering_score() returns an integer value used to order the
+/// non-capturing moves in the MovePicker class.
+
+int History::move_ordering_score(Piece p, Move m) const {
+ assert(piece_is_ok(p));
+ assert(move_is_ok(m));
+
+ return history[p][move_to(m)];
+}
+
+
+/// History::ok_to_prune() decides whether a move has been sufficiently
+/// unsuccessful that it makes sense to prune it entirely.
+
+bool History::ok_to_prune(Piece p, Move m, Depth d) const {
+ assert(piece_is_ok(p));
+ assert(move_is_ok(m));
+
+ return (int(d) * successCount[p][move_to(m)] < failureCount[p][move_to(m)]);
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(HISTORY_H_INCLUDED)
+#define HISTORY_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "depth.h"
+#include "move.h"
+#include "piece.h"
+
+
+////
+//// Types
+////
+
+/// The History class stores statistics about how often different moves have
+/// been successful or unsuccessful during the current search. These
+/// statistics are used for reduction and move ordering decisions.
+
+class History {
+
+public:
+ History();
+ void clear();
+ void success(Piece p, Move m, Depth d);
+ void failure(Piece p, Move m);
+ int move_ordering_score(Piece p, Move m) const;
+ bool ok_to_prune(Piece p, Move m, Depth d) const;
+
+private:
+ int history[16][64]; // [piece][square]
+ int successCount[16][64];
+ int failureCount[16][64];
+};
+
+
+////
+//// Constants and variables
+////
+
+/// HistoryMax controls how often the history counters will be scaled down:
+/// When the history score for a move gets bigger than HistoryMax, all
+/// entries in the table are divided by 2. It is difficult to guess what
+/// the ideal value of this constant is. Scaling down the scores often has
+/// the effect that parts of the search tree which have been searched
+/// recently have a bigger importance for move ordering than the moves which
+/// have been searched a long time ago.
+///
+/// Note that HistoryMax should probably be changed whenever the constant
+/// OnePly in depth.h is changed. This is somewhat annoying. Perhaps it
+/// would be better to scale down the history table at regular intervals?
+
+const int HistoryMax = 50000;
+
+
+#endif // !defined(HISTORY_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(LOCK_H_INCLUDED)
+#define LOCK_H_INCLUDED
+
+
+// x86 assembly language locks or OS spin locks may perform faster than
+// mutex locks on some platforms. On my machine, mutexes seem to be the
+// best.
+
+//#define ASM_LOCK
+//#define OS_SPIN_LOCK
+
+
+#if defined(ASM_LOCK)
+
+
+typedef volatile int Lock;
+
+static inline void LockX86(Lock *lock) {
+ int dummy;
+ asm __volatile__("1: movl $1, %0" "\n\t"
+ " xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
+ " jz 3f" "\n\t" "2: pause" "\n\t"
+ " movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
+ " jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
+ "\n\t":"=&q"(dummy)
+ :"q"(lock)
+ :"cc");
+}
+
+static inline void UnlockX86(Lock *lock) {
+ int dummy;
+ asm __volatile__("movl $0, (%1)":"=&q"(dummy)
+ :"q"(lock));
+}
+
+# define lock_init(x, y) (*(x) = 0)
+# define lock_grab(x) LockX86(x)
+# define lock_release(x) UnlockX86(x)
+# define lock_destroy(x)
+
+
+#elif defined(OS_SPIN_LOCK)
+
+
+# include <libkern/OSAtomic.h>
+
+typedef OSSpinLock Lock;
+
+# define lock_init(x, y) (*(x) = 0)
+# define lock_grab(x) OSSpinLockLock(x)
+# define lock_release(x) OSSpinLockUnlock(x)
+# define lock_destroy(x)
+
+
+#elif !defined(_MSC_VER)
+
+# include <pthread.h>
+
+typedef pthread_mutex_t Lock;
+
+# define lock_init(x, y) pthread_mutex_init(x, y)
+# define lock_grab(x) pthread_mutex_lock(x)
+# define lock_release(x) pthread_mutex_unlock(x)
+# define lock_destroy(x) pthread_mutex_destroy(x)
+
+
+#else
+
+
+# include <windows.h>
+
+typedef CRITICAL_SECTION Lock;
+# define lock_init(x, y) InitializeCriticalSection(x)
+# define lock_grab(x) EnterCriticalSection(x)
+# define lock_release(x) LeaveCriticalSection(x)
+# define lock_destroy(x) DeleteCriticalSection(x)
+
+
+#endif
+
+
+#endif // !defined(LOCK_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cstdlib>
+#include <iostream>
+
+#include "benchmark.h"
+#include "bitboard.h"
+#include "direction.h"
+#include "endgame.h"
+#include "evaluate.h"
+#include "material.h"
+#include "mersenne.h"
+#include "misc.h"
+#include "movepick.h"
+#include "position.h"
+#include "search.h"
+#include "thread.h"
+#include "uci.h"
+#include "ucioption.h"
+
+
+////
+//// Functions
+////
+
+int main(int argc, char *argv[]) {
+
+ // Disable IO buffering
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+ std::cout.rdbuf()->pubsetbuf(NULL, 0);
+ std::cin.rdbuf()->pubsetbuf(NULL, 0);
+
+ // Initialization
+
+ init_mersenne();
+ init_direction_table();
+ init_bitboards();
+ init_uci_options();
+ Position::init_zobrist();
+ Position::init_piece_square_tables();
+ MaterialInfo::init();
+ MovePicker::init_phase_table();
+ init_eval(1);
+ init_bitbases();
+ init_threads();
+
+ // Make random number generation less deterministic, for book moves
+ int i = abs(get_system_time() % 10000);
+ for(int j = 0; j < i; j++)
+ genrand_int32();
+
+ // Process command line arguments
+ if(argc >= 2) {
+ if(std::string(argv[1]) == "bench") {
+ if(argc != 4) {
+ std::cout << "Usage: glaurung bench <hash> <threads>" << std::endl;
+ exit(0);
+ }
+ benchmark(std::string(argv[2]), std::string(argv[3]));
+ return 0;
+ }
+ }
+
+ // Print copyright notice
+ std::cout << engine_name() << ". "
+ << "Copyright (C) 2004-2008 Tord Romstad."
+ << std::endl;
+
+ // Enter UCI mode
+ uci_main_loop();
+
+ return 0;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "material.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ const Value BishopPairMidgameBonus = Value(100);
+ const Value BishopPairEndgameBonus = Value(100);
+
+ Key KPKMaterialKey, KKPMaterialKey;
+ Key KBNKMaterialKey, KKBNMaterialKey;
+ Key KRKPMaterialKey, KPKRMaterialKey;
+ Key KRKBMaterialKey, KBKRMaterialKey;
+ Key KRKNMaterialKey, KNKRMaterialKey;
+ Key KQKRMaterialKey, KRKQMaterialKey;
+ Key KRPKRMaterialKey, KRKRPMaterialKey;
+ Key KRPPKRPMaterialKey, KRPKRPPMaterialKey;
+ Key KNNKMaterialKey, KKNNMaterialKey;
+ Key KBPKBMaterialKey, KBKBPMaterialKey;
+ Key KBPKNMaterialKey, KNKBPMaterialKey;
+ Key KNPKMaterialKey, KKNPMaterialKey;
+ Key KPKPMaterialKey;
+
+};
+
+
+////
+//// Functions
+////
+
+/// MaterialInfo::init() is called during program initialization. It
+/// precomputes material hash keys for a few basic endgames, in order
+/// to make it easy to recognize such endgames when they occur.
+
+void MaterialInfo::init() {
+ KPKMaterialKey = Position::zobMaterial[WHITE][PAWN][1];
+ KKPMaterialKey = Position::zobMaterial[BLACK][PAWN][1];
+ KBNKMaterialKey =
+ Position::zobMaterial[WHITE][BISHOP][1] ^
+ Position::zobMaterial[WHITE][KNIGHT][1];
+ KKBNMaterialKey =
+ Position::zobMaterial[BLACK][BISHOP][1] ^
+ Position::zobMaterial[BLACK][KNIGHT][1];
+ KRKPMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KPKRMaterialKey =
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][ROOK][1];
+ KRKBMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[BLACK][BISHOP][1];
+ KBKRMaterialKey =
+ Position::zobMaterial[WHITE][BISHOP][1] ^
+ Position::zobMaterial[BLACK][ROOK][1];
+ KRKNMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[BLACK][KNIGHT][1];
+ KNKRMaterialKey =
+ Position::zobMaterial[WHITE][KNIGHT][1] ^
+ Position::zobMaterial[BLACK][ROOK][1];
+ KQKRMaterialKey =
+ Position::zobMaterial[WHITE][QUEEN][1] ^
+ Position::zobMaterial[BLACK][ROOK][1];
+ KRKQMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[BLACK][QUEEN][1];
+ KRPKRMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][ROOK][1];
+ KRKRPMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[BLACK][ROOK][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KRPPKRPMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[WHITE][PAWN][2] ^
+ Position::zobMaterial[BLACK][ROOK][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KRPKRPPMaterialKey =
+ Position::zobMaterial[WHITE][ROOK][1] ^
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][ROOK][1] ^
+ Position::zobMaterial[BLACK][PAWN][1] ^
+ Position::zobMaterial[BLACK][PAWN][2];
+ KNNKMaterialKey =
+ Position::zobMaterial[WHITE][KNIGHT][1] ^
+ Position::zobMaterial[WHITE][KNIGHT][2];
+ KKNNMaterialKey =
+ Position::zobMaterial[BLACK][KNIGHT][1] ^
+ Position::zobMaterial[BLACK][KNIGHT][2];
+ KBPKBMaterialKey =
+ Position::zobMaterial[WHITE][BISHOP][1] ^
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][BISHOP][1];
+ KBKBPMaterialKey =
+ Position::zobMaterial[WHITE][BISHOP][1] ^
+ Position::zobMaterial[BLACK][BISHOP][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KBPKNMaterialKey =
+ Position::zobMaterial[WHITE][BISHOP][1] ^
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][KNIGHT][1];
+ KNKBPMaterialKey =
+ Position::zobMaterial[WHITE][KNIGHT][1] ^
+ Position::zobMaterial[BLACK][BISHOP][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KNPKMaterialKey =
+ Position::zobMaterial[WHITE][KNIGHT][1] ^
+ Position::zobMaterial[WHITE][PAWN][1];
+ KKNPMaterialKey =
+ Position::zobMaterial[BLACK][KNIGHT][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+ KPKPMaterialKey =
+ Position::zobMaterial[WHITE][PAWN][1] ^
+ Position::zobMaterial[BLACK][PAWN][1];
+}
+
+
+/// Constructor for the MaterialInfoTable class.
+
+MaterialInfoTable::MaterialInfoTable(unsigned numOfEntries) {
+ size = numOfEntries;
+ entries = new MaterialInfo[size];
+ if(entries == NULL) {
+ std::cerr << "Failed to allocate " << (numOfEntries * sizeof(MaterialInfo))
+ << " bytes for material hash table." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ this->clear();
+}
+
+
+/// Destructor for the MaterialInfoTable class.
+
+MaterialInfoTable::~MaterialInfoTable() {
+ delete [] entries;
+}
+
+
+/// MaterialInfoTable::clear() clears a material hash table by setting
+/// all entries to 0.
+
+void MaterialInfoTable::clear() {
+ memset(entries, 0, size * sizeof(MaterialInfo));
+}
+
+
+/// MaterialInfoTable::get_material_info() takes a position object as input,
+/// computes or looks up a MaterialInfo object, and returns a pointer to it.
+/// If the material configuration is not already present in the table, it
+/// is stored there, so we don't have to recompute everything when the
+/// same material configuration occurs again.
+
+MaterialInfo *MaterialInfoTable::get_material_info(const Position &pos) {
+ Key key = pos.get_material_key();
+ int index = key & (size - 1);
+ MaterialInfo *mi = entries + index;
+
+ // If mi->key matches the position's material hash key, it means that we
+ // have analysed this material configuration before, and we can simply
+ // return the information we found the last time instead of recomputing it:
+ if(mi->key == key)
+ return mi;
+
+ // Clear the MaterialInfo object, and set its key:
+ mi->clear();
+ mi->key = key;
+
+ // A special case before looking for a specialized evaluation function:
+ // KNN vs K is a draw:
+ if(key == KNNKMaterialKey || key == KKNNMaterialKey) {
+ mi->factor[WHITE] = mi->factor[BLACK] = 0;
+ return mi;
+ }
+
+ // Let's look if we have a specialized evaluation function for this
+ // particular material configuration:
+ if(key == KPKMaterialKey) {
+ mi->evaluationFunction = &EvaluateKPK;
+ return mi;
+ }
+ else if(key == KKPMaterialKey) {
+ mi->evaluationFunction = &EvaluateKKP;
+ return mi;
+ }
+ else if(key == KBNKMaterialKey) {
+ mi->evaluationFunction = &EvaluateKBNK;
+ return mi;
+ }
+ else if(key == KKBNMaterialKey) {
+ mi->evaluationFunction = &EvaluateKKBN;
+ return mi;
+ }
+ else if(key == KRKPMaterialKey) {
+ mi->evaluationFunction = &EvaluateKRKP;
+ return mi;
+ }
+ else if(key == KPKRMaterialKey) {
+ mi->evaluationFunction = &EvaluateKPKR;
+ return mi;
+ }
+ else if(key == KRKBMaterialKey) {
+ mi->evaluationFunction = &EvaluateKRKB;
+ return mi;
+ }
+ else if(key == KBKRMaterialKey) {
+ mi->evaluationFunction = &EvaluateKBKR;
+ return mi;
+ }
+ else if(key == KRKNMaterialKey) {
+ mi->evaluationFunction = &EvaluateKRKN;
+ return mi;
+ }
+ else if(key == KNKRMaterialKey) {
+ mi->evaluationFunction = &EvaluateKNKR;
+ return mi;
+ }
+ else if(key == KQKRMaterialKey) {
+ mi->evaluationFunction = &EvaluateKQKR;
+ return mi;
+ }
+ else if(key == KRKQMaterialKey) {
+ mi->evaluationFunction = &EvaluateKRKQ;
+ return mi;
+ }
+ else if(pos.non_pawn_material(BLACK) == Value(0) &&
+ pos.pawn_count(BLACK) == 0 &&
+ pos.non_pawn_material(WHITE) >= RookValueEndgame) {
+ mi->evaluationFunction = &EvaluateKXK;
+ return mi;
+ }
+ else if(pos.non_pawn_material(WHITE) == Value(0) &&
+ pos.pawn_count(WHITE) == 0 &&
+ pos.non_pawn_material(BLACK) >= RookValueEndgame) {
+ mi->evaluationFunction = &EvaluateKKX;
+ return mi;
+ }
+
+ // OK, we didn't find any special evaluation function for the current
+ // material configuration. Is there a suitable scaling function?
+ //
+ // The code below is rather messy, and it could easily get worse later,
+ // if we decide to add more special cases. We face problems when there
+ // are several conflicting applicable scaling functions and we need to
+ // decide which one to use.
+
+ if(key == KRPKRMaterialKey) {
+ mi->scalingFunction[WHITE] = &ScaleKRPKR;
+ return mi;
+ }
+ if(key == KRKRPMaterialKey) {
+ mi->scalingFunction[BLACK] = &ScaleKRKRP;
+ return mi;
+ }
+ if(key == KRPPKRPMaterialKey) {
+ mi->scalingFunction[WHITE] = &ScaleKRPPKRP;
+ return mi;
+ }
+ else if(key == KRPKRPPMaterialKey) {
+ mi->scalingFunction[BLACK] = &ScaleKRPKRPP;
+ return mi;
+ }
+ if(key == KBPKBMaterialKey) {
+ mi->scalingFunction[WHITE] = &ScaleKBPKB;
+ return mi;
+ }
+ if(key == KBKBPMaterialKey) {
+ mi->scalingFunction[BLACK] = &ScaleKBKBP;
+ return mi;
+ }
+ if(key == KBPKNMaterialKey) {
+ mi->scalingFunction[WHITE] = &ScaleKBPKN;
+ return mi;
+ }
+ if(key == KNKBPMaterialKey) {
+ mi->scalingFunction[BLACK] = &ScaleKNKBP;
+ return mi;
+ }
+ if(key == KNPKMaterialKey) {
+ mi->scalingFunction[WHITE] = &ScaleKNPK;
+ return mi;
+ }
+ if(key == KKNPMaterialKey) {
+ mi->scalingFunction[BLACK] = &ScaleKKNP;
+ return mi;
+ }
+
+ if(pos.non_pawn_material(WHITE) == BishopValueMidgame &&
+ pos.bishop_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
+ mi->scalingFunction[WHITE] = &ScaleKBPK;
+ if(pos.non_pawn_material(BLACK) == BishopValueMidgame &&
+ pos.bishop_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
+ mi->scalingFunction[BLACK] = &ScaleKKBP;
+
+ if(pos.pawn_count(WHITE) == 0 &&
+ pos.non_pawn_material(WHITE) == QueenValueMidgame &&
+ pos.queen_count(WHITE) == 1 &&
+ pos.rook_count(BLACK) == 1 && pos.pawn_count(BLACK) >= 1)
+ mi->scalingFunction[WHITE] = &ScaleKQKRP;
+ else if(pos.pawn_count(BLACK) == 0 &&
+ pos.non_pawn_material(BLACK) == QueenValueMidgame &&
+ pos.queen_count(BLACK) == 1 &&
+ pos.rook_count(WHITE) == 1 && pos.pawn_count(WHITE) >= 1)
+ mi->scalingFunction[BLACK] = &ScaleKRPKQ;
+
+ if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0)) {
+ if(pos.pawn_count(BLACK) == 0) {
+ assert(pos.pawn_count(WHITE) >= 2);
+ mi->scalingFunction[WHITE] = &ScaleKPsK;
+ }
+ else if(pos.pawn_count(WHITE) == 0) {
+ assert(pos.pawn_count(BLACK) >= 2);
+ mi->scalingFunction[BLACK] = &ScaleKKPs;
+ }
+ else if(pos.pawn_count(WHITE) == 1 && pos.pawn_count(BLACK) == 1) {
+ mi->scalingFunction[WHITE] = &ScaleKPKPw;
+ mi->scalingFunction[BLACK] = &ScaleKPKPb;
+ }
+ }
+
+ // Evaluate the material balance.
+
+ Color c;
+ int sign;
+ Value egValue = Value(0), mgValue = Value(0);
+
+ for(c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign) {
+
+ // No pawns makes it difficult to win, even with a material advantage:
+ if(pos.pawn_count(c) == 0 &&
+ pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c))
+ <= BishopValueMidgame) {
+ if(pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c)))
+ mi->factor[c] = 0;
+ else if(pos.non_pawn_material(c) < RookValueMidgame)
+ mi->factor[c] = 0;
+ else {
+ switch(pos.bishop_count(c)) {
+ case 2:
+ mi->factor[c] = 32; break;
+ case 1:
+ mi->factor[c] = 12; break;
+ case 0:
+ mi->factor[c] = 6; break;
+ }
+ }
+ }
+
+ // Bishop pair:
+ if(pos.bishop_count(c) >= 2) {
+ mgValue += sign * BishopPairMidgameBonus;
+ egValue += sign * BishopPairEndgameBonus;
+ }
+
+ // Knights are stronger when there are many pawns on the board. The
+ // formula is taken from Larry Kaufman's paper "The Evaluation of Material
+ // Imbalances in Chess":
+ // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
+ mgValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
+ egValue += sign * Value(pos.knight_count(c)*(pos.pawn_count(c)-5)*16);
+
+ // Redundancy of major pieces, again based on Kaufman's paper:
+ if(pos.rook_count(c) >= 1) {
+ Value v = Value((pos.rook_count(c) - 1) * 32 + pos.queen_count(c) * 16);
+ mgValue -= sign * v;
+ egValue -= sign * v;
+ }
+
+ }
+
+ mi->mgValue = int16_t(mgValue);
+ mi->egValue = int16_t(egValue);
+
+ return mi;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(MATERIAL_H_INCLUDED)
+#define MATERIAL_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "endgame.h"
+#include "position.h"
+#include "scale.h"
+
+
+////
+//// Types
+////
+
+/// MaterialInfo is a class which contains various information about a
+/// material configuration. It contains a material balance evaluation,
+/// a function pointer to a special endgame evaluation function (which in
+/// most cases is NULL, meaning that the standard evaluation function will
+/// be used), and "scale factors" for black and white.
+///
+/// The scale factors are used to scale the evaluation score up or down.
+/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
+/// of 4, which will result in scores of absolute value less than one pawn.
+
+class MaterialInfo {
+
+ friend class MaterialInfoTable;
+
+public:
+ Value mg_value() const;
+ Value eg_value() const;
+ ScaleFactor scale_factor(const Position &pos, Color c) const;
+ bool specialized_eval_exists() const;
+ Value evaluate(const Position &pos) const;
+
+ static void init();
+
+private:
+ void clear();
+
+ Key key;
+ int16_t mgValue;
+ int16_t egValue;
+ uint8_t factor[2];
+ EndgameEvaluationFunction *evaluationFunction;
+ ScalingFunction *scalingFunction[2];
+};
+
+
+/// The MaterialInfoTable class represents a pawn hash table. It is basically
+/// just an array of MaterialInfo objects and a few methods for accessing these
+/// objects. The most important method is get_material_info, which looks up a
+/// position in the table and returns a pointer to a MaterialInfo object.
+
+class MaterialInfoTable {
+
+public:
+ MaterialInfoTable(unsigned numOfEntries);
+ ~MaterialInfoTable();
+ void clear();
+ MaterialInfo *get_material_info(const Position &pos);
+
+private:
+ unsigned size;
+ MaterialInfo *entries;
+};
+
+
+////
+//// Inline functions
+////
+
+/// MaterialInfo::mg_value and MaterialInfo::eg_value simply returns the
+/// material balance evaluation for the middle game and the endgame.
+
+inline Value MaterialInfo::mg_value() const {
+ return Value(mgValue);
+}
+
+inline Value MaterialInfo::eg_value() const {
+ return Value(egValue);
+}
+
+
+/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
+/// with all slots at their default values.
+
+inline void MaterialInfo::clear() {
+ mgValue = egValue = 0;
+ factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
+ evaluationFunction = NULL;
+ scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
+}
+
+
+/// MaterialInfo::scale_factor takes a position and a color as input, and
+/// returns a scale factor for the given color. We have to provide the
+/// position in addition to the color, because the scale factor need not
+/// be a constant: It can also be a function which should be applied to
+/// the position. For instance, in KBP vs K endgames, a scaling function
+/// which checks for draws with rook pawns and wrong-colored bishops.
+
+inline ScaleFactor MaterialInfo::scale_factor(const Position &pos, Color c)
+ const {
+ if(scalingFunction[c] != NULL) {
+ ScaleFactor sf = scalingFunction[c]->apply(pos);
+ if(sf != SCALE_FACTOR_NONE)
+ return sf;
+ }
+ return ScaleFactor(factor[c]);
+}
+
+
+/// MaterialInfo::specialized_eval_exists decides whether there is a
+/// specialized evaluation function for the current material configuration,
+/// or if the normal evaluation function should be used.
+
+inline bool MaterialInfo::specialized_eval_exists() const {
+ return evaluationFunction != NULL;
+}
+
+
+/// MaterialInfo::evaluate applies a specialized evaluation function to a
+/// given position object. It should only be called when
+/// this->specialized_eval_exists() returns 'true'.
+
+inline Value MaterialInfo::evaluate(const Position &pos) const {
+ return evaluationFunction->apply(pos);
+}
+
+#endif // !defined(MATERIAL_H_INCLUDED)
--- /dev/null
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+#include "types.h"
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializes mt[N] with a seed */
+void init_genrand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+void init_by_array(unsigned long init_key[], int key_length)
+{
+ int i, j, k;
+ init_genrand(19650218UL);
+ i=1; j=0;
+ k = (N>key_length ? N : key_length);
+ for (; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ + init_key[j] + j; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++; j++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ if (j>=key_length) j=0;
+ }
+ for (k=N-1; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
+ - i; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ }
+
+ mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+uint32_t genrand_int32(void) {
+ unsigned long y;
+ static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ init_genrand(5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+uint64_t genrand_int64(void) {
+ uint64_t x, y;
+
+ x = genrand_int32(); y = genrand_int32();
+ return (x<<32)|y;
+}
+
+void init_mersenne(void) {
+ unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
+ init_by_array(init, length);
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(MERSENNE_H_INCLUDED)
+#define MERSENNE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "types.h"
+
+
+////
+//// Prototypes
+////
+
+extern uint32_t genrand_int32(void);
+extern uint64_t genrand_int64(void);
+extern void init_mersenne(void);
+
+
+#endif // !defined(MERSENNE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#if !defined(_MSC_VER)
+
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+
+#else
+
+# include <windows.h>
+# include <time.h>
+# include "dos.h"
+int gettimeofday(struct timeval * tp, struct timezone * tzp);
+
+#endif
+
+#include <cstdio>
+#include <iomanip>
+#include <sstream>
+
+#include "misc.h"
+
+
+////
+//// Functions
+////
+
+/// engine_name() returns the full name of the current Glaurung version.
+/// This will be either "Glaurung YYMMDD" (where YYMMDD is the date when the
+/// program was compiled) or "Glaurung <version number>", depending on whether
+/// the constant EngineVersion (defined in misc.h) is empty.
+
+const std::string engine_name() {
+ if(EngineVersion == "") {
+ static const char monthNames[12][4] = {
+ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+ };
+ const char *dateString = __DATE__;
+ std::stringstream s;
+ int month = 0, day = 0;
+
+ for(int i = 0; i < 12; i++)
+ if(strncmp(dateString, monthNames[i], 3) == 0)
+ month = i + 1;
+ day = atoi(dateString+4);
+
+ s << "Glaurung " << (dateString+9) << std::setfill('0') << std::setw(2)
+ << month << std::setfill('0') << std::setw(2) << day;
+
+ return s.str();
+ }
+ else
+ return "Glaurung " + EngineVersion;
+}
+
+
+/// get_system_time() returns the current system time, measured in
+/// milliseconds.
+
+int get_system_time() {
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return t.tv_sec*1000 + t.tv_usec/1000;
+}
+
+
+/// cpu_count() tries to detect the number of CPU cores.
+
+#if !defined(_MSC_VER)
+
+# if defined(_SC_NPROCESSORS_ONLN)
+int cpu_count() {
+ return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
+}
+# else
+int cpu_count() {
+ return 1;
+}
+# endif
+
+#else
+
+int cpu_count() {
+ SYSTEM_INFO s;
+ GetSystemInfo(&s);
+ return Min(s.dwNumberOfProcessors, 8);
+}
+
+#endif
+
+
+/*
+ From Beowulf, from Olithink
+*/
+#ifndef _WIN32
+/* Non-windows version */
+int Bioskey()
+{
+ fd_set readfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_SET(fileno(stdin), &readfds);
+ /* Set to timeout immediately */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ select(16, &readfds, 0, 0, &timeout);
+
+ return (FD_ISSET(fileno(stdin), &readfds));
+}
+
+#else
+/* Windows-version */
+#include <windows.h>
+#include <conio.h>
+int Bioskey()
+{
+ static int init = 0,
+ pipe;
+ static HANDLE inh;
+ DWORD dw;
+ /* If we're running under XBoard then we can't use _kbhit() as the input
+ * commands are sent to us directly over the internal pipe */
+
+#if defined(FILE_CNT)
+ if (stdin->_cnt > 0)
+ return stdin->_cnt;
+#endif
+ if (!init) {
+ init = 1;
+ inh = GetStdHandle(STD_INPUT_HANDLE);
+ pipe = !GetConsoleMode(inh, &dw);
+ if (!pipe) {
+ SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
+ FlushConsoleInputBuffer(inh);
+ }
+ }
+ if (pipe) {
+ if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
+ return 1;
+ return dw;
+ } else {
+ GetNumberOfConsoleInputEvents(inh, &dw);
+ return dw <= 1 ? 0 : dw;
+ }
+}
+#endif
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(MISC_H_INCLUDED)
+#define MISC_H_INCLUDED
+
+
+////
+//// Includes
+////
+
+#include <string>
+
+
+////
+//// Constants
+////
+
+
+/// Version number. If this is left empty, the current date (in the format
+/// YYMMDD) is used as a version number.
+
+const std::string EngineVersion = "2.1";
+
+
+////
+//// Macros
+////
+
+#define Min(x, y) (((x) < (y))? (x) : (y))
+#define Max(x, y) (((x) < (y))? (y) : (x))
+
+
+////
+//// Prototypes
+////
+
+extern const std::string engine_name();
+extern int get_system_time();
+extern int cpu_count();
+extern int Bioskey();
+
+
+#endif // !defined(MISC_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "move.h"
+#include "piece.h"
+#include "position.h"
+#include "ucioption.h"
+
+
+////
+//// Functions
+////
+
+/// move_from_string() takes a position and a string as input, and attempts to
+/// convert the string to a move, using simple coordinate notation (g1f3,
+/// a7a8q, etc.). In order to correctly parse en passant captures and castling
+/// moves, we need the position. This function is not robust, and expects that
+/// the input move is legal and correctly formatted.
+
+Move move_from_string(const Position &pos, const std::string &str) {
+ Square from, to;
+ Piece piece;
+ Color us = pos.side_to_move();
+
+ if(str.length() < 4) return MOVE_NONE;
+
+ // Read the from and to squares:
+ from = square_from_string(str.substr(0, 2));
+ to = square_from_string(str.substr(2, 4));
+
+ // Find the moving piece:
+ piece = pos.piece_on(from);
+
+ // If the string has more than 4 characters, try to interpret the 5th
+ // character as a promotion:
+ if(type_of_piece(piece) == PAWN && str.length() >= 5) {
+ switch(str[4]) {
+ case 'n': case 'N':
+ return make_promotion_move(from, to, KNIGHT);
+ case 'b': case 'B':
+ return make_promotion_move(from, to, BISHOP);
+ case 'r': case 'R':
+ return make_promotion_move(from, to, ROOK);
+ case 'q': case 'Q':
+ return make_promotion_move(from, to, QUEEN);
+ }
+ }
+
+ if(piece == king_of_color(us)) {
+ // Is this a castling move? A king move is assumed to be a castling
+ // move if the destination square is occupied by a friendly rook, or
+ // if the distance between the source and destination squares is more
+ // than 1.
+ if(pos.piece_on(to) == rook_of_color(us))
+ return make_castle_move(from, to);
+ else if(square_distance(from, to) > 1) {
+ // This is a castling move, but we have to translate it to the
+ // internal "king captures rook" representation.
+ SquareDelta delta = (to > from)? DELTA_E : DELTA_W;
+ Square s;
+ for(s = from + delta;
+ pawn_rank(us, s) == RANK_1 && pos.piece_on(s) != rook_of_color(us);
+ s += delta);
+ if(pawn_rank(us, s) == RANK_1 && pos.piece_on(s) == rook_of_color(us))
+ return make_castle_move(from, s);
+ }
+ }
+ else if(piece == pawn_of_color(us)) {
+ // En passant move? We assume that a pawn move is an en passant move
+ // without further testing if the destination square is epSquare.
+ if(to == pos.ep_square())
+ return make_ep_move(from, to);
+ }
+
+ return make_move(from, to);
+}
+
+
+/// move_to_string() converts a move to a string in coordinate notation
+/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
+/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
+/// Chess960 mode.
+
+const std::string move_to_string(Move move) {
+ std::string str;
+
+ if(move == MOVE_NONE)
+ str = "(none)";
+ else if(move == MOVE_NULL)
+ str = "0000";
+ else {
+ if(!Chess960) {
+ if(move_from(move) == SQ_E1 && move_is_short_castle(move)) {
+ str = "e1g1"; return str;
+ }
+ else if(move_from(move) == SQ_E1 && move_is_long_castle(move)) {
+ str = "e1c1"; return str;
+ }
+ if(move_from(move) == SQ_E8 && move_is_short_castle(move)) {
+ str = "e8g8"; return str;
+ }
+ else if(move_from(move) == SQ_E8 && move_is_long_castle(move)) {
+ str = "e8c8"; return str;
+ }
+ }
+ str = square_to_string(move_from(move)) + square_to_string(move_to(move));
+ if(move_promotion(move))
+ str += piece_type_to_char(move_promotion(move), false);
+ }
+ return str;
+}
+
+
+/// Overload the << operator, to make it easier to print moves.
+
+std::ostream &operator << (std::ostream &os, Move m) {
+ return os << move_to_string(m);
+}
+
+
+/// move_is_ok(), for debugging.
+
+bool move_is_ok(Move m) {
+ return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(MOVE_H_INCLUDED)
+#define MOVE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include <iostream>
+
+#include "misc.h"
+#include "piece.h"
+#include "square.h"
+
+
+////
+//// Types
+////
+
+class Position;
+
+enum Move {
+ MOVE_NONE = 0,
+ MOVE_NULL = 65,
+ MOVE_MAX = 0xFFFFFF
+};
+
+
+struct MoveStack {
+ Move move;
+ int score;
+};
+
+
+////
+//// Inline functions
+////
+
+inline Square move_from(Move m) {
+ return Square((int(m) >> 6) & 077);
+}
+
+inline Square move_to(Move m) {
+ return Square(m & 077);
+}
+
+inline PieceType move_promotion(Move m) {
+ return PieceType((int(m) >> 12) & 7);
+}
+
+inline bool move_is_ep(Move m) {
+ return bool((int(m) >> 15) & 1);
+}
+
+inline bool move_is_castle(Move m) {
+ return bool((int(m) >> 16) & 1);
+}
+
+inline bool move_is_short_castle(Move m) {
+ return move_is_castle(m) && (move_to(m) > move_from(m));
+}
+
+inline bool move_is_long_castle(Move m) {
+ return move_is_castle(m) && (move_to(m) < move_from(m));
+}
+
+inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
+ return Move(int(to) | (int(from) << 6) | (int(promotion) << 12));
+}
+
+inline Move make_move(Square from, Square to) {
+ return Move(int(to) | (int(from) << 6));
+}
+
+inline Move make_castle_move(Square from, Square to) {
+ return Move(int(to) | (int(from) << 6) | (1 << 16));
+}
+
+inline Move make_ep_move(Square from, Square to) {
+ return Move(int(to) | (int(from) << 6) | (1 << 15));
+}
+
+
+////
+//// Prototypes
+////
+
+extern std::ostream &operator << (std::ostream &os, Move m);
+extern Move move_from_string(const Position &pos, const std::string &str);
+extern const std::string move_to_string(Move m);
+extern bool move_is_ok(Move m);
+
+
+#endif // !defined(MOVE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "movegen.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ int generate_white_pawn_captures(const Position &pos, MoveStack *mlist);
+ int generate_black_pawn_captures(const Position &pos, MoveStack *mlist);
+ int generate_white_pawn_noncaptures(const Position &pos, MoveStack *mlist);
+ int generate_black_pawn_noncaptures(const Position &pos, MoveStack *mlist);
+ int generate_knight_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target);
+ int generate_bishop_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target);
+ int generate_rook_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target);
+ int generate_queen_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target);
+ int generate_king_moves(const Position &pos, MoveStack *mlist,
+ Square from, Bitboard target);
+ int generate_castle_moves(const Position &pos, MoveStack *mlist, Color us);
+
+}
+
+
+////
+//// Functions
+////
+
+
+/// generate_captures generates() all pseudo-legal captures and queen
+/// promotions. The return value is the number of moves generated.
+
+int generate_captures(const Position &pos, MoveStack *mlist) {
+ Color us = pos.side_to_move();
+ Bitboard target = pos.pieces_of_color(opposite_color(us));
+ int n = 0;
+
+ assert(pos.is_ok());
+ assert(!pos.is_check());
+
+ if(us == WHITE)
+ n += generate_white_pawn_captures(pos, mlist);
+ else
+ n += generate_black_pawn_captures(pos, mlist);
+ n += generate_knight_moves(pos, mlist+n, us, target);
+ n += generate_bishop_moves(pos, mlist+n, us, target);
+ n += generate_rook_moves(pos, mlist+n, us, target);
+ n += generate_queen_moves(pos, mlist+n, us, target);
+ n += generate_king_moves(pos, mlist+n, pos.king_square(us), target);
+
+ return n;
+}
+
+
+/// generate_noncaptures() generates all pseudo-legal non-captures and
+/// underpromotions. The return value is the number of moves generated.
+
+int generate_noncaptures(const Position &pos, MoveStack *mlist) {
+ Color us = pos.side_to_move();
+ Bitboard target = pos.empty_squares();
+ int n = 0;
+
+ assert(pos.is_ok());
+ assert(!pos.is_check());
+
+ if(us == WHITE)
+ n += generate_white_pawn_noncaptures(pos, mlist);
+ else
+ n += generate_black_pawn_noncaptures(pos, mlist);
+ n += generate_knight_moves(pos, mlist+n, us, target);
+ n += generate_bishop_moves(pos, mlist+n, us, target);
+ n += generate_rook_moves(pos, mlist+n, us, target);
+ n += generate_queen_moves(pos, mlist+n, us, target);
+ n += generate_king_moves(pos, mlist+n, pos.king_square(us), target);
+ n += generate_castle_moves(pos, mlist+n, us);
+
+ return n;
+}
+
+
+/// generate_checks() generates all pseudo-legal non-capturing, non-promoting
+/// checks, except castling moves (will add this later). It returns the
+/// number of generated moves.
+
+int generate_checks(const Position &pos, MoveStack *mlist, Bitboard dc) {
+ Color us, them;
+ Square ksq, from, to;
+ Bitboard empty, checkSqs, b1, b2, b3;
+ int n = 0;
+
+ assert(pos.is_ok());
+ assert(!pos.is_check());
+
+ us = pos.side_to_move();
+ them = opposite_color(us);
+
+ ksq = pos.king_square(them);
+ assert(pos.piece_on(ksq) == king_of_color(them));
+
+ dc = pos.discovered_check_candidates(us);
+ empty = pos.empty_squares();
+
+ // Pawn moves. This is somewhat messy, and we use separate code for white
+ // and black, because we can't shift by negative numbers in C/C++. :-(
+
+ if(us == WHITE) {
+ // Pawn moves which give discovered check. This is possible only if the
+ // pawn is not on the same file as the enemy king, because we don't
+ // generate captures.
+
+ // Find all friendly pawns not on the enemy king's file:
+ b1 = pos.pawns(us) & ~file_bb(ksq);
+
+ // Discovered checks, single pawn pushes:
+ b2 = b3 = ((b1 & dc) << 8) & ~Rank8BB & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_N, to);
+ }
+
+ // Discovered checks, double pawn pushes:
+ b3 = ((b2 & Rank3BB) << 8) & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
+ }
+
+ // Direct checks. These are possible only for pawns on neighboring files
+ // of the enemy king:
+
+ b1 &= (~dc & neighboring_files_bb(ksq));
+
+ // Direct checks, single pawn pushes:
+ b2 = (b1 << 8) & empty;
+ b3 = b2 & pos.black_pawn_attacks(ksq);
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_N, to);
+ }
+
+ // Direct checks, double pawn pushes:
+ b3 = ((b2 & Rank3BB) << 8) & empty & pos.black_pawn_attacks(ksq);
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
+ }
+ }
+ else { // (us == BLACK)
+
+ // Pawn moves which give discovered check. This is possible only if the
+ // pawn is not on the same file as the enemy king, because we don't
+ // generate captures.
+
+ // Find all friendly pawns not on the enemy king's file:
+ b1 = pos.pawns(us) & ~file_bb(ksq);
+
+ // Discovered checks, single pawn pushes:
+ b2 = b3 = ((b1 & dc) >> 8) & ~Rank1BB & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_S, to);
+ }
+
+ // Discovered checks, double pawn pushes:
+ b3 = ((b2 & Rank6BB) >> 8) & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
+ }
+
+ // Direct checks. These are possible only for pawns on neighboring files
+ // of the enemy king:
+
+ b1 &= (~dc & neighboring_files_bb(ksq));
+
+ // Direct checks, single pawn pushes:
+ b2 = (b1 >> 8) & empty;
+ b3 = b2 & pos.white_pawn_attacks(ksq);
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_S, to);
+ }
+
+ // Direct checks, double pawn pushes:
+ b3 = ((b2 & Rank6BB) >> 8) & empty & pos.black_pawn_attacks(ksq);
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
+ }
+ }
+
+ // Knight moves
+ b1 = pos.knights(us);
+ if(b1) {
+ // Discovered knight checks:
+ b2 = b1 & dc;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.knight_attacks(from) & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Direct knight checks:
+ b2 = b1 & ~dc;
+ checkSqs = pos.knight_attacks(ksq) & empty;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.knight_attacks(from) & checkSqs;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ }
+
+ // Bishop moves
+ b1 = pos.bishops(us);
+ if(b1) {
+ // Discovered bishop checks:
+ b2 = b1 & dc;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.bishop_attacks(from) & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Direct bishop checks:
+ b2 = b1 & ~dc;
+ checkSqs = pos.bishop_attacks(ksq) & empty;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.bishop_attacks(from) & checkSqs;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ }
+
+ // Rook moves
+ b1 = pos.rooks(us);
+ if(b1) {
+ // Discovered rook checks:
+ b2 = b1 & dc;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.rook_attacks(from) & empty;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Direct rook checks:
+ b2 = b1 & ~dc;
+ checkSqs = pos.rook_attacks(ksq) & empty;
+ while(b2) {
+ from = pop_1st_bit(&b2);
+ b3 = pos.rook_attacks(from) & checkSqs;
+ while(b3) {
+ to = pop_1st_bit(&b3);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ }
+
+ // Queen moves
+ b1 = pos.queens(us);
+ if(b1) {
+ // Discovered queen checks are impossible!
+
+ // Direct queen checks:
+ checkSqs = pos.queen_attacks(ksq) & empty;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ b2 = pos.queen_attacks(from) & checkSqs;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ }
+
+ // King moves
+ from = pos.king_square(us);
+ if(bit_is_set(dc, from)) {
+ b1 = pos.king_attacks(from) & empty & ~QueenPseudoAttacks[ksq];
+ while(b1) {
+ to = pop_1st_bit(&b1);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // TODO: Castling moves!
+
+ return n;
+}
+
+
+/// generate_evasions() generates all check evasions when the side to move is
+/// in check. Unlike the other move generation functions, this one generates
+/// only legal moves. It returns the number of generated moves. This
+/// function is very ugly, and needs cleaning up some time later. FIXME
+
+int generate_evasions(const Position &pos, MoveStack *mlist) {
+ Color us, them;
+ Bitboard checkers = pos.checkers();
+ Bitboard pinned, b1, b2;
+ Square ksq, from, to;
+ int n = 0;
+
+ assert(pos.is_ok());
+ assert(pos.is_check());
+
+ us = pos.side_to_move();
+ them = opposite_color(us);
+
+ ksq = pos.king_square(us);
+ assert(pos.piece_on(ksq) == king_of_color(us));
+
+ // Generate evasions for king:
+ b1 = pos.king_attacks(ksq) & ~pos.pieces_of_color(us);
+ b2 = pos.occupied_squares();
+ clear_bit(&b2, ksq);
+ while(b1) {
+ to = pop_1st_bit(&b1);
+
+ // Make sure to is not attacked by the other side. This is a bit ugly,
+ // because we can't use Position::square_is_attacked. Instead we use
+ // the low-level bishop_attacks_bb and rook_attacks_bb with the bitboard
+ // b2 (the occupied squares with the king removed) in order to test whether
+ // the king will remain in check on the destination square.
+ if(((pos.pawn_attacks(us, to) & pos.pawns(them)) == EmptyBoardBB) &&
+ ((pos.knight_attacks(to) & pos.knights(them)) == EmptyBoardBB) &&
+ ((pos.king_attacks(to) & pos.kings(them)) == EmptyBoardBB) &&
+ ((bishop_attacks_bb(to, b2) & pos.bishops_and_queens(them))
+ == EmptyBoardBB) &&
+ ((rook_attacks_bb(to, b2) & pos.rooks_and_queens(them)) == EmptyBoardBB))
+ mlist[n++].move = make_move(ksq, to);
+ }
+
+
+ // Generate evasions for other pieces only if not double check. We use a
+ // simple bit twiddling hack here rather than calling count_1s in order to
+ // save some time (we know that pos.checkers() has at most two nonzero bits).
+ if(!(checkers & (checkers - 1))) {
+ Square checksq = first_1(checkers);
+ assert(pos.color_of_piece_on(checksq) == them);
+
+ // Find pinned pieces:
+ pinned = pos.pinned_pieces(us);
+
+ // Generate captures of the checking piece:
+
+ // Pawn captures:
+ b1 = pos.pawn_attacks(them, checksq) & pos.pawns(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ if(pawn_rank(us, checksq) == RANK_8) {
+ mlist[n++].move = make_promotion_move(from, checksq, QUEEN);
+ mlist[n++].move = make_promotion_move(from, checksq, ROOK);
+ mlist[n++].move = make_promotion_move(from, checksq, BISHOP);
+ mlist[n++].move = make_promotion_move(from, checksq, KNIGHT);
+ }
+ else
+ mlist[n++].move = make_move(from, checksq);
+ }
+
+ // Knight captures:
+ b1 = pos.knight_attacks(checksq) & pos.knights(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ mlist[n++].move = make_move(from, checksq);
+ }
+
+ // Bishop and queen captures:
+ b1 = pos.bishop_attacks(checksq) & pos.bishops_and_queens(us)
+ & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ mlist[n++].move = make_move(from, checksq);
+ }
+
+ // Rook and queen captures:
+ b1 = pos.rook_attacks(checksq) & pos.rooks_and_queens(us)
+ & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ mlist[n++].move = make_move(from, checksq);
+ }
+
+ // Blocking check evasions are possible only if the checking piece is
+ // a slider:
+ if(checkers & pos.sliders()) {
+ Bitboard blockSquares = squares_between(checksq, ksq);
+ assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB);
+
+ // Pawn moves. Because a blocking evasion can never be a capture, we
+ // only generate pawn pushes. As so often, the code for pawns is a bit
+ // ugly, and uses separate clauses for white and black pawns. :-(
+ if(us == WHITE) {
+ // Find non-pinned pawns:
+ b1 = pos.pawns(WHITE) & ~pinned;
+
+ // Single pawn pushes. We don't have to AND with empty squares here,
+ // because the blocking squares will always be empty.
+ b2 = (b1 << 8) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ assert(pos.piece_on(to) == EMPTY);
+ if(square_rank(to) == RANK_8) {
+ mlist[n++].move = make_promotion_move(to - DELTA_N, to, QUEEN);
+ mlist[n++].move = make_promotion_move(to - DELTA_N, to, ROOK);
+ mlist[n++].move = make_promotion_move(to - DELTA_N, to, BISHOP);
+ mlist[n++].move = make_promotion_move(to - DELTA_N, to, KNIGHT);
+ }
+ else
+ mlist[n++].move = make_move(to - DELTA_N, to);
+ }
+ // Double pawn pushes.
+ b2 = (((b1 << 8) & pos.empty_squares() & Rank3BB) << 8) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ assert(pos.piece_on(to) == EMPTY);
+ assert(square_rank(to) == RANK_4);
+ mlist[n++].move = make_move(to - DELTA_N - DELTA_N, to);
+ }
+ }
+ else { // (us == BLACK)
+ // Find non-pinned pawns:
+ b1 = pos.pawns(BLACK) & ~pinned;
+
+ // Single pawn pushes. We don't have to AND with empty squares here,
+ // because the blocking squares will always be empty.
+ b2 = (b1 >> 8) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ assert(pos.piece_on(to) == EMPTY);
+ if(square_rank(to) == RANK_1) {
+ mlist[n++].move = make_promotion_move(to - DELTA_S, to, QUEEN);
+ mlist[n++].move = make_promotion_move(to - DELTA_S, to, ROOK);
+ mlist[n++].move = make_promotion_move(to - DELTA_S, to, BISHOP);
+ mlist[n++].move = make_promotion_move(to - DELTA_S, to, KNIGHT);
+ }
+ else
+ mlist[n++].move = make_move(to - DELTA_S, to);
+ }
+ // Double pawn pushes.
+ b2 = (((b1 >> 8) & pos.empty_squares() & Rank6BB) >> 8) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ assert(pos.piece_on(to) == EMPTY);
+ assert(square_rank(to) == RANK_5);
+ mlist[n++].move = make_move(to - DELTA_S - DELTA_S, to);
+ }
+ }
+
+ // Knight moves
+ b1 = pos.knights(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ b2 = pos.knight_attacks(from) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Bishop moves
+ b1 = pos.bishops(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ b2 = pos.bishop_attacks(from) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Rook moves
+ b1 = pos.rooks(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ b2 = pos.rook_attacks(from) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+
+ // Queen moves
+ b1 = pos.queens(us) & ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+ b2 = pos.queen_attacks(from) & blockSquares;
+ while(b2) {
+ to = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ }
+
+ // Finally, the ugly special case of en passant captures. An en passant
+ // capture can only be a check evasion if the check is not a discovered
+ // check. If pos.ep_square() is set, the last move made must have been
+ // a double pawn push. If, furthermore, the checking piece is a pawn,
+ // an en passant check evasion may be possible.
+ if(pos.ep_square() != SQ_NONE && (checkers & pos.pawns(them))) {
+ to = pos.ep_square();
+ b1 = pos.pawn_attacks(them, to) & pos.pawns(us);
+ assert(b1 != EmptyBoardBB);
+ b1 &= ~pinned;
+ while(b1) {
+ from = pop_1st_bit(&b1);
+
+ // Before generating the move, we have to make sure it is legal.
+ // This is somewhat tricky, because the two disappearing pawns may
+ // cause new "discovered checks". We test this by removing the
+ // two relevant bits from the occupied squares bitboard, and using
+ // the low-level bitboard functions for bishop and rook attacks.
+ b2 = pos.occupied_squares();
+ clear_bit(&b2, from);
+ clear_bit(&b2, checksq);
+ if(((bishop_attacks_bb(ksq, b2) & pos.bishops_and_queens(them))
+ == EmptyBoardBB) &&
+ ((rook_attacks_bb(ksq, b2) & pos.rooks_and_queens(them))
+ == EmptyBoardBB))
+ mlist[n++].move = make_ep_move(from, to);
+ }
+ }
+ }
+
+ return n;
+}
+
+
+/// generate_legal_moves() computes a complete list of legal moves in the
+/// current position. This function is not very fast, and should be used
+/// only in situations where performance is unimportant. It wouldn't be
+/// very hard to write an efficient legal move generator, but for the moment
+/// we don't need it.
+
+int generate_legal_moves(const Position &pos, MoveStack *mlist) {
+ assert(pos.is_ok());
+
+ if(pos.is_check())
+ return generate_evasions(pos, mlist);
+ else {
+ int i, n;
+ Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
+
+ // Generate pseudo-legal moves:
+ n = generate_captures(pos, mlist);
+ n += generate_noncaptures(pos, mlist + n);
+
+ // Remove illegal moves from the list:
+ for(i = 0; i < n; i++) {
+ if(!pos.move_is_legal(mlist[i].move, pinned))
+ mlist[i--].move = mlist[--n].move;
+ }
+
+ return n;
+ }
+}
+
+
+/// generate_move_if_legal() takes a position and a (not necessarily
+/// pseudo-legal) move and a pinned pieces bitboard as input, and tests
+/// whether the move is legal. If the move is legal, the move itself is
+/// returned. If not, the function returns MOVE_NONE. This function must
+/// only be used when the side to move is not in check.
+
+Move generate_move_if_legal(const Position &pos, Move m, Bitboard pinned) {
+ Color us, them;
+ Square from, to;
+ Piece pc;
+
+ assert(pos.is_ok());
+ assert(!pos.is_check());
+ assert(move_is_ok(m));
+
+ us = pos.side_to_move();
+ them = opposite_color(us);
+ from = move_from(m);
+ pc = pos.piece_on(from);
+
+ // If the from square is not occupied by a piece belonging to the side to
+ // move, the move is obviously not legal.
+ if(color_of_piece(pc) != us )
+ return MOVE_NONE;
+
+ to = move_to(m);
+
+ // En passant moves:
+ if(move_is_ep(m)) {
+
+ // The piece must be a pawn:
+ if(type_of_piece(pc) != PAWN)
+ return MOVE_NONE;
+
+ // The destination square must be the en passant square:
+ if(to != pos.ep_square())
+ return MOVE_NONE;
+
+ assert(pos.square_is_empty(to));
+ assert(pos.piece_on(to - pawn_push(us)) == pawn_of_color(them));
+
+ // The move is pseudo-legal. If it is legal, return it.
+ if(pos.move_is_legal(m))
+ return m;
+ else
+ return MOVE_NONE;
+ }
+
+ // Castling moves:
+ else if(move_is_short_castle(m)) {
+
+ // The piece must be a king:
+ if(type_of_piece(pc) != KING)
+ return MOVE_NONE;
+
+ // The side to move must still have the right to castle kingside:
+ if(!pos.can_castle_kingside(us))
+ return MOVE_NONE;
+
+ assert(from == pos.king_square(us));
+ assert(to == pos.initial_kr_square(us));
+ assert(pos.piece_on(to) == rook_of_color(us));
+
+ Square g1 = relative_square(us, SQ_G1);
+ Square f1 = relative_square(us, SQ_F1);
+ Square s;
+ bool illegal = false;
+
+ for(s = Min(from, g1); s <= Max(from, g1); s++)
+ if((s != from && s != to && !pos.square_is_empty(s)) ||
+ pos.square_is_attacked(s, them))
+ illegal = true;
+ for(s = Min(to, f1); s <= Max(to, f1); s++)
+ if(s != from && s != to && !pos.square_is_empty(s))
+ illegal = true;
+
+ if(!illegal)
+ return m;
+ else
+ return MOVE_NONE;
+ }
+ else if(move_is_long_castle(m)) {
+
+ // The piece must be a king:
+ if(type_of_piece(pc) != KING)
+ return MOVE_NONE;
+
+ // The side to move must still have the right to castle kingside:
+ if(!pos.can_castle_queenside(us))
+ return MOVE_NONE;
+
+ assert(from == pos.king_square(us));
+ assert(to == pos.initial_qr_square(us));
+ assert(pos.piece_on(to) == rook_of_color(us));
+
+ Square c1 = relative_square(us, SQ_C1);
+ Square d1 = relative_square(us, SQ_D1);
+ Square s;
+ bool illegal = false;
+
+ for(s = Min(from, c1); s <= Max(from, c1); s++)
+ if((s != from && s != to && !pos.square_is_empty(s)) ||
+ pos.square_is_attacked(s, them))
+ illegal = true;
+ for(s = Min(to, d1); s <= Max(to, d1); s++)
+ if(s != from && s != to && !pos.square_is_empty(s))
+ illegal = true;
+ if(square_file(to) == FILE_B &&
+ (pos.piece_on(to + DELTA_W) == rook_of_color(them) ||
+ pos.piece_on(to + DELTA_W) == queen_of_color(them)))
+ illegal = true;
+
+ if(!illegal)
+ return m;
+ else
+ return MOVE_NONE;
+ }
+
+ // Normal moves
+ else {
+
+ // The destination square cannot be occupied by a friendly piece:
+ if(pos.color_of_piece_on(to) == us)
+ return MOVE_NONE;
+
+ // Proceed according to the type of the moving piece.
+ switch(type_of_piece(pc)) {
+
+ case PAWN:
+ // Pawn moves, as usual, are somewhat messy.
+ if(us == WHITE) {
+ // If the destination square is on the 8th rank, the move must be a
+ // promotion.
+ if(square_rank(to) == RANK_8 && !move_promotion(m))
+ return MOVE_NONE;
+
+ // Proceed according to the square delta between the source and
+ // destionation squares.
+ switch(to - from) {
+
+ case DELTA_NW: case DELTA_NE:
+ // Capture. The destination square must be occupied by an enemy piece
+ // (en passant captures was handled earlier).
+ if(pos.color_of_piece_on(to) != them)
+ return MOVE_NONE;
+ break;
+
+ case DELTA_N:
+ // Pawn push. The destination square must be empty.
+ if(!pos.square_is_empty(to))
+ return MOVE_NONE;
+ break;
+
+ case DELTA_NN:
+ // Double pawn push. The destination square must be on the fourth
+ // rank, and both the destination square and the square between the
+ // source and destination squares must be empty.
+ if(square_rank(to) != RANK_4 || !pos.square_is_empty(to) ||
+ !pos.square_is_empty(from + DELTA_N))
+ return MOVE_NONE;
+ break;
+
+ default:
+ return MOVE_NONE;
+ }
+ }
+ else { // (us == BLACK)
+ // If the destination square is on the 1st rank, the move must be a
+ // promotion.
+ if(square_rank(to) == RANK_1 && !move_promotion(m))
+ return MOVE_NONE;
+
+ // Proceed according to the square delta between the source and
+ // destionation squares.
+ switch(to - from) {
+
+ case DELTA_SW: case DELTA_SE:
+ // Capture. The destination square must be occupied by an enemy piece
+ // (en passant captures was handled earlier).
+ if(pos.color_of_piece_on(to) != them)
+ return MOVE_NONE;
+ break;
+
+ case DELTA_S:
+ // Pawn push. The destination square must be empty.
+ if(!pos.square_is_empty(to))
+ return MOVE_NONE;
+ break;
+
+ case DELTA_SS:
+ // Double pawn push. The destination square must be on the fifth
+ // rank, and both the destination square and the square between the
+ // source and destination squares must be empty.
+ if(square_rank(to) != RANK_5 || !pos.square_is_empty(to) ||
+ !pos.square_is_empty(from + DELTA_S))
+ return MOVE_NONE;
+ break;
+
+ default:
+ return MOVE_NONE;
+ }
+ }
+ // The move is pseudo-legal. Return it if it is legal.
+ if(pos.move_is_legal(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ case KNIGHT:
+ if(pos.knight_attacks_square(from, to) && pos.move_is_legal(m) &&
+ !move_promotion(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ case BISHOP:
+ if(pos.bishop_attacks_square(from, to) && pos.move_is_legal(m) &&
+ !move_promotion(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ case ROOK:
+ if(pos.rook_attacks_square(from, to) && pos.move_is_legal(m) &&
+ !move_promotion(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ case QUEEN:
+ if(pos.queen_attacks_square(from, to) && pos.move_is_legal(m) &&
+ !move_promotion(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ case KING:
+ if(pos.king_attacks_square(from, to) && pos.move_is_legal(m) &&
+ !move_promotion(m))
+ return m;
+ else
+ return MOVE_NONE;
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ assert(false);
+ return MOVE_NONE;
+}
+
+
+namespace {
+
+ int generate_white_pawn_captures(const Position &pos, MoveStack *mlist) {
+ Bitboard pawns = pos.pawns(WHITE);
+ Bitboard enemyPieces = pos.pieces_of_color(BLACK);
+ Bitboard b1, b2;
+ Square sq;
+ int n = 0;
+
+ // Captures in the a1-h8 direction:
+ b1 = (pawns << 9) & ~FileABB & enemyPieces;
+
+ // Promotions:
+ b2 = b1 & Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, QUEEN);
+ }
+
+ // Non-promotions:
+ b2 = b1 & ~Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_NE, sq);
+ }
+
+ // Captures in the h1-a8 direction:
+ b1 = (pawns << 7) & ~FileHBB & enemyPieces;
+
+ // Promotions:
+ b2 = b1 & Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, QUEEN);
+ }
+
+ // Non-promotions:
+ b2 = b1 & ~Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_NW, sq);
+ }
+
+ // Non-capturing promotions:
+ b1 = (pawns << 8) & pos.empty_squares() & Rank8BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, QUEEN);
+ }
+
+ // En passant captures:
+ if(pos.ep_square() != SQ_NONE) {
+ assert(square_rank(pos.ep_square()) == RANK_6);
+ b1 = pawns & pos.black_pawn_attacks(pos.ep_square());
+ assert(b1 != EmptyBoardBB);
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_ep_move(sq, pos.ep_square());
+ }
+ }
+
+ return n;
+ }
+
+
+ int generate_black_pawn_captures(const Position &pos, MoveStack *mlist) {
+ Bitboard pawns = pos.pawns(BLACK);
+ Bitboard enemyPieces = pos.pieces_of_color(WHITE);
+ Bitboard b1, b2;
+ Square sq;
+ int n = 0;
+
+ // Captures in the a8-h1 direction:
+ b1 = (pawns >> 7) & ~FileABB & enemyPieces;
+
+ // Promotions:
+ b2 = b1 & Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, QUEEN);
+ }
+
+ // Non-promotions:
+ b2 = b1 & ~Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_SE, sq);
+ }
+
+ // Captures in the h8-a1 direction:
+ b1 = (pawns >> 9) & ~FileHBB & enemyPieces;
+
+ // Promotions:
+ b2 = b1 & Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, QUEEN);
+ }
+
+ // Non-promotions:
+ b2 = b1 & ~Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_SW, sq);
+ }
+
+ // Non-capturing promotions:
+ b1 = (pawns >> 8) & pos.empty_squares() & Rank1BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, QUEEN);
+ }
+
+ // En passant captures:
+ if(pos.ep_square() != SQ_NONE) {
+ assert(square_rank(pos.ep_square()) == RANK_3);
+ b1 = pawns & pos.white_pawn_attacks(pos.ep_square());
+ assert(b1 != EmptyBoardBB);
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_ep_move(sq, pos.ep_square());
+ }
+ }
+
+ return n;
+ }
+
+
+ int generate_white_pawn_noncaptures(const Position &pos, MoveStack *mlist) {
+ Bitboard pawns = pos.pawns(WHITE);
+ Bitboard enemyPieces = pos.pieces_of_color(BLACK);
+ Bitboard emptySquares = pos.empty_squares();
+ Bitboard b1, b2;
+ Square sq;
+ int n = 0;
+
+ // Underpromotion captures in the a1-h8 direction:
+ b1 = (pawns << 9) & ~FileABB & enemyPieces & Rank8BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NE, sq, KNIGHT);
+ }
+
+ // Underpromotion captures in the h1-a8 direction:
+ b1 = (pawns << 7) & ~FileHBB & enemyPieces & Rank8BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_NW, sq, KNIGHT);
+ }
+
+ // Single pawn pushes:
+ b1 = (pawns << 8) & emptySquares;
+ b2 = b1 & Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_N, sq, KNIGHT);
+ }
+ b2 = b1 & ~Rank8BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_N, sq);
+ }
+
+ // Double pawn pushes:
+ b2 = ((b1 & Rank3BB) << 8) & emptySquares;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_N - DELTA_N, sq);
+ }
+
+ return n;
+ }
+
+
+ int generate_black_pawn_noncaptures(const Position &pos, MoveStack *mlist) {
+ Bitboard pawns = pos.pawns(BLACK);
+ Bitboard enemyPieces = pos.pieces_of_color(WHITE);
+ Bitboard emptySquares = pos.empty_squares();
+ Bitboard b1, b2;
+ Square sq;
+ int n = 0;
+
+ // Underpromotion captures in the a8-h1 direction:
+ b1 = (pawns >> 7) & ~FileABB & enemyPieces & Rank1BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SE, sq, KNIGHT);
+ }
+
+ // Underpromotion captures in the h8-a1 direction:
+ b1 = (pawns >> 9) & ~FileHBB & enemyPieces & Rank1BB;
+ while(b1) {
+ sq = pop_1st_bit(&b1);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_SW, sq, KNIGHT);
+ }
+
+ // Single pawn pushes:
+ b1 = (pawns >> 8) & emptySquares;
+ b2 = b1 & Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, ROOK);
+ mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, BISHOP);
+ mlist[n++].move = make_promotion_move(sq - DELTA_S, sq, KNIGHT);
+ }
+ b2 = b1 & ~Rank1BB;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_S, sq);
+ }
+
+ // Double pawn pushes:
+ b2 = ((b1 & Rank6BB) >> 8) & emptySquares;
+ while(b2) {
+ sq = pop_1st_bit(&b2);
+ mlist[n++].move = make_move(sq - DELTA_S - DELTA_S, sq);
+ }
+
+ return n;
+ }
+
+
+ int generate_knight_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target) {
+ Square from, to;
+ Bitboard b;
+ int i, n = 0;
+
+ for(i = 0; i < pos.knight_count(side); i++) {
+ from = pos.knight_list(side, i);
+ b = pos.knight_attacks(from) & target;
+ while(b) {
+ to = pop_1st_bit(&b);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ return n;
+ }
+
+
+ int generate_bishop_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target) {
+ Square from, to;
+ Bitboard b;
+ int i, n = 0;
+
+ for(i = 0; i < pos.bishop_count(side); i++) {
+ from = pos.bishop_list(side, i);
+ b = pos.bishop_attacks(from) & target;
+ while(b) {
+ to = pop_1st_bit(&b);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ return n;
+ }
+
+
+ int generate_rook_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target) {
+ Square from, to;
+ Bitboard b;
+ int i, n = 0;
+
+ for(i = 0; i < pos.rook_count(side); i++) {
+ from = pos.rook_list(side, i);
+ b = pos.rook_attacks(from) & target;
+ while(b) {
+ to = pop_1st_bit(&b);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ return n;
+ }
+
+
+ int generate_queen_moves(const Position &pos, MoveStack *mlist,
+ Color side, Bitboard target) {
+ Square from, to;
+ Bitboard b;
+ int i, n = 0;
+
+ for(i = 0; i < pos.queen_count(side); i++) {
+ from = pos.queen_list(side, i);
+ b = pos.queen_attacks(from) & target;
+ while(b) {
+ to = pop_1st_bit(&b);
+ mlist[n++].move = make_move(from, to);
+ }
+ }
+ return n;
+ }
+
+
+ int generate_king_moves(const Position &pos, MoveStack *mlist,
+ Square from, Bitboard target) {
+ Square to;
+ Bitboard b;
+ int n = 0;
+
+ b = pos.king_attacks(from) & target;
+ while(b) {
+ to = pop_1st_bit(&b);
+ mlist[n++].move = make_move(from, to);
+ }
+ return n;
+ }
+
+
+ int generate_castle_moves(const Position &pos, MoveStack *mlist, Color us) {
+ int n = 0;
+
+ if(pos.can_castle(us)) {
+ Color them = opposite_color(us);
+ Square ksq = pos.king_square(us);
+ assert(pos.piece_on(ksq) == king_of_color(us));
+
+ if(pos.can_castle_kingside(us)) {
+ Square rsq = pos.initial_kr_square(us);
+ Square g1 = relative_square(us, SQ_G1);
+ Square f1 = relative_square(us, SQ_F1);
+ Square s;
+ bool illegal = false;
+
+ assert(pos.piece_on(rsq) == rook_of_color(us));
+
+ for(s = Min(ksq, g1); s <= Max(ksq, g1); s++)
+ if((s != ksq && s != rsq && pos.square_is_occupied(s))
+ || pos.square_is_attacked(s, them))
+ illegal = true;
+ for(s = Min(rsq, f1); s <= Max(rsq, f1); s++)
+ if(s != ksq && s != rsq && pos.square_is_occupied(s))
+ illegal = true;
+
+ if(!illegal)
+ mlist[n++].move = make_castle_move(ksq, rsq);
+ }
+
+ if(pos.can_castle_queenside(us)) {
+ Square rsq = pos.initial_qr_square(us);
+ Square c1 = relative_square(us, SQ_C1);
+ Square d1 = relative_square(us, SQ_D1);
+ Square s;
+ bool illegal = false;
+
+ assert(pos.piece_on(rsq) == rook_of_color(us));
+
+ for(s = Min(ksq, c1); s <= Max(ksq, c1); s++)
+ if((s != ksq && s != rsq && pos.square_is_occupied(s))
+ || pos.square_is_attacked(s, them))
+ illegal = true;
+ for(s = Min(rsq, d1); s <= Max(rsq, d1); s++)
+ if(s != ksq && s != rsq && pos.square_is_occupied(s))
+ illegal = true;
+ if(square_file(rsq) == FILE_B &&
+ (pos.piece_on(relative_square(us, SQ_A1)) == rook_of_color(them) ||
+ pos.piece_on(relative_square(us, SQ_A1)) == queen_of_color(them)))
+ illegal = true;
+
+ if(!illegal)
+ mlist[n++].move = make_castle_move(ksq, rsq);
+ }
+ }
+
+ return n;
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(MOVEGEN_H_INCLUDED)
+#define MOVEGEN_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "position.h"
+
+
+////
+//// Prototypes
+////
+
+extern int generate_captures(const Position &pos, MoveStack *mlist);
+extern int generate_noncaptures(const Position &pos, MoveStack *mlist);
+extern int generate_checks(const Position &pos, MoveStack *mlist, Bitboard dc);
+extern int generate_evasions(const Position &pos, MoveStack *mlist);
+extern int generate_legal_moves(const Position &pos, MoveStack *mlist);
+extern Move generate_move_if_legal(const Position &pos, Move m,
+ Bitboard pinned);
+
+#endif // !defined(MOVEGEN_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "history.h"
+#include "movegen.h"
+#include "movepick.h"
+#include "search.h"
+#include "value.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ /// Types
+
+ enum MovegenPhase {
+ PH_TT_MOVE, // Transposition table move
+ PH_MATE_KILLER, // Mate killer from the current ply
+ PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
+ PH_BAD_CAPTURES, // Queen promotions and captures with SEE valuse <= 0
+ PH_KILLER_1, // Killer move 1 from the current ply (not used yet).
+ PH_KILLER_2, // Killer move 2 from the current ply (not used yet).
+ PH_NONCAPTURES, // Non-captures and underpromotions
+ PH_EVASIONS, // Check evasions
+ PH_QCAPTURES, // Captures in quiescence search
+ PH_QCHECKS, // Checks in quiescence search
+ PH_STOP
+ };
+
+
+ /// Variables
+
+ MovegenPhase PhaseTable[32];
+ int MainSearchPhaseIndex;
+ int EvasionsPhaseIndex;
+ int QsearchWithChecksPhaseIndex;
+ int QsearchWithoutChecksPhaseIndex;
+
+}
+
+
+////
+//// Functions
+////
+
+
+/// Constructor for the MovePicker class. Apart from the position for which
+/// it is asked to pick legal moves, MovePicker also wants some information
+/// to help it to return the presumably good moves first, to decide which
+/// moves to return (in the quiescence search, for instance, we only want to
+/// search captures, promotions and some checks) and about how important good
+/// move ordering is at the current node.
+
+MovePicker::MovePicker(Position &p, bool pvnode, Move ttm, Move mk,
+ Move k1, Move k2, Depth dpth) {
+ pos = &p;
+ pvNode = pvnode;
+ ttMove = ttm;
+ mateKiller = (mk == ttm)? MOVE_NONE : mk;
+ killer1 = k1;
+ killer2 = k2;
+ depth = dpth;
+ movesPicked = 0;
+ numOfMoves = 0;
+ numOfBadCaptures = 0;
+ dc = p.discovered_check_candidates(p.side_to_move());
+
+ if(p.is_check())
+ phaseIndex = EvasionsPhaseIndex;
+ else if(depth > Depth(0))
+ phaseIndex = MainSearchPhaseIndex;
+ else if(depth == Depth(0))
+ phaseIndex = QsearchWithChecksPhaseIndex;
+ else
+ phaseIndex = QsearchWithoutChecksPhaseIndex;
+
+ pinned = p.pinned_pieces(p.side_to_move());
+
+ finished = false;
+}
+
+
+/// MovePicker::get_next_move() is the most important method of the MovePicker
+/// class. It returns a new legal move every time it is called, until there
+/// are no more moves left of the types we are interested in.
+
+Move MovePicker::get_next_move() {
+ Move move;
+
+ while(true) {
+ // If we already have a list of generated moves, pick the best move from
+ // the list, and return it:
+ move = this->pick_move_from_list();
+ if(move != MOVE_NONE) {
+ assert(move_is_ok(move));
+ return move;
+ }
+
+ // Next phase:
+ phaseIndex++;
+ switch(PhaseTable[phaseIndex]) {
+
+ case PH_TT_MOVE:
+ if(ttMove != MOVE_NONE) {
+ assert(move_is_ok(ttMove));
+ Move m = generate_move_if_legal(*pos, ttMove, pinned);
+ if(m != MOVE_NONE) {
+ assert(m == ttMove);
+ return m;
+ }
+ }
+ break;
+
+ case PH_MATE_KILLER:
+ if(mateKiller != MOVE_NONE) {
+ assert(move_is_ok(mateKiller));
+ Move m = generate_move_if_legal(*pos, mateKiller, pinned);
+ if(m != MOVE_NONE) {
+ assert(m == mateKiller);
+ return m;
+ }
+ }
+ break;
+
+ case PH_GOOD_CAPTURES:
+ // pinned = pos->pinned_pieces(pos->side_to_move());
+ numOfMoves = generate_captures(*pos, moves);
+ this->score_captures();
+ movesPicked = 0;
+ break;
+
+ case PH_BAD_CAPTURES:
+ badCapturesPicked = 0;
+ break;
+
+ case PH_NONCAPTURES:
+ numOfMoves = generate_noncaptures(*pos, moves);
+ this->score_noncaptures();
+ movesPicked = 0;
+ break;
+
+ case PH_EVASIONS:
+ assert(pos->is_check());
+ // pinned = pos->pinned_pieces(pos->side_to_move());
+ numOfMoves = generate_evasions(*pos, moves);
+ this->score_evasions();
+ movesPicked = 0;
+ break;
+
+ case PH_QCAPTURES:
+ // pinned = pos->pinned_pieces(pos->side_to_move());
+ numOfMoves = generate_captures(*pos, moves);
+ this->score_qcaptures();
+ movesPicked = 0;
+ break;
+
+ case PH_QCHECKS:
+ numOfMoves = generate_checks(*pos, moves, dc);
+ movesPicked = 0;
+ break;
+
+ case PH_STOP:
+ return MOVE_NONE;
+
+ default:
+ assert(false);
+ return MOVE_NONE;
+ }
+ }
+
+ assert(false);
+
+ return MOVE_NONE;
+}
+
+
+/// A variant of get_next_move() which takes a lock as a parameter, used to
+/// prevent multiple threads from picking the same move at a split point.
+
+Move MovePicker::get_next_move(Lock &lock) {
+ Move m;
+
+ lock_grab(&lock);
+ if(finished) {
+ lock_release(&lock);
+ return MOVE_NONE;
+ }
+ m = this->get_next_move();
+ if(m == MOVE_NONE)
+ finished = true;
+ lock_release(&lock);
+
+ return m;
+}
+
+
+/// MovePicker::number_of_moves() simply returns the numOfMoves member
+/// variable. It is intended to be used in positions where the side to move
+/// is in check, for detecting checkmates or situations where there is only
+/// a single reply to check.
+
+int MovePicker::number_of_moves() const {
+ return numOfMoves;
+}
+
+
+/// MovePicker::score_captures(), MovePicker::score_noncaptures(),
+/// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a
+/// numerical move ordering score to each move in a move list. The moves
+/// with highest scores will be picked first by
+/// MovePicker::pick_move_from_list().
+
+void MovePicker::score_captures() {
+ // Winning and equal captures in the main search are ordered by MVV/LVA.
+ // Suprisingly, this appears to perform slightly better than SEE based
+ // move ordering. The reason is probably that in a position with a winning
+ // capture, capturing a more valuable (but sufficiently defended) piece
+ // first usually doesn't hurt. The opponent will have to recapture, and
+ // the hanging piece will still be hanging (except in the unusual cases
+ // where it is possible to recapture with the hanging piece). Exchanging
+ // big pieces before capturing a hanging piece probably helps to reduce
+ // the subtree size.
+ for(int i = 0; i < numOfMoves; i++) {
+ int seeValue = pos->see(moves[i].move);
+ if(seeValue >= 0) {
+ if(move_promotion(moves[i].move))
+ moves[i].score = QueenValueMidgame;
+ else
+ moves[i].score =
+ int(pos->midgame_value_of_piece_on(move_to(moves[i].move))) -
+ int(pos->type_of_piece_on(move_from(moves[i].move)));
+ }
+ else
+ moves[i].score = seeValue;
+
+ }
+}
+
+void MovePicker::score_noncaptures() {
+ for(int i = 0; i < numOfMoves; i++) {
+ Move m = moves[i].move;
+ if(m == killer1)
+ moves[i].score = HistoryMax + 2;
+ else if(m == killer2)
+ moves[i].score = HistoryMax + 1;
+ else
+ moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m);
+ }
+}
+
+void MovePicker::score_evasions() {
+ for(int i = 0; i < numOfMoves; i++) {
+ Move m = moves[i].move;
+ if(m == ttMove)
+ moves[i].score = 2*HistoryMax;
+ else if(!pos->square_is_empty(move_to(m))) {
+ int seeScore = pos->see(m);
+ moves[i].score = (seeScore >= 0)? seeScore + HistoryMax : seeScore;
+ }
+ else
+ moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m);
+ }
+}
+
+void MovePicker::score_qcaptures() {
+ // Use MVV/LVA ordering.
+ for(int i = 0; i < numOfMoves; i++) {
+ Move m = moves[i].move;
+ if(move_promotion(m))
+ moves[i].score = QueenValueMidgame;
+ else
+ moves[i].score =
+ int(pos->midgame_value_of_piece_on(move_to(m))) -
+ int(pos->midgame_value_of_piece_on(move_to(m))) / 64;
+ }
+}
+
+
+/// MovePicker::pick_move_from_list() picks the move with the biggest score
+/// from a list of generated moves (moves[] or badCaptures[], depending on
+/// the current move generation phase). It takes care not to return the
+/// transposition table move if that has already been serched previously.
+/// While picking captures in the PH_GOOD_CAPTURES phase (i.e. while picking
+/// non-losing captures in the main search), it moves all captures with
+/// negative SEE values to the badCaptures[] array.
+
+Move MovePicker::pick_move_from_list() {
+ int bestScore = -10000000;
+ int bestIndex;
+ Move move;
+
+ switch(PhaseTable[phaseIndex]) {
+
+ case PH_GOOD_CAPTURES:
+ assert(!pos->is_check());
+ assert(movesPicked >= 0);
+ while(movesPicked < numOfMoves) {
+ bestScore = -10000000;
+ bestIndex = -1;
+ for(int i = movesPicked; i < numOfMoves; i++) {
+ if(moves[i].score < 0) {
+ // Losing capture, move it to the badCaptures[] array
+ assert(numOfBadCaptures < 63);
+ badCaptures[numOfBadCaptures++] = moves[i];
+ moves[i--] = moves[--numOfMoves];
+ }
+ else if(moves[i].score > bestScore) {
+ bestIndex = i;
+ bestScore = moves[i].score;
+ }
+ }
+ if(bestIndex != -1) { // Found a good capture
+ MoveStack tmp = moves[movesPicked];
+ moves[movesPicked] = moves[bestIndex];
+ moves[bestIndex] = tmp;
+ move = moves[movesPicked++].move;
+ if(move != ttMove && move != mateKiller &&
+ pos->move_is_legal(move, pinned))
+ return move;
+ }
+ }
+ break;
+
+ case PH_NONCAPTURES:
+ assert(!pos->is_check());
+ assert(movesPicked >= 0);
+ while(movesPicked < numOfMoves) {
+ bestScore = -10000000;
+
+ // If this is a PV node or we have only picked a few moves, scan
+ // the entire move list for the best move. If many moves have already
+ // been searched and it is not a PV node, we are probably failing low
+ // anyway, so we just pick the first move from the list.
+ if(pvNode || movesPicked < 12) {
+ bestIndex = -1;
+ for(int i = movesPicked; i < numOfMoves; i++)
+ if(moves[i].score > bestScore) {
+ bestIndex = i;
+ bestScore = moves[i].score;
+ }
+ }
+ else
+ bestIndex = movesPicked;
+
+ if(bestIndex != -1) {
+ MoveStack tmp = moves[movesPicked];
+ moves[movesPicked] = moves[bestIndex];
+ moves[bestIndex] = tmp;
+ move = moves[movesPicked++].move;
+ if(move != ttMove && move != mateKiller &&
+ pos->move_is_legal(move, pinned))
+ return move;
+ }
+ }
+ break;
+
+ case PH_EVASIONS:
+ assert(pos->is_check());
+ assert(movesPicked >= 0);
+ while(movesPicked < numOfMoves) {
+ bestScore = -10000000;
+ bestIndex = -1;
+ for(int i = movesPicked; i < numOfMoves; i++)
+ if(moves[i].score > bestScore) {
+ bestIndex = i;
+ bestScore = moves[i].score;
+ }
+
+ if(bestIndex != -1) {
+ MoveStack tmp = moves[movesPicked];
+ moves[movesPicked] = moves[bestIndex];
+ moves[bestIndex] = tmp;
+ move = moves[movesPicked++].move;
+ return move;
+ }
+ }
+ break;
+
+ case PH_BAD_CAPTURES:
+ assert(!pos->is_check());
+ assert(badCapturesPicked >= 0);
+ // It's probably a good idea to use SEE move ordering here, instead
+ // of just picking the first move. FIXME
+ while(badCapturesPicked < numOfBadCaptures) {
+ move = badCaptures[badCapturesPicked++].move;
+ if(move != ttMove && move != mateKiller &&
+ pos->move_is_legal(move, pinned))
+ return move;
+ }
+ break;
+
+ case PH_QCAPTURES:
+ assert(!pos->is_check());
+ assert(movesPicked >= 0);
+ while(movesPicked < numOfMoves) {
+ bestScore = -10000000;
+ if(movesPicked < 4) {
+ bestIndex = -1;
+ for(int i = movesPicked; i < numOfMoves; i++)
+ if(moves[i].score > bestScore) {
+ bestIndex = i;
+ bestScore = moves[i].score;
+ }
+ }
+ else
+ bestIndex = movesPicked;
+
+ if(bestIndex != -1) {
+ MoveStack tmp = moves[movesPicked];
+ moves[movesPicked] = moves[bestIndex];
+ moves[bestIndex] = tmp;
+
+ move = moves[movesPicked++].move;
+ // Remember to change the line below if we decide to hash the qsearch!
+ // Maybe also postpone the legality check until after futility pruning?
+ if(/* move != ttMove && */ pos->move_is_legal(move, pinned))
+ return move;
+ }
+ }
+ break;
+
+ case PH_QCHECKS:
+ assert(!pos->is_check());
+ assert(movesPicked >= 0);
+ // Perhaps we should do something better than just picking the first
+ // move here? FIXME
+ while(movesPicked < numOfMoves) {
+ move = moves[movesPicked++].move;
+ // Remember to change the line below if we decide to hash the qsearch!
+ if(/* move != ttMove && */ pos->move_is_legal(move, pinned))
+ return move;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return MOVE_NONE;
+}
+
+
+/// MovePicker::init_phase_table() initializes the PhaseTable[],
+/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
+/// and QsearchWithoutChecksPhaseIndex variables. It is only called once
+/// during program startup, and never again while the program is running.
+
+void MovePicker::init_phase_table() {
+ int i = 0;
+
+ // Main search
+ MainSearchPhaseIndex = i - 1;
+ PhaseTable[i++] = PH_TT_MOVE;
+ PhaseTable[i++] = PH_MATE_KILLER;
+ PhaseTable[i++] = PH_GOOD_CAPTURES;
+ // PH_KILLER_1 and PH_KILLER_2 are not yet used.
+ // PhaseTable[i++] = PH_KILLER_1;
+ // PhaseTable[i++] = PH_KILLER_2;
+ PhaseTable[i++] = PH_NONCAPTURES;
+ PhaseTable[i++] = PH_BAD_CAPTURES;
+ PhaseTable[i++] = PH_STOP;
+
+ // Check evasions
+ EvasionsPhaseIndex = i - 1;
+ PhaseTable[i++] = PH_EVASIONS;
+ PhaseTable[i++] = PH_STOP;
+
+ // Quiescence search with checks
+ QsearchWithChecksPhaseIndex = i - 1;
+ PhaseTable[i++] = PH_QCAPTURES;
+ PhaseTable[i++] = PH_QCHECKS;
+ PhaseTable[i++] = PH_STOP;
+
+ // Quiescence search without checks
+ QsearchWithoutChecksPhaseIndex = i - 1;
+ PhaseTable[i++] = PH_QCAPTURES;
+ PhaseTable[i++] = PH_STOP;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined MOVEPICK_H_INCLUDED
+#define MOVEPICK_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "depth.h"
+#include "lock.h"
+#include "position.h"
+
+
+////
+//// Types
+////
+
+/// MovePicker is a class which is used to pick one legal move at a time from
+/// the current position. It is initialized with a Position object and a few
+/// moves we have reason to believe are good. The most important method is
+/// MovePicker::pick_next_move(), which returns a new legal move each time it
+/// is called, until there are no legal moves left, when MOVE_NONE is returned.
+/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
+/// attempts to return the moves which are most likely to be strongest first.
+
+class MovePicker {
+
+public:
+ MovePicker(Position &p, bool pvnode, Move ttm, Move mk, Move k1, Move k2,
+ Depth dpth);
+ Move get_next_move();
+ Move get_next_move(Lock &lock);
+ int number_of_moves() const;
+ int current_move_score() const;
+ Bitboard discovered_check_candidates();
+
+ static void init_phase_table();
+
+private:
+ void score_captures();
+ void score_noncaptures();
+ void score_evasions();
+ void score_qcaptures();
+ Move pick_move_from_list();
+
+ Position *pos;
+ Move ttMove, mateKiller, killer1, killer2;
+ Bitboard pinned, dc;
+ MoveStack moves[256], badCaptures[64];
+ bool pvNode;
+ Depth depth;
+ int phaseIndex;
+ int numOfMoves, numOfBadCaptures;
+ int movesPicked, badCapturesPicked;
+ bool finished;
+};
+
+
+////
+//// Inline functions
+////
+
+/// MovePicker::discovered_check_candidates() returns a bitboard containing
+/// all pieces which can possibly give discovered check. This bitboard is
+/// computed by the constructor function.
+
+inline Bitboard MovePicker::discovered_check_candidates() {
+ return dc;
+}
+
+#endif // !defined(MOVEPICK_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+
+#include "pawns.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ /// Constants and variables
+
+ // Doubled pawn penalty by file, middle game.
+ const Value DoubledPawnMidgamePenalty[8] = {
+ Value(20), Value(30), Value(34), Value(34),
+ Value(34), Value(34), Value(30), Value(20)
+ };
+
+ // Doubled pawn penalty by file, endgame.
+ const Value DoubledPawnEndgamePenalty[8] = {
+ Value(35), Value(40), Value(40), Value(40),
+ Value(40), Value(40), Value(40), Value(35)
+ };
+
+ // Isolated pawn penalty by file, middle game.
+ const Value IsolatedPawnMidgamePenalty[8] = {
+ Value(20), Value(30), Value(34), Value(34),
+ Value(34), Value(34), Value(30), Value(20)
+ };
+
+ // Isolated pawn penalty by file, endgame.
+ const Value IsolatedPawnEndgamePenalty[8] = {
+ Value(35), Value(40), Value(40), Value(40),
+ Value(40), Value(40), Value(40), Value(35)
+ };
+
+ // Backward pawn penalty by file, middle game.
+ const Value BackwardPawnMidgamePenalty[8] = {
+ Value(16), Value(24), Value(27), Value(27),
+ Value(27), Value(27), Value(24), Value(16)
+ };
+
+ // Backward pawn penalty by file, endgame.
+ const Value BackwardPawnEndgamePenalty[8] = {
+ Value(28), Value(32), Value(32), Value(32),
+ Value(32), Value(32), Value(32), Value(28)
+ };
+
+ // Pawn chain membership bonus by file, middle game.
+ const Value ChainMidgameBonus[8] = {
+ Value(14), Value(16), Value(17), Value(18),
+ Value(18), Value(17), Value(16), Value(14)
+ };
+
+ // Pawn chain membership bonus by file, endgame.
+ const Value ChainEndgameBonus[8] = {
+ Value(16), Value(16), Value(16), Value(16),
+ Value(16), Value(16), Value(16), Value(16)
+ };
+
+ // Candidate passed pawn bonus by rank, middle game.
+ const Value CandidateMidgameBonus[8] = {
+ Value(0), Value(12), Value(12), Value(20),
+ Value(40), Value(90), Value(0), Value(0)
+ };
+
+ // Candidate passed pawn bonus by rank, endgame.
+ const Value CandidateEndgameBonus[8] = {
+ Value(0), Value(24), Value(24), Value(40),
+ Value(80), Value(180), Value(0), Value(0)
+ };
+
+ // Evaluate pawn storms?
+ const bool EvaluatePawnStorms = true;
+
+ // Pawn storm tables for positions with opposite castling:
+ const int QStormTable[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ -22, -22, -22, -13, -4, 0, 0, 0,
+ -4, -9, -9, -9, -4, 0, 0, 0,
+ 9, 18, 22, 18, 9, 0, 0, 0,
+ 22, 31, 31, 22, 0, 0, 0, 0,
+ 31, 40, 40, 31, 0, 0, 0, 0,
+ 31, 40, 40, 31, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ const int KStormTable[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -4, -13, -22, -27, -27,
+ 0, 0, 0, -4, -9, -13, -18, -18,
+ 0, 0, 0, 0, 9, 9, 9, 9,
+ 0, 0, 0, 0, 9, 18, 27, 27,
+ 0, 0, 0, 0, 9, 27, 40, 36,
+ 0, 0, 0, 0, 0, 31, 40, 31,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ // Pawn storm open file bonuses by file:
+ const int KStormOpenFileBonus[8] = {
+ 45, 45, 30, 0, 0, 0, 0, 0
+ };
+
+ const int QStormOpenFileBonus[8] = {
+ 0, 0, 0, 0, 0, 30, 45, 30
+ };
+
+}
+
+
+////
+//// Functions
+////
+
+/// Constructor
+
+PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
+ size = numOfEntries;
+ entries = new PawnInfo[size];
+ if(entries == NULL) {
+ std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
+ << " bytes for pawn hash table." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ this->clear();
+}
+
+
+/// Destructor
+
+PawnInfoTable::~PawnInfoTable() {
+ delete [] entries;
+}
+
+
+/// PawnInfoTable::clear() clears the pawn hash table by setting all
+/// entries to 0.
+
+void PawnInfoTable::clear() {
+ memset(entries, 0, size * sizeof(PawnInfo));
+}
+
+
+/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
+/// a PawnInfo object, and returns a pointer to it. The result is also
+/// stored in a hash table, so we don't have to recompute everything when
+/// the same pawn structure occurs again.
+
+PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
+ assert(pos.is_ok());
+
+ Key key = pos.get_pawn_key();
+ int index = int(key & (size - 1));
+ PawnInfo *pi = entries + index;
+
+ // If pi->key matches the position's pawn hash key, it means that we
+ // have analysed this pawn structure before, and we can simply return the
+ // information we found the last time instead of recomputing it:
+ if(pi->key == key)
+ return pi;
+
+ // Clear the PawnInfo object, and set the key:
+ pi->clear();
+ pi->key = key;
+
+ Value mgValue[2] = {Value(0), Value(0)};
+ Value egValue[2] = {Value(0), Value(0)};
+
+ // Loop through the pawns for both colors:
+ for(Color us = WHITE; us <= BLACK; us++) {
+ Color them = opposite_color(us);
+ Bitboard ourPawns = pos.pawns(us);
+ Bitboard theirPawns = pos.pawns(them);
+ Bitboard pawns = ourPawns;
+
+ // Initialize pawn storm scores by giving bonuses for open files:
+ if(EvaluatePawnStorms)
+ for(File f = FILE_A; f <= FILE_H; f++)
+ if(pos.file_is_half_open(us, f)) {
+ pi->ksStormValue[us] += KStormOpenFileBonus[f];
+ pi->qsStormValue[us] += QStormOpenFileBonus[f];
+ }
+
+ // Loop through all pawns of the current color and score each pawn:
+ while(pawns) {
+ Square s = pop_1st_bit(&pawns);
+ File f = square_file(s);
+ Rank r = square_rank(s);
+ bool passed, doubled, isolated, backward, chain, candidate;
+ int bonus;
+
+ assert(pos.piece_on(s) == pawn_of_color(us));
+
+ // The file containing the pawn is not half open:
+ pi->halfOpenFiles[us] &= ~(1 << f);
+
+ // Passed, isolated or doubled pawn?
+ passed = pos.pawn_is_passed(us, s);
+ isolated = pos.pawn_is_isolated(us, s);
+ doubled = pos.pawn_is_doubled(us, s);
+
+ if(EvaluatePawnStorms) {
+ // We calculate kingside and queenside pawn storm
+ // scores for both colors. These are used when evaluating
+ // middle game positions with opposite side castling.
+ //
+ // Each pawn is given a base score given by a piece square table
+ // (KStormTable[] or QStormTable[]). This score is increased if
+ // there are enemy pawns on adjacent files in front of the pawn.
+ // This is because we want to be able to open files against the
+ // enemy king, and to avoid blocking the pawn structure (e.g. white
+ // pawns on h6, g5, black pawns on h7, g6, f7).
+
+ // Kingside pawn storms:
+ bonus = KStormTable[relative_square(us, s)];
+ if(bonus > 0 && outpost_mask(us, s) & theirPawns) {
+ switch(f) {
+
+ case FILE_F:
+ bonus += bonus / 4;
+ break;
+
+ case FILE_G:
+ bonus += bonus / 2 + bonus / 4;
+ break;
+
+ case FILE_H:
+ bonus += bonus / 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+ pi->ksStormValue[us] += bonus;
+
+ // Queenside pawn storms:
+ bonus = QStormTable[relative_square(us, s)];
+ if(bonus > 0 && passed_pawn_mask(us, s) & theirPawns) {
+ switch(f) {
+
+ case FILE_A:
+ bonus += bonus / 2;
+ break;
+
+ case FILE_B:
+ bonus += bonus / 2 + bonus / 4;
+ break;
+
+ case FILE_C:
+ bonus += bonus / 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+ pi->qsStormValue[us] += bonus;
+ }
+
+ // Member of a pawn chain? We could speed up the test a little by
+ // introducing an array of masks indexed by color and square for doing
+ // the test, but because everything is hashed, it probably won't make
+ // any noticable difference.
+ chain = (us == WHITE)?
+ (ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r-1))) :
+ (ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r+1)));
+
+
+ // Test for backward pawn.
+
+ // If the pawn is isolated, passed, or member of a pawn chain, it cannot
+ // be backward:
+ if(passed || isolated || chain)
+ backward = false;
+ // If the pawn can capture an enemy pawn, it's not backward:
+ else if(pos.pawn_attacks(us, s) & theirPawns)
+ backward = false;
+ // Check for friendly pawns behind on neighboring files:
+ else if(ourPawns & in_front_bb(them, r) & neighboring_files_bb(f))
+ backward = false;
+ else {
+ // We now know that there is no friendly pawns beside or behind this
+ // pawn on neighboring files. We now check whether the pawn is
+ // backward by looking in the forward direction on the neighboring
+ // files, and seeing whether we meet a friendly or an enemy pawn first.
+ Bitboard b;
+ if(us == WHITE) {
+ for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b<<=8);
+ backward = (b | (b << 8)) & theirPawns;
+ }
+ else {
+ for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b>>=8);
+ backward = (b | (b >> 8)) & theirPawns;
+ }
+ }
+
+ // Test for candidate passed pawn.
+ candidate =
+ (!passed && pos.file_is_half_open(them, f) &&
+ count_1s_max_15(neighboring_files_bb(f)
+ & (in_front_bb(them, r) | rank_bb(r))
+ & ourPawns)
+ - count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r)
+ & theirPawns)
+ >= 0);
+
+ // In order to prevent doubled passed pawns from receiving a too big
+ // bonus, only the frontmost passed pawn on each file is considered as
+ // a true passed pawn.
+ if(passed && (ourPawns & squares_in_front_of(us, s))) {
+ // candidate = true;
+ passed = false;
+ }
+
+ // Score this pawn:
+ Value mv = Value(0), ev = Value(0);
+ if(isolated) {
+ mv -= IsolatedPawnMidgamePenalty[f];
+ ev -= IsolatedPawnEndgamePenalty[f];
+ if(pos.file_is_half_open(them, f)) {
+ mv -= IsolatedPawnMidgamePenalty[f] / 2;
+ ev -= IsolatedPawnEndgamePenalty[f] / 2;
+ }
+ }
+ if(doubled) {
+ mv -= DoubledPawnMidgamePenalty[f];
+ ev -= DoubledPawnEndgamePenalty[f];
+ }
+ if(backward) {
+ mv -= BackwardPawnMidgamePenalty[f];
+ ev -= BackwardPawnEndgamePenalty[f];
+ if(pos.file_is_half_open(them, f)) {
+ mv -= BackwardPawnMidgamePenalty[f] / 2;
+ ev -= BackwardPawnEndgamePenalty[f] / 2;
+ }
+ }
+ if(chain) {
+ mv += ChainMidgameBonus[f];
+ ev += ChainEndgameBonus[f];
+ }
+ if(candidate) {
+ mv += CandidateMidgameBonus[pawn_rank(us, s)];
+ ev += CandidateEndgameBonus[pawn_rank(us, s)];
+ }
+
+ mgValue[us] += mv;
+ egValue[us] += ev;
+
+ // If the pawn is passed, set the square of the pawn in the passedPawns
+ // bitboard:
+ if(passed)
+ set_bit(&(pi->passedPawns), s);
+ }
+ }
+
+ pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
+ pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
+
+ return pi;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(PAWNS_H_INCLUDED)
+#define PAWNS_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "position.h"
+
+
+////
+//// Types
+////
+
+/// PawnInfo is a class which contains various information about a pawn
+/// structure. Currently, it only includes a middle game and an end game
+/// pawn structure evaluation, and a bitboard of passed pawns. We may want
+/// to add further information in the future. A lookup to the pawn hash table
+/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
+/// returns a pointer to a PawnInfo object.
+
+class PawnInfo {
+
+ friend class PawnInfoTable;
+
+public:
+ Value mg_value() const;
+ Value eg_value() const;
+ Value kingside_storm_value(Color c) const;
+ Value queenside_storm_value(Color c) const;
+ Bitboard passed_pawns() const;
+ bool file_is_half_open(Color c, File f) const;
+ bool has_open_file_to_left(Color c, File f) const;
+ bool has_open_file_to_right(Color c, File f) const;
+
+private:
+ void clear();
+
+ Key key;
+ Bitboard passedPawns;
+ int16_t mgValue, egValue;
+ int8_t ksStormValue[2], qsStormValue[2];
+ uint8_t halfOpenFiles[2];
+};
+
+
+/// The PawnInfoTable class represents a pawn hash table. It is basically
+/// just an array of PawnInfo objects and a few methods for accessing these
+/// objects. The most important method is get_pawn_info, which looks up a
+/// position in the table and returns a pointer to a PawnInfo object.
+
+class PawnInfoTable {
+
+public:
+ PawnInfoTable(unsigned numOfEntries);
+ ~PawnInfoTable();
+ void clear();
+ PawnInfo *get_pawn_info(const Position &pos);
+
+private:
+ unsigned size;
+ PawnInfo *entries;
+};
+
+
+////
+//// Inline functions
+////
+
+inline Value PawnInfo::mg_value() const {
+ return Value(mgValue);
+}
+
+inline Value PawnInfo::eg_value() const {
+ return Value(egValue);
+}
+
+inline Bitboard PawnInfo::passed_pawns() const {
+ return passedPawns;
+}
+
+inline Value PawnInfo::kingside_storm_value(Color c) const {
+ return Value(ksStormValue[c]);
+}
+
+inline Value PawnInfo::queenside_storm_value(Color c) const {
+ return Value(qsStormValue[c]);
+}
+
+inline bool PawnInfo::file_is_half_open(Color c, File f) const {
+ return (halfOpenFiles[c] & (1 << int(f)));
+}
+
+inline bool PawnInfo::has_open_file_to_left(Color c, File f) const {
+ return halfOpenFiles[c] & ((1 << int(f)) - 1);
+}
+
+inline bool PawnInfo::has_open_file_to_right(Color c, File f) const {
+ return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
+}
+
+inline void PawnInfo::clear() {
+ mgValue = egValue = 0;
+ passedPawns = EmptyBoardBB;
+ ksStormValue[WHITE] = ksStormValue[BLACK] = 0;
+ qsStormValue[WHITE] = qsStormValue[BLACK] = 0;
+ halfOpenFiles[WHITE] = halfOpenFiles[BLACK] = 0xFF;
+}
+
+
+#endif // !defined(PAWNS_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(PHASE_H_INCLUDED)
+#define PHASE_H_INCLUDED
+
+////
+//// Types
+////
+
+enum Phase {
+ PHASE_ENDGAME = 0,
+ PHASE_MIDGAME = 128
+};
+
+
+#endif // !defined(PHASE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cstring>
+
+#include "piece.h"
+
+
+////
+//// Constants and variables
+////
+
+const int SlidingArray[18] = {
+ 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
+};
+
+const SquareDelta Directions[16][16] = {
+ {DELTA_ZERO},
+ {DELTA_NW, DELTA_NE, DELTA_ZERO},
+ {DELTA_SSW, DELTA_SSE, DELTA_SWW, DELTA_SEE,
+ DELTA_NWW, DELTA_NEE, DELTA_NNW, DELTA_NNE, DELTA_ZERO},
+ {DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N,
+ DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N,
+ DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+ {DELTA_ZERO},
+ {DELTA_ZERO},
+ {DELTA_SW, DELTA_SE, DELTA_ZERO},
+ {DELTA_SSW, DELTA_SSE, DELTA_SWW, DELTA_SEE,
+ DELTA_NWW, DELTA_NEE, DELTA_NNW, DELTA_NNE, DELTA_ZERO},
+ {DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N,
+ DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+ {DELTA_S, DELTA_E, DELTA_W, DELTA_N,
+ DELTA_SE, DELTA_SW, DELTA_NE, DELTA_NW, DELTA_ZERO},
+};
+
+const SquareDelta PawnPush[2] = {
+ DELTA_N, DELTA_S
+};
+
+
+////
+//// Functions
+////
+
+/// Translating piece types to/from English piece letters:
+
+static const char PieceChars[] = " pnbrqk";
+
+char piece_type_to_char(PieceType pt, bool upcase = false) {
+ return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
+}
+
+PieceType piece_type_from_char(char c) {
+ const char *ch = strchr(PieceChars, tolower(c));
+ return ch? PieceType(ch - PieceChars) : NO_PIECE_TYPE;
+}
+
+
+/// piece_is_ok() and piece_type_is_ok(), for debugging:
+
+bool piece_is_ok(Piece pc) {
+ return
+ piece_type_is_ok(type_of_piece(pc)) &&
+ color_is_ok(color_of_piece(pc));
+}
+
+bool piece_type_is_ok(PieceType pc) {
+ return pc >= PAWN && pc <= KING;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(PIECE_H_INCLUDED)
+#define PIECE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "color.h"
+#include "misc.h"
+#include "square.h"
+
+
+////
+//// Types
+////
+
+enum PieceType {
+ NO_PIECE_TYPE = 0,
+ PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
+};
+
+enum Piece {
+ NO_PIECE = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
+ BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14,
+ EMPTY = 16, OUTSIDE = 17
+};
+
+
+////
+//// Constants and variables
+////
+
+const PieceType PieceTypeMin = PAWN;
+const PieceType PieceTypeMax = KING;
+
+extern const int SlidingArray[18];
+extern const SquareDelta Directions[16][16];
+extern const SquareDelta PawnPush[2];
+
+
+////
+//// Inline functions
+////
+
+inline Piece operator+ (Piece p, int i) { return Piece(int(p) + i); }
+inline void operator++ (Piece &p, int) { p = Piece(int(p) + 1); }
+inline Piece operator- (Piece p, int i) { return Piece(int(p) - i); }
+inline void operator-- (Piece &p, int) { p = Piece(int(p) - 1); }
+inline PieceType operator+ (PieceType p, int i) {return PieceType(int(p) + i);}
+inline void operator++ (PieceType &p, int) { p = PieceType(int(p) + 1); }
+inline PieceType operator- (PieceType p, int i) {return PieceType(int(p) - i);}
+inline void operator-- (PieceType &p, int) { p = PieceType(int(p) - 1); }
+
+inline PieceType type_of_piece(Piece p) {
+ return PieceType(int(p) & 7);
+}
+
+inline Color color_of_piece(Piece p) {
+ return Color(int(p) >> 3);
+}
+
+inline Piece piece_of_color_and_type(Color c, PieceType pt) {
+ return Piece((int(c) << 3) | int(pt));
+}
+
+inline Piece pawn_of_color(Color c) {
+ return piece_of_color_and_type(c, PAWN);
+}
+
+inline Piece knight_of_color(Color c) {
+ return piece_of_color_and_type(c, KNIGHT);
+}
+
+inline Piece bishop_of_color(Color c) {
+ return piece_of_color_and_type(c, BISHOP);
+}
+
+inline Piece rook_of_color(Color c) {
+ return piece_of_color_and_type(c, ROOK);
+}
+
+inline Piece queen_of_color(Color c) {
+ return piece_of_color_and_type(c, QUEEN);
+}
+
+inline Piece king_of_color(Color c) {
+ return piece_of_color_and_type(c, KING);
+}
+
+inline int piece_is_slider(Piece p) {
+ return SlidingArray[int(p)];
+}
+
+inline int piece_type_is_slider(PieceType pt) {
+ return SlidingArray[int(pt)];
+}
+
+inline SquareDelta pawn_push(Color c) {
+ return PawnPush[c];
+}
+
+
+////
+//// Prototypes
+////
+
+extern char piece_type_to_char(PieceType pt, bool upcase);
+extern PieceType piece_type_from_char(char c);
+extern bool piece_is_ok(Piece pc);
+extern bool piece_type_is_ok(PieceType pt);
+
+
+#endif // !defined(PIECE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+
+#include "mersenne.h"
+#include "movegen.h"
+#include "movepick.h"
+#include "position.h"
+#include "psqtab.h"
+#include "ucioption.h"
+
+
+////
+//// Variables
+////
+
+int Position::castleRightsMask[64];
+
+Key Position::zobrist[2][8][64];
+Key Position::zobEp[64];
+Key Position::zobCastle[16];
+Key Position::zobMaterial[2][8][16];
+Key Position::zobSideToMove;
+
+Value Position::MgPieceSquareTable[16][64];
+Value Position::EgPieceSquareTable[16][64];
+
+
+////
+//// Functions
+////
+
+/// Constructors
+
+Position::Position() { } // Do we really need this one?
+
+Position::Position(const Position &pos) {
+ this->copy(pos);
+}
+
+Position::Position(const std::string &fen) {
+ this->from_fen(fen);
+}
+
+
+/// Position::from_fen() initializes the position object with the given FEN
+/// string. This function is not very robust - make sure that input FENs are
+/// correct (this is assumed to be the responsibility of the GUI).
+
+void Position::from_fen(const std::string &fen) {
+ File file;
+ Rank rank;
+ int i;
+
+ this->clear();
+
+ // Board
+ rank = RANK_8;
+ file = FILE_A;
+ for(i = 0; fen[i] != ' '; i++) {
+ if(isdigit(fen[i]))
+ // Skip the given number of files
+ file += (fen[i] - '1' + 1);
+ else {
+ Square square = make_square(file, rank);
+ switch(fen[i]) {
+ case 'K': this->put_piece(WK, square); file++; break;
+ case 'Q': this->put_piece(WQ, square); file++; break;
+ case 'R': this->put_piece(WR, square); file++; break;
+ case 'B': this->put_piece(WB, square); file++; break;
+ case 'N': this->put_piece(WN, square); file++; break;
+ case 'P': this->put_piece(WP, square); file++; break;
+ case 'k': this->put_piece(BK, square); file++; break;
+ case 'q': this->put_piece(BQ, square); file++; break;
+ case 'r': this->put_piece(BR, square); file++; break;
+ case 'b': this->put_piece(BB, square); file++; break;
+ case 'n': this->put_piece(BN, square); file++; break;
+ case 'p': this->put_piece(BP, square); file++; break;
+ case '/': file = FILE_A; rank--; break;
+ case ' ': break;
+ default:
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+ }
+ }
+
+ // Side to move
+ i++;
+ if(fen[i] == 'w')
+ sideToMove = WHITE;
+ else if(fen[i] == 'b')
+ sideToMove = BLACK;
+ else {
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+
+ // Castling rights:
+ i++;
+ if(fen[i] != ' ') {
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+
+ i++;
+ while(strchr("KQkqabcdefghABCDEFGH-", fen[i])) {
+ if(fen[i] == '-') {
+ i++; break;
+ }
+ else if(fen[i] == 'K') this->allow_oo(WHITE);
+ else if(fen[i] == 'Q') this->allow_ooo(WHITE);
+ else if(fen[i] == 'k') this->allow_oo(BLACK);
+ else if(fen[i] == 'q') this->allow_ooo(BLACK);
+ else if(fen[i] >= 'A' && fen[i] <= 'H') {
+ File rookFile, kingFile = FILE_NONE;
+ for(Square square = SQ_B1; square <= SQ_G1; square++)
+ if(this->piece_on(square) == WK)
+ kingFile = square_file(square);
+ if(kingFile == FILE_NONE) {
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+ initialKFile = kingFile;
+ rookFile = File(fen[i] - 'A') + FILE_A;
+ if(rookFile < initialKFile) {
+ this->allow_ooo(WHITE);
+ initialQRFile = rookFile;
+ }
+ else {
+ this->allow_oo(WHITE);
+ initialKRFile = rookFile;
+ }
+ }
+ else if(fen[i] >= 'a' && fen[i] <= 'h') {
+ File rookFile, kingFile = FILE_NONE;
+ for(Square square = SQ_B8; square <= SQ_G8; square++)
+ if(this->piece_on(square) == BK)
+ kingFile = square_file(square);
+ if(kingFile == FILE_NONE) {
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+ initialKFile = kingFile;
+ rookFile = File(fen[i] - 'a') + FILE_A;
+ if(rookFile < initialKFile) {
+ this->allow_ooo(BLACK);
+ initialQRFile = rookFile;
+ }
+ else {
+ this->allow_oo(BLACK);
+ initialKRFile = rookFile;
+ }
+ }
+ else {
+ std::cout << "Error in FEN at character " << i << std::endl;
+ return;
+ }
+ i++;
+ }
+
+ while(fen[i] == ' ')
+ i++;
+
+ // En passant square
+ if(i < int(fen.length()) - 2)
+ if(fen[i] >= 'a' && fen[i] <= 'h' && (fen[i+1] == '3' || fen[i+1] == '6'))
+ epSquare = square_from_string(fen.substr(i, 2));
+
+ // Various initialisation
+
+ for(Square sq = SQ_A1; sq <= SQ_H8; sq++)
+ castleRightsMask[sq] = ALL_CASTLES;
+ castleRightsMask[make_square(initialKFile, RANK_1)] ^=
+ (WHITE_OO|WHITE_OOO);
+ castleRightsMask[make_square(initialKFile, RANK_8)] ^=
+ (BLACK_OO|BLACK_OOO);
+ castleRightsMask[make_square(initialKRFile, RANK_1)] ^= WHITE_OO;
+ castleRightsMask[make_square(initialKRFile, RANK_8)] ^= BLACK_OO;
+ castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO;
+ castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO;
+
+ this->find_checkers();
+
+ key = this->compute_key();
+ pawnKey = this->compute_pawn_key();
+ materialKey = this->compute_material_key();
+ mgValue = this->compute_mg_value();
+ egValue = this->compute_eg_value();
+ npMaterial[WHITE] = this->compute_non_pawn_material(WHITE);
+ npMaterial[BLACK] = this->compute_non_pawn_material(BLACK);
+}
+
+
+/// Position::to_fen() converts the position object to a FEN string. This is
+/// probably only useful for debugging.
+
+const std::string Position::to_fen() const {
+ char pieceLetters[] = " PNBRQK pnbrqk";
+ std::string result;
+ int skip;
+
+ for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
+ skip = 0;
+ for(File file = FILE_A; file <= FILE_H; file++) {
+ Square square = make_square(file, rank);
+ if(this->square_is_occupied(square)) {
+ if(skip > 0) result += (char)skip + '0';
+ result += pieceLetters[this->piece_on(square)];
+ skip = 0;
+ }
+ else skip++;
+ }
+ if(skip > 0) result += (char)skip + '0';
+ result += (rank > RANK_1)? '/' : ' ';
+ }
+
+ result += (sideToMove == WHITE)? 'w' : 'b';
+ result += ' ';
+ if(castleRights == NO_CASTLES) result += '-';
+ else {
+ if(this->can_castle_kingside(WHITE)) result += 'K';
+ if(this->can_castle_queenside(WHITE)) result += 'Q';
+ if(this->can_castle_kingside(BLACK)) result += 'k';
+ if(this->can_castle_queenside(BLACK)) result += 'q';
+ }
+
+ result += ' ';
+ if(this->ep_square() == SQ_NONE) result += '-';
+ else result += square_to_string(this->ep_square());
+
+ return result;
+}
+
+
+/// Position::print() prints an ASCII representation of the position to
+/// the standard output.
+
+void Position::print() const {
+ char pieceStrings[][8] =
+ {"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
+ "| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
+ };
+
+ for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
+ std::cout << "+---+---+---+---+---+---+---+---+\n";
+ for(File file = FILE_A; file <= FILE_H; file++) {
+ Square sq = make_square(file, rank);
+ Piece piece = this->piece_on(sq);
+ if(piece == EMPTY)
+ std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
+ else
+ std::cout << pieceStrings[piece];
+ }
+ std::cout << "|\n";
+ }
+ std::cout << "+---+---+---+---+---+---+---+---+\n";
+ std::cout << this->to_fen() << std::endl;
+ std::cout << key << std::endl;
+}
+
+
+/// Position::copy() creates a copy of the input position.
+
+void Position::copy(const Position &pos) {
+ memcpy(this, &pos, sizeof(Position));
+}
+
+
+/// Position:pinned_pieces() returns a bitboard of all pinned (against the
+/// king) pieces for the given color.
+
+Bitboard Position::pinned_pieces(Color c) const {
+ Bitboard b1, b2, pinned, pinners, sliders;
+ Square ksq = this->king_square(c), s;
+ Color them = opposite_color(c);
+
+ pinned = EmptyBoardBB;
+ b1 = this->occupied_squares();
+
+ sliders = this->rooks_and_queens(them) & ~this->checkers();
+ if(sliders & RookPseudoAttacks[ksq]) {
+ b2 = this->rook_attacks(ksq) & this->pieces_of_color(c);
+ pinners = rook_attacks_bb(ksq, b1 ^ b2) & sliders;
+ while(pinners) {
+ s = pop_1st_bit(&pinners);
+ pinned |= (squares_between(s, ksq) & b2);
+ }
+ }
+
+ sliders = this->bishops_and_queens(them) & ~this->checkers();
+ if(sliders & BishopPseudoAttacks[ksq]) {
+ b2 = this->bishop_attacks(ksq) & this->pieces_of_color(c);
+ pinners = bishop_attacks_bb(ksq, b1 ^ b2) & sliders;
+ while(pinners) {
+ s = pop_1st_bit(&pinners);
+ pinned |= (squares_between(s, ksq) & b2);
+ }
+ }
+
+ return pinned;
+}
+
+/// Position:discovered_check_candidates() returns a bitboard containing all
+/// pieces for the given side which are candidates for giving a discovered
+/// check. The code is almost the same as the function for finding pinned
+/// pieces.
+
+Bitboard Position::discovered_check_candidates(Color c) const {
+ Bitboard b1, b2, dc, checkers, sliders;
+ Square ksq = this->king_square(opposite_color(c)), s;
+
+ dc = EmptyBoardBB;
+ b1 = this->occupied_squares();
+
+ sliders = this->rooks_and_queens(c);
+ if(sliders & RookPseudoAttacks[ksq]) {
+ b2 = this->rook_attacks(ksq) & this->pieces_of_color(c);
+ checkers = rook_attacks_bb(ksq, b1 ^ b2) & sliders;
+ while(checkers) {
+ s = pop_1st_bit(&checkers);
+ dc |= (squares_between(s, ksq) & b2);
+ }
+ }
+
+ sliders = this->bishops_and_queens(c);
+ if(sliders & BishopPseudoAttacks[ksq]) {
+ b2 = this->bishop_attacks(ksq) & this->pieces_of_color(c);
+ checkers = bishop_attacks_bb(ksq, b1 ^ b2) & sliders;
+ while(checkers) {
+ s = pop_1st_bit(&checkers);
+ dc |= (squares_between(s, ksq) & b2);
+ }
+ }
+
+ return dc;
+}
+
+
+/// Position::square_is_attacked() checks whether the given side attacks the
+/// given square.
+
+bool Position::square_is_attacked(Square s, Color c) const {
+ return
+ (this->pawn_attacks(opposite_color(c), s) & this->pawns(c)) ||
+ (this->knight_attacks(s) & this->knights(c)) ||
+ (this->king_attacks(s) & this->kings(c)) ||
+ (this->rook_attacks(s) & this->rooks_and_queens(c)) ||
+ (this->bishop_attacks(s) & this->bishops_and_queens(c));
+}
+
+
+/// Position::attacks_to() computes a bitboard containing all pieces which
+/// attacks a given square. There are two versions of this function: One
+/// which finds attackers of both colors, and one which only finds the
+/// attackers for one side.
+
+Bitboard Position::attacks_to(Square s) const {
+ return
+ (this->black_pawn_attacks(s) & this->pawns(WHITE)) |
+ (this->white_pawn_attacks(s) & this->pawns(BLACK)) |
+ (this->knight_attacks(s) & this->pieces_of_type(KNIGHT)) |
+ (this->rook_attacks(s) & this->rooks_and_queens()) |
+ (this->bishop_attacks(s) & this->bishops_and_queens()) |
+ (this->king_attacks(s) & this->pieces_of_type(KING));
+}
+
+Bitboard Position::attacks_to(Square s, Color c) const {
+ return this->attacks_to(s) & this->pieces_of_color(c);
+}
+
+
+/// Position::piece_attacks_square() tests whether the piece on square f
+/// attacks square t.
+
+bool Position::piece_attacks_square(Square f, Square t) const {
+ assert(square_is_ok(f));
+ assert(square_is_ok(t));
+
+ switch(this->piece_on(f)) {
+ case WP: return this->white_pawn_attacks_square(f, t);
+ case BP: return this->black_pawn_attacks_square(f, t);
+ case WN: case BN: return this->knight_attacks_square(f, t);
+ case WB: case BB: return this->bishop_attacks_square(f, t);
+ case WR: case BR: return this->rook_attacks_square(f, t);
+ case WQ: case BQ: return this->queen_attacks_square(f, t);
+ case WK: case BK: return this->king_attacks_square(f, t);
+ default: return false;
+ }
+
+ return false;
+}
+
+
+/// Position::find_checkers() computes the checkersBB bitboard, which
+/// contains a nonzero bit for each checking piece (0, 1 or 2). It
+/// currently works by calling Position::attacks_to, which is probably
+/// inefficient. Consider rewriting this function to use the last move
+/// played, like in non-bitboard versions of Glaurung.
+
+void Position::find_checkers() {
+ checkersBB = attacks_to(this->king_square(this->side_to_move()),
+ opposite_color(this->side_to_move()));
+}
+
+
+/// Position::move_is_legal() tests whether a pseudo-legal move is legal.
+/// There are two versions of this function: One which takes only a
+/// move as input, and one which takes a move and a bitboard of pinned
+/// pieces. The latter function is faster, and should always be preferred
+/// when a pinned piece bitboard has already been computed.
+
+bool Position::move_is_legal(Move m) const {
+ return this->move_is_legal(m, this->pinned_pieces(this->side_to_move()));
+}
+
+
+bool Position::move_is_legal(Move m, Bitboard pinned) const {
+ Color us, them;
+ Square ksq, from;
+
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+ assert(pinned == this->pinned_pieces(this->side_to_move()));
+
+ // If we're in check, all pseudo-legal moves are legal, because our
+ // check evasion generator only generates true legal moves.
+ if(this->is_check()) return true;
+
+ // Castling moves are checked for legality during move generation.
+ if(move_is_castle(m)) return true;
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ ksq = this->king_square(us);
+
+ assert(this->color_of_piece_on(from) == us);
+ assert(this->piece_on(ksq) == king_of_color(us));
+
+ // En passant captures are a tricky special case. Because they are
+ // rather uncommon, we do it simply by testing whether the king is attacked
+ // after the move is made:
+ if(move_is_ep(m)) {
+ Square to = move_to(m);
+ Square capsq = make_square(square_file(to), square_rank(from));
+ Bitboard b = this->occupied_squares();
+
+ assert(to == this->ep_square());
+ assert(this->piece_on(from) == pawn_of_color(us));
+ assert(this->piece_on(capsq) == pawn_of_color(them));
+ assert(this->piece_on(to) == EMPTY);
+
+ clear_bit(&b, from); clear_bit(&b, capsq); set_bit(&b, to);
+ return
+ (!(rook_attacks_bb(ksq, b) & this->rooks_and_queens(them)) &&
+ !(bishop_attacks_bb(ksq, b) & this->bishops_and_queens(them)));
+ }
+
+ // If the moving piece is a king, check whether the destination
+ // square is attacked by the opponent.
+ if(from == ksq) return !(this->square_is_attacked(move_to(m), them));
+
+ // A non-king move is legal if and only if it is not pinned or it
+ // is moving along the ray towards or away from the king.
+ if(!bit_is_set(pinned, from)) return true;
+ if(direction_between_squares(from, ksq) ==
+ direction_between_squares(move_to(m), ksq))
+ return true;
+
+ return false;
+}
+
+
+/// Position::move_is_check() tests whether a pseudo-legal move is a check.
+/// There are two versions of this function: One which takes only a move as
+/// input, and one which takes a move and a bitboard of discovered check
+/// candidates. The latter function is faster, and should always be preferred
+/// when a discovered check candidates bitboard has already been computed.
+
+bool Position::move_is_check(Move m) const {
+ Bitboard dc = this->discovered_check_candidates(this->side_to_move());
+ return this->move_is_check(m, dc);
+}
+
+
+bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
+ Color us, them;
+ Square ksq, from, to;
+
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+ assert(dcCandidates ==
+ this->discovered_check_candidates(this->side_to_move()));
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ to = move_to(m);
+ ksq = this->king_square(them);
+ assert(this->color_of_piece_on(from) == us);
+ assert(this->piece_on(ksq) == king_of_color(them));
+
+ // Proceed according to the type of the moving piece:
+ switch(this->type_of_piece_on(from)) {
+ case PAWN:
+ // Normal check?
+ if(bit_is_set(this->pawn_attacks(them, ksq), to))
+ return true;
+ // Discovered check?
+ else if(bit_is_set(dcCandidates, from) &&
+ direction_between_squares(from, ksq) !=
+ direction_between_squares(to, ksq))
+ return true;
+ // Promotion with check?
+ else if(move_promotion(m)) {
+ Bitboard b = this->occupied_squares();
+ clear_bit(&b, from);
+
+ switch(move_promotion(m)) {
+ case KNIGHT:
+ return this->knight_attacks_square(to, ksq);
+ case BISHOP:
+ return bit_is_set(bishop_attacks_bb(to, b), ksq);
+ case ROOK:
+ return bit_is_set(rook_attacks_bb(to, b), ksq);
+ case QUEEN:
+ return bit_is_set(queen_attacks_bb(to, b), ksq);
+ default:
+ assert(false);
+ }
+ }
+ // En passant capture with check? We have already handled the case
+ // of direct checks and ordinary discovered check, the only case we
+ // need to handle is the unusual case of a discovered check through the
+ // captured pawn.
+ else if(move_is_ep(m)) {
+ Square capsq = make_square(square_file(to), square_rank(from));
+ Bitboard b = this->occupied_squares();
+
+ clear_bit(&b, from); clear_bit(&b, capsq); set_bit(&b, to);
+ return
+ ((rook_attacks_bb(ksq, b) & this->rooks_and_queens(us)) ||
+ (bishop_attacks_bb(ksq, b) & this->bishops_and_queens(us)));
+ }
+ return false;
+
+ case KNIGHT:
+ // Discovered check?
+ if(bit_is_set(dcCandidates, from))
+ return true;
+ // Normal check?
+ else
+ return bit_is_set(this->knight_attacks(ksq), to);
+
+ case BISHOP:
+ // Discovered check?
+ if(bit_is_set(dcCandidates, from))
+ return true;
+ // Normal check?
+ else
+ return bit_is_set(this->bishop_attacks(ksq), to);
+
+ case ROOK:
+ // Discovered check?
+ if(bit_is_set(dcCandidates, from))
+ return true;
+ // Normal check?
+ else
+ return bit_is_set(this->rook_attacks(ksq), to);
+
+ case QUEEN:
+ // Discovered checks are impossible!
+ assert(!bit_is_set(dcCandidates, from));
+ // Normal check?
+ return bit_is_set(this->queen_attacks(ksq), to);
+
+ case KING:
+ // Discovered check?
+ if(bit_is_set(dcCandidates, from) &&
+ direction_between_squares(from, ksq) !=
+ direction_between_squares(to, ksq))
+ return true;
+ // Castling with check?
+ if(move_is_castle(m)) {
+ Square kfrom, kto, rfrom, rto;
+ Bitboard b = this->occupied_squares();
+
+ kfrom = from;
+ rfrom = to;
+ if(rfrom > kfrom) {
+ kto = relative_square(us, SQ_G1);
+ rto = relative_square(us, SQ_F1);
+ }
+ else {
+ kto = relative_square(us, SQ_C1);
+ rto = relative_square(us, SQ_D1);
+ }
+
+ clear_bit(&b, kfrom); clear_bit(&b, rfrom);
+ set_bit(&b, rto); set_bit(&b, kto);
+
+ return bit_is_set(rook_attacks_bb(rto, b), ksq);
+ }
+
+ return false;
+
+ default:
+ assert(false);
+ return false;
+ }
+
+ assert(false);
+ return false;
+}
+
+
+/// Position::move_is_capture() tests whether a move from the current
+/// position is a capture.
+
+bool Position::move_is_capture(Move m) const {
+ return
+ this->color_of_piece_on(move_to(m)) == opposite_color(this->side_to_move())
+ || move_is_ep(m);
+}
+
+
+/// Position::move_attacks_square() tests whether a move from the current
+/// position attacks a given square. Only attacks by the moving piece are
+/// considered; the function does not handle X-ray attacks.
+
+bool Position::move_attacks_square(Move m, Square s) const {
+ assert(move_is_ok(m));
+ assert(square_is_ok(s));
+
+ Square f = move_from(m), t = move_to(m);
+
+ assert(this->square_is_occupied(f));
+
+ switch(this->piece_on(f)) {
+ case WP: return this->white_pawn_attacks_square(t, s);
+ case BP: return this->black_pawn_attacks_square(t, s);
+ case WN: case BN: return this->knight_attacks_square(t, s);
+ case WB: case BB: return this->bishop_attacks_square(t, s);
+ case WR: case BR: return this->rook_attacks_square(t, s);
+ case WQ: case BQ: return this->queen_attacks_square(t, s);
+ case WK: case BK: return this->king_attacks_square(t, s);
+ default: assert(false);
+ }
+
+ return false;
+}
+
+
+
+/// Position::backup() is called when making a move. All information
+/// necessary to restore the position when the move is later unmade
+/// is saved to an UndoInfo object. The function Position::restore
+/// does the reverse operation: When one does a backup followed by
+/// a restore with the same UndoInfo object, the position is restored
+/// to the state before backup was called.
+
+void Position::backup(UndoInfo &u) const {
+ u.castleRights = castleRights;
+ u.epSquare = epSquare;
+ u.checkersBB = checkersBB;
+ u.key = key;
+ u.pawnKey = pawnKey;
+ u.materialKey = materialKey;
+ u.rule50 = rule50;
+ u.lastMove = lastMove;
+ u.capture = NO_PIECE_TYPE;
+ u.mgValue = mgValue;
+ u.egValue = egValue;
+}
+
+
+/// Position::restore() is called when unmaking a move. It copies back
+/// the information backed up during a previous call to Position::backup.
+
+void Position::restore(const UndoInfo &u) {
+ castleRights = u.castleRights;
+ epSquare = u.epSquare;
+ checkersBB = u.checkersBB;
+ key = u.key;
+ pawnKey = u.pawnKey;
+ materialKey = u.materialKey;
+ rule50 = u.rule50;
+ lastMove = u.lastMove;
+ mgValue = u.mgValue;
+ egValue = u.egValue;
+}
+
+
+/// Position::do_move() makes a move, and backs up all information necessary
+/// to undo the move to an UndoInfo object. The move is assumed to be legal.
+/// Pseudo-legal moves should be filtered out before this function is called.
+/// There are two versions of this function, one which takes only the move and
+/// the UndoInfo as input, and one which takes a third parameter, a bitboard of
+/// discovered check candidates. The second version is faster, because knowing
+/// the discovered check candidates makes it easier to update the checkersBB
+/// member variable in the position object.
+
+void Position::do_move(Move m, UndoInfo &u) {
+ this->do_move(m, u, this->discovered_check_candidates(this->side_to_move()));
+}
+
+void Position::do_move(Move m, UndoInfo &u, Bitboard dcCandidates) {
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+
+ // Back up the necessary information to our UndoInfo object (except the
+ // captured piece, which is taken care of later:
+ this->backup(u);
+
+ // Save the current key to the history[] array, in order to be able to
+ // detect repetition draws:
+ history[gamePly] = key;
+
+ // Increment the 50 moves rule draw counter. Resetting it to zero in the
+ // case of non-reversible moves is taken care of later.
+ rule50++;
+
+ if(move_is_castle(m))
+ this->do_castle_move(m);
+ else if(move_promotion(m))
+ this->do_promotion_move(m, u);
+ else if(move_is_ep(m))
+ this->do_ep_move(m);
+ else {
+ Color us, them;
+ Square from, to;
+ PieceType piece, capture;
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ to = move_to(m);
+
+ assert(this->color_of_piece_on(from) == us);
+ assert(this->color_of_piece_on(to) == them || this->piece_on(to) == EMPTY);
+
+ piece = this->type_of_piece_on(from);
+ capture = this->type_of_piece_on(to);
+
+ if(capture) {
+ assert(capture != KING);
+
+ // Remove captured piece:
+ clear_bit(&(byColorBB[them]), to);
+ clear_bit(&(byTypeBB[capture]), to);
+
+ // Update hash key:
+ key ^= zobrist[them][capture][to];
+
+ // If the captured piece was a pawn, update pawn hash key:
+ if(capture == PAWN)
+ pawnKey ^= zobrist[them][PAWN][to];
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(them, capture, to);
+ egValue -= this->eg_pst(them, capture, to);
+
+ // Update material:
+ if(capture != PAWN)
+ npMaterial[them] -= piece_value_midgame(capture);
+
+ // Update material hash key:
+ materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
+
+ // Update piece count:
+ pieceCount[them][capture]--;
+
+ // Update piece list:
+ pieceList[them][capture][index[to]] =
+ pieceList[them][capture][pieceCount[them][capture]];
+ index[pieceList[them][capture][index[to]]] = index[to];
+
+ // Remember the captured piece, in order to be able to undo the move
+ // correctly:
+ u.capture = capture;
+
+ // Reset rule 50 counter:
+ rule50 = 0;
+ }
+
+ // Move the piece:
+ clear_bit(&(byColorBB[us]), from);
+ clear_bit(&(byTypeBB[piece]), from);
+ clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
+ set_bit(&(byColorBB[us]), to);
+ set_bit(&(byTypeBB[piece]), to);
+ set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+ board[to] = board[from];
+ board[from] = EMPTY;
+
+ // Update hash key:
+ key ^= zobrist[us][piece][from] ^ zobrist[us][piece][to];
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(us, piece, from);
+ mgValue += this->mg_pst(us, piece, to);
+ egValue -= this->eg_pst(us, piece, from);
+ egValue += this->eg_pst(us, piece, to);
+
+ // If the moving piece was a king, update the king square:
+ if(piece == KING)
+ kingSquare[us] = to;
+
+ // If the move was a double pawn push, set the en passant square.
+ // This code is a bit ugly right now, and should be cleaned up later.
+ // FIXME
+ if(epSquare != SQ_NONE) {
+ key ^= zobEp[epSquare];
+ epSquare = SQ_NONE;
+ }
+ if(piece == PAWN) {
+ if(abs(int(to) - int(from)) == 16) {
+ if((us == WHITE && (this->white_pawn_attacks(from + DELTA_N) &
+ this->pawns(BLACK))) ||
+ (us == BLACK && (this->black_pawn_attacks(from + DELTA_S) &
+ this->pawns(WHITE)))) {
+ epSquare = Square((int(from) + int(to)) / 2);
+ key ^= zobEp[epSquare];
+ }
+ }
+ // Reset rule 50 draw counter.
+ rule50 = 0;
+ // Update pawn hash key:
+ pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
+ }
+
+ // Update piece lists:
+ pieceList[us][piece][index[from]] = to;
+ index[to] = index[from];
+
+ // Update castle rights:
+ key ^= zobCastle[castleRights];
+ castleRights &= castleRightsMask[from];
+ castleRights &= castleRightsMask[to];
+ key ^= zobCastle[castleRights];
+
+ // Update checkers bitboard:
+ checkersBB = EmptyBoardBB;
+ Square ksq = this->king_square(them);
+
+ switch(piece) {
+
+ case PAWN:
+ if(bit_is_set(this->pawn_attacks(them, ksq), to))
+ set_bit(&checkersBB, to);
+ if(bit_is_set(dcCandidates, from))
+ checkersBB |=
+ ((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
+ (this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
+ break;
+
+ case KNIGHT:
+ if(bit_is_set(this->knight_attacks(ksq), to))
+ set_bit(&checkersBB, to);
+ if(bit_is_set(dcCandidates, from))
+ checkersBB |=
+ ((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
+ (this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
+ break;
+
+ case BISHOP:
+ if(bit_is_set(this->bishop_attacks(ksq), to))
+ set_bit(&checkersBB, to);
+ if(bit_is_set(dcCandidates, from))
+ checkersBB |=
+ (this->rook_attacks(ksq) & this->rooks_and_queens(us));
+ break;
+
+ case ROOK:
+ if(bit_is_set(this->rook_attacks(ksq), to))
+ set_bit(&checkersBB, to);
+ if(bit_is_set(dcCandidates, from))
+ checkersBB |=
+ (this->bishop_attacks(ksq) & this->bishops_and_queens(us));
+ break;
+
+ case QUEEN:
+ if(bit_is_set(this->queen_attacks(ksq), to))
+ set_bit(&checkersBB, to);
+ break;
+
+ case KING:
+ if(bit_is_set(dcCandidates, from))
+ checkersBB |=
+ ((this->rook_attacks(ksq) & this->rooks_and_queens(us)) |
+ (this->bishop_attacks(ksq) & this->bishops_and_queens(us)));
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ // Finish
+ key ^= zobSideToMove;
+ sideToMove = opposite_color(sideToMove);
+ gamePly++;
+
+ mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
+ egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
+
+ assert(this->is_ok());
+}
+
+
+/// Position::do_castle_move() is a private method used to make a castling
+/// move. It is called from the main Position::do_move function. Note that
+/// castling moves are encoded as "king captures friendly rook" moves, for
+/// instance white short castling in a non-Chess960 game is encoded as e1h1.
+
+void Position::do_castle_move(Move m) {
+ Color us, them;
+ Square kfrom, kto, rfrom, rto;
+
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+ assert(move_is_castle(m));
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ // Find source squares for king and rook:
+ kfrom = move_from(m);
+ rfrom = move_to(m); // HACK: See comment at beginning of function.
+
+ assert(this->piece_on(kfrom) == king_of_color(us));
+ assert(this->piece_on(rfrom) == rook_of_color(us));
+
+ // Find destination squares for king and rook:
+ if(rfrom > kfrom) { // O-O
+ kto = relative_square(us, SQ_G1);
+ rto = relative_square(us, SQ_F1);
+ }
+ else { // O-O-O
+ kto = relative_square(us, SQ_C1);
+ rto = relative_square(us, SQ_D1);
+ }
+
+ // Remove pieces from source squares:
+ clear_bit(&(byColorBB[us]), kfrom);
+ clear_bit(&(byTypeBB[KING]), kfrom);
+ clear_bit(&(byTypeBB[0]), kfrom); // HACK: byTypeBB[0] == occupied squares
+ clear_bit(&(byColorBB[us]), rfrom);
+ clear_bit(&(byTypeBB[ROOK]), rfrom);
+ clear_bit(&(byTypeBB[0]), rfrom); // HACK: byTypeBB[0] == occupied squares
+
+ // Put pieces on destination squares:
+ set_bit(&(byColorBB[us]), kto);
+ set_bit(&(byTypeBB[KING]), kto);
+ set_bit(&(byTypeBB[0]), kto); // HACK: byTypeBB[0] == occupied squares
+ set_bit(&(byColorBB[us]), rto);
+ set_bit(&(byTypeBB[ROOK]), rto);
+ set_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
+
+ // Update board array:
+ board[kfrom] = board[rfrom] = EMPTY;
+ board[kto] = king_of_color(us);
+ board[rto] = rook_of_color(us);
+
+ // Update king square:
+ kingSquare[us] = kto;
+
+ // Update piece lists:
+ pieceList[us][KING][index[kfrom]] = kto;
+ pieceList[us][ROOK][index[rfrom]] = rto;
+ int tmp = index[rfrom];
+ index[kto] = index[kfrom];
+ index[rto] = tmp;
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(us, KING, kfrom);
+ mgValue += this->mg_pst(us, KING, kto);
+ egValue -= this->eg_pst(us, KING, kfrom);
+ egValue += this->eg_pst(us, KING, kto);
+ mgValue -= this->mg_pst(us, ROOK, rfrom);
+ mgValue += this->mg_pst(us, ROOK, rto);
+ egValue -= this->eg_pst(us, ROOK, rfrom);
+ egValue += this->eg_pst(us, ROOK, rto);
+
+ // Update hash key:
+ key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
+ key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto];
+
+ // Clear en passant square:
+ if(epSquare != SQ_NONE) {
+ key ^= zobEp[epSquare];
+ epSquare = SQ_NONE;
+ }
+
+ // Update castling rights:
+ key ^= zobCastle[castleRights];
+ castleRights &= castleRightsMask[kfrom];
+ key ^= zobCastle[castleRights];
+
+ // Reset rule 50 counter:
+ rule50 = 0;
+
+ // Update checkers BB:
+ checkersBB = attacks_to(this->king_square(them), us);
+}
+
+
+/// Position::do_promotion_move() is a private method used to make a promotion
+/// move. It is called from the main Position::do_move function. The
+/// UndoInfo object, which has been initialized in Position::do_move, is
+/// used to store the captured piece (if any).
+
+void Position::do_promotion_move(Move m, UndoInfo &u) {
+ Color us, them;
+ Square from, to;
+ PieceType capture, promotion;
+
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+ assert(move_promotion(m));
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ to = move_to(m);
+
+ assert(pawn_rank(us, to) == RANK_8);
+ assert(this->piece_on(from) == pawn_of_color(us));
+ assert(this->color_of_piece_on(to) == them || this->square_is_empty(to));
+
+ capture = this->type_of_piece_on(to);
+
+ if(capture) {
+ assert(capture != KING);
+
+ // Remove captured piece:
+ clear_bit(&(byColorBB[them]), to);
+ clear_bit(&(byTypeBB[capture]), to);
+
+ // Update hash key:
+ key ^= zobrist[them][capture][to];
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(them, capture, to);
+ egValue -= this->eg_pst(them, capture, to);
+
+ // Update material. Because our move is a promotion, we know that the
+ // captured piece is not a pawn.
+ assert(capture != PAWN);
+ npMaterial[them] -= piece_value_midgame(capture);
+
+ // Update material hash key:
+ materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
+
+ // Update piece count:
+ pieceCount[them][capture]--;
+
+ // Update piece list:
+ pieceList[them][capture][index[to]] =
+ pieceList[them][capture][pieceCount[them][capture]];
+ index[pieceList[them][capture][index[to]]] = index[to];
+
+ // Remember the captured piece, in order to be able to undo the move
+ // correctly:
+ u.capture = capture;
+ }
+
+ // Remove pawn:
+ clear_bit(&(byColorBB[us]), from);
+ clear_bit(&(byTypeBB[PAWN]), from);
+ clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
+ board[from] = EMPTY;
+
+ // Insert promoted piece:
+ promotion = move_promotion(m);
+ assert(promotion >= KNIGHT && promotion <= QUEEN);
+ set_bit(&(byColorBB[us]), to);
+ set_bit(&(byTypeBB[promotion]), to);
+ set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+ board[to] = piece_of_color_and_type(us, promotion);
+
+ // Update hash key:
+ key ^= zobrist[us][PAWN][from] ^ zobrist[us][promotion][to];
+
+ // Update pawn hash key:
+ pawnKey ^= zobrist[us][PAWN][from];
+
+ // Update material key:
+ materialKey ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]];
+ materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1];
+
+ // Update piece counts:
+ pieceCount[us][PAWN]--;
+ pieceCount[us][promotion]++;
+
+ // Update piece lists:
+ pieceList[us][PAWN][index[from]] =
+ pieceList[us][PAWN][pieceCount[us][PAWN]];
+ index[pieceList[us][PAWN][index[from]]] = index[from];
+ pieceList[us][promotion][pieceCount[us][promotion] - 1] = to;
+ index[to] = pieceCount[us][promotion] - 1;
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(us, PAWN, from);
+ mgValue += this->mg_pst(us, promotion, to);
+ egValue -= this->eg_pst(us, PAWN, from);
+ egValue += this->eg_pst(us, promotion, to);
+
+ // Update material:
+ npMaterial[us] += piece_value_midgame(promotion);
+
+ // Clear the en passant square:
+ if(epSquare != SQ_NONE) {
+ key ^= zobEp[epSquare];
+ epSquare = SQ_NONE;
+ }
+
+ // Update castle rights:
+ key ^= zobCastle[castleRights];
+ castleRights &= castleRightsMask[to];
+ key ^= zobCastle[castleRights];
+
+ // Reset rule 50 counter:
+ rule50 = 0;
+
+ // Update checkers BB:
+ checkersBB = attacks_to(this->king_square(them), us);
+}
+
+
+/// Position::do_ep_move() is a private method used to make an en passant
+/// capture. It is called from the main Position::do_move function. Because
+/// the captured piece is always a pawn, we don't need to pass an UndoInfo
+/// object in which to store the captured piece.
+
+void Position::do_ep_move(Move m) {
+ Color us, them;
+ Square from, to, capsq;
+
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+ assert(move_is_ep(m));
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ // Find from, to and capture squares:
+ from = move_from(m);
+ to = move_to(m);
+ capsq = (us == WHITE)? (to - DELTA_N) : (to - DELTA_S);
+
+ assert(to == epSquare);
+ assert(pawn_rank(us, to) == RANK_6);
+ assert(this->piece_on(to) == EMPTY);
+ assert(this->piece_on(from) == pawn_of_color(us));
+ assert(this->piece_on(capsq) == pawn_of_color(them));
+
+ // Remove captured piece:
+ clear_bit(&(byColorBB[them]), capsq);
+ clear_bit(&(byTypeBB[PAWN]), capsq);
+ clear_bit(&(byTypeBB[0]), capsq); // HACK: byTypeBB[0] == occupied squares
+ board[capsq] = EMPTY;
+
+ // Remove moving piece from source square:
+ clear_bit(&(byColorBB[us]), from);
+ clear_bit(&(byTypeBB[PAWN]), from);
+ clear_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
+
+ // Put moving piece on destination square:
+ set_bit(&(byColorBB[us]), to);
+ set_bit(&(byTypeBB[PAWN]), to);
+ set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+ board[to] = board[from];
+ board[from] = EMPTY;
+
+ // Update material hash key:
+ materialKey ^= zobMaterial[them][PAWN][pieceCount[them][PAWN]];
+
+ // Update piece count:
+ pieceCount[them][PAWN]--;
+
+ // Update piece list:
+ pieceList[us][PAWN][index[from]] = to;
+ index[to] = index[from];
+ pieceList[them][PAWN][index[capsq]] =
+ pieceList[them][PAWN][pieceCount[them][PAWN]];
+ index[pieceList[them][PAWN][index[capsq]]] = index[capsq];
+
+ // Update hash key:
+ key ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
+ key ^= zobrist[them][PAWN][capsq];
+ key ^= zobEp[epSquare];
+
+ // Update pawn hash key:
+ pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
+ pawnKey ^= zobrist[them][PAWN][capsq];
+
+ // Update incremental scores:
+ mgValue -= this->mg_pst(them, PAWN, capsq);
+ mgValue -= this->mg_pst(us, PAWN, from);
+ mgValue += this->mg_pst(us, PAWN, to);
+ egValue -= this->eg_pst(them, PAWN, capsq);
+ egValue -= this->eg_pst(us, PAWN, from);
+ egValue += this->eg_pst(us, PAWN, to);
+
+ // Reset en passant square:
+ epSquare = SQ_NONE;
+
+ // Reset rule 50 counter:
+ rule50 = 0;
+
+ // Update checkers BB:
+ checkersBB = attacks_to(this->king_square(them), us);
+}
+
+
+/// Position::undo_move() unmakes a move. When it returns, the position should
+/// be restored to exactly the same state as before the move was made. It is
+/// important that Position::undo_move is called with the same move and UndoInfo
+/// object as the earlier call to Position::do_move.
+
+void Position::undo_move(Move m, const UndoInfo &u) {
+ assert(this->is_ok());
+ assert(move_is_ok(m));
+
+ gamePly--;
+ sideToMove = opposite_color(sideToMove);
+
+ // Restore information from our UndoInfo object (except the captured piece,
+ // which is taken care of later):
+ this->restore(u);
+
+ if(move_is_castle(m))
+ this->undo_castle_move(m);
+ else if(move_promotion(m))
+ this->undo_promotion_move(m, u);
+ else if(move_is_ep(m))
+ this->undo_ep_move(m);
+ else {
+ Color us, them;
+ Square from, to;
+ PieceType piece, capture;
+
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ to = move_to(m);
+
+ assert(this->piece_on(from) == EMPTY);
+ assert(color_of_piece_on(to) == us);
+
+ // Put the piece back at the source square:
+ piece = this->type_of_piece_on(to);
+ set_bit(&(byColorBB[us]), from);
+ set_bit(&(byTypeBB[piece]), from);
+ set_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
+ board[from] = piece_of_color_and_type(us, piece);
+
+ // Clear the destination square
+ clear_bit(&(byColorBB[us]), to);
+ clear_bit(&(byTypeBB[piece]), to);
+ clear_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+
+ // If the moving piece was a king, update the king square:
+ if(piece == KING)
+ kingSquare[us] = from;
+
+ // Update piece list:
+ pieceList[us][piece][index[to]] = from;
+ index[from] = index[to];
+
+ capture = u.capture;
+
+ if(capture) {
+ assert(capture != KING);
+ // Replace the captured piece:
+ set_bit(&(byColorBB[them]), to);
+ set_bit(&(byTypeBB[capture]), to);
+ set_bit(&(byTypeBB[0]), to);
+ board[to] = piece_of_color_and_type(them, capture);
+
+ // Update material:
+ if(capture != PAWN)
+ npMaterial[them] += piece_value_midgame(capture);
+
+ // Update piece list:
+ pieceList[them][capture][pieceCount[them][capture]] = to;
+ index[to] = pieceCount[them][capture];
+
+ // Update piece count:
+ pieceCount[them][capture]++;
+ }
+ else
+ board[to] = EMPTY;
+ }
+
+ assert(this->is_ok());
+}
+
+
+/// Position::undo_castle_move() is a private method used to unmake a castling
+/// move. It is called from the main Position::undo_move function. Note that
+/// castling moves are encoded as "king captures friendly rook" moves, for
+/// instance white short castling in a non-Chess960 game is encoded as e1h1.
+
+void Position::undo_castle_move(Move m) {
+ Color us, them;
+ Square kfrom, kto, rfrom, rto;
+
+ assert(move_is_ok(m));
+ assert(move_is_castle(m));
+
+ // When we have arrived here, some work has already been done by
+ // Position::undo_move. In particular, the side to move has been switched,
+ // so the code below is correct.
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ // Find source squares for king and rook:
+ kfrom = move_from(m);
+ rfrom = move_to(m); // HACK: See comment at beginning of function.
+
+ // Find destination squares for king and rook:
+ if(rfrom > kfrom) { // O-O
+ kto = relative_square(us, SQ_G1);
+ rto = relative_square(us, SQ_F1);
+ }
+ else { // O-O-O
+ kto = relative_square(us, SQ_C1);
+ rto = relative_square(us, SQ_D1);
+ }
+
+ assert(this->piece_on(kto) == king_of_color(us));
+ assert(this->piece_on(rto) == rook_of_color(us));
+
+ // Remove pieces from destination squares:
+ clear_bit(&(byColorBB[us]), kto);
+ clear_bit(&(byTypeBB[KING]), kto);
+ clear_bit(&(byTypeBB[0]), kto); // HACK: byTypeBB[0] == occupied squares
+ clear_bit(&(byColorBB[us]), rto);
+ clear_bit(&(byTypeBB[ROOK]), rto);
+ clear_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
+
+ // Put pieces on source squares:
+ set_bit(&(byColorBB[us]), kfrom);
+ set_bit(&(byTypeBB[KING]), kfrom);
+ set_bit(&(byTypeBB[0]), kfrom); // HACK: byTypeBB[0] == occupied squares
+ set_bit(&(byColorBB[us]), rfrom);
+ set_bit(&(byTypeBB[ROOK]), rfrom);
+ set_bit(&(byTypeBB[0]), rfrom); // HACK: byTypeBB[0] == occupied squares
+
+ // Update board:
+ board[rto] = board[kto] = EMPTY;
+ board[rfrom] = rook_of_color(us);
+ board[kfrom] = king_of_color(us);
+
+ // Update king square:
+ kingSquare[us] = kfrom;
+
+ // Update piece lists:
+ pieceList[us][KING][index[kto]] = kfrom;
+ pieceList[us][ROOK][index[rto]] = rfrom;
+ int tmp = index[rto]; // Necessary because we may have rto == kfrom in FRC.
+ index[kfrom] = index[kto];
+ index[rfrom] = tmp;
+}
+
+
+/// Position::undo_promotion_move() is a private method used to unmake a
+/// promotion move. It is called from the main Position::do_move
+/// function. The UndoInfo object, which has been initialized in
+/// Position::do_move, is used to put back the captured piece (if any).
+
+void Position::undo_promotion_move(Move m, const UndoInfo &u) {
+ Color us, them;
+ Square from, to;
+ PieceType capture, promotion;
+
+ assert(move_is_ok(m));
+ assert(move_promotion(m));
+
+ // When we have arrived here, some work has already been done by
+ // Position::undo_move. In particular, the side to move has been switched,
+ // so the code below is correct.
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ from = move_from(m);
+ to = move_to(m);
+
+ assert(pawn_rank(us, to) == RANK_8);
+ assert(this->piece_on(from) == EMPTY);
+
+ // Remove promoted piece:
+ promotion = move_promotion(m);
+ assert(this->piece_on(to)==piece_of_color_and_type(us, promotion));
+ assert(promotion >= KNIGHT && promotion <= QUEEN);
+ clear_bit(&(byColorBB[us]), to);
+ clear_bit(&(byTypeBB[promotion]), to);
+ clear_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+
+ // Insert pawn at source square:
+ set_bit(&(byColorBB[us]), from);
+ set_bit(&(byTypeBB[PAWN]), from);
+ set_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares
+ board[from] = pawn_of_color(us);
+
+ // Update material:
+ npMaterial[us] -= piece_value_midgame(promotion);
+
+ // Update piece list:
+ pieceList[us][PAWN][pieceCount[us][PAWN]] = from;
+ index[from] = pieceCount[us][PAWN];
+ pieceList[us][promotion][index[to]] =
+ pieceList[us][promotion][pieceCount[us][promotion] - 1];
+ index[pieceList[us][promotion][index[to]]] = index[to];
+
+ // Update piece counts:
+ pieceCount[us][promotion]--;
+ pieceCount[us][PAWN]++;
+
+ capture = u.capture;
+ if(capture) {
+ assert(capture != KING);
+
+ // Insert captured piece:
+ set_bit(&(byColorBB[them]), to);
+ set_bit(&(byTypeBB[capture]), to);
+ set_bit(&(byTypeBB[0]), to); // HACK: byTypeBB[0] == occupied squares
+ board[to] = piece_of_color_and_type(them, capture);
+
+ // Update material. Because the move is a promotion move, we know
+ // that the captured piece cannot be a pawn.
+ assert(capture != PAWN);
+ npMaterial[them] += piece_value_midgame(capture);
+
+ // Update piece list:
+ pieceList[them][capture][pieceCount[them][capture]] = to;
+ index[to] = pieceCount[them][capture];
+
+ // Update piece count:
+ pieceCount[them][capture]++;
+ }
+ else
+ board[to] = EMPTY;
+}
+
+
+/// Position::undo_ep_move() is a private method used to unmake an en passant
+/// capture. It is called from the main Position::undo_move function. Because
+/// the captured piece is always a pawn, we don't need to pass an UndoInfo
+/// object from which to retrieve the captured piece.
+
+void Position::undo_ep_move(Move m) {
+ Color us, them;
+ Square from, to, capsq;
+
+ assert(move_is_ok(m));
+ assert(move_is_ep(m));
+
+ // When we have arrived here, some work has already been done by
+ // Position::undo_move. In particular, the side to move has been switched,
+ // so the code below is correct.
+ us = this->side_to_move();
+ them = opposite_color(us);
+
+ // Find from, to and captures squares:
+ from = move_from(m);
+ to = move_to(m);
+ capsq = (us == WHITE)? (to - DELTA_N) : (to - DELTA_S);
+
+ assert(to == this->ep_square());
+ assert(pawn_rank(us, to) == RANK_6);
+ assert(this->piece_on(to) == pawn_of_color(us));
+ assert(this->piece_on(from) == EMPTY);
+ assert(this->piece_on(capsq) == EMPTY);
+
+ // Replace captured piece:
+ set_bit(&(byColorBB[them]), capsq);
+ set_bit(&(byTypeBB[PAWN]), capsq);
+ set_bit(&(byTypeBB[0]), capsq);
+ board[capsq] = pawn_of_color(them);
+
+ // Remove moving piece from destination square:
+ clear_bit(&(byColorBB[us]), to);
+ clear_bit(&(byTypeBB[PAWN]), to);
+ clear_bit(&(byTypeBB[0]), to);
+ board[to] = EMPTY;
+
+ // Replace moving piece at source square:
+ set_bit(&(byColorBB[us]), from);
+ set_bit(&(byTypeBB[PAWN]), from);
+ set_bit(&(byTypeBB[0]), from);
+ board[from] = pawn_of_color(us);
+
+ // Update piece list:
+ pieceList[us][PAWN][index[to]] = from;
+ index[from] = index[to];
+ pieceList[them][PAWN][pieceCount[them][PAWN]] = capsq;
+ index[capsq] = pieceCount[them][PAWN];
+
+ // Update piece count:
+ pieceCount[them][PAWN]++;
+}
+
+
+/// Position::do_null_move makes() a "null move": It switches the side to move
+/// and updates the hash key without executing any move on the board.
+
+void Position::do_null_move(UndoInfo &u) {
+ assert(this->is_ok());
+ assert(!this->is_check());
+
+ // Back up the information necessary to undo the null move to the supplied
+ // UndoInfo object. In the case of a null move, the only thing we need to
+ // remember is the last move made and the en passant square.
+ u.lastMove = lastMove;
+ u.epSquare = epSquare;
+
+ // Save the current key to the history[] array, in order to be able to
+ // detect repetition draws:
+ history[gamePly] = key;
+
+ // Update the necessary information.
+ sideToMove = opposite_color(sideToMove);
+ if(epSquare != SQ_NONE)
+ key ^= zobEp[epSquare];
+ epSquare = SQ_NONE;
+ rule50++;
+ gamePly++;
+ key ^= zobSideToMove;
+
+ mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
+ egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
+
+ assert(this->is_ok());
+}
+
+
+/// Position::undo_null_move() unmakes a "null move".
+
+void Position::undo_null_move(const UndoInfo &u) {
+ assert(this->is_ok());
+ assert(!this->is_check());
+
+ // Restore information from the supplied UndoInfo object:
+ lastMove = u.lastMove;
+ epSquare = u.epSquare;
+ if(epSquare != SQ_NONE)
+ key ^= zobEp[epSquare];
+
+ // Update the necessary information.
+ sideToMove = opposite_color(sideToMove);
+ rule50--;
+ gamePly--;
+ key ^= zobSideToMove;
+
+ mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
+ egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
+
+ assert(this->is_ok());
+}
+
+
+/// Position::see() is a static exchange evaluator: It tries to estimate the
+/// material gain or loss resulting from a move. There are two versions of
+/// this function: One which takes a move as input, and one which takes a
+/// 'from' and a 'to' square. The function does not yet understand promotions
+/// or en passant captures.
+
+int Position::see(Square from, Square to) const {
+ // Approximate material values, with pawn = 1:
+ static const int seeValues[18] = {
+ 0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0
+ };
+ Color us, them;
+ Piece piece, capture;
+ Bitboard attackers, occ, b;
+
+ assert(square_is_ok(from));
+ assert(square_is_ok(to));
+
+ // Initialize colors:
+ us = this->color_of_piece_on(from);
+ them = opposite_color(us);
+
+ // Initialize pieces:
+ piece = this->piece_on(from);
+ capture = this->piece_on(to);
+
+ // Find all attackers to the destination square, with the moving piece
+ // removed, but possibly an X-ray attacker added behind it:
+ occ = this->occupied_squares();
+ clear_bit(&occ, from);
+ attackers =
+ (rook_attacks_bb(to, occ) & this->rooks_and_queens()) |
+ (bishop_attacks_bb(to, occ) & this->bishops_and_queens()) |
+ (this->knight_attacks(to) & this->knights()) |
+ (this->king_attacks(to) & this->kings()) |
+ (this->white_pawn_attacks(to) & this->pawns(BLACK)) |
+ (this->black_pawn_attacks(to) & this->pawns(WHITE));
+ attackers &= occ;
+
+ // If the opponent has no attackers, we are finished:
+ if((attackers & this->pieces_of_color(them)) == EmptyBoardBB)
+ return seeValues[capture];
+
+ // The destination square is defended, which makes things rather more
+ // difficult to compute. We proceed by building up a "swap list" containing
+ // the material gain or loss at each stop in a sequence of captures to the
+ // destianation square, where the sides alternately capture, and always
+ // capture with the least valuable piece. After each capture, we look for
+ // new X-ray attacks from behind the capturing piece.
+ int lastCapturingPieceValue = seeValues[piece];
+ int swapList[32], n = 1;
+ Color c = them;
+ PieceType pt;
+
+ swapList[0] = seeValues[capture];
+
+ do {
+ // Locate the least valuable attacker for the side to move. The loop
+ // below looks like it is potentially infinite, but it isn't. We know
+ // that the side to move still has at least one attacker left.
+ for(pt = PAWN; !(attackers&this->pieces_of_color_and_type(c, pt)); pt++)
+ assert(pt < KING);
+
+ // Remove the attacker we just found from the 'attackers' bitboard,
+ // and scan for new X-ray attacks behind the attacker:
+ b = attackers & this->pieces_of_color_and_type(c, pt);
+ occ ^= (b & -b);
+ attackers |=
+ (rook_attacks_bb(to, occ) & this->rooks_and_queens()) |
+ (bishop_attacks_bb(to, occ) & this->bishops_and_queens());
+ attackers &= occ;
+
+ // Add the new entry to the swap list:
+ assert(n < 32);
+ swapList[n] = -swapList[n - 1] + lastCapturingPieceValue;
+ n++;
+
+ // Remember the value of the capturing piece, and change the side to move
+ // before beginning the next iteration:
+ lastCapturingPieceValue = seeValues[pt];
+ c = opposite_color(c);
+
+ // Stop after a king capture:
+ if(pt == KING && (attackers & this->pieces_of_color(c))) {
+ assert(n < 32);
+ swapList[n++] = 100;
+ break;
+ }
+ } while(attackers & this->pieces_of_color(c));
+
+ // Having built the swap list, we negamax through it to find the best
+ // achievable score from the point of view of the side to move:
+ while(--n) swapList[n-1] = Min(-swapList[n], swapList[n-1]);
+
+ return swapList[0];
+}
+
+
+int Position::see(Move m) const {
+ assert(move_is_ok(m));
+ return this->see(move_from(m), move_to(m));
+}
+
+
+/// Position::clear() erases the position object to a pristine state, with an
+/// empty board, white to move, and no castling rights.
+
+void Position::clear() {
+ int i, j;
+
+ for(i = 0; i < 64; i++) {
+ board[i] = EMPTY;
+ index[i] = 0;
+ }
+
+ for(i = 0; i < 2; i++)
+ byColorBB[i] = EmptyBoardBB;
+
+ for(i = 0; i < 7; i++) {
+ byTypeBB[i] = EmptyBoardBB;
+ pieceCount[0][i] = pieceCount[1][i] = 0;
+ for(j = 0; j < 8; j++)
+ pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
+ }
+
+ checkersBB = EmptyBoardBB;
+
+ lastMove = MOVE_NONE;
+
+ sideToMove = WHITE;
+ castleRights = NO_CASTLES;
+ initialKFile = FILE_E;
+ initialKRFile = FILE_H;
+ initialQRFile = FILE_A;
+ epSquare = SQ_NONE;
+ rule50 = 0;
+ gamePly = 0;
+}
+
+
+/// Position::reset_game_ply() simply sets gamePly to 0. It is used from the
+/// UCI interface code, whenever a non-reversible move is made in a
+/// 'position fen <fen> moves m1 m2 ...' command. This makes it possible
+/// for the program to handle games of arbitrary length, as long as the GUI
+/// handles draws by the 50 move rule correctly.
+
+void Position::reset_game_ply() {
+ gamePly = 0;
+}
+
+
+/// Position::put_piece() puts a piece on the given square of the board,
+/// updating the board array, bitboards, and piece counts.
+
+void Position::put_piece(Piece p, Square s) {
+ Color c = color_of_piece(p);
+ PieceType pt = type_of_piece(p);
+
+ board[s] = p;
+ index[s] = pieceCount[c][pt];
+ pieceList[c][pt][index[s]] = s;
+
+ set_bit(&(byTypeBB[pt]), s);
+ set_bit(&(byColorBB[c]), s);
+ set_bit(&byTypeBB[0], s); // HACK: byTypeBB[0] contains all occupied squares.
+
+ pieceCount[c][pt]++;
+
+ if(pt == KING)
+ kingSquare[c] = s;
+}
+
+
+/// Position::allow_oo() gives the given side the right to castle kingside.
+/// Used when setting castling rights during parsing of FEN strings.
+
+void Position::allow_oo(Color c) {
+ castleRights |= (1 + int(c));
+}
+
+
+/// Position::allow_ooo() gives the given side the right to castle queenside.
+/// Used when setting castling rights during parsing of FEN strings.
+
+void Position::allow_ooo(Color c) {
+ castleRights |= (4 + 4*int(c));
+}
+
+
+/// Position::compute_key() computes the hash key of the position. The hash
+/// key is usually updated incrementally as moves are made and unmade, the
+/// compute_key() function is only used when a new position is set up, and
+/// to verify the correctness of the hash key when running in debug mode.
+
+Key Position::compute_key() const {
+ Key result = Key(0ULL);
+
+ for(Square s = SQ_A1; s <= SQ_H8; s++)
+ if(this->square_is_occupied(s))
+ result ^=
+ zobrist[this->color_of_piece_on(s)][this->type_of_piece_on(s)][s];
+
+ if(this->ep_square() != SQ_NONE)
+ result ^= zobEp[this->ep_square()];
+ result ^= zobCastle[castleRights];
+ if(this->side_to_move() == BLACK) result ^= zobSideToMove;
+
+ return result;
+}
+
+
+/// Position::compute_pawn_key() computes the hash key of the position. The
+/// hash key is usually updated incrementally as moves are made and unmade,
+/// the compute_pawn_key() function is only used when a new position is set
+/// up, and to verify the correctness of the pawn hash key when running in
+/// debug mode.
+
+Key Position::compute_pawn_key() const {
+ Key result = Key(0ULL);
+ Bitboard b;
+ Square s;
+
+ for(Color c = WHITE; c <= BLACK; c++) {
+ b = this->pawns(c);
+ while(b) {
+ s = pop_1st_bit(&b);
+ result ^= zobrist[c][PAWN][s];
+ }
+ }
+ return result;
+}
+
+
+/// Position::compute_material_key() computes the hash key of the position.
+/// The hash key is usually updated incrementally as moves are made and unmade,
+/// the compute_material_key() function is only used when a new position is set
+/// up, and to verify the correctness of the material hash key when running in
+/// debug mode.
+
+Key Position::compute_material_key() const {
+ Key result = Key(0ULL);
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(PieceType pt = PAWN; pt <= QUEEN; pt++) {
+ int count = this->piece_count(c, pt);
+ for(int i = 0; i <= count; i++)
+ result ^= zobMaterial[c][pt][i];
+ }
+ return result;
+}
+
+
+/// Position::compute_mg_value() and Position::compute_eg_value() compute the
+/// incremental scores for the middle game and the endgame. These functions
+/// are used to initialize the incremental scores when a new position is set
+/// up, and to verify that the scores are correctly updated by do_move
+/// and undo_move when the program is running in debug mode.
+
+Value Position::compute_mg_value() const {
+ Value result = Value(0);
+ Bitboard b;
+ Square s;
+
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(PieceType pt = PAWN; pt <= KING; pt++) {
+ b = this->pieces_of_color_and_type(c, pt);
+ while(b) {
+ s = pop_1st_bit(&b);
+ assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
+ result += this->mg_pst(c, pt, s);
+ }
+ }
+ result += (this->side_to_move() == WHITE)?
+ (TempoValueMidgame / 2) : -(TempoValueMidgame / 2);
+ return result;
+}
+
+Value Position::compute_eg_value() const {
+ Value result = Value(0);
+ Bitboard b;
+ Square s;
+
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(PieceType pt = PAWN; pt <= KING; pt++) {
+ b = this->pieces_of_color_and_type(c, pt);
+ while(b) {
+ s = pop_1st_bit(&b);
+ assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
+ result += this->eg_pst(c, pt, s);
+ }
+ }
+ result += (this->side_to_move() == WHITE)?
+ (TempoValueEndgame / 2) : -(TempoValueEndgame / 2);
+ return result;
+}
+
+
+/// Position::compute_non_pawn_material() computes the total non-pawn middle
+/// game material score for the given side. Material scores are updated
+/// incrementally during the search, this function is only used while
+/// initializing a new Position object.
+
+Value Position::compute_non_pawn_material(Color c) const {
+ Value result = Value(0);
+ Square s;
+
+ for(PieceType pt = KNIGHT; pt <= QUEEN; pt++) {
+ Bitboard b = this->pieces_of_color_and_type(c, pt);
+ while(b) {
+ s = pop_1st_bit(&b);
+ assert(this->piece_on(s) == piece_of_color_and_type(c, pt));
+ result += piece_value_midgame(pt);
+ }
+ }
+ return result;
+}
+
+
+/// Position::is_mate() returns true or false depending on whether the
+/// side to move is checkmated. Note that this function is currently very
+/// slow, and shouldn't be used frequently inside the search.
+
+bool Position::is_mate() {
+ if(this->is_check()) {
+ MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
+ MOVE_NONE, Depth(0));
+ return mp.get_next_move() == MOVE_NONE;
+ }
+ else
+ return false;
+}
+
+
+/// Position::is_draw() tests whether the position is drawn by material,
+/// repetition, or the 50 moves rule. It does not detect stalemates, this
+/// must be done by the search.
+
+bool Position::is_draw() const {
+ // Draw by material?
+ if(!this->pawns() &&
+ this->non_pawn_material(WHITE) + this->non_pawn_material(BLACK)
+ <= BishopValueMidgame)
+ return true;
+
+ // Draw by the 50 moves rule?
+ if(rule50 > 100 || (rule50 == 100 && !this->is_check()))
+ return true;
+
+ // Draw by repetition?
+ for(int i = 2; i < Min(gamePly, rule50); i += 2)
+ if(history[gamePly - i] == key)
+ return true;
+
+ return false;
+}
+
+
+/// Position::has_mate_threat() tests whether a given color has a mate in one
+/// from the current position. This function is quite slow, but it doesn't
+/// matter, because it is currently only called from PV nodes, which are rare.
+
+bool Position::has_mate_threat(Color c) {
+ UndoInfo u1, u2;
+ Color stm = this->side_to_move();
+
+ // The following lines are useless and silly, but prevents gcc from
+ // emitting a stupid warning stating that u1.lastMove and u1.epSquare might
+ // be used uninitialized.
+ u1.lastMove = lastMove;
+ u1.epSquare = epSquare;
+
+ if(this->is_check())
+ return false;
+
+ // If the input color is not equal to the side to move, do a null move
+ if(c != stm) this->do_null_move(u1);
+
+ MoveStack mlist[120];
+ int count;
+ bool result = false;
+
+ // Generate legal moves
+ count = generate_legal_moves(*this, mlist);
+
+ // Loop through the moves, and see if one of them is mate.
+ for(int i = 0; i < count; i++) {
+ this->do_move(mlist[i].move, u2);
+ if(this->is_mate()) result = true;
+ this->undo_move(mlist[i].move, u2);
+ }
+
+ // Undo null move, if necessary
+ if(c != stm) this->undo_null_move(u1);
+
+ return result;
+}
+
+
+/// Position::init_zobrist() is a static member function which initializes the
+/// various arrays used to compute hash keys.
+
+void Position::init_zobrist() {
+
+ for(int i = 0; i < 2; i++)
+ for(int j = 0; j < 8; j++)
+ for(int k = 0; k < 64; k++)
+ zobrist[i][j][k] = Key(genrand_int64());
+
+ for(int i = 0; i < 64; i++)
+ zobEp[i] = Key(genrand_int64());
+
+ for(int i = 0; i < 16; i++)
+ zobCastle[i] = genrand_int64();
+
+ zobSideToMove = genrand_int64();
+
+ for(int i = 0; i < 2; i++)
+ for(int j = 0; j < 8; j++)
+ for(int k = 0; k < 16; k++)
+ zobMaterial[i][j][k] = (k > 0)? Key(genrand_int64()) : Key(0LL);
+
+ for(int i = 0; i < 16; i++)
+ zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
+}
+
+
+/// Position::init_piece_square_tables() initializes the piece square tables.
+/// This is a two-step operation: First, the white halves of the tables are
+/// copied from the MgPST[][] and EgPST[][] arrays, with a small random number
+/// added to each entry if the "Randomness" UCI parameter is non-zero.
+/// Second, the black halves of the tables are initialized by mirroring
+/// and changing the sign of the corresponding white scores.
+
+void Position::init_piece_square_tables() {
+ int r = get_option_value_int("Randomness"), i;
+ for(Square s = SQ_A1; s <= SQ_H8; s++) {
+ for(Piece p = WP; p <= WK; p++) {
+ i = (r == 0)? 0 : (genrand_int32() % (r*2) - r);
+ MgPieceSquareTable[p][s] = Value(MgPST[p][s] + i);
+ EgPieceSquareTable[p][s] = Value(EgPST[p][s] + i);
+ }
+ }
+ for(Square s = SQ_A1; s <= SQ_H8; s++)
+ for(Piece p = BP; p <= BK; p++) {
+ MgPieceSquareTable[p][s] = -MgPieceSquareTable[p-8][flip_square(s)];
+ EgPieceSquareTable[p][s] = -EgPieceSquareTable[p-8][flip_square(s)];
+ }
+}
+
+
+/// Position::flipped_copy() makes a copy of the input position, but with
+/// the white and black sides reversed. This is only useful for debugging,
+/// especially for finding evaluation symmetry bugs.
+
+void Position::flipped_copy(const Position &pos) {
+ assert(pos.is_ok());
+
+ this->clear();
+
+ // Board
+ for(Square s = SQ_A1; s <= SQ_H8; s++)
+ if(!pos.square_is_empty(s))
+ this->put_piece(Piece(int(pos.piece_on(s)) ^ 8), flip_square(s));
+
+ // Side to move
+ sideToMove = opposite_color(pos.side_to_move());
+
+ // Castling rights
+ if(pos.can_castle_kingside(WHITE)) this->allow_oo(BLACK);
+ if(pos.can_castle_queenside(WHITE)) this->allow_ooo(BLACK);
+ if(pos.can_castle_kingside(BLACK)) this->allow_oo(WHITE);
+ if(pos.can_castle_queenside(BLACK)) this->allow_ooo(WHITE);
+
+ initialKFile = pos.initialKFile;
+ initialKRFile = pos.initialKRFile;
+ initialQRFile = pos.initialQRFile;
+
+ for(Square sq = SQ_A1; sq <= SQ_H8; sq++)
+ castleRightsMask[sq] = ALL_CASTLES;
+ castleRightsMask[make_square(initialKFile, RANK_1)] ^= (WHITE_OO|WHITE_OOO);
+ castleRightsMask[make_square(initialKFile, RANK_8)] ^= (BLACK_OO|BLACK_OOO);
+ castleRightsMask[make_square(initialKRFile, RANK_1)] ^= WHITE_OO;
+ castleRightsMask[make_square(initialKRFile, RANK_8)] ^= BLACK_OO;
+ castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO;
+ castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO;
+
+ // En passant square
+ if(pos.epSquare != SQ_NONE)
+ epSquare = flip_square(pos.epSquare);
+
+ // Checkers
+ this->find_checkers();
+
+ // Hash keys
+ key = this->compute_key();
+ pawnKey = this->compute_pawn_key();
+ materialKey = this->compute_material_key();
+
+ // Incremental scores
+ mgValue = this->compute_mg_value();
+ egValue = this->compute_eg_value();
+
+ // Material
+ npMaterial[WHITE] = this->compute_non_pawn_material(WHITE);
+ npMaterial[BLACK] = this->compute_non_pawn_material(BLACK);
+
+ assert(this->is_ok());
+}
+
+
+/// Position::is_ok() performs some consitency checks for the position object.
+/// This is meant to be helpful when debugging.
+
+bool Position::is_ok() const {
+
+ // What features of the position should be verified?
+ static const bool debugBitboards = false;
+ static const bool debugKingCount = false;
+ static const bool debugKingCapture = false;
+ static const bool debugCheckerCount = false;
+ static const bool debugKey = false;
+ static const bool debugMaterialKey = false;
+ static const bool debugPawnKey = false;
+ static const bool debugIncrementalEval = false;
+ static const bool debugNonPawnMaterial = false;
+ static const bool debugPieceCounts = false;
+ static const bool debugPieceList = false;
+
+ // Side to move OK?
+ if(!color_is_ok(this->side_to_move()))
+ return false;
+
+ // Are the king squares in the position correct?
+ if(this->piece_on(this->king_square(WHITE)) != WK)
+ return false;
+ if(this->piece_on(this->king_square(BLACK)) != BK)
+ return false;
+
+ // Castle files OK?
+ if(!file_is_ok(initialKRFile))
+ return false;
+ if(!file_is_ok(initialQRFile))
+ return false;
+
+ // Do both sides have exactly one king?
+ if(debugKingCount) {
+ int kingCount[2] = {0, 0};
+ for(Square s = SQ_A1; s <= SQ_H8; s++)
+ if(this->type_of_piece_on(s) == KING)
+ kingCount[this->color_of_piece_on(s)]++;
+ if(kingCount[0] != 1 || kingCount[1] != 1)
+ return false;
+ }
+
+ // Can the side to move capture the opponent's king?
+ if(debugKingCapture) {
+ Color us = this->side_to_move();
+ Color them = opposite_color(us);
+ Square ksq = this->king_square(them);
+ if(this->square_is_attacked(ksq, us))
+ return false;
+ }
+
+ // Is there more than 2 checkers?
+ if(debugCheckerCount && count_1s(checkersBB) > 2)
+ return false;
+
+ // Bitboards OK?
+ if(debugBitboards) {
+ // The intersection of the white and black pieces must be empty:
+ if((this->pieces_of_color(WHITE) & this->pieces_of_color(BLACK))
+ != EmptyBoardBB)
+ return false;
+
+ // The union of the white and black pieces must be equal to all
+ // occupied squares:
+ if((this->pieces_of_color(WHITE) | this->pieces_of_color(BLACK))
+ != this->occupied_squares())
+ return false;
+
+ // Separate piece type bitboards must have empty intersections:
+ for(PieceType p1 = PAWN; p1 <= KING; p1++)
+ for(PieceType p2 = PAWN; p2 <= KING; p2++)
+ if(p1 != p2 && (this->pieces_of_type(p1) & this->pieces_of_type(p2)))
+ return false;
+ }
+
+ // En passant square OK?
+ if(this->ep_square() != SQ_NONE) {
+ // The en passant square must be on rank 6, from the point of view of the
+ // side to move.
+ if(pawn_rank(this->side_to_move(), this->ep_square()) != RANK_6)
+ return false;
+ }
+
+ // Hash key OK?
+ if(debugKey && key != this->compute_key())
+ return false;
+
+ // Pawn hash key OK?
+ if(debugPawnKey && pawnKey != this->compute_pawn_key())
+ return false;
+
+ // Material hash key OK?
+ if(debugMaterialKey && materialKey != this->compute_material_key())
+ return false;
+
+ // Incremental eval OK?
+ if(debugIncrementalEval) {
+ if(mgValue != this->compute_mg_value())
+ return false;
+ if(egValue != this->compute_eg_value())
+ return false;
+ }
+
+ // Non-pawn material OK?
+ if(debugNonPawnMaterial) {
+ if(npMaterial[WHITE] != compute_non_pawn_material(WHITE))
+ return false;
+ if(npMaterial[BLACK] != compute_non_pawn_material(BLACK))
+ return false;
+ }
+
+ // Piece counts OK?
+ if(debugPieceCounts)
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(PieceType pt = PAWN; pt <= KING; pt++)
+ if(pieceCount[c][pt] != count_1s(this->pieces_of_color_and_type(c, pt)))
+ return false;
+
+ if(debugPieceList) {
+ for(Color c = WHITE; c <= BLACK; c++)
+ for(PieceType pt = PAWN; pt <= KING; pt++)
+ for(int i = 0; i < pieceCount[c][pt]; i++) {
+ if(this->piece_on(this->piece_list(c, pt, i)) !=
+ piece_of_color_and_type(c, pt))
+ return false;
+ if(index[this->piece_list(c, pt, i)] != i)
+ return false;
+ }
+ }
+
+ return true;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(POSITION_H_INCLUDED)
+#define POSITION_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "bitboard.h"
+#include "color.h"
+#include "direction.h"
+#include "move.h"
+#include "piece.h"
+#include "phase.h"
+#include "square.h"
+#include "value.h"
+
+
+////
+//// Constants
+////
+
+/// FEN string for the initial position:
+const std::string StartPosition =
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+
+/// Maximum number of plies per game (220 should be enough, because the
+/// maximum search depth is 100, and during position setup we reset the
+/// move counter for every non-reversible move):
+const int MaxGameLength = 220;
+
+
+////
+//// Types
+////
+
+/// Castle rights, encoded as bit fields:
+
+enum CastleRights {
+ NO_CASTLES = 0,
+ WHITE_OO = 1, BLACK_OO = 2,
+ WHITE_OOO = 4, BLACK_OOO = 8,
+ ALL_CASTLES = 15
+};
+
+
+/// The UndoInfo struct stores information we need to restore a Position
+/// object to its previous state when we retract a move. Whenever a move
+/// is made on the board (by calling Position::do_move), an UndoInfo object
+/// must be passed as a parameter. When the move is unmade (by calling
+/// Position::undo_move), the same UndoInfo object must be passed again.
+
+struct UndoInfo {
+ int castleRights;
+ Square epSquare;
+ Bitboard checkersBB;
+ Key key, pawnKey, materialKey;
+ int rule50;
+ Move lastMove;
+ PieceType capture;
+ Value mgValue, egValue;
+};
+
+
+/// The position data structure. A position consists of the following data:
+///
+/// * For each piece type, a bitboard representing the squares occupied
+/// by pieces of that type.
+/// * For each color, a bitboard representing the squares occupiecd by
+/// pieces of that color.
+/// * A bitboard of all occupied squares.
+/// * A bitboard of all checking pieces.
+/// * A 64-entry array of pieces, indexed by the squares of the board.
+/// * The current side to move.
+/// * Information about the castling rights for both sides.
+/// * The initial files of the kings and both pairs of rooks. This is
+/// used to implement the Chess960 castling rules.
+/// * The en passant square (which is SQ_NONE if no en passant capture is
+/// possible).
+/// * The squares of the kings for both sides.
+/// * The last move played.
+/// * Hash keys for the position itself, the current pawn structure, and
+/// the current material situation.
+/// * Hash keys for all previous positions in the game (for detecting
+/// repetition draws.
+/// * A counter for detecting 50 move rule draws.
+
+class Position {
+
+ friend class MaterialInfo;
+
+public:
+ // Constructors
+ Position();
+ Position(const Position &pos);
+ Position(const std::string &fen);
+
+ // Text input/output
+ void from_fen(const std::string &fen);
+ const std::string to_fen() const;
+ void print() const;
+
+ // Copying
+ void copy(const Position &pos);
+ void flipped_copy(const Position &pos);
+
+ // The piece on a given square
+ Piece piece_on(Square s) const;
+ PieceType type_of_piece_on(Square s) const;
+ Color color_of_piece_on(Square s) const;
+ bool square_is_empty(Square s) const;
+ bool square_is_occupied(Square s) const;
+ Value midgame_value_of_piece_on(Square s) const;
+ Value endgame_value_of_piece_on(Square s) const;
+
+ // Side to move
+ Color side_to_move() const;
+
+ // Bitboard representation of the position
+ Bitboard empty_squares() const;
+ Bitboard occupied_squares() const;
+ Bitboard pieces_of_color(Color c) const;
+ Bitboard pieces_of_type(PieceType pt) const;
+ Bitboard pieces_of_color_and_type(Color c, PieceType pt) const;
+ Bitboard pawns() const;
+ Bitboard knights() const;
+ Bitboard bishops() const;
+ Bitboard rooks() const;
+ Bitboard queens() const;
+ Bitboard kings() const;
+ Bitboard rooks_and_queens() const;
+ Bitboard bishops_and_queens() const;
+ Bitboard sliders() const;
+ Bitboard pawns(Color c) const;
+ Bitboard knights(Color c) const;
+ Bitboard bishops(Color c) const;
+ Bitboard rooks(Color c) const;
+ Bitboard queens(Color c) const;
+ Bitboard kings(Color c) const;
+ Bitboard rooks_and_queens(Color c) const;
+ Bitboard bishops_and_queens(Color c) const;
+ Bitboard sliders_of_color(Color c) const;
+
+ // Number of pieces of each color and type
+ int piece_count(Color c, PieceType pt) const;
+ int pawn_count(Color c) const;
+ int knight_count(Color c) const;
+ int bishop_count(Color c) const;
+ int rook_count(Color c) const;
+ int queen_count(Color c) const;
+
+ // The en passant square:
+ Square ep_square() const;
+
+ // Current king position for each color
+ Square king_square(Color c) const;
+
+ // Castling rights.
+ bool can_castle_kingside(Color c) const;
+ bool can_castle_queenside(Color c) const;
+ bool can_castle(Color c) const;
+ Square initial_kr_square(Color c) const;
+ Square initial_qr_square(Color c) const;
+
+ // Attack bitboards
+ Bitboard sliding_attacks(Square s, Direction d) const;
+ Bitboard ray_attacks(Square s, SignedDirection d) const;
+ Bitboard pawn_attacks(Color c, Square s) const;
+ Bitboard white_pawn_attacks(Square s) const;
+ Bitboard black_pawn_attacks(Square s) const;
+ Bitboard knight_attacks(Square s) const;
+ Bitboard bishop_attacks(Square s) const;
+ Bitboard rook_attacks(Square s) const;
+ Bitboard queen_attacks(Square s) const;
+ Bitboard king_attacks(Square s) const;
+
+ // Bitboards for pinned pieces and discovered check candidates
+ Bitboard discovered_check_candidates(Color c) const;
+ Bitboard pinned_pieces(Color c) const;
+
+ // Checking pieces
+ Bitboard checkers() const;
+
+ // Piece lists:
+ Square piece_list(Color c, PieceType pt, int index) const;
+ Square pawn_list(Color c, int index) const;
+ Square knight_list(Color c, int index) const;
+ Square bishop_list(Color c, int index) const;
+ Square rook_list(Color c, int index) const;
+ Square queen_list(Color c, int index) const;
+
+ // Attack information for a given square
+ bool square_is_attacked(Square s, Color c) const;
+ Bitboard attacks_to(Square s) const;
+ Bitboard attacks_to(Square s, Color c) const;
+ bool is_check() const;
+ bool piece_attacks_square(Square f, Square t) const;
+ bool white_pawn_attacks_square(Square f, Square t) const;
+ bool black_pawn_attacks_square(Square f, Square t) const;
+ bool knight_attacks_square(Square f, Square t) const;
+ bool bishop_attacks_square(Square f, Square t) const;
+ bool rook_attacks_square(Square f, Square t) const;
+ bool queen_attacks_square(Square f, Square t) const;
+ bool king_attacks_square(Square f, Square t) const;
+
+ // Properties of moves
+ bool move_is_legal(Move m) const;
+ bool move_is_legal(Move m, Bitboard pinned) const;
+ bool move_is_check(Move m) const;
+ bool move_is_check(Move m, Bitboard dcCandidates) const;
+ bool move_is_capture(Move m) const;
+ bool move_is_pawn_push_to_7th(Move m) const;
+ bool move_is_passed_pawn_push(Move m) const;
+ bool move_was_passed_pawn_push(Move m) const;
+ bool move_attacks_square(Move m, Square s) const;
+
+ // Information about pawns
+ bool pawn_is_passed(Color c, Square s) const;
+ bool pawn_is_isolated(Color c, Square s) const;
+ bool pawn_is_doubled(Color c, Square s) const;
+
+ // Open and half-open files
+ bool file_is_open(File f) const;
+ bool file_is_half_open(Color c, File f) const;
+
+ // Weak squares
+ bool square_is_weak(Square s, Color c) const;
+
+ // Doing and undoing moves
+ void backup(UndoInfo &u) const;
+ void restore(const UndoInfo &u);
+ void do_move(Move m, UndoInfo &u);
+ void do_move(Move m, UndoInfo &u, Bitboard dcCandidates);
+ void undo_move(Move m, const UndoInfo &u);
+ void do_null_move(UndoInfo &u);
+ void undo_null_move(const UndoInfo &u);
+
+ // Static exchange evaluation
+ int see(Square from, Square to) const;
+ int see(Move m) const;
+
+ // Accessing hash keys
+ Key get_key() const;
+ Key get_pawn_key() const;
+ Key get_material_key() const;
+
+ // Incremental evaluation
+ Value mg_value() const;
+ Value eg_value() const;
+ Value non_pawn_material(Color c) const;
+ Phase game_phase() const;
+
+ // Game termination checks
+ bool is_mate();
+ bool is_draw() const;
+
+ // Check if one side threatens a mate in one
+ bool has_mate_threat(Color c);
+
+ // Number of plies since the last non-reversible move
+ int rule_50_counter() const;
+
+ // Other properties of the position
+ bool opposite_colored_bishops() const;
+ bool has_pawn_on_7th(Color c) const;
+
+ // Reset the gamePly variable to 0
+ void reset_game_ply();
+
+ // Position consistency check, for debugging
+ bool is_ok() const;
+
+ // Static member functions:
+ static void init_zobrist();
+ static void init_piece_square_tables();
+
+private:
+ // Initialization helper functions (used while setting up a position)
+ void clear();
+ void put_piece(Piece p, Square s);
+ void allow_oo(Color c);
+ void allow_ooo(Color c);
+
+ // Helper functions for doing and undoing moves
+ void do_castle_move(Move m);
+ void do_promotion_move(Move m, UndoInfo &u);
+ void do_ep_move(Move m);
+ void undo_castle_move(Move m);
+ void undo_promotion_move(Move m, const UndoInfo &u);
+ void undo_ep_move(Move m);
+ void find_checkers();
+
+ // Computing hash keys from scratch (for initialization and debugging)
+ Key compute_key() const;
+ Key compute_pawn_key() const;
+ Key compute_material_key() const;
+
+ // Computing incremental evaluation scores and material counts
+ Value mg_pst(Color c, PieceType pt, Square s) const;
+ Value eg_pst(Color c, PieceType pt, Square s) const;
+ Value compute_mg_value() const;
+ Value compute_eg_value() const;
+ Value compute_non_pawn_material(Color c) const;
+
+ // Bitboards
+ Bitboard byColorBB[2], byTypeBB[8];
+ Bitboard checkersBB;
+
+ // Board
+ Piece board[64];
+
+ // Piece counts
+ int pieceCount[2][8]; // [color][pieceType]
+
+ // Piece lists
+ Square pieceList[2][8][16]; // [color][pieceType][index]
+ int index[64];
+
+ // Other info
+ Color sideToMove;
+ int castleRights;
+ File initialKFile, initialKRFile, initialQRFile;
+ Square epSquare;
+ Square kingSquare[2];
+ Move lastMove;
+ Key key, pawnKey, materialKey, history[MaxGameLength];
+ int rule50, gamePly;
+ Value mgValue, egValue;
+ Value npMaterial[2];
+
+ // Static variables
+ static int castleRightsMask[64];
+ static Key zobrist[2][8][64];
+ static Key zobEp[64];
+ static Key zobCastle[16];
+ static Key zobMaterial[2][8][16];
+ static Key zobSideToMove;
+ static Value MgPieceSquareTable[16][64];
+ static Value EgPieceSquareTable[16][64];
+};
+
+
+////
+//// Inline functions
+////
+
+inline Piece Position::piece_on(Square s) const {
+ return board[s];
+}
+
+inline Color Position::color_of_piece_on(Square s) const {
+ return color_of_piece(this->piece_on(s));
+}
+
+inline PieceType Position::type_of_piece_on(Square s) const {
+ return type_of_piece(this->piece_on(s));
+}
+
+inline bool Position::square_is_empty(Square s) const {
+ return this->piece_on(s) == EMPTY;
+}
+
+inline bool Position::square_is_occupied(Square s) const {
+ return !this->square_is_empty(s);
+}
+
+inline Value Position::midgame_value_of_piece_on(Square s) const {
+ return piece_value_midgame(this->piece_on(s));
+}
+
+inline Value Position::endgame_value_of_piece_on(Square s) const {
+ return piece_value_endgame(this->piece_on(s));
+}
+
+inline Color Position::side_to_move() const {
+ return sideToMove;
+}
+
+inline Bitboard Position::occupied_squares() const {
+ return byTypeBB[0];
+}
+
+inline Bitboard Position::empty_squares() const {
+ return ~(this->occupied_squares());
+}
+
+inline Bitboard Position::pieces_of_color(Color c) const {
+ return byColorBB[c];
+}
+
+inline Bitboard Position::pieces_of_type(PieceType pt) const {
+ return byTypeBB[pt];
+}
+
+inline Bitboard Position::pieces_of_color_and_type(Color c, PieceType pt)
+ const {
+ return this->pieces_of_color(c) & this->pieces_of_type(pt);
+}
+
+inline Bitboard Position::pawns() const {
+ return this->pieces_of_type(PAWN);
+}
+
+inline Bitboard Position::knights() const {
+ return this->pieces_of_type(KNIGHT);
+}
+
+inline Bitboard Position::bishops() const {
+ return this->pieces_of_type(BISHOP);
+}
+
+inline Bitboard Position::rooks() const {
+ return this->pieces_of_type(ROOK);
+}
+
+inline Bitboard Position::queens() const {
+ return this->pieces_of_type(QUEEN);
+}
+
+inline Bitboard Position::kings() const {
+ return this->pieces_of_type(KING);
+}
+
+inline Bitboard Position::rooks_and_queens() const {
+ return this->rooks() | this->queens();
+}
+
+inline Bitboard Position::bishops_and_queens() const {
+ return this->bishops() | this->queens();
+}
+
+inline Bitboard Position::sliders() const {
+ return this->bishops() | this->queens() | this->rooks();
+}
+
+inline Bitboard Position::pawns(Color c) const {
+ return this->pieces_of_color_and_type(c, PAWN);
+}
+
+inline Bitboard Position::knights(Color c) const {
+ return this->pieces_of_color_and_type(c, KNIGHT);
+}
+
+inline Bitboard Position::bishops(Color c) const {
+ return this->pieces_of_color_and_type(c, BISHOP);
+}
+
+inline Bitboard Position::rooks(Color c) const {
+ return this->pieces_of_color_and_type(c, ROOK);
+}
+
+inline Bitboard Position::queens(Color c) const {
+ return this->pieces_of_color_and_type(c, QUEEN);
+}
+
+inline Bitboard Position::kings(Color c) const {
+ return this->pieces_of_color_and_type(c, KING);
+}
+
+inline Bitboard Position::rooks_and_queens(Color c) const {
+ return this->rooks_and_queens() & this->pieces_of_color(c);
+}
+
+inline Bitboard Position::bishops_and_queens(Color c) const {
+ return this->bishops_and_queens() & this->pieces_of_color(c);
+}
+
+inline Bitboard Position::sliders_of_color(Color c) const {
+ return this->sliders() & this->pieces_of_color(c);
+}
+
+inline int Position::piece_count(Color c, PieceType pt) const {
+ return pieceCount[c][pt];
+}
+
+inline int Position::pawn_count(Color c) const {
+ return this->piece_count(c, PAWN);
+}
+
+inline int Position::knight_count(Color c) const {
+ return this->piece_count(c, KNIGHT);
+}
+
+inline int Position::bishop_count(Color c) const {
+ return this->piece_count(c, BISHOP);
+}
+
+inline int Position::rook_count(Color c) const {
+ return this->piece_count(c, ROOK);
+}
+
+inline int Position::queen_count(Color c) const {
+ return this->piece_count(c, QUEEN);
+}
+
+inline Square Position::piece_list(Color c, PieceType pt, int index) const {
+ return pieceList[c][pt][index];
+}
+
+inline Square Position::pawn_list(Color c, int index) const {
+ return this->piece_list(c, PAWN, index);
+}
+
+inline Square Position::knight_list(Color c, int index) const {
+ return this->piece_list(c, KNIGHT, index);
+}
+
+inline Square Position::bishop_list(Color c, int index) const {
+ return this->piece_list(c, BISHOP, index);
+}
+
+inline Square Position::rook_list(Color c, int index) const {
+ return this->piece_list(c, ROOK, index);
+}
+
+inline Square Position::queen_list(Color c, int index) const {
+ return this->piece_list(c, QUEEN, index);
+}
+
+inline Square Position::ep_square() const {
+ return epSquare;
+}
+
+inline Square Position::king_square(Color c) const {
+ return kingSquare[c];
+}
+
+inline bool Position::can_castle_kingside(Color side) const {
+ return castleRights & (1+int(side));
+}
+
+inline bool Position::can_castle_queenside(Color side) const {
+ return castleRights & (4+4*int(side));
+}
+
+inline bool Position::can_castle(Color side) const {
+ return can_castle_kingside(side) || can_castle_queenside(side);
+}
+
+inline Square Position::initial_kr_square(Color c) const {
+ return relative_square(c, make_square(initialKRFile, RANK_1));
+}
+
+inline Square Position::initial_qr_square(Color c) const {
+ return relative_square(c, make_square(initialQRFile, RANK_1));
+}
+
+inline Bitboard Position::pawn_attacks(Color c, Square s) const {
+ return StepAttackBB[pawn_of_color(c)][s];
+}
+
+inline Bitboard Position::white_pawn_attacks(Square s) const {
+ return this->pawn_attacks(WHITE, s);
+}
+
+inline Bitboard Position::black_pawn_attacks(Square s) const {
+ return this->pawn_attacks(BLACK, s);
+}
+
+inline Bitboard Position::knight_attacks(Square s) const {
+ return StepAttackBB[KNIGHT][s];
+}
+
+inline Bitboard Position::rook_attacks(Square s) const {
+ return rook_attacks_bb(s, this->occupied_squares());
+}
+
+inline Bitboard Position::bishop_attacks(Square s) const {
+ return bishop_attacks_bb(s, this->occupied_squares());
+}
+
+inline Bitboard Position::queen_attacks(Square s) const {
+ return this->rook_attacks(s) | this->bishop_attacks(s);
+}
+
+inline Bitboard Position::king_attacks(Square s) const {
+ return StepAttackBB[KING][s];
+}
+
+inline Bitboard Position::checkers() const {
+ return checkersBB;
+}
+
+inline bool Position::is_check() const {
+ return this->checkers() != EmptyBoardBB;
+}
+
+inline bool Position::white_pawn_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->white_pawn_attacks(f), t);
+}
+
+inline bool Position::black_pawn_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->black_pawn_attacks(f), t);
+}
+
+inline bool Position::knight_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->knight_attacks(f), t);
+}
+
+inline bool Position::bishop_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->bishop_attacks(f), t);
+}
+
+inline bool Position::rook_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->rook_attacks(f), t);
+}
+
+inline bool Position::queen_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->queen_attacks(f), t);
+}
+
+inline bool Position::king_attacks_square(Square f, Square t) const {
+ return bit_is_set(this->king_attacks(f), t);
+}
+
+inline bool Position::pawn_is_passed(Color c, Square s) const {
+ return !(this->pawns(opposite_color(c)) & passed_pawn_mask(c, s));
+}
+
+inline bool Position::pawn_is_isolated(Color c, Square s) const {
+ return !(this->pawns(c) & neighboring_files_bb(s));
+}
+
+inline bool Position::pawn_is_doubled(Color c, Square s) const {
+ return this->pawns(c) & squares_behind(c, s);
+}
+
+inline bool Position::file_is_open(File f) const {
+ return !(this->pawns() & file_bb(f));
+}
+
+inline bool Position::file_is_half_open(Color c, File f) const {
+ return !(this->pawns(c) & file_bb(f));
+}
+
+inline bool Position::square_is_weak(Square s, Color c) const {
+ return !(this->pawns(c) & outpost_mask(opposite_color(c), s));
+}
+
+inline Key Position::get_key() const {
+ return key;
+}
+
+inline Key Position::get_pawn_key() const {
+ return pawnKey;
+}
+
+inline Key Position::get_material_key() const {
+ return materialKey;
+}
+
+inline Value Position::mg_pst(Color c, PieceType pt, Square s) const {
+ return MgPieceSquareTable[piece_of_color_and_type(c, pt)][s];
+}
+
+inline Value Position::eg_pst(Color c, PieceType pt, Square s) const {
+ return EgPieceSquareTable[piece_of_color_and_type(c, pt)][s];
+}
+
+inline Value Position::mg_value() const {
+ return mgValue;
+}
+
+inline Value Position::eg_value() const {
+ return egValue;
+}
+
+inline Value Position::non_pawn_material(Color c) const {
+ return npMaterial[c];
+}
+
+inline Phase Position::game_phase() const {
+
+ // The purpose of the Value(325) terms below is to make sure the difference
+ // between MidgameLimit and EndgameLimit is a power of 2, which should make
+ // the division at the end of the function a bit faster.
+
+ static const Value MidgameLimit =
+ 2*QueenValueMidgame+2*RookValueMidgame+6*BishopValueMidgame+Value(325);
+ static const Value EndgameLimit = 4*RookValueMidgame-Value(325);
+ Value npm = this->non_pawn_material(WHITE) + this->non_pawn_material(BLACK);
+
+ if(npm >= MidgameLimit)
+ return PHASE_MIDGAME;
+ else if(npm <= EndgameLimit)
+ return PHASE_ENDGAME;
+ else
+ return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
+}
+
+inline bool Position::move_is_pawn_push_to_7th(Move m) const {
+ Color c = this->side_to_move();
+ return
+ this->piece_on(move_from(m)) == pawn_of_color(c) &&
+ pawn_rank(c, move_to(m)) == RANK_7;
+}
+
+inline bool Position::move_is_passed_pawn_push(Move m) const {
+ Color c = this->side_to_move();
+ return
+ this->piece_on(move_from(m)) == pawn_of_color(c) &&
+ this->pawn_is_passed(c, move_to(m));
+}
+
+inline bool Position::move_was_passed_pawn_push(Move m) const {
+ Color c = opposite_color(this->side_to_move());
+ return
+ this->piece_on(move_to(m)) == pawn_of_color(c) &&
+ this->pawn_is_passed(c, move_to(m));
+}
+
+inline int Position::rule_50_counter() const {
+ return rule50;
+}
+
+inline bool Position::opposite_colored_bishops() const {
+ return
+ this->bishop_count(WHITE) == 1 && this->bishop_count(BLACK) == 1 &&
+ square_color(this->bishop_list(WHITE, 0)) !=
+ square_color(this->bishop_list(BLACK, 0));
+}
+
+inline bool Position::has_pawn_on_7th(Color c) const {
+ return this->pawns(c) & relative_rank_bb(c, RANK_7);
+}
+
+
+#endif // !defined(POSITION_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(PSQTAB_H_INCLUDED)
+#define PSQTAB_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "position.h"
+
+
+////
+//// Variables
+////
+
+static const int MgPST[][64] = {
+ { },
+ { // Pawn
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 166, 192, 204, 216, 216, 204, 192, 166,
+ 166, 192, 210, 242, 242, 210, 192, 166,
+ 166, 192, 220, 268, 268, 220, 192, 166,
+ 166, 192, 220, 242, 242, 220, 192, 166,
+ 166, 192, 210, 216, 216, 210, 192, 166,
+ 166, 192, 204, 216, 216, 204, 192, 166,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ },
+ { // Knight
+ 704, 730, 756, 768, 768, 756, 730, 704,
+ 743, 768, 794, 807, 807, 794, 768, 743,
+ 781, 807, 832, 844, 844, 832, 807, 781,
+ 807, 832, 857, 870, 870, 857, 832, 807,
+ 820, 844, 870, 883, 883, 870, 844, 820,
+ 820, 844, 870, 883, 883, 870, 844, 820,
+ 781, 807, 832, 844, 844, 832, 807, 781,
+ 650, 768, 794, 807, 807, 794, 768, 650
+ },
+ { // Bishop
+ 786, 786, 792, 797, 797, 792, 786, 786,
+ 812, 832, 827, 832, 832, 827, 832, 812,
+ 817, 827, 842, 837, 837, 842, 827, 817,
+ 822, 832, 837, 852, 852, 837, 832, 822,
+ 822, 832, 837, 852, 852, 837, 832, 822,
+ 817, 827, 842, 837, 837, 842, 827, 817,
+ 812, 832, 827, 832, 832, 827, 832, 812,
+ 812, 812, 817, 822, 822, 817, 812, 812
+ },
+ { // Rook
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267,
+ 1267, 1275, 1282, 1289, 1289, 1282, 1275, 1267
+ },
+ { // Queen
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560,
+ 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560
+ },
+ { // King
+ 302, 328, 276, 225, 225, 276, 328, 302,
+ 276, 302, 251, 200, 200, 251, 302, 276,
+ 225, 251, 200, 149, 149, 200, 251, 225,
+ 200, 225, 175, 124, 124, 175, 225, 200,
+ 175, 200, 149, 98, 98, 149, 200, 175,
+ 149, 175, 124, 72, 72, 124, 175, 149,
+ 124, 149, 98, 47, 47, 98, 149, 124,
+ 98, 124, 72, 21, 21, 72, 124, 98,
+ }
+};
+
+
+static const int EgPST[][64] = {
+ { },
+ { // Pawn
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ },
+ { // Knight
+ 730, 756, 781, 794, 794, 781, 756, 730,
+ 756, 781, 807, 820, 820, 807, 781, 756,
+ 781, 807, 832, 844, 844, 832, 807, 781,
+ 794, 820, 844, 857, 857, 844, 820, 794,
+ 794, 820, 844, 857, 857, 844, 820, 794,
+ 781, 807, 832, 844, 844, 832, 807, 781,
+ 756, 781, 807, 820, 820, 807, 781, 756,
+ 730, 756, 781, 794, 794, 781, 756, 730
+ },
+ { // Bishop
+ 786, 802, 809, 817, 817, 809, 802, 786,
+ 802, 817, 825, 832, 832, 825, 817, 802,
+ 809, 825, 832, 839, 839, 832, 825, 809,
+ 817, 832, 839, 847, 847, 839, 832, 817,
+ 817, 832, 839, 847, 847, 839, 832, 817,
+ 809, 825, 832, 839, 839, 832, 825, 809,
+ 802, 817, 825, 832, 832, 825, 817, 802,
+ 786, 802, 809, 817, 817, 809, 802, 786
+ },
+ { // Rook
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282,
+ 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282
+ },
+ { // Queen
+ 2499, 2520, 2530, 2540, 2540, 2530, 2520, 2499,
+ 2520, 2540, 2550, 2560, 2560, 2550, 2540, 2520,
+ 2530, 2550, 2560, 2570, 2570, 2560, 2550, 2530,
+ 2540, 2560, 2570, 2580, 2580, 2570, 2560, 2540,
+ 2540, 2560, 2570, 2580, 2580, 2570, 2560, 2540,
+ 2530, 2550, 2560, 2570, 2570, 2560, 2550, 2530,
+ 2520, 2540, 2550, 2560, 2560, 2550, 2540, 2520,
+ 2499, 2520, 2530, 2540, 2540, 2530, 2520, 2499
+ },
+ { // King
+ 16, 78, 108, 139, 139, 108, 78, 16,
+ 78, 139, 170, 200, 200, 170, 139, 78,
+ 108, 170, 200, 230, 230, 200, 170, 108,
+ 139, 200, 230, 261, 261, 230, 200, 139,
+ 139, 200, 230, 261, 261, 230, 200, 139,
+ 108, 170, 200, 230, 230, 200, 170, 108,
+ 78, 139, 170, 200, 200, 170, 139, 78,
+ 16, 78, 108, 139, 139, 108, 78, 16
+ }
+};
+
+
+#endif // !defined(PSQTAB_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstring>
+#include <iomanip>
+#include <string>
+#include <sstream>
+
+#include "movepick.h"
+#include "san.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ /// Types
+
+ enum Ambiguity {
+ AMBIGUITY_NONE,
+ AMBIGUITY_FILE,
+ AMBIGUITY_RANK,
+ AMBIGUITY_BOTH
+ };
+
+
+ /// Functions
+
+ Ambiguity move_ambiguity(Position &pos, Move m);
+ const std::string time_string(int milliseconds);
+ const std::string score_string(Value v);
+}
+
+
+////
+//// Functions
+////
+
+/// move_to_san() takes a position and a move as input, where it is assumed
+/// that the move is a legal move from the position. The return value is
+/// a string containing the move in short algebraic notation.
+
+const std::string move_to_san(Position &pos, Move m) {
+ std::string str;
+
+ assert(pos.is_ok());
+ assert(move_is_ok(m));
+
+ if(m == MOVE_NONE) {
+ str = "(none)";
+ return str;
+ }
+ else if(m == MOVE_NULL) {
+ str = "(null)";
+ return str;
+ }
+ else if(move_is_long_castle(m))
+ str = "O-O-O";
+ else if(move_is_short_castle(m))
+ str = "O-O";
+ else {
+ Square from, to;
+ Piece pc;
+
+ from = move_from(m);
+ to = move_to(m);
+ pc = pos.piece_on(move_from(m));
+
+ str = "";
+
+ if(type_of_piece(pc) == PAWN) {
+ if(pos.move_is_capture(m))
+ str += file_to_char(square_file(move_from(m)));
+ }
+ else {
+ str += piece_type_to_char(type_of_piece(pc), true);
+
+ Ambiguity amb = move_ambiguity(pos, m);
+ switch(amb) {
+
+ case AMBIGUITY_NONE:
+ break;
+
+ case AMBIGUITY_FILE:
+ str += file_to_char(square_file(from));
+ break;
+
+ case AMBIGUITY_RANK:
+ str += rank_to_char(square_rank(from));
+ break;
+
+ case AMBIGUITY_BOTH:
+ str += square_to_string(from);
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ if(pos.move_is_capture(m))
+ str += "x";
+
+ str += square_to_string(move_to(m));
+
+ if(move_promotion(m)) {
+ str += "=";
+ str += piece_type_to_char(move_promotion(m), true);
+ }
+ }
+
+ // Is the move check? We don't use pos.move_is_check(m) here, because
+ // Position::move_is_check doesn't detect all checks (not castling moves,
+ // promotions and en passant captures).
+ UndoInfo u;
+ pos.do_move(m, u);
+ if(pos.is_check())
+ str += pos.is_mate()? "#" : "+";
+ pos.undo_move(m, u);
+
+ return str;
+}
+
+
+/// move_from_san() takes a position and a string as input, and tries to
+/// interpret the string as a move in short algebraic notation. On success,
+/// the move is returned. On failure (i.e. if the string is unparsable, or
+/// if the move is illegal or ambiguous), MOVE_NONE is returned.
+
+Move move_from_san(Position &pos, const std::string &movestr) {
+ assert(pos.is_ok());
+
+ MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
+ MOVE_NONE, OnePly);
+
+ // Castling moves
+ if(movestr == "O-O-O") {
+ Move m;
+ while((m = mp.get_next_move()) != MOVE_NONE)
+ if(move_is_long_castle(m) && pos.move_is_legal(m))
+ return m;
+ return MOVE_NONE;
+ }
+ else if(movestr == "O-O") {
+ Move m;
+ while((m = mp.get_next_move()) != MOVE_NONE)
+ if(move_is_short_castle(m) && pos.move_is_legal(m))
+ return m;
+ return MOVE_NONE;
+ }
+
+ // Normal moves
+ const char *cstr = movestr.c_str();
+ const char *c;
+ char *cc;
+ char str[10];
+ int i;
+
+ // Initialize str[] by making a copy of movestr with the characters
+ // 'x', '=', '+' and '#' removed.
+ cc = str;
+ for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
+ if(!strchr("x=+#", *c)) {
+ *cc = strchr("nrq", *c)? toupper(*c) : *c;
+ cc++;
+ }
+ *cc = '\0';
+
+ int left = 0, right = strlen(str) - 1;
+ PieceType pt = NO_PIECE_TYPE, promotion;
+ Square to;
+ File fromFile = FILE_NONE;
+ Rank fromRank = RANK_NONE;
+
+ // Promotion?
+ if(strchr("BNRQ", str[right])) {
+ promotion = piece_type_from_char(str[right]);
+ right--;
+ }
+ else
+ promotion = NO_PIECE_TYPE;
+
+ // Find the moving piece:
+ if(left < right) {
+ if(strchr("BNRQK", str[left])) {
+ pt = piece_type_from_char(str[left]);
+ left++;
+ }
+ else
+ pt = PAWN;
+ }
+
+ // Find the to square:
+ if(left < right) {
+ if(str[right] < '1' || str[right] > '8' ||
+ str[right-1] < 'a' || str[right-1] > 'h')
+ return MOVE_NONE;
+ to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
+ right -= 2;
+ }
+ else
+ return MOVE_NONE;
+
+ // Find the file and/or rank of the from square:
+ if(left <= right) {
+ if(strchr("abcdefgh", str[left])) {
+ fromFile = file_from_char(str[left]);
+ left++;
+ }
+ if(strchr("12345678", str[left]))
+ fromRank = rank_from_char(str[left]);
+ }
+
+ // Look for a matching move:
+ Move m, move = MOVE_NONE;
+ int matches = 0;
+
+ while((m = mp.get_next_move()) != MOVE_NONE) {
+ bool match = true;
+ if(pos.type_of_piece_on(move_from(m)) != pt)
+ match = false;
+ else if(move_to(m) != to)
+ match = false;
+ else if(move_promotion(m) != promotion)
+ match = false;
+ else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
+ match = false;
+ else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
+ match = false;
+ if(match) {
+ move = m;
+ matches++;
+ }
+ }
+
+ if(matches == 1)
+ return move;
+ else
+ return MOVE_NONE;
+}
+
+
+/// line_to_san() takes a position and a line (an array of moves representing
+/// a sequence of legal moves from the position) as input, and returns a
+/// string containing the line in short algebraic notation. If the boolean
+/// parameter 'breakLines' is true, line breaks are inserted, with a line
+/// length of 80 characters. After a line break, 'startColumn' spaces are
+/// inserted at the beginning of the new line.
+
+const std::string line_to_san(const Position &pos, Move line[], int startColumn,
+ bool breakLines) {
+ Position p = Position(pos);
+ UndoInfo u;
+ std::stringstream s;
+ std::string moveStr;
+ int length, maxLength;
+
+ length = 0;
+ maxLength = 80 - startColumn;
+
+ for(int i = 0; line[i] != MOVE_NONE; i++) {
+ moveStr = move_to_san(p, line[i]);
+ length += moveStr.length() + 1;
+ if(breakLines && length > maxLength) {
+ s << "\n";
+ for(int j = 0; j < startColumn; j++)
+ s << " ";
+ length = moveStr.length() + 1;
+ }
+ s << moveStr << " ";
+
+ if(line[i] == MOVE_NULL)
+ p.do_null_move(u);
+ else
+ p.do_move(line[i], u);
+ }
+
+ return s.str();
+}
+
+
+/// pretty_pv() creates a human-readable string from a position and a PV.
+/// It is used to write search information to the log file (which is created
+/// when the UCI parameter "Use Search Log" is "true").
+
+const std::string pretty_pv(const Position &pos, int time, int depth,
+ uint64_t nodes, Value score, Move pv[]) {
+ std::stringstream s;
+
+ // Depth
+ s << std::setw(2) << std::setfill(' ') << depth << " ";
+
+ // Score
+ s << std::setw(8) << score_string(score);
+
+ // Time
+ s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
+
+ // Nodes
+ if(nodes < 1000000ULL)
+ s << std::setw(8) << std::setfill(' ') << nodes << " ";
+ else if(nodes < 1000000000ULL)
+ s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
+ else
+ s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
+
+ // PV
+ s << line_to_san(pos, pv, 30, true);
+
+ return s.str();
+}
+
+
+namespace {
+
+ Ambiguity move_ambiguity(Position &pos, Move m) {
+ Square from, to;
+ Piece pc;
+
+ from = move_from(m);
+ to = move_to(m);
+ pc = pos.piece_on(from);
+
+ // King moves are never ambiguous, because there is never two kings of
+ // the same color.
+ if(type_of_piece(pc) == KING)
+ return AMBIGUITY_NONE;
+
+ MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
+ MOVE_NONE, OnePly);
+ Move mv, moveList[8];
+ int i, j, n;
+
+ n = 0;
+ while((mv = mp.get_next_move()) != MOVE_NONE)
+ if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
+ && pos.move_is_legal(mv))
+ moveList[n++] = mv;
+ if(n == 1)
+ return AMBIGUITY_NONE;
+
+ j = 0;
+ for(i = 0; i < n; i++)
+ if(square_file(move_from(moveList[i])) == square_file(from))
+ j++;
+ if(j == 1)
+ return AMBIGUITY_FILE;
+
+ j = 0;
+ for(i = 0; i < n; i++)
+ if(square_rank(move_from(moveList[i])) == square_rank(from))
+ j++;
+ if(j == 1)
+ return AMBIGUITY_RANK;
+
+ return AMBIGUITY_BOTH;
+ }
+
+
+ const std::string time_string(int milliseconds) {
+ std::stringstream s;
+
+ int hours = milliseconds / (1000 * 60 * 60);
+ int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
+ int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
+
+ if(hours)
+ s << hours << ':';
+ s << std::setw(2) << std::setfill('0') << minutes << ':';
+ s << std::setw(2) << std::setfill('0') << seconds;
+
+ return s.str();
+ }
+
+
+ const std::string score_string(Value v) {
+ std::stringstream s;
+
+ if(abs(v) >= VALUE_MATE - 200) {
+ if(v < 0)
+ s << "-#" << (VALUE_MATE + v) / 2;
+ else
+ s << "#" << (VALUE_MATE - v + 1) / 2;
+ }
+ else {
+ float floatScore = float(v) / float(PawnValueMidgame);
+ if(v >= 0)
+ s << '+';
+ s << std::setprecision(2) << std::fixed << floatScore;
+ }
+ return s.str();
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(SAN_H_INCLUDED)
+#define SAN_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include <string>
+
+#include "move.h"
+#include "position.h"
+#include "value.h"
+
+
+////
+//// Prototypes
+////
+
+extern const std::string move_to_san(Position &pos, Move m);
+extern Move move_from_san(Position &pos, const std::string &str);
+extern const std::string line_to_san(const Position &pos, Move line[],
+ int startColumn, bool breakLines);
+extern const std::string pretty_pv(const Position &pos, int time, int depth,
+ uint64_t nodes, Value score, Move pv[]);
+
+
+#endif // !defined(SAN_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(SCALE_H_INCLUDED)
+#define SCALE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "value.h"
+
+
+////
+//// Types
+////
+
+enum ScaleFactor {
+ SCALE_FACTOR_ZERO = 0,
+ SCALE_FACTOR_NORMAL = 64,
+ SCALE_FACTOR_MAX = 128,
+ SCALE_FACTOR_NONE = 255
+};
+
+
+////
+//// Inline functions
+////
+
+inline Value apply_scale_factor(Value v, ScaleFactor f) {
+ return Value((v * f) / int(SCALE_FACTOR_NORMAL));
+}
+
+
+#endif // !defined(SCALE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include "book.h"
+#include "evaluate.h"
+#include "history.h"
+#include "misc.h"
+#include "movepick.h"
+#include "san.h"
+#include "search.h"
+#include "thread.h"
+#include "tt.h"
+#include "ucioption.h"
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ /// Types
+
+ // The RootMove class is used for moves at the root at the tree. For each
+ // root move, we store a score, a node count, and a PV (really a refutation
+ // in the case of moves which fail low).
+
+ class RootMove {
+
+ public:
+ RootMove();
+ Move move;
+ Value score;
+ int64_t nodes, cumulativeNodes;
+ Move pv[PLY_MAX_PLUS_2];
+ };
+
+
+ // The RootMoveList class is essentially an array of RootMove objects, with
+ // a handful of methods for accessing the data in the individual moves.
+
+ class RootMoveList {
+
+ public:
+ RootMoveList(Position &pos, Move searchMoves[]);
+ Move get_move(int moveNum) const;
+ Value get_move_score(int moveNum) const;
+ void set_move_score(int moveNum, Value score);
+ void set_move_nodes(int moveNum, int64_t nodes);
+ void set_move_pv(int moveNum, const Move pv[]);
+ Move get_move_pv(int moveNum, int i) const;
+ int64_t get_move_cumulative_nodes(int moveNum);
+ int move_count() const;
+ Move scan_for_easy_move() const;
+ void sort();
+ void sort_multipv(int n);
+
+ private:
+ static int compare_root_moves(const RootMove &rm1, const RootMove &rm2);
+ static const int MaxRootMoves = 500;
+ RootMove moves[MaxRootMoves];
+ int count;
+ };
+
+
+ /// Constants and variables
+
+ // Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV
+ // nodes:
+ int LMRPVMoves = 15;
+ int LMRNonPVMoves = 4;
+
+ // Depth limit for use of dynamic threat detection:
+ Depth ThreatDepth = 5*OnePly;
+
+ // Depth limit for selective search:
+ Depth SelectiveDepth = 7*OnePly;
+
+ // Use internal iterative deepening?
+ const bool UseIIDAtPVNodes = true;
+ const bool UseIIDAtNonPVNodes = false;
+
+ // Internal iterative deepening margin. At Non-PV moves, when
+ // UseIIDAtNonPVNodes is true, we do an internal iterative deepening search
+ // when the static evaluation is at most IIDMargin below beta.
+ const Value IIDMargin = Value(0x100);
+
+ // Use easy moves?
+ const bool UseEasyMove = true;
+
+ // Easy move margin. An easy move candidate must be at least this much
+ // better than the second best move.
+ const Value EasyMoveMargin = Value(0x200);
+
+ // Problem margin. If the score of the first move at iteration N+1 has
+ // dropped by more than this since iteration N, the boolean variable
+ // "Problem" is set to true, which will make the program spend some extra
+ // time looking for a better move.
+ const Value ProblemMargin = Value(0x28);
+
+ // No problem margin. If the boolean "Problem" is true, and a new move
+ // is found at the root which is less than NoProblemMargin worse than the
+ // best move from the previous iteration, Problem is set back to false.
+ const Value NoProblemMargin = Value(0x14);
+
+ // Null move margin. A null move search will not be done if the approximate
+ // evaluation of the position is more than NullMoveMargin below beta.
+ const Value NullMoveMargin = Value(0x300);
+
+ // Pruning criterions. See the code and comments in ok_to_prune() to
+ // understand their precise meaning.
+ const bool PruneEscapeMoves = false;
+ const bool PruneDefendingMoves = false;
+ const bool PruneBlockingMoves = false;
+
+ // Use futility pruning?
+ bool UseQSearchFutilityPruning = true;
+ bool UseFutilityPruning = true;
+
+ // Margins for futility pruning in the quiescence search, at frontier
+ // nodes, and at pre-frontier nodes:
+ Value FutilityMargin0 = Value(0x80);
+ Value FutilityMargin1 = Value(0x100);
+ Value FutilityMargin2 = Value(0x300);
+
+ // Razoring
+ Depth RazorDepth = 4*OnePly;
+ Value RazorMargin = Value(0x300);
+
+ // Extensions. Array index 0 is used at non-PV nodes, index 1 at PV nodes.
+ Depth CheckExtension[2] = {OnePly, OnePly};
+ Depth SingleReplyExtension[2] = {OnePly / 2, OnePly / 2};
+ Depth PawnPushTo7thExtension[2] = {OnePly / 2, OnePly / 2};
+ Depth PassedPawnExtension[2] = {Depth(0), Depth(0)};
+ Depth PawnEndgameExtension[2] = {OnePly, OnePly};
+ Depth MateThreatExtension[2] = {Depth(0), Depth(0)};
+
+ // Search depth at iteration 1:
+ const Depth InitialDepth = OnePly /*+ OnePly/2*/;
+
+ // Node counters
+ int NodesSincePoll;
+ int NodesBetweenPolls = 30000;
+
+ // Iteration counter:
+ int Iteration;
+
+ // Scores and number of times the best move changed for each iteration:
+ Value ValueByIteration[PLY_MAX_PLUS_2];
+ int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
+
+ // MultiPV mode:
+ int MultiPV = 1;
+
+ // Time managment variables
+ int SearchStartTime;
+ int MaxNodes, MaxDepth;
+ int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
+ Move BestRootMove, PonderMove, EasyMove;
+ int RootMoveNumber;
+ bool InfiniteSearch;
+ bool PonderSearch;
+ bool StopOnPonderhit;
+ bool AbortSearch;
+ bool Quit;
+ bool FailHigh;
+ bool Problem;
+ bool PonderingEnabled;
+ int ExactMaxTime;
+
+ // Show current line?
+ bool ShowCurrentLine = false;
+
+ // Log file
+ bool UseLogFile = false;
+ std::ofstream LogFile;
+
+ // MP related variables
+ Depth MinimumSplitDepth = 4*OnePly;
+ int MaxThreadsPerSplitPoint = 4;
+ Thread Threads[THREAD_MAX];
+ Lock MPLock;
+ bool AllThreadsShouldExit = false;
+ const int MaxActiveSplitPoints = 8;
+ SplitPoint SplitPointStack[THREAD_MAX][MaxActiveSplitPoints];
+ bool Idle = true;
+
+#if !defined(_MSC_VER)
+ pthread_cond_t WaitCond;
+ pthread_mutex_t WaitLock;
+#else
+ HANDLE SitIdleEvent[THREAD_MAX];
+#endif
+
+
+ /// Functions
+
+ void id_loop(const Position &pos, Move searchMoves[]);
+ Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml);
+ Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta,
+ Depth depth, int ply, int threadID);
+ Value search(Position &pos, SearchStack ss[], Value beta,
+ Depth depth, int ply, bool allowNullmove, int threadID);
+ Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta,
+ Depth depth, int ply, int threadID);
+ void sp_search(SplitPoint *sp, int threadID);
+ void sp_search_pv(SplitPoint *sp, int threadID);
+ void init_search_stack(SearchStack ss[]);
+ void init_node(const Position &pos, SearchStack ss[], int ply, int threadID);
+ void update_pv(SearchStack ss[], int ply);
+ void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
+ bool connected_moves(const Position &pos, Move m1, Move m2);
+ Depth extension(const Position &pos, Move m, bool pvNode, bool check,
+ bool singleReply, bool mateThreat);
+ bool ok_to_do_nullmove(const Position &pos);
+ bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d);
+
+ bool fail_high_ply_1();
+ int current_search_time();
+ int nps();
+ void poll();
+ void ponderhit();
+ void print_current_line(SearchStack ss[], int ply, int threadID);
+ void wait_for_stop_or_ponderhit();
+
+ void idle_loop(int threadID, SplitPoint *waitSp);
+ void init_split_point_stack();
+ void destroy_split_point_stack();
+ bool thread_should_stop(int threadID);
+ bool thread_is_available(int slave, int master);
+ bool idle_thread_exists(int master);
+ bool split(const Position &pos, SearchStack *ss, int ply,
+ Value *alpha, Value *beta, Value *bestValue, Depth depth,
+ int *moves, MovePicker *mp, Bitboard dcCandidates, int master,
+ bool pvNode);
+ void wake_sleeping_threads();
+
+#if !defined(_MSC_VER)
+ void *init_thread(void *threadID);
+#else
+ DWORD WINAPI init_thread(LPVOID threadID);
+#endif
+
+}
+
+
+////
+//// Global variables
+////
+
+// The main transposition table
+TranspositionTable TT = TranspositionTable(TTDefaultSize);
+
+
+// Number of active threads:
+int ActiveThreads = 1;
+
+// Locks. In principle, there is no need for IOLock to be a global variable,
+// but it could turn out to be useful for debugging.
+Lock IOLock;
+
+History H; // Should be made local?
+
+
+////
+//// Functions
+////
+
+/// think() is the external interface to Glaurung's search, and is called when
+/// the program receives the UCI 'go' command. It initializes various
+/// search-related global variables, and calls root_search()
+
+void think(const Position &pos, bool infinite, bool ponder, int time,
+ int increment, int movesToGo, int maxDepth, int maxNodes,
+ int maxTime, Move searchMoves[]) {
+
+ // Look for a book move:
+ if(!infinite && !ponder && get_option_value_bool("OwnBook")) {
+ Move bookMove;
+ if(get_option_value_string("Book File") != OpeningBook.file_name()) {
+ OpeningBook.close();
+ OpeningBook.open("book.bin");
+ }
+ bookMove = OpeningBook.get_move(pos);
+ if(bookMove != MOVE_NONE) {
+ std::cout << "bestmove " << bookMove << std::endl;
+ return;
+ }
+ }
+
+ // Initialize global search variables:
+ Idle = false;
+ SearchStartTime = get_system_time();
+ BestRootMove = MOVE_NONE;
+ PonderMove = MOVE_NONE;
+ EasyMove = MOVE_NONE;
+ for(int i = 0; i < THREAD_MAX; i++) {
+ Threads[i].nodes = 0ULL;
+ Threads[i].failHighPly1 = false;
+ }
+ NodesSincePoll = 0;
+ InfiniteSearch = infinite;
+ PonderSearch = ponder;
+ StopOnPonderhit = false;
+ AbortSearch = false;
+ Quit = false;
+ FailHigh = false;
+ Problem = false;
+ ExactMaxTime = maxTime;
+
+ // Read UCI option values:
+ TT.set_size(get_option_value_int("Hash"));
+ if(button_was_pressed("Clear Hash"))
+ TT.clear();
+ PonderingEnabled = get_option_value_int("Ponder");
+ MultiPV = get_option_value_int("MultiPV");
+
+ CheckExtension[1] = Depth(get_option_value_int("Check Extension (PV nodes)"));
+ CheckExtension[0] =
+ Depth(get_option_value_int("Check Extension (non-PV nodes)"));
+ SingleReplyExtension[1] = Depth(get_option_value_int("Single Reply Extension (PV nodes)"));
+ SingleReplyExtension[0] =
+ Depth(get_option_value_int("Single Reply Extension (non-PV nodes)"));
+ PawnPushTo7thExtension[1] =
+ Depth(get_option_value_int("Pawn Push to 7th Extension (PV nodes)"));
+ PawnPushTo7thExtension[0] =
+ Depth(get_option_value_int("Pawn Push to 7th Extension (non-PV nodes)"));
+ PassedPawnExtension[1] =
+ Depth(get_option_value_int("Passed Pawn Extension (PV nodes)"));
+ PassedPawnExtension[0] =
+ Depth(get_option_value_int("Passed Pawn Extension (non-PV nodes)"));
+ PawnEndgameExtension[1] =
+ Depth(get_option_value_int("Pawn Endgame Extension (PV nodes)"));
+ PawnEndgameExtension[0] =
+ Depth(get_option_value_int("Pawn Endgame Extension (non-PV nodes)"));
+ MateThreatExtension[1] =
+ Depth(get_option_value_int("Mate Threat Extension (PV nodes)"));
+ MateThreatExtension[0] =
+ Depth(get_option_value_int("Mate Threat Extension (non-PV nodes)"));
+
+ LMRPVMoves = get_option_value_int("Full Depth Moves (PV nodes)") + 1;
+ LMRNonPVMoves = get_option_value_int("Full Depth Moves (non-PV nodes)") + 1;
+ ThreatDepth = get_option_value_int("Threat Depth") * OnePly;
+ SelectiveDepth = get_option_value_int("Selective Plies") * OnePly;
+
+ Chess960 = get_option_value_bool("UCI_Chess960");
+ ShowCurrentLine = get_option_value_bool("UCI_ShowCurrLine");
+ UseLogFile = get_option_value_bool("Use Search Log");
+ if(UseLogFile)
+ LogFile.open(get_option_value_string("Search Log Filename").c_str(),
+ std::ios::out | std::ios::app);
+
+ UseQSearchFutilityPruning =
+ get_option_value_bool("Futility Pruning (Quiescence Search)");
+ UseFutilityPruning =
+ get_option_value_bool("Futility Pruning (Main Search)");
+
+ FutilityMargin0 =
+ value_from_centipawns(get_option_value_int("Futility Margin 0"));
+ FutilityMargin1 =
+ value_from_centipawns(get_option_value_int("Futility Margin 1"));
+ FutilityMargin2 =
+ value_from_centipawns(get_option_value_int("Futility Margin 2"));
+
+ RazorDepth = (get_option_value_int("Maximum Razoring Depth") + 1) * OnePly;
+ RazorMargin = value_from_centipawns(get_option_value_int("Razoring Margin"));
+
+ MinimumSplitDepth = get_option_value_int("Minimum Split Depth") * OnePly;
+ MaxThreadsPerSplitPoint =
+ get_option_value_int("Maximum Number of Threads per Split Point");
+
+ read_weights(pos.side_to_move());
+
+ int newActiveThreads = get_option_value_int("Threads");
+ if(newActiveThreads != ActiveThreads) {
+ ActiveThreads = newActiveThreads;
+ init_eval(ActiveThreads);
+ }
+
+ // Write information to search log file:
+ if(UseLogFile) {
+ LogFile << "Searching: " << pos.to_fen() << '\n';
+ LogFile << "infinite: " << infinite << " ponder: " << ponder
+ << " time: " << time << " increment: " << increment
+ << " moves to go: " << movesToGo << '\n';
+ }
+
+ // Wake up sleeping threads:
+ wake_sleeping_threads();
+
+ for(int i = 1; i < ActiveThreads; i++)
+ assert(thread_is_available(i, 0));
+
+ // Set thinking time:
+ if(!movesToGo) { // Sudden death time control
+ if(increment) {
+ MaxSearchTime = time / 30 + increment;
+ AbsoluteMaxSearchTime = Max(time / 4, increment - 100);
+ }
+ else { // Blitz game without increment
+ MaxSearchTime = time / 40;
+ AbsoluteMaxSearchTime = time / 8;
+ }
+ }
+ else { // (x moves) / (y minutes)
+ if(movesToGo == 1) {
+ MaxSearchTime = time / 2;
+ AbsoluteMaxSearchTime = Min(time / 2, time - 500);
+ }
+ else {
+ MaxSearchTime = time / Min(movesToGo, 20);
+ AbsoluteMaxSearchTime = Min((4 * time) / movesToGo, time / 3);
+ }
+ }
+ if(PonderingEnabled) {
+ MaxSearchTime += MaxSearchTime / 4;
+ MaxSearchTime = Min(MaxSearchTime, AbsoluteMaxSearchTime);
+ }
+
+ // Fixed depth or fixed number of nodes?
+ MaxDepth = maxDepth;
+ if(MaxDepth)
+ InfiniteSearch = true; // HACK
+
+ MaxNodes = maxNodes;
+ if(MaxNodes) {
+ NodesBetweenPolls = Min(MaxNodes, 30000);
+ InfiniteSearch = true; // HACK
+ }
+ else
+ NodesBetweenPolls = 30000;
+
+ // We're ready to start thinking. Call the iterative deepening loop
+ // function:
+ id_loop(pos, searchMoves);
+
+ if(UseLogFile)
+ LogFile.close();
+
+ if(Quit) {
+ OpeningBook.close();
+ stop_threads();
+ quit_eval();
+ exit(0);
+ }
+
+ Idle = true;
+}
+
+
+/// init_threads() is called during startup. It launches all helper threads,
+/// and initializes the split point stack and the global locks and condition
+/// objects.
+
+void init_threads() {
+ volatile int i;
+#if !defined(_MSC_VER)
+ pthread_t pthread[1];
+#endif
+
+ for(i = 0; i < THREAD_MAX; i++)
+ Threads[i].activeSplitPoints = 0;
+
+ // Initialize global locks:
+ lock_init(&MPLock, NULL);
+ lock_init(&IOLock, NULL);
+
+ init_split_point_stack();
+
+#if !defined(_MSC_VER)
+ pthread_mutex_init(&WaitLock, NULL);
+ pthread_cond_init(&WaitCond, NULL);
+#else
+ for(i = 0; i < THREAD_MAX; i++)
+ SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
+#endif
+
+ // All threads except the main thread should be initialized to idle state:
+ for(i = 1; i < THREAD_MAX; i++) {
+ Threads[i].stop = false;
+ Threads[i].workIsWaiting = false;
+ Threads[i].idle = true;
+ Threads[i].running = false;
+ }
+
+ // Launch the helper threads:
+ for(i = 1; i < THREAD_MAX; i++) {
+#if !defined(_MSC_VER)
+ pthread_create(pthread, NULL, init_thread, (void*)(&i));
+#else
+ {
+ DWORD iID[1];
+ CreateThread(NULL, 0, init_thread, (LPVOID)(&i), 0, iID);
+ }
+#endif
+
+ // Wait until the thread has finished launching:
+ while(!Threads[i].running);
+ }
+}
+
+
+/// stop_threads() is called when the program exits. It makes all the
+/// helper threads exit cleanly.
+
+void stop_threads() {
+ ActiveThreads = THREAD_MAX; // HACK
+ Idle = false; // HACK
+ wake_sleeping_threads();
+ AllThreadsShouldExit = true;
+ for(int i = 1; i < THREAD_MAX; i++) {
+ Threads[i].stop = true;
+ while(Threads[i].running);
+ }
+ destroy_split_point_stack();
+}
+
+
+/// nodes_searched() returns the total number of nodes searched so far in
+/// the current search.
+
+int64_t nodes_searched() {
+ int64_t result = 0ULL;
+ for(int i = 0; i < ActiveThreads; i++)
+ result += Threads[i].nodes;
+ return result;
+}
+
+
+namespace {
+
+ // id_loop() is the main iterative deepening loop. It calls root_search
+ // repeatedly with increasing depth until the allocated thinking time has
+ // been consumed, the user stops the search, or the maximum search depth is
+ // reached.
+
+ void id_loop(const Position &pos, Move searchMoves[]) {
+ Position p(pos);
+ RootMoveList rml(p, searchMoves);
+ SearchStack ss[PLY_MAX_PLUS_2];
+
+ // Initialize
+ TT.new_search();
+ H.clear();
+ init_search_stack(ss);
+
+ ValueByIteration[0] = Value(0);
+ ValueByIteration[1] = rml.get_move_score(0);
+ Iteration = 1;
+
+ EasyMove = rml.scan_for_easy_move();
+
+ // Iterative deepening loop
+ while(!AbortSearch && Iteration < PLY_MAX) {
+
+ // Initialize iteration
+ rml.sort();
+ Iteration++;
+ BestMoveChangesByIteration[Iteration] = 0;
+ if(Iteration <= 5)
+ ExtraSearchTime = 0;
+
+ std::cout << "info depth " << Iteration << std::endl;
+
+ // Search to the current depth
+ ValueByIteration[Iteration] = root_search(p, ss, rml);
+
+ // Erase the easy move if it differs from the new best move
+ if(ss[0].pv[0] != EasyMove)
+ EasyMove = MOVE_NONE;
+
+ Problem = false;
+
+ if(!InfiniteSearch) {
+ // Time to stop?
+ bool stopSearch = false;
+
+ // Stop search early if there is only a single legal move:
+ if(Iteration >= 6 && rml.move_count() == 1)
+ stopSearch = true;
+
+ // Stop search early when the last two iterations returned a mate
+ // score:
+ if(Iteration >= 6
+ && abs(ValueByIteration[Iteration]) >= abs(VALUE_MATE) - 100
+ && abs(ValueByIteration[Iteration-1]) >= abs(VALUE_MATE) - 100)
+ stopSearch = true;
+
+ // Stop search early if one move seems to be much better than the
+ // rest:
+ int64_t nodes = nodes_searched();
+ if(Iteration >= 8 && EasyMove == ss[0].pv[0] &&
+ ((rml.get_move_cumulative_nodes(0) > (nodes * 85) / 100 &&
+ current_search_time() > MaxSearchTime / 16) ||
+ (rml.get_move_cumulative_nodes(0) > (nodes * 98) / 100 &&
+ current_search_time() > MaxSearchTime / 32)))
+ stopSearch = true;
+
+ // Add some extra time if the best move has changed during the last
+ // two iterations:
+ if(Iteration > 5 && Iteration <= 50)
+ ExtraSearchTime =
+ BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2) +
+ BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
+
+ // Stop search if most of MaxSearchTime is consumed at the end of the
+ // iteration. We probably don't have enough time to search the first
+ // move at the next iteration anyway.
+ if(current_search_time() > ((MaxSearchTime + ExtraSearchTime)*80) / 128)
+ stopSearch = true;
+
+ if(stopSearch) {
+ if(!PonderSearch)
+ break;
+ else
+ StopOnPonderhit = true;
+ }
+ }
+
+ // Write PV to transposition table, in case the relevant entries have
+ // been overwritten during the search:
+ TT.insert_pv(p, ss[0].pv);
+
+ if(MaxDepth && Iteration >= MaxDepth)
+ break;
+ }
+
+ rml.sort();
+
+ // If we are pondering, we shouldn't print the best move before we
+ // are told to do so
+ if(PonderSearch)
+ wait_for_stop_or_ponderhit();
+ else
+ // Print final search statistics
+ std::cout << "info nodes " << nodes_searched() << " nps " << nps()
+ << " time " << current_search_time()
+ << " hashfull " << TT.full() << std::endl;
+
+ // Print the best move and the ponder move to the standard output:
+ std::cout << "bestmove " << ss[0].pv[0];
+ if(ss[0].pv[1] != MOVE_NONE)
+ std::cout << " ponder " << ss[0].pv[1];
+ std::cout << std::endl;
+
+ if(UseLogFile) {
+ UndoInfo u;
+ LogFile << "Nodes: " << nodes_searched() << '\n';
+ LogFile << "Nodes/second: " << nps() << '\n';
+ LogFile << "Best move: " << move_to_san(p, ss[0].pv[0]) << '\n';
+ p.do_move(ss[0].pv[0], u);
+ LogFile << "Ponder move: " << move_to_san(p, ss[0].pv[1]) << '\n';
+ LogFile << std::endl;
+ }
+ }
+
+
+ // root_search() is the function which searches the root node. It is
+ // similar to search_pv except that it uses a different move ordering
+ // scheme (perhaps we should try to use this at internal PV nodes, too?)
+ // and prints some information to the standard output.
+
+ Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml) {
+ Value alpha = -VALUE_INFINITE, beta = VALUE_INFINITE, value;
+ Bitboard dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
+
+ // Loop through all the moves in the root move list:
+ for(int i = 0; i < rml.move_count() && !AbortSearch; i++) {
+ int64_t nodes;
+ Move move;
+ UndoInfo u;
+ Depth ext, newDepth;
+
+ RootMoveNumber = i + 1;
+ FailHigh = false;
+
+ // Remember the node count before the move is searched. The node counts
+ // are used to sort the root moves at the next iteration.
+ nodes = nodes_searched();
+
+ // Pick the next root move, and print the move and the move number to
+ // the standard output:
+ move = ss[0].currentMove = rml.get_move(i);
+ if(current_search_time() >= 1000)
+ std::cout << "info currmove " << move
+ << " currmovenumber " << i + 1 << std::endl;
+
+ // Decide search depth for this move:
+ ext = extension(pos, move, true, pos.move_is_check(move), false, false);
+ newDepth = (Iteration-2)*OnePly + ext + InitialDepth;
+
+ // Make the move, and search it.
+ pos.do_move(move, u, dcCandidates);
+
+ if(i < MultiPV) {
+ value = -search_pv(pos, ss, -beta, VALUE_INFINITE, newDepth, 1, 0);
+ // If the value has dropped a lot compared to the last iteration,
+ // set the boolean variable Problem to true. This variable is used
+ // for time managment: When Problem is true, we try to complete the
+ // current iteration before playing a move.
+ Problem = (Iteration >= 2 &&
+ value <= ValueByIteration[Iteration-1] - ProblemMargin);
+ if(Problem && StopOnPonderhit)
+ StopOnPonderhit = false;
+ }
+ else {
+ value = -search(pos, ss, -alpha, newDepth, 1, true, 0);
+ if(value > alpha) {
+ // Fail high! Set the boolean variable FailHigh to true, and
+ // re-search the move with a big window. The variable FailHigh is
+ // used for time managment: We try to avoid aborting the search
+ // prematurely during a fail high research.
+ FailHigh = true;
+ value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0);
+ }
+ }
+
+ pos.undo_move(move, u);
+
+ // Finished searching the move. If AbortSearch is true, the search
+ // was aborted because the user interrupted the search or because we
+ // ran out of time. In this case, the return value of the search cannot
+ // be trusted, and we break out of the loop without updating the best
+ // move and/or PV:
+ if(AbortSearch)
+ break;
+
+ // Remember the node count for this move. The node counts are used to
+ // sort the root moves at the next iteration.
+ rml.set_move_nodes(i, nodes_searched() - nodes);
+
+ assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
+
+ if(value <= alpha && i >= MultiPV)
+ rml.set_move_score(i, -VALUE_INFINITE);
+ else {
+ // New best move!
+
+ // Update PV:
+ rml.set_move_score(i, value);
+ update_pv(ss, 0);
+ rml.set_move_pv(i, ss[0].pv);
+
+ if(MultiPV == 1) {
+ // We record how often the best move has been changed in each
+ // iteration. This information is used for time managment: When
+ // the best move changes frequently, we allocate some more time.
+ if(i > 0)
+ BestMoveChangesByIteration[Iteration]++;
+
+ // Print search information to the standard output:
+ std::cout << "info depth " << Iteration
+ << " score " << value_to_string(value)
+ << " time " << current_search_time()
+ << " nodes " << nodes_searched()
+ << " nps " << nps()
+ << " pv ";
+ for(int j = 0; ss[0].pv[j] != MOVE_NONE && j < PLY_MAX; j++)
+ std::cout << ss[0].pv[j] << " ";
+ std::cout << std::endl;
+
+ if(UseLogFile)
+ LogFile << pretty_pv(pos, current_search_time(), Iteration,
+ nodes_searched(), value, ss[0].pv)
+ << std::endl;
+
+ alpha = value;
+
+ // Reset the global variable Problem to false if the value isn't too
+ // far below the final value from the last iteration.
+ if(value > ValueByIteration[Iteration - 1] - NoProblemMargin)
+ Problem = false;
+ }
+ else { // MultiPV > 1
+ rml.sort_multipv(i);
+ for(int j = 0; j < Min(MultiPV, rml.move_count()); j++) {
+ int k;
+ std::cout << "info multipv " << j + 1
+ << " score " << value_to_string(rml.get_move_score(j))
+ << " depth " << ((j <= i)? Iteration : Iteration - 1)
+ << " time " << current_search_time()
+ << " nodes " << nodes_searched()
+ << " nps " << nps()
+ << " pv ";
+ for(k = 0; rml.get_move_pv(j, k) != MOVE_NONE && k < PLY_MAX; k++)
+ std::cout << rml.get_move_pv(j, k) << " ";
+ std::cout << std::endl;
+ }
+ alpha = rml.get_move_score(Min(i, MultiPV-1));
+ }
+ }
+ }
+ return alpha;
+ }
+
+
+ // search_pv() is the main search function for PV nodes.
+
+ Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta,
+ Depth depth, int ply, int threadID) {
+ assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
+ assert(beta > alpha && beta <= VALUE_INFINITE);
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ EvalInfo ei;
+
+ // Initialize, and make an early exit in case of an aborted search,
+ // an instant draw, maximum ply reached, etc.
+ Value oldAlpha = alpha;
+
+ if(AbortSearch || thread_should_stop(threadID))
+ return Value(0);
+
+ if(depth < OnePly)
+ return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID);
+
+ init_node(pos, ss, ply, threadID);
+
+ if(pos.is_draw())
+ return VALUE_DRAW;
+
+ if(ply >= PLY_MAX - 1)
+ return evaluate(pos, ei, threadID);
+
+ // Mate distance pruning
+ alpha = Max(value_mated_in(ply), alpha);
+ beta = Min(value_mate_in(ply+1), beta);
+ if(alpha >= beta)
+ return alpha;
+
+ // Transposition table lookup. At PV nodes, we don't use the TT for
+ // pruning, but only for move ordering.
+ Value ttValue;
+ Depth ttDepth;
+ Move ttMove = MOVE_NONE;
+ ValueType ttValueType;
+
+ TT.retrieve(pos, &ttValue, &ttDepth, &ttMove, &ttValueType);
+
+ // Internal iterative deepening.
+ if(UseIIDAtPVNodes && ttMove == MOVE_NONE && depth >= 5*OnePly) {
+ search_pv(pos, ss, alpha, beta, depth-2*OnePly, ply, threadID);
+ ttMove = ss[ply].pv[ply];
+ }
+
+ // Initialize a MovePicker object for the current position, and prepare
+ // to search all moves:
+ MovePicker mp = MovePicker(pos, true, ttMove, ss[ply].mateKiller,
+ ss[ply].killer1, ss[ply].killer2, depth);
+ Move move, movesSearched[256];
+ int moveCount = 0;
+ Value value, bestValue = -VALUE_INFINITE;
+ Bitboard dcCandidates = mp.discovered_check_candidates();
+ bool mateThreat =
+ MateThreatExtension[1] > Depth(0)
+ && pos.has_mate_threat(opposite_color(pos.side_to_move()));
+
+ // Loop through all legal moves until no moves remain or a beta cutoff
+ // occurs.
+ while(alpha < beta && !thread_should_stop(threadID)
+ && (move = mp.get_next_move()) != MOVE_NONE) {
+ UndoInfo u;
+ Depth ext, newDepth;
+ bool singleReply = (pos.is_check() && mp.number_of_moves() == 1);
+ bool moveIsCheck = pos.move_is_check(move, dcCandidates);
+ bool moveIsCapture = pos.move_is_capture(move);
+ bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
+
+ assert(move_is_ok(move));
+ movesSearched[moveCount++] = ss[ply].currentMove = move;
+
+ ss[ply].currentMoveCaptureValue = move_is_ep(move)?
+ PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
+
+ // Decide the new search depth.
+ ext = extension(pos, move, true, moveIsCheck, singleReply, mateThreat);
+ newDepth = depth - OnePly + ext;
+
+ // Make and search the move.
+ pos.do_move(move, u, dcCandidates);
+
+ if(moveCount == 1)
+ value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID);
+ else {
+ if(depth >= 2*OnePly && ext == Depth(0) && moveCount >= LMRPVMoves
+ && !moveIsCapture && !move_promotion(move)
+ && !moveIsPassedPawnPush && !move_is_castle(move)
+ && move != ss[ply].killer1 && move != ss[ply].killer2) {
+ ss[ply].reduction = OnePly;
+ value = -search(pos, ss, -alpha, newDepth-OnePly, ply+1, true,
+ threadID);
+ }
+ else value = alpha + 1;
+ if(value > alpha) {
+ ss[ply].reduction = Depth(0);
+ value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID);
+ if(value > alpha && value < beta) {
+ if(ply == 1 && RootMoveNumber == 1)
+ // When the search fails high at ply 1 while searching the first
+ // move at the root, set the flag failHighPly1. This is used for
+ // time managment: We don't want to stop the search early in
+ // such cases, because resolving the fail high at ply 1 could
+ // result in a big drop in score at the root.
+ Threads[threadID].failHighPly1 = true;
+ value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1,
+ threadID);
+ Threads[threadID].failHighPly1 = false;
+ }
+ }
+ }
+ pos.undo_move(move, u);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ // New best move?
+ if(value > bestValue) {
+ bestValue = value;
+ if(value > alpha) {
+ alpha = value;
+ update_pv(ss, ply);
+ if(value == value_mate_in(ply + 1))
+ ss[ply].mateKiller = move;
+ }
+ // If we are at ply 1, and we are searching the first root move at
+ // ply 0, set the 'Problem' variable if the score has dropped a lot
+ // (from the computer's point of view) since the previous iteration:
+ if(Iteration >= 2 &&
+ -value <= ValueByIteration[Iteration-1] - ProblemMargin)
+ Problem = true;
+ }
+
+ // Split?
+ if(ActiveThreads > 1 && bestValue < beta && depth >= MinimumSplitDepth
+ && Iteration <= 99 && idle_thread_exists(threadID)
+ && !AbortSearch && !thread_should_stop(threadID)
+ && split(pos, ss, ply, &alpha, &beta, &bestValue, depth,
+ &moveCount, &mp, dcCandidates, threadID, true))
+ break;
+ }
+
+ // All legal moves have been searched. A special case: If there were
+ // no legal moves, it must be mate or stalemate:
+ if(moveCount == 0) {
+ if(pos.is_check())
+ return value_mated_in(ply);
+ else
+ return VALUE_DRAW;
+ }
+
+ // If the search is not aborted, update the transposition table,
+ // history counters, and killer moves. This code is somewhat messy,
+ // and definitely needs to be cleaned up. FIXME
+ if(!AbortSearch && !thread_should_stop(threadID)) {
+ if(bestValue <= oldAlpha)
+ TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE,
+ VALUE_TYPE_UPPER);
+ else if(bestValue >= beta) {
+ Move m = ss[ply].pv[ply];
+ if(pos.square_is_empty(move_to(m)) && !move_promotion(m) &&
+ !move_is_ep(m)) {
+ for(int i = 0; i < moveCount - 1; i++)
+ if(pos.square_is_empty(move_to(movesSearched[i]))
+ && !move_promotion(movesSearched[i])
+ && !move_is_ep(movesSearched[i]))
+ H.failure(pos.piece_on(move_from(movesSearched[i])),
+ movesSearched[i]);
+
+ H.success(pos.piece_on(move_from(m)), m, depth);
+
+ if(m != ss[ply].killer1) {
+ ss[ply].killer2 = ss[ply].killer1;
+ ss[ply].killer1 = m;
+ }
+ }
+ TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
+ }
+ else
+ TT.store(pos, value_to_tt(bestValue, ply), depth, ss[ply].pv[ply],
+ VALUE_TYPE_EXACT);
+ }
+
+ return bestValue;
+ }
+
+
+ // search() is the search function for zero-width nodes.
+
+ Value search(Position &pos, SearchStack ss[], Value beta, Depth depth,
+ int ply, bool allowNullmove, int threadID) {
+ assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ EvalInfo ei;
+
+ // Initialize, and make an early exit in case of an aborted search,
+ // an instant draw, maximum ply reached, etc.
+ if(AbortSearch || thread_should_stop(threadID))
+ return Value(0);
+
+ if(depth < OnePly)
+ return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
+
+ init_node(pos, ss, ply, threadID);
+
+ if(pos.is_draw())
+ return VALUE_DRAW;
+
+ if(ply >= PLY_MAX - 1)
+ return evaluate(pos, ei, threadID);
+
+ // Mate distance pruning
+ if(value_mated_in(ply) >= beta)
+ return beta;
+ if(value_mate_in(ply+1) < beta)
+ return beta-1;
+
+ // Transposition table lookup
+ bool ttFound;
+ Value ttValue;
+ Depth ttDepth;
+ Move ttMove = MOVE_NONE;
+ ValueType ttValueType;
+
+ ttFound = TT.retrieve(pos, &ttValue, &ttDepth, &ttMove, &ttValueType);
+ if(ttFound) {
+ ttValue = value_from_tt(ttValue, ply);
+ if(ttDepth >= depth
+ || ttValue >= Max(value_mate_in(100), beta)
+ || ttValue < Min(value_mated_in(100), beta)) {
+ if((is_lower_bound(ttValueType) && ttValue >= beta) ||
+ (is_upper_bound(ttValueType) && ttValue < beta)) {
+ ss[ply].currentMove = ttMove;
+ return ttValue;
+ }
+ }
+ }
+
+ Value approximateEval = quick_evaluate(pos);
+ bool mateThreat = false;
+
+ // Null move search
+ if(!pos.is_check() && allowNullmove && ok_to_do_nullmove(pos)
+ && approximateEval >= beta - NullMoveMargin) {
+ UndoInfo u;
+ Value nullValue;
+
+ ss[ply].currentMove = MOVE_NULL;
+ pos.do_null_move(u);
+ nullValue = -search(pos, ss, -(beta-1), depth-4*OnePly, ply+1, false,
+ threadID);
+ pos.undo_null_move(u);
+
+ if(nullValue >= beta) {
+ if(depth >= 6 * OnePly) { // Do zugzwang verification search
+ Value v = search(pos, ss, beta, depth-5*OnePly, ply, false, threadID);
+ if(v >= beta)
+ return beta;
+ }
+ else
+ return beta;
+ }
+ else {
+ // The null move failed low, which means that we may be faced with
+ // some kind of threat. If the previous move was reduced, check if
+ // the move that refuted the null move was somehow connected to the
+ // move which was reduced. If a connection is found, return a fail
+ // low score (which will cause the reduced move to fail high in the
+ // parent node, which will trigger a re-search with full depth).
+ if(nullValue == value_mated_in(ply+2))
+ mateThreat = true;
+ ss[ply].threatMove = ss[ply+1].currentMove;
+ if(depth < ThreatDepth && ss[ply-1].reduction &&
+ connected_moves(pos, ss[ply-1].currentMove, ss[ply].threatMove))
+ return beta - 1;
+ }
+ }
+ // Razoring:
+ else if(depth < RazorDepth && approximateEval < beta - RazorMargin &&
+ evaluate(pos, ei, threadID) < beta - RazorMargin) {
+ Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
+ if(v < beta)
+ return v;
+ }
+
+ // Internal iterative deepening
+ if(UseIIDAtNonPVNodes && ttMove == MOVE_NONE && depth >= 8*OnePly &&
+ evaluate(pos, ei, threadID) >= beta - IIDMargin) {
+ search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
+ ttMove = ss[ply].pv[ply];
+ }
+
+ // Initialize a MovePicker object for the current position, and prepare
+ // to search all moves:
+ MovePicker mp = MovePicker(pos, false, ttMove, ss[ply].mateKiller,
+ ss[ply].killer1, ss[ply].killer2, depth);
+ Move move, movesSearched[256];
+ int moveCount = 0;
+ Value value, bestValue = -VALUE_INFINITE, futilityValue = VALUE_NONE;
+ Bitboard dcCandidates = mp.discovered_check_candidates();
+ bool isCheck = pos.is_check();
+ bool useFutilityPruning =
+ UseFutilityPruning && depth < SelectiveDepth && !isCheck;
+
+ // Loop through all legal moves until no moves remain or a beta cutoff
+ // occurs.
+ while(bestValue < beta && !thread_should_stop(threadID)
+ && (move = mp.get_next_move()) != MOVE_NONE) {
+ UndoInfo u;
+ Depth ext, newDepth;
+ bool singleReply = (isCheck && mp.number_of_moves() == 1);
+ bool moveIsCheck = pos.move_is_check(move, dcCandidates);
+ bool moveIsCapture = pos.move_is_capture(move);
+ bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
+
+ assert(move_is_ok(move));
+ movesSearched[moveCount++] = ss[ply].currentMove = move;
+
+ // Decide the new search depth.
+ ext = extension(pos, move, false, moveIsCheck, singleReply, mateThreat);
+ newDepth = depth - OnePly + ext;
+
+ // Futility pruning
+ if(useFutilityPruning && ext == Depth(0) && !moveIsCapture &&
+ !moveIsPassedPawnPush && !move_promotion(move)) {
+
+ if(moveCount >= 2 + int(depth)
+ && ok_to_prune(pos, move, ss[ply].threatMove, depth))
+ continue;
+
+ if(depth < 3 * OnePly && approximateEval < beta) {
+ if(futilityValue == VALUE_NONE)
+ futilityValue = evaluate(pos, ei, threadID)
+ + ((depth < 2 * OnePly)? FutilityMargin1 : FutilityMargin2);
+ if(futilityValue < beta) {
+ if(futilityValue > bestValue)
+ bestValue = futilityValue;
+ continue;
+ }
+ }
+ }
+
+ // Make and search the move.
+ pos.do_move(move, u, dcCandidates);
+
+ if(depth >= 2*OnePly && ext == Depth(0) && moveCount >= LMRNonPVMoves
+ && !moveIsCapture && !move_promotion(move) && !moveIsPassedPawnPush
+ && !move_is_castle(move)
+ && move != ss[ply].killer1 && move != ss[ply].killer2) {
+ ss[ply].reduction = OnePly;
+ value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true,
+ threadID);
+ }
+ else
+ value = beta;
+ if(value >= beta) {
+ ss[ply].reduction = Depth(0);
+ value = -search(pos, ss, -(beta-1), newDepth, ply+1, true, threadID);
+ }
+ pos.undo_move(move, u);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ // New best move?
+ if(value > bestValue) {
+ bestValue = value;
+ if(value >= beta)
+ update_pv(ss, ply);
+ if(value == value_mate_in(ply + 1))
+ ss[ply].mateKiller = move;
+ }
+
+ // Split?
+ if(ActiveThreads > 1 && bestValue < beta && depth >= MinimumSplitDepth
+ && Iteration <= 99 && idle_thread_exists(threadID)
+ && !AbortSearch && !thread_should_stop(threadID)
+ && split(pos, ss, ply, &beta, &beta, &bestValue, depth, &moveCount,
+ &mp, dcCandidates, threadID, false))
+ break;
+ }
+
+ // All legal moves have been searched. A special case: If there were
+ // no legal moves, it must be mate or stalemate:
+ if(moveCount == 0) {
+ if(pos.is_check())
+ return value_mated_in(ply);
+ else
+ return VALUE_DRAW;
+ }
+
+ // If the search is not aborted, update the transposition table,
+ // history counters, and killer moves. This code is somewhat messy,
+ // and definitely needs to be cleaned up. FIXME
+ if(!AbortSearch && !thread_should_stop(threadID)) {
+ if(bestValue < beta)
+ TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE,
+ VALUE_TYPE_UPPER);
+ else {
+ Move m = ss[ply].pv[ply];
+
+ if(pos.square_is_empty(move_to(m)) && !move_promotion(m) &&
+ !move_is_ep(m)) {
+ for(int i = 0; i < moveCount - 1; i++)
+ if(pos.square_is_empty(move_to(movesSearched[i]))
+ && !move_promotion(movesSearched[i])
+ && !move_is_ep(movesSearched[i]))
+ H.failure(pos.piece_on(move_from(movesSearched[i])),
+ movesSearched[i]);
+ H.success(pos.piece_on(move_from(m)), m, depth);
+
+ if(m != ss[ply].killer1) {
+ ss[ply].killer2 = ss[ply].killer1;
+ ss[ply].killer1 = m;
+ }
+ }
+ TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
+ }
+ }
+
+ return bestValue;
+ }
+
+
+ // qsearch() is the quiescence search function, which is called by the main
+ // search function when the remaining depth is zero (or, to be more precise,
+ // less than OnePly).
+
+ Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta,
+ Depth depth, int ply, int threadID) {
+ Value staticValue, bestValue, value;
+ EvalInfo ei;
+
+ assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
+ assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+ assert(depth <= 0);
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ // Initialize, and make an early exit in case of an aborted search,
+ // an instant draw, maximum ply reached, etc.
+ if(AbortSearch || thread_should_stop(threadID))
+ return Value(0);
+
+ init_node(pos, ss, ply, threadID);
+
+ if(pos.is_draw())
+ return VALUE_DRAW;
+
+ // Evaluate the position statically:
+ staticValue = evaluate(pos, ei, threadID);
+
+ if(ply == PLY_MAX - 1) return staticValue;
+
+ // Initialize "stand pat score", and return it immediately if it is
+ // at least beta.
+ if(pos.is_check())
+ bestValue = -VALUE_INFINITE;
+ else {
+ bestValue = staticValue;
+ if(bestValue >= beta)
+ return bestValue;
+ if(bestValue > alpha)
+ alpha = bestValue;
+ }
+
+ // Initialize a MovePicker object for the current position, and prepare
+ // to search the moves. Because the depth is <= 0 here, only captures,
+ // queen promotions and checks (only if depth == 0) will be generated.
+ MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
+ MOVE_NONE, depth);
+ Move move;
+ int moveCount = 0;
+ Bitboard dcCandidates = mp.discovered_check_candidates();
+ bool isCheck = pos.is_check();
+
+ // Loop through the moves until no moves remain or a beta cutoff
+ // occurs.
+ while(alpha < beta && ((move = mp.get_next_move()) != MOVE_NONE)) {
+ UndoInfo u;
+ bool moveIsCheck = pos.move_is_check(move, dcCandidates);
+ bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
+
+ assert(move_is_ok(move));
+
+ moveCount++;
+ ss[ply].currentMove = move;
+
+ // Futility pruning
+ if(UseQSearchFutilityPruning && !isCheck && !moveIsCheck &&
+ !move_promotion(move) && !moveIsPassedPawnPush &&
+ beta - alpha == 1 &&
+ pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame) {
+ Value futilityValue =
+ staticValue
+ + Max(pos.midgame_value_of_piece_on(move_to(move)),
+ pos.endgame_value_of_piece_on(move_to(move)))
+ + FutilityMargin0
+ + ei.futilityMargin;
+ if(futilityValue < alpha) {
+ if(futilityValue > bestValue)
+ bestValue = futilityValue;
+ continue;
+ }
+ }
+
+ // Don't search captures and checks with negative SEE values.
+ if(!isCheck && !move_promotion(move) &&
+ pos.midgame_value_of_piece_on(move_from(move)) >
+ pos.midgame_value_of_piece_on(move_to(move)) &&
+ pos.see(move) < 0)
+ continue;
+
+ // Make and search the move.
+ pos.do_move(move, u, dcCandidates);
+ value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID);
+ pos.undo_move(move, u);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ // New best move?
+ if(value > bestValue) {
+ bestValue = value;
+ if(value > alpha) {
+ alpha = value;
+ update_pv(ss, ply);
+ }
+ }
+ }
+
+ // All legal moves have been searched. A special case: If we're in check
+ // and no legal moves were found, it is checkmate:
+ if(pos.is_check() && moveCount == 0) // Mate!
+ return value_mated_in(ply);
+
+ assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
+
+ return bestValue;
+ }
+
+
+ // sp_search() is used to search from a split point. This function is called
+ // by each thread working at the split point. It is similar to the normal
+ // search() function, but simpler. Because we have already probed the hash
+ // table, done a null move search, and searched the first move before
+ // splitting, we don't have to repeat all this work in sp_search(). We
+ // also don't need to store anything to the hash table here: This is taken
+ // care of after we return from the split point.
+
+ void sp_search(SplitPoint *sp, int threadID) {
+ assert(threadID >= 0 && threadID < ActiveThreads);
+ assert(ActiveThreads > 1);
+
+ Position pos = Position(sp->pos);
+ SearchStack *ss = sp->sstack[threadID];
+ Value value;
+ Move move;
+ int moveCount = sp->moves;
+ bool isCheck = pos.is_check();
+ bool useFutilityPruning =
+ UseFutilityPruning && sp->depth < SelectiveDepth && !isCheck;
+
+ while(sp->bestValue < sp->beta && !thread_should_stop(threadID)
+ && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) {
+ UndoInfo u;
+ Depth ext, newDepth;
+ bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
+ bool moveIsCapture = pos.move_is_capture(move);
+ bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
+
+ assert(move_is_ok(move));
+
+ lock_grab(&(sp->lock));
+ sp->moves++;
+ moveCount = sp->moves;
+ lock_release(&(sp->lock));
+
+ ss[sp->ply].currentMove = move;
+
+ // Decide the new search depth.
+ ext = extension(pos, move, false, moveIsCheck, false, false);
+ newDepth = sp->depth - OnePly + ext;
+
+ // Prune?
+ if(useFutilityPruning && ext == Depth(0) && !moveIsCapture
+ && !moveIsPassedPawnPush && !move_promotion(move)
+ && moveCount >= 2 + int(sp->depth)
+ && ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
+ continue;
+
+ // Make and search the move.
+ pos.do_move(move, u, sp->dcCandidates);
+ if(ext == Depth(0) && moveCount >= LMRNonPVMoves
+ && !moveIsCapture && !move_promotion(move) && !moveIsPassedPawnPush
+ && !move_is_castle(move)
+ && move != ss[sp->ply].killer1 && move != ss[sp->ply].killer2) {
+ ss[sp->ply].reduction = OnePly;
+ value = -search(pos, ss, -(sp->beta-1), newDepth - OnePly, sp->ply+1,
+ true, threadID);
+ }
+ else
+ value = sp->beta;
+ if(value >= sp->beta) {
+ ss[sp->ply].reduction = Depth(0);
+ value = -search(pos, ss, -(sp->beta - 1), newDepth, sp->ply+1, true,
+ threadID);
+ }
+ pos.undo_move(move, u);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ if(thread_should_stop(threadID))
+ break;
+
+ // New best move?
+ lock_grab(&(sp->lock));
+ if(value > sp->bestValue && !thread_should_stop(threadID)) {
+ sp->bestValue = value;
+ if(sp->bestValue >= sp->beta) {
+ sp_update_pv(sp->parentSstack, ss, sp->ply);
+ for(int i = 0; i < ActiveThreads; i++)
+ if(i != threadID && (i == sp->master || sp->slaves[i]))
+ Threads[i].stop = true;
+ sp->finished = true;
+ }
+ }
+ lock_release(&(sp->lock));
+ }
+
+ lock_grab(&(sp->lock));
+
+ // If this is the master thread and we have been asked to stop because of
+ // a beta cutoff higher up in the tree, stop all slave threads:
+ if(sp->master == threadID && thread_should_stop(threadID))
+ for(int i = 0; i < ActiveThreads; i++)
+ if(sp->slaves[i])
+ Threads[i].stop = true;
+
+ sp->cpus--;
+ sp->slaves[threadID] = 0;
+
+ lock_release(&(sp->lock));
+ }
+
+
+ // sp_search_pv() is used to search from a PV split point. This function
+ // is called by each thread working at the split point. It is similar to
+ // the normal search_pv() function, but simpler. Because we have already
+ // probed the hash table and searched the first move before splitting, we
+ // don't have to repeat all this work in sp_search_pv(). We also don't
+ // need to store anything to the hash table here: This is taken care of
+ // after we return from the split point.
+
+ void sp_search_pv(SplitPoint *sp, int threadID) {
+ assert(threadID >= 0 && threadID < ActiveThreads);
+ assert(ActiveThreads > 1);
+
+ Position pos = Position(sp->pos);
+ SearchStack *ss = sp->sstack[threadID];
+ Value value;
+ Move move;
+ int moveCount = sp->moves;
+
+ while(sp->alpha < sp->beta && !thread_should_stop(threadID)
+ && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) {
+ UndoInfo u;
+ Depth ext, newDepth;
+ bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
+ bool moveIsCapture = pos.move_is_capture(move);
+ bool moveIsPassedPawnPush = pos.move_is_passed_pawn_push(move);
+
+ assert(move_is_ok(move));
+
+ ss[sp->ply].currentMoveCaptureValue = move_is_ep(move)?
+ PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
+
+ lock_grab(&(sp->lock));
+ sp->moves++;
+ moveCount = sp->moves;
+ lock_release(&(sp->lock));
+
+ ss[sp->ply].currentMove = move;
+
+ // Decide the new search depth.
+ ext = extension(pos, move, true, moveIsCheck, false, false);
+ newDepth = sp->depth - OnePly + ext;
+
+ // Make and search the move.
+ pos.do_move(move, u, sp->dcCandidates);
+ if(ext == Depth(0) && moveCount >= LMRPVMoves && !moveIsCapture
+ && !move_promotion(move) && !moveIsPassedPawnPush
+ && !move_is_castle(move)
+ && move != ss[sp->ply].killer1 && move != ss[sp->ply].killer2) {
+ ss[sp->ply].reduction = OnePly;
+ value = -search(pos, ss, -sp->alpha, newDepth - OnePly, sp->ply+1,
+ true, threadID);
+ }
+ else
+ value = sp->alpha + 1;
+ if(value > sp->alpha) {
+ ss[sp->ply].reduction = Depth(0);
+ value = -search(pos, ss, -sp->alpha, newDepth, sp->ply+1, true,
+ threadID);
+ if(value > sp->alpha && value < sp->beta) {
+ if(sp->ply == 1 && RootMoveNumber == 1)
+ // When the search fails high at ply 1 while searching the first
+ // move at the root, set the flag failHighPly1. This is used for
+ // time managment: We don't want to stop the search early in
+ // such cases, because resolving the fail high at ply 1 could
+ // result in a big drop in score at the root.
+ Threads[threadID].failHighPly1 = true;
+ value = -search_pv(pos, ss, -sp->beta, -sp->alpha, newDepth,
+ sp->ply+1, threadID);
+ Threads[threadID].failHighPly1 = false;
+ }
+ }
+ pos.undo_move(move, u);
+
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
+
+ if(thread_should_stop(threadID))
+ break;
+
+ // New best move?
+ lock_grab(&(sp->lock));
+ if(value > sp->bestValue && !thread_should_stop(threadID)) {
+ sp->bestValue = value;
+ if(value > sp->alpha) {
+ sp->alpha = value;
+ sp_update_pv(sp->parentSstack, ss, sp->ply);
+ if(value == value_mate_in(sp->ply + 1))
+ ss[sp->ply].mateKiller = move;
+ if(value >= sp->beta) {
+ for(int i = 0; i < ActiveThreads; i++)
+ if(i != threadID && (i == sp->master || sp->slaves[i]))
+ Threads[i].stop = true;
+ sp->finished = true;
+ }
+ }
+ // If we are at ply 1, and we are searching the first root move at
+ // ply 0, set the 'Problem' variable if the score has dropped a lot
+ // (from the computer's point of view) since the previous iteration:
+ if(Iteration >= 2 &&
+ -value <= ValueByIteration[Iteration-1] - ProblemMargin)
+ Problem = true;
+ }
+ lock_release(&(sp->lock));
+ }
+
+ lock_grab(&(sp->lock));
+
+ // If this is the master thread and we have been asked to stop because of
+ // a beta cutoff higher up in the tree, stop all slave threads:
+ if(sp->master == threadID && thread_should_stop(threadID))
+ for(int i = 0; i < ActiveThreads; i++)
+ if(sp->slaves[i])
+ Threads[i].stop = true;
+
+ sp->cpus--;
+ sp->slaves[threadID] = 0;
+
+ lock_release(&(sp->lock));
+ }
+
+
+ /// The RootMove class
+
+ // Constructor
+
+ RootMove::RootMove() {
+ nodes = cumulativeNodes = 0ULL;
+ }
+
+
+ /// The RootMoveList class
+
+ // Constructor
+
+ RootMoveList::RootMoveList(Position &pos, Move searchMoves[]) {
+ MoveStack mlist[MaxRootMoves];
+ bool includeAllMoves = (searchMoves[0] == MOVE_NONE);
+ int i, j = 0, k;
+
+ // Generate all legal moves
+ count = generate_legal_moves(pos, mlist);
+
+ // Add each move to the moves[] array
+ for(i = 0; i < count; i++) {
+ UndoInfo u;
+ SearchStack ss[PLY_MAX_PLUS_2];
+ bool includeMove;
+
+ if(includeAllMoves)
+ includeMove = true;
+ else {
+ includeMove = false;
+ for(k = 0; searchMoves[k] != MOVE_NONE; k++)
+ if(searchMoves[k] == mlist[i].move) {
+ includeMove = true;
+ break;
+ }
+ }
+
+ if(includeMove) {
+ moves[j].move = mlist[i].move;
+ moves[j].nodes = 0ULL;
+ pos.do_move(moves[j].move, u);
+ moves[j].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE,
+ Depth(0), 1, 0);
+ pos.undo_move(moves[j].move, u);
+ moves[j].pv[0] = moves[i].move;
+ moves[j].pv[1] = MOVE_NONE; // FIXME
+ j++;
+ }
+ }
+ count = j;
+ this->sort();
+ }
+
+
+ // Simple accessor methods for the RootMoveList class
+
+ Move RootMoveList::get_move(int moveNum) const {
+ return moves[moveNum].move;
+ }
+
+ Value RootMoveList::get_move_score(int moveNum) const {
+ return moves[moveNum].score;
+ }
+
+ void RootMoveList::set_move_score(int moveNum, Value score) {
+ moves[moveNum].score = score;
+ }
+
+ void RootMoveList::set_move_nodes(int moveNum, int64_t nodes) {
+ moves[moveNum].nodes = nodes;
+ moves[moveNum].cumulativeNodes += nodes;
+ }
+
+ void RootMoveList::set_move_pv(int moveNum, const Move pv[]) {
+ int j;
+ for(j = 0; pv[j] != MOVE_NONE; j++)
+ moves[moveNum].pv[j] = pv[j];
+ moves[moveNum].pv[j] = MOVE_NONE;
+ }
+
+ Move RootMoveList::get_move_pv(int moveNum, int i) const {
+ return moves[moveNum].pv[i];
+ }
+
+ int64_t RootMoveList::get_move_cumulative_nodes(int moveNum) {
+ return moves[moveNum].cumulativeNodes;
+ }
+
+ int RootMoveList::move_count() const {
+ return count;
+ }
+
+
+ // RootMoveList::scan_for_easy_move() is called at the end of the first
+ // iteration, and is used to detect an "easy move", i.e. a move which appears
+ // to be much bester than all the rest. If an easy move is found, the move
+ // is returned, otherwise the function returns MOVE_NONE. It is very
+ // important that this function is called at the right moment: The code
+ // assumes that the first iteration has been completed and the moves have
+ // been sorted.
+
+ Move RootMoveList::scan_for_easy_move() const {
+ Value bestMoveValue = this->get_move_score(0);
+ for(int i = 1; i < this->move_count(); i++)
+ if(this->get_move_score(i) >= bestMoveValue - EasyMoveMargin)
+ return MOVE_NONE;
+ return this->get_move(0);
+ }
+
+
+ // RootMoveList::sort() sorts the root move list at the beginning of a new
+ // iteration.
+
+ void RootMoveList::sort() {
+ for(int i = 1; i < count; i++) {
+ RootMove rm = moves[i];
+ int j;
+ for(j = i; j > 0 && compare_root_moves(moves[j-1], rm); j--)
+ moves[j] = moves[j-1];
+ moves[j] = rm;
+ }
+ }
+
+
+ // RootMoveList::sort_multipv() sorts the first few moves in the root move
+ // list by their scores and depths. It is used to order the different PVs
+ // correctly in MultiPV mode.
+
+ void RootMoveList::sort_multipv(int n) {
+ for(int i = 1; i <= n; i++) {
+ RootMove rm = moves[i];
+ int j;
+ for(j = i; j > 0 && moves[j-1].score < rm.score; j--)
+ moves[j] = moves[j-1];
+ moves[j] = rm;
+ }
+ }
+
+
+ // RootMoveList::compare_root_moves() is the comparison function used by
+ // RootMoveList::sort when sorting the moves. A move m1 is considered to
+ // be better than a move m2 if it has a higher score, or if the moves have
+ // equal score but m1 has the higher node count.
+
+ int RootMoveList::compare_root_moves(const RootMove &rm1,
+ const RootMove &rm2) {
+ if(rm1.score < rm2.score) return 1;
+ else if(rm1.score > rm2.score) return 0;
+ else if(rm1.nodes < rm2.nodes) return 1;
+ else if(rm1.nodes > rm2.nodes) return 0;
+ else return 1;
+ }
+
+
+ // init_search_stack() initializes a search stack at the beginning of a
+ // new search from the root.
+
+ void init_search_stack(SearchStack ss[]) {
+ for(int i = 0; i < 3; i++) {
+ ss[i].pv[i] = MOVE_NONE;
+ ss[i].pv[i+1] = MOVE_NONE;
+ ss[i].currentMove = MOVE_NONE;
+ ss[i].mateKiller = MOVE_NONE;
+ ss[i].killer1 = MOVE_NONE;
+ ss[i].killer2 = MOVE_NONE;
+ ss[i].threatMove = MOVE_NONE;
+ ss[i].reduction = Depth(0);
+ }
+ }
+
+
+ // init_node() is called at the beginning of all the search functions
+ // (search(), search_pv(), qsearch(), and so on) and initializes the search
+ // stack object corresponding to the current node. Once every
+ // NodesBetweenPolls nodes, init_node() also calls poll(), which polls
+ // for user input and checks whether it is time to stop the search.
+
+ void init_node(const Position &pos, SearchStack ss[], int ply, int threadID) {
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ Threads[threadID].nodes++;
+
+ if(threadID == 0) {
+ NodesSincePoll++;
+ if(NodesSincePoll >= NodesBetweenPolls) {
+ poll();
+ NodesSincePoll = 0;
+ }
+ }
+
+ ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE;
+ ss[ply+2].mateKiller = MOVE_NONE;
+ ss[ply+2].killer1 = ss[ply+2].killer2 = MOVE_NONE;
+ ss[ply].threatMove = MOVE_NONE;
+ ss[ply].reduction = Depth(0);
+ ss[ply].currentMoveCaptureValue = Value(0);
+
+ if(Threads[threadID].printCurrentLine)
+ print_current_line(ss, ply, threadID);
+ }
+
+
+ // update_pv() is called whenever a search returns a value > alpha. It
+ // updates the PV in the SearchStack object corresponding to the current
+ // node.
+
+ void update_pv(SearchStack ss[], int ply) {
+ assert(ply >= 0 && ply < PLY_MAX);
+
+ ss[ply].pv[ply] = ss[ply].currentMove;
+ int p;
+ for(p = ply + 1; ss[ply+1].pv[p] != MOVE_NONE; p++)
+ ss[ply].pv[p] = ss[ply+1].pv[p];
+ ss[ply].pv[p] = MOVE_NONE;
+ }
+
+
+ // sp_update_pv() is a variant of update_pv for use at split points. The
+ // difference between the two functions is that sp_update_pv also updates
+ // the PV at the parent node.
+
+ void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply) {
+ assert(ply >= 0 && ply < PLY_MAX);
+
+ ss[ply].pv[ply] = pss[ply].pv[ply] = ss[ply].currentMove;
+ int p;
+ for(p = ply + 1; ss[ply+1].pv[p] != MOVE_NONE; p++)
+ ss[ply].pv[p] = pss[ply].pv[p] = ss[ply+1].pv[p];
+ ss[ply].pv[p] = pss[ply].pv[p] = MOVE_NONE;
+ }
+
+
+ // connected_moves() tests whether two moves are 'connected' in the sense
+ // that the first move somehow made the second move possible (for instance
+ // if the moving piece is the same in both moves). The first move is
+ // assumed to be the move that was made to reach the current position, while
+ // the second move is assumed to be a move from the current position.
+
+ bool connected_moves(const Position &pos, Move m1, Move m2) {
+ Square f1, t1, f2, t2;
+
+ assert(move_is_ok(m1));
+ assert(move_is_ok(m2));
+
+ if(m2 == MOVE_NONE)
+ return false;
+
+ // Case 1: The moving piece is the same in both moves.
+ f2 = move_from(m2);
+ t1 = move_to(m1);
+ if(f2 == t1)
+ return true;
+
+ // Case 2: The destination square for m2 was vacated by m1.
+ t2 = move_to(m2);
+ f1 = move_from(m1);
+ if(t2 == f1)
+ return true;
+
+ // Case 3: Moving through the vacated square:
+ if(piece_is_slider(pos.piece_on(f2)) &&
+ bit_is_set(squares_between(f2, t2), f1))
+ return true;
+
+ // Case 4: The destination square for m2 is attacked by the moving piece
+ // in m1:
+ if(pos.piece_attacks_square(t1, t2))
+ return true;
+
+ // Case 5: Discovered check, checking piece is the piece moved in m1:
+ if(piece_is_slider(pos.piece_on(t1)) &&
+ bit_is_set(squares_between(t1, pos.king_square(pos.side_to_move())),
+ f2) &&
+ !bit_is_set(squares_between(t2, pos.king_square(pos.side_to_move())),
+ t2)) {
+ Bitboard occ = pos.occupied_squares();
+ Color us = pos.side_to_move();
+ Square ksq = pos.king_square(us);
+ clear_bit(&occ, f2);
+ if(pos.type_of_piece_on(t1) == BISHOP) {
+ if(bit_is_set(bishop_attacks_bb(ksq, occ), t1))
+ return true;
+ }
+ else if(pos.type_of_piece_on(t1) == ROOK) {
+ if(bit_is_set(rook_attacks_bb(ksq, occ), t1))
+ return true;
+ }
+ else {
+ assert(pos.type_of_piece_on(t1) == QUEEN);
+ if(bit_is_set(queen_attacks_bb(ksq, occ), t1))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ // extension() decides whether a move should be searched with normal depth,
+ // or with extended depth. Certain classes of moves (checking moves, in
+ // particular) are searched with bigger depth than ordinary moves.
+
+ Depth extension(const Position &pos, Move m, bool pvNode,
+ bool check, bool singleReply, bool mateThreat) {
+ Depth result = Depth(0);
+
+ if(check)
+ result += CheckExtension[pvNode];
+ if(singleReply)
+ result += SingleReplyExtension[pvNode];
+ if(pos.move_is_pawn_push_to_7th(m))
+ result += PawnPushTo7thExtension[pvNode];
+ if(pos.move_is_passed_pawn_push(m))
+ result += PassedPawnExtension[pvNode];
+ if(mateThreat)
+ result += MateThreatExtension[pvNode];
+ if(pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
+ && (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
+ - pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
+ && !move_promotion(m))
+ result += PawnEndgameExtension[pvNode];
+ if(pvNode && pos.move_is_capture(m)
+ && pos.type_of_piece_on(move_to(m)) != PAWN && pos.see(m) >= 0)
+ result += OnePly/2;
+
+ return Min(result, OnePly);
+ }
+
+
+ // ok_to_do_nullmove() looks at the current position and decides whether
+ // doing a 'null move' should be allowed. In order to avoid zugzwang
+ // problems, null moves are not allowed when the side to move has very
+ // little material left. Currently, the test is a bit too simple: Null
+ // moves are avoided only when the side to move has only pawns left. It's
+ // probably a good idea to avoid null moves in at least some more
+ // complicated endgames, e.g. KQ vs KR. FIXME
+
+ bool ok_to_do_nullmove(const Position &pos) {
+ if(pos.non_pawn_material(pos.side_to_move()) == Value(0))
+ return false;
+ return true;
+ }
+
+
+ // ok_to_prune() tests whether it is safe to forward prune a move. Only
+ // non-tactical moves late in the move list close to the leaves are
+ // candidates for pruning.
+
+ bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d) {
+ Square mfrom, mto, tfrom, tto;
+
+ assert(move_is_ok(m));
+ assert(threat == MOVE_NONE || move_is_ok(threat));
+ assert(!move_promotion(m));
+ assert(!pos.move_is_check(m));
+ assert(!pos.move_is_capture(m));
+ assert(!pos.move_is_passed_pawn_push(m));
+ assert(d >= OnePly);
+
+ mfrom = move_from(m);
+ mto = move_to(m);
+ tfrom = move_from(threat);
+ tto = move_to(threat);
+
+ // Case 1: Castling moves are never pruned.
+ if(move_is_castle(m))
+ return false;
+
+ // Case 2: Don't prune moves which move the threatened piece
+ if(!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
+ return false;
+
+ // Case 3: If the threatened piece has value less than or equal to the
+ // value of the threatening piece, don't prune move which defend it.
+ if(!PruneDefendingMoves && threat != MOVE_NONE
+ && (piece_value_midgame(pos.piece_on(tfrom))
+ >= piece_value_midgame(pos.piece_on(tto)))
+ && pos.move_attacks_square(m, tto))
+ return false;
+
+ // Case 4: Don't prune moves with good history.
+ if(!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
+ return false;
+
+ // Case 5: If the moving piece in the threatened move is a slider, don't
+ // prune safe moves which block its ray.
+ if(!PruneBlockingMoves && threat != MOVE_NONE
+ && piece_is_slider(pos.piece_on(tfrom))
+ && bit_is_set(squares_between(tfrom, tto), mto) && pos.see(m) >= 0)
+ return false;
+
+ return true;
+ }
+
+
+ // fail_high_ply_1() checks if some thread is currently resolving a fail
+ // high at ply 1 at the node below the first root node. This information
+ // is used for time managment.
+
+ bool fail_high_ply_1() {
+ for(int i = 0; i < ActiveThreads; i++)
+ if(Threads[i].failHighPly1)
+ return true;
+ return false;
+ }
+
+
+ // current_search_time() returns the number of milliseconds which have passed
+ // since the beginning of the current search.
+
+ int current_search_time() {
+ return get_system_time() - SearchStartTime;
+ }
+
+
+ // nps() computes the current nodes/second count.
+
+ int nps() {
+ int t = current_search_time();
+ return (t > 0)? int((nodes_searched() * 1000) / t) : 0;
+ }
+
+
+ // poll() performs two different functions: It polls for user input, and it
+ // looks at the time consumed so far and decides if it's time to abort the
+ // search.
+
+ void poll() {
+ int t, data;
+ static int lastInfoTime;
+
+ t = current_search_time();
+
+ // Poll for input
+ data = Bioskey();
+ if(data) {
+ char input[256];
+ if(fgets(input, 255, stdin) == NULL)
+ strcpy(input, "quit\n");
+ if(strncmp(input, "quit", 4) == 0) {
+ AbortSearch = true;
+ PonderSearch = false;
+ Quit = true;
+ }
+ else if(strncmp(input, "stop", 4) == 0) {
+ AbortSearch = true;
+ PonderSearch = false;
+ }
+ else if(strncmp(input, "ponderhit", 9) == 0)
+ ponderhit();
+ }
+
+ // Print search information
+ if(t < 1000)
+ lastInfoTime = 0;
+ else if(lastInfoTime > t)
+ // HACK: Must be a new search where we searched less than
+ // NodesBetweenPolls nodes during the first second of search.
+ lastInfoTime = 0;
+ else if(t - lastInfoTime >= 1000) {
+ lastInfoTime = t;
+ lock_grab(&IOLock);
+ std::cout << "info nodes " << nodes_searched() << " nps " << nps()
+ << " time " << t << " hashfull " << TT.full() << std::endl;
+ lock_release(&IOLock);
+ if(ShowCurrentLine)
+ Threads[0].printCurrentLine = true;
+ }
+
+ // Should we stop the search?
+ if(!PonderSearch && Iteration >= 2 &&
+ (!InfiniteSearch && (t > AbsoluteMaxSearchTime ||
+ (RootMoveNumber == 1 &&
+ t > MaxSearchTime + ExtraSearchTime) ||
+ (!FailHigh && !fail_high_ply_1() && !Problem &&
+ t > 6*(MaxSearchTime + ExtraSearchTime)))))
+ AbortSearch = true;
+
+ if(!PonderSearch && ExactMaxTime && t >= ExactMaxTime)
+ AbortSearch = true;
+
+ if(!PonderSearch && Iteration >= 3 && MaxNodes
+ && nodes_searched() >= MaxNodes)
+ AbortSearch = true;
+ }
+
+
+ // ponderhit() is called when the program is pondering (i.e. thinking while
+ // it's the opponent's turn to move) in order to let the engine know that
+ // it correctly predicted the opponent's move.
+
+ void ponderhit() {
+ int t = current_search_time();
+ PonderSearch = false;
+ if(Iteration >= 2 &&
+ (!InfiniteSearch && (StopOnPonderhit ||
+ t > AbsoluteMaxSearchTime ||
+ (RootMoveNumber == 1 &&
+ t > MaxSearchTime + ExtraSearchTime) ||
+ (!FailHigh && !fail_high_ply_1() && !Problem &&
+ t > 6*(MaxSearchTime + ExtraSearchTime)))))
+ AbortSearch = true;
+ }
+
+
+ // print_current_line() prints the current line of search for a given
+ // thread. Called when the UCI option UCI_ShowCurrLine is 'true'.
+
+ void print_current_line(SearchStack ss[], int ply, int threadID) {
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ if(!Threads[threadID].idle) {
+ lock_grab(&IOLock);
+ std::cout << "info currline " << (threadID + 1);
+ for(int p = 0; p < ply; p++)
+ std::cout << " " << ss[p].currentMove;
+ std::cout << std::endl;
+ lock_release(&IOLock);
+ }
+ Threads[threadID].printCurrentLine = false;
+ if(threadID + 1 < ActiveThreads)
+ Threads[threadID + 1].printCurrentLine = true;
+ }
+
+
+ // wait_for_stop_or_ponderhit() is called when the maximum depth is reached
+ // while the program is pondering. The point is to work around a wrinkle in
+ // the UCI protocol: When pondering, the engine is not allowed to give a
+ // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
+ // We simply wait here until one of these commands is sent, and return,
+ // after which the bestmove and pondermove will be printed (in id_loop()).
+
+ void wait_for_stop_or_ponderhit() {
+ std::string command;
+
+ while(true) {
+ if(!std::getline(std::cin, command))
+ command = "quit";
+
+ if(command == "quit") {
+ OpeningBook.close();
+ stop_threads();
+ quit_eval();
+ exit(0);
+ }
+ else if(command == "ponderhit" || command == "stop")
+ break;
+ }
+ }
+
+
+ // idle_loop() is where the threads are parked when they have no work to do.
+ // The parameter "waitSp", if non-NULL, is a pointer to an active SplitPoint
+ // object for which the current thread is the master.
+
+ void idle_loop(int threadID, SplitPoint *waitSp) {
+ assert(threadID >= 0 && threadID < THREAD_MAX);
+
+ Threads[threadID].running = true;
+
+ while(true) {
+ if(AllThreadsShouldExit && threadID != 0)
+ break;
+
+ // If we are not thinking, wait for a condition to be signaled instead
+ // of wasting CPU time polling for work:
+ while(threadID != 0 && (Idle || threadID >= ActiveThreads)) {
+#if !defined(_MSC_VER)
+ pthread_mutex_lock(&WaitLock);
+ if(Idle || threadID >= ActiveThreads)
+ pthread_cond_wait(&WaitCond, &WaitLock);
+ pthread_mutex_unlock(&WaitLock);
+#else
+ WaitForSingleObject(SitIdleEvent[threadID], INFINITE);
+#endif
+ }
+
+ // If this thread has been assigned work, launch a search:
+ if(Threads[threadID].workIsWaiting) {
+ Threads[threadID].workIsWaiting = false;
+ if(Threads[threadID].splitPoint->pvNode)
+ sp_search_pv(Threads[threadID].splitPoint, threadID);
+ else
+ sp_search(Threads[threadID].splitPoint, threadID);
+ Threads[threadID].idle = true;
+ }
+
+ // If this thread is the master of a split point and all threads have
+ // finished their work at this split point, return from the idle loop:
+ if(waitSp != NULL && waitSp->cpus == 0)
+ return;
+ }
+
+ Threads[threadID].running = false;
+ }
+
+
+ // init_split_point_stack() is called during program initialization, and
+ // initializes all split point objects.
+
+ void init_split_point_stack() {
+ for(int i = 0; i < THREAD_MAX; i++)
+ for(int j = 0; j < MaxActiveSplitPoints; j++) {
+ SplitPointStack[i][j].parent = NULL;
+ lock_init(&(SplitPointStack[i][j].lock), NULL);
+ }
+ }
+
+
+ // destroy_split_point_stack() is called when the program exits, and
+ // destroys all locks in the precomputed split point objects.
+
+ void destroy_split_point_stack() {
+ for(int i = 0; i < THREAD_MAX; i++)
+ for(int j = 0; j < MaxActiveSplitPoints; j++)
+ lock_destroy(&(SplitPointStack[i][j].lock));
+ }
+
+
+ // thread_should_stop() checks whether the thread with a given threadID has
+ // been asked to stop, directly or indirectly. This can happen if a beta
+ // cutoff has occured in thre thread's currently active split point, or in
+ // some ancestor of the current split point.
+
+ bool thread_should_stop(int threadID) {
+ assert(threadID >= 0 && threadID < ActiveThreads);
+
+ SplitPoint *sp;
+
+ if(Threads[threadID].stop)
+ return true;
+ if(ActiveThreads <= 2)
+ return false;
+ for(sp = Threads[threadID].splitPoint; sp != NULL; sp = sp->parent)
+ if(sp->finished) {
+ Threads[threadID].stop = true;
+ return true;
+ }
+ return false;
+ }
+
+
+ // thread_is_available() checks whether the thread with threadID "slave" is
+ // available to help the thread with threadID "master" at a split point. An
+ // obvious requirement is that "slave" must be idle. With more than two
+ // threads, this is not by itself sufficient: If "slave" is the master of
+ // some active split point, it is only available as a slave to the other
+ // threads which are busy searching the split point at the top of "slave"'s
+ // split point stack (the "helpful master concept" in YBWC terminology).
+
+ bool thread_is_available(int slave, int master) {
+ assert(slave >= 0 && slave < ActiveThreads);
+ assert(master >= 0 && master < ActiveThreads);
+ assert(ActiveThreads > 1);
+
+ if(!Threads[slave].idle || slave == master)
+ return false;
+
+ if(Threads[slave].activeSplitPoints == 0)
+ // No active split points means that the thread is available as a slave
+ // for any other thread.
+ return true;
+
+ if(ActiveThreads == 2)
+ return true;
+
+ // Apply the "helpful master" concept if possible.
+ if(SplitPointStack[slave][Threads[slave].activeSplitPoints-1].slaves[master])
+ return true;
+
+ return false;
+ }
+
+
+ // idle_thread_exists() tries to find an idle thread which is available as
+ // a slave for the thread with threadID "master".
+
+ bool idle_thread_exists(int master) {
+ assert(master >= 0 && master < ActiveThreads);
+ assert(ActiveThreads > 1);
+
+ for(int i = 0; i < ActiveThreads; i++)
+ if(thread_is_available(i, master))
+ return true;
+ return false;
+ }
+
+
+ // split() does the actual work of distributing the work at a node between
+ // several threads at PV nodes. If it does not succeed in splitting the
+ // node (because no idle threads are available, or because we have no unused
+ // split point objects), the function immediately returns false. If
+ // splitting is possible, a SplitPoint object is initialized with all the
+ // data that must be copied to the helper threads (the current position and
+ // search stack, alpha, beta, the search depth, etc.), and we tell our
+ // helper threads that they have been assigned work. This will cause them
+ // to instantly leave their idle loops and call sp_search_pv(). When all
+ // threads have returned from sp_search_pv (or, equivalently, when
+ // splitPoint->cpus becomes 0), split() returns true.
+
+ bool split(const Position &p, SearchStack *sstck, int ply,
+ Value *alpha, Value *beta, Value *bestValue,
+ Depth depth, int *moves,
+ MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode) {
+ assert(p.is_ok());
+ assert(sstck != NULL);
+ assert(ply >= 0 && ply < PLY_MAX);
+ assert(*bestValue >= -VALUE_INFINITE && *bestValue <= *alpha);
+ assert(!pvNode || *alpha < *beta);
+ assert(*beta <= VALUE_INFINITE);
+ assert(depth > Depth(0));
+ assert(master >= 0 && master < ActiveThreads);
+ assert(ActiveThreads > 1);
+
+ SplitPoint *splitPoint;
+ int i;
+
+ lock_grab(&MPLock);
+
+ // If no other thread is available to help us, or if we have too many
+ // active split points, don't split:
+ if(!idle_thread_exists(master) ||
+ Threads[master].activeSplitPoints >= MaxActiveSplitPoints) {
+ lock_release(&MPLock);
+ return false;
+ }
+
+ // Pick the next available split point object from the split point stack:
+ splitPoint = SplitPointStack[master] + Threads[master].activeSplitPoints;
+ Threads[master].activeSplitPoints++;
+
+ // Initialize the split point object:
+ splitPoint->parent = Threads[master].splitPoint;
+ splitPoint->finished = false;
+ splitPoint->ply = ply;
+ splitPoint->depth = depth;
+ splitPoint->alpha = pvNode? *alpha : (*beta - 1);
+ splitPoint->beta = *beta;
+ splitPoint->pvNode = pvNode;
+ splitPoint->dcCandidates = dcCandidates;
+ splitPoint->bestValue = *bestValue;
+ splitPoint->master = master;
+ splitPoint->mp = mp;
+ splitPoint->moves = *moves;
+ splitPoint->cpus = 1;
+ splitPoint->pos.copy(p);
+ splitPoint->parentSstack = sstck;
+ for(i = 0; i < ActiveThreads; i++)
+ splitPoint->slaves[i] = 0;
+
+ // Copy the current position and the search stack to the master thread:
+ memcpy(splitPoint->sstack[master], sstck, (ply+1)*sizeof(SearchStack));
+ Threads[master].splitPoint = splitPoint;
+
+ // Make copies of the current position and search stack for each thread:
+ for(i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint;
+ i++)
+ if(thread_is_available(i, master)) {
+ memcpy(splitPoint->sstack[i], sstck, (ply+1)*sizeof(SearchStack));
+ Threads[i].splitPoint = splitPoint;
+ splitPoint->slaves[i] = 1;
+ splitPoint->cpus++;
+ }
+
+ // Tell the threads that they have work to do. This will make them leave
+ // their idle loop.
+ for(i = 0; i < ActiveThreads; i++)
+ if(i == master || splitPoint->slaves[i]) {
+ Threads[i].workIsWaiting = true;
+ Threads[i].idle = false;
+ Threads[i].stop = false;
+ }
+
+ lock_release(&MPLock);
+
+ // Everything is set up. The master thread enters the idle loop, from
+ // which it will instantly launch a search, because its workIsWaiting
+ // slot is 'true'. We send the split point as a second parameter to the
+ // idle loop, which means that the main thread will return from the idle
+ // loop when all threads have finished their work at this split point
+ // (i.e. when // splitPoint->cpus == 0).
+ idle_loop(master, splitPoint);
+
+ // We have returned from the idle loop, which means that all threads are
+ // finished. Update alpha, beta and bestvalue, and return:
+ lock_grab(&MPLock);
+ if(pvNode) *alpha = splitPoint->alpha;
+ *beta = splitPoint->beta;
+ *bestValue = splitPoint->bestValue;
+ Threads[master].stop = false;
+ Threads[master].idle = false;
+ Threads[master].activeSplitPoints--;
+ Threads[master].splitPoint = splitPoint->parent;
+ lock_release(&MPLock);
+
+ return true;
+ }
+
+
+ // wake_sleeping_threads() wakes up all sleeping threads when it is time
+ // to start a new search from the root.
+
+ void wake_sleeping_threads() {
+ if(ActiveThreads > 1) {
+ for(int i = 1; i < ActiveThreads; i++) {
+ Threads[i].idle = true;
+ Threads[i].workIsWaiting = false;
+ }
+#if !defined(_MSC_VER)
+ pthread_mutex_lock(&WaitLock);
+ pthread_cond_broadcast(&WaitCond);
+ pthread_mutex_unlock(&WaitLock);
+#else
+ for(int i = 1; i < THREAD_MAX; i++)
+ SetEvent(SitIdleEvent[i]);
+#endif
+ }
+ }
+
+
+ // init_thread() is the function which is called when a new thread is
+ // launched. It simply calls the idle_loop() function with the supplied
+ // threadID. There are two versions of this function; one for POSIX threads
+ // and one for Windows threads.
+
+#if !defined(_MSC_VER)
+
+ void *init_thread(void *threadID) {
+ idle_loop(*(int *)threadID, NULL);
+ return NULL;
+ }
+
+#else
+
+ DWORD WINAPI init_thread(LPVOID threadID) {
+ idle_loop(*(int *)threadID, NULL);
+ return NULL;
+ }
+
+#endif
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(SEARCH_H_INCLUDED)
+#define SEARCH_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "types.h"
+#include "depth.h"
+#include "history.h"
+#include "lock.h"
+#include "movegen.h"
+#include "position.h"
+#include "tt.h"
+#include "value.h"
+
+
+////
+//// Constants
+////
+
+const int PLY_MAX = 100;
+const int PLY_MAX_PLUS_2 = 102;
+
+
+////
+//// Types
+////
+
+/// The SearchStack struct keeps track of the information we need to remember
+/// from nodes shallower and deeper in the tree during the search. Each
+/// search thread has its own array of SearchStack objects, indexed by the
+/// current ply.
+
+struct SearchStack {
+ Move pv[PLY_MAX];
+ Move currentMove;
+ Value currentMoveCaptureValue;
+ Move mateKiller, killer1, killer2;
+ Move threatMove;
+ Depth reduction;
+};
+
+
+////
+//// Global variables
+////
+
+extern TranspositionTable TT;
+
+extern int ActiveThreads;
+
+extern Lock SMPLock;
+
+// Perhaps better to make H local, and pass as parameter to MovePicker?
+extern History H;
+
+
+////
+//// Prototypes
+////
+
+extern void init_threads();
+extern void stop_threads();
+extern void think(const Position &pos, bool infinite, bool ponder, int time,
+ int increment, int movesToGo, int maxDepth, int maxNodes,
+ int maxTime, Move searchMoves[]);
+extern int64_t nodes_searched();
+
+
+#endif // !defined(SEARCH_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstdio>
+#include <string>
+
+#include "square.h"
+
+
+////
+//// Functions
+////
+
+
+/// Translating files, ranks and squares to/from characters and strings:
+
+File file_from_char(char c) {
+ return File(c - 'a') + FILE_A;
+}
+
+
+char file_to_char(File f) {
+ return char(f - FILE_A) + 'a';
+}
+
+
+Rank rank_from_char(char c) {
+ return Rank(c - '1') + RANK_1;
+}
+
+
+char rank_to_char(Rank r) {
+ return char(r - RANK_1) + '1';
+}
+
+
+Square square_from_string(const std::string &str) {
+ return make_square(file_from_char(str[0]), rank_from_char(str[1]));
+}
+
+
+const std::string square_to_string(Square s) {
+ std::string str;
+ str += file_to_char(square_file(s));
+ str += rank_to_char(square_rank(s));
+ return str;
+}
+
+
+/// file_is_ok(), rank_is_ok() and square_is_ok(), for debugging:
+
+bool file_is_ok(File f) {
+ return f >= FILE_A && f <= FILE_H;
+}
+
+
+bool rank_is_ok(Rank r) {
+ return r >= RANK_1 && r <= RANK_8;
+}
+
+
+bool square_is_ok(Square s) {
+ return file_is_ok(square_file(s)) && rank_is_ok(square_rank(s));
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(SQUARE_H_INCLUDED)
+#define SQUARE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include <string>
+
+#include "color.h"
+#include "misc.h"
+
+
+////
+//// Types
+////
+
+enum Square {
+ SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
+ SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
+ SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
+ SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
+ SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
+ SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
+ SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
+ SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
+ SQ_NONE
+};
+
+enum File {
+ FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NONE
+};
+
+enum Rank {
+ RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NONE
+};
+
+enum SquareDelta {
+ DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
+ DELTA_SW = -011, DELTA_S = -010, DELTA_SE = -07, DELTA_SEE = -06,
+ DELTA_W = -01, DELTA_ZERO = 0, DELTA_E = 01, DELTA_NWW = 06, DELTA_NW = 07,
+ DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017,
+ DELTA_NN = 020, DELTA_NNE = 021
+};
+
+
+////
+//// Constants
+////
+
+const int FlipMask = 070;
+const int FlopMask = 07;
+
+
+////
+//// Inline functions
+////
+
+inline File operator+ (File x, int i) { return File(int(x) + i); }
+inline File operator+ (File x, File y) { return x + int(y); }
+inline void operator++ (File &x, int) { x = File(int(x) + 1); }
+inline void operator+= (File &x, int i) { x = File(int(x) + i); }
+inline File operator- (File x, int i) { return File(int(x) - i); }
+inline void operator-- (File &x, int) { x = File(int(x) - 1); }
+inline void operator-= (File &x, int i) { x = File(int(x) - i); }
+
+inline Rank operator+ (Rank x, int i) { return Rank(int(x) + i); }
+inline Rank operator+ (Rank x, Rank y) { return x + int(y); }
+inline void operator++ (Rank &x, int) { x = Rank(int(x) + 1); }
+inline void operator+= (Rank &x, int i) { x = Rank(int(x) + i); }
+inline Rank operator- (Rank x, int i) { return Rank(int(x) - i); }
+inline void operator-- (Rank &x, int) { x = Rank(int(x) - 1); }
+inline void operator-= (Rank &x, int i) { x = Rank(int(x) - i); }
+
+inline Square operator+ (Square x, int i) { return Square(int(x) + i); }
+inline void operator++ (Square &x, int) { x = Square(int(x) + 1); }
+inline void operator+= (Square &x, int i) { x = Square(int(x) + i); }
+inline Square operator- (Square x, int i) { return Square(int(x) - i); }
+inline void operator-- (Square &x, int) { x = Square(int(x) - 1); }
+inline void operator-= (Square &x, int i) { x = Square(int(x) - i); }
+inline Square operator+ (Square x, SquareDelta i) { return Square(int(x) + i); }
+inline void operator+= (Square &x, SquareDelta i) { x = Square(int(x) + i); }
+inline Square operator- (Square x, SquareDelta i) { return Square(int(x) - i); }
+inline void operator-= (Square &x, SquareDelta i) { x = Square(int(x) - i); }
+inline SquareDelta operator- (Square x, Square y) {
+ return SquareDelta(int(x) - int(y));
+}
+
+inline Square make_square(File f, Rank r) {
+ return Square(int(f) | (int(r) << 3));
+}
+
+inline File square_file(Square s) {
+ return File(int(s) & 7);
+}
+
+inline Rank square_rank(Square s) {
+ return Rank(int(s) >> 3);
+}
+
+inline Square flip_square(Square s) {
+ return Square(int(s) ^ FlipMask);
+}
+
+inline Square flop_square(Square s) {
+ return Square(int(s) ^ FlopMask);
+}
+
+inline Square relative_square(Color c, Square s) {
+ return Square(int(s) ^ (int(c) * FlipMask));
+}
+
+inline Rank pawn_rank(Color c, Square s) {
+ return square_rank(relative_square(c, s));
+}
+
+inline Color square_color(Square s) {
+ return Color((int(square_file(s)) + int(square_rank(s))) & 1);
+}
+
+inline int file_distance(File f1, File f2) {
+ return abs(int(f1) - int(f2));
+}
+
+inline int file_distance(Square s1, Square s2) {
+ return file_distance(square_file(s1), square_file(s2));
+}
+
+inline int rank_distance(Rank r1, Rank r2) {
+ return abs(int(r1) - int(r2));
+}
+
+inline int rank_distance(Square s1, Square s2) {
+ return rank_distance(square_rank(s1), square_rank(s2));
+}
+
+inline int square_distance(Square s1, Square s2) {
+ return Max(file_distance(s1, s2), rank_distance(s1, s2));
+}
+
+
+////
+//// Prototypes
+////
+
+extern File file_from_char(char c);
+extern char file_to_char(File f);
+extern Rank rank_from_char(char c);
+extern char rank_to_char(Rank r);
+extern Square square_from_string(const std::string &str);
+extern const std::string square_to_string(Square s);
+
+extern bool file_is_ok(File f);
+extern bool rank_is_ok(Rank r);
+extern bool square_is_ok(Square s);
+
+
+#endif // !defined(SQUARE_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(THREAD_H_INCLUDED)
+#define THREAD_H_INCLUDED
+
+
+////
+//// Includes
+////
+
+#include "lock.h"
+#include "movepick.h"
+#include "position.h"
+#include "search.h"
+
+
+////
+//// Constants and variables
+////
+
+const int THREAD_MAX = 8;
+
+
+////
+//// Types
+////
+
+struct SplitPoint {
+ SplitPoint *parent;
+ Position pos;
+ SearchStack sstack[THREAD_MAX][PLY_MAX];
+ SearchStack *parentSstack;
+ int ply;
+ Depth depth;
+ volatile Value alpha, beta, bestValue;
+ bool pvNode;
+ Bitboard dcCandidates;
+ int master, slaves[THREAD_MAX];
+ Lock lock;
+ MovePicker *mp;
+ volatile int moves;
+ volatile int cpus;
+ bool finished;
+};
+
+
+struct Thread {
+ SplitPoint *splitPoint;
+ int activeSplitPoints;
+ uint64_t nodes;
+ bool failHighPly1;
+ volatile bool stop;
+ volatile bool running;
+ volatile bool idle;
+ volatile bool workIsWaiting;
+ volatile bool printCurrentLine;
+ unsigned char pad[64];
+};
+
+
+#endif // !defined(THREAD_H_INCLUDED)
--- /dev/null
+/*
+ (c) Copyright 1992 Eric Backus
+
+ This software may be used freely so long as this copyright notice is
+ left intact. There is no warrantee on this software.
+ */
+#include <windows.h>
+#include <time.h>
+#include "dos.h"
+
+
+int gettimeofday(struct timeval * tp, struct timezone * tzp)
+{
+ SYSTEMTIME systime;
+
+ if (tp) {
+ struct tm tmrec;
+ time_t theTime = time(NULL);
+
+
+ tmrec = *localtime(&theTime);
+ tp->tv_sec = mktime(&tmrec);
+ GetLocalTime(&systime); /* system time */
+
+ tp->tv_usec = systime.wMilliseconds * 1000;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cmath>
+
+#include "tt.h"
+
+
+////
+//// Functions
+////
+
+/// Constructor
+
+TranspositionTable::TranspositionTable(unsigned mbSize) {
+ size = 0;
+ generation = 0;
+ writes = 0;
+ entries = 0;
+ this->set_size(mbSize);
+}
+
+
+/// Destructor
+
+TranspositionTable::~TranspositionTable() {
+ delete [] entries;
+}
+
+
+/// TranspositionTable::set_size sets the size of the transposition table,
+/// measured in megabytes.
+
+void TranspositionTable::set_size(unsigned mbSize) {
+ unsigned newSize;
+
+ assert(mbSize >= 4 && mbSize <= 1024);
+
+ for(newSize = 1024; newSize * 4 * (sizeof(TTEntry)) <= (mbSize << 20);
+ newSize *= 2);
+ newSize /= 2;
+
+ if(newSize != size) {
+ size = newSize;
+ delete [] entries;
+ entries = new TTEntry[size * 4];
+ if(entries == NULL) {
+ std::cerr << "Failed to allocate " << mbSize
+ << " MB for transposition table."
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ this->clear();
+ }
+}
+
+
+/// TranspositionTable::clear overwrites the entire transposition table
+/// with zeroes. It is called whenever the table is resized, or when the
+/// user asks the program to clear the table (from the UCI interface).
+/// Perhaps we should also clear it when the "ucinewgame" command is recieved?
+
+void TranspositionTable::clear() {
+ memset(entries, 0, size * 4 * sizeof(TTEntry));
+}
+
+
+/// TranspositionTable::store writes a new entry containing a position,
+/// a value, a value type, a search depth, and a best move to the
+/// transposition table. The transposition table is organized in clusters
+/// of four TTEntry objects, and when a new entry is written, it replaces
+/// the least valuable of the four entries in a cluster. A TTEntry t1 is
+/// considered to be more valuable than a TTEntry t2 if t1 is from the
+/// current search and t2 is from a previous search, or if the depth of t1
+/// is bigger than the depth of t2.
+
+void TranspositionTable::store(const Position &pos, Value v, Depth d,
+ Move m, ValueType type) {
+ TTEntry *tte, *replace;
+
+ tte = replace = entries + int(pos.get_key() & (size - 1)) * 4;
+ for(int i = 0; i < 4; i++) {
+ if((tte+i)->key() == pos.get_key()) {
+ if(m == MOVE_NONE)
+ m = (tte+i)->move();
+ *(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
+ return;
+ }
+ if(replace->generation() == generation) {
+ if((tte+i)->generation() != generation ||
+ (tte+i)->depth() < replace->depth())
+ replace = tte+i;
+ }
+ else if((tte+i)->generation() != generation &&
+ (tte+i)->depth() < replace->depth())
+ replace = tte+i;
+ }
+ *replace = TTEntry(pos.get_key(), v, type, d, m, generation);
+ writes++;
+}
+
+
+/// TranspositionTable::retrieve looks up the current position in the
+/// transposition table, and extracts the value, value type, depth and
+/// best move if the position is found. The return value is true if
+/// the position is found, and false if it isn't.
+
+bool TranspositionTable::retrieve(const Position &pos, Value *value,
+ Depth *d, Move *move,
+ ValueType *type) const {
+ TTEntry *tte;
+ bool found = false;
+
+ tte = entries + int(pos.get_key() & (size - 1)) * 4;
+ for(int i = 0; i < 4 && !found ; i++)
+ if((tte+i)->key() == pos.get_key()) {
+ tte = tte + i;
+ found = true;
+ }
+ if(!found) {
+ *move = MOVE_NONE;
+ return false;
+ }
+
+ *value = tte->value();
+ *type = tte->type();
+ *d = tte->depth();
+ *move = tte->move();
+
+ return true;
+}
+
+
+/// TranspositionTable::new_search() is called at the beginning of every new
+/// search. It increments the "generation" variable, which is used to
+/// distinguish transposition table entries from previous searches from
+/// entries from the current search.
+
+void TranspositionTable::new_search() {
+ generation++;
+ writes = 0;
+}
+
+
+/// TranspositionTable::insert_pv() is called at the end of a search
+/// iteration, and inserts the PV back into the PV. This makes sure the
+/// old PV moves are searched first, even if the old TT entries have been
+/// overwritten.
+
+void TranspositionTable::insert_pv(const Position &pos, Move pv[]) {
+ UndoInfo u;
+ Position p(pos);
+
+ for(int i = 0; pv[i] != MOVE_NONE; i++) {
+ this->store(p, VALUE_NONE, Depth(0), pv[i], VALUE_TYPE_NONE);
+ p.do_move(pv[i], u);
+ }
+}
+
+
+/// TranspositionTable::full() returns the permill of all transposition table
+/// entries which have received at least one write during the current search.
+/// It is used to display the "info hashfull ..." information in UCI.
+
+int TranspositionTable::full() {
+ double N = double(size) * 4.0;
+ return int(1000 * (1 - exp(writes * log(1.0 - 1.0/N))));
+}
+
+
+/// Constructors
+
+TTEntry::TTEntry() {
+}
+
+TTEntry::TTEntry(Key k, Value v, ValueType t, Depth d, Move m,
+ int generation) {
+ key_ = k;
+ data = (m & 0x7FFFF) | (t << 20) | (generation << 23);
+ value_ = v;
+ depth_ = int16_t(d);
+}
+
+
+/// Functions for extracting data from TTEntry objects.
+
+Key TTEntry::key() const {
+ return key_;
+}
+
+Depth TTEntry::depth() const {
+ return Depth(depth_);
+}
+
+Move TTEntry::move() const {
+ return Move(data & 0x7FFFF);
+}
+
+Value TTEntry::value() const {
+ return Value(value_);
+}
+
+ValueType TTEntry::type() const {
+ return ValueType((data >> 20) & 3);
+}
+
+int TTEntry::generation() const {
+ return (data >> 23);
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(TT_H_INCLUDED)
+#define TT_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "depth.h"
+#include "position.h"
+#include "value.h"
+
+
+////
+//// Types
+////
+
+/// The TTEntry class is the class of transposition table entries.
+
+class TTEntry {
+
+public:
+ TTEntry();
+ TTEntry(Key k, Value v, ValueType t, Depth d, Move m, int generation);
+ Key key() const;
+ Depth depth() const;
+ Move move() const;
+ Value value() const;
+ ValueType type() const;
+ int generation() const;
+
+private:
+ Key key_;
+ uint32_t data;
+ int16_t value_;
+ int16_t depth_;
+};
+
+
+/// The transposition table class. This is basically just a huge array
+/// containing TTEntry objects, and a few methods for writing new entries
+/// and reading new ones.
+
+class TranspositionTable {
+
+public:
+ TranspositionTable(unsigned mbSize);
+ ~TranspositionTable();
+ void set_size(unsigned mbSize);
+ void clear();
+ void store(const Position &pos, Value v, Depth d, Move m, ValueType type);
+ bool retrieve(const Position &pos, Value *value, Depth *d, Move *move,
+ ValueType *type) const;
+ void new_search();
+ void insert_pv(const Position &pos, Move pv[]);
+ int full();
+
+private:
+ unsigned size;
+ int writes;
+ TTEntry* entries;
+ uint8_t generation;
+};
+
+
+////
+//// Constants and variables
+////
+
+// Default transposition table size, in megabytes:
+const int TTDefaultSize = 32;
+
+
+#endif // !defined(TT_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(TYPES_H_INCLUDED)
+#define TYPES_H_INCLUDED
+
+#if !defined(_MSC_VER)
+
+#include <inttypes.h>
+
+#else
+
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64;
+typedef unsigned __int64 uint64_t;
+
+typedef __int16 int16_t;
+typedef __int64 int64_t;
+
+#endif // !defined(_MSC_VER)
+
+// Hash keys:
+typedef uint64_t Key;
+
+#endif // !defined(TYPES_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <iostream>
+#include <string>
+
+#include "book.h"
+#include "evaluate.h"
+#include "misc.h"
+#include "move.h"
+#include "movegen.h"
+#include "position.h"
+#include "san.h"
+#include "search.h"
+#include "uci.h"
+#include "ucioption.h"
+
+
+////
+//// Local definitions:
+////
+
+namespace {
+
+ // UCIInputParser is a class for parsing UCI input. The class is
+ // very simple, and basically just consist of a constant input
+ // string and a current location in the string. There are methods
+ // for checking if we are at the end of the line, for getting the
+ // next token (defined as any whitespace-delimited sequence of
+ // characters), and for getting the rest of the line as a single
+ // string.
+
+ class UCIInputParser {
+
+ public:
+ UCIInputParser(const std::string &line);
+ std::string get_next_token();
+ std::string get_rest_of_line();
+ bool at_end_of_line();
+
+ private:
+ const std::string &inputLine;
+ int length, currentIndex;
+
+ void skip_whitespace();
+
+ };
+
+
+ // The root position. This is set up when the user (or in practice, the GUI)
+ // sends the "position" UCI command. The root position is sent to the think()
+ // function when the program receives the "go" command.
+ Position RootPosition;
+
+ // Local functions
+ void wait_for_command();
+ void handle_command(const std::string &command);
+ void set_option(UCIInputParser &uip);
+ void set_position(UCIInputParser &uip);
+ void go(UCIInputParser &uip);
+}
+
+
+////
+//// Functions
+////
+
+/// uci_main_loop() is the only global function in this file. It is
+/// called immediately after the program has finished initializing.
+/// The program remains in this loop until it receives the "quit" UCI
+/// command.
+
+void uci_main_loop() {
+ RootPosition.from_fen(StartPosition);
+ while(1) wait_for_command();
+}
+
+
+////
+//// Local functions
+////
+
+namespace {
+
+ ///
+ /// Implementation of the UCIInputParser class.
+ ///
+
+ // Constructor for the UCIInputParser class. The constructor takes a
+ // text string containing a single UCI command as input.
+
+ UCIInputParser::UCIInputParser(const std::string &line) : inputLine(line) {
+ this->currentIndex = 0;
+ this->length = line.length();
+ }
+
+
+ // UCIInputParser::skip_whitspace() skips any number of whitespace
+ // characters from the current location in an input string.
+
+ void UCIInputParser::skip_whitespace() {
+ while(isspace((int)(unsigned char)this->inputLine[this->currentIndex]))
+ this->currentIndex++;
+ }
+
+
+ // UCIInputParser::get_next_token() gets the next token in an UCI
+ // command. A 'token' in an UCI command is simply any
+ // whitespace-delimited sequence of characters.
+
+ std::string UCIInputParser::get_next_token() {
+ int i, j;
+
+ this->skip_whitespace();
+ for(i = j = this->currentIndex;
+ j < this->length && !isspace(this->inputLine[j]);
+ j++);
+ this->currentIndex = j;
+ this->skip_whitespace();
+
+ std::string str = this->inputLine.substr(i, j - i);
+
+ return str;
+ }
+
+
+ // UCIInputParser::get_rest_of_line() returns the rest of the input
+ // line (from the current location) as a single string.
+
+ std::string UCIInputParser::get_rest_of_line() {
+ this->skip_whitespace();
+ return this->inputLine.substr(this->currentIndex, this->length);
+ }
+
+
+ // UCIInputParser::at_end_of_line() tests whether we have reached the
+ // end of the input string, i.e. if any more input remains to be
+ // parsed.
+
+ bool UCIInputParser::at_end_of_line() {
+ return this->currentIndex == this->length;
+ }
+
+
+ ///
+ /// Other functions
+ ///
+
+
+ // wait_for_command() waits for a command from the user, and passes
+ // this command to handle_command. wait_for_command also intercepts
+ // EOF from stdin, by translating EOF to the "quit" command. This
+ // ensures that Glaurung exits gracefully if the GUI dies
+ // unexpectedly.
+
+ void wait_for_command() {
+ std::string command;
+ if(!std::getline(std::cin, command)) command = "quit";
+ handle_command(command);
+ }
+
+
+ // handle_command() takes a text string as input, uses a
+ // UCIInputParser object to parse this text string as a UCI command,
+ // and calls the appropriate functions. In addition to the UCI
+ // commands, the function also supports a few debug commands.
+
+ void handle_command(const std::string &command) {
+ UCIInputParser uip(command);
+ std::string s = uip.get_next_token();
+
+ if(s == "quit") {
+ OpeningBook.close();
+ stop_threads();
+ quit_eval();
+ exit(0);
+ }
+ else if(s == "uci") {
+ std::cout << "id name " << engine_name() << std::endl;
+ std::cout << "id author Tord Romstad" << std::endl;
+ print_uci_options();
+ std::cout << "uciok" << std::endl;
+ }
+ else if(s == "ucinewgame") {
+ TT.clear();
+ Position::init_piece_square_tables();
+ RootPosition.from_fen(StartPosition);
+ }
+ else if(s == "isready")
+ std::cout << "readyok" << std::endl;
+ else if(s == "position")
+ set_position(uip);
+ else if(s == "setoption")
+ set_option(uip);
+ else if(s == "go")
+ go(uip);
+
+ // The remaining commands are for debugging purposes only.
+ // Perhaps they should be removed later in order to reduce the
+ // size of the program binary.
+ else if(s == "d")
+ RootPosition.print();
+ else if(s == "flip") {
+ Position p(RootPosition);
+ RootPosition.flipped_copy(p);
+ }
+ else if(s == "eval") {
+ EvalInfo ei;
+ std::cout << "Incremental mg: " << RootPosition.mg_value()
+ << std::endl;
+ std::cout << "Incremental eg: " << RootPosition.eg_value()
+ << std::endl;
+ std::cout << "Full eval: "
+ << evaluate(RootPosition, ei, 0)
+ << std::endl;
+ }
+ else if(s == "key") {
+ std::cout << "key: " << RootPosition.get_key()
+ << " material key: " << RootPosition.get_material_key()
+ << " pawn key: " << RootPosition.get_pawn_key()
+ << std::endl;
+ }
+ else {
+ std::cout << "Unknown command: " << command << std::endl;
+ while(!uip.at_end_of_line()) {
+ std::cout << uip.get_next_token() << std::endl;
+ }
+ }
+ }
+
+
+ // set_position() is called when Glaurung receives the "position" UCI
+ // command. The input parameter is a UCIInputParser. It is assumed
+ // that this parser has consumed the first token of the UCI command
+ // ("position"), and is ready to read the second token ("startpos"
+ // or "fen", if the input is well-formed).
+
+ void set_position(UCIInputParser &uip) {
+ std::string token;
+
+ token = uip.get_next_token();
+ if(token == "startpos")
+ RootPosition.from_fen(StartPosition);
+ else if(token == "fen") {
+ std::string fen;
+ while(token != "moves" && !uip.at_end_of_line()) {
+ token = uip.get_next_token();
+ fen += token;
+ fen += ' ';
+ }
+ RootPosition.from_fen(fen);
+ }
+
+ if(!uip.at_end_of_line()) {
+ if(token != "moves")
+ token = uip.get_next_token();
+ if(token == "moves") {
+ Move move;
+ UndoInfo u;
+ while(!uip.at_end_of_line()) {
+ token = uip.get_next_token();
+ move = move_from_string(RootPosition, token);
+ RootPosition.do_move(move, u);
+ if(RootPosition.rule_50_counter() == 0)
+ RootPosition.reset_game_ply();
+ }
+ }
+ }
+ }
+
+
+ // set_option() is called when Glaurung receives the "setoption" UCI
+ // command. The input parameter is a UCIInputParser. It is assumed
+ // that this parser has consumed the first token of the UCI command
+ // ("setoption"), and is ready to read the second token ("name", if
+ // the input is well-formed).
+
+ void set_option(UCIInputParser &uip) {
+ std::string token;
+ if(!uip.at_end_of_line()) {
+ token = uip.get_next_token();
+ if(token == "name" && !uip.at_end_of_line()) {
+ std::string name = uip.get_next_token();
+ std::string nextToken;
+ while(!uip.at_end_of_line()
+ && (nextToken = uip.get_next_token()) != "value")
+ name += (" " + nextToken);
+ if(nextToken == "value")
+ set_option_value(name, uip.get_rest_of_line());
+ else
+ push_button(name);
+ }
+ }
+ }
+
+
+ // go() is called when Glaurung receives the "go" UCI command. The
+ // input parameter is a UCIInputParser. It is assumed that this
+ // parser has consumed the first token of the UCI command ("go"),
+ // and is ready to read the second token. The function sets the
+ // thinking time and other parameters from the input string, and
+ // calls think() (defined in search.cpp) with the appropriate
+ // parameters.
+
+ void go(UCIInputParser &uip) {
+ std::string token;
+ int time[2] = {0, 0}, inc[2] = {0, 0}, movesToGo = 0, depth = 0, nodes = 0;
+ int moveTime = 0;
+ bool infinite = false, ponder = false;
+ Move searchMoves[500];
+
+ searchMoves[0] = MOVE_NONE;
+
+ while(!uip.at_end_of_line()) {
+ token = uip.get_next_token();
+
+ if(token == "infinite")
+ infinite = true;
+ else if(token == "ponder")
+ ponder = true;
+ else if(token == "wtime") {
+ if(!uip.at_end_of_line())
+ time[0] = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "btime") {
+ if(!uip.at_end_of_line())
+ time[1] = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "winc") {
+ if(!uip.at_end_of_line())
+ inc[0] = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "binc") {
+ if(!uip.at_end_of_line())
+ inc[1] = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "movestogo") {
+ if(!uip.at_end_of_line())
+ movesToGo = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "depth") {
+ if(!uip.at_end_of_line())
+ depth = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "nodes") {
+ if(!uip.at_end_of_line())
+ nodes = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "movetime") {
+ if(!uip.at_end_of_line())
+ moveTime = atoi(uip.get_next_token().c_str());
+ }
+ else if(token == "searchmoves" && !uip.at_end_of_line()) {
+ int numOfMoves = 0;
+ while(!uip.at_end_of_line()) {
+ token = uip.get_next_token();
+ searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
+ }
+ searchMoves[numOfMoves] = MOVE_NONE;
+ }
+ }
+
+ if(moveTime)
+ infinite = true; // HACK
+
+ think(RootPosition, infinite, ponder, time[RootPosition.side_to_move()],
+ inc[RootPosition.side_to_move()], movesToGo, depth, nodes, moveTime,
+ searchMoves);
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(UCI_H_INCLUDED)
+#define UCI_H_INCLUDED
+
+////
+//// Prototypes
+////
+
+extern void uci_main_loop();
+
+
+#endif // !defined(UCI_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <cassert>
+#include <cstdarg>
+#include <cstdio>
+
+#include "misc.h"
+#include "thread.h"
+#include "ucioption.h"
+
+
+////
+//// Variables
+////
+
+bool Chess960 = false;
+
+
+////
+//// Local definitions
+////
+
+namespace {
+
+ ///
+ /// Types
+ ///
+
+ enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON, OPTION_TYPE_NONE};
+
+ struct Option {
+ char name[50], defaultValue[300], currentValue[300];
+ OptionType type;
+ int minValue, maxValue;
+ char comboValues[8][64];
+ };
+
+
+ ///
+ /// Variables
+ ///
+
+ Option Options[] = {
+ { "Use Search Log", "false", "false", CHECK, 0, 0, {""} },
+ { "Search Log Filename", "SearchLog.txt", "SearchLog.txt", STRING, 0, 0, {""} },
+ { "Book File", "book.bin", "book.bin", STRING, 0, 0, {""} },
+ { "Mobility (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
+ { "Mobility (Endgame)", "100", "100", SPIN, 0, 200, {""} },
+ { "Pawn Structure (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
+ { "Pawn Structure (Endgame)", "100", "100", SPIN, 0, 200, {""} },
+ { "Passed Pawns (Middle Game)", "100", "100", SPIN, 0, 200, {""} },
+ { "Passed Pawns (Endgame)", "100", "100", SPIN, 0, 200, {""} },
+ { "Aggressiveness", "100", "100", SPIN, 0, 200, {""} },
+ { "Cowardice", "100", "100", SPIN, 0, 200, {""} },
+ { "King Safety Curve", "Quadratic", "Quadratic", COMBO, 0, 0,
+ { "Quadratic", "Linear" /*, "From File"*/ } },
+ { "King Safety Coefficient", "40", "40", SPIN, 1, 100 , {""} },
+ { "King Safety X Intercept", "0", "0", SPIN, 0, 20, {""} },
+ { "King Safety Max Slope", "30", "30", SPIN, 10, 100, {""} },
+ { "King Safety Max Value", "500", "500", SPIN, 100, 1000, {""} },
+ { "Queen Contact Check Bonus", "4", "4", SPIN, 0, 8, {""} },
+ { "Rook Contact Check Bonus", "2", "2", SPIN, 0, 4, {""} },
+ { "Queen Check Bonus", "2", "2", SPIN, 0, 4, {""} },
+ { "Rook Check Bonus", "1", "1", SPIN, 0, 4, {""} },
+ { "Bishop Check Bonus", "1", "1", SPIN, 0, 4, {""} },
+ { "Knight Check Bonus", "1", "1", SPIN, 0, 4, {""} },
+ { "Discovered Check Bonus", "3", "3", SPIN, 0, 8, {""} },
+ { "Mate Threat Bonus", "3", "3", SPIN, 0, 8, {""} },
+ { "Check Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
+ { "Check Extension (non-PV nodes)", "1", "1", SPIN, 0, 2, {""} },
+ { "Single Reply Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
+ { "Single Reply Extension (non-PV nodes)", "2", "2", SPIN, 0, 2, {""} },
+ { "Mate Threat Extension (PV nodes)", "0", "0", SPIN, 0, 2, {""} },
+ { "Mate Threat Extension (non-PV nodes)", "0", "0", SPIN, 0, 2, {""} },
+ { "Pawn Push to 7th Extension (PV nodes)", "1", "1", SPIN, 0, 2, {""} },
+ { "Pawn Push to 7th Extension (non-PV nodes)", "1", "1", SPIN, 0, 2, {""} },
+ { "Passed Pawn Extension (PV nodes)", "1", "1", SPIN, 0, 2, {""} },
+ { "Passed Pawn Extension (non-PV nodes)", "0", "0", SPIN, 0, 2, {""} },
+ { "Pawn Endgame Extension (PV nodes)", "2", "2", SPIN, 0, 2, {""} },
+ { "Pawn Endgame Extension (non-PV nodes)", "2", "2", SPIN, 0, 2, {""} },
+ { "Full Depth Moves (PV nodes)", "14", "14", SPIN, 1, 100, {""} },
+ { "Full Depth Moves (non-PV nodes)", "3", "3", SPIN, 1, 100, {""} },
+ { "Threat Depth", "5", "5", SPIN, 0, 100, {""} },
+ { "Selective Plies", "7", "7", SPIN, 0, 10, {""} },
+ { "Futility Pruning (Main Search)", "true", "true", CHECK, 0, 0, {""} },
+ { "Futility Pruning (Quiescence Search)", "true", "true", CHECK, 0, 0, {""} },
+ { "Futility Margin 0", "50", "50", SPIN, 0, 1000, {""} },
+ { "Futility Margin 1", "100", "100", SPIN, 0, 1000, {""} },
+ { "Futility Margin 2", "300", "300", SPIN, 0, 1000, {""} },
+ { "Maximum Razoring Depth", "3", "3", SPIN, 0, 4, {""} },
+ { "Razoring Margin", "300", "300", SPIN, 150, 600, {""} },
+ { "Randomness", "0", "0", SPIN, 0, 10, {""} },
+ { "Minimum Split Depth", "4", "4", SPIN, 4, 7, {""} },
+ { "Maximum Number of Threads per Split Point", "5", "5", SPIN, 4, 8, {""} },
+ { "Threads", "1", "1", SPIN, 1, 8, {""} },
+ { "Hash", "32", "32", SPIN, 4, 4096, {""} },
+ { "Clear Hash", "false", "false", BUTTON, 0, 0, {""} },
+ { "Ponder", "true", "true", CHECK, 0, 0, {""} },
+ { "OwnBook", "true", "true", CHECK, 0, 0, {""} },
+ { "MultiPV", "1", "1", SPIN, 1, 500, {""} },
+ { "UCI_ShowCurrLine", "false", "false", CHECK, 0, 0, {""} },
+ { "UCI_Chess960", "false", "false", CHECK, 0, 0, {""} },
+ { "", "", "", OPTION_TYPE_NONE, 0, 0, {""}}
+ };
+
+
+ ///
+ /// Functions
+ ///
+
+ Option *option_with_name(const char *optionName);
+
+}
+
+
+////
+//// Functions
+////
+
+/// init_uci_options() initializes the UCI options. Currently, the only
+/// thing this function does is to initialize the default value of the
+/// "Threads" parameter to the number of available CPU cores.
+
+void init_uci_options() {
+ Option *o;
+
+ o = option_with_name("Threads");
+ assert(o != NULL);
+
+ // Limit the default value of "Threads" to 7 even if we have 8 CPU cores.
+ // According to Ken Dail's tests, Glaurung plays much better with 7 than
+ // with 8 threads. This is weird, but it is probably difficult to find out
+ // why before I have a 8-core computer to experiment with myself.
+ sprintf(o->defaultValue, "%d", Min(cpu_count(), 7));
+ sprintf(o->currentValue, "%d", Min(cpu_count(), 7));
+
+ // Increase the minimum split depth when the number of CPUs is big.
+ // It would probably be better to let this depend on the number of threads
+ // instead.
+ o = option_with_name("Minimum Split Depth");
+ assert(o != NULL);
+ if(cpu_count() > 4) {
+ sprintf(o->defaultValue, "%d", 6);
+ sprintf(o->defaultValue, "%d", 6);
+ }
+}
+
+
+/// print_uci_options() prints all the UCI options to the standard output,
+/// in the format defined by the UCI protocol.
+
+void print_uci_options() {
+ static const char optionTypeName[][16] = {
+ "spin", "combo", "check", "string", "button"
+ };
+ for(Option *o = Options; o->type != OPTION_TYPE_NONE; o++) {
+ printf("option name %s type %s", o->name, optionTypeName[o->type]);
+ if(o->type != BUTTON) {
+ printf(" default %s", o->defaultValue);
+ if(o->type == SPIN)
+ printf(" min %d max %d", o->minValue, o->maxValue);
+ else if(o->type == COMBO)
+ for(int i = 0; strlen(o->comboValues[i]) > 0; i++)
+ printf(" var %s", o->comboValues[i]);
+ }
+ printf("\n");
+ }
+}
+
+
+/// get_option_value_bool() returns the current value of a UCI parameter of
+/// type "check".
+
+bool get_option_value_bool(const std::string &optionName) {
+ Option *o = option_with_name(optionName.c_str());
+ return o != NULL && strcmp(o->currentValue, "true") == 0;
+}
+
+
+/// get_option_value_int() returns the value of a UCI parameter as an integer.
+/// Normally, this function will be used for a parameter of type "spin", but
+/// it could also be used with a "combo" parameter, where all the available
+/// values are integers.
+
+int get_option_value_int(const std::string &optionName) {
+ Option *o = option_with_name(optionName.c_str());
+ return atoi(o->currentValue);
+}
+
+
+/// get_option_value_string() returns the current value of a UCI parameter as
+/// a string. It is used with parameters of type "combo" and "string".
+
+const std::string get_option_value_string(const std::string &optionName) {
+ Option *o = option_with_name(optionName.c_str());
+ return o->currentValue;
+}
+
+
+/// button_was_pressed() tests whether a UCI parameter of type "button" has
+/// been selected since the last time the function was called.
+
+bool button_was_pressed(const std::string &buttonName) {
+ if(get_option_value_bool(buttonName)) {
+ set_option_value(buttonName, "false");
+ return true;
+ }
+ else
+ return false;
+}
+
+
+/// set_option_value() inserts a new value for a UCI parameter. Note that
+/// the function does not check that the new value is legal for the given
+/// parameter: This is assumed to be the responsibility of the GUI.
+
+void set_option_value(const std::string &optionName,
+ const std::string &newValue) {
+ Option *o = option_with_name(optionName.c_str());
+
+ if(o != NULL)
+ strcpy(o->currentValue, newValue.c_str());
+ else
+ std::cout << "No such option: " << optionName << std::endl;
+}
+
+
+/// push_button() is used to tell the engine that a UCI parameter of type
+/// "button" has been selected:
+
+void push_button(const std::string &buttonName) {
+ set_option_value(buttonName, "true");
+}
+
+
+namespace {
+
+ // option_with_name() tries to find a UCI option with a given
+ // name. It returns a pointer to the UCI option or the null pointer,
+ // depending on whether an option with the given name exists.
+
+ Option *option_with_name(const char *optionName) {
+ for(Option *o = Options; o->type != OPTION_TYPE_NONE; o++)
+ if(strcmp(o->name, optionName) == 0)
+ return o;
+ return NULL;
+ }
+
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(UCIOPTION_H_INCLUDED)
+#define UCIOPTION_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include <string>
+
+
+////
+//// Variables
+////
+
+extern bool Chess960;
+
+
+////
+//// Prototypes
+////
+
+extern void init_uci_options();
+extern void print_uci_options();
+extern bool get_option_value_bool(const std::string &optionName);
+extern int get_option_value_int(const std::string &optionName);
+extern const std::string get_option_value_string(const std::string &optionName);
+extern bool button_was_pressed(const std::string &buttonName);
+extern void set_option_value(const std::string &optionName,
+ const std::string &newValue);
+extern void push_button(const std::string &buttonName);
+
+
+#endif // !defined(UCIOPTION_H_INCLUDED)
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+////
+//// Includes
+////
+
+#include <sstream>
+#include <string>
+
+#include "value.h"
+
+
+////
+//// Functions
+////
+
+/// value_to_tt() adjusts a mate score from "plies to mate from the root" to
+/// "plies to mate from the current ply". Non-mate scores are unchanged.
+/// The function is called before storing a value to the transposition table.
+
+Value value_to_tt(Value v, int ply) {
+ if(v >= value_mate_in(100))
+ return v + ply;
+ else if(v <= value_mated_in(100))
+ return v - ply;
+ else
+ return v;
+}
+
+
+/// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
+/// from the transposition table to a mate score corrected for the current
+/// ply depth.
+
+Value value_from_tt(Value v, int ply) {
+ if(v >= value_mate_in(100))
+ return v - ply;
+ else if(v <= value_mated_in(100))
+ return v + ply;
+ else
+ return v;
+}
+
+
+/// value_to_centipawns() converts a value from Glaurung's somewhat unusual
+/// scale of pawn = 256 to the more conventional pawn = 100.
+
+int value_to_centipawns(Value v) {
+ return (int(v) * 100) / int(PawnValueMidgame);
+}
+
+
+/// value_from_centipawns() converts a centipawn value to Glaurung's internal
+/// evaluation scale. It's used when reading the values of UCI options
+/// containing material values (e.g. futility pruning margins).
+
+Value value_from_centipawns(int cp) {
+ return Value((cp * 256) / 100);
+}
+
+
+/// value_to_string() converts a value to a string suitable for use with the
+/// UCI protocol.
+
+const std::string value_to_string(Value v) {
+ std::stringstream s;
+
+ if(abs(v) < VALUE_MATE - 200)
+ s << "cp " << value_to_centipawns(v);
+ else {
+ s << "mate ";
+ if(v > 0)
+ s << (VALUE_MATE - v + 1) / 2;
+ else
+ s << -(VALUE_MATE + v) / 2;
+ }
+ return s.str();
+}
--- /dev/null
+/*
+ Glaurung, a UCI chess playing engine.
+ Copyright (C) 2004-2008 Tord Romstad
+
+ Glaurung is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Glaurung is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#if !defined(VALUE_H_INCLUDED)
+#define VALUE_H_INCLUDED
+
+////
+//// Includes
+////
+
+#include "piece.h"
+
+
+////
+//// Types
+////
+
+enum ValueType {
+ VALUE_TYPE_NONE = 0,
+ VALUE_TYPE_UPPER = 1, // Upper bound
+ VALUE_TYPE_LOWER = 2, // Lower bound
+ VALUE_TYPE_EXACT = 3 // Exact score
+};
+
+
+enum Value {
+ VALUE_DRAW = 0,
+ VALUE_KNOWN_WIN = 15000,
+ VALUE_MATE = 30000,
+ VALUE_INFINITE = 30001,
+ VALUE_NONE = 30002
+};
+
+
+////
+//// Constants and variables
+////
+
+/// Piece values, middle game and endgame
+
+/// Important: If the material values are changed, one must also
+/// adjust the piece square tables, and the method game_phase() in the
+/// Position class!
+
+const Value PawnValueMidgame = Value(0xCC);
+const Value PawnValueEndgame = Value(0x100);
+const Value KnightValueMidgame = Value(0x340);
+const Value KnightValueEndgame = Value(0x340);
+const Value BishopValueMidgame = Value(0x340);
+const Value BishopValueEndgame = Value(0x340);
+const Value RookValueMidgame = Value(0x505);
+const Value RookValueEndgame = Value(0x505);
+const Value QueenValueMidgame = Value(0xA00);
+const Value QueenValueEndgame = Value(0xA00);
+
+const Value PieceValueMidgame[17] = {
+ Value(0),
+ PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
+ RookValueMidgame, QueenValueMidgame,
+ Value(0), Value(0), Value(0),
+ PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
+ RookValueMidgame, QueenValueMidgame,
+ Value(0), Value(0), Value(0)
+};
+
+const Value PieceValueEndgame[17] = {
+ Value(0),
+ PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
+ RookValueEndgame, QueenValueEndgame,
+ Value(0), Value(0), Value(0),
+ PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
+ RookValueEndgame, QueenValueEndgame,
+ Value(0), Value(0), Value(0)
+};
+
+/// Bonus for having the side to move
+
+const Value TempoValueMidgame = Value(50);
+const Value TempoValueEndgame = Value(20);
+
+
+////
+//// Inline functions
+////
+
+inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
+inline Value operator+ (Value v1, Value v2) { return Value(int(v1) + int(v2)); }
+inline void operator+= (Value &v1, Value v2) {
+ v1 = Value(int(v1) + int(v2));
+}
+inline Value operator- (Value v, int i) { return Value(int(v) - i); }
+inline Value operator- (Value v) { return Value(-int(v)); }
+inline Value operator- (Value v1, Value v2) { return Value(int(v1) - int(v2)); }
+inline void operator-= (Value &v1, Value v2) {
+ v1 = Value(int(v1) - int(v2));
+}
+inline Value operator* (Value v, int i) { return Value(int(v) * i); }
+inline void operator*= (Value &v, int i) { v = Value(int(v) * i); }
+inline Value operator* (int i, Value v) { return Value(int(v) * i); }
+inline Value operator/ (Value v, int i) { return Value(int(v) / i); }
+inline void operator/= (Value &v, int i) { v = Value(int(v) / i); }
+
+
+inline Value value_mate_in(int ply) {
+ return Value(VALUE_MATE - Value(ply));
+}
+
+inline Value value_mated_in(int ply) {
+ return Value(-VALUE_MATE + Value(ply));
+}
+
+inline bool is_upper_bound(ValueType vt) {
+ return (int(vt) & int(VALUE_TYPE_UPPER)) != 0;
+}
+
+inline bool is_lower_bound(ValueType vt) {
+ return (int(vt) & int(VALUE_TYPE_LOWER)) != 0;
+}
+
+inline Value piece_value_midgame(PieceType pt) {
+ return PieceValueMidgame[pt];
+}
+
+inline Value piece_value_endgame(PieceType pt) {
+ return PieceValueEndgame[pt];
+}
+
+inline Value piece_value_midgame(Piece p) {
+ return PieceValueMidgame[p];
+}
+
+inline Value piece_value_endgame(Piece p) {
+ return PieceValueEndgame[p];
+}
+
+
+////
+//// Prototypes
+////
+
+extern Value value_to_tt(Value v, int ply);
+extern Value value_from_tt(Value v, int ply);
+extern int value_to_centipawns(Value v);
+extern Value value_from_centipawns(int cp);
+extern const std::string value_to_string(Value v);
+
+
+#endif // !defined(VALUE_H_INCLUDED)