]> git.sesse.net Git - stockfish/commitdiff
Initial import of Glaurung 2.1
authorMarco Costalba <mcostalba@gmail.com>
Mon, 1 Sep 2008 05:59:13 +0000 (07:59 +0200)
committerMarco Costalba <mcostalba@gmail.com>
Mon, 1 Sep 2008 05:59:13 +0000 (07:59 +0200)
64 files changed:
Copying.txt [new file with mode: 0644]
Readme.txt [new file with mode: 0644]
polyglot.ini [new file with mode: 0644]
src/COPYING [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/benchmark.cpp [new file with mode: 0644]
src/benchmark.h [new file with mode: 0644]
src/bitbase.cpp [new file with mode: 0644]
src/bitbase.h [new file with mode: 0644]
src/bitboard.cpp [new file with mode: 0644]
src/bitboard.h [new file with mode: 0644]
src/book.cpp [new file with mode: 0644]
src/book.h [new file with mode: 0644]
src/color.cpp [new file with mode: 0644]
src/color.h [new file with mode: 0644]
src/depth.h [new file with mode: 0644]
src/direction.cpp [new file with mode: 0644]
src/direction.h [new file with mode: 0644]
src/endgame.cpp [new file with mode: 0644]
src/endgame.h [new file with mode: 0644]
src/evaluate.cpp [new file with mode: 0644]
src/evaluate.h [new file with mode: 0644]
src/history.cpp [new file with mode: 0644]
src/history.h [new file with mode: 0644]
src/lock.h [new file with mode: 0644]
src/main.cpp [new file with mode: 0644]
src/material.cpp [new file with mode: 0644]
src/material.h [new file with mode: 0644]
src/mersenne.cpp [new file with mode: 0644]
src/mersenne.h [new file with mode: 0644]
src/misc.cpp [new file with mode: 0644]
src/misc.h [new file with mode: 0644]
src/move.cpp [new file with mode: 0644]
src/move.h [new file with mode: 0644]
src/movegen.cpp [new file with mode: 0644]
src/movegen.h [new file with mode: 0644]
src/movepick.cpp [new file with mode: 0644]
src/movepick.h [new file with mode: 0644]
src/pawns.cpp [new file with mode: 0644]
src/pawns.h [new file with mode: 0644]
src/phase.h [new file with mode: 0644]
src/piece.cpp [new file with mode: 0644]
src/piece.h [new file with mode: 0644]
src/position.cpp [new file with mode: 0644]
src/position.h [new file with mode: 0644]
src/psqtab.h [new file with mode: 0644]
src/san.cpp [new file with mode: 0644]
src/san.h [new file with mode: 0644]
src/scale.h [new file with mode: 0644]
src/search.cpp [new file with mode: 0644]
src/search.h [new file with mode: 0644]
src/square.cpp [new file with mode: 0644]
src/square.h [new file with mode: 0644]
src/thread.h [new file with mode: 0644]
src/timeoday.cpp [new file with mode: 0644]
src/tt.cpp [new file with mode: 0644]
src/tt.h [new file with mode: 0644]
src/types.h [new file with mode: 0644]
src/uci.cpp [new file with mode: 0644]
src/uci.h [new file with mode: 0644]
src/ucioption.cpp [new file with mode: 0644]
src/ucioption.h [new file with mode: 0644]
src/value.cpp [new file with mode: 0644]
src/value.h [new file with mode: 0644]

diff --git a/Copying.txt b/Copying.txt
new file mode 100644 (file)
index 0000000..818433e
--- /dev/null
@@ -0,0 +1,674 @@
+                    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
diff --git a/Readme.txt b/Readme.txt
new file mode 100644 (file)
index 0000000..defd2fc
--- /dev/null
@@ -0,0 +1,328 @@
+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
diff --git a/polyglot.ini b/polyglot.ini
new file mode 100644 (file)
index 0000000..7d5c8d9
--- /dev/null
@@ -0,0 +1,70 @@
+\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
diff --git a/src/COPYING b/src/COPYING
new file mode 100644 (file)
index 0000000..818433e
--- /dev/null
@@ -0,0 +1,674 @@
+                    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
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..2e37ccc
--- /dev/null
@@ -0,0 +1,145 @@
+# 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
diff --git a/src/benchmark.cpp b/src/benchmark.cpp
new file mode 100644 (file)
index 0000000..4bd1ac8
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  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);
+  }
+    
+}
diff --git a/src/benchmark.h b/src/benchmark.h
new file mode 100644 (file)
index 0000000..9ca68f8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+  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)
diff --git a/src/bitbase.cpp b/src/bitbase.cpp
new file mode 100644 (file)
index 0000000..80db0a4
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+  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;
+  }
+      
+}  
diff --git a/src/bitbase.h b/src/bitbase.h
new file mode 100644 (file)
index 0000000..2de5413
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+  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)
diff --git a/src/bitboard.cpp b/src/bitboard.cpp
new file mode 100644 (file)
index 0000000..0bbd155
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+  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)
+    
+}
diff --git a/src/bitboard.h b/src/bitboard.h
new file mode 100644 (file)
index 0000000..d9a9bf5
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+  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)
diff --git a/src/book.cpp b/src/book.cpp
new file mode 100644 (file)
index 0000000..bd2f481
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+  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;
+  }
+
+}
diff --git a/src/book.h b/src/book.h
new file mode 100644 (file)
index 0000000..ee3f5c5
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+  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)
diff --git a/src/color.cpp b/src/color.cpp
new file mode 100644 (file)
index 0000000..50965a0
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+  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;
+}
diff --git a/src/color.h b/src/color.h
new file mode 100644 (file)
index 0000000..37ba795
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  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)
diff --git a/src/depth.h b/src/depth.h
new file mode 100644 (file)
index 0000000..493ecc4
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+  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)
diff --git a/src/direction.cpp b/src/direction.cpp
new file mode 100644 (file)
index 0000000..45fda4f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+  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;
+        }
+      }
+    }
+}
diff --git a/src/direction.h b/src/direction.h
new file mode 100644 (file)
index 0000000..63c7c73
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+  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)
diff --git a/src/endgame.cpp b/src/endgame.cpp
new file mode 100644 (file)
index 0000000..c320ba3
--- /dev/null
@@ -0,0 +1,867 @@
+/*
+  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));
+  }
+  
+}
diff --git a/src/endgame.h b/src/endgame.h
new file mode 100644 (file)
index 0000000..ae3526f
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+  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)
diff --git a/src/evaluate.cpp b/src/evaluate.cpp
new file mode 100644 (file)
index 0000000..48cbfc1
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+  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);
+  }
+  
+}
diff --git a/src/evaluate.h b/src/evaluate.h
new file mode 100644 (file)
index 0000000..8662f4b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+  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)
diff --git a/src/history.cpp b/src/history.cpp
new file mode 100644 (file)
index 0000000..2963148
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+  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)]);
+}
diff --git a/src/history.h b/src/history.h
new file mode 100644 (file)
index 0000000..ced416a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+  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)
diff --git a/src/lock.h b/src/lock.h
new file mode 100644 (file)
index 0000000..2efb207
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+  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)
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..49c66c0
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+  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;
+}
diff --git a/src/material.cpp b/src/material.cpp
new file mode 100644 (file)
index 0000000..c7bb810
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+  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;
+}
diff --git a/src/material.h b/src/material.h
new file mode 100644 (file)
index 0000000..7d5c3ce
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+  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)
diff --git a/src/mersenne.cpp b/src/mersenne.cpp
new file mode 100644 (file)
index 0000000..2213e4a
--- /dev/null
@@ -0,0 +1,148 @@
+/* 
+   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);
+}
diff --git a/src/mersenne.h b/src/mersenne.h
new file mode 100644 (file)
index 0000000..3c1c226
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  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)
diff --git a/src/misc.cpp b/src/misc.cpp
new file mode 100644 (file)
index 0000000..a2dc53a
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+  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
diff --git a/src/misc.h b/src/misc.h
new file mode 100644 (file)
index 0000000..d629045
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  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)
diff --git a/src/move.cpp b/src/move.cpp
new file mode 100644 (file)
index 0000000..2de102d
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+  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));
+}
diff --git a/src/move.h b/src/move.h
new file mode 100644 (file)
index 0000000..65fcc25
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+  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)
diff --git a/src/movegen.cpp b/src/movegen.cpp
new file mode 100644 (file)
index 0000000..ecdcd8f
--- /dev/null
@@ -0,0 +1,1238 @@
+/*
+  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;
+  }
+    
+}
diff --git a/src/movegen.h b/src/movegen.h
new file mode 100644 (file)
index 0000000..93c1c4a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  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)
diff --git a/src/movepick.cpp b/src/movepick.cpp
new file mode 100644 (file)
index 0000000..6df070b
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+  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;
+}
diff --git a/src/movepick.h b/src/movepick.h
new file mode 100644 (file)
index 0000000..ca052c7
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+  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)
diff --git a/src/pawns.cpp b/src/pawns.cpp
new file mode 100644 (file)
index 0000000..19a37ee
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+  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;
+}
diff --git a/src/pawns.h b/src/pawns.h
new file mode 100644 (file)
index 0000000..c4a616c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+  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)
diff --git a/src/phase.h b/src/phase.h
new file mode 100644 (file)
index 0000000..a9c0958
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+  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)
diff --git a/src/piece.cpp b/src/piece.cpp
new file mode 100644 (file)
index 0000000..737a091
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+  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;
+}
diff --git a/src/piece.h b/src/piece.h
new file mode 100644 (file)
index 0000000..2104baa
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+  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)
diff --git a/src/position.cpp b/src/position.cpp
new file mode 100644 (file)
index 0000000..72ab5f7
--- /dev/null
@@ -0,0 +1,2238 @@
+/*
+  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;
+}
diff --git a/src/position.h b/src/position.h
new file mode 100644 (file)
index 0000000..ff907d8
--- /dev/null
@@ -0,0 +1,746 @@
+/*
+  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)
diff --git a/src/psqtab.h b/src/psqtab.h
new file mode 100644 (file)
index 0000000..0607c2d
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+  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)
diff --git a/src/san.cpp b/src/san.cpp
new file mode 100644 (file)
index 0000000..74fde68
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+  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();
+  }
+  
+}
diff --git a/src/san.h b/src/san.h
new file mode 100644 (file)
index 0000000..087e52b
--- /dev/null
+++ b/src/san.h
@@ -0,0 +1,46 @@
+/*
+  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)
diff --git a/src/scale.h b/src/scale.h
new file mode 100644 (file)
index 0000000..5e9af02
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+  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)
diff --git a/src/search.cpp b/src/search.cpp
new file mode 100644 (file)
index 0000000..2069315
--- /dev/null
@@ -0,0 +1,2436 @@
+/*
+  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
+
+}
diff --git a/src/search.h b/src/search.h
new file mode 100644 (file)
index 0000000..831248b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+  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)
diff --git a/src/square.cpp b/src/square.cpp
new file mode 100644 (file)
index 0000000..6ac9b2f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+  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));
+}
diff --git a/src/square.h b/src/square.h
new file mode 100644 (file)
index 0000000..3afa127
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+  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)
diff --git a/src/thread.h b/src/thread.h
new file mode 100644 (file)
index 0000000..1984448
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+  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)
diff --git a/src/timeoday.cpp b/src/timeoday.cpp
new file mode 100644 (file)
index 0000000..1078af3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+   (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;
+}
diff --git a/src/tt.cpp b/src/tt.cpp
new file mode 100644 (file)
index 0000000..01d4365
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+  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);
+}
diff --git a/src/tt.h b/src/tt.h
new file mode 100644 (file)
index 0000000..f39d37d
--- /dev/null
+++ b/src/tt.h
@@ -0,0 +1,92 @@
+/*
+  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)
diff --git a/src/types.h b/src/types.h
new file mode 100644 (file)
index 0000000..e540afc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  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)
diff --git a/src/uci.cpp b/src/uci.cpp
new file mode 100644 (file)
index 0000000..66a2ade
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+  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);
+  }
+  
+}
diff --git a/src/uci.h b/src/uci.h
new file mode 100644 (file)
index 0000000..23ece63
--- /dev/null
+++ b/src/uci.h
@@ -0,0 +1,30 @@
+/*
+  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)
diff --git a/src/ucioption.cpp b/src/ucioption.cpp
new file mode 100644 (file)
index 0000000..764324d
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+  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;
+  }
+
+}
diff --git a/src/ucioption.h b/src/ucioption.h
new file mode 100644 (file)
index 0000000..f4209de
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  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)
diff --git a/src/value.cpp b/src/value.cpp
new file mode 100644 (file)
index 0000000..a93236c
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+  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();
+}
diff --git a/src/value.h b/src/value.h
new file mode 100644 (file)
index 0000000..f60e286
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+  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)