SjaakII/sjaakii.pod000644 000765 000024 00000004715 12470157255 015225 0ustar00eglebbkstaff000000 000000 =head1 NAME sjaakii - xboard-compatible chess and chess-variant engine 'SjaakII' =head1 SYNOPSIS B [-log|-newlog [filename]] [-variant name] [-no_user_variants] [-xboard|-uci|-uci|-ucci] [variant file] =head1 DESCRIPTION B is a program that plays chess and chess variants. It uses the xboard/winboard chess-engine protocol to communicate. Apart from normal chess, SjaakII can play Seirawan Chess, Mongolian Shatar, Makruk (including variants ASEAN and Ai-Wok), Shatranj, Sittuyin (Burmese Chess), Crazy House Chess, Chessgi, Spartan Chess, Pocket Knight Chess, King-of-th-Hill, Knightmate, Amazon Chess, Chancellor Chess, Berolina Chess, Los Alamos Chess, Micro Chess, Capablanca Chess, Gothic Chess, Embassy Chess, Courier Chess, Grand Chess, Opulent Chess, Omega Chess, Shogi (including variants Mini Shogi, Sho Shogi and Tori Shogi) and Xiang-Qi. Other variants can be added through a configuration file. See xboard(6) for instructions about how to use B through xboard. To start up quickly, you just need the command: B. Some of the variants SjaakII plays are only partially supported by XBoard, and can only be played whith the legality-testing function of the latter switched off. =head1 OPTIONS =over 8 =item B<-log [filename]> Append all communication by SjaakII and XBoard to a logfile. If no filename is specified, the log is written to sjaakii.log. =item B<-newlog [filename]> As B<-log>, but the file is overwritten rather than added to. =item B<-variant name> Normally, SjaakII starts with variant "normal" (regular chess), but it can be made to start with any other named variant with this option. =item B<-no_user_variants> Do not read the default variant configuration file. You can still specify a file in the engine options. =item B<-xboard> Start in xboard mode rather than the default mode. =item B<-uci> Start in UCI mode rather than the default mode. =item B<-usi> Start in USI mode rather than the default mode. =item B<-ucci> Start in UCCI mode rather than the default mode. =item B Read variant descriptions from the named file. =back =head1 AVAILABILITY From http://www.eglebbk.dds.nl/program/chess-index.html =head1 SEE ALSO xboard(6) =head1 STANDARDS WinBoard, B(6) interface ("Chess Engine Communication Protocol") =head1 AUTHOR Evert Glebbeek . This manual page was based on the man page for Fairy-Max by H.G. Muller and was generated with pod2man(1). SjaakII/COPYING000644 000765 000024 00000104513 12433144250 014124 0ustar00eglebbkstaff000000 000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . SjaakII/CHANGES000644 000765 000024 00000174104 13014616475 014100 0ustar00eglebbkstaff000000 000000 Between 1.3.1 and 1.4.0 * include/game.h, src/xboard.cc: Fix setting of promotion choice/case ordering. * src/xboard.cc: Implement interrupt on|off commands, to disable breaking off the search on keyboard input. This to help with batch processing of input. * include/board.h: Add a function to print the actions that would be performed when making a move. * include/eval_param.h, include/game.h, include/search.h: Fix two search issues: when injecting the PV into the transposition table, the score and moves could be mangled, corrupting the board struct. The other issue is that for large material differences, the initial aspiration window could break out without finding a PV (it would print a fail-high, but then fail to do the re-search to obtain a new score). * include/eval_param.h, include/game.h: Revert accidental commit. * src/xboard.cc: Suppress UCI_Chess960 in USI/UCCI mode. * src/xboard.cc: Gracefully handle a few USI peculiarities from Shogidogoro. * src/rules/move.cc: Fix in-place promotions being printed as "@@@@" (null-move). * include/eval_param.h, include/game.h: Add an absolute floor to the game phase scale. This helps in variants that essentially begin in the end game. * include/search.h: Replace check test with call to already defined function that does the exact same thing. * include/betza_string.h, include/game.h, include/moveflag.h, include/movegen.h, include/pieces.h: Add "rider" as a move type, allowing for the implementation of Nightriders. The way this is handled is probably not ideal, since move generation first builds a bitboard step-by-step, before serialising it. It also does not handle multiple atoms in the move description yet. * variants.txt: Add Nightrider Chess, which replaces Knights by Nightriders. * src/rules/san.cc: Fix SAN corner-case where neither rank nor file disambiguation is sufficient. * variants.txt: Remove comment. * include/betza_string.h, include/movegen.h: Allow for multiple atoms for riders. Replace W and F riders by sliders. * include/betza_string.h, include/board.h, include/game.h, include/movegen.h, include/variants.h: Add a new rule option: en-passant check. Sliding royals can then not "move through check". * variants.txt: New variant: Caissa Britannia, with a royal Queen and several riders. * include/board.h, include/movegen.h, include/variants.h, variants.txt: Add two new rules: "promote by move" promotes by moving the piece immediately as the piece it promotes to. "Quiet promotion" requires that the promotion move is a quiet move (which currently just means it cannot be a capture). * include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h: Add a new option for promotion zones: the ability to disable promotion when entering a promotion zone (so only moves that start in the promotion zone allow for promotion). * include/game.h, include/search.h: Filter out illegal promotions (non-quiet) during search. * include/movegen.h, include/piece_types.h, include/variants.h: Add "promote last piece anywhere" rule, implemented in Sittuyin. * include/variants.h: Adjust piece value of pawn in Sittuyin: because promotion is so restricted, the pawn value is practically just what it would get from its moves, which puts it at ~half the value of the Ferz. * include/game.h, include/movegen.h, include/search.h: Correctly allow "wild" promotions to be optional. Test for check explicitly when filtering illegal moves when QUIET_PROMOTION is in effect; otherwise we miss discovered checks. * include/variants.h: Piece drops on the back rank are allowed in Sittuyin (but unusual). * include/movegen.h: Make "promote by move" replace the normal promotion rule. * include/game.h, src/xboard.cc: If the piece abbreviation is " " (as normal for pawns), then base its name in the choice string on the white FEN-ID. * include/game.h, src/xboard.cc: First attempt at evaluation parameter configuration files. They can be specified on the command line, or from the engine options menu. There is also an option to store this file or not, intended for use with automatic tuning. * include/mate.h: Disqualify repeated positions from consideration in the mate-search: they cannot normally be part of optimal play, but may lead to the program blundering into a repetition loop if the opponent deviates from the expected line. Worst case, the mate-search doesn't find the mate and it is up to the main search to find it. * src/xboard.cc: Re-use function to load evaluation parameter file. Avoid clling fclose() on a NULL-pointer. * include/game.h: Set value of royal piece very high if not initialised. This is mainly so that SEE returns sensible numbers if royal pieces are involved (without this they have value 0, causing mis-evaluation of things like QxP / KxQ / RxK). * include/pipe2.h, include/sprt.h, src/misc/pipe2.c, src/misc/sprt.c: Add support files for Sjef. * include/fen.h: Very minor cleanup of FEN generation. * CMakeLists.txt, include/pipe2.h, src/misc/pipe2.c, src/xboard.cc: Add non-standard feature "sjef" to indicate that SjaakII implements some extra commands to act as a referee engine in Sjef: the "fen" and "san MOVE" query commands. * src/sjef.c: Re-add the Sjef match-playing program, using SjaakII as an external referee (incomplete). * src/xboard.cc: Fixes to the "san MOVE" command. * src/misc/pipe2.c, src/sjef.c: Rewrite large chunks of sjef, so it can now run correctly using an external referee program. Some features are still missing. * src/sjef.c: Add support for the "memory" command to Sjef. * src/sjef.c: Fix compilation errors. * src/sjef.c: Record score/depth/time statistics during a game, for storage in the PGN file. * CMakeLists.txt: Explicitly link libm to sjef executable (needed on Linux). * src/xboard.cc: Fix uninitialised variable. * CMakeLists.txt: Bump required CMake version to 2.8.12. * src/misc/pipe2.c: Cleanup of bidirectional pipe stuff. * src/xboard.cc: Make sure evaluation parameters are loaded correctly when starting a new game. * src/misc/pipe2.c: Cleanup of bidirectional pipe code. * include/pipe2.h, src/sjef.c: Correctly parse commandline options for referee and engines. * src/sjef.c: Print an error message when an engine fails to load (instead of crashing). * src/xboard.cc: Consider start move count (as passed in FEN) in time control. * include/evaluate.h: Fix mobility evaluation: do not count occupied squares as possible move destinations. * include/eval_param.h, include/evaluate.h: Implement tempo bonus in drop games, depending on material in-hand. This awards a bonus for having the initiative, and tests as an improvement (52%) in both Crazyhouse and Shogi. * include/eval_param.h: Apparently INT16_MAX is not a standard define. * include/evaluate.h: Slight refactor of tempo bonus. * src/sjef.c: Do not clear USERMOVE flag when starting a new game. * src/xboard.cc: Update Sittuyin perft counts for the updated rules. * include/evaluate.h: Fix a typo: bbc[side] instead of bbp[side]. * variants.txt: Correct PieceToChar string for Caissa Brittania Between 1.3.0 and 1.3.1 * include/betza_string.h, src/xboard.cc: Silence "unused variable" warnings. * include/game.h, include/variants.h, variants.txt: Make the 50 move rule configurable from the configuration file. * variants.txt: Remove castling from suicide chess, it's apparently not a move. * msvs2015, msvs2015/SjaakII.props, msvs2015/SjaakII.sln, msvs2015/SjaakII.vcxproj, msvs2015/SjaakII.vcxproj.filters: Add MSVS 2015 build files. * include/compilerdef.h, include/eval_types.h, include/evaluate.h, include/fen.h, include/game.h, include/movegen.h, include/movestring.h, include/variants.h, src/misc/ansi.c, src/misc/cfgpath.c, src/misc/snprintf.c, src/xboard.cc: Code changes by Martin Sedlak, to make the code compile on MSVS 2015 in Windows. * include/board_rules.h, include/game.h, src/xboard.cc: Fix result claim for illegal repetitions in games that do not use the chase rule. * variants.txt: Remove outdated comment. * include/variants.h: Fix incorrect deallocation of array of fixed size. * include/movegen.h: Move caculation of occupied board inside piece loop. This is a bit faster because saving the value turns out to be more expensive than reconstructing the data from a simple OR. * include/betza_string.h: Fix spurious output of "again" modifier in Betza string if the first leaper is a compound leaper. * include/variants.h: There is no real need to force an empty line to terminate the piece description, so don't. * include/fen.h: Emit error message also using "tellusererror", so the user gets to see it. * include/variants.h: Add error message if a variant fails to load because the board is too large. * include/piece_types.h: Move pawn structure related bitmasks away from move generation related datastructures to improve data locality and cache coherence. * include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h, variants.txt: Add a new piece movement restriction: "block", which marks a square as always occupied for the purpose of determining movement options for a piece. * include/movegen.h: Minor cleanup/simplification. * include/movestring.h: Match SAN moves that include a "+" to indicate check. * include/movegen.h: Correct capture victim for en-passant captures in Crazy-house. * src/xboard.cc: Be a bit more strict about matching commands on input; through the "name" command and various options arbitrary strings can be entered. * src/xboard.cc: Add Crazyhouse to the list of variants that are verified in the move generator test. * src/xboard.cc: Add Crazyhouse position that tests whether demote-on-capture works. * src/xboard.cc: Defer reply to "ping" until after we break out of the search loop. * include/search.h: Log the FEN of the position that is being searched. This helps with debugging. * include/betza_string.h: Fix a few minor bugs in the Betza string compiler. * variants.txt: Remove redundant empty lines. * src/xboard.cc: Allow for '-' in a variant name. * src/xboard.cc: Implement new highlight colour Blue: for mandatory promotions with only one choice. * include/game.h, src/xboard.cc: Allow for optional promotions in response to lift/put by including the piece ID itself. * src/xboard.cc: Extend functionality: send promotion choice in response to "lift" if all valid moves have the same promotion options. * include/movegen.h: Fix en-passant captures for forced-capture variants: these were not counted as captures, and so would not trigger the skipping of the generation of "quiet" moves. * include/movegen.h: Implement "initial" moves (in addition to special moves) for pieces other than pawns. * include/variants.h: Rename G->A in Xiangqi * variants.txt: Add Wa-Shogi, with and without drops. * include/game.h, include/search.h, src/xboard.cc: Add difficulty level combo-box. So far this has two options: normal alpha/beta search, and random. * include/evaluate.h, include/game.h, include/search.h, src/xboard.cc: Rename the "Random" level to "clueless" and add a "random" level that is based on returning a random number from the evaluation. * include/game.h, include/movelist.h, include/search.h, src/xboard.cc: Add multi-pv mode for analysis. * include/search.h: Award victory if the 50 move counter reaches 50, but it is mate. * include/search.h: Fix typo. * include/game.h, include/movelist.h, include/search.h, src/xboard.cc: Add another simple level of play, based on static analysis of legal moves. * include/search.h: Slightly improve play for static move ordering skill level; the main goal is to make play "look" more natural/planned. * src/xboard.cc: Add non-protocol "skill" command, to control playing level. * include/search.h: Fix move ordering for static move order AI so it always prefers to promote to Queen. It will also avoid centralising its king in the opening phase of the game. * src/xboard.cc: Fix spurious output of destination squares in response to "lift" due to drop moves. * src/xboard.cc: Add non-protocol "history" command, to show game history. * variants.txt: New variant, Theban Chess. http://www.talkchess.com/forum/viewtopic.php?t=52277 Between 1.2.1 and 1.3.0 * src/xboard.cc: Report the left-hand side of the user-defined alias as well as the right-hand side as a supported variant, so that you can assign a new name to a variant in XBoard. * include/search.h: Do not age the hash table in analysis mode. * variants.txt: Expand description of piecetochar table. Include "Diana Chess", a 6x6 variant. * include/variants.h: Fix a bug in the definition for queen-side castling for Capablanca Chess. * src/xboard.cc: Send engine options from a dedicated subroutine. * include/game.h, include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h: Increase the number of promotion zones from 1 (plus optional) to 16(which can each be optional). This slows things down a bit, but it increases versatility. * variants.txt: Add three variants that use the larger number of promotion zones: * Sherwin's Chess, where knights are allowed to promote on the second rank * Viceroy Chess, which is FIDE chess but with promotion to file-piece (e-file to non-royal K) * Chaturanga, with rules as given on chessvariants.org. It is basically Shatranj, but with promotion by file-type to captured pieces only. As implemented, promotions are compulsory and so pawns cannot advance with deferral of promotion. * include/fen.h, include/piece_types.h, variants.txt: Add initial Knight-jump for King in Chaturanga. * src/xboard.cc: Fix win/loss claim for variants where the point is to lose all your pieces: the code checked the wrong flag for the victory condition. * include/movegen.h, include/search.h, include/variants.h: Allow for variants to specify that captures are forced. * variants.txt: The ability to add forced captures in a variant allows for the definition of Suicide Chess. The play looks dreadful though (but alpha-beta isn't the best tool for the job here). * variants.txt: New variant: Roman Chess (10x10) * include/movegen.h: Change prototype of deduce_castle_flags so that it returns the index of the castle move it just defined. * include/variants.h: Fix a regression in Sittuyin: promotions are always optional. * include/betza_string.h, include/evaluate.h, include/fen.h, include/game.h, include/movegen.h, include/variants.h, src/xboard.cc, variants.txt: Implement flexible castling: multiple destinations can be given for the king, which can choose any of the given options. Add variant "schoolbook" that implements this. * include/betza_string.h: Fix l/r/s output for castling moves. * src/rules/san.cc: Fix disambiguation of moves by first piece type, which was broken. * include/san.h, include/search.h, src/rules/san.cc, src/xboard.cc: Supress O-O/O-O-O notation for castling in variants with flexible castling. * variants.txt: Add "supercapablanca chess", 12x8 with amazons and flexible castling. * include/betza_string.h: Fix Betza output: the "i" modifier needs to be present on castling moves independently of whether other initial moves are specified. * src/xboard.cc: Send variants again if the order in which they are sorted is changed. This used to work before, but the code was lost. * src/xboard.cc: Slight re-write of code to resend options. * include/board.h: Disable ANSI colours on Windows. * cmake/FileList.cmake, include/ansi.h, include/board.h, src/misc/ansi.c: Add colour output to Windows terminal. * include/movegen.h, include/piece_types.h, include/variants.h, variants.txt: Add a new piece property: iron, which means a piece cannot be captured. * include/game.h, include/piece_types.h: Display iron status in rule output. * include/board.h, include/movegen.h, include/variants.h, variants.txt: Add a new possible rule: attacking any king counts as check (if there are more than one). * include/search.h: Eliminate some compiler warnings on MingW builds. * MSVS2012/SjaakII.vcxproj, MSVS2012/SjaakII.vcxproj.filters: Add ansi.c to MSVS build files. * include/movegen.h: Fix any king counts as check (if there are more than one) rule so it is actually tested. * include/move.h, include/movegen.h: Simplify move generator code slightly be removing unused "ptaken" arguments. * include/betza_string.h, include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h, src/rules/move.cc, src/rules/san.cc, src/xboard.cc, variants.txt: Add a new "Initial" move option, in addition to the "Special:" move option. It currently only works for stepper moves. Fix output of single-step castle moves (this is hard coded as a single step, which is assumed to be the normal king step as well). Add definition for Wildebeest Chess. Make variant parser a bit more robust, and recognise "WinBoard Pieces:" correctly (as documented). Make KxR input for castling work correctly. * include/variants.h: Fix a crash when reading an optional promotion zone listing that does not specify a piece type: this only sets the optional zone, but does not add a new one. * include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h, variants.txt: Remove "zone" for Initial moves: it is (should be) redundant. * include/movegen.h: Fix a bug in the handling of pieces with initial moves: we should only set the "initial" zone if pieces actually have a different special move in that case. * include/board.h, include/game.h, include/movegen.h, include/piece_types.h: Add a special rule flag to deal with side-effects of capture victims, and build a template over this for the move generator. * include/game.h, include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h: Allow limiting the capture victims for each piece. * include/evaluate.h, include/game.h: Include pieces that are initially in holdings for the purpose of determining the scale factor for the game phase. * include/game.h: Increase weight of piece square tables in XiangQi. * include/evaluate.h, include/game.h, src/xboard.cc: Add randomisation to the evaluation for the opening moves of the game (enabled by default). This can be configured through the engine options dialog. Fix a bug that prevented options from being set correctly from the GUI. Fix a bug that caused the setting of "random" to be ignored for variants other than "normal". * src/xboard.cc: Under XBoard protocol, "random" is supposed to toggle. * include/eval_param.h, include/game.h: Make scale factor for PST in XiangQi an adjustable parameter. * src/misc/ansi.c: Make ansi.c compile on Windows (not C99). * src/misc/ansi.c: Silence warning about implicit declaration. Between 1.2.0 and 1.2.1 * variants.txt: Bump value of Hawk and Unicorn in Musketeer chess to take into account the long-distance jumps that are not considered by the piece-value estimator. * src/rules/move.cc, src/xboard.cc: Include KxR castling notation in the "longmoves" listing. Do not print castling moves as @@@@. * include/search.h: Silence uninitialised variable warnings from Valgrind. * include/game.h: Fix minor memory leaks, found by Valgrind. * MSVS2012/SjaakII.vcxproj: Bump version number to 1.2.1. Between 1.2RC1 and 1.2: * include/movegen.h: Simplify the generation of drop moves by omitting redundant conditions. * src/xboard.cc: Add an option to disable sending O-O/O-O-O for castling to XBoard, which might break on some versions. However, doing this WILL break shuffle variants. * src/xboard.cc: Place boolean (checkbox) options in a list at the top of the file, for easier expansion and editing: adding an option to the table will make it show up in the engine options box and it will be saved/restored from the configuration file. * src/xboard.cc: Respond immediately to hashtable size option changes in UCI mode. * include/variants.h: Change piece symbols for Marshall and Cardinal in Grand Chess to C and A (for Chancellor and Archbishop) to avoid conflicts with XBoard. * src/xboard.cc: Add a commandline option "-normal" and a dialog option to override the game that is selected when the current variant is "normal". This is generally inadvisable but there is a use-case for it. For safety reasons, the option is not saved. * variants.txt: Changes to Musketeer definition: use "C" rather than "O" for the Cannon (C was Chancellor, which is now M). Add virgin state to other files in startup FEN. Do not use Lion graphic for the Leopard (I picked Nightrider instead). * variants.txt: Add a new variant, "Spanish archer", which features the archer (fRbB). * variants.txt: Added another one: "enlarged and improved chess", which features mRcB and mBcR Between 1.0.0 and 1.2RC1: * include/piece_rules.h, include/piece_types.h, include/variants.h: Add a "Demotion:" property that can be set from the config file, to force demotion on capture. * include/movegen.h, include/piece_rules.h, include/piece_types.h: Store piece bitflags in a piece_flag_t (which is just a typedef for uint16_t, for now) rather than a uint16_t. For later expansion. * include/piece_types.h: Fix typedef. * include/movegen.h, include/variants.h, variants.txt: New piece property: assimilate. When capturing a piece with this property, the capturing piece promotes to its victim. * include/movegen.h: Disallow special move for royal pieces when evading check. * variants.txt: Add Ouk Chatrang as a variant in the config file. * include/movegen.h, include/variants.h, src/rules/move.cc: Implement Chul Lion-style double moves from leapers. Notation leaves something to be desired, which needs a few changes to how moves are printed. * include/chase.h, include/history.h, include/mate.h, include/search.h, include/see.h: Eliminate 'get_move_piece()' from much of the code. * include/fen.h: Correctly set en-passant victim if the FEN string terminates after the en-passant square. * src/xboard.cc: Give priority to user-defined variants when matching variant names. This allows the user to override the description for a build-in variant. * src/rules/move.cc, src/rules/san.cc: Fix output of double moves in output. Has a slight hack: it relies on the piece symbol ' ' to suppress special treatment of captures that are not by replacement (which is needed to treat en-passant captures correctly). * variants.txt: Fix a mistake in the definition for Ouk Chatrang: rank3/rank6 were not defined. * include/variants.h: Fix a bug in the parser for Prison: tags in a variant description. It would fail if it did not find a third argument, which should be non-existent. * variants.txt: Add MiniXiangqi, a 7x7 Xiangqi variant http://mlwi.magix.net/bg/minixiangqi.htm * include/betza_string.h: Make Betza notation work (sortof) for double-step leapers. * include/betza_string.h: Change output of Betza string for multi-leg moves: we need to explicitly mention the move/capture options, as well as the reverse move. * include/board.h, include/movegen.h, include/piece_types.h, include/variants.h, variants.txt: Add the no_retaliate property for pieces: after capture of a piece of this type, your own pieces may not be recaptured immediately. * src/xboard.cc: Fix highlight command for multi-step moves. * include/movegen.h: Fix generation of lion attack maps: lions do not defend themselves. * include/movegen.h, include/piece_types.h, include/variants.h: Add "endangered" property: pieces of this type are not allowed to capture eachother if the victim is (pseudo-)protected. With this change, SjaakII can play "Elven" chess. * variants.txt: Add Elven Chess as a new variant defined in the input file. * include/movegen.h: Fix calculation of leaper moves for ENDANGERED pieces. Fix evasion generation for double-leapers (they can capture the checking piece and then move again). * include/board.h: pieces with the no_retaliate property may capture eachother without setting the no_retaliate flag. * variants.txt: Fix castling flags for Elven Chess. * include/piece_types.h, include/variants.h, variants.txt: Expose PF_CAPTUREFLAG in config file. * include/board.h, include/game.h, include/search.h, include/variants.h, src/xboard.cc, variants.txt: Implement 3-check chess by exposing check-count a game termination condition. * variants.txt: Set parent variant of 3check to 3check (it is a standard variant). * include/evaluate.h: Award evaluation bonus for having checked the enemy king in 3check. * include/search.h: Accept exact hash cut-offs if they are outside the window. * src/xboard.cc: Creating a movelist before search is only needed if we're going to use it for SAN output. * include/movegen.h: Fix holdings update after en-passant capture: SjaakII used to take the piece from the destination square, but since this is supposed to be empty it retrieves an old (stale) entry from the board and places an arbitrary piece in the holdings. * src/xboard.cc: Minor cleanup of command-line interface code. * include/movegen.h: Fix an oversight that broke store-to-holdings for normal (as opposed to en-passant) captures. * include/search.h: More agressive null-move reductions with increasing depth. This seems to be what everyone else does anyway, and it tests as a modest improvement in Chess and an improvement or neutral in Shogi. * include/game.h: Tuning: reduce the centralisation score in the piece square tables, except for drop games. * include/evaluate.h: Change the way material scoring is handled in relation to defensive pieces. In particular don't treat the presence of defensive material as a reason to accept losing a piece. * include/game.h, src/xboard.cc: Add non-protocol 'pst' command that outputs the PST. * include/variants.h: Set stale_score = mate_score = won for Xiangqi. * include/eval_param.h, include/evaluate.h, include/game.h: Add a bunch of Xiangqi evaluation tweaks: penalty for a Cannon on the same file as the king, change king PST and tweak hopper/slider palace-related PST. Scores ~70% in self play after 100 games. * include/movegen.h: Fix a problem in detecting attacks in Xiangqi: if any attackers along a ray were restricted in their moves teh entire attack mask would be intersected by this, not just attacks from the piece under consideration. Also keep the "to" square set in the move mask to test for new ray-based attacks: this is needed to detect new attacks by cannons. * include/evaluate.h: Revert 486, which is redundant with the scaling of piece values and drops ~40 elo in self-play. * variants.txt: Add Mighty Lion Chess as a defined variant. It appears to work correctly, but there may still be some issues. * include/evaluate.h, include/game.h, include/search.h, include/variants.h, variants.txt: Disable 50 move rule for Xiangqi. * include/search.h: Correct move number in PV output for black to move. * include/evaluate.h, include/variants.h: Normalise scoring of defensive pieces in Xiangqi. * include/search.h: Fix a problem in ponder search: if the search finishes, wait until we are taken out of ponder mode to prevent a second (useless) pnder search from being started and flooding the interface with output. * include/variants.h: Promotions are mandatory in Tori Shogi. * include/movegen.h: Fix a few inconsistencies and oversights that could cause random pieces to be placed in holdings for steppers due to accessing the en-passant victim rather than the destination square. * src/rules/move.cc: Fix duplicate piece symbol at end of gating move in LAN. * src/rules/move.cc: Fix a bug when translating moves to LAN: drop moves could incorrectly be printed as NULL moves if the passed the "from == to" test. Drop moves are now excluded explicitly. * src/xboard.cc: Add 'playother' command, but don't tell xboard we support it (I'm not sure what it's supposed to be used for and it seems rather pointless, but it's useful to have for debugging purposes). * src/rules/san.cc: Fix SAN disambiguation of gating moves. * include/move.h: Fix an overflow problem when updating holdings for castle moves by shrinking the size of the holding slot in the move encoding by one bit. * include/variants.h, src/xboard.cc, variants.txt: Move Amazon Chess and Chancellor Chess to the variants.txt file (from the build-in list of variants). * include/eval_param.h, include/evaluate.h: Rename PASSER_RANK_SCALE to PASSER_RANK_BASE. Add a new PASSER_RANK_SCALE that can be used to scale the weight of the quadratic term in the passed pawn evaluation. * include/eval_param.h, include/game.h: Parameterise mobility evaluation and allow for different scale factors in middle game and end game. * include/game.h: Fix typo in commented-out Senpai formula. * include/eval_param.h: Document some STS scores for mobility scaling parameters. * include/game.h: Fix a typo that caused the sign of some of the rook PST entries to be flipped. * include/eval_param.h, include/game.h: Make the general form of the piece square tables bilinear. * variants.txt: Add "Musketeer chess", with Seirawan-like gating rules. * include/chase.h, src/xboard.cc, variants.txt: Add auto-complete for some additional commands: moves, longmoves, pieces, pieceinfo. * src/xboard.cc: When printing moves in LAN format for the longmoves command, print castlingmoves as "fromto" rather than "O-O". * include/fen.h: Partial fix to decoding castling rights from FEN positions: the original code assumed that castling was with king and rooks on the back rank, the new code just assumes that they are on the same rank. This still breaks Troitzky chess, but that was already broken and fixing it needs a more thorough re-design of the code since we can no longer assume that king and rooks are on the same rank, or indeed all on the same ray. Between 1.0RC7 and 1.0.0: * include/game.h: Make sure SEE cache is initialised to 0 at startup. * include/piece_types.h: Use piece_bit_t to encode promotion choices. * include/chase.h: Fix chase detection: the retrieved from/to squares were entirely bogus. Avoid using get_move_captured_piece(). * include/move.h, include/see.h: Retire get_move_captured_piece. * include/move.h: Reduce size of pickup encoding to a single square. * include/movegen.h, include/variants.h, src/rules/move.cc: Add a prototype version of the Chu Shogi Lion to the test variant (as WFADN0 leaper). Add the ability to store a null-move to the move generator (implemented as a 0-jump). This is output as @@@@ in LAN. * include/move.h: Allow encoding of double captures (Lion moves). DO not reset 50 move counter on castling. Make move description agree with what is implemented. * include/variants.h: Add Lion definition to test game. * include/board.h, include/history.h, include/killer.h, include/move.h, include/search.h, include/see.h: Eliminate get_move_player. * include/movegen.h: Try to unwind some of the nested template code. This still does the same, but it leaves the job to the compiler. * include/movestring.h: Remove move_is_long_algebraic, it is no longer used. * src/xboard.cc: Add longmoves command, which prints out the moves in different formats. * include/piece_rules.h: Apply "cyclic" promotions on Shogi pieces: if a + piece is promoted, it reverts to its unpromoted state. * src/rules/move.cc, src/rules/san.cc: Correctly output "+" for "promotion" of promotion pieces (subject to change) * include/fen.h: Allow multi-character piece encodings in FEN strings. In particular, this allows ' and ! which are allowed extra characters in XBoard. * src/xboard.cc: Backport UCI ponder fix from Jazz. * src/xboard.cc: Remove stray ; * include/game.h: Push pawns more agressively in drop games, since we cannot rely on game phase to activate an end-game term for this. * include/eval_param.h, include/game.h: Add general quadratic terms for pawn advancement in piece square tables. The only term currently in use is the quadratic end-game term, but at least the linear MG term may be useful to have as well. * include/search.h: Remove unused maxdepth/seldepth output. * include/board.h, include/movegen.h, include/variants.h, src/rules/move.cc, variants.txt: Allow promotions on drop moves. Allows for the implementation of Kyoto Shogi. * include/game.h, include/movegen.h, include/piece_types.h: Use 32 bit integers for piece masks, since we now have 32 piece types. Between 1.0RC6 and 1.0RC7: * include/board.h: Trap illegal square numbers being passed to board.get_piece() in debug mode. * include/game.h: Reflect changes in castle rights in the hash key. This needs to be done in "playmove" rather than "makemove" because "makemove" does nt have access to the move generator. * include/fen.h: Correct presence of castling and en-passant fields in FEN string for whether these moves occur in a game. Correct full-move counter output in FEN. * include/movegen.h: More direct and explicit test of whether promotion moves out of the promotion zone are allowed or not. Again, this should perhaps be a separate flag in the future, instead of being implied by RF_PROMOTION_IN_PLACE. * src/xboard.cc: Fix Sittuyin test positions. * include/see.h: Do not order non-captures by LVA. This seems to cause problems with optimised builds under Windows. * include/movegen.h: Print an error message if a config file defines too many pieces. * include/movegen.h: Separate leaper and aleaper counts. * include/game.h: Avoid floating point exception when assigning a scaled piece value with a scale of 0 * include/movegen.h: Proper re-use of stepper descriptions/indices. * include/game.h: Print number of pieces of a particular type as output of pieceinfo. * include/betza_string.h, include/movegen.h: Rewrite matching of leaper moves to more efficiently reuse existing entries. The code is now simpler as well. * include/movegen.h: Rewrite matching of aleaper moves to more efficiently reuse existing entries. The code is now simpler as well. * include/variants.h: Promotions are not optional in Makruk. Between 1.0RC5 and 1.0RC6: * src/xboard.cc: Fix compilation if readline is not available. * include/movegen.h, include/piece_rules.h, include/piece_types.h, include/variants.h: Fix a problem in Tori Shogi: the drop-rule states a sparrow may not be dropped on a file that has *two* friendly sparrows. * include/evaluate.h: Disable game-phase interpolation for variants where captured pieces are returned to the board: material on the board does not reflect game phase in this case. * include/movegen.h: Fix a problem with promotions in Shogi: pawn moves out of the optional promotion zone are not optional. * include/movegen.h: Better fix for optional promotions in Shogi that does not potentially break EuroShogi. * include/move.h: Replace hard-coded numbers with symbolic constants for shifts and masks when building up the move struct. * include/move.h: Replace more hard-coded numbers with symbolic constants for shifts and masks when building up the move struct. * include/move.h: Replace more hard-coded numbers with symbolic constants for shifts and masks when building up the move struct. * include/move.h, include/pieces.h: Replace hard-coded numbers with symbolic constants for shifts and masks when building up piece IDs. * include/board.h, include/pieces.h: Remove almost unused piece_t. This also fixes a hidden conversion of unsigned->signed. * include/game.h, include/movegen.h, include/piece_types.h, include/search.h: Do not defer promotion (in the search) if this is unneeded because the unpromoted piece has a subset of the moves of the promoted piece. Mainly affects Shogi-like variants. * include/piece_types.h, include/pieces.h: Use typedef for piece bitfields (instead of uint16_t). * include/movegen.h: Replace 0xffffffff by ~0 for bitmasks representing piece-sets: this makes it agnostic of the size of the bitmask. * include/movegen.h: Reuse leaper tables more efficiently by just looping over existing tables at the end (the clever way that avoids this doesn't seem to actually work, and only captures a sub-set anyway). Also fixes a bug where defining a new leaper could clear the aleaper tables. * include/movegen.h: Reuse stepper descriptions in the same way as leaper descriptions. * include/movegen.h: Make sure piece-type masks have enough space. * include/movegen.h, include/pieces.h: We do not need to keep track of the number of sliders and hoppers; to construct the super slider/hopper we only need to keep track of the joined move flags. * include/move.h: Re-use piece side encode/decode in move decoding. * include/move.h, include/see.h: Fix a bug in MVV/LVA ordering: make sure a move is a capture move before extracting a captured piece. * include/betza_string.h: Betza notation for drop moves (future-proofing for XBoard). * include/variants.h: Handle arbitrary-length pieceToChar strings in input file. * include/search.h: Fix cosmetic typo: use hash_move when storing the counter-move table. * include/game.h: Cleanup. * include/board.h: Keep track of a separate hash key for the board hash (in addition to the full hash). * include/search.h: More agressive pruning of futile drops near the leaves of the tree. * include/search.h: Always accept mate scores from the TT, independent of remaining depth: more depth is not going to alter the mate result... * include/search.h: Offer a draw if the game lasts extremely long (> 400 moves). This may take some 8 moves or so to trigger for the first time. * include/board.h, include/fen.h, include/game.h, include/search.h: Implement detection of "pseudo-repeats" in drop games: these are repeats where the position is repeated, but the holdings are not. In a typical case, we may be leaking pieces to the hand of the opponent - which means we are now worse off than we were when the position occurred last. If this situation is detected, we simply fail low immediately at this node. * include/variants.h: Handle arbitrary-length pieceToChar strings in input file: now without crashing (the required size was calculated incorrectly). * include/movegen.h: Fix generation of evasions if the checker is a lame leaper (which can be blocked). This caused Sjaak to miss some possible evasions. * src/xboard.cc: Switch to force mode after setboard. This is safe to do, but not strictly specified in the protocol. * include/evaluate.h, src/xboard.cc: Add another Xiangqi test position, this one testing evasion by blocking a lame leaper. * include/evaluate.h: Revert accidental inclusion of change in evaluate.h in last commit. * include/eval_param.h, include/game.h: Implement framework for piece value scaling of defensive pieces with game phase. Swap the test for defensive pieces and lame leapers. * include/game.h: Fix calculation of pair bonus for defensive pieces, which was completely wrong (resulting in a bonus that was larger than the piece value). * include/eval_param.h: Decrease the value of defensive pieces in the end game. This encourages advancing the game phase (exchanging attacking pieces) if we are behind an attacking piece for a defensive piece, and discourages simplification to dead-drawn endings where the advantage consists entirely of defensive pieces. The end point of the scale (0.8 of the nominal value) has not been tuned. * include/movegen.h: If promotions are flagged as in-place, disallow optional promotions by moving out of the promotion zone. Fixes a bug in Sittuyin that allowed them. * src/xboard.cc: Modularise testing of move generator. Add Sittuyin test position, to handle drop promotions. * variants.txt: Add three (four) contributed variants: Euro Shogi, Yari Shogi and Goro goro Shogi (with and without knight/lance in hand). Thanks to Harm-Geert Muller and Keith White. * include/move.h: Make move format description match what is actually implemented in the code. * include/pieces.h: Increase number of allowed pieces to 32 (up from 16). * include/evaluate.h: Revert Shogi forward king-safety patch. It is a regression at longer time controls. * include/search.h: Change repetition detection: if the first repetition is in the game history (as opposed to search) do not automatically score the second repetition as a draw if it occurs in the search. * src/xboard.cc: Fix a cosmetic mistake when listing variant names for the "Fairy selects" option (stray ,). * include/movegen.h: Generalise castle mask code to work along general rays (not just ranks) * include/fen.h: Make FEN parsing of castle moves work if king and rook are not on the same rank. * variants.txt: Fix board FEN for Troitzky Chess to include dark squares. * variants.txt: Fix board FEN for Troitzky Chess to not list castling. * include/movegen.h: Guard against accidentally setting off-the-board squares. * variants.txt: Added the 'Byway' setup as an alternative to the 'Jelliss' setup for Troitzky Chess. This implements castling with a rook that is not on the same rank. This will NOT work in XBoard. * include/variants.h: Rename Marshall->Marshal. * include/game.h, include/movegen.h, src/xboard.cc: Make rules include pawn doubl-pushes in board output. Add a special "wikirules" command that prints out the rules in a template suitable for putting on a wiki. * include/game.h: Fix wiki formatting for bullet lists. * include/game.h: Add templated note about rules being auto-generated. * include/variants.h: Fix promotion in Shatranj: to queen (ferz) only. * include/variants.h: Fix white promotion zone in Gardner Minichess. * variants.txt: Fix promotions in Omicron Chess to include Wizard and Champion. * include/game.h: Tweaks to Wiki-output for rules. Add a section on promotions, with boards indicating the promotion zones. * include/game.h: Clarify drop rules in rule description. * include/game.h: Tweaks to wiki-fied output. * variants.txt: Fix Black promotion zone for Peasant's Revolt. Cosmetic fix for Corner Chess. Between 1.0RC4 and 1.0RC5: * include/move.h: Fix a bug in decoding the move piece. This seems to have mainly had cosmetic effects, but it is an actual bug that could trip up move matching and things like SEE. * include/piece_rules.h, src/rules/move.cc, src/rules/san.cc: Correct SAN output of piece symbol for promoted pieces in Shogi. * variants.txt: Mark perpetual check as illegal in Judkins Shogi. * include/game.h, include/piece_rules.h, include/piece_types.h, include/variants.h, variants.txt: Allow specifying of the optional promotion zone, prison and drop-zones from the configuration file. Add a new piece property, "drop dead", which means the piece can be dropped in locations where it has no legal moves. * src/xboard.cc: Fix missing strdup(). * include/search.h: Fix overzealous pruning of drops near the root to finish a 1-ply search: doing it at greater depth leads to ugly and misleading analysis from shallow searches. * src/xboard.cc: Add protocol extension load and unload commands, to load variant definitions on the fly. * src/xboard.cc: Invalidate a file before loading it again. * include/search.h: Code cleanup: put code to score moves in the move list in its own function. * include/search.h: Replace static capture search with actual q-search. * include/move.h, include/search.h, include/see.h: Fix a major bug when extracting the side that moved from a move: it was always extracted as white. This completely broke SEE for black, and broke history and counter-move tables as well by lumping all moves from both sides together. Add proper SEE evaluation of drop moves. * src/xboard.cc: Print the correct repetition count for N-fold repetition (instead of just 3) * include/board.h, src/xboard.cc: Add "(debug)" to the version number of debugging builds. Add an extra sanity-check/assert in makemove. * include/search.h: Re-organise code for pruning decisions. This is a bit cleaner, and allows us to avoid calculating the static evaluationif it is not needed. * include/search.h: Get rid of the beta==alpha-1 condition on razoring. First of all it is a bug (should be beta==alpha+1), second of all, razoring in PV nodes is perfectly fine. * include/fen.h, include/game.h, include/movegen.h, include/piece_rules.h, include/piece_types.h, include/pieces.h, include/variants.h, src/xboard.cc: Fix parsing of initial state in FEN positions. There was a bug that could cause both castle flags to be set if one of them was, and the parser did not work correctly for Seirawan-style strings. There are a number of caveats with this though, for the interaction between backrank virgin pieces and castling rights in s-FEN. There may also be issues with shuffle variants on large boards (where 'k' denotes a rank, but 'k' is also used to indicate castling rights in Omega chess, for instance). * include/eval_param.h, include/evaluate.h: Award a penalty to a loose minor (a minor that is not defended by a pawn), scaled with the number of super pieces. * include/search.h: Fixes for analysis mode: do not break out early if mate is found or if there is only one move in the current position. If we do reach the end of the line, just wait until we're told to exit analysis mode. Otherwise the engine starts spamming the GUI over and over again until something breaks and things crash. * include/search.h: Fixes for analysis mode: do not break out of the analysis loop if asked to analyse in a checkmate position. This floods the output queue. * include/fen.h: Make sure king_from was set before trying derive castle flags from it. * include/game.h, include/piece_types.h: Add a function to guestimate piece values if none are specified. * include/fen.h, include/movegen.h: Make sure variables related to castle moves that are not defined in a variant are well-defined. * include/evaluate.h, include/variants.h: Scale piece-values in Shogi by 0.25. * include/variants.h: Tune scale-factor for material in Shogi. * include/game.h: Tune scale-factor for material in Shogi. * include/game.h: Minor tweaks to the piece value guestimator to try to give a better indication of piece strength for asymmetric pieces (it now scores shogi-knight ahead of shogi-lance) as well as take into account the elephantiasis of all pieces promoting to gold generals in shogi. * include/eval_param.h, include/evaluate.h: Parameterise space advantage. * include/eval_param.h, include/evaluate.h: Minor refactoring and code cleanup for evaluation. * include/evaluate.h: Prophylactic commit: penalise the king shelter in shogi if there are empty squares in front of the king. This is a very clear gain at slow time controls, less so at long time controls (test is still running, but doesn't look bad - it's still better). * src/xboard.cc: If reading input from a file rather than a terminal, skip comments. * include/variants.h: Fix parsing of optional promotion zones from the input file. * include/movegen.h: If a move originates within the promotion zone, but not the optional promotion zone, disallow generation of regular move. * src/rules/san.cc: SAN output: no need to disambiguate with respect to drop moves. * include/variants.h: Ensure that the promotion zone includes the optional promotion zone, if both are set from the config file. * include/fen.h, include/game.h, include/search.h: Two unrelated things: 1. Detect (some) mates in quiescence search: if the path leading to mate consists entirely of check/evasion moves, then the mate score from quiescence can be trusted and returned. 2. Record the correct full-move number from the startup FEN (instead of always behaving as though it were 1). * include/movegen.h: Add a special staged move generation. This is as yet not used much, but it could be. * include/mate.h, include/search.h: Add special mate-search code: this is a special search function that uses the staged move-generator. It returns either a mate-score (which is accurate) or a non-mate score (which is arbitrary and should be treated as an indication to continue the regular search). * include/fen.h, include/search.h: Fix parsing FEN records that do not include an en-passant square: we don't want to skip over the next field in that case. * include/mate.h, include/search.h: Make sure the mate-search is aborted if time runs out. * include/search.h: Fix move number in PV output with black to move. * include/search.h: Do not break out with a mate score based on the depth of the iteration matching the mate score, but based on the length of the PV matching the distance to mate. * include/search.h: Perform a special mate-search close to the root or at great depth in drop variants. Tests as a mild improvement there, can probably be tweaked for more. * include/game.h, include/search.h, src/xboard.cc: Add an option to enable/disable the special mate search. * include/search.h: Allow for future expansion of the mate-search options. Between 1.0RC3 and 1.0RC4: * include/board.h: Avoid warning about shadowing a member variable of the same name. * CMakeLists.txt: Fix make install target for manpage. * include/game.h, src/xboard.cc: Fix handling of commands in analysis mode: commands that should act on the root position should not be handled until after we return from the search. * src/xboard.cc: Add some FRC/Chess960 castling test positions from Reinhard Scharnagl. * src/xboard.cc: Report depth for expected perft positions. * src/xboard.cc: Fix a mistake in the perft count for one of the new FRC test positions. * include/board.h: Fix a design flaw in makemove: also for swaps, the destination squares need to be cleared before we resolve the drop side of the swap. Not doing this will corrupt the board state in FRC castle positions where the king moves to the rook square. * include/fen.h: Correct FEN output strings for boards with virtual ranks/files and holes. Fix a bug where the 50-move counter would be wrong (~arbitrary) for FEN strings that didn't include the field. * include/betza_string.h: Fix a bug in the generation of Betza strings for steppers: the direction masks for the Ferz were not properly calculated, leading to a buggy definition of the pawn move in Legan Chess (mF rather than lfmF). * include/game.h, src/xboard.cc: Keep track of time at each move, so the program will keep proper time during takenback/undo. * src/xboard.cc: Remove temporary debugging print statement. * include/test_suite.h: Expand STS test suite positions to include the full range of positions. * include/test_suite.h, src/xboard.cc: Add STS 13 and 14. Avoid hanging the program in case of a bad testsuite string. Print number of positions that got awarded points (regardless of whether the score is optimal or not) in the intermediate outout. * include/variants.h, src/xboard.cc: Add Great Shatranj as a known variant. http://hgm.nubati.net/rules/Great.html or http://www.chessvariants.org/index/msdisplay.php?itemid=MSgreatshatranjm * include/variants.h, src/xboard.cc: Add Superchess. XBoard has a bug in that it will publish castling rights if no corresponding rook is present, which will probably mess us up. Fix missing limit on number of black pieces in Great Shatranj. * include/variants.h, variants.txt: Allow specifying the maximum number of a particular piece type in the configuration file. * variants.txt: Rename Judkins Shogi -> Judkins' Shogi. This changes the short name to 'judkins'. * src/xboard.cc: Unload the user-defined variant config file before loading a new one. Reset the options that are sent to XBoard, which updates the variant list. * src/xboard.cc: Allow changing whether user-defined variants are listed first or not through a config options. This is useful because the XBoard variant dialog box is not large enough to contain all build-in variants and all user-defined variants defined in the standard config file, so some of them will be pushed out. * include/movegen.h, src/xboard.cc: Minor cleanup of leaper move generation: use bitboard generation functions where they exist. * include/bitboard.h, include/movegen.h: Minor cleanup for calculating "in-between" squares along a ray. Move #define update_leaper_bb to before make_aleaper_bitboard(). * include/search.h: Spacing of PV output in standard mode. * sjaakii.pod, src/xboard.cc: Add -xboard/-uci/-usi/-ucci commandline options to start the engine directly in a particular mode. These aren't really needed, but they serve to suppress the board/prompt output, which may be desirable for some interfaces. * include/evaluate.h: Document purpose of evaluation term attacking flags. * src/xboard.cc: Fix highlight FEN on normal (non-virtual/omega) boards, which were broken by the fix that made them work for Omega (duh). Fix using input[1] to determine the UCI dialect if -uci/usi/ucci are passed as startup options (reported by Martin). * src/rules/san.cc: Fix a bug in the output of SAN moves: the disambiguation didn't work correctly if the alternatives were a regular move and a promotion move: the code would still end up trying to 'disambiguate' by file and rank, rather than by promotion/no promotion. * include/movestring.h: Discard '=' token as optional when parsing a string. It is used as a promotion deferral in Shogi, or sometimes in variants of SAN to indicate promotion. Either way, we don't need it, so just skip it. * src/xboard.cc: Make it possible to defer commands received while thinking until after we have broken out of the thinking loop. Fixes cases of Sjaak ignoring 'new' while it is thinking, but giving the impression that it handled the command (by responding to ping with the proper pong). * include/evaluate.h: Include a space-advantage term, as suggested in http://www.talkchess.com/forum/viewtopic.php?p=609260. No measurable improvement in regular chess, but a massive plus for Gothic chess. * src/xboard.cc: Fix an issue with colour-FENs: if there is an optional promotion, the colour of the target square used to depend on the order in which moves are generated. This is no longer the case now, and the square gets the colour for promotion in preference to its default colour. * include/variants.h, variants.txt: Allow setting of the number of repeats required to trigger the "repeated position" end-game condition from the config file. * include/game.h, include/search.h, include/variants.h, src/xboard.cc, variants.txt: Implement triggering of no_pieces game-end condition. * include/game.h, include/search.h, src/xboard.cc: Implement resign and draw offer code (disabled by default) * include/game.h, include/search.h, src/xboard.cc: Optionally supress the claiming of repetition draws. * include/movegen.h: Do not generate drop moves in QS: they are pruned anyway (for now). * include/game.h, include/search.h, include/variants.h, src/xboard.cc, variants.txt: Score "perpetual" seperately from "repeat" (but the same by default). Try really hard to finish the first iteration (up to possibly using all our available time) to avoid the situation where we end up with no move to play. * include/game.h, include/search.h: Use finer granularity in clock-testing for drop variants. If we cannot complete the d=1 iteration, prefer to use a move from the hash table to playing a random move, if there is one. Between 1.0RC2 and 1.0RC3: * CMakeLists.txt, include/bitboard.h, include/eval_types.h, include/evaluate.h, include/game.h, include/killer.h, include/move.h, include/movegen.h, include/search.h, include/see.h, include/variants.h, src/xboard.cc: Fix many compiler warnings. Enable warnings by default. * include/movestring.h: Fix disambiguation of castling move when a piece is gated in. * include/fen.h: Ignore squares marked * in a FEN: these are meant to be void (off-board) squares * src/xboard.cc: Include promotions in the highlight colour FEN. * include/bitboard.h, include/board_rules.h: Fix the double-use of board_files[0] as the file_mask: this will not work correctly if a variant deletes squares from the file. * variants.txt: Fix a mistake in the definition of Judkins Shogi: the drop restrictions for pawns were accidentally applied to promoted pawns instead. * variants.txt: Add a new variant: Omicron Chess, which is like Omega Chess but on a 12x10 board rather than 12x12. Castling is disabled for now and off-board squares are not marked as "dark" to avoid crashes in XBoard (this should be purely cosmetic). * variants.txt: Replace Wizard pictograph with Wazir (instead of Vortex Chancellor) * src/xboard.cc: Get rid of __DATE__ in output because it causes binaries built on different dates to test as different even if they're not. Change order of variants in XBoard variants tag so variants from config files are listed first. * variants.txt: Remove outdated and misleading comment. * CMakeLists.txt, src/timer/timer.c: Don't try to use the realtime clock if we can't link to librt. * CMakeLists.txt, src/timer/timer.c: Better detection of clock_gettime. * variants.txt: Set Omicron Chess FEN to black out the deleted squares. * src/xboard.cc: Add a config option to mark holes in the board in the FEN sent to XBoard. Needs 4.8+ to work correctly, or XBoard will crash. The option is disabled by default for this reason. * src/xboard.cc: Flip the default for "mask dark squares" from true to false. This means a work-around needs to be enabled for XBoard 4.8 * include/board.h, include/fen.h, include/movegen.h: Rename ep_capture->ep_victim. It is clearer. * include/movegen.h: Not all instances of ep_capture referred to the victim (which is yet another reason to change the name). Change instances where it referred to the destination square accordingly. * include/board.h, include/fen.h, include/movegen.h: Replace en-passant square with a bitboard. The idea is that this can hold multiple en-passant squares, for large variants where pawns can move three ranks on their first step (such as Omega Chess) * include/board_rules.h, include/fen.h, include/game.h, include/movestring.h, include/squares.h, src/rules/squares.cc: Loosen the connection between the square ID in the FEN string and the bit index on the board. This allows us to make mangled boards, like for Omega Chess, that are larger than 128 bits. Better (more robust) identification of LAN strings. More accurate square-string matching. * include/variants.h, src/xboard.cc: Add Omega Chess (not enabled). It works, after a fashion, by hacking the movement tables so we can fake the 12x12 on a 12x10 board. Castling is missing (no real reason, but XBoard can't do it), SAN input is likely broken, board output is broken (it displays the mangled internal view of the board) and detecting of mate potential does not work (the routine is not designed with holey boards in mind). * include/game.h, include/variants.h, src/xboard.cc, variants.txt: Add a "Xboard parent" tag for variants defined in the config file. Needed because "fairy" does not always work correctly. * include/chase.h: Fix "may be used uninitialised" warning. * include/game.h, include/movegen.h: Apply board mask to leaper tables, otherwise "holes" in the board will not be masked out. This is not a problem during play, because the move generator applies the mask anyway, but it breaks mate potential detection and is generally sloppy. Fix a few issues in the mate potential detection: the defending king is allowed an entire quadrant (to take into account boards with holes, where we may not just be able to stick the king in a corner), but the corner and edge are tested first for efficiency reasons. Discard positions where the attacking king blocks all escape squares by itself: such positions are unreachable in legal play. They occur in the analysis of Omega Chess and Knightmate. * include/betza_string.h, include/variants.h, src/xboard.cc, variants.txt: Enable castling in Omega and Omicron Chess. * include/game.h: Fix detection of mating pairs and mate-potential assesment in Omega Chess. It fails to detect that a rook cannot force mate. * src/xboard.cc: Send multi-leg moves in two consecutive lines. Not that we generate those at the moment. * include/game.h, include/movestring.h, include/xstring.h, src/xboard.cc: Simplify matching of move string to move: instead of parsing the string, simply convert all legal moves to a string and test if the strings match. This is much less error-prone. * include/xstring.h: Add missing header files. * include/board.h, include/fen.h, include/game.h, include/piece_rules.h, include/variants.h, src/rules/squares.cc: Change the way the board is printed so "virtual" boards are printed accurately. So far, this only affects the output for Omega Chess. * src/xboard.cc: Fix a bug in the formatting of the variant list that is printed in response to "variants". * include/board.h, include/game.h: Unify the board.print() and board.print_demo_board() routines. * CMakeLists.txt, sjaakii.pod: Add a basic manpage. * CMakeLists.txt, src/xboard.cc: Rename SVNVERSION->SJAAKIIVERSION. SjaakII/README000644 000765 000024 00000010100 12462364731 013747 0ustar00eglebbkstaff000000 000000 Sjaak II - the next-generation general chess-like game playing program Copyright (C) 2011, 2014 Evert Glebbeek (eglebbk@dds.nl) Getting started --------------- To compile Sjaak, you will need: * A C++ compiler that understands some C99 constructs, for instance gcc (standard on Linux and Mac OS X, available for Windows in the form of MinGW) * CMake, a cross-platform setup and configuration system. Version 2.4 or better. * GNU Make. Technically this is optional and you could pick another back-end in CMake. For optimal performance, you will also need * A 64 bit operating system and compiler Configure Sjaak using CMake, either using cmake-gui, or from the command line. From the top-level Sjaak/ directory, run $ mkdir Build $ cd Build $ cmake .. Then run $ make This will place all generated files in the Build/ subdirectory, leaving the source tree clean. On Windows, substitute mingw32-make for "make". The program is called "sjaakii". You can load this as an engine in XBoard/Winboard, or use it directly as a terminal program. License ------- With a few exceptions (listed below), the source-code of SjaakII is released under the terms of the GNU General Public License (GPL) v3.0. A copy of the licence text is included in the file COPYING, or available from the Free Software Foundation at http://www.gnu.org/licenses/gpl.html The source-file src/misc/genrand.c is based on the 1997 implementation of the Mersenne Twister by Makoto Matsumoto and Takuji Nishimura and is released under GPL v2.0. The source files src/misc/cfgpath.c and include/cfgpath.h are based on public domain code by Adam Nielsen and are free for use without restriction. Castling, en-passant captures and repetitions --------------------------------------------- In chess, a position is not repeated if the en-passant capture rights are different between the two positions, even if all places are otherwise in the same position. The same goes for castling rights. To take this into account, it is necessary to fold the rights into the position signature. Sjaak currently does not do this, possibly leading to incorrect draw claims. This will not generally be a problem and it's a lot of hassle to code these things in for the general case. So it's omitted. For the en-passant square, we'd have to know the type of enemy piece that could capture en-passant, which in general we don't. Q&A --- Q: What is Sjaak? A: Sjaak is a general framework for playing chess-like games. It is basically a chess program that can play chess as well as a number of chess variants. Q: How do you pronounce that? A: Similar to French "Jacques". I chose the name because it sounds somewhat similar to what the common root of chess ("schaak" in Dutch), Chaturanga, Shatranj or Xiang-qi might sounds like. Appropriate for a program that plays general chess-like games. Q: Is Sjaak based on other programs? A: The move generator, evaluation function and search function were written from scratch, but the overall structure of the program and the data structures it uses are very heavily based on those I use in my normal chess program, Jazz. In fact, the code is so similar that I later copied parts of the code from Jazz to Sjaak with little or no modification, and so the programs are very similar in many ways. I would say they are siblings. The move structure in Sjaak is inspired by a description of the move structure of ChessV on TalkChess. Smooth scaling of the evaluation when approaching a 50-move draw as well as not using the score from the transposition table in that case is an idea picked up from Crafty. Q: Why doesn't Sjaak play atomic/losers/giveaway chess? A: A number of reasons. First of all there's another unrelated program by the same name that plays thsoe variants (and I even looked to see whether the name was used before I chose it). Having two programs with the same name playing the same game would be a bit confusing. Second of all, I'm really not that interested in these variants. Third, I'd need to make some modifications to the move generator and the way moves are stored, which I'm not inclined to do in light of the second point above. SjaakII/variants.txt000644 000765 000024 00000241726 13014616475 015502 0ustar00eglebbkstaff000000 000000 # Sjaak, a program for playing chess variants # Copyright (C) 2011, 2014 Evert Glebbeek # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # This is a variant description file for Sjaak. # It can be used to configure Sjaak to play a chess variant by specifying the movement mode of the pieces. # Empty lines and anything following a # character are ignored. # To define a variant, begin a line with "Variant: ", then follow on subsequent lines with descriptions # of the board, the pieces used and their movement, and other rules used in the variant. # There are a number of examples below, but first we'll go into some background. # THE BOARD AND ZONES # # The board size is specified by (files)x(ranks), for instance # Board: 8x8 # defines a standard chess board. There are some limits on the size of a board: the number of files # and ranks needs to be smaller than 16 and the total size of the board needs to be less than 128 # squares. # For some of the rules, it's necessary to identify a part of the board. This is done by defining a "zone", # which lists the squares on the board that are part of that zone. # For instance: # Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 # Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 # defines the 8th rank (rank8) of the board. # Files are identified by letters, "a" being the first. Ranks are labelled by numbers, starting at "1". # The square "a1" is in the lower-left hand side of the board. # Two special zones, "empty" and "all", are pre-defined. As the names indicate, "empty" corresponds to an # empty board while "all" corresponds to the entire board. # # It's possible to exclude certain regions of the board. Moves are never generated from or to these squares: # Exclude: g1,h1,g2,h2,g3,h3,g4,h4,g5,h5,g6,h6,a7,b7,c7,d7,e7,f7,g7,h7,a8,b8,c8,d8,e8,f8,g8,h8 # excludes the last two rows and the rightmost files on the board, effectively reducing the board to 6x6. # # For some games, the victory (or drawing!) condition can be to occupy a specific region of the board. # This is referred to here as "capture the flag". To enable this, a special zone has to be defined for each # player, WhiteFlag for white and BlackFlag for black: # WhiteFlag: e1 # BlackFlag: e8 # The game ends when white captures and holds all black flags and vice versa. # PIECES # # Piece descriptions start with a "Piece:" line. On following lines the piece move and special properties can # be defined. # MOVEMENT TYPES # # In order to define new variants, it is important to know how to specify the movement of the pieces. # Sjaak distinguishes three types of moves: # # Sliders - move along a ray direction until they encounter another piece or the edge of the board # Leapers - perform single steps to specified target squares # Steppers - perform single (repeated) steps in a particular board direction # # Each of these moves treats the board in a slightly different way, and so the description of moves for each # is different: # # Sliders move along a ray, either horizontal (H), vertical (V), diagonal (D) or anti-diaginal (A). # Examples: # slide (H,V) - describes a chess rook # slide (A,D) - describes a bishop # slide (H,V,A,D) - describes a queen # # Leaper moves can become complicated, but in their simplest form they just specify the step-vector, which is # then mirrored to give a total of up to 8 target squares. # Examples: # leap (2,1) - describes a knight # leap (1,2) - alternative description of a knight # leap (1,1) - describes a ferz # leap (2,2) - an elephant # leap (1,0) - a wazir # leap (2,0) - a dabbabah # The complications begin when a piece is a compound leaper, meaning it can choose between different leaper # moves. For instance, a king can move as a ferz, or as a wazir: # leap (1,0)|(1,1) - a king # However, a leaper can also be a two-step leaper, which first moves as one type of leaper and then again as # another type of leaper: # leap (1,0)+(1,1) - move as a wazir first and then as a ferz # Typically, this is useful to define lame leapers by also specifying a mask: # leap ((1,0)+(1,1)) & (1,2) # describes a piece that moves first as a wazir (only to empty squares) and then again as a ferz. However, # the final destination must be a square that could have been reached by a knight. In other words, this # describes a piece that moves like a knight, but can be blocked. A Xiangqi horse. # # For steppers, board directions are indicated by compass points, so "north" (the side of the board where # black starts out), "south" (where white starts), "east" (the king-side, the direction of the H-file) and # "west" (towards the A-file). # Steppers are intended to be asymmetric (the move description is for white and mirrored for black), # step N - describes a piece that moves one square "north", a white pawn # step S - describes a piece that moves one square "south", a black pawn # Optionally, the direction can be prefixed with a number (1-7): # step 2N - the initial double step of a white pawn. # Steppers are normally used to define pawns, and a number of pawn-specific evaluation terms are only # activated for pieces that are defined as stepper moves. Steppers also have their moves generated in bulk, # one direction at a time (as opposed to one piece at a time), which impacts performance. # # Finally, there are two variations to the three above movement types: hoppers and asymmetric leapers. # # Hoppers are sliders that need to jump over another piece before they can move. # hop (H,V) - describes the capture move of a cannon in Xiangqi # Currently, capture is replacement only. It is not possible (yet) to define a piece that captures by leaping # over its victim. # # Asymmetric leapers are leapers that, as the name implies, move differently for white and black. # Their syntax is similar to that of normal leapers, but instead of specifying the movement vector, it's # necessary to specify each step: # aleap (0,1) - a piece that moves one square north for white and one square south for black (like a pawn) # aleap (1,1)|(1,-1)|(-1,-1)|(-1,1)|(0,1) - a Makruk elephant or silver general # aleap (1,1)|(-1,1)|(1,0)|(-1,0)|(0,1)|(0,-1) - a gold general # MOVES AND CAPTURES # # A piece can have more than one movement type defined for it, simply by specifying them one after the other # on separate lines: # # Move: slide (H,V,A,D) # Move: leap (1,2) # # defines the move for an Amazon. The move types must be different on each line, or the result will be # undefined. # If a capture move is not specified, it is assumed to be the same as the normal move. To disable captures for # a particular piece type, specify "none": # # Capture: none # Piece cannot capture # SPECIAL MOVES (DOUBLE PUSHES) # # For pieces that can perform special moves, it's necessary to specify both the move and the location on the # board where they can make the special move. This is done by defining a zone and then referencing it from the # piece description: # Special: rank2, rank7, step 2N # defines the initial double step for a white pawn (on rank 2) and a black pawn (on rank 7). # Special moves currently cannot be captures, and do not depend on whether the piece has moved or not. # INITIAL MOVES (DOUBLE PUSHES, ALTERNATIVE) # # Initial moves are moves that can only be made by a piece that has not moved yet. # Initial: step 2N # defines the initial double step for a white pawn (on rank 2) and a black pawn (on rank 7). # Initial moves currently cannot be captures, and must be "step" moves. # SPECIAL MOVES (CASTLING) # # Castling is a special move where two friendly pieces move. It is only possible if neither of the two pieces # has moved already. All squares between the initial and final positions of the two pieces must be empty # (apart from the two pieces themselves). # If the primary castling piece is royal, then all squares between the king's initial and final position must # be "safe". # To define a castling move, specify the (default) starting position of the king, the king's destination # square and the secondary's (default) starting position. The secondary's destination square is derived from # the king's move: if the king moves to the king-side (east side) of the board, the secondary moves to the # left of the king. If the king moves to the queen-side (west side) of the board the secondary moves to the # right of the king. # It is possible to specify multiple destination squares for the king in a comma-separated list. This defines # a flexible form of castling where the king is allowed to move to any of the given destination squares. # Example: # # Castle: white e1-g1 with h1 # Canonical king-side castling # Castle: white e1-c1 with a1 # Canonical queen-side castling # Castle: white e1-c1,b1 with a1 # Queen-side castling, either 2-step or 3-step # # To define FRC-style castling, simply define the "standard" castling rule (which provide the destination # square for the king and the default location of the rook). The FRC rule will be based on this # and the actual specified position of the king and rook in the startup FEN. # Behaviour is undefined if all squares are not on one rank. # RESTRICTING MOVEMENT # # It is possible to restrict the movement options of a piece by specifying the locations it is allowed to move # to: # Zone: white_palace = d1,e1,f1, d2,e2,f2, d3,e3,f3 # Zone: black_palace = d8,e8,f8, d9,e9,f9, d10,e10,f10 # # Piece: King # Move: leap (0,1) # Symbol: "K", "K,k" # Flags: royal # Prison: white_palace, black_palace # Defines the XiangQi king, which is only allowed to move inside the palace. # # Alternatively, it is possible to mark squares as unpassable by particular pieces: # Zone: white_centre = e4,d4 # Zone: black_centre = e5,d5 # # Piece: Rook # Move: slide (H, V) # Symbol: "R", "R,r" # Block: white_centre, black_centre # defines a rook that cannot move to the centre (e4/d4 for white, e5/d5 for black). # DROPS # # For variants that include drops, the default rule is that pieces in-hand may be dropped anywhere on the # board, as long as pieces are allowed to move from their new location. You can override this: # Drop zone: all, all # allows pieces to be dropped everywhere, while # Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 # # Drop zone: rank1, rank8 # restricts drops to the (own) back rank. # Specifying the drop zone in this way implicitly sets the "drop_dead" piece property (see below). # PROMOTIONS # # Pieces promote as soon as they reach their promotion zone, to one of the possible promotion choices: # Promotion: rank8, rank1, "QRBN" # defines the promotion for a white pawn on rank 8 and for a black pawn on rank 1. # A promotion choice of "+" is treated in a special way: if the piece # symbol is "P" and it has promotion choice "+", then the piece will be # promoted to a piece with symbol "+P". It is your responsibility to make # sure that this piece is defined in the game! # If capture/drop rules are used, pieces that have a symbol that starts # with "+" will be demoted when captured. # Example: # Piece: Queen # Move: slide (D,A,H,V) # Symbol: "+P", "+P,+p" # # Piece: Pawn # Move: step N # Capture: step NE,NW # Special: rank2, rank7, step 2N # Symbol: " ", "P,p" # Flags: set_ep,take_ep # Promotion: rank8, rank1, "+" # # This example defines a Queen that will demote to Pawn when captured, and # a Pawn whose only promotion choice is the Queen (+P). # By default, promotion is mandatory if the piece has no legal moves from a given square, otherwise it is # optional. You can override this rule by specifying the optional promotion zone explicitly: # Optional promotion: empty, empty # makes all promotions for this piece type mandatory. # PIECE PROPERTIES # # Pieces can have a number of special properties, listed on a "Flags:" line. Multiple properties can be # specified in one line, separated by a comma. The following properties can be specified: # # royal This is a royal piece: the game is lost if a side were to lose all royal pieces. # set_ep This piece sets the "en-passant" capture square when it makes a special move. # take_ep This piece type can capture on the "en-passant" square. # drop_no_check This piece may not be dropped to give check. # drop_no_mate This piece may not be dropped to give mate (it may give check). # drop_one_file This piece may not be dropped on a file that already contains a friendly piece of the same type. # drop_dead This piece may be dropped even where it cannot move. # no_mate This piece may not deliver mate. It may deliver check. # shak This piece delivers a special "shak" check. # assimilate Pieces that capture this piece promote to this piece. # no_retaliate After capturing a piece with this flag, your own pieces with the flag may not becaptured the next turn. # endangered This piece may not capture another endangered piece if that piece is protected. # iron This piece may not be captured at all. # capture_flag This piece may "capture the flag". By default, all pieces may do this. # LIMIT THE NUMBER OF PIECES # # It is possible to limit the number of pieces of a particular type (by promotion) by specifying that number # in a "Max: number" or "Max: white,black" line: # Max: 2 # allows only two of this piece type for white and for black. You can set a different number for white and for # black. Pass 0 to leave this at the default (whch is 1 for royal pieces and unlimited for all other piece # types). # EXTRA RULES AND VICTORY CONDITIONS # # It's possible to specify a number of extra rule options or game-ending conditions. # End-of-game scores can be any of "win", "loss", "draw" or "illegal". # The following game-ending conditions can be defined: # # checkmate The enemy royal piece is under attack and cannot avoid being captured (win, loss or draw, normally win) # stalemate The enemy cannot make any legal moves (win, loss or draw, normally draw) # repeatN The game is declared a draw if a position occurs for the Nth time (win, loss or draw, normally draw and N = 3) # Set N=0 to disable any special treatment for repetitions. # perpetual As repeatN, but refers to in-check positions. If not specified defaults to "repeat" (win, loss, draw or repeat). # loneking Triggered if a side has a lone king remaining and baring rules are in effect (win, loss or draw, normally loss) # nopieces Triggered if the side-to-move has no pieces left (win, loss or draw, normally loss). # captureanyflag Triggered when capturing any flag (win, loss or draw, normally win). # captureallflags Triggered when capturing all flags (win, loss or draw, normally win). # movecountdraw After the specified number of reversible moves, the game is declared a draw. Set to 0 to disable. # # Examples (these are the defaults): # Rule: checkmate = win # Rule: stalemate = draw # Rule: repeat3 = draw # Rule: movecountdraw = 50 # # The following special rules can be defined: # # taboo The kings may not share a ray with no pieces between them # keep capture Captured pieces are held in your hand (rather than just disappearing) # return capture Captured pieces are returned to their owner's hand # forced capture You must make a capture if you can # duplecheck If a side has more than one king, he is assumed to be in check if all of them are attacked. # check any king If there are multiple kings, attacking any one of them counts as check. # Without either of the last two rules a side with more than one king can never be in check. # allow pickup As a move you can take one of your non-royal pieces from the board into your hand. # allow drops As a move you may place a piece from your hand on the board. # force drops If you have a piece in-hand you must place one on the board. # gate drops You may "gate" pieces from your hand when first moving a back-rank piece. # promote here A piece that is in the promomotion zone may promote without making a move. # promote drop A piece that is dropped in the promomotion zone may promote immediately. # special init Special moves are only allowed if the piece has not moved before. # bare rule If you have a bare king, you have one turn to bare your opponent's king in turn. # chase rule Xiangqi-style chases are not allowed. # shak rule Checkmate can only be given if a chain of checks leading to it contains at least one "shak" check. # en-passant check Sliding royals can be checked "en-passant", that is, they cannot slide through check. # quiet promotion Promotion moves must be "quiet" moves. # promote by move A piece in the promotion zone can promote and move immediately as the new piece. # PIECE NAMES AND SYMBOLS # # Pieces can be assigned names and symbols. # The piece name property is given on the "Piece:" line. It is an arbitrary # string with no special meaning; SjaakII uses this when asked to list the # rules and dump the internal board representation, but it is otherwise # purely cosmetic. # The piece symbol is set using the "Symbol:" property: # Symbol: "N", "N,n" # The first string ("N") is used for the output of moves in standard # algebraic notation (SAN), for instance "Nf3". The second string ("N,n") # identifies the symbol by which the piece is identified in a FEN string, # first for white and then for black. Normally, the black character is just # the lower case version of the white character, which is in upper case, # but Sjaak doesn't require this. The SAN character is normally the same as # the white symbol, but again this is not required. # There are two special symbols that have meaning when defining piece # symbols: # If a piece symbol starts with "+", say "+P", then this piece will demote to # "P" when captured, and a piece "P" will promote to this piece (if it has # promotion choice "+"). # If a piece symbol ends with "~", say "Q~", then this piece will demote to # whichever piece type promotes to "Q~". Make sure that there is a piece # type that lists "Q~" as a promotion option! # PIECE VISUALISATION (XBOARD/WINBOARD) # # You can specify the piece graphic that is used by XBoard/WinBoard to represent the pieces in your variant. # XBoard (4.8) uses a string of 44 (2x22) or more (4.9+) characters in length. To use the N-th image you # simply have to replace the N-th character in the string with the symbol you assigned to the piece. The first # set of characters are for white, the last set are for black. The last character listed is always the King. # The default value of the string is "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk", where the characters # represent the following pieces: # 1 P=Pawn # 2 N=Knight # 3 B=Bishop # 4 R=Rook # 5 Q=Queen # 6 F=Ferz # 7 E=Alfil # 8 A=Archbishop # 9 C=Chancellor # 10 W=Wazir # 11 M=Man # 12 O=Cannon # 13 H=Nightrider # 14 I=Dragon Horse # 15 J=Dragon King # 16 G=Grasshopper # 17 D=Alternative Chancellor image # 18 V=Falcon # 19 L=Lance (or Amazon/Berolina pawn) # 20 S=Snake # 21 U=Unicorn # For XBoard 4.9 and later: # 22 L!=Lion # 23 W!=Wolf # 24 C'=Camel # 25 Z'=Zebra # 26 Z=Wizard # 27 L'=Amazon (Lance duplicat without Pawn character) # 28 minor Lion (Lion duplicat without trading restriction) # # After this follows the "promoted sequence", which is paired against the normal series: # p1 +P=Tokin (turban without ornament) # p2 +N=Claw # p3 +B=flat mitre without cross # p4 +R=roofed Rook without battlements # p5 +Q=turban with circle ornament (+L in Shogi) # p6 +F=Sword without stripe # p7 +E=alternate King (not visually different and still royal) # p8 +A=alternate Queen (not visually different) # p9 +C=alternate Lion (not visually different, still untradable) # p10 +W=Rook without battlements # p11 +M=lying sword without stripe # p12 +O=Dolphin # p13 +H=Sword # p14 +I=Leopard # p15 +J=lying sword # p16 +G=turban with star ornament # p17 +D=narrow Queen crown # p18 +V=lying narrow Queen crown # p19 +L=Knight (not visually different) # p20 +S=Elephant (not visually different) # p21 +U=Bishop without cross # p22 +L!=Gnu # p23 +W!=Viking helmet # p24 +C'=Iron General (simple helmet) # p25 +Z'=Champion (hoplit helmet) # p26 +Z=Tower # p27 +L'=Flying Dragon # And finally at the end, # xxx K=King # # If you do not use a particular bitmap, simply put "." in its place. Example: # XBoard pieces: "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk" # When specifying the string, you can write both "XBoard" and "WinBoard". ############################################################# # Example: duplicate the definition for standard FIDE chess # ############################################################# Variant: FIDE Chess (8x8) # Name with which the variant can be selected Board: 8x8 # Board size FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" # Startup FEN, pieces defined below XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 # Special regions on the board, used below Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Initial: step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ############################################################################################ # Example: wolves and Sheep. The wolves try to corner the sheep, the sheep tries to escape # ############################################################################################ Variant: Wolves and Sheep (8x8) Board: 8x8 FEN: "1w1w1w1w/8/8/8/8/8/8/4S3 w - -" XBoard pieces: ".....W...S.................w...s............" BlackFlag: a8,b8,c8,d8,e8,f8,g8,h8 Piece: Sheep Move: leap (1,1) Capture: none Symbol: "S", "S,s" Piece: Wolf Move: aleap (1,1)|(-1,1) Capture: none Symbol: "W", "W,w" Rule: captureanyflag = win Rule: stalemate = win ########################### # Maharaja and the Sepoys # ########################### Variant: Maharaja Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/8/4M3/ w KQkq -" XBoard pieces: ".........M............pnbrq................k" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 32 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 32 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 50 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 95 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Maharaja Move: slide (D,A,H,V) Move: leap (2,1) Symbol: "M", "M,m" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 10 ###################################################### # Peasant Revolt (Pritchard 2007, chessvariants.org) # ###################################################### Variant: Peasant's Revolt (8x8) Board: 8x8 FEN: "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 250 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Promotion: rank8, rank1, "QRBN" Value: 100 ################################### # Corridor Chess (Pritchard 2007) # ################################### Variant: Corridor Chess (8x8) Board: 8x8 FEN: "1nrqkrn1/2b2b2/1pppppp1/8/8/1PPPPPP1/2B2B2/1NRQKRN1 w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Promotion: rank8, rank1, "QRBN" Value: 100 ############################################################ # Legan's chess (http://en.wikipedia.org/wiki/Legan_chess) # ############################################################ Variant: Legan's Chess (8x8) Board: 8x8 FEN: "knbrp3/bqpp4/npp5/rp1p3P/p3P1PR/5PPN/4PPQB/3PRBNK w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promo = a5,a6,a7,a8,b8,c8,d8 Zone: black_promo = e1,f1,g1,h1,h2,h3,h4 # Define the pieces Piece: Knight # New piece and its name Move: leap (2,1) # Movement type Symbol: "N", "N,n" # Symbol used for SAN and symbols used in FEN Value: 320 # Piece value, used by evaluation Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step NW Capture: step N,W Symbol: " ", "P,p" Promotion: white_promo, black_promo, "QRBN" Value: 100 ################################## # Diamond Chess (Pritchard 2007) # ################################## Variant: Diamond Chess (8x8) Board: 8x8 FEN: "krbp4/rqnp4/nbpp4/pppp4/4PPPP/4PPBN/4PNQR/4PBRK w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promo = a1,a2,a3,a4,a5,a6,a7,a8,b8,c8,d8,e8,f8,g8,h8 Zone: black_promo = a1,b1,c1,d1,e1,f1,g1,h1,h2,h3,h4,h5,h6,h7,h8 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step NW Capture: step N,W Symbol: " ", "P,p" Promotion: white_promo, black_promo, "QRBN" Value: 100 ################################### # Diagonal Chess (Pritchard 2007) # ################################### Variant: Diagonal Chess (8x8) Board: 8x8 FEN: "4pnrk/4pbpr/4pbqn/4pppp/PPPP4/NQBP4/RPBP4/KRNP4 w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promo = h1,h2,h3,h4,h5,h6,h7,a8,b8,c8,d8,e8,f8,g8,h8 Zone: black_promo = a1,b1,c1,d1,e1,f1,g1,h1,a2,a3,a4,a5,a6,a7,a8 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step NE Capture: step N,E Symbol: " ", "P,p" Promotion: white_promo, black_promo, "QRBN" Value: 100 ################################# # Corner Chess (Pritchard 2007) # ################################# Variant: Corner Chess (8x8) Board: 8x8 FEN: "kbp2pbq/nrp2prn/pp4pp/8/8/PP4PP/NRP2PRN/QBP2PBK w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promo = a8,b8,c8,d8,e8,f8,g8,h8 Zone: black_promo = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step NE,NW Capture: step N Symbol: " ", "P,p" Promotion: white_promo, black_promo, "QRBN" Value: 100 ############################################################################ # A variant of Shatranj with dabbabah's, supposedly from 9th century India # ############################################################################ Variant: Ninth Century Indian Chess (8x8) Board: 8x8 FEN: "dnrkfrnd/pppppppp/8/8/8/8/PPPPPPPP/DNRKFRND w - -" XBoard pieces: "PN.R.F........D......Kpn.r.f........d......k" # Use the "crowned rook" for the dabbabah Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Dabbabah Move: leap (2,0) Symbol: "D", "D,d" Value: 200 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Ferz Move: leap (1,1) Symbol: "F", "F,f" Value: 150 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Promotion: rank8, rank1, "F" Value: 100 Rule: stalemate = win # The stale-mated player wins (!) Rule: loneking = loss ########################################################################### # Troitzky chess # # http://www.chessvariants.org/index/msdisplay.php?itemid=MPtroitzkychess # ########################################################################### Variant: Troitzky Chess (10x10) Board: 10x10 FEN: "****qk****/**rnbbnr**/*pppppppp*/*8*/10/10/*8*/*PPPPPPPP*/**RNBBNR**/****QK**** w - -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promotion = a6,b8,c9,d9,e10,f10,g9,h9,i8,j6 Zone: black_promotion = a5,b3,c2,d2,e1,f1,g2,h2,i3,j5 Zone: rank8 = b8,c8,d8,e8,f8,g8,h8,i8 Zone: rank3 = b3,c3,d3,e3,f3,g3,h3,i3 Exclude: a1,b1,c1,d1,g1,h1,i1,j1,a2,b2,i2,j2,a3,j3,a4,j4 Exclude: a10,b10,c10,d10,g10,h10,i10,j10,a9,b9,i9,j9,a8,j8,a7,j7 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f2-e1 with e1 Castle: white f2-f1 with f1 Castle: black f9-e10 with e10 Castle: black f9-f10 with f10 Piece: Pawn Move: step N Capture: step NE,NW Special: rank3, rank8, step 2N Symbol: " ", "P,p" Promotion: white_promotion, black_promotion, "QRBN" Value: 100 ########################################################################### # Troitzky chess, Byway setup # # http://www.chessvariants.org/index/msdisplay.php?itemid=MPtroitzkychess # ########################################################################### Variant: Troitzky Chess Byway (10x10) Board: 10x10 FEN: "****rr****/**nbqkbn**/*pppppppp*/*8*/10/10/*8*/*PPPPPPPP*/**NBQKBN**/****RR**** w KQkq -" XBoard pieces: "PNBRQKpnbrqk" Zone: white_promotion = a6,b8,c9,d9,e10,f10,g9,h9,i8,j6 Zone: black_promotion = a5,b3,c2,d2,e1,f1,g2,h2,i3,j5 Zone: rank8 = b8,c8,d8,e8,f8,g8,h8,i8 Zone: rank3 = b3,c3,d3,e3,f3,g3,h3,i3 Exclude: a1,b1,c1,d1,g1,h1,i1,j1,a2,b2,i2,j2,a3,j3,a4,j4 Exclude: a10,b10,c10,d10,g10,h10,i10,j10,a9,b9,i9,j9,a8,j8,a7,j7 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f2-e1 with e1 Castle: white f2-f1 with f1 Castle: black f9-e10 with e10 Castle: black f9-f10 with f10 Piece: Pawn Move: step N Capture: step NE,NW Special: rank3, rank8, step 2N Symbol: " ", "P,p" Promotion: white_promotion, black_promotion, "QRBN" Value: 100 ########################################## # Twilight Chess, with drops and pickups # ########################################## Variant: Twilight Chess (8x8) Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Rule: allow pickup, allow drops ################## # Judkins' Shogi # ################## Variant: Judkins' Shogi (6x6) Board: 6x6 FEN: "rbnsgk/5p/6/6/P5/KGSNBR" XBoard pieces: "PNBR.S...G.++++.+Kpnbr.s...g.++++.+k" XBoard parent: "shogi" Zone: white_promotion = a5,b5,c5,d5,e5,f5,a6,b6,c6,d6,e6,f6 Zone: black_promotion = a2,b2,c2,d2,e2,f2,a1,b1,c1,d1,e1,f1 # Define the pieces Piece: Knight Move: aleap (1,2)|(-1,2) Symbol: "N", "N,n" Promotion: white_promotion, black_promotion, "+" Value: 250 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Promotion: white_promotion, black_promotion, "+" Value: 575 Piece: Silver general Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Symbol: "S", "S,s" Promotion: white_promotion, black_promotion, "+" Value: 375 Piece: Gold general Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "G", "G,g" Value: 450 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Promotion: white_promotion, black_promotion, "+" Value: 650 Piece: Pawn Move: step N Symbol: "P", "P,p" Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: Promoted Knight Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+N", "+N,+n" Value: 500 Piece: Dragon Horse Move: slide (D,A) Move: leap (1, 0) Symbol: "+B", "+B,+b" Value: 825 Piece: Promoted Silver Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+S", "+S,+s" Value: 490 Piece: Dragon King Move: slide (H,V) Move: leap (1, 1) Symbol: "+R", "+R,+r" Value: 950 Piece: Promoted Pawn Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+P", "+P,+p" Value: 530 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Rule: keep capture, allow drops Rule: repeat4 = draw Rule: perpetual = illegal ################################################################### # Omicron chess: omega chess on a smaller board (2 ranks deleted) # ################################################################### Variant: Omicron Chess (12x10) Board: 12x10 FEN: "w**********w/*crnbqkbnrc*/*pppppppppp*/*10*/*10*/*10*/*10*/*PPPPPPPPPP*/*CRNBQKBNRC*/W**********W w KQkq -" XBoard pieces: "PNBRQ..C.W...........Kpnbrq..c.w...........k" Exclude: b1,c1,d1,e1,f1,g1,h1,i1,j1,k1 Exclude: b10,c10,d10,e10,f10,g10,h10,i10,j10,k10 Exclude: a2,a3,a4,a5,a6,a7,a8,a9 Exclude: l2,l3,l4,l5,l6,l7,l8,l9 Zone: white_promotion = b9,c9,d9,e9,f9,g9,h9,i9,j9,k9; Zone: black_promotion = b2,c2,d2,e2,f2,g2,h2,i2,j2,k2; Zone: rank3 = b3,c3,d3,e3,f3,g3,h3,i3,j3,k3; Zone: rank8 = b8,c8,d8,e8,f8,g8,h8,i8,j8,k8; # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 250 Piece: Champion Move: leap (0,1)|(0,2)|(2,2) Symbol: "C", "C,c" Value: 375 Piece: Wizard Move: leap (1,1)|(1,3) Symbol: "W", "W,w" Value: 350 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 400 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 600 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white g2-i2 with j2 Castle: white g2-e2 with c2 Castle: black g9-i9 with h9 Castle: black g9-e9 with j9 Piece: Pawn Move: step N Capture: step NE,NW Special: rank3, rank8, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: white_promotion, black_promotion, "QRBNWC" Value: 100 ############# # EuroShogi # ############# Variant: EuroShogi (8x8) Board: 8x8 FEN: "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1[-] w 0 1" XBoard pieces: "PNBR.....G.++++Kpnbr.....g.++++k" XBoard parent: "shogi" Zone: white_promotion = a8,b8,c8,d8,e8,f8,g8,h8,a7,b7,c7,d7,e7,f7,g7,h7,a6,b6,c6,d6,e6,f6,g6,h6 Zone: black_promotion = a3,b3,c3,d3,e3,f3,g3,h3,a2,b2,c2,d2,e2,f2,g2,h2,a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: aleap (1,2)|(-1,2)|(1,0)|(-1,0) Symbol: "N", "N,n" Promotion: white_promotion, black_promotion, "+" Optional promotion: empty, empty Value: 250 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Promotion: white_promotion, black_promotion, "+" Optional promotion: empty, empty Value: 575 Piece: Gold general Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "G", "G,g" Value: 450 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Promotion: white_promotion, black_promotion, "+" Optional promotion: empty, empty Value: 650 Piece: Pawn Move: step N Symbol: "P", "P,p" Promotion: white_promotion, black_promotion, "+" Optional promotion: empty, empty Flags: drop_no_mate, drop_one_file Value: 80 Piece: Promoted Knight Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+N", "+N,+n" Value: 500 Piece: Dragon Horse Move: slide (D,A) Move: leap (1, 0) Symbol: "+B", "+B,+b" Value: 825 Piece: Dragon King Move: slide (H,V) Move: leap (1, 1) Symbol: "+R", "+R,+r" Value: 950 Piece: Promoted Pawn Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+P", "+P,+p" Value: 530 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Rule: keep capture, allow drops Rule: repeat4 = draw ############# # YariShogi # ############# Variant: YariShogi (7x9) Board: 7x9 FEN: "rnnkbbr/7/ppppppp/7/7/7/PPPPPPP/7/RBBKNNR[-] w 0 1" XBoard pieces: "PNBR.......++++Kpnbr.......++++k" XBoard parent: "shogi" Zone: white_promotion = a9,b9,c9,d9,e9,f9,g9,a8,b8,c8,d8,e8,f8,g8,a7,b7,c7,d7,e7,f7,g7 Zone: black_promotion = a3,b3,c3,d3,e3,f3,g3,a2,b2,c2,d2,e2,f2,g2,a1,b1,c1,d1,e1,f1,g1 # Define the pieces Piece: YariKnight Move: aleap (1,2)|(-1,2) Move: step 9N Symbol: "N", "N,n" Promotion: white_promotion, black_promotion, "+" Value: 300 Piece: YariBishop Move: aleap (1,1)|(-1,1) Move: step 9N Symbol: "B", "B,b" Promotion: white_promotion, black_promotion, "+" Value: 400 Piece: YariRook Move: slide (H) Move: step 9N Symbol: "R", "R,r" Promotion: white_promotion, black_promotion, "+" Value: 550 Piece: Pawn Move: step N Symbol: "P", "P,p" Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: YariSilver Move: aleap (0,1)|(1,1)|(-1,1) Move: step 9S Symbol: "+P", "+P,+p" Value: 375 Piece: YariGoldKnight Move: aleap (0,1)|(1,1)|(-1,1)|(0,1)|(0,-1) Move: step 9S Symbol: "+N", "+N,+n" Value: 425 Piece: YariGoldBishop Move: aleap (0,1)|(1,1)|(-1,1)|(0,1)|(0,-1) Move: step 9S Symbol: "+B", "+B,+b" Value: 425 Piece: Rook Move: slide (H,V) Symbol: "+R", "+R,+r" Value: 650 Piece: General Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Rule: keep capture, allow drops Rule: repeat4 = draw Rule: perpetual = loss ################# # Goro Goro Shogi # ################# Variant: GoroGoro Shogi (5x6) Board: 5x6 FEN: "sgkgs/5/1ppp1/1PPP1/5/SGKGS[-] w 0 1" XBoard pieces: "P....S...G.+....+Kp....s...g.+....+k" XBoard parent: "shogi" Zone: white_promotion = a5,b5,c5,d5,e5,a6,b6,c6,d6,e6 Zone: black_promotion = a2,b2,c2,d2,e2,a1,b1,c1,d1,e1 # Define the pieces Piece: Silver general Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Symbol: "S", "S,s" Promotion: white_promotion, black_promotion, "+" Value: 375 Piece: Gold general Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "G", "G,g" Value: 450 Piece: Pawn Move: step N Symbol: "P", "P,p" Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: Promoted Silver Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+S", "+S,+s" Value: 490 Piece: Promoted Pawn Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+P", "+P,+p" Value: 530 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Rule: keep capture, allow drops Rule: repeat4 = draw Rule: perpetual = loss ####################################### # Goro Goro Shogi Knight & Lance Drop # ####################################### Variant: GoroGoroNL Shogi (5x6) Board: 5x6 FEN: "sgkgs/5/1ppp1/1PPP1/5/SGKGS[NLnl] w 0 1" XBoard pieces: "PN..LS...G.++..++Kpn..ls...g.++..++k" XBoard parent: "shogi" Zone: white_promotion = a5,b5,c5,d5,e5,a6,b6,c6,d6,e6 Zone: black_promotion = a2,b2,c2,d2,e2,a1,b1,c1,d1,e1 # Define the pieces Piece: Silver general Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Symbol: "S", "S,s" Promotion: white_promotion, black_promotion, "+" Value: 375 Piece: Gold general Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "G", "G,g" Value: 450 Piece: Pawn Move: step N Symbol: "P", "P,p" Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: Knight Move: aleap (1,2)|(-1,2) Symbol: "N", "N,n" Promotion: white_promotion, black_promotion, "+" Value: 250 Piece: Lance Move: step 5N Symbol: "L", "L,l" Promotion: white_promotion, black_promotion, "+" Value: 250 Piece: Promoted Silver Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+S", "+S,+s" Value: 490 Piece: Promoted Pawn Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+P", "+P,+p" Value: 530 Piece: Promoted Knight Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+N", "+N,+n" Value: 500 Piece: Promoted Lance Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Symbol: "+L", "+L,+l" Value: 480 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Rule: keep capture, allow drops Rule: repeat4 = draw Rule: perpetual = loss ############################### # Ouk Chatrang, per Pritchard # ############################### Variant: Cambodian (Ouk Chatrang) Board: 8x8 FEN: "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w DEde - 0 1" XBoard pieces: "PN.R.M....SKpn.r.m....sk" XBoard parent: "makruk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank6 = a6,b6,c6,d6,e6,f6,g6,h6 Zone: rank3 = a3,b3,c3,d3,e3,f3,g3,h3 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 325 Piece: Silver general Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Symbol: "S", "S,s" Value: 275 Piece: Met Move: leap (1,1) Special: rank1, rank8, aleap (-1,1)|(1,1) | (0,2) Symbol: "M", "M,m" Value: 150 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: King Move: leap (0,1)|(1,1) Special: rank1, rank8, aleap (-1,1)|(0,1)|(1,1)|(-1,0)|(1,0) | (-2,1)|(2,) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Promotion: rank6, rank3, "M" Optional promotion: empty, empty Value: 80 Rule: special init Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ########################################################## # Mini XiangQi, http://mlwi.magix.net/bg/minixiangqi.htm # ########################################################## Variant: Mini Xiangqi Board: 7x7 FEN: "rchkhcr/p1ppp1p/7/7/7/P1PPP1P/RCHKHCR w 0 1" XBoard pieces: "PH.R.....K.C.ph.r.....k.c." XBoard parent: "xiangqi" Zone: palace = c1,d1,e1,c2,d2,e2,c3,d3,e3,c5,d5,e5,c6,d6,e6,c7,d7,e7 Piece: Knight Move: leap (1,0)+(1,1)&(2,1) Symbol: "H", "H,h" Value: 400 Piece: Cannon Move: slide (H,V) Capture: hop (H,V) Symbol: "C", "C,c" Value: 450 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 950 Piece: King Move: leap (0,1) Prison: palace, palace Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: aleap (1,0)|(0,1)|(-1,0) Symbol: "P", "P,p" Value: 200 Rule: taboo, chase rule Rule: checkmate = win Rule: stalemate = win Rule: repeat3 = draw ##################################################################################### # Elven chess, http://www.chessvariants.org/index/msdisplay.php?itemid=MSelvenchess # # This is an XBoard standard variant under the name "elven", so "XBoard pieces" and # # "XBoard parent" are not needed. # ##################################################################################### Variant: Elven (Chu) Chess Board: 10x10 FEN: "r3k4r/mnb+rql+bbnm/pppppppppp/10/10/10/10/PPPPPPPPPP/MNB+BLQ+RBNM/R4K3R w KQkq - 0 1" #XBoard pieces: "PNBRQ.....M.+++......LKpnbrq.....m.+++......lk" #XBoard parent: elven Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8,i8,j8 Zone: rank3 = a3,b3,c3,d3,e3,f3,g3,h3,i3,j3 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 300 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 350 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-i1 with j1 Castle: white f1-c1 with a1 Castle: black f10-i10 with j10 Castle: black f10-c10 with a10 Piece: Pawn Move: step N Capture: step NE,NW Special: rank3, rank8, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank3, "QRBN" Value: 100 Piece: Dwarf Move: leap (0,1)|(1,1) Symbol: "M", "M,m" Value: 300 Piece: Elf Move: slide (D,A) Move: leap (0,1) Symbol: "+B", "+B,+b" Value: 525 Piece: Goblin Move: slide (H,V) Move: leap (1,1) Symbol: "+R", "+R,+r" Value: 725 Piece: Warlock Move: leap ((0,1)|(1,1))+((0,1)|(1,1)) Flags: no_retaliate,endangered Symbol: "L", "L,l" Value: 1500 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw #################### # Example: 3-check # #################### Variant: 3check (8x8) Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" XBoard pieces: "PNBRQKpnbrqk" XBoard parent: "3check" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw Rule: check3 = win #################### # Might Lion Chess # #################### Variant: Lion (8x8, Mighty Lion) Board: 8x8 FEN: "rlbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RLBQKBNR w KQkq - 0 1" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 # Special regions on the board, used below Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 300 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 350 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Piece: Lion Move: leap ((0,1)|(1,1))+((0,1)|(1,1)) Flags: no_retaliate,endangered Symbol: "L", "L,l" Value: 1500 #################### # Chancellor Chess # #################### Variant: Chancellor (8x8) Board: 8x8 FEN: "rnbckbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBCKBNR w KQkq -" XBoard pieces: "PNBR............CKpnbr............ck" XBoard parent: "fairy" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Chancellor Move: leap (2,1) Move: slide (H,V) Symbol: "C", "C,c" Value: 875 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "CRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ################ # Amazon Chess # ################ Variant: Amazon (8x8) Board: 8x8 FEN: "rnbakbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBAKBNR w KQkq -" XBoard pieces: "PNBR..............AKpnbr..............ak" XBoard parent: "fairy" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Amazon Move: leap (2,1) Move: slide (D,A,H,V) Symbol: "A", "A,a" Value: 1200 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "ARBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ################ # Musketeerish # ################ Variant: Musketeer Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[EAMCHDULeamchdul] w KGFDCBQkgfdcbq -" XBoard pieces: "PNBRQ.EAM..CLF.S.HD.UKpnbrqfeam..cl....hdsuk" FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[CLcl] w KGFDCBQkgfdcbq -" XBoard pieces: "PNBRQ......CL........Kpnbrq......cl........k" XBoard parent: "seirawan" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: Dragon Move: slide (D,A,H,V) Move: leap (2,1) Symbol: "D", "D,d" Value: 1200 Piece: Chancellor Move: slide (H,V) Move: leap (2,1) Symbol: "M", "M,m" Value: 875 Piece: Archbishop Move: slide (A,D) Move: leap (2,1) Symbol: "A", "A,a" Value: 825 Piece: Elephant Move: leap (2,0)|(2,2)|(1,1)|(1,0) Symbol: "E", "E,e" Value: 500 Piece: Hawk Move: leap (2,0)|(2,2)|(3,3)|(3,0) Symbol: "H", "H,h" Value: 425 Piece: Leopard Move: leap (2,1) Move: step 2NE,2NW,2SE,2SW Symbol: "L", "L,l" Value: 500 Piece: Cannon Move: aleap (2,0)|(-2,0)|(0,2)|(0,-2)|(1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(1,-1)|(-1,1)|(-1,-1)|(2,1)|(2,-1)|(-2,1)|(-2,-1) Symbol: "C", "C,c" Value: 500 Piece: Unicorn Move: leap (2,1)|(3,1) Symbol: "U", "U,u" Value: 425 Piece: Fortress Move: aleap (1,2)|(-1,2)|(1,-2)|(-1,-2)|(2,0)|(-2,0)|(0,2)|(0,-2) Move: step 3NE,3NW,3SE,3SW Symbol: "F", "F,f" Value: 700 Piece: Spider Move: leap (1,2)|(0,2) Move: step 2NE,2NW,2SE,2SW Symbol: "S", "S,s" Value: 800 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "CLQRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw Rule: gate drops ################################################# # Archer/Spanish chess. Supposedly from 1739 # # See http://www.quadibloc.com/chess/ch0502.htm # ################################################# Variant: Spanish archer (10x8) Board: 10x8 FEN: "rnbaqkabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBAQKABNR w KQkq -" XBoard pieces: "PNBRQ.........AKpnbrq.........ak" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8,i8,j8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7,i7,j7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,j2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Archer Move: step 9N,9SE,9SW Symbol: "A", "A,a" Value: 400 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-i1 with j1 Castle: white f1-c1 with a1 Castle: black f8-i8 with j8 Castle: black f8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QARBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ################################################################################### # Enlarged and Improved Chess # # http://www.quadibloc.com/chess/ch0502.htm # # http://www.chess10x10.com/10x10-chess-variants-Enlarged-and-Improved-Chess.html # ################################################################################### Variant: Enlarged and improved chess Board: 10x10 FEN: "rnbgqkgbnr/ppppeepppp/4pp4/10/10/10/10/4PP4/PPPPEEPPPP/RNBGQKGBNR w KQkq -" XBoard pieces: "PNBRQ........EGKpnbrq........egk" Zone: rank10 = a10,b10,c10,d10,e10,f10,g10,h10,i10,j10 Zone: rank9 = a9,b9,c9,d9,e9,f9,g9,h9,i9,j9 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,j2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Guard Move: slide (H,V) Capture: slide (D,A) Symbol: "G", "G,g" Value: 400 Piece: Ensign Move: slide (D,A) Capture: slide (H,V) Symbol: "E", "E,e" Value: 450 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-i1 with j1 Castle: white f1-c1 with a1 Castle: black f8-i8 with j8 Castle: black f8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank9, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank10, rank1, "QEGRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ##################################################### # Diana Chess # # http://www.chessvariants.com/small.dir/diana.html # ##################################################### Variant: diana Board: 6x6 FEN: "rbnkbr/pppppp/6/6/PPPPPP/RBNKBR w - -" XBoard pieces: "PNBR.Kpnbr.k" Zone: rank6 = a6,b6,c6,d6,e6,f6 Zone: rank1 = a1,b1,c1,d1,e1,f1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 575 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 600 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 650 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal # Castling: does not work correctly in XBoard, which cannot place the king correctly Castle: white d1-f1 with f1 Castle: white d1-a1 with a1 Castle: black d8-f8 with f8 Castle: black d8-a8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank6, rank1, "RBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw #################################################### # Sherwin's Chess # # http://talkchess.com/forum/viewtopic.php?t=58943 # #################################################### Variant: Sherwin's Chess Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 # Define the pieces Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Optional promotion: empty, rank2, "N" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ################################## # Viceroy Chess # # http://elajedrezdelvirrey.com/ # ################################## Variant: Viceroy Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" XBoard pieces: "PNBRQ.....VKpnbrq.....vk" Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rook8 = a8,h8 Zone: knight8 = b8,g8 Zone: bishop8 = c8,f8 Zone: queen8 = d8 Zone: viceroy8 = e8 Zone: rook1 = a1,h1 Zone: knight1 = b1,g1 Zone: bishop1 = c1,f1 Zone: queen1 = d1 Zone: viceroy1 = e1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Viceroy Move: leap (0,1)|(1,1) Symbol: "V", "V,v" Value: 250 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rook8, rook1, "R" Promotion: knight8, knight1, "N" Promotion: bishop8, bishop1, "B" Promotion: queen8, queen1, "Q" Promotion: viceroy8, viceroy1, "V" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ############################################################# # Chaturanga # # http://www.chessvariants.com/old.dir/chaturanga.html # # http://www.chessvariants.com/historic.dir/chaturanga.html # ############################################################# Variant: chaturanga Board: 8x8 FEN: "rnekfenr/pppppppp/8/8/8/8/PPPPPPPP/RNEFKENR w Ed" XBoard pieces: "PN.R.FEKpn.r.fek" XBoard parent: "shatranj" Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rook8 = a8,h8 Zone: knight8 = b8,g8 Zone: bishop8 = c8,f8 Zone: queen8 = e8 Zone: pawn8 = d8 Zone: rook1 = a1,h1 Zone: knight1 = b1,g1 Zone: bishop1 = c1,f1 Zone: queen1 = d1 Zone: pawn1 = e1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 600 Max: 2 Piece: Elephant Move: leap (2,2) Symbol: "E", "E,e" Value: 150 Max: 2 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 1000 Max: 2 Piece: Ferz Move: leap (1,1) Symbol: "F", "F,f" Value: 250 Max: 1 Piece: King Move: leap (0,1)|(1,1) Special: pawn1, pawn8, leap (2,1) Symbol: "K", "K,k" Flags: royal Max: 1 Piece: Pawn Move: step N Capture: step NE,NW Symbol: " ", "P,p" Promotion: rook8, rook1, "R" Promotion: knight8, knight1, "N" Promotion: bishop8, bishop1, "E" Promotion: queen8, queen1, "F" Promotion: pawn8, pawn1, "K" Value: 100 Rule: checkmate = win Rule: stalemate = win Rule: repeat3 = draw Rule: bare rule Rule: special init ############################################################################################## # Some form of suicide chess # # Piece values from http://www.ke.tu-darmstadt.de/publications/papers/ICGA-ChessVariants.pdf # ############################################################################################## Variant: suicide Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - -" XBoard pieces: "PNBRQKpnbrqk" XBoard parent: "suicide" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: -320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: -450 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: -500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: -480 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Value: -300 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "KQRBN" Value: -250 Rule: stalemate = loss Rule: nopieces = win Rule: repeat3 = draw Rule: forced capture ################################################################### # Roman Chess # # http://www.chess10x10.com/10x10-chess-variants-Roman-Chess.html # ################################################################### Variant: roman Board: 10x10 FEN: "rnabqkbanr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RNABQKBANR w KQkq -" XBoard pieces: "PNBRQ.....AKpnbrq.....ak" Zone: rank9 = a9,b9,c9,d9,e9,f9,g9,h9,i9,j9 Zone: rank10 = a10,b10,c10,d10,e10,f10,g10,h10,i10,j10 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,h1 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,h2 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 300 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-i1 with j1 Castle: white f1-c1 with a1 Castle: black f10-i10 with j10 Castle: black f10-c10 with a10 Piece: Archer Move: leap (0,1)|(1,1) Symbol: "A", "A,a" Value: 275 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank9, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank10, rank1, "RNABQ" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ######################################################################## # Schoolbook Chess # # http://www.chessvariants.com/index/msdisplay.php?itemid=MSschoolbook # ######################################################################## Variant: Schoolbook (10x8) Board: 10x8 FEN: "rqnbakbncr/pppppppppp/10/10/10/10/PPPPPPPPPP/RQNBAKBNCR w KQkq -" XBoard pieces: "PNBRQ..ACKpnbrq..ack" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8,i8,j8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7,i7,j7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,j2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 275 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 350 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 475 Piece: Chancellor Move: leap (2,1) Move: slide (H,V) Symbol: "C", "C,c" Value: 875 Piece: Archbishop Move: leap (2,1) Move: slide (D,A) Symbol: "A", "A,a" Value: 825 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-i1,h1 with j1 Castle: white f1-c1,b1,d1 with a1 Castle: black f8-i8,h8 with j8 Castle: black f8-c8,b8,d8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QACRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ################################ # Supercapablanca Chess # # Pritchard, CECV section 14.1 # ################################ Variant: supercapablanca (12x8) Board: 12x8 FEN: "rnbacqkgabnr/pppppppppppp/12/12/12/12/PPPPPPPPPPPP/RNBACQKGABNR w KQkq -" XBoard pieces: "PNBRQ..AC......GKpnbrq..ac......gk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8,i8,j8,k8,l8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7,i7,j7,k7,l7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 275 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 350 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 475 Piece: Chancellor Move: leap (2,1) Move: slide (H,V) Symbol: "C", "C,c" Value: 875 Piece: Archbishop Move: leap (2,1) Move: slide (D,A) Symbol: "A", "A,a" Value: 825 Piece: General Move: leap (2,1) Move: slide (D,A,H,V) Symbol: "G", "G,g" Value: 1200 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Special: rank1, rank8, leap (2,1) Castle: white g1-i1,j1,k1 with l1 Castle: white g1-e1,d1,c1 with a1 Castle: black g8-i8,j8,k8 with l8 Castle: black g8-e8,d8,c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "GQACRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw Rule: special init ########################################################## # Wildebeest Chess # # http://www.chessvariants.com/large.dir/wildebeest.html # ########################################################## Variant: Wildebeest (11x10) Board: 11x10 FEN: "rnccgkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKGCCNR w KQkq - 0 1" XBoard pieces: "PNBRQ...................C......GKpnbrq...................c......gk" Zone: rank10 = a10,b10,c10,d10,e10,f10,g10,h10,i10,j10,k10 Zone: rank9 = a9,b9,c9,d9,e9,f9,g9,h9,i9,j9,k9 Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8,i8,j8,k8 Zone: rank3 = a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 275 Piece: Camel Move: leap (3,1) Symbol: "C", "C,c" Value: 225 Piece: Gnu Move: leap (2,1)|(3,1) Symbol: "G", "G,g" Value: 700 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 350 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 475 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white f1-g1,h1,i1,j1 with k1 Castle: white f1-e1,d1,c1,b1 with a1 Castle: black f10-g10,h10,i10,j10 with k10 Castle: black f10-e10,d10,c10,b10 with a10 Piece: Pawn Move: step N Capture: step NE,NW Initial: step 3N Special: rank3, rank8, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank10, rank1, "GQCRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ####################### # WaShogi, with drops # ####################### Variant: Wa-Shogi-drop (11x11) Board: 11x11 FEN: "hmlcvkwgudo/1e3s3f1/ppprpppxppp/3p3p3/11/11/11/3P3P3/PPPXPPPRPPP/1F3S3E1/ODUGWKVCLMH w 0 1" XBoard pieces: "P..^S^FV..^LW^OH.F.^R.E....R...D.GOL^M..^H.M.C.^CU.^W/.......^V.^P.^U..^DS.^GXKp..^s^fv..^lw^oh.f.^r.e....r...d.gol^m..^h.m.c.^cu.^w/.......^v.^p.^u..^ds.^gxk" XBoard parent: "chu" Zone: white_promotion = a11,b11,c11,d11,e11,f11,g11,h11,i11,j11,k11,a10,b10,c10,d10,e10,f10,g10,h10,i10,j10,k10,a9,b9,c9,d9,e9,f9,g9,h9,i9,j9,k9 Zone: black_promotion = a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1 # crane King, K=King # cloud Eagle, E # flying Falcon F promotes to *Tenacious Falcon # Swallows wings, S promotes to *Gliding Swallow # treacherous foX, X # running Rabbit, R promotes to *Treacherous fox # violent Wolf, W promotes to *Bear's Eyes # Violent stag, V promotes to *Roaming Boar # flying Goose, G promotes to *Swallow's Wings # flying Cock, C promotes to *Raiding Falcon # strUtting crow, U promotes to *Flying Falcon # swooping owL, L promotes to *Cloud Eagle # blind Dog, D promotes to *Violent Wolf # climbing Monkey, M promotes to *Violent Stag # liberated Horse, H promotes to *Heavenly Horse # Oxcart, O promotes to *Plodding Ox # sparrow Pawn, P=Pawn promotes to *Golden Bird Piece: Crane King Symbol: "K", "K,k" Move: leap (0,1)|(1,1) Flags: royal Piece: Cloud Eagle Symbol: "E", "E,e" Move: slide (V) Move: step W,E,SW,SE,3NW,3NE Value: 850 Piece: Treacherous Fox Symbol: "X", "X,x" Move: aleap (1,1)|(2,2)|(-1,1)|(-2,2)|(1,-1)|(2,-2)|(-1,-1)|(-2,-2)|(0,1)|(0,2)|(0,-1)|(0,-2) Value: 750 Piece: Flying Falcon Symbol: "F", "F,f" Move: slide (D,A) Move: step N Promotion: white_promotion, black_promotion, "+" Value: 600 Piece: Tenacious Falcon Symbol: "+F", "+F,+f" Move: slide (V,A,D) Move: step W,E Value: 700 Piece: Running Rabbit Symbol: "R", "R,r" Move: step NE,NW,SE,S,SW,10N Promotion: white_promotion, black_promotion, "+" Value: 400 Piece: Promoted Running Rabbit Symbol: "+R", "+R,+r" Move: aleap (1,1)|(2,2)|(-1,1)|(-2,2)|(1,-1)|(2,-2)|(-1,-1)|(-2,-2)|(0,1)|(0,2)|(0,-1)|(0,-2) Value: 750 Piece: Violent Wolf Symbol: "W", "W,w" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Promotion: white_promotion, black_promotion, "+" Value: 400 Piece: Bears Eyes Symbol: "+W", "+W,+w" Move: leap (0,1)|(1,1) Piece: Violent Stag Symbol: "V", "V,v" Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Promotion: white_promotion, black_promotion, "+" Piece: Roaming Boar Symbol: "+V", "+V,+v" Move: aleap (1,0)|(-1,0)|(0,1)|(1,1)|(-1,1)|(-1,-1)|(1,-1) Piece: Blind Dog Symbol: "D", "D,d" Move: aleap (-1,1)|(1,1)|(-1,0)|(1,0)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Blind Dog Symbol: "+D", "+D,+d" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Piece: Climbing Monkey Symbol: "M", "M,m" Move: aleap (-1,1)|(0,1)|(1,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Climbing Monkey Symbol: "+M", "+M,+m" Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Piece: Flying Goose Symbol: "G", "G,g" Move: aleap (-1,1)|(0,1)|(1,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Flying Goose Symbol: "+G", "+G,+g" Move: slide (H) Move: aleap (0,1)|(0,-1) Piece: Flying Cock Symbol: "C", "C,c" Move: aleap (-1,1)|(-1,0)|(1,1)|(1,0) Promotion: white_promotion, black_promotion, "+" Piece: Raiding Falcon Symbol: "+C", "+C,+c" Move: aleap (-1,1)|(-1,0)|(1,1)|(1,0) Move: slide (V) Piece: Swallows Wings Symbol: "S", "S,s" Move: slide (H) Move: aleap (0,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Gliding Swallow Symbol: "+S", "+S,+s" Move: slide (H,V) Piece: Strutting Crow Symbol: "U", "U,u" Move: aleap (-1,-1)|(1,-1)|(0,1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Strutting Cow Symbol: "+U", "+U,+u" Move: slide (D,A) Move: step N Piece: Swooping Owl Symbol: "L", "L,l" Move: aleap (-1,-1)|(1,-1)|(0,1) Promotion: white_promotion, black_promotion, "+" Piece: Cloud Eagle (Promoted Swooping Owl) Symbol: "+L", "+L,+l" Move: slide (V) Move: step W,E,SW,SE,3NW,3NE Piece: Liberated Horse Symbol: "H", "H,h" Move: step 10N,2S Promotion: white_promotion, black_promotion, "+" Piece: Heavenly Horse Symbol: "+H", "+H,+h" Move: aleap (1,2)|(-1,2)|(1,-2)|(-1,-2) Piece: Oxcart Symbol: "O", "O,o" Move: step 10N Promotion: white_promotion, black_promotion, "+" Piece: Plodding Ox (Promoted Oxcart) Symbol: "+O", "+O,+o" Move: leap (0,1)|(1,1) Piece: Sparrow Pawn Symbol: "P", "P,p" Move: step N Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: Golden Bird (Promoted Sparrow Pawn) Symbol: "+P", "+P,+p" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Value: 450 Rule: keep capture, allow drops Rule: repeat4 = draw Rule: perpetual4 = loss ########################## # WaShogi, without drops # ########################## Variant: Wa-Shogi (11x11) Board: 11x11 FEN: "hmlcvkwgudo/1e3s3f1/ppprpppxppp/3p3p3/11/11/11/3P3P3/PPPXPPPRPPP/1F3S3E1/ODUGWKVCLMH w 0 1" XBoard pieces: "P..^S^FV..^LW^OH.F.^R.E....R...D.GOL^M..^H.M.C.^CU.^W/.......^V.^P.^U..^DS.^GXKp..^s^fv..^lw^oh.f.^r.e....r...d.gol^m..^h.m.c.^cu.^w/.......^v.^p.^u..^ds.^gxk" XBoard parent: "chu" Zone: white_promotion = a11,b11,c11,d11,e11,f11,g11,h11,i11,j11,k11,a10,b10,c10,d10,e10,f10,g10,h10,i10,j10,k10,a9,b9,c9,d9,e9,f9,g9,h9,i9,j9,k9 Zone: black_promotion = a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1 # crane King, K=King # cloud Eagle, E # flying Falcon F promotes to *Tenacious Falcon # Swallows wings, S promotes to *Gliding Swallow # treacherous foX, X # running Rabbit, R promotes to *Treacherous fox # violent Wolf, W promotes to *Bear's Eyes # Violent stag, V promotes to *Roaming Boar # flying Goose, G promotes to *Swallow's Wings # flying Cock, C promotes to *Raiding Falcon # strUtting crow, U promotes to *Flying Falcon # swooping owL, L promotes to *Cloud Eagle # blind Dog, D promotes to *Violent Wolf # climbing Monkey, M promotes to *Violent Stag # liberated Horse, H promotes to *Heavenly Horse # Oxcart, O promotes to *Plodding Ox # sparrow Pawn, P=Pawn promotes to *Golden Bird Piece: Crane King Symbol: "K", "K,k" Move: leap (0,1)|(1,1) Flags: royal Piece: Cloud Eagle Symbol: "E", "E,e" Move: slide (V) Move: step W,E,SW,SE,3NW,3NE Value: 850 Piece: Treacherous Fox Symbol: "X", "X,x" Move: aleap (1,1)|(2,2)|(-1,1)|(-2,2)|(1,-1)|(2,-2)|(-1,-1)|(-2,-2)|(0,1)|(0,2)|(0,-1)|(0,-2) Value: 750 Piece: Flying Falcon Symbol: "F", "F,f" Move: slide (D,A) Move: step N Promotion: white_promotion, black_promotion, "+" Value: 600 Piece: Tenacious Falcon Symbol: "+F", "+F,+f" Move: slide (V,A,D) Move: step W,E Value: 700 Piece: Running Rabbit Symbol: "R", "R,r" Move: step NE,NW,SE,S,SW,10N Promotion: white_promotion, black_promotion, "+" Value: 400 Piece: Promoted Running Rabbit Symbol: "+R", "+R,+r" Move: aleap (1,1)|(2,2)|(-1,1)|(-2,2)|(1,-1)|(2,-2)|(-1,-1)|(-2,-2)|(0,1)|(0,2)|(0,-1)|(0,-2) Value: 750 Piece: Violent Wolf Symbol: "W", "W,w" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Promotion: white_promotion, black_promotion, "+" Value: 400 Piece: Bears Eyes Symbol: "+W", "+W,+w" Move: leap (0,1)|(1,1) Piece: Violent Stag Symbol: "V", "V,v" Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Promotion: white_promotion, black_promotion, "+" Piece: Roaming Boar Symbol: "+V", "+V,+v" Move: aleap (1,0)|(-1,0)|(0,1)|(1,1)|(-1,1)|(-1,-1)|(1,-1) Piece: Blind Dog Symbol: "D", "D,d" Move: aleap (-1,1)|(1,1)|(-1,0)|(1,0)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Blind Dog Symbol: "+D", "+D,+d" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Piece: Climbing Monkey Symbol: "M", "M,m" Move: aleap (-1,1)|(0,1)|(1,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Climbing Monkey Symbol: "+M", "+M,+m" Move: aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1) Piece: Flying Goose Symbol: "G", "G,g" Move: aleap (-1,1)|(0,1)|(1,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Flying Goose Symbol: "+G", "+G,+g" Move: slide (H) Move: aleap (0,1)|(0,-1) Piece: Flying Cock Symbol: "C", "C,c" Move: aleap (-1,1)|(-1,0)|(1,1)|(1,0) Promotion: white_promotion, black_promotion, "+" Piece: Raiding Falcon Symbol: "+C", "+C,+c" Move: aleap (-1,1)|(-1,0)|(1,1)|(1,0) Move: slide (V) Piece: Swallows Wings Symbol: "S", "S,s" Move: slide (H) Move: aleap (0,1)|(0,-1) Promotion: white_promotion, black_promotion, "+" Piece: Gliding Swallow Symbol: "+S", "+S,+s" Move: slide (H,V) Piece: Strutting Crow Symbol: "U", "U,u" Move: aleap (-1,-1)|(1,-1)|(0,1) Promotion: white_promotion, black_promotion, "+" Piece: Promoted Strutting Cow Symbol: "+U", "+U,+u" Move: slide (D,A) Move: step N Piece: Swooping Owl Symbol: "L", "L,l" Move: aleap (-1,-1)|(1,-1)|(0,1) Promotion: white_promotion, black_promotion, "+" Piece: Cloud Eagle (Promoted Swooping Owl) Symbol: "+L", "+L,+l" Move: slide (V) Move: step W,E,SW,SE,3NW,3NE Piece: Liberated Horse Symbol: "H", "H,h" Move: step 10N,2S Promotion: white_promotion, black_promotion, "+" Piece: Heavenly Horse Symbol: "+H", "+H,+h" Move: aleap (1,2)|(-1,2)|(1,-2)|(-1,-2) Piece: Oxcart Symbol: "O", "O,o" Move: step 10N Promotion: white_promotion, black_promotion, "+" Piece: Plodding Ox (Promoted Oxcart) Symbol: "+O", "+O,+o" Move: leap (0,1)|(1,1) Piece: Sparrow Pawn Symbol: "P", "P,p" Move: step N Promotion: white_promotion, black_promotion, "+" Flags: drop_no_mate, drop_one_file Value: 80 Piece: Golden Bird (Promoted Sparrow Pawn) Symbol: "+P", "+P,+p" Move: aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1) Value: 450 Rule: repeat4 = draw Rule: perpetual4 = loss ###################################################################### # Theban Chess, http://www.talkchess.com/forum/viewtopic.php?t=52277 # ###################################################################### Variant: Theban Board: 8x8 FEN: "1p6/2p3kn/3p2pp/4pppp/5ppp/8/PPPPPPPP/PPPPPPKN w - - 0 1" XBoard pieces: "PNBRQKpnbrqk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Piece: Pawn Move: step N Capture: step NE,NW Special: rank2, rank7, step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw #################### # Nightrider chess # #################### Variant: Nightrider (8x8) Board: 8x8 FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -" XBoard pieces: "P.BRQ.......NKp.brq.......nk" Zone: rank8 = a8,b8,c8,d8,e8,f8,g8,h8 Zone: rank7 = a7,b7,c7,d7,e7,f7,g7,h7 Zone: rank2 = a2,b2,c2,d2,e2,f2,g2,h2 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1 Piece: Nightrider Move: ride (2,1) Symbol: "N", "N,n" Value: 550 Piece: Bishop Move: slide (D,A) Symbol: "B", "B,b" Value: 325 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Value: 950 Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Flags: royal Castle: white e1-g1 with h1 Castle: white e1-c1 with a1 Castle: black e8-g8 with h8 Castle: black e8-c8 with a8 Piece: Pawn Move: step N Capture: step NE,NW Initial: step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank8, rank1, "QRBN" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw ####################################################### # Caissa Britannia # # http://www.chessvariants.com/large.dir/british.html # ####################################################### Variant: Caissa Britannia (10x10) Board: 10x10 FEN: "drubqkburd/1l6l1/pppppppppp/10/10/10/10/PPPPPPPPPP/1L6L1/DRUBQKBURD[NNNNNNNNNNnnnnnnnnnn] w - - 0 1" XBoard parent: "grand" XBoard pieces: "PNBRQ...............U.........D.LKpnbrq...............u.........d.lk" Zone: rank10 = a10,b10,c10,d10,e10,f10,g10,h10,i10,j10 Zone: rank1 = a1,b1,c1,d1,e1,f1,g1,h1,i1,j1 Piece: Knight Move: leap (2,1) Symbol: "N", "N,n" Value: 320 Piece: Unicorn Move: slide (D,A) Move: ride (2,1) Symbol: "U", "U,u" Value: 925 Max: 2 Piece: Dragon Move: ride (0,2)|(2,2) Symbol: "D", "D,d" Value: 300 Max: 2 Piece: Lion Move: slide (A,D,H,V) Capture: hop (A,D,H,V) Symbol: "L", "L,l" Value: 475 Max: 2 Piece: Bishop Move: slide (D,A) Move: leap (1,0) Capture: slide (D,A) Symbol: "B", "B,b" Value: 325 Max: 2 Piece: Rook Move: slide (H,V) Symbol: "R", "R,r" Value: 500 Max: 2 Piece: Queen Move: slide (D,A,H,V) Symbol: "Q", "Q,q" Flags: royal Piece: King Move: leap (0,1)|(1,1) Symbol: "K", "K,k" Value: 250 Max: 2 Piece: Pawn Move: step N Capture: step NE,NW Initial: step 2N Symbol: " ", "P,p" Flags: set_ep,take_ep Promotion: rank10, rank1, "RBNUDKL" Value: 100 Rule: checkmate = win Rule: stalemate = draw Rule: repeat3 = draw Rule: en-passant check SjaakII/CMakeLists.txt000644 000765 000024 00000026434 13017310305 015631 0ustar00eglebbkstaff000000 000000 cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) # Set build type. Do this *before* we set the project name if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo Profile." FORCE) endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_CONFIGURATION_TYPES "${CMAKE_BUILD_TYPE}" CACHE INTERNAL "internal") if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) project(sjaakxx CXX C) # Search in the `cmake' directory for additional CMake modules. list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) # Declare program options option(WANT_WARN "Generate compiler warnings" on) option(WANT_WERROR "Treat warnings as errors" off) option(WANT_NATIVE "Optimise for the current machine" off) option(WANT_ASSERT "Enable assertions in the code (for debugging)" off) option(WANT_OPT "Standard optimisations on/off" on) option(WANT_POPCNT "Use POPCNT instruction" off) option(WANT_SSE42 "Switch on SSE 4.2" on) option(WANT_SSE3 "Switch on SSE 3" off) option(WANT_SSE2 "Switch on SSE 2" off) option(WANT_32BIT "Force compiler to generate 32 bit code" off) option(WANT_64BIT "Force compiler to generate 64 bit code" off) #option(WANT_GUI "Wether you want to build the GUI or not (requires Allegro)" off) #option(WANT_MGUI "Wether you want to build the mobile GUI or not (requires Allegro) (experimental)" off) option(WANT_REFEREE "Wether you want to build the game referee" on) option(WANT_STATIC "Wether you want to static link to standard libraries" off) option(WANT_PROFILE_GENERATE "Generate executable suitable for running profile-guided optimisation" off) option(WANT_PROFILE_USE "Generate executable using data from profile-generate run" off) option(WANT_PROFILE "Generate executable suitable for running with gprof" off) option(WANT_RELEASE "Generate a binary with a mangled name to represent the current platform" off) set(SJAAKII_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/games/sjaakii/" CACHE STRING "Install location for data files (${CMAKE_INSTALL_PREFIX}/share/games/sjaakii/)") # Parse user options if(WANT_OPT) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -finline") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O3 -finline") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -finline") endif(WANT_OPT) if(WANT_WARN) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wall -Wextra -Wno-unused") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused") endif(WANT_WARN) if(WANT_WERROR) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused -Werror") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wall -Wno-unused -Werror") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused -Werror") endif(WANT_WERROR) if(WANT_NATIVE) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -mtune=native") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -march=native -mtune=native") endif(WANT_NATIVE) if(WANT_POPCNT) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mpopcnt") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mpopcnt") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mpopcnt") endif(WANT_POPCNT) if(WANT_SSE42) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.2") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -msse4.2") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2") endif(WANT_SSE42) if(WANT_SSE3) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse3") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -msse3") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse3") endif(WANT_SSE3) if(WANT_SSE2) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -msse2") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2") endif(WANT_SSE2) if(WANT_PROFILE_GENERATE) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-generate") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-generate") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-generate") endif(WANT_PROFILE_GENERATE) if(WANT_PROFILE_USE) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-use") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-use") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-use") endif(WANT_PROFILE_USE) if(WANT_ASSERT) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUGMODE") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -DDEBUGMODE") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUGMODE") endif(WANT_ASSERT) if(WANT_32BIT) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") endif(WANT_32BIT) if(WANT_PROFILE) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg -g") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg -g") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -g") endif(WANT_PROFILE) if(WANT_64BIT) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m64") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") endif(WANT_64BIT) if(WANT_STATIC) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") endif(WANT_STATIC) # Search for C header files in these directories. include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) set (SJAAKIIVERSION "\"1.4.1\"") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSJAAKIIVERSION=\\\"${SJAAKIIVERSION}\\\"") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSJAAKIIVERSION=\\\"${SJAAKIIVERSION}\\\"") if(WANT_GUI) set(WANT_ALLEGRO 1) endif(WANT_GUI) if(WANT_MGUI) set(WANT_ALLEGRO 1) endif(WANT_MGUI) if(WANT_ALLEGRO) # Find zlib find_package(ZLIB) if (ZLIB_FOUND) list(APPEND GUI_LIBS ${ZLIB_LIBRARIES}) include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS}) else (ZLIB_FOUND) message(FATAL_ERROR "Can't find zlib library") endif(ZLIB_FOUND) # Find Allegro set(ALLEGRO5_VERSION "5.1") find_package(Allegro5) if (ALLEGRO5_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5_INCLUDE_DIRS}) else (ALLEGRO5_FOUND) message(FATAL_ERROR "Can't find Allegro library") endif(ALLEGRO5_FOUND) # Find Allegro addons find_package(Allegro5Main) if (ALLEGRO5MAIN_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5MAIN_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5MAIN_INCLUDE_DIRS}) else (ALLEGRO5MAIN_FOUND) message(FATAL_ERROR "Can't find Allegro main addon") endif(ALLEGRO5MAIN_FOUND) find_package(Allegro5Primitives) if (ALLEGRO5PRIMITIVES_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5PRIMITIVES_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5PRIMITIVES_INCLUDE_DIRS}) else (ALLEGRO5PRIMITIVES_FOUND) message(FATAL_ERROR "Can't find Allegro Primitives addon") endif(ALLEGRO5PRIMITIVES_FOUND) find_package(Allegro5Font) if (ALLEGRO5FONT_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5FONT_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5FONT_INCLUDE_DIRS}) else (ALLEGRO5FONT_FOUND) message(FATAL_ERROR "Can't find Allegro font addon") endif(ALLEGRO5FONT_FOUND) find_package(Allegro5TTF) if (ALLEGRO5TTF_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5TTF_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5TTF_INCLUDE_DIRS}) else (ALLEGRO5TTF_FOUND) message(FATAL_ERROR "Can't find Allegro TTF addon") endif(ALLEGRO5TTF_FOUND) find_package(Allegro5Image) if (ALLEGRO5IMAGE_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5IMAGE_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5IMAGE_INCLUDE_DIRS}) else (ALLEGRO5IMAGE_FOUND) message(FATAL_ERROR "Can't find Allegro image addon") endif(ALLEGRO5IMAGE_FOUND) find_package(Allegro5Dialog) if (ALLEGRO5DIALOG_FOUND) list(APPEND GUI_LIBS ${ALLEGRO5DIALOG_LIBRARIES}) include_directories(SYSTEM ${ALLEGRO5DIALOG_INCLUDE_DIRS}) else (ALLEGRO5DIALOG_FOUND) message(FATAL_ERROR "Can't find Allegro DIALOG addon") endif(ALLEGRO5DIALOG_FOUND) endif(WANT_ALLEGRO) # Set install directory if (NOT SJAAKII_DATA_DIR) set(SJAAKII_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/games/sjaakii/") endif (NOT SJAAKII_DATA_DIR) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDATADIR=\\\"${SJAAKII_DATA_DIR}\\\"") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDATADIR=\\\"${SJAAKII_DATA_DIR}\\\"") # code source files include(FileList) # Make a link library out of the source files add_library ("libsjaak" STATIC ${SJAAK_SRC_FILES}) # Look for the standard math library find_library(M_LIB m) target_link_libraries("libsjaak" ${M_LIB}) # Look for the standard math library include(CheckLibraryExists) CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) if (HAVE_CLOCK_GETTIME) target_link_libraries("libsjaak" rt) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_CLOCK_GETTIME") endif (HAVE_CLOCK_GETTIME) add_executable ("sjaakii" src/xboard.cc) target_link_libraries("sjaakii" libsjaak) add_custom_command(TARGET "sjaakii" POST_BUILD COMMAND pod2man -s 6 ${CMAKE_SOURCE_DIR}/sjaakii.pod | gzip > ${CMAKE_BINARY_DIR}/sjaakii.6.gz ) # Find readline find_package(Readline) if (READLINE_FOUND) target_link_libraries("sjaakii" ${READLINE_LIBRARY}) include_directories(SYSTEM ${READLINE_INCLUDE_DIRS}) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_READLINE") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_READLINE") else (READLINE_FOUND) message(WARNING "Can't find readline library - disabling support") endif(READLINE_FOUND) if(WANT_REFEREE) add_executable ("sjef" src/sjef.c src/timer/timer.c src/misc/keypressed.c src/misc/genrand.c src/misc/pipe2.c src/misc/sprt.c) target_link_libraries("sjef" ${M_LIB}) target_compile_options(sjef PRIVATE -std=gnu99) endif(WANT_REFEREE) if(WANT_RELEASE) set(PLATF "-unknown") if(CMAKE_SYSTEM_NAME MATCHES "Windows") set(PLATF "-win") endif(CMAKE_SYSTEM_NAME MATCHES "Windows") if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(PLATF "-linux") endif(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_SYSTEM_NAME MATCHES "Darwin") set(PLATF "-mac") endif(CMAKE_SYSTEM_NAME MATCHES "Darwin") set(SSE "-sse") if(WANT_SSE2) set(SSE "-sse2") endif(WANT_SSE2) if(WANT_SSE3) set(SSE "-sse3") endif(WANT_SSE3) if(WANT_SSE42) set(SSE "-sse42") endif(WANT_SSE42) set(POPCNT "") if(WANT_POPCNT) set(POPCNT "-popcnt") endif(WANT_POPCNT) set(BITS "") if(WANT_64BIT) set(BITS "64") endif(WANT_64BIT) if(WANT_32BIT) set(BITS "32") endif(WANT_32BIT) set(VERSION "-${Project_WC_REVISION}") set(RELEASE_NAME "sjaakii${VERSION}${PLATF}${BITS}${SSE}${POPCNT}") set_target_properties(sjaakii PROPERTIES OUTPUT_NAME "${RELEASE_NAME}") endif(WANT_RELEASE) # Installation targets install (TARGETS "sjaakii" RUNTIME DESTINATION "bin") install (FILES "${CMAKE_BINARY_DIR}/sjaakii.6.gz" DESTINATION "share/man/man6") install (FILES "${CMAKE_SOURCE_DIR}/variants.txt" DESTINATION "share/games/sjaakii/") install (FILES "${CMAKE_SOURCE_DIR}/misc/sjaakii.eng" DESTINATION "share/games/plugins/xboard/") install (FILES "${CMAKE_SOURCE_DIR}/misc/sjaakii.png" DESTINATION "share/games/plugins/logos/") SjaakII/MSVS2012/SjaakII.sln000644 000765 000024 00000003224 12452237254 016164 0ustar00eglebbkstaff000000 000000  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SjaakII", "SjaakII.vcxproj", "{33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release Intel|Win32 = Release Intel|Win32 Release Intel|x64 = Release Intel|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Debug|Win32.ActiveCfg = Debug|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Debug|Win32.Build.0 = Debug|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Debug|x64.ActiveCfg = Debug|x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Debug|x64.Build.0 = Debug|x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release Intel|Win32.ActiveCfg = Release Intel|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release Intel|Win32.Build.0 = Release Intel|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release Intel|x64.ActiveCfg = Release Intel|x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release Intel|x64.Build.0 = Release Intel|x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release|Win32.ActiveCfg = Release|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release|Win32.Build.0 = Release|Win32 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release|x64.ActiveCfg = Release|x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal SjaakII/MSVS2012/SjaakII.vcxproj000644 000765 000024 00000034015 13017310351 017051 0ustar00eglebbkstaff000000 000000  Debug Win32 Debug x64 Release Intel Win32 Release Intel x64 Release Win32 Release x64 {33B8E0A6-E08B-4F2C-9BE0-615DAA4436F7} Win32Proj SjaakII Application true v110 Unicode Application true v110 Unicode Application false v110_xp true MultiByte Application false Intel C++ Compiler XE 15.0 true MultiByte Application false v110_xp true MultiByte Application false Intel C++ Compiler XE 15.0 true MultiByte true true false false false false Level3 Disabled SJAAKIIVERSION="1.4.1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) Console true Level3 Disabled SJAAKIIVERSION="1.4.1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) Console true Level3 Full true true SJAAKIIVERSION="1.4.1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) AnySuitable Speed true false MultiThreaded false Console true true true /SUBSYSTEM:CONSOLE,"5.01" %(AdditionalOptions) Level3 Full true true SJAAKIIVERSION="1.4.1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) OnlyExplicitInline Speed true false MultiThreaded false /Qopt-report-embed- %(AdditionalOptions) false Console false true true /SUBSYSTEM:CONSOLE,"5.01" %(AdditionalOptions) Level3 Full true true SJAAKIIVERSION="1.4.1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) AnySuitable Speed true false MultiThreaded false Console true true true /SUBSYSTEM:CONSOLE,"5.02" %(AdditionalOptions) Level3 Full true true SJAAKIIVERSION="1.4.1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\include;%(AdditionalIncludeDirectories) OnlyExplicitInline Speed true false MultiThreaded false /Qopt-report-embed- %(AdditionalOptions) false Console false true true /SUBSYSTEM:CONSOLE,"5.02" %(AdditionalOptions) SjaakII/MSVS2012/SjaakII.vcxproj.filters000644 000765 000024 00000002111 13014616475 020525 0ustar00eglebbkstaff000000 000000  SjaakII/MSVS2015/SjaakII.props000644 000765 000024 00000001105 13017310364 016522 0ustar00eglebbkstaff000000 000000  ../include Level3 SJAAKIIVERSION="1.4.1";%(PreprocessorDefinitions) SjaakII/MSVS2015/SjaakII.sln000644 000765 000024 00000002417 13014616475 016173 0ustar00eglebbkstaff000000 000000  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SjaakII", "SjaakII.vcxproj", "{0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Debug|x64.ActiveCfg = Debug|x64 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Debug|x64.Build.0 = Debug|x64 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Debug|x86.ActiveCfg = Debug|Win32 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Debug|x86.Build.0 = Debug|Win32 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Release|x64.ActiveCfg = Release|x64 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Release|x64.Build.0 = Release|x64 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Release|x86.ActiveCfg = Release|Win32 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal SjaakII/MSVS2015/SjaakII.vcxproj000644 000765 000024 00000026661 13014616475 017101 0ustar00eglebbkstaff000000 000000  Debug Win32 Release Win32 Debug x64 Release x64 {0025FFCB-FCDD-444C-AB47-0C3762FC4ACD} Win32Proj SjaakII 8.1 Application true v140 Unicode Application false v140_xp true MultiByte Application true v140 Unicode Application false v140_xp true MultiByte true true false false Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Full true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true AnySuitable Speed true MultiThreaded false false false NotSet Console true true true Full true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true AnySuitable Speed true MultiThreaded false false false NotSet Console true true true SjaakII/MSVS2015/SjaakII.vcxproj.filters000644 000765 000024 00000014221 13014616475 020535 0ustar00eglebbkstaff000000 000000  {7d10e825-1978-4a1f-ab82-bf18ffc384fc} include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include SjaakII/sjaakii.pod000644 000765 000024 00000004715 12470157255 015225 0ustar00eglebbkstaff000000 000000 =head1 NAME sjaakii - xboard-compatible chess and chess-variant engine 'SjaakII' =head1 SYNOPSIS B [-log|-newlog [filename]] [-variant name] [-no_user_variants] [-xboard|-uci|-uci|-ucci] [variant file] =head1 DESCRIPTION B is a program that plays chess and chess variants. It uses the xboard/winboard chess-engine protocol to communicate. Apart from normal chess, SjaakII can play Seirawan Chess, Mongolian Shatar, Makruk (including variants ASEAN and Ai-Wok), Shatranj, Sittuyin (Burmese Chess), Crazy House Chess, Chessgi, Spartan Chess, Pocket Knight Chess, King-of-th-Hill, Knightmate, Amazon Chess, Chancellor Chess, Berolina Chess, Los Alamos Chess, Micro Chess, Capablanca Chess, Gothic Chess, Embassy Chess, Courier Chess, Grand Chess, Opulent Chess, Omega Chess, Shogi (including variants Mini Shogi, Sho Shogi and Tori Shogi) and Xiang-Qi. Other variants can be added through a configuration file. See xboard(6) for instructions about how to use B through xboard. To start up quickly, you just need the command: B. Some of the variants SjaakII plays are only partially supported by XBoard, and can only be played whith the legality-testing function of the latter switched off. =head1 OPTIONS =over 8 =item B<-log [filename]> Append all communication by SjaakII and XBoard to a logfile. If no filename is specified, the log is written to sjaakii.log. =item B<-newlog [filename]> As B<-log>, but the file is overwritten rather than added to. =item B<-variant name> Normally, SjaakII starts with variant "normal" (regular chess), but it can be made to start with any other named variant with this option. =item B<-no_user_variants> Do not read the default variant configuration file. You can still specify a file in the engine options. =item B<-xboard> Start in xboard mode rather than the default mode. =item B<-uci> Start in UCI mode rather than the default mode. =item B<-usi> Start in USI mode rather than the default mode. =item B<-ucci> Start in UCCI mode rather than the default mode. =item B Read variant descriptions from the named file. =back =head1 AVAILABILITY From http://www.eglebbk.dds.nl/program/chess-index.html =head1 SEE ALSO xboard(6) =head1 STANDARDS WinBoard, B(6) interface ("Chess Engine Communication Protocol") =head1 AUTHOR Evert Glebbeek . This manual page was based on the man page for Fairy-Max by H.G. Muller and was generated with pod2man(1). SjaakII/misc/sjaakii.png000644 000765 000024 00000026344 12433414314 016153 0ustar00eglebbkstaff000000 000000 ‰PNG  IHDR‚A|çbKGDÿÿÿ ½§“ pHYs  šœtIMEÞ /×¶†iTXtCommentCreated with GIMPd.e IDATxÚÕwxUö3»›NB „Þ”&UšØPAP¤J—^^z¤ /E@AŠ©"¨( UB - éewf¾?fgvfw6Ñ÷»¾ï\×\›Ý3sæœûÜçyîç9!t×CIïÒŽÐ]ðµør~z—v¦÷Š¢x® ¦÷î×~صŒõ•ƒkù·‹Ðv€Ç½ÝÛìÞ¦°°0îÜÙ@ppS¶´ø×ÚôÊá\²²Nü£ëŠî„î:àÑÙÿK<¬õ‹ ã ý[ 0‚K»Ÿ·vôïßI’HKK#==àà¦> Ö+‡s‹Ô®-­þ1¸Doú†¾€¡8Lðo‚Á_À°~ýzÖ¯_@BB‚þy©R¥|AQÀðÊá\~ü<¸È2=ŸûÒàË`¾”‡±Eq—÷™üO™ (ËDéÒ¥©T©GÅßßAˆ‰‰an¥{>1/ìñãçÁúû=³ŠÅ ba_zc†â€Àjµ²aÃΟ?Ï¢E‹ˆŽŽæÐ¡Cœ?žÞ½{û¼L×&ø'ÌPˆÜ™aĈ<ûì³lÛ¶¤¤$E!11QÀöíÛ¹~ý:ãÊú´6ËÝA› eo[T|òÉ'ôíÛ—°°0öíÛG“&MHKK#,,Œ3f0yòä"3ƒ·ÙçËŒ/*3øz¾7f(¨H’DÆ ¹~ý:¥J•bvùÛ§(ŠÇ3»Ïro 0–¢2ƒøoS…­ãiiiôéÓ‡íÛ·ÆöíÛM عs±ÏÌ B±¼ƒ¢0CQ@£ÛÎvΙ3‡±cÇz=×b±pøða5jă¼‚ÀÛ3Ï{ŠÃ âÿ hkåöíÛéÞ½;wïÞ¥{÷î,^¼ØtΞ=ËŠ´LÇEô Ee޼|»éý­[·˜7o^燅…ñÅ_’’Rè3zƒ/ (D_8t×ýðf> V«•Ù³g“‘‘ÁÔ©S©S§{öì¡iÓ¦´mÛ䃡ÈFi!`(Ža¹j÷AŽ;¦¿_ºtéCÛZ²dI6mÚôp ÿÃgö ¯Îýç^CQu÷¤®cŸ}6‹Å…Ë^â»ÍPLÀC(NÇßäÑ7¦kÀ6›äädJ–,iz¾‡=³¯¬ð0›Ac±8:Æ ÞØ!00–-[²`ÁSã[·n¬ÿݱcGºuëÀðá}غu‹È™3—X°`=}ûŽ÷è‡ÃQ00Šé™¡8 HÍÈâƒ5_’™™Y¬û[­VŽ=Zdm¥CϬÌ ÆeÆ+#ÇEüþûï©U«‡ƒóçÏÓ©S'ý»gÓ¯ßDdY!""‚äädDQ¤råÊ”)S‚7Þx‘åË·81ŽfÍšÎÕ«WÙºu+Ë–-Ó-nQÿçÌP”z¿ÿþ;5*6uoß¾—^zÉä)T­šE||°OlX\fp·5ÄK1lРdÓ¦M¤¤¤pýúuRSS1bÆÍA–ÕJOOçÔ©S\ºt‰¤¤$Μù›¹s×±|ùZNŸ¾D­Zµ8pà7n$++‹Å‹³gÏÝâ.t*3h (Nݘ2%ùì³ÏþÑ^¹reÓW­šezý_1ƒ;€¬ÿ¾ûî;zõêŃ\Ež{î9æÍ›Ç+¯¼B«V­p8H’D£Fôu°iÓz<ø+»ví¢víÚ\¸pÁtýæÍ›óí·ß2hÐ V­Zå±~ºûÜE™Ýî²qQ™Áî¸uë–çu&5+“ž•CÏö1cãŸÔTmðïããƒúÌzfÑí9×PîçïqŸoöÛiÓÌJ—þÙli@‡žY&0èKCq—ƒ-ZPµjU’’’¼ÖñóócÊ”)ܹsG§x­øûû3eÊÛ=z™¯¿þºÀû8W_}•öíÛÿkda²qQ€tèÐ!Z·n €,ËX,&ö}ž‰};aµˆøuT¨!¹k×.ºvíj,g•µ‡ÌÌídgïE2Ľ-s4ŽiUÚB£’"ý~ÎÓÏÿIì zõê;v¬@äçç3qâDîݻǨQ£ˆˆˆà‘Gaùòå¬_ÿm¡ ؽ{75kÖôImôe™x˜lìë2qnà ڴiCBBñññX,~X8Š™ov#Ðß¿ƒz®]»R¥Ê}Óg))ÓILHNÎQ¬Ö …ü°aÃPEQ6lX¡÷*$ð×™%íìIpèÌ w9Àn·{¥FoeÛ¶m<ú裼ð ”)S†Ù³g³gÏ.]ºôꉉ‰dffb±ˆìÚµäÁ—ï+jW.Çœ·ºS·fuªU«ÆõÏçñd“:>Û+ZûEÑ»œž~›Áƒ»j3øj ¼?kIÒ=¤ë=³‹JnÒ¤ çÎÓ‡•Ž;’››Kzz:GeäÈ‘ 6LgˆB‘\¾<ׯ_§råòˆ¢X$ÑÉ8 E•}̱½Ÿ#=;åàZ*•)iºçüQè²ö^¡× ñ°|ƒÑ€t×J”(áqþ–Ö*#G6îÞ½»®†††Ú°àà`¦OŸÎœ9sxõÕWéܹ3ãÆcîܹ…J±Zyê©§øá‡xì±zÅR …¶Šå"GŽÖÎ_´h 4(°Þ¥K—ÈÏ?ïa Tâãƒu—ÒW0h€ðEf¶jâPQSÂFŒAtt4sçÎ¥oß¾,_¾¼À›Œ=š¥K—2tèPFŽIBB¿üò‹.µ6lØ?þø£Àúo¾ù&;?ϼy#=ähM|˜eýOåh_ K#hªT©RèuçÏŸONξ‡Þ¿mÛ¶…JÍmÚ´Ñ¿3þí­4hÐÀÔ2žëÕkðc%£¨3`ÀfΜI­ZµHKKóªž8q‚.]ºð×_‘——Ghh(©©©\¸pñãÇ3hÐ ú÷ïïµñ½zõ¢E‹|õÕÞ{ïU¯çü¿ G»3G`` AAA«’©©”.ý !!]=¾KNMZÚ dYæQDQ¤[E‘16OA© ÙX–åAðå—‹9uêçÎcþüùЧO:ÄÊ•+q84kÖŒ5jФIê֭˸qã8~ü8¡¡¡lÞ¼™† zf&NœÈ¬YÓèÛ÷…̸LøùùýcÑI¾ñ¬Q_QÚ@>°¦ÀkÆÅÅ‘””DrrrÇ… ÈË;EBB›BìqxºQböwM@ÓüwïV#lW¯ÞfåÊìß¿ŸiÓ¦±jÕ**V¬ÈÚµk9räË—/çþýûX,^{í5&NœHll,ß|ó Ó¦Mã×_UýÛÆ™:u*çÎc„ ˆ¢ÈÞ½{Ù°aK:]»>ñÐÔ˜!((ˆ¬¬¬"3ƒ/#±Ý›&ˆíÞô¸Odd¤o±ŠTYN¥t鄆¾¦·!-m÷ïr‚!Šààéßß¿Ðv>lt8¬[¹‚\ç¾]ÝJ›23¼ŒF(ŠBXX˜\Ù¹s‰Û¹˜5kß}÷Ó§O§]»vŒ?^w - Ÿ}ö=zôðh\¿~ýL2í믿N›6m --W1a›>¯÷"##¹ÿ¾O`ð5r¨Àøª•R¥JÑ¡CV¬Xá3._¾ÌO|ø0§OŸöø|CK· “$I&)&&Æ‚8ºuÎöíq¦s5êÕ«Àš5kx饗HLLä¹çž£OŸ><ûì³’’Bnn.aaa„‡‡såÊvîÜÉÊ•+¹zõ*/¼ðõêÕcÊ” DEEË |á…!ºkyçÎ*W®Ìµk× t-Åvo"ý´ÚÅ v¿ƒÈÛ· Ùù¹ÿ“ƒÜüûxªV­ª kZúYddX¡mLIIC# }™ê Ѐúõë3gΜBm÷|ÎÑ£GsúôiÒÓÓMzÏë?çñq Å#bccõ™ôÅ‹$…mÛѽû>ÿ|a¡©æ­Zµ"&&†ãÇÂgŸ}ÆÂ… 9sæ ùùù¦uµyóæ¼øâ‹œ'ôkfddèE‘€€gè˜ÇŒ, ZÛT0dèLдÞ^ªT8Gë†{©SíO¯ÏýçÙ`láOQ·Ñ³XJ´ÓÛ"g@É¿ˆœ±%×{Ý”Ì*¬Þ|ó—eœPÇQ$IÑ Äš5kÒ 6—…Ò¹u×÷4¥v£¦”-¤Û7®§óǯ·‰°üʰ7n¬>Ì팆ø«&,AÌöÃoÛC)©nMN±Ð¤{úCÑSŽÅ‹ÇéVza€èÜù=vï^úÐkÁpñâEžõr ‰) ­—à•Nö’’bgÂÃÃjà!u½u¡ ö´WRCšaI…<ƨʓ-M¦? gÇ¡—øpÅáI„";î †lsèàÔÅj·ß Z¨^½º‹µÕ¬Y)'™9£3Ô°ñtx¡=eË!Ë ’$#IåÊñ|×jÔlÙ•ÁS›™-è~V@€?‹wóÁØ'¾Ú}ׯßVM6EÑ8ov‰¯ ôöåË—©Q£†~=Y–Y±)ƒ¯H,X÷ Àú÷îÝÃápèî^tt4V«•»w÷#Ë2²¬èö‡»"[\ï 9ãÊ•0ç3«vAü• <ÙÃ3†³`uZ<^—üü|Ãáp`·ÛqH rØpº×Ò{awHäççsþüy3ªW¯ÎÕ«Wyëå`üý®ÝôçÑæ ‘$‡CB’$óæÇ³Ýš1ki¬ÓÀ‚°°úVpSÐižÈýT ÷S-Œ™gy耕)S’uëfеë0}íÔ!ËŠÞá;¿Ç®]K<:׫D,«õ51,>>žš5kê&Ë2¤šÈÊuÍ„„ìv;’¤ö‡æ%\½ús’Ȧû¸ßÿaî#@ZÚÏ\¹(\½Z†›7@¶–÷8ÿêüü-äççc·ÛÕwÔø·CŒñ¨;bÌbS½³gϪ@ˆÕ]«¶ÍT‹>Àl6Y–M`$YG»,ËD– ƒG¸–àÏÁƒ¿"Ë2«WOUÝ,—$ºÿ—4î–NãnéüøKj¡RµjE&O~›°°v숣k×aÎŽ5[Õ]º eçÎÅúwfà1ƺ_~©ŠbW®\¡zõê&0$%¥xÀ ¦RŒÎv»ÚµkcµZÉÌÌ4õ‰ñoíþîºLက””#\½ZšÛ·÷RO@rHú`Á U’$¯A«!¡<ùä“`oÞTEŒçP¦¤ „rÑyØ3þÖ— FfÐÞ7nÅ7?EÒ¦McT7z¬^=•éÓß-’ïïïoãå—ŸeæÌ÷(]:RÔíÛãxñÅa(Š«“»vÆŽü~ï‡;@ÔCuÉvìˆàÆT«VÍpeêÆu½Ó{ì1, !!¿O… OšúC»‡k©À'fо‹Œ|œ*U)Wî ù¼Y–MË”û¡Ó“m¶áˆ¢HçÎMuEM'ðó³žér¥êEíFRtÚ3ÞÀø Qeùù¤?Š+WNâ­·¦¡( cÇÆ±fÍ4&MT À"б±èÕ«#K—N K—öÎN‘ ³TÕ/ºu$)të6‚/¾XäìXÙ÷CûÞ¸n«öŽ¢ë"·oß&44´@ €šŸÙºukDQ$0p?Šâ$bbâ©Xñ)g§Ê†IãÉCxxK=±rå[T®üLç«“Ò“Œ äÕFAÆfÛ„ tíÚU¯'ìØ§H’B#Ùº¸2Íq¹v©i~œºûéù•LW-SU†Ec®R»Âv"kŒAÞ~{†¾D 8•µk§;Ý®l¶m 3CA,݃Ÿ~¯êimK´ª}‚vU?!*ð/$}ÔÁ|ï½ÿ°téxPïÌ@’$†¾Q™CÇ’9u¾TˆÖ®‘xûÏ5=‚Í Çþz”¬ÀWq·ã¥A'ø3áIDºµûƒë—.²qãlz÷g²]D%Ø;±XVl(Ç¥û ­Ü‘ÖÎüÚQ„‰CnpáÔNÿy>}ƱqãlÝ&e™õëgØm¿þú+‡“ÙòµC_fd>þ<ŠgúufÕŽµ|}d$%Â/R¹ò3Î~1žšˆÊ,PÔð²1Æà~<¤¶s,6#vï>€Í›çѧÏxDaÝØs¬-÷Slž1ˆ\ÚÖ=B«Ê› ÷¿,ËŒ1¸¸1(ŠÂàÁ³X¾|"ï¾;Gø‹4vÃg%›Ön£¯Â/çg‚*e~` $Iâµ×&°nÝ 2•†¤gY±ÙB”ãØíÖ­›Ák¯MÐ-ö2¿c³ª2ö¶¯KÓ q””»õÈ—ü±Ù^î”HÜ‚X·n†î«Æ—Êšm£“$½Í’$qìØ1Þà‡Õ"™-ðö„ê,úøüJ¼‹(Šâ &Æ£°dœµ¡¡-LL`ƒàå³¢£ÈyŒª¥¢çÓOçЯß‚€5¢1‡¯÷gï±Xòíž/•ÁSõ¿! cq ßÕ— u™PX¾|¢Þïèáú¸{þj§?Ȱ !ôï?‰Õ«§ª3^VHNU×íŠQ): ¬^=•~ý>ÀáP퉔4 ¶€p¬VtØ29ùª\¿V&¡!eõkhƒml‹7•$‰€¼­¼ò¼Œ,£kòÓq‰ŒÜUNI]BQìN0HÄÄüíƒìᆅµ$66ݧÙZð·‚ŠÆæïD£Õ-êö7ßœ‚( Xý‚ÈzŽí¿tçÈïeðæY½Ð^¡U•mZS¼ˆ*š—a-Nê †}¶å×›=ysà4V­š¢§FGÕ†—ŒÈ#7×®ÏæU«¦0`Àd.'·àÔíŽì;Ó•÷Þo¨ÛÚ«â|ð¨Rv²2íäæÚuêÖA0χL¸u‹ ûgD(«ÉÈ´ÒT-Nž³cç/@!!¡¥ÓÒ—œV¿*ÇÄÄS©ÒÓ&+"¢•Ó0ôi~ûÌÅc‰&Œ9/¶nÝ  šît}ü‚£¸-wå“}ÏòëiÏIQÙ4©øÈy0(P(†\¼7é×黋þäû7bàà5|ôÑ$“Z—ñ ›E»"Ãd¤ç›D®>šÄ€³¹›Y—°¨*Y k´ rI÷nëõÒÓòõú1‚Åi‘y=‘e=i™VúލX—ÝßÎw“ [êPƒFŠS¸Nùòœ¡ö6ÄÆ¦ðo÷Á÷f/<`‚ƂɿEõX½z*ƒÏÒ?€ðJ\Î~‰Þ#ý¹t5À YT ?í!CkÂŽV‚‚2d¶›ú¦âÝwg²|ùDg†Dò³¦oæÉšë)®z~6Å9›Íªçòå4hšÉ‹±%PÑMÊ|D¹Ò½>@^žËû)li> ¡ýTmeøÔX.\¦YËX­"?þø£™9š»¥ñ)º8T¦L;ªTI.Ò,ÿ¿Y¬.)DI«¹£ô×C™«VMaРiúº0vìBfÏƾsÙœ<û#¯trý‚ht‰¿‰¿ßHO>QE AOóÒʇŽeèÐ9|øáXA5‡òeªñ—~ûwW;MÙf¹üv©‡®u¢nä6ªUJÃfSÈËSà¯*r-[6wÞ™ÁÖ{P.ðÁ¬ÝŽ#°o½x˜`±9Ÿ/9—Q‚ûn©ÚÕ3©_Ë•A4yØu^Q‹5í%0ЧžmÀO?ýdÚ®óf*V<… ¸®síZyîÝ;@™2¥ˆMõ9±°òý÷ßsæÜCˆºdL¶™ñ†@P‘§ÙY™(–Û~åtføè£I¼óÎ >üp,#GÎgÞ¼‘ȲBxd×®µgç÷{éúŒJw¥Â2LQB­=Š›q¡( qqc>|.qqc1bK–Œ#5)‘šßÑåÙTŽŸåʧpøÙÜ6Ø€$«, ©‘Š¢>¼¿-“ßנbéoøü«ÒL_ÈÌ9¯8íW}M¢Ug¿\ nµÀÝ$9¹"U*æQ¥bŸ,>Ïë#j±tÑWX,"ž~”ƒê[ÓT0Ÿ~x‘ ѹ,üÏN8‹¢(üöÛoEÈ;}è,ÍÌÜÀÀ¯ù´Œøª*æåýæ%ßBñB­*H»{F_{œÊªUª;¹lÙ Z å~R.!AjgÞJ­R€ÛãèQAð> 0wî(ÕV——»÷« H²¬˜Y’=?Ê”¸é<¢Ë–C–.ͨQóÕN2¦Þ£ Ëæ4 ]ÞÊÄI<Í<¤ Wzú1BCKW!?ÿ"wïöÃá¸H³fÍhÛ¢ð©=U*åRª)•ª˜ŒEcÎ"@é°tÀìÙ4kT†c'÷a·ÿMdä$ßäÈ‘X½ÑfÉÏÔßDæ°æœ=s*U#ð÷· (’$3~Ü0FØIóú!ÄM¹Bv¶È/W;¢þˆ‚9ßÀ½s³³sY¸ð}S§Øl"×Bˆ¨“ND¨DâßG)ÛŠÀ +—/¦"ŸÀ¿ë÷Žc#'5­uÊýÁñ;T"„?ÏfÓ¼•È0‰ŽmÙºé ÝzV&(Hä¿óç~Ò9¢œ)ƒ^½ÍoýèÞæ¿¬ŒH˜J«kólx¨zO¿=IÔ!û±÷t;º<ö-!þ9”Š”øtñ¦.ªÌôI›I¸yŸÁúè_t*º=II‡tVÐ&‰ iddl&)i$ÃÛo¿ÍäIwëpû-Øòã ÒKU­VÝ>ÐÂÐ ãŸ>òÍuG½!±6ê¾ýî{ßdüxuÿ¦°cÇ‡Š¶V¤, nµt$ œiúdf ?Ƶ„ R3ýÈÏ‘ˆŽÊ§É#™ÏÔŒ[»¶î)MRŠ êf¿ QQþtjüÆm?-E’Ø• óñŠ—i_û+Sý{É~4¨›Åºmh×!šÏý 3Àž}‘,\_k·¾u.SõË¥Fô)ýœ!“ª±ïHO=M㦮IQ¡÷x¾Ñ÷¦çýfóWU¤RÕG™4£Õ«W$&&ÆÍ<„Åb11§{ÐJÏNÑt|²*ÕÊ%P±t²W–:ùW0ç¯G‘-Er)¡ŒÎ5+Þ#ºd:µcîSÆ»xuáZ öÎæä_™8!3tho3¬I‹6ã³çMÇÆ=òÓ/á'Ý tx2¡y„ÉØ%i~ܸAj^-Þó‹—Œ+0ÓXÂëmW{Á]Ý’ROÒ¤Æ)*Fç’œbåÏ¿Ës_iG@P ²•ŽÓ²Î)$IàÄ™hnåµcƬÿ2wîA bD<±%S2$“¤?‹äÀõiÕ¾ÁÁVª•>CläQÅʾ#‘|±¯.OwŠeͪ¥ì^[@[!æŸÓ•d¸rß„;LXX ó¦> ¦Ô "C<3žÄ_à÷Ó%øü‡6T¯Yžm_šCÜ))GL‰*cÆ,båÊml˜ëOÛ¦V7©󯨲Ø9Èl˜@Û¦ëª\zôxÒ „=FñÙgs áeÉAS[6eûhÇeË& ( ï½÷–,ÏСê+ÀС ¹ö“J1®„q.­wìa¢ÀóX´h´¾¦j†œ¶1c1þ(ƒMR>¯˜ö5޽ùóGéëë¨Qó‰‹ã徊[Y6±˜úêÊFzÿýÎ,¦Šìß¿›Í¦QQQ¦=xð³)«jìØ8V®ÜFNNŽ)#9??Ÿ¼¼<òòòô”u»Ý®‰&$Y,¯J®àšÃ”Ò¦Ù­Zµ¢gϧ]Æb£Øºu)gO›\¤è™D®ÎX±â]Ö4U*†!Cf1sÆ(`¥ê¾Ø­¸D,O9U3t/ǰasœƒ$83£Ì®ga@2ù Šâ|E÷&Œƒ¯ý2üСsœJª¦…(NÀÈzàK³'´¶k©i~8–áÃçróæMÚ¶mËÏ?ÿl`ó¾Ãð𖤥ýìÑÒ÷ß_wkI¨ÆW÷=Æ­wÆÃ3¥ß³ž>‰¾ür±òâ‹Ãضm‘)> éð®\}ϽæÜ@…Aƒ¦³rå$ý³wÞ™ÁŠš’KïVpâLYnËÝ=–*±í>*ËŒÓï7l˜*O›Ÿ\WãŒV]e/ÍZ¶l¢+ÍSVt¯EÓ/\×L´X\ûàÁ³œb4'OžÔ9«Õêñ tF0¨?ZÞŠ*U¹z5Š7öb·;°ÛäçÛÉÏ·c·;L1õþ"‹hÐwDS¤W’dš6íE… GHHxœC‡Öë}n±ˆØlVUßܱ#Δîí"Ö銞Ÿ¨%«þ÷¿Sxë­i¬Z5EQx÷¹\Ù¯. ?üZ—œ€'Lþº1qÕÚ=4–ÑÄ-Ï gÐÆûN#ó¹Ú |ôÑ$¯Bë𦉉·¢(2hÐ4Õ+Y’ .˜h;88Ø+Ô<…g([âêÕh®^ý‡Ã¡ƒ@˱Ð&©+ÎàJšuBƒ=¨Xñ7=DžÐ‚cÇ>ÕëY­„;+F)×5,œ Œ3ì7&éÙÊL6cÖôaôn­z _ýþ<²_U¥AD=ã×óèjûÿ[Ñþå€ÕjÕ×tmã¬Ë…lAlì\[áÕl§k×Êÿs?‰F6Õ 1“VêÔéJ¥J§ Ûëe=8vòäç. Ê—_.vî0Â¥Ó{WÜ4@¼þúDÖ¯ŸIVz*‡¿û˜ÇŸìÆ‘kX³FÝã0uâBŽn•¹ÀtÉ=v®Î(єҮ•&³vítýõß*¸þÁØÇÏ0¼(‰¡îÏѯß|òÉLÿ„DýaW„ÐýàÔ`”ì´¿$*pùò7Ȳ–°«–cú€hØjÕ«w"&梳í²~mmÞ¸ñçÎíÔa‰ÒµëP¼ÁÛþ=oô¨uZ©Ü…Ô©ž‹,ÃOÔçÍ÷aõši$Çïã•§Oòõ‰'!¤žÇ/­­=˜ñßúôë÷6¸(üõ×'šÞûÃ÷æÖ¾öÚ6nœíñZ\_kûk¯MàÓOÿ£¸1e>77×dÜiÿÅÅ#+™¡"ññß’y“·ddUAˆíHL̼ïStçúõÄǃ°k×EQ kסÎßHòÜÈù0FP…Þ½Çqø³H*–ÍÓ¿¿x%Œ#¿Ùéù|.GÏ6D mkªc–GÍÔ¦!»OŸñzʹ±ôé3Nÿ¼89½{cÓ¦9…þm^þ|ƒ@Ÿ>j}mÖž­GQúYÚ/­¸@oñ 2‡õ˜ žyŠ®ë´iSË-uÝÁ•&"j•wî\B·nÃujv¢¾®¹ÿp£f ôî=ŽÍ›çñçÝ—ùáp©,äÛÂCs¨U#‚W†;#Ú›(ß8øFh¯‹HŸ>ãÙ¼yžþÞjµèÇ–-óéÓgV«E7Â|=z÷Ç–-ó¾·:Úu·nOïÞî×}ºnŸ>êu­V‹©ÏAÐ÷ZØl6Óà¹o³»r%Ü馋αA°Ð¦M=l6«~øùÙLïÕgPÿnÛ¶>‚`s†°­€ AÐ?ýo5ÎbEؽ{©bŒô¹– ×ZâbO¡FÓÜ}U÷|þ&³jÕ“û¥Ñ˜ÙêUñê«cÙºu¾ŽÚ‚f}£Ø¶m¡)`Š×ó{ô©ïƒ4¶Ð«ëé­ôì9Êp¾ÙµÔÜbQ [ý=ÐÔýg|\ÌàJ ™cÇκ¿¡Y³:Y Î}p2‚y}S™áŇ醛zNõÌuht·mÛ"ƒÛd6X\{aÍši 4Íô¹qÆYFÁDÑâÆfưXDÔZ£Lm+èèÑc;vÄ9ëº>W%ó¹Úu º¯ñèÙsÛ·ÇÎsõ‘¶vký`ü±R÷÷Î çXœû-­´hQßÄD*{©ŒÙ¬Y=ý\m¦ÊÆWãçVÁÂÿª»–k«IEND®B`‚SjaakII/misc/sjaakii.eng000644 000765 000024 00000000155 12433414314 016130 0ustar00eglebbkstaff000000 000000 plugin spec 0.0 sjaakii chess,shogi,xiangqi,shatranj,capablanca,dropchess,fairychess,shogivariants,asean SjaakII/cmake/FileList.cmake000644 000765 000024 00000000577 13014616475 016704 0ustar00eglebbkstaff000000 000000 set(SJAAK_SRC_FILES src/misc/ansi.c src/misc/aligned_malloc.c src/misc/cfgpath.c src/misc/genrand.c src/misc/keypressed.c src/misc/snprintf.c src/misc/softexp.c src/eval/pst.cc src/rules/game.cc src/rules/move.cc src/rules/san.cc src/rules/squares.cc src/hash/hashkey.c src/hash/hashtable.c src/hash/evalhash.c src/timer/timer.c ) SjaakII/cmake/FindReadline.cmake000644 000765 000024 00000000704 12433144250 017474 0ustar00eglebbkstaff000000 000000 # GNU Readline library finder if(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) set(READLINE_FIND_QUIETLY TRUE) endif(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h) FIND_LIBRARY(READLINE_LIBRARY NAMES readline) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR ) MARK_AS_ADVANCED(READLINE_INCLUDE_DIR READLINE_LIBRARY) SjaakII/include/aligned_malloc.h000644 000765 000024 00000000251 12433153270 017613 0ustar00eglebbkstaff000000 000000 #include #ifdef __cplusplus extern "C" { #endif void *aligned_malloc(size_t size, size_t align); void aligned_free(void *ptr); #ifdef __cplusplus } #endif SjaakII/include/ansi.h000644 000765 000024 00000001600 13014616475 015621 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2016 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ANSI_H #define ANSI_H #ifdef __cplusplus extern "C" { #endif extern void ansi_code(const char *s); #ifdef __cplusplus } #endif #endif SjaakII/include/assert.h000644 000765 000024 00000002142 12433153270 016163 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ASSERT_H #define ASSERT_H #include #include #ifdef DEBUGMODE #define assert(x) {if(!(x)){fprintf(stderr, "assert() failed in line %d of %s!\n", __LINE__, __FILE__); abort();}} #define trace(s) {fprintf(stderr, "%s in %s, line %d of %s.\n", s, __FUNCTION__, __LINE__, __FILE__);} #else #define assert(x) {} #define trace(x) {} #endif #endif SjaakII/include/betza_string.h000644 000765 000024 00000057677 13014616475 017413 0ustar00eglebbkstaff000000 000000 int slider_hopper_move_flags_to_betza(move_flag_t flags, char *buffer, size_t size) const { static struct { move_flag_t flags; const char *txt; } hopper_move_string[] = { { MF_SLIDER_H | MF_SLIDER_V | MF_SLIDER_D | MF_SLIDER_A, "Q" }, { MF_SLIDER_H | MF_SLIDER_V , "R" }, { MF_SLIDER_D | MF_SLIDER_A, "B" }, { MF_SLIDER_H , "sR" }, { MF_SLIDER_V , "vR" }, { MF_SLIDER_D , "rflbB" }, { MF_SLIDER_A, "lfrbB" }, { 0, NULL } }; int n = 0; const char *hop = ""; while (is_slider(flags) || is_hopper(flags)) { if (is_slider(flags)) { for (int k=0; hopper_move_string[k].flags; k++) { if ((flags & hopper_move_string[k].flags) == hopper_move_string[k].flags) { n += snprintf(buffer+n, size-n, "%s%s", hop, hopper_move_string[k].txt); flags &= ~hopper_move_string[k].flags; } } flags &= ~MF_SLIDER; } if (is_hopper(flags)) { flags |= (flags >> 4) & MF_SLIDER; hop = "p"; flags &= ~MF_HOPPER; } } return n; } int leaper_move_flags_to_betza(move_flag_t flags, char *buffer, size_t size) const { int n = 0; enum sym4_t { V, S, F, B, L, R, RF4, LF4, RB4, LB4, NUM_SYM4 }; enum sym8_t { FL, LF, FR, RF, BL, LB, BR, RB, FF, BB, LL, RR, FH, BH, LH, RH, NUM_SYM8 }; static struct { int dx, dy; const char *txt; int symmetry; } leaper_description[] = { { 1, 0, "W", 4 }, { 1, 1, "F", 4 }, { 2, 0, "D", 4 }, { 2, 1, "N", 8 }, { 2, 2, "A", 4 }, { 3, 0, "H", 4 }, { 3, 1, "C", 8 }, { 3, 2, "Z", 8 }, { 3, 3, "G", 4 }, { 0, 0, NULL, 0 } }; bitboard_t sym4[NUM_SYM4]; const char *sym4txt[NUM_SYM4]; bitboard_t sym8[NUM_SYM8]; const char *sym8txt[NUM_SYM8]; sym4txt[V] = "v"; sym4txt[S] = "s"; sym4txt[F] = "f"; sym4txt[B] = "b"; sym4txt[L] = "l"; sym4txt[R] = "r"; sym4txt[RF4] = "rf"; sym4txt[LF4] = "lf"; sym4txt[RB4] = "rb"; sym4txt[LB4] = "lb"; sym8txt[FH] = "fh"; sym8txt[BH] = "bh"; sym8txt[LH] = "lh"; sym8txt[RH] = "rh"; sym8txt[FF] = "ff"; sym8txt[BB] = "bb"; sym8txt[LL] = "ll"; sym8txt[RR] = "rr"; sym8txt[FL] = "fl"; sym8txt[LF] = "lf"; sym8txt[FR] = "fr"; sym8txt[RF] = "rf"; sym8txt[BL] = "bl"; sym8txt[LB] = "lb"; sym8txt[BR] = "br"; sym8txt[RB] = "rb"; /* Lame leapers */ const char *block = ""; if (is_masked_leaper(flags)) { move_flag_t f = flags & MF_LEAPER_MASK; flags &= ~MF_LEAPER_FLAGS; flags |= (f >> 8) | MF_IS_LEAPER; block = "n"; } /* Simple leapers */ if (is_simple_leaper(flags) || is_double_leaper(flags)) { int index = get_leaper_index(flags); int index2 = get_leaper_index2(flags); /* Some combination of simple leapers, this is a bit complicated. * We use the following algorithm: * - Find the square where the leaper has maximum mobility. * - Find the simple leaper that has the largest overlap (test the King first) * - If the overlap is complete, we are done. If not, we need to extract directions. * - Repeat until all compound leapers have been identified. */ /* Find the square where the leaper has best mobility. * We need this because the movement tables could have been hacked to exclude certain regions of the board. */ bitboard_t leaper; int square = 0; int max_moves = 0; for (int nn=0; nn bb = is_aleaper(flags) ? movegen.aleaper[WHITE][index][nn] : movegen.leaper[index][nn]; int moves = bb.popcount(); if (moves > max_moves || (moves == max_moves && nn < 4*files)) { /* Don't get stuck on back rank */ max_moves = moves; square = nn; leaper = bb; } } /* Construct symmetry masks */ if (is_aleaper(flags)) { bitboard_t fd, bd, fa, ba; int rank = unpack_rank(square); int file = unpack_file(square); int diag = bitboard_t::diagonal_nr[square]; int anti = bitboard_t::anti_diagonal_nr[square]; sym4[F] = bitboard_t::board_northward[rank]; sym4[B] = bitboard_t::board_southward[rank]; sym4[R] = bitboard_t::board_eastward[file]; sym4[L] = bitboard_t::board_westward[file]; sym4[V] = sym4[F] | sym4[B]; sym4[S] = sym4[R] | sym4[L]; sym4[RF4] = sym4[R] & sym4[L]; sym4[LF4] = sym4[L] & sym4[L]; sym4[RB4] = sym4[R] & sym4[B]; sym4[LB4] = sym4[L] & sym4[B]; sym8[FH] = sym4[F]; sym8[BH] = sym4[B]; sym8[LH] = sym4[L]; sym8[RH] = sym4[R]; fd.clear(); for (int n = diag+1; n<16; n++) fd |= bitboard_t::board_diagonal[n]; fa.clear(); for (int n = anti+1; n<16; n++) fa |= bitboard_t::board_antidiagonal[n]; bd.clear(); for (int n = 0; n < diag; n++) bd |= bitboard_t::board_diagonal[n]; ba.clear(); for (int n = 0; n < anti; n++) ba |= bitboard_t::board_antidiagonal[n]; sym8[FF] = fd & fa; sym8[BB] = bd & ba; sym8[LL] = fd & ba; sym8[RR] = bd & fa; sym8[FL] = sym8[FH] & sym8[LL]; sym8[LF] = sym8[LH] & sym8[FF]; sym8[FR] = sym8[FH] & sym8[RR]; sym8[RF] = sym8[RH] & sym8[FF]; sym8[BL] = sym8[BH] & sym8[LL]; sym8[LB] = sym8[LH] & sym8[BB]; sym8[BR] = sym8[BH] & sym8[RR]; sym8[RB] = sym8[RH] & sym8[BB]; } /* Find which combination of simple leapers matches best. * We try the non-atom king first. */ bitboard_t kb = movegen.make_leaper_bitboard(square, 1, 1)|movegen.make_leaper_bitboard(square, 1, 0); if (leaper == kb) { if (index2 == index && is_double_leaper(flags)) n += snprintf(buffer+n, size-n, "a"); n += snprintf(buffer+n, size-n, "K"); leaper.clear(); } /* Loop over all fundamental leapers. * FIXME: the loop can become infinite if the leaper is long (> 4) in any direction. */ while (!leaper.is_empty()) { bitboard_t best_leaper; int best = -1; int best_count = 0; for (int nn = 0; leaper_description[nn].txt; nn++) { int dx = leaper_description[nn].dx; int dy = leaper_description[nn].dy; bitboard_t bb = movegen.make_leaper_bitboard(square, dx, dy); int count = (bb & leaper).popcount(); if (count > best_count) { best_count = count; best = nn; best_leaper = bb; if (leaper == bb) break; } } if (best == -1) break; /* Break out if nothing matches */ if ((leaper & best_leaper) == best_leaper) { n += snprintf(buffer+n, size-n, "%s%s", block, leaper_description[best].txt); } else { assert(is_aleaper(flags)); /* Only some of the directions match, find out which */ if (leaper_description[best].symmetry == 4) { /* 4-point symmetry */ while (!(leaper & best_leaper).is_empty()) { int best = -1; int best_count = 0; for (int k = 0; k bb = sym4[k] & best_leaper & leaper; int count = bb.popcount(); if (count > best_count) { best_count = count; best = k; } } if (best == -1) break; /* Break out if nothing matches */ n += snprintf(buffer+n, size-n, "%s", sym4txt[best]); leaper &= ~(best_leaper & sym4[best]); } } else if (leaper_description[best].symmetry == 8) { /* 8-point symmetry */ while (!(leaper & best_leaper).is_empty()) { int best = -1; int best_count = 0; for (int k = 0; k bb = sym8[k] & best_leaper & leaper; int count = bb.popcount(); if (count > best_count) { best_count = count; best = k; } } if (best == -1) break; /* Break out if nothing matches */ n += snprintf(buffer+n, size-n, "%s", sym8txt[best]); leaper &= ~(best_leaper & sym8[best]); } } if (is_double_leaper(flags)) { if (index2 == index) n += snprintf(buffer+n, size-n, "%sa%s%s", block, block, leaper_description[best].txt); } else { n += snprintf(buffer+n, size-n, "%s%s", block, leaper_description[best].txt); } } leaper &= ~best_leaper; } } return n; } int stepper_move_flags_to_betza(move_flag_t flags, char *buffer, size_t size) const { if (!is_stepper(flags)) return 0; int n = 0; enum sym4_t { A, V, S, F, B, L, R, RF4, LF4, RB4, LB4, NUM_SYM4 }; bitboard_t sym4[NUM_SYM4]; const char *sym4txt[NUM_SYM4]; sym4txt[A] = ""; sym4txt[V] = "v"; sym4txt[S] = "s"; sym4txt[F] = "f"; sym4txt[B] = "b"; sym4txt[L] = "l"; sym4txt[R] = "r"; sym4txt[RF4] = "rf"; sym4txt[LF4] = "lf"; sym4txt[RB4] = "rb"; sym4txt[LB4] = "lb"; int square = bitboard_t::pack_rank_file(ranks/2, files/2); int rank = unpack_rank(square); int file = unpack_file(square); sym4[A] = bitboard_t::board_all; sym4[F] = bitboard_t::board_northward[rank]; sym4[B] = bitboard_t::board_southward[rank]; sym4[R] = bitboard_t::board_eastward[file]; sym4[L] = bitboard_t::board_westward[file]; sym4[V] = sym4[F] | sym4[B]; sym4[S] = sym4[R] | sym4[L]; sym4[RF4] = sym4[R] & sym4[F]; sym4[LF4] = sym4[L] & sym4[F]; sym4[RB4] = sym4[R] & sym4[B]; sym4[LB4] = sym4[L] & sym4[B]; bitboard_t atombb[2]; atombb[0] = movegen.make_leaper_bitboard(square, 1, 0); atombb[1] = movegen.make_leaper_bitboard(square, 1, 1); int si = get_stepper_index(flags); int dim = std::max(files, ranks)-1; /* Decompose the stepper. * The array index runs over the number of steps, bit indicate which directions are possible. */ #if defined _MSC_VER std::vector direction_mask(dim, 0); #else uint8_t direction_mask[dim]; memset(direction_mask, 0, dim); #endif /* Check for single stepper moves, which are generated in parallel */ for (int d=0; d<8; d++) { int max_c = (movegen.stepper_description[si][WHITE] >> (d*4)) & 15; if (max_c > dim) max_c = dim; for (int c = 0; c= 0; c--) { if (direction_mask[c] == 0) continue; /* Atoms that describe the motion, decompose in F and W */ const char *atom[2] = { "W", "F" }; char countstr[10]; countstr[0] = 0; if (c == dim-1) { atom[0] = "R"; atom[1] = "B"; } else if (c > 0) { snprintf(countstr, 10, "%d", c+1); } /* Construct move atom */ bitboard_t moves; for (int d=0; d<8; d++) { if ((direction_mask[c] & (1< bb = sym4[kk] & atombb[ai] & moves; int count = bb.popcount(); if (count > best_count) { best_count = count; best = kk; } } if (best == -1) break; /* Break out if nothing matches */ /* Make sure directions are unambiguous for diagonal atoms by repeating the last direction if needed. * Due to the way the matching works, the first iteration will always try to match the SVFBRL directions. * This maps (NW,NE,SE) -> fF + SE -> ffrbF. */ if (ai == 1 && strlen(sym4txt[best]) == 2 && k == 1) k += snprintf(s+k, sizeof(s)-k, "%c", s[k-1]); k += snprintf(s+k, sizeof(s)-k, "%s", sym4txt[best]); moves &= ~(sym4[best]&atombb[ai]); } n += snprintf(buffer+n, size-n, "%s%s%s", s, atom[ai], countstr); } /* Mask these directions for all lower-number directions */ for (int k = c-1; k>=0; k--) direction_mask[k] &= ~direction_mask[c]; } return n; } int rider_move_flags_to_betza(move_flag_t flags, char *buffer, size_t size) const { int n = 0; static struct { int dx, dy; const char *txt; int symmetry; } leaper_description[] = { { 1, 0, "W", 4 }, { 1, 1, "F", 4 }, { 2, 0, "D", 4 }, { 2, 1, "N", 8 }, { 2, 2, "A", 4 }, { 3, 0, "H", 4 }, { 3, 1, "C", 8 }, { 3, 2, "Z", 8 }, { 3, 3, "G", 4 }, { 0, 0, NULL, 0 } }; int index = get_rider_index(flags); for (int k=0; k<4; k++) for (int nn = 0; leaper_description[nn].txt; nn++) { int dx = leaper_description[nn].dx; int dy = leaper_description[nn].dy; if ((dx == movegen.rider_step[index][k].dx && dy == movegen.rider_step[index][k].dy) || (dy == movegen.rider_step[index][k].dx && dx == movegen.rider_step[index][k].dy)) { n += snprintf(buffer+n, size-n, "%s0", leaper_description[nn].txt); break; } } return n; } const char *move_flags_to_betza(move_flag_t flags, char *buffer = NULL, size_t size = 256) const { static char static_buffer[256] = { 0 }; int n = 0; if (buffer == NULL) { buffer = static_buffer; size = sizeof static_buffer; } buffer[0] = 0; if (flags == 0) goto done; buffer[0] = '?'; buffer[1] = 0; /* Describe slider moves */ n += slider_hopper_move_flags_to_betza(flags, buffer + n, size - n); flags &= ~MF_SLIDER; flags &= ~MF_HOPPER; if (flags == 0) goto done; /* Describe leaper moves */ n += leaper_move_flags_to_betza(flags, buffer + n, size - n); flags &= ~MF_LEAPER_FLAGS; if (flags == 0) goto done; /* Describe rider moves */ n += rider_move_flags_to_betza(flags, buffer + n, size - n); flags &= ~MF_RIDER; if (flags == 0) goto done; /* Describe stepper moves */ n += stepper_move_flags_to_betza(flags, buffer + n, size - n); flags &= ~MF_STEPPER; if (flags == 0) goto done; done: return buffer; } const char *piece_moves_to_betza(int piece, char *buffer = NULL, size_t size = 256) const { const char *post_movetype = "pn"; char s_string[256] = { 0 }; char m_string[256] = { 0 }; char c_string[256] = { 0 }; char i_string[256] = { 0 }; char b_string[256] = { 0 }; static char static_buffer[768] = { 0 }; char *s; int n = 0; if (buffer == NULL) { buffer = static_buffer; size = sizeof static_buffer; } buffer[0] = 0; move_flag_t move = pt.piece_move_flags[piece]; move_flag_t capture = pt.piece_capture_flags[piece]; move_flag_t special = pt.piece_special_move_flags[piece]; move_flag_t initial = pt.piece_initial_move_flags[piece]; move_flags_to_betza(move, m_string, sizeof(m_string)); move_flags_to_betza(capture, c_string, sizeof(c_string)); move_flags_to_betza(special, i_string, sizeof(i_string)); move_flags_to_betza(initial, b_string, sizeof(b_string)); /* Eliminate duplicates between the normal move and the initial move */ if ((s = strstr(i_string, m_string))) { size_t n1 = strlen(m_string); size_t n2 = strlen(i_string); char *p = s + n1; if (!isdigit(p[0])) for (size_t k = 0; k <= (n2-n1); k++) { s[k] = p[k]; s[k+1] = 0; if (s[k] == 0) break; } } if ((s = strstr(b_string, m_string))) { size_t n1 = strlen(m_string); size_t n2 = strlen(b_string); char *p = s + n1; if (!isdigit(p[0])) for (size_t k = 0; k <= (n2-n1); k++) { s[k] = p[k]; s[k+1] = 0; if (s[k] == 0) break; } } if (move == capture) { strncpy(s_string, m_string, sizeof s_string); m_string[0] = c_string[0] = 0; } /* Move options common to move and capture to a shared string */ if (c_string[0] && (s = strstr(c_string, m_string))) { if (s == c_string || isupper(s[-1])) { strncpy(s_string, m_string, sizeof s_string); m_string[0] = 0; size_t n1 = strlen(s_string); size_t n2 = strlen(c_string); char *p = s + n1; if (!isdigit(p[0])) for (size_t k = 0; k <= (n2-n1); k++) { s[k] = p[k]; s[k+1] = 0; if (s[k] == 0) break; } } } if (m_string[0] && (s = strstr(m_string, c_string))) { if (s == m_string || isupper(s[-1])) { strncpy(s_string, c_string, sizeof s_string); c_string[0] = 0; size_t n1 = strlen(s_string); size_t n2 = strlen(m_string); char *p = s + n1; if (!isdigit(p[0])) for (size_t k = 0; k <= (n2-n1); k++) { s[k] = p[k]; s[k+1] = 0; if (s[k] == 0) break; } } } if (strstr(s_string, "a")) { char *s = strstr(s_string, "a")+1; s[-1] = 0; n += snprintf(buffer+n, size-n, "mcpa%s", s); n += snprintf(buffer+n, size-n, "mcab%s", s); } /* Common between move and capture */ n += snprintf(buffer+n, size-n, "%s", s_string); /* Pure moves */ size_t l = strlen(m_string); for (size_t k=0; k kd = movegen.castle_king_dest[c][WHITE]; while (!kd.is_empty()) { bitboard_t bb = movegen.castle_mask[SHORT][WHITE] & movegen.castle_mask[LONG][WHITE]; int s1 = bb.bitscan(); int s2 = kd.bitscan(); int r = abs(s2 - s1); castle_range[c] |= 1< side = bitboard_t::board_east_edge | bitboard_t::board_west_edge; bitboard_t kd = movegen.castle_king_dest[c][WHITE]; while (!kd.is_empty()) { bitboard_t bb = movegen.castle_mask[SHORT][WHITE] & movegen.castle_mask[LONG][WHITE]; int s1 = bb.bitscan(); int s2 = kd.bitscan(); int r = abs(s2 - s1); kd.reset(s2); char ch = 's'; if (castle_range[c] & (1 << r)) ch = "rl"[c]; if (ch == 's' && c > SHORT) continue; n += snprintf(buffer+n, size-n, "i%c", ch); /* We need the "j" modifier if the castle partner is not a * "corner piece". */ bb = movegen.castle_mask[c][WHITE]; bb.reset(s1); int s3 = bb.bitscan(); if ((side & bb).is_empty() && bitboard_t::board_all.test(s3+1)) n += snprintf(buffer+n, size-n, "j"); n += snprintf(buffer+n, size-n, "O%d", r); } } } if (strlen(buffer) == 0) { /* Hack: try to convince XBoard that this piece can do anything */ snprintf(buffer, size, "QNCZt"); } /* Drop extension, * http://www.talkchess.com/forum/viewtopic.php?p=612101#612101 */ if (board.rule_flags & RF_USE_DROPS) { bool print = false; /* No drops on first file */ if ((pt.drop_zone[WHITE][piece] & bitboard_t::board_rank[0]).is_empty()) { n += snprintf(buffer+n, size-n, "j"); print = true; } /* Restriction on number of pieces of this type, board */ if (pt.piece_maximum[piece][WHITE]<128 && !(pt.piece_maximum[piece][WHITE]==1 && (pt.piece_flags[piece]&PF_ROYAL))) { print = true; for (int k = 0; k 0 && (pt.drop_zone[WHITE][piece] & bitboard_t::board_rank[last_rank-1]).is_empty()) last_rank--; if (last_rank != ranks) print = true; if (print) { n += snprintf(buffer+n, size-n, "@"); if (last_rank != ranks) n += snprintf(buffer+n, size-n, "%d", last_rank); } } return buffer; } SjaakII/include/bitboard.h000644 000765 000024 00000045141 12470166274 016467 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BITBOARD_H #define BITBOARD_H #include #include #include "assert.h" #include "bits.h" #include "squares.h" template class bitboard_t { public: kind bb; public: static int board_files, board_ranks; static uint32_t rank_mask; static uint32_t file_mask; static uint8_t diagonal_nr[sizeof(kind)*8]; static uint8_t anti_diagonal_nr[sizeof(kind)*8]; static bitboard_t king_zone[2][sizeof(kind)*8]; static bitboard_t neighbour_board[sizeof(kind)*8]; static bitboard_t square_bitboards[sizeof(kind)*8]; static bitboard_t board_empty; static bitboard_t board_all; static bitboard_t board_edge; static bitboard_t board_corner; static bitboard_t board_east_edge; static bitboard_t board_west_edge; static bitboard_t board_north_edge; static bitboard_t board_south_edge; static bitboard_t board_light; static bitboard_t board_dark; static bitboard_t board_centre; static bitboard_t board_xcentre; static bitboard_t board_xxcentre; static bitboard_t board_rank[16]; static bitboard_t board_file[16]; static bitboard_t board_file_mask; static bitboard_t board_south; static bitboard_t board_north; static bitboard_t board_northward[16]; static bitboard_t board_southward[16]; static bitboard_t board_eastward[16]; static bitboard_t board_westward[16]; static bitboard_t board_homeland[2]; static bitboard_t board_diagonal[32]; static bitboard_t board_antidiagonal[32]; static bitboard_t board_between[sizeof(kind)*8][sizeof(kind)*8]; static void initialise_bitboards(int files, int ranks); bitboard_t() : bb(0) { } bitboard_t(const kind &b) : bb(b) { } kind value() { return bb; } bitboard_t operator = (const bitboard_t& b) { bb = b.bb; return *this; } /* Default function definitions, should work with any normal integer * type, but can be overridden if a more optimal solution is * possible. */ void clear() { bb ^= bb; }; bool onebit() const { return (bb & (bb-1)) == 0; } bool twobit() const { if (onebit()) return false; kind b = bb & (bb-1); return (b & (b-1)) == 0; } int popcount() const { kind x = bb; int count = 0; while (x) { count++; x &= x-1; } return count; } int bitscan() const { kind x = bb; int i = 0; while (x && !(x&1)) { i++; x>>=1; } return i; } int msb() const { kind x = bb; int n = 8*sizeof(kind)-1; assert(x); while ((x&((kind)1< sshift(int bits) const { if (bits>0) return bitboard_t(bb << bits); return bitboard_t(bb >> (-bits)); } inline bool is_empty() const { return bb == 0; } /* Default definitions of operators */ inline bool operator == (const bitboard_t& b2) const { return bb == b2.bb; } inline bool operator != (const bitboard_t& b2) const { return !(*this == b2); } inline bitboard_t operator << (const int bits) const { return bitboard_t(bb << bits); } inline bitboard_t operator >> (const int bits) const { return bitboard_t(bb >> bits); } inline bitboard_t operator ~() const { return bitboard_t(~bb); } inline bitboard_t operator |(const bitboard_t &b2) const { return bitboard_t(bb | b2.bb); } inline bitboard_t operator &(const bitboard_t &b2) const { return bitboard_t(bb & b2.bb); } inline bitboard_t operator ^(const bitboard_t &b2) const { return bitboard_t(bb ^ b2.bb); } inline bitboard_t& operator |=(const bitboard_t &b2) { bb |= b2.bb; return *this; } inline bitboard_t& operator &=(const bitboard_t &b2) { bb &= b2.bb; return *this; } inline bitboard_t& operator ^=(const bitboard_t &b2) { bb ^= b2.bb; return *this; } inline uint32_t get_rank(int rank) const { assert(rank < board_ranks); bitboard_t b = (*this) >> (rank * board_files); return (uint32_t)(b.bb & rank_mask); } inline uint32_t get_file(int file) const { #if 0 uint32_t file_bits = 0; int n; int bit = file; for (n=0; n b(bb); b = (b >> file) & board_file_mask; do { b |= (b >> (shift * board_files)) << shift; shift <<= 1; //b |= (b >> board_files) << 1; //shift++; } while (shift < board_ranks); return (uint32_t)(b.bb & file_mask); #endif } bitboard_t fill_north() const { int shift = 1; bitboard_t b(bb); do { b |= (b << (shift * board_files)); shift <<= 1; } while (shift < board_ranks); return b; } inline bitboard_t fill_south() const { int shift = 1; bitboard_t b(bb); do { b |= (b >> (shift * board_files)); shift <<= 1; } while (shift < board_ranks); return b; } char *rank_string(int rank, char *buffer = NULL) const { static char static_buffer[256]; if (buffer == NULL) buffer = static_buffer; char *s = buffer; uint32_t bits = get_rank(rank); for (int bit = 0; bit < board_files; bit++) { if (bits & (1 << bit)) *s = '1'; else *s = '.'; s++; } *s = '\0'; return buffer; } void print(const char *msg = NULL) const { for (int n = 0; n int bitboard_t::board_files; template int bitboard_t::board_ranks; template uint32_t bitboard_t::rank_mask; template uint32_t bitboard_t::file_mask; template uint8_t bitboard_t::diagonal_nr[sizeof(kind)*8]; template uint8_t bitboard_t::anti_diagonal_nr[sizeof(kind)*8]; template bitboard_t bitboard_t::king_zone[2][sizeof(kind)*8]; template bitboard_t bitboard_t::neighbour_board[sizeof(kind)*8]; template bitboard_t bitboard_t::square_bitboards[sizeof(kind)*8]; template bitboard_t bitboard_t::board_empty; template bitboard_t bitboard_t::board_all; template bitboard_t bitboard_t::board_edge; template bitboard_t bitboard_t::board_corner; template bitboard_t bitboard_t::board_east_edge; template bitboard_t bitboard_t::board_west_edge; template bitboard_t bitboard_t::board_north_edge; template bitboard_t bitboard_t::board_south_edge; template bitboard_t bitboard_t::board_light; template bitboard_t bitboard_t::board_dark; template bitboard_t bitboard_t::board_centre; template bitboard_t bitboard_t::board_xcentre; template bitboard_t bitboard_t::board_xxcentre; template bitboard_t bitboard_t::board_rank[16]; template bitboard_t bitboard_t::board_file[16]; template bitboard_t bitboard_t::board_file_mask; template bitboard_t bitboard_t::board_south; template bitboard_t bitboard_t::board_north; template bitboard_t bitboard_t::board_northward[16]; template bitboard_t bitboard_t::board_southward[16]; template bitboard_t bitboard_t::board_eastward[16]; template bitboard_t bitboard_t::board_westward[16]; template bitboard_t bitboard_t::board_homeland[2]; template bitboard_t bitboard_t::board_diagonal[32]; template bitboard_t bitboard_t::board_antidiagonal[32]; template bitboard_t bitboard_t::board_between[sizeof(kind)*8][sizeof(kind)*8]; template inline void bitboard_t::initialise_bitboards(int files, int ranks) { board_ranks = ranks; board_files = files; assert(ranks * files <= 8*sizeof(kind)); rank_mask = (1< size) board_north_edge |= square_bitboards[n]; } board_edge = board_south_edge | board_north_edge | board_east_edge | board_west_edge; board_corner = (board_south_edge | board_north_edge) & (board_east_edge | board_west_edge); for (n=0; n> files); neighbour_board[n] |= (neighbour_board[n] & ~board_west_edge) >> 1; neighbour_board[n] |= (neighbour_board[n] & ~board_east_edge) << 1; king_zone[0][n] = king_zone[1][n] = neighbour_board[n]; king_zone[0][n] |= neighbour_board[n] << files; king_zone[1][n] |= neighbour_board[n] >> files; neighbour_board[n] &= ~square_bitboards[n]; king_zone[0][n] &= ~square_bitboards[n]; king_zone[1][n] &= ~square_bitboards[n]; } /* Set up diagonals and anti-diagonals * For the mapping of diagonal numbers, we pretend the board is square; it may not be, but this is easiest. */ int s = files; if (ranks > files) s = ranks; for (n = 0; n<32; n++) { diagonal_nr[n] = 255; anti_diagonal_nr[n] = 255; } for (n = 0; n::board_south = bitboard_t::board_north = bitboard_t::board_empty; for (n=0; n centre_files, centre_ranks; for (int f = files/2 - 1; f::anti_diagonal_nr[square] == anti_diagonal_nr[attack]) { for (int n=square;n<=(attack);n+=board_files-1) board_between[square][attack] |= square_bitboards[n]; } board_between[square][attack].reset(square); board_between[square][attack].reset(attack); board_between[attack][square] = board_between[square][attack]; } } board_file_mask = board_file[0]; } /* Specialisation for 32 bits: use optimised functions */ template<> inline int bitboard_t::popcount() const { return popcount32(bb); } template<> inline int bitboard_t::bitscan() const { return bitscan32(bb); } template<> inline int bitboard_t::lsb() const { return lsb32(bb); } template<> inline int bitboard_t::msb() const { return msb32(bb); } /* Specialisation for 64 bits: use optimised functions */ template<> inline int bitboard_t::popcount() const { return popcount64(bb); } template<> inline int bitboard_t::bitscan() const { return bitscan64(bb); } template<> inline int bitboard_t::lsb() const { return lsb64(bb); } template<> inline int bitboard_t::msb() const { return msb64(bb); } template<> inline int bitboard_t::popcount() const { return popcount128(bb); } template<> inline int bitboard_t::bitscan() const { return bitscan128(bb); } template<> inline int bitboard_t::lsb() const { return lsb128(bb); } template<> inline int bitboard_t::msb() const { return msb128(bb); } template<> inline bool bitboard_t::onebit() const { return onebit128(bb); } template<> inline bitboard_t bitboard_t::operator << (const int bits) const { return bitboard_t(shl128(bb, bits)); } template<> inline bitboard_t bitboard_t::operator >> (const int bits) const { return bitboard_t(shr128(bb, bits)); } template<> inline bool bitboard_t::test(int bit) const { return test128(bb, bit); } template<> inline bool bitboard_t::is_empty() const { return is_zero128(bb); } #ifndef HAVE_UINT128_T template<> inline uint32_t bitboard_t::get_rank(int rank) const { uint128_t b; b = shr128(bb, rank * board_files); return b.i64[0] & rank_mask; } template<> inline uint32_t bitboard_t::get_file(int file) const { uint32_t file_bits = 0; int n; int bit = file; for (n=0; n. */ #include "bits32.h" #include "bits64.h" #include "bits128.h" SjaakII/include/bits128.h000644 000765 000024 00000024462 12452217646 016100 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BITS128_H #define BITS128_H #include #include "bits64.h" #if defined __x86_64__ #define HAVE_UINT128_T 1 #endif #if HAVE_UINT128_T typedef __uint128_t uint128_t; static inline uint128_t u128(uint64_t u1, uint64_t u2) { return (uint128_t)u2 << 64 | u1; } static inline bool onebit128(uint128_t x) { return (x & (x-1)) == 0; } static inline int bitscan128(uint128_t x) { if (x & (uint128_t)0xFFFFFFFFFFFFFFFFll<<64) return 64 + bitscan64(x>>64); else return bitscan64(x); } static inline int lsb128(uint128_t x) { if ((x & (uint128_t)0xFFFFFFFFFFFFFFFFll) == 0) return 64 + lsb64(x>>64); else return lsb64(x); } static inline int msb128(uint128_t x) { if (x & (uint128_t)0xFFFFFFFFFFFFFFFFll<<64) return 64 + msb64(x>>64); else return msb64(x); } static inline int popcount128(uint128_t x) { return popcount64(x >> 64) + popcount64(x); } static inline uint128_t shr128(uint128_t x, int bits) { return x >> bits; } static inline uint128_t shl128(uint128_t x, int bits) { return x << bits; } static inline uint128_t mul128(uint128_t x, uint128_t y) { return x * y; } static inline uint128_t sshift128(uint128_t x, int s) { if (s>0) return shl128(x, s); return shr128(x, -s); #if 0 signed char left = (signed char) s; signed char right = -((signed char)(s >> 8) & left); return shl128(shr128(x, right), right + left); #endif } static inline bool test128(uint128_t x, int bit) { return shr128(x, bit) & 0x01; } static inline bool is_zero128(uint128_t x) { return x == 0; } static inline bool is_equal128(uint128_t x, uint128_t y) { return x == y; } #else #ifdef __cplusplus struct uint128_t { uint64_t i64[2]; uint128_t() { i64[0] = 0; i64[1] = 0; } uint128_t(int i) { i64[0] = i; i64[1] = 0; } uint128_t(uint64_t u1, uint64_t u2) { i64[0] = u1; i64[1] = u2; } inline uint128_t operator << (const int bits) const { uint64_t u1 = i64[0]; uint64_t u2 = i64[1]; if (bits >= 64) { u2 = u1 << (bits-64); u1 = 0; } else if (bits > 0) { uint64_t uu = u1 >> (64 - bits); u2 <<= bits; u1 <<= bits; u2 |= uu; } return uint128_t(u1, u2); } inline uint128_t operator >> (const int bits) const { uint64_t u1 = i64[0]; uint64_t u2 = i64[1]; if (bits >= 64) { u1 = u2 >> (bits-64); u2 = 0; } else if (bits > 0) { uint64_t uu = u2 << (64 - bits); u1 >>= bits; u2 >>= bits; u1 |= uu; } return uint128_t(u1, u2); } inline bool operator == (const uint128_t y) const { return (i64[0] == y.i64[0]) && (i64[1] == y.i64[1]); } inline bool operator != (const uint128_t y) const { return !(*this == y); } inline uint128_t operator |=(const uint128_t &x) { i64[0] |= x.i64[0]; i64[1] |= x.i64[1]; return *this; } inline uint128_t operator &=(const uint128_t &x) { i64[0] &= x.i64[0]; i64[1] &= x.i64[1]; return *this; } inline uint128_t operator ^=(const uint128_t &x) { i64[0] ^= x.i64[0]; i64[1] ^= x.i64[1]; return *this; } inline uint128_t operator +=(const uint128_t &x) { i64[0] += x.i64[0]; i64[1] += x.i64[1]; return *this; } inline uint128_t operator -=(const uint128_t &x) { i64[0] -= x.i64[0]; i64[1] -= x.i64[1]; return *this; } inline uint128_t operator ~ () const { return uint128_t(~i64[0], ~i64[1]); } inline uint128_t operator | (const uint128_t &x) const { return uint128_t(*this) |= x; } inline uint128_t operator & (const uint128_t &x) const { return uint128_t(*this) &= x; } inline uint128_t operator ^ (const uint128_t &x) const { return uint128_t(*this) ^= x; } inline uint128_t operator + (const uint128_t &x) const { return uint128_t(*this) += x; } inline uint128_t operator - (const uint128_t &x) const { return uint128_t(*this) -= x; } }; inline uint128_t operator - (int lhs, const uint128_t &x) { return uint128_t(lhs) -= x; } inline uint128_t operator + (int lhs, const uint128_t &x) { return uint128_t(lhs) += x; } static inline uint128_t u128(uint64_t u1, uint64_t u2) { return uint128_t(u1, u2); } static inline bool onebit128(uint128_t x) { uint64_t u1 = x.i64[0]; uint64_t u2 = x.i64[1]; return onebit64(u1 ^ u2) && onebit64(u1 | u2); } static inline int bitscan128(uint128_t x) { if (x.i64[1]) return 64 + bitscan64(x.i64[1]); else return bitscan64(x.i64[0]); } static inline int lsb128(uint128_t x) { if (x.i64[0] == 0) return 64 + lsb64(x.i64[1]); else return lsb64(x.i64[0]); } static inline int msb128(uint128_t x) { if (x.i64[1]) return 64 + msb64(x.i64[1]); else return msb64(x.i64[0]); } static inline int popcount128(uint128_t x) { return popcount64(x.i64[0]) + popcount64(x.i64[1]); } static inline uint128_t shr128(uint128_t x, int bits) { return x >> bits; } static inline uint128_t shl128(uint128_t x, int bits) { return x << bits; } static inline bool test128(uint128_t x, int bit) { if (bit < 64) return (x.i64[0] >> bit) & 0x01; return (x.i64[1] >> (bit-64)) & 0x01; } static inline bool is_zero128(uint128_t x) { uint64_t x1 = x.i64[0]; uint64_t x2 = x.i64[1]; return (x1 | x2) == 0; } static inline bool is_equal128(uint128_t x, uint128_t y) { return x == y; } #else typedef uint64_t uint128_t __attribute__ ((vector_size(sizeof(uint64_t)*2), aligned(8))); static inline uint128_t u128(uint64_t u1, uint64_t u2) { return (uint128_t){u1, u2}; } static inline bool onebit128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; uint64_t u1 = xx.i64[0]; uint64_t u2 = xx.i64[1]; return onebit64(u1 ^ u2) && onebit64(u1 | u2); } static inline int bitscan128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; if (xx.i64[1]) return 64 + bitscan64(xx.i64[1]); else return bitscan64(xx.i64[0]); } static inline int lsb128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; if (xx.i64[0] == 0) return 64 + lsb64(xx.i64[1]); else return lsb64(xx.i64[0]); } static inline int msb128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; if (xx.i64[1]) return 64 + msb64(xx.i64[1]); else return msb64(xx.i64[0]); } static inline int popcount128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; return popcount64(xx.i64[0]) + popcount64(xx.i64[1]); } static inline uint128_t shr128(uint128_t x, int bits) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t y; y.i128 = x; uint64_t u1 = y.i64[0]; uint64_t u2 = y.i64[1]; if (bits >= 64) { u1 = u2 >> (bits-64); u2 = 0; } else if (bits > 0) { uint64_t uu = u2 << (64 - bits); u1 >>= bits; u2 >>= bits; u1 |= uu; } return (uint128_t){u1, u2}; } static inline uint128_t shl128(uint128_t x, int bits) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; uint64_t u1 = xx.i64[0]; uint64_t u2 = xx.i64[1]; if (bits >= 64) { u2 = u1 << (bits-64); u1 = 0; } else if (bits > 0) { uint64_t uu = u1 >> (64 - bits); u2 <<= bits; u1 <<= bits; u2 |= uu; } return (uint128_t){u1, u2}; } static inline uint128_t mul128(uint128_t x, uint128_t y) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx, yy; xx.i128 = x; yy.i128 = y; uint64_t x1 = xx.i64[0]; uint64_t x2 = xx.i64[1]; uint64_t y1 = yy.i64[0]; uint64_t y2 = yy.i64[1]; uuint128_t r, q, w; /* Multiply upper word */ w.i128 = u128(0, y1*x2 + x1*y2); /* Lower word, but we have to be careful about carries */ q.i128 = u128(y1,0); r.i128 = u128(0,0); while (x1 > 0) { if (x1 & 0x1) { uint64_t r1 = r.i64[0]; r.i128 += q.i128; /* Carry */ r.i64[1] += (r.i64[0] < r1 || r.i64[0] < q.i64[0]); } int n = 1; x1 >>= 1; while (x1 && !(x1 & 0x1)) {x1>>=1; n++;} q.i128 = shl128(q.i128, n); } q.i128 = r.i128 + w.i128; if (q.i64[0] < r.i64[0] || q.i64[0] < w.i64[0]) q.i64[1]++; return q.i128; } static inline uint128_t sshift128(uint128_t x, int s) { if (s>0) return shl128(x, s); return shr128(x, -s); #if 0 signed char left = (signed char) s; signed char right = -((signed char)(s >> 8) & left); return shl128(shr128(x, right), right + left); #endif } static inline bool test128(uint128_t x, int bit) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; x = shr128(x, bit); uuint128_t xx; xx.i128 = x; uint64_t u1 = xx.i64[0]; return u1 & 0x01; } static inline bool is_zero128(uint128_t x) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx; xx.i128 = x; uint64_t x1 = xx.i64[0]; uint64_t x2 = xx.i64[1]; return (x1 | x2) == 0; } static inline bool is_equal128(uint128_t x, uint128_t y) { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t xx, yy; xx.i128 = x; yy.i128 = y; uint64_t x1 = xx.i64[0]; uint64_t x2 = xx.i64[1]; uint64_t y1 = yy.i64[0]; uint64_t y2 = yy.i64[1]; return x1 == y1 && x2 == y2; } #endif #endif #endif SjaakII/include/bits32.h000644 000765 000024 00000004715 12452217646 016011 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BITS32_H #define BITS32_H #include #include "bool.h" #if defined _MSC_VER # include #endif static inline bool onebit32(uint32_t x) { return (x & (x-1)) == 0; } static inline int lsb32(uint32_t x) { #ifdef __GNUC__ return __builtin_ctz (x); #elif defined _MSC_VER unsigned long res; _BitScanForward(&res, x); return (int)res; #else int n = 0; assert(x); while ((x&((uint32_t)1<> 8) & left); return (x >> right) << (right + left); } static inline int bitscan32(uint32_t x) { #ifdef __GNUC__ return __builtin_ctz (x); #elif defined _MSC_VER unsigned long res; _BitScanForward(&res, x); return (int)res; #else int i = 0; assert(x); while (!(x & 1)) { i++; x >>= 1; } return i; #endif } /* Return the number of bits set on a bitboard * From http://chessprogramming.wikispaces.com/Population+Count */ static inline int popcount32(uint32_t x) { #ifdef __GNUC__ return __builtin_popcount(x); #else const uint32_t k1 = 0x55555555; const uint32_t k2 = 0x33333333; const uint32_t k4 = 0x0f0f0f0f; const uint32_t kf = 0x01010101; x = x - ((x >> 1) & k1); x = (x & k2) + ((x >> 2) & k2); x = (x + (x >> 4)) & k4; x = (x * kf) >> 24; return (int) x; #endif } #endif SjaakII/include/bits64.h000644 000765 000024 00000006534 12452217646 016017 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BITS64_H #define BITS64_H #include #include "bool.h" #if defined _MSC_VER # include # if !defined _M_AMD64 && !defined _M_X64 inline void _BitScanForward64(unsigned long *res, uint64_t b) { unsigned __int32 l, h; l = uint32_t(b & 0xffffffffu); h = uint32_t(b >> 32); if (l) { _BitScanForward(res, l); } else { _BitScanForward(res, h); *res += 32; } } inline void _BitScanReverse64(unsigned long *res, uint64_t b) { unsigned __int32 l, h; l = uint32_t(b & 0xffffffffu); h = uint32_t(b >> 32); if (h) { _BitScanReverse(res, h); *res += 32; } else { _BitScanReverse(res, l); } } #endif #endif static inline bool onebit64(uint64_t x) { return (x & (x-1)) == 0; } static inline int bitscan64(uint64_t x) { #ifdef __GNUC__ return __builtin_ctzll (x); #elif defined _MSC_VER unsigned long res; _BitScanForward64(&res, x); return (int)res; #else int i = 0; assert(x); while (!(x & 1)) { i++; x >>= 1; } return i; #endif } static inline int lsb64(uint64_t x) { #ifdef __GNUC__ return __builtin_ctzll (x); #elif defined _MSC_VER unsigned long res; _BitScanForward64(&res, x); return (int)res; #else int n = 0; assert(x); while ((x&((uint64_t)1<> 8) & left); return (x >> right) << (right + left); } static inline int bitscan16(uint16_t x) { #ifdef __GNUC__ return __builtin_ctz (x); #elif defined _MSC_VER unsigned long res; _BitScanForward(&res, x); return res; #else int i = 0; assert(x); while (!(x & 1)) { i++; x >>= 1; } return i; #endif } /* Return the number of bits set on a bitboard * From http://chessprogramming.wikispaces.com/Population+Count */ static inline int popcount64(uint64_t x) { #ifdef __GNUC__ return __builtin_popcountll(x); #else const uint64_t k1 = 0x5555555555555555ll; const uint64_t k2 = 0x3333333333333333ll; const uint64_t k4 = 0x0f0f0f0f0f0f0f0fll; const uint64_t kf = 0x0101010101010101ll; x = x - ((x >> 1) & k1); x = (x & k2) + ((x >> 2) & k2); x = (x + (x >> 4)) & k4; x = (x * kf) >> 56; return (int) x; #endif } #endif SjaakII/include/board.h000644 000765 000024 00000051224 13017310267 015756 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BOARD_H #define BOARD_H #include "compilerdef.h" #include "bitboard.h" #include "pieces.h" #include "piece_types.h" #include "move.h" #include "hashkey.h" #include "squares.h" #include "ansi.h" /* Possible rule flags */ #define RF_FORCE_CAPTURE 0x00000001 #define RF_MULTI_CAPTURE 0x00000002 #define RF_KEEP_CAPTURE 0x00000004 /* Captured pieces go to your hand */ #define RF_RETURN_CAPTURE 0x00000008 /* Captured pieces go back to their owner's hand */ #define RF_USE_CAPTURE (RF_KEEP_CAPTURE | RF_RETURN_CAPTURE) #define RF_KING_TABOO 0x00000010 /* Kings cannot face eachother along a ray */ #define RF_KING_TRAPPED 0x00000020 /* Kings are trapped in a "palace" */ #define RF_CHECK_ANY_KING 0x00000040 /* If there are multiple kings, attacking any one of them counts as check. */ #define RF_KING_DUPLECHECK 0x00000080 /* It's check if all kings are under attack, if there is more than one. */ #define RF_ALLOW_DROPS 0x00000100 /* The game allows drop moves */ #define RF_FORCE_DROPS 0x00000200 /* Drops are forced if possible */ #define RF_GATE_DROPS 0x00000400 /* Drops work as S-chess gates */ #define RF_USE_DROPS 0x00000700 /* Game uses drops */ #define RF_ALLOW_PICKUP 0x00000800 /* The player is allowed to take pieces in-hand */ #define RF_PROMOTE_IN_PLACE 0x00001000 /* Promotions can be done in-place, without moving a piece */ #define RF_PROMOTE_ON_DROP 0x00002000 /* Pieces can promote when they are dropped */ #define RF_SPECIAL_IS_INIT 0x00004000 /* Special moves are only initial moves */ #define RF_VICTIM_SIDEEFFECT 0x00008000 /* Capture victims may have side effects */ #define RF_USE_HOLDINGS (RF_USE_DROPS | RF_USE_CAPTURE | RF_ALLOW_PICKUP) /* Game uses holdings in some way */ #define RF_USE_SHAKMATE 0x00010000 /* The checking sequence prior to mate needs particular pieces */ #define RF_USE_BARERULE 0x00020000 /* Shatranj-style baring rule */ #define RF_USE_CHASERULE 0x00040000 /* Xiangqi-style chase rule is in effect */ #define RF_QUIET_PROMOTION 0x00080000 /* Promotion moves must be quiet moves (Sittuyin style) */ #define RF_CAPTURE_ANY_FLAG 0x00100000 /* There is a "capture the flag" victory condition. */ #define RF_CAPTURE_ALL_FLAG 0x00200000 /* There is a "capture the flag" victory condition. */ #define RF_CAPTURE_THE_FLAG (RF_CAPTURE_ANY_FLAG | RF_CAPTURE_ALL_FLAG) /* There is a "capture the flag" victory condition. */ #define RF_NO_MOVE_PAST_CHECK 0x00400000 /* Sliding royals may not slide through check */ #define RF_PROMOTE_BY_MOVE 0x00800000 /* Promote by moving as target piece */ /* Board state */ #define BF_CHECK 0x0001 /* Whether the side to move is in-check or not */ #define BF_WSHAK 0x0002 /* Whether a "shak" was given or not */ #define BF_BSHAK 0x0004 /* Whether a "shak" was given or not */ #define BF_NO_RETALIATE 0x0008 /* Whether retaliation is allowed or not */ template struct unmake_info_t { bitboard_t init; bitboard_t ep; uint64_t board_hash; uint64_t hash; int8_t fifty_counter; int8_t check_count[2]; int8_t ep_victim; uint8_t board_flags; uint8_t pickup_piece[4]; #ifdef DEBUGMODE move_t move; #endif }; template struct board_t { bitboard_t bbc[NUM_SIDES]; bitboard_t bbp[MAX_PIECE_TYPES]; bitboard_t flag[NUM_SIDES]; /* Flag bitboard, for "capture the flag" */ bitboard_t royal; bitboard_t init; bitboard_t ep; int8_t piece[8 * sizeof(kind)]; /* Piece holdings. * These are indexed by [piece type][side to move] * We actually have a rule flag to specify whether we're interested in these or not, so we can skip a chunk * of code for variants where we're not. */ int8_t holdings[MAX_PIECE_TYPES][NUM_SIDES]; /* Hash key */ uint64_t hash; uint64_t board_hash; /* Rule flags, to change the behaviour of the move generator or the evaluation function */ uint32_t rule_flags; /* Record the board state, in check, castle status */ uint8_t board_flags; /* En-passant target square and capture location. */ int8_t ep_victim; /* Half-move clock (50-move counter) */ int8_t fifty_counter; /* Check count */ int8_t check_count[2]; /* Side to move */ side_t side_to_move; /* Description of all piece types */ piece_description_t *piece_types; int virtual_files; int virtual_ranks; int bit_to_square[128]; bool check() const { return (board_flags & BF_CHECK); } void check(bool chk) { board_flags &= ~BF_CHECK; board_flags |= uint8_t(chk); if (!chk) board_flags &= ~(BF_WSHAK << side_to_move); if (chk) check_count[side_to_move]++; } void shak() { board_flags |= (BF_WSHAK << side_to_move); } bool have_shak() { return (board_flags & (BF_WSHAK << side_to_move)) != 0; } bool retaliate_ok() const { return !(board_flags & BF_NO_RETALIATE); } void clear() { for (side_t side = WHITE; sidepiece_flags[type] & PF_ROYAL) royal.set(square); hash ^= piece_key[type][side][square]; board_hash ^= piece_key[type][side][square]; } void clear_piece(int type, side_t side, int square) { assert(bbc[side].test(square)); assert(bbp[type].test(square)); assert(piece[square] == type); bbc[side].reset(square); bbp[type].reset(square); royal.reset(square); init.reset(square); hash ^= piece_key[type][side][square]; board_hash ^= piece_key[type][side][square]; } void put_new_piece(int type, side_t side, int square) { put_piece(type, side, square); init.set(square); } int8_t get_piece(int square) const { assert(square >= 0); assert(square < 8*sizeof(kind)); return piece[square]; } side_t get_side(int square) const { if (bbc[WHITE].test(square)) return WHITE; if (bbc[BLACK].test(square)) return BLACK; return NONE; } bitboard_t get_occupied() const { return bbc[WHITE] | bbc[BLACK]; } int piece_count(int piece, side_t side) const { return (bbc[side] & bbp[piece]).popcount(); } int locate_least_valued_piece(bitboard_t mask) const { int *perm = piece_types->val_perm; for (int n=0; nnum_piece_types; n++) { if (!(bbp[perm[n]] & mask).is_empty()) return (bbp[perm[n]] & mask).bitscan(); } return -1; } void print_move(move_t move) const { side_t swap_side[3]; int swap_piece[3]; int swap_to[3]; int n; /* Second: resolve all pickups */ n = get_move_pickups(move); printf("\n%d pickups\n", n); for (int c=0; cpiece_abbreviation[piece][side], side); } /* Third: resolve all swaps */ n = get_move_swaps(move); printf("%d swaps\n", n); for (int c=0; cpiece_abbreviation[swap_piece[c]][swap_side[c]]); } /* Fourth: resolve all drops */ n = get_move_drops(move); printf("%d drops\n", n); for (int c=0; cpiece_abbreviation[piece][side], side); } /* Fifth: update holdings */ if ((rule_flags & RF_USE_HOLDINGS) && get_move_holdings(move)) { uint16_t p = get_move_holding(move); int count = decode_holding_count(p); int piece = decode_holding_piece(p); side_t side = decode_holding_side(p); printf("Holdings %s %d\n", piece_types->piece_abbreviation[piece][side], count); } } void makemove(move_t move, unmake_info_t *ui) { side_t swap_side[3]; int swap_piece[3]; int swap_to[3]; int n; /* First: backup information for unmake */ ui->init = init; ui->hash = hash; ui->board_hash = board_hash; ui->fifty_counter = fifty_counter; ui->ep = ep; ui->ep_victim = ep_victim; ui->board_flags = board_flags; ui->check_count[WHITE] = check_count[WHITE]; ui->check_count[BLACK] = check_count[BLACK]; #ifdef DEBUGMODE ui->move = move; #endif board_flags &= ~BF_NO_RETALIATE; /* Second: resolve all pickups */ n = get_move_pickups(move); for (int c=0; cpickup_piece[c] = piece_for_side(piece, side); clear_piece(piece, side, square); if ((piece_types->piece_flags[piece] & PF_NO_RETALIATE) && side != side_to_move) board_flags |= BF_NO_RETALIATE; } /* Third: resolve all swaps */ n = get_move_swaps(move); for (int c=0; cpiece_flags[swap_piece[c]] & PF_NO_RETALIATE) && swap_side[c] == side_to_move) board_flags &= ~BF_NO_RETALIATE; clear_piece(swap_piece[c], swap_side[c], from); } for (int c=0; c 0) hash ^= hold_key[piece][side][holdings[piece][side]]; } /* Sixth: update status bits */ ep_victim = 0; ep.clear(); if (move & MOVE_SET_ENPASSANT) { ep = bitboard_t::board_between[get_move_from(move)][get_move_to(move)]; ep_victim = get_move_to(move); } /* Seventh: flip side to move */ if (expect((move & MOVE_KEEP_TURN) == 0, true)) { side_to_move = next_side[side_to_move]; hash ^= side_to_move_key; board_hash ^= side_to_move_key; } /* Finally: update 50-move clock */ fifty_counter++; if (move & MOVE_RESET50) fifty_counter = 0; /* Assume we're not in check */ check(false); } void unmakemove(move_t move, unmake_info_t *ui) { side_t swap_side[3]; int swap_piece[3]; int swap_to[3]; int n; /* First: flip side to move */ if (expect((move & MOVE_KEEP_TURN) == 0, true)) side_to_move = next_side[side_to_move]; /* Second: reverse all drops */ n = get_move_drops(move); for (int c=0; cpickup_piece[c]); side_t side = side_for_piece(ui->pickup_piece[c]); put_piece(piece, side, square); } /* Fifth: update holdings */ if (expect((rule_flags & RF_USE_HOLDINGS) && get_move_holdings(move), false)) { uint16_t p = get_move_holding(move); int count = decode_holding_count(p); int piece = decode_holding_piece(p); side_t side = decode_holding_side(p); holdings[piece][side] -= count; assert(holdings[piece][side] >= 0); } /* Finally: restore backedup information */ init = ui->init; hash = ui->hash; board_hash = ui->board_hash; fifty_counter = ui->fifty_counter; ep = ui->ep; ep_victim = ui->ep_victim; board_flags = ui->board_flags; check_count[WHITE] = ui->check_count[WHITE]; check_count[BLACK] = ui->check_count[BLACK]; } void print(FILE* file = stdout, bitboard_t xmark = bitboard_t::board_empty, bitboard_t omark = bitboard_t::board_empty, bool ansi = true) const { const char *bg_colour_string[] = { "\033[45m", "\033[46m", "\033[44m" }; char mark[256]; int colour[256]; int pieces[256]; bool occupied[256]; side_t side[256]; bitboard_t occ = get_occupied(); int c, n; for (int r=0; r::board_ranks; r++) { for (int f=0; f::board_files; f++) { int bit = bitboard_t::pack_rank_file(r, f); int square = bit_to_square[bit]; if (square < 0) continue; colour[square] = ((square / virtual_files) ^ (square % virtual_files)) & 1; if (!bitboard_t::board_all.test(bit)) colour[square] = 2; if (omark.test(bit)) mark[square] ='*'; if (xmark.test(bit)) mark[square] ='+'; if (occ.test(bit)) { occupied[square] = true; side[square] = BLACK; if (bbc[WHITE].test(bit)) side[square] = WHITE; pieces[square] = get_piece(bit); } else { occupied[square] = false; } } } if (file != stdout) ansi = false; for (c=virtual_ranks-1; c>=0; c--) { fprintf(file, "%2s", rank_names[c]); if (ansi) ansi_code("\033[1m"); for (n=0; npiece_abbreviation[piece][1-white]); } else { if (ansi) fprintf(file, " %c", mark[square]); else fprintf(file, "%c%c", colour[square] ? '+' : '.', mark[square]); } } if (ansi) ansi_code("\033[0m"); if (side_to_move == BLACK && c == virtual_ranks-1) fprintf(file, "*"); if (side_to_move == WHITE && c == 0) fprintf(file, "*"); fprintf(file, "\n"); } fprintf(file, " "); for (n=0; nnum_piece_types; n++) if (holdings[n][c]) fprintf(file, "%s: %02d ", piece_types->piece_abbreviation[n][c], holdings[n][c]); fprintf(file, "]\n"); } } } void print_bitboards() const { int c, n; if (side_to_move == WHITE) printf("White to move\n"); else printf("Black to move\n"); printf("White pieces\tBlack pieces\tUnmoved pieces\tRoyal\t\tep\t\tepc\n"); bitboard_t epbb, epcbb; epbb = ep; if (ep_victim) epcbb.set(ep_victim); for (c=bitboard_t::board_ranks-1; c>=0; c--) { printf("%s", bbc[0].rank_string(c)); printf("\t"); printf("%s", bbc[1].rank_string(c)); printf("\t"); printf("%s", init.rank_string(c)); printf("\t"); printf("%s", royal.rank_string(c)); printf("\t"); printf("%s", epbb.rank_string(c)); printf("\t"); printf("%s", epcbb.rank_string(c)); printf("\n"); } if (!piece_types) return; if (!flag[WHITE].is_empty() || !flag[BLACK].is_empty()) { printf("\nWhite flags\tBlack flags\n"); for (c=bitboard_t::board_ranks-1; c>=0; c--) { printf("%s", flag[WHITE].rank_string(c)); printf("\t"); printf("%s", flag[BLACK].rank_string(c)); printf("\n"); } } for (n=0; nnum_piece_types; n+=7) { printf("\n"); int k; for (k=0; k<7; k++) { if (n+k >= piece_types->num_piece_types) break; char *s = piece_types->piece_name[n+k]; printf("%*s", -bitboard_t::board_files-2, s); } printf("\n"); for (c=bitboard_t::board_ranks-1; c>=0; c--) { for (k=0; k<7; k++) { if (n+k >= piece_types->num_piece_types) break; printf("%s", bbp[n+k].rank_string(c)); printf(" "); } printf("\n"); } } if (rule_flags & RF_USE_HOLDINGS) { for (c=0; cnum_piece_types; n++) if (holdings[n][c]) printf("%s: %02d ", piece_types->piece_abbreviation[n][c], holdings[n][c]); printf("]\n"); } } } }; #endif SjaakII/include/board_rules.h000644 000765 000024 00000004236 13014616475 017200 0ustar00eglebbkstaff000000 000000 void add_rule(uint32_t rule) { board.rule_flags |= rule; } void remove_rule(uint32_t rule) { board.rule_flags &= ~rule; } uint32_t get_rules(void) { return board.rule_flags; } void set_board_size(int files, int ranks) { assert(files*ranks <= 8*sizeof(kind)); initialise_square_names(files, ranks); bitboard_t::initialise_bitboards(files,ranks); movegen.initialise(); movegen.initialise_slider_tables(); this->ranks = ranks; this->files = files; top_left = files*(ranks-1); initialise_base_evaluation_tables(files, ranks); } void remove_square(int square) { bitboard_t bb = ~bitboard_t::square_bitboards[square]; int n; bitboard_t::board_all &= bb; bitboard_t::board_edge &= bb; bitboard_t::board_east_edge &= bb; bitboard_t::board_west_edge &= bb; bitboard_t::board_north_edge &= bb; bitboard_t::board_south_edge &= bb; bitboard_t::board_south &= bb; bitboard_t::board_north &= bb; bitboard_t::board_corner &= bb; bitboard_t::board_light &= bb; bitboard_t::board_dark &= bb; bitboard_t::board_centre &= bb; bitboard_t::board_xcentre &= bb; bitboard_t::board_xxcentre &= bb; bitboard_t::board_homeland[0] &= bb; bitboard_t::board_homeland[1] &= bb; for (n=0; n<16; n++) { bitboard_t::board_rank[n] &= bb; bitboard_t::board_file[n] &= bb; bitboard_t::board_northward[n] &= bb; bitboard_t::board_southward[n] &= bb; bitboard_t::board_eastward[n] &= bb; bitboard_t::board_westward[n] &= bb; } for (n=0; n<32; n++) { bitboard_t::board_diagonal[n] &= bb; bitboard_t::board_antidiagonal[n] &= bb; } int board_size = 8*sizeof(kind); for (int n=0; n::neighbour_board[n] &= bb; for (int k=0; k::board_between[n][k] &= bb; } } void place_flag(side_t side, int square) { board.flag[side].set(square); } void remove_flag(side_t side, int square) { board.flag[side].reset(square); } SjaakII/include/bool.h000644 000765 000024 00000000362 12452217646 015630 0ustar00eglebbkstaff000000 000000 #if defined(_MSC_VER) # include # if !defined __cplusplus // FIXME: hack # define inline typedef int bool; # define true 1 # define false 0 # endif #else # include #endif SjaakII/include/cfgpath.h000644 000765 000024 00000003022 12433153270 016274 0ustar00eglebbkstaff000000 000000 /** * @file cfgpath.h * @brief Cross platform methods for obtaining paths to configuration files. * * Copyright (C) 2013 Adam Nielsen * * This code is placed in the public domain. You are free to use it for any * purpose. If you add new platform support, please contribute a patch! * * Example use: * * char cfgdir[256]; * get_user_config_file(cfgdir, sizeof(cfgdir), "myapp"); * if (cfgdir[0] == 0) { * printf("Unable to find home directory.\n"); * return 1; * } * printf("Saving configuration file to %s\n", cfgdir); * * A number of constants are also defined: * * - MAX_PATH: Maximum length of a path, in characters. Used to allocate a * char array large enough to hold the returned path. * * - PATH_SEPARATOR_CHAR: The separator between folders. This will be either a * forward slash or a backslash depending on the platform. This is a * character constant. * * - PATH_SEPARATOR_STRING: The same as PATH_SEPARATOR_CHAR but as a C string, * to make it easier to append to other string constants. */ #ifndef CFGPATH_H_ #define CFGPATH_H_ #ifdef __cplusplus extern "C" { #endif extern void get_user_config_file(char *out, size_t maxlen, const char *appname); extern void get_user_config_folder(char *out, size_t maxlen, const char *appname); extern void get_user_data_folder(char *out, size_t maxlen, const char *appname); extern void get_user_cache_folder(char *out, size_t maxlen, const char *appname); #ifdef __cplusplus } #endif #endif /* CFGPATH_H_ */ SjaakII/include/chase.h000644 000765 000024 00000013543 13014616475 015763 0ustar00eglebbkstaff000000 000000 bitboard_t get_chased_pieces(int backtrack) { side_t chaser = next_side[board.side_to_move]; move_t last_move = 0; for (int n = 0; n<2*backtrack; n++) takeback(); if (moves_played) last_move = move_list[moves_played-1]; /* 1. Identify threats: * A "winning capture" is an up-capture, or an equal capture with SEE > 0 * A "threat move" is a winning capture, except for captures * - with the king * - with a pawn * - with a defensive piece * - of a pawn on the enemy side of the board * Discard pieces that are pinned - they cannot chase * * A threat move is a candidate chase. */ movelist_t chase_candidates, old_threats; movegen.generate_chase_candidates(&chase_candidates, &board, chaser); //printf("Threats:\n"); //chase_candidates.print(); /* Filter out old threats */ takeback(); movegen.generate_chase_candidates(&old_threats, &board, chaser); for (int n=0; npiece_capture_flags[n]; if (is_leaper(flags) && is_masked_leaper(flags)) { bitboard_t occ = board.get_occupied(); bitboard_t rev; rev = movegen.generate_leaper_move_bitboard(flags, board.side_to_move, cap, occ); if (rev.test(from)) chase_candidates.move[n--] = chase_candidates.move[--chase_candidates.num_moves]; /* No chase */ } else { chase_candidates.move[n--] = chase_candidates.move[--chase_candidates.num_moves]; /* No chase */ } } else { int to = get_move_to(move); bitboard_t revatk = movegen.get_all_attackers(&board, board.bbc[board.side_to_move], to); revatk &= board.royal; if (!revatk.is_empty()) chase_candidates.move[n--] = chase_candidates.move[--chase_candidates.num_moves]; /* No chase */ } } /* Identify chased pieces */ bitboard_t chased; for (int n=0; n 0); /* TODO */ int count = count_repetition(); //printf("Position repeated %d times\n", count); int backup = 0; for (int n=(int)moves_played-2; n>=0; n-=2) { //printf("%3d %016llx %016llx\n", n, ui[n].hash, board.hash); if (is_irreversible_move(move_list[n+1])) break; if (is_irreversible_move(move_list[n])) break; backup+=2; if (ui[n].hash == board.hash) break; } side_t chaser = next_side[board.side_to_move]; side_t chasee = board.side_to_move; bitboard_t chased_pieces[NUM_SIDES];// = board.bbc[board.side_to_move]; bool chasing[NUM_SIDES]; chased_pieces[WHITE] = board.bbc[WHITE]; chased_pieces[BLACK] = board.bbc[BLACK]; /* Get chased pieces on the current ply */ chased_pieces[chasee] = board.bbc[chasee]; for (int n = 0; n chased = get_chased_pieces(n); chased_pieces[chasee] &= chased; } chasing[chaser] = !chased_pieces[chasee].is_empty(); /* Other side */ takeback(); chased_pieces[chaser] = board.bbc[chaser]; for (int n = 0; n chased = get_chased_pieces(n); chased_pieces[chaser] &= chased; } replaymove(); move_t move = move_list[moves_played-1]; if ( chased_pieces[chaser].test(get_move_from(move)) ) { chased_pieces[chaser].reset(get_move_from(move)); chased_pieces[chaser].set(get_move_to(move)); } chasing[chasee] = !chased_pieces[chaser].is_empty(); /* If one side is evading check, then it cannot chase */ if (!(chased_pieces[WHITE] & board.royal).is_empty()) chasing[WHITE] = false; if (!(chased_pieces[BLACK] & board.royal).is_empty()) chasing[BLACK] = false; //printf("White chasing (%d)\n", chasing[WHITE]); //chased_pieces[BLACK].print(); //printf("Black chasing (%d)\n", chasing[BLACK]); //chased_pieces[WHITE].print(); //if (!chasing[WHITE] && !chasing[BLACK]) // return NO_CHASE; if (chasing[WHITE] == chasing[BLACK]) return DRAW_CHASE; if (!chasing[board.side_to_move]) return WIN_CHASE; return LOSE_CHASE; } SjaakII/include/compilerdef.h000644 000765 000024 00000003452 13014616475 017167 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014, 2015 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMPILERDEF_H #define COMPILERDEF_H /* Various compiler-specific definitions and declarations. Mainly to deal * with differences between GNU and Microsoft compiler families. */ #if defined _MSC_VER # define _CRT_SECURE_NO_WARNINGS # define _CRT_NONSTDC_NO_WARNINGS # if !defined _HAS_EXCEPTIONS # define _HAS_EXCEPTIONS 0 # endif # define expect(x,y) (x) # define prefetch(x) (void)0 # define ATTRIBUTE_FORMAT_PRINTF # define ATTRIBUTE_ALIGNED(x) # define ATTRIBUTE_UNUSED # define PRIu64 "I64u" # define strdup _strdup # if _MSC_VER < 1900 # if defined __cplusplus extern "C" { # endif int snprintf(char *buf, size_t size, const char *fmt, ...); # if defined __cplusplus } # endif #endif #else /* Assume GNU */ # include # define expect(x,y) __builtin_expect((x), (y)) # define prefetch(x) __builtin_prefetch(x) # define ATTRIBUTE_FORMAT_PRINTF __attribute__((format(printf, 1, 2))) # define ATTRIBUTE_ALIGNED(x) __attribute__((aligned(x))) # define ATTRIBUTE_UNUSED __attribute__((unused)) #endif #endif SjaakII/include/eval_param.h000644 000765 000024 00000010551 13014616475 017003 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Integer constants are used within the evaluation itself, floating point * values are only used when initialising the evaluation tables at startup. */ #define GAME_PHASE_FLOOR 80 #define SAFE_MOB_WEIGHT 8 // Weight for safe mobility squares #define MOB_SCALE 16 // Overall mobility weight #define MOB_FORWARD_BLOCKED 5 // Penalty for forward mobility blocked by pieces #define KS_ATTACK_WEIGHT 256 #define KS_SHELTER_WEIGHT 32 #define KING_SAFETY_WEIGHT 416 // Overall scale-factor for king safety // Weight vs. STS score: // 512 (3126) // 256 (3044) // 384 (3118) // 448 (3117) // 416 (3128) #define PST_HOLDINGS 5 // PST value for a piece in holdings #define PST_SPACE_MG 5 // PST value for empty squares on the own side of the board #define PST_CENTRE_Q 1.0 #define PST_CENTRE_L 0.0 #define PST_ADVANCE_Q 0.0 #define PST_ADVANCE_L 0.0 #define PST_SCALE_PALACE 1.1 // Scale factor for PST in games with a palace #define MOBILITY_SCALE_MG 128.0 // Scale factor for mobility, middle game // Weight vs. STS score: // 96 (8001, 923) // 112 (8143, 932) // 120 (8055, 932) // 128 (8234, 940) // 130 (8110, 922) // 140 (8086, 926) // 192 (8094, 931) #define MOBILITY_SCALE_EG 128.0 // Scale factor for mobility, end game // Weight vs. STS score: // 120 (8114, 928) // 128 (8234, 940) // 140 (8007, 916) #define PASSER_RANK_BASE 4 #define PASSER_RANK_SCALE 128 #define PAWN_ADVANCE_Q_EG 98.0 // Bonus for advancing pawns in the end game, quadratic term #define PAWN_ADVANCE_L_EG 0.0 // Bonus for advancing pawns in the end game, linear term #define PAWN_ADVANCE_Q_MG 0.0 // Bonus for advancing pawns in the middle game, quadratic term #define PAWN_ADVANCE_L_MG 0.0 // Bonus for advancing pawns in the middle game, linear term #define ROOK_BASE_PAWN_MG 5 // Bonus for a rook that attacks base of enemy pawn structure, middle game #define ROOK_BASE_PAWN_EG 10 // Bonus for a rook that attacks base of enemy pawn structure, end game #define WEAK_PAWN_BASE_MG 5 // Penalty for a weak pawn, middle game #define WEAK_PAWN_BASE_EG 10 // Penalty for a weak pawn, end game #define LOOSE_MINOR_PENALTY 5 // Penalty for an undefended minor on the enemy side of the board #define PAIR_BONUS_MG 0.10 // Fraction of piece value #define PAIR_BONUS_EG 0.15 // Fraction of piece value #define SLIDER_OPENFILE_MG 10 // Slider bonus on open file, middle game #define SLIDER_OPENFILE_EG 15 // Slider bonus on open file, end game #define HOPPER_KINGFILE_MG 50 // Hopper on same file as enemy king, middle game #define HOPPER_KINGFILE_EG 0 // Hopper on same file as enemy king, middle game #define DEF_PROTECT 7 // Bonus for defensive pieces that protect eachother #define DEF_SHIELD_FILE 10 // Bonus for defensive pieces on king file #define FUTILITY_DEPTH 3 #define PAWN_SCALE_MG 0.8 // Fraction of nominal piece value #define LAME_SCALE_MG 0.9 // Fraction of nominal piece value #define LAME_SCALE_EG 1.1 // Fraction of nominal piece value #define DEF_SCALE_MG 1.1 // Fraction of nominal piece value #define DEF_SCALE_EG 0.8 // Fraction of nominal piece value #define HOP_SCALE_EG 0.7 // Fraction of nominal piece value #define TEMPO_DROP_WEIGHT 2 // Weighting of in-hand material for the tempo bonus #define TEMPO_DROP_MAX 32767 // Absolute limit for the tempo bonus SjaakII/include/eval_types.h000644 000765 000024 00000007770 13014616475 017060 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EVAL_TYPES_H #define EVAL_TYPES_H #include "compilerdef.h" typedef int16_t eval_t; struct eval_pair_t { eval_t mg, eg; eval_pair_t () { mg = eg = 0; } eval_pair_t (int v) { mg = eg = v; } eval_pair_t (int m, int e) { mg = m; eg = e; } inline eval_pair_t operator = (const eval_pair_t p) { mg = p.mg; eg = p.eg; return *this; } inline eval_pair_t operator = (const int v) { mg = v; eg = v; return *this; } inline eval_pair_t operator << (const int bits) const { return eval_pair_t(mg << bits, eg << bits); } inline eval_pair_t operator >> (const int bits) const { return eval_pair_t(mg >> bits, eg >> bits); } inline eval_pair_t operator +=(const eval_pair_t &rhs) { mg += rhs.mg; eg += rhs.eg; return *this; } inline eval_pair_t operator -=(const eval_pair_t &rhs) { mg -= rhs.mg; eg -= rhs.eg; return *this; } inline eval_pair_t operator *=(const eval_pair_t &rhs) { mg *= rhs.mg; eg *= rhs.eg; return *this; } inline eval_pair_t operator /=(const eval_pair_t &rhs) { mg /= rhs.mg; eg /= rhs.eg; return *this; } inline eval_pair_t operator +=(const int &rhs) { mg += rhs; eg += rhs; return *this; } inline eval_pair_t operator -=(const int &rhs) { mg -= rhs; eg -= rhs; return *this; } inline eval_pair_t operator *=(const int &rhs) { mg *= rhs; eg *= rhs; return *this; } inline eval_pair_t operator /=(const int &rhs) { mg /= rhs; eg /= rhs; return *this; } inline const eval_pair_t operator +(const eval_pair_t &rhs) const { return eval_pair_t(*this) += rhs; } inline const eval_pair_t operator -(const eval_pair_t &rhs) const { return eval_pair_t(*this) -= rhs; } inline const eval_pair_t operator *(const eval_pair_t &rhs) const { return eval_pair_t(*this) *= rhs; } inline const eval_pair_t operator /(const eval_pair_t &rhs) const { return eval_pair_t(*this) /= rhs; } inline const eval_pair_t operator +(const int &rhs) const { return eval_pair_t(*this) += eval_pair_t(rhs); } inline const eval_pair_t operator -(const int &rhs) const { return eval_pair_t(*this) -= eval_pair_t(rhs); } inline const eval_pair_t operator *(const int &rhs) const { return eval_pair_t(*this) *= eval_pair_t(rhs); } inline const eval_pair_t operator /(const int &rhs) const { return eval_pair_t(*this) /= eval_pair_t(rhs); } inline const eval_pair_t operator *(const float &rhs) const { return eval_pair_t(int(this->mg * rhs), int(this->eg * rhs)); } inline eval_t interpolate(int x, int scale) const { //if (x < 0) x = 0; //if (x > scale) x = scale; if (expect(scale == 0, false)) return mg; return (scale * mg + (scale - x) * (eg-mg) ) / scale; } inline eval_pair_t operator -() const { return eval_pair_t(-mg, -eg); } }; /* Pawn structure tables */ template struct pawn_structure_t { bitboard_t open; bitboard_t passed; bitboard_t stop; bitboard_t weak; bitboard_t attacked[NUM_SIDES]; eval_t shelter_score[NUM_SIDES][16]; }; #endif SjaakII/include/evalhash.h000644 000765 000024 00000002573 12452217646 016476 0ustar00eglebbkstaff000000 000000 /* Leonidas, a program for playing chess variants * Copyright (C) 2009, 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EVALHASH_H #define EVALHASH_H #ifdef __cplusplus extern "C" { #endif #include #include "bool.h" #undef DEBUG_EVHASH typedef struct { #ifdef DEBUG_EVHASH uint64_t key; #endif uint64_t data; } eval_hash_t; typedef struct { eval_hash_t *data; size_t number_of_elements; } eval_hash_table_t; eval_hash_table_t *create_eval_hash_table(size_t nelem); void destroy_eval_hash_table(eval_hash_table_t *table); bool query_eval_table_entry(eval_hash_table_t *table, uint64_t key, int16_t *score); void store_eval_hash_entry(eval_hash_table_t *table, uint64_t key, int16_t score); #ifdef __cplusplus } #endif #endif SjaakII/include/evaluate.h000644 000765 000024 00000074661 13014616475 016516 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if 0 struct pawn_structure_t { bitboard_t open; bitboard_t passed; bitboard_t stop; bitboard_t weak; bitboard_t attacked[NUM_SIDES]; }; #endif template void game_template_t::calculate_pawn_structure(pawn_structure_t *ps) { bitboard_t passed; bitboard_t stop; bitboard_t open; bitboard_t weak; bitboard_t pawns; open = bitboard_t::board_all; memset(ps, 0, sizeof *ps); for (side_t side=WHITE; side bb = pawns & board.bbc[side]; bitboard_t ob = pawns & ~board.bbc[side]; /* Shelter score (0-8) */ for (int f = 0; f::board_files; f++) { int lf = (f == 0) ? f+1 : f-1; int rf = (f == files-1) ? f-1 : f+1; int r = 0; if (side == BLACK) r = bitboard_t::board_ranks-1; int square = pack_rank_file(r, f); bitboard_t mask1 = bitboard_t::neighbour_board[square]; bitboard_t mask2 = bitboard_t::king_zone[side][square] ^ mask1; bitboard_t mask3 = bitboard_t::board_file[f]; bitboard_t mask4 = bitboard_t::board_file[lf]; bitboard_t mask5 = bitboard_t::board_file[rf]; if (!(bb & mask1 & mask3).is_empty()) ps->shelter_score[side][f] += 4; else if (!(bb & mask2 & mask3).is_empty()) ps->shelter_score[side][f] += 2; if (!(bb & mask1 & mask4).is_empty()) ps->shelter_score[side][f] += 2; else if (!(bb & mask2 & mask4).is_empty()) ps->shelter_score[side][f] += 1; if (!(bb & mask1 & mask5).is_empty()) ps->shelter_score[side][f] += 2; else if (!(bb & mask2 & mask5).is_empty()) ps->shelter_score[side][f] += 1; } /* Identify passers, open files, candidate weak pawns */ bitboard_t bp = bb; while(!bp.is_empty()) { int square = bp.bitscan(); bp.reset(square); if (pt.piece_promotion_choice[pt.pawn_index[side]] && (pt.passer_mask[side][square] & ob).is_empty()) passed.set(square); open &= ~bitboard_t::board_file[unpack_file(square)]; if (!pt.weak_mask[side][square].is_empty() && (pt.weak_mask[side][square] & bb).is_empty()) weak.set(square); } } ps->passed = passed; ps->open = open; ps->stop = stop; ps->weak = weak; } template template eval_t game_template_t::static_evaluation(side_t side_to_move, int /* alpha */, int /* beta */) { bitboard_t moves[8*sizeof(kind)]; bitboard_t attack[8*sizeof(kind)]; bitboard_t less_attacks[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t attacks[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t all_attacks[NUM_SIDES]; bitboard_t multi_attacks[NUM_SIDES]; bitboard_t pawn_attacks[NUM_SIDES]; bitboard_t pawns[NUM_SIDES]; bitboard_t minors[NUM_SIDES]; bitboard_t occ = board.get_occupied(); bitboard_t defence, castle; bitboard_t defatk; pawn_structure_t ps; bool can_win[NUM_SIDES] = { false, false }; int mate_potential[NUM_SIDES] = { 0, 0 }; int num_pieces[NUM_SIDES] = { 0, 0 }; int num_royals[NUM_SIDES] = { 0, 0 }; int num_pawns[NUM_SIDES] = { 0, 0 }; int num_supers[NUM_SIDES] = { 0, 0 }; int num_def[NUM_SIDES] = { 0, 0 }; int num_light_bound[NUM_SIDES]= { 0, 0 }; int num_dark_bound[NUM_SIDES] = { 0, 0 }; uint32_t piece_ids[NUM_SIDES] = { 0, 0 }; int square_list[8*sizeof(kind)] = {0}, square_count = 0; int king[NUM_SIDES] = { -1, -1 }; int shelter[2] = {0, 0}; eval_pair_t def[2] = { 0, 0 }; // Defensive pieces eval_pair_t mat = 0; // Material balance eval_pair_t psq = 0; // Piece square tables eval_pair_t mob = 0; // Mobility eval_pair_t pss = 0; // Pawn structure score eval_pair_t kss = 0; // King safety score eval_pair_t tp = 0; // Tempo bonus eval_t ev = 0; eval_t hash_ev = 0; eval_t tempo = 0; int phase = 0; bool symmetric = true; bool have_eval_hash = query_eval_table_entry(eval_table, board.hash, &hash_ev); #ifndef DEBUG_EVHASH if (have_eval_hash && !print) { ev = hash_ev; goto exit; } #endif /* Randomised evaluation */ if (level == LEVEL_BEAL) { ev = genrandui() & 0x7ff; if (side_to_move == BLACK) ev = -ev; goto exit; } calculate_pawn_structure(&ps); for (side_t side = WHITE; side less_attack; /* Accumulate attack bitmask of pieces less valuable than the current piece. */ if (board.rule_flags & RF_CAPTURE_ALL_FLAG) { int flag_count = board.flag[side].popcount(); int cflag_count = (board.flag[side] & board.bbc[side]).popcount(); if (flag_count) { psq += 5 * cflag_count*cflag_count / flag_count; } } /* Evaluate pieces and collect mobility and attack information. * Do the pieces in order of increasing value, so we can do safe * mobility. */ int gate_space = 0; if (board.rule_flags & RF_GATE_DROPS) { for (int n=0; n::board_homeland[side] & ~occ).popcount(); for (int n=0; n bb = board.bbc[side] & board.bbp[piece]; if (board.rule_flags & RF_GATE_DROPS) { if (gate_space) { int gate_scale = files*(files-1); int gate_score = (board.init & ~board.royal & board.bbc[side] & (bitboard_t::board_south_edge | bitboard_t::board_north_edge)).popcount(); int pst = PST_HOLDINGS; float scale = (float)gate_score*(gate_score-1) / gate_scale; if (gate_score > gate_space+2) { scale = 1.0; phase += pt.phase_weight[piece] * board.holdings[piece][side]; } else if (gate_score >= gate_space) { gate_scale = gate_score*(gate_score-1) + 1; scale = (float)gate_score*(gate_score-1) / gate_scale; phase += pt.phase_weight[piece] * board.holdings[piece][side]; pst = 0; } else { phase += (int)(pt.phase_weight[piece] * board.holdings[piece][side] * scale); pst = 0; } mat += pt.eval_value[piece] * board.holdings[piece][side] * scale; psq += (int)(pst * board.holdings[piece][side] * scale); } } else { mat += pt.eval_value[piece] * board.holdings[piece][side]; psq += PST_HOLDINGS * board.holdings[piece][side]; } if (bb.is_empty()) continue; if (!(pt.piece_flags[piece] & PF_ROYAL)) piece_ids[side] |= 1<::board_light).is_empty() && !(bb & bitboard_t::board_dark).is_empty()) mat += pt.eval_pair_bonus[piece]; else if ( (pt.defensive_pieces & (1 << piece)) && !bb.onebit()) mat += pt.eval_pair_bonus[piece]; } /* Piece square tables/material evaluation */ while(!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); square_list[square_count++] = square; if (pt.defensive_pieces & (1<::board_homeland[side] & pt.prison[side][piece]; } if (pt.piece_flags[piece] & PF_COLOURBOUND) { if (bitboard_t::board_light.test(square)) num_light_bound[side]++; else num_dark_bound[side]++; } if (!(pt.piece_flags[piece] & PF_CANTMATE)) mate_potential[side]++; moves[square] = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[piece], square, occ, side); /* Collect attack bitmasks * TODO: for pawns it is more efficient to do this for all pawns * at once (in bulk) after the loop is done. */ bitboard_t atk = moves[square]; if (pt.piece_move_flags[piece] != pt.piece_capture_flags[piece]) atk = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[piece], square, occ, side); multi_attacks[side] |= all_attacks[side] & atk; attacks[side][piece] |= atk; all_attacks[side] |= atk; attack[square] = atk; moves[square] &= ~occ; if (pt.defensive_pieces & (1< 0) mat.mg = std::max(0, mat.mg - def[BLACK].mg/2); //if (mat.eg > 0) mat.eg = std::max(0, mat.eg - def[BLACK].eg/2); /* Gather data for shelter and assess winning chances. */ for (side_t side = WHITE; side= 0); int piece = board.get_piece(king[side]); eval_t score = 0; eval_t cscore = 0; int f = unpack_file(king[side]); score = ps.shelter_score[side][f]; /* 0-8 */ shelter[side] = 4*score; /* 0-32 */ for (int c = SHORT; c bb = movegen.castle_king_dest[c][side]; while (!bb.is_empty()) { int square = bb.bitscan(); int f = unpack_file(square); bb.reset(square); cscore = std::max(cscore, ps.shelter_score[side][f]); } } //printf("Shelter: %d %d\n", score, cscore); if (cscore > score) score = (score + cscore) / 2; kss.mg += 4*score; /* Defensive pieces */ if ( !(defence & castle).is_empty() ) { eval_t score = 0; score += 2*(bitboard_t::board_file[f] & defence & castle & bitboard_t::board_homeland[side]).popcount(); score += ((bitboard_t::board_file[f-1]|bitboard_t::board_file[f+1]) & defence & castle & bitboard_t::board_homeland[side]).popcount(); score += (defatk & castle).popcount(); score += (defatk & defence & castle).popcount(); shelter[side] = std::min(2*score, KS_SHELTER_WEIGHT); kss.mg += shelter[side]; } /* Shelter score for drop-games */ if ( pt.defensive_pieces == 0 && (board.rule_flags & RF_USE_CAPTURE) ) { bitboard_t king_zone = bitboard_t::neighbour_board[king[side]]; eval_t score = 0; score = ps.shelter_score[side][f]; score += 2*(king_zone & board.bbc[side]).popcount(); score += (king_zone & less_attacks[side][piece]).popcount(); shelter[side] = std::min(1*score, KS_SHELTER_WEIGHT); if ((king_zone & (multi_attacks[oside]|(all_attacks[oside]&~less_attacks[side][piece]))).is_empty()) shelter[side] /= 2; kss.mg += shelter[side] / 2; } } /* King safety */ /* TODO */ /* Winning chances * TODO: test if pawns can promote to something with mate potential. */ if (mate_potential[side] >= 1) can_win[side] = true; else { int non_pawn_non_royal = num_pieces[side]-num_pawns[side]-num_royals[side] - num_def[side]; if (num_pawns[side] >= 1 || non_pawn_non_royal > 2) can_win[side] = true; if (non_pawn_non_royal == 2 && !can_win[side]) { uint32_t p = piece_ids[side]; int n1, n2; n1 = n2 = bitscan32(p); p ^= 1< bb = board.bbc[oside]; while (!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); int score = (ranks + files)/2 - pt.tropism[piece][king[side]][square]; psq += 4*score*abs(score); } psq += 8*(all_attacks[side] & ~all_attacks[oside]).popcount(); int weight = num_light_bound[oside] - num_dark_bound[oside]; for (int n = 0; n<2; n++) { if (weight > 0) { bitboard_t avoid = (n == 0 ? bitboard_t::board_light : bitboard_t::board_dark ) & bitboard_t::board_corner; while (!avoid.is_empty()) { int square = avoid.bitscan(); avoid.reset(square); int score = (ranks + files)/2 - pt.tropism[piece][king[side]][square]; psq -= 2*weight * score * abs(score); psq += 1*abs(centre_table[king[side]]) * centre_table[king[side]] - 3*abs(centre_table[king[oside]])* centre_table[king[oside]]; psq -= pt.tropism[piece][king[oside]][square]/2; } } weight = -weight; } } } /* Flip evaluation values for side to move. * Idea from Senpai. */ psq = -psq; kss = -kss; pss = -pss; } /* Draw-ish material combinations */ if (num_pawns[WHITE] == 0 && num_pawns[BLACK] == 0) { if (mate_potential[WHITE] == mate_potential[BLACK] && mate_potential[WHITE] == 1 && abs(num_pieces[WHITE]-num_pieces[BLACK]) <= 1) mat /= 4; if (num_pieces[WHITE] == num_pieces[BLACK] && num_pieces[WHITE] == 1 && mate_potential[WHITE]+mate_potential[BLACK] == 1) mat /= 8; } /* Mobility, piece safety */ { int wa_weight[NUM_SIDES][8*sizeof(kind)] = { { 0 } }; int wa_count[NUM_SIDES][8*sizeof(kind)] = { { 0 } }; int si = 0; for (side_t side = WHITE; side king_zone; side_t oside = next_side[side]; if (num_royals[oside] == 1) { king_zone = bitboard_t::king_zone[oside][king[oside]]; if ( board.rule_flags & RF_USE_HOLDINGS ) { for (int piece = 0; piece wpa = attack[square] & ps.weak; while (!wpa.is_empty()) { int square = wpa.bitscan(); wpa.reset(square); wa_weight[side][square] += std::max(1, pt.phase_weight[piece]); wa_count[side][square]++; } /* Attack on base of pawn structure */ if ((pt.major_pieces & (1<::board_ranks - 1) - rank; eval_t scale = PASSER_RANK_BASE; bitboard_t fs = pt.front_span[side][square]; if ((fs & occ).is_empty()) scale++; fs.set(square); if (all_attacks[side].test(square)) scale++; if ((fs & all_attacks[side]) == fs) scale++; if ((fs & all_attacks[oside]).is_empty()) scale++; scale *= PASSER_RANK_SCALE; scale /= 128; pss.eg += scale*rank*rank; } } /* Kings should stay put until the end game, so don't score king * mobility in the middle game. */ if (pt.piece_flags[piece] & PF_ROYAL) { bitboard_t safe = moves[square] & ~all_attacks[oside]; bitboard_t unsafe = moves[square] ^ safe; mob.eg += pt.eval_mobility[piece][safe.popcount()].eg * SAFE_MOB_WEIGHT / 128; } else { bitboard_t up_attacks = all_attacks[oside] ^ less_attacks[oside][piece]; bitboard_t safe = (moves[square] & ~less_attacks[oside][piece]) | (moves[square] & up_attacks & all_attacks[side]); bitboard_t unsafe = moves[square] ^ safe; mob += pt.eval_mobility[piece][safe.popcount()] * SAFE_MOB_WEIGHT / 128; bitboard_t forward = (side == WHITE) ? bitboard_t::board_northward[unpack_rank(square)] : bitboard_t::board_southward[unpack_rank(square)]; if ((moves[square] & forward & ~pawns[side]).is_empty()) psq.mg -= MOB_FORWARD_BLOCKED; } /* Board control * Important squares to control in the middle game are in the * centre and the opponent side of the board. * In the end game focus changes to pawns and blocking passers. */ bitboard_t control = attack[square] & ~pawn_attacks[oside]; int cw = 0; if ( pt.minor_pieces & (1 << piece) ) cw = 4; if ( pt.major_pieces & (1 << piece) ) cw = 2; if ( pt.super_pieces & (1 << piece) ) cw = 1; eval_t score = 3*(control & bitboard_t::board_centre).popcount() + 2*(control & bitboard_t::board_xcentre).popcount() + 1*(control & bitboard_t::board_xxcentre).popcount() + 1*(control & bitboard_t::board_homeland[oside]).popcount(); mob.mg += MOB_SCALE * cw * (score - 4); if ( pt.royal_pieces & (1 << piece) ) cw = 4; mob.eg += MOB_SCALE * cw * 4 * (control & (ps.weak | ps.passed) & board.bbc[side]).popcount(); /* Piece placement */ if (pt.minor_pieces & (1<::board_edge & bitboard_t::board_homeland[oside]).test(square) ) { // psq.mg += 5; //} } /* In the end game the king should move towards enemy pawns */ /* TODO */ /* Defensive pieces should defend eachother * By definition, defensive pieces cannot attack squares on the * opponent's side of the board, so if they are ever attacked, * it's by one of their own pieces. */ if (pt.defensive_pieces & (1<::board_rank[rank]).is_empty()) psq += DEF_SHIELD_FILE; } /* Hoppers on same file as king (unblocked) */ if (is_hopper(pt.piece_capture_flags[piece]) && unpack_file(square) == unpack_file(king[oside]) && bitboard_t::board_between[square][king[oside]].is_empty()) { kss.mg += HOPPER_KINGFILE_MG; kss.eg += HOPPER_KINGFILE_EG; } si++; } /* King attack pattern: the (safe) attack count causes the score * to increase exponentially, while a good shelter causes it to * decrease linearly. The exact form of the expression is * based on Senpai's implementation of the idea. * TODO: tune the overall constant scale factor. */ assert(KS_SHELTER_WEIGHT - shelter[oside] >= 0); ka_weight *= (KS_ATTACK_WEIGHT - (KS_ATTACK_WEIGHT >> ka_count)) * (KS_SHELTER_WEIGHT - shelter[oside]); if (ka_scale) kss.mg += KING_SAFETY_WEIGHT * ka_weight / ka_scale; mob = -mob; psq = -psq; kss = -kss; pss = -pss; } /* Pressure against weak pawns */ for (side_t side = WHITE; side wp = ps.weak & board.bbc[side]; while (!wp.is_empty()) { int square = wp.bitscan(); wp.reset(square); int weight = (8 - (8 >> wa_count[oside][square])) * std::max(0, 8 - wa_count[side][square]); pss -= base; pss -= base * weight / 64; } pss = -pss; } mob /= MOB_SCALE; assert(si == square_count); } /* Disable phase-scaling in variants where captured pieces are returned: * there material does not represent game phase. */ if (board.rule_flags & RF_USE_CAPTURE) phase = pt.phase_scale; if ( board.rule_flags & (RF_USE_CAPTURE | RF_ALLOW_PICKUP) ) { int t = 0; for (int piece = 0; piece 0 && !can_win[WHITE]) { symmetric = false; ev = psq.interpolate(phase, pt.phase_scale); } if (ev < 0 && !can_win[BLACK]) { symmetric = false; ev = psq.interpolate(phase, pt.phase_scale); } #ifdef DEBUG_EVHASH /* Sanity check: the hashed score should equal the current score. * We only ever get here if we're debugging the evaluation hash. */ if (symmetric) { if (!(!have_eval_hash || (hash_ev == ev))) { printf("%d %d %d %d\n", have_eval_hash, hash_ev, ev, symmetric); printf("0x%016llx\n", board.hash); board.print_bitboards(); //printf("%s\n", make_fen_string(game, NULL)); } assert(!have_eval_hash || (hash_ev == ev)); } #endif /* Store the results of the evaluation in the evaluation hash table. * If the evaluation is symmetric, we can store it for the other side * to move as well, which will safe an evaluation after null-move. */ store_eval_hash_entry(eval_table, board.hash, ev); if (symmetric) store_eval_hash_entry(eval_table, side_to_move_key^board.hash, ev); exit: /* Tapered evaluation when we're about to hit the 50 move counter */ if (fifty_scale_limit && board.fifty_counter > fifty_scale_limit) ev = ev * (fifty_limit - board.fifty_counter) / (fifty_limit - fifty_scale_limit); /* Add a pseudo-random contribution to the opening moves */ if (random_ok && random_amplitude && (start_move_count + moves_played) < random_ply_count) { unsigned int rand = (unsigned int)(board.hash ^ random_key); int amp = int(random_amplitude * (random_ply_count - start_move_count - moves_played) / random_ply_count); int r = ((int)(rand & 0xff) - 0x7f) * amp / 0x7f; if (print) printf("Random factor: % 4d\n", r); ev += r; } return ((side_to_move == WHITE) ? ev : -ev) + tempo; } SjaakII/include/fen.h000644 000765 000024 00000033354 13014616475 015452 0ustar00eglebbkstaff000000 000000 void record_castle_state(char state, bitboard_t *castle_init) { /* Deal with different type of castle flags */ int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int rook_file = -1; int rook_from = -1; if (state == '-') return; if (!isalpha(state)) return; side_t side = BLACK; if (isupper(state)) side = WHITE; /* Fischer-random style castling. This identifies the file of the piece with which * we can castle. We can find the file of the king by looking at the royal bitboard, and we * know the king destination after castling from the rule description. From this, we can work * out the required bitmasks. */ int king_from = -1; for (int n = 0; n side_mask = bitboard_t::board_rank[(side == WHITE) ? 0 : board_ranks-1]; char file_char = (side == WHITE) ? 'A' : 'a'; int file = state - file_char; if (file >= board_files || file < 0) return; *castle_init |= bitboard_t::board_file[file] & side_mask; return; } int king_rank = unpack_rank(king_from); char file_char = (side == WHITE) ? 'A' : 'a'; if (state-file_char < board_files) { rook_file = state - file_char; rook_from = bitboard_t::pack_rank_file(king_rank, rook_file); castle_init->set(rook_from); if (board.rule_flags & RF_GATE_DROPS) return; } switch(state) { case 'K': /* Starting at the right edge of the board, find the first castle * piece. */ state = '-'; for (int n = board_files - 1; n>=0; n--) { int sqr = bitboard_t::pack_rank_file(king_rank, n); if (board.get_piece(sqr) == pt.castle_piece[side][SHORT]) { rook_from = sqr; state = file_char + n; break; } } if (state != 'K') record_castle_state(state, castle_init); break; case 'Q': /* Starting at the left edge of the board, find the first castle * piece. */ state = '-'; for (int n = 0; n::pack_rank_file(king_rank, n); if (board.get_piece(sqr) == pt.castle_piece[side][LONG]) { rook_from = sqr; state = file_char + n; break; } } if (state != 'Q') record_castle_state(state, castle_init); break; case 'k': /* Starting at the right edge of the board, find the first castle * piece. */ state = '-'; for (int n = board_files - 1; n>=0; n--) { int sqr = bitboard_t::pack_rank_file(king_rank, n); if (board.get_piece(sqr) == pt.castle_piece[side][SHORT]) { rook_from = sqr; state = file_char + n; break; } } if (state != 'k') record_castle_state(state, castle_init); break; case 'q': /* Starting at the left edge of the board, find the first castle * piece. */ state = '-'; for (int n = 0; n::pack_rank_file(king_rank, n); if (board.get_piece(sqr) == pt.castle_piece[side][LONG]) { rook_from = sqr; state = file_char + n; break; } } if (state != 'q') record_castle_state(state, castle_init); break; } if (rook_from < 0) return; int castle_side = LONG; if (unpack_file(rook_from) >= unpack_file(king_from)) castle_side = SHORT; bitboard_t king_dest = movegen.castle_king_dest[castle_side][side]; movegen.clear_castle_rule(castle_side, side); while (!king_dest.is_empty()) { int king_to = king_dest.bitscan(); king_dest.reset(king_to); //printf("%d (%s %s) (%s)\n", side, square_names[king_from], square_names[king_to], square_names[rook_from]); movegen.deduce_castle_flags(side, king_from, king_to, rook_from); castle_init->set(king_from); } return; } void setup_fen_position(const char *str, bool skip_castle = false) { const char *s = str; int prev_rank = 2*bitboard_t::board_files; int square = top_left; int n; if (!s) return; moves_played = 0; board.clear(); memset(repetition_hash_table, 0, sizeof repetition_hash_table); memset(board_repetition_hash_table, 0, sizeof board_repetition_hash_table); /* Parse first record: piece positions */ while(*s && (*s != ' ') && (*s != '[') && square>=0) { switch (*s) { case '/': square -= prev_rank; break; case '*': square++; break; case '1': if (isdigit(s[1])) { square += 10; break; } case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': square += (*s - '1')+1; break; default: side_t side = NONE; int piece = -1; int best_len = 0; for (n=0; n best_len) { best_len = len; side = match_side; piece = n; } } } if (piece == -1) { if (error_output) error_output("Error: unknown piece type '%c' (bad FEN %s)\n", *s, str); if (xboard_output) xboard_output("tellusererror unknown piece type '%c' (bad FEN %s)\n\n", *s, str); return; } assert(side != NONE); assert(square_to_bit[square] >= 0); board.put_new_piece(piece, side, square_to_bit[square]); s += strlen(pt.piece_abbreviation[piece][side])-1; square++; break; } s++; } /* Optional: check for holdings */ while(*s && (*s == ' ')) s++; if (*s == '[') { s++; while (*s != ']') { for (n=0; n castle_init; if (!skip_castle) record_castle_state(*s, &castle_init); s++; board.init |= castle_init; } if (*s) s++; } /* Make sure the initial flags are at least somewhat sane by making sure only occupied squares have their * init bits set. */ board.init &= board.get_occupied(); /* Fourth record: En-passant square * If this record is a number, then the game doesn't have en-passant capture and we skip it. */ while(*s && (*s == ' ')) s++; if (!isdigit(*s)) { if (*s && (*s != '-')) { int file = *s - 'a'; int rank = s[1] - '1'; s+=2; /* En-passant move-to square */ board.ep.set(bitboard_t::pack_rank_file(rank, file)); /* En-passant capture square, this may be encoded in the FEN */ if (*s && !isspace(*s)) { int file = *s - 'a'; int rank = s[1] - '1'; s+=2; board.ep_victim = bitboard_t::pack_rank_file(rank, file); } else { /* Assume we have normal pawns, in which case we can simply derive it from the move-to square */ if (board.side_to_move == WHITE) board.ep_victim = board.ep.bitscan() - bitboard_t::board_files; else board.ep_victim = board.ep.bitscan() + bitboard_t::board_files; } } while(*s && (*s != ' ')) s++; } /* Fifth record: half-move counter (50 move counter) */ while(*s && (*s == ' ')) s++; n = 0; sscanf(s, "%d\n", &n); board.fifty_counter = n; while(*s && (*s != ' ')) s++; /* Sixth record: full-move counter */ while(*s && (*s == ' ')) s++; n = 0; sscanf(s, "%d\n", &n); start_move_count = 2*std::max(0, n-1); while(*s && (*s != ' ')) s++; repetition_hash_table[board.hash&0xFFFF] = 1; board_repetition_hash_table[board.board_hash&0xFFFF] = 1; /* Record check state */ board.check(player_in_check(board.side_to_move)); } const char *make_fen_string(char *buffer = NULL) const { static char static_buffer[4096]; bitboard_t occ; char *fen = buffer; int n = 0; int r, f; if (!fen) fen = static_buffer; fen[0] = '\0'; occ = board.bbc[WHITE] | board.bbc[BLACK]; /* First record: board position */ /* Scan all ranks */ for (r = board.virtual_ranks-1; r>=0; r--) { int count = 0; for (f = 0; f < board.virtual_files; f++) { int square = f + r*board.virtual_files; int bit = square_to_bit[square]; if (bit < 0 || bit_to_square[bit] < 0 || !bitboard_t::board_all.test(bit)) { if (count) n += snprintf(fen+n, 4096 - n, "%d", count); count = 0; n += snprintf(fen+n, 4096-n, "*"); continue; } /* Empty? */ if (!occ.test(bit)) { count++; continue; } /* Not empty, do we have a count? */ if (count) n += snprintf(fen+n, 4096 - n, "%d", count); count = 0; /* Print piece */ side_t side = board.get_side(bit); int piece = board.get_piece(bit); n += snprintf(fen+n, 4096-n, "%s", pt.piece_abbreviation[piece][side]); } if (count) n += snprintf(fen+n, 4096 - n, "%d", count); if (r) n += snprintf(fen+n, 4096 - n, "/"); } /* Holdings */ if (board.rule_flags & RF_USE_HOLDINGS) { n += snprintf(fen+n, 4096 - n, "["); bool empty = true; for (side_t side = WHITE; side <= BLACK; side++) { for (int piece = 0; piece < pt.num_piece_types; piece++) { for (int count = 0; count < board.holdings[piece][side]; count++) { empty = false; n += snprintf(fen+n, 4096 - n, "%s", pt.piece_abbreviation[piece][side]); } } } if (empty) n += snprintf(fen+n, 4096 - n, "-"); n += snprintf(fen+n, 4096 - n, "]"); } /* Second record: side to move */ if (board.side_to_move == WHITE) n += snprintf(fen+n, 4096 - n, " w "); else n += snprintf(fen+n, 4096 - n, " b "); /* Third record: castling rights * TODO: FRC-style strings. */ bool have_castle = false; for (int n = 0; n short_mask, long_mask; bool wrote_castle = false; for (side_t side = WHITE; side <= BLACK; side++) { short_mask = movegen.castle_mask[SHORT][side]; long_mask = movegen.castle_mask[LONG][side]; if (!short_mask.is_empty() && (short_mask & board.init) == short_mask) { char cc = 'k'; if (side == WHITE) cc = toupper(cc); n += snprintf(fen+n, 4096 - n, "%c", cc); wrote_castle = true; } if (!long_mask.is_empty() && (long_mask & board.init) == long_mask) { char cc = 'q'; if (side == WHITE) cc = toupper(cc); n += snprintf(fen+n, 4096 - n, "%c", cc); wrote_castle = true; } } if (!wrote_castle) n += snprintf(fen+n, 4096 - n, "-"); n += snprintf(fen+n, 4096 - n, " "); } /* Fourth record: en-passant square */ bool have_ep = false; for (int n = 0; n. */ #ifndef GAME_H #define GAME_H #include #include #include #include "xstring.h" #include "genrand.h" #include "bitboard.h" #include "pieces.h" #include "movegen.h" #include "move.h" #include "score.h" #include "hashtable.h" #include "evalhash.h" #include "eval_types.h" #include "eval_param.h" #include "timer.h" #include "pst.h" #include "san.h" #define MAX_SEARCH_DEPTH 60 /* maximum depth of search tree */ #define HARD_HORIZON -20 #define MAX_TOTAL_DEPTH (MAX_SEARCH_DEPTH - HARD_HORIZON) #define HASH_TABLE_SIZE (16*1024*1024) #undef USE_HISTORY_HEURISTIC static bool abort_search; enum play_state_t { SEARCH_OK=0, SEARCH_GAME_ENDED, SEARCH_GAME_ENDED_REPEAT, SEARCH_GAME_ENDED_50_MOVE, SEARCH_GAME_ENDED_MATE, SEARCH_GAME_ENDED_STALEMATE, SEARCH_GAME_ENDED_INSUFFICIENT, SEARCH_GAME_ENDED_LOSEBARE, SEARCH_GAME_ENDED_WINBARE, SEARCH_GAME_ENDED_FORFEIT, SEARCH_GAME_ENDED_INADEQUATEMATE, SEARCH_GAME_ENDED_FLAG_CAPTURED, SEARCH_GAME_ENDED_NOPIECES, SEARCH_GAME_ENDED_CHECK_COUNT }; enum chase_state_t { NO_CHASE=0, DRAW_CHASE, LOSE_CHASE, WIN_CHASE }; /* Settings */ enum { MATE_SEARCH_DISABLED=0, MATE_SEARCH_ENABLE_DROP, MATE_SEARCH_ENABLED }; /* Level of play */ enum level_t { LEVEL_RANDOM, LEVEL_NORMAL, LEVEL_BEAL, LEVEL_STATIC, LEVEL_NUM_LEVELS }; extern void (*default_iteration_output)(const char *, ...); extern void (*default_uci_output)(const char *, ...); extern void (*default_xboard_output)(const char *, ...); extern void (*default_error_output)(const char *, ...); extern size_t default_hash_size; struct game_t { /* Functions */ virtual ~game_t() {} virtual void setup_fen_position(const char * /* str */, bool skip_castle = false) { (void)skip_castle; } virtual const char *make_fen_string(char *buffer = NULL) const { return buffer; } virtual void start_new_game(void) {} virtual void set_transposition_table_size(size_t /* size */) {} virtual void print_board(FILE * file = stdout) const {(void)file;} virtual void print_bitboards() const {} virtual void generate_moves(movelist_t * /* movelist */) const {} virtual void generate_legal_moves(movelist_t * /* movelist */) const {} virtual void test_move_game_check() {} virtual side_t get_side_to_move() { return NUM_SIDES; } virtual bool player_in_check(side_t /* side*/ ) { return false; } virtual side_t side_piece_on_square(int /* square */) { return NONE; } virtual void playmove(move_t /* move*/) {} virtual void takeback() {} virtual int eval() { return 0; } virtual int static_qsearch(int /* beta */, int depth = 0) { (void)depth; return 0; } virtual int see(move_t /* move */) { return 0; } virtual size_t get_moves_played() { return 0 ; } virtual move_t move_string_to_move(const char * move_str, const movelist_t * external_movelist = NULL) const { (void)move_str,external_movelist; return 0; } virtual play_state_t think(int /* max_depth */) { return SEARCH_OK; } virtual bool ponder() { return false; } virtual bool analyse() { return false; } virtual void write_piece_descriptions(bool xb = false) const { (void)xb; } virtual void print_wiki_rules(void) {} virtual void print_rules(void) {} virtual void print_pieces(void) const {} virtual void print_eval_parameters(FILE * file = stdout) {(void)file;} virtual void load_eval_parameters(FILE *f) {(void)f;} virtual void print_attacker_bitboard(int /* square */) {} virtual void print_attack_bitboard(int /* square */) {} virtual int pack_rank_file(int /* rank */, int /* file */) { return 0; } virtual play_state_t get_game_end_state(movelist_t * movelist = NULL) { (void)movelist; return SEARCH_OK; } virtual void deduce_castle_flags(side_t /* side */, int /* king_from */, int /* king_to */, int /* rook_from */) {}; virtual void add_rule(uint32_t /* rule */) {} virtual void remove_rule(uint32_t /* rule */) {} virtual uint32_t get_rules(void) { return 0; } virtual void set_maximum_number_of_pieces(const char * /* symbol */, side_t /* side */, int /* count */) {} virtual void set_maximum_number_of_kings(side_t /* side */, int /* count */) {} virtual chase_state_t test_chase() {return NO_CHASE;} virtual bool side_captured_flag(side_t /* side */) { return false; } virtual void print_pst(void) {} const char *get_name() const { return name; } virtual const char *get_piece_notation(int id) const { (void)id; return ""; } virtual const char *get_piece_abbreviation(side_t side, int id) const { (void)side; (void)id; return ""; } virtual int get_most_valuable_piece_id(uint32_t mask) const { (void)mask; return -1; } void set_default_output_function(void (*func)(const char *, ...)) { output_iteration = func; default_iteration_output = func; } void set_uci_output_function(void (*func)(const char *, ...)) { uci_output = func; default_uci_output = func; } void set_xboard_output_function(void (*func)(const char *, ...)) { xboard_output = func; default_xboard_output = func; } void set_error_output_function(void (*func)(const char *, ...)) { error_output = func; default_error_output = func; } move_t get_last_move() { if (moves_played) { return move_list[moves_played-1]; } return 0; } int files, ranks, holdsize; int virtual_files, virtual_ranks; /* Variables */ eval_t mate_score; /* The score returned for mate, normally -LEGALWIN */ eval_t stale_score; /* The score returned for stale mate, normally -LEGALDRAW */ eval_t rep_score; /* The score returned for a repetition, normally -LEGALDRAW */ eval_t bare_king_score; /* The score penalty for a lone king. Normally 0, but -LEGALWIN for shatranj */ eval_t no_piece_score; /* The score returned for having no pieces, normally -LEGALWIN */ eval_t flag_score; /* The score returned when flags are captured, normally -LEGALWIN */ eval_t perpetual; /* The score returned when an in-check position is repeated */ eval_t check_score; /* The score returned when the check-count exceeds the limit */ int check_limit; /* Terminate the game when the check count exceeds this limit */ int repeat_claim; /* Make a claim if the root position has been repeated this many times */ eval_t resign_threshold; /* Increment resign count if the evaluation is below this limit */ eval_t draw_threshold; /* Increment draw count if the absolute evaluation is below this limit */ int resign_count; int draw_count; chess_clock_t clock; char *start_fen; /* FEN string encoding the starting position of this game */ char *xb_setup; /* setup string that has to be sent to XBoard (startup position will be attached) */ char *xb_parent; /* parent variant for XBoard */ int clock_nodes; int start_move_count; /* Full-move counter at the beginning of the game. */ size_t moves_played; /* Number of moves played to current position */ size_t last_move; /* Number of the last move played in the game; useful when we take back a move */ size_t max_moves; /* Maximum number of moves that can be stored */ move_t *move_list; /* list of moves played in the game */ int *move_clock; /* Time-control for engine clock */ uint64_t branches_pruned; movelist_t *movelist; /* Data structure for retrieving the principle variation. At each depth, * there are two branches for the tree: the principle variation and the * "current" branch. If the current branch turns out to be better than * the PV, it becomes the new PV. * A binary tree would be enough to store this information (and be easy * to manipulate), but for now we use a plain NxN array (of which half * the space is wasted, obviously) */ move_t principle_variation[MAX_TOTAL_DEPTH][MAX_TOTAL_DEPTH]; int length_of_variation[MAX_TOTAL_DEPTH]; move_t best_move[MAX_TOTAL_DEPTH]; /* Transposition table */ size_t hash_size; hash_table_t *transposition_table; /* Evaluation table */ eval_hash_table_t *eval_table; /* Hash table for repetition detection */ int8_t repetition_hash_table[0xFFFF+1]; int8_t board_repetition_hash_table[0xFFFF+1]; int8_t fifty_limit; int8_t fifty_scale_limit; /* Whether the engine is currently pondering or not, if it is, disable * time control. */ bool pondering; move_t ponder_move; int multipv; movelist_t exclude; bool analysing; move_t analyse_move; movelist_t analyse_movelist; const char *analyse_fen; bool analyse_new; int analyse_undo; int analyse_moves_played; int option_ms; unsigned int random_key; eval_t random_amplitude; size_t random_ply_count; bool random_ok; bool castle_san_ok; bool trace; bool show_fail_high; bool show_fail_low; bool repetition_claim; level_t level; int square_to_bit[256]; int bit_to_square[256]; int top_left; /* Various function pointers, so we can easily customise things and * adjust to different UIs. */ void (*output_iteration)(const char *, ...) ATTRIBUTE_FORMAT_PRINTF; void (*uci_output)(const char *, ...) ATTRIBUTE_FORMAT_PRINTF; void (*xboard_output)(const char *, ...) ATTRIBUTE_FORMAT_PRINTF; void (*error_output)(const char *, ...) ATTRIBUTE_FORMAT_PRINTF; bool (*check_keyboard)(struct game_t *game); /* Meta-data */ char *name; void truncate_principle_variation(int depth) { length_of_variation[depth] = depth; } inline void backup_principle_variation(int depth, move_t move) { /* Copy principle variation */ if (length_of_variation[depth+1] >= (depth+1)) { principle_variation[depth][depth] = move; for (int c=depth+1; c struct game_template_t : public game_t { board_t board; /* pointer to the current board position */ /* Rules */ piece_description_t pt; board_t root_board; /* The board position at the root of the search */ /* State information */ unmake_info_t *ui; movegen_t movegen; /* Killer moves, storage space requirements must come from the search * function. */ move_t killer[MAX_TOTAL_DEPTH][2]; move_t mate_killer[MAX_TOTAL_DEPTH]; /* Botwinnik-Markov extension. * This is like a killer move or a refutation move, but for NULL moves. * When encountering it on consecutive plies, extend the search (or at least sort the move higher in the * list). */ move_t null_killer[MAX_TOTAL_DEPTH]; /* Counter moves, for use with the counter move heuristic. * Indexed by from- and to square and side to move. */ move_t counter[8*sizeof(kind)][8*sizeof(kind)][NUM_SIDES]; /* Combination moves, similar to counter moves except indexed by our own * previous moves. * Indexed by from- and to square and side to move. */ move_t combo[8*sizeof(kind)][8*sizeof(kind)][2]; #if 0 /* History reductions, as discussed by Ed Schroeder. * The idea is to reduce the depth of moves that have failed low in the * past. This requires a history table. * This is indexed by colour, piece type and destination square. * NB: I tried colour/from square/destination square, and it seems to be * worse, at least with the parameters given by Ed Schroeder. Probably * because using the piece type means we can catch a bad move more * easily even if we moved the piece first (Rf5 exf5 is bad independent * of whether the rook was on f8 or f7, this situation catches both). */ int history_reduce[MAX_PIECE_TYPES][NUM_SIDES][8*sizeof(kind)]; #endif /* History heuristic */ int history[NUM_SIDES][MAX_PIECE_TYPES][8*sizeof(kind)]; int max_history[NUM_SIDES]; int drop_history[NUM_SIDES][MAX_PIECE_TYPES][8*sizeof(kind)]; int max_drop_history[NUM_SIDES]; void init() { movegen = movegen_t(); output_iteration = default_iteration_output; uci_output = default_uci_output; xboard_output = default_xboard_output; error_output = default_error_output; xb_setup = NULL; xb_parent = NULL; check_keyboard = NULL; max_moves = 0; move_list = NULL; move_clock = NULL; ui = NULL; board.flag[WHITE].clear(); board.flag[BLACK].clear(); transposition_table = NULL; eval_table = NULL; hash_size = default_hash_size; board.clear(); memset(&pt, 0, sizeof(pt)); board.piece_types = &pt; trace = false; show_fail_high = false; show_fail_low = false; repetition_claim = true; random_key = 0; random_amplitude = 0; random_ply_count = 0; random_ok = false; analysing = false; pondering = false; analyse_fen = NULL; analyse_new = false; analyse_undo = 0; option_ms = MATE_SEARCH_ENABLE_DROP; fifty_limit = 101; movelist = new movelist_t[MAX_TOTAL_DEPTH+2]; keep_labels = false; for (int n=0; n<256; n++) { square_to_bit[n] = n; bit_to_square[n] = n; } for (int n=0; n() { init(); } game_template_t(int files, int ranks) { assert(files*ranks <= 8*sizeof(kind)); init(); set_board_size(files, ranks); } ~game_template_t() { free(move_list); free(move_clock); free(ui); free(xb_setup); free(start_fen); free(name); for (int n=0; n reach_from[MAX_PIECE_TYPES][8*sizeof(kind)], bitboard_t attack_from[MAX_PIECE_TYPES][8*sizeof(kind)], bitboard_t attack_to[MAX_PIECE_TYPES][8*sizeof(kind)] ) { int king[NUM_SIDES] = { -1, -1 }; for (side_t side = WHITE; side <= BLACK; side++) { if (!(board.bbc[side] & board.royal).is_empty()) { king[side] = board.get_piece((board.bbc[side] & board.royal).bitscan()); continue; } assert(king[side] == -1); for (int n = 0; n dkzone = bitboard_t::board_all & bitboard_t::board_north & bitboard_t::board_westward[files/2]; int num_dks = dkzone.popcount(); #if defined _MSC_VER std::vector dks_list(num_dks); #else int dks_list[num_dks]; #endif int dki = 0; while (!dkzone.is_empty()) { bitboard_t mask = bitboard_t::board_all; if (!(dkzone & bitboard_t::board_east_edge).is_empty()) mask &= bitboard_t::board_east_edge; if (!(dkzone & mask & bitboard_t::board_north_edge).is_empty()) mask &= bitboard_t::board_north_edge; int sq = (mask & dkzone).bitscan(); dkzone.reset(sq); dks_list[dki++] = sq; } /* Mate potential for single pieces: detect if a mate position * exists */ for (int n=0; n dk, ak, dkm, akm; dk.set(dks); ak.set(aks); dkm = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[king[BLACK]], dks, (dk|ak), BLACK); akm = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[king[WHITE]], aks, (dk|ak), WHITE); dkm &= ~dk; /* Kings should not attack eachother. * The attacking king should not block all escape squares (making the position unreachable). * The kings should at least influence eachother, however. */ if ( !((ak|akm) & dk).is_empty() ) continue; if ( !((dk|dkm) & ak).is_empty() ) continue; if ( ((ak|akm) & dkm) == dkm ) continue; if ((attack_to[king[WHITE]][aks] & (dkm|dk)).is_empty()) continue; for (int ps = 0; ps < files*ranks; ps++) { if ( (ak|dk).test(ps) ) continue; if ((attack_to[n][ps] & (dkm|dk)).is_empty()) continue; bitboard_t p, pm; p.set(ps); pm = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[n], ps, (ak|p), WHITE); if ( ((pm | akm) & (dkm|dk)) == (dkm|dk) ) { pt.piece_flags[n] &= ~PF_CANTMATE; break; } } } } } /* Mate potential for pairs of pieces. * This is slightly more complicated because we need to do some * retrograde analysis to test whether the mate can be forced at * all. * TODO: only consider piece placement where it makes sense: * defending king near a corner, and attacking king where it takes * away some escape squares. Similarly, the other pieces should at * least attack some of the escape squares. * TODO: Do not look for mating pairs if royals are not allowed to * slide through check, it's not so useful (and far too slow). */ if (!(board.rule_flags & RF_NO_MOVE_PAST_CHECK)) for (int n1=0; n1 dk, ak, dkm, akm; dk.set(dks); ak.set(aks); dkm = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[king[BLACK]], dks, (dk|ak), BLACK); akm = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[king[WHITE]], aks, (dk|ak), WHITE); dkm &= ~dk; /* Kings should not attack eachother. * The attacking king should not block all escape squares (making the position unreachable). * The kings should at least influence eachother, however. */ if ( !((ak|akm) & dk).is_empty() ) continue; if ( !((dk|dkm) & ak).is_empty() ) continue; if ( ((ak|akm) & dkm) == dkm ) continue; if ((attack_to[king[WHITE]][aks] & (dkm|dk)).is_empty()) continue; /* Second and third piece */ int nn[2] = {n1, n2}; int ps[2]; for (ps[0] = 0; ps[0] < files*ranks; ps[0]++) { if ( (ak|dk).test(ps[0]) ) continue; bitboard_t p[2]; p[0].set(ps[0]); if ((attack_to[nn[0]][ps[0]] & (dkm|dk)).is_empty()) continue; for (ps[1] = 0; ps[1] < files*ranks; ps[1]++) { if ( (ak|dk|p[0]).test(ps[1]) ) continue; p[1].clear(); p[1].set(ps[1]); if ((attack_to[nn[1]][ps[1]] & (dkm|dk)).is_empty()) continue; bitboard_t pa[2]; pa[0] = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[nn[0]], ps[0], (ak|p[0]|p[1]), WHITE); pa[1] = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[nn[1]], ps[1], (ak|p[0]|p[1]), WHITE); /* Is this mate? If not, skip. */ bitboard_t full_attack = akm | pa[0] | pa[1]; if ((full_attack & (dkm|dk)) != (dkm|dk)) continue; /* Identify checking piece; skip double-check. */ int c = 0; if (!(pa[1] & dk).is_empty()) c = 1; if (!(pa[0] & dk).is_empty() && c == 1) continue; /* Find all squares the defending king could * have come from, prior to stepping into the * corner. */ bitboard_t pk = dkm & ~(akm | ak | p[1-c]); /* Now find all alternative escape squares */ bitboard_t bb = pk; while(!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); bitboard_t escape = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[king[BLACK]], square, (dk|ak), BLACK); escape &= ~(ak | akm | pa[1-c]); /* Now find all places the checking piece could * have come from. */ bitboard_t sentry = reach_from[nn[c]][ps[c]] & ~(ak | dk | p[1-c]) & ~attack_from[nn[c]][dks]; if (escape == dk) { bitboard_t alt_escape = pk; alt_escape.reset(square); bitboard_t sp = sentry; bool exit = false; while (!sp.is_empty()) { int s1 = sp.bitscan(); sp.reset(s1); bitboard_t mask = alt_escape | p[c]; mask &= ~attack_from[nn[c]][s1]; if (mask.is_empty()) { pt.pieces_can_win[n1][n2] = pt.pieces_can_win[n2][n1] = true; exit = true; break; } } /* Alternative that distinguishes KFFK and * KBBK: if the alternate piece can cover * both its present location and the * alternate escape, that would also work. */ sp = p[1-c] | alt_escape; for (int s2 = 0; s2 board; board.piece_types = &pt; board.put_piece(king[BLACK], BLACK, dks); board.put_piece(king[WHITE], WHITE, aks); board.put_piece(nn[0], WHITE, ps[0]); board.put_piece(nn[1], WHITE, ps[1]); board.print(); pa[0].print(); printf("\n"); pa[1].print(); printf("\n"); dk.print(); } #endif /* If this set is not empty, then we could have delivered mate */ if (!sentry.is_empty()) pt.pieces_can_win[n1][n2] = pt.pieces_can_win[n2][n1] = true; } } } } } } } } } void initialise_tropism_tables() { bitboard_t occ; memset(pt.tropism, 127, sizeof pt.tropism); for (int piece = 0; piece kn; kn.set(s1); int n = 0; while (kn != bitboard_t::board_all && n < 32) { bitboard_t bb = kn; while (!bb.is_empty()) { int s2 = bb.bitscan(); bb.reset(s2); kn |= movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[piece], s2, occ, WHITE); if (n < pt.tropism[piece][s1][s2]) pt.tropism[piece][s1][s2] = n; } n++; } for (int s2=0; s2 eval_t correctly. // Slightly cleaner (on this end) would be to just implement this // directly by making eval_t a class. static void scale_eval(eval_t &ev, double scl) { ev = eval_t(ev*scl); } /* Crude guestimates for piece values. * This is largely a matter of numerology, but it is better than nothing... * See http://www.chess.com/forum/view/chess960-chess-variants/mathematics-of-relative-chess-piece-value?quote_id=4383142 * for a discussion of the relative worth of forward and capture moves. */ void initialise_piece_values() { bool set = false; for (int n=0; n init; init.set(cs); bitboard_t occ; bitboard_t forward = bitboard_t::board_northward[ranks/2]; bitboard_t backward = bitboard_t::board_southward[ranks/2]; bitboard_t sideward = bitboard_t::board_eastward[files/2] | bitboard_t::board_westward[files/2]; bitboard_t move = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], cs, occ, WHITE); bitboard_t atk = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[n], cs, occ, WHITE); bitboard_t board33 = init; board33 |= (board33 << 1) | (board33 >> 1); board33 |= (board33 << files) | (board33 >> files); bitboard_t board55 = board33; board55 |= (board55 << 1) | (board55 >> 1); board55 |= (board55 << files) | (board55 >> files); /* Get evaluation weight. * Penalise short-range pieces for which the 3x3 and 5x5 attack sets coincide. * Penalise colour-bound pieces. */ int move_count5 = (move & board55).popcount(); int move_count3 = (move & board33).popcount(); int attack_count5 = (move & board55).popcount(); int attack_count3 = (move & board33).popcount(); int forward_move_count5 = (move & board55 & forward).popcount(); int forward_move_count3 = (move & board33 & forward).popcount(); int forward_attack_count5 = (move & board55 & forward).popcount(); int forward_attack_count3 = (move & board33 & forward).popcount(); int backward_move_count5 = (move & board55 & backward).popcount(); int backward_move_count3 = (move & board33 & backward).popcount(); int backward_attack_count5 = (move & board55 & backward).popcount(); int backward_attack_count3 = (move & board33 & backward).popcount(); int sideward_move_count5 = (move & board55 & sideward).popcount(); int sideward_move_count3 = (move & board33 & sideward).popcount(); int sideward_attack_count5 = (move & board55 & sideward).popcount(); int sideward_attack_count3 = (move & board33 & sideward).popcount(); if ( ((move|init)&bitboard_t::board_dark).is_empty() || ((move|init)&bitboard_t::board_light).is_empty() ) { move_count3 = 0; attack_count3 = 0; } /* Captures count for 2/3 of the piece value */ attack_count5 *= 2; attack_count3 *= 2; if (forward_move_count5 != backward_move_count5) { move_count5 += (2*forward_move_count5 + sideward_move_count5)/3; move_count3 += forward_move_count3; attack_count5 += (2*forward_attack_count5 + sideward_attack_count5)/3; attack_count3 += forward_attack_count3; } if (sideward_move_count5 == 0) move_count3 = 0; if (sideward_attack_count5 == 0) attack_count3 = 0; if (move_count3 == move_count5 && move_count5 != forward_move_count5 ) move_count3 = 0; if (attack_count3 == attack_count5 && attack_count5 != forward_attack_count5) attack_count3 = 0; /* Guestimate piece value, normalised such that a rook (with * attack rank 12) is ~500. * This gets the pawn value wrong, however, since that also * depends on the possibility of promotion. */ pt.piece_value[n] = ((attack_count5 + move_count5 + attack_count3 + move_count3) * 500) / (3*12); } if (!set) return; /* Add contribution from promotions */ for (int n=0; n%s (%d %d %d)\n", pt.piece_name[n], pt.piece_name[id], pt.piece_name[k],pt.piece_value[id], pt.piece_value[k], pt.piece_value[id] - pt.piece_value[k]); pt.piece_value[n] += 100*(pt.piece_value[id] - pt.piece_value[k])/pt.piece_value[id]; } else { pt.piece_value[n] += pt.piece_value[k] / 20; } } } /* Down-scale material if we're playing a drop variant */ if (board.rule_flags & RF_USE_CAPTURE) for (int n=0; n reach_from[MAX_PIECE_TYPES][8*sizeof(kind)]; bitboard_t attack_from[MAX_PIECE_TYPES][8*sizeof(kind)]; bitboard_t attack_to[MAX_PIECE_TYPES][8*sizeof(kind)]; bitboard_t moves[MAX_PIECE_TYPES][8*sizeof(kind)][NUM_SIDES]; int ranks = bitboard_t::board_ranks; int files = bitboard_t::board_files; pt.pawn_index[WHITE] = -1; pt.pawn_index[BLACK] = -1; pt.royal_pieces = 0; pt.defensive_pieces = 0; pt.pawn_pieces = 0; pt.minor_pieces = 0; pt.major_pieces = 0; pt.super_pieces = 0; pt.shak_pieces = 0; memset(reach_from, 0, sizeof reach_from); memset(attack_from, 0, sizeof attack_from); setup_fen_position(start_fen); /* Classify the opening position as "closed" (unbroken pawn chain) or * "open" (open files exist). * This affects whether centre play is an important concept for pawns * or not. */ for (int n=0; n init; init.set(cs); bitboard_t occ; bitboard_t move = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], cs, occ, WHITE); bitboard_t from = init; bitboard_t to = move; /* Test whether this piece is colour bound */ if ( ((from|to) & bitboard_t::board_dark).is_empty() || ((from|to) & bitboard_t::board_light).is_empty() ) { pt.piece_flags[n] |= PF_PAIRBONUS | PF_COLOURBOUND; pt.eval_pair_bonus[n].mg = eval_t(PAIR_BONUS_MG * pt.piece_value[n]); pt.eval_pair_bonus[n].eg = eval_t(PAIR_BONUS_EG * pt.piece_value[n]); } /* Test whether this piece can return to its starting square */ for (int k = 0; k < files; k++) { if ( !(pt.piece_flags[n] & PF_NORET) ) break; from = to; to.clear(); while (!from.is_empty() && (to & init).is_empty()) { int fs = from.bitscan(); from.reset(fs); to |= movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], fs, occ, WHITE); } if (!(to & init).is_empty()) pt.piece_flags[n] &= ~PF_NORET; } /* Hack: the above algorithm detects if the piece can return to * its origin for some of its moves, but not for all of its moves. * Thus, it fails to detect the irreversibility of forward pawn * pushes in Xiangqi. * We "fix" this by explicitly testing for this situation. The * correct fix is to try each move on the initial square, and only * mark the piece as reversible if ALL of them can return. */ if ( (move & bitboard_t::board_southward[ranks/2]).is_empty() ) pt.piece_flags[n] |= PF_NORET; /* Determine weight of this piece for king attacks */ pt.king_safety_weight[n] = 1; bitboard_t board33 = init; board33 |= (board33 << 1) | (board33 >> 1); board33 |= (board33 << files) | (board33 >> files); bitboard_t board55 = board33; board55 |= (board55 << 1) | (board55 >> 1); board55 |= (board55 << files) | (board55 >> files); /* Get evaluation weight. * Penalise short-range pieces for which the 3x3 and 5x5 attack sets coincide. * Penalise colour-bound pieces. */ int count5 = (move & board55).popcount(); int count3 = (move & board33).popcount(); if ( (pt.piece_flags[n] & PF_COLOURBOUND) && !(pt.piece_flags[n] & PF_NORET) ) count3 = 0; if (count3 == count5) pt.king_safety_weight[n] = count5; else pt.king_safety_weight[n] = count5 + count3; //printf("%10s %d %d\n", pt.piece_name[n], count3, count5); if ((pt.piece_flags[n] & PF_NORET) && (count3 == count5)) { if (!(board.bbc[WHITE] & board.bbp[n]).is_empty()) pt.pawn_index[WHITE] = n; if (!(board.bbc[BLACK] & board.bbp[n]).is_empty()) pt.pawn_index[BLACK] = n; } /* Weighting for game-phase */ pt.phase_weight[n] = pt.king_safety_weight[n]; if (pt.piece_flags[n] & PF_ROYAL) pt.phase_weight[n] = 0; if (pt.pawn_index[WHITE] == n ) pt.phase_weight[n] = 0; if (pt.pawn_index[BLACK] == n ) pt.phase_weight[n] = 0; /* Mark all squares that could be reached from this square for * reverse lookup. * Collect global information about mobility while we're at it. */ pt.max_moves[n] = 0; pt.avg_moves[n] = 0; pt.min_moves[n] = ranks*files; from = bitboard_t :: board_all; int mobility[8*sizeof(kind)] = { 0 }; while (!from.is_empty()) { int fs = from.bitscan(); from.reset(fs); moves[n][fs][WHITE] = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], fs, occ, WHITE); moves[n][fs][BLACK] = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], fs, occ, BLACK); moves[n][fs][WHITE] &= pt.prison[WHITE][n]; moves[n][fs][BLACK] &= pt.prison[BLACK][n]; move = moves[n][fs][WHITE]; int mc = move.popcount(); mobility[fs] = std::max(1, mc); pt.avg_moves[n] += mc; if (pt.max_moves[n] < mc) pt.max_moves[n] = mc; if (pt.min_moves[n] > mc) pt.min_moves[n] = mc; bitboard_t bb = move; while (!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); bitboard_t move = movegen.generate_move_bitboard_for_flags(pt.piece_move_flags[n], square, occ, WHITE); mobility[fs] += move.popcount()/2; } while (!move.is_empty()) { int ts = move.bitscan(); move.reset(ts); reach_from[n][ts].set(fs); } move = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[n], fs, occ, WHITE); attack_to[n][fs] |= move; while (!move.is_empty()) { int ts = move.bitscan(); move.reset(ts); attack_from[n][ts].set(fs); } } pt.avg_moves[n] = (pt.avg_moves[n] + ranks*files/2) / (ranks*files); /* Centre the mobility table */ int avg = 0; for (int square=0; square= 0) scale_eval(pt.eval_value[pt.pawn_index[WHITE]].mg, PAWN_SCALE_MG); if (pt.pawn_index[BLACK] >= 0 && pt.pawn_index[BLACK] != pt.pawn_index[WHITE]) scale_eval(pt.eval_value[pt.pawn_index[BLACK]].mg, PAWN_SCALE_MG); /* Detect defensive pieces, which do not contribute to the game phase. */ for (int n =0; n::board_north).is_empty() ) pt.defensive_pieces |= (1 << n); if ( (pt.prison[BLACK][n] & bitboard_t::board_south).is_empty() ) pt.defensive_pieces |= (1 << n); if (pt.defensive_pieces & (1 << n)) { pt.piece_flags[n] |= PF_PAIRBONUS; pt.eval_pair_bonus[n].mg = eval_t(PAIR_BONUS_MG * pt.piece_value[n]); pt.eval_pair_bonus[n].eg = eval_t(PAIR_BONUS_EG * pt.piece_value[n]); pt.phase_weight[n] = 0; } } /* Make lame leapers increase in value as pieces are exchanged. * Conversely, hoppers become (much) less valuable in the end game. */ for (int n =0; n front_attack_span[NUM_SIDES][8*sizeof(kind)]; for (side_t side=WHITE; side moves; moves.set(square); front_attack_span[side][square] |= movegen.generate_move_bitboard_from_squares_for_flags(attack_flags, moves, bitboard_t::board_empty, side); int n = 0; while (!moves.is_empty() && n < ranks*files) { moves = movegen.generate_move_bitboard_from_squares_for_flags(move_flags, moves, bitboard_t::board_empty, side); moves &= ~((bitboard_t::board_eastward[unpack_file(square)] | bitboard_t::board_westward[unpack_file(square)]) & bitboard_t::board_rank[unpack_rank(square)] ); pt.front_span[side][square] |= moves; n++; } front_attack_span[side][square] |= movegen.generate_move_bitboard_from_squares_for_flags(attack_flags, pt.front_span[side][square], bitboard_t::board_empty, side); } } for (side_t side=WHITE; side block = pt.front_span[next_side[side]][ds] | front_attack_span[next_side[side]][ds]; block.set(ds); if ( !(block & pt.front_span[side][as]).is_empty() ) pt.passer_mask[side][as].set(ds); } } } /* Weak pawns: pawns that cannot be protected because they are * backward or isolated. * Pawns are weak if there are no friendly pawns in the registered * mask. */ bitboard_t back_attack_span[NUM_SIDES][8*sizeof(kind)]; memset(back_attack_span, 0, sizeof back_attack_span); for (side_t side=WHITE; side back_span[8*sizeof(kind)]; memset(back_span, 0, sizeof back_span); for (int as=0; as::board_empty, side); pt.weak_mask[side][as] = back_attack_span[side][as]; } //int square = pack_rank_file(ranks/2, files/2); //printf("%s\n", square_names[square]); //back_span[square].print("\n"); //back_attack_span[side][square].print("\n"); //if (side == BLACK) exit(0); } /* Determine mate potential for pieces */ assess_piece_mate_potential(reach_from, attack_from, attack_to); /* Piece classification, for evaluation purposes: * - Royal: a royal piece type * - Pawn: the most basic piece; can promote * - Minor: a piece that cannot deliver mate (knight/bishop class) * - Major: a piece that can deliver mate (rook class) * - Super: an extremely strong piece (queen or better) */ for (int n=0; n= 20) pt.super_pieces |= (1<%s is compatible\n", pt.piece_notation[n], pt.piece_notation[k]); } //if (pointless) printf("Deferral of promotion of %s is pointless\n", pt.piece_name[n]); if (!pointless) pt.deferral_allowed |= (1< draw * major+minor vs major -> draw * super vs minor or major -> won */ pt.pawn_pieces &= ~pt.royal_pieces; pt.defensive_pieces &= ~(pt.royal_pieces | pt.pawn_pieces); pt.minor_pieces &= ~pt.pawn_pieces; pt.minor_pieces |= pt.defensive_pieces; pt.major_pieces &= ~(pt.minor_pieces | pt.pawn_pieces); pt.super_pieces &= ~(pt.major_pieces | pt.minor_pieces | pt.pawn_pieces); /* If we're not using a "shak" rule, set all pieces as able to give * "shak" (all checks are good for winning). */ if (!(board.rule_flags & RF_USE_SHAKMATE)) pt.shak_pieces = ~0; /* Classify the opening position as "closed" (unbroken pawn chain) or * "open" (open files exist). * This affects whether centre play is an important concept for pawns * or not. */ bitboard_t pawns; for (int n=0; n prison = pt.prison[WHITE][n] | pt.prison[BLACK][n]; if ((pt.piece_flags[n]&PF_ROYAL) & (prison != bitboard_t::board_all)) { for (int square=0; square 0) pt.eval_pst[n][square].mg -= rank; if (board.rule_flags & RF_USE_CAPTURE) pt.eval_pst[n][square].mg += x; } } else { for (int square=0; square::board_north).test(square) ) { pt.eval_pst[n][square].mg += 2*pt.piece_value[n]/3; pt.eval_pst[n][square].eg += pt.piece_value[n]; } } } } /* Palace tropism */ for (int royal = 0; royal < pt.num_piece_types; royal++) { if ( !(pt.piece_flags[royal] & PF_ROYAL) ) continue; if ( pt.prison[WHITE][royal] == bitboard_t::board_all) continue; if ( pt.prison[BLACK][royal] == bitboard_t::board_all) continue; int piece = n; if (pt.royal_pieces & (1 << piece)) continue; if (pt.defensive_pieces & (1 << piece)) continue; #if defined _MSC_VER std::vector tropism(files*ranks); #else int tropism[files*ranks]; #endif int max_trop = 0; int avg_trop = 0; bitboard_t palace = pt.prison[BLACK][royal] & bitboard_t::board_north; for (int square = 0; square < ranks*files; square++) { tropism[square] = 0; //if (palace.test(square)) continue; bitboard_t bb = palace; while(!bb.is_empty()) { int s2 = bb.bitscan(); bb.reset(s2); tropism[square] += pt.tropism[piece][square][s2]; } max_trop = std::max(max_trop, tropism[square]); avg_trop += tropism[square]; } avg_trop = (avg_trop + ranks*files/2) / (ranks*files); for (int square = 0; square < ranks*files; square++) tropism[square] = avg_trop - tropism[square]; if (pt.pawn_pieces & (1 << piece)) { for (int square=files*(ranks-1); square occ; for (int square=0; square attack = movegen.generate_move_bitboard_for_flags(attack_flags, square, occ, WHITE); if (!(attack & palace).is_empty()) { pt.eval_pst[n][square].mg += 5; if (!(attack & palace & bitboard_t::board_file[unpack_file(square)]).is_empty()) pt.eval_pst[n][square].eg += 10; } } } for (int square=0; square flags = board.flag[n]; #if defined _MSC_VER std::vector tropism(files*ranks, 0); #else int tropism[files*ranks]; memset(tropism, 0, sizeof tropism); #endif int max_trop = 0; int avg_trop = 0; while (!flags.is_empty()) { int fs = flags.bitscan(); flags.reset(fs); for (int square=0; square= 0) ? virtual_files : files; board.virtual_ranks = (virtual_ranks >= 0) ? virtual_ranks : ranks; for (int n=0; n<128; n++) board.bit_to_square[n] = -1; for (int n=0; n<256; n++) if (square_to_bit[n] >= 0 && square_to_bit[n] < 128) { bit_to_square[square_to_bit[n]] = n; board.bit_to_square[square_to_bit[n]] = n; } /* Make sure any gaps in the board are deleted from masks */ for (side_t side = WHITE; side<=BLACK; side++) for (int n=0; n::board_all; pt.optional_promotion_zone[side][n] &= bitboard_t::board_all; pt.special_zone[side][n] &= bitboard_t::board_all; pt.prison[side][n] &= bitboard_t::board_all; pt.drop_zone[side][n] &= bitboard_t::board_all; for (int k=0; k::board_all; } /* Holding size. * Royal pieces and pieces that demote on capture never go into * holdings. */ holdsize = 0; if (board.rule_flags & RF_USE_HOLDINGS) { for (int n=0; n>= 1; castle_san_ok = true; for (int c = SHORT; c line && isspace(s[0])) { s[0] = '\0'; s--; } /* New variant */ if (strstr(line, "Variant:") == line) { found_variant = false; const char *p = line + 8; while (isspace(*p) && *p) p++; if (strstr(p, get_name())) /* We've found the variant we're looking for */ found_variant = true; continue; } /* Have we found the correct variant? */ if (!found_variant) continue; /* What are we trying to read? */ if ((s = strstr(line, "Value"))) { s += 5; while (*s && isspace(*s)) s++; id = -1; for (int n=0; n-1) { pt.eval_value[id].mg = mg; pt.eval_value[id].eg = eg; } } if ((s = strstr(line, "PST"))) { s += 3; while (*s && isspace(*s)) s++; id = -1; for (int n=0; n::pack_rank_file(rank, file); } void print_board(FILE *file = stdout) const { board.print(file); } void print_pieces(void) const { pt.print(); printf("Steppers: %d\n", movegen.number_of_steppers-1); printf("Leapers: %d\n", movegen.number_of_leapers); printf("ALeapers: %d\n", movegen.number_of_aleapers); } void print_bitboards() const { board.print_bitboards(); } void generate_moves(movelist_t *movelist) const { movegen.generate_moves(movelist, &board, board.side_to_move); } void print_attacker_bitboard(int square) { movegen.get_all_attackers(&board, bitboard_t::board_all, square).print(); } void print_attack_bitboard(int square) { bitboard_t test_squares; bitboard_t source_mask; source_mask.set(square); movegen.generate_attack_bitboard(&board, test_squares, source_mask, board.get_side(square)).print(); } virtual void print_pst(void) { for (int n=0; n=0; rank--) { printf("%2s ", rank_names[rank]); for (int file=0; file=0; rank--) { printf("%2s ", rank_names[rank]); for (int file=0; file=0; rank--) { printf("%2s ", rank_names[rank]); for (int file=0; file=0; n--) if (mask & (1< board_copy = board; /* Now filter out the illegal moves */ int n = 0; side_t me = board.side_to_move; while(nnum_moves) { unmake_info_t ui; move_t move = movelist->move[n]; board_copy.makemove(move, &ui); bool illegal = movegen.player_in_check(&board_copy, me); if ((board.rule_flags & RF_QUIET_PROMOTION) && is_promotion_move(move) ) { int square = get_move_to(move); int p = board_copy.get_piece(square); bitboard_t atk = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[p], square, board_copy.get_occupied(), next_side[board_copy.side_to_move]); if (!(atk & board_copy.bbc[board_copy.side_to_move]).is_empty()) illegal = true; if (movegen.was_checking_move(&board_copy, board_copy.side_to_move, move)) illegal = true; } board_copy.unmakemove(move, &ui); if (illegal) { movelist->num_moves--; movelist->move[n] = movelist->move[movelist->num_moves]; } else { n++; } } } #include "piece_rules.h" #include "board_rules.h" #include "fen.h" void start_new_game(void) { board.clear(); if (max_moves < 1000) max_moves = 1000; start_move_count = 0; moves_played = last_move = 0; random_ok = false; fifty_scale_limit = int8_t(fifty_limit * 0.8); level = LEVEL_NORMAL; multipv = 1; move_list = (move_t *)realloc(move_list, max_moves * sizeof *move_list); move_clock = (int *)realloc(move_clock, max_moves * sizeof *move_clock); ui = (unmake_info_t *)realloc(ui, max_moves * sizeof *ui); setup_fen_position(start_fen); memset(see_cache, 0, sizeof(see_cache)); destroy_hash_table(transposition_table); destroy_eval_hash_table(eval_table); transposition_table = create_hash_table(hash_size); eval_table = create_eval_hash_table(hash_size / 16); } void set_transposition_table_size(size_t size) { hash_size = default_hash_size = size; destroy_hash_table(transposition_table); destroy_eval_hash_table(eval_table); transposition_table = create_hash_table(hash_size); eval_table = create_eval_hash_table(hash_size / 16); } size_t get_moves_played() { return moves_played; } bool player_in_check(side_t side) { return movegen.player_in_check(&board, side); } side_t side_piece_on_square(int square) { if (board.bbc[WHITE].test(square)) return WHITE; if (board.bbc[BLACK].test(square)) return BLACK; return NONE; } side_t get_side_to_move() { return board.side_to_move; } void playmove(move_t move) { board.makemove(move, ui + moves_played); move_list[moves_played] = move; repetition_hash_table[board.hash&0xFFFF]++; if (board.rule_flags & RF_USE_CAPTURE) board_repetition_hash_table[board.board_hash&0xFFFF]++; for (int c = SHORT; c mask = movegen.castle_mask[c][next_side[board.side_to_move]]; if ((board.init & mask) != (ui[moves_played-1].init & mask)) { board.hash ^= flag_key[next_side[board.side_to_move]][c]; board.board_hash ^= flag_key[next_side[board.side_to_move]][c]; } } moves_played++; } void replaymove() { playmove(move_list[moves_played]); } void takeback() { if (moves_played) { moves_played--; repetition_hash_table[board.hash&0xFFFF]--; if (board.rule_flags & RF_USE_CAPTURE) board_repetition_hash_table[board.board_hash&0xFFFF]--; board.unmakemove(move_list[moves_played], ui + moves_played); } } #include "see.h" #include "killer.h" #include "history.h" #include "search.h" #include "movestring.h" void calculate_pawn_structure(pawn_structure_t *ps); template eval_t static_evaluation(side_t side_to_move, int alpha = -LEGALWIN, int beta = LEGALWIN); int eval() { return static_evaluation(board.side_to_move); } #include "betza_string.h" static const char *describe_move_flags(move_flag_t flags) { const char *desc = "compound"; if ( (flags & ~MF_LEAPER_FLAGS) == 0) { desc = "leaper"; if (is_aleaper(flags)) desc = "asymmetric leaper"; if (is_double_leaper(flags)) desc = "double leaper"; if (is_masked_leaper(flags)) desc = "lame leaper"; } else if ( (flags & ~MF_SLIDER) == 0) { desc = "slider"; } else if ( (flags & ~MF_HOPPER) == 0) { desc = "hopper (it must leap over enemy pieces)"; } else if ( (flags & ~MF_STEPPER) == 0) { desc = "stepper"; } else if ( (flags & ~MF_RIDER) == 0) { desc = "rider"; } return desc; } void print_wiki_rules(void) { board_t backup_board = board; int8_t backup_repetition_hash_table[0xFFFF+1]; int8_t backup_board_repetition_hash_table[0xFFFF+1]; memcpy(backup_repetition_hash_table, repetition_hash_table, sizeof repetition_hash_table); memcpy(backup_board_repetition_hash_table, board_repetition_hash_table, sizeof board_repetition_hash_table); bool promotions = false; bool drops = false; setup_fen_position(start_fen); printf("{{AutomaticRules}}\n"); printf("
\n%s\nThe opening position for %s.
\n", start_fen, name); int ranks = (virtual_ranks > 0) ? virtual_ranks : this->ranks; int files = (virtual_files > 0) ? virtual_files : this->files; printf("=Rules=\n"); printf("\n"); printf("%s is played on a %dx%d board.
\n", name, files, ranks); printf("%s moves first.
\n", (board.side_to_move == WHITE) ? "White" : "Black"); for (int n=0; n 0) { printf("White begins the game with %d %s%s (%s)", nw, pt.piece_name[n], (nw == 1) ? "" : plural, pt.piece_abbreviation[n][WHITE]); if (nhw) printf(" and %d held in-hand", nhw); if (nb) printf("
\n"); } if (nb > 0) { printf("Black begins the game with %d %s%s (%s)", nb, pt.piece_name[n], (nb == 1) ? "" : plural, pt.piece_abbreviation[n][WHITE]); if (nhb) printf(" and %d held in-hand", nhb); } } printf("
\n"); } for (int n=0; n\n", pt.piece_name[n]); if (board.rule_flags & RF_KING_TRAPPED) printf("The %s may not leave the Palace.
\n", pt.piece_name[n]); if (board.rule_flags & RF_KING_TABOO) printf("The %ss may not face eachother along an open file.
\n", pt.piece_name[n]); if (board.rule_flags & RF_KING_DUPLECHECK) printf("If all %ss are under attack, at least one of them must be taken out of check.
\n", pt.piece_name[n]); } } if (board.rule_flags & RF_FORCE_CAPTURE) printf("Captures are compulsory.
\n"); if (board.rule_flags & RF_KEEP_CAPTURE) printf("Captured pieces go to your hand.
\n"); if (board.rule_flags & RF_RETURN_CAPTURE) printf("Captured pieces are returned to your opponent's hand.
\n"); if (board.rule_flags & RF_ALLOW_PICKUP) printf("Pieces may be taken in-hand as a move.
\n"); if (board.rule_flags & RF_ALLOW_DROPS) printf("Pieces in-hand %s be dropped on an empty square. This counts as a move.
\n", (board.rule_flags & RF_FORCE_DROPS) ? "must immediately" : "may"); if (board.rule_flags & RF_GATE_DROPS) printf("Pieces in-hand may be gated in when moving a back-rank piece.
\n"); printf("\n"); printf("==Objectives==\n"); printf("The game is won:\n"); if (mate_score < 0) printf("* If the enemy king is check-mated.\n"); if (stale_score < 0) printf("* If the enemy king is stale-mated.\n"); if (bare_king_score < 0) printf("* If the enemy only has a king left and cannot take your last piece\n"); if (rep_score == LEGALLOSS) printf("* If the same position occurs for the third time.\n"); if (no_piece_score < 0 && board.royal.is_empty()) printf("* If the enemy has no pieces left.\n"); if (flag_score < 0 && board.rule_flags & RF_CAPTURE_THE_FLAG) printf("* If the enemy flags have been captured.\n"); printf("\n"); printf("The game is a draw:\n"); if (mate_score == 0) printf("* If the enemy king is check-mated.\n"); if (stale_score == 0) printf("* If the enemy king is stale-mated.\n"); if (bare_king_score == 0) printf("* If the enemy only has a king left and cannot take your last piece\n"); if (no_piece_score == 0 && board.royal.is_empty()) printf("* If the enemy has no pieces left.\n"); if (flag_score == 0 && board.rule_flags & RF_CAPTURE_THE_FLAG) printf("* If the enemy flags have been captured.\n"); if (rep_score == 0) printf("* If the same position occurs for the third time.\n"); if (fifty_limit) printf("* If no progress has been made for %d consecutive moves.\n", (int)fifty_limit/2); if (mate_score < 0) printf("* If there is insufficient material to mate the enemy king.\n"); printf("\n"); printf("==Movement of the pieces==\n"); printf("\n"); for (int n=0; n::pack_rank_file(ranks/2, files/2); const char lc = pt.piece_name[n][strlen(pt.piece_name[n])-1]; const char *plural = (lc == 's' || lc == 'z') ? "es" : "s"; printf("\n\n", pt.piece_name[n], pt.piece_abbreviation[n][WHITE]); const char *move_desc = describe_move_flags(pt.piece_move_flags[n]); const char *cap_desc = describe_move_flags(pt.piece_capture_flags[n]); printf("\n\n\n"); } printf("
[[%s]] (%s)
\n"); if (pt.piece_move_flags[n] == pt.piece_capture_flags[n] && pt.piece_move_flags[n]) { printf("The %s moves and captures as a %s (marked •).
\n", pt.piece_name[n], move_desc); } else { if (pt.piece_move_flags[n]) printf("The %s moves as a %s (marked •).
\n", pt.piece_name[n], move_desc); else printf("The %s cannot make non-capture moves.
\n", pt.piece_name[n]); if (pt.piece_capture_flags[n]) printf("The %s captures as a %s (marked ×).
\n", pt.piece_name[n], move_desc); else printf("The %s cannot make capture moves.
\n", pt.piece_name[n]); } printf("[[Betza description]]: %s
\n", piece_moves_to_betza(n));//move_flags_to_betza(pt.piece_move_flags[n])); if (pt.piece_flags[n] & PF_NORET) printf("The %s cannot move back to the square it came from.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_CASTLE) printf("The %s can castle.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_TAKE_EP) printf("The %s can capture en-passant.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_SHAK) printf("The %s delivers \"shak\" check.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_NOMATE) printf("The %s is not allowed to deliver mate.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPNOCHECK) printf("The %s may not be dropped with check.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPNOMATE) printf("The %s may not be dropped to deliver mate.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPONEFILE) { if (pt.piece_drop_file_maximum[n] == 1) printf("The %s may not be dropped if there is already a friendly %s on the same file.
\n", pt.piece_name[n], pt.piece_name[n]); else printf("The %s may not be dropped if there are already %d friendly %s%s on the same file.
\n", pt.piece_name[n], pt.piece_drop_file_maximum[n], pt.piece_name[n], plural); } if (pt.piece_flags[n] & PF_IRON) printf("The %s may not be captured.
\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPDEAD) printf("The %s may be dropped where it cannot move.
\n", pt.piece_name[n]); if (pt.piece_promotion_choice[n]) { printf("The %s can promote to a ", pt.piece_name[n]); promotions = true; bool first = true; piece_bit_t pc = pt.piece_promotion_choice[n]; while (pc) { int k = bitscan32(pc); pc ^= (1 << k); if (!first) { if (pc == 0) printf(" or "); else printf(", "); } printf("[[%s]]", pt.piece_name[k]); first = false; } printf(".
\n"); } if (pt.demotion[n] != n) printf("The %s demotes to %s when captured
\n", pt.piece_name[n], pt.piece_name[pt.demotion[n]]); char fen[4096] = { 0 }; int move_board[256]; memset(move_board, 0, sizeof(move_board)); bitboard_t xmark, omark; board_t demo = board; demo.clear(); demo.side_to_move = (nw > 0 || nb == 0) ? WHITE : BLACK; demo.put_piece(n, demo.side_to_move, centre_square); if (pt.piece_special_move_flags[n]) { int f = files/4; bitboard_t bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f = 1; while (bb.is_empty() && f < files) { bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f++; } if (!bb.is_empty()) { int s = bb.bitscan(); demo.put_piece(n, demo.side_to_move, s); move_board[bit_to_square[s]] = -1; } } omark = movegen.generate_moves_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); xmark = movegen.generate_attack_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); xmark &= ~omark; while (!xmark.is_empty()) { int s = xmark.bitscan(); xmark.reset(s); s = bit_to_square[s]; if (s < 0) continue; move_board[s] = 2; } while (!omark.is_empty()) { int s = omark.bitscan(); omark.reset(s); s = bit_to_square[s]; if (s < 0) continue; move_board[s] = 1; } for (int s = 0; s::board_all.test(bit)) move_board[s] = 3; } move_board[bit_to_square[centre_square]] = -1; int k = 0; for (int r = ranks-1; r>=0; r--) { int count = 0; for (int f = 0; f < files; f++) { int s = f + r * files; //printf("%+3d", move_board[s]); /* Empty? */ if (move_board[s] == 0) { count++; continue; } /* Not empty, do we have a count? */ if (count) k += snprintf(fen+k, 4096 - k, "%d", count); count = 0; if (move_board[s] == -1) { k += snprintf(fen+k, 4096-k, "%s", pt.piece_abbreviation[n][demo.side_to_move]); continue; } const char *colour_string = " .#*"; k += snprintf(fen+k, 4096-k, "%c", colour_string[move_board[s]]); } if (count) k += snprintf(fen+k, 4096 - k, "%d", count); if (r) k += snprintf(fen+k, 4096 - k, "/"); //printf("\n"); } //demo.print(stdout, xmark, omark); //printf("\n"); printf("
"); printf("%s", fen); printf("
\n\n"); if (promotions) { printf("==Promotion==\n"); printf("Promotions are allowed when a piece moves into %sthe promotion zone.
\n", (board.rule_flags & RF_PROMOTE_IN_PLACE) ? "" : "or out of "); if (board.rule_flags & RF_PROMOTE_IN_PLACE) printf("Pieces inside the promotion zone can also be promoted later. This counts as a move.
\n"); printf("\n"); for (int n=0; n 0 || nb == 0) ? WHITE : BLACK; printf("\n\n"); char fen[4096] = { 0 }; int move_board[256]; memset(move_board, 0, sizeof(move_board)); for (side_t side_to_move = (nw?WHITE:BLACK); side_to_move <= (nb?BLACK:WHITE); side_to_move++) { bitboard_t omark = pt.promotion_zone[side_to_move][n]; bitboard_t xmark = pt.optional_promotion_zone[side_to_move][n]; omark &= ~xmark; while (!xmark.is_empty()) { int s = xmark.bitscan(); xmark.reset(s); s = bit_to_square[s]; if (s < 0) continue; move_board[s] = 2 + 3*side_to_move; } while (!omark.is_empty()) { int s = omark.bitscan(); omark.reset(s); s = bit_to_square[s]; if (s < 0) continue; move_board[s] = 1 + 3*side_to_move; } for (int s = 0; s::board_all.test(bit)) move_board[s] = 3; } } int k = 0; for (int r = ranks-1; r>=0; r--) { int count = 0; for (int f = 0; f < files; f++) { int s = f + r * files; //printf("%+3d", move_board[s]); /* Empty? */ if (move_board[s] == 0) { count++; continue; } /* Not empty, do we have a count? */ if (count) k += snprintf(fen+k, 4096 - k, "%d", count); count = 0; const char *colour_string = " ,$*.#"; k += snprintf(fen+k, 4096-k, "%c", colour_string[move_board[s]]); } if (count) k += snprintf(fen+k, 4096 - k, "%d", count); if (r) k += snprintf(fen+k, 4096 - k, "/"); //printf("\n"); } //demo.print(stdout, xmark, omark); //printf("\n"); printf("\n\n", fen); } printf("
[[%s]] (%s)
\n", pt.piece_name[n], pt.piece_abbreviation[n][WHITE]); bitboard_t omark = pt.promotion_zone[side_to_move][n]; bitboard_t xmark = pt.optional_promotion_zone[side_to_move][n]; omark &= ~xmark; if (!omark.is_empty()) printf("The %s must promote when it reaches one of the squares marked with •.
\n", pt.piece_name[n]); if (!xmark.is_empty()) printf("The %s may promote when it reaches one of the squares marked with ×.
\n", pt.piece_name[n]); printf("The %s can promote to a ", pt.piece_name[n]); promotions = true; bool first = true; piece_bit_t pc = pt.piece_promotion_choice[n]; while (pc) { int k = bitscan32(pc); pc ^= (1 << k); if (!first) { if (pc == 0) printf(" or "); else printf(", "); } printf("[[%s]]", pt.piece_name[k]); first = false; } printf(".
\n"); if (pt.demotion[n] != n) printf("The %s demotes to %s when captured
\n", pt.piece_name[n], pt.piece_name[pt.demotion[n]]); printf("
%s
\n\n"); } printf("[[Category: %dx%d]]\n", files, ranks); if (board.rule_flags & RF_USE_DROPS) printf("[[Category: Games with drops]]\n"); board = backup_board; memcpy(repetition_hash_table, backup_repetition_hash_table, sizeof repetition_hash_table); memcpy(board_repetition_hash_table, backup_board_repetition_hash_table, sizeof board_repetition_hash_table); } void print_rules(void) { board_t backup_board = board; int8_t backup_repetition_hash_table[0xFFFF+1]; int8_t backup_board_repetition_hash_table[0xFFFF+1]; memcpy(backup_repetition_hash_table, repetition_hash_table, sizeof repetition_hash_table); memcpy(backup_board_repetition_hash_table, board_repetition_hash_table, sizeof board_repetition_hash_table); setup_fen_position(start_fen); int files = bitboard_t::board_files; int ranks = bitboard_t::board_ranks; printf("Rules of %s\n", name); printf("\n"); printf("%s is played on a %dx%d board\n", name, files, ranks); printf("%s moves first.\n", (board.side_to_move == WHITE) ? "White" : "Black"); printf("\n"); printf("The game is won:\n"); if (mate_score < 0) printf(" * If the enemy king is check-mated.\n"); if (stale_score < 0) printf(" * If the enemy king is stale-mated.\n"); if (bare_king_score < 0) printf(" * If the enemy only has a king left and cannot take your last piece\n"); if (rep_score == LEGALLOSS) printf(" * If the same position occurs for the third time.\n"); if (no_piece_score < 0 && board.royal.is_empty()) printf(" * If the enemy has no pieces left.\n"); if (flag_score < 0 && board.rule_flags & RF_CAPTURE_THE_FLAG) printf(" * If the enemy flags have been captured.\n"); printf("\n"); printf("The game is a draw:\n"); if (mate_score == 0) printf(" * If the enemy king is check-mated.\n"); if (stale_score == 0) printf(" * If the enemy king is stale-mated.\n"); if (bare_king_score == 0) printf(" * If the enemy only has a king left and cannot take your last piece\n"); if (no_piece_score == 0 && board.royal.is_empty()) printf(" * If the enemy has no pieces left.\n"); if (flag_score == 0 && board.rule_flags & RF_CAPTURE_THE_FLAG) printf(" * If the enemy flags have been captured.\n"); if (rep_score == 0) printf(" * If the same position occurs for the third time.\n"); if (fifty_limit) printf(" * If no progress has been made for %d consecutive moves.\n", (int)fifty_limit/2); if (mate_score < 0) printf(" * If there is insufficient material to mate the enemy king.\n"); printf("\n"); printf("Movement of the pieces:\n"); for (int n=0; n::pack_rank_file(ranks/2, files/2); const char lc = pt.piece_name[n][strlen(pt.piece_name[n])-1]; const char *plural = (lc == 's' || lc == 'z') ? "es" : "s"; printf("The %s\n", pt.piece_name[n]); const char *move_desc = describe_move_flags(pt.piece_move_flags[n]); const char *cap_desc = describe_move_flags(pt.piece_capture_flags[n]); if (pt.piece_move_flags[n] == pt.piece_capture_flags[n] && pt.piece_move_flags[n]) { printf("The %s moves and captures as a %s (marked +).\n", pt.piece_name[n], move_desc); } else { if (pt.piece_move_flags[n]) printf("The %s moves as a %s (marked +).\n", pt.piece_name[n], move_desc); else printf("The %s cannot make non-capture moves.\n", pt.piece_name[n]); if (pt.piece_capture_flags[n]) printf("The %s captures as a %s (marked *).\n", pt.piece_name[n], move_desc); else printf("The %s cannot make capture moves.\n", pt.piece_name[n]); } printf("Betza description: %s\n", piece_moves_to_betza(n));//move_flags_to_betza(pt.piece_move_flags[n])); if (pt.piece_flags[n] & PF_NORET) printf("The %s cannot move back to the square it came from.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_CASTLE) printf("The %s can castle.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_TAKE_EP) printf("The %s can capture en-passant.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_SHAK) printf("The %s delivers \"shak\" check.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_NOMATE) printf("The %s is not allowed to deliver mate.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPNOCHECK) printf("The %s may not be dropped with check.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPNOMATE) printf("The %s may not be dropped to deliver mate.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPONEFILE) { if (pt.piece_drop_file_maximum[n] == 1) printf("The %s may not be dropped if there is already a friendly %s on the same file.\n", pt.piece_name[n], pt.piece_name[n]); else printf("The %s may not be dropped if there are already %d friendly %s%s on the same file.\n", pt.piece_name[n], pt.piece_drop_file_maximum[n], pt.piece_name[n], plural); } if (pt.piece_flags[n] & PF_COLOURBOUND) printf("The %s is colour bound.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_IRON) printf("The %s may not be captured.\n", pt.piece_name[n]); if (pt.piece_flags[n] & PF_DROPDEAD) printf("The %s may be dropped where it cannot move.\n", pt.piece_name[n]); if (pt.defensive_pieces & (1 << n)) printf("The %s is a defensive piece.\n", pt.piece_name[n]); if (pt.pawn_pieces & (1 << n)) printf("The %s is a pawn-class piece.\n", pt.piece_name[n]); if (pt.minor_pieces & (1 << n)) printf("The %s is a minor piece.\n", pt.piece_name[n]); if (pt.major_pieces & (1 << n)) printf("The %s is a major piece.\n", pt.piece_name[n]); if (pt.super_pieces & (1 << n)) printf("The %s is a super piece.\n", pt.piece_name[n]); if (pt.pawn_index[WHITE] == n) printf("The %s is white's pawn-type piece\n", pt.piece_name[n]); if (pt.pawn_index[BLACK] == n) printf("The %s is black's pawn-type piece\n", pt.piece_name[n]); //printf("The %s has safety weight %d.\n", pt.piece_name[n], pt.king_safety_weight[n]); int nw = (board.bbp[n]&board.bbc[WHITE]).popcount(); int nb = (board.bbp[n]&board.bbc[BLACK]).popcount(); if (nw == nb) printf("White and black begin the game with %d %s%s\n", nw, pt.piece_name[n], (nw == 1) ? "" : (lc == 's' || lc == 'z') ? "es" : "s"); else { if (nw > 0) printf("White begins the game with %d %s%s\n", nw, pt.piece_name[n], (nw == 1) ? "" : (lc == 's' || lc == 'z') ? "es" : "s"); if (nb > 0) printf("Black begins the game with %d %s%s\n", nb, pt.piece_name[n], (nb == 1) ? "" : (lc == 's' || lc == 'z') ? "es" : "s"); } if (pt.piece_flags[n] & PF_ROYAL) printf("The %s is a royal piece (king).\n", pt.piece_name[n]); if (pt.piece_promotion_choice[n]) { printf("The %s can promote to a ", pt.piece_name[n]); bool first = true; piece_bit_t pc = pt.piece_promotion_choice[n]; while (pc) { int k = bitscan32(pc); pc ^= (1 << k); if (!first) { if (pc == 0) printf(" or "); else printf(", "); } printf("%s", pt.piece_name[k]); first = false; } printf(".\n"); bitboard_t pz[2], opz[2]; pz[WHITE] = pt.promotion_zone[WHITE][n]; pz[BLACK] = pt.promotion_zone[BLACK][n]; opz[WHITE] = pt.optional_promotion_zone[WHITE][n]; opz[BLACK] = pt.optional_promotion_zone[BLACK][n]; if (nw) { printf("A white %s promotes when it reaches ", pt.piece_name[n]); bool first = true; while(!pz[WHITE].is_empty()) { int square = pz[WHITE].bitscan(); pz[WHITE].reset(square); if (!first) { if (pz[WHITE].is_empty()) printf(" or "); else printf(", "); } first = false; printf("%s", square_names[square]); } printf(".\n"); if (!opz[WHITE].is_empty()) { printf("Promotion is optional on "); bool first = true; while(!opz[WHITE].is_empty()) { int square = opz[WHITE].bitscan(); opz[WHITE].reset(square); if (!first) { if (opz[WHITE].is_empty()) printf(" or "); else printf(", "); } first = false; printf("%s", square_names[square]); } printf(".\n"); } } if (nb) { printf("A black %s promotes when it reaches ", pt.piece_name[n]); bool first = true; while(!pz[BLACK].is_empty()) { int square = pz[BLACK].bitscan(); pz[BLACK].reset(square); if (!first) { if (pz[BLACK].is_empty()) printf(" or "); else printf(", "); } first = false; printf("%s", square_names[square]); } printf(".\n"); if (!opz[BLACK].is_empty()) { printf("Promotion is optional on "); bool first = true; while(!opz[BLACK].is_empty()) { int square = opz[BLACK].bitscan(); opz[BLACK].reset(square); if (!first) { if (opz[BLACK].is_empty()) printf(" or "); else printf(", "); } first = false; printf("%s", square_names[square]); } printf(".\n"); } } } if (pt.demotion[n] != n) printf("The %s demotes to %s when captured\n", pt.piece_name[n], pt.piece_name[pt.demotion[n]]); bitboard_t xmark, omark; board_t demo = board; demo.clear(); demo.side_to_move = (nw > 0 || nb == 0) ? WHITE : BLACK; demo.put_piece(n, demo.side_to_move, centre_square); if (pt.piece_special_move_flags[n]) { int f = files/4; bitboard_t bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f = 1; while (bb.is_empty() && f < files) { bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f++; } if (!bb.is_empty()) demo.put_piece(n, demo.side_to_move, bb.bitscan()); } xmark = movegen.generate_moves_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); omark = movegen.generate_attack_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); demo.print(stdout, xmark, omark); if (is_aleaper(pt.piece_move_flags[n]) || is_stepper(pt.piece_move_flags[n])) { demo.clear(); demo.side_to_move = next_side[demo.side_to_move]; demo.put_piece(n, demo.side_to_move, centre_square); if (pt.piece_special_move_flags[n]) { int f = files/4; bitboard_t bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f = 1; while (bb.is_empty() && f < files) { bb = pt.special_zone[demo.side_to_move][n] & bitboard_t::board_file[f]; f++; } if (!bb.is_empty()) demo.put_piece(n, demo.side_to_move, bb.bitscan()); } xmark = movegen.generate_moves_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); omark = movegen.generate_attack_bitboard(&demo, bitboard_t::board_empty, demo.bbp[n], demo.side_to_move); demo.print(stdout, xmark, omark); } printf("\n"); } board = backup_board; memcpy(repetition_hash_table, backup_repetition_hash_table, sizeof repetition_hash_table); memcpy(board_repetition_hash_table, backup_board_repetition_hash_table, sizeof board_repetition_hash_table); } void write_piece_descriptions(bool xb = false) const { if (xb) { for (int n=0; n. */ #include #include "pieces.h" #ifdef __cplusplus extern "C" { #endif extern uint64_t piece_key[MAX_PIECE_TYPES][2][128]; extern uint64_t hold_key[MAX_PIECE_TYPES][2][128]; extern uint64_t side_to_move_key; extern uint64_t flag_key[2][8]; extern uint64_t en_passant_key[128]; void initialise_hash_keys(void); #ifdef __cplusplus } #endif SjaakII/include/hashpath.h000644 000765 000024 00000001156 12433153270 016466 0ustar00eglebbkstaff000000 000000 #ifndef HASHPATH_H #define HASHPATH_H #include #include "move.h" #define MAX_HASH_PATH_LENGTH 128 typedef struct { uint64_t lock; move_t moves[MAX_HASH_PATH_LENGTH]; int length; int generation; } hash_path_entry_t; typedef struct { /* We keep two parallel hash tables: a "depth priority" table and an * "always replace" table. */ hash_path_table_entry_t *data; size_t number_of_elements; size_t write_count; uint8_t generation; } hash_path_table_t; hash_path_table_t *create_hash_path_table(size_t nelem); void destroy_hash_path_table(hash_path_table_t *table); #endif SjaakII/include/hashtable.h000644 000765 000024 00000004443 12433153270 016623 0ustar00eglebbkstaff000000 000000 /* Jazz, a program for playing chess * Copyright (C) 2009, 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef HASHTABLE_H #define HASHTABLE_H #include #include "move.h" /* Types of entries that may occur in the table (flags) */ #define HASH_TYPE_EXACT 0x00000001 #define HASH_TYPE_LOWER 0x00000002 #define HASH_TYPE_UPPER 0x00000004 #ifdef __cplusplus extern "C" { #endif /* Entries in the hash table */ typedef struct { uint32_t lock; /* Store the full key (for position verification) */ move_t best_move; /* The best move returned from the previous search */ int16_t score; /* The value of this node */ int16_t depth; /* The depth to which this position was searched; distance from horizon */ uint8_t flags; /* Properties of this entry */ uint8_t generation; /* Generation of this entry */ } hash_table_entry_t; typedef struct { /* We keep two parallel hash tables: a "depth priority" table and an * "always replace" table. */ hash_table_entry_t *data; size_t number_of_elements; size_t write_count; uint8_t generation; } hash_table_t; hash_table_t *create_hash_table(size_t nelem); void destroy_hash_table(hash_table_t *table); void store_table_entry(hash_table_t *table, uint64_t key, int depth, int score, unsigned int flags, move_t best_move); bool retrieve_table(hash_table_t *table, uint64_t key, int *depth, int *score, unsigned int *flags, move_t *best_move); void prefetch_hashtable(hash_table_t *table, uint64_t key); void prepare_hashtable_search(hash_table_t *table); int count_unused_table_entries(hash_table_t *table); #ifdef __cplusplus } #endif #endif SjaakII/include/history.h000644 000765 000024 00000006024 13017310267 016366 0ustar00eglebbkstaff000000 000000 #define HISTORY_MAX 0x4000 #define USE_HISTORY_HEURISTIC void update_drop_history(move_t move, int score) { #ifdef USE_HISTORY_HEURISTIC int history_score; int piece = get_move_piece(move); int side = board.side_to_move; int to = get_move_to(move); drop_history[side][piece][to] += score; history_score = abs(drop_history[side][piece][to]); if (history_score > max_drop_history[side]) max_drop_history[side] = history_score; /* Rescale as needed */ if (max_drop_history[side] > HISTORY_MAX) { max_drop_history[side] /= 2; for (side = 0; side <= 1; side++) for (piece=0; piece::board_ranks*bitboard_t::board_files; to++) drop_history[side][piece][to] /= 2; } #endif } void update_history(move_t move, int score) { #ifdef USE_HISTORY_HEURISTIC int history_score; if (board.check() || is_promotion_move(move) || is_capture_move(move) || is_pickup_move(move) || is_castle_move(move)) return; if (is_drop_move(move)) { update_drop_history(move, score); return; } int piece = board.get_piece(get_move_from(move)); int side = board.side_to_move; int to = get_move_to(move); history[side][piece][to] += score; history_score = abs(history[side][piece][to]); if (history_score > max_history[side]) max_history[side] = history_score; /* Rescale as needed */ if (max_history[side] > HISTORY_MAX) { max_history[side] /= 2; for (side = 0; side <= 1; side++) for (piece=0; piece::board_ranks*bitboard_t::board_files; to++) history[side][piece][to] /= 2; } #endif } int get_move_history_score(const move_t move) const { #ifdef USE_HISTORY_HEURISTIC int piece = get_move_piece(move); int side = board.side_to_move; int to = get_move_to(move); if (is_drop_move(move)) return drop_history[side][piece][to]; else return history[side][piece][to]; #else return 0; #endif } int get_move_history_scale(move_t move) const { #ifdef USE_HISTORY_HEURISTIC int side = board.side_to_move; if (is_drop_move(move)) return max_drop_history[side]; else return max_history[side]; #else return 1; #endif } void clear_history() { #ifdef USE_HISTORY_HEURISTIC memset(history, 0, sizeof history); max_history[0] = max_history[1] = 0; memset(drop_history, 0, sizeof drop_history); max_drop_history[0] = max_drop_history[1] = 0; #endif } void scale_history() { #ifdef USE_HISTORY_HEURISTIC /* Scale down history scores */ for (int side = 0; side <= 1; side++) { max_history[side] /= 2; max_drop_history[side] /= 2; for (int piece=0; piece::board_ranks*bitboard_t::board_files; to++) { history[side][piece][to] /= 2; drop_history[side][piece][to] /= 2; } } #endif } SjaakII/include/keypressed.h000644 000765 000024 00000001700 12452217646 017050 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KEYPRESSED_H #define KEYPRESSED_H #ifdef __cplusplus extern "C" { #endif #include "bool.h" extern const bool ponder_ok; extern bool keyboard_input_waiting(void); #ifdef __cplusplus } #endif #endif SjaakII/include/killer.h000644 000765 000024 00000003775 13017310267 016161 0ustar00eglebbkstaff000000 000000 /* Store a "counter move"; this is a depth-independent extension of the * killer principle. */ void inline store_counter_move(int /* depth */, move_t prev_move, move_t move) { if (board.check() || is_promotion_move(move) || is_capture_move(move) || is_pickup_move(move) || !prev_move) return; int side = board.side_to_move; int to = get_move_to(prev_move); int from = is_drop_move(prev_move) ? to : get_move_from(prev_move); counter[from][to][side] = move; } bool inline is_counter(int /* depth */, move_t prev_move, move_t move) const { int side = board.side_to_move; int to = get_move_to(prev_move); int from = is_drop_move(prev_move) ? to : get_move_from(prev_move); return counter[from][to][side] == move; } bool is_killer(int depth, move_t move) const { if (killer[depth][0] == move || killer[depth][1] == move) return true; return false; } /* Store moves that kill a branch in the killer slots, but only if: * - we were not in check at the beginning of this move * - the move is not a promotion (already high in the tree) * - the move was not a capture (already high in the tree) */ inline void store_killer(move_t move, int depth) { if (board.check() || is_promotion_move(move) || is_capture_move(move)) return; if (move == killer[depth][0]) { /* The move was the first killer - do nothing */ } else { /* This was either the last killer (out of 2 or 3), or it's a new * move. Either way, Degrade first killer to second killer (etc) and * store the new first killer. */ killer[depth][1]=killer[depth][0]; killer[depth][0]=move; } } inline void store_mate_killer(move_t move, int depth, int best_score) { if (board.check() || is_promotion_move(move) || is_capture_move(move)) return; if (best_score >= (LEGALWIN-1000)) mate_killer[depth] = move; } void store_null_killer(int depth, move_t move) { if (!is_capture_move(move) && !is_promotion_move(move)) null_killer[depth] = move; } SjaakII/include/mate.h000644 000765 000024 00000016343 13014616475 015627 0ustar00eglebbkstaff000000 000000 //stage_t generate_staged_moves(stage_t stage, movelist_t *movelist, const board_t *board, side_t side_to_move) const // STAGE_DROP=STAGE_START, STAGE_NORMAL, /* Normal move generation */ // STAGE_CHECKING_DROP, STAGE_CHECKING_MOVE, /* Mate/Tsume search */ // STAGE_CHECK_EVADE, /* Check evasion */ // STAGE_DONE } stage_t; struct { uint32_t lock; int16_t score; int16_t ply; } mate_cache[0xFFFF + 1 + 8]; bool probe_mate_cache(int ply, int *score) { int index = board.hash & 0xFFFF; uint32_t key = board.hash >> 32; for (int n = 0; n<8; n++) { if (mate_cache[index + n].lock == key && mate_cache[index + n].ply <= ply) { *score = mate_cache[index + n].score; return true; } } return false; } void store_mate_cache(int ply, int score) { int index = board.hash & 0xFFFF; uint32_t key = board.hash >> 32; uint32_t okey = key; int oply = ply; int oscore = score; for (int n = 0; n<8; n++) { uint32_t new_key = mate_cache[index + n].lock; int new_ply = mate_cache[index + n].ply; int new_score = mate_cache[index + n].score; mate_cache[index+n].lock = okey; mate_cache[index+n].ply = oply; mate_cache[index+n].score = oscore; if (new_key == key) return; okey = new_key; oply = new_ply; oscore = new_score; } } /* Test if the current position is a +mate-in-mply-or-better, using * drop-checks. * Returns either a mate-score (if mate is found) or not. Matescores can be * trusted, non-mate scores indicate a call to reqular qsearch is in order. */ int msearch(int alpha, int beta, int mply, int depth, int ply = 0) { /* Check whether our search time for this move has expired */ check_clock(); if (abort_search) return 0; truncate_principle_variation(depth); move_t move = 0; int best_score = -ILLEGAL; if (ply == 0 && !(mply & 1)) mply--; if (mply < 0 || depth >= MAX_SEARCH_DEPTH) return 0; /* If the position is repeated, it cannot be part of a line of optimal * play. */ if (position_repeated()) return 0; /* Test for transposition table cut-offs */ int hash_depth, hash_score; unsigned int hash_flag; move_t hash_move = 0; bool have_hash = retrieve_table(transposition_table, board.hash, &hash_depth, &hash_score, &hash_flag, &hash_move); have_hash = false; if (have_hash) { hash_score = score_from_hashtable(hash_score, depth); bool exact_ok = (depth > 0); if (is_mate_score(hash_score)) { if (hash_score > alpha) backup_principle_variation(depth, hash_move); return hash_score; } if (hash_depth >= mply) return 0; } if (probe_mate_cache(ply, &best_score)) { if (!is_mate_score(best_score)) return best_score; /* Follow the mating path, to reconstruct the like */ } best_score = -ILLEGAL; stage_t stage = STAGE_START; /* Generate moves */ if (ply & 1) { /* Evade */ assert(board.check()); stage = STAGE_CHECK_EVADE; } else { /* Begin with a drop */ if (board.check()) return 0; stage = STAGE_CHECKING_DROP; } move_t best = 0; side_t me = board.side_to_move; int legal_moves = 0; while (stage != STAGE_DONE && (alpha < beta)) { stage = movegen.generate_staged_moves(stage, movelist+depth, &board, me); legal_moves += movelist[depth].num_moves; for (int n = 0; n best_score) { best_score = score; hash_move = move; if (score > alpha && score) { hash_flag = HASH_TYPE_EXACT; best_move[depth] = move; backup_principle_variation(depth, move); alpha = score; if (ply == 0 && alpha >= LEGALWIN - 1000) alpha = beta; /* Cut-off if we found a mate - not necessarily the fastest one*/ if (alpha >= beta) { store_killer(move, depth); store_mate_killer(move, depth, score); hash_flag = HASH_TYPE_LOWER; break; } } } /* Take a cut-off: if we've reached the limit and found at least * one evasion, it's not mate. */ if (mply == 0) return 0; } /* Only consider check-drops in the first leg */ //if (ply == 0) break; } /* We only look for mates on odd plies */ if (legal_moves == 0 && (ply & 1)) { best_score = board.check() ? (mate_score + depth) : (stale_score + depth); move_t prev_move = move_list[moves_played-1]; /* Make sure mate wasn't caused by an illegal drop */ if (board.check() && is_drop_move(prev_move)) { int p = board.get_piece(get_move_to(prev_move)); if (pt.piece_flags[p] & PF_DROPNOMATE) return ILLEGAL; } /* Test if the mate is legal, that is to say: * - The checking piece is allowed to deliver mate * - The mate follows a sequence of checks at least one of which * makes the mate OK (is classified as "shak"). * The first is illegal, the second is only a draw. */ if (board.check()) { if (!is_valid_mate()) return ILLEGAL; if (board.rule_flags & RF_USE_SHAKMATE) { if (!board.have_shak()) best_score = 0; } } } if (best_score == -ILLEGAL) best_score = 0; store_mate_cache(ply, best_score); if (is_mate_score(best_score)) /* Only mate scores are reliable */ store_table_entry(transposition_table, board.hash, mply, score_to_hashtable(best_score, depth), hash_flag, hash_move); return best_score; } SjaakII/include/move.h000644 000765 000024 00000036541 13017310267 015642 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MOVE_H #define MOVE_H #include #include "assert.h" #include "pieces.h" #include "bool.h" /* Define structure to hold a move. * Because of the specific requirements of variants or other games, this has to be a fairly flexible data * structure. Each move can consist of (in that order): * N pickups (N = 0, 1, 2, 3) * N swaps (N = 0, 1, 2, 3) * N drops (N = 0, 1, 2, 3) * N store/retrieve (N = 0, 1) * A "pickup" stores a square (7 bits). * A "swap" stores the from - and to-square (7+7 = 14 bits). * A "drop" stores a square and a piece to be dropped there (7+5+1 = 13 bits). * A "store" stores a piece in the holdings (1+5+1 = 7 bits), a "retrieve" does the opposite * The extra bits ("4+1") for each piece are used to encode the colour of the piece. * * The order in which the components should be resolved is: * 1. pickups (so all destination squares are empty) * 2. swaps (which move pieces on the board) * 3. drops (which may happen on the square that was just cleared by the move) * 4. store/retrieve (could be any order really) * * Normal moves then consist of one swap (14 bits) * Captures consist of a pickup and a swap (7+14 = 21 bits) * Promotions consist of one (or two, for captures) pickup(s) and a drop (7(+7)+13 = 20(27) bits) * Castling consists of two swaps (14+14=28 bits) * Gating moves add an extra drop: * A castle+gate in this case takes up 28+13 = 41 bits (48 including retrieve) * A capture+gate takes 21+13 = 34 bits (41 including retrieve) * * For printing of SAN moves alone it is convenient to have the "primary" piece * type as well (4+1 bits). * bits * 0-1 Number of pickups (2 bits) * 2-3 Number of "swaps" (2 bits) * 4 Number of pieces taken/from holdings (0 or 1, 1 bit) * 5-6 Number of drops (2 bits) * 7-11 Primary piece (5 bits) * 12-59 Move actions (48 bits) * 60-63 Move flags (4 bits) * We even have room to expand the number of piece types to 32, which makes * implementing variants with many pieces easier (or possible at all). */ typedef uint64_t move_t; /* Offsets and test masks */ #define MOVE_SIDE_BITS 1 #define MOVE_PIECE_BITS (PIECE_BITS) #define MOVE_SQUARE_BITS 7 #define MOVE_PICKUP_SIZE (MOVE_SQUARE_BITS) #define MOVE_DROP_SIZE (MOVE_SQUARE_BITS + MOVE_PIECE_BITS + MOVE_SIDE_BITS) #define MOVE_SWAP_SIZE (MOVE_SQUARE_BITS + MOVE_SQUARE_BITS) #define MOVE_SQUARE_MASK ((1<> MOVE_PICK_SHIFT) & 0x03; } static inline int get_move_drops(move_t move) { return (move >> MOVE_DROP_SHIFT) & 0x03; } static inline int get_move_swaps(move_t move) { return (move >> MOVE_SWAP_SHIFT) & 0x03; } static inline int get_move_holdings(move_t move) { return (move >> MOVE_HOLD_SHIFT) & 0x01; } static inline move_t encode_number_pickups(int count) { assert(count <= 3); return count << MOVE_PICK_SHIFT; } static inline move_t encode_number_drops(int count) { assert(count <= 3); return count << MOVE_DROP_SHIFT; } static inline move_t encode_number_swaps(int count) { assert(count <= 3); return count << MOVE_SWAP_SHIFT; } static inline move_t encode_number_holdings(int count) { assert(count <= 1); return count << MOVE_HOLD_SHIFT; } static inline move_t encode_move_piece(int piece) { return (piece << MOVE_PIECE_SHIFT); } /* Get the first, second or third pickup */ static inline int get_move_pickup(move_t move, int n) { int shift; assert (n<=get_move_pickups(move)); shift = n * MOVE_PICKUP_SIZE; return (move >> (MOVE_SLOT1 + shift)) & MOVE_PICKUP_MASK; } /* Get the first, second or third drop */ static inline int get_move_drop(move_t move, int n) { int shift; assert (n<=get_move_drops(move)); shift = MOVE_DROP_SIZE*n + MOVE_SWAP_SIZE*get_move_swaps(move) + MOVE_PICKUP_SIZE*get_move_pickups(move); return (move >> (MOVE_SLOT1 + shift)) & MOVE_DROP_MASK; } static inline int get_move_swap(move_t move, int n) { int shift; assert (n<=get_move_swaps(move)); shift = MOVE_SWAP_SIZE*n + MOVE_PICKUP_SIZE*get_move_pickups(move); return (move >> (MOVE_SLOT1 + shift)) & MOVE_SWAP_MASK; } static inline int get_move_holding(move_t move) { int shift = MOVE_SWAP_SIZE*get_move_swaps(move) + MOVE_DROP_SIZE*get_move_drops(move) + MOVE_PICKUP_SIZE*get_move_pickups(move); return (move >> (MOVE_SLOT1 + shift)) & MOVE_HOLDING_MASK; } static inline int decode_swap_from(int swap) { return swap & MOVE_SQUARE_MASK; } static inline int decode_swap_to(int swap) { return (swap >> MOVE_SQUARE_BITS) & MOVE_SQUARE_MASK; } /* Decode drop and pickup piece/squares */ static inline int decode_drop_square(int drop) { return (drop >> DROP_SQUARE_SHIFT) & MOVE_SQUARE_MASK; } static inline int decode_drop_piece(int drop) { return drop & MOVE_PIECE_MASK; } static inline side_t decode_drop_side(int drop) { return side_for_piece(drop); } static inline int decode_pickup_square(int pickup) { return pickup; } /* The code for holdings is almost the same as for drops */ static inline int decode_holding_count(int hold) { return 2*decode_drop_square(hold) - 1; } #define decode_holding_piece decode_drop_piece #define decode_holding_side decode_drop_side static inline uint64_t encode_swap(int from, int to) { return from | to << MOVE_SQUARE_BITS; } static inline uint64_t encode_pickup(int from) { return from & MOVE_SQUARE_MASK; } static inline uint64_t encode_drop(int piece, int to) { return piece | to< -2); assert((get_move_pickups(move) + get_move_drops(move)) < 5); count = (count + 1) / 2; shift = MOVE_SLOT1 + MOVE_DROP_SIZE * get_move_drops(move) + MOVE_SWAP_SIZE * get_move_swaps(move) + MOVE_PICKUP_SIZE * get_move_pickups(move); return move | encode_number_holdings(1) | encode_holding(piece, count) << shift; } #define add_move_retrieve(move, piece, count) add_move_store((move), (piece), -(count)) /* Add gate information to a move: one extra drop, plus removing from * holdings. */ static inline move_t add_move_gate(move_t move, uint8_t piece, uint8_t to) { int drops = get_move_drops(move); int swaps = get_move_swaps(move); int pickups = get_move_pickups(move); int shift = MOVE_DROP_SIZE*drops + MOVE_SWAP_SIZE*get_move_swaps(move) + MOVE_PICKUP_SIZE*get_move_pickups(move); move &= ~encode_number_drops(3); move |= encode_number_drops(drops + 1) | encode_drop(piece, to) << (MOVE_SLOT1 + shift); move = add_move_retrieve(move, piece, 1); return move; } /* Query move types: * A normal move is one pickup, one drop (by the same piece) * A capture is two pickups (from two different sides), one drop * A promotion is a drop by a different piece type than a capture * A drop has no pickups * A "shot" is a pickup without a drop * Castling is two pickups and two drops */ static inline bool is_capture_move(move_t move) { return get_move_pickups(move) > get_move_drops(move); } static inline bool is_double_capture_move(move_t move) { return get_move_pickups(move) > get_move_drops(move)+1; } static inline bool is_promotion_move(move_t move) { unsigned int drop_piece; if (get_move_drops(move) != 1 || get_move_pickups(move) < 1) return false; drop_piece = decode_drop_piece(get_move_drop(move, 0)); return drop_piece != ((move >> MOVE_PIECE_SHIFT) & MOVE_PIECE_MASK); } static inline bool is_irreversible_move(move_t move) { return (move & MOVE_RESET50) != 0; } static inline bool is_drop_move(move_t move) { return (get_move_pickups(move) == 0) && (get_move_swaps(move) == 0) && (get_move_drops(move) == 1); } static inline bool is_pickup_move(move_t move) { return (get_move_pickups(move) == 1) && (get_move_swaps(move) == 0) && (get_move_drops(move) == 0); } static inline bool is_castle_move(move_t move) { return (get_move_swaps(move) == 2) || ((get_move_pickups(move) == 2) && (get_move_drops(move) == 2)); } static inline bool is_gate_move(move_t move) { return !is_drop_move(move) && (get_move_drops(move) > get_move_pickups(move)); } /* Convenience macros: the piece type of the first pickup and the first pickup and drop squares * By lucky coincidence, this works correctly for pure drop-moves too. */ static inline int get_move_piece(move_t move) { return (move >> MOVE_PIECE_SHIFT) & MOVE_PIECE_MASK; } static inline int get_move_promotion_piece(move_t move) { uint16_t p = get_move_drop(move, 0); return decode_drop_piece(p); } static inline int get_move_drop_square(move_t move) { uint16_t p; assert(is_drop_move(move) || is_gate_move(move)); p = get_move_drop(move, 0); return decode_drop_square(p); } static inline int get_move_from(move_t move) { assert(!is_drop_move(move)); uint16_t p; if (get_move_swaps(move)) { int swap = get_move_swap(move, 0); return decode_swap_from(swap); } p = get_move_pickup(move, 0); return decode_pickup_square(p); } static inline int get_move_to(move_t move) { uint16_t p; if (get_move_swaps(move)) { int swap = get_move_swap(move, 0); return decode_swap_to(swap); } p = get_move_drop(move, 0); return decode_drop_square(p); } static inline int get_castle_move_from2(move_t move) { uint16_t p; if (get_move_swaps(move) == 2) { int swap = get_move_swap(move, 1); return decode_swap_from(swap); } p = get_move_pickup(move, 1); return decode_pickup_square(p); } static inline int get_castle_move_to2(move_t move) { uint16_t p; if (get_move_swaps(move) == 2) { int swap = get_move_swap(move, 1); return decode_swap_to(swap); } p = get_move_drop(move, 1); return decode_drop_square(p); } static inline int get_move_capture_square(move_t move) { uint16_t p = 0; if (get_move_swaps(move)) p = get_move_pickup(move, 0); else p = get_move_pickup(move, 1); return decode_pickup_square(p); } #endif SjaakII/include/moveflag.h000644 000765 000024 00000006747 13014616475 016510 0ustar00eglebbkstaff000000 000000 #ifndef MOVEFLAG_H #define MOVEFLAG_H #include #include "assert.h" #include "bool.h" typedef uint32_t move_flag_t; #define MF_SLIDER_H 0x00000001 #define MF_SLIDER_V 0x00000002 #define MF_SLIDER_D 0x00000004 #define MF_SLIDER_A 0x00000008 #define MF_SLIDER 0x0000000F #define MF_HOPPER_H 0x00000010 #define MF_HOPPER_V 0x00000020 #define MF_HOPPER_D 0x00000040 #define MF_HOPPER_A 0x00000080 #define MF_HOPPER 0x000000F0 #define MF_HOPSLIDE (MF_HOPPER | MF_SLIDER) #define MF_STEPPER 0x00000F00 /* This is an index in an array with repeat counts */ #define MF_RIDER 0x0000F000 /* This is an index in an array with repeat counts */ #define MF_LEAPER 0x000F0000 /* This is an index in an array */ #define MF_LEAPER2 0x00F00000 /* Second index, for compound leapers */ #define MF_LEAPER_MASK 0x0F000000 /* Mask index, for compound leapers */ #define MF_IS_LEAPER 0x10000000 /* Whether this is actually a leaper */ #define MF_LEAPER_HAVE2 0x20000000 /* Leaper is a compound leaper */ #define MF_LEAPER_HAVEM 0x40000000 /* Leaper has a mask (only makes sense for compound leapers) */ #define MF_LEAPER_ASYMM 0x80000000 /* Leaper is asymmetric */ #define MF_LEAPER_FLAGS 0xFFFF0000 /* All leaper flags */ #define MF_HOPSLIDELEAP (MF_HOPSLIDE | MF_IS_LEAPER | MF_RIDER) /* Stepper repeat count masks (3 bits per direction) */ #define MF_STEPPER_N (0xf << 0) #define MF_STEPPER_NE (0xf << 4) #define MF_STEPPER_E (0xf << 8) #define MF_STEPPER_SE (0xf << 12) #define MF_STEPPER_S (0xf << 16) #define MF_STEPPER_SW (0xf << 20) #define MF_STEPPER_W (0xf << 24) #define MF_STEPPER_NW (0xf << 28) /* TODO: repeated leapers = riders, needs extension of the move_flag type. * Intended input file syntax: * Ride: (x,y) * (similar to asymmetric leaper) */ static inline bool is_slider(move_flag_t mf) { return (mf & MF_SLIDER) != 0; } static inline bool is_hopper(move_flag_t mf) { return (mf & MF_HOPPER) != 0; } static inline bool is_rider(move_flag_t mf) { return (mf & MF_RIDER) != 0; } static inline bool is_leaper(move_flag_t mf) { return (mf & MF_IS_LEAPER) != 0; } static inline bool is_aleaper(move_flag_t mf) { const move_flag_t flags = MF_LEAPER_ASYMM;// | MF_IS_LEAPER; return (mf & flags) == flags; } static inline bool is_simple_leaper(move_flag_t mf) { const move_flag_t flags = MF_LEAPER_HAVE2 | MF_LEAPER_HAVEM; return (mf & MF_IS_LEAPER) && !(mf & flags); } static inline bool is_double_leaper(move_flag_t mf) { const move_flag_t flags = MF_LEAPER_HAVE2;// | MF_IS_LEAPER; return (mf & flags) == flags; } static inline bool is_masked_leaper(move_flag_t mf) { const move_flag_t flags = MF_LEAPER_HAVEM;// | MF_IS_LEAPER; return (mf & flags) == flags; } static inline int get_leaper_index(move_flag_t mf) { return (mf & MF_LEAPER) >> 16; } static inline int get_leaper_index2(move_flag_t mf) { return (mf & MF_LEAPER2) >> 20; } static inline int get_leaper_indexm(move_flag_t mf) { return (mf & MF_LEAPER_MASK) >> 24; } static inline bool is_stepper(move_flag_t mf) { return (mf & MF_STEPPER) != 0; } static inline int get_stepper_index(move_flag_t mf) { return (mf & MF_STEPPER) >> 8; } static inline int make_stepper_index(int id) { assert(id < 16); return (id << 8); } static inline int get_rider_index(move_flag_t mf) { return (mf & MF_RIDER) >> 12; } static inline int make_rider_index(int id) { assert(id < 16); return (id << 12); } #endif SjaakII/include/movegen.h000644 000765 000024 00000366540 13017310267 016341 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MOVEGEN_H #define MOVEGEN_H #include #include #include "assert.h" #include "bitboard.h" #include "pieces.h" #include "board.h" #include "move.h" #include "movelist.h" #include "aligned_malloc.h" /* Stages for staged move generation */ typedef enum stage_t { STAGE_START=0, STAGE_DROP=STAGE_START, STAGE_NORMAL, /* Normal move generation */ STAGE_CHECKING_DROP, STAGE_CHECKING_MOVE, /* Mate/Tsume search */ STAGE_CHECK_EVADE, /* Check evasion */ STAGE_DONE } stage_t; static const stage_t next_stage[STAGE_DONE+1] = { // STAGE_DROP, STAGE_NORMAL, /* Normal move generation */ STAGE_NORMAL, STAGE_DONE, // STAGE_CHECKING_DROP, STAGE_CHECKING_MOVE, /* Mate/Tsume search */ STAGE_CHECKING_MOVE, STAGE_DONE, // STAGE_CHECK_EVADE, /* Check evasion */ STAGE_DONE, // STAGE_DONE STAGE_DONE }; inline stage_t& operator++(stage_t& stage, int) { assert(stage >= STAGE_START); assert(stage < STAGE_DONE); const int i = static_cast(stage)+1; stage = static_cast(i); return stage; } template struct movegen_t { /* Leapers and asymmetric leapers. */ bitboard_t leaper[MAX_LEAPER_TYPES][sizeof(kind)*8]; bitboard_t aleaper[NUM_SIDES][MAX_LEAPER_TYPES][sizeof(kind)*8]; int number_of_leapers; int number_of_aleapers; /* Rider descriptions */ int number_of_riders; struct { int dx, dy; } rider_step[MAX_RIDER_TYPES][4]; bitboard_t rider_ray[MAX_RIDER_TYPES][sizeof(kind)*8][sizeof(kind)*8]; /* Stepper descriptions */ uint32_t stepper_description[MAX_STEPPER_TYPES][NUM_SIDES]; // 8 directions, with repeat counts (0-15) for each->32 bits bitboard_t stepper_step[MAX_STEPPER_TYPES][NUM_SIDES][sizeof(kind)*8]; bitboard_t step_mask[8]; int inverse_step[8]; int step_shift[8]; int number_of_steppers; /* Castling move bitboards */ bitboard_t castle_mask[NUM_CASTLE_MOVES][NUM_SIDES]; bitboard_t castle_free[NUM_CASTLE_MOVES][NUM_SIDES]; bitboard_t castle_safe[NUM_CASTLE_MOVES][NUM_SIDES]; bitboard_t castle_king_dest[NUM_CASTLE_MOVES][NUM_SIDES]; bitboard_t castle_rook_dest[NUM_CASTLE_MOVES][NUM_SIDES]; int castle_king_from[NUM_CASTLE_MOVES][NUM_SIDES]; /* Normal slider tables */ bitboard_t **horizontal_slider_move; bitboard_t **vertical_slider_move; bitboard_t **horizontal_hopper_move; bitboard_t **vertical_hopper_move; move_flag_t super_slider_flags; move_flag_t super_hopper_flags; /* Super piece */ bitboard_t super[sizeof(kind)*8]; bitboard_t super_slider[sizeof(kind)*8]; bitboard_t super_hopper[sizeof(kind)*8]; bitboard_t super_leaper[sizeof(kind)*8]; bitboard_t super_stepper[sizeof(kind)*8]; bitboard_t super_rider[sizeof(kind)*8]; void initialise() { /* Clear leaper/stepper descriptions */ number_of_leapers = 0; number_of_aleapers = 0; number_of_steppers = 1; number_of_riders = 1; super_slider_flags = 0; super_hopper_flags = 0; memset(leaper, 0, sizeof leaper); memset(aleaper, 0, sizeof aleaper); memset(step_mask, 0, sizeof step_mask); memset(stepper_step, 0, sizeof stepper_step); for (int n = 0; n::board_files; // N step_shift[1] = bitboard_t::board_files+1; // NE step_shift[2] = 1; // E step_shift[3] =-bitboard_t::board_files+1; // SE step_shift[4] =-bitboard_t::board_files; // S step_shift[5] =-bitboard_t::board_files-1; // SW step_shift[6] =-1; // W step_shift[7] = bitboard_t::board_files-1; // NW /* N NE E SE S SW W NW * 0 1 2 3 4 5 6 7 * S SW W NW N NE E SE * 4 5 6 7 0 1 2 3 */ for (int n = 0; n<8; n++) inverse_step[n] = (n+4)&7; step_mask[0] = ~ bitboard_t::board_north_edge; step_mask[1] = ~(bitboard_t::board_east_edge | bitboard_t::board_north_edge); step_mask[2] = ~ bitboard_t::board_east_edge; step_mask[3] = ~(bitboard_t::board_east_edge | bitboard_t::board_south_edge); step_mask[4] = ~ bitboard_t::board_south_edge; step_mask[5] = ~(bitboard_t::board_west_edge | bitboard_t::board_south_edge);; step_mask[6] = ~ bitboard_t::board_west_edge; step_mask[7] = ~(bitboard_t::board_west_edge | bitboard_t::board_north_edge); for (int n = 0; n::board_files; int board_ranks = bitboard_t::board_ranks; int board_size = board_files * board_ranks; int file, rank; int occ; int n; /* Allocate memory for tables */ size_t file_table_size; size_t rank_table_size; size_t file_table_start; size_t rank_table_start; size_t file_table_offset; size_t rank_table_offset; uint8_t *memory; /* Determine size of tables: rank attacks, so index by file */ rank_table_size = board_files*sizeof(bitboard_t *); if (rank_table_size & 15) rank_table_size += 16 - (rank_table_size & 15); rank_table_start = rank_table_size; rank_table_size += board_files*(1<); rank_table_offset = ((size_t)1<); /* Determine size of tables: file attacks, so index by rank */ file_table_size = board_ranks*sizeof(bitboard_t *); if (file_table_size & 15) file_table_size += 16 - (file_table_size & 15); file_table_start = file_table_size; file_table_size += board_ranks*(1<); file_table_offset = ((size_t)1<); /* Free tables if previously allocated */ if (horizontal_slider_move) aligned_free(horizontal_slider_move); if (vertical_slider_move) aligned_free(vertical_slider_move ); if (horizontal_hopper_move) aligned_free(horizontal_hopper_move); if (vertical_hopper_move) aligned_free(vertical_hopper_move ); /* Allocate tables for horizontal (rank) attacks */ memory = (uint8_t *)aligned_malloc(rank_table_size, 16); assert(memory); memset(memory, 0, rank_table_size); horizontal_slider_move = (bitboard_t **)memory; for(n = 0; n *)(memory + rank_table_start + n*rank_table_offset); } memory = (uint8_t *)aligned_malloc(rank_table_size, 16); assert(memory); memset(memory, 0, rank_table_size); horizontal_hopper_move = (bitboard_t **)memory; for(n = 0; n *)(memory + rank_table_start + n*rank_table_offset); } /* Allocate tables for vertical (file) attacks */ memory = (uint8_t *)aligned_malloc(file_table_size, 16); assert(memory); memset(memory, 0, file_table_size); vertical_slider_move = (bitboard_t **)memory; for(n = 0; n *)(memory + file_table_start + n*file_table_offset); } memory = (uint8_t *)aligned_malloc(file_table_size, 16); assert(memory); memset(memory, 0, file_table_size); vertical_hopper_move = (bitboard_t **)memory; for(n = 0; n *)(memory + file_table_start + n*file_table_offset); } /* Rank attacks */ for (file = 0; file=0; n--) { horizontal_slider_move[file][occ] |= bitboard_t::board_file[n];//bitboard_t::square_bitboards[n]; if ( occ & (1 << n) ) break; } n--; /* Cannon attacks */ for (; n>=0; n--) { horizontal_hopper_move[file][occ] |= bitboard_t::board_file[n];//bitboard_t::square_bitboards[n]; if ( occ & (1 << n) ) break; } /* Right of slider position */ for (n=file+1; n::board_file[n];//bitboard_t::square_bitboards[n]; if ( occ & (1 << n) ) break; } n++; /* Cannon attacks */ for (; n::board_file[n];//bitboard_t::square_bitboards[n]; if ( occ & (1 << n) ) break; } } } /* File attacks */ for (rank = 0; rank < board_ranks; rank++) { for (occ = 0; occ < 1<=0; n--) { vertical_slider_move[rank][occ] |= bitboard_t::board_rank[n];//bitboard_t::square_bitboards[board_files*n]; if ( occ & (1 << n) ) break; } n--; /* Cannon attacks */ for (; n>=0; n--) { vertical_hopper_move[rank][occ] |= bitboard_t::board_rank[n];//bitboard_t::square_bitboards[board_files*n]; if ( occ & (1 << n) ) break; } /* North of slider position */ for (n=rank+1; n::board_rank[n];//bitboard_t::square_bitboards[board_files*n]; if ( occ & (1 << n) ) break; } n++; /* Cannon attacks */ for (; n::board_rank[n];//bitboard_t::square_bitboards[board_files*n]; if ( occ & (1 << n) ) break; } } } /* Initialise superpiece attacks to a full board */ for (n=0; n::board_all; } } void apply_board_masks(void) { int size = bitboard_t::board_files * bitboard_t::board_ranks; for (int n=0; n<8; n++) step_mask[n] &= bitboard_t::board_all; for (int n=0; n::board_all; } for (int n=0; n::board_all; aleaper[BLACK][n][s] &= bitboard_t::board_all; } } for (int n=0; n::board_all; stepper_step[n][BLACK][s] &= bitboard_t::board_all; } } } void initialise_super_tables(void) { int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int board_size = board_files * board_ranks; int n, c; /* Initialise stepper masks */ for (int c = 1; c stepper; stepper.set(n); stepper_step[c][WHITE][n] = generate_stepper_move_bitboard(make_stepper_index(c), WHITE, bitboard_t::board_empty, stepper); stepper_step[c][BLACK][n] = generate_stepper_move_bitboard(make_stepper_index(c), BLACK, bitboard_t::board_empty, stepper); } } for(n=0; n bb = stepper_step[c][side][n]; super_stepper[n] |= bb; while(!bb.is_empty()) { int s = bb.bitscan(); bb.reset(s); super_stepper[s].set(n); } } } } for(n=0; n::board_all; super_leaper[n].clear(); for (c=0; c::board_all; super_rider[n].clear(); for (c=1; c::board_empty); super_rider[n] |= generate_rider_move_bitboard(make_rider_index(c), BLACK, n, bitboard_t::board_empty); } super_rider[n] &= bitboard_t::board_all; super_slider[n].clear(); super_slider[n] |= generate_slider_move_bitboard(super_slider_flags, WHITE, n, bitboard_t::board_empty); super_slider[n] &= bitboard_t::board_all; super_hopper[n].clear(); if (super_hopper_flags) super_hopper[n] |= generate_slider_move_bitboard(super_hopper_flags>>4, WHITE, n, bitboard_t::board_empty); super_hopper[n] &= bitboard_t::board_all; super[n] = super_hopper[n] | super_leaper[n] | super_slider[n] | super_stepper[n] | super_rider[n]; } } /* Move generator, per piece type */ bitboard_t generate_leaper_move_bitboard(move_flag_t flags, side_t side, int square, bitboard_t occ) const { assert(is_leaper(flags)); bitboard_t moves; int index = get_leaper_index(flags); moves = is_aleaper(flags) ? aleaper[side][index][square] : leaper [index][square]; /* Simple leaper? */ if (is_simple_leaper(flags)) return moves; /* Double-step leaper */ if (is_double_leaper(flags)) { bitboard_t bb = moves; if (is_masked_leaper(flags)) bb &= ~occ; index = get_leaper_index2(flags); while (!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); moves |= leaper[index][square]; } } /* Masked leaper */ if (is_masked_leaper(flags)) { index = get_leaper_indexm(flags); moves &= leaper[index][square]; } return moves; } bitboard_t generate_rider_move_bitboard(move_flag_t flags, side_t /* side */, int from, bitboard_t occ) const { assert(is_rider(flags)); bitboard_t moves; int index = get_rider_index(flags); int file = unpack_file(from); int rank = unpack_rank(from); for (int k=0; k<4; k++) { int sx = rider_step[index][k].dx; int sy = rider_step[index][k].dy; if (sx == 0 && sy == 0) break; int dx[8] = { sx, sx, -sx, -sx, sy, sy, -sy, -sy }; int dy[8] = { sy, -sy, sy, -sy, sx, -sx, sx, -sx }; for (int n = 0; n<8; n++) { int f = file + dx[n]; int r = rank + dy[n]; while (f>=0 && r>=0 && f::board_files && r < bitboard_t::board_ranks) { int to = bitboard_t::pack_rank_file(r, f); moves.set(to); f += dx[n]; r += dy[n]; if (occ.test(to)) break; } } } return moves; } bitboard_t generate_slider_move_bitboard(move_flag_t flags, side_t /* side */, int square, bitboard_t occ) const { assert(is_slider(flags)); bitboard_t moves; int file = unpack_file(square); int rank = unpack_rank(square); int diag = occ.diagonal_nr[square]; int anti = occ.anti_diagonal_nr[square]; int index; if (flags & MF_SLIDER_H) { index = occ.get_rank(rank); moves |= horizontal_slider_move[file][index] & bitboard_t::board_rank[rank]; } if (flags & MF_SLIDER_V) { index = occ.get_file(file); moves |= vertical_slider_move[rank][index] & bitboard_t::board_file[file]; } if (flags & MF_SLIDER_D) { bitboard_t mask = bitboard_t::board_diagonal[diag]; index = (occ & mask).fill_south().get_rank(0); moves |= horizontal_slider_move[file][index] & mask; } if (flags & MF_SLIDER_A) { bitboard_t mask = bitboard_t::board_antidiagonal[anti]; index = (occ & mask).fill_south().get_rank(0); moves |= horizontal_slider_move[file][index] & mask; } return moves; } bitboard_t generate_hopper_move_bitboard(move_flag_t flags, side_t /* side */, int square, bitboard_t occ) const { assert(is_hopper(flags)); bitboard_t moves; int file = unpack_file(square); int rank = unpack_rank(square); int diag = occ.diagonal_nr[square]; int anti = occ.anti_diagonal_nr[square]; int index; //moves = generate_slider_move_bitboard(flags>>4, side, square, occ); //return generate_slider_move_bitboard(flags>>4, side, square, occ&~moves) ^ moves; if (flags & MF_HOPPER_H) { index = occ.get_rank(rank); moves |= horizontal_hopper_move[file][index] & bitboard_t::board_rank[rank]; } if (flags & MF_HOPPER_V) { index = occ.get_file(file); moves |= vertical_hopper_move[rank][index] & bitboard_t::board_file[file]; } if (flags & MF_HOPPER_D) { bitboard_t mask = bitboard_t::board_diagonal[diag]; index = (occ & mask).fill_south().get_rank(0); moves |= horizontal_hopper_move[file][index] & mask; } if (flags & MF_HOPPER_A) { bitboard_t mask = bitboard_t::board_antidiagonal[anti]; index = (occ & mask).fill_south().get_rank(0); moves |= horizontal_hopper_move[file][index] & mask; } return moves; } bitboard_t generate_stepper_move_bitboard(move_flag_t flags, side_t side, bitboard_t occ, bitboard_t steppers) const { bitboard_t moves; /* Check for single stepper moves, which are generated in parallel */ int si = get_stepper_index(flags); int d; for (d=0; d<8; d++) { int c = (stepper_description[si][side] >> (d*4)) & 15; bitboard_t dmoves = steppers; if (c == 0) continue; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for ( ; c>0; c--) { dmoves &= step_mask[d]; dmoves = dmoves.sshift(step_shift[d]); moves |= dmoves; dmoves &= ~occ; } } return moves; } bitboard_t generate_super_attacks_for_squares(bitboard_t squares, const bitboard_t super[sizeof(kind)*8]) const { bitboard_t attacks; while (!squares.is_empty()) { int square = squares.bitscan(); squares.reset(square); attacks |= super[square]; } return attacks; } /* Generate an attack bitboard for all attackers within a specified mask */ inline bitboard_t generate_attack_bitboard_mask(const board_t *board, const bitboard_t test_squares, const bitboard_t source_mask, const bitboard_t occ_mask, side_t side_to_move) const { piece_description_t *piece_types; move_flag_t *piece_capture_flags; bitboard_t own, enemy, own_movers; bitboard_t occupied; bitboard_t attacked; int n; piece_types = board->piece_types; piece_capture_flags = piece_types->piece_capture_flags; /* Bookkeeping: we keep a pointer to the next move in the move list, and * update the number of moves in the list at the end of this function */ own = board->bbc[side_to_move] & occ_mask; enemy = board->bbc[next_side[side_to_move]] & occ_mask; occupied = own | enemy | test_squares; own_movers = own & source_mask; bitboard_t possible_attackers = own_movers; for (n=0; nnum_piece_types && !possible_attackers.is_empty(); n++) { if ((possible_attackers & board->bbp[n]).is_empty()) continue; possible_attackers &= ~board->bbp[n]; bitboard_t bb = own_movers & board->bbp[n]; bitboard_t occ = occupied | piece_types->block[side_to_move][n]; /* Steppers */ if (is_stepper(piece_capture_flags[n])) { int si = get_stepper_index(piece_capture_flags[n]); int d; for (d=0; d<8; d++) { int c = (stepper_description[si][side_to_move] >> (d*4)) & 15; bitboard_t captures = bb; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for ( ; c>0; c--) { captures &= step_mask[d]; captures = captures.sshift(step_shift[d]); captures &= piece_types->prison[side_to_move][n]; attacked |= captures; captures &= ~occ; } } } /* Sliders and leapers */ if (piece_capture_flags[n] & MF_HOPSLIDELEAP) while (!bb.is_empty()) { move_flag_t capture_flags = piece_capture_flags[n]; int from = bb.bitscan(); bb.reset(from); bitboard_t from_bb = bitboard_t::square_bitboards[from]; bitboard_t attack; if (is_leaper(capture_flags)) attack |= generate_leaper_move_bitboard(capture_flags, side_to_move, from, occ) &~ from_bb; if (is_slider(capture_flags)) attack |= generate_slider_move_bitboard(capture_flags, side_to_move, from, occ); if (is_hopper(capture_flags)) attack |= generate_hopper_move_bitboard(capture_flags, side_to_move, from, occ); if (is_rider (capture_flags)) attack |= generate_rider_move_bitboard (capture_flags, side_to_move, from, occ); attacked |= attack & piece_types->prison[side_to_move][n]; } } return attacked; } inline bitboard_t generate_attack_bitboard(const board_t *board, const bitboard_t test_squares, const bitboard_t source_mask, side_t side_to_move) const { return generate_attack_bitboard_mask(board, test_squares, source_mask, bitboard_t::board_all, side_to_move); } inline bitboard_t generate_move_bitboard_for_flags(move_flag_t flags, int square, const bitboard_t occupied, side_t side_to_move) const { bitboard_t attacked; /* Steppers */ if (is_stepper(flags)) { bitboard_t bb = bitboard_t::square_bitboards[square]; int si = get_stepper_index(flags); for (int d=0; d<8; d++) { int c = (stepper_description[si][side_to_move] >> (d*4)) & 15; bitboard_t captures = bb; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for ( ; c>0; c--) { captures &= step_mask[d]; captures = captures.sshift(step_shift[d]); attacked |= captures; captures &= ~occupied; } } } /* Sliders and leapers */ if (flags & MF_HOPSLIDELEAP) { bitboard_t from_bb = bitboard_t::square_bitboards[square]; if (is_leaper(flags)) attacked |= generate_leaper_move_bitboard(flags, side_to_move, square, occupied) &~ from_bb; if (is_slider(flags)) attacked |= generate_slider_move_bitboard(flags, side_to_move, square, occupied); if (is_hopper(flags)) attacked |= generate_hopper_move_bitboard(flags, side_to_move, square, occupied); if (is_rider (flags)) attacked |= generate_rider_move_bitboard (flags, side_to_move, square, occupied); } return attacked; } inline bitboard_t generate_move_bitboard_from_squares_for_flags(move_flag_t flags, bitboard_t squares, const bitboard_t occupied, side_t side_to_move) const { bitboard_t attacked; while (!squares.is_empty()) { int square = squares.bitscan(); squares.reset(square); attacked |= generate_move_bitboard_for_flags(flags, square, occupied, side_to_move); } return attacked; } /* Generate an attack bitboard for all attackers within a specified mask */ inline bitboard_t generate_moves_bitboard(const board_t *board, bitboard_t test_squares, bitboard_t source_mask, side_t side_to_move) const { piece_description_t *piece_types; move_flag_t *piece_move_flags; bitboard_t own, enemy, own_movers; bitboard_t occupied; bitboard_t attacked; piece_types = board->piece_types; /* Bookkeeping: we keep a pointer to the next move in the move list, and * update the number of moves in the list at the end of this function */ own = board->bbc[side_to_move]; enemy = board->bbc[next_side[side_to_move]]; occupied = own | enemy | test_squares; own_movers = own & source_mask; for (int k = 0; k<2; k++) { piece_move_flags = (k == 0) ? piece_types->piece_move_flags : piece_types->piece_special_move_flags; for (int n=0; nnum_piece_types; n++) { bitboard_t possible_attackers = own_movers & board->bbp[n]; if (piece_move_flags[n] == 0) continue; if (k == 1) possible_attackers &= piece_types->special_zone[side_to_move][n]; if (possible_attackers.is_empty()) continue; bitboard_t bb = possible_attackers; /* Steppers */ if (is_stepper(piece_move_flags[n])) { int si = get_stepper_index(piece_move_flags[n]); int d; for (d=0; d<8; d++) { int c = (stepper_description[si][side_to_move] >> (d*4)) & 15; bitboard_t captures = bb; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for ( ; c>0; c--) { captures &= step_mask[d]; captures = captures.sshift(step_shift[d]); captures &= piece_types->prison[side_to_move][n]; attacked |= captures; captures &= ~occupied; } } } /* Sliders and leapers */ if (piece_move_flags[n] & MF_HOPSLIDELEAP) while (!bb.is_empty()) { move_flag_t capture_flags = piece_move_flags[n]; int from = bb.bitscan(); bb.reset(from); if (is_leaper(capture_flags)) attacked |= generate_leaper_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_slider(capture_flags)) attacked |= generate_slider_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_hopper(capture_flags)) attacked |= generate_hopper_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_rider (capture_flags)) attacked |= generate_rider_move_bitboard (capture_flags, side_to_move, from, occupied); } attacked &= piece_types->prison[side_to_move][n]; } } return attacked; } move_flag_t define_slider(const char *movestr) { const char *s = movestr; move_flag_t flags = 0; while (*s && isspace(*s)) s++; if (*s == '\0') return 0; int shift = 0; if (strstr(s, "slide") == s) shift = 0; if (strstr(s, "hop") == s) shift = 4; while (*s) { switch (*s) { case 'H': flags |= MF_SLIDER_H << shift; break; case 'V': flags |= MF_SLIDER_V << shift; break; case 'D': flags |= MF_SLIDER_D << shift; break; case 'A': flags |= MF_SLIDER_A << shift; break; case ')': break; default: break; } s++; } if (shift == 0) super_slider_flags |= flags; else super_hopper_flags |= flags; return flags; } move_flag_t define_asymmetric_leaper(const char *movestr) { int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int size = board_files * board_ranks; bitboard_t moves[3][NUM_SIDES][sizeof(kind)*8]; const char *s = movestr; char op = ' '; int ii = 0; int index_flags = 1; uint8_t description = 0; while (*s && *s != ' ') s++; s++; while (*s) { int n, m; while (*s && s[-1] != '(') s++; while (*s && *s == '(') s++; if(!*s) break; sscanf(s, "%d", &n); while (*s && s[-1] != ',') s++; if(!*s) break; sscanf(s, "%d", &m); s++; switch (op) { case '|': /* Define a leaper with more than one type of move */ case ' ': break; case '+': /* A compound leaper, with two steps one after the other */ ii = 1; index_flags |= 2; printf("Error (Compound asymmetric leapers are not implemented)\n"); break; case '&': /* A compound leaper, with a mask (used to implement "lame leapers") */ /* Define a new type of leaper for the mask. * FIXME: check if this type was already defined and re-use. */ ii = 2; index_flags |= 4; printf("Error (lame asymmetric leapers are not implemented)\n"); break; } for (int sqr=0; sqr= MAX_LEAPER_TYPES) { printf("Error (too many aleapers)\n"); return 0; } for (int sqr = 0; sqr < size; sqr++) { aleaper[WHITE][number_of_aleapers][sqr] = moves[ii][WHITE][sqr]; aleaper[BLACK][number_of_aleapers][sqr] = moves[ii][BLACK][sqr]; } index[ii] = number_of_aleapers; number_of_aleapers++; } } return (index[0] | (index[1] << 4) | (index[2] << 8) | (index_flags << 12))<<16 | MF_LEAPER_ASYMM | MF_IS_LEAPER; } move_flag_t define_symmetric_leaper(const char *movestr) { int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int size = board_files * board_ranks; bitboard_t moves[3][sizeof(kind)*8]; const char *s = movestr; char op = ' '; int ii = 0; int index_flags = 1; uint8_t description = 0; while (*s && *s != ' ') s++; s++; while (*s) { int n, m; while (*s && s[-1] != '(') s++; while (*s && *s == '(') s++; if(!*s) break; sscanf(s, "%d", &n); while (*s && s[-1] != ',') s++; if(!*s) break; sscanf(s, "%d", &m); s++; switch (op) { case '|': /* Define a leaper with more than one type of move */ case ' ': break; case '+': /* A compound leaper, with two steps one after the other */ ii = 1; index_flags |= 2; break; case '&': /* A compound leaper, with a mask (used to implement "lame leapers") */ /* Define a new type of leaper for the mask. * FIXME: check if this type was already defined and re-use. */ ii = 2; index_flags |= 4; break; } for (int sqr=0; sqr= MAX_LEAPER_TYPES) { printf("Error (too many leapers)\n"); return 0; } for (int sqr = 0; sqr < size; sqr++) leaper[number_of_leapers][sqr] = moves[ii][sqr]; index[ii] = number_of_leapers; number_of_leapers++; } } move_flag_t move_flags = (index[0] | (index[1] << 4) | (index[2] << 8) | (index_flags << 12))<<16 | MF_IS_LEAPER; /* Define double-step leapers, for super leaper */ if (index_flags == 3) { for (int sqr = 0; sqr < size; sqr++) { bitboard_t from_bb = bitboard_t::square_bitboards[sqr]; leaper[number_of_leapers][sqr] = generate_leaper_move_bitboard(move_flags, WHITE, sqr, from_bb) &~ from_bb; } number_of_leapers++; } return move_flags; } #define update_leaper_bb(bb,n,m) \ if ( (x+n) >= 0 && (y+m) >= 0 && \ (x+n) < board_files && (y+m) < board_ranks) { \ int dest_sqr = bitboard_t::pack_rank_file(y+m, x+n); \ bb.set(dest_sqr); \ } inline bitboard_t make_aleaper_bitboard(int sqr, int n, int m) const { int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int x = unpack_file(sqr); int y = unpack_rank(sqr); bitboard_t leaper; update_leaper_bb(leaper, n, m); return leaper; } inline bitboard_t make_leaper_bitboard(int sqr, int n, int m) const { int board_files = bitboard_t::board_files; int board_ranks = bitboard_t::board_ranks; int x = unpack_file(sqr); int y = unpack_rank(sqr); bitboard_t leaper; update_leaper_bb(leaper, n, m); update_leaper_bb(leaper, n,-m); update_leaper_bb(leaper,-n, m); update_leaper_bb(leaper,-n,-m); update_leaper_bb(leaper, m, n); update_leaper_bb(leaper,-m, n); update_leaper_bb(leaper, m,-n); update_leaper_bb(leaper,-m,-n); return leaper; } #undef update_leaper_bb move_flag_t define_rider(const char *movestr) { const char *s = movestr; uint32_t wstep = 0; uint32_t bstep = 0; /* Dimensions of the board */ int w = bitboard_t::board_files; int h = bitboard_t::board_ranks; int board_size = w*h; if (!movestr) return 0; while (*s && isspace(*s)) s++; if (*s == '\0') return 0; if (strstr(s, "ride ") != s) return 0; s+=5; int index = number_of_riders; for (int n=0; n<4; n++) rider_step[index][n].dx = rider_step[index][n].dy = 0; for (int from = 0; from::pack_rank_file(rank, file); int f = file + dx[n]; int r = rank + dy[n]; int to = bitboard_t::pack_rank_file(r, f); while (f>=0 && r>=0 && f::board_files && r < bitboard_t::board_ranks) { rider_ray[index][from][to] = rider_ray[index][from][old_to]; if (old_to != from) rider_ray[index][from][to].set(old_to); f += dx[n]; r += dy[n]; old_to = to; to = bitboard_t::pack_rank_file(r, f); } } } } return flags; } move_flag_t define_stepper(const char *movestr) { const char *s = movestr; uint32_t wstep = 0; uint32_t bstep = 0; /* Dimensions of the board */ int w = bitboard_t::board_files; int h = bitboard_t::board_ranks; if (!movestr) return 0; while (*s && isspace(*s)) s++; if (*s == '\0') return 0; if (strstr(s, "step ") != s) return 0; s+=5; while (*s) { int count = 1; int shift = 0; if (isdigit(*s)) { sscanf(s, "%d", &count); assert(count < 16); assert(count >= 0); s++; } if (strstr(s, "NE") == s) shift = 1; else if (strstr(s, "NW") == s) shift = 7; else if (strstr(s, "SE") == s) shift = 3; else if (strstr(s, "SW") == s) shift = 5; else if (strstr(s, "N") == s) shift = 0; else if (strstr(s, "E") == s) shift = 2; else if (strstr(s, "S") == s) shift = 4; else if (strstr(s, "W") == s) shift = 6; wstep |= count << (4*shift); bstep |= count << (4*inverse_step[shift]); while(*s && *s != ',') s++; if (*s) s++; while (*s && isspace(*s)) s++; } for (int n = 1; n= MAX_STEPPER_TYPES) { return 0; } int index = number_of_steppers; number_of_steppers++; stepper_description[index][WHITE] = wstep; stepper_description[index][BLACK] = bstep; return make_stepper_index(index); } /* TODO: Betza/Mueller notation for piece movement describes moves and * captures in one go, not individually. */ move_flag_t define_betza(const char *movestr) { const char *s = movestr; move_flag_t flags = 0; while (*s) { const char *atom = s; /* Find first upper-case character */ while (*atom && (islower(*atom) || isspace(*atom))) atom++; if (atom != s) { /* TODO: modifiers */ } /* Repeated atoms = riders. * The only ones we implement are WW and FF */ char a = atom[0]; if (atom[1] == atom[0] || atom[1] == '0') { switch (*atom) { case 'W': a = 'R'; break; case 'F': a = 'B'; break; default: return 0; } atom++; } switch (a) { case 'K': /* King = FW */ flags |= define_piece_move("leap (1,0)|(1,1)"); break; case 'Q': /* Queen = RB */ flags |= define_piece_move("slide (H,V,A,D)"); break; case 'R': /* Rook = WW */ flags |= define_piece_move("slide (H,V)"); break; case 'B': /* Bishop = FF */ flags |= define_piece_move("slide (A,D)"); break; case ' ': case 'O': /* No move, or castling */ break; case 'W': /* Wazir = (1,0) */ flags |= define_piece_move("leap (1,0)"); break; case 'F': /* Ferz = (1,1) */ flags |= define_piece_move("leap (1,1)"); break; case 'D': /* Dabbabah = (2,0) */ flags |= define_piece_move("leap (2,0)"); break; case 'N': /* Knight = (2,1) */ flags |= define_piece_move("leap (2,1)"); break; case 'A': /* Alfil = (2, 2) */ flags |= define_piece_move("leap (2,2)"); break; case 'H': /* Threeleaper = (3, 0) */ flags |= define_piece_move("leap (3,0)"); break; case 'C': /* Camel = (3, 1) */ case 'L': flags |= define_piece_move("leap (3,1)"); break; case 'Z': /* Zebra = (3, 2) */ case 'J': flags |= define_piece_move("leap (3,2)"); break; case 'G': /* (3, 3) leaper */ flags |= define_piece_move("leap (3,3)"); break; default: return 0; } s = atom + 1; } return flags; } move_flag_t define_piece_move(const char *movestr) { if (!movestr) return 0; const char *s = movestr; while (isspace(*s)) s++; if (s[0] == '\0') return 0; /* What type of mover is this? */ if (strstr(s, "none ") == s) return 0; if (strstr(s, "slide ") == s) return define_slider(s); if (strstr(s, "hop ") == s) return define_slider(s); if (strstr(s, "step ") == s) return define_stepper(s); if (strstr(s, "aleap ") == s) return define_asymmetric_leaper(s); if (strstr(s, "leap ") == s) return define_symmetric_leaper(s); if (strstr(s, "ride ") == s) return define_rider(s); /* TODO: try to interpret a Betza-like move description and translate * it to what is used internally. */ return 0; } void clear_castle_rule(int idx, side_t side) { castle_mask[idx][side].clear(); castle_free[idx][side].clear(); castle_safe[idx][side].clear(); castle_king_from[idx][side] = -1; castle_king_dest[idx][side].clear(); castle_rook_dest[idx][side].clear(); } /* Deduce castle flags from king positions and destinations and rook locations. */ int deduce_castle_flags(side_t side, int king_from, int king_to, int rook_from) { /* King-side or queen side? */ bool king_side = (unpack_file(king_to) >= bitboard_t::board_files/2); bitboard_t mask, free, safe; int c, c_first, c_last; int delta = 0; if (unpack_rank(king_from) == unpack_rank(rook_from)) delta = 1; if (unpack_file(king_from) == unpack_file(rook_from)) delta = bitboard_t::board_files; if (bitboard_t::diagonal_nr[king_from] == bitboard_t::diagonal_nr[rook_from]) delta = bitboard_t::board_files+1; if (bitboard_t::anti_diagonal_nr[king_from] == bitboard_t::anti_diagonal_nr[rook_from]) delta = bitboard_t::board_files-1; if (delta == 0) return -1; int rook_to = king_side ? (king_to - delta) : (king_to + delta); /* It is not enough that the king and rook have a clear path * between them: the path to the destination squares needs to be cleared * as well. * This is implied in normal chess, but not in FRC. */ mask = bitboard_t::square_bitboards[king_from] | bitboard_t::square_bitboards[rook_from]; free.clear(); /* The path of the King */ c_first = std::min(king_from, king_to); c_last = std::max(king_from, king_to); for (c = c_first; c <= c_last; c+=delta) free.set(c); safe = free; /* The path of the Rook */ c_first = std::min(rook_to, rook_from); c_last = std::max(rook_to, rook_from); for (c = c_first; c <= c_last; c+=delta) free.set(c); /* Make sure the king and rook are not marked on the "free" bitboard. * Makes no difference for normal chess, but does affect FRC. */ free &= ~mask; mask &= bitboard_t::board_all; free &= bitboard_t::board_all; safe &= bitboard_t::board_all; int idx = king_side ? SHORT : LONG; castle_mask[idx][side] |= mask; castle_free[idx][side] |= free; castle_safe[idx][side] |= safe; castle_king_from[idx][side] = king_from; castle_king_dest[idx][side].set(king_to); castle_rook_dest[idx][side].set(rook_to); return idx; } bitboard_t get_all_attackers(const board_t *board, bitboard_t mask, int square) const { bitboard_t occupied = board->get_occupied() & mask; bitboard_t possible_attackers = occupied & super[square]; occupied.set(square); bitboard_t attacked; bitboard_t attacker; for (int n=0; npiece_types->num_piece_types && !possible_attackers.is_empty(); n++) { move_flag_t capture_flags = board->piece_types->piece_capture_flags[n]; for (side_t side = WHITE; side<=BLACK; side++) { bitboard_t bb = possible_attackers & board->bbp[n] & board->bbc[side]; if (bb.is_empty()) continue; possible_attackers ^= bb; /* Steppers */ if (is_stepper(capture_flags)) { int si = get_stepper_index(board->piece_types->piece_capture_flags[n]); int d; for (d=0; d<8; d++) { int max_c = (stepper_description[si][side] >> (d*4)) & 15; bitboard_t captures = bb & super_stepper[square]; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for (int c = 1; c<=max_c && !captures.is_empty(); c++) { captures &= step_mask[d]; captures = captures.sshift(step_shift[d]); captures &= board->piece_types->prison[side][n]; if (captures.test(square)) { attacker.set(square - c*step_shift[d]); break; } captures &= ~occupied; } } } /* Sliders and leapers */ if (is_leaper(capture_flags)) { bitboard_t bp = bb; while (!(bp & super_leaper[square]).is_empty()) { int s = (bp & super_leaper[square]).bitscan(); bp.reset(s); attacked = generate_leaper_move_bitboard(capture_flags, side, s, occupied); attacked &= board->piece_types->prison[side][n]; if (attacked.test(square)) { attacker.set(s); bb.reset(s); } } } if (is_rider(capture_flags)) { bitboard_t bp = bb; while (!(bp & super_rider[square]).is_empty()) { int s = (bp & super_rider[square]).bitscan(); bp.reset(s); attacked = generate_rider_move_bitboard(capture_flags, side, s, occupied); attacked &= board->piece_types->prison[side][n]; if (attacked.test(square)) { attacker.set(s); bb.reset(s); } } } #if 0 if (is_slider(capture_flags)) { bitboard_t bp = bb; while (!(bp & super_slider[square]).is_empty()) { int s = (bp & super_slider[square]).bitscan(); bp.reset(s); attacked = generate_slider_move_bitboard(capture_flags, side, s, occupied); attacked &= board->piece_types->prison[side][n]; if (attacked.test(square)) { attacker.set(s); bb.reset(s); } } } #endif if (is_hopper(capture_flags)) { bitboard_t bp = bb; while (!(bp & super_hopper[square]).is_empty()) { int s = (bp & super_hopper[square]).bitscan(); bp.reset(s); attacked = generate_hopper_move_bitboard(capture_flags, side, s, occupied); attacked &= board->piece_types->prison[side][n]; if (attacked.test(square)) { attacker.set(s); bb.reset(s); } } } } } /* Find sliders */ move_flag_t cf[] = { MF_SLIDER_V, MF_SLIDER_H, MF_SLIDER_D, MF_SLIDER_A }; int imax = sizeof cf / sizeof *cf; for (int i=0; i sliders; for (int piece = 0; piecepiece_types->num_piece_types; piece++) { if (board->piece_types->piece_capture_flags[piece] & cf[i]) sliders |= board->bbp[piece]; } sliders &= super_slider[square] & mask; if (sliders.is_empty()) continue; attacker |= sliders & generate_slider_move_bitboard(cf[i], WHITE, square, occupied); } return attacker; } bool player_in_check(const board_t *board, side_t side) const { bitboard_t royal = board->royal & board->bbc[side]; bitboard_t empty; /* If there are no royal pieces, then there is no check */ if (royal.is_empty()) return false; /* If there is more than one king, we can never be in check - unless * the rules say we are when all kings are under attack. */ if (!royal.onebit() && !(board->rule_flags & (RF_KING_DUPLECHECK|RF_CHECK_ANY_KING))) return false; move_flag_t *capture_flags = board->piece_types->piece_capture_flags; bitboard_t sup[4]; bitboard_t mask[4]; bitboard_t kmask; bitboard_t bb = royal; while (!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); sup[0] |= super_slider[square]; sup[1] |= super_leaper[square] | super_rider[square]; sup[2] |= super_stepper[square]; sup[3] |= super_hopper[square]; kmask = super[square] & board->bbc[next_side[side]]; } if (royal.onebit() && kmask.is_empty()) return false; for (int n=0; n<4; n++) sup[n] &= board->bbc[next_side[side]]; for (int n = 0; npiece_types->num_piece_types; n++) { if (is_slider(capture_flags[n])) mask[0] |= sup[0] & board->bbp[n]; if (is_leaper(capture_flags[n]) || is_rider(capture_flags[n])) mask[1] |= sup[1] & board->bbp[n]; if (is_stepper(capture_flags[n])) mask[2] |= sup[2] & board->bbp[n]; if (is_hopper(capture_flags[n])) mask[3] |= sup[3] & board->bbp[n]; } /* Mask out pieces that occur in more than one mask: we only need to * test them once, afterall. */ mask[3] &= ~(mask[0] | mask[1] | mask[2]); mask[2] &= ~(mask[0] | mask[1]); mask[1] &= ~(mask[0]); bitboard_t attacked_squares; /* TODO: we can do one better, at least for sliders and normal * leapers (lame leapers are more tricky): when generating the attack * bitboard, first generate appropriate attacks from the target * square and intersect with the piece type. This allows us to test * against all pieces of the particular type in one go, and we avoid * some possible false positives. */ for (int n=0; n<4; n++) { if (mask[n].is_empty()) continue; attacked_squares |= generate_attack_bitboard(board, empty, mask[n], next_side[side]); if ((attacked_squares & royal) == royal) return true; } if (expect(board->rule_flags & RF_KING_TABOO, false)) { bitboard_t other_king = board->royal & kmask; if (!other_king.is_empty()) { int square = other_king.bitscan(); bitboard_t occ = board->get_occupied(); attacked_squares |= generate_slider_move_bitboard(MF_SLIDER_V, next_side[side], square, occ); } return (attacked_squares & royal) == royal; } if (board->rule_flags & RF_CHECK_ANY_KING) return !(attacked_squares & royal).is_empty(); return false; } bool was_checking_move(board_t *board, side_t side, move_t lastmove) const { side_t oside = next_side[side]; bitboard_t royal = board->royal & board->bbc[side]; bitboard_t empty; if (royal.is_empty()) return false; if (royal.onebit()) { bitboard_t move_bb; int king = royal.bitscan(); int to = get_move_to(lastmove); assert(to < 8*sizeof(kind)); move_bb.set(to); if (!is_drop_move(lastmove)) { int from = get_move_from(lastmove); assert(from < 8*sizeof(kind)); move_bb.set(from); } if (is_castle_move(lastmove)) { int from = get_castle_move_from2(lastmove); int to = get_castle_move_to2(lastmove); assert(from < 8*sizeof(kind)); assert(to < 8*sizeof(kind)); move_bb.set(from); move_bb.set(to); } if (is_capture_move(lastmove)) { int square = get_move_capture_square(lastmove); assert(square < 8*sizeof(kind)); move_bb.set(square); } if ((super[king] & move_bb).is_empty()) { assert(!player_in_check(board, side)); return false; } else { bitboard_t mask; // FIXME: the following test can be excluded for games without // hoppers, where moving to the same ray as the king either // blocks the ray (harmless, or it would already have been // check) or is a direct attack. However, if hoppers are in the // game, then it's possible to add an attacker. //move_bb.reset(to); if (is_castle_move(lastmove)) { mask.set(get_castle_move_to2(lastmove)); move_bb.reset(get_castle_move_to2(lastmove)); } int king_file = unpack_file(king); int king_rank = unpack_rank(king); int king_diag = bitboard_t::diagonal_nr[king]; int king_anti = bitboard_t::anti_diagonal_nr[king]; while (!move_bb.is_empty()) { int square = move_bb.bitscan(); int file = unpack_file(square); int rank = unpack_rank(square); int diag = bitboard_t::diagonal_nr[square]; int anti = bitboard_t::anti_diagonal_nr[square]; move_bb.reset(square); if (file == king_file) mask |= bitboard_t::board_file[file]; if (rank == king_rank) mask |= bitboard_t::board_rank[rank]; if (diag == king_diag) mask |= bitboard_t::board_diagonal[diag]; if (anti == king_anti) mask |= bitboard_t::board_antidiagonal[anti]; } //bitboard_t sliders; //for (int n = 0; npiece_types->num_piece_types; n++) { // if (board->piece_types->piece_capture_flags[n] & (MF_HOPSLIDE|MF_STEPPER)) // sliders |= board->bbp[n]; //} //mask &= sliders; mask.set(to); /* Possible lame leapers */ mask |= super_leaper[king] & board->bbc[oside]; bitboard_t attacked_squares = generate_attack_bitboard(board, empty, mask, oside); if (attacked_squares.test(king)) return true; assert(!player_in_check(board, side)); return false; } } return player_in_check(board, side); } bitboard_t get_pinned_pieces(const board_t *board, side_t side) const { bitboard_t royal = board->royal & board->bbc[side]; bitboard_t pinned; bitboard_t potential_pins; /* If there is more than one king, or no king at all - ignore pins. */ if (!royal.onebit()) return pinned; int king = royal.bitscan(); potential_pins = board->bbc[side] & super[king]; for (int n = 0; npiece_types->num_piece_types; n++) { bitboard_t atk = board->bbp[n] & board->bbc[next_side[side]] & super[king]; move_flag_t atk_flags = board->piece_types->piece_capture_flags[n]; while(!atk.is_empty()) { int attacker = atk.bitscan(); atk.reset(attacker); /* Sliders */ if (is_slider(atk_flags)) { bitboard_t occ = board->get_occupied(); bitboard_t bb = occ & bitboard_t::board_between[king][attacker]; bb &= generate_slider_move_bitboard(atk_flags, next_side[side], attacker, occ & ~bb); if (bb.onebit()) pinned |= bb&potential_pins; } /* Hoppers */ if (is_hopper(atk_flags)) { bitboard_t occ = board->get_occupied(); bitboard_t bb = occ & bitboard_t::board_between[king][attacker]; bb &= generate_slider_move_bitboard(atk_flags>>4, next_side[side], attacker, occ & ~bb); if (bb.twobit()) pinned |= bb&potential_pins; } /* Riders */ if (is_rider(atk_flags)) { int index = get_rider_index(atk_flags); bitboard_t occ = board->get_occupied(); bitboard_t bb = occ & rider_ray[index][king][attacker]; bb &= generate_rider_move_bitboard(atk_flags>>4, next_side[side], attacker, occ & ~bb); if (bb.twobit()) pinned |= bb&potential_pins; } /* TODO: multi-steppers */ /* Lame leapers */ if (is_leaper(atk_flags) && is_masked_leaper(atk_flags) && is_double_leaper(atk_flags)) { bitboard_t occ = board->get_occupied(); bitboard_t atk = generate_leaper_move_bitboard(atk_flags, next_side[side], attacker, occ); if (!atk.test(king)) { int index = get_leaper_index(atk_flags); potential_pins &= leaper[index][attacker]; while (!potential_pins.is_empty()) { int square = potential_pins.bitscan(); potential_pins.reset(square); occ.reset(square); atk = generate_leaper_move_bitboard(atk_flags, next_side[side], attacker, occ); if (atk.test(king)) { pinned.set(square); break; } } } } } } return pinned; } template void generate_stepper_moves_mask_for_piece(movelist_t *movelist, const board_t *board, int piece, move_flag_t move_flags, piece_flag_t piece_flags, piece_description_t *piece_types, bitboard_t from_bb, bitboard_t destination_mask, bitboard_t occupied, promotion_zone_t *promotion, bitboard_t promotion_zone, bitboard_t optional_promotion_zone, side_t side, piece_bit_t allowed_promotion_pieces) const { move_t move; /* Check for stepper moves, which are generated in parallel */ if (!is_stepper(move_flags) || from_bb.is_empty()) return; int si = get_stepper_index(move_flags); for (int d=0; d<8; d++) { /* Loop over all directions */ int max_c = (stepper_description[si][side] >> (d*4)) & 15; bitboard_t moves = from_bb; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for (int c = 1; c<=max_c; c++) { moves &= step_mask[d]; moves = moves.sshift(step_shift[d]); moves &= ~occupied; moves &= piece_types->prison[side][neutral_piece(piece)]; /* Scan all bits */ bitboard_t bb = moves & destination_mask; while (!bb.is_empty()) { int to = bb.bitscan(); int from = to - c*step_shift[d]; bb.reset(to); /* Check for promotions * When the piece moves into the promotion zone, it will get promoted to one of the allowed * promotion pieces, which can be different for each piece type (and further restricted, for * instance during Q-search). * Promotion to a royal piece is only allowed if the number of royal pieces a player has is * smaller than the maximum number of royal pieces. */ bool norm = true; bool opt = false; bool comp = false; bitboard_t mask, wild; if ((piece_flags & PF_PROMOTEWILD) && (board->bbc[side]&board->bbp[neutral_piece(piece)]).onebit()) wild = bitboard_t::board_all; mask.clear(); if (!(board->rule_flags & RF_PROMOTE_BY_MOVE)) { mask.set(to); mask.set(from); mask &= piece_types->entry_promotion_zone[side][neutral_piece(piece)]; mask |= wild; } piece_bit_t ptried = 0; if (!(mask & promotion_zone).is_empty()) for (int k=0; k pz = (promotion[k].zone[side] & mask) | wild; if (pz.test(to) || (pz.test(from) && !(board->rule_flags & RF_PROMOTE_IN_PLACE))) { piece_bit_t c = promotion[k].choice & allowed_promotion_pieces & ~ptried; ptried |= promotion[k].choice; while (c) { int tpiece = bitscan32(c); c ^= 1<piece_maximum[tpiece][side] == 128 || board->piece_count(tpiece, side) < piece_types->piece_maximum[tpiece][side]) { tpiece = piece_for_side(tpiece, side); move = encode_normal_promotion(piece, from, to, tpiece); movelist->push(move); } } /* If promotions are optional, we also encode a normal move */ //if (!(pz & optional_promotion_zone).is_empty()) if (optional_promotion_zone.test(to) || (optional_promotion_zone.test(from) && !promotion_zone.test(to) ) || wild.test(from)) opt = true; else if (promotion[k].zone[side].test(to)) comp = true; norm = false; } } opt = opt && !comp; if (norm || opt) { move = encode_normal_move(piece, from, to); if (special && c>1 && piece_flags & PF_SET_EP) move |= MOVE_SET_ENPASSANT; if (piece_flags & PF_NORET) move |= MOVE_RESET50; movelist->push(move); } } } } } template void generate_stepper_captures_mask_for_piece(movelist_t *movelist, const board_t *board, int piece, move_flag_t move_flags, piece_flag_t piece_flags, piece_description_t *piece_types, bitboard_t from_bb, bitboard_t destination_mask, bitboard_t occupied, bitboard_t enemy, bitboard_t ep_dest, promotion_zone_t *promotion, bitboard_t promotion_zone, bitboard_t optional_promotion_zone, side_t side, piece_bit_t allowed_promotion_pieces) const { move_t move; /* Check for stepper moves, which are generated in parallel */ if (is_stepper(move_flags)) { int si = get_stepper_index(move_flags); for (int d=0; d<8; d++) { /* Loop over all directions */ int max_c = (stepper_description[si][side] >> (d*4)) & 15; bitboard_t captures = from_bb; /* We have a repetition count, so we do a number of steps one after the other. * This can effectively duplicate a slider. */ for (int c = 1; c<=max_c; c++) { captures &= step_mask[d]; captures = captures.sshift(step_shift[d]); captures &= piece_types->prison[side][neutral_piece(piece)]; /* Scan all bits */ bitboard_t bb = captures & (enemy | ep_dest) & destination_mask; while (!bb.is_empty()) { int to = bb.bitscan(); int from = to - c*step_shift[d]; int idtaken = board->get_piece(to); bb.reset(to); if (ep_dest.test(to)) idtaken = board->get_piece(board->ep_victim); if ( (piece_types->piece_allowed_victims[neutral_piece(piece)] & (1 << idtaken)) == 0 ) continue; /* Check for promotions * When the piece moves into the promotion zone, it will get promoted to one of the allowed * promotion pieces, which can be different for each piece type (and further restricted, for * instance during Q-search). * Promotion to a royal piece is only allowed if the number of royal pieces a player has is * smaller than the maximum number of royal pieces. */ bool norm = true; bool opt = false; bool comp = false; bitboard_t mask, wild; if ((piece_flags & PF_PROMOTEWILD) && (board->bbc[side]&board->bbp[neutral_piece(piece)]).onebit()) wild = bitboard_t::board_all; mask.clear(); if (!(board->rule_flags & RF_PROMOTE_BY_MOVE)) { mask.set(to); mask.set(from); mask &= piece_types->entry_promotion_zone[side][neutral_piece(piece)]; } piece_bit_t ptried = 0; if (!(mask & promotion_zone).is_empty() && !(board->rule_flags & RF_QUIET_PROMOTION)) for (int k=0; k pz = (promotion[k].zone[side] & mask) | wild; if (pz.test(to) || (pz.test(from) && !(board->rule_flags & RF_PROMOTE_IN_PLACE))) { piece_bit_t c = promotion[k].choice & allowed_promotion_pieces & ~ptried; ptried |= promotion[k].choice; if (piece_types->piece_flags[idtaken] & PF_ASSIMILATE) { c = (1<piece_maximum[tpiece][side] == 128 || board->piece_count(tpiece, side) < piece_types->piece_maximum[tpiece][side]) { tpiece = piece_for_side(tpiece, side); move = encode_capture_promotion(piece, from, to, tpiece); if (capture_to_holdings) { int victim = to; if (ep_dest.test(to)) victim = board->ep_victim; int pstore = piece_types->demotion[board->get_piece(victim)]; side_t store_side = NONE; if (board->rule_flags & RF_KEEP_CAPTURE) store_side = side; if (board->rule_flags & RF_RETURN_CAPTURE) store_side = next_side[side]; assert(store_side != NONE); move = add_move_store(move, piece_for_side(pstore, store_side), 1); } movelist->push(move); } } /* If promotions are optional, we also encode a normal move */ //if (!(pz & optional_promotion_zone).is_empty()) if (optional_promotion_zone.test(to) || (optional_promotion_zone.test(from) && !promotion_zone.test(to) ) || wild.test(from)) opt = true; else if (promotion[k].zone[side].test(to)) comp = true; norm = false; } } opt = opt && !comp; if (norm || opt) { if (ep_dest.test(to)) { move = encode_en_passant_capture(piece, from, to, board->ep_victim); } else { if (piece_types->piece_flags[idtaken] & PF_ASSIMILATE) { int tpiece = piece_for_side(idtaken, side); move = encode_capture_promotion(piece, from, to, tpiece); } else { move = encode_normal_capture(piece, from, to); } } if (capture_to_holdings) { int pstore = piece_types->demotion[idtaken]; side_t store_side = NONE; if (board->rule_flags & RF_KEEP_CAPTURE) store_side = side; if (board->rule_flags & RF_RETURN_CAPTURE) store_side = next_side[side]; assert(store_side != NONE); move = add_move_store(move, piece_for_side(pstore, store_side), 1); } movelist->push(move); } } captures &= ~occupied; } } } } void generate_double_moves(movelist_t *movelist, move_flag_t move_flags, bitboard_t destination_mask, side_t side_to_move, bitboard_t occupied, int piece, int from) const { move_flag_t cf1 = move_flags & ( MF_LEAPER | MF_IS_LEAPER | MF_LEAPER_ASYMM ); move_flag_t cf2 = move_flags & ( MF_IS_LEAPER | MF_LEAPER_ASYMM ); cf2 |= get_leaper_index2(move_flags) << 16; bitboard_t moves1 = generate_leaper_move_bitboard(cf1, side_to_move, from, occupied); bitboard_t moves2, bb; bb = moves1; while (!bb.is_empty()) { int to = bb.bitscan(); bb.reset(to); moves2 |= generate_leaper_move_bitboard(cf2, side_to_move, to, occupied); } if (!(moves1 & ~occupied).is_empty()) bb = moves2 & bitboard_t::square_bitboards[from]; moves2 &= destination_mask & ~occupied; moves2 |= bb; /* Serialise */ while (!moves2.is_empty()) { int to = moves2.bitscan(); moves2.reset(to); /* Push this move directly */ move_t move = encode_normal_move(piece, from, to); movelist->push(move); } } void generate_double_captures(movelist_t *movelist, move_flag_t capture_flags, bitboard_t destination_mask, side_t side_to_move, bitboard_t occupied, bitboard_t enemy, int piece, int from) const { move_flag_t cf1 = capture_flags & ( MF_LEAPER | MF_IS_LEAPER | MF_LEAPER_ASYMM ); move_flag_t cf2 = capture_flags & ( MF_IS_LEAPER | MF_LEAPER_ASYMM ); cf2 |= get_leaper_index2(capture_flags) << 16; bitboard_t captures = generate_leaper_move_bitboard(cf1, side_to_move, from, occupied); bitboard_t moves = captures;// & ~occupied; captures &= destination_mask & enemy; /* Serialise primary captures */ bitboard_t done; while (!captures.is_empty()) { int to = captures.bitscan(); captures.reset(to); /* Push this move directly */ move_t move = encode_normal_capture(piece, from, to); movelist->push(move); done.set(to); /* Second step */ bitboard_t c2 = generate_leaper_move_bitboard(cf2, side_to_move, to, occupied) & destination_mask; bitboard_t m2 = c2 & ~occupied; c2 &= enemy; m2.set(from); /* Serialise second capture */ while(!c2.is_empty()) { int to2 = c2.bitscan(); c2.reset(to2); move_t move = encode_double_capture(piece, from, to2, to); movelist->push(move); } /* Serialise second move (non-capture) */ while(!m2.is_empty()) { int to2 = m2.bitscan(); m2.reset(to2); move_t move = encode_en_passant_capture(piece, from, to2, to); movelist->push(move); } } /* Serialise primary moves */ while (!moves.is_empty()) { int to = moves.bitscan(); moves.reset(to); bitboard_t c2 = generate_leaper_move_bitboard(cf2, side_to_move, to, occupied); c2 &= destination_mask & enemy; c2 &= ~done; while(!c2.is_empty()) { int to2 = c2.bitscan(); c2.reset(to2); if (!done.test(to2)) { move_t move = encode_normal_capture(piece, from, to2); movelist->push(move); } done.set(to2); } } } template void do_generate_moves_mask(movelist_t *movelist, const board_t *board, bitboard_t source_mask, bitboard_t destination_mask, side_t side_to_move, piece_bit_t allowed_promotion_pieces, piece_bit_t allowed_drop_pieces, piece_bit_t allowed_piece_deferrals) const { piece_description_t *piece_types; move_flag_t *piece_capture_flags; move_flag_t *piece_move_flags; move_flag_t *special_move_flags; move_flag_t *initial_move_flags; bitboard_t own, enemy, own_movers; bitboard_t attacked; move_t move; int n; assert(!player_in_check(board, next_side[side_to_move])); piece_types = board->piece_types; piece_capture_flags = piece_types->piece_capture_flags; piece_move_flags = piece_types->piece_move_flags; special_move_flags = piece_types->piece_special_move_flags; initial_move_flags = piece_types->piece_initial_move_flags; /* Bookkeeping: we keep a pointer to the next move in the move list, and * update the number of moves in the list at the end of this function */ own = board->bbc[side_to_move]; enemy = board->bbc[next_side[side_to_move]]; own_movers = own & source_mask; bitboard_t movers = own_movers; /* Generate drops */ if (generate_drops && allowed_drop_pieces) { bool dropped = false; for (n=0; nnum_piece_types; n++) { bitboard_t occupied = board->get_occupied(); if (board->holdings[n][side_to_move] && (allowed_drop_pieces & (1 << n))) { dropped = true; int piece = piece_for_side(n, side_to_move); bitboard_t drops = destination_mask & ~occupied & piece_types->drop_zone[side_to_move][n]; if (piece_types->piece_flags[n] & PF_DROPONEFILE) { for (int f = 0; f::board_files; f++) { bitboard_t bb = own & board->bbp[n] & bitboard_t::board_file[f]; if (!bb.is_empty()) if (piece_types->piece_drop_file_maximum[n] < 2 || bb.popcount() >= piece_types->piece_drop_file_maximum[n]) drops &= ~bitboard_t::board_file[f]; } } while (!drops.is_empty()) { int to = drops.bitscan(); drops.reset(to); move = encode_drop_move(piece, to); move = add_move_retrieve(move, piece, 1); movelist->push(move); if (board->rule_flags & RF_PROMOTE_ON_DROP) { piece_bit_t c = piece_types->piece_promotion_choice[n] & allowed_promotion_pieces; while (c) { int tpiece = bitscan32(c); c ^= 1<piece_maximum[tpiece][side_to_move] == 128 || board->piece_count(tpiece, side_to_move) < piece_types->piece_maximum[tpiece][side_to_move]) { tpiece = piece_for_side(tpiece, side_to_move); move = encode_drop_move(tpiece, to); move = add_move_retrieve(move, piece, 1); movelist->push(move); } } } } } } /* Break out early if drops are possible and are forced if possible; no other moves are legal. */ if (dropped && (board->rule_flags & RF_FORCE_DROPS)) goto done; } /* Generate lifts */ if (generate_pickup && allowed_drop_pieces && !board->check()) { for (n=0; nnum_piece_types; n++) { if (piece_types->piece_flags[n] & PF_ROYAL) continue; bitboard_t lift = own_movers & board->bbp[n]; if (!lift.is_empty() && (allowed_drop_pieces & (1 << n))) { int piece = piece_for_side(n, side_to_move); while (!lift.is_empty()) { int from = lift.bitscan(); lift.reset(from); move = encode_pickup_move(piece, from); move = add_move_store(move, piece, 1); movelist->push(move); } } } } /* Now generate moves for all pieces; only scan our own pieces. This mainly helps variants with different * armies. * We generate all moves for a particular piece-type first. */ for (n=0; nnum_piece_types && !movers.is_empty(); n++) { if ((movers & board->bbp[n]).is_empty()) continue; bitboard_t occupied = board->get_occupied() | piece_types->block[side_to_move][n]; movers &= ~board->bbp[n]; bitboard_t special_zone = piece_types->special_zone[side_to_move][n]; bitboard_t initial_zone = initial_move_flags[n] ? board->init : bitboard_t::board_empty; if (board->rule_flags & RF_SPECIAL_IS_INIT) special_zone &= board->init; if ((piece_types->piece_flags[n] & PF_ROYAL) && board->check()) special_zone.clear(); bitboard_t ep_dest; if (piece_types->piece_flags[n] & PF_TAKE_EP) ep_dest = board->ep; promotion_zone_t *promotion = piece_types->promotion[n]; bitboard_t promotion_zone = piece_types->promotion_zone[side_to_move][n]; bitboard_t optional_promotion_zone = piece_types->optional_promotion_zone[side_to_move][n]; bitboard_t bb = own_movers & board->bbp[n]; int piece = piece_for_side(n, side_to_move); /* In-place promotions */ if (promote_in_place) { promotion_zone_t *promotion = piece_types->promotion[n]; for (int k=0; k pz = promotion[k].zone[side_to_move]; if ((piece_types->piece_flags[n] & PF_PROMOTEWILD) && (board->bbc[side_to_move]&board->bbp[n]).onebit()) pz = bitboard_t::board_all; bitboard_t bp = bb & pz; while (!bp.is_empty()) { int square = bp.bitscan(); bp.reset(square); piece_bit_t c = piece_types->piece_promotion_choice[n] & promotion[k].choice & allowed_promotion_pieces; while (c) { int tpiece = bitscan32(c); c ^= 1<piece_maximum[tpiece][side_to_move] == 128 || board->piece_count(tpiece, side_to_move) < piece_types->piece_maximum[tpiece][side_to_move]) { tpiece = piece_for_side(tpiece, side_to_move); move = encode_normal_promotion(piece, square, square, tpiece); movelist->push(move); } } } } } /* Generate stepper moves, in parallel */ generate_stepper_moves_mask_for_piece(movelist, board, piece, piece_move_flags[n], piece_types->piece_flags[n], piece_types, bb & ~(special_zone|initial_zone), destination_mask, occupied, promotion, promotion_zone, optional_promotion_zone, side_to_move, allowed_promotion_pieces); generate_stepper_moves_mask_for_piece(movelist, board, piece, special_move_flags[n], piece_types->piece_flags[n], piece_types, bb & special_zone, destination_mask, occupied, promotion, promotion_zone, optional_promotion_zone, side_to_move, allowed_promotion_pieces); generate_stepper_moves_mask_for_piece(movelist, board, piece, initial_move_flags[n], piece_types->piece_flags[n], piece_types, bb & initial_zone, destination_mask, occupied, promotion, promotion_zone, optional_promotion_zone, side_to_move, allowed_promotion_pieces); generate_stepper_captures_mask_for_piece(movelist, board, piece, piece_capture_flags[n], piece_types->piece_flags[n], piece_types, bb, destination_mask, occupied, enemy, ep_dest, promotion, promotion_zone, optional_promotion_zone, side_to_move, allowed_promotion_pieces); /* Castling * Because of the hassle when doing legality testing, we explicitly test whether castling is allowed in * the current position by testing for attacks on any of the critical squares. This is a hassle and * potentially slow, but only if castling may be possible in the current position. */ if (expect(piece_types->piece_flags[n] & PF_CASTLE, false) && !(board->init&bb).is_empty()) { if (!((piece_types->piece_flags[n] & PF_ROYAL) && board->check())) for (int c = SHORT; cinit & castle_mask[c][side_to_move]) == castle_mask[c][side_to_move]) { if (castle_free[c][side_to_move].is_empty() || ((occupied & castle_free[c][side_to_move]).is_empty() && !(destination_mask & castle_free[c][side_to_move]).is_empty())) { bitboard_t test = castle_safe[c][side_to_move]; bitboard_t mask = generate_super_attacks_for_squares(test, super); bitboard_t attacked_squares = generate_attack_bitboard(board, test, mask, next_side[side_to_move]); if ((attacked_squares & castle_safe[c][side_to_move]).is_empty()) { int from1 = (castle_mask[c][side_to_move] & bb).bitscan(); int from2 = (castle_mask[c][side_to_move] & ~bb).bitscan(); int piece2 = piece_for_side(board->get_piece(from2), side_to_move); bitboard_t king_dest = castle_king_dest[c][side_to_move]; bitboard_t rook_dest = castle_rook_dest[c][side_to_move]; while (!king_dest.is_empty() && !rook_dest.is_empty()) { int to1 = king_dest.bitscan(); int to2 = rook_dest.bitscan(); king_dest.reset(to1); rook_dest.reset(to2); move = encode_castle_move(piece, from1, to1, piece2, from2, to2); movelist->push(move); } } } } } } /* Now determine slider and leaper moves for this piece type - if it has any */ bool test_other_moves = (piece_move_flags[n] | piece_capture_flags[n] | special_move_flags[n]) & (MF_SLIDER|MF_HOPPER|MF_IS_LEAPER|MF_RIDER); if ( test_other_moves || (board->rule_flags & RF_PROMOTE_BY_MOVE)) { while (!bb.is_empty()) { bitboard_t moves; bitboard_t captures; int from = bb.bitscan(); bb.reset(from); move_flag_t move_flags = piece_move_flags[n]; move_flag_t capture_flags = piece_capture_flags[n]; if (special_zone.test(from)) move_flags = special_move_flags[n]; if (initial_move_flags[n] && initial_zone.test(from)) move_flags = initial_move_flags[n]; move_flags &= (MF_SLIDER | MF_HOPPER | MF_LEAPER_FLAGS | MF_RIDER); capture_flags &= (MF_SLIDER | MF_HOPPER | MF_LEAPER_FLAGS | MF_RIDER); /* Multi-step leapers (Lion) */ if (is_double_leaper(move_flags) && !is_masked_leaper(move_flags)) { generate_double_moves(movelist, move_flags, destination_mask, side_to_move, occupied, piece, from); move_flags &= (MF_SLIDER | MF_HOPPER | MF_RIDER); } if (is_double_leaper(capture_flags) && !is_masked_leaper(capture_flags)) { generate_double_captures(movelist, capture_flags, destination_mask, side_to_move, occupied, enemy, piece, from); capture_flags &= (MF_SLIDER | MF_HOPPER | MF_RIDER); } if (is_leaper(move_flags)) moves |= generate_leaper_move_bitboard(move_flags, side_to_move, from, occupied); if (is_slider(move_flags)) moves |= generate_slider_move_bitboard(move_flags, side_to_move, from, occupied); if (is_hopper(move_flags)) moves |= generate_hopper_move_bitboard(move_flags, side_to_move, from, occupied); if (is_rider (move_flags)) moves |= generate_rider_move_bitboard (move_flags, side_to_move, from, occupied); moves &= piece_types->prison[side_to_move][n]; /* Filter royal moves that pass through check */ if ( (board->rule_flags & RF_NO_MOVE_PAST_CHECK) && (piece_types->piece_flags[n] & PF_ROYAL) ) { bitboard_t mask = generate_super_attacks_for_squares(moves, super); bitboard_t attacked_squares = generate_attack_bitboard(board, bitboard_t::board_empty, mask, next_side[side_to_move]); if (!(moves & attacked_squares).is_empty()) { bitboard_t ok_moves; while (!moves.is_empty()) { int to = moves.bitscan(); moves.reset(to); if ( (bitboard_t::board_between[from][to] & attacked_squares).is_empty()) ok_moves.set(to); } moves = ok_moves; } } /* Optimise the common case where pieces move the same way * they capture. */ if (capture_flags == move_flags) { captures = moves; } else { if (is_leaper(capture_flags)) captures |= generate_leaper_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_slider(capture_flags)) captures |= generate_slider_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_hopper(capture_flags)) captures |= generate_hopper_move_bitboard(capture_flags, side_to_move, from, occupied); if (is_rider (capture_flags)) captures |= generate_rider_move_bitboard (capture_flags, side_to_move, from, occupied); captures &= piece_types->prison[side_to_move][n]; } /* Pass */ if (moves.test(from)) { move_t move = encode_normal_move(piece, from, from); movelist->push(move); } /* Mask out occupied squares from normal moves, only capture enemy pieces */ moves &= ~occupied; captures &= enemy; moves &= destination_mask; captures &= destination_mask; /* Serialise moves * We separate out the promotion moves and options and * serialise those in a separate loop. */ bitboard_t pmoves = moves & (promotion_zone & ~optional_promotion_zone); bitboard_t pcaptures = captures & (promotion_zone & ~optional_promotion_zone); moves ^= pmoves; captures ^= pcaptures; pmoves |= moves & optional_promotion_zone; pcaptures |= captures & optional_promotion_zone; pmoves &= piece_types->entry_promotion_zone[side_to_move][n]; pcaptures &= piece_types->entry_promotion_zone[side_to_move][n]; /* Also include moves that originate in the promotion zone. */ if (promotion_zone.test(from)) { pmoves |= moves; pcaptures |= captures; if (!optional_promotion_zone.test(from)) { moves.clear(); captures.clear(); } } if (!(allowed_piece_deferrals & (1<push(move); } while (!captures.is_empty()) { int to = captures.bitscan(); int ptaken = board->get_piece(to); captures.reset(to); if ( capture_victim_sideeffect && (piece_types->piece_allowed_victims[n] & (1 << ptaken)) == 0 ) continue; if (capture_victim_sideeffect && (piece_types->piece_flags[ptaken] & PF_ASSIMILATE) && !(piece_types->piece_flags[n] & PF_ROYAL)) { int tpiece = piece_for_side(ptaken, side_to_move); move = encode_capture_promotion(piece, from, to, tpiece); } else { move = encode_normal_capture(piece, from, to); } if (capture_to_holdings) { int pstore = piece_types->demotion[board->get_piece(to)]; side_t store_side = NONE; if (board->rule_flags & RF_KEEP_CAPTURE) store_side = side_to_move; if (board->rule_flags & RF_RETURN_CAPTURE) store_side = next_side[side_to_move]; assert(store_side != NONE); move = add_move_store(move, piece_for_side(pstore, store_side), 1); } movelist->push(move); } /* Special promotion moves */ if (board->rule_flags & RF_PROMOTE_BY_MOVE) { bitboard_t pm; bitboard_t pc; piece_bit_t ptried = 0; for (int k=0; k pz = promotion[k].zone[side_to_move]; if ((piece_types->piece_flags[n] & PF_PROMOTEWILD) && (board->bbc[side_to_move]&board->bbp[n]).onebit()) pz = bitboard_t::board_all; if (!pz.test(from)) continue; piece_bit_t c = promotion[k].choice & allowed_promotion_pieces & ~ptried; ptried |= promotion[k].choice; while (c) { int tp = bitscan32(c); c ^= 1<rule_flags & RF_QUIET_PROMOTION) pcaptures.clear(); /* Promotions */ while (!pmoves.is_empty()) { int to = pmoves.bitscan(); piece_bit_t ptried = 0; pmoves.reset(to); for (int k=0; k pz = promotion[k].zone[side_to_move]; if ((piece_types->piece_flags[n] & PF_PROMOTEWILD) && (board->bbc[side_to_move]&board->bbp[n]).onebit()) pz = bitboard_t::board_all; if (!pz.test(to) && !pz.test(from)) continue; piece_bit_t c = promotion[k].choice & allowed_promotion_pieces & ~ptried; ptried |= promotion[k].choice; while (c) { int tpiece = bitscan32(c); c ^= 1<piece_maximum[tpiece][side_to_move] == 128 || board->piece_count(tpiece, side_to_move) < piece_types->piece_maximum[tpiece][side_to_move]) { tpiece = piece_for_side(tpiece, side_to_move); move = encode_normal_promotion(piece, from, to, tpiece); movelist->push(move); } } } } while (!pcaptures.is_empty()) { int to = pcaptures.bitscan(); int ptaken = board->get_piece(to); pcaptures.reset(to); if ( capture_victim_sideeffect && (piece_types->piece_allowed_victims[n] & (1 << ptaken)) == 0 ) continue; piece_bit_t ptried = 0; for (int k=0; k pz = promotion[k].zone[side_to_move]; if ((piece_types->piece_flags[n] & PF_PROMOTEWILD) && (board->bbc[side_to_move]&board->bbp[n]).onebit()) pz = bitboard_t::board_all; if (!pz.test(to) && !pz.test(from)) continue; piece_bit_t c = promotion[k].choice & allowed_promotion_pieces & ~ptried; ptried |= promotion[k].choice; if (capture_victim_sideeffect && (piece_types->piece_flags[ptaken] & PF_ASSIMILATE)) { c = (1<piece_maximum[tpiece][side_to_move] == 128 || board->piece_count(tpiece, side_to_move) < piece_types->piece_maximum[tpiece][side_to_move]) { tpiece = piece_for_side(tpiece, side_to_move); move = encode_capture_promotion(piece, from, to, tpiece); if (capture_to_holdings) { int victim = to; if (ep_dest.test(to)) victim = board->ep_victim; int pstore = piece_types->demotion[board->get_piece(victim)]; side_t store_side = NONE; if (board->rule_flags & RF_KEEP_CAPTURE) store_side = side_to_move; if (board->rule_flags & RF_RETURN_CAPTURE) store_side = next_side[side_to_move]; assert(store_side != NONE); move = add_move_store(move, piece_for_side(pstore, store_side), 1); } movelist->push(move); } } } } } } } done: return; } template void do_generate_moves_mask_victim(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer) const { bool victim_effect = (board->rule_flags & RF_VICTIM_SIDEEFFECT) != 0; bool immune = false; if (victim_effect) { bitboard_t iron, danger; if (!board->retaliate_ok()) { for (int n = 0; npiece_types->num_piece_types; n++) { if (board->piece_types->piece_flags[n] & PF_NO_RETALIATE) iron |= board->bbp[n]; } } for (int n = 0; npiece_types->num_piece_types; n++) { if (board->piece_types->piece_flags[n] & PF_ENDANGERED) danger |= board->bbp[n]; if (board->piece_types->piece_flags[n] & PF_IRON) iron |= board->bbp[n]; } iron &= to; to &= ~iron; //if ((from & danger).is_empty() && iron.is_empty() && !immune) victim_effect = false; if (!(from & danger).is_empty()) { bitboard_t to_danger = to & ~danger; while (!(danger & to & board->bbc[next_side[stm]]).is_empty()) { int sqr = (danger & to & board->bbc[next_side[stm]]).bitscan(); danger.reset(sqr); bitboard_t test = bitboard_t::square_bitboards[sqr]; bitboard_t mask = super[sqr] & board->bbc[next_side[stm]]; bitboard_t attacked_squares = generate_attack_bitboard(board, test, mask, next_side[stm]); if ((attacked_squares & test).is_empty()) to_danger.set(sqr); } do_generate_moves_mask(ml, board, from&danger, to_danger, stm, allowed_prom, allowed_drop, allowed_defer); from &= ~danger; } } if (victim_effect) do_generate_moves_mask(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); else do_generate_moves_mask(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } template void do_generate_moves_mask_inplace(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer) const { if (board->rule_flags & RF_PROMOTE_IN_PLACE) do_generate_moves_mask_victim(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); else do_generate_moves_mask_victim(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } template void do_generate_moves_mask_pickup(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer) const { if ((board->rule_flags & RF_ALLOW_PICKUP)) do_generate_moves_mask_inplace(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); else do_generate_moves_mask_inplace(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } template void do_generate_moves_mask_hold(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer) const { /* If we don't use drop rules, we don't update holdings. */ if (!drop_rules) { do_generate_moves_mask_pickup(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); return; } if (!quiesc_only) { if (board->rule_flags & RF_USE_CAPTURE) do_generate_moves_mask_pickup(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); else do_generate_moves_mask_pickup(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } else { bitboard_t oking = board->royal & board->bbc[next_side[stm]]; bitboard_t king_zone; if (oking.onebit()) king_zone = bitboard_t::neighbour_board[oking.bitscan()]; if (board->rule_flags & RF_USE_CAPTURE) { do_generate_moves_mask_pickup(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); //do_generate_moves_mask_pickup(ml, board, from, king_zone, stm, allowed_prom, allowed_drop, allowed_defer); } else { do_generate_moves_mask_pickup(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); //do_generate_moves_mask_pickup(ml, board, from, king_zone, stm, allowed_prom, allowed_drop, allowed_defer); } } } template void do_generate_moves_mask_quiesc(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer) const { if (board->rule_flags & (RF_ALLOW_DROPS | RF_FORCE_DROPS)) { do_generate_moves_mask_hold(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } else do_generate_moves_mask_hold(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } inline void generate_moves_mask(movelist_t *ml, const board_t *board, bitboard_t from, bitboard_t to, side_t stm, uint32_t allowed_prom, uint32_t allowed_drop, uint32_t allowed_defer, bool quiesc_only = false) const { if (quiesc_only) do_generate_moves_mask_quiesc(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); else do_generate_moves_mask_quiesc(ml, board, from, to, stm, allowed_prom, allowed_drop, allowed_defer); } /* Generate pseudo-legal check evasions. This is mainly useful in that * it removes most illegal moves. It may not catch all of them though, * depending on rules and piece moves in a particular variant. */ bool generate_evasions(movelist_t *movelist, const board_t *board, side_t side_to_move) const { assert(board->check()); bitboard_t destination = bitboard_t::board_all; bitboard_t origin = bitboard_t::board_all; bitboard_t attacker, bb, kings, pinned, occ, destest, safe; kings = board->royal & board->bbc[side_to_move]; occ = board->get_occupied(); /* FIXME: if there are multiple kings we get duplicate moves in the * movelist. */ if (!kings.onebit()) return false; movelist->num_moves = 0; /* Identify attacking pieces */ bb = kings; while (!bb.is_empty()) { int king = bb.bitscan(); bb.reset(king); attacker |= get_all_attackers(board, occ, king); } assert(!attacker.is_empty()); attacker &= ~board->bbc[side_to_move]; /* Evasions */ if (kings.onebit()) { safe = destination; safe &= ~generate_attack_bitboard_mask(board, bitboard_t::board_empty, attacker, ~kings, next_side[side_to_move]); generate_moves_mask(movelist, board, kings, safe, side_to_move, ~0, 0, ~0); } else { bb = kings; while (!bb.is_empty()) { bitboard_t king_bb; int king = bb.bitscan(); bb.reset(king); king_bb.set(king); safe = destination; safe &= ~generate_attack_bitboard_mask(board, king_bb, attacker, ~king_bb, side_to_move); generate_moves_mask(movelist, board, king_bb, safe, side_to_move, ~0, 0, ~0); } } /* Evacuate interposing pieces acting as a cannon mount. * Collect double leapers. */ bitboard_t multi; for (int n=0; npiece_types->num_piece_types; n++) { move_flag_t atk_flags = board->piece_types->piece_capture_flags[n]; if (is_double_leaper(atk_flags) && !is_masked_leaper(atk_flags)) multi |= board->bbp[n]; if (!is_hopper(atk_flags)) continue; bb = attacker & board->bbp[n]; while (!bb.is_empty()) { int square = bb.bitscan(); bb.reset(square); bitboard_t king_bb = kings; while (!king_bb.is_empty()) { int king = king_bb.bitscan(); king_bb.reset(king); bitboard_t from_bb = bitboard_t::board_between[king][square]; generate_moves_mask(movelist, board, from_bb, ~attacker, side_to_move, ~0, ~0, ~0); } } } /* Captures of attacking pieces. Take sepecial care of multi-step * pieces, which can move again after capturing the attacker, so we * cannot mask out everything but the attacker. */ bb = multi & board->bbc[side_to_move]; if (!bb.is_empty()) generate_moves_mask(movelist, board, multi, destination, side_to_move, ~0, 0, ~0); generate_moves_mask(movelist, board, origin^kings^multi, attacker, side_to_move, ~0, 0, ~0); destest |= attacker; /* En-passant captures */ if (board->ep_victim && attacker.test(board->ep_victim)) { bitboard_t bb = board->ep; generate_moves_mask(movelist, board, origin^kings, bb, side_to_move, ~0, 0, ~0); } /* Interpose */ bb = kings; pinned = get_pinned_pieces(board, side_to_move); while (!bb.is_empty()) { int king = bb.bitscan(); bb.reset(king); bitboard_t bp = attacker; while (!bp.is_empty()) { int square = bp.bitscan(); int piece = board->get_piece(square); bp.reset(square); bitboard_t destination = bitboard_t::board_between[king][square] & ~occ; if (!destination.is_empty()) generate_moves_mask(movelist, board, origin^(kings | pinned), destination, side_to_move, ~0, ~0, ~0); else if (is_masked_leaper(board->piece_types->piece_capture_flags[piece])) { /* Block a lame leaper */ int index = get_leaper_index(board->piece_types->piece_capture_flags[piece]); destination = leaper[index][square] & ~occ; generate_moves_mask(movelist, board, origin^(kings | pinned), destination, side_to_move, ~0, ~0, ~0); } destest |= destination; } } /* Promotion to king, if promotion to king is allowed */ for (int n = 0; npiece_types->num_piece_types; n++) { if (!(board->piece_types->piece_promotion_choice[n] & board->piece_types->royal_pieces)) continue; if ((board->bbp[n] & board->bbc[side_to_move]).is_empty()) continue; bitboard_t from_mask = board->bbp[n]&(~pinned); bitboard_t to_mask = destination & (~destest | board->piece_types->promotion_zone[side_to_move][n]); if (!to_mask.is_empty() && !from_mask.is_empty()) generate_moves_mask(movelist, board, from_mask, to_mask, side_to_move, (~0) & board->piece_types->royal_pieces, 0, ~0); } return true; } /* Add gating moves to the move list. * Intended for Seirawan chess. */ void generate_gate_moves(movelist_t *movelist, const board_t *board, side_t side_to_move) const { bitboard_t king = board->royal & board->bbc[side_to_move]; bitboard_t rank = bitboard_t::board_north_edge; int n_last, n; if (side_to_move == WHITE) rank = bitboard_t::board_south_edge; /* We only care about pieces that have not yet moved */ if ((rank & board->init).is_empty()) return; /* If the holdings are empty, there is nothing to do here */ uint32_t gate_mask = 0; for (int n=0; npiece_types->num_piece_types; n++) if (board->holdings[n][side_to_move]) gate_mask |= 1< pinned = get_pinned_pieces(board, side_to_move); pinned &= rank & board->init; if (!pinned.is_empty()) { /* Filter out moves of pinned pieces that are not along the same rank: * they are illegal because they expose the king to check. */ n_last = movelist->num_moves-1; n = 0; while (nnum_moves) { move_t move = movelist->move[n]; bitboard_t from = bitboard_t::square_bitboards[get_move_from(move)]; bitboard_t to = bitboard_t::square_bitboards[get_move_to(move)]; if (!(pinned & from).is_empty() && (rank & to).is_empty()) { movelist->move[n] = movelist->move[n_last]; movelist->move[n_last] = move; movelist->num_moves--; n_last--; } n++; } } /* Go through the move list and add appropriate gating moves */ n_last = movelist->num_moves; for (n=0; nmove[n]; int from = get_move_from(base); bitboard_t bb_from = bitboard_t::square_bitboards[from]; if (!(bb_from & rank & board->init).is_empty()) { for (int n=0; npiece_types->num_piece_types; n++) { if (board->holdings[n][side_to_move] == 0) continue; int tpiece = piece_for_side(n, side_to_move); movelist->push(add_move_gate(base, tpiece, from) | MOVE_RESET50); } } if (is_castle_move(base)) { int from = get_castle_move_from2(base); bitboard_t bb_from = bitboard_t::square_bitboards[from]; for (int n=0; npiece_types->num_piece_types; n++) { if (board->holdings[n][side_to_move] == 0) continue; int tpiece = piece_for_side(n, side_to_move); movelist->push(add_move_gate(base, tpiece, from) | MOVE_RESET50); } } } } void generate_moves(movelist_t *movelist, const board_t *board, side_t side_to_move, bool quiesc_only = false, uint32_t allowed_piece_deferrals = ~0) const { bitboard_t destination = bitboard_t::board_all; bitboard_t origin = bitboard_t::board_all; /* If we are in check, then only generate moves in/to the area that can be reached by a superpiece standing * in the location of the king(s). These will be the only candidates for resolving the check, all other * moves will be pruned anyway. */ if (board->check() && !(board->rule_flags & RF_FORCE_CAPTURE)) { if (generate_evasions(movelist, board, side_to_move)) goto finalise; else { bitboard_t royal = board->royal & board->bbc[side_to_move]; assert(!royal.is_empty()); destination = generate_super_attacks_for_squares(royal, super); quiesc_only = false; } } else if (quiesc_only || (board->rule_flags & RF_FORCE_CAPTURE)) { destination = board->bbc[next_side[side_to_move]]; } movelist->num_moves = 0; generate_moves_mask(movelist, board, origin, destination, side_to_move, ~0, ~0, allowed_piece_deferrals, quiesc_only); if ((board->rule_flags & RF_FORCE_CAPTURE) && !board->ep.is_empty()) { bitboard_t origin; for (int n=0; npiece_types->num_piece_types; n++) if (board->piece_types->piece_flags[n] & PF_TAKE_EP) origin |= board->bbp[n]; origin &= board->bbc[side_to_move]; generate_moves_mask(movelist, board, origin, board->ep, side_to_move, ~0, ~0, allowed_piece_deferrals, quiesc_only); } if (movelist->num_moves == 0 && (board->rule_flags & RF_FORCE_CAPTURE) && !quiesc_only) { destination = bitboard_t::board_all ^ board->bbc[next_side[side_to_move]]; generate_moves_mask(movelist, board, origin, destination, side_to_move, ~0, ~0, allowed_piece_deferrals, quiesc_only); } if (quiesc_only) { for (int n=0; npiece_types->num_piece_types; n++) { destination = board->piece_types->promotion_zone[side_to_move][n] & ~board->bbc[next_side[side_to_move]]; origin = board->bbp[n] & board->bbc[side_to_move]; if (destination.is_empty()) continue; if (origin.is_empty()) continue; generate_moves_mask(movelist, board, origin, destination, side_to_move, ~0, 0, allowed_piece_deferrals, quiesc_only); } } finalise: if ( (board->rule_flags & RF_GATE_DROPS) && !(board->init & board->bbc[side_to_move] & (bitboard_t::board_south_edge | bitboard_t::board_north_edge)).is_empty()) { generate_gate_moves(movelist, board, side_to_move); } return; } stage_t generate_staged_moves(stage_t stage, movelist_t *movelist, const board_t *board, side_t side_to_move) const { movelist->clear(); if (stage == STAGE_DONE) return stage; side_t oside = next_side[side_to_move]; bitboard_t oking = board->royal & board->bbc[oside]; bitboard_t occ = board->get_occupied(); uint32_t defer = board->piece_types->deferral_allowed; switch (stage) { /* Normal move generation (TODO) */ case STAGE_DROP: break; case STAGE_NORMAL: break; /* Mate/Tsume search */ case STAGE_CHECKING_DROP: if (oking.onebit()) for (int n = 0; npiece_types->num_piece_types; n++) { if (board->holdings[n][side_to_move]) { move_flag_t mf = board->piece_types->piece_capture_flags[n]; bitboard_t check_mask = generate_move_bitboard_for_flags(mf, oking.bitscan(), occ, oside); generate_moves_mask(movelist, board, bitboard_t::board_empty, check_mask, side_to_move, 0, 1<piece_types->num_piece_types; n++) { if ( !(board->bbp[n] & board->bbc[side_to_move]).is_empty() ) { move_flag_t mf = board->piece_types->piece_capture_flags[n]; bitboard_t check_mask = generate_move_bitboard_for_flags(mf, oking.bitscan(), occ, oside); generate_moves_mask(movelist, board, board->bbp[n], check_mask, side_to_move, 0, 0, defer); } } break; /* Evasion generation */ case STAGE_CHECK_EVADE: generate_moves(movelist, board, side_to_move, defer != 0); break; case STAGE_DONE: break; } return next_stage[stage]; } void generate_chase_candidates(movelist_t *movelist, const board_t *board, side_t side_to_move) const { assert(board->rule_flags & RF_USE_CHASERULE); bitboard_t destination = board->bbc[next_side[side_to_move]]; bitboard_t origin = bitboard_t::board_all; bitboard_t self = bitboard_t::board_north; bitboard_t other = bitboard_t::board_north; if (side_to_move == BLACK) { self = bitboard_t::board_north; other = bitboard_t::board_south; } for (int n = 0; npiece_types->num_piece_types; n++) { if (board->piece_types->royal_pieces & (1 << n)) origin &= ~board->bbp[n]; if (board->piece_types->defensive_pieces & (1 << n)) origin &= ~board->bbp[n]; if (board->piece_types->pawn_pieces & (1 << n)) origin &= ~(board->bbp[n] & self); if (board->piece_types->pawn_pieces & (1 << n)) destination &= ~(board->bbp[n] & other); } movelist->num_moves = 0; generate_moves_mask(movelist, board, origin, destination, side_to_move, ~0, ~0, ~0); } }; #endif SjaakII/include/movelist.h000644 000765 000024 00000006520 13014616475 016537 0ustar00eglebbkstaff000000 000000 #ifndef MOVELIST_H #define MOVELIST_H #include "move.h" /* datastructure to hold the number of legal moves in a position */ #define MAX_MOVES 256 typedef struct movelist_t { move_t *move; int *score; int num_moves; int cur_move; int max_moves; movelist_t () { cur_move = num_moves = 0; max_moves = MAX_MOVES; move = (move_t *)malloc(max_moves * sizeof *move); score = (int *)malloc(max_moves * sizeof *score); } ~movelist_t() { free(move); free(score); } void clear() { cur_move = num_moves = 0; } void push(move_t m) { move[num_moves++] = m; if (num_moves >= max_moves) { max_moves += MAX_MOVES; move = (move_t *)realloc(move, max_moves * sizeof *move); score = (int *)realloc(score, max_moves * sizeof *score); } } void print() const { for (int n=0; n 0); assert(cur_move-1 < num_moves); return score[cur_move-1]; } void set_move_score(int s) { assert(cur_move > 0); assert(cur_move-1 < num_moves); score[cur_move-1] = s; } void rewind() { cur_move = 0; } move_t next_move() { move_t m = 0; if (cur_move < num_moves) { int nm = cur_move; int sv = score[nm]; int n; for (n=cur_move+1; n score[nm]) nm = n; } n = cur_move; m = move[n]; move[n] = move[nm]; move[nm] = m; sv = score[n]; score[n] = score[nm]; score[nm] = sv; m = move[cur_move]; cur_move++; } return m; } void show() { int n = cur_move; rewind(); while (move_t m = next_move()) { printf("% 3d. % 6d %s\n", cur_move, get_move_score(), move_to_string(m)); } } /* A bit of syntactic sugar: define a functor so we can call movelist() * instead of movelist.next_move(). * Then if we call it "movelist_t next_move" we can write "move = * next_move()". * Perhaps this is not quite the same as readable code... */ move_t operator()() { return next_move(); } void sort() { } /* Returns the index in the movelist of a valid move corresponding to the * given origin/destination. Intended for player interaction. * Returns -1 if no moves in the list match (ie, the requested move is * invalid). In the case of a promotion, there will be more than one move * that matches. */ int validate_move(int from, int to, int ppiece = MAX_PIECE_TYPES) const { int n; for (n=0; n::board_files*bitboard_t::board_ranks; movelist_t movelist; int src = 0, dest = 0, ppiece = 0; char *s; /* First, generate the list of moves for this position */ if (external_movelist) { movelist.clear(); for(int n = 0; nnum_moves; n++) movelist.push(external_movelist->move[n]); } else { generate_legal_moves(&movelist); } while (isspace(*move_str) && *move_str) move_str++; char ms[16]; snprintf(ms, sizeof(ms), "%s", move_str); s = strstr(ms, "="); if (s) { while (*s) { *s = *(s+1); s++; } } for (int n = 0; n. */ int add_piece_type(move_flag_t move_flags, move_flag_t capture_flags, piece_flag_t piece_flags, const bitboard_t promotion_zone[2], const char *promotion_string, const char *name, const char *symbol, const char *notation, int value = 0) { if (pt.num_piece_types>=MAX_PIECE_TYPES) return -1; if (!promotion_string) promotion_string = ""; if (!name) name = ""; if (!symbol) symbol = ""; if (!notation) notation = ""; int n = pt.num_piece_types; free(pt.piece_name[n]); free(pt.piece_abbreviation[n][WHITE]); free(pt.piece_notation[n]); free(pt.demotion_string[n]); memset(pt.promotion[n], 0, sizeof pt.promotion[n]); pt.piece_move_flags[n] = move_flags; pt.piece_capture_flags[n] = capture_flags; pt.piece_flags[n] = piece_flags; pt.special_zone[WHITE][n] = bitboard_t::board_empty; pt.special_zone[BLACK][n] = bitboard_t::board_empty; pt.promotion[n][0].zone[WHITE] = promotion_zone[WHITE]; pt.promotion[n][0].zone[BLACK] = promotion_zone[BLACK]; pt.promotion[n][0].string = strdup(promotion_string); pt.promotion[n][0].choice = 0; pt.promotion_zone[WHITE][n].clear(); pt.promotion_zone[BLACK][n].clear(); pt.optional_promotion_zone[WHITE][n].clear(); pt.optional_promotion_zone[BLACK][n].clear(); pt.entry_promotion_zone[WHITE][n] = bitboard_t::board_all; pt.entry_promotion_zone[BLACK][n] = bitboard_t::board_all; pt.prison[WHITE][n] = bitboard_t::board_all; pt.prison[BLACK][n] = bitboard_t::board_all; pt.block[WHITE][n] = bitboard_t::board_empty; pt.block[BLACK][n] = bitboard_t::board_empty; pt.drop_zone[WHITE][n] = bitboard_t::board_all; pt.drop_zone[BLACK][n] = bitboard_t::board_all; pt.piece_name[n] = strdup(name); pt.piece_notation[n] = strdup(notation); pt.demotion_string[n] = NULL; pt.allowed_victim[n] = NULL; pt.piece_value[n] = value; pt.demotion[n] = n; pt.piece_promotion_choice[n] = 0; pt.piece_allowed_victims[n] = ~0; pt.pzset[n] = false; pt.piece_drop_file_maximum[n] = 1; if (piece_flags & PF_ROYAL) { pt.piece_maximum[n][WHITE] = 1; pt.piece_maximum[n][BLACK] = 1; } else { pt.piece_maximum[n][WHITE] = 128; pt.piece_maximum[n][BLACK] = 128; } pt.piece_abbreviation[n][WHITE] = strdup(symbol); char *p = strchr(pt.piece_abbreviation[n][WHITE], ','); if (p) { p[0] = '\0'; p++; } else { p = pt.piece_abbreviation[n][WHITE] + strlen(pt.piece_abbreviation[n][WHITE]); } pt.piece_abbreviation[n][BLACK] = p; pt.num_piece_types++; piece_symbol_string[n ] = notation[0]; piece_symbol_string[n+1] = '\0'; piece_psymbol_string[n ] = notation[1]; piece_psymbol_string[n+1] = '\0'; piece_drop_string[n ] = toupper(pt.piece_abbreviation[n][WHITE][0]); piece_drop_string[n+1] = '\0'; return n; } bool add_special_move(const char *symbol, const bitboard_t *zone, move_flag_t move_flags) { int n; if (!symbol) return false; for (n=0; n zone, move_flag_t move_flags) { bitboard_t bz[2] = { zone, zone }; return add_special_move(symbol, bz, move_flags); } void set_maximum_number_of_kings(side_t side, int count) { for (int n=0; n bb = movegen.castle_mask[n][side]; bb.reset(square); assert(bb.onebit()); pt.castle_piece[side][n] = board.get_piece(bb.bitscan()); } board.clear(); } void deduce_castle_flags(side_t side, int king_from, int king_to, int rook_from) { movegen.deduce_castle_flags(side, king_from, king_to, rook_from); } void identify_promotion_options() { char buffer[3]; for (int n = 0; n. */ #ifndef PIECESTYPES_H #define PIECESTYPES_H #include #include #include "bitboard.h" #include "moveflag.h" #include "pieces.h" #include "eval_types.h" /* Piece flags */ #define PF_ROYAL 0x00000001 /* Piece is royal */ #define PF_CASTLE 0x00000002 /* Piece may castle */ #define PF_SET_EP 0x00000004 /* Piece sets "en-passant" square when doing a special move */ #define PF_TAKE_EP 0x00000008 /* Piece may capture en-passant */ #define PF_DROPNOCHECK 0x00000010 /* Piece may not be dropped if it gives check */ #define PF_DROPNOMATE 0x00000020 /* Piece may not be dropped if it gives mate */ #define PF_DROPONEFILE 0x00000040 /* Piece may not be dropped if another piece of this type is on the same file */ #define PF_DROPDEAD 0x00000080 /* Piece may be dropped where it has no moves */ #define PF_CANTMATE 0x00000100 /* This piece, by itself, cannot mate */ #define PF_COLOURBOUND 0x00000200 /* This piece is colour-bound */ #define PF_PAIRBONUS 0x00000400 /* Piece gets a pair bonus for similar pieces on opposite colour */ #define PF_NORET 0x00000800 /* Piece cannot return to its original position */ #define PF_NOMATE 0x00001000 /* Piece may not give mate (it may give check) */ #define PF_SHAK 0x00002000 #define PF_CAPTUREFLAG 0x00004000 /* This piece is allowed to capture the flag */ #define PF_ASSIMILATE 0x00008000 /* This piece assimilates a cpaturing piece */ #define PF_NO_RETALIATE 0x00010000 /* A piece with this property may not be recaptured. */ #define PF_ENDANGERED 0x00020000 /* A piece with this property may only capture unprotected pieces with this property */ #define PF_IRON 0x00040000 /* A piece with this property may not be captured */ #define PF_PROMOTEWILD 0x00080000 /* A piece with this property can promote anywhere if it is the last of its kind */ typedef uint32_t piece_flag_t; struct value_comparator_t { int sort_value[256]; bool operator()(int a, int b) const { return sort_value[a] < sort_value[b]; } }; #define MAX_PZ 16 template struct promotion_zone_t { bitboard_t zone[NUM_SIDES]; const char * string; piece_bit_t choice; }; template struct piece_description_t { /* Define the properties of all piece types. * We don't use a struct for a piece type so that we can keep properties of different pieces more closely * packed, which is better for cache performance. * The index in the arrays here corresponds to the index in the board piece list. * * Basic entries: * move_flags (32 bits) * capture_flags (32 bits) * flags (32 bits) * name (string) * abbreviation (string) * notation (string) * For evaluation: * piece_value (16 bit) * centre_weight (8 bit) * advance_weight (8 bit) * shield_weight (8 bit) * mobility_score (8 bit) */ promotion_zone_t promotion[MAX_PIECE_TYPES][MAX_PZ]; bitboard_t promotion_zone[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t optional_promotion_zone[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t special_zone[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t prison[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t block[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t drop_zone[NUM_SIDES][MAX_PIECE_TYPES]; bitboard_t entry_promotion_zone[NUM_SIDES][MAX_PIECE_TYPES]; int num_piece_types; int pawn_steps[NUM_SIDES]; /* The number of moves a pawn has, a factor in pawn mobility and passer eval */ int8_t pawn_index[NUM_SIDES]; /* The piece type that will be evaluated as a pawn */ int8_t castle_piece[NUM_SIDES][NUM_CASTLE_MOVES];/* The piece type that will castle with the king, nominally "rook" */ move_flag_t piece_move_flags[MAX_PIECE_TYPES]; move_flag_t piece_capture_flags[MAX_PIECE_TYPES]; move_flag_t piece_special_move_flags[MAX_PIECE_TYPES]; move_flag_t piece_initial_move_flags[MAX_PIECE_TYPES]; piece_bit_t piece_promotion_choice[MAX_PIECE_TYPES]; piece_bit_t piece_allowed_victims[MAX_PIECE_TYPES]; piece_flag_t piece_flags[MAX_PIECE_TYPES]; int8_t demotion[MAX_PIECE_TYPES]; uint8_t piece_maximum[MAX_PIECE_TYPES][NUM_SIDES]; uint8_t piece_drop_file_maximum[MAX_PIECE_TYPES]; bool pieces_can_win[MAX_PIECE_TYPES][MAX_PIECE_TYPES]; int val_perm[MAX_PIECE_TYPES]; int16_t piece_value[MAX_PIECE_TYPES]; int16_t see_piece_value[MAX_PIECE_TYPES]; int16_t piece_promotion_value[MAX_PIECE_TYPES]; bitboard_t passer_mask[NUM_SIDES][8*sizeof(kind)]; bitboard_t weak_mask[NUM_SIDES][8*sizeof(kind)]; bitboard_t front_span[NUM_SIDES][8*sizeof(kind)]; /* Piece classifications */ piece_bit_t royal_pieces; piece_bit_t defensive_pieces; piece_bit_t pawn_pieces; piece_bit_t minor_pieces; piece_bit_t major_pieces; piece_bit_t super_pieces; piece_bit_t shak_pieces; piece_bit_t deferral_allowed; eval_pair_t eval_value[MAX_PIECE_TYPES]; eval_pair_t eval_mobility[MAX_PIECE_TYPES][8*sizeof(kind)]; eval_pair_t eval_pair_bonus[MAX_PIECE_TYPES]; eval_pair_t eval_pst[MAX_PIECE_TYPES][8*sizeof(kind)]; int king_safety_weight[MAX_PIECE_TYPES]; int phase_weight[MAX_PIECE_TYPES]; int phase_scale; int avg_moves[MAX_PIECE_TYPES]; int max_moves[MAX_PIECE_TYPES]; int min_moves[MAX_PIECE_TYPES]; int8_t tropism[MAX_PIECE_TYPES][8*sizeof(kind)][8*sizeof(kind)]; int8_t mobility_score[MAX_PIECE_TYPES][8*sizeof(kind)]; char * piece_name[MAX_PIECE_TYPES]; char * piece_abbreviation[MAX_PIECE_TYPES][NUM_SIDES]; char * piece_notation[MAX_PIECE_TYPES]; char * demotion_string[MAX_PIECE_TYPES]; char * allowed_victim[MAX_PIECE_TYPES]; bool pzset[MAX_PIECE_TYPES]; void sort_piece_values() { value_comparator_t compare; for (int n=0; n\n", min_moves[n], max_moves[n], avg_moves[n]); if (piece_flags[n] & PF_ROYAL) printf(" Royal\n"); if (piece_flags[n] & PF_PAIRBONUS) printf(" Pair bonus\n"); if (piece_flags[n] & PF_NORET) printf(" No return\n"); if (piece_flags[n] & PF_CANTMATE) printf(" Cannot deliver mate alone\n"); if (mate_helpers) { printf(" Can deliver mate with help of "); while (mate_helpers) { int n = bitscan32(mate_helpers); mate_helpers ^= (1<board.rule_flags & RF_KING_TRAPPED) // printf("Trapped kings\n"); } }; #endif SjaakII/include/pieces.h000644 000765 000024 00000003736 13014616475 016153 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PIECES_H #define PIECES_H #include #include "moveflag.h" #define MAX_PIECE_TYPES 32 //16 #define PIECE_BITS 5 #define MAX_STEPPER_TYPES 16 #define MAX_LEAPER_TYPES 16 #define MAX_RIDER_TYPES 16 typedef uint32_t piece_bit_t; /* Bitfield colours */ typedef enum side_t { NONE=-1, WHITE, BLACK, NUM_SIDES } side_t; static const side_t next_side[NUM_SIDES] = { BLACK, WHITE }; /* Castle move options */ enum { SHORT, LONG, NUM_CASTLE_MOVES }; #ifdef __cplusplus inline side_t& operator++(side_t& side, int) { assert(side >= WHITE); assert(side <= BLACK); const int i = static_cast(side)+1; side = static_cast(i); return side; } #endif /* For the generalised description of moves, we need to pack the piece type and the colour bit into one bit * field. This feels a bit like a hack... */ static inline uint8_t piece_for_side(int piece, side_t side) { return piece | (side << PIECE_BITS); } static inline side_t side_for_piece(int piece) { #ifdef __cplusplus side_t side = static_cast((piece >> PIECE_BITS) & 0x01); return side; #else return (piece >> PIECE_BITS) & 0x01; #endif } static inline uint8_t neutral_piece(int piece) { return piece & ((1 << PIECE_BITS)-1); } #endif SjaakII/include/pipe2.h000644 000765 000024 00000001014 13014616475 015705 0ustar00eglebbkstaff000000 000000 #ifndef PIPE2_H #define PIPE2_H #include #include #include typedef struct { FILE *in; /* Input stream for parent */ FILE *out; /* Output stream for parent */ int in_fd, out_fd; pid_t pid; /* pid of child process */ } pipe2_t; pipe2_t *p2open(const char *cmd, char *const argv[]); int p2close(pipe2_t *pipe); char *p2gets(char *s, size_t n, pipe2_t *pipe); bool p2_input_waiting(pipe2_t *pipe); size_t p2write(pipe2_t *pipe, const void *ptr, size_t size); #endif SjaakII/include/prefetch.h000644 000765 000024 00000000032 12452217646 016467 0ustar00eglebbkstaff000000 000000 #include "compilerdef.h" SjaakII/include/pst.h000644 000765 000024 00000001075 12433153270 015474 0ustar00eglebbkstaff000000 000000 #ifndef PST_H #define PST_H extern int psq_map[NUM_SIDES][128]; /* Base centralisation score for each square */ extern int centre_table[128]; /* Base advancement score for each square */ extern int advance_table[128]; /* Extra advancement score for proximity to promotion zone, for pawns */ extern int promo_table[128]; /* End game bonus for driving the defending king to the edge */ extern int lone_king_table[128]; /* Base shield score, for pawn shields */ extern int shield_table[128]; extern void initialise_base_evaluation_tables(int files, int ranks); #endif SjaakII/include/san.h000644 000765 000024 00000000250 13014616475 015450 0ustar00eglebbkstaff000000 000000 #include "move.h" #include "movelist.h" const char *move_to_short_string(move_t move, const movelist_t *movelist = NULL, char *buffer = NULL, bool san_castle = true); SjaakII/include/score.h000644 000765 000024 00000001724 12433153270 016002 0ustar00eglebbkstaff000000 000000 #ifndef SCORE_H #define SCORE_H #define LEGALWIN 16000 #define LEGALLOSS (-LEGALWIN) #define LEGALDRAW 0 #define ILLEGAL (LEGALWIN+500) static inline bool is_mate_score(int score) { static const int mate = LEGALWIN - 1000; if ( score > mate || score < -mate ) return true; return false; } static inline int score_to_hashtable(int score, int depth) { if (is_mate_score(score)) { assert( (score>0 && score + depth <= LEGALWIN) || (score<0 && score - depth >= -LEGALWIN) ); if (score > 0) return score + depth; else return score - depth; } return score; } static inline int score_from_hashtable(int score, int depth) { if (is_mate_score(score)) { assert( (score>0 && (score - depth <= LEGALWIN)) || (score<0 && (score + depth >= -LEGALWIN)) ); if (score > 0) return score - depth; else return score + depth; } return score; } #endif SjaakII/include/search.h000644 000765 000024 00000141314 13017310267 016134 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "chase.h" #define iter if (output_iteration) output_iteration #define uci if (uci_output) uci_output #define xb if (xboard_output) xboard_output #define check_clock() \ {\ if (clock.max_nodes && (clock.nodes_searched >= clock.max_nodes)) abort_search |= 1;\ if (check_keyboard && check_keyboard(this)) abort_search |= 1;\ if (clock.check_clock && ((clock.nodes_searched & clock_nodes)==0))\ abort_search |= clock.check_clock(&clock);\ }(void)0 #include "mate.h" void dump_moves_since_root(void) { for (int n=clock.root_moves_played; n king = board.bbc[board.side_to_move] & board.royal; assert(!king.is_empty()); bitboard_t atk = movegen.get_all_attackers(&board, bitboard_t::board_all, king.bitscan()); for (int n=0; n king = board.bbc[board.side_to_move] & board.royal; assert(!king.is_empty()); bitboard_t atk = movegen.get_all_attackers(&board, bitboard_t::board_all, king.bitscan()); for (int n=0; n flag = board.flag[side]; bitboard_t bb = flag & board.bbc[side]; if (bb.is_empty()) return false;; /* Filter out pieces that are not allowed to capture the flag */ for (int n=0; n flag = board.flag[side]; bitboard_t bb = flag & board.bbc[side]; if (bb.is_empty()) continue; /* Filter out pieces that are not allowed to capture the flag */ for (int n=0; n=0; n-=2) { if ((board.rule_flags & RF_USE_CAPTURE) == 0 && is_irreversible_move(move_list[n+1])) return count; if ((board.rule_flags & RF_USE_CAPTURE) == 0 && is_irreversible_move(move_list[n])) return count; count += (ui[n].hash == board.hash); } return count; } inline bool position_repeated() { int rep_in_game = 0; int count = 0; if (repetition_hash_table[board.hash&0xFFFF]>=2) { int n; for (n=(int)moves_played-2; n>=0; n-=2) { if ((board.rule_flags & RF_USE_CAPTURE) == 0 && is_irreversible_move(move_list[n])) break; if ((board.rule_flags & RF_USE_CAPTURE) == 0 && is_irreversible_move(move_list[n+1])) break; if (ui[n].hash == board.hash) { count++; rep_in_game += (n <= clock.root_moves_played); } } } return count && count >= rep_in_game; } inline bool board_position_repeated_loss() { if (board_repetition_hash_table[board.board_hash&0xFFFF]>=2) { const side_t me = board.side_to_move; bool pure_loss = true; bool pure_gain = true; int8_t holdings[MAX_PIECE_TYPES][NUM_SIDES]; memset(holdings, 0, sizeof holdings); for (int n=(int)moves_played-1; n>=0; n--) { move_t move = move_list[n]; if ((board.rule_flags & RF_USE_CAPTURE) == 0 && is_irreversible_move(move)) break; if (get_move_holdings(move)) { uint16_t p = get_move_holding(move); int count = decode_holding_count(p); int piece = decode_holding_piece(p); side_t side = decode_holding_side(p); holdings[piece][side] += count; } if (ui[n].board_hash == board.board_hash && ui[n].hash != board.hash) { for (int piece = 0; piece 0) pure_loss = false; } if (pure_gain) return false; return pure_loss; } } } return false; } inline bool material_draw() { if (board.rule_flags & RF_CAPTURE_THE_FLAG) return false; if (lone_king(WHITE) && lone_king(BLACK)) return true; /* TODO */ return false; } inline bool lone_king(side_t side) { return (board.royal & board.bbc[side]) == board.bbc[side] && board.bbc[side].onebit(); } inline bool zugzwang_threat() { for (side_t side = WHITE; side < NUM_SIDES; side++) { /* If one side has a lone king, there is a zugzwang threat */ if (lone_king(side)) return true; } int tempo_movers = 0; /* If the side to move only has pieces that cannot make a tempo move, * ditto. * FIXME: currently, we only detect the presence of pieces that cannot * return. The correct zugzwang threat detection needs to know if a * piece can return to its original square in an odd number of steps. */ for (int n=0; nnum_piece_types; n++) { if (!(board.bbc[board.side_to_move] & board.bbp[n]).is_empty()) { if (!(board.piece_types->piece_flags[n] & PF_NORET)) tempo_movers++; } } return tempo_movers <= 1; } bool is_pawn_push(move_t move, side_t side) { if (pt.pawn_pieces & (1 << board.get_piece(get_move_from(move)))) { if (is_drop_move(move)) return false; if (bitboard_t::board_homeland[next_side[side]].test(get_move_from(move))) return true; } return false; } bool is_recapture_move(move_t move, move_t prev_move) { return is_capture_move(move) && is_capture_move(prev_move) && get_move_to(move) == get_move_to(prev_move); } void score_moves(int depth, move_t hash_move, move_t prev_move, move_t threat_move) { const side_t me = board.side_to_move; for (int n = 0; n 2 && is_killer(depth-2, move)) { movelist[depth].score[n] += 900; if (move == killer[depth-2][0]) movelist[depth].score[n] += 50; } else if (is_killer(depth+2, move)) { movelist[depth].score[n] += 800; if (move == killer[depth+2][0]) movelist[depth].score[n] += 50; } else if (is_counter(depth, prev_move, move)) { movelist[depth].score[n] += 1500; } else if (is_castle_move(move)) { movelist[depth].score[n] += 600; } else if (is_capture_move(move)) { int s = see(move); if (s > 0) movelist[depth].score[n] += 2400 + s / 10; else movelist[depth].score[n] += s; if (threat_move && !is_drop_move(threat_move) && get_move_to(move) == get_move_from(threat_move) && s < 0) movelist[depth].score[n] += 1000; } else if (is_pickup_move(move)) { if (threat_move && get_move_to(threat_move) == get_move_from(move)) { movelist[depth].score[n] += 1500; } else { movelist[depth].score[n] += 0; } } else if (is_drop_move(move)) { int history_score = get_move_history_score(move); int history_scale = get_move_history_scale(move); int s = see(move); if (s > 0) { int h = history_scale ? 100 * history_score / history_scale : 100; if (!(board.bbc[me] & board.royal & bitboard_t::neighbour_board[get_move_to(move)]).is_empty()) movelist[depth].score[n] += 1200 + h; else if (bitboard_t::board_homeland[next_side[me]].test(get_move_to(move))) movelist[depth].score[n] += 1100 + h; movelist[depth].score[n] += s / 10; } else { int h = history_scale ? 500 * history_score / history_scale : 500; movelist[depth].score[n] += s + h; } } else if (max_history && !board.check() && !is_promotion_move(move) && !is_capture_move(move)) { int history_score = get_move_history_score(move); int history_scale = get_move_history_scale(move); if (history_scale) { int s = 500 * history_score / history_scale; if (s) { movelist[depth].score[n] = 0 + s; } else { s = see(move); movelist[depth].score[n] += s; } } if (threat_move && get_move_from(move) == get_move_to(threat_move)) movelist[depth].score[n] += 500; } else { int s = see(move); movelist[depth].score[n] += s; if (threat_move && get_move_from(move) == get_move_to(threat_move)) movelist[depth].score[n] += 500; } if (is_gate_move(move)) movelist[depth].score[n] += 200; if (depth < 3 && clock.root_moves_played < 10 && random_ok) { movelist[depth].score[n] += genrandui() & 0xff; } } } int qsearch(int alpha, int beta, int draft, int depth) { int score = -LEGALWIN; side_t me = board.side_to_move; truncate_principle_variation(depth); /* Check whether our search time for this move has expired */ check_clock(); if (abort_search) return 0; if (!board.check() && (board.rule_flags & RF_FORCE_CAPTURE) && (draft < -2)) { return static_qsearch(beta, depth+1); } if ((board.rule_flags & RF_ALLOW_PICKUP) && (draft < -2)) return static_qsearch(beta, depth+1); if (depth >= MAX_TOTAL_DEPTH) return static_qsearch(beta, depth+1); int static_score = 0; if (!board.check()) { static_score = static_evaluation(me, alpha, beta); /* Stand-pat cut-off */ if (static_score >= beta) return static_score; if (static_score > alpha) alpha = static_score; } /* Generate moves */ movelist[depth].clear(); movegen.generate_moves(&movelist[depth], &board, me, true, pt.deferral_allowed); if (movelist[depth].num_moves == 0) return static_score; /* Store the previous move: this is used to change move ordering on this * move. If there is no previous move, then this move will be null. */ assert(moves_played > 0); move_t prev_move = move_list[moves_played-1]; /* Score and sort the moves */ for (int n = 0; n best_score) { best_score2 = best_score; best_score = score; } if (score > alpha) { /* New best line */ alpha = score; backup_principle_variation(depth, move); } } if (board.rule_flags & RF_ALLOW_PICKUP) best_score = best_score2; if (best_score == -LEGALWIN) best_score = static_score; if (board.check() && legal_moves == 0) { best_score = (mate_score + depth); move_t prev_move = move_list[moves_played-1]; /* Make sure mate wasn't caused by an illegal drop */ if (board.check() && is_drop_move(prev_move)) { int p = board.get_piece(get_move_to(prev_move)); if (pt.piece_flags[p] & PF_DROPNOMATE) return ILLEGAL; } /* Test if the mate is legal, that is to say: * - The checking piece is allowed to deliver mate * - The mate follows a sequence of checks at least one of which * makes the mate OK (is classified as "shak"). * The first is illegal, the second is only a draw. */ if (board.check()) { if (!is_valid_mate()) return ILLEGAL; if (board.rule_flags & RF_USE_SHAKMATE) { if (!board.have_shak()) best_score = 0; } } /* Verify that all moves during QS were check moves, so we know that * all evasions were examined. */ int c = 2; for (int n=draft+2; n<=0; n+=2) { if ((ui[moves_played-c].board_flags & BF_CHECK) == 0) best_score = static_score; c+=2; } } return best_score; } /* Idea taken from Senpai */ int static_qsearch(int beta, int depth) { int static_score = static_evaluation(board.side_to_move); int best_score = static_score; if (static_score >= beta) return static_score; movelist[depth].clear(); movegen.generate_moves(&movelist[depth], &board, board.side_to_move, true, pt.deferral_allowed); bitboard_t cdone; for (int n=0; n 0) // value += pt.piece_value[get_move_promotion_piece(move)] - pt.piece_value[board.get_piece(get_move_to(prev_move))]; int score = static_score + value; if (score > best_score) { best_score = score; if (best_score >= beta) break; } } return best_score; } int get_extension(move_t /* move */, int move_score) { /* Check extension: only for safe checks. * TODO: exempt dicovered checks */ return board.check() && move_score >= 0;//see(move) >= 0; } int get_reduction(move_t move, int move_score, int /* move_count */, int draft) { int r = 0; if (move_score > 1500) return 0; r = 1; if (is_drop_move(move) && move_score < 1000) r++; if (is_pickup_move(move) && move_score < 1000) r++; if (r && draft > 4) r += draft / 4; return r; } int search(int alpha, int beta, int draft, int depth) { assert(beta > alpha); assert(depth >= 0); int best_score = -LEGALWIN; int score = -LEGALWIN; int moves_searched = 0; const side_t me = board.side_to_move; move_t prev_move = 0; move_t move = 0; truncate_principle_variation(depth); /* Check whether our search time for this move has expired */ check_clock(); if (abort_search) return 0; /* Check whether we're looking for a checkmate. * If so, prune branches that are worse than the best mate found so far * (mate distance pruning). */ alpha = std::max(alpha, -LEGALWIN + depth); beta = std::min(beta, LEGALWIN - depth - 1); if (alpha >= beta) return alpha; assert(alpha >= -LEGALWIN); assert(beta <= LEGALWIN); if (flags_are_captured()) return flag_score + depth; /* We still need to return a move at the root, even if the * heuristic says the current position is a repetition draw! * If it were, we would not get here. */ if (depth > 0) { if (fifty_limit && board.fifty_counter >= fifty_limit) { if (!board.check()) /* defer the 50-move claim when in check, in case it's mate */ return LEGALDRAW; } if (position_repeated()) { if (board.rule_flags & RF_USE_CHASERULE) { if (depth > 1) switch (test_chase()) { case NO_CHASE: break; case DRAW_CHASE: return LEGALDRAW; case WIN_CHASE: if (beta == alpha+1) return ILLEGAL; break; case LOSE_CHASE: return -ILLEGAL; break; } } else { if (board.check()) { if (perpetual == LEGALDRAW) return -perpetual; if (perpetual == -ILLEGAL) return -perpetual; return -(perpetual + depth); } if (rep_score == LEGALDRAW) return rep_score; /* Allow check evasions to repeat */ if (rep_score == -ILLEGAL) { if ((ui[moves_played-1].board_flags & BF_CHECK) == 0) /* HACK: allow check evasion */ return -rep_score; } else { return -(rep_score + depth); } } } else if ((board.rule_flags & RF_USE_CAPTURE) && board_position_repeated_loss() && depth) { return alpha-1; } if (material_draw()) return LEGALDRAW; if (board.rule_flags & RF_USE_BARERULE) { if (lone_king(next_side[me])) { /* We know that not both sides have a bare king (otherwise it * would already have been declared a legal draw) and it is our * turn, so if the enemy could have captured our last piece he * clearly failed to do so - in other words, we have won. */ return -bare_king_score; } } if (check_limit && board.check_count[board.side_to_move] >= check_limit) return (check_score == LEGALDRAW) ? check_score : check_score + depth; } /* Test if the last move was an illegal promotion */ if ((board.rule_flags & RF_QUIET_PROMOTION) && moves_played > 0 && is_promotion_move(move_list[moves_played-1]) ) { if (board.check()) return ILLEGAL; int square = get_move_to(move_list[moves_played-1]); int p = board.get_piece(square); bitboard_t atk = movegen.generate_move_bitboard_for_flags(pt.piece_capture_flags[p], square, board.get_occupied(), next_side[board.side_to_move]); if (!(atk & board.bbc[board.side_to_move]).is_empty()) return ILLEGAL; } //if ( (depth == 0 || depth == 1 || draft > 8 || is_mate_score(beta)) && (board.rule_flags & RF_USE_DROPS)) { bool mate_search = (depth == 1) || (depth == 0 && best_move[0] != 0) || (draft > 8); if ( mate_search && ((option_ms >= MATE_SEARCH_ENABLED) || ((board.rule_flags & RF_USE_DROPS) && option_ms >= MATE_SEARCH_ENABLE_DROP)) ) { int xply = (depth < 2) ? draft / 4 : 0; int mply = (depth == 0 && best_move[0] == 0) ? 1 : (3 + 2*xply); int score = msearch(alpha, beta, mply, depth); if (is_mate_score(score)) return score; } if (draft <= 0 || depth > MAX_SEARCH_DEPTH) return qsearch(alpha, beta, 0, depth); /* Test for transposition table cut-offs */ int hash_depth = 0, hash_score = 0; unsigned int hash_flag = 0; move_t hash_move = 0; bool have_hash = retrieve_table(transposition_table, board.hash, &hash_depth, &hash_score, &hash_flag, &hash_move); bool hash_ok = hash_depth >= draft || is_mate_score(hash_score); if (have_hash && hash_ok && (fifty_limit == 0 || board.fifty_counter < fifty_scale_limit) && depth > 0) { hash_score = score_from_hashtable(hash_score, depth); //positions_in_hashtable++; //bool exact_ok = (depth > 1) || (beta == alpha+1); bool exact_ok = (beta == alpha+1) || (hash_score <= alpha) || hash_score >= beta; // FIXME: use crafty-like PV table if ((hash_flag & HASH_TYPE_EXACT) && exact_ok) { if (hash_score > alpha) backup_principle_variation(depth, hash_move); return hash_score; } else if ((hash_flag & (HASH_TYPE_UPPER|HASH_TYPE_EXACT)) && (hash_score<=alpha)) { return hash_score; } else if ((hash_flag & (HASH_TYPE_LOWER|HASH_TYPE_EXACT)) && (hash_score>=beta)) { return hash_score; } } /* Store the previous move: this is used to change move ordering on this * move. If there is no previous move, then this move will be null. */ if (moves_played > 0) prev_move = move_list[moves_played-1]; /* Pre-conditions for particular pruning options. */ bool razor = draft <= 3 && !is_mate_score(beta) && !board.check(); bool avoid_null = beta > alpha+1 || board.check() || prev_move == 0 || zugzwang_threat() || is_mate_score(beta); /* Calculate static evaluation if needed */ bool need_static = razor || !avoid_null; int static_score = 0; if (need_static) { static_score = static_evaluation(me, alpha, beta); avoid_null = avoid_null || static_score < beta; } /* Razoring */ if (razor) { assert(need_static); int score = static_score - draft * draft * 50; if (score >= beta) return score; } /* Null-move */ move_t threat_move = 0; if (!avoid_null) { assert(need_static); assert(!player_in_check(me)); int r = 2 + draft / 4; int score; playmove(0); clock.nodes_searched++; if (draft - r < 1) score = -static_qsearch(-(beta-1), depth+1); else score = -search(-beta, -(beta-1), draft - 1 -r, depth+1); takeback(); if (score >= beta && !is_mate_score(score) && !abort_search) {// && draft >= 3) { if (abs(score) < LEGALWIN) store_table_entry(transposition_table, board.hash, draft - 1 - r, score_to_hashtable(score, depth), HASH_TYPE_LOWER, 0); return score; } threat_move = best_move[depth+1]; if (!is_capture_move(threat_move) && !is_mate_score(score)) threat_move = 0; } /* Internal iterative deepening */ if (hash_move == 0 && beta>alpha+1 && draft > 3) { int score = search(alpha, beta, draft-2, depth); if (score > alpha) hash_move = best_move[depth]; } /* Generate all moves */ movelist[depth].clear(); movegen.generate_moves(&movelist[depth], &board, me, false, pt.deferral_allowed); int legal_moves = movelist[depth].num_moves; /* No moves generated at all? */ if (legal_moves == 0) goto no_moves; /* Score and sort the moves */ score_moves(depth, hash_move, prev_move, threat_move); /* Search the first move */ hash_flag = HASH_TYPE_UPPER; while ((move = movelist[depth].next_move())) { /* Multi-pv mode */ if (multipv > 1 && depth == 0 && exclude.contains(move)) continue; int move_score = movelist[depth].get_move_score(); playmove(move); if (player_in_check(me)) { /* Illegal move */ legal_moves--; takeback(); continue; } clock.nodes_searched++; board.check(movegen.was_checking_move(&board, board.side_to_move, move)); if ((board.rule_flags & RF_USE_SHAKMATE) && board.check()) test_shak(); int e = get_extension(move, move_score); score = -search(-beta, -alpha, draft-1 + e, depth+1); if (score < LEGALLOSS) { /* Illegal move, but requires search to identify */ legal_moves--; takeback(); continue; } if (score > alpha && !abort_search) { /* New best line found */ hash_flag = HASH_TYPE_EXACT; alpha = score; backup_principle_variation(depth, move); } takeback(); moves_searched++; break; } /* Search all other moves, until we find a cut-off */ best_score = score; hash_move = move; while (alpha < beta && (move = movelist[depth].next_move())) { if (multipv > 1 && depth == 0 && exclude.contains(move)) continue; bool in_check = board.check(); int move_score = movelist[depth].get_move_score(); /* Prune unsafe drops near the tips */ if (is_drop_move(move)) { if (draft < 3 && move_score < 500 && !in_check) continue; //if (draft < 2 && !in_check && !is_mate_score(beta)) continue; if (depth == 0 && draft == 1 && moves_searched) continue; } if (draft < 3 && is_pickup_move(move)) continue; playmove(move); if (player_in_check(me)) { legal_moves--; takeback(); continue; } board.check(movegen.was_checking_move(&board, board.side_to_move, move)); if ((board.rule_flags & RF_USE_SHAKMATE) && board.check()) test_shak(); clock.nodes_searched++; /* Futility pruning of drop moves in frontier nodes */ if (draft == 1 && is_drop_move(move) && !in_check && !board.check()) { takeback(); continue; } bool avoid_reduction = in_check || board.check() || is_capture_move(move) || is_promotion_move(move) || is_castle_move(move) || is_pawn_push(move, me); int e = get_extension(move, move_score); int r = (e || avoid_reduction) ? 0 : get_reduction(move, move_score, moves_searched, draft); if (depth < 2) { score = -search(-beta, -alpha, draft-1 + e-r, depth+1); if (score > alpha && score < beta && r) score = -search(-beta, -alpha, draft-1 + e, depth+1); } else { score = -search(-(alpha+1), -alpha, draft-1 + e-r, depth+1); if (score < LEGALLOSS) { /* Illegal move, but requires search to identify */ legal_moves--; takeback(); continue; } if (score > alpha && ((beta > alpha+1) || r)) score = -search(-beta, -alpha, draft-1 + e, depth+1); } takeback(); moves_searched++; if (abort_search) return 0; if (score > best_score) { best_score = score; if (score > alpha) { /* New best line */ hash_flag = HASH_TYPE_EXACT; alpha = score; hash_move = move; backup_principle_variation(depth, move); } } } if (alpha >= beta) { /* Beta cutoff */ /* Store good moves in the killer slots, but only if: * - we were not in check at the beginning of this move * - the move is not a promotion (already high in the list) * - the move was not a capture (already high in the list) */ store_killer(hash_move, depth); store_mate_killer(hash_move, depth, best_score); store_counter_move(depth, prev_move, hash_move); update_history(move, draft * draft); if (prev_move == 0) store_null_killer(depth, hash_move); hash_flag = HASH_TYPE_LOWER; branches_pruned++; /* Update history for all other moves that have failed to produce a * cut-off, rather than doing this for all moves that do not improve * alpha. * Doing this here avoids polluting the history tables in ALL-nodes. */ for (int n=0; n 0); hash_flag = HASH_TYPE_EXACT; hash_move = 0; best_score = board.check() ? (mate_score + depth) : (stale_score + depth); /* Make sure mate wasn't caused by an illegal drop */ if (board.check() && is_drop_move(prev_move)) { int p = board.get_piece(get_move_to(prev_move)); if (pt.piece_flags[p] & PF_DROPNOMATE) return ILLEGAL; } if (board.bbc[board.side_to_move].is_empty()) best_score = (no_piece_score < 0) ? no_piece_score + depth : no_piece_score - depth; if (best_score == depth) hash_flag = HASH_TYPE_UPPER; /* Test if the mate is legal, that is to say: * - The checking piece is allowed to deliver mate * - The mate follows a sequence of checks at least one of which * makes the mate OK (is classified as "shak"). * The first is illegal, the second is only a draw. */ if (board.check()) { if (!is_valid_mate()) return ILLEGAL; if (board.rule_flags & RF_USE_SHAKMATE) { if (!board.have_shak()) best_score = 0; } } } assert(best_score > -LEGALWIN || legal_moves == 0); assert(hash_move || legal_moves == 0); best_move[depth] = hash_move; /* store evaluation in the transposition table */ //if (hash_flag & HASH_TYPE_UPPER) // hash_move = max_nodes_move; if (legal_moves) store_table_entry(transposition_table, board.hash, draft, score_to_hashtable(best_score, depth), hash_flag, hash_move); return best_score; } void print_principle_variation(int d = 0) { int c; int root_move_number = start_move_count; if (board.side_to_move) { iter(" %d. ...", (int)(root_move_number+moves_played)/2+1); root_move_number += 1; } for (c=0; c LEGALWIN) depth = LEGALWIN - score; for (int c=0; c= fifty_limit) { /* Take care: we are allowed to mate on the 50th move */ if (board.check()) { generate_legal_moves(movelist); if (movelist->num_moves) return SEARCH_GAME_ENDED_50_MOVE; } else return SEARCH_GAME_ENDED_50_MOVE; } if (count_repetition() >= repeat_claim && !(board.rule_flags & RF_USE_CHASERULE)) return SEARCH_GAME_ENDED_REPEAT; if (flags_are_captured()) return SEARCH_GAME_ENDED_FLAG_CAPTURED; if (material_draw()) return SEARCH_GAME_ENDED_INSUFFICIENT; generate_legal_moves(movelist); /* No legal moves? */ if (movelist->num_moves == 0) { if (board.bbc[board.side_to_move].is_empty()) return SEARCH_GAME_ENDED_NOPIECES; if (!board.check()) return SEARCH_GAME_ENDED_STALEMATE; /* Make sure mate wasn't caused by an illegal drop or a piece that is * not allowed to deliver mate. */ if (moves_played) { move_t prev_move = move_list[moves_played-1]; int p = board.get_piece(get_move_to(prev_move)); if (is_drop_move(prev_move)) { if (pt.piece_flags[p] & PF_DROPNOMATE) return SEARCH_GAME_ENDED_FORFEIT; } if (!is_valid_mate()) return SEARCH_GAME_ENDED_FORFEIT; } /* Test if the mate is legal, that is to say: * - The checking piece is allowed to deliver mate * - The mate follows a sequence of checks at least one of which * makes the mate OK (is classified as "shak"). * The first is illegal, the second is only a draw. */ if (board.rule_flags & RF_USE_SHAKMATE) if (!board.have_shak()) return SEARCH_GAME_ENDED_INADEQUATEMATE; return SEARCH_GAME_ENDED_MATE; } if (board.rule_flags & RF_USE_BARERULE) { side_t other = next_side[board.side_to_move]; if (lone_king(board.side_to_move)) { /* Can we capture the last piece? */ if ((board.bbc[other] & ~board.royal).onebit()) { for (int n = 0; nnum_moves; n++) { if (is_capture_move(movelist->move[n])) return SEARCH_OK; } /* Apparently not - we've lost! */ return SEARCH_GAME_ENDED_LOSEBARE; } } if (lone_king(other)) return SEARCH_GAME_ENDED_WINBARE; } if (check_limit && board.check_count[board.side_to_move] >= check_limit) return SEARCH_GAME_ENDED_CHECK_COUNT; return SEARCH_OK; } void score_static_moves(movelist_t *movelist) { const side_t me = board.side_to_move; movelist_t omoves; bitboard_t threat; int threat_see = 0; playmove(0); generate_legal_moves(&omoves); for (int n = 0; nnum_moves; n++) { move_t move = movelist->move[n]; if (is_capture_move(move)) { int s = see(move); if (s > threat_see) { threat.clear(); threat.set(get_move_to(move)); threat_see = s; } } } takeback(); int phase = 0; for (side_t side = WHITE; sidenum_moves; n++) { move_t move = movelist->move[n]; movelist->score[n] = 0; if (is_promotion_move(move)) { int s = see(move); if (s >= 0) movelist->score[n] += 2200 + s + pt.piece_value[get_move_promotion_piece(move)]; else movelist->score[n] += s; } else if (is_castle_move(move)) { movelist->score[n] += 600; } else if (is_capture_move(move)) { int s = see(move); if (s > 0) movelist->score[n] += 2400 + s / 10; else movelist->score[n] += s; } else if (is_drop_move(move)) { int s = see(move); if (s >= 0) { if (!(board.bbc[me] & board.royal & bitboard_t::neighbour_board[get_move_to(move)]).is_empty()) movelist->score[n] += 1200; else if (bitboard_t::board_homeland[next_side[me]].test(get_move_to(move))) movelist->score[n] += 1100; movelist->score[n] += s / 10; } else { movelist->score[n] += s; } } else { int s = see(move); movelist->score[n] += s; } int piece = get_move_piece(move); int pst = centre_table[get_move_to(move)] - (is_drop_move(move) ? 0 : centre_table[get_move_from(move)]); if (pt.royal_pieces & (1<score[n] += (-pst*4*phase + pst*(pt.phase_scale - phase)) / pt.phase_scale; } else { movelist->score[n] += pst; } /* Penalise moves that just unmake the last move */ if (moves_played >= 2) { move_t prev_move = move_list[moves_played-2]; if (!is_drop_move(move) && !is_drop_move(prev_move) && get_move_from(move) == get_move_to(prev_move)) { movelist->score[n] -= 10; if (get_move_to(move) == get_move_from(prev_move)) movelist->score[n] -= 100; } } /* Push passed pawns */ if (pt.pawn_pieces & (1< ps; calculate_pawn_structure(&ps); if (!is_drop_move(move) && ps.passed.test(get_move_from(move))) movelist->score[n] += 50; } /* TODO: * - Close with defending king in end game. * - Some sense of game phase (for pushing pawns) * - Some sort of threat detection: double attacks, attacking pinned * pieces, and the like. */ if (threat_see && !is_drop_move(move) && !threat.test(get_move_from(move))) movelist->score[n] -= threat_see; if (is_gate_move(move)) movelist->score[n] += 200; playmove(move); generate_legal_moves(&omoves); if (player_in_check(next_side[me])) { movelist->score[n] += 100; if (omoves.num_moves == 0) movelist->score[n] += 2000; } else { if (omoves.num_moves == 0) movelist->score[n] = -50; } takeback(); movelist->score[n] *= 8; movelist->score[n] += genrandui() & 0xf; } //movelist->show(); } play_state_t think(int max_depth) { play_state_t state; uint64_t start_time; bool mate_search = false; movelist_t movelist; int depth; clock.extra_time = 0; clock.root_moves_played = (int)moves_played; clock.nodes_searched = 0; branches_pruned = 0; abort_search = false; /* Start the clock */ start_time = get_timer(); start_clock(&clock); /* Test if the game has ended */ state = get_game_end_state(&movelist); if (!repetition_claim && state == SEARCH_GAME_ENDED_REPEAT) state = SEARCH_OK; if (state != SEARCH_OK) return state; for (side_t side = WHITE; side<=BLACK; side++) if ((board.bbc[side] & ~board.royal).is_empty()) mate_search = true; /* Level of play */ switch (level) { /* Pure random mover */ case LEVEL_RANDOM: playmove(movelist.move[int(genrandf()*movelist.num_moves)]); return SEARCH_OK; break; case LEVEL_STATIC: score_static_moves(&movelist); //movelist.show(); movelist.rewind(); playmove(movelist.next_move()); return SEARCH_OK; break; /* Normal alpha/beta search */ default: break; } /* Prepare the transpostion table for a new search iteration: * Resets the write count and increases the generation counter. */ if (!analysing) prepare_hashtable_search(transposition_table); int hash_depth, hash_score; uint32_t hash_flag; retrieve_table(transposition_table, board.hash, &hash_depth, &hash_score, &hash_flag, &best_move[0]); xb("# Begin iterative deepening loop for position \"%s\"\n", make_fen_string()); /* Iterative deepening loop */ int e = board.check(); clock.extra_time = clock.time_left; best_move[0] = 0; int score = search(-LEGALWIN, LEGALWIN, 1, 0); clock.extra_time = 0; if (abort_search) { xb("# Aborted ply 1 search - no move!\n"); if (best_move[0] == 0) { xb("# Pick random move\n"); best_move[0] = movelist.move[0]; } length_of_variation[0] = 1; principle_variation[0][0] = best_move[0]; xb("% 3d % 5d %6d %9d ", 1, score, (int)(get_timer()-start_time)/10000, (int)clock.nodes_searched); print_principle_variation_xb(); xb("\n"); } move_t move = best_move[0]; if (!abort_search) for (depth=2; depth<=max_depth; depth++) { scale_history(); movelist.rewind(); exclude.clear(); for (int pv=0; pv movelist.num_moves || exclude.num_moves >= movelist.num_moves) break; for (int window = 10; window < 2*LEGALWIN; window *= 2) { if (depth <= 2) window = (LEGALWIN+1000) / (depth - 1); if (mate_search && !is_mate_score(score)) window = LEGALWIN; int alpha = std::max(score - window, -LEGALWIN); int beta = std::min(score + window, LEGALWIN); int new_score = search(alpha, beta, depth + e, 0); clock.extra_time = 0; if (abort_search) break; score = new_score; if (score > alpha && score < beta) { move = best_move[0]; break; } /* Report a fail-low */ if (score < alpha && move) { move_t move = best_move[0]; if (show_fail_low) { iter("% 3d. %6.2f %9d %+2.2f ", depth, (get_timer()-start_time)/1000000.0, (int)clock.nodes_searched, score/100.0); iter(" %d.", (int)(start_move_count + moves_played)/2+1); if (board.side_to_move) iter(" ..."); iter(" %s?", move_to_short_string(move, &movelist, NULL, castle_san_ok)); iter("\n"); xb("% 3d % 5d %6d %9d ", depth, score, (int)(get_timer()-start_time)/10000, (int)clock.nodes_searched); xb(" %d.", (int)(start_move_count + moves_played)/2+1); if (board.side_to_move) xb(" ..."); xb(" %s?", move_to_short_string(move, &movelist, NULL, castle_san_ok)); xb("\n"); } clock.extra_time = (9*clock.time_left)/10; } /* Report a fail-high */ if (score >= beta) { move = best_move[0]; if (show_fail_high) { iter("% 3d. %6.2f %9d %+2.2f ", depth, (get_timer()-start_time)/1000000.0, (int)clock.nodes_searched, score/100.0); iter(" %d.", (int)(start_move_count + moves_played)/2+1); if (board.side_to_move) iter(" ..."); iter(" %s!", move_to_short_string(move, &movelist, NULL, castle_san_ok)); iter("\n"); xb("% 3d % 5d %6d %9d ", depth, score, (int)(get_timer()-start_time)/10000, (int)clock.nodes_searched); xb(" %d.", (int)(start_move_count + moves_played)/2+1); if (board.side_to_move) xb(" ..."); xb(" %s!", move_to_short_string(move, &movelist, NULL, castle_san_ok)); xb("\n"); } clock.extra_time = (5*clock.time_left)/10; } } iter("% 3d. %6.2f %9d %+2.2f ", depth, (get_timer() - start_time)/1000000.0, (int)clock.nodes_searched, score/100.0); print_principle_variation(); iter("\n"); xb("% 3d % 5d %6d %9d ", depth, score, (int)(get_timer()-start_time)/10000, (int)clock.nodes_searched); print_principle_variation_xb(); xb("\n"); uci("info depth %d score cp %d nodes %d time %d hashfull %d pv", depth, score, (int)clock.nodes_searched, (int)peek_timer(&clock), (int)std::min(1000, (int)(transposition_table->write_count*1000/transposition_table->number_of_elements))); print_principle_variation_uci(); if (multipv>1) { uci(" multipv %d", pv+1); } uci("\n"); store_principle_variation(score, depth); exclude.push(principle_variation[0][0]); } if (is_mate_score(score) && (LEGALWIN - abs(score)) == length_of_variation[0] && move && !analysing) break; if (abort_search) break; if (movelist.num_moves == 1 && move && !pondering && !analysing) break; /* Check if we have enough time for the next iteration, assuming an * effective branching ratio of 2. */ if ( clock.check_clock && peek_timer(&clock) > get_chess_clock_time_for_move(&clock)/2 && move) break; } while (analysing && !abort_search) check_clock(); while (pondering && !abort_search) check_clock(); if (score < resign_threshold) resign_count++; else resign_count = 0; if (abs(score) < draw_threshold) draw_count++; else draw_count = 0; if (moves_played > 400) draw_count += 50; //printf("# %d [draw %d / %d] [resign %d / %d]\n", score, draw_threshold, draw_count, resign_threshold, resign_count); assert(move); playmove(move); ponder_move = 0; if (length_of_variation[0] > 2) ponder_move = principle_variation[1][0]; return SEARCH_OK; } bool ponder() { move_t ponder_move = this->ponder_move; /* Verify that the ponder move is legal. * There is a corner-case where the ponder move is illegal if the game * was terminated by 50-move or three-fold repetition. * There may be a more elegant way to resolve this, but this works. */ movelist_t movelist; generate_legal_moves(&movelist); pondering = false; for (int n = 0; nponder_move = ponder_move; return true; } bool analyse() { move_t ponder_move = this->ponder_move; if (analyse_move) return false; analyse_move = 0; generate_legal_moves(&analyse_movelist); /* Search the current position */ abort_search = false; while (analysing && !abort_search) { if (think(MAX_SEARCH_DEPTH) == SEARCH_OK) takeback(); else { while (analysing && !abort_search) check_clock(); } } if (analyse_move) playmove(analyse_move); analyse_move = 0; this->ponder_move = ponder_move; return true; } #undef iter #undef uci #undef xb #undef check_clock SjaakII/include/see.h000644 000765 000024 00000012547 13017310267 015450 0ustar00eglebbkstaff000000 000000 int move_mvvlva(move_t move) const { int value = 0; if (is_capture_move(move)) { value += pt.see_piece_value[board.get_piece(get_move_capture_square(move))] * 8; value -= pt.see_piece_value[board.get_piece(get_move_from(move))] / 8; } return value; } int move_value(move_t move, side_t /* side_to_move */) const { /* Capture a piece */ int value = 0; if (is_drop_move(move)) return 0; if (is_capture_move(move)) value += pt.see_piece_value[board.get_piece(get_move_capture_square(move))]; if (is_promotion_move(move)) { //value += pt.see_piece_value[get_move_promotion_piece(move)]; //value -= pt.see_piece_value[board.get_piece(get_move_from(move))]; } /* TODO: gate moves have an extra drop */ return value; } struct { move_t move; uint32_t lock; int score; } see_cache[0xFFFF + 1 + 8]; bool probe_see_cache(move_t move, int *score) { int index = board.hash & 0xFFFF; uint32_t key = board.hash >> 32; for (int n = 0; n<8; n++) { if (see_cache[index + n].lock == key && see_cache[index + n].move == move) { *score = see_cache[index + n].score; return true; } } return false; } void store_see_cache(move_t move, int score) { int index = board.hash & 0xFFFF; uint32_t key = board.hash >> 32; uint32_t okey = key; move_t omove = move; int oscore = score; for (int n = 0; n<8; n++) { uint32_t new_key = see_cache[index + n].lock; move_t new_move = see_cache[index + n].move; int new_score = see_cache[index + n].score; see_cache[index+n].lock = okey; see_cache[index+n].move = omove; see_cache[index+n].score = oscore; if (new_key == key && new_move == move) return; okey = new_key; omove = new_move; oscore = new_score; } } /* Perform static exchange evaluation for a given capture move. * Uses the "swap" algorithm * (http://chessprogramming.wikispaces.com/SEE+-+The+Swap+Algorithm). * returns the evaluation in centipawns. * The move doesn't need to be a capture; if it isn't then this function * can be used to find out if it puts a piece "en-prise" */ int see(move_t move) { int score[128]; int depth; side_t side = board.side_to_move; int square = get_move_to(move); int piece = get_move_piece(move); int last_piece = piece; bitboard_t attackers, hidden_attackers, pinned; bitboard_t xray_update; bitboard_t own; bitboard_t mask = bitboard_t::board_all; /* Test if this position/move was calculated before and re-use */ if (probe_see_cache(move, &depth)) return depth; /* Initial gain: the piece on the target square */ depth = 0; score[depth] = move_value(move, board.side_to_move); if (is_capture_move(move)) { /* Easy: if the piece we use to capture the enemy piece is worth less * than the piece we captured, it's always a winning capture, so return a * rough estimate for the SEE score. */ if (score[depth] > pt.piece_value[piece]) return score[depth] - pt.piece_value[piece]/2; /* Perform the capture on the board. * In this case we need to explicitly clear the capture square because * the first move might be an en-passant capture. */ mask.reset(get_move_capture_square(move)); } /* We need to update the list of attackers if the moving piece is a * slider or moves along a ray. This includes some types of leaper, but * that is currently NOT taken into account. * *TODO* */ for (int n = 0; n. */ #ifdef __cplusplus extern "C" { #endif double myexp(double x); #ifdef __cplusplus } #endif SjaakII/include/sprt.h000644 000765 000024 00000000510 13014616475 015656 0ustar00eglebbkstaff000000 000000 #ifndef SPRT_H #define SPRT_H typedef enum { SPRT_UNKNOWN = 0, SPRT_ACCEPT, SPRT_REJECT } sprt_result_t; sprt_result_t sprt(int n_wins, int n_loss, int n_draw, double elo0, double elo1, double a, double b); void print_sprt(FILE *file, int n_wins, int n_loss, int n_draw, double elo0, double elo1, double a, double b); #endif SjaakII/include/squares.h000644 000765 000024 00000001600 12465222762 016353 0ustar00eglebbkstaff000000 000000 #ifndef SQUARE_H #define SQUARE_H #include extern char *square_names[128]; extern char *file_names[16]; extern char *rank_names[16]; extern int rank_offset; extern uint8_t packed_file_rank[128]; extern const char *kingside_castle; extern const char *queenside_castle; extern int div_file; extern bool keep_labels; void initialise_square_names(int files, int ranks); void relabel_shogi_square_names(void); void relabel_chess_square_names(void); int square_from_string(const char *str); /******************************************************************** * Functions for dealing with packing/unpacking square rows/files * ********************************************************************/ static inline int unpack_rank(int packed) { return packed_file_rank[packed] >> 4; } static inline int unpack_file(int packed) { return packed_file_rank[packed] & 0xf; } #endif SjaakII/include/test_suite.h000644 000765 000024 00000624664 12470166274 017106 0ustar00eglebbkstaff000000 000000 static const char *wac_test[] = { "2rr3k/pp3pp1/1nnqbN1p/3pN3/2pP4/2P3Q1/PPB4P/R4RK1 w - - bm Qg6; id \"WAC.001\";", "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - bm Rxb2; id \"WAC.002\";", "5rk1/1ppb3p/p1pb4/6q1/3P1p1r/2P1R2P/PP1BQ1P1/5RKN w - - bm Rg3; id \"WAC.003\";", "r1bq2rk/pp3pbp/2p1p1pQ/7P/3P4/2PB1N2/PP3PPR/2KR4 w - - bm Qxh7+; id \"WAC.004\";", "5k2/6pp/p1qN4/1p1p4/3P4/2PKP2Q/PP3r2/3R4 b - - bm Qc4+; id \"WAC.005\";", "7k/p7/1R5K/6r1/6p1/6P1/8/8 w - - bm Rb7; id \"WAC.006\";", "rnbqkb1r/pppp1ppp/8/4P3/6n1/7P/PPPNPPP1/R1BQKBNR b KQkq - bm Ne3; id \"WAC.007\";", "r4q1k/p2bR1rp/2p2Q1N/5p2/5p2/2P5/PP3PPP/R5K1 w - - bm Rf7; id \"WAC.008\";", "3q1rk1/p4pp1/2pb3p/3p4/6Pr/1PNQ4/P1PB1PP1/4RRK1 b - - bm Bh2+; id \"WAC.009\";", "2br2k1/2q3rn/p2NppQ1/2p1P3/Pp5R/4P3/1P3PPP/3R2K1 w - - bm Rxh7; id \"WAC.010\";", "r1b1kb1r/3q1ppp/pBp1pn2/8/Np3P2/5B2/PPP3PP/R2Q1RK1 w kq - bm Bxc6; id \"WAC.011\";", "4k1r1/2p3r1/1pR1p3/3pP2p/3P2qP/P4N2/1PQ4P/5R1K b - - bm Qxf3+; id \"WAC.012\";", "5rk1/pp4p1/2n1p2p/2Npq3/2p5/6P1/P3P1BP/R4Q1K w - - bm Qxf8+; id \"WAC.013\";", "r2rb1k1/pp1q1p1p/2n1p1p1/2bp4/5P2/PP1BPR1Q/1BPN2PP/R5K1 w - - bm Qxh7+; id \"WAC.014\";", "1R6/1brk2p1/4p2p/p1P1Pp2/P7/6P1/1P4P1/2R3K1 w - - bm Rxb7; id \"WAC.015\";", "r4rk1/ppp2ppp/2n5/2bqp3/8/P2PB3/1PP1NPPP/R2Q1RK1 w - - bm Nc3; id \"WAC.016\";", "1k5r/pppbn1pp/4q1r1/1P3p2/2NPp3/1QP5/P4PPP/R1B1R1K1 w - - bm Ne5; id \"WAC.017\";", "R7/P4k2/8/8/8/8/r7/6K1 w - - bm Rh8; id \"WAC.018\";", "r1b2rk1/ppbn1ppp/4p3/1QP4q/3P4/N4N2/5PPP/R1B2RK1 w - - bm c6; id \"WAC.019\";", "r2qkb1r/1ppb1ppp/p7/4p3/P1Q1P3/2P5/5PPP/R1B2KNR b kq - bm Bb5; id \"WAC.020\";", "5rk1/1b3p1p/pp3p2/3n1N2/1P6/P1qB1PP1/3Q3P/4R1K1 w - - bm Qh6; id \"WAC.021\";", "r1bqk2r/ppp1nppp/4p3/n5N1/2BPp3/P1P5/2P2PPP/R1BQK2R w KQkq - bm Ba2 Nxf7; id \"WAC.022\";", "r3nrk1/2p2p1p/p1p1b1p1/2NpPq2/3R4/P1N1Q3/1PP2PPP/4R1K1 w - - bm g4; id \"WAC.023\";", "6k1/1b1nqpbp/pp4p1/5P2/1PN5/4Q3/P5PP/1B2B1K1 b - - bm Bd4; id \"WAC.024\";", "3R1rk1/8/5Qpp/2p5/2P1p1q1/P3P3/1P2PK2/8 b - - bm Qh4+; id \"WAC.025\";", "3r2k1/1p1b1pp1/pq5p/8/3NR3/2PQ3P/PP3PP1/6K1 b - - bm Bf5; id \"WAC.026\";", "7k/pp4np/2p3p1/3pN1q1/3P4/Q7/1r3rPP/2R2RK1 w - - bm Qf8+; id \"WAC.027\";", "1r1r2k1/4pp1p/2p1b1p1/p3R3/RqBP4/4P3/1PQ2PPP/6K1 b - - bm Qe1+; id \"WAC.028\";", "r2q2k1/pp1rbppp/4pn2/2P5/1P3B2/6P1/P3QPBP/1R3RK1 w - - bm c6; id \"WAC.029\";", "1r3r2/4q1kp/b1pp2p1/5p2/pPn1N3/6P1/P3PPBP/2QRR1K1 w - - bm Nxd6; id \"WAC.030\";", "rb3qk1/pQ3ppp/4p3/3P4/8/1P3N2/1P3PPP/3R2K1 w - - bm Qxa8 d6 dxe6 g3; id \"WAC.031\";", "6k1/p4p1p/1p3np1/2q5/4p3/4P1N1/PP3PPP/3Q2K1 w - - bm Qd8+; id \"WAC.032\";", "8/p1q2pkp/2Pr2p1/8/P3Q3/6P1/5P1P/2R3K1 w - - bm Qe5+ Qf4; id \"WAC.033\";", "7k/1b1r2p1/p6p/1p2qN2/3bP3/3Q4/P5PP/1B1R3K b - - bm Bg1; id \"WAC.034\";", "r3r2k/2R3pp/pp1q1p2/8/3P3R/7P/PP3PP1/3Q2K1 w - - bm Rxh7+; id \"WAC.035\";", "3r4/2p1rk2/1pQq1pp1/7p/1P1P4/P4P2/6PP/R1R3K1 b - - bm Re1+; id \"WAC.036\";", "2r5/2rk2pp/1pn1pb2/pN1p4/P2P4/1N2B3/nPR1KPPP/3R4 b - - bm Nxd4+; id \"WAC.037\";", "4k3/p4prp/1p6/2b5/8/2Q3P1/P2R1PKP/4q3 w - - bm Qd3 Rd8+; id \"WAC.038\";", "r1br2k1/pp2bppp/2nppn2/8/2P1PB2/2N2P2/PqN1B1PP/R2Q1R1K w - - bm Na4; id \"WAC.039\";", "3r1r1k/1p4pp/p4p2/8/1PQR4/6Pq/P3PP2/2R3K1 b - - bm Rc8; id \"WAC.040\";", "1k6/5RP1/1P6/1K6/6r1/8/8/8 w - - bm Ka5 Kc5 b7; id \"WAC.041\";", "r1b1r1k1/pp1n1pbp/1qp3p1/3p4/1B1P4/Q3PN2/PP2BPPP/R4RK1 w - - bm Ba5; id \"WAC.042\";", "r2q3k/p2P3p/1p3p2/3QP1r1/8/B7/P5PP/2R3K1 w - - bm Be7 Qxa8; id \"WAC.043\";", "3rb1k1/pq3pbp/4n1p1/3p4/2N5/2P2QB1/PP3PPP/1B1R2K1 b - - bm dxc4; id \"WAC.044\";", "7k/2p1b1pp/8/1p2P3/1P3r2/2P3Q1/1P5P/R4qBK b - - bm Qxa1; id \"WAC.045\";", "r1bqr1k1/pp1nb1p1/4p2p/3p1p2/3P4/P1N1PNP1/1PQ2PP1/3RKB1R w K - bm Nb5; id \"WAC.046\";", "r1b2rk1/pp2bppp/2n1pn2/q5B1/2BP4/2N2N2/PP2QPPP/2R2RK1 b - - bm Nxd4; id \"WAC.047\";", "1rbq1rk1/p1p1bppp/2p2n2/8/Q1BP4/2N5/PP3PPP/R1B2RK1 b - - bm Rb4; id \"WAC.048\";", "2b3k1/4rrpp/p2p4/2pP2RQ/1pP1Pp1N/1P3P1P/1q6/6RK w - - bm Qxh7+; id \"WAC.049\";", "k4r2/1R4pb/1pQp1n1p/3P4/5p1P/3P2P1/r1q1R2K/8 w - - bm Rxb6+; id \"WAC.050\";", "r1bq1r2/pp4k1/4p2p/3pPp1Q/3N1R1P/2PB4/6P1/6K1 w - - bm Rg4+; id \"WAC.051\";", "r1k5/1p3q2/1Qpb4/3N1p2/5Pp1/3P2Pp/PPPK3P/4R3 w - - bm Re7 c4; id \"WAC.052\";", "6k1/6p1/p7/3Pn3/5p2/4rBqP/P4RP1/5QK1 b - - bm Re1; id \"WAC.053\";", "r3kr2/1pp4p/1p1p4/7q/4P1n1/2PP2Q1/PP4P1/R1BB2K1 b q - bm Qh1+; id \"WAC.054\";", "r3r1k1/pp1q1pp1/4b1p1/3p2B1/3Q1R2/8/PPP3PP/4R1K1 w - - bm Qxg7+; id \"WAC.055\";", "r1bqk2r/pppp1ppp/5n2/2b1n3/4P3/1BP3Q1/PP3PPP/RNB1K1NR b KQkq - bm Bxf2+; id \"WAC.056\";", "r3q1kr/ppp5/3p2pQ/8/3PP1b1/5R2/PPP3P1/5RK1 w - - bm Rf8+; id \"WAC.057\";", "8/8/2R5/1p2qp1k/1P2r3/2PQ2P1/5K2/8 w - - bm Qd1+; id \"WAC.058\";", "r1b2rk1/2p1qnbp/p1pp2p1/5p2/2PQP3/1PN2N1P/PB3PP1/3R1RK1 w - - bm Nd5; id \"WAC.059\";", "rn1qr1k1/1p2np2/2p3p1/8/1pPb4/7Q/PB1P1PP1/2KR1B1R w - - bm Qh8+; id \"WAC.060\";", "3qrbk1/ppp1r2n/3pP2p/3P4/2P4P/1P3Q2/PB6/R4R1K w - - bm Qf7+; id \"WAC.061\";", "6r1/3Pn1qk/p1p1P1rp/2Q2p2/2P5/1P4P1/P3R2P/5RK1 b - - bm Rxg3+; id \"WAC.062\";", "r1brnbk1/ppq2pp1/4p2p/4N3/3P4/P1PB1Q2/3B1PPP/R3R1K1 w - - bm Nxf7; id \"WAC.063\";", "8/6pp/3q1p2/3n1k2/1P6/3NQ2P/5PP1/6K1 w - - bm g4+; id \"WAC.064\";", "1r1r1qk1/p2n1p1p/bp1Pn1pQ/2pNp3/2P2P1N/1P5B/P6P/3R1RK1 w - - bm Ne7+; id \"WAC.065\";", "1k1r2r1/ppq5/1bp4p/3pQ3/8/2P2N2/PP4P1/R4R1K b - - bm Qxe5; id \"WAC.066\";", "3r2k1/p2q4/1p4p1/3rRp1p/5P1P/6PK/P3R3/3Q4 w - - bm Qb3 Rxd5; id \"WAC.067\";", "6k1/5ppp/1q6/2b5/8/2R1pPP1/1P2Q2P/7K w - - bm Qxe3; id \"WAC.068\";", "2k5/pppr4/4R3/4Q3/2pp2q1/8/PPP2PPP/6K1 w - - bm f3 h3; id \"WAC.069\";", "2kr3r/pppq1ppp/3p1n2/bQ2p3/1n1PP3/1PN1BN1P/1PP2PP1/2KR3R b - - bm Na2+; id \"WAC.070\";", "2kr3r/pp1q1ppp/5n2/1Nb5/2Pp1B2/7Q/P4PPP/1R3RK1 w - - bm Nxa7+; id \"WAC.071\";", "r3r1k1/pp1n1ppp/2p5/4Pb2/2B2P2/B1P5/P5PP/R2R2K1 w - - bm e6; id \"WAC.072\";", "r1q3rk/1ppbb1p1/4Np1p/p3pP2/P3P3/2N4R/1PP1Q1PP/3R2K1 w - - bm Qd2; id \"WAC.073\";", "5r1k/pp4pp/2p5/2b1P3/4Pq2/1PB1p3/P3Q1PP/3N2K1 b - - bm Rd8 Qf1+ Qxe4 a5 Bb6; id \"WAC.074; c0 \"Rd8 is probably best. Shredder says: 18/50 02:21 376.379.119 3.222.065 +10.49 Rf8d8 Kg1h1 Qf4xe4 Bc3a5 Rd8f8 h2h3 Qe4xe5 Ba5c3 Qe5f4 Nd1xe3 Bc5xe3 Qe2d1 Be3c5 Qd1e1\"\";", "r3r1k1/pppq1ppp/8/8/1Q4n1/7P/PPP2PP1/RNB1R1K1 b - - bm Qd6; id \"WAC.075\";", "r1b1qrk1/2p2ppp/pb1pnn2/1p2pNB1/3PP3/1BP5/PP2QPPP/RN1R2K1 w - - bm Bxf6; id \"WAC.076\";", "3r2k1/ppp2ppp/6q1/b4n2/3nQB2/2p5/P4PPP/RN3RK1 b - - bm Ng3; id \"WAC.077\";", "r2q3r/ppp2k2/4nbp1/5Q1p/2P1NB2/8/PP3P1P/3RR1K1 w - - bm Ng5+; id \"WAC.078\";", "r3k2r/pbp2pp1/3b1n2/1p6/3P3p/1B2N1Pq/PP1PQP1P/R1B2RK1 b kq - bm Qxh2+; id \"WAC.079\";", "r4rk1/p1B1bpp1/1p2pn1p/8/2PP4/3B1P2/qP2QP1P/3R1RK1 w - - bm Ra1; id \"WAC.080\";", "r4rk1/1bR1bppp/4pn2/1p2N3/1P6/P3P3/4BPPP/3R2K1 b - - bm Bd6; id \"WAC.081\";", "3rr1k1/pp3pp1/4b3/8/2P1B2R/6QP/P3q1P1/5R1K w - - bm Bh7+; id \"WAC.082\";", "3rr1k1/ppqbRppp/2p5/8/3Q1n2/2P3N1/PPB2PPP/3R2K1 w - - bm Qxd7; id \"WAC.083\";", "r2q1r1k/2p1b1pp/p1n5/1p1Q1bN1/4n3/1BP1B3/PP3PPP/R4RK1 w - - bm Qg8+; id \"WAC.084\";", "kr2R3/p4r2/2pq4/2N2p1p/3P2p1/Q5P1/5P1P/5BK1 w - - bm Na6; id \"WAC.085\";", "8/p7/1ppk1n2/5ppp/P1PP4/2P1K1P1/5N1P/8 b - - bm Ng4+; id \"WAC.086\";", "8/p3k1p1/4r3/2ppNpp1/PP1P4/2P3KP/5P2/8 b - - bm Rxe5; id \"WAC.087\";", "r6k/p1Q4p/2p1b1rq/4p3/B3P3/4P3/PPP3P1/4RRK1 b - - bm Rxg2+; id \"WAC.088\";", "1r3b1k/p4rpp/4pp2/3q4/2ppbPPQ/6RK/PP5P/2B1NR2 b - - bm g5; id \"WAC.089\";", "3qrrk1/1pp2pp1/1p2bn1p/5N2/2P5/P1P3B1/1P4PP/2Q1RRK1 w - - bm Nxg7; id \"WAC.090\";", "2qr2k1/4b1p1/2p2p1p/1pP1p3/p2nP3/PbQNB1PP/1P3PK1/4RB2 b - - bm Be6; id \"WAC.091\";", "r4rk1/1p2ppbp/p2pbnp1/q7/3BPPP1/2N2B2/PPP4P/R2Q1RK1 b - - bm Bxg4; id \"WAC.092\";", "r1b1k1nr/pp3pQp/4pq2/3pn3/8/P1P5/2P2PPP/R1B1KBNR w KQkq - bm Bh6; id \"WAC.093\";", "8/k7/p7/3Qp2P/n1P5/3KP3/1q6/8 b - - bm e4+; id \"WAC.094\";", "2r5/1r6/4pNpk/3pP1qp/8/2P1QP2/5PK1/R7 w - - bm Ng4+; id \"WAC.095\";", "r1b4k/ppp2Bb1/6Pp/3pP3/1qnP1p1Q/8/PPP3P1/1K1R3R w - - bm Qd8+ b3; id \"WAC.096\";", "6k1/5p2/p5np/4B3/3P4/1PP1q3/P3r1QP/6RK w - - bm Qa8+; id \"WAC.097\";", "1r3rk1/5pb1/p2p2p1/Q1n1q2p/1NP1P3/3p1P1B/PP1R3P/1K2R3 b - - bm Nxe4; id \"WAC.098\";", "r1bq1r1k/1pp1Np1p/p2p2pQ/4R3/n7/8/PPPP1PPP/R1B3K1 w - - bm Rh5; id \"WAC.099\";", "8/k1b5/P4p2/1Pp2p1p/K1P2P1P/8/3B4/8 w - - bm Be3 b6+; c0 \"b6+ is probably better.\"; id \"WAC.100\";", "5rk1/p5pp/8/8/2Pbp3/1P4P1/7P/4RN1K b - - bm Bc3; id \"WAC.101\";", "2Q2n2/2R4p/1p1qpp1k/8/3P3P/3B2P1/5PK1/r7 w - - bm Qxf8+; id \"WAC.102\";", "6k1/2pb1r1p/3p1PpQ/p1nPp3/1q2P3/2N2P2/PrB5/2K3RR w - - bm Qxg6+; id \"WAC.103\";", "b4r1k/pq2rp2/1p1bpn1p/3PN2n/2P2P2/P2B3K/1B2Q2N/3R2R1 w - - bm Qxh5; id \"WAC.104\";", "r2r2k1/pb3ppp/1p1bp3/7q/3n2nP/PP1B2P1/1B1N1P2/RQ2NRK1 b - - bm Bxg3 Qxh4; id \"WAC.105\";", "4rrk1/pppb4/7p/3P2pq/3Qn3/P5P1/1PP4P/R3RNNK b - - bm Nf2+; id \"WAC.106\";", "5n2/pRrk2p1/P4p1p/4p3/3N4/5P2/6PP/6K1 w - - bm Nb5; id \"WAC.107\";", "r5k1/1q4pp/2p5/p1Q5/2P5/5R2/4RKPP/r7 w - - bm Qe5; id \"WAC.108\";", "rn2k1nr/pbp2ppp/3q4/1p2N3/2p5/QP6/PB1PPPPP/R3KB1R b KQkq - bm c3; id \"WAC.109\";", "2kr4/bp3p2/p2p2b1/P7/2q5/1N4B1/1PPQ2P1/2KR4 b - - bm Be3; id \"WAC.110\";", "6k1/p5p1/5p2/2P2Q2/3pN2p/3PbK1P/7P/6q1 b - - bm Qd1+ Qf1+; c0 \"Qd1+ also wins, but Qf1 is faster and better.\"; id \"WAC.111\";", "r4kr1/ppp5/4bq1b/7B/2PR1Q1p/2N3P1/PP3P1P/2K1R3 w - - bm Rxe6; id \"WAC.112\";", "rnbqkb1r/1p3ppp/5N2/1p2p1B1/2P5/8/PP2PPPP/R2QKB1R b KQkq - bm Qxf6; id \"WAC.113\";", "r1b1rnk1/1p4pp/p1p2p2/3pN2n/3P1PPq/2NBPR1P/PPQ5/2R3K1 w - - bm Bxh7+; id \"WAC.114\";", "4N2k/5rpp/1Q6/p3q3/8/P5P1/1P3P1P/5K2 w - - bm Nd6; id \"WAC.115\";", "r2r2k1/2p2ppp/p7/1p2P1n1/P6q/5P2/1PB1QP1P/R5RK b - - bm Rd2; id \"WAC.116\";", "3r1rk1/q4ppp/p1Rnp3/8/1p6/1N3P2/PP3QPP/3R2K1 b - - bm Ne4; id \"WAC.117\";", "r5k1/pb2rpp1/1p6/2p4q/5R2/2PB2Q1/P1P3PP/5R1K w - - bm Rh4; id \"WAC.118\";", "r2qr1k1/p1p2ppp/2p5/2b5/4nPQ1/3B4/PPP3PP/R1B2R1K b - - bm Qxd3; id \"WAC.119\";", "r4rk1/1bn2qnp/3p1B1Q/p2P1pP1/1pp5/5N1P/PPB2P2/2KR3R w - - bm Rhg1 g6; id \"WAC.120\";", "6k1/5p1p/2bP2pb/4p3/2P5/1p1pNPPP/1P1Q1BK1/1q6 b - - bm Bxf3+; id \"WAC.121\";", "1k6/ppp4p/1n2pq2/1N2Rb2/2P2Q2/8/P4KPP/3r1B2 b - - bm Rxf1+; id \"WAC.122\";", "6k1/1b2rp2/1p4p1/3P4/PQ4P1/2N2q2/5P2/3R2K1 b - - bm Bxd5 Rc7 Re6; id \"WAC.123\";", "6k1/3r4/2R5/P5P1/1P4p1/8/4rB2/6K1 b - - bm g3; id \"WAC.124\";", "r1bqr1k1/pp3ppp/1bp5/3n4/3B4/2N2P1P/PPP1B1P1/R2Q1RK1 b - - bm Bxd4+; id \"WAC.125\";", "r5r1/pQ5p/1qp2R2/2k1p3/4P3/2PP4/P1P3PP/6K1 w - - bm Rxc6+; id \"WAC.126\";", "2k4r/1pr1n3/p1p1q2p/5pp1/3P1P2/P1P1P3/1R2Q1PP/1RB3K1 w - - bm Rxb7; id \"WAC.127\";", "6rk/1pp2Qrp/3p1B2/1pb1p2R/3n1q2/3P4/PPP3PP/R6K w - - bm Qg6; id \"WAC.128\";", "3r1r1k/1b2b1p1/1p5p/2p1Pp2/q1B2P2/4P2P/1BR1Q2K/6R1 b - - bm Bf3; id \"WAC.129\";", "6k1/1pp3q1/5r2/1PPp4/3P1pP1/3Qn2P/3B4/4R1K1 b - - bm Qh6 Qh8; id \"WAC.130\";", "2rq1bk1/p4p1p/1p4p1/3b4/3B1Q2/8/P4PpP/3RR1K1 w - - bm Re8; id \"WAC.131\";", "4r1k1/5bpp/2p5/3pr3/8/1B3pPq/PPR2P2/2R2QK1 b - - bm Qxf1+ Re1; id \"WAC.132\";", "r1b1k2r/1pp1q2p/p1n3p1/3QPp2/8/1BP3B1/P5PP/3R1RK1 w kq - bm Bh4; id \"WAC.133\";", "3r2k1/p6p/2Q3p1/4q3/2P1p3/P3Pb2/1P3P1P/2K2BR1 b - - bm Rd1+; id \"WAC.134\";", "3r1r1k/N2qn1pp/1p2np2/2p5/2Q1P2N/3P4/PP4PP/3R1RK1 b - - bm Nd4; id \"WAC.135\";", "6kr/1q2r1p1/1p2N1Q1/5p2/1P1p4/6R1/7P/2R3K1 w - - bm Rc8+; id \"WAC.136\";", "3b1rk1/1bq3pp/5pn1/1p2rN2/2p1p3/2P1B2Q/1PB2PPP/R2R2K1 w - - bm Rd7; id \"WAC.137\";", "r1bq3r/ppppR1p1/5n1k/3P4/6pP/3Q4/PP1N1PP1/5K1R w - - bm Qe3+ h5; id \"WAC.138\";", "rnb3kr/ppp2ppp/1b6/3q4/3pN3/Q4N2/PPP2KPP/R1B1R3 w - - bm Nf6+; id \"WAC.139\";", "r2b1rk1/pq4p1/4ppQP/3pB1p1/3P4/2R5/PP3PP1/5RK1 w - - bm Bc7 Rc7; id \"WAC.140\";", "4r1k1/p1qr1p2/2pb1Bp1/1p5p/3P1n1R/1B3P2/PP3PK1/2Q4R w - - bm Qxf4; id \"WAC.141\";", "r2q3n/ppp2pk1/3p4/5Pr1/2NP1Qp1/2P2pP1/PP3K2/4R2R w - - bm Re8 f6+; id \"WAC.142\";", "5b2/pp2r1pk/2pp1pRp/4rP1N/2P1P3/1P4QP/P3q1P1/5R1K w - - bm Rxh6+; id \"WAC.143\";", "r2q1rk1/pp3ppp/2p2b2/8/B2pPPb1/7P/PPP1N1P1/R2Q1RK1 b - - bm d3; id \"WAC.144\";", "r1bq4/1p4kp/3p1n2/p4pB1/2pQ4/8/1P4PP/4RRK1 w - - bm Re8; id \"WAC.145\";", "8/8/2Kp4/3P1B2/2P2k2/5p2/8/8 w - - bm Bc8 Bd3 Bh3; id \"WAC.146\";", "r2r2k1/ppqbppbp/2n2np1/2pp4/6P1/1P1PPNNP/PBP2PB1/R2QK2R b KQ - bm Nxg4; id \"WAC.147\";", "2r1k3/6pr/p1nBP3/1p3p1p/2q5/2P5/P1R4P/K2Q2R1 w - - bm Rxg7; id \"WAC.148\";", "6k1/6p1/2p4p/4Pp2/4b1qP/2Br4/1P2RQPK/8 b - - bm Bxg2; id \"WAC.149\";", "r3r1k1/5p2/pQ1b2pB/1p6/4p3/6P1/Pq2BP1P/2R3K1 b - - bm Ba3 Be5 Bf8 e3; c0 \"All win but e3 is best.\"; id \"WAC.150\";", "8/3b2kp/4p1p1/pr1n4/N1N4P/1P4P1/1K3P2/3R4 w - - bm Nc3; id \"WAC.151\";", "1br2rk1/1pqb1ppp/p3pn2/8/1P6/P1N1PN1P/1B3PP1/1QRR2K1 w - - bm Ne4; id \"WAC.152\";", "2r3k1/q4ppp/p3p3/pnNp4/2rP4/2P2P2/4R1PP/2R1Q1K1 b - - bm Nxd4; id \"WAC.153\";", "r1b2rk1/2p2ppp/p7/1p6/3P3q/1BP3bP/PP3QP1/RNB1R1K1 w - - bm Qxf7+; id \"WAC.154\";", "5bk1/1rQ4p/5pp1/2pP4/3n1PP1/7P/1q3BB1/4R1K1 w - - bm d6; id \"WAC.155\";", "r1b1qN1k/1pp3p1/p2p3n/4p1B1/8/1BP4Q/PP3KPP/8 w - - bm Qxh6+; id \"WAC.156\";", "5rk1/p4ppp/2p1b3/3Nq3/4P1n1/1p1B2QP/1PPr2P1/1K2R2R w - - bm Ne7+; id \"WAC.157\";", "5rk1/n1p1R1bp/p2p4/1qpP1QB1/7P/2P3P1/PP3P2/6K1 w - - bm Rxg7+; id \"WAC.158\";", "r1b2r2/5P1p/ppn3pk/2p1p1Nq/1bP1PQ2/3P4/PB4BP/1R3RK1 w - - bm Ne6+; id \"WAC.159\";", "qn1kr2r/1pRbb3/pP5p/P2pP1pP/3N1pQ1/3B4/3B1PP1/R5K1 w - - bm Qxd7+; id \"WAC.160\";", "3r3k/3r1P1p/pp1Nn3/2pp4/7Q/6R1/Pq4PP/5RK1 w - - bm Qxd8+; id \"WAC.161\";", "r3kbnr/p4ppp/2p1p3/8/Q1B3b1/2N1B3/PP3PqP/R3K2R w KQkq - bm Bd5; id \"WAC.162\";", "5rk1/2p4p/2p4r/3P4/4p1b1/1Q2NqPp/PP3P1K/R4R2 b - - bm Qg2+; id \"WAC.163\";", "8/6pp/4p3/1p1n4/1NbkN1P1/P4P1P/1PR3K1/r7 w - - bm Rxc4+; id \"WAC.164\";", "1r5k/p1p3pp/8/8/4p3/P1P1R3/1P1Q1qr1/2KR4 w - - bm Qd8+ Re2; id \"WAC.165\";", "r3r1k1/5pp1/p1p4p/2Pp4/8/q1NQP1BP/5PP1/4K2R b K - bm d4; id \"WAC.166\";", "7Q/ppp2q2/3p2k1/P2Ppr1N/1PP5/7R/5rP1/6K1 b - - bm Rxg2+; id \"WAC.167\";", "r3k2r/pb1q1p2/8/2p1pP2/4p1p1/B1P1Q1P1/P1P3K1/R4R2 b kq - bm Qd2+; id \"WAC.168\";", "5rk1/1pp3bp/3p2p1/2PPp3/1P2P3/2Q1B3/4q1PP/R5K1 b - - bm Bh6; id \"WAC.169\";", "5r1k/6Rp/1p2p3/p2pBp2/1qnP4/4P3/Q4PPP/6K1 w - - bm Qxc4; id \"WAC.170\";", "2rq4/1b2b1kp/p3p1p1/1p1nNp2/7P/1B2B1Q1/PP3PP1/3R2K1 w - - bm Bh6+; id \"WAC.171\";", "5r1k/p5pp/8/1P1pq3/P1p2nR1/Q7/5BPP/6K1 b - - bm Qe1+; id \"WAC.172\";", "2r1b3/1pp1qrk1/p1n1P1p1/7R/2B1p3/4Q1P1/PP3PP1/3R2K1 w - - bm Qh6+; id \"WAC.173\";", "2r2rk1/6p1/p3pq1p/1p1b1p2/3P1n2/PP3N2/3N1PPP/1Q2RR1K b - - bm Nxg2; id \"WAC.174\";", "r5k1/pppb3p/2np1n2/8/3PqNpP/3Q2P1/PPP5/R4RK1 w - - bm Nh5; id \"WAC.175\";", "r1bq3r/ppp2pk1/3p1pp1/8/2BbPQ2/2NP2P1/PPP4P/R4R1K b - - bm Rxh2+; id \"WAC.176\";", "r1b3r1/4qk2/1nn1p1p1/3pPp1P/p4P2/1p3BQN/PKPBN3/3R3R b - - bm Qa3+; id \"WAC.177\";", "3r2k1/p1rn1p1p/1p2pp2/6q1/3PQNP1/5P2/P1P4R/R5K1 w - - bm Nxe6; id \"WAC.178\";", "r1b2r1k/pp4pp/3p4/3B4/8/1QN3Pn/PP3q1P/R3R2K b - - bm Qg1+; id \"WAC.179\";", "r1q2rk1/p3bppb/3p1n1p/2nPp3/1p2P1P1/6NP/PP2QPB1/R1BNK2R b KQ - bm Nxd5; id \"WAC.180\";", "r3k2r/2p2p2/p2p1n2/1p2p3/4P2p/1PPPPp1q/1P5P/R1N2QRK b kq - bm Ng4; id \"WAC.181\";", "r1b2rk1/ppqn1p1p/2n1p1p1/2b3N1/2N5/PP1BP3/1B3PPP/R2QK2R w KQ - bm Qh5; id \"WAC.182\";", "1r2k1r1/5p2/b3p3/1p2b1B1/3p3P/3B4/PP2KP2/2R3R1 w - - bm Bf6; id \"WAC.183\";", "4kn2/r4p1r/p3bQ2/q1nNP1Np/1p5P/8/PPP3P1/2KR3R w - - bm Qe7+; id \"WAC.184\";", "1r1rb1k1/2p3pp/p2q1p2/3PpP1Q/Pp1bP2N/1B5R/1P4PP/2B4K w - - bm Ng6 Qxh7+; c0 \"Ng6 is probably slightly better than Qxh7+\"; id \"WAC.185\";", "r5r1/p1q2p1k/1p1R2pB/3pP3/6bQ/2p5/P1P1NPPP/6K1 w - - bm Bf8+; id \"WAC.186\";", "6k1/5p2/p3p3/1p3qp1/2p1Qn2/2P1R3/PP1r1PPP/4R1K1 b - - bm Nh3+; id \"WAC.187\";", "3RNbk1/pp3p2/4rQpp/8/1qr5/7P/P4P2/3R2K1 w - - bm Qg7+; id \"WAC.188\";", "3r1k2/1ppPR1n1/p2p1rP1/3P3p/4Rp1N/5K2/P1P2P2/8 w - - bm Re8+; id \"WAC.189\";", "8/p2b2kp/1q1p2p1/1P1Pp3/4P3/3B2P1/P2Q3P/2Nn3K b - - bm Bh3; id \"WAC.190\";", "2r1Rn1k/1p1q2pp/p7/5p2/3P4/1B4P1/P1P1QP1P/6K1 w - - bm Qc4; id \"WAC.191\";", "r3k3/ppp2Npp/4Bn2/2b5/1n1pp3/N4P2/PPP3qP/R2QKR2 b Qq - bm Nd3+; id \"WAC.192\";", "5bk1/p4ppp/Qp6/4B3/1P6/Pq2P1P1/2rr1P1P/R4RK1 b - - bm Qxe3; id \"WAC.193\";", "5rk1/ppq2ppp/2p5/4bN2/4P3/6Q1/PPP2PPP/3R2K1 w - - bm Nh6+; id \"WAC.194\";", "3r1rk1/1p3p2/p3pnnp/2p3p1/2P2q2/1P5P/PB2QPPN/3RR1K1 w - - bm g3; id \"WAC.195\";", "rr4k1/p1pq2pp/Q1n1pn2/2bpp3/4P3/2PP1NN1/PP3PPP/R1B1K2R b KQ - bm Nb4; id \"WAC.196\";", "7k/1p4p1/7p/3P1n2/4Q3/2P2P2/PP3qRP/7K b - - bm Qf1+; id \"WAC.197\";", "2br2k1/ppp2p1p/4p1p1/4P2q/2P1Bn2/2Q5/PP3P1P/4R1RK b - - bm Rd3; id \"WAC.198\";", "r1br2k1/pp2nppp/2n5/1B1q4/Q7/4BN2/PP3PPP/2R2RK1 w - - bm Bxc6 Rcd1 Rfd1; id \"WAC.199\";", "2rqrn1k/pb4pp/1p2pp2/n2P4/2P3N1/P2B2Q1/1B3PPP/2R1R1K1 w - - bm Bxf6; id \"WAC.200\";", "2b2r1k/4q2p/3p2pQ/2pBp3/8/6P1/1PP2P1P/R5K1 w - - bm Ra7; id \"WAC.201\";", "QR2rq1k/2p3p1/3p1pPp/8/4P3/8/P1r3PP/1R4K1 b - - bm Rxa2; id \"WAC.202\";", "r4rk1/5ppp/p3q1n1/2p2NQ1/4n3/P3P3/1B3PPP/1R3RK1 w - - bm Qh6; id \"WAC.203\";", "r1b1qrk1/1p3ppp/p1p5/3Nb3/5N2/P7/1P4PQ/K1R1R3 w - - bm Rxe5; id \"WAC.204\";", "r3rnk1/1pq2bb1/p4p2/3p1Pp1/3B2P1/1NP4R/P1PQB3/2K4R w - - bm Qxg5; id \"WAC.205\";", "1Qq5/2P1p1kp/3r1pp1/8/8/7P/p4PP1/2R3K1 b - - bm Rc6; id \"WAC.206\";", "r1bq2kr/p1pp1ppp/1pn1p3/4P3/2Pb2Q1/BR6/P4PPP/3K1BNR w - - bm Qxg7+; id \"WAC.207\";", "3r1bk1/ppq3pp/2p5/2P2Q1B/8/1P4P1/P6P/5RK1 w - - bm Bf7+; id \"WAC.208\";", "4kb1r/2q2p2/r2p4/pppBn1B1/P6P/6Q1/1PP5/2KRR3 w k - bm Rxe5+; id \"WAC.209\";", "3r1rk1/pp1q1ppp/3pn3/2pN4/5PP1/P5PQ/1PP1B3/1K1R4 w - - bm Rh1; id \"WAC.210\";", "r1bqrk2/pp1n1n1p/3p1p2/P1pP1P1Q/2PpP1NP/6R1/2PB4/4RBK1 w - - bm Qxf7+; id \"WAC.211\";", "rn1qr2Q/pbppk1p1/1p2pb2/4N3/3P4/2N5/PPP3PP/R4RK1 w - - bm Qxg7+; id \"WAC.212\";", "3r1r1k/1b4pp/ppn1p3/4Pp1R/Pn5P/3P4/4QP2/1qB1NKR1 w - - bm Rxh7+; id \"WAC.213\";", "r2r2k1/1p2qpp1/1np1p1p1/p3N3/2PPN3/bP5R/4QPPP/4R1K1 w - - bm Ng5; id \"WAC.214\";", "3r2k1/pb1q1pp1/1p2pb1p/8/3N4/P2QB3/1P3PPP/1Br1R1K1 w - - bm Qh7+; id \"WAC.215\";", "r2qr1k1/1b1nbppp/p3pn2/1p1pN3/3P1B2/2PB1N2/PP2QPPP/R4RK1 w - - bm Nxf7 a4; id \"WAC.216\";", "r3kb1r/1pp3p1/p3bp1p/5q2/3QN3/1P6/PBP3P1/3RR1K1 w kq - bm Qd7+; id \"WAC.217\";", "6k1/pp5p/2p3q1/6BP/2nPr1Q1/8/PP3R1K/8 w - - bm Bh6; id \"WAC.218\";", "7k/p4q1p/1pb5/2p5/4B2Q/2P1B3/P6P/7K b - - bm Qf1+; id \"WAC.219\";", "3rr1k1/ppp2ppp/8/5Q2/4n3/1B5R/PPP1qPP1/5RK1 b - - bm Qxf1+; id \"WAC.220\";", "r3k3/P5bp/2N1bp2/4p3/2p5/6NP/1PP2PP1/3R2K1 w q - bm Rd8+; id \"WAC.221\";", "2r1r2k/1q3ppp/p2Rp3/2p1P3/6QB/p3P3/bP3PPP/3R2K1 w - - bm Bf6; id \"WAC.222\";", "r1bqk2r/pp3ppp/5n2/8/1b1npB2/2N5/PP1Q2PP/1K2RBNR w kq - bm Nxe4; id \"WAC.223\";", "5rk1/p1q3pp/1p1r4/2p1pp1Q/1PPn1P2/3B3P/P2R2P1/3R2K1 b - - bm Rh6 e4; id \"WAC.224\";", "4R3/4q1kp/6p1/1Q3b2/1P1b1P2/6KP/8/8 b - - bm Qh4+; id \"WAC.225\";", "2b2rk1/p1p4p/2p1p1p1/br2N1Q1/1p2q3/8/PB3PPP/3R1RK1 w - - bm Nf7; id \"WAC.226\";", "2k1rb1r/ppp3pp/2np1q2/5b2/2B2P2/2P1BQ2/PP1N1P1P/2KR3R b - - bm d5; id \"WAC.227\";", "r4rk1/1bq1bp1p/4p1p1/p2p4/3BnP2/1N1B3R/PPP3PP/R2Q2K1 w - - bm Bxe4; id \"WAC.228\";", "8/8/8/1p5r/p1p1k1pN/P2pBpP1/1P1K1P2/8 b - - bm Rxh4 b4; id \"WAC.229\";", "2b5/1r6/2kBp1p1/p2pP1P1/2pP4/1pP3K1/1R3P2/8 b - - bm Rb4; id \"WAC.230\";", "r4rk1/1b1nqp1p/p5p1/1p2PQ2/2p5/5N2/PP3PPP/R1BR2K1 w - - bm Bg5; id \"WAC.231\";", "1R2rq1k/2p3p1/Q2p1pPp/8/4P3/8/P1r3PP/1R4K1 w - - bm Qb5 Rxe8; id \"WAC.232\";", "5rk1/p1p2r1p/2pp2p1/4p3/PPPnP3/3Pq1P1/1Q1R1R1P/4NK2 b - - bm Nb3; id \"WAC.233\";", "2kr1r2/p6p/5Pp1/2p5/1qp2Q1P/7R/PP6/1KR5 w - - bm Rb3; id \"WAC.234\";", "5r2/1p1RRrk1/4Qq1p/1PP3p1/8/4B3/1b3P1P/6K1 w - - bm Qe4 Qxf7+ Rxf7+; id \"WAC.235\";", "1R6/p5pk/4p2p/4P3/8/2r3qP/P3R1b1/4Q1K1 b - - bm Rc1; id \"WAC.236\";", "r5k1/pQp2qpp/8/4pbN1/3P4/6P1/PPr4P/1K1R3R b - - bm Rc1+; id \"WAC.237\";", "1k1r4/pp1r1pp1/4n1p1/2R5/2Pp1qP1/3P2QP/P4PB1/1R4K1 w - - bm Bxb7; id \"WAC.238\";", "8/6k1/5pp1/Q6p/5P2/6PK/P4q1P/8 b - - bm Qf1+; id \"WAC.239\";", "2b4k/p1b2p2/2p2q2/3p1PNp/3P2R1/3B4/P1Q2PKP/4r3 w - - bm Qxc6; id \"WAC.240\";", "2rq1rk1/pp3ppp/2n2b2/4NR2/3P4/PB5Q/1P4PP/3R2K1 w - - bm Qxh7+; id \"WAC.241\";", "r1b1r1k1/pp1nqp2/2p1p1pp/8/4N3/P1Q1P3/1P3PPP/1BRR2K1 w - - bm Rxd7; id \"WAC.242\";", "1r3r1k/3p4/1p1Nn1R1/4Pp1q/pP3P1p/P7/5Q1P/6RK w - - bm Qe2; id \"WAC.243\";", "r6r/pp3ppp/3k1b2/2pb4/B4Pq1/2P1Q3/P5PP/1RBR2K1 w - - bm Qxc5+; id \"WAC.244\";", "4rrn1/ppq3bk/3pPnpp/2p5/2PB4/2NQ1RPB/PP5P/5R1K w - - bm Qxg6+; id \"WAC.245\";", "6R1/4qp1p/ppr1n1pk/8/1P2P1QP/6N1/P4PP1/6K1 w - - bm Qh5+; id \"WAC.246\";", "2k1r3/1p2Bq2/p2Qp3/Pb1p1p1P/2pP1P2/2P5/2P2KP1/1R6 w - - bm Rxb5; id \"WAC.247\";", "5r1k/1p4pp/3q4/3Pp1R1/8/8/PP4PP/4Q1K1 b - - bm Qc5+; id \"WAC.248\";", "r4rk1/pbq2pp1/1ppbpn1p/8/2PP4/1P1Q1N2/PBB2PPP/R3R1K1 w - - bm c5 d5; id \"WAC.249\";", "1b5k/7P/p1p2np1/2P2p2/PP3P2/4RQ1R/q2r3P/6K1 w - - bm Qxc6 Re8+; id \"WAC.250\";", "k7/p4p2/P1q1b1p1/3p3p/3Q4/7P/5PP1/1R4K1 w - - bm Qe5 Qf4; id \"WAC.251\";", "1rb1r1k1/p1p2ppp/5n2/2pP4/5P2/2QB4/qNP3PP/2KRB2R b - - bm Bg4 Re2; c0 \"Bg4 wins, but Re2 is far better.\"; id \"WAC.252\";", "k5r1/p4b2/2P5/5p2/3P1P2/4QBrq/P5P1/4R1K1 w - - bm Qe8+; id \"WAC.253\";", "r6k/pp3p1p/2p1bp1q/b3p3/4Pnr1/2PP2NP/PP1Q1PPN/R2B2RK b - - bm Nxh3; id \"WAC.254\";", "3r3r/p4pk1/5Rp1/3q4/1p1P2RQ/5N2/P1P4P/2b4K w - - bm Rfxg6+; id \"WAC.255\";", "3r1rk1/1pb1qp1p/2p3p1/p7/P2Np2R/1P5P/1BP2PP1/3Q1BK1 w - - bm Nf5; id \"WAC.256\";", "4r1k1/pq3p1p/2p1r1p1/2Q1p3/3nN1P1/1P6/P1P2P1P/3RR1K1 w - - bm Rxd4; id \"WAC.257\";", "r3brkn/1p5p/2p2Ppq/2Pp3B/3Pp2Q/4P1R1/6PP/5R1K w - - bm Bxg6; id \"WAC.258\";", "r1bq1rk1/ppp2ppp/2np4/2bN1PN1/2B1P3/3p4/PPP2nPP/R1BQ1K1R w - - bm Qh5; id \"WAC.259\";", "2r2b1r/p1Nk2pp/3p1p2/N2Qn3/4P3/q6P/P4PP1/1R3K1R w - - bm Qe6+; id \"WAC.260\";", "r5k1/1bp3pp/p2p4/1p6/5p2/1PBP1nqP/1PP3Q1/R4R1K b - - bm Nd4; id \"WAC.261\";", "6k1/p1B1b2p/2b3r1/2p5/4p3/1PP1N1Pq/P2R1P2/3Q2K1 b - - bm Rh6; id \"WAC.262\";", "rnbqr2k/pppp1Qpp/8/b2NN3/2B1n3/8/PPPP1PPP/R1B1K2R w KQ - bm Qg8+; id \"WAC.263\";", "r2r2k1/1R2qp2/p5pp/2P5/b1PN1b2/P7/1Q3PPP/1B1R2K1 b - - bm Qe5 Rab8; id \"WAC.264\";", "2r1k2r/2pn1pp1/1p3n1p/p3PP2/4q2B/P1P5/2Q1N1PP/R4RK1 w k - bm exf6; id \"WAC.265\";", "r3q2r/2p1k1p1/p5p1/1p2Nb2/1P2nB2/P7/2PNQbPP/R2R3K b - - bm Rxh2+; id \"WAC.266\";", "2r1kb1r/pp3ppp/2n1b3/1q1N2B1/1P2Q3/8/P4PPP/3RK1NR w Kk - bm Nc7+; id \"WAC.267\";", "2r3kr/ppp2n1p/7B/5q1N/1bp5/2Pp4/PP2RPPP/R2Q2K1 w - - bm Re8+; id \"WAC.268\";", "2kr2nr/pp1n1ppp/2p1p3/q7/1b1P1B2/P1N2Q1P/1PP1BPP1/R3K2R w KQ - bm axb4; id \"WAC.269\";", "2r1r1k1/pp1q1ppp/3p1b2/3P4/3Q4/5N2/PP2RPPP/4R1K1 w - - bm Qg4; id \"WAC.270\";", "2kr4/ppp3Pp/4RP1B/2r5/5P2/1P6/P2p4/3K4 w - - bm Rd6; id \"WAC.271\";", "nrq4r/2k1p3/1p1pPnp1/pRpP1p2/P1P2P2/2P1BB2/1R2Q1P1/6K1 w - - bm Bxc5; id \"WAC.272\";", "2k4B/bpp1qp2/p1b5/7p/1PN1n1p1/2Pr4/P5PP/R3QR1K b - - bm Ng3+ g3; id \"WAC.273\";", "8/1p6/p5R1/k7/Prpp4/K7/1NP5/8 w - - am Rd6; bm Rb6 Rg5+; id \"WAC.274\";", "r1b2rk1/1p1n1ppp/p1p2q2/4p3/P1B1Pn2/1QN2N2/1P3PPP/3R1RK1 b - - bm Nc5 Nxg2 b5; id \"WAC.275\";", "r5k1/pp1RR1pp/1b6/6r1/2p5/B6P/P4qPK/3Q4 w - - bm Qd5+; id \"WAC.276\";", "1r4r1/p2kb2p/bq2p3/3p1p2/5P2/2BB3Q/PP4PP/3RKR2 b - - bm Rg3 Rxg2; c0 \"This one is questionable.\"; c1 \"Rybka 1.2 says: 20 09:39 45.122.112 79.881 +0.55 Rg8g3 Qh3xg3 Be7h4 Bd3xa6 Bh4xg3+ h2xg3 Qb6xa6 Rf1h1 Kd7c6 Rh1xh7 Qa6xa2 Rd1c1 Kc6b5 Bc3e5\"; id \"WAC.277\";", "r2qkb1r/pppb2pp/2np1n2/5pN1/2BQP3/2N5/PPP2PPP/R1B1K2R w KQkq - bm Bf7+; id \"WAC.278\";", "r7/4b3/2p1r1k1/1p1pPp1q/1P1P1P1p/PR2NRpP/2Q3K1/8 w - - bm Nxf5 Rc3; id \"WAC.279\";", "r1r2bk1/5p1p/pn4p1/N2b4/3Pp3/B3P3/2q1BPPP/RQ3RK1 b - - bm Bxa3; id \"WAC.280\";", "2R5/2R4p/5p1k/6n1/8/1P2QPPq/r7/6K1 w - - bm Rxh7+; id \"WAC.281\";", "6k1/2p3p1/1p1p1nN1/1B1P4/4PK2/8/2r3b1/7R w - - bm Rh8+; id \"WAC.282\";", "3q1rk1/4bp1p/1n2P2Q/3p1p2/6r1/Pp2R2N/1B4PP/7K w - - bm Ng5; id \"WAC.283\";", "3r3k/pp4pp/8/1P6/3N4/Pn2P1qb/1B1Q2B1/2R3K1 w - - bm Nf5; id \"WAC.284\";", "2rr3k/1b2bppP/p2p1n2/R7/3P4/1qB2P2/1P4Q1/1K5R w - - bm Qxg7+; id \"WAC.285\";", "3r1k2/1p6/p4P2/2pP2Qb/8/1P1KB3/P6r/8 b - - bm Rxd5+; id \"WAC.286\";", "rn3k1r/pp2bBpp/2p2n2/q5N1/3P4/1P6/P1P3PP/R1BQ1RK1 w - - bm Bc4 Bd2 Qe2 Qg4 Qh5; c0 \"Best solution is probably Bd2\"; id \"WAC.287\";", "r1b2rk1/p4ppp/1p1Qp3/4P2N/1P6/8/P3qPPP/3R1RK1 w - - bm Nf6+; id \"WAC.288\";", "2r3k1/5p1p/p3q1p1/2n3P1/1p1QP2P/1P4N1/PK6/2R5 b - - bm Qe5; id \"WAC.289\";", "2k2r2/2p5/1pq5/p1p1n3/P1P2n1B/1R4Pp/2QR4/6K1 b - - bm Ne2+; id \"WAC.290\";", "5r1k/3b2p1/p6p/1pRpR3/1P1P2q1/P4pP1/5QnP/1B4K1 w - - bm h3; id \"WAC.291\";", "4r3/1Q1qk2p/p4pp1/3Pb3/P7/6PP/5P2/4R1K1 w - - bm d6+; id \"WAC.292\";", "1nbq1r1k/3rbp1p/p1p1pp1Q/1p6/P1pPN3/5NP1/1P2PPBP/R4RK1 w - - bm Nfg5; id \"WAC.293\";", "3r3k/1r3p1p/p1pB1p2/8/p1qNP1Q1/P6P/1P4P1/3R3K w - - bm Bf8 Nf5 Qf4; id \"WAC.294\";", "4r3/p4r1p/R1p2pp1/1p1bk3/4pNPP/2P1K3/2P2P2/3R4 w - - bm Rxd5+; id \"WAC.295\";", "3r4/1p2k2p/p1b1p1p1/4Q1Pn/2B3KP/4pP2/PP2R1N1/6q1 b - - bm Rd4+ Rf8; id \"WAC.296\";", "3r1rk1/p3qp1p/2bb2p1/2p5/3P4/1P6/PBQN1PPP/2R2RK1 b - - bm Bxg2 Bxh2+; id \"WAC.297\";", "3Q4/p3b1k1/2p2rPp/2q5/4B3/P2P4/7P/6RK w - - bm Qh8+; id \"WAC.298\";", "1n2rr2/1pk3pp/pNn2p2/2N1p3/8/6P1/PP2PPKP/2RR4 w - - bm Nca4; id \"WAC.299\";", "b2b1r1k/3R1ppp/4qP2/4p1PQ/4P3/5B2/4N1K1/8 w - - bm g6; id \"WAC.300\";", NULL }; static const char *sts_test[] = { "1kr5/3n4/q3p2p/p2n2p1/PppB1P2/5BP1/1P2Q2P/3R2K1 w - - bm f5; id \"Undermine.001\"; c0 \"f5=10, Be5+=2, Bf2=3, Bg4=2\";", "1n5k/3q3p/pp1p2pB/5r2/1PP1Qp2/P6P/6P1/2R3K1 w - - bm c5; id \"Undermine.002\"; c0 \"c5=10, Qd4+=4, b5=4, g4=3\";", "1n6/4bk1r/1p2rp2/pP2pN1p/K1P1N2P/8/P5R1/3R4 w - - bm c5; id \"Undermine.003\"; c0 \"c5=10, Rd3=7, Rdd2=7, Rg3=7 Rd5=9\";", "1nr5/1k5r/p3pqp1/3p4/1P1P1PP1/R4N2/3Q1PK1/R7 w - - bm b5; id \"Undermine.004\"; c0 \"b5=10, Kg3=4, Ng5=4, Qe3=4\";", "1q2r1k1/1b2bpp1/p2ppn1p/2p5/P3PP1B/2PB1RP1/2P1Q2P/2KR4 b - - bm c4; id \"Undermine.005\"; c0 \"c4=10, Bc6=7, Qa8=7, Qc8=7\";", "1q4k1/5p1p/p1rprnp1/3R4/N1P1P3/1P6/P5PP/3Q1R1K w - - bm e5; id \"Undermine.006\"; c0 \"e5=10, Nc3=3, Qd3=1, Qf3=2\";", "1qr1k2r/1p2bp2/pBn1p3/P2pPbpp/5P2/2P1QBPP/1P1N3R/R4K2 b k - bm h4; id \"Undermine.007\"; c0 \"h4=10, Bd8=1, Bf8=1, Rh7=1\";", "1r1b2k1/2r2ppp/p1qp4/3R1NPP/1pn1PQB1/8/PPP3R1/1K6 w - - bm g6; id \"Undermine.008\"; c0 \"g6=10, Ka1=2, Nd4=2, Rd3=2\";", "1r1qk1nr/p3ppbp/3p2p1/1pp5/2bPP3/4B1P1/2PQNPBP/R2R2K1 w k - bm e5; id \"Undermine.009\"; c0 \"e5=10, Bf3=5, Nc1=5, Rxa7=4\";", "1r1r2k1/p3n2p/b1nqpbp1/2pp4/1p3PP1/2PP1N2/PPN3BP/R1BRQ2K w - - bm d4; id \"Undermine.010\"; c0 \"d4=10, Ng5=6, a4=6, h3=6\";", "1r2n1rk/pP2q2p/P2p4/4pQ2/2P2p2/5B1P/3R1P1K/3R4 w - - bm c5; id \"Undermine.011\"; c0 \"c5=10, Bc6=6, Qc2=5, Rg1=5\";", "1r3bk1/7p/pp1q2p1/P1pPp3/2P3b1/4B3/1P1Q2BP/R6K w - - bm b4; id \"Undermine.012\"; c0 \"b4=10, Bg1=3, axb6=3, h3=1\";", "1r3rk1/3n1pbp/1q1pp1p1/p1p5/2PnPP2/PPB1N1PP/6B1/1R1Q1RK1 b - - bm a4; id \"Undermine.013\"; c0 \"a4=10, Ne2+=2, Rfd8=2, h5=2\";", "1r3rk1/p5bp/6p1/q1pPppn1/7P/1B1PQ1P1/PB3P2/R4RK1 b - - bm f4; id \"Undermine.014\"; c0 \"f4=10, Nf7=4, c4=5, e4=5\";", "1r4k1/1rq2pp1/3b1nn1/pBpPp3/P1N4p/2PP1Q1P/6PB/2R2RK1 w - - bm d4; id \"Undermine.015\"; c0 \"d4=10, Bc6=4, Qf5=6, Rce1=4\";", "1r4k1/p1rqbp1p/b1p1p1p1/NpP1P3/3PB3/3Q2P1/P4P1P/3RR1K1 w - - bm a4; id \"Undermine.016\"; c0 \"a4=10, Kg2=4, Qf3=4, h4=5\";", "2r3k1/p2q1pp1/Pbrp3p/6n1/1BP1PpP1/R4P2/2QN2KP/1R6 b - - bm h5; id \"Undermine.017\"; c0 \"h5=10, Be3=4, Ne6=4, Qd8=4\";", "1r6/2q2pk1/2n1p1pp/p1Pr4/P1RP4/1p1RQ2P/1N3PP1/7K b - - bm e5; id \"Undermine.018\"; c0 \"e5=10, Kh7=4, Qb7=5, Rbd8=5\";", "1r6/R1nk1p2/1p4pp/pP1p1P2/P2P3P/5PN1/5K2/8 w - - bm h5; id \"Undermine.019\"; c0 \"h5=10, Ne2=4, Nf1=7, f4=7\";", "1rb3k1/2pn2pp/p2p4/4p3/1pP4q/1P1PBP1P/1PQ2P2/R3R1K1 w - - bm c5; id \"Undermine.020\"; c0 \"c5=10, Kf1=5, Kg2=5, Ra5=5\";", "1rbqnrk1/6bp/pp3np1/2pPp3/P1P1N3/2N1B3/1P2Q1BP/R4R1K w - - bm a5; id \"Undermine.021\"; c0 \"a5=10, Bg5=6, Kg1=6, Nxf6+=6\";", "1rr3k1/1q3pp1/pnbQp2p/1p2P3/3B1P2/2PB4/P1P2RPP/R5K1 w - - bm f5; id \"Undermine.022\"; c0 \"f5=10, Qa3=3, Rd1=2, h3=2\";", "2kr2r1/1bpnqp2/1p1ppn2/p5pp/P1PP4/4PP2/1P1NBBPP/R2Q1RK1 w - - bm b4; id \"Undermine.023\"; c0 \"b4=10, Qb1=3, Qc2=4, Re1=4\";", "2b1k2r/5p2/pq1pNp1b/1p6/2r1PPBp/3Q4/PPP3PP/1K1RR3 w k - bm e5; id \"Undermine.024\"; c0 \"e5=10, Qd5=1, Qh3=7, f5=1\";", "2b1r1k1/1p6/pQ1p1q1p/P2P3P/2P1pPpN/6P1/4R1K1/8 w - - bm c5; id \"Undermine.025\"; c0 \"c5=10, Kg1=6, Kh2=5, Re3=5\";", "2b2rk1/2qn1p2/p2p2pp/2pPP3/8/4NN1P/P1Q2PP1/bB2R1K1 w - - bm e6; id \"Undermine.026\"; c0 \"e6=10, Nc4=1, Ng4=1, exd6=2\";", "2bq2k1/1pr3bp/1Qpr2p1/P2pNp2/3P1P1P/6P1/5PB1/1RR3K1 w - - bm a6; id \"Undermine.027\"; c0 \"a6=10, Qc5=2, Rc5=3, h5=2\";", "rr6/8/2pbkp2/ppp1p1p1/P3P3/1P1P1PB1/R1P2PK1/R7 b - - bm c4; id \"Undermine.28\"; c0 \"c4=10, Bc7=4, Rb6=1, b4=1\";", "2r2rk1/pb2q2p/1pn1p2p/5p1Q/3P4/P1NB4/1P3PPP/R4RK1 w - - bm d5; id \"Undermine.029\"; c0 \"d5=10, Qxh6=5, Rac1=5, Rfd1=5\";", "2kr4/ppqnbp1r/2n1p1p1/P2pP3/3P2P1/3BBN2/1P1Q1PP1/R4RK1 w - - bm a6; id \"Undermine.030\"; c0 \"a6=10, Qc2=3, Rfc1=4, g3=4\";", "2q5/1pb2r1k/p1b3pB/P1Pp3p/3P4/3B1pPP/1R3P1K/2Q5 b - - bm h4; id \"Undermine.031\"; c0 \"h4=10, Bb5=5, Bd7=1, Qd8=5\";", "2r1kb1r/1bqn1pp1/p3p3/1p2P1P1/3Np3/P1N1B3/1PP1Q2P/R4RK1 w k - bm g6; id \"Undermine.032\"; c0 \"g6=10, Bf4=6, Rae1=6, Rf4=6\";", "2r1rb2/1bq2p1k/3p1np1/p1p5/1pP1P1P1/PP2BPN1/2Q3P1/R2R1BK1 b - - bm d5; id \"Undermine.033\"; c0 \"d5=10, Bg7=7, Kg8=7, Nd7=7\";", "2r2bk1/pq3r1p/6p1/2ppP1P1/P7/BP1Q4/2R3P1/3R3K b - - bm d4; id \"Undermine.034\"; c0 \"d4=10, Qe7=1, Rcc7=1, c4=1\";", "2r2rk1/1bb2ppp/p2ppn2/1p4q1/1PnNP3/P1N4P/2P1QPPB/3RRBK1 w - - bm a4; id \"Undermine.035\"; c0 \"a4=10, Nf3=4, Rb1=5, Rd3=5\";", "2r2rk1/3q3p/p3pbp1/1p1pp3/4P3/2P5/PPN1QPPP/3R1RK1 b - - bm d4; id \"Undermine.036\"; c0 \"d4=10, Bg7=7, Qb7=6, Qd6=7\";", "2r4k/pp3q1b/5PpQ/3p4/3Bp3/1P6/P5RP/6K1 w - - bm h4; id \"Undermine.037\"; c0 \"h4=10, Rg3=2, Rg4=2, Rg5=2\";", "2r3k1/1b2b2p/r2p1pp1/pN1Pn3/1pPB2P1/1P5P/P3R1B1/5RK1 w - - bm g5; id \"Undermine.038\"; c0 \"g5=10, Rd1=4, Rff2=4, h4=5\";", "2r3k1/5pp1/1pq4p/p7/P1nR4/2P2P2/Q5PP/4B1K1 b - - bm b5; id \"Undermine.039\"; c0 \"b5=10, Nd6=1, Ne3=1, Ne5=1\";", "6k1/6pp/4r3/p1qpp3/Pp6/1n1P1B1P/1B2Q1P1/3R1K2 w - - bm d4; id \"Undermine.040\"; c0 \"d4=10, Bxe5=3, Qf2=2, Re1=3\";", "r2qkb1r/1b1n1ppp/p3pn2/1pp5/3PP3/2NB1N2/PP3PPP/R1BQ1RK1 w kq - bm d5; id \"Undermine.041\"; c0 \"d5=10, Be3=3, a3=2, e5=3\";", "r3r1k1/pn1bnpp1/1p2p2p/1q1pPP2/1BpP3N/2P2BP1/2P3QP/R4RK1 w - - bm f6; id \"Undermine.42\"; c0 \"f6=10, Bxe7=6, Rab1=5, g4=5\";", "2r5/p3kpp1/1pn1p2p/8/1PP2P2/PB1R1KP1/7P/8 b - - bm a5; id \"Undermine.043\"; c0 \"a5=10, a6=1, e5=1, f5=1\";", "2rq1rk1/1b2bppp/p2p1n2/1p1Pp3/1Pn1P3/5N1P/P1B2PP1/RNBQR1K1 w - - bm a4; id \"Undermine.044\"; c0 \"a4=10, Bb3=1, Nbd2=1, Nc3=1\";", "2rqr1k1/1b2bp1p/ppn1p1pB/3n4/3P3P/P1NQ1N2/1PB2PP1/3RR1K1 w - - bm h5; id \"Undermine.045\"; c0 \"h5=10, Bc1=4, Nxd5=1, Rb1=1\";", "3Rb3/5ppk/2r1r3/p5Pp/1pN2P1P/1P5q/P4Q2/K2R4 b - - bm a4; id \"Undermine.046\"; c0 \"a4=10, Rc7=7, Re7=6, Rxc4=7\";", "3Rbrk1/4Q2p/6q1/pp3p2/4p2P/1P4P1/8/5R1K w - - bm g4; id \"Undermine.047\"; c0 \"g4=10, Kh2=5, Rc8=2, Rf2=3\";", "3bn3/3r1p1k/3Pp1p1/1q6/Np2BP1P/3R2PK/8/3Q4 w - - bm h5; id \"Undermine.048\"; c0 \"h5=10, Bf3=6, Rd4=6, g4=6\";", "3k1r1r/p2n1p1p/q2p2pQ/1p2P3/2pP4/P4N2/5PPP/2R1R1K1 w - - bm a4; id \"Undermine.049\"; c0 \"a4=10, Ng5=5, Qh4+=8, exd6=6\";", "3r1bk1/1p2qp1p/p5p1/P1pPp3/2QnP3/3BB3/1P3PPP/2R3K1 w - - bm f4; id \"Undermine.050\"; c0 \"f4=10, Rb1=4, Rf1=4, h3=4\";", "3r1bkr/2q3pp/1p1Npp2/pPn1P3/5B2/1P6/2P2PPP/R2QR1K1 w - - bm b4; id \"Undermine.051\"; c0 \"b4=10, Bd2=5, Qf3=4, exf6=3\";", "3r2k1/p2q1pp1/1p2n1p1/2p1P2n/P4P2/2B1Q1P1/7P/1R3BK1 w - - bm a5; id \"Undermine.052\"; c0 \"a5=10, Bb5=3, Qe4=1, Ra1=3\";", "3r4/8/pq3kr1/3Bp3/7p/1P3P2/P5PP/3RQ2K b - - bm h3; id \"Undermine.053\"; c0 \"h3=10, Kg7=5, Rd7=4, Rh6=5\";", "3r4/pk1p3p/1p2pp2/1N6/2P1KP2/6P1/3R3P/8 w - - bm f5; id \"Undermine.054\"; c0 \"f5=10, Kd4=5, Ke3=5, Nc3=5\";", "4k2r/1b2b3/p3pp1p/1p1p4/3BnpP1/P1P4R/1KP4P/5BR1 w k - bm g5; id \"Undermine.055\"; c0 \"g5=10, Be2=5, a4=6, c4=5\";", "4k3/r2bbprp/3p1p1N/2qBpP2/ppP1P1P1/1P1R3P/P7/1KR1Q3 w - - bm a3; id \"Undermine.056\"; c0 \"a3=10, Qd2=3, Rc2=3, h4=3\";", "4q1k1/pb5p/Nbp1p1r1/3r1p2/PP1Pp1pP/4P1P1/1BR1QP2/2R3K1 w - - bm b5; id \"Undermine.057\"; c0 \"b5=10, Ba1=3, Kg2=3, a5=3\";", "4r1k1/1pb3qp/p1b1r1p1/P1Pp4/3P1p2/2BB4/1R1Q1PPP/1R4K1 b - - bm f3; id \"Undermine.058\"; c0 \"f3=10, Qd7=7, Qe7=7, Qf6=7\";", "4r1k1/5p1p/p2q2p1/3p4/3Qn3/2P1RN2/Pr3PPP/R5K1 w - - bm c4; id \"Undermine.059\"; c0 \"c4=10, Ree1=5, a3=5, g3=5\";", "4rr1k/pp1n2bp/7n/1Pp1pp1q/2Pp3N/1N1P1PP1/P5QP/2B1RR1K b - - bm f4; id \"Undermine.060\"; c0 \"f4=10, Nf7=3, Rf7=4, b6=1\";", "4rrk1/p6p/2q2pp1/1p6/2pP1BQP/5N2/P4PP1/2R3K1 w - - bm h5; id \"Undermine.061\"; c0 \"h5=10, Bd2=1, Qg3=1, a4=1\";", "5nk1/1bp1rnp1/pp1p4/4p1P1/2PPP3/NBP5/P2B4/4R1K1 w - - bm c5; id \"Undermine.062\"; c0 \"c5=10, Kf1=7, Kf2=7, d5=7\";", "5r2/1p1k4/2bp4/r3pp1p/PRP4P/2P2PP1/2B2K2/7R b - - bm f4; id \"Undermine.063\"; c0 \"f4=10, Kc7=4, Raa8=4, Rf7=4\";", "5r2/5p1Q/4pkp1/p7/1pb2q1P/5P2/P4RP1/3R2K1 w - - bm h5; id \"Undermine.064\"; c0 \"h5=10, Rb2=1, Rd7=1, Rdd2=1\";", "5rk1/1Q3pp1/p2p3p/4p1b1/N3PqP1/1N1K4/PP6/3R4 b - - bm d5; id \"Undermine.065\"; c0 \"d5=10, Qf3+=1, Qxg4=1, h5=1\";", "7r/3nkpp1/4p3/p1pbP3/1r3P1p/1P2B2P/P2RBKP1/7R b - - bm a4; id \"Undermine.066\"; c0 \"a4=10, Rc8=6, Rd8=6, Rhb8=6\";", "8/1r1rq2k/2p3p1/3b1p1p/4p2P/1N1nP1P1/2Q2PK1/RR3B2 b - - bm f4; id \"Undermine.067\"; c0 \"f4=10, Rb4=3, c5=3, g5=3\";", "8/1r2k3/4p2p/R3K2P/1p1P1P2/1P6/8/8 w - - bm f5; id \"Undermine.068\"; c0 \"f5=10, Ke4=4, Rc5=4, d5=3\";", "8/3r1pp1/p7/2k2PpP/rp1pB3/2pK1P2/P1R5/1R6 w - - bm f6; id \"Undermine.069\"; c0 \"f6=10, Rg1=1, Rh1=1, f4=2\";", "8/6k1/3P1bp1/2B1p3/1P6/1Q3P1q/7r/1K2R3 b - - bm e4; id \"Undermine.070\"; c0 \"e4=10, Qc8=1, Qf5+=1, g5=1\";", "b2rrbk1/2q2p1p/pn1p2p1/1p4P1/2nNPB1P/P1N3Q1/1PP3B1/1K1RR3 w - - bm h5; id \"Undermine.071\"; c0 \"h5=10, Bc1=6, Na2=6, Qh3=6\";", "b7/2pr1kp1/1p3p2/p2p3p/P1nP1N2/4P1P1/P1R2P1P/2R3K1 w - - bm e4; id \"Undermine.072\"; c0 \"e4=10, Rc3=5, Re2=6, f3=5\";", "k1qbr1n1/1p4p1/p1p1p1Np/2P2p1P/3P4/R7/PP2Q1P1/1K1R4 w - - bm d5; id \"Undermine.073\"; c0 \"d5=10, Ra4=3, Rdd3=4, g4=3\";", "r1b1rnk1/pp3pq1/2p3p1/6P1/2B2P1R/2P5/PP1Q2P1/2K4R w - - bm f5; id \"Undermine.074\"; c0 \"f5=10, Bd3=3, Rh8+=1, g4=4\";", "r1bq1rk1/pp3pbp/3Pp1p1/2p5/4PP2/2P5/P2QB1PP/1RB1K2R b K - bm e5; id \"Undermine.075\"; c0 \"e5=10, Bd7=7, Qh4+=7, b6=6\";", "r1bqr2k/pppn2bp/4n3/2P1p1p1/1P2Pp2/5NPB/PBQN1P1P/R4RK1 w - - bm c6; id \"Undermine.076\"; c0 \"c6=10, Nb3=5, Nc4=5, Rac1=5\";", "r1br1k2/1pq2pb1/1np1p1pp/2N1N3/p2P1P1P/P3P1R1/1PQ3P1/1BR3K1 w - - bm h5; id \"Undermine.077\"; c0 \"h5=10, Ba2=4, Re1=4, Rf1=5\";", "r1n2k1r/5pp1/2R5/pB2pPq1/P2pP3/6Pp/1P2Q2P/5RK1 w - - bm f6; id \"Undermine.078\"; c0 \"f6=10, Qd3=2, Rc5=2, Rg6=2\";", "r1r2bk1/pp1n1p1p/2pqb1p1/3p4/1P1P4/1QN1PN2/P3BPPP/2RR2K1 w - - bm b5; id \"Undermine.079\"; c0 \"b5=10, Qc2=5, Rb1=4, a3=4\";", "r2q1r2/pp1b2kp/2n1p1p1/3p4/3P1P1P/2PB1N2/6P1/R3QRK1 w - - bm h5; id \"Undermine.080\"; c0 \"h5=10, Qe3=3, Rb1=4, g3=4\";", "r2q1rk1/pp2b1pp/1np1b3/4pp2/1P6/P1NP1BP1/2Q1PP1P/1RB2RK1 w - - bm b5; id \"Undermine.081\"; c0 \"b5=10, Be3=1, Bg2=1, Re1=1\";", "r2q4/6k1/r1p3p1/np1p1p2/3P4/4P1P1/R2QBPK1/7R w - - bm e4; id \"Undermine.082\"; c0 \"e4=10, Qb2=5, Qc3=5, Rc2=2\";", "r2qr1k1/pp3pbp/5np1/2p2b2/8/2PP1Q2/PPB3PP/RNB2RK1 b - - bm c4; id \"Undermine.083\"; c0 \"c4=10, Bg4=3, Ng4=2, Qd7=3\";", "r3k2r/1bq1bpp1/p4n2/2p1pP2/2NpP2p/3B4/PPP3PP/R1B1QR1K b k - bm h3; id \"Undermine.084\"; c0 \"h3=10, Bc6=3, Bd8=1, Kf8=1\";", "r3k2r/2q2p2/p2bpPpp/1b1p4/1p1B1PPP/8/PPPQ4/1K1R1B1R w kq - bm f5; id \"Undermine.085\"; c0 \"f5=10, Be3=6, Bxb5+=4, h5=4\";", "r3k2r/ppq2p1p/2n1p1p1/3pP3/5PP1/2P1Q3/PP2N2P/3R1RK1 b k - bm h5; id \"Undermine.086\"; c0 \"h5=10, O-O=3, Qb6=1, Rc8=1, Rc8=3\";", "r3r1k1/1pp1np1p/1b1p1p2/pP2p3/2PP2b1/P3PN2/1B3PPP/R3KB1R w KQ - bm c5; id \"Undermine.087\"; c0 \"c5=10, Be2=3, Rd1=3, dxe5=3\";", "r3r1k1/1pq2pbp/p1ppbnp1/4n3/2P1PB2/1NN2P2/PP1Q2PP/R3RBK1 w - - bm c5; id \"Undermine.088\"; c0 \"c5=10, Bxe5=7, Nd1=7, Red1=7\";", "r3r1k1/bpp1np1p/3p1p2/pPP1p3/3P2b1/P3PN2/1B3PPP/R3KB1R w KQ - bm b6; id \"Undermine.089\"; c0 \"b6=10, Rc1=3, Rd1=2, h3=5\";", "r3r1k1/pp2q3/2b1pp2/6pN/Pn1P4/6R1/1P3PP1/3QRBK1 w - - bm f4; id \"Undermine.090\"; c0 \"f4=10, Qd2=4, b3=5, f3=3\";", "r4r2/1p2pbk1/1np1qppp/p7/3PP2P/P1Q2NP1/1P3PB1/2R1R1K1 w - - bm h5; id \"Undermine.091\"; c0 \"h5=10, Qc5=5, Qe3=3, b4=2\";", "r4r2/2p2kb1/1p1p2p1/qPnPp2n/2B1PP2/pP6/P1Q1N2R/1KB4R w - - bm f5; id \"Undermine.092\"; c0 \"f5=10, Bd2=5, Rg1=3, Rg2=4\";", "r4rk1/2p5/p2p1n2/1p1P3p/2P1p1pP/1P4B1/1P3PP1/3RR1K1 w - - bm c5; id \"Undermine.093\"; c0 \"c5=10, Rc1=5, Rd2=4, Re2=6\";", "r4rk1/2qnb1pp/4p3/ppPb1p2/3Pp3/1PB3P1/R1QNPPBP/R5K1 b - - bm a4; id \"Undermine.094\"; c0 \"a4=10, Bf6=6, Qc6=6, e5=6\";", "r4rk1/p5pp/1p2b3/2Pn1p2/P2Pp2P/4P1Pq/2Q1BP2/R1BR2K1 w - - bm a5; id \"Undermine.095\"; c0 \"a5=10, Bc4=5, Bf1=5, Rb1=6\";", "r4rk1/pbq2p2/2p2np1/1p2b2p/4P3/2N1BPP1/PPQ1B2P/R2R2K1 b - - bm h4; id \"Undermine.096\"; c0 \"h4=10, Rfd8=2, a5=3, a6=2\";", "r4rk1/pp1b2b1/n2p1nq1/2pP1p1p/2P1pP2/PP4PP/1BQ1N1B1/R3RNK1 b - - bm h4; id \"Undermine.097\"; c0 \"h4=10, Rab8=1, Rae8=1, Rf7=1\";", "rn3rk1/p1p1qp2/1pbppn1p/6p1/P1PP4/2PBP1B1/3N1P1P/R2QK1R1 w Q - bm h4; id \"Undermine.098\"; c0 \"h4=10, Qe2=1, a5=1, f4=1\";", "rnbq1rk1/2p1p1bp/p3pnp1/1p6/3P4/1QN1BN2/PP3PPP/R3KB1R w KQ - bm a4; id \"Undermine.099\"; c0 \"a4=10, Rc1=6, g3=6, h3=6\";", "rr3n1k/q3bpn1/2p1p1p1/2PpP2p/pP1P1N1P/2BB1NP1/P2Q1P2/6RK w - - bm g4; id \"Undermine.100\"; c0 \"g4=10, Kh2=4, Qc1=2, a3=3\";", "1b1r4/3rkp2/p3p2p/4q3/P5P1/2RBP3/P1Q4P/1R3K2 b - - bm Ba7; c0 \"Ba7=10, Qf6+=3, a5=3, h5=5\"; id \"STS(v2.2) Open Files and Diagonals.001\";", "1bq3rk/R6p/5n1P/1N1p4/1PnP2p1/6P1/5B2/2Q2BK1 w - - bm Re7; c0 \"Re7=10, Ra2=5, Rg7=2\"; id \"STS(v2.2) Open Files and Diagonals.002\";", "1k1r3r/1p1b1Q1p/p7/q3p3/4p3/2P1N3/P4PPP/R4RK1 w - - bm Rad1; c0 \"Rad1=10, Qf6=7, Rfd1=7, a4=2\"; id \"STS(v2.2) Open Files and Diagonals.003\";", "1Q6/1b4pk/2q2b1p/1p1ppP2/1Pp5/2P2P1P/2BB2P1/6K1 b - - bm Qa6; c0 \"Qa6=10, Bc8=5, Qd7=7\"; id \"STS(v2.2) Open Files and Diagonals.004\";", "1qrr3k/1p2bp1p/1n2p1pP/p2pP3/P4B2/1PPB2P1/2R1QP2/3R2K1 w - - bm Bb5; c0 \"Bb5=10, Qe1=2, Qe3=2\"; id \"STS(v2.2) Open Files and Diagonals.005\";", "1r1n1rk1/3qp2p/P2p2p1/1p6/5pP1/1p3P1P/5PB1/R1QR2K1 w - - bm Bf1; c0 \"Bf1=10, Qb2=7, Qc3=7, Qd2=6\"; id \"STS(v2.2) Open Files and Diagonals.006\";", "1r1n2k1/5r1p/P2qp1p1/3p4/1p3pP1/1Q3P1P/R4P2/2R2BK1 w - - bm Rac2; c0 \"Rac2=10, Kg2=5, Qa4=4\"; id \"STS(v2.2) Open Files and Diagonals.007\";", "1r1q1nk1/p3bpp1/b1p1p2p/4P3/1P2NB2/P4N2/5PPP/2Q1R1K1 w - - bm Be3; c0 \"Be3=10, Nd6=5, Rd1=7, h3=6\"; id \"STS(v2.2) Open Files and Diagonals.008\";", "1r1q4/5p2/2p4k/1n2p1pP/4P3/P4BR1/3Q1PKP/8 w - - bm Qc1; c0 \"Qc1=10, Qa2=5, Qe3=6\"; id \"STS(v2.2) Open Files and Diagonals.009\";", "1r1qbr1k/4bp1p/p3p2Q/3pP3/2pP4/P1N1PN2/1PR2RP1/6K1 b - - bm Rg8; c0 \"Rg8=10, Bc6=1, Bd7=4\"; id \"STS(v2.2) Open Files and Diagonals.010\";", "1r3k2/8/R7/4pPP1/P1p5/1nP5/R5P1/3rB1K1 w - - bm Rh6; c0 \"Rh6=10, Kf1=6, Rf6+=6\"; id \"STS(v2.2) Open Files and Diagonals.011\";", "1r3rk1/2b3pp/p4p2/2p5/P1Np4/1P1R2P1/1P3P1P/2R3K1 b - - bm Rfe8; c0 \"Rfe8=10, Rfc8=7, Rfd8=8\"; id \"STS(v2.2) Open Files and Diagonals.012\";", "1r5k/p7/3pQ1p1/1Np5/2P3P1/7P/1PK5/7q b - - bm Qf1; c0 \"Qf1=10, Qg2+=2, Qh2+=2\"; id \"STS(v2.2) Open Files and Diagonals.013\";", "1r6/4q3/2p2p1k/4p1pP/P2nP2P/5BR1/5PK1/2Q5 w - - bm Bg4; c0 \"Bg4=10, Bd1=6, a5=8\"; id \"STS(v2.2) Open Files and Diagonals.014\";", "1r6/7k/1p4r1/1P2p3/2P1p2P/2RbB3/6P1/2R4K w - - bm Ra1; c0 \"Ra1=10, Kg1=7, Kh2=8\"; id \"STS(v2.2) Open Files and Diagonals.015\";", "1r6/8/1r5p/p2pk3/4p3/2P4P/RP2B1n1/1RK5 b - - bm Rf6; c0 \"Rf6=10, Rf8=8, Rg6=6\"; id \"STS(v2.2) Open Files and Diagonals.016\";", "1rbr2k1/pp3pp1/q6p/4P3/3p1P2/6P1/P2QP1BP/R1R3K1 w - - bm Be4; c0 \"Be4=10, Bf3=4, Qd3=5\"; id \"STS(v2.2) Open Files and Diagonals.017\";", "2b1r1k1/2r5/p3pbp1/5p2/PN5P/1Pp2BP1/4PP2/2R2RK1 w - - bm Rfd1; c0 \"Rfd1=10\"; id \"STS(v2.2) Open Files and Diagonals.018\";", "2r1k3/p5pp/1p2p3/7P/P2B2Q1/1bP5/R4PPK/4q3 w - - bm Re2; c0 \"Re2=10, Qxg7=4, Rb2=4\"; id \"STS(v2.2) Open Files and Diagonals.019\";", "2r1q1k1/2p2rbp/p2p2p1/Q2P4/b3P3/3p2P1/3B1PBP/2R1R1K1 w - - bm Bh3; c0 \"Bh3=10, Bf1=7, e5=8\"; id \"STS(v2.2) Open Files and Diagonals.020\";", "2r1q2k/7p/p1np1P1P/8/1pP2R2/8/PP1Q4/R1KN2r1 b - - bm Qg6; c0 \"Qg6=10, Ne5=4, Qe5=2\"; id \"STS(v2.2) Open Files and Diagonals.021\";", "2r1q3/p1p1nk1p/4p2p/2R1Pp2/1P1Q4/P3N1P1/5P2/6K1 w - - bm Qh4; c0 \"Qh4=10, Rc1=6, Rc2=9, Rc4=6\"; id \"STS(v2.2) Open Files and Diagonals.022\";", "2r2k2/R4bpp/2p2p2/1pN5/1n3PP1/1P2P2P/8/5BK1 w - - bm Bg2; c0 \"Bg2=10, e4=6\"; id \"STS(v2.2) Open Files and Diagonals.023\";", "2r2rk1/1p1nbppp/p2p4/B2Pp1PP/5P2/1P6/P1P5/1K1R1B1R w - - bm Bh3; c0 \"Bh3=10, Bd2=5, Re1=6\"; id \"STS(v2.2) Open Files and Diagonals.024\";", "2r2rk1/4q1pp/p7/1pb1Pp2/5P2/1P1QB3/b3N1PP/2R1K2R b K - bm Rfd8; c0 \"Rfd8=10, Bxe3=5, Kh8=3\"; id \"STS(v2.2) Open Files and Diagonals.025\";", "2r3k1/1b1n1npp/1pq2p2/8/1P1QP3/6P1/1B1NBP1P/3R2K1 w - - bm Rc1; c0 \"Rc1=10, Bg4=7, f3=7, f4=5\"; id \"STS(v2.2) Open Files and Diagonals.026\";", "2r3n1/p1p3kp/1q2p2p/4Pp2/1P4N1/P5P1/1Q3P2/2R3K1 w - - bm Qd2; c0 \"Qd2=10, Ne3=9, Nf6=5\"; id \"STS(v2.2) Open Files and Diagonals.027\";", "2rq2k1/1p3pb1/1n4pp/pP2p3/P1b1P3/2N4P/2B1NPP1/R1Q3K1 b - - bm Bf8; c0 \"Bg7f8=10, Bg7f6=8, Qd8d6=7\"; id \"STS(v2.2) Open Files and Diagonals.028\";", "2rq2k1/4rpp1/p3p3/P2n2pP/2pPR3/2P3B1/2Q2PP1/R5K1 b - - bm Rb7; c0 \"Rb7=10, Ra7=4, Rd7=3\"; id \"STS(v2.2) Open Files and Diagonals.029\";", "2rr1b2/3q3k/p4n1p/1p1p2p1/2nNp3/P1N2PQ1/1PP3PP/R1BR2K1 b - - bm Bd6; c0 \"Bd6=10, Bc5=7, Bg7=5\"; id \"STS(v2.2) Open Files and Diagonals.030\";", "3b2k1/1p1b4/4p2B/1n1p1p2/pN6/P2P4/1PR3PP/7K w - - bm Be3; c0 \"Be3=10, Bf4=7, Re2=7\"; id \"STS(v2.2) Open Files and Diagonals.031\";", "3q2k1/5rpp/r1pPp3/1bQn4/p2B4/4P1P1/1P1R2BP/R5K1 w - - bm Bh3; c0 \"Bh3=10, Rc1=8, Rc2=7\"; id \"STS(v2.2) Open Files and Diagonals.032\";", "3Q4/5pk1/4p1p1/3pb2p/P3q2P/1r4P1/2R2P1K/2R5 b - - bm Bf4; c0 \"Bf4=10, Ra3=3, Rd3=4\"; id \"STS(v2.2) Open Files and Diagonals.033\";", "3q4/7k/3ppp2/p3n2B/P1r1P2P/8/1PPQ4/1K3R2 w - - bm Rg1; c0 \"Rg1=10, Qe3=7, Qg2=5\"; id \"STS(v2.2) Open Files and Diagonals.034\";", "3r1k2/R4bpp/2p2p2/1pN5/1n3PP1/1P2P2P/6B1/6K1 w - - bm Be4; c0 \"Be4=10, Bf3=2, g5=3\"; id \"STS(v2.2) Open Files and Diagonals.035\";", "3r1r2/p5k1/1pnpP2p/6p1/2P3P1/4P3/P1B1K1P1/3R3R w - - bm Ba4; c0 \"Ba4=10, Rdf1=6, Rhf1=7\"; id \"STS(v2.2) Open Files and Diagonals.036\";", "3r2k1/4q1pp/p7/1p2Pp2/5P2/1P2Q3/b5PP/2N1K2R b K - bm Bb1; c0 \"Bb1=10\"; id \"STS(v2.2) Open Files and Diagonals.037\";", "3r2k1/pp1q1ppp/4rnn1/3p4/P1pP4/2P1P2P/1BQN1PP1/RR4K1 w - - bm Ba3; c0 \"Ba3=10, Qd1=4, Qf5=4\"; id \"STS(v2.2) Open Files and Diagonals.038\";", "3r2k1/R7/1p3np1/1P1b4/1p6/5PqP/2Q3P1/3R2K1 b - - bm Re8; c0 \"Re8=10, Rf8=4\"; id \"STS(v2.2) Open Files and Diagonals.039\";", "3r3k/1p2R2p/pP4p1/8/2rp1p2/5P2/2P3PP/4R1K1 w - - bm Rc7; c0 \"Rc7=10\"; id \"STS(v2.2) Open Files and Diagonals.040\";", "3r4/3pkpp1/4p3/2p3q1/6r1/1P6/P5B1/2R1RQK1 b - - bm Rh8; c0 \"Rh8=10, Ra8=1, f5=1\"; id \"STS(v2.2) Open Files and Diagonals.041\";", "3r4/3q2bk/3rp1p1/1p5p/4QP2/R1Pp2P1/P2B1R1P/6K1 b - - bm Rc8; c0 \"Rc8=10, Rc6=6, Re8=6\"; id \"STS(v2.2) Open Files and Diagonals.042\";", "3r4/p2rppk1/2R3p1/4q3/3b4/PP4P1/2QRNP2/5K2 b - - bm Qh5; c0 \"Qh5=10, Bb6=2, Qd5=2\"; id \"STS(v2.2) Open Files and Diagonals.043\";", "3rr1k1/1p2bppp/p1p5/P2P3q/2B1PPb1/1P1R4/3B1QP1/4R1K1 w - - bm Bc3; c0 \"Bc3=10, b4=8, f5=4\"; id \"STS(v2.2) Open Files and Diagonals.044\";", "3rr1k1/pp2q1bp/n1p1P1p1/5p2/P2N1Pb1/1BP5/6PP/R1B1QRK1 w - - bm Ba3; c0 \"Ba3=10, Ra2=5, h3=5\"; id \"STS(v2.2) Open Files and Diagonals.045\";", "4b2k/1q2bprp/pr6/3pP3/2pP4/P4N2/1P3RPQ/3N1RK1 b - - bm Bd7; c0 \"Bd7=10, Bd8=8, Rg8=1\"; id \"STS(v2.2) Open Files and Diagonals.046\";", "4k2r/p2n3p/4q1pQ/1p2b2P/8/P1P2R2/1P4P1/K4R2 w - - bm Rd1; c0 \"Rd1=10, Re1=9, Re3=5\"; id \"STS(v2.2) Open Files and Diagonals.047\";", "4qrk1/pb4p1/2p1p2p/PpP1Pr2/1P2QP1R/2B3P1/6R1/6K1 w - - bm Rd2; c0 \"Rd2=10, Be1=9, Ra2=9\"; id \"STS(v2.2) Open Files and Diagonals.048\";", "4r1k1/3q1ppb/2p5/2Pp1P1p/p2P3P/P3P1PB/5Q1K/4R3 w - - bm Rb1; c0 \"Rb1=10, Qf4=5, Re2=2, Re2=5\"; id \"STS(v2.2) Open Files and Diagonals.049\";", "4r1k1/pbq2rpp/1p2p3/4P2P/P1p4Q/1nP1B3/R1B2PP1/1R4K1 w - - bm Bg6; c0 \"Bg6=10, Qg5=1, Rd1=3, a5=4\"; id \"STS(v2.2) Open Files and Diagonals.050\";", "4r1k1/R5p1/4b2p/1p2P3/7P/P1nP4/6PK/4R3 w - - bm Rc1; c0 \"Rc1=10, Ra6=7, d4=6\"; id \"STS(v2.2) Open Files and Diagonals.051\";", "4r2k/1pp1n1pp/pb3r2/6Nq/P2P4/2PQ2P1/1R3RKP/2B5 w - - bm Rbe2; c0 \"Rbe2=10, Rf3=4, Rxf6=3, a5=3, h3=4, h4=3\"; id \"STS(v2.2) Open Files and Diagonals.052\";", "4rrk1/1b3pp1/1q3b1p/p2p1P2/3N4/2P3N1/1P3QPP/1R3R1K b - - bm Ba6; c0 \"Ba6=10\"; id \"STS(v2.2) Open Files and Diagonals.053\";", "5b2/1p1q3n/pB1p2k1/P4N1p/6pP/4R3/6P1/5RK1 w - - bm Bd4; c0 \"Bd4=10\"; id \"STS(v2.2) Open Files and Diagonals.054\";", "5k2/ppQbbp1p/8/5p2/P7/1P6/4KPPP/q4B1R b - - bm Bb4; c0 \"Bb4=10, Qa2+=2, Qb2+=2\"; id \"STS(v2.2) Open Files and Diagonals.055\";", "5r1k/6p1/p1n1pr1p/2P1p2q/P5RN/4Q2P/5PP1/2R4K w - - bm Rb1; c0 \"Rb1=10, Qd3=6, Rd1=7\"; id \"STS(v2.2) Open Files and Diagonals.056\";", "5rk1/p3qpb1/1p5p/5Q2/P7/1B2P3/1p3PPP/5RK1 b - - bm Rd8; c0 \"Rd8=10, Qb4=6, Qe5=5\"; id \"STS(v2.2) Open Files and Diagonals.057\";", "5rk1/p7/1n1q1p2/1Prp1pNp/8/5NPP/P2Q1PB1/5RK1 w - - bm Re1; c0 \"Re1=10, Qe3=4, a4=3\"; id \"STS(v2.2) Open Files and Diagonals.058\";", "5rk1/pp4p1/5rnp/3p1b2/2p2P2/P1P3P1/2P1B2P/R1B1R1K1 w - - bm Bf3; c0 \"Bf3=10, Bd1=2, Be3=7\"; id \"STS(v2.2) Open Files and Diagonals.059\";", "5rrk/pR4p1/3p2q1/P6p/2Q1pn2/7P/1PP2PP1/R3N1K1 w - - bm Ra3; c0 \"Ra3=10, Kh2=6, Rb3=4\"; id \"STS(v2.2) Open Files and Diagonals.060\";", "6k1/1b3p2/p2q2p1/P2Pp2p/1p2Pb1P/1Br5/Q4PP1/3RN1K1 b - - bm Bc8; c0 \"Bc8=10\"; id \"STS(v2.2) Open Files and Diagonals.061\";", "6k1/1q3rpp/5n2/Rp2r3/4p3/1B5P/3BQPP1/6K1 w - - bm Be3; c0 \"Be3=10, Bc3=5, g3=5\"; id \"STS(v2.2) Open Files and Diagonals.062\";", "6k1/1q5p/5np1/pb1pNp2/3P1B1P/1Q6/1P2PP2/6K1 w - - bm Qa3; c0 \"Qa3=10, Qa2=6, Qc3=7\"; id \"STS(v2.2) Open Files and Diagonals.063\";", "6k1/2q4p/r3b1p1/2P1p3/r7/4QP2/p1RN2PP/R5K1 b - - bm Rb4; c0 \"Rb4=10, R6a5=8, Ra8=8\"; id \"STS(v2.2) Open Files and Diagonals.064\";", "6k1/pb4p1/1p1b2q1/1P1p3p/3Pn3/P1r2N1P/4QPB1/2B1R1K1 b - - bm Bc8; c0 \"Bc8=10, Bb8=5, Rc4=7\"; id \"STS(v2.2) Open Files and Diagonals.065\";", "6k1/q4p2/2b1p2p/4Pp2/pP3N2/Q4PP1/6KP/8 b - - bm Qd4; c0 \"Qd4=10, Bb5=9, Qb8=6\"; id \"STS(v2.2) Open Files and Diagonals.066\";", "6r1/1R6/1bp1knp1/pp1n3p/4pP1P/1PN3B1/1P3PB1/5K2 w - - bm Bh3+; c0 \"Bh3+=10, Nxd5=6, Nxe4=6\"; id \"STS(v2.2) Open Files and Diagonals.067\";", "6r1/4bbk1/p3p1p1/Pp1pPp2/3P1P2/2P2B2/3B2K1/1R6 b - - bm Rh8; c0 \"Rh8=10, Bd8=7, Be8=7, g5=7\"; id \"STS(v2.2) Open Files and Diagonals.068\";", "6rk/1n4bp/p3R2r/1p1P1Pp1/1P6/P1pB2P1/5P2/2R3K1 b - - bm Rc8; c0 \"Rc8=10\"; id \"STS(v2.2) Open Files and Diagonals.069\";", "8/2b1rk2/2b1p1p1/5n2/p1pP2Q1/2p3P1/P4P2/2B1RK2 w - - bm Bg5; c0 \"Bg5=10, Ba3=5, Bf4=4\"; id \"STS(v2.2) Open Files and Diagonals.070\";", "8/3q1pk1/3p4/2pB2p1/P2n4/1P4BP/6P1/4R1K1 b - - bm Qf5; c0 \"Qf5=10, Kh6=3, g4=5\"; id \"STS(v2.2) Open Files and Diagonals.071\";", "8/3r1k2/2p3p1/1pPb1p2/p7/P1B2P2/4BK2/3R4 w - - bm Rh1; c0 \"Rh1=10, Bd3=5, Rd4=6\"; id \"STS(v2.2) Open Files and Diagonals.072\";", "8/5r1k/p3b1pp/4p1q1/P3Pn2/2B1N2P/3R1PPK/3Q4 w - - bm Qb1; c0 \"Qb1=10, Ba1=5, Bb2=5\"; id \"STS(v2.2) Open Files and Diagonals.073\";", "8/5r2/1pp2p1R/p3n3/4Pp1p/2P2P1k/4K2P/R7 b - - bm Rg7; c0 \"Rg7=10, b5=3\"; id \"STS(v2.2) Open Files and Diagonals.074\";", "8/p1r3kp/r3p1p1/PRp1n1P1/7R/8/4K2P/7B b - - bm Rd6; c0 \"Rd6=10, Nd7=5, Nf7=7\"; id \"STS(v2.2) Open Files and Diagonals.075\";", "8/p1rb3k/1p1b1p1p/3q1p2/1P1P4/P1n1PPP1/3N3P/R1R2Q1K b - - bm Bb5; c0 \"Bb5=10, Kg6=5, Kh8=3, h5=3\"; id \"STS(v2.2) Open Files and Diagonals.076\";", "b1r3k1/3n1pb1/p2q2p1/P2Pp2p/1p2P2P/1Rr1NN2/Q1B2PP1/3R2K1 b - - bm Bh6; c0 \"Bh6=10\"; id \"STS(v2.2) Open Files and Diagonals.077\";", "b2r4/3qk3/1p1pp3/pN4b1/P3Pp1p/2P5/4BQPP/5R1K w - - bm Bg4; c0 \"Bg4=10, Rd1=1\"; id \"STS(v2.2) Open Files and Diagonals.078\";", "q1b2k2/5ppp/2n5/P1N1n3/8/2PpB1P1/3N1P1P/R4RK1 w - - bm Rfb1; c0 \"Rfb1=10, Rfe1=7, a6=7\"; id \"STS(v2.2) Open Files and Diagonals.079\";", "r1b2r1k/1p2qpb1/1np3pp/p7/3P4/PBN2N2/1PQ2PPP/3R1RK1 w - - bm Rfe1; c0 \"Rfe1=10, Qc1=7, Qd2=6, h3=7\"; id \"STS(v2.2) Open Files and Diagonals.080\";", "r1b2rk1/4bpp1/7p/1B1pP3/3B4/8/1PP3PP/2KR3R b - - bm Bg4; c0 \"Bg4=10, Bf5=9, Ra5=8\"; id \"STS(v2.2) Open Files and Diagonals.081\";", "r1b2rk1/pp3pb1/1npN1qpp/2P5/1PBp4/P4NPP/5P2/2RQ1RK1 w - - bm Re1; c0 \"Re1=10, Nxc8=7, Qd3=7\"; id \"STS(v2.2) Open Files and Diagonals.082\";", "r1b3k1/4brp1/p6p/1p1p4/3B4/8/PPP3PP/2KR1B1R w - - bm Bd3; c0 \"Bd3=10, Be3=4, Kb1=2\"; id \"STS(v2.2) Open Files and Diagonals.083\";", "r1b3k1/pp2qrpp/1n6/1Bb1Pp2/3p1P2/7Q/PP1BN1PP/R3K2R b KQ - bm Be6; c0 \"Be6=10, a6=2\"; id \"STS(v2.2) Open Files and Diagonals.084\";", "r1b3kr/p3qpp1/1pn1p2p/3pP2P/7n/3B3Q/2P2PPN/1RB1R1K1 w - - bm Ba3; c0 \"Ba3=10, Qg3=4\"; id \"STS(v2.2) Open Files and Diagonals.085\";", "r1br2k1/p4p2/1qp4p/1p2P1p1/6n1/5NP1/PP2QN1P/R1B2BK1 b - - bm Be6; c0 \"Be6=10, Bf5=3, b4=4\"; id \"STS(v2.2) Open Files and Diagonals.086\";", "r3r1k1/1b3ppp/2p2n2/1p2b1B1/4P1B1/P6P/1PQ1NPP1/6K1 w - - bm Bf5; c0 \"Bf5=10, Bf3=3, Bxf6=3, f3=2\"; id \"STS(v2.2) Open Files and Diagonals.087\";", "r3r1k1/2q2pp1/1pp2np1/4p3/nP2P1P1/P3Q2P/3R1PB1/B1R3K1 w - - bm Bf1; c0 \"Bf1=10, Bf3=5, Rdc2=6\"; id \"STS(v2.2) Open Files and Diagonals.088\";", "r3r1k1/ppn2ppp/2p5/5P2/P7/2N4P/1PP5/R1B2RK1 w - - bm Bf4; c0 \"Bf4=10, Kg2=7, Rd1=7, f6=6\"; id \"STS(v2.2) Open Files and Diagonals.089\";", "r3r2k/2R4p/3B2b1/p7/1p2p3/6PP/PPP4K/4R3 w - - bm Bc5; c0 \"Bc5=10, c3=7, c4=7\"; id \"STS(v2.2) Open Files and Diagonals.090\";", "r4qk1/1p3rbp/3p1np1/2pPp3/b1P5/P1NBBPP1/3Q1R1P/4R1K1 w - - bm Rb1; c0 \"Rb1=10, Kg2=3, Qc1=3, Ree2=3\"; id \"STS(v2.0) Open Files and Diagonals.091\";", "r4rk1/1p2pp1p/3p1np1/q2P1P2/2P3BQ/pP5R/P1R3PP/7K w - - bm Re2; c0 \"Re2=10, Be2=2, fxg6=4\"; id \"STS(v2.2) Open Files and Diagonals.092\";", "r4rk1/1p2ppbp/1q1p2p1/p1nP1P2/2P3B1/5R2/PPRB2PP/1Q5K w - - bm Qe1; c0 \"Qe1=10, Bg5=4, Rh3=5\"; id \"STS(v2.2) Open Files and Diagonals.093\";", "r4rk1/5pb1/2p3pp/pp2n3/1n3B2/1PN1PP1P/1P2BPK1/2R3R1 w - - bm Rgd1; c0 \"Rgd1=10\"; id \"STS(v2.2) Open Files and Diagonals.094\";", "r4rk1/6b1/2p1N1p1/8/p2p3R/8/R4P1P/5K2 b - - bm Rfb8; c0 \"Rfb8=10, Rf6=6, c5=8\"; id \"STS(v2.2) Open Files and Diagonals.095\";", "r4rk1/ppp1qp1p/1n4p1/4P3/6Q1/1N6/PP3PPP/2R1R1K1 b - - bm Rad8; c0 \"Rad8=10, Nd5=5, Rfd8=6\"; id \"STS(v2.2) Open Files and Diagonals.096\";", "r5k1/p1q2pbp/Ppp1bnp1/4p1B1/Q1P1P3/2N4P/1P2BPP1/5RK1 w - - bm Rd1; c0 \"Rd1=10, Be3=1, Rc1=8, b4=1\"; id \"STS(v2.2) Open Files and Diagonals.097\";", "r5k1/p4nbp/2qN2p1/2P2p2/3p1B2/6P1/P2Q1P1P/3R2K1 w - - bm Qe2; c0 \"Qe2=10, Nxf7=8, Qd3=7\"; id \"STS(v2.2) Open Files and Diagonals.098\";", "r7/3b1pk1/6p1/3Pp2p/2P4P/p4B2/5PP1/2R3K1 b - - bm Bf5; c0 \"Bf5=10, Kf6=6, Kf8=5\"; id \"STS(v2.2) Open Files and Diagonals.099\";", "rbbrqnk1/pp3pp1/2p2n1p/5N2/3P1P2/1BN4P/PPQB2P1/R4RK1 w - - bm Rae1; c0 \"Rae1=10, Rfe1=5\"; id \"STS(v2.2) Open Files and Diagonals.100\";", "1k2r2r/1bq2p2/pn4p1/3pP3/pbpN1P1p/4QN1B/1P4PP/2RR3K b - - bm Nd7; c0 \"Nd7=10, Bc5=8, Bc6=2, Be7=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.001\";", "1q2bn2/6pk/2p1pr1p/2Q2p1P/1PP5/5N2/5PP1/4RBK1 w - - bm Ne5; c0 \"Ne5=10, Nd4=8, Ra1=6, b5=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.002\";", "1r1q1rk1/1b1n1p1p/p2b1np1/3pN3/3P1P2/P1N5/3BB1PP/1R1Q1RK1 b - - bm Ne4; c0 \"Ne4=10, Bxa3=6, Nb6=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.003\";", "1r1r1bk1/1bq2p1p/pn2p1p1/2p1P3/5P2/P1NBB3/1P3QPP/R2R2K1 b - - bm Nd5; c0 \"Nd5=10, Ba8=8, Kg7=8, a5=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.004\";", "1r1r2k1/5pp1/p2p4/1p2pnqp/1BP1Q3/PP1R2P1/5P1P/3R2K1 b - - bm Nd4; c0 \"Nd4=10, Qf6=5, bxc4=3, h4=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.005\";", "1r1r4/R3pk2/4n1p1/2p2p2/8/4B3/Pn2BPPP/5RK1 b - - bm Nd4; c0 \"Nd4=10, Nd3=1, c4=4, f4=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.006\";", "1r2k2r/pp2ppb1/2n2np1/7p/4P3/P3BB1P/1P1N1PP1/R2R2K1 b k - bm Nd7; c0 \"Nd7=10, Bh6=6, a6=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.007\";", "1r2qrk1/3bn3/pp1p3p/n1p1p1p1/P1P5/B1PP1NPP/2Q2PB1/1R2R1K1 w - - bm Nd2; c0 \"Nd2=10, Bc1=6, Qe2=9, Rb2=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.008\";", "1r2r2k/2b2q1p/p4p2/3Pn2P/3N1N2/1P2R3/4Q3/1K1R4 w - - bm Nfe6; c0 \"Nfe6=10, Ka1=4, Nf5=6, Qxa6=2, Rc3=3, h6=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.009\";", "1r3rk1/8/3p3p/p1qP2p1/R1b1P3/2Np1P2/1P1Q1RP1/6K1 w - - bm Nd1; c0 \"Nd1=10, Na2=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.010\";", "1r6/1q2b1k1/pn1pb3/B1p1p1pp/2P1Pp2/NP3P1P/1R2Q1PN/6K1 b - - bm Nc8; c0 \"Nc8=10, Bg8=8, Kg8=8, Nd7=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.011\";", "1r6/2qnrpk1/2pp1np1/pp2P3/4P3/PBN2Q2/1PPR1PP1/3R2K1 b - - bm Nxe5; c0 \"Nxe5=10, Nxe4=8, Rxe5=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.012\";", "1rr2qk1/3p1pp1/1pb2n1p/4p3/p1P1P2P/P1NQ1BP1/1P3PK1/2RR4 w - - bm Nb5; c0 \"Nb5=10, Kh2=5, Rc2=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.013\";", "2b2rk1/1r1nbppp/4p3/1p2P3/p4P2/P1N1B3/BPP4P/R2R2K1 w - - bm Ne4; c0 \"Ne4=10, Rac1=1, Rd2=3, b4=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.014\";", "2b3n1/6kp/p1nB1pp1/8/1p2P1P1/4NP2/PP3K2/3B4 w - - bm Nd5; c0 \"Nd5=10, Ba4=8, Kg3=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.015\";", "2b5/2p1r2k/1pP2q1p/p2Pp3/4R3/1PN1Q2P/P2KP3/8 w - - bm Nb5; c0 \"Nb5=10, Kc1=7, Rc4=8, h4=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.016\";", "2k3r1/1b2bp2/2p2n2/ppn1p1Bp/2p1P2P/P4B2/1P1RN1P1/4K2R b K - bm Nd3+; c0 \"Nd3+=10, Kc7=9, Ne6=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.017\";", "2r2r1k/p3ppbp/qpnp2pn/5P2/2P1PP2/P1N1BB2/1PQ3RP/3R2K1 w - - bm Nb5; c0 \"Nb5=10, Be2=8, Nd5=9, fxg6=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.018\";", "2r2rk1/4bpp1/p2pbn1p/Pp2p3/1Pq1P2N/2P4P/1BB2PP1/R2QR1K1 w - - bm Nf5; c0 \"Nf5=10, Bb1=5, Qe2=5, Qf3=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.019\";", "2r3k1/3q1pp1/ppr1p1np/4P3/P1nPQ3/5N1P/5PPK/RRB5 b - - bm Ne7; c0 \"Ne7=10, Qc7=9, Qd8=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.020\";", "2r3k1/p1r1qpb1/1p2p1p1/nR2P3/P2B4/2P5/3NQPP1/R5K1 w - - bm Ne4; c0 \"Ne4=10, Nb3=8, f4=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.021\";", "2rq1rk1/1p2b1p1/pn2p3/2p1Pn2/2pP3p/5N1P/PPQ2BP1/1BRR2K1 b - - bm Nd5; c0 \"Nd5=10, Bg5=6, Qc7=6, Qe8=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.022\";", "r7/p1r2nk1/1pNq1np1/1P1p1p2/P2Qp3/4P1P1/2R1P1BP/2R3K1 b - - bm Ng5; id \"STS(v3.1) Knight Outposts/Centralization/Repositioning.023\"; c0 \"Ng5=10, Rac8=4, Rb7=3, Re8=3\";", "2rq2k1/3nbppp/pprp1nb1/4p3/P1P1P3/1PN1BN1P/2Q1BPP1/R2R2K1 w - - bm Nh4; c0 \"Nh4=10, Ra2=5, Rab1=6, g3=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.024\";", "2rqr1k1/1p2bppp/p2p1n2/3P1P2/2Pp4/1P1B4/P3Q1PP/R1B2RK1 b - - bm Nd7; c0 \"Nd7=10, Qa5=7, h6=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.025\";", "2rr4/Bp3k1p/5pp1/8/2n3b1/P1N5/1PP2PPP/R1R3K1 w - - bm Ne4; c0 \"Ne4=10, Na4=9, f3=9, h3=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.026\";", "3R4/5pk1/2p4r/1p2p1p1/p3P1P1/P1P2P2/1P2B1K1/n7 b - - bm Nb3; c0 \"Nb3=10, Rf6=9, Rg6=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.027\";", "3q3r/2p2pk1/6p1/2p1p1Pn/1pBnP3/1P2BP1R/P5Q1/7K b - - bm Nf4; c0 \"Nf4=10, Qd7=5, c6=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.028\";", "3r1bk1/1rq2p2/2npb1p1/p3p2p/2P1P2P/1PN3P1/2N1QPBK/R2R4 w - - bm Nb5; c0 \"Nb5=10, Rdb1=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.029\";", "3r1rk1/1p4bp/2qPp1p1/p3n3/P2BN3/1PN4P/2PR2P1/4Q1K1 w - - bm Nb5; c0 \"Nb5=10, Qe3=5, Qh4=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.030\";", "3r2k1/2q2ppp/p1p1bn2/1p2b1B1/4P3/1PN2B1P/P1Q2PP1/2R4K w - - bm Nd5; c0 \"Nd5=10\"; id \"STS: Knight Outposts/Repositioning/Centralization.031\";", "3r2k1/4qpn1/R2p3p/1Pp1p1p1/1rP1P1P1/6P1/3Q1P2/4RBK1 b - - bm Ne6; c0 \"Ne6=10, Kf8=5, Kh7=4, Rd7=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.032\";", "3r4/2p2pk1/2q1n1p1/2p1p1Pn/1pB1P3/1P2BP2/P6R/4Q1K1 b - - bm Nd4; c0 \"Nd4=10, Kg8=8, Nhf4=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.033\";", "3r4/bp1r2pk/p3npqp/P2Np3/1PR1P2B/5Q1P/2P3P1/5R1K b - - bm Nd4; c0 \"Nd4=10, Bb8=4, Kh8=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.034\";", "3r4/r1pb3p/1p4kB/2p3P1/4pP2/1P2NnKP/PR3R2/8 b - - bm Nd4; c0 \"Nd4=10, Bc8=2, Bf5=2, c4=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.035\";", "3rb1k1/4qpbp/1p2p1p1/1P3n2/Q1P2p2/2N2B1P/6PK/1NR2R2 b - - bm Ne3; c0 \"Ne3=10, Bd4=3, Rb8=7, Rc8=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.036\";", "3rr1k1/1p3ppp/p1q2b2/P4P2/2P1p3/1P6/2N1Q1PP/4RR1K w - - bm Nb4; c0 \"Nb4=10, Ne3=9, Qh5=9, b4=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.037\";", "3rr1k1/pb1n1pp1/2q2b1p/2p5/2P1p2N/1P2B1P1/P1QR1PBP/3R2K1 b - - bm Ne5; c0 \"Ne5=10, Bxh4=7, Nf8=7, g6=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.038\";", "3rr3/p3b1kp/2p2pp1/1q1np3/4Q1PB/1NP5/PP3P1P/R2R2K1 b - - bm Nf4; c0 \"Nf4=10, Kf7=3, Qa6=2, a5=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.039\";", "4b1nk/p1r1p1rp/Bpq5/n3Ppp1/8/5N1P/2P2BPQ/R3R1K1 w - - bm Nd4; c0 \"Nd4=10, Bd3=5, Rad1=3, e6=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.040\";", "4k2r/1r2np2/2q1p1p1/p2pP3/n1pP1PP1/1pP1NNK1/1P2Q3/R4R2 w k - bm Ng5; c0 \"Ng5=10, Qd2=3, Qg2=3, Rf2=4, Rh1=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.041\";", "4n3/1p1b1pk1/2n5/rN4p1/1p1Np3/1B2P1P1/PP3PK1/2R5 b - - bm Ne5; c0 \"Ne5=10\"; id \"STS: Knight Outposts/Repositioning/Centralization.042\";", "4n3/1p1b1pk1/8/rNR1n1p1/1p1Np3/1B2P1P1/PP3PK1/8 b - - bm Nf3; c0 \"Nf3=10, Bxb5=3, f6=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.043\";", "4nk2/p4rr1/1pRp3b/1P1Pp2p/1P5P/2NBpP2/4R1P1/5K2 w - - bm Ne4; c0 \"Ne4=10, Kg1=1, Nd1=1, Rc8=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.044\";", "4r1k1/2pbqp1p/1r3p2/pP1p4/8/P1QBPN2/3P1PPP/4K2R w K - bm Nd4; c0 \"Nd4=10, Qxc7=6, a4=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.045\";", "4r1k1/3q1pp1/3p1n2/rp2nP1p/3B1R2/2N5/2PQ2PP/4R1K1 w - - bm Ne4; c0 \"Ne4=10, Kh1=8, Rb1=6, h3=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.046\";", "4r1k1/3r2p1/b1q2n1p/p7/Pp2P2Q/1N2BP2/1PP4P/2R1R2K w - - bm Nd4; c0 \"Nd4=10, Nc5=6, Nxa5=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.047\";", "4r3/1p3pk1/2p2n1p/2n1qP2/5bPQ/P3pB2/2R5/1R3N1K b - - bm Nfe4; c0 \"Nfe4=10, Nd3=9, Nh7=9, Qd4=2, Rd8=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.048\";", "4rrk1/p4pp1/1b1p3p/2pP4/6q1/5N2/PP3PPP/2RQ1R1K w - - bm Nd2; c0 \"Nd2=10, Kg1=7, Qd3=2, h3=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.049\";", "5k2/6p1/Bnp1p2p/5p2/3P1n2/2q2P2/7P/5RQK b - - bm Nbd5; c0 \"Nbd5=10, Kf7=7, Kg8=4, g6=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.050\";", "5r1k/2p3q1/1p1npr2/pPn1N1pp/P1PN4/R4PPP/4Q1K1/3R4 w - - bm Ndc6; c0 \"Ndc6=10, Kh1=1, Rb1=8, Rc1=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.051\";", "5r2/2r2kp1/3n1p2/1p1Pp2p/p3P2P/PnP4R/1BB2KP1/4R3 b - - bm Nc4; c0 \"Nc4=10, Ke7=9, Rb8=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.052\";", "5rk1/3nbp1p/2p1p3/2PpP1pN/3P2B1/q3PQ1P/6P1/5RK1 w - - bm Nf6+; c0 \"Nf6+=10, Qf2=1, Qg3=4, h4=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.053\";", "5rk1/6pp/pn1qp1r1/1p2R3/2pP1P2/P1P2Q1P/5P2/2B1R2K b - - bm Nd5; c0 \"Nd5=10, Nd7=9, Qd7=9, Rgf6=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.054\";", "5rk1/pb1qbppp/1p6/n1rpP3/7P/2P2NP1/P3QPB1/R1BR2K1 w - - bm Nd4; c0 \"Nd4=10, Bb2=2, Bd2=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.055\";", "6k1/3pbpp1/p3p2n/r3P2p/2rBNP2/2P3PP/P3R3/3R3K b - - bm Nf5; c0 \"Nf5=10, Rd5=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.056\";", "6k1/p2b3n/1p2pn1q/3r1p2/5P1B/P5NR/1Q2B1P1/4K3 b - - bm Ng4; c0 \"Ng4=10, Qf8=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.057\";", "7k/Rb4r1/3pPp1q/1pp2P2/3n2BP/4N1K1/1P3Q2/8 b - - bm Nc6; c0 \"Nc6=10, Kg8=8, Kh7=6, Qxe3+=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.058\";", "8/1b5p/1p1rrkp1/p2p1p2/P2P3P/1R1B1N2/nP3PPK/3R4 w - - bm Ne5; c0 \"Ne5=10, Bb5=4, h5=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.059\";", "8/1p2kp2/p3pn2/4n1p1/1P2P3/P1r1NB1P/5PPK/R7 b - - bm Ne8; c0 \"Ne8=10, Nxf3+=1, b5=3, b6=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.060\";", "8/1q6/3pn1k1/2p1p1p1/2P1P1Pp/1rBP1p1P/2Q2P2/R5K1 b - - bm Nf4; c0 \"Nf4=10, Kf7=4, Kg7=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.061\";", "8/2k2p2/pr1pbpn1/2p5/2P1P1P1/2P1KP1p/7P/R2N1B2 b - - bm Ne5; c0 \"Ne5=10, Kd7=5, Rb3=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.062\";", "8/pB1b2k1/1p2pn2/5p2/5P1B/P7/3K1NPn/8 b - - bm Ne8; c0 \"Ne8=10, Bb5=8, Kg6=7, Nf1+=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.063\";", "b1r2r1k/p2qp1bp/1p1pn1p1/8/1PP2P2/R1NBB3/P2Q2PP/5RK1 w - - bm Nb5; c0 \"Nb5=10, Be2=7, Kh1=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.064\";", "b4q2/1r5k/3p4/1p1Pn2B/1PpQP3/2N4P/6P1/B5K1 w - - bm Nd1; c0 \"Nd1=10, Ne2=6, Qe3=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.065\";", "q3n3/2rp1ppk/2b4p/4p2P/p3P3/Pr2NBP1/1P1R1PK1/2R1Q3 w - - bm Nd5; c0 \"Nd5=10, Kg1=3, Qe2=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.066\";", "r1b1k1nr/pp2p1bp/1q1p2p1/2pP4/2Pnp3/2NB1N1P/PP1B1PP1/R2QK2R w KQkq - bm Nxe4; c0 \"Nxe4=10, Bxe4=3, Na4=7, Nxd4=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.067\";", "r1b2r1k/pp3pbp/3n4/4p3/2N4R/2N5/PP3PPP/R1B3K1 b - - bm Nf5; c0 \"Nf5=10, Bf6=2, Nxc4=3, Rd8=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.068\";", "r1b2rk1/1pq1bppp/p2p4/P3p3/2N1n3/4B3/1PP1BPPP/R2QK2R w KQ - bm Nb6; c0 \"Nb6=10, O-O=9, f3=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.069\";", "r1b2rk1/p2q2b1/1nppp1pp/5pN1/P2P1B2/Q5P1/1P2PPBP/R1R3K1 b - - bm Nd5; c0 \"Nd5=10, Bb7=3, hxg5=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.070\";", "r1b2rk1/pp3pp1/2np2qp/b1p1p3/2P1P3/2NPB1P1/PP3PBP/R2Q1RK1 w - - bm Nd5; c0 \"Nd5=10, Qb3=1, Qc2=1, f4=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.071\";", "r1bq1rk1/ppp3bp/n2p1pp1/3P4/2P1Pp2/2N2N1P/PP2BPP1/R2QR1K1 w - - bm Nd4; c0 \"Nd4=10, Bf1=9, Qd2=4, Rc1=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.072\";", "r1r3k1/pb3p1p/1pqBp1p1/4P3/3b4/2P2P2/PR1N2PP/2RQ3K w - - bm Ne4; c0 \"Ne4=10\"; id \"STS: Knight Outposts/Repositioning/Centralization.073\";", "r2b2k1/2pr4/1pn1b1qp/3Np1p1/p1P1p3/1P2B1P1/PQ2PP1P/R2R2NK b - - bm Nd4; c0 \"Nd4=10, Bf5=4, a3=5, h5=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.074\";", "r2br3/p2b1q1k/n2P2pp/1p1N1p2/4pP2/BP2Q1PN/P1R4P/3R2K1 w - - bm Nc7; c0 \"Nc7=10, Nf2=3, Qd4=2, Qe2=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.075\";", "r2q1bk1/1p1brpp1/p1np1n1p/4p3/PN2P3/1QPP1N1P/B2B1PP1/R3R1K1 w - - bm Nd5; c0 \"Nd5=10, Be3=4, Qb2=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.076\";", "r2q1k2/2p2pb1/p2n2rp/1p1RB1p1/8/2PQRN1P/P4PP1/6K1 w - - bm Nd4; c0 \"Nd4=10, Bxg7+=2, Re2=3, h4=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.077\";", "r2q1rk1/3nbpp1/p2p3p/8/1p1BP3/3B2Q1/PPP4P/2KR3R b - - bm Ne5; c0 \"Ne5=10, Bf6=1, Bg5+=4, g6=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.078\";", "r2q1rk1/pp1bp1bp/5np1/2pP1p2/8/2N2NP1/PP2PPBP/2RQ1RK1 w - - bm Ne5; c0 \"Ne5=10, Ng5=5, Qb3=7, e3=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.079\";", "r2q2kr/p3n3/1p1Bp1bp/3pP1pN/5PPn/3B3Q/2P2K2/1R5R w - - bm Nf6+; c0 \"Nf6+=10, Bxg6=3, Rbg1=2\"; id \"STS: Knight Outposts/Repositioning/Centralization.080\";", "r2qk2r/1bpnnpbp/p2pp1p1/4P3/Pp1P1P2/2NBBN2/1PP3PP/R2Q1RK1 w kq - bm Ne4; c0 \"Ne4=10, Na2=2, Ne2=2, exd6=8\"; id \"STS: Knight Outposts/Repositioning/Centralization.081\";", "r2qr1k1/pp3ppp/2n2nb1/1P4B1/3p4/P2B1P2/2P1N1PP/R2Q1RK1 b - - bm Ne5; c0 \"Ne5=10, Na5=9, Ne7=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.082\";", "r2r2k1/1bqn1pp1/1p1p1b1p/1B2n3/2P1P3/P1N1B3/1P1NQ1PP/4RR1K w - - bm Nd5; c0 \"Nd5=10, Bd4=9, Nb3=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.083\";", "r2r2k1/1p1n2q1/2ppbp2/6p1/2PBP2p/p1N2P2/Pb4PP/1R1RQBK1 b - - bm Ne5; c0 \"Ne5=10, Qf7=2, Qh7=5, h3=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.084\";", "r2rq3/pp1b3k/n2P1bpp/4pp2/8/BPN1Q1PN/P4P1P/2RR2K1 w - - bm Nd5; c0 \"Nd5=10, Bb2=5, Qd3=5, Qf3=5, f3=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.085\";", "r3r1k1/1b1nq2p/p2pNppQ/1ppP3n/P3P3/7P/1PB2PP1/R3RNK1 b - - bm Nf8; c0 \"Nf8=10, Ne5=4, Rec8=6\"; id \"STS: Knight Outposts/Repositioning/Centralization.086\";", "r3r1k1/1p1q2bp/1n1p2p1/1PpPpp1n/p3P3/R1NQBN1P/1PP2PP1/4R1K1 w - - bm Ng5; c0 \"Ng5=10, Qf1=7, Rd1=8, exf5=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.087\";", "r3r1k1/3nbppp/p1q5/1pp1P2b/5B2/1P2QN2/1P1N1PPP/3RR1K1 w - - bm Ne4; c0 \"Ne4=10, Bg3=7, Rc1=7\"; id \"STS: Knight Outposts/Repositioning/Centralization.088\";", "r3r1k1/4bppp/pnq5/1pp1P2b/4NB2/1P2QN2/1P3PPP/3RR1K1 w - - bm Nd6; c0 \"Nd6=10, Bg3=4, Qc1=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.089\";", "r3r2k/pp3pp1/1np4p/3p2q1/1P1P2b1/P1NBP3/2Q2PPP/R4RK1 w - - bm Ne2; c0 \"Ne2=10, Kh1=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.090\";", "r3r3/2P4k/3Bbbqp/ppQ2pp1/4pPP1/1P6/P1R2N1P/3R2K1 w - - bm Nxe4; c0 \"Nxe4=10, Qe3=4, Rcd2=5, Re2=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.091\";", "r3rnk1/1bpq1pp1/p2p3p/1p1Pp1b1/P3P1P1/1BP1N1P1/1P3P2/R1BQR1K1 w - - bm Nf5; c0 \"Nf5=10, Bc2=3, Kg2=4, Kg2=9, Qe2=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.092\";", "r4rk1/1b1q1ppp/pb1p1nn1/1pp1p3/1PP1P3/P2PNN1P/B2B1PP1/2RQR1K1 w - - bm Nf5; c0 \"Nf5=10, Bb1=7, Qb3=4\"; id \"STS: Knight Outposts/Repositioning/Centralization.093\";", "r4rk1/1pq1bppp/1n2p3/p1n1P3/2PR4/2N1BN2/P3QPPP/1R4K1 w - - bm Nb5; c0 \"Nb5=10, Qd2=5, h4=9\"; id \"STS: Knight Outposts/Repositioning/Centralization.094\";", "r4rk1/5p1p/p2qpnp1/1p2b3/3p4/3B1R2/PPQ3PP/R1BN3K b - - bm Nd7; c0 \"Nd7=10, Nd5=7, Nh5=5, Rac8=1, Rfc8=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.095\";", "r4rk1/ppp3b1/3p1q1p/3Ppn2/P1P3n1/2NQ1N2/1P1B1PP1/R3R1K1 w - - bm Ne4; c0 \"Ne4=10, Nb5=4, Nh2=1, Ra3=1\"; id \"STS: Knight Outposts/Repositioning/Centralization.096\";", "r5r1/1pp2k1p/2bn4/2p3B1/p3pPP1/1P2N2P/P1P1R3/R5K1 b - - bm Nb5; c0 \"Nb5=10\"; id \"STS: Knight Outposts/Repositioning/Centralization.097\";", "r6k/pp3pp1/1n6/1pQp1q2/3PrN1p/P3P2P/5PP1/2R2RK1 w - - bm Nd3; c0 \"Nd3=10, Ne2=2, Qxb5=2, Rce1=3\"; id \"STS: Knight Outposts/Repositioning/Centralization.098\";", "r6r/1q1bbkp1/p1p1pn2/5p1p/N2Bp3/2Q3P1/PPP2PBP/R2R2K1 b - - bm Nd5; c0 \"Nd5=10, Rad8=5, h4=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.099\";", "rqr3k1/1p2bppp/3pn3/p3p1Pn/P3P3/1PNBBP2/1P1Q3P/2KR3R b - - bm Nd4; c0 \"Nd4=10, Nef4=6, Nhf4=5\"; id \"STS: Knight Outposts/Repositioning/Centralization.100\";", "6k1/p2pp2p/bp4n1/q1r4R/1RP1P3/2P2B2/P2Q2P1/4K3 w - - bm Rd5; c0 \"Rd5=10, Rf5=6, g4=7\"; id \"STS(v4.0) Square Vacancy.001\";", "r2r2k1/pp3ppp/2p1qn2/5N1b/1n2PP2/4Q2P/PPP3B1/R1B2RK1 w - - bm Qc5; c0 \"Qc5=10, Qg3=3\"; id \"STS(v4.0) Square Vacancy.002\";", "3r4/p4pk1/P1pr3p/3nb3/1p6/5B1P/1P3PP1/R1BR2K1 w - - bm Ra5; c0 \"Ra5=10, Kf1=2, g3=5\"; id \"STS(v4.0) Square Vacancy.003\";", "1b1r3r/3pkpp1/3np1q1/2p5/2P1PPP1/5Q2/PP4B1/R1BR2K1 b - - bm Rh4; c0 \"Rh4=10, Nxc4=6, Rc8=7\"; id \"STS(v4.0) Square Vacancy.004\";", "7k/1p6/1n1rrq1p/1R1p1p1p/3P1P2/QR2P2P/6PK/5B2 w - - bm Qa7; c0 \"Qa7=10, Kg1=7, R5b4=7\"; id \"STS(v4.0) Square Vacancy.005\";", "5r1k/5rp1/p1n1p1qp/2P1p3/P7/4QN1P/5PP1/2R1R2K w - - bm Rc4; c0 \"Rc4=10, Qe2=5, Rc3=7\"; id \"STS(v4.0) Square Vacancy.006\";", "6r1/1p2Q1pk/2p2p1p/3p1P1P/p1nP4/PqP1P3/1P2RN2/2K5 b - - bm Qa2; c0 \"Qa2=10, Qb5=5, b6=3\"; id \"STS(v4.0) Square Vacancy.007\";", "6r1/1p2Q1pk/2p2p1p/n2p1P1P/p2P4/P1P1P3/1P1KR3/q2N4 b - - bm Qb1; c0 \"Qb1=10, Kh8=3, Nb3+=4\"; id \"STS(v4.0) Square Vacancy.008\";", "5r1k/1b3pp1/p3pb2/4N2q/3R1B2/4P3/1PB2PPP/1K4R1 b - - bm Qe2; c0 \"Qe2=10, Bxe5=7, Kg8=4\"; id \"STS(v4.0) Square Vacancy.009\";", "8/1b2r2p/1p1r1kp1/p2p1p2/Pn1P3P/1R1B1N2/1P3PPK/2R5 w - - bm Bb5; c0 \"Bb5=10, Bf1=2, Ne5=6\"; id \"STS(v4.0) Square Vacancy.010\";", "1q1k2r1/p2bn3/1r3n2/2QPp1Rp/2P4P/2PB1P2/2K1N3/R7 w - - bm Qa5; c0 \"Qa5=10, Qa3=4, Rxg8+=5\"; id \"STS(v4.0) Square Vacancy.011\";", "8/p3q1kp/1p1p1rp1/3Bn3/P1PQ1p2/1P6/6PP/4R2K b - - bm Rf5; c0 \"Rf5=10, Kh6=6, Rf8=3\"; id \"STS(v4.0) Square Vacancy.012\";", "4r1rk/pp6/5q2/3pNp1P/2pPnQ2/2P1P2P/P6K/R5R1 w - - bm Rg6; c0 \"Rg6=10, Rab1=4, Rac1=4\"; id \"STS(v4.0) Square Vacancy.013\";", "5r2/p1qn3k/bp1p1pp1/3P4/P2BPP2/2Pp4/3N2P1/R2Q2K1 w - - bm Qg4; c0 \"Qg4=10, Qe1=4, a5=5\"; id \"STS(v4.0) Square Vacancy.014\";", "1r3q2/1n4pk/R4p1p/4p2P/2B1P1P1/2P1QPK1/8/8 w - - bm Be6; c0 \"Be6=10, Bd5=7, Rb6=8\"; id \"STS(v4.0) Square Vacancy.015\";", "4r1k1/1pb3p1/p1p3q1/3n4/1P1P4/P3pRP1/4Q1K1/2B2R2 b - - bm Qe4; c0 \"Qe4=10, Bb6=5, Bd8=5\"; id \"STS(v4.0) Square Vacancy.016\";", "3R4/pkn5/1p2qp2/1p5p/1P2P1pR/P5P1/1N3PP1/5K2 b - - bm Qb3; c0 \"Qb3=10, Qa2=5\"; id \"STS(v4.0) Square Vacancy.017\";", "4r1k1/p5pp/4q3/P2R4/2p2P2/2N3P1/2nB1K1P/1R6 b - - bm Qh3; c0 \"Qh3=10, a6=5, h6=6\"; id \"STS(v4.0) Square Vacancy.018\";", "1Q6/1p2p2k/1r4pp/p1p2P2/q2nr1P1/7R/1P1R4/5NK1 w - - bm Qf8; c0 \"Qf8=10, Kh2=7, Rg2=7\"; id \"STS(v4.0) Square Vacancy.019\";", "4R3/p2q2k1/2p2r1p/6p1/8/P5P1/4QP1P/3rB1K1 b - - bm Qd4; c0 \"Qd4=10, Kf7=6, Kg6=6\"; id \"STS(v4.0) Square Vacancy.020\";", "3q1r1k/5ppp/1pR1pn2/p7/B1PP4/P3QP1P/5PK1/8 w - - bm Qe5; id \"STS(v4.0) Square Vacancy.021\"; c0 \"Qe5=10, f4=1, Kf1=1, Kh2=1\";", "8/Qpnbk2q/4pp2/1P1p4/P1r5/2P3P1/1K2N3/1R3B2 b - - bm Qd3; c0 \"Qd3=10, Qh2=2, e5=2\"; id \"STS(v4.0) Square Vacancy.022\";", "2r2rk1/pb2qp1p/n3p1p1/2pp4/N1P1n3/1P2P1P1/PN3PBP/2R1QRK1 w - - bm Qa5; c0 \"Qa5=10, Qe2=6, Rd1=4\"; id \"STS(v4.0) Square Vacancy.023\";", "r5k1/1p5p/p1p1qrnP/2B1pb2/R1P5/2P2B2/P2Q2P1/5RK1 w - - bm Qg5; c0 \"Qg5=10, Qb2=1, Rb4=6\"; id \"STS(4.0) Square Vacancy.024\";", "Q7/2pq3k/1rp2b1p/2R5/8/1P1NP3/P2K1Pr1/5R2 w - - bm Qf8; c0 \"Qf8=10, Qa4=5, Rc2=4\"; id \"STS(v4.0) Square Vacancy.025\";", "q5k1/p2p2bp/1p1p2r1/2p1np2/6p1/1PP2PP1/P2PQ1KP/4R1NR b - - bm Qd5; c0 \"Qd5=10, Re6=4, a5=1, c4=2\"; id \"STS(v4.0) Square Vacancy.026\";", "4r1k1/1p5p/1nq1p1p1/4B3/6R1/P6P/3Q1PPK/8 w - - bm Qh6; c0 \"Qh6=10, Ba1=4, Qd3=4\"; id \"STS(v4.0) Square Vacancy.027\";", "r5nk/p4p1r/qp1Rb2p/2p1Pp2/5P1Q/P4N2/2B3PP/5RK1 w - - bm Qh5; c0 \"Qh5=10, a4=2\"; id \"STS(v4.0) Square Vacancy.028\";", "r2r2k1/1q2bpp1/4p2p/p6Q/N4P2/1P2R1P1/2P4P/2KR4 b - - bm Qb4; c0 \"Qb4=10, Qg2=5, Rdc8=5\"; id \"STS(v4.0) Square Vacancy.029\";", "r4rk1/1b2bpp1/1P1p4/p1q1p2p/R3PPn1/3B3Q/2P1N1PP/1R1N3K b - - bm Qf2; c0 \"Qf2=10, d5=1\"; id \"STS(v4.0) Square Vacancy.030\";", "1r5r/1p6/p2p2p1/P2P1pkq/1R1Qp3/2P1P1bP/1P2R3/5BK1 b - - bm Qf3; c0 \"Qf3=10, Be5=8, Rh6=5\"; id \"STS(v4.0) Square Vacancy.031\";", "2r1r2k/q6p/6p1/3R1p2/1p1P1B2/2bQPBP1/5P1P/6K1 w - - bm Qb5; c0 \"Qb5=10, Kg2=5, Rb5=5\"; id \"STS(v4.0) Square Vacancy.032\";", "1r3q1k/6p1/r6p/3P3b/2PQ1R2/pp4P1/5P2/R1B3K1 b - - bm Qb4; c0 \"Qb4=10, Qd6=1, Qe7=1\"; id \"STS(v4.0) Square Vacancy.033\";", "5k2/pb3pp1/qp3n1p/2p5/2P2B2/8/P1PN1PPP/R3R1K1 b - - bm Qa4; c0 \"Qa4=10, Bc6=1\"; id \"STS(v4.0) Square Vacancy.034\";", "2r1qr1k/1p4bp/p1n1bpp1/4p3/B3P3/4QN1P/PP1B1PP1/R1R3K1 w - - bm Qb6; c0 \"Qb6=10, Rc2=5, Rc3=5, a3=4\"; id \"STS(v4.0) Square Vacancy.035\";", "6rk/5q1p/p2p4/P1nP1p2/2B2N1Q/1Pb1pPR1/7P/7K w - - bm Qh6; c0 \"Qh6=10, Rg5=3, Rxg8+=5\"; id \"STS(v4.0) Square Vacancy.036\";", "3r2k1/p4rp1/1pRp3q/1P1Ppb2/7P/Q4P2/P5BP/2R4K b - - bm Qd2; c0 \"Qd2=10, Kh7=5, Qf4=4\"; id \"STS(v4.0) Square Vacancy.037\";", "rq4k1/3bn1bp/3p2p1/3Pp1N1/4P1p1/2N1B1P1/1P2QP1P/4R1K1 b - - bm Qb3; c0 \"Qb3=10, Kh8=4, h5=4\"; id \"STS(v4.0) Square Vacancy.038\";", "3rk3/2p1r1p1/7q/p1p1P1R1/2B4P/4P3/PPQ5/6K1 w - - bm Qe4; c0 \"Qe4=10, Kg2=4, b3=3\"; id \"STS(v4.0) Square Vacancy.039\";", "2r2r1k/3q2np/p2P1pp1/1pP1p3/7N/5QP1/P4P1P/2R1R1K1 w - - bm Qd5; c0 \"Qd5=10, Qd1=7, Qd3=7\"; id \"STS(v4.0) Square Vacancy.040\";", "r2r2k1/5p1p/6nQ/ppq5/2p1P3/P4P2/1P4PP/R1N2R1K b - - bm Qd4; c0 \"Qd4=10, Qd6=5, Qf2=1\"; id \"STS(v4.0) Square Vacancy.041\";", "r3r1k1/ppq3p1/2p1n3/3pN1p1/Pb1P2P1/3QB2P/1P3P2/R1R3K1 w - - bm Qf5; c0 \"Qf5=10, Kg2=4, Ng6=3, Qg6=4, Rc2=4\"; id \"STS(v4.0) Square Vacancy.042\";", "2bq1rk1/1r3p2/3p2pp/p1p1p3/PnP1P3/2QPP1N1/2B3PP/1R3RK1 b - - bm Qg5; c0 \"Qg5=10, Bd7=3, Qe8=3, h5=4\"; id \"STS(v4.0) Square Vacancy.043\";", "2q5/5kp1/pP2p1r1/3b1p1Q/3P1P2/6P1/5K2/4RB2 b - - bm Qc3; c0 \"Qc3=10, Bb7=2, Qc2+=3, Qc6=1\"; id \"STS(v4.0) Square Vacancy.044\";", "2q2r1k/7p/p2p1p2/1p1Np3/nP6/4Q2P/P4PP1/4R1K1 w - - bm Qh6; c0 \"Qh6=10, Qd2=2, Rc1=6, Re2=1\"; id \"STS(v4.0) Square Vacancy.045\";", "8/3k4/2p5/3r4/1PQBp1pq/4P3/P4PpP/6K1 w - - bm Qa6; c0 \"Qa6=10, Kxg2=4, a4=6, b5=5\"; id \"STS(v4.0) Square Vacancy.046\";", "8/7p/p2p1kqP/2r1b3/P1B1p1r1/1P6/4QP2/3R1K1R b - - bm Qf5; c0 \"Qf5=10, Qg5=1, Rf4=3, a5=3\"; id \"STS(v4.0) Square Vacancy.047\";", "2b1rnk1/1p3pp1/r3p2p/1q2P3/p2PQ3/P1P2PB1/6PP/1BKR3R b - - bm Qb3; c0 \"Qb3=10, Bd7=5, Qa5=4, Rc6=4\"; id \"STS(v4.0) Square Vacancy.048\";", "2r5/p4kp1/1pn2n2/3q1b2/3p4/P2P2Q1/2RB1PPP/4R1K1 b - - bm Qb3; c0 \"Qb3=10, Nd7=5, Nh5=3, Qb5=3\"; id \"STS(v4.0) Square Vacancy.049\";", "r6k/1p2rR1P/2p3p1/4p1q1/8/pP6/P1P5/1K2Q2R w - - bm Qa5; c0 \"Qa5=10, Rf2=6, Rf3=6, Rff1=6\"; id \"STS(v4.0) Square Vacancy.050\";", "r3r1k1/2q3p1/p2p1p2/3P2p1/n2B2P1/8/P1PQ3P/K2R1R2 b - - bm Qc4; c0 \"Qc4=10, Qc8=2, Rac8=6, Re4=6\"; id \"STS(v4.0) Square Vacancy.051\";", "3r2k1/2p2pp1/p6p/1p5q/1PbB4/P1Q3P1/5P1P/4R1K1 b - - bm Qg4; c0 \"Qg4=10, Rd6=2, a5=2, f6=2\"; id \"STS(v4.0) Square Vacancy.052\";", "r5k1/1q4b1/6n1/3pp1Nn/1N3p2/3P2Pp/2QBP2P/1R4K1 w - - bm Qc5; c0 \"Qc5=10, Nxh3=4\"; id \"STS(v4.0) Square Vacancy.053\";", "6r1/4pp1k/3p3p/2qP1P2/r3P1PK/1R6/4Q3/1R6 b - - bm Qd4; c0 \"Qd4=10, Ra3=1, Rg5=1, Rga8=2, d5d4=1\"; id \"STS(v4.0) Square Vacancy.054\";", "8/p3b1k1/2p5/3qp2p/P2p3P/1P1Q1PP1/3NP1K1/8 w - - bm Qf5; c0 \"Qf5=10, Nc4=7, Qa6=6, Qb1=5, Qc2=5\"; id \"STS(v4.0) Square Vacancy.055\";", "1rb2rk1/2p3pp/p1p1p3/2N5/8/1PQ2PPq/P3P3/R2R2K1 w - - bm Qe5; c0 \"Qe5=10, Kf2=4, Ne4=6\"; id \"STS(v4.0) Square Vacancy.056\";", "2rqrbk1/1b1n1p2/p2p1npp/1p6/4P2B/1B3NNP/PP1Q1PP1/3RR1K1 w - - bm Qf4; c0 \"Qf4=10, Re2=3, a3=1, a4=2\"; id \"STS(v4.0) Square Vacancy.057\";", "r1k5/4qp2/P1p1b1p1/6Pp/n3P2P/6Q1/2N5/1K1R2R1 b - - bm Qc5; c0 \"Qc5=10, Rxa6=2\"; id \"STS(v4.0) Square Vacancy.058\";", "6k1/3qbp1p/6p1/3Pp1P1/3n3P/pP1Q4/P7/1K2B2B b - - bm Qg4; c0 \"Qg4=10, Bd6=5, Kf8=3, h6=3\"; id \"STS(v4.0) Square Vacancy.059\";", "3n4/p2rk3/3pq1p1/pR3p1p/2PQ1P2/1PB4P/6PK/8 w - - bm Qh8; c0 \"Qh8=10, Bb2=5, Kh1=5, Rb8=6, Rd5=5\"; id \"STS(v4.0) Square Vacancy.060\";", "5b1k/r7/Pq5p/2p5/3p3N/R2Q2P1/5PK1/8 w - - bm Qf5; c0 \"Qf5=10, Ng6+=2, Qf3=8, Qg6=2\"; id \"STS(v4.0) Square Vacancy.061\";", "5rk1/3n4/3Rp1p1/2P2q1p/1p1P2n1/6P1/1B1NQ2P/6K1 b - - bm Qc2; id \"STS(v4.0) Square Vacancy.062\"; c0 \"Qc2=10, Ndf6=4, Nxc5=4\";", "2r3k1/5pbp/2pB2p1/p3P3/2R2P2/2Q3P1/5K1P/1q6 b - - bm Qh1; c0 \"Qh1=10, Qa2+=2, Qb6+=3, c5=2\"; id \"STS(v4.0) Square Vacancy.063\";", "5r2/3q2pk/5n1p/3p1p1P/1p1P4/4PP2/1P2R1PQ/2rNR1K1 b - - bm Qb5; c0 \"Qb5=10, Re8=4\"; id \"STS(v4.0) Square Vacancy.064\";", "5r1k/2pb1q2/1p1p4/pP1Pp1Rp/2P1P3/1KN4r/P7/4Q2R b - - bm Qf3; c0 \"Qf3=10, Qh7=4, Rg8=2, Rxh1=3\"; id \"STS(v4.0) Square Vacancy.065\";", "7k/p4r2/b3p2q/Pp1p2pp/3P2p1/1P2P1P1/2Q2PBP/2R3K1 w - - bm Qc6; c0 \"Qc6=10, Bf1=5, Qe2=4, e4=6\"; id \"STS(v4.0) Square Vacancy.066\";", "3r1k2/2q5/1p6/5P1p/pP2Q3/P2p4/3R4/1K6 b - - bm Qg3; c0 \"Qg3=10, Qc3=5, Qe7=7, Re8=4\"; id \"STS(v4.0) Square Vacancy.067\";", "6k1/4bp2/4q3/3pP3/p1pr3p/P1NbQR2/1P4PR/3K4 b - - bm Qg4; c0 \"Qg4=10, Bc2+=4, Rg4=6\"; id \"STS(v4.0) Square Vacancy.068\";", "5bk1/3r1p1p/3P1Pp1/1q4Pn/2pN3P/1P2B3/1K3Q2/R7 b - - bm Qe5; c0 \"Qe5=10, Qb8=7, Qc5=4, Qd5=8\"; id \"STS(v4.0) Square Vacancy.069\";", "1r1n3k/1pq3pp/p2Nrn2/2P1p3/3p4/Q2P2P1/4PPB1/1RR3K1 w - - bm Rb6; c0 \"Rb6=10, Qa2=4, Qa4=4, Qb3=4, Rb3=5\"; id \"STS(v4.0) Square Vacancy.070\";", "3r1q1k/6pp/4R3/8/n2p1PB1/3PpQP1/4P1K1/8 w - - bm Qc6; c0 \"Qc6=10, Bh3=3, Kh2=3, Re5=3, f5=3\"; id \"STS(v4.0) Square Vacancy.071\";", "8/1p3q1k/p1p2b1P/2B1nP2/8/1Pn2PN1/2Q3P1/6K1 b - - bm Qd5; c0 \"Qd5=10, Bh4=3, Nd5=3, Nd7=5\"; id \"STS(v4.0) Square Vacancy.072\";", "2r4k/7p/2q2nr1/p3R3/Ppp1P3/7P/P1B3P1/3R1Q1K w - - bm Qf5; c0 \"Qf5=10, Qf3=4, Rd2=4, Re7=2\"; id \"STS(v4.0) Square Vacancy.073\";", "bq6/4bk1p/1B1p2p1/3Ppp2/2P5/7P/1Q1N1PP1/6K1 w - - bm Qb5; c0 \"Qb5=10, Qb1=2, Qb3=4, Qb4=2\"; id \"STS(v4.0) Square Vacancy.074\";", "b3qbk1/B6p/1QNp2p1/3Pp3/2P2p2/5P1P/6P1/6K1 w - - bm Qc7; c0 \"Qc7=10, Kh2=2, Nd8=4, Qb5=2\"; id \"STS(v4.0) Square Vacancy.075\";", "r3r1k1/1b3p1p/3b2p1/p2Pp1P1/1pq1B3/4B3/PPP3QP/1K1R1R2 w - - bm Rf6; c0 \"Rf6=10, Bd3=2, b3=1, h4=3\"; id \"STS(v4.0) Square Vacancy.076\";", "r4q1k/1p2b2r/7P/p1pPp3/8/P2PP1R1/1P4Q1/1K5R w - - bm Qe4; c0 \"Qe4=10, Rf1=5, Rg4=5, Rg7=5\"; id \"STS(v4.0) Square Vacancy.077\";", "2b1q3/p4rbk/1p1R2pp/1P1N4/P2pPp2/B4P1P/4Q1P1/6K1 w - - bm Qc4; c0 \"Qc4=10, Nb4=5, Qd2=2, Qd3=2\"; id \"STS(v4.0) Square Vacancy.078\";", "r1b2rk1/p6p/1p1P1pp1/q1p5/2P1PQ2/P7/1R2B1PP/5R1K b - - bm Qc3; c0 \"Qc3=10, Bb7=3, Bd7=1, Be6=2\"; id \"STS(v4.0) Square Vacancy.079\";", "2b2kn1/5pp1/r2q3p/p2pN3/2pP1PP1/5P2/P1Q4P/1B2R2K w - - bm Qh7; c0 \"Qh7=10, Kg1=6, Qc3=6, g5=6\"; id \"STS(v4.0) Square Vacancy.080\";", "2r1r3/1Rpp2pk/2n4p/p1PNqp2/P3p3/2P1P1P1/4QP1P/3R2K1 w - - bm Qh5; c0 \"Qh5=10, Nf4=5, Qa6=2, Rb5=2\"; id \"STS(v4.0) Square Vacancy.081\";", "r1b1k2r/2q1pp2/1p5p/p1pPp1p1/2P5/3B3P/PP2QPP1/4RRK1 w kq - bm Qh5; c0 \"Qh5=10, Bc2=4, Qxe5=4, a3=4\"; id \"STS(v4.0) Square Vacancy.082\";", "1rr3k1/pp3p1p/6p1/1PPB4/4P2q/2Q2P2/P4P1P/2R2K2 w - - bm Qe5; c0 \"Qe5=10, Kg1=3, Kg2=3, Rd1=3\"; id \"STS(v4.0) Square Vacancy.083\";", "r2bn1k1/1p3pp1/pqb1p2p/4B3/2B2Q1P/2N2P2/PPP3P1/1K1R4 b - - bm Qf2; c0 \"Qf2=10, Bf6=1, Qc5=2, Rc8=1\"; id \"STS(v4.0) Square Vacancy.084\";", "7r/q3kp2/2rp1p2/3RpPb1/1p2P1P1/1P1N4/P4R1P/1K2Q3 b - - bm Qa3; c0 \"Qa3=10, Kd7=2\"; id \"STS(v4.0) Square Vacancy.085\";", "1n2r1k1/1q4pp/4pp2/p1R5/Pp1Q4/4B2P/1P3PP1/6K1 w - - bm Qd6; c0 \"Qd6=10, Qc4=5, Qd3=5, Qg4=4\"; id \"STS(v4.0) Square Vacancy.086\";", "2r5/p4p1k/1p5p/2qPnN2/P2R4/1P4Pp/7P/3Q2K1 b - - bm Qc3; c0 \"Qc3=10, Qc1=2\"; id \"STS(v4.0) Square Vacancy.087\";", "4r1k1/1rp2ppp/p3b3/1n1p4/1q1P1B2/1P3P1P/P1Q3P1/3RRNK1 w - - bm Qc6; c0 \"Qc6=10, Ne3=4, Qd2=5, Qd3=5, Qf2=3\"; id \"STS(v4.0) Square Vacancy.088\";", "r1b1n2r/1p1pqpkp/p1n1p3/4P3/4NP2/3B3P/PPP3P1/R2Q1R1K w - - bm Qh5; c0 \"Qh5=10, Qg4+=7, c4=7, f5=7\"; id \"STS(v4.0) Square Vacancy.089\";", "6rk/5Qp1/7p/4p2P/1p1rq3/6R1/PP6/K5R1 b - - bm Qc2; c0 \"Qc2=10, Qd5=3, Qe2=5, Qf4=5\"; id \"STS(v4.0) Square Vacancy.090\";", "3r2k1/5pb1/5qpp/1Np5/8/1P3P1P/1PQ3P1/1K5R b - - bm Qg5; c0 \"Qg5=10, Qa6=3, Qh4=3, Rb8=4\"; id \"STS(v4.0) Square Vacancy.091\";", "5k2/q7/2p5/4pppn/1pP2nN1/1P2RPPP/3r1B1K/4Q3 b - - bm Qd4; c0 \"Qd4=10, Nxg3=2, Rxf2+=4\"; id \"STS(v4.0) Square Vacancy.092\";", "2q2rk1/p1P1bppp/Pn3n2/3p1b2/3P1B2/1P3N1P/4BPP1/2Q2RK1 w - - bm Qc6; c0 \"Qc6=10, Nh4=7, Qe3=7, g4=6\"; id \"STS(v4.0) Square Vacancy.093\";", "5rk1/1Q3pp1/p2ppq1p/6b1/N2NP1P1/8/PP2K1P1/3R4 b - - bm Qf4; c0 \"Qf4=10, Qe5=5, a5=6, d5=6\"; id \"STS(v4.0) Square Vacancy.094\";", "2k3r1/1b1n1p2/5n2/1Rp1q2p/1pPpP1p1/1N3P2/4B1PP/Q4RK1 w - - bm Qa7; c0 \"Qa7=10, Qa5=1, Rf2=2, fxg4=3\"; id \"STS(v4.0) Square Vacancy.095\";", "r3r1k1/5pp1/p2p3p/2nPP3/p5q1/2P1RNP1/2Q2P1P/2R3K1 b - - bm Qc4; c0 \"Qc4=10, Rac8=3, Rad8=3\"; id \"STS(v4.0) Square Vacancy.096\";", "7k/ppr1qpb1/1n2pN1p/4P1p1/8/1B3QP1/PP3PKP/4R3 w - - bm Qh5; c0 \"Qh5=10, Re2=3, Re3=1, h3=2\"; id \"STS(v4.0) Square Vacancy.097\";", "3r2k1/R5p1/2b2npp/1pP5/4pP1P/6Pq/1Q2BP2/4B1K1 w - - bm Qe5; c0 \"Qe5=10, Bf1=2, Ra6=3, Rc7=6\"; id \"STS(v4.0) Square Vacancy.098\";", "3r4/k1pr4/npR1p3/p3Pp1p/P1QP2pN/q5P1/6PP/1R5K b - - bm Qe3; c0 \"Qe3=10, Kb7=4, Qe7=5, f4=4\"; id \"STS(v4.0) Square Vacancy.099\";", "R7/2q3k1/p1r2p1n/2p1pPp1/P3P3/2PP4/7Q/5BK1 w - - bm Qh5; c0 \"Qh5=10, Be2=1, Qb2=1, Qe2=1\"; id \"STS(v4.0) Square Vacancy.100\";", "1b3rk1/5ppp/2p2rq1/1p1n4/3P2P1/1BPbBP2/1P1N2QP/R3R1K1 w - - bm Bxd5; id \"STS(v5.0) Bishop vs Knight.001\";", "1k1r2r1/1p2bp2/4q2p/p1ppP3/6b1/2PQ2B1/PP2NPP1/R1R3K1 b - - bm Bxe2; id \"STS(v5.0) Bishop vs Knight.002\";", "1q2n1k1/r4pb1/6p1/1bpPN1Pp/p1N1PP2/3B2KP/R3Q3/8 b - - bm Bxe5; id \"STS(v5.0) Bishop vs Knight.003\";", "1r1qr1k1/1b1nbpp1/ppnp3p/8/2P1PBB1/2N5/PPNQ2PP/2R2RK1 w - - bm Bxd7; id \"STS(v5.0) Bishop vs Knight.004\";", "1r1r2k1/2qp1ppp/2pbpn2/8/3BP3/3B2P1/P1P2P1P/R2Q1RK1 w - - bm Bxf6; id \"STS(v5.0) Bishop vs Knight.005\";", "1r1r2n1/1pb3pk/p1p3bp/4Np2/3P2P1/2N1RB1P/PP3PK1/4R3 w - - bm Nxg6; id \"STS(v5.0) Bishop vs Knight.006\";", "1r2r1k1/2q3p1/3bp2p/p2nNp2/1ppP1P2/2P3Q1/PP1RN1PP/R5K1 b - - bm Bxe5; id \"STS(v5.0) Bishop vs Knight.007\";", "1r2rbk1/1bqn1pp1/pp1p2np/3N1p2/2P1P3/PP2B1PB/4NQKP/2RR4 b - - bm Bxd5; id \"STS(v5.0) Bishop vs Knight.008\";", "1r3bk1/1q2r1p1/pp1p3p/2nP2p1/3BP3/PP3QP1/4N1KP/2RR4 w - - bm Bxc5; id \"STS(v5.0) Bishop vs Knight.009\";", "1r4k1/2qnbpp1/r3p2p/3pP2b/1p1N4/3P1NP1/nBPQ1PBP/R3R1K1 b - - bm Bxf3; id \"STS(v5.0) Bishop vs Knight.010\";", "1r4k1/6pp/2b3n1/Nnq1p3/4Pp2/3B2PP/4QP1K/2B1R3 w - - bm Nxc6; id \"STS(v5.0) Bishop vs Knight.011\";", "1rr4k/5p1p/4b1p1/pp1Nb3/4P3/PP1N1P2/K5RP/3R4 b - - bm Bxd5; id \"STS(v5.0) Bishop vs Knight.012\";", "2b1rr2/4q1pk/p2ppb1p/1p2n3/4PNB1/P1N4Q/1PP3PP/R4R1K b - - bm Nxg4; id \"STS(v5.0) Bishop vs Knight.013\";", "2b1rr2/4q1pk/p2ppb1p/1p5P/4PNQ1/P1N5/1PP3P1/R4R1K b - - bm Bxc3; id \"STS(v5.0) Bishop vs Knight.014\";", "2b3k1/p3qpbp/2p3p1/1pn1p3/4P3/PBP1NN1P/1PQ2PP1/6K1 b - - bm Nxb3; id \"STS(v5.0) Bishop vs Knight.015\";", "2k5/1p1nbqpp/pPprp1b1/P1p1p3/2P1P2N/4BP1P/1Q1N2P1/3R2K1 w - - bm Nxg6; id \"STS(v5.0) Bishop vs Knight.016\";", "2k5/p7/1pp3r1/6b1/3P1p1p/P4NrP/1PP2R2/1K1R4 w - - bm Nxg5; id \"STS(v5.0) Bishop vs Knight.017\";", "2kr1b1r/p1qn3p/Ppp3p1/4np2/8/2NBBP2/1PP3PP/RQ2R1K1 b - - bm Nxd3; id \"STS(v5.0) Bishop vs Knight.018\";", "2kr3b/1pr2p1p/p1n1p1p1/2NpPn2/1P1P4/3RBP1B/1P2KP1P/6R1 w - - bm Bxf5; id \"STS(v5.0) Bishop vs Knight.019\";", "2krr3/pp1qbp2/4b3/1P2P2p/P1pPB1pN/4B1Q1/5PPP/3R2K1 b - - bm Bxh4; id \"STS(v5.0) Bishop vs Knight.020\";", "2n2rk1/p2b2bp/3p2p1/q1pPp2n/P1P1P3/1QN5/3BBNPP/1R4K1 w - - bm Bxh5; id \"STS(v5.0) Bishop vs Knight.021\";", "2q3k1/1b4r1/p2np2p/QnBpN3/3P1pPp/1P1BP3/8/R5K1 w - - bm Bxb5; id \"STS(v5.0) Bishop vs Knight.022\";", "2r1r1k1/1q1bp1bp/3p2p1/1pnP4/3NB3/1P2BP2/3Q2PP/1RR3K1 b - - bm Nxe4; id \"STS(v5.0) Bishop vs Knight.023\";", "2r1r1k1/pp1q1pbp/2npbnp1/4p1N1/1P2P3/2P2N1P/P4PP1/R1BQRBK1 w - - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.024\";", "2r2nk1/1b3ppp/p2q1n2/1p1p4/3B1P1Q/1P1NP1P1/P5BP/2R3K1 w - - bm Bxf6; id \"STS(v5.0) Bishop vs Knight.025\";", "2r2rk1/4bp2/p6p/1p1R4/3nn1p1/4P1B1/PP1NBPPP/4R1K1 b - - bm Nxe2+; id \"STS(v5.0) Bishop vs Knight.026\";", "2r2rk1/p2qbppp/5n2/3p1N2/1p2n3/1NP1BQ2/PP3PPP/R4RK1 w - - bm Nxe7+; id \"STS(v5.0) Bishop vs Knight.027\";", "2r5/2q2pk1/3n1b1p/3Pp1p1/b1B1P1N1/P4N2/5PPP/2Q2RK1 w - - bm Nxf6; id \"STS(v5.0) Bishop vs Knight.028\";", "2r5/3n2k1/5p2/Pp2nB1r/1B1pP2P/P1pP2P1/6K1/3R2R1 w - - bm Bxd7; id \"STS(v5.0) Bishop vs Knight.029\";", "2rq1rk1/1b2bppp/p3p3/n7/3P4/1BBQ1N1P/P4PP1/R3R1K1 b - - bm Nxb3; id \"STS(v5.0) Bishop vs Knight.030\";", "2rq1rk1/1p1npp1p/p2pb1p1/3N4/3QP3/1BP2P2/PP4PP/R4R1K b - - bm Bxd5; id \"STS(v5.0) Bishop vs Knight.031\";", "2rq1rk1/1p1nppbp/p2pn1p1/6B1/2P1P3/1PN1Q1PP/P4PB1/2R2RK1 b - - bm Nxg5; id \"STS(v5.0) Bishop vs Knight.032\";", "2rq1rk1/3bppbp/p4np1/n3Q3/Np6/1B3N1P/PPP2PP1/R1BR2K1 b - - bm Nxb3; id \"STS(v5.0) Bishop vs Knight.033\";", "2rq1rk1/4bppp/p1p1pn2/1p1b4/3P4/2NB1NQ1/PPP2PPP/1K1RR3 b - - bm Bxf3; id \"STS(v5.0) Bishop vs Knight.034\";", "2rq1rk1/pp1bppbp/3p1np1/8/2nNP3/1BN1BP2/PPP2QPP/R4RK1 w - - bm Bxc4; id \"STS(v5.0) Bishop vs Knight.035\";", "2rqr1k1/1b3p1p/pn1p1bpB/2p5/3P4/1P3N1P/P2QBPP1/2RR2K1 b - - bm Bxf3; id \"STS(v5.0) Bishop vs Knight.036\";", "2rqr1k1/1b3ppp/3b2n1/p2pN3/1p1P1B2/3BPP1P/P2Q2P1/1R1R2K1 b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.037\";", "2rr2k1/ppq1pp1p/2npbnpB/8/2PNP3/1QP2P2/P3B1PP/R4RK1 w - - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.038\";", "2rr4/p3q1pk/bn2p2p/2b1Pp2/1pN4P/1P4Q1/P2BBPP1/2RR2K1 b - - bm Bxc4; id \"STS(v5.0) Bishop vs Knight.039\";", "3b1k2/1b3p1p/pP4p1/3p4/1p1PnBP1/1K3B2/PP2N2P/8 w - - bm Bxe4; id \"STS(v5.0) Bishop vs Knight.040\";", "3qr2k/p5r1/1pRp1n1b/1P1Pp2p/1P3p1P/2N1nP1Q/1R3BP1/5BK1 w - - bm Bxe3; id \"STS(v5.0) Bishop vs Knight.041\";", "3r1rk1/1b2p1bp/pp6/4Pp1n/B2n4/2NNBP1p/PP3KPP/3RR3 w - - bm Bxd4; id \"STS(v5.0) Bishop vs Knight.042\";", "3r2k1/1b3ppp/1p2pq2/p7/2P5/1Pn1QNP1/4PPBP/4R1K1 b - - bm Bxf3; id \"STS(v5.0) Bishop vs Knight.043\";", "3r2k1/5p1p/p2qn1p1/P3N1r1/1P1pb1B1/2nN3P/5PPQ/2R1R2K b - - bm Bxd3; id \"STS(v5.0) Bishop vs Knight.044\";", "3r2k1/pr2bpp1/6bp/3nN3/2R3P1/4B2P/P3BP2/3R2K1 b - - bm Nxe3; id \"STS(v5.0) Bishop vs Knight.045\";", "3rk2r/1p2bp2/p1n1q2p/3pPbp1/3B4/2P3Q1/BN3PPP/R3R1K1 b k - bm Nxd4; id \"STS(v5.0) Bishop vs Knight.046\";", "3rr1k1/p1p1p1bp/2p2pp1/3bP3/5P2/BPP2N2/P5PP/4RRK1 b - - bm Bxf3; id \"STS(v5.0) Bishop vs Knight.047\";", "3rr3/p3b1kp/2p2pp1/1q1npb2/4N1PB/1NP2Q2/PP3P1P/R2R2K1 b - - bm Bxe4; id \"STS(v5.0) Bishop vs Knight.048\";", "4qr1k/2bn1p1p/2N1p3/p2nP3/NpRQ1B2/1P4PP/P6K/8 b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.049\";", "4r1k1/p4bp1/1p1p1p2/1P1N2n1/4P1P1/4qP1P/P4RQ1/5BK1 b - - bm Bxd5; id \"STS(v5.0) Bishop vs Knight.050\";", "4r1k1/p4p1p/1p2q1pB/1Q2Pn2/P3B2P/5P2/bR3P2/6K1 w - - bm Bxf5; id \"STS(v5.0) Bishop vs Knight.051\";", "4r1k1/pb6/2pbpp1B/2N2p2/3P4/1P6/P4KPP/R7 b - - bm Bxc5; id \"STS(v5.0) Bishop vs Knight.052\";", "4r3/1p2rpk1/p1p1bqp1/2QnN1bp/1B1PB3/P6P/1P2RPP1/4R1K1 b - - bm Nxb4; id \"STS(v5.0) Bishop vs Knight.053\";", "4r3/5pkp/2b1n1p1/3p3n/p1pP4/P1N1BP1B/1P3KPP/2R5 w - - bm Bxe6; id \"STS(v5.0) Bishop vs Knight.054\";", "4rrk1/2q3pp/pp1p1bn1/2nR4/2P2B2/N4PP1/PP3Q1P/3R1BK1 b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.055\";", "5k2/p2b3r/1ppb1p2/1q1p1P2/1P1PrNpP/4P1P1/1RQ2BK1/2R5 b - - bm Bxf4; id \"STS(v5.0) Bishop vs Knight.056\";", "5k2/p2q2p1/bp1p1p1p/3PnP1Q/1P1BP3/2P5/3N2P1/6K1 w - - bm Bxe5; id \"STS(v5.0) Bishop vs Knight.057\";", "6k1/r2rbpp1/p1q2n2/1pp2PB1/5Q2/1PN1R2P/1P3P2/4R1K1 w - - bm Bxf6; id \"STS(v5.0) Bishop vs Knight.058\";", "6k1/r6p/2pb1ppn/p1Np4/1P1PrPP1/P6P/2R5/3K1RB1 b - - bm Bxc5; id \"STS(v5.0) Bishop vs Knight.059\";", "6n1/p6k/2R5/2p1rp2/p1P1n3/P3PB1P/5PP1/6K1 w - - bm Bxe4; id \"STS(v5.0) Bishop vs Knight.060\";", "6r1/r2Nbnk1/2R3pp/p4p2/PpB2PP1/1P6/6K1/3R4 w - - bm Bxf7; id \"STS(v5.0) Bishop vs Knight.061\";", "8/1R3pp1/p3rnk1/3pBb1p/3P3P/1B1P1PP1/5K2/8 w - - bm Bxf6; id \"STS(v5.0) Bishop vs Knight.062\";", "8/5p1k/2p3pp/2p1p3/2P2nPP/2Q2P2/1P1B1K2/q7 w - - bm Bxf4; id \"STS(v5.0) Bishop vs Knight.063\";", "8/6k1/4ppp1/r2Pn2p/bpq1PB1P/6P1/R4PK1/1QN5 w - - bm Bxe5; id \"STS(v5.0) Bishop vs Knight.064\";", "8/r2b1nk1/Np1qp2p/pP1pNrpP/Pp1P4/3Q1PP1/2R3K1/7R w - - bm Nxd7; id \"STS(v5.0) Bishop vs Knight.065\";", "q1r1r1k1/p2nppbp/1pnpb1p1/6N1/1PP1P3/P1N1Q2P/4BPPB/1R1R2K1 w - - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.066\";", "q5k1/1pbb2pn/2ppNr1p/p3rP2/P1P1pNB1/1P5P/5PP1/1RQR2K1 b - - bm Bxe6; id \"STS(v5.0) Bishop vs Knight.067\";", "r1b1r1k1/p1qn1ppp/2pb1n2/4pN2/Np2P3/7P/PPQ1BPP1/R1B2RK1 w - - bm Nxd6; id \"STS(v5.0) Bishop vs Knight.068\";", "r1b1r1k1/ppq2ppp/2n5/3p4/P1pPn3/B1P1PN2/2B2PPP/R2Q1RK1 w - - bm Bxe4; id \"STS(v5.0) Bishop vs Knight.069\";", "r1b2rk1/p3qppp/1pn5/2b5/3pN3/5NP1/PP2PPBP/R2Q1RK1 w - - bm Nxc5; id \"STS(v5.0) Bishop vs Knight.070\";", "r1bq2k1/pp3rbp/2n1p1p1/1BBpPn2/8/2N2N1P/PP3PP1/R2Q1RK1 w - - bm Bxc6; id \"STS(v5.0) Bishop vs Knight.071\";", "r1br4/pp3pk1/2p2npp/2n1N3/P2q4/2NB2QP/1PP2PP1/R3R1K1 b - - bm Nxd3; id \"STS(v5.0) Bishop vs Knight.072\";", "r1r3k1/1nqb1ppp/3p4/pp1Pb3/1P2Pp2/R1PB1N1P/Q4PP1/1N2R1K1 w - - bm Nxe5; id \"STS(v5.0) Bishop vs Knight.073\";", "r1r3k1/1p1b1p2/1q1p3p/1NpPb1pB/PpP1Pp2/1P6/R4PPP/1Q1R3K b - - bm Bxb5; id \"STS(v5.0) Bishop vs Knight.074\";", "r1r4k/pb1nq1pp/1p1bp3/1N1p1p2/3Pn3/1P1NB1P1/PQ2PPBP/R4RK1 w - - bm Nxd6; id \"STS(v5.0) Bishop vs Knight.075\";", "r2q1rk1/2pb2b1/1p1p2pp/p1nP1p1n/2P5/2N1B1P1/PPQ1B2P/R3NRK1 w - - bm Bxh5; id \"STS(v5.0) Bishop vs Knight.076\";", "r2q1rk1/pb3pbp/1pn1p1p1/2pnN3/2N2B2/3P2P1/PPP2PBP/R2QR1K1 b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.077\";", "r2q1rk1/pppb1ppp/1b1p2n1/3Pp3/2N1P1n1/2PB1N2/PP3PPP/R1BQR1K1 w - - bm Nxb6; id \"STS(v5.0) Bishop vs Knight.078\";", "r2r2k1/1p2qppp/2n1bn2/p2Bp1N1/2P5/R5P1/1P1NPP1P/1Q1R2K1 w - - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.079\";", "r2r2k1/1pq2pp1/p1b1p2p/3n4/8/1QB3P1/PP2PPBP/R3R1K1 b - - bm Nxc3; id \"STS(v5.0) Bishop vs Knight.080\";", "r2r2k1/ppp2pb1/2n1p1pp/1B2P3/3PNn1q/2Q1BP2/PP3P1P/2R1K2R w K - bm Bxf4; id \"STS(v5.0) Bishop vs Knight.081\";", "r3k2r/ppqbnpb1/n1p1p2p/P2pP1p1/3P4/1P1BBN1P/1NPQ1PP1/R3K2R w KQkq - bm Bxa6; id \"STS(v5.0) Bishop vs Knight.082\";", "r3kb1B/1p1b3p/pqnpp1p1/7n/8/2NRp3/PPP1B3/1K1RQ3 w q - bm Bxh5; id \"STS(v5.0) Bishop vs Knight.083\";", "r3qrk1/1pp2bp1/p1n1p2p/2Pp1pbP/1P1P4/P1NBPN2/2Q2PP1/2R1K2R w K - bm Nxg5; id \"STS(v5.0) Bishop vs Knight.084\";", "r3r1k1/p1q3bp/2p1bpp1/2nnp3/2N1N3/2PB2B1/PPQ2PPP/R3R1K1 b - - bm Nxd3; id \"STS(v5.0) Bishop vs Knight.085\";", "r3r1k1/pp3pbp/2p3p1/P3p3/1n2P1n1/2N1B3/1PPR1PPP/R3N1K1 b - - bm Nxe3; id \"STS(v5.0) Bishop vs Knight.086\";", "r3r1k1/ppq2pb1/2p1b1p1/P3n2p/2PN1B2/7P/1P1QBPP1/R2R2K1 w - - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.087\";", "r3r2k/6p1/q3pp1p/1b1pn2P/3Q2B1/1P6/1BP2PP1/1K1RR3 b - - bm Nxg4; id \"STS(v5.0) Bishop vs Knight.088\";", "r3rbk1/1p3pp1/1qp1b2p/p1Nn4/2Rp4/P2P1BP1/1P1BPP1P/2Q1R1K1 b - - bm Bxc5; id \"STS(v5.0) Bishop vs Knight.089\";", "r4r1k/1p2b1pp/pBq3n1/2PNp3/2N1Ppn1/1Q4P1/PP1R3P/5RK1 w - - bm Nxe7; id \"STS(v5.0) Bishop vs Knight.090\";", "r4r2/p1n1q1bk/6pp/1pp2b2/2P1p3/P3N1P1/R2QPPBP/2BR2K1 w - - bm Nxf5; id \"STS(v5.0) Bishop vs Knight.091\";", "r4r2/p3pp1k/1nR1b2p/5Np1/4P3/5NP1/Pb3PBP/5RK1 b - - bm Bxf5; id \"STS(v5.0) Bishop vs Knight.092\";", "r4rk1/1bq2pp1/1p1bpn1p/p2nN3/P1BP4/5NP1/1P1BQPKP/R3R3 b - - bm Bxe5; id \"STS(v5.0) Bishop vs Knight.093\";", "r4rk1/1p2bppp/p2p1nn1/3P4/2PN1B2/8/PP2B1PP/R4R1K b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.094\";", "r4rk1/1ppbq1bp/1n4p1/p1NPpp2/1n6/1PN1P1PP/PB3PB1/R2Q1RK1 w - - bm Nxd7; id \"STS(v5.0) Bishop vs Knight.095\";", "r4rk1/ppq1ppbp/1np1P1p1/2Nb4/Pn6/3B1N1P/1PP1QPP1/R1B1R1K1 b - - bm Nxd3; id \"STS(v5.0) Bishop vs Knight.096\";", "r5k1/pp1n1rpp/2pb1q2/2N5/1P3B2/P2P2P1/3Q1PKP/1R2R3 b - - bm Bxc5; id \"STS(v5.0) Bishop vs Knight.097\";", "r5k1/pp2q1bp/n2p2p1/2pPpr2/2P1Nn2/PQ3N1P/1P1B1PP1/1R2R1K1 w - - bm Bxf4; id \"STS(v5.0) Bishop vs Knight.098\";", "r6r/4bkpp/2p1np2/p1p5/2P1NB2/6P1/PP2P2P/3R1R1K b - - bm Nxf4; id \"STS(v5.0) Bishop vs Knight.099\";", "rnq1k2r/p3ppbp/1p2bnp1/2p3N1/4P3/3BB2P/P1QN1PP1/1R3RK1 w kq - bm Nxe6; id \"STS(v5.0) Bishop vs Knight.100\";", "1k1r1r2/p1p5/Bpnbb3/3p2pp/3P4/P1N1NPP1/1PP4P/2KR1R2 w - - bm Ncxd5; id \"STS(v6.0) Recapturing.001\"; ", "1k1r4/4rp2/1p1qbnp1/p2p3p/P2P4/1Nn2P2/1P1QBRPP/2R3K1 w - - bm Rxc3; id \"STS(v6.0) Recapturing.002\"; ", "1k1rr3/pp2qpp1/1b2p3/4N2p/2R1n2P/P2R2P1/1PP1QP2/1K6 w - - bm Rxe4; id \"STS(v6.0) Recapturing.003\"; ", "1k3rn1/3r1p2/p2p1np1/Pp1P3p/1Pp1PP2/2P1R1bP/2B2K1B/6R1 w - - bm Bxg3; id \"STS(v6.0) Recapturing.004\"; ", "1q2rnk1/5rb1/bp1p1np1/pNpP2Bp/P1P1Pp1P/3B2P1/3QNRR1/7K w - - bm gxf4; id \"STS(v6.0) Recapturing.005\"; ", "1q4k1/1r3pp1/3p1n1p/2pPpP2/b1PnP3/rPB1R1NP/3Q2PK/1R3B2 b - - bm Bxb3; id \"STS(v6.0) Recapturing.006\"; ", "1qr2rk1/4ppbp/6p1/pp1b4/2NP4/4B3/PPB2PPP/2RQR2K b - - bm bxc4; id \"STS(v6.0) Recapturing.007\"; ", "1qrr3k/6p1/1p1pp2p/pNn5/Pn1bP1PP/5Q2/1PP1N3/1K1R2R1 w - - bm Nexd4; id \"STS(v6.0) Recapturing.008\"; ", "1r1q2k1/5pp1/2bp2rp/p1pNp2n/2PBP3/1P1B1P2/P2Q2PP/1R3RK1 b - - bm cxd4; id \"STS(v6.0) Recapturing.009\"; ", "1r1r2k1/5pb1/6p1/p1pb2Pp/PpB1PR2/7P/1PP5/2KR4 w - - bm exd5; id \"STS(v6.0) Recapturing.010\"; ", "1r2r1k1/pb1n1pp1/1p1p2np/2P1p1q1/P1P1P3/2P2PP1/2QNBB1P/1R1R3K b - - bm Nxc5; id \"STS(v6.0) Recapturing.011\"; ", "1r2r2k/1p3pbp/b5p1/p2np3/P1P1B1PP/1PN2P2/6K1/3R1R2 w - - bm Nxd5; id \"STS(v6.0) Recapturing.012\"; ", "1r2rbk1/1b1q1p2/3pn1p1/1p5p/1p2P2P/P1P1BPP1/4QNBK/2R1R3 w - - bm axb4; id \"STS(v6.0) Recapturing.013\"; ", "1r3nk1/3r1qb1/bp1p1np1/pNpP2Bp/P1P1Pp1P/7N/1P1QBR2/6RK w - - bm Rxf4; id \"STS(v6.0) Recapturing.014\"; ", "1r3r2/1b4bk/p1n2ppp/qp1p4/4PN2/1B2B1P1/P3QP2/1R1R2K1 w - - bm exd5; id \"STS(v6.0) Recapturing.015\"; ", "1r3rk1/p1qnppbp/p5p1/2pPP3/2P5/4R2P/PP2Q1P1/1RBN3K b - - bm Bxe5; id \"STS(v6.0) Recapturing.016\"; ", "1r4k1/1rq2ppp/3p1n2/2pPp3/p1PnP3/PPB4P/3Q1PP1/1R2RBK1 b - - bm axb3; id \"STS(v6.0) Recapturing.017\"; ", "1r4k1/pr3ppp/q2p1n2/2pPp3/b1PnP3/PPB3NP/3Q1PP1/1R2RBK1 b - - bm Nxb3; id \"STS(v6.0) Recapturing.018\"; ", "1rq2rk1/4bp2/2np2p1/p1p1p3/P1PNP1P1/1PB2P2/1Q3K1P/R2R1N2 b - - bm exd4; id \"STS(v6.0) Recapturing.019\"; ", "1rq2rk1/4bp2/2np2p1/p1p1p3/P1PNP1P1/1PB2P2/1Q4P1/R2R1NK1 b - - bm cxd4; id \"STS(v6.0) Recapturing.020\"; ", "1rq2rk1/4bp2/2np2p1/p1p1p3/P1PNP1P1/1PB2P2/1Q4PK/R2R1N2 b - - bm cxd4; id \"STS(v6.0) Recapturing.021\"; ", "1rq2rk1/4bp2/3pn1p1/p1p1p3/P1PNP1P1/1PB2P2/1Q5P/R2R1NK1 b - - bm Nxd4; id \"STS(v6.0) Recapturing.022\"; ", "2kr3r/p1p4p/1np2p2/3P4/1b2N3/1P4P1/PB3PP1/R2K1R2 b - - bm Nxd5; id \"STS(v6.0) Recapturing.023\"; ", "2qr2r1/1p3pk1/p1np1np1/7p/2P1Pp2/1PN5/3QN1PP/4RR1K w - - bm Nxf4; id \"STS(v6.0) Recapturing.024\"; ", "2r1rbk1/1p3p2/1qn3p1/p1Pp1b1p/5P2/2P1RNP1/PP1Q2BP/RN5K b - - bm Bxc5; id \"STS(v6.0) Recapturing.025\"; ", "2r2r1k/1q1nbpp1/p3p2p/2P1P3/1p1N1B1P/6Q1/PP3PP1/R2R2K1 b - - bm Nxc5; id \"STS(v6.0) Recapturing.026\"; ", "2r2rk1/1p1q1pb1/p2p1np1/7p/2P1Pp1Q/1PN5/P2B2PP/4RR1K w - - bm Rxf4; id \"STS(v6.0) Recapturing.027\"; ", "2r2rk1/3bqppp/1p6/p2pb2Q/Pn1P1P2/1P2PN2/6PP/R1B2RK1 w - - bm fxe5; id \"STS(v6.0) Recapturing.028\"; ", "2r2rk1/3bqppp/2p5/p2pb2Q/Pn1P1P2/1P2PN2/6PP/R1B2RK1 w - - bm Qxe5; id \"STS(v6.0) Recapturing.029\"; ", "2r3k1/1bqnbpp1/1p2p2p/2P5/P7/4PN1B/1B2QPPP/R5K1 b - - bm Nxc5; id \"STS(v6.0) Recapturing.030\"; ", "2r3k1/1p1nqp1p/1P4p1/Q2Pp3/1RbnP3/5P1P/3N2P1/5B1K w - - bm Rxc4; id \"STS(v6.0) Recapturing.031\"; ", "2r5/4bkp1/p3q3/1b2p2n/N3PBp1/1p2Q3/1P3PPP/RB1R2K1 b - - bm Nxf4; id \"STS(v6.0) Recapturing.032\"; ", "2rq1r1k/p2nb1p1/1p3P1p/3p4/8/1P1p3Q/P2B1PPP/1R1N1RK1 b - - bm Bxf6; id \"STS(v6.0) Recapturing.033\"; ", "2rq1rk1/4ppbp/p5p1/1p1b4/1PNP4/4B3/P1B2PPP/2RQR1K1 b - - bm Rxc4; id \"STS(v6.0) Recapturing.034\"; ", "2rq1rk1/4ppbp/p5p1/1p1b4/2NP4/P3B2P/1PB2PP1/2RQR1K1 b - - bm Rxc4; id \"STS(v6.0) Recapturing.035\"; ", "2rq1rk1/4ppbp/p5p1/1p1b4/2NP4/P3B3/1P3PPP/1BRQR2K b - - bm Rxc4; id \"STS(v6.0) Recapturing.036\"; ", "2rqr1k1/1b2bp1n/1pnpp1pp/p7/P2pP2P/1NPB1NP1/1P2QPK1/1R2B1R1 w - - bm Nbxd4; id \"STS(v6.0) Recapturing.037\"; ", "2rr4/1pqb1ppk/p1Nppn1p/4n3/2P1P3/2N3P1/PP1QB1PP/R3BRK1 b - - bm Bxc6; id \"STS(v6.0) Recapturing.038\"; ", "3b2nk/p2Nqrpp/1pB1p3/3pPnp1/1P1P1P1P/P7/2R1QB1K/8 w - - bm fxg5; id \"STS(v6.0) Recapturing.039\"; ", "3q4/p2b2kp/1R1P1p2/b1p5/2B1rPp1/8/P5P1/B1Q2RK1 b - - bm Bxb6; id \"STS(v6.0) Recapturing.040\"; ", "3r1r2/pkp4p/1np1p3/3P4/1b2N3/1P3P2/1B4PP/2R2RK1 b - - bm exd5; id \"STS(v6.0) Recapturing.041\"; ", "3r1rk1/ppp2ppp/1nnN4/7q/8/1B5P/PP1BRPP1/R3Q1K1 b - - bm cxd6; id \"STS(v6.0) Recapturing.042\"; ", "3r4/pkp2r2/1np5/3P3p/1b1BN1p1/1P3PP1/6P1/2R2R1K b - - bm cxd5; id \"STS(v6.0) Recapturing.043\"; ", "3r4/pkp2r2/1np5/3P3p/1b1BN1pP/1P3P2/6P1/2R2R1K b - - bm Rxd5; id \"STS(v6.0) Recapturing.044\"; ", "3rkb1r/p2npp1p/1p4p1/1Q1p4/P2p1B2/2q1PN2/2P2PPP/R4RK1 w k - bm exd4; id \"STS(v6.0) Recapturing.045\"; ", "3rr1k1/p4bbp/1p1p4/3p1BPp/P2p1N1P/2P1P1K1/R1P5/3R4 w - - bm exd4; id \"STS(v6.0) Recapturing.046\"; ", "3rr1k1/pR1n1p1p/5b2/3N4/6p1/P2bp1P1/3N1PBP/2R3K1 w - - bm Nxe3; id \"STS(v6.0) Recapturing.047\"; ", "4q1k1/p2n1pp1/4p2p/3n4/1P1P4/P4P2/1B1QN1PP/2r2K2 w - - bm Bxc1; id \"STS(v6.0) Recapturing.048\"; ", "4r1k1/2p2rp1/1pnp4/p2N3p/2P2p2/PP1RP1P1/6KP/5R2 w - - bm gxf4; id \"STS(v6.0) Recapturing.049\"; ", "4rnk1/3r1qb1/bp1p1np1/pNpP2Bp/P1P1Pp1P/3B4/1P1QNR2/6RK w - - bm Nxf4; id \"STS(v6.0) Recapturing.050\"; ", "5r2/4k3/2qpp2b/pN2p2n/P3PR1p/2P5/6PP/QB2R2K b - - bm Bxf4; id \"STS(v6.0) Recapturing.051\"; ", "6k1/pp2npp1/2r1bq1p/3p4/PP3N1P/5BP1/4QP2/3R3K w - - bm Bxd5; id \"STS(v6.0) Recapturing.052\"; ", "6n1/1k1qn1r1/1p1p4/p1pPpPp1/2P3N1/2B2PP1/PP4Q1/KR6 b - - bm Nxf5; id \"STS(v6.0) Recapturing.053\"; ", "8/1p4k1/3p4/p1pbp1r1/P1P1P2p/1PK2B2/5R2/8 w - - bm exd5; id \"STS(v6.0) Recapturing.054\"; ", "8/p1p2p1k/1p1pqn1p/7r/P1PPP1rP/2P1RQ2/3N1K2/6R1 b - - bm Rhxh4; id \"STS(v6.0) Recapturing.055\"; ", "b2r1r2/6bk/p1n2ppp/q2p4/1p2PN2/1B2B1P1/P3QP2/1R1R2K1 w - - bm Rxd5; id \"STS(v6.0) Recapturing.056\"; ", "b2r1r2/6bk/p1n2ppp/qp1p4/4PN2/1B2B1PP/P3Q3/1R1R2K1 w - - bm Nxd5; id \"STS(v6.0) Recapturing.057\"; ", "b4r1r/3kq1p1/p1p1p1p1/NpPpPP2/1P1P2P1/4Q2p/1K5P/4R2R w - - bm fxe6+; id \"STS(v6.0) Recapturing.058\"; ", "br1r2k1/p2n1pp1/np1p4/2P1p1qp/2P1P3/P1P2PP1/2QNBB1P/2R2R1K b - - bm Naxc5; id \"STS(v6.0) Recapturing.059\"; ", "q1rb2rk/1b3ppp/2n1p2n/p1BpP3/B2P1PP1/1p2RN1P/2PN1Q2/1R4K1 w - - bm Rexb3; id \"STS(v6.0) Recapturing.060\"; ", "q1rb2rk/1b3ppp/2n1p2n/p2pP3/B2P1PP1/Bp2RN1P/1RPNQ3/6K1 w - - bm Nxb3; id \"STS(v6.0) Recapturing.061\"; ", "q2r2k1/1br1bpp1/pp3n2/2nN4/P1P4P/1P3P1B/1BQ4P/1R1RN1K1 b - - bm Nxd5; id \"STS(v6.0) Recapturing.062\"; ", "r1b2rk1/4qppp/pp6/3pb2Q/Pn1P1P2/1P2PN2/6PP/R1B2RK1 w - - bm dxe5; id \"STS(v6.0) Recapturing.063\"; ", "r1b2rk1/p3qppp/Pp6/3pb2Q/1n1P1P2/1P2PN2/1B4PP/R4RK1 w - - bm dxe5; id \"STS(v6.0) Recapturing.064\"; ", "r1b3k1/5r2/p4n1p/3q2pP/2p1p3/P1PBB3/3N1PP1/R2QK2R b KQ - bm exd3; id \"STS(v6.0) Recapturing.065\"; ", "r1bq1rk1/1p1nbppp/p3pn2/P1p5/4p3/2NP2N1/BPPB1PPP/R2Q1RK1 w - - bm dxe4; id \"STS(v6.0) Recapturing.066\"; ", "r1q1k2r/1p1nbppp/p7/3bpP2/2P1P1P1/1PN1BQ2/7P/2KR3R w kq - bm Nxd5; id \"STS(v6.0) Recapturing.067\"; ", "r1q1k2r/1p1nbppp/p7/Q2bpP2/2B1P1P1/1P2N3/PK5P/3R3R w kq - bm Qxd5; id \"STS(v6.0) Recapturing.068\"; ", "r1qr2k1/1b1nbpp1/1P6/p1B1p2p/4P3/3B3Q/2P1N1PP/RR1N3K b - - bm Nxc5; id \"STS(v6.0) Recapturing.069\"; ", "r1qr2k1/4bp1p/2p1p1p1/2pb4/PP1P1P2/3NQ3/5BPP/R2R2K1 w - - bm Nxc5; id \"STS(v6.0) Recapturing.070\"; ", "r1qr2k1/4bp2/p1p1p1p1/2pb4/PP1P1P2/3NQ3/5BPP/R2R2K1 w - - bm bxc5; id \"STS(v6.0) Recapturing.071\"; ", "r1qr2k1/p3bp1p/P1p1p1p1/2pb4/1P1P1P2/3NQ3/5BPP/R2R2K1 w - - bm dxc5; id \"STS(v6.0) Recapturing.072\"; ", "r1r2nk1/5pp1/1pnpbq2/2B5/p1P1PN1p/8/PPNR1QP1/3R1BK1 b - - bm dxc5; id \"STS(v6.0) Recapturing.073\"; ", "r1r3k1/1p2q1pp/n2pp3/pN6/2nbP1P1/P1N2Q2/1PP4P/1K1R1R2 w - - bm Rxd4; id \"STS(v6.0) Recapturing.074\"; ", "r2q1rk1/1b1pbppp/1pn1pn2/p7/P2pP3/2PB1N2/1P1NQPPP/R1B2RK1 w - - bm Nxd4; id \"STS(v6.0) Recapturing.075\"; ", "r2q1rk1/2pnbppp/p3p3/3b4/3P2P1/3QnN1P/PP1BPP2/RNR3K1 w - - bm Qxe3; id \"STS(v6.0) Recapturing.076\"; ", "r2q1rk1/p1p1n1pp/1p1p4/P2Ppb2/1PP5/4b1P1/3QNPBP/R4RK1 w - - bm fxe3; id \"STS(v6.0) Recapturing.077\"; ", "r2qk1r1/1b2bp2/3ppp1p/p1p5/2B1PP2/1nN5/PPPQ2PP/1K1R3R w q - bm axb3; id \"STS(v6.0) Recapturing.078\"; ", "r2qk1r1/1b3pb1/2pppp1p/8/2B1PP2/1nN5/PPPQ2PP/1K1R3R w q - bm Bxb3; id \"STS(v6.0) Recapturing.079\"; ", "r2qk1r1/3bbp2/3ppp2/p7/2B1PP1p/1nN5/PPPQ2PP/1K1R3R w q - bm cxb3; id \"STS(v6.0) Recapturing.080\"; ", "r2r1k2/2b1npp1/p1np3p/1p6/3P1B2/P1N4P/1PBR1PP1/3qR1K1 w - - bm Bxd1; id \"STS(v6.0) Recapturing.081\"; ", "r2r1nk1/1q2bp2/p3pnpp/2p1B3/2p5/1P2N1P1/P1QNPP1P/2R2RK1 w - - bm Ndxc4; id \"STS(v6.0) Recapturing.082\"; ", "r3b3/4kpp1/3p1n1p/5P2/1r2P1P1/1p2RN1P/2PN4/1R4K1 w - - bm Rbxb3; id \"STS(v6.0) Recapturing.083\"; ", "r3r1k1/1p3pb1/6p1/p2b2Pp/PpB1PR2/1P6/2P4P/2KR4 w - - bm Rxd5; id \"STS(v6.0) Recapturing.084\"; ", "r4r1k/p2nb1p1/1p2pPqp/3p4/P1pPn3/2P1BN1P/1QP1BPP1/R5RK b - - bm Qxf6; id \"STS(v6.0) Recapturing.085\"; ", "r4r2/5pk1/1bR1n1p1/Pp1Np2p/1P1pP3/8/5PPP/2R3K1 w - - bm Nxb6; id \"STS(v6.0) Recapturing.086\"; ", "r4rk1/1b1pq1pp/1p6/p1n1pp2/2PBN3/P3P3/1PQ1BPPP/3R1RK1 b - - bm Bxe4; id \"STS(v6.0) Recapturing.087\"; ", "r4rk1/1p1bqpp1/2p4p/3pb2Q/Pn1P1P2/1P2PN2/6PP/R1B2RK1 w - - bm Nxe5; id \"STS(v6.0) Recapturing.088\"; ", "r4rk1/1p5p/2p2p2/2P2Pp1/p5B1/PbR2n2/1P2PPKP/2R5 w - - bm Bxf3; id \"STS(v6.0) Recapturing.089\"; ", "r4rk1/3p1b2/2p2p2/1pP1pPp1/4P1B1/P1R2n2/1P3PKP/2R5 w - - bm Bxf3; id \"STS(v6.0) Recapturing.090\"; ", "r4rk1/pp1qppbp/2p1b1p1/n7/P2PpB1N/2P3P1/2P1QPBP/1R2R1K1 w - - bm Bxe4; id \"STS(v6.0) Recapturing.091\"; ", "r4rk1/ppp2ppp/1nnb4/8/1P1P3q/PBN1B2P/4bPP1/R2QR1K1 w - - bm Qxe2; id \"STS(v6.0) Recapturing.092\"; ", "r4rn1/2q2p1k/p2p2np/4pb1Q/1pP1P3/4B2P/2P3PN/3R1R1K w - - bm Rxf5; id \"STS(v6.0) Recapturing.093\"; ", "r5b1/3rn1pk/p1n4p/1p1p1p1P/3P1B2/P1N3P1/1P1R1P2/3qRBK1 w - - bm Nxd1; id \"STS(v6.0) Recapturing.094\"; ", "r5k1/5pbp/6P1/p2R4/1p5r/P5R1/1PP5/2KB4 b - - bm hxg6; id \"STS(v6.0) Recapturing.095\"; ", "r6r/ppk1npp1/1b2p2p/2pnP2P/4N3/2p2N2/PPPB1PP1/1K1RR3 w - - bm Bxc3; id \"STS(v6.0) Recapturing.096\"; ", "rn1q1rk1/1b2bppp/1p1pp1n1/pN6/P2pP3/2PB1NP1/1P2QP1P/R1B2RK1 w - - bm cxd4; id \"STS(v6.0) Recapturing.097\"; ", "rq1r2k1/pb2npbp/4pnp1/3P2N1/Q6P/1B4N1/PP3PP1/R1BR2K1 b - - bm Bxd5; id \"STS(v6.0) Recapturing.098\"; ", "rqr3k1/3bb1p1/p1n2n1p/3p1p2/1P1NpP2/2N1B2P/1PPQB1P1/1K1R3R b - - bm Bxb4; id \"STS(v6.0) Recapturing.099\"; ", "rqr3k1/4bpp1/pBbp1n1p/Pp2p3/3PPP2/2N1QNP1/1P4P1/R4R1K w - - bm fxe5; id \"STS(v6.0) Recapturing.100\"; ", "1R3b2/r4pk1/2qpn1p1/P1p1p2p/2P1P2P/5PP1/6K1/1Q1BB3 w - - bm Qb6; id \"STS(v7.0) Simplification.001\"; ", "1b4k1/6p1/pr2p2p/5p2/P2N4/P1Q1P1qP/6P1/5RK1 b - - bm Be5; id \"STS(v7.0) Simplification.002\"; ", "1k1r1b1r/ppqbnp2/4p1pp/n2pP3/2pP4/P1P3B1/1P1NBPPP/1R1QRNK1 b - - bm Nf5; id \"STS(v7.0) Simplification.003\"; ", "1nb2rk1/2p1pqb1/1r2p2p/R6P/1PBP2p1/B4NN1/5PK1/3QR3 w - - bm Rf5; id \"STS(v7.0) Simplification.004\"; ", "1nr3k1/1pr2pp1/p3bn2/P3p2p/1PP4P/3BNPP1/3RN1K1/R7 w - - bm Bf5; id \"STS(v7.0) Simplification.005\"; ", "1q1rb1k1/5p1p/p3pnpb/P1r5/2B1P3/1P3P2/2R1N1PP/R2NQ2K b - - bm Bb5; id \"STS(v7.0) Simplification.006\"; ", "1r1r2k1/2p1qpp1/pnP1b2p/1p2P3/3N4/P3P2P/1QB2PP1/3RR1K1 w - - bm Qb4; id \"STS(v7.0) Simplification.007\"; ", "1r2kbQ1/1r1q1p2/p2p2n1/6P1/N1pBP3/P1N5/1PPR4/K7 w - - bm Bg7; id \"STS(v7.0) Simplification.008\"; ", "1r2r1k1/5pp1/R2p3n/3P1P1p/2p2PP1/1qb2B1P/Q7/2BR1K2 b - - bm Bd2; id \"STS(v7.0) Simplification.009\"; ", "1r3qk1/rpb2pp1/2p2n1p/p1P1p3/P1bPP2Q/2N1PN1P/1RB3P1/1R5K w - - bm Bb3; id \"STS(v7.0) Simplification.010\"; ", "1r3rk1/1nqb1p1p/p3p1p1/1ppPb3/2P1N3/1P1Q2PP/P2B2BK/1RR5 w - - bm Bc3; id \"STS(v7.0) Simplification.011\"; ", "1r4k1/3b1pbp/R2Np1p1/4n3/1P1NP3/3BP3/4K1PP/8 w - - bm Nf3; id \"STS(v7.0) Simplification.012\"; ", "1r4k1/4qpp1/r1b4p/pp1pPB2/n1pP3P/2P2N2/R1P3P1/R3Q1K1 b - - bm Bd7; id \"STS(v7.0) Simplification.013\"; ", "1r4k1/5bpp/3b2p1/3PpqPn/1pB5/rP1NBP2/P4Q2/1K1R3R w - - bm Bc5; id \"STS(v7.0) Simplification.014\"; ", "1r4k1/pp1r1p1p/2np1np1/2q5/P3PB2/1QP2B2/5PPP/R2R2K1 w - - bm Qb5; id \"STS(v7.0) Simplification.015\"; ", "1rb1r1k1/2q1bp1p/3p1np1/p1nPp3/4P3/1PRB1N1P/3BQPP1/1R3NK1 w - - bm Be3; id \"STS(v7.0) Simplification.016\"; ", "2b1r1k1/p1B1ppbp/1p4p1/8/4n3/7P/P1RRBPP1/6K1 w - - bm Rd8; id \"STS(v7.0) Simplification.017\"; ", "2bq2k1/4pp2/p2p1n2/n1pP2p1/2P3Pp/P1Q1N2P/3NPPB1/6K1 w - - bm Be4; id \"STS(v7.0) Simplification.018\"; ", "2br3k/2pqb2r/p1n1n3/P3PQ2/1pB5/5N2/1P1N1PPP/R3R1K1 b - - bm Ncd4; id \"STS(v7.0) Simplification.019\"; ", "2q1r1k1/1p2rpp1/p1p3pb/2Pp2n1/3P1R2/1Q2P1P1/PP3NKP/2B2R2 b - - bm Ne4; id \"STS(v7.0) Simplification.020\"; ", "2r1r1k1/1bq1bpp1/pp1pp1n1/2n3P1/2P1PP1p/BPN1QB1P/P1R5/3RN1K1 w - - bm Bh5; id \"STS(v7.0) Simplification.021\"; ", "2r1r1k1/1p1bn1p1/p2Bpb1p/5p1q/P2P4/5NQP/1P2BPP1/R2R2K1 w - - bm Ne5; id \"STS(v7.0) Simplification.022\"; ", "2r1r1k1/pN2b1p1/2p4p/1p1nBp1P/3P2n1/1NP5/PP2KPP1/3R3R b - - bm Bf6; id \"STS(v7.0) Simplification.023\"; ", "2r1r1k1/pb2bp1p/8/2p1Rp1q/P1N5/6P1/2Q2P1P/3R1BK1 w - - bm Bg2; id \"STS(v7.0) Simplification.024\"; ", "2r1rk2/1p2bpp1/p4nbp/4R3/P2R1P1B/2N5/1P2B1PP/5K2 b - - bm Red8; id \"STS(v7.0) Simplification.025\"; ", "2r2n2/6k1/1pNBbb2/3p1p2/3P1P2/P4KP1/2R5/4N3 w - - bm Ne5; id \"STS(v7.0) Simplification.026\"; ", "2r2rk1/p3qp1p/2b1pp1Q/2b5/1n6/2N2NP1/1P2PPBP/R2R2K1 w - - bm Nh4; id \"STS(v7.0) Simplification.027\"; ", "2r3k1/1p1b1pb1/1qn3p1/pN1p2Pp/P3r1nP/1NP3B1/1P2BPR1/R2Q2K1 b - - bm Be5; id \"STS(v7.0) Simplification.028\"; ", "2r3k1/2q1bpp1/p1P2n1p/4pb2/8/2N1BB2/1rPR1QPP/R5K1 w - - bm Nd5; id \"STS(v7.0) Simplification.029\"; ", "2r3k1/p1q1bp1p/2p1p1p1/4P3/1p6/1P1QP1P1/PB3P1P/3R2K1 b - - bm Rd8; id \"STS(v7.0) Simplification.030\"; ", "2r3k1/p2q1pp1/2p4p/1p1p1b1n/2PP4/P1QP1PP1/3B3P/4RBK1 b - - bm Bh3; id \"STS(v7.0) Simplification.031\"; ", "2r5/4k1p1/4p2p/1pnbPp2/3B4/2P5/1PB3PP/5R1K b - - bm Be4; id \"STS(v7.0) Simplification.032\"; ", "2r5/p4k2/4bp2/Ppp3p1/4p3/B1P4r/2P2P1P/R3KNR1 w - - bm Rg3; id \"STS(v7.0) Simplification.033\"; ", "2rn4/2p1rpk1/p3n1p1/P3N1P1/BpN1b2P/1P4K1/2PR4/3R4 w - - bm Rd7; id \"STS(v7.0) Simplification.034\"; ", "2rq1rk1/pb3p1p/1p1p2p1/4p3/1PPnP3/2N2P2/P2QB1PP/2RR2K1 w - - bm Nb5; id \"STS(v7.0) Simplification.035\"; ", "2rr2k1/1p3pp1/p3b2p/3pP2Q/3B4/bqP5/1P1RB1PP/1K3R2 b - - bm Bc5; id \"STS(v7.0) Simplification.036\"; ", "3Q4/2p2k1p/p3b1p1/2p3P1/4Pq2/1P2NP2/P5K1/8 w - - bm Nc4; id \"STS(v7.0) Simplification.037\"; ", "3br1k1/r4pp1/p1qpbn1p/n1p1pN2/P1B1P3/2PPN2P/3BQPP1/R4RK1 w - - bm Bd5; id \"STS(v7.0) Simplification.038\"; ", "3qr3/2n1rpbk/3p2np/R1pP2p1/P3P3/2N2P1P/2QB2P1/1R3B1K w - - bm Nb5; id \"STS(v7.0) Simplification.039\"; ", "3r1r2/1p2k1pp/p2ppn2/4n3/P3P3/1BN4P/1PP2RP1/5R1K b - - bm Nfd7; id \"STS(v7.0) Simplification.040\"; ", "3r1rk1/4q1pp/1pb1p3/p1p1P3/P3pP2/1P2P3/4Q1PP/N2R1RK1 b - - bm Rd3; id \"STS(v7.0) Simplification.041\"; ", "3r2k1/1R3ppp/8/p1p1r3/4n3/4P2P/PP2BP2/1K5R w - - bm Rd1; id \"STS(v7.0) Simplification.042\"; ", "3r2k1/1b3qp1/pp2p1p1/4P1P1/2PN1P1P/b1NR2K1/P3Q3/8 w - - bm Nc2; id \"STS(v7.0) Simplification.043\"; ", "3r2k1/1p3ppp/p3nn2/Qbp4r/2N5/1B3PP1/PP3RKP/8 w - - bm Rd2; id \"STS(v7.0) Simplification.044\"; ", "3r2k1/2rq2pp/4bp2/p1p1pn2/1n2N3/B2PP1P1/1R1Q1P1P/1R3BK1 b - - bm Nd6; id \"STS(v7.0) Simplification.045\"; ", "3r3k/p3bppp/4q3/2p1P1Bb/3p4/P6P/1P1Q1PP1/1B2R1K1 w - - bm Ba2; id \"STS(v7.0) Simplification.046\"; ", "3r4/1R4bk/7p/p3p2p/3rP3/3p3Q/P1qN1PP1/1R4K1 b - - bm Rb4; id \"STS(v7.0) Simplification.047\"; ", "3rr3/1k3p1p/p2P1np1/8/pn6/1bN1NP2/1P1RBKPP/7R w - - bm Bd1; id \"STS(v7.0) Simplification.048\"; ", "4qnk1/1p1rbppb/4p2p/1P2P2P/3N1P2/2Q2BP1/5BK1/2R5 w - - bm Qc8; id \"STS(v7.0) Simplification.049\"; ", "4r1k1/1p1n1p1b/2n2q2/2P4p/1PBp1PpN/6P1/3Q2P1/3R1NK1 w - - bm Bd3; id \"STS(v7.0) Simplification.050\"; ", "4r1k1/2R3p1/p4qnp/8/2P2p2/1Pn2Q2/5BPP/5N1K b - - bm Ne4; id \"STS(v7.0) Simplification.051\"; ", "4r1k1/4bp1p/p4np1/1r1p4/2NP1B1P/P7/1P3PP1/3RR1K1 w - - bm Bg5; id \"STS(v7.0) Simplification.052\"; ", "4r1k1/p2n3p/1qp3p1/3rp3/6P1/2B1R3/PP2Q2P/4R2K b - - bm Qb5; id \"STS(v7.0) Simplification.053\"; ", "4r1k1/p6p/2pB2pb/3pP1q1/8/1P5b/P1P2Q1P/3BR2K b - - bm Qd2; id \"STS(v7.0) Simplification.054\"; ", "4r1k1/pp3p2/2n1r1p1/2q4p/n3p3/4B1Q1/P1PRBPPP/3R2K1 b - - bm Qe5; id \"STS(v7.0) Simplification.055\"; ", "4r3/q4pkp/2p1bnp1/p3p3/PbP1P2P/4NNP1/1RQ2P2/5BK1 b - - bm Ng4; id \"STS(v7.0) Simplification.056\"; ", "4rb1k/3q1p2/2R4p/1P1npN2/4Q3/7P/1P3PP1/6K1 b - - bm Re6; id \"STS(v7.0) Simplification.057\"; ", "4rk2/1b2rqb1/p4p2/1p1P1Pp1/2pRp1N1/P1N1R1PQ/1P4P1/6K1 w - - bm Nh6; id \"STS(v7.0) Simplification.058\"; ", "5k2/1p1n1pp1/p5bp/P2p4/1P1N4/2P2PP1/4QK1P/2q2B2 w - - bm Qe3; id \"STS(v7.0) Simplification.059\"; ", "5k2/3q1pp1/pB6/4b2p/4N3/2N3P1/BP3P1P/6K1 b - - bm Bd4; id \"STS(v7.0) Simplification.060\"; ", "5k2/4rpp1/pp6/4b2p/4N1q1/2NRB1P1/BP3P1P/6K1 b - - bm Rd7; id \"STS(v7.0) Simplification.061\"; ", "5r1k/2q2rpp/2BbRn2/p1pP1P2/1p1p2PB/1P1P1Q1P/6K1/R7 w - - bm Bg3; id \"STS(v7.0) Simplification.062\"; ", "5r2/6pk/5qnp/4p3/2QpP3/P2R1NP1/1r3P1P/2R3K1 b - - bm Nf4; id \"STS(v7.0) Simplification.063\"; ", "5rk1/2p1q2p/1r2p2Q/3nNp2/8/6P1/1P3P1P/2RR2K1 w - - bm Rc6; id \"STS(v7.0) Simplification.064\"; ", "5rk1/p3qp1p/2r1p2Q/2b2p2/1n5N/2N3P1/1P2PP1P/R2R2K1 w - - bm e4; id \"STS(v7.0) Simplification.065\"; ", "5rk1/pb2qppp/1p2p3/7B/2P5/1P4QP/P4PP1/3R2K1 w - - bm Qd6; id \"STS(v7.0) Simplification.066\"; ", "6bk/6p1/1b1q2n1/p6p/3N4/1P1Q4/P4PPP/3R2K1 w - - bm Nf3; id \"STS(v7.0) Simplification.067\"; ", "6k1/p4ppp/4p3/3n4/8/1Pn1PP1P/P3b1P1/R1B3K1 w - - bm Bd2; id \"STS(v7.0) Simplification.068\"; ", "6k1/pp3p1p/1q2r1p1/3p4/6n1/6P1/PPPQ1P1P/3R1BK1 w - - bm Qd4; id \"STS(v7.0) Simplification.069\"; ", "6rk/2R4p/Pp1pq3/1P2pp2/1P2b3/8/6PP/2Q2BK1 w - - bm Qc4; id \"STS(v7.0) Simplification.070\"; ", "7k/4r1p1/1p1b3p/p4q2/8/P5PP/1P1NQPK1/4R3 w - - bm Qf3; id \"STS(v7.0) Simplification.071\"; ", "7r/2q2pkp/2b1n1pN/3pP3/2pP4/4Q2P/2B2PP1/R5K1 w - - bm Bf5; id \"STS(v7.0) Simplification.072\"; ", "7r/p1P2kb1/1nB1b3/6pp/4p3/2P3BP/PP3PP1/R5K1 b - - bm Nd5; id \"STS(v7.0) Simplification.073\"; ", "8/p4k2/1p3r2/2pPnb2/2Pr3P/PPK3P1/4B1Q1/8 b - - bm Bg4; id \"STS(v7.0) Simplification.074\"; ", "b5k1/q4p1p/3p1bp1/4p3/1NB1P3/3P3P/1Q3PP1/6K1 w - - bm Bd5; id \"STS(v7.0) Simplification.075\"; ", "b7/3r2bk/p2Np1p1/2B1p1Pp/qP2P2P/P2Q1RK1/8/8 w - - bm Rf7; id \"STS(v7.0) Simplification.076\"; ", "q1rr2k1/pbnn1pbp/1p4p1/4p1P1/2P1P3/1NN2B2/PP2QB1P/3RR1K1 w - - bm Bg4; id \"STS(v7.0) Simplification.077\"; ", "r1b2kr1/pp1nbp2/4pn1p/2q3p1/B1P5/P1B2N1P/1P2QPP1/3R1KNR w - - bm Ne5; id \"STS(v7.0) Simplification.078\"; ", "r1b2rk1/1p2Bpbp/1np3p1/4P3/pn6/1N3N1P/1P1RBPP1/R5K1 b - - bm Nc2; id \"STS(v7.0) Simplification.079\"; ", "r1b2rk1/p1qn1ppp/2pb1n2/8/Np1NP3/7P/PPQ1BPP1/R1B2RK1 w - - bm Nf5; id \"STS(v7.0) Simplification.080\"; ", "r1b2rk1/pp1p1ppp/4pn2/6q1/2PP4/P1N5/1PQ2PPP/R3KB1R w KQ - bm Qd2; id \"STS(v7.0) Simplification.081\"; ", "r1bR4/1p3pkp/1np3p1/4P3/pB6/1n3N1P/1P2BPP1/6K1 b - - bm Be6; id \"STS(v7.0) Simplification.082\"; ", "r1bq1rk1/pp2ppbp/1n1n2p1/2p5/5B2/1PN2NP1/P1Q1PPBP/3R1RK1 w - - bm Nb5; id \"STS(v7.0) Simplification.083\"; ", "r1bq2k1/ppbnr1p1/2p1ppBp/2Pp4/P2P3N/2Q5/1P1B1PPP/4RRK1 b - - bm Nf8; id \"STS(v7.0) Simplification.084\"; ", "r1q2r1k/1bBnbpp1/4p2p/pN1p3P/8/5BQ1/PPP2PP1/1K1R3R b - - bm Bc6; id \"STS(v7.0) Simplification.085\"; ", "r2q1r1k/ppp3bp/3pb3/6p1/2Pn4/1PN3BP/P2QBPP1/3RR1K1 w - - bm Bg4; id \"STS(v7.0) Simplification.086\"; ", "r2q1rk1/1p3pp1/6p1/3Bn3/Ppp1P1P1/1n3P2/1P5P/1RBQ1RK1 w - - bm Bf4; id \"STS(v7.0) Simplification.087\"; ", "r2r2k1/1q3pbp/p3b3/2pPN3/2p1RB2/7P/PP3PP1/R2Q2K1 w - - bm Nc6; id \"STS(v7.0) Simplification.088\"; ", "r2r2k1/p2bppbp/1p4p1/2pP4/2q1P3/2P1B3/P1QN1PPP/2R2RK1 b - - bm Qa4; id \"STS(v7.0) Simplification.089\"; ", "r2r4/5kpp/p2q1n2/1p4B1/3b4/2Np1Q2/PP3PPP/3R1RK1 w - - bm Ne4; id \"STS(v7.0) Simplification.090\"; ", "r3k1r1/2q1bp2/2p1p1np/p1Pp2P1/Pp1Pn2P/1P2P3/1B2NNP1/R3QRK1 b q - bm Ng3; id \"STS(v7.0) Simplification.091\"; ", "r3kb1r/3n1ppp/p3p3/3bP3/Pp6/4B3/1P1NNPPP/R4RK1 w kq - bm Nf4; id \"STS(v7.0) Simplification.092\"; ", "r3nbk1/1b3ppp/pr6/1pp1PBq1/P1P5/7P/1B1NQPP1/R3R1K1 w - - bm Be4; id \"STS(v7.0) Simplification.093\"; ", "r3r1k1/2q2np1/3p1pNp/p2P4/R4PP1/2P3K1/1P1Q4/7R w - - bm Re1; id \"STS(v7.0) Simplification.094\"; ", "r3r1k1/5pp1/p1n2q1p/P1bp1P2/2p2P2/5N2/2BB3P/R2QRK2 b - - bm Nd4; id \"STS(v7.0) Simplification.095\"; ", "r4bk1/1p1nqp1p/p1p1b1p1/P3p3/2P1P3/1QN2PP1/1P5P/R4BBK b - - bm Qb4; id \"STS(v7.0) Simplification.096\"; ", "r4rk1/2pqbppp/p1n5/1p1pP3/3P4/1B2Bb1P/PP1Q1PP1/R1R3K1 w - - bm Qc3; id \"STS(v7.0) Simplification.097\"; ", "r7/4pp1k/6pb/p1rPp2p/2P1P2q/7P/1RQ1BPP1/1R4K1 w - - bm Rb5; id \"STS(v7.0) Simplification.098\"; ", "rn3rk1/pp1q2bp/3ppnp1/2p3N1/2P1P3/8/PP2NPPP/R1BQ1RK1 w - - bm e5; id \"STS(v7.0) Simplification.099\"; ", "rq4k1/1b1nbpp1/4p2p/r1PnP3/Np6/1P1B1N2/1Q1B1PPP/R2R2K1 b - - bm Bc6; id \"STS(v7.0) Simplification.100\"; ", "1qr2k1r/pb3pp1/1b2p2p/3nP3/1p6/3B2QN/PP3PPP/R1BR2K1 b - - bm g5; id \"STS(v8.0) AKPC.001\"; ", "1r1qr1k1/p5b1/2p2ppp/3p4/1Pp5/P1Nn1Q2/3BN1PP/R4RK1 b - - bm f5; id \"STS(v8.0) AKPC.002\"; ", "1r1rq1k1/1p4p1/pNb1pp1p/1pP5/3P4/1PQ5/5PPP/R3R1K1 w - - bm f3; id \"STS(v8.0) AKPC.003\"; ", "1r3r2/1p3q1k/1Q1p4/2pNbpp1/2P5/7P/PP2R1P1/3R3K b - - bm f4; id \"STS(v8.0) AKPC.004\"; ", "1r4k1/2p2p1p/4n1p1/2qpP3/2nN4/1BPQ4/Pr3PPP/3RR1K1 w - - bm h4; id \"STS(v8.0) AKPC.005\"; ", "1r4k1/3q2pp/2np4/2p1pp2/2P1P3/R1BP1Q1P/5PP1/6K1 b - - bm f4; id \"STS(v8.0) AKPC.006\"; ", "1r4k1/ppq2ppp/r4nn1/P2p4/2pP4/B1P1P1PP/1Q1N1PK1/RR6 b - - bm h5; id \"STS(v8.0) AKPC.007\"; ", "1rb1r1k1/4qpb1/p1np1npp/1pp5/2P1PN2/1P3PP1/PB4BP/1QRR1N1K b - - bm g5; id \"STS(v8.0) AKPC.008\"; ", "1rq1n3/2pr3k/2n1bppp/p1PNp3/Pp2P3/1P2Q1PP/3R1PBK/3RN3 w - - bm f4; id \"STS(v8.0) AKPC.009\"; ", "2b2kn1/5ppQ/1r5p/p2pN3/1qpP1PP1/5P2/P6P/1B4RK w - - bm g5; id \"STS(v8.0) AKPC.010\"; ", "2qn4/p5k1/1p1Qbpp1/1B1p3p/3P3P/2P5/P4PP1/4N1K1 w - - bm g3; id \"STS(v8.0) AKPC.011\"; ", "2r1r1k1/p2b2bp/1pn1p1pq/4Pp2/2BP4/P4N2/1P2NQPP/2R2RK1 w - - bm h4; id \"STS(v8.0) AKPC.012\"; ", "2r2bk1/p1q2p2/4p1p1/3nP1B1/1p2Q2P/1P3N2/Pr3PP1/R4RK1 w - - bm h5; id \"STS(v8.0) AKPC.013\"; ", "2r2k1r/1q1nbpp1/p2p4/1p1Pp3/8/P1B2BP1/1PP1QP2/3RR1K1 b - - bm g6; id \"STS(v8.0) AKPC.014\"; ", "2r2rk1/1b1qb1pp/p2p4/1p1PpP2/4Q3/1P2BN1P/P4PP1/R3R1K1 w - - bm g4; id \"STS(v8.0) AKPC.015\"; ", "2r2rk1/2qbnppp/1p2p3/p2pPn2/P5QP/2PB1N2/2PB1PP1/1R2R1K1 w - - bm h5; id \"STS(v8.0) AKPC.016\"; ", "2r2rk1/p3ppbp/1pnp1np1/4q3/2P1P3/PPN1B2P/2Q1BPP1/R2R2K1 w - - bm f4; id \"STS(v8.0) AKPC.017\"; ", "2r2rk1/p4qb1/1p1ppp2/2n1n1pp/2PN4/1PN2PPP/P1QBP1K1/2RR4 w - - bm f4; id \"STS(v8.0) AKPC.018\"; ", "2r2rk1/pq2ppbp/1pnp1np1/8/2P1P3/2N1BP2/PPQ1BPKP/3R1R2 w - - bm f4; id \"STS(v8.0) AKPC.019\"; ", "2r3k1/p3np1p/1p1pp1p1/4b3/1PP5/P3B2P/2R2PP1/3R3K w - - bm g4; id \"STS(v8.0) AKPC.020\"; ", "2r3k1/pp2p1b1/q2pP1p1/3pnrPp/P7/1P5P/2PB1PB1/2RQR1K1 w - - bm f4; id \"STS(v8.0) AKPC.021\"; ", "2r5/1bq3kp/4pNp1/3pP1Q1/2pP4/7P/5PP1/R5K1 w - - bm h4; id \"STS(v8.0) AKPC.022\"; ", "2r5/5k2/p1n3p1/P1Pqpp1p/3n4/4BPQN/6PP/3R3K w - - bm f4; id \"STS(v8.0) AKPC.023\"; ", "2rr1bk1/p4pp1/1p2q2p/1PpN4/2Q1P3/P4PP1/3R3P/3R2K1 w - - bm f4; id \"STS(v8.0) AKPC.024\"; ", "2rr2k1/5ppp/p3p3/1p2P3/1Pn5/2B1PqP1/P1Q2P1P/3RR1K1 b - - bm h5; id \"STS(v8.0) AKPC.025\"; ", "3bn1k1/1bq1npp1/3p3p/1p1Pp3/1Pp1P3/2P1B1NP/2BN1PPK/Q7 b - - bm f5; id \"STS(v8.0) AKPC.026\"; ", "3q1r2/2r2ppk/5b1p/2pBpR2/P3P2P/1P4PK/5Q2/5R2 w - - bm g4; id \"STS(v8.0) AKPC.027\"; ", "3q2k1/rb1r3p/6p1/pQRp1p2/3N3P/1P2PBP1/P4P2/6K1 w - - bm h5; id \"STS(v8.0) AKPC.028\"; ", "3q3k/3r1p1p/4p3/p4p2/PbP2Q1P/1p3N2/1P2KPP1/1R6 b - - bm f6; id \"STS(v8.0) AKPC.029\"; ", "3q3k/5p1p/3rp3/p4p1P/PbP2Q2/1p3N2/1P2KPP1/1R6 b - - bm f6; id \"STS(v8.0) AKPC.030\"; ", "3qr1k1/5r1p/p1p1n1p1/Ppp1Q3/4Pp2/1P1P1N1P/2P2RPK/R7 b - - bm g5; id \"STS(v8.0) AKPC.031\"; ", "3qrr1k/pp1n1ppp/2n3b1/2p5/3Pp1P1/P1P1P2P/BB2QPN1/2RR2K1 b - - bm f5; id \"STS(v8.0) AKPC.032\"; ", "3r1r1k/1p2q2b/p1p1p2p/2P1P3/PPQP1pPP/5B2/7K/4BR2 b - - bm h5; id \"STS(v8.0) AKPC.033\"; ", "3r1rk1/pb1pb2p/1p2p1q1/1N2npp1/B1P5/4PPN1/PP2Q1PP/3R1RK1 b - - bm h5; id \"STS(v8.0) AKPC.034\"; ", "3r1rk1/pb2bppp/1pp1p3/2n1P2Q/2P1P3/1Pp2NP1/P4PBP/2R3K1 b - - bm g6; id \"STS(v8.0) AKPC.035\"; ", "3r2k1/2q1rp2/4p1p1/2p1P2p/1p3Q2/1P1bP1PP/P4RBK/2R5 w - - bm g4; id \"STS(v8.0) AKPC.036\"; ", "3r2k1/3n1p1p/p1r3p1/q1P3Q1/P5B1/2R1B2P/5PP1/6K1 b - - bm h6; id \"STS(v8.0) AKPC.037\"; ", "3r2k1/p1r3pp/Q1p1pb2/2Pn1p1q/PpBPp3/1P2P1PP/1B2RPK1/4R3 b - - bm g5; id \"STS(v8.0) AKPC.038\"; ", "3r2r1/2qb2pk/1p2p2p/p1n1Pp2/P1PNpP2/1P2Q3/3RB1PP/3R2K1 b - - bm g5; id \"STS(v8.0) AKPC.039\"; ", "3r4/1pq3kp/2pp1pp1/p1n1r3/P1PNP3/1P2BPPB/2Q1N1K1/8 w - - bm f4; id \"STS(v8.0) AKPC.040\"; ", "3rb1k1/2q1b1p1/pp2p3/5p1p/1PP1p3/P3Q1PP/4P1RK/3N1R2 b - - bm h4; id \"STS(v8.0) AKPC.041\"; ", "3rb1k1/2q1b1pp/pp2p3/5p2/1PP1p3/P3Q1PP/4PR1K/3N1R2 b - - bm h5; id \"STS(v8.0) AKPC.042\"; ", "3rrk2/p2n1p1B/1pq4p/2ppB1p1/2bP4/PnN1PP2/1PQ3PP/3RR1K1 w - - bm f4; id \"STS(v8.0) AKPC.043\"; ", "4b1k1/pp1qbpp1/4p3/4Q2p/8/2P1N1P1/PP3PBP/6K1 b - - bm h4; id \"STS(v8.0) AKPC.044\"; ", "4qr1k/p4n1p/1p1p2p1/2pQ1n2/2P1NB2/P1P2P2/5KPP/3R4 w - - bm g4; id \"STS(v8.0) AKPC.045\"; ", "4r1k1/p4bpp/1p6/1P6/2PQ4/4rPq1/PR2PRB1/6K1 b - - bm h5; id \"STS(v8.0) AKPC.046\"; ", "4r2k/6p1/2b2p2/pq5p/r2P2P1/2P1N1PP/B2Q1K2/4R3 b - - bm h4; id \"STS(v8.0) AKPC.047\"; ", "4r3/2pq1pk1/1r4p1/p2pP1Np/1pnP4/2PQ3P/P3RPP1/4R1K1 w - - bm f4; id \"STS(v8.0) AKPC.048\"; ", "4r3/p4rk1/1p4p1/3Rp1q1/4P2p/2Q2P2/P5PP/3R1K2 w - - bm h3; id \"STS(v8.0) AKPC.049\"; ", "4rr1k/1pq3bp/p1b2np1/P1Ppp3/1P1N2P1/1B3PB1/Q6P/3RR1K1 b - - bm h5; id \"STS(v8.0) AKPC.050\"; ", "4rr1k/2q5/1pnn1b1p/p1p2b2/N1PppPp1/PP1P4/2QBPPBP/3R1RNK b - - bm h5; id \"STS(v8.0) AKPC.051\"; ", "5k1r/1b2rp2/pR4pp/1p3n2/2p5/2N5/PP3PPP/3Q2K1 w - - bm f3; id \"STS(v8.0) AKPC.052\"; ", "5k2/q7/2p2pp1/4p1pn/1pP1PnN1/1P2RPPP/3r1B1K/5Q2 b - - bm f5; id \"STS(v8.0) AKPC.053\"; ", "5r1k/2qb1ppp/6nb/3B4/pp1BQ3/5NP1/P1P2P1P/4R1K1 w - - bm h4; id \"STS(v8.0) AKPC.054\"; ", "5r1k/2qb1ppp/6nb/3B4/ppPBQ3/5NP1/P4P1P/4R1K1 w - - bm h4; id \"STS(v8.0) AKPC.055\"; ", "5r2/3br1kp/3q2p1/1pNp4/1P1Pnp2/5B2/2P1R1PP/2Q2RK1 b - - bm g5; id \"STS(v8.0) AKPC.056\"; ", "5rk1/2r1bpp1/p2p1n1p/1p2pP2/1P3q2/1BP4P/PB3PP1/R2QR1K1 w - - bm g4; id \"STS(v8.0) AKPC.057\"; ", "5rk1/5p2/pnB1p1pp/q3P3/2nP1P2/1R1N3P/6PK/2Q5 w - - bm f5; id \"STS(v8.0) AKPC.058\"; ", "5rk1/p1r3pp/1p2q3/1P1p4/1QnB2P1/2R1PP1P/4R1K1/8 b - - bm h5; id \"STS(v8.0) AKPC.059\"; ", "6k1/2qn3p/pp1pprpB/5r2/1PP5/P4PbP/3QB1P1/2R2RK1 w - - bm f4; id \"STS(v8.0) AKPC.060\"; ", "6k1/5p1p/1p1n2pP/1n6/q1rP4/2pQP3/r1N2PP1/BRR3K1 b - - bm f5; id \"STS(v8.0) AKPC.061\"; ", "6k1/p2nqpp1/Qp2p2p/8/3PNb2/P4N2/1Pr2PPP/4R1K1 w - - bm g3; id \"STS(v8.0) AKPC.062\"; ", "8/3qpk2/1p3r1p/p1pPRp2/2P2P2/P4KPP/4Q3/8 w - - bm g4; id \"STS(v8.0) AKPC.063\"; ", "8/3r1pk1/p1pqp1p1/8/P1Pb4/5RPP/2Q5/4R2K b - - bm f5; id \"STS(v8.0) AKPC.064\"; ", "8/6pk/p5rp/2NpPq2/2bPp2b/P3P1P1/6Q1/1R2B1K1 b - - bm h5; id \"STS(v8.0) AKPC.065\"; ", "b4rk1/2pq1pp1/3p3p/B1p5/2PpP1n1/3P2P1/PR1Q1PBP/6K1 b - - bm f5; id \"STS(v8.0) AKPC.066\"; ", "b4rk1/p2q1pp1/3p3p/B1p5/2PpP1n1/P2P2P1/1R1Q1PBP/6K1 b - - bm f5; id \"STS(v8.0) AKPC.067\"; ", "b5k1/6bp/3qp1p1/3n2P1/5P1K/2rBB3/6NP/3R1Q2 b - - bm h6; id \"STS(v8.0) AKPC.068\"; ", "n1rr2k1/3qppbp/p1nP2p1/6P1/5P1Q/1P2B3/P4PBP/2RR2K1 w - - bm f5; id \"STS(v8.0) AKPC.069\"; ", "nr4k1/1p1qrpb1/p6p/P2p2p1/1P1N4/4B1P1/2RQ1PKP/2R5 w - - bm h4; id \"STS(v8.0) AKPC.070\"; ", "q5k1/4bpp1/r3p2p/2p5/1pP5/1P3NPP/1RQ1PP2/6K1 b - - bm f5; id \"STS(v8.0) AKPC.071\"; ", "r1b1r1k1/1p1nqpbp/2ppn1p1/1P6/2P1P3/2N1BPP1/2Q1N1BP/1R1R2K1 w - - bm f4; id \"STS(v8.0) AKPC.072\"; ", "r1b2rk1/1pp1q1b1/2nppn1p/pN3pp1/2PP4/1Q4PN/PP2PPBP/R1BR2K1 w - - bm f4; id \"STS(v8.0) AKPC.073\"; ", "r1b2rk1/p4pp1/1q2p2p/1p1n4/3b2N1/3B2Q1/PPP2PPP/R1B2RK1 b - - bm f5; id \"STS(v8.0) AKPC.074\"; ", "r1b2rk1/pp2qppp/n2p4/2pPp3/P1N1P3/3P4/1P2BPPP/R1Q2RK1 w - - bm f4; id \"STS(v8.0) AKPC.075\"; ", "r1b2rk1/pp3pp1/1q2p2p/3n4/3b2N1/3B2Q1/PPP2PPP/R1B2RK1 b - - bm h5; id \"STS(v8.0) AKPC.076\"; ", "r1b4r/pp3k1p/3R1p2/B1p5/2P4Q/5q2/PP2NP1P/2K5 b - - bm h5; id \"STS(v8.0) AKPC.077\"; ", "r1q1k2r/3n3p/p5p1/1pbpP3/2p2P2/8/PPB1QP1P/R1B2RK1 w kq - bm f5; id \"STS(v8.0) AKPC.078\"; ", "r1q2rk1/p3npb1/1pnBb1pp/2p1P3/8/2N2NPP/PP2QPB1/R4RK1 w - - bm g4; id \"STS(v8.0) AKPC.079\"; ", "r1r3k1/1bqn1pbp/pp1pp1p1/4n3/2P2N1P/1PN1PP2/P2BB1P1/2R1QRK1 w - - bm h5; id \"STS(v8.0) AKPC.080\"; ", "r2q1rk1/1p2n1bp/p1npp1p1/2p1p3/4P3/N1PPB1PP/1P3PB1/R2QR1K1 w - - bm h4; id \"STS(v8.0) AKPC.081\"; ", "r2q1rk1/1p2p3/2p2ppp/p2n1b2/P2P4/1B3N1P/1PP2PP1/R2QR1K1 b - - bm g5; id \"STS(v8.0) AKPC.082\"; ", "r2r2k1/1p3p1p/1qbppQp1/p7/4P3/P1NR4/1P3PPP/1R4K1 w - - bm h4; id \"STS(v8.0) AKPC.083\"; ", "r2r2k1/2pq1pp1/p1p2b1p/5p2/P2P4/2N3P1/1PQ1PP1P/R2R2K1 b - - bm f4; id \"STS(v8.0) AKPC.084\"; ", "r2r2k1/pp1qppbp/2p1bnp1/4B3/3P2P1/4QN1P/PP2PPB1/R1R3K1 b - - bm h5; id \"STS(v8.0) AKPC.085\"; ", "r3k3/1p1n1p2/2p1r1p1/3bP3/3P3Q/8/p1B2PPP/R5K1 w q - bm f4; id \"STS(v8.0) AKPC.086\"; ", "r3qrk1/1p4bp/1n1p1p1n/pPpPp1p1/P3P3/1P1N3P/3B1PP1/2QBRRK1 w - - bm h4; id \"STS(v8.0) AKPC.087\"; ", "r3qrk1/2p3pp/np6/3pN3/p2P4/P5P1/1PQ1PP2/3R1RK1 w - - bm f4; id \"STS(v8.0) AKPC.088\"; ", "r3r1k1/1pq4p/p2b1pp1/3pnb2/3N3Q/2P5/PP1B1PPP/R3RBK1 b - - bm g5; id \"STS(v8.0) AKPC.089\"; ", "r3r1k1/4qpp1/2p5/p1ppP2p/6P1/1PQ2P1P/P1P5/3RR1K1 w - - bm f4; id \"STS(v8.0) AKPC.090\"; ", "r3r1k1/pn1bnpp1/1p2p2p/1q1pP3/2pP1P1N/B1P2BP1/2P3QP/R4RK1 w - - bm f5; id \"STS(v8.0) AKPC.091\"; ", "r3rb2/1p3k2/p1p2p2/2q2bpp/2PR4/1PQ4P/P4PPB/3R1BK1 b - - bm h4; id \"STS(v8.0) AKPC.092\"; ", "r4k2/1b1qbp2/2pnp1r1/pp2B2p/2pPP1pP/P1N2P2/1P2B1PQ/2RR2K1 b - - bm f5; id \"STS(v8.0) AKPC.093\"; ", "r4rk1/1b3pbp/1qp1p1p1/p2n2B1/3P3P/P2B1N2/1PQ2PP1/R3R1K1 w - - bm h5; id \"STS(v8.0) AKPC.094\"; ", "r4rk1/1p1qn1bp/p1npp1p1/2p1p3/4P2P/N1PPB1P1/1P3PB1/R2QR1K1 w - - bm h5; id \"STS(v8.0) AKPC.095\"; ", "r4rk1/1p2qppp/1np5/p2pNb2/P2Pn3/2NBP3/1PQ2PPP/2R2RK1 b - - bm f6; id \"STS(v8.0) AKPC.096\"; ", "r4rk1/1pq1p3/2p2ppp/p2n1b2/P2P4/1B3N1P/1PP2PP1/R2QR1K1 b - - bm g5; id \"STS(v8.0) AKPC.097\"; ", "r4rk1/3n1pbp/3Pq1p1/4p3/p7/2Q1B1N1/P2R1PPP/2R3K1 b - - bm f5; id \"STS(v8.0) AKPC.098\"; ", "r4rk1/ppp1qp1p/2p3p1/4n3/4P2P/2N1QP2/PPP2P2/2KR3R w - - bm h5; id \"STS(v8.0) AKPC.099\"; ", "r5k1/p2b1r2/1p1p1q2/2pPbp1p/P1P1p3/1P4P1/2RQNPBP/4R1K1 b - - bm h4; id \"STS(v8.0) AKPC.100\"; ", "1b2r1k1/1bqn1pp1/p1p4p/Pp2p3/1P2B3/2B1PN1P/5PP1/1Q1R2K1 b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.001\"; ", "1k4nr/pppr1q2/3p2p1/3Nn1Qp/2P1PN2/1P6/P1P3PR/2KR4 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.002\"; ", "1n4k1/2pb2q1/1p1p3p/3P1p2/1PP1pP2/3rN3/4N1PP/1RQ3K1 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.003\"; ", "1nr5/p2p1qpk/1pb1p2p/5p1P/1PP2N2/P3PPQ1/6P1/3RKB2 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.004\"; ", "1q2r1k1/1b2bpp1/p2ppn1p/2p5/P3PP1B/2PB1RP1/2P1Q2P/2KR4 b - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.005\"; ", "1q2rb2/3b1r1k/p1p4p/B3p1p1/1PPpN3/3P1P1P/3QR1P1/4R1K1 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.006\"; ", "1r1qr1k1/1p3pp1/p1n1bn1p/2NN4/4P3/6P1/PP3PB1/2RQR1K1 w - - bm b3; id \"STS(v9.0) Advancement of a/b/c pawns.007\"; ", "1r1r1bk1/1bq2ppp/pnp1p3/4P3/5P2/P1NBB3/1P4PP/R1QR2K1 b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.008\"; ", "1r1r2k1/p1p1p3/4n1p1/5p2/8/R3B3/Pn2BPPP/4K2R b K - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.009\"; ", "1r1r2k1/pb3ppp/1p1ppn2/8/PPPP1P2/2NB2P1/3K3P/RR6 w - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.010\"; ", "1r1r3k/p4pp1/3nb2p/2p1q3/8/P1PBPP2/2Q3PP/R1B2RK1 b - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.011\"; ", "1r2r1k1/2qp1ppp/2p1pn2/8/8/2PBQ1P1/PP3P1P/R4RK1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.012\"; ", "1r2r1k1/2qp1ppp/2p2n2/4p3/1P6/2PBQ1P1/P4P1P/R4RK1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.013\"; ", "1r2rn2/1p3pk1/p4npp/P2p4/1P1N2P1/1P3P1P/4BK2/2R1R3 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.014\"; ", "1r3rk1/2qnbppp/2p1p3/pbPpP3/8/PP2QNP1/3B1PBP/2R1R1K1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.015\"; ", "1r4k1/p3pr1p/1qbp1pp1/2p5/2P5/1PN1Q2P/P2R1PP1/3R3K b - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.016\"; ", "1rbr2k1/p1q2p1p/2n1p1p1/1p4Q1/2pPP3/2P3P1/P1B1N1PP/2R2RK1 b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.017\"; ", "1rq2r2/Q3pp1k/1B1p2pb/4n2p/2PN4/PP5P/3nBPP1/R2R2K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.018\"; ", "1rr3k1/p2qbpnp/Ppp3p1/3pP3/3P4/1Q1B1P2/1P1B1P1P/2R3RK b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.019\"; ", "1rrqb3/1p3kp1/p3p2p/P1Rp1p1P/3P1P2/6P1/1P1QBPK1/2R5 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.020\"; ", "2r1n1k1/1p1R1r1p/2q1pp1B/p5p1/6P1/Q1P4P/PP6/1K1R4 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.021\"; ", "2r1r1k1/1b1n1pp1/1q2p2p/3p3n/P2N4/BP1NPP1P/2B3P1/2R1Q1K1 w - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.022\"; ", "2r1r1k1/1b1q1pp1/4p2p/1Nnp3n/1N6/PP2PP1P/1BB2QP1/3R2K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.023\"; ", "2r1r1k1/1p1npp1p/p2pb1p1/q7/2PnP3/1PNN2PP/P2Q1PB1/2RR2K1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.024\"; ", "2r2bk1/R4pp1/3pqnnp/R3p3/1P2P3/3Q1NNP/2rB1PPK/8 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.025\"; ", "2r2r1k/1bqnbppp/p3p3/1p1pP3/3B1P2/P1NB4/1PP2QPP/R4R1K w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.026\"; ", "2r2rk1/1p2bpp1/p3p3/4P1N1/PqB3P1/1Pn2NQ1/2b2P1P/R3R1K1 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.027\"; ", "2r2rk1/pp3ppp/2nb1q2/P3pb2/2P5/1NB3P1/1Q2PPBP/R4RK1 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.028\"; ", "2r3k1/1pq1b1p1/p1b1pr1p/8/PP2pP2/2P1B3/4B1PP/RQR4K w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.029\"; ", "2r3k1/5p2/pr3p1p/8/3N2q1/P3P1P1/1P1Q1PP1/5RK1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.030\"; ", "2r5/1r2nkp1/p3p2p/1p1b1p1P/8/PP3P2/1KPRB1P1/R5B1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.031\"; ", "2r5/3npk1p/1p1p1r2/pP1P2p1/3N1pP1/1P3P2/2P1R2P/R5K1 w - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.032\"; ", "2rqr1k1/5ppp/n4b2/pbP5/3p4/P4NP1/1N2PPBP/2RQR1K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.033\"; ", "2rr2k1/1b3ppp/pp2pn2/2n5/2BNP3/4KPNP/PP4P1/2R4R w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.034\"; ", "2rr2k1/1q3pp1/pp1pp2p/4n3/2PNP3/1P4P1/P1R1QPKP/3R4 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.035\"; ", "3b2rk/5pp1/2q2n1p/1p2BP2/7P/2NQ4/1PP5/1K4R1 b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.036\"; ", "3q2k1/1p1bprbp/3pn1p1/3N4/1PP5/r3B3/3QBPPP/2R1R1K1 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.037\"; ", "3r1nk1/pp2q2p/2p3p1/2NnPp2/5P2/1Q6/PP1rNRPP/2R3K1 b - - bm b6; id \"STS(v9.0) Advancement of a/b/c pawns.038\"; ", "3r1rk1/2pq2pp/p4pb1/1p6/4P1P1/2PnQN1P/PP1N1P2/RR4K1 b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.039\"; ", "3r1rk1/3n1pbp/1p1p2p1/pN1Ppn1q/2P3b1/PPN3P1/1B1Q1P1P/R3R1KB w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.040\"; ", "3r2k1/1p2bpp1/pNrppnqp/8/1PPBP3/P4Q1P/5PP1/3RR1K1 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.041\"; ", "3r2k1/5p2/p1prb2p/1p2q3/4Pp2/1P1B1P2/P3Q1PP/2RR1K2 b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.042\"; ", "3rn1k1/1pqn1pp1/p3br1p/2P1p3/P3P3/3BBN1P/2P3PQ/RR5K w - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.043\"; ", "3rr1k1/1bqn1ppp/p1p5/1p2b3/4P3/P3BB1P/1PQ1NPP1/1R1R3K b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.044\"; ", "3rr1k1/1p5p/p2B1bp1/5b2/1RP2P2/1PN5/1P1R1K1P/8 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.045\"; ", "3rr1k1/1q2bp1p/2b3pB/3npP2/1p6/5B2/NPP3PP/3RQR1K w - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.046\"; ", "4b3/ppr1bpk1/2n1p1p1/1B1rP2p/3P3P/P1R1BNP1/5PK1/2R5 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.047\"; ", "4q1k1/p5p1/1rp1p1p1/2R1Pp2/1PQ5/P5P1/5PK1/8 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.048\"; ", "4r1k1/3bpr1p/1qpp1ppQ/p7/2P1N3/1P5P/P2R1PP1/3R3K b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.049\"; ", "4r1k1/p2bpr1p/1qpp2pQ/5p2/2P4P/1P4N1/P2R1PP1/3R3K b - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.050\"; ", "4rk2/2p1rpp1/pbn3bp/1p2P3/PP6/1BP2NNP/5PP1/2R1R1K1 w - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.051\"; ", "4rrk1/1q4pp/4p3/2bp4/5P2/3Q4/1PP3PP/R1B2R1K w - - bm c3; id \"STS(v9.0) Advancement of a/b/c pawns.052\"; ", "4rrk1/p2n2pp/qp2pb2/2p5/2P2P2/P1BP1Q2/4N1PP/1R3RK1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.053\"; ", "5nk1/1bq2pp1/rp1r1b1p/1R1p1B2/2pP4/2N1PN2/PQ3PPP/1R4K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.054\"; ", "5r1k/1p2q1pp/p1p2n2/5r2/3Pp3/4P2R/PPB2PQP/6RK b - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.055\"; ", "5r2/5r1k/p2pN1p1/1p1Pn2p/2q1P2b/P4pR1/1PPQ1B1P/1K2R3 w - - bm b3; id \"STS(v9.0) Advancement of a/b/c pawns.056\"; ", "5rk1/1prn1qp1/p2p3p/3Ppp1P/7R/1NPQ1PP1/PP6/1K1R4 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.057\"; ", "5rk1/2nq2pp/3p1p2/3P4/3B1Q2/PrN3P1/4PP1P/R5K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.058\"; ", "5rk1/p2qp2p/1p1p1pp1/2nN1P1r/P1P1P3/1P2R1P1/1Q4KP/4R3 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.059\"; ", "6k1/1b2bp1p/p3p3/1p2P1p1/2r5/2N1BP2/PP4PP/3R2K1 b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.060\"; ", "6k1/1p1n1rbp/r2n1pp1/4p3/4P3/4B1P1/PP2N1BP/R4RK1 w - - bm b3; id \"STS(v9.0) Advancement of a/b/c pawns.061\"; ", "6k1/1pr2rp1/p4q1p/2p1p3/2Q1P3/P6P/2P2PP1/3R1RK1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.062\"; ", "6k1/2rqbrpp/2n2n2/pBpppN2/Pp4P1/2PP3P/1P2QP2/R1B1R1K1 w - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.063\"; ", "6k1/4brpp/3p4/p1q1p3/1pN1Pr2/1P1R1P2/P1P3QP/1K4R1 b - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.064\"; ", "6k1/5r2/p4pp1/Br1pp3/2b5/6NP/1P3RP1/2R4K w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.065\"; ", "8/5p2/bk2p3/1pr1Pp2/3p4/bP3BPP/P1nBN3/1R1K4 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.066\"; ", "8/r2b2pk/2p1pr1p/3nQp2/p7/qP3N2/P1PRBPPP/4R1K1 w - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.067\"; ", "b2qr2k/2p3pp/8/1p3p2/2p2B2/2Pp1PPP/5QB1/R6K b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.068\"; ", "r1b2k2/ppq2p1p/1np3p1/4p3/2n5/3N2P1/P1Q1PPBP/R2R2K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.069\"; ", "r1bq1r2/1pp3bk/3p1npp/1N1Ppn2/PBP1N3/1Q1B4/5PPP/R4RK1 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.070\"; ", "r1r3k1/3nqpp1/1pp1pn1p/p2pN3/2PP1Q2/1P4P1/P2BPPKP/2RR4 b - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.071\"; ", "r1r3k1/p3pp1p/3p2pb/R1nP4/4PP2/2Pp4/1P1N2PP/2BR2K1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.072\"; ", "r1r4k/1q1nbpp1/2bp1n1p/pp2pP2/4P3/P1N2BQ1/1PPN1BPP/2R1R1K1 b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.073\"; ", "r2q1nk1/1p2rppp/p1p5/3p3b/PP1P4/2NBP3/2Q2PPP/2R2RK1 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.074\"; ", "r2rn1k1/3nqppp/1pp1p3/3p4/pPPP4/P2QN1P1/1B2PP1P/2RR2K1 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.075\"; ", "r3kr2/pp1b1pp1/2p1p2p/4q3/7Q/2PBPR2/PP4PP/2KR4 b q - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.076\"; ", "r3r1k1/1p3pp1/1qp1b2p/p1Rn4/3p4/P2P1BP1/1P1BPP1P/2Q1R1K1 b - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.077\"; ", "r3r1k1/1pb1qpp1/2p1b2p/p3P3/4Q3/2B1PN1P/PP3PP1/3RR1K1 w - - bm a3; id \"STS(v9.0) Advancement of a/b/c pawns.078\"; ", "r3r1k1/pn1qnppp/1p2p3/3pP3/b1pP4/B1P2N2/R1PQBPPP/R5K1 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.079\"; ", "r3rbk1/1npq1pp1/p2p3p/1p6/1P1P4/P1R1BN1P/5PP1/3QR1K1 b - - bm c6; id \"STS(v9.0) Advancement of a/b/c pawns.080\"; ", "r3rbk1/1qpn1pp1/p4n1p/1p2pN2/2P1P3/5N2/PPQB1PPP/R3R1K1 b - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.081\"; ", "r3rn1k/1q2b1p1/pp1p1n1p/4pQ2/P3P3/1NN1BR2/1PP3PP/5R1K b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.082\"; ", "r4rk1/1p4bp/2qp1np1/p1n1p3/2P5/P1B1N3/1PB2PPP/R2Q1RK1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.083\"; ", "r4rk1/2Q2ppp/4p3/p3P3/q1P5/P5P1/5P1P/2R1R1K1 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.084\"; ", "r4rk1/2qb1ppp/p2pp3/1pn3P1/3RPP2/P1N2Q2/1PP3BP/2KR4 b - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.085\"; ", "r4rk1/4bppp/b3pn2/1p1q2B1/3p4/3B1N2/PP2QPPP/R2R2K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.086\"; ", "r4rkb/p2bp2p/2nq2pP/1p6/2pP4/2P1B1N1/P1B2PP1/2RQR1K1 w - - bm a4; id \"STS(v9.0) Advancement of a/b/c pawns.087\"; ", "r5k1/1pp1r1pp/3pbpq1/pB2n3/P1P5/1P2BP2/2PQ2PP/2KRR3 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.088\"; ", "r5k1/1rpn1pbp/p2pp3/1p4Np/2PP4/1PN1P1P1/P4PK1/2RR4 w - - bm c5; id \"STS(v9.0) Advancement of a/b/c pawns.089\"; ", "r5k1/2p1qp2/1p1p2bp/p1nPr1p1/2P5/PP2P1N1/1Q1B2PP/3R1RK1 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.090\"; ", "r5k1/5q1p/2ppNr1b/2pPp1pP/p1P3P1/P7/1PQ1RPK1/1R6 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.091\"; ", "rr2q1k1/3bnpb1/ppp3pp/3p4/1P1P4/PQ1BP1NP/1NR2PP1/3R2K1 b - - bm a5; id \"STS(v9.0) Advancement of a/b/c pawns.092\"; ", "1r1rb1k1/pp3pbp/4p1p1/1P2P3/2PBQ3/q7/P3B1PP/2R2R1K b - - bm a6; id \"STS(v9.0) Advancement of a/b/c pawns.093\"; ", "2r1k2r/1n2ppb1/p2p2p1/1p1P3p/8/1P2BP1P/P1P1BP2/2KR2R1 w k - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.094\"; ", "2r2rk1/1pqn3p/p2p1npb/N2Pp3/2P2p2/Q4PP1/PP2BBKP/3RR3 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.095\"; ", "2r3k1/p1q1bpp1/2p2n2/2Np3r/1P1B3p/P1Q4P/5PP1/2R1R1K1 w - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.096\"; ", "3r2k1/4rppp/2ppbn2/Nq2p3/4P3/1P2Q1PP/P1PR1PBK/4R3 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.097\"; ", "r6k/p3Npb1/qp4pp/4p3/1B6/1PP3P1/P2R1P1P/3R2K1 b - - bm b5; id \"STS(v9.0) Advancement of a/b/c pawns.098\"; ", "r6r/4p1k1/3q1p2/p1pPn1p1/1p2P2p/4Q3/PP2BPPP/1R2R1K1 b - - bm c4; id \"STS(v9.0) Advancement of a/b/c pawns.099\"; ", "rq4kb/5p1p/3p1Pp1/p2Pp1P1/3pP1bP/1P6/P1Q2R2/4BBK1 w - - bm b4; id \"STS(v9.0) Advancement of a/b/c pawns.100\"; ", "1b1qrr2/1p4pk/1np4p/p3Np1B/Pn1P4/R1N3B1/1Pb2PPP/2Q1R1K1 b - - bm Bxe5; id \"STS(v10.0) Simplification.001\"; c0 \"Bxe5=10, f4=3, Nc4=2\";", "1k1r2r1/1b4p1/p4n1p/1pq1pPn1/2p1P3/P1N2N2/1PB1Q1PP/3R1R1K b - - bm Nxf3; id \"STS(v10.0) Simplification.002\"; c0 \"Nxf3=10, Rge8=7, Rgf8=6, Rh8=7\";", "1k1r3r/pb1q2p1/B4p2/2p4p/Pp1bPPn1/7P/1P2Q1P1/R1BN1R1K b - - bm Bxa6; id \"STS(v10.0) Simplification.003\"; c0 \"Bxa6=10, Qc6=3, Qe6=3, Rde8=5\";", "1k1r4/1br2p2/3p1p2/pp2pPb1/2q1P2p/P1PQNB1P/1P4P1/1K1RR3 b - - bm Qxd3+; id \"STS(v10.0) Simplification.004\"; c0 \"Qxd3+=10, Qa4=6, Qb3=5, Qc5=6\";", "1k1r4/1br2p2/3p1p2/pp2pPb1/4P2p/P1PRNB1P/1P4P1/1K2R3 b - - bm Bxe3; id \"STS(v10.0) Simplification.005\"; c0 \"Bxe3=10, b4=7, Ka7=6, Rc5=6\";", "1k1r4/5qp1/p1b2n1p/1p2p3/2p1P2P/P1N1P1P1/1PB1Q3/3R2K1 b - - bm Rxd1+; id \"STS(v10.0) Simplification.006\"; c0 \"Rxd1+=10, Qc7=4, Qe7=4, Qf8=4\";", "1k1rr3/p1p4p/Bpnb4/3p1qp1/N2P4/P2Q1PPb/1PP3NP/2KR2R1 w - - bm Qxf5; id \"STS(v10.0) Simplification.007\"; c0 \"Qxf5=10, Bb5=3, Nc3=5, Qd2=1\";", "1kr5/3n2p1/q2rpn1p/p7/Ppp2P2/5BP1/1PQ2B1P/2RR2K1 w - - bm Rxd6; id \"STS(v10.0) Simplification.008\"; c0 \"Rxd6=10, Kh1=4, Qe2=7, Rd4=4\";", "1nr2qk1/pb1r1p1p/1b4pP/1p1Bp1B1/4P1Q1/2NR4/P1R2PP1/6K1 w - - bm Bxb7; id \"STS(v10.0) Simplification.009\"; c0 \"Bxb7=10, Rdd2=1\";", "1qr3k1/p3bppp/4pn2/1B2n3/N7/P3P2P/1P2QPP1/2R1B2K w - - bm Rxc8+; id \"STS(v10.0) Simplification.010\"; c0 \"Rxc8+=10, Nc3=6, Rc3=6, Rd1=6\";", "1r1q1n2/2p2ppk/p2p3p/P1b1p3/2P1P3/3P1N1P/1R1B1PP1/1Q4K1 b - - bm Rxb2; id \"STS(v10.0) Simplification.011\"; c0 \"Rxb2=10, Nd7=3, Ra8=2, Rc8=2\";", "1r1qr1k1/2Q1bp1p/2n3p1/2PN4/4B3/2N3P1/5P1P/5RK1 b - - bm Qxc7; id \"STS(v10.0) Simplification.012\"; c0 \"Qxc7=10, Nb4=2, Rc8=1\";", "1r1r2k1/6pp/1b2p2q/3b1p2/1BB1n3/P3PN1P/2Q2PP1/2R2RK1 w - - bm Bxd5; id \"STS(v10.0) Simplification.013\"; c0 \"Bxd5=10, Nd4=3, Qb3=3, Rfe1=2\";", "1r1r3k/1qpn1pbp/p1n3pB/P2bp3/BpN3Q1/3P3P/1PPN1PP1/R3R1K1 w - - bm Bxg7+; id \"STS(v10.0) Simplification.014\"; c0 \"Bxg7+=10, Qg5=1, Qh4=4\";", "1r2k2r/pp1npp2/2n3p1/7p/3bP3/P3BB1P/1P3PP1/1R1R1NK1 b k - bm Bxe3; id \"STS(v10.0) Simplification.015\"; c0 \"Bxe3=10, Bb6=6, Bc5=6, e5=6\";", "1r3r2/3q2kp/p2p1pp1/2pPp3/4Pn2/P2P1N1P/2Q2PP1/1R2R1K1 w - - bm Rxb8; id \"STS(v10.0) Simplification.016\"; c0 \"Rxb8=10, Kh1=5, Kh2=5, Nd2=5\";", "1r3rk1/pp3pp1/1np4p/2Nn2bq/3P3N/P2Q2PP/1P1BRP2/4R1K1 w - - bm Bxg5; id \"STS(v10.0) Simplification.017\"; c0 \"Bxg5=10, Qf3=2, Re4=2, Re5=2\";", "1r4k1/1q2r2p/1n4pQ/2pp1pN1/7P/P5P1/1P3P2/3RR1K1 w - - bm Rxe7; id \"STS(v10.0) Simplification.018\"; c0 \"Rxe7=10, h5=4, Nf3=4, Nxh7=3\";", "1r4k1/5ppp/1p3n2/5n2/NP1r4/1B3P2/1P4PP/3RR2K w - - bm Rxd4; id \"STS(v10.0) Simplification.019\"; c0 \"Rxd4=10, b5=1, Bc2=2, Nc3=2\";", "1r4k1/p1rn1ppp/2p1p1b1/N1PpP3/3P1P2/1R4P1/P6P/R4BK1 w - - bm Rxb8+; id \"STS(v10.0) Simplification.020\"; c0 \"Rxb8+=10, Ba6=3, Rc1=3, Re1=3\";", "1r4r1/4kp1p/3p1p1b/2q1pP2/Bn2P3/2N5/1PPNQ1PP/1K1R4 b - - bm Bxd2; id \"STS(v10.0) Simplification.021\"; c0 \"Bxd2=10, Rgd8=1\";", "1r4rk/2q1b2p/pN6/P1p1Pp2/3n1p2/5N2/5QPP/R4RK1 b - - bm Nxf3+; id \"STS(v10.0) Simplification.022\"; c0 \"Nxf3+=10, Qb7=5, Rbd8=7, Rgd8=5\";", "1r6/1R2bk2/P3p1pp/4Pp2/1r1P3P/1N2B1P1/5PK1/8 w - - bm Rxb4; id \"STS(v10.0) Simplification.023\"; c0 \"Rxb4=10, Rxb8=6\";", "1r6/3n4/p2b3p/2nN1kpP/1BP1p3/3rP3/3NKP2/1R1R4 w - - bm Bxc5; id \"STS(v10.0) Simplification.024\"; c0 \"Bxc5=10, Bc3=3, Nxe4=2, Rf1=2\";", "1r6/8/p2b3p/2nN1kpP/2P1p3/3rP3/3NKP2/1R1R4 w - - bm Rxb8; id \"STS(v10.0) Simplification.025\"; c0 \"Rxb8=10, Ne7+=6\";", "1rb2nk1/8/1p1PNbrp/3Pp1pq/p3Pn1P/P2N1PB1/4B3/R2Q1R1K w - - bm Nxf8; id \"STS(v10.0) Simplification.026\"; c0 \"Nxf8=10, Nc7=4, Rc1=1\";", "2br1rk1/2q3p1/1pp1p2p/p1n1b3/2BNN1Q1/4P3/PP3PP1/2RR3K w - - bm Nxc5; id \"STS(v10.0) Simplification.027\"; c0 \"Nxc5=10, Kg1=2, Nf3=2, Nxe6=3\";", "2br2k1/r5p1/4p3/5p2/8/pP3B2/1q2RPPP/Q3R1K1 b - - bm Qxa1; id \"STS(v10.0) Simplification.028\"; c0 \"Qxa1=10, Qd4=1, Qxb3=1, Rd2=3\";", "2brr1k1/2q1bpp1/pNnpp2p/P1p5/1P2PP2/2P1B1P1/4Q1BP/R2R2K1 w - - bm Nxc8; id \"STS(v10.0) Simplification.029\"; c0 \"Nxc8=10, b5=3, bxc5=4, Rab1=3\";", "2r1n1k1/5ppp/1prp4/p3p1q1/2b1P3/P1B1QB1P/1PPR1PPK/R7 b - - bm Qxe3; id \"STS(v10.0) Simplification.030\"; c0 \"Qxe3=10, h6=2, Qe7=2, Qh4=2\";", "2r1r1k1/1p1q1bbp/pn3pp1/n2p4/3P1B1P/2NB1N2/PPQ1RPP1/4R1K1 w - - bm Rxe8+; id \"STS(v10.0) Simplification.031\"; c0 \"Rxe8+=10, h5=8, Qb1=5\";", "2r1r1k1/2qbppbp/pp1p2p1/n2P4/P1P4P/1P3N2/1BQ1BPP1/R2R2K1 w - - bm Bxg7; id \"STS(v10.0) Simplification.032\"; c0 \"Bxg7=10, Bd4=3, h5=3, Ng5=2\";", "2r2nk1/pp3p2/5qpb/4p1Np/P3P2P/1P2B1P1/2r2P2/2RRQ1K1 w - - bm Rxc2; id \"STS(v10.0) Simplification.033\"; c0 \"Rxc2=10, Kg2=2\";", "2r2r1k/1p1q2b1/pQn1b1pp/4pp2/B3P3/1P3N1P/P1RB1PP1/R5K1 w - - bm Bxc6; id \"STS(v10.0) Simplification.034\"; c0 \"Bxc6=10, Bb4=4, Be3=2, Rac1=1\";", "2r3k1/4qpp1/4p2p/1P1bP3/4pPP1/4Q3/4B2P/2R3K1 b - - bm Rxc1+; id \"STS(v10.0) Simplification.035\"; c0 \"Rxc1+=10\";", "2r3k1/pp3pp1/4q2p/4p3/bB2Pn2/P4P2/1P1Q2PP/2R2BK1 w - - bm Rxc8+; id \"STS(v10.0) Simplification.036\"; c0 \"Rxc8+=10, Bc5=4, Qe3=2, Rc3=4\";", "2r5/4q1kp/1nB2pp1/p3p3/Pb2Q3/4P3/1B1r1PPP/1R1R2K1 b - - bm Rxd1+; id \"STS(v10.0) Simplification.037\"; c0 \"Rxd1+=10, Rcd8=2\";", "2rk1br1/4np1p/p2pb3/1p1Npp2/P3P3/2PB2Pq/1PN2P1P/R2Q1R1K w - - bm Nxe7; id \"STS(v10.0) Simplification.038\"; c0 \"Nxe7=10, Nb6=4, Ncb4=6, Ndb4=3\";", "2rnq2k/pb3pp1/1p5p/3B4/1P3Q2/P4N1P/2R2PP1/6K1 w - - bm Rxc8; id \"STS(v10.0) Simplification.039\"; c0 \"Rxc8=10, Bb3=3, Rc4=5, Rd2=1\";", "2rq1r1k/3n2pp/1p2p3/1P1b2bn/p1BP4/P4NP1/1B1NQP2/2R1R1K1 w - - bm Bxd5; id \"STS(v10.0) Simplification.040\"; c0 \"Bxd5=10, Bd3=1, Nxg5=2, Rc3=2\";", "2rq1rk1/1p2bpp1/4p3/pP1pP1np/P2P4/R3BP2/3Q2PP/1NR3K1 w - - bm Rxc8; id \"STS(v10.0) Simplification.041\"; c0 \"Rxc8=10, Ra2=1, Rac3=3, Rb3=1\";", "2rr1bk1/3qnppp/pp6/3p4/2PP2Q1/PP5P/1B1N2P1/2R2R1K b - - bm Qxg4; id \"STS(v10.0) Simplification.042\"; c0 \"Qxg4=10, dxc4=2, f6=1, g6=2\";", "2rr2k1/p4pp1/1p2pn1p/3b1P2/1qNP4/1P1B4/P2Q2PP/2RR2K1 b - - bm Qxd2; id \"STS(v10.0) Simplification.043\"; c0 \"Qxd2=10, a5=3, Qe7=2, Qf8=2\";", "3b4/2q2pkp/p2p1np1/1b1Pp1B1/1pN1P3/1P5P/P1Q2PP1/5NK1 b - - bm Bxc4; id \"STS(v10.0) Simplification.044\"; c0 \"Bxc4=10, a5=1, Be7=1, h6=2\";", "3n1r2/2r4p/p2bp2k/1p1p2pP/1P1P4/P1R1B1Pq/4QP2/1B2R1K1 w - - bm Rxc7; id \"STS(v10.0) Simplification.045\"; c0 \"Rxc7=10, Rec1=2\";", "3q4/pp2b1k1/2r5/4p2p/P1RpN3/1P3PP1/1Q2P1K1/8 w - - bm Rxc6; id \"STS(v10.0) Simplification.046\"; c0 \"Rxc6=10, Nd2=3, Qb1=2, Qc2=4\";", "3r1k2/p4ppp/1r2p3/n1p1P3/5P1P/P1PBK1P1/8/1R3R2 w - - bm Rxb6; id \"STS(v10.0) Simplification.047\"; c0 \"Rxb6=10, a4=5, h5=8, Rfd1=4\";", "3r1q1k/Q2R1bpp/3b1r2/2p2p2/2N2P2/1Pp3P1/5PBP/4R1K1 w - - bm Rxd8; id \"STS(v10.0) Simplification.048\"; c0 \"Rxd8=10, h4=5, Nxd6=5, Rc1=6\";", "3r1rk1/4b1p1/p3p3/4pbPp/2R4P/1PN5/PB3P2/3R2K1 w - - bm Rxd8; id \"STS(v10.0) Simplification.049\"; c0 \"Rxd8=10, Rc7=2\";", "3r2k1/1b1rnp1p/p2qpbp1/1pR5/1P1PB2P/P3BN2/4QPP1/3R2K1 w - - bm Bxb7; id \"STS(v10.0) Simplification.050\"; c0 \"Bxb7=10, Bg5=7\";", "3r4/5pk1/4rqp1/p2p4/Pb1Q3p/1Pp1P2P/2R1BPP1/3R2K1 b - - bm Qxd4; id \"STS(v10.0) Simplification.051\"; c0 \"Qxd4=10, g5=6, Re4=5, Red6=5\";", "3rkb2/1p4rp/p4p2/2p2B2/2N1Pp2/3R2P1/PPP2P1P/6K1 b - - bm Rxd3; id \"STS(v10.0) Simplification.052\"; c0 \"Rxd3=10, Be7=2, fxg3=2, Rd4=3\";", "3rr1k1/6p1/pb1p3p/1p1RnBqP/4PQN1/2P5/PP6/1K3R2 w - - bm Qxg5; id \"STS(v10.0) Simplification.053\"; c0 \"Qxg5=10, a3=3, Kc2=3, Nxe5=4\";", "4qbk1/r1p1nbpp/1p3p2/1Q6/P2P4/2BN1BP1/5P1P/2R3K1 w - - bm Qxe8; id \"STS(v10.0) Simplification.054\"; c0 \"Qxe8=10, Bb2=5, h3=5, Nf4=5, Nf4=6\";", "4r1k1/1p2qp1p/2p1n1pB/p3p3/P3P1B1/2N2Q1P/2P2PP1/6K1 w - - bm Bxe6; id \"STS(v10.0) Simplification.055\"; c0 \"Bxe6=10, Qd1=2, Qd3=1, Qe3=2\";", "4r1k1/1p5p/1r4p1/2pq1p2/P4P2/3P2Q1/1bP3RP/4R1BK b - - bm Rxe1; id \"STS(v10.0) Simplification.056\"; c0 \"Rxe1=10, Ra8=2, Rbe6=3, Rc8=3\";", "4r1k1/2r1bp2/1p4p1/3BP2p/nP5P/5K2/1B1R1P2/2R5 b - - bm Rxc1; id \"STS(v10.0) Simplification.057\"; c0 \"Rxc1=10, Ra7=1, Rcc8=3\";", "4r1k1/2r2ppp/2nq1n2/1BbppP2/Q7/2P1BN1P/5PP1/RR4K1 w - - bm Bxc5; id \"STS(v10.0) Simplification.058\"; c0 \"Bxc5=10, Nd2=6, Qb3=6, Re1=6\";", "4r2k/p1r1b1pp/1p1pqn2/4p3/1PP4B/P4B1P/4QPP1/2RR2K1 w - - bm Bxf6; id \"STS(v10.0) Simplification.059\"; c0 \"Bxf6=10, Qd2=6, Qe3=6, Re1=6\";", "4r2k/p2n1pp1/4p2p/1q1r3P/2pR1BP1/P1P1R3/1P2QP2/2K5 b - - bm Rxd4; id \"STS(v10.0) Simplification.060\"; c0 \"Rxd4=10, Nc5=4, Rc8=2\";", "4r3/1pn3k1/4p1b1/p1Pp3r/3P2NR/1P3B2/3K2P1/4R3 w - - bm Rxh5; id \"STS(v10.0) Simplification.061\"; c0 \"Rxh5=10, g3=3, Reh1=3\";", "4rrk1/2pb3p/p7/1ppPq3/2P1P1pP/2N5/PP2Q2P/4RR1K w - - bm Rxf8+; id \"STS(v10.0) Simplification.062\"; c0 \"Rxf8+=10, a3=3, Kg2=4, Rf2=2\";", "5B1k/p6p/1p2q1r1/2p1P1p1/3b1n2/PQ3R2/1P4P1/K2R4 w - - bm Qxe6; id \"STS(v10.0) Simplification.063\"; c0 \"Qxe6=10, g3=3, Qc2=1\";", "5k2/4pp2/1N2n1pp/r3P3/P5PP/2rR1K2/P7/3R4 b - - bm Rxd3+; id \"STS(v10.0) Simplification.064\"; c0 \"Rxd3+=10, Rc6=4\";", "5qk1/1p1rbrpp/pP3p2/P1pbp3/1n6/BN1P2P1/1Q2PPBP/R1R3K1 w - - bm Bxd5; id \"STS(v10.0) Simplification.065\"; c0 \"Bxd5=10, Bxb4=7, Rab1=6, Rc3=4\";", "5rk1/8/3p3p/p1qP2p1/Rrb1P3/3p1P2/1P1Q1RP1/3N2K1 w - - bm Rxb4; id \"STS(v10.0) Simplification.066\"; c0 \"Rxb4=10, b3=3, Ra3=2\";", "6k1/1r3pp1/4b2p/p2pP2P/1r1R1Q2/1Pq1N3/2P3P1/3R3K b - - bm Rxd4; id \"STS(v10.0) Simplification.067\"; c0 \"Rxd4=10, R7b5=5, R7b6=4, Rb8=5\";", "6k1/3q2b1/p1rrnpp1/P3p3/4P3/1pBR3Q/1P4PP/1B1R3K b - - bm Rxd3; id \"STS(v10.0) Simplification.068\"; c0 \"Rxd3=10, Bf8=6, Nd4=5, Qc7=5\";", "6k1/3qbppp/5n2/PQpPp3/4P3/5PP1/1P4KP/2B5 w - - bm Qxd7; id \"STS(v10.0) Simplification.069\"; c0 \"Qxd7=10\";", "6k1/5p2/1q1bp1p1/1Pp1B2p/2Pn3P/6P1/1Q2BP2/6K1 w - - bm Bxd6; id \"STS(v10.0) Simplification.070\"; c0 \"Bxd6=10, Bf6=2, Bxd4=1, f4=2\";", "6r1/4pp1k/3p3p/2qP1Pb1/r3P1PB/1R5K/4Q3/1R6 b - - bm Bxh4; id \"STS(v10.0) Simplification.071\"; c0 \"Bxh4=10, h5=5, Qd4=5, Rc8=2\";", "7r/2r1kp2/3pp2p/p3q1p1/4B3/P5P1/1Q1B1P1P/4R1K1 w - - bm Qxe5; id \"STS(v10.0) Simplification.072\"; c0 \"Qxe5=10, Qb1=5, Qb3=3, Qb6=3\";", "8/1p1bkq2/p2pp1n1/P1b1p1P1/4P1QN/1KNB4/1PP5/8 b - - bm Nxh4; id \"STS(v10.0) Simplification.073\"; c0 \"Nxh4=10, Bc6=5, Be8=4, Bf2=5\";", "8/3n1nk1/q2b4/3p2p1/2pPp3/2P1P3/1Q4PN/3BB1K1 b - - bm Bxh2+; id \"STS(v10.0) Simplification.074\"; c0 \"Bxh2+=10, Bc7=4, Nf6=4, Nh6=4\";", "8/p1r4p/1p1qnkpP/3Ppp2/8/P2Q2P1/BP3P2/2R3K1 w - - bm Rxc7; id \"STS(v10.0) Simplification.075\"; c0 \"Rxc7=10, Rc3=6, Rc4=6, Re1=6\";", "8/r4p2/P1ppk3/2p4p/1rPb1BqP/RR1PpQP1/4P1K1/8 b - - bm Qxf3+; id \"STS(v10.0) Simplification.076\"; c0 \"Qxf3+=10, f5=2, Ra8=1, Rxb3=1\";", "br2r1k1/5p1p/p7/nq4Pp/p3P3/P1PQ4/1P2R1B1/K1BR4 b - - bm Qxd3; id \"STS(v10.0) Simplification.077\"; c0 \"Qxd3=10, Nb3+=5, Rbc8=4, Rbd8=4\";", "br3rk1/p2q1pp1/3p1n1p/2p1p3/2PnP3/P1BP1NP1/1R1Q1PBP/1R4K1 b - - bm Rxb2; id \"STS(v10.0) Simplification.078\"; c0 \"Rxb2=10, Nxf3+=4, Qc7=4, Rb7=5\";", "q3b3/4Bpkp/6p1/3Pp3/r3r3/1Q5P/R4PP1/R5K1 w - - bm Rxa4; id \"STS(v10.0) Simplification.079\"; c0 \"Rxa4=10, Bd6=4, Kh1=5, Ra3=5\";", "qr3nk1/r4p1p/2p1p1pP/p1PpPbP1/3P1P2/QR2R3/P2NB2K/8 w - - bm Rxb8; id \"STS(v10.0) Simplification.080\"; c0 \"Rxb8=10, Kg3=2, Qb2=4, Rb6=3\";", "qr4k1/3b1pp1/4p2p/n2pP3/n1pP4/Q1P2NP1/3N1P1P/1R3BK1 b - - bm Rxb1; id \"STS(v10.0) Simplification.081\"; c0 \"Rxb1=10, Bc6=2, Be8=3, Rb7=2\";", "R1b2b2/2kp1p1p/B2npp2/2p5/4P1r1/6P1/3N1P1P/3K3R w - - bm Bxc8; id \"STS(v10.0) Simplification.082\"; c0 \"Bxc8=10, f3=3, h3=3, Kc2=3\";", "r1b2rk1/1p2bpp1/5n2/1Q2pq2/2P1N3/p3B1Pp/P2RPP1P/3R2KB w - - bm Nxf6+; id \"STS(v10.0) Simplification.083\"; c0 \"Nxf6+=10, Nc3=4, Ng5=2, Qb1=1\";", "r1bq4/3n1pkp/2pRr1p1/8/nPP1PP2/6PP/2Q1N1B1/1R4K1 w - - bm Rxe6; id \"STS(v10.0) Simplification.084\"; c0 \"Rxe6=10, Rdd1=3\";", "r1r1bnk1/5pp1/p2pp3/2q1n1P1/3QP2P/1PN4B/2P1NR1K/3R4 b - - bm Qxd4; id \"STS(v10.0) Simplification.085\"; c0 \"Qxd4=10, a5=5, Bb5=4, Rab8=3\";", "r2qr1k1/2p2pp1/3p1n2/3P1n1p/1Q3P2/1B5P/PP4PB/2R1R2K w - - bm Rxe8+; id \"STS(v10.0) Simplification.086\"; c0 \"Rxe8+=10, Qc4=5, Ra1=4, Rb1=4\";", "r2r2k1/1b1nqpp1/pp5p/2pNP3/P3Q3/5N2/1P3PPP/2RR2K1 b - - bm Bxd5; id \"STS(v10.0) Simplification.087\"; c0 \"Bxd5=10, Kh8=6, Qe6=6, Qe8=6\";", "r2r2k1/4qp2/p5p1/2p4p/5P2/3RP1K1/PPQ4P/3R4 b - - bm Rxd3; id \"STS(v10.0) Simplification.088\"; c0 \"Rxd3=10, h4+=2, Rdb8=2, Re8=4\";", "r3r1k1/2qn3p/1p2ppp1/3b4/p1PP1P2/4QN2/R5PP/1R3BK1 b - - bm Bxf3; id \"STS(v10.0) Simplification.089\"; c0 \"Bxf3=10, Bb7=4, Bc6=5, Bxc4=3\";", "r3r1k1/5pbp/4bpp1/pp2q3/4P1PP/P1NR2Q1/1PP1B3/2KR4 b - - bm Qxg3; id \"STS(v10.0) Simplification.090\"; c0 \"Qxg3=10, Qb8=1, Qc5=2\";", "r3r1k1/6p1/p2p1qp1/1p1P3p/4P1bP/P2Q3B/1PP5/1K2R2R b - - bm Bxh3; id \"STS(v10.0) Simplification.091\"; c0 \"Bxh3=10, Bc8=5, Qxh4=5, Rf8=5\";", "r3r3/p5bk/2pq1ppp/1p1b4/3P4/P2N1N2/1P3PPP/2RQR1K1 b - - bm Rxe1+; id \"STS(v10.0) Simplification.092\"; c0 \"Rxe1+=10, a5=6, h5=7, Rac8=5\";", "r3rbk1/p1qn1p1p/2p1n1p1/Pp2p1B1/1P2P3/2N1Q3/2PN1PPP/1R2R1K1 b - - bm Nxg5; id \"STS(v10.0) Simplification.093\"; c0 \"Nxg5=10, a6=3, Nb8=3, Rac8=4\";", "r4r1k/1bq1bppp/p2p4/1p1Nn3/4PR2/PN1B3Q/1PP3PP/R5K1 b - - bm Bxd5; id \"STS(v10.0) Simplification.094\"; c0 \"Bxd5=10, Bc8=5, Qc8=5, Qd8=1\";", "r4rk1/1p2bppp/p3bn2/8/Pq1BP3/1BN1Q3/1PP3PP/R3K2R w KQ - bm Bxe6; id \"STS(v10.0) Simplification.095\"; c0 \"Bxe6=10, e5=2, O-O-O=2, Rd1=1\";", "r4rk1/pp1qppbp/2n3p1/3b4/3PB2B/2P5/3N1PPP/1R1QR1K1 b - - bm Bxe4; id \"STS(v10.0) Simplification.096\"; c0 \"Bxe4=10, e6=5, Rac8=3, Rfe8=4\";", "r4rk1/pp5p/2pp2pb/2n1nq2/2P2P2/2N1B3/PP2B1PP/2RQR1K1 w - - bm Bxc5; id \"STS(v10.0) Simplification.097\"; c0 \"Bxc5=10, g4=6, Kh1=6, Rf1=6\";", "r4rk1/pppb3p/8/2pPqp2/2P1P1nP/1QN4B/PP5P/4RRK1 w - - bm Bxg4; id \"STS(v10.0) Simplification.098\"; c0 \"Bxg4=10, Qc2=3, Rf4=5\";", "r5k1/5p2/7p/5qP1/1P1QR3/5P2/r4RK1/8 b - - bm Rxf2+; id \"STS(v10.0) Simplification.099\"; c0 \"Rxf2+=10, h5=6, hxg5=6, R2a6=6\";", "rr4k1/p2nBpp1/q1p1pn1p/7P/2pPN3/P1P5/1PQ2PP1/2KR3R b - - bm Nxe4; id \"STS(v10.0) Simplification.100\"; c0 \"Nxe4=10, Rb3=4, Rb5=4\";", "1k2r3/1p1bP3/2p2p1Q/Ppb5/4Rp1P/2q2N1P/5PB1/6K1 b - - bm Kc7; id \"STS(v11.0) King Activity.001\"; c0 \"Kc7=10, Kc8=4, Qa1+=4\";", "1k2r3/p7/Pp1pP1p1/4p2p/2r4P/5P1B/4RB1K/8 w - - bm Kg3; id \"STS(v11.0) King Activity.002\"; c0 \"Kg3=10, Kg2=5, Re1=5, Re3=3\";", "1k5r/6p1/p2b4/7p/2r1p2P/R1B1P3/6P1/2R3K1 b - - bm Ka7; id \"STS(v11.0) King Activity.003\"; c0 \"Ka7=10, Bxa3=3, Kb7=4, Rc6=2\";", "1n5k/3r2p1/2p1qp1p/3p1N1P/1P1P1rP1/p1R5/P7/1KRQ4 w - - bm Ka1; id \"STS(v11.0) King Activity.004\"; c0 \"Ka1=10, Re3=2, Rg3=3\";", "1r2r3/1p1b3k/2p2n2/p1Pp4/P2N1PpP/1R2p3/1P2P1BP/3R2K1 b - - bm Kg6; id \"STS(v11.0) King Activity.005\"; c0 \"Kg6=10, Kh6=8, Ne4=8, Nh5=8\";", "1r2rqk1/8/bn1p3p/B1p2p2/p1PnpP2/P3R2Q/1P3NPP/2R2BK1 b - - bm Kh7; id \"STS(v11.0) King Activity.006\"; c0 \"Kh7=10, Re6=3\";", "1R3bk1/7p/6p1/8/1pN3P1/8/r4P1P/6K1 b - - bm Kf7; id \"STS(v11.0) King Activity.007\"; c0 \"Kf7=10, Kg7=1\";", "1r3k2/2N2pp1/1pR2n1p/4p3/8/1P1K1P2/P5PP/8 w - - bm Kc4; id \"STS(v11.0) King Activity.008\"; c0 \"Kc4=10, a4=6, Ke3=2, Nb5=2\";", "1r3k2/3n1p2/6pN/2p1PP2/p2q4/Pr1p2P1/1P1R1R1P/3Q2K1 b - - bm Ke7; id \"STS(v11.0) King Activity.009\"; c0 \"Ke7=10, Ke8=2, Kg7=2\";", "2b4r/1p1k4/1pnbppp1/r2p3p/2pP1P1P/2P2NPB/PPN2P2/R3R1K1 b - - bm Ke7; id \"STS(v11.0) King Activity.010\"; c0 \"Ke7=10, Nd8=3, Re8=2\";", "2k2r1r/1b4p1/1nq1p3/1p5p/5P2/1Pp1B1KP/2B1QRP1/R7 w - - bm Kh2; id \"STS(v11.0) King Activity.011\"; c0 \"Kh2=10, Bd3=7\";", "2k3r1/1b2bp2/2p2n2/pp2p1Bp/2p1P2P/P2n1B2/1P1RN1P1/5K1R b - - bm Kc7; id \"STS(v11.0) King Activity.012\"; c0 \"Kc7=10, a4=3, Ba6=1, c5=2, Kb8=3\";", "2kr1b1r/1bq2pp1/p3pn2/7p/1ppPN2P/4PQ2/PPBB1PP1/R1R3K1 b - - bm Kb8; id \"STS(v11.0) King Activity.013\"; c0 \"Kb8=10, a5=2, Bd5=2, e5=2, Rh6=3\";", "2kr3r/p3p3/1pn2pp1/1R5p/4P2P/2P1BPP1/P3K3/7R b - - bm Kb7; id \"STS(v11.0) King Activity.014\"; c0 \"Kb7=10, Rd6=7, Rd7=7\";", "2q2r2/3bbpkp/r2p4/p1pPp1P1/PpP1P2P/1P3QK1/4RN2/2B3R1 w - - bm Kh2; id \"STS(v11.0) King Activity.015\"; c0 \"Kh2=10, Ng4=6, Qh5=5\";", "2r1b3/p3kpp1/7p/3P4/7P/2p1KPP1/P7/1BR5 w - - bm Kd4; id \"STS(v11.0) King Activity.016\"; c0 \"Kd4=10, Bc2=7, Bf5=7\";", "2r1k2r/1q1bbp2/p4p2/1p2pP1p/8/1N1B2Q1/PPP3PP/1K1RR3 b k - bm Kf8; id \"STS(v11.0) King Activity.017\"; c0 \"Kf8=10, Kd8=6, Rf8=6\";", "2r1r3/1k1b1p2/1p4p1/p3n3/2PRP2p/PP1NK2P/2R3B1/8 b - - bm Kc7; id \"STS(v11.0) King Activity.018\"; c0 \"Kc7=10, Bc6=9, Kc6=9, Rc7=9\";", "2r2bk1/1Q3ppp/p6r/P2BP2q/5Pb1/1P1RR1P1/3B4/6K1 w - - bm Kf1; id \"STS(v11.0) King Activity.019\"; c0 \"Kf1=10, Bc3=8, Bg2=8, Bxf7+=8\";", "2r2k2/4pp2/pp6/2pPn3/4PN1p/1P5P/P4P2/2R2K2 w - - bm Ke2; id \"STS(v11.0) King Activity.020\"; c0 \"Ke2=10, Ng2=3, Rc3=3\";", "2r2k2/p4pp1/b3n2p/8/1pp1P2P/4NPP1/PPB3K1/3R4 w - - bm Kf2; id \"STS(v11.0) King Activity.021\"; c0 \"Kf2=10, Nd5=2, Rd2=1\";", "2r3k1/1q6/4p3/p2p2bp/P2P1n2/2P1NP2/1PB4B/1R2R1K1 b - - bm Kf7; id \"STS(v11.0) King Activity.022\"; c0 \"Kf7=10, Kf8=3, Rf8=6\";", "2r3k1/2r1b3/p3p2p/3n2p1/2N1KP2/P2N2PP/2R5/2R5 b - - bm Kg7; id \"STS(v11.0) King Activity.023\"; c0 \"Kg7=10, Kh7=1\";", "2rb2k1/1p1q3p/1P2b1p1/1N3p2/3B4/4PP1P/1Q4P1/R5K1 w - - bm Kh2; id \"STS(v11.0) King Activity.024\"; c0 \"Kh2=10, Kh1=4, Ra7=3\";", "2rk4/1Rp5/1bBp1r2/4p2p/8/6P1/P1P2P1P/5RK1 w - - bm Kg2; id \"STS(v11.0) King Activity.025\"; c0 \"Kg2=10, c4=4, h4=6\";", "3b4/3k1p2/4p1p1/p1rpP1Pp/R4P2/1P3K2/P2B1P2/8 b - - bm Kc6; id \"STS(v11.0) King Activity.026\"; c0 \"Kc6=10, Bb6=4, Kc8=5\";", "3b4/5p2/2k1p1p1/p1rpP1Pp/R4P2/1P6/P2BKP2/8 b - - bm Kb5; id \"STS(v11.0) King Activity.027\"; c0 \"Kb5=10, Bb6=6, Kb7=6\";", "3k1b1r/pp3p1p/5P2/3Bn3/8/2N5/PP5P/2K4R b - - bm Kc7; id \"STS(v11.0) King Activity.028\"; c0 \"Kc7=10, b6=4, Kc8=4\";", "3q1rk1/5p2/1pQ1p1p1/p2p4/P2P2P1/1P2P3/6RP/6K1 b - - bm Kg7; id \"STS(v11.0) King Activity.029\"; c0 \"Kg7=10, Kh7=3, Qe7=4, Qf6=4, Qh4=4\";", "3q4/p2r1k2/1p1b2nP/1Pp5/2P1Q3/1P2B3/3R1K2/8 w - - bm Kg1; id \"STS(v11.0) King Activity.030\"; c0 \"Kg1=10, Kf1=8\";", "3r1k2/4qp1p/6p1/p3p1P1/P2nQ2P/1B1R4/5PK1/8 b - - bm Kg7; id \"STS(v11.0) King Activity.031\"; c0 \"Kg7=10, Rc8=4, Rd7=4, Re8=4\";", "3r1k2/p4pp1/2n1p2p/1NPr4/P7/6P1/5P1P/2R1RK2 b - - bm Ke7; id \"STS(v11.0) King Activity.032\"; c0 \"Ke7=10, a6=3, g5=4, g6=4\";", "3r1r2/p3bpp1/1p2p1k1/4P2p/2P2P2/5K1P/PP3B2/3R1R2 b - - bm Kf5; id \"STS(v11.0) King Activity.033\"; c0 \"Kf5=10, f5=5, f6=1, Rfe8=5\";", "3r1r2/p3bppk/1p2p2p/4P3/2P2P2/8/PP3BKP/3R1R2 b - - bm Kg6; id \"STS(v11.0) King Activity.034\"; c0 \"Kg6=10\";", "3r2k1/1p4p1/p2P3p/1pPN4/1K4b1/8/2R4P/8 w - - bm Ka5; id \"STS(v11.0) King Activity.035\"; c0 \"Ka5=10, Rd2=5, Rf2=6, Rg2=5\";", "3r2k1/1rq2p2/2bp2pP/p3p2n/4P3/1BN2P2/1PP5/R2RQK2 b - - bm Kh7; id \"STS(v11.0) King Activity.036\"; c0 \"Kh7=10, Nf4=5, Ra7=2\";", "3r2k1/2p2ppp/8/P1b1N3/8/1Bn1nP2/5B1P/4K2R b - - bm Kf8; id \"STS(v11.0) King Activity.037\"; c0 \"Kf8=10, g6=3, Ncd5=1\";", "3r2k1/4pp1p/2q3p1/8/1P6/r1P2P2/P3Q1PP/RKR5 w - - bm Kb2; id \"STS(v11.0) King Activity.038\"; c0 \"Kb2=10, h3=6, Qe1=6, Qe4=5\";", "3r3r/ppk2pb1/4bn1p/3p2p1/3N4/2NR1BP1/PPP1P2P/5RK1 b - - bm Kb8; id \"STS(v11.0) King Activity.039\"; c0 \"Kb8=10, h5=2, Rhe8=1\";", "3r4/6p1/5kBp/7P/6P1/4BP2/1p2K3/8 b - - bm Ke5; id \"STS(v11.0) King Activity.040\"; c0 \"Ke5=10, Ke6=6, Rd5=7, Rd7=7\";", "3R4/6pk/p4p1p/1r1p3P/2pP4/2P1PN2/4bPP1/6K1 w - - bm Kh2; id \"STS(v11.0) King Activity.041\"; c0 \"Kh2=10, Kh1=2\";", "3r4/p3k3/1p2bpp1/2p5/2P2q2/2Q2N2/PP4PP/4R2K w - - bm Kg1; id \"STS(v11.0) King Activity.042\"; c0 \"Kg1=10, b3=9, h3=9, Qc2=9\";", "3rbk2/4pp1p/6p1/3PP3/p1PnN1P1/Nn5P/5PBK/1R6 w - - bm Kg3; id \"STS(v11.0) King Activity.043\"; c0 \"Kg3=10, Nc3=4, Re1=4\";", "3rbk2/5p1p/1p2p3/pP1nr1p1/P2RN1P1/3BP3/3K3P/3R4 b - - bm Ke7; id \"STS(v11.0) King Activity.044\"; c0 \"Ke7=10, Bd7=4, h6=6, Rd7=8\";", "3rr1k1/q4pp1/p1ppbR1p/2P4R/3PP3/2N1Q2P/6P1/6K1 b - - bm Kf8; id \"STS(v11.0) King Activity.045\"; c0 \"Kf8=10, dxc5=3\";", "3rr3/p1k4p/1p3p1b/2p2p1n/5P2/2P1BR2/PP1N3P/1K2R3 b - - bm Kc6; id \"STS(v11.0) King Activity.046\"; c0 \"Kc6=10, Kb7=5, Kc8=5\";", "4k3/1p4pp/p1b2p2/P4P2/1PNp2qN/2nP2P1/3Q1K1P/8 b - - bm Kd8; id \"STS(v11.0) King Activity.047\"; c0 \"Kd8=10, Kf8=2, Qd1=3\";", "4k3/3p3p/p2Pn1p1/Pr2Pp2/5P1P/1NR3P1/5K2/8 w - - bm Ke3; id \"STS(v11.0) King Activity.048\"; c0 \"Ke3=10, h5=7, Ke1=5, Rc8+=4, Rd3=3\";", "4r1k1/1p1q2pp/p4pn1/3p4/3N4/1PPK2RP/P2Q1PP1/8 w - - bm Kc2; id \"STS(v11.0) King Activity.049\"; c0 \"Kc2=10, Qc1=5, Qd1=5\";", "4r1k1/1Q3ppp/2PB4/pb6/8/P1K5/6P1/1r6 w - - bm Kc2; id \"STS(v11.0) King Activity.050\"; c0 \"Kc2=10, c7=7, Kd4=1\";", "4r1k1/2q1p3/2p2pp1/1p1b4/p7/P1Q3R1/1P3PP1/4R1K1 b - - bm Kg7; id \"STS(v11.0) King Activity.051\"; c0 \"Kg7=10, Bf7=8, g5=8, Kf7=8\";", "4r1k1/3b1q2/2pn1p1b/1p3P2/p2PN1PP/P4Q2/1P4R1/4R1K1 b - - bm Kf8; id \"STS(v11.0) King Activity.052\"; c0 \"Kf8=10\";", "4r1k1/5r2/p5p1/6p1/1PbB4/2P2B2/5PP1/R5K1 w - - bm Kh2; id \"STS(v11.0) King Activity.053\"; c0 \"Kh2=10, Be3=8, Ra5=6, Rd1=4\";", "4r1k1/p4p1p/1bpp2p1/3p4/8/1P1Q2P1/P1P1rPKP/5R2 w - - bm Kf3; id \"STS(v11.0) King Activity.054\"; c0 \"Kf3=10, Kg1=2, Qc3=2\";", "4r2r/1p6/2p2n2/p1Pp3k/P2NbPp1/4R3/1P2P2P/2R2BK1 b - - bm Kg6; id \"STS(v11.0) King Activity.055\"; c0 \"Kg6=10, Kh4=4, Ref8=3, Rh7=3\";", "4r3/2R1pk2/3p2pp/2bP1p2/8/1R4P1/5PK1/8 b - - bm Kf6; id \"STS(v11.0) King Activity.056\"; c0 \"Kf6=10, Bd4=8, g5=9, h5=8, Ra8=8\";", "4r3/5k2/2PB4/p4p2/8/1P1R2KP/2P5/5r2 w - - bm Kg2; id \"STS(v11.0) King Activity.057\"; c0 \"Kg2=10, Rd2=1\";", "4r3/5k2/3p1npp/1p1P1p2/p4P2/P3N2P/1P2KP2/2R5 w - - bm Kf3; id \"STS(v11.0) King Activity.058\"; c0 \"Kf3=10, h4=8, Kf1=8, Rc7+=8\";", "4r3/5k2/p2B2r1/2P2p2/1P5p/3R3P/1P3KP1/8 w - - bm Kf3; id \"STS(v11.0) King Activity.059\"; c0 \"Kf3=10\";", "4R3/5pkp/b3p1p1/2Q5/1P6/P2qP3/K7/8 w - - bm Kb2; id \"STS(v11.0) King Activity.060\"; c0 \"Kb2=10, Qc1=4\";", "4rbk1/p2R1p2/2p2p1p/5p2/8/PR2p3/2P3PP/5K2 w - - bm Ke2; id \"STS(v11.0) King Activity.061\"; c0 \"Ke2=10, g3=3, Rc3=3\";", "4rnk1/1p2rp1p/2p3p1/3p1B1P/N4R2/pP2P1R1/P5P1/6K1 w - - bm Kf2; id \"STS(v11.0) King Activity.062\"; c0 \"Kf2=10, e4=5, hxg6=7, Rff3=6\";", "4rrk1/7p/pn1p1npb/N2P4/1P6/5BP1/PB6/3R1RK1 w - - bm Kg2; id \"STS(v11.0) King Activity.063\"; c0 \"Kg2=10, Bxf6=2\";", "5bk1/1r3p2/6p1/2pRP3/8/6P1/3N1PK1/8 b - - bm Kg7; id \"STS(v11.0) King Activity.064\"; c0 \"Kg7=10\";", "5k2/p3np2/2b1p2p/2P2p1P/8/2N1PP2/2K1B1P1/8 b - - bm Ke8; id \"STS(v11.0) King Activity.065\"; c0 \"Ke8=10, Bd7=7, Nd5=8, Ng8=6\";", "5n2/1p2r1kp/1Qp1r1p1/p3Pp2/P6P/4R1P1/1P6/5K2 w - - bm Kg2; id \"STS(v11.0) King Activity.066\"; c0 \"Kg2=10, b3=6, Kg1=7, Re1=9, Re2=8\";", "5r2/1N6/2p2Pk1/p1P1P1p1/3b3p/KP1N2n1/P6R/8 b - - bm Kf5; id \"STS(v11.0) King Activity.067\"; c0 \"Kf5=10, Ne4=1, Nf1=1\";", "5rk1/1p5p/3p2p1/qP1N1n2/2P2P2/8/P4Q1P/4R1K1 w - - bm Kg2; id \"STS(v11.0) King Activity.068\"; c0 \"Kg2=10, Kf1=3, Kh1=5, Rc1=2, Re4=5\";", "5rrk/6qp/2R2b2/1P1pp2Q/5p2/7R/P2B1P2/5K2 w - - bm Ke2; id \"STS(v11.0) King Activity.069\"; c0 \"Ke2=10, Be1=9, Rc1=9, Rh1=9\";", "6k1/5b1p/6p1/r1P5/3pNK2/5P1P/p5P1/R7 b - - bm Kf8; id \"STS(v11.0) King Activity.070\"; c0 \"Kf8=10, h6=6, Kg7=4, Rb5=6\";", "6k1/8/p2Br1r1/2P2p1p/1P6/7P/1P1R2PK/8 w - - bm Kg1; id \"STS(v11.0) King Activity.071\"; c0 \"Kg1=10, Rc2=4, Rf2=2\";", "6r1/1b1k4/2p2p2/ppB1p3/2p1P2P/P2n4/1P1R2P1/5K1R b - - bm Ke6; id \"STS(v11.0) King Activity.072\"; c0 \"Ke6=10, Kc7=4, Ke8=2\";", "6r1/pb3pk1/1p2p3/1Bp1P3/2Pb4/P7/4RPPR/6K1 b - - bm Kf8; id \"STS(v11.0) King Activity.073\"; c0 \"Kf8=10, a6=8, Ba8=8, Rb8=7\";", "6rk/q2p3p/4pp2/1P1n3P/2R5/p4NP1/P4P2/1Q4K1 w - - bm Kg2; id \"STS(v11.0) King Activity.074\"; c0 \"Kg2=10, Nd4=4, Rd4=4\";", "7k/1b1n1pp1/p2Ppq1p/1p5Q/1P1N3P/P3P3/B5P1/6K1 b - - bm Kg8; id \"STS(v11.0) King Activity.075\"; c0 \"Kg8=10, Kh7=6, Nf8=1\";", "7k/1q5p/5prP/1p1pr3/1PbR1QPK/2N3P1/2P5/3R4 w - - bm Kh3; id \"STS(v11.0) King Activity.076\"; c0 \"Kh3=10, R1d2=8, Ra1=9, Rb1=8\";", "7k/3N1p2/p7/3p1rp1/8/1P2P2p/P4n2/2R2NK1 w - - bm Kh2; id \"STS(v11.0) King Activity.077\"; c0 \"Kh2=10, Ng3=6, Rc2=5, Rc8+=6\";", "7k/3r1p2/p1r1p2p/1q1n3P/1PNRQP2/P7/K7/2R5 w - - bm Kb2; id \"STS(v11.0) King Activity.078\"; c0 \"Kb2=10\";", "7r/5p2/3k4/1p1p4/rPnP3P/4P3/4RPN1/1R5K w - - bm Kh2; id \"STS(v11.0) King Activity.079\"; c0 \"Kh2=10, Kg1=7, Nf4=5, Rb3=4\";", "7r/r1p2k1p/1p5B/1bp5/4pPP1/1P2Nn1P/PR3R2/7K b - - bm Kg6; id \"STS(v11.0) King Activity.080\"; c0 \"Kg6=10, Ne1=2, Ra3=2, Rd8=1, Rha8=2\";", "8/1k1r3p/pp3p1R/2pn4/4r3/N1P5/PP5P/2KR4 b - - bm Kc7; id \"STS(v11.0) King Activity.081\"; c0 \"Kc7=10, b5=4, Ree7=4\";", "8/1p2bk2/r7/p2P4/2P5/4B1R1/PP4Pn/1K6 w - - bm Kc2; id \"STS(v11.0) King Activity.082\"; c0 \"Kc2=10, Bd2=2, Bd4=7, Bg1=8\";", "8/2kr3p/5p2/pppn4/4r3/1PP4R/P1N4P/2KR4 b - - bm Kc6; id \"STS(v11.0) King Activity.083\"; c0 \"Kc6=10, c4=4, Re2=5\";", "8/3k3p/2p2p2/2B5/8/1r1b1P1P/3R1KP1/8 b - - bm Ke6; id \"STS(v11.0) King Activity.084\"; c0 \"Ke6=10, Kc7=7, Ke8=7\";", "8/4q1k1/5pp1/pp2b3/2p1P3/P1P1Q1Pp/7P/3R2K1 w - - bm Kf1; id \"STS(v11.0) King Activity.085\"; c0 \"Kf1=10, Qd2=3, Rd5=4\";", "8/4r1n1/1k5p/1p1prp1P/1PpN2p1/p1K1P1R1/P1B3PR/8 w - - bm Kd2; id \"STS(v11.0) King Activity.086\"; c0 \"Kd2=10, Bb1=9, Rh1=9, Rh4=9\";", "8/4r2p/2k2p2/1p1nr3/1Pp5/2P2R2/2NR3P/1K6 b - - bm Kb6; id \"STS(v11.0) King Activity.087\"; c0 \"Kb6=10, f5=6, Rg7=5, Rh5=6\";", "8/p1r1rk1p/2R1pb2/P4p2/1N4p1/1Pp1P1P1/5P1P/2R3K1 w - - bm Kf1; id \"STS(v11.0) King Activity.088\"; c0 \"Kf1=10, a6=7, Kg2=7, Rd6=7, Rxc7=6\";", "8/p3p1k1/1p1p1pp1/4n3/3R3P/1Pr5/P5P1/3R2K1 b - - bm Kh6; id \"STS(v11.0) King Activity.089\"; c0 \"Kh6=10, a5=8, f5=8, Rc2=6\";", "8/pQ3qk1/P2pp2p/2p2n2/2P2PB1/3P3P/8/7K b - - bm Kf6; id \"STS(v11.0) King Activity.090\"; c0 \"Kf6=10, h5=7, Kf8=7, Ng3+=6\";", "8/pr3pbk/6p1/7p/R7/2p1P1P1/2R2P1P/6K1 w - - bm Kf1; id \"STS(v11.0) King Activity.091\"; c0 \"Kf1=10, Kg2=4, Ra5=4\";", "8/R2n4/pr1k2p1/4pp1p/5P2/1N2P1PP/Pn2B1K1/8 w - - bm Kf1; id \"STS(v11.0) King Activity.092\"; c0 \"Kf1=10, Bxa6=3, Kf2=4, Kh2=3\";", "b2q2r1/5p2/2prpkp1/1pN5/p2P1PP1/P3PQ1p/1PB4P/2R3K1 b - - bm Kg7; id \"STS(v11.0) King Activity.093\"; c0 \"Kg7=10, Ke7=4, Qa5=6, Re8=5, Rh8=4\";", "br3rk1/2q1bpp1/pnnpp2p/1p3P2/4P3/PNN1BBQ1/1PPR2PP/1R4K1 b - - bm Kh7; id \"STS(v11.0) King Activity.094\"; c0 \"Kh7=10, Bf6=4, Kh8=8\";", "r1b3qr/3kppb1/p2p4/2pPn1B1/1pP1P3/1N5P/1P2BQP1/1R3RK1 b - - bm Ke8; id \"STS(v11.0) King Activity.095\"; c0 \"Ke8=10\";", "r2r2k1/p4pp1/b6p/8/1pp1Pn2/4NP2/PPB3PP/3RR1K1 w - - bm Kf2; id \"STS(v11.0) King Activity.096\"; c0 \"Kf2=10, g3=4, Nd5=2\";", "r2r4/5qbk/2p3pp/pp2p3/Pn1P1p1Q/4P2N/1P1N1PPP/2RR2K1 b - - bm Kg8; id \"STS(v11.0) King Activity.097\"; c0 \"Kg8=10, Bf6=7, Qe8=2, Qf6=6\";", "r4b1r/pp1k1pp1/4bn1p/3p4/4p3/1NN3P1/PPP1PPBP/3R1RK1 b - - bm Kc7; id \"STS(v11.0) King Activity.098\"; c0 \"Kc7=10, Kc6=7, Ke8=7\";", "r5k1/4pp1p/6p1/3q4/1P6/r1P2P2/PKR1Q1PP/R7 w - - bm Kc1; id \"STS(v11.0) King Activity.099\"; c0 \"Kc1=10, Kb1=3, Kb1=9\";", "R7/2k5/2p2p2/4p3/1pp1P1b1/n7/6P1/2R2K2 b - - bm Kb6; id \"STS(v11.0) King Activity.100\"; c0 \"Kb6=10\";", "1k1r4/4bp2/p1q1pnr1/6B1/NppP3P/6P1/1P3P2/2RQR1K1 w - - bm Re5; id \"STS(v12.0) Center Control.001\";", "1k5r/1pq1b2r/p2p1p2/4n1p1/R3P1p1/1BP3B1/PP1Q3P/1K1R4 w - - bm Bd5; id \"STS(v12.0) Center Control.002\";", "1kb4r/1p3pr1/3b1p1p/q2B1p2/p7/P1P3P1/1P1Q2NP/K2RR3 b - - bm Be5; id \"STS(v12.0) Center Control.003\";", "1kr5/1b3ppp/p4n2/3p4/2qN1P2/2r2B2/PQ4PP/R2R3K b - - bm Ne4; id \"STS(v12.0) Center Control.004\";", "1n1r4/p1q2pk1/b2bp2p/4N1p1/3P1P2/1QN1P3/5PBP/1R5K w - - bm Ne4; id \"STS(v12.0) Center Control.005\";", "1n1rr1k1/1pq2pp1/3b2p1/2p3N1/P1P5/P3B2P/2Q2PP1/R2R2K1 w - - bm Ne4; id \"STS(v12.0) Center Control.006\";", "1n1rr1k1/5pp1/1qp4p/3p3P/3P4/pP1Q1N2/P1R2PP1/1KR5 w - - bm Ne5; id \"STS(v12.0) Center Control.007\";", "1r1r1bk1/1bq2p1p/pn2p1p1/2p1P3/5P2/P1NBB3/1P3QPP/R2R2K1 b - - bm Nd5; id \"STS(v12.0) Center Control.008\";", "1r2qrk1/2p3pp/2Qb1p2/2p1pP2/8/BP6/3P1PPP/R4RK1 w - - bm Qe4; id \"STS(v12.0) Center Control.009\";", "1r2qrk1/4n3/ppbp3p/n1p1p1p1/2P5/B1PP2PP/Q2N1PB1/1R2R1K1 w - - bm Ne4; id \"STS(v12.0) Center Control.010\";", "1r3b2/3R2pk/6q1/3Q1p2/p6P/4B2P/1P3P1K/8 w - - bm Bd4; id \"STS(v12.0) Center Control.011\";", "1r3r1k/6pp/p2p4/q3pb2/2P4P/1P2Q1P1/P4PB1/R4K1R w - - bm Bd5; id \"STS(v12.0) Center Control.012\";", "1r3r1k/7p/p1p1q3/1pP1P3/3P4/Pb6/3Q2BP/2R1R1K1 b - - bm Bd5; id \"STS(v12.0) Center Control.013\";", "1r4k1/p1rnpp2/p2p1bp1/2qP4/4PQ2/1PN4P/P2B2P1/1R3R1K b - - bm Qd4; id \"STS(v12.0) Center Control.014\";", "1rb2rnk/2qn4/pp1p3p/2pP1p2/PPPbp2B/1QN5/R2NBPPP/1R4K1 b - - bm Ne5; id \"STS(v12.0) Center Control.015\";", "2b2q1k/2p4p/1rNn1p2/1p1P4/p3p3/P1P5/1P1Q2PP/1B1R2K1 w - - bm Qd4; id \"STS(v12.0) Center Control.016\";", "2b3k1/1q3pp1/p2R3p/8/Q7/2P1r3/6PP/R5K1 w - - bm Qd4; id \"STS(v12.0) Center Control.017\";", "2br4/2q2k1p/p2bp1p1/1r6/4BP2/2p1B3/P3Q1PP/2R2R1K w - - bm Bd4; id \"STS(v12.0) Center Control.018\";", "2q1rbk1/1b3p2/p4Pp1/1p1p3p/3B4/1P1Q1N1P/1P3PP1/R5K1 w - - bm Ne5; id \"STS(v12.0) Center Control.019\";", "2r1r1k1/p1qn1pp1/1p3n1p/3p4/3P3B/P1NQ4/1P3PPP/2RR2K1 b - - bm Ne4; id \"STS(v12.0) Center Control.020\";", "2r1rbk1/pp2p2p/6p1/n4p2/1NbP4/4PBBP/P4PP1/2RR2K1 w - - bm Nd5; id \"STS(v12.0) Center Control.021\";", "2r2k2/p1q1bpp1/2p2n2/2Np3r/1P5p/P1Q1B2P/5PP1/2R1R1K1 w - - bm Bd4; id \"STS(v12.0) Center Control.022\";", "2r2rk1/1p3ppp/p2q1n2/3pn3/N7/4P2P/PP2BPP1/2RQ1RK1 w - - bm Qd4; id \"STS(v12.0) Center Control.023\";", "2r2rk1/1pp2qpb/1n2p2p/pP1p1P2/P2PPBP1/7P/3NQ3/R1R3K1 w - - bm Be5; c0 \"Be5=10, Bg3=4, Ra3=4, Rf1=4\"; id \"STS(v12.0) Center Control.024\";", "2r3k1/1b2q1pp/1pr1p3/4B3/2P1p3/4P3/4QPPP/1R1R2K1 w - - bm Rd4; c0 \"Rd4=10, h3=2, h4=2, Qg4=2\"; id \"STS(v12.0) Center Control.025\";", "2r3k1/p2npp2/p2p1bp1/3P4/1qN1PQ1P/1P2B3/r5P1/1R3R1K b - - bm Ne5; c0 \"Ne5=10, a5=7, Bd4=10Qb8=6, Rc7=6\"; id \"STS(v12.0) Center Control.026\";", "2r3k1/ppr2p2/4b2p/4b1p1/P7/2N1P3/1PB2PPP/2RRK3 w - - bm Be4; c0 \"Be4=10, Bd3=6, h3=6, Rd3=6\"; id \"STS(v12.0) Center Control.027\";", "2r4k/5p1p/1prp1q1P/2n1p1p1/p3P1P1/P3BP2/1PP5/1KRR3Q w - - bm Rd5; c0 \"Rd5=10, Qg2=3, Qh3=7, Qh5=5, Re1=6, Rf1=7\"; id \"STS(v12.0) Center Control.028\";", "2r5/1b2kp1p/3qp1p1/1B6/3pn2N/8/PP2QPPP/4R1K1 b - - bm Qd5; c0 \"Qd5=10, Nf6=4, Qf4=5, Rc5=4\"; id \"STS(v12.0) Center Control.029\";", "2r5/1bq2k2/p2pp2r/5p2/2PN1Pp1/1PB1Q3/P5PP/4R1K1 b - - bm Be4; c0 \"Be4=10, g3=1, Qc5=1, Qd7=2\"; id \"STS(v12.0) Center Control.030\";", "2rq1k1r/pp3p2/7p/1BPp1bp1/1P6/P1N1P3/3Q1PPP/2R1K2n w - - bm Qd4; c0 \"Qd4=10, Ke2=4, Kf1=5, Nxd5=5\"; id \"STS(v12.0) Center Control.031\";", "2rq1rk1/p3b1pp/2p1p3/8/P2Pp3/1P1bP1P1/3B3P/2RQRBK1 b - - bm Qd5; c0 \"Qd5=10, Bxf1=1, e5=1\"; id \"STS(v12.0) Center Control.032\";", "2rq1rk1/pb2bppp/1p2pn2/4N3/3P1B2/2PB4/P4PPP/2RQR1K1 b - - bm Qd5; c0 \"Qd5=10, Ba3=7, Nd5=7, Rc7=6\"; id \"STS(v12.0) Center Control.033\";", "2rq1rk1/pb3ppp/1p1p1b2/2n1p3/P1B1P3/1P3N2/2P1RPPP/R1BQ2K1 w - - bm Bd5; c0 \"Bd5=10, Bb2=6, Nd2=3, Rb1=5\"; id \"STS(v12.0) Center Control.034\";", "2rr4/p1N1pk1p/1p4pb/n4p2/2bP4/4PB1P/P1R2PPB/3R2K1 w - - bm d5; c0 \"d5=10, Be5=4, Rdc1=5\"; id \"STS(v12.0) Center Control.035\";", "3r1bk1/3q1p2/4bP1p/pp4pP/2pP4/2P3B1/P1Q3P1/4RRK1 w - - bm Re5; c0 \"Re5=10, a3=1, Re3=4, Rf3=3\"; id \"STS(v12.0) Center Control.036\";", "3r1qk1/6pp/1p1np3/2n4Q/p7/P3N1P1/1B2PP1P/3R2K1 w - - bm Rd4; c0 \"Rd4=10, Bd4=6, f3=3, Nc4=5, Qg4=4, Qh4=3\"; id \"STS(v12.0) Center Control.037\";", "3r1r2/2n3kp/1pp1p1p1/p2nP3/P7/2NR2PB/1P3P1P/3R2K1 w - - bm Ne4; c0 \"Ne4=10, f4=4, Rd4=3\"; id \"STS(v12.0) Center Control.038\";", "3r1r2/2p3k1/1p3b1p/p7/2P1qP1P/1P1R2P1/P4P2/1Q1R2K1 b - - bm Bd4; c0 \"Bd4=10, Rde8=2, Rf7=2, Rxd3=2\"; id \"STS(v12.0) Center Control.039\";", "3r2k1/1p1b1pp1/p1q4p/8/2nNRB2/2P3P1/P4P1P/4Q1K1 b - - bm Qd5; c0 \"Qd5=10, Qb6=2, Qc5=3, Qc8=2\"; id \"STS(v12.0) Center Control.040\";", "3r2k1/3r1pp1/2QP4/pP5p/1qN5/4R1P1/4PK1P/8 w - - bm Re4; c0 \"Re4=10, Rd3=1\"; id \"STS(v12.0) Center Control.041\";", "3r2k1/3r2p1/2q1p2p/1p1n1p2/p7/P3RQ1P/BPP2PP1/3R2K1 w - - bm Re5; c0 \"Re5=10, h4=3, Rd2=3, Rde1=2, Ree1=1\"; id \"STS(v12.0) Center Control.042\";", "3r2r1/BpqP1pkp/6p1/3Qb3/8/2p3P1/PP1R1PBP/5K2 w - - bm Bd4; c0 \"Bd4=10, Bb6=3, bxc3=7, Rd1=5\"; id \"STS(v12.0) Center Control.043\";", "3r4/1p4kp/p1b3p1/2q1p3/P1P5/1B3P1P/4Q1P1/1R5K b - - bm Rd4; c0 \"Rd4=10, a5=3, b6=4, Kf6=4, Kf7=4\"; id \"STS(v12.0) Center Control.044\";", "3rbk2/4np1p/1p2p3/pP2r1p1/P1R1N1P1/3BP3/3K3P/3R4 b - - bm Nd5; c0 \"Nd5=10, Bd7=3, h6=4, Kg7=4\"; id \"STS(v12.0) Center Control.045\";", "4k3/p1p3p1/1pbp2qp/n3p3/2P3PB/2PPQ2P/P3P1B1/6K1 w - - bm Bd5; c0 \"Bd5=10, Bf1=5, Bf2=5, Bxc6+=7\"; id \"STS(v12.0) Center Control.046\";", "4r1k1/1b3pp1/p2pqb2/1p4r1/4PR2/PNN4P/1PP2QP1/5R1K w - - bm Nd4; c0 \"Nd4=10, Nd5=1, Qb6=3\"; id \"STS(v12.0) Center Control.047\";", "4r1k1/1pb1qp2/p1b3p1/3p1rN1/P4P1p/1PPRB1P1/3Q3P/3R2K1 w - - bm Bd4; c0 \"Bd4=10, Bf2=4, Nf3=3, Re1=4, Rf1=4\"; id \"STS(v12.0) Center Control.048\";", "4r1k1/3r1p1p/3p2p1/3P2Q1/4PR2/2n2B1P/5qPK/R7 b - - bm Qd4; c0 \"Qd4=10, Qd2=1, Qe3=1, Ra8=6, Rb7=1\"; id \"STS(v12.0) Center Control.049\";", "4r1k1/4r1pp/p7/3n4/2p1P3/2P1BPN1/P1P4P/1K2R3 w - - bm Bd4; c0 \"Bd4=10, Bd2=7, Kc1=7, Nf5=7\"; id \"STS(v12.0) Center Control.050\";", "4r1k1/p5b1/P2p1pp1/q1pP3p/2Pn1BbP/2NP2P1/3Q2BK/1R6 w - - bm Ne4; c0 \"Ne4=10, Be5=7, Kh1=6, Nb5=10Rb7=7\"; id \"STS(v12.0) Center Control.051\";", "4r1k1/pb2qp1p/4n2P/3p1pP1/1P1N1Q2/2N5/P1P5/5R1K w - - bm Qe5; c0 \"Qe5=10, Qd2=1\"; id \"STS(v12.0) Center Control.052\";", "4r1k1/R3rpp1/1p3n1p/8/2Q5/4B2P/1q3PP1/3R2K1 w - - bm Bd4; c0 \"Bd4=10, Raa1=3, Rxe7=2\"; id \"STS(v12.0) Center Control.053\";", "4r2k/1q4p1/p2Bp2p/P3P3/1PpN2P1/2Pb2b1/Q7/K2R4 b - - bm Qe4; c0 \"Qe4=10, Qa8=2, Qd7=1, Qf7=1\"; id \"STS(v12.0) Center Control.054\";", "4r3/p4pk1/3p1n1p/1P4p1/4q3/1P4P1/1N1QP2P/4R1K1 b - - bm Qe5; c0 \"Qe5=10, d5=4, Qf5=4, Rb8=4\"; id \"STS(v12.0) Center Control.055\";", "5n2/kbr3p1/5n1p/Pp2pP2/4P1PP/2p5/P1Br4/2RNRNK1 b - - bm Rd4; c0 \"Rd4=10, Ka6=5, Rd8=2, Rdd7=2\"; id \"STS(v12.0) Center Control.056\";", "5r1k/p1r3np/1p3q2/2p4P/2Q2P2/8/PPB5/1K1R1R2 w - - bm Rd5; c0 \"Rd5=10, f5=2, Rfe1=2, Rg1=2\"; id \"STS(v12.0) Center Control.057\";", "5rk1/p3pp1p/2Q2bp1/q1P5/3p4/5BP1/Pr2PP1P/2RR2K1 w - - bm Qe4; c0 \"Qe4=10, a4=6, h4=6, Kg2=3\"; id \"STS(v12.0) Center Control.058\";", "5rk1/pb3ppp/1p3q2/2n5/2B1p3/2P5/P3QPPP/R2R2K1 w - - bm Rd4; c0 \"Rd4=10, a4=4, Bd5=5, Qe1=4, Qe3=5, Rac1=4\"; id \"STS(v12.0) Center Control.059\";", "5rk1/qbrpnppp/p3p3/P2nP3/Np6/1N1B4/1PPQ1PPP/3RR1K1 w - - bm Re4; c0 \"Re4=10, c3=2, c4=2, g3=2\"; id \"STS(v12.0) Center Control.060\";", "5rqk/3b2pp/1pnNp3/3r4/6P1/PQ2B1RP/5PK1/3R4 b - - bm Ne5; c0 \"Ne5=10, Na5=7, Ne7=7, Rxd1=6\"; id \"STS(v12.0) Center Control.061\";", "6k1/1p2p3/2n1P2p/p3b3/2P2p2/1P4qP/P7/1B3QBK w - - bm Be4; c0 \"Be4=10, Bf2=4, Qg2=2\"; id \"STS(v12.0) Center Control.062\";", "6k1/1p3pp1/p3q2p/3p3P/1Pn2Q2/P3PP2/4NKP1/8 w - - bm Qd4; c0 \"Qd4=10, g4=2\"; id \"STS(v12.0) Center Control.063\";", "6k1/4qp2/p6p/1p6/2n2p2/P4Q2/1P4PP/6BK b - - bm Qe5; c0 \"Qe5=10, Qd6=3, Qf6=4, Qg5=1\"; id \"STS(v12.0) Center Control.064\";", "6k1/5p2/p3b2p/1pp4q/4Pp2/1P3P2/P1Q3PP/3B1K2 b - - bm Qe5; c0 \"Qe5=10, c4=2, f6=1, Kh8=3\"; id \"STS(v12.0) Center Control.065\";", "7k/1qr2p2/p3p2p/P1b1P1p1/2Q1n3/1p2BN1P/1P3PP1/2R3K1 w - - bm Nd4; c0 \"Nd4=10, Bd4=1, Kf1=1, Kh2=1\"; id \"STS(v12.0) Center Control.066\";", "8/2p1r1pk/2pq2bp/2p5/2P3Q1/pP1P2NP/P3PRK1/8 b - - bm Qe5; c0 \"Qe5=10, Qd8=7, Re5=7, Re6=7\"; id \"STS(v12.0) Center Control.067\";", "8/3n1kpp/1pr1p3/2n5/pR6/P3NPP1/1B2PK1P/8 w - - bm Bd4; c0 \"Bd4=10, f4=4, Nc4=4, Ng4=4\"; id \"STS(v12.0) Center Control.068\";", "8/6k1/1b1p1pp1/1b1Pp3/2q1P3/6P1/2B2PKN/3Q4 b - - bm Qd4; c0 \"Qd4=10, Bc5=4, Bd4=4, Qa2=4\"; id \"STS(v12.0) Center Control.069\";", "8/pp2r1kp/2prP3/5R2/2P5/3n2P1/P5BP/1R5K w - - bm Be4; c0 \"Be4=10, Bd5=6, Bf1=7, Rb3=7, Rf3=7\"; id \"STS(v12.0) Center Control.070\";", "b4rk1/8/4pr1p/2q5/P4p2/2PB4/6PP/R3QR1K w - - bm Be4; c0 \"Be4=10, Be2=2, Qe4=5, Rb1=4, Rd1=3, Rg1=2\"; id \"STS(v12.0) Center Control.071\";", "brr1n1k1/4bpp1/q2p4/Np2n3/1P1RP2p/2N4P/2P3PB/3RQB1K w - - bm Nd5; c0 \"Nd5=10, Be2=1, Qd2=1, Ra1=1\"; id \"STS(v12.0) Center Control.072\";", "k2r4/2q3p1/p1Pr1p2/P1R1p2P/5pP1/8/1PQp1P2/1K1R4 b - - bm Rd4; c0 \"Rd4=10, Ka7=6, Kb8=6, Rd3=6\"; id \"STS(v12.0) Center Control.073\";", "nr2q1k1/1p2rpb1/p2p2pp/P7/1PRN4/4BBPb/3Q1P1P/2R3K1 w - - bm Bd5; c0 \"Bd5=10, Kh1=5, Ne2=6, R4c2=5\"; id \"STS(v12.0) Center Control.074\";", "r1q1r1k1/1p2b1pp/2p1P1b1/3p1p2/p2N1B2/P1Q1PP1P/1P4P1/2RR2K1 w - - bm Be5; c0 \"Be5=10, Kf2=1, Kh2=2, Rd2=2\"; id \"STS(v12.0) Center Control.075\";", "r1q3k1/p2rnpb1/1pnBb1pp/2p1P3/6P1/2N2N1P/PP2QPB1/R2R2K1 w - - bm Qe4; c0 \"Qe4=10, Qe3=4, Rac1=5, Rd2=5\"; id \"STS(v12.0) Center Control.076\";", "r1r3k1/pb3p1p/1pqBp1p1/4P3/3b4/2P2P2/PR1N2PP/2RQ3K w - - bm Ne4; c0 \"Ne4=10, Qe2=3, Rbb1=1, Rbc2=1\"; id \"STS(v12.0) Center Control.077\";", "r2q1rk1/pp3pbp/3P2p1/4nb2/2p2N2/4B1P1/PP3PBP/R2Q1RK1 w - - bm Qd5; c0 \"Qd5=10, b3=2, Rc1=2, Re1=1\"; id \"STS(v12.0) Center Control.078\";", "r2q3r/1p3pk1/2b1p1p1/2R1P2p/p2P1P1R/P1Q1N1P1/1P3K2/8 b - - bm Be4; c0 \"Be4=10, Rc8=4\"; id \"STS(v12.0) Center Control.079\";", "r2qk2r/3nbpp1/2bBp2p/p3P3/1p6/8/PPPQBPPP/1NKR3R w kq - bm Qd4; c0 \"Qd4=10, f4=5, Qe3=7, Rhg1=5\"; id \"STS(v12.0) Center Control.080\";", "r2r2k1/p2n1p2/4q2p/3p2p1/1PpB4/P1NnPP2/2Q3PP/R2R2K1 b - - bm N7e5; c0 \"N7e5=10, a5=6, a6=6, Nb8=5\"; id \"STS(v12.0) Center Control.081\";", "r2r2k1/pN3p1p/2n1pp2/4q3/2P1P3/1Q6/P4PPP/R4RK1 b - - bm Rd4; c0 \"Rd4=10, Rd2=6, Rd7=7, Rdb8=6\"; id \"STS(v12.0) Center Control.082\";", "r3k2r/2qb1ppp/p3p3/2PpP3/2p2P2/P1P5/4B1PP/1R1Q1RK1 w kq - bm Qd4; c0 \"Qd4=10, c6=7, Rb2=7, Rb6=7\"; id \"STS(v12.0) Center Control.083\";", "r3kb1r/3n1ppp/p1bPp3/1q6/Npp1BB2/8/PP3PPP/2RQR1K1 w kq - bm Qd4; c0 \"Qd4=10, b3=2, Bxc6=4, Qc2=3, Qf3=7\"; id \"STS(v12.0) Center Control.084\";", "r3q1k1/p3r1p1/3R3p/2p5/1p2P3/1P4P1/P1Q4P/4R1K1 b - - bm Re5; c0 \"Re5=10, Qb5=5, Qh5=5, Rc8=4\"; id \"STS(v12.0) Center Control.085\";", "r3qrk1/4bpp1/Rp2pn1p/2p1N3/3P4/2P1P1B1/4QPPP/3R2K1 b - - bm Ne4; c0 \"Ne4=10, cxd4=2, Nd5=1, Rxa6=1\"; id \"STS(v12.0) Center Control.086\";", "r3r1k1/1b2qpp1/p3pn1p/1pPp4/PP1Q3P/1B2PPB1/6P1/R3K2R w KQ - bm Be5; c0 \"Be5=10, axb5=3, Bd6=3, Qb2=3\"; id \"STS(v12.0) Center Control.087\";", "r3r1k1/pp2qpb1/1n2b1pp/8/5B2/1P3NP1/P4PBP/R2QR1K1 w - - bm Be5; c0 \"Be5=10, Bd6=6, Nd2=6, Rc1=2\"; id \"STS(v12.0) Center Control.088\";", "r3r3/2q1bp1k/2N3pp/p1p5/Pp2Q3/4P3/1P3PPP/2RR2K1 w - - bm Qd5; c0 \"Qd5=10, Nxe7=6, Qf3=5\"; id \"STS(v12.0) Center Control.089\";", "r4r1k/p5p1/1qp1bp1p/8/3N4/P1Q1P3/1P4PP/2R2RK1 b - - bm Bd5; c0 \"Bd5=10, Bd7=4, Bf7=5, Bg8=5\"; id \"STS(v12.0) Center Control.090\";", "r4rk1/1pp2qpb/1n2p2p/pP1p1P2/P2PPBP1/7P/3NQ3/R3R1K1 w - - bm Be5; c0 \"Be5=10, Bg3=3, Ra3=3, Rac1=2, Rec1=2, Rf1=1\"; id \"STS(v12.0) Center Control.091\";", "r4rk1/2pb2b1/np1p3p/3P1p2/1PP1pP1q/4B3/2QNN1PP/1R3RK1 w - - bm Bd4; c0 \"Bd4=10, Nd4=1, Rf2=3\"; id \"STS(v12.0) Center Control.092\";", "r4rk1/pb1p2pp/1q1Ppn2/2p5/2P2P2/3BB3/PP2Q1PP/R3K2R b KQ - bm Be4; c0 \"Be4=10, Ne4=6, Qxd6=6, Rab8=6\"; id \"STS(v12.0) Center Control.093\";", "r5k1/3pppbp/6p1/4n3/1q2P3/4B3/P4PPP/2RQ1K1R w - - bm Qd5; c0 \"Qd5=10, h4=1, Qc2=4\"; id \"STS(v12.0) Center Control.094\";", "r5k1/8/r6p/P1q1p3/5p2/2P5/6PP/R3QR1K w - - bm Qe4; c0 \"Qe4=10, Qb1=2, Rb1=2, Rd1=1\"; id \"STS(v12.0) Center Control.095\";", "r5k1/p2b1pbp/6p1/2p5/1nn5/2N3P1/PP1N1P1P/R1B2RK1 b - - bm Ne5; c0 \"Ne5=10, Nb6=2, Nc2=2\"; id \"STS(v12.0) Center Control.096\";", "r5r1/1pp4p/2bn1k1B/p1p1p3/5PP1/1P5P/P1P1R3/R3N1K1 b - - bm e4; c0 \"e4=10, exf4=3, Kg6=2, Nf7=2, Rae8=3\"; id \"STS(v12.0) Center Control.097\";", "r6k/pp1q1pp1/1n6/1pQp4/3Pr2p/P2NP2P/5PP1/2R2RK1 w - - bm Ne5; c0 \"Ne5=10, Qb4=3, Qc7=4, Rb1=4\"; id \"STS(v12.0) Center Control.098\";", "r6r/4kp2/1qbp1p1b/p3pP1N/1pB1P2p/8/PPP1Q1PP/1K2R2R b - - bm Qd4; c0 \"Qd4=10, Bg5=3, Qc5=3, Rac8=3\"; id \"STS(v12.0) Center Control.099\";", "r7/4kp2/3Rp2b/2p1P3/bp3P1P/rB2N3/P1P5/1K1R4 w - - bm Bd5; c0 \"Bd5=10, Nc4=2\"; id \"STS(v12.0) Center Control.100\";", "1k1r4/1p3p2/p1bq1p1p/4p3/3r1P1Q/P5P1/1PP1B2P/K2RR3 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.001\"; c0 \"e4=10, Qc5=7, Qe6=7, Qe7=7, Re8=7\";", "1k2r1r1/ppp1q1b1/nn1pp3/1N3p1p/1PP2P2/PQ2B1PB/4PK1P/3R2R1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.002\"; c0 \"e5=10, Rgf8=3\";", "1kr3r1/1p1nqp2/p2p4/3PpBp1/1PP5/5R1P/P3QP2/R5K1 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.003\"; c0 \"e4=10\";", "1q1r2k1/3n1bp1/1p2pp2/pNn5/P3PP2/1P2QB2/6NP/3R2K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.004\"; c0 \"e5=10, Be2=5, Nd4=6, Nd6=3, Rb1=7\";", "1q2rrk1/p5bp/2p1p1p1/3p4/5P2/4QBP1/PPP2R1P/1R4K1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.005\"; c0 \"e5=10, a6=1, Qc7=2, Rf7=1\";", "1qr1r1k1/5pp1/1p2p2p/1Qbn3b/2R5/3P1NPP/3NPPB1/1R4K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.006\"; c0 \"e4=10, d4=2, Ne4=2, Rbc1=1\";", "1qr1r3/5ppk/1p2p2p/1Qbn3b/2R5/3P1NPP/3NPPB1/1R5K w - - bm d4; id \"STS(v13.0) Pawn Play in the Center.007\"; c0 \"d4=10, e3=3, Kg1=3, Ne4=4\";", "1r1r2k1/1p3pp1/pNn1b2p/3p1q2/5B2/P7/1P1Q1PP1/2R1R1K1 b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.008\"; c0 \"d4=10, Kh7=1\";", "1r1r2k1/p1R2ppp/1p6/2n1p1q1/3P4/Q4PP1/PP2B2P/3R2K1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.009\"; c0 \"d5=10\";", "1r1rb1k1/2q1bp1p/3ppnp1/8/pn1NPP2/2N2R1P/1PP1B1P1/2QR2BK b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.010\"; c0 \"d5=10, e5=2, Nc6=2\";", "1r2r1k1/2qn1pb1/3p2pp/p1pP4/PpQ1PP2/1P4PP/1B4B1/3RR1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.011\"; c0 \"e5=10, Ba1=8, Bc1=8, Bxg7=9\";", "1r2r1k1/p2bp1b1/2p1nnpp/qpP2p2/3P3P/P1N1PBPN/1BQ2P2/1R2R1K1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.012\"; c0 \"d5=10, Na2=4, Rec1=4, Red1=4\";", "1r2r2k/5pp1/1n2qn1p/3p4/p1pR4/Q3PBNP/PP3PP1/3R2K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.013\"; c0 \"e4=10, Ne2=5, Nh5=4, Qc3=6\";", "1r3k1r/1q2bpp1/4pn2/1p4p1/p1pPP1P1/P3P2P/1PQ4R/K1R1BB2 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.014\"; c0 \"e5=10, Bg2=8, Bg3=8, d5=6, Qe2=8\";", "1r3rk1/1p1qbbp1/2n1pn1p/1Np5/2P5/2BPP1PB/2N1Q2P/1R3RK1 w - - bm d4; id \"STS(v13.0) Pawn Play in the Center.015\"; c0 \"d4=10, Bg2=1, Ne1=2\";", "1r3rk1/1p3ppp/pN2bn2/P1p5/4P3/2P5/1P2B1PP/R4RK1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.016\"; c0 \"e5=10, Bf3=2, Rad1=1, Rfe1=1\";", "1r4k1/1br3p1/1ppbp3/p2p1q1p/P1PP1Pp1/1P2P1P1/1Q3NBP/2RR2K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.017\"; c0 \"e4=10, cxd5=3, Nd3=6, Qa2=5, Qe2=9\";", "1r6/pp1rqpbk/6p1/P2Bpb2/2P4p/1QN1P1P1/P2R1PKP/3R4 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.018\"; c0 \"e4=10, Bh6=7, h3+=7, hxg3=6, Rbd8=7, Rdd8=7\";", "1rb1r1k1/2n1ppbp/pq1p2p1/1ppP4/2P1PP2/NP3B1P/P2B1QP1/2R1R1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.019\"; c0 \"e5=10, Kh1=1\";", "1rb2r1k/2q1bppp/p1pppn2/P7/4P3/1PNQ4/2P1BPPP/R1B3RK b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.020\"; c0 \"d5=10\";", "1rb2rk1/2q2pb1/3p1np1/1pnP2Bp/4P3/1pN2PN1/3QB1PP/1RR3K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.021\"; c0 \"e5=10, Be3=7, Kh1=7, Nxb5=7\";", "1rnr2k1/2Rb2bp/3p2p1/2pPpp2/1p5P/1P1PP1P1/3B1PK1/R4BN1 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.022\"; c0 \"e4=10, Bb5=9, Be8=9, h6=8\";", "1rnrbk2/2R3bp/3p2p1/1ppPpp2/7P/3PP1P1/1P1B1PK1/R2N1B2 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.023\"; c0 \"e4=10, Kf3=1\";", "1rr5/p2bppkp/3p1np1/p1p5/Pq1PP3/1P2QN1P/2P1NPP1/2R1R1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.024\"; c0 \"e5=10, dxc5=4, Rcd1=1, Red1=3\";", "2b1k1r1/3nppb1/2pp1npp/qp6/3PP2B/1RNB1N2/P1PQ1PPP/7K w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.025\"; c0 \"e5=10, h3=2\";", "2b1r1k1/pp2rppp/2p3q1/3p4/1P1P4/P1N1PP2/5QPP/4RRK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.026\"; c0 \"e4=10, Nd1=8, Qd2=8, Qe2=7, Rc1=8\";", "2br1r1k/4b1pp/pq2pn2/2p5/Pp2PN2/1B2BP2/1PQ2P1P/R5RK b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.027\"; c0 \"e5=10, a5=7, Qc6=7, Qd6=7\";", "2kr4/1p3p2/p1bqpp1p/8/3r1P1Q/3B2P1/PPP4P/K2RR3 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.028\"; c0 \"e5=10, Bf3=9, f5=7, Qe7=7\";", "2r1r1k1/1pqn1pp1/p2b1n1p/P2Ppb2/R7/2NNB3/1PPQBPPP/3R2K1 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.029\"; c0 \"e4=10, Bh7=6, Nf8=6, Qb8=7, Qd8=7\";", "2r1r1k1/3n2pp/b1p1pb2/1pPp1p1q/p2PnB1P/P4NP1/1PQNPPB1/R2R1K2 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.030\"; c0 \"e5=10, Bb7=1, h6=1, Kh8=1\";", "2r1r1k1/5npp/3q4/1QpP1p2/1p6/4PP2/1B2R1PP/2R3K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.031\"; c0 \"e4=10, h3=6, Qd3=8, Rec2=8, Ree1=7\";", "2r1r1k1/qp2ppb1/p1np1npp/5b2/2P5/PPN3PP/1BN1PPBK/1RQR4 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.032\"; c0 \"e4=10, e3=2, f3=5, Kg1=2, Rd2=3, Rf1=4\";", "2r2nk1/2q1pp1p/3p2p1/pr6/2P1PP2/1PR3PP/3Q2B1/3R2K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.033\"; c0 \"e5=10, cxb5=6, Qe2=7, Qe3=7, Re1=7\";", "2r2rk1/pp2qpp1/2nbp2p/2Np1b2/1P1Pn3/P3PN2/3BBPPP/R1RQ2K1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.034\"; c0 \"e5=10, a6=2, Nxd2=1, Rfd8=1, Rfe8=2\";", "2r3k1/4n1b1/1p1pq1p1/4p3/2r2P1P/P2NP3/1B1Q3P/4RRK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.035\"; c0 \"e4=10, fxe5=9, Nb4=9, Qg2=9\";", "2r3k1/4Qp1p/3p2p1/3q4/b1p5/P1b3P1/R3PP1P/1R3BK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.036\"; c0 \"e4=10\";", "2r4r/2qb1nk1/3ppp2/p1p5/P3P3/1PR3P1/3NRPK1/3Q1B2 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.037\"; c0 \"d5=10, Rh6=1, Rh7=1\";", "2rbr1k1/1p1nqp1p/4bn1P/pP2p1p1/N7/P1NRPP2/1Q2BBP1/1K5R w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.038\"; c0 \"e4=10, Bg3=2, Ka1=2\";", "2rq1rk1/6bp/p1npb3/1p1Npp1Q/8/2PBN3/PP3PPP/R3K2R b KQ - bm e4; id \"STS(v13.0) Pawn Play in the Center.039\"; c0 \"e4=10, b4=3, Bxd5=1, Kh8=2, Qd7=1\";", "2rqr2k/pp3pp1/1nb2n2/7p/3pPP2/1P1B2NP/P5P1/RQB2R1K w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.040\"; c0 \"e5=10, Ba3=3, Bb2=1, Re1=3\";", "2rrb1k1/1p3pp1/p1p4p/P5q1/2BP4/1P1RPQP1/5P2/3R1K2 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.041\"; c0 \"d5=10, Ra1=6, Rc3=6\";", "3qr1k1/1p2npb1/2ppb1pp/4p3/1PP5/R1NP2P1/2NQPPBP/6K1 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.042\"; c0 \"d5=10, f5=8, h5=2, Kh7=6, Qc7=9, Qd7=2, Rf8=2\";", "3r1k2/5p2/1p2q1p1/p2p4/P7/1P1Q3P/4nPP1/B2R3K b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.043\"; c0 \"d4=10, f5=8, Nf4=8, Rd7=8\";", "3r1r2/1b2qpkp/p2p1np1/1pp5/4P3/P1N1QB2/1PP2PPP/3R1RK1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.044\"; c0 \"e5=10, h3=7, Qc1=7, Rfe1=7\";", "3r1rk1/ppqnppb1/n1p3pp/8/N2P4/3QP1P1/PP1B1PBP/R4RK1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.045\"; c0 \"e5=10, Ndb8=8, Nf6=8, Rfe8=8\";", "3r2k1/1pp1q2p/p5pb/2n1p2r/5PQ1/P1PP3P/1P5K/1BBR1R2 w - - bm d4; id \"STS(v13.0) Pawn Play in the Center.046\"; c0 \"d4=10, Ba2+=1\";", "3r2rk/1q2b2p/pNb2p2/P4Qn1/1p1p4/4PP2/1P1BB2P/2R2R1K w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.047\"; c0 \"e4=10, b3=7, Bxb4=9, exd4=7, Rc4=7\";", "3rr1k1/1p3p1p/2b1pqp1/p3N3/2PP4/4R1P1/P2Q1P1P/4R1K1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.048\"; c0 \"d5=10, a4=4, Rd1=5, Rd3=5\";", "3rr1k1/pp4bp/2p3p1/P4p2/2qPP3/B1P4P/2P3P1/R3QRK1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.049\"; c0 \"e5=10, Bc5=1, Qg3=1, Rb1=1\";", "4b3/3k1n2/2pB1p1p/1p3Pp1/1P2P1P1/8/r3BK2/3R4 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.050\"; c0 \"e5=10, Bc5+=9, Bf8+=9\";", "4r1k1/1p1r1ppb/1qpp1n1p/p7/P1PRPN2/2Q2P2/1P4PP/1B1R2K1 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.051\"; c0 \"d5=10, Qa7=6, Rdd8=6, Red8=6\";", "4r1k1/1r2ppnp/1q4p1/ppp5/3nP3/1P2QP2/P2R1BPP/2R2B1K b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.052\"; c0 \"e5=10, Rbb8=3, Rc7=3, Rd8=5\";", "4r1k1/2qn1pb1/rn1p2pp/2p5/p1b1PPP1/R1N1B2P/1PP1NQB1/R6K b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.053\"; c0 \"d5=10, Qb8=4, Ra5=3, Ra7=3\";", "4r3/2qnr1k1/4ppp1/p1p4p/p1P2P1P/3PQNP1/1P2R3/4R1K1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.054\"; c0 \"e5=10, Qb7=7, Qc6=9, Rb8=9, Rd8=4\";", "4rrk1/p4p1p/1qb3p1/2pp4/8/1P3B2/P1PQ1PPP/R3R1K1 b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.055\"; c0 \"d4=10, Qb7=9, Rd8=9, Rxe1+=9\";", "5k2/4pp1p/r2p2p1/8/6P1/5P2/1R3B1P/2r2BK1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.056\"; c0 \"e5=10, f6=9, Ra8=4, Rac6=9\";", "5rk1/1ppqnr2/3p3p/1P4p1/3PPp2/5P2/P2QN2P/2R3RK w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.057\"; c0 \"d5=10\";", "5rk1/1r2bpp1/pp2qn1p/2p5/P7/2N1PPP1/1PQB2KP/2RR4 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.058\"; c0 \"e4=10, b3=1, Ne2=2\";", "5rk1/6p1/2pp4/4q1p1/1PP1PnP1/4Q2P/R7/5BK1 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.059\"; c0 \"d5=10, Kh8=5\";", "5rk1/rbqnbppp/1p2pn2/2p5/4P3/P1P2NPP/1B1N1P2/R2QRBK1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.060\"; c0 \"e5=10, Bb5=2, c4=1, Qc2=2\";", "r1b1kb1r/1p1ppppp/p2q2n1/2pN3Q/8/8/PPPP1PPP/R1B1RBK1 w kq - bm d4; id \"STS(v13.0) Pawn Play in the Center.061\"; c0 \"d4=10, b3=2, c4=3, g3=2, Ne3=1\";", "r1b1r1k1/4q1pp/1bp1pp2/np6/3PN3/3NP1PB/R1Q2P1P/3R2K1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.062\"; c0 \"e5=10, Kh8=2, Ra7=2, Rd8=2\";", "r1b1r1k1/ppq2pp1/2n1pn1p/8/2P5/PN4P1/1BP2PBP/R2Q1RK1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.063\"; c0 \"e5=10, a5=2, Rb8=2\";", "r1b1r3/pp1nq1kp/2pp2p1/5p2/1PP2P2/1Q2P1PP/P2N2B1/3R1RK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.064\"; c0 \"e4=10, Qb2+=2, Qc3+=3, Rfe1=4\";", "r1b2rk1/1p1n1p1p/p5p1/q2p2B1/1bpP4/2N1PN1P/PPQ2PP1/2R2RK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.065\"; c0 \"e4=10, Bf4=1, Rfe1=2\";", "r1b2rk1/1p3pb1/pq1Pp1p1/5n1p/2B2P2/1NP2Q2/PP1P2PP/R1B1K2R b KQ - bm e5; id \"STS(v13.0) Pawn Play in the Center.066\"; c0 \"e5=10, a5=7, Bd7=7, Nxd6=7, Qxd6=7\";", "r1b2rk1/p1B1bpp1/q3pn1p/3p4/8/2N3PP/PPQ1PPB1/R3R1K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.067\"; c0 \"e4=10, Be5=2, Rac1=2, Rad1=2\";", "r1b3k1/1p1n1qpp/2p1p3/3p1r2/2PP4/1PR5/2QNPPBP/5RK1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.068\"; c0 \"e4=10, b4=1, Rf3=1\";", "r1br2k1/1p3ppp/p1p5/P5q1/1bBP4/1P2P3/1BQ2PP1/R2R2K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.069\"; c0 \"e4=10, Bc3=7, Be2=7, Qe2=7\";", "r1q1nrk1/pp2ppbp/2n3p1/8/Q3P3/N5P1/PB3PBP/3RR1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.070\"; c0 \"e5=10, Bc1=8, Qb3=8, Qc2=8\";", "r1r1q1k1/1n1b2bp/p4pp1/1p6/2pNP2B/Q1P5/P1B2PPP/3RR1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.071\"; c0 \"e5=10, Bg3=5, h3=3, Qc1=4, Re2=2\";", "r1r3k1/1pp2ppp/2np1n2/1N6/p1PP4/Q3RNPq/PP3P1P/2R3K1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.072\"; c0 \"d5=10, Rce1=4, Re2=3, Ree1=2\";", "r2q1kr1/1b2np2/p2p1p1p/4p3/2N1P3/3B4/PP3PPP/1R1Q1RK1 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.073\"; c0 \"d5=10\";", "r2q1rk1/1p1n1ppp/6b1/n2B4/Ppp2NP1/4PP2/1P4KP/R1BQ1R2 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.074\"; c0 \"e4=10, Bd2=4, h4=4, Nxg6=3, Qe2=2\";", "r2q1rk1/4nbbp/p2p4/1p3p1Q/4pN2/1BP1N3/PP3PPP/R3K2R b KQ - bm d5; id \"STS(v13.0) Pawn Play in the Center.075\"; c0 \"d5=10, b4=9, Be5=7, Qe8=7\";", "r2qr1k1/1b3pb1/p2p2p1/8/2BNn1P1/2p1B3/P1P5/1NKRQR2 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.076\"; c0 \"d5=10\";", "r2r1k2/4q1p1/2b2p1p/ppP1pQ2/8/1P2PB2/5PPP/R1R3K1 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.077\"; c0 \"e4=10\";", "r2r2k1/1b2qppp/p3pn2/R7/2N5/1P1BP3/2Q2PPP/R5K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.078\"; c0 \"e4=10, f3=8, Ne5=2, Rd1=2\";", "r2r2k1/1bq2p1p/p3nnpQ/P2pp3/8/2PNN3/1P3PPP/R2R1BK1 b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.079\"; c0 \"d4=10, Ne8=1, Nh5=9, Rab8=6, Rac8=9, Rd7=1\";", "r2r2k1/6bp/1P2q1p1/4pp2/p1N5/2Q5/PPR3PP/4R2K b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.080\"; c0 \"e4=10, Qc6=6, Rab8=2, Rd7=4\";", "r2r2k1/p1q3pp/1p1npp2/2p5/P1PP4/B3PP2/6PP/2RQ1RK1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.081\"; c0 \"d5=10, dxc5=8, Qb3=8, Re1=7\";", "r2r2k1/pbq2ppp/1p3n2/4p3/2PP4/PB1Q4/1B3PPP/R4RK1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.082\"; c0 \"d5=10, f3=1, Rfd1=1\";", "r2r2k1/pp2ppbp/2q3p1/2pnP3/2Q5/1P4P1/PB2PPNP/R2R2K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.083\"; c0 \"e4=10, a4=3, f4=2, Ne1=2, Rac1=8\";", "r2r2k1/ppq1p2p/1np1p1p1/2B5/P7/3P3P/1P2QPP1/R3R1K1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.084\"; c0 \"e5=10, Qc8=7, Qd7=7, Rf8=7\";", "r2r2k1/ppq2pp1/2p1pnbp/8/3P3P/1QP2P2/P3P2P/R1B2BRK w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.085\"; c0 \"e4=10, Bd2=4, Be3=4, Rg2=4\";", "r2r2k1/ppq2pp1/5nnp/1B3b2/2PPp3/P6P/1B1NQPP1/2R2RK1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.086\"; c0 \"d5=10, Ba1=1, Ba4=2, Qe3=5\";", "r2r3k/p2qppbp/1pn2np1/2p5/2P2P2/B1N1PN2/P2PQ1PP/3R1R1K w - - bm d4; id \"STS(v13.0) Pawn Play in the Center.087\"; c0 \"d4=10, d3=1\";", "r2r4/2q1bp1k/1nb1p1pp/p1ppP3/P1P2P2/1P3BPP/1N2QB2/2RR2K1 b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.088\"; c0 \"d4=10, dxc4=2, Rab8=1\";", "r3kb1r/1q1n2p1/2bpp1Pp/pN3p2/1p1BP2P/P2B1P2/1PP5/2KR1Q1R b kq - bm e5; id \"STS(v13.0) Pawn Play in the Center.089\"; c0 \"e5=10\";", "r3r1k1/1p2bppp/nqp2n2/p5B1/2bPP3/P1N3P1/1P2N1BP/R1Q2RK1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.090\"; c0 \"e5=10, Bf3=7, Kh1=7, Rf5=7\";", "r3r1k1/1p2qpp1/1bp2n1p/2n1pP2/p5P1/B6P/PPPNQPB1/R2R2K1 b - - bm e4; id \"STS(v13.0) Pawn Play in the Center.091\"; c0 \"e4=10, Ba7=3, Bc7=6, Nd5=5, Qc7=5\";", "r3r1k1/4npbp/bqnpp1p1/2p3P1/1p2PP2/3PN3/1PPQNBBP/1R3RK1 b - - bm d5; id \"STS(v13.0) Pawn Play in the Center.092\"; c0 \"d5=10\";", "r3r1k1/5pp1/1qp3bp/p2pP3/8/P1Q3N1/1P3PPP/2R1R1K1 b - - bm d4; id \"STS(v13.0) Pawn Play in the Center.093\"; c0 \"d4=10\";", "r3r1k1/pp4pp/2pnqpn1/3p1b2/3P4/P1NBPN2/1PQ2PPP/2R1R1K1 w - - bm e4; id \"STS(v13.0) Pawn Play in the Center.094\"; c0 \"e4=10\";", "r4r1k/1bq3p1/pp1bpn1p/2pp4/3P3Q/1NP1B3/PP2BPPP/R4RK1 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.095\"; c0 \"e5=10, a5=5, Bc8=3, Kg8=3, Rac8=3, Rf7=2\";", "r4rk1/1p1qppbp/1n1p2p1/1PpP3n/p3P3/2NQ1N1P/1PPB1PP1/R3R1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.096\"; c0 \"e5=10, Bg5=3, Ra2=3, Ra3=2\";", "r4rk1/2p1bppp/p2p1n2/1p6/3PP3/1QN1BP1q/PP3P1P/2R1R1K1 w - - bm e5; id \"STS(v13.0) Pawn Play in the Center.097\"; c0 \"e5=10, Bf4=8, Nd5=7, Qd1=7\";", "r4rk1/3qbppp/p1np1n2/1pp4b/3PP3/PB2BN1P/1P1N1PP1/RQ2R1K1 w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.098\"; c0 \"d5=10, Bc2=1\";", "r4rk1/pbp5/1p1p1n1p/5Bp1/P1PP4/4q3/1P1N2PP/2RQ1R1K w - - bm d5; id \"STS(v13.0) Pawn Play in the Center.099\"; c0 \"d5=10, Bb1=4, Nf3=1\";", "rq3k2/1p3bp1/2p1pp1p/2P5/1P2PPP1/4Q2P/6BK/3R4 b - - bm e5; id \"STS(v13.0) Pawn Play in the Center.100\"; c0 \"e5=10, Kg8=8, Qa7=8, Qc7=8\";", "1k5r/1p1b4/4pp1r/3p4/q4PQ1/3B1R1P/2P1R1PK/8 w - - bm Qg7; id \"STS(v14.0) 7th Rank.001\"; c0 \"Qg7=10, Qg3=3, Re1=3, Ree3=2, Ref2=2\";", "1n1r1rk1/4bpp1/p2p3p/1q1Qp3/1P2P3/P3BN1P/5PP1/2R1R1K1 w - - bm Rc7; id \"STS(v14.0) 7th Rank.002\"; c0 \"Rc7=10, Nd2=6, Qxb5=8, Red1=6\";", "1n2r3/ppb2kp1/5p1p/2p2N1P/6P1/2B5/PPP2P2/2KR4 b - g3 bm Re2; id \"STS(v14.0) 7th Rank.003\"; c0 \"Re2=10, Bb6=7, Bf4+=7, Re4=7\";", "1r1n4/5pk1/Qp1r1qp1/3B3p/1R2P2P/6P1/5P2/1R4K1 w - - bm Qa7; id \"STS(v14.0) 7th Rank.004\"; c0 \"Qa7=10\";", "1r1q2k1/3b1pb1/p2p2p1/3Np3/4P1P1/1P1Q1P2/KP1B4/2R5 w - - bm Rc7; id \"STS(v14.0) 7th Rank.005\"; c0 \"Rc7=10, g5=5, Rc2=7, Rc3=5\";", "1r1r2k1/4pp2/1PR3p1/p2P1n1p/1p1q1Q2/5P1P/2B1RPPK/8 w - - bm Qc7; id \"STS(v14.0) 7th Rank.006\"; c0 \"Qc7=10, Qc1=1, Re4=1\";", "1r1r4/5ppk/4pb2/7p/PN3P2/1Rp3P1/2P3P1/4RK2 b - - bm Rd2; id \"STS(v14.0) 7th Rank.007\"; c0 \"Rd2=10, Be7=5, h4=6, Rd4=7\";", "1r1r4/6bk/p1Rp2p1/P6p/1q1pPB1P/5PP1/Q4K2/2R5 w - - bm Rc7; id \"STS(v14.0) 7th Rank.008\"; c0 \"Rc7=10, Bd2=7, Kg2=6, R1c4=6\";", "1r2r1k1/1b1n1ppp/1p2p3/1N1pP3/Pb3P2/4BB2/1PR3PP/R6K w - - bm Rc7; id \"STS(v14.0) 7th Rank.009\"; c0 \"Rc7=10, Bd2=2, Kg1=1, Nd6=3, Rac1=1\";", "1r2r2k/B1p2p1p/p2p2p1/4b3/n1P1P1P1/5B2/P4P1P/1R3RK1 b - - bm Rb2; id \"STS(v14.0) 7th Rank.010\"; c0 \"Rb2=10, Ra8=1, Rbc8=1, Rxb1=6\";", "1r3rk1/qn2n2p/3p1p2/pp1Pp1p1/1N2P3/QP5P/P1R2PP1/2R2NK1 w - - bm Rc7; id \"STS(v14.0) 7th Rank.011\"; c0 \"Rc7=10, Nc6=3, Ne3=2, Ng3=3, Rd1=6\";", "1r4k1/3b2pp/4p3/2bpq3/3N4/2PB4/P4QPP/5RK1 w - - bm Qf7+; id \"STS(v14.0) 7th Rank.012\"; c0 \"Qf7+=10, Kh1=8, Qc2=9, Re1=9\";", "1r4k1/6b1/2r1n2p/2pRp1p1/2P3P1/4BK2/R2N1P1P/8 w - - bm Ra7; id \"STS(v14.0) 7th Rank.013\"; c0 \"Ra7=10, Kg3=8, Ne4=3, Ra5=7, Rd7=8\";", "1r4k1/pp3p2/3p1B2/3P3p/2R5/7P/P1P3P1/2b4K w - - bm Rc7; id \"STS(v14.0) 7th Rank.014\"; c0 \"Rc7=10, Be7=8, g3=8, Kg1=4, Kh2=7\";", "1r4k1/qr1p2p1/2n1p2p/2b1P2P/2BpPB2/p2P4/3Q1PPK/R1R5 b - - bm Rb2; id \"STS(v14.0) 7th Rank.015\"; c0 \"Rb2=10, Bb4=5, Kh7=4, Rb6=4\";", "1r6/5pk1/Qp1rnqp1/3B3p/1R2P2P/6P1/5PK1/1R6 w - - bm Qa7; id \"STS(v14.0) 7th Rank.016\"; c0 \"Qa7=10, Qc4=6, Qe2=6, R4b2=6\";", "1rb1k2r/2qpbppp/p3pn2/1p2n3/3QP3/PNN1BP2/1PP3PP/2KR1B1R w k - bm Qa7; id \"STS(v14.0) 7th Rank.017\"; c0 \"Qa7=10, Be2=3, h4=3, Kb1=3, Na5=3\";", "1rbqr1k1/3n1pbp/p2Q2p1/2pN4/1pP1n3/5NPP/PP3PB1/1RB1R1K1 w - - bm Qc7; id \"STS(v14.0) 7th Rank.018\"; c0 \"Qc7=10, Qc6=2, Qf4=3\";", "2b4r/5kpp/p2q4/1p1p1P2/2n3P1/8/PPP2Q1P/1K1RR3 w - - bm Qa7+; id \"STS(v14.0) 7th Rank.019\"; c0 \"Qa7+=10, b3=3, g5=2, Qd4=2, Rd3=1\";", "2k5/p1pb1p2/1p4p1/2p1P3/2P2KPb/1P2N2r/PB1R1P2/8 b - - bm Rh2; id \"STS(v14.0) 7th Rank.020\"; c0 \"Rh2=10, a5=4, g5+=6, Rh1=4\";", "2qb1rk1/1brn1pp1/3p3p/1B1Pp3/4P1P1/R3BN1P/4QP1K/1R6 w - - bm Ra7; id \"STS(v14.0) 7th Rank.021\"; c0 \"Ra7=10, Bd3=7, Bxd7=9, Nd2=9, Qb2=7\";", "2r1kb1r/1b1p1ppp/p3pn2/qp6/3QPP2/2N1BB2/PPPR2PP/2K4R w k - bm Qa7; id \"STS(v14.0) 7th Rank.022\"; c0 \"Qa7=10, e5=2, Kb1=1, Rd3=3, Re1=3\";", "2r1N3/pp3p1k/2p3p1/2n5/7P/5KP1/PP3P2/4R3 w - - bm Re7; id \"STS(v14.0) 7th Rank.023\"; c0 \"Re7=10, b4=2, Nd6=2, Re5=2\";", "2r2k2/5p2/R1b2p2/1pP1p2p/1Pp1P2P/2K2P2/5BP1/8 w - - bm Ra7; id \"STS(v14.0) 7th Rank.024\"; c0 \"Ra7=10, Be3=5, Bg1=3, Kd2=3\";", "2r2r1k/3n3p/3q2p1/4p1Q1/Pp1bp1NN/1P2B2P/4nPP1/1R1R3K b - - bm Rc2; id \"STS(v14.0) 7th Rank.025\"; c0 \"Rc2=10, Qe6=3\";", "2r2rk1/4pp2/Qp3qp1/3B3p/3nP3/8/P4PPP/1R1R2K1 b - - bm Rc2; id \"STS(v14.0) 7th Rank.026\"; c0 \"Rc2=10, e6=3, h4=4, Kg7=3\";", "2r2rk1/p4ppp/bp2p3/3pB3/1b1PP1P1/qP2Q3/P4PBP/R1R3K1 b - - bm Qb2; id \"STS(v14.0) 7th Rank.027\"; c0 \"Qb2=10, f6=6, Rxc1+=2\";", "2r3k1/1qpbbppp/3p4/Qp1P4/3p4/1B1P4/1PPB1PPP/R5K1 w - - bm Qa7; id \"STS(v14.0) 7th Rank.028\"; c0 \"Qa7=10, f3=6, Qa6=8, Ra2=8\";", "2r3k1/3q2p1/3b1p1p/1p1PpP2/1Pp1B3/2P4P/Q5P1/R5K1 w - - bm Qa7; id \"STS(v14.0) 7th Rank.029\"; c0 \"Qa7=10, Kf2=6, Kh2=6, Qa6=7\";", "2r3k1/4bqp1/p3p3/1p5p/2n2P1P/P2Q1N2/1PP5/1KBR4 w - - bm Qd7; id \"STS(v14.0) 7th Rank.030\"; c0 \"Qd7=10, Ng5=4, Qe2=4, Re1=4, Rg1=4\";", "2r3k1/4np2/p3p1p1/1q1r1n1p/1p1P4/PN5R/1P2NPPP/3RQ1K1 b - - bm Rc2; id \"STS(v14.0) 7th Rank.031\"; c0 \"Rc2=10, bxa3=6, Rd6=7, Rdd8=8\";", "2r3k1/5pp1/p3n3/4p2p/1QP1P3/1PR2P2/P6q/1KB5 w - - bm Qb7; id \"STS(v14.0) 7th Rank.032\"; c0 \"Qb7=10, Qa3=8, Qa4=8, Qa5=8, Qd6=8, Rd3=8\";", "2r3k1/p1q1np2/2P1pp1p/P7/8/3Q1BPP/5PK1/2R5 w - - bm Qd7; id \"STS(v14.0) 7th Rank.033\"; c0 \"Qd7=10, Qa6=5, Qd2=5, Qd4=7\";", "3q1rk1/5b2/5Rp1/p1p1p1P1/2P1PP2/1P4Qp/4B2K/8 b - - bm Qd2; id \"STS(v14.0) 7th Rank.034\"; c0 \"Qd2=10, a4=8, Be8=8, exf4=7\";", "3q1rk1/5ppp/pp1p4/6b1/2rNQ3/6P1/PP2PP1P/R2R2K1 w - - bm Qb7; id \"STS(v14.0) 7th Rank.035\"; c0 \"Qb7=10, b3=4, e3=3, Qd5=4\";", "3q4/5ppk/2p1n2p/2n1pN1P/4P3/Q1P2PP1/6BK/8 w - - bm Qa7; id \"STS(v14.0) 7th Rank.036\"; c0 \"Qa7=10, Kg1=1, Kh1=1, Nh4=1, Qb2=1, Qb4=1\";", "3qr3/2p1kp2/1p4pQ/1P1P3p/7P/6P1/5P2/3R2K1 w - - bm Qg7; id \"STS(v14.0) 7th Rank.037\"; c0 \"Qg7=10, d6+=7, Kg2=3, Qd2=2, Rd4=2\";", "3qr3/5pkp/p1npb1p1/3N4/2PpPP2/6P1/PQ4BP/1R5K w - - bm Qb7; id \"STS(v14.0) 7th Rank.038\"; c0 \"Qb7=10, Qa3=2, Rf1=1\";", "3r1k2/1p3p1Q/p1br1p2/2p1pB2/2P1PP2/2q5/P5PP/1R2R1K1 b - - bm Rd2; id \"STS(v14.0) 7th Rank.039\"; c0 \"Rd2=10, Ke7=2, Qxc4=2, Rd3=2\";", "3r2k1/2R2pp1/p4n1p/1p3P2/1P6/5B1P/P5P1/6K1 b - - bm Rd2; id \"STS(v14.0) 7th Rank.040\"; c0 \"Rd2=10, Nd5=6, Rd3=8, Rd4=7\";", "3r2k1/6pp/1pq5/p2pR3/3Q4/P5P1/1P3P1P/6K1 w - - bm Re7; id \"STS(v14.0) 7th Rank.041\"; c0 \"Re7=10, b4=6, h4=4, Kg2=3, Re3=5\";", "3r2k1/p2n1p1p/2p1p1p1/P7/3P4/P2B4/5PPP/1R4K1 w - - bm Rb7; id \"STS(v14.0) 7th Rank.042\"; c0 \"Rb7=10, Bc2=8, Be2=8, Bf1=8, Kf1=8\";", "3r2k1/p3pp1p/6p1/2p5/2P1n3/RP2P1P1/4KP1P/B7 b - - bm Rd2+; id \"STS(v14.0) 7th Rank.043\"; c0 \"Rd2+=10, f6=3, Nd2=2, Rd7=3\";", "3r2k1/pp3bbp/3q1p2/2p5/5pQ1/1P3N2/P3R1PP/4R2K w - - bm Re7; id \"STS(v14.0) 7th Rank.044\"; c0 \"Re7=10\";", "3r2r1/3kp1P1/2p5/1q1pP3/1p6/1Pb2Q2/PN4R1/1K1R4 w - - bm Qf7; id \"STS(v14.0) 7th Rank.045\"; c0 \"Qf7=10, Nd3=8, Qe3=4, Rc1=5\";", "3r3k/p4p1p/b4Prp/1p2R3/1P1p4/6PP/P4PB1/3R2K1 w - - bm Re7; id \"STS(v14.0) 7th Rank.046\"; c0 \"Re7=10, Rd2=4, Re4=6, Rf5=7\";", "3r4/3r2bk/1p4pp/pN2n3/P6P/4Q1P1/1P2B1K1/8 b - h3 bm Rd2; id \"STS(v14.0) 7th Rank.047\"; c0 \"Rd2=10, h5=7\";", "3r4/3rq1k1/1R1p2pp/1Rp1p3/P3N3/2P1nP2/1Q4PP/6K1 w - - bm Rb7; id \"STS(v14.0) 7th Rank.048\"; c0 \"Rb7=10, Qa2=4, Qe2=5, Rc6=4\";", "3rk3/1p3pQp/p4R2/3p4/2P5/1P2q1P1/P1K4P/8 b - - bm Qe2+; id \"STS(v14.0) 7th Rank.049\"; c0 \"Qe2+=10, Kd7=7, Qe4+=7, Qe7=7\";", "3rkb1r/pp2pppp/1n2q3/8/7Q/2P5/PP3PPP/RNB2RK1 b k - bm Qe2; id \"STS(v14.0) 7th Rank.050\"; c0 \"Qe2=10, Qc6=1, Qd5=1, Qf5=1\";", "4b3/p2nP1k1/1r2R1p1/q1p5/2Br2P1/1P2QP2/P1P5/4R1K1 b - - bm Qd2; id \"STS(v14.0) 7th Rank.051\"; c0 \"Qd2=10, Rb8=6, Rd2=5, Rdd6=6, Rxe6=6\";", "4n3/7k/2Q3pb/1ppPp3/2q1P3/r5PP/1RB2BK1/8 b - - bm Qe2; id \"STS(v14.0) 7th Rank.052\"; c0 \"Qe2=10\";", "4q1k1/2p2pp1/p6p/Pr1P4/2p1P3/2P3P1/r6P/3QRR1K b - - bm Rbb2; id \"STS(v14.0) 7th Rank.053\"; c0 \"Rbb2=10, c6=6, Qe7=4, Rbxa5=6\";", "4r1k1/1p3p2/2p3p1/8/6P1/3r2P1/n5BP/N4RBK b - - bm Re2; id \"STS(v14.0) 7th Rank.054\"; c0 \"Re2=10\";", "4r1k1/1p6/pB3p2/P1R2Pp1/1P2b1r1/4P3/2N4b/2KR4 b - - bm Rg2; id \"STS(v14.0) 7th Rank.055\"; c0 \"Rg2=10, Bc6=5, Be5=7, Bf3=5\";", "4rk2/4p2p/3p1pp1/1p1P4/1Pq2P1P/P2RQ3/2r3P1/4R1K1 b - - bm Qa2; id \"STS(v14.0) 7th Rank.056\"; c0 \"Qa2=10, Kg7=3, Kg8=3, Qc8=6, Rb2=4\";", "5k2/1p1b2p1/pq2p1Q1/3p2P1/8/8/PPP1N2P/1K6 b - - bm Qf2; id \"STS(v14.0) 7th Rank.057\"; c0 \"Qf2=10, Kg8=7, Qd6=7, Qe3=5\";", "5k2/1p1r1pp1/6b1/q1r1pNPp/1Nn1P2P/p1P2Q2/P4P2/K1RR4 b - - bm Rd2; id \"STS(v14.0) 7th Rank.058\"; c0 \"Rd2=10, Nd2=2, Qc7=1, Rcc7=4, Rxd1=3\";", "5k2/5p2/3p2p1/p3q1P1/6b1/P1NR4/1PP2r2/1KQ5 b - - bm Qh2; id \"STS(v14.0) 7th Rank.059\"; c0 \"Qh2=10, Be6=4, Bf5=1, Qf5=3, Rh2=1\";", "5r1k/p2nq1pp/8/P7/1b1Pp3/1B3p1P/2Q2PP1/2B2RK1 w - - bm Qc7; id \"STS(v14.0) 7th Rank.060\"; c0 \"Qc7=10\";", "5r2/6k1/3p3p/1p1Qbbp1/1PP5/6NP/1q5P/4RB1K w - - bm Qb7+; id \"STS(v14.0) 7th Rank.061\"; c0 \"Qb7+=10, Bg2=8, c5=8, cxb5=8\";", "5rk1/5bpp/8/r2p1P2/3Bp1P1/1BR1P1K1/1n5P/2R5 w - - bm Rc7; id \"STS(v14.0) 7th Rank.062\"; c0 \"Rc7=10, Rc5=5, Rc6=4, Rc8=4\";", "6k1/1b2bp2/p3p2p/4P1p1/1pr5/4BP1P/PP2N1P1/3R2K1 b - - bm Rc2; id \"STS(v14.0) 7th Rank.063\"; c0 \"Rc2=10, Bc5=3, Bd5=4, Kf8=2, Rc7=4\";", "6k1/1p3pb1/p2qp1np/7p/Q7/4N1BP/PPP2PP1/6K1 b - - bm Qd2; id \"STS(v14.0) 7th Rank.064\"; c0 \"Qd2=10, Be5=2, Qb6=4, Qd8=2\";", "6k1/2nr2p1/2R2p1p/R2n3P/p2P4/r4NN1/5PP1/6K1 w - - bm Ra7; id \"STS(v14.0) 7th Rank.065\"; c0 \"Ra7=10, Nf5=3, Rcc5=1\";", "6k1/5p1p/4pPpP/3p4/qpnB1P2/rN4P1/2B3K1/3Q4 b - - bm Ra2; id \"STS(v14.0) 7th Rank.066\"; c0 \"Ra2=10\";", "7k/4p1q1/3pnbr1/8/1N5p/1NPP1P2/PP6/K1R3BR b - - bm Rg2; id \"STS(v14.0) 7th Rank.067\"; c0 \"Rg2=10, Kg8=5, Nf4=7, Qh6=6, Qh7=6\";", "7k/rr4p1/4qnQp/8/2P1p3/p3N2P/R4PP1/5RK1 b - - bm Rb2; id \"STS(v14.0) 7th Rank.068\"; c0 \"Rb2=10, Qe7=1\";", "7r/4k1p1/p2pP1p1/n2P4/1p2N2r/1P3P2/P2R2K1/4R3 b - - bm Rh2+; id \"STS(v14.0) 7th Rank.069\"; c0 \"Rh2+=10, Nb7=7, Nxb3=7, Rc8=7\";", "7r/8/5p1r/P3p1k1/1p1bP1p1/p2N4/P1PR2P1/1K3R2 b - - bm Rh2; id \"STS(v14.0) 7th Rank.070\"; c0 \"Rh2=10, Kg6=6, Rh7=6, Ra8=7, Rh1=9\";", "8/5nkq/1rNp1pp1/2pPp1p1/1n2P1P1/1PN2P2/6P1/R2Q2K1 w - - bm Ra7; id \"STS(v14.0) 7th Rank.071\"; c0 \"Ra7=10, Na4=2, Nb1=2, Qd2=2, Ra8=2\";", "8/5pk1/3p2p1/p1q3Pn/5rb1/1BN5/PPPQ4/1K2R3 b - - bm Rf2; id \"STS(v14.0) 7th Rank.072\"; c0 \"Rf2=10, Bf3=8, Bf5=8, Qxg5=8\";", "qr4k1/6b1/r6p/P2p1pp1/1B1P4/Q2NP1P1/5P1P/2R3K1 w - - bm Rc7; id \"STS(v14.0) 7th Rank.073\"; c0 \"Rc7=10\";", "r1b1k2r/pp2pp1p/1qnp2p1/8/2P1P1n1/2Q2P2/PPN3PP/1RB1KB1R b Kkq - bm Qf2+; id \"STS(v14.0) 7th Rank.074\"; c0 \"Qf2+=10, Nce5=8, Nf6=6, Nge5=8\";", "r1b1k2r/ppp2p1p/2n2p2/1B1q4/8/6Q1/PPP2PPP/R3K1NR w KQkq - bm Qg7; id \"STS(v14.0) 7th Rank.075\"; c0 \"Qg7=10, Bxc6+=4, c4=3, Qe3+=4\";", "r1b1k2r/ppp2p1p/2q2p2/8/8/6Q1/PPP2PPP/R3K1NR w KQkq - bm Qg7; id \"STS(v14.0) 7th Rank.076\"; c0 \"Qg7=10, Ne2=3, O-O-O=2, Qe3+=3\";", "r1b1qrk1/pp2bppp/4pn2/4Q1B1/8/2N5/PPP2PPP/2KR1B1R w - - bm Qc7; id \"STS(v14.0) 7th Rank.077\"; c0 \"Qc7=10, Be2=4, h4=3, Qg3=4\";", "r1b2rk1/1p2q1b1/1n4p1/pP1p4/3Pp3/6BN/P2QBPP1/2R1K2R w K - bm Rc7; id \"STS(v14.0) 7th Rank.078\"; c0 \"Rc7=10, Bc7=3, Be5=3\";", "r1b2rk1/pp2ppbp/2n3p1/2pq4/8/P2P1NP1/1P2PPBP/1RBQ1RK1 b - - bm Qa2; id \"STS(v14.0) 7th Rank.079\"; c0 \"Qa2=10, b6=8, Bd7=6, Qh5=7\";", "r1b5/pp2b1kp/3rpqp1/2Q2p2/3N4/1B2P1P1/PP3P1P/2RR2K1 w - - bm Qc7; id \"STS(v14.0) 7th Rank.080\"; c0 \"Qc7=10, Qc2=2, Qc3=2, Qc4=2\";", "r1br3k/ppp3bp/2n3p1/4ppN1/2P5/1P4P1/PB2PPBP/R4RK1 b - - bm Rd2; id \"STS(v14.0) 7th Rank.081\"; c0 \"Rd2=10, Kg8=8, Rd7=9, Re8=6, Rf8=7\";", "r2q1rk1/4bppp/3pb3/2n1pP2/1p2P1PP/1P3Q2/1BP1N1B1/2KR3R b - - bm Ra2; id \"STS(v14.0) 7th Rank.082\"; c0 \"Ra2=10\";", "r2q1rk1/pp3pp1/2p2n1p/8/2B5/4PQ2/PP3PPP/2R2RK1 b - - bm Qd2; id \"STS(v14.0) 7th Rank.083\"; c0 \"Qd2=10, g6=1, Qb6=1, Qc7=1, Qe7=1\";", "r3r1k1/3b2pp/2pP1q2/ppP2pp1/5N2/8/PP1Q1PPP/3RR1K1 w - - bm Re7; id \"STS(v14.0) 7th Rank.084\"; c0 \"Re7=10, Nd3=5, Nh5=2, Rxe8+=3\";", "r4b1r/p4ppp/3kp3/1R6/8/2P2P2/P4P1P/2B1K2R w K - bm Rb7; id \"STS(v14.0) 7th Rank.085\"; c0 \"Rb7=10, a4=1\";", "r4r1k/4bpp1/p2p1nbp/q1p5/2PnPPB1/P1NPB3/1R3N1P/3Q1RK1 w - - bm Rb7; id \"STS(v14.0) 7th Rank.086\"; c0 \"Rb7=10, Bd2=6, Bxd4=7, Nd5=8\";", "r4rk1/1p5p/p3pp2/2b5/P7/2N5/1P3PPP/R2R2K1 w - - bm Rd7; id \"STS(v14.0) 7th Rank.087\"; c0 \"Rd7=10, Ne4=5, Rac1=1\";", "r4rk1/p1p2pp1/2pp1q1p/4p3/4P1b1/2PP1N2/P1P2PPP/1R1Q1RK1 w - - bm Rb7; id \"STS(v14.0) 7th Rank.088\"; c0 \"Rb7=10, a4=4, c4=3, h3=3, Rb3=3\";", "r4rk1/pp2bppp/2n3b1/3R4/8/1P2BN1P/1P2BPP1/R5K1 w - - bm Rd7; id \"STS(v14.0) 7th Rank.089\"; c0 \"Rd7=10, Nd4=2, Ne5=1, Rb5=1\";", "r4rk1/pp2n2p/4P1p1/2Qp2q1/1P6/5R2/P5PP/4RBK1 w - - bm Rf7; id \"STS(v14.0) 7th Rank.090\"; c0 \"Rf7=10, Qe3=1\";", "r4rk1/pp3ppp/2n1p3/4Pb2/2N5/3q1N2/PP3PPP/R1QR2K1 b - - bm Qc2; id \"STS(v14.0) 7th Rank.091\"; c0 \"Qc2=10, Nxe5=9, Qe2=6, Qe4=6\";", "r4rk1/ppp1bppp/2np4/3Q1q2/2P5/2P2N2/PP3PPP/R1B1R1K1 b - - bm Qc2; id \"STS(v14.0) 7th Rank.092\"; c0 \"Qc2=10, Qd7=3, Qg4=3, Qg6=3\";", "r5k1/1p2bppp/pB6/3rn3/4p3/1N2P2P/PP3PP1/2R2RK1 w - - bm Rc7; id \"STS(v14.0) 7th Rank.093\"; c0 \"Rc7=10\";", "r5k1/4bpp1/7p/2Pp4/3Bp3/r2bPP1P/3N2P1/2R1K1R1 b - - bm Ra2; id \"STS(v14.0) 7th Rank.094\"; c0 \"Ra2=10, Bd8=7, Bh4+=7, R3a5=6\";", "r5k1/5pp1/p2p1n1p/1q1Pp3/2R1P3/3Q1N1P/P4PPK/8 b - - bm Qb2; id \"STS(v14.0) 7th Rank.095\"; c0 \"Qb2=10, Kh7=6, Ne8=6, Rb8=5, Rd8=6\";", "r7/4bpk1/p5p1/1p1RP2p/8/2r1B1PP/P4P2/3R2K1 w - - bm Rd7; id \"STS(v14.0) 7th Rank.096\"; c0 \"Rd7=10\";", "rn1q1rk1/pb1nbppp/1p2p3/3pN3/3P4/6P1/PPQBPPBP/RNR3K1 w - - bm Qc7; id \"STS(v14.0) 7th Rank.097\"; c0 \"Qc7=10, Nd3=2, Nf3=2, Nxd7=2\";", "rn1r2k1/pb2ppbp/1p4p1/8/3PP3/3B1N2/P2B1PPP/2R1K2R w K - bm Rc7; id \"STS(v14.0) 7th Rank.098\"; c0 \"Rc7=10, Bb1=6, Bg5=6, Ke2=1\";", "rnb2k2/1p3ppp/4p3/1p6/4Q3/1Bq5/P3KPPP/R6R b - - bm Qb2+; id \"STS(v14.0) 7th Rank.099\"; c0 \"Qb2+=10, f5=8, Na6=8, Nc6=8, Nd7=8\";", "Rr6/5kp1/1qQb1p1p/1p1PpP2/1Pp1B3/2P4P/6P1/5K2 w - - bm Qd7+; id \"STS(v14.0) 7th Rank.100\"; c0 \"Qd7+=10, Qxb6=4, Ra1=3, Ra2=2\";", NULL }; SjaakII/include/timer.h000644 000765 000024 00000004177 12452217646 016025 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TIMER_H #define TIMER_H #ifdef __cplusplus extern "C" { #endif #include "bool.h" uint64_t get_timer(void); typedef struct chess_clock_t { /* various things having to do with time management */ int time_left; /* Total time left on the clock, in msec */ int time_inc; /* Time increment per move, in msec */ int time_per_move; /* Time per move, in msec */ int extra_time; /* Extra time allocated for this move */ uint64_t start_time; /* Timer value when the search started */ int movestogo; /* Number of moves until the next cycle */ int movestotc; /* Number of moves per cycle */ int root_moves_played; /* Total number of moves played so far in the game */ size_t max_nodes; /* Maximum number of nodes to search */ size_t nodes_searched; /* Number of nodes currently searched */ bool pondering; /* true: not our move, ignore clock but mind keyboard */ bool (*check_clock)(const struct chess_clock_t *clock); } chess_clock_t; void start_clock(chess_clock_t *clock); int peek_timer(const chess_clock_t *clock); int get_chess_clock_time_for_move(const chess_clock_t *clock); void set_ponder_timer(chess_clock_t *clock); void set_infinite_time(chess_clock_t *clock); void set_time_per_move(chess_clock_t *clock, int msec); void set_time_for_game(chess_clock_t *clock); #ifdef __cplusplus } #endif #endif SjaakII/include/variants.h000644 000765 000024 00000327543 13017310267 016530 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VARIANTS_H #define VARIANTS_H #include typedef game_t *(*new_variant_game_t)(const char *shortname); game_t *create_standard_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); game->name = strdup("Chess"); game->finalise_variant(); return game; } game_t *create_seirawan_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fh = fb | fn; move_flag_t fe = fr | fn; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fh, fh, 0, pz, "", "Hawk", "H,h", "H", 825); game->add_piece_type(fe, fe, 0, pz, "", "Elephant","E,e","E", 875); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QHERBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KGFDCBQkgfdcbq - 0 1"); game->name = strdup("Seirawan"); game->add_rule(RF_GATE_DROPS); game->finalise_variant(); return game; } game_t *create_crazyhouse_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; float m_scale = 0.5f; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", int(m_scale*325)); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", int(m_scale*325)); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", int(m_scale*450)); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", int(m_scale*675)); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "Q~R~B~N~", "Pawn", "P,p", " ", int(m_scale*150)); game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N~,n~", "N~", int(m_scale*375)); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B~,b~", "B~", int(m_scale*375)); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R~,r~", "R~", int(m_scale*500)); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q~,q~", "Q~", int(m_scale*725)); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR [-] w KQkq - 0 1"); game->name = strdup("Crazyhouse"); game->add_rule(RF_ALLOW_DROPS | RF_KEEP_CAPTURE); game->finalise_variant(); /* Pawns may not be dropped on the first or last ranks */ for (int n = 0; npt.num_piece_types; n++) { game->pt.drop_zone[WHITE][n] &= game->pt.drop_zone[BLACK][n]; game->pt.drop_zone[BLACK][n] &= game->pt.drop_zone[WHITE][n]; } return game; } game_t *create_chessgi_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR [-] w KQkq - 0 1"); game->xb_setup = strdup("(PNBRQKpnbrqk) 8x8+6_crazyhouse"); game->name = strdup("Chessgi"); game->add_rule(RF_ALLOW_DROPS | RF_KEEP_CAPTURE); game->finalise_variant(); return game; } game_t *create_twilight_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR [-] w KQkq - 0 1"); game->name = strdup("Twilight Chess"); game->add_rule(RF_ALLOW_DROPS | RF_ALLOW_PICKUP); game->finalise_variant(); return game; } game_t *create_shatranj_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("leap (2,2)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 400); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 100); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 800); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 200); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "Q", "Pawn", "P,p", " ", 100); /* Stale mate is a loss */ game->stale_score = -LEGALWIN; /* A bare king is a loss (except if we can bare the enemy king on the next move, but the search takes care * of that). */ game->add_rule(RF_USE_BARERULE); game->bare_king_score = -LEGALWIN; game->start_fen = strdup("rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w 0 1"); game->name = strdup("Shatranj"); game->finalise_variant(); return game; } game_t *create_courier_game(const char *) { int files = 12; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(12, 8); move_flag_t fe = game->movegen.define_piece_move("leap (2,2)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 400); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 500); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 800); game->add_piece_type(ff, ff, 0, pz, "", "Ferz", "F,f", "F", 200); game->add_piece_type(fe, fe, 0, pz, "", "Elephant", "E,e", "E", 250); game->add_piece_type(fw, fw, 0, pz, "", "Wazir", "W,w", "W", 225); game->add_piece_type(fk, fk, 0, pz, "", "Man", "M,m", "M", 300); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "F", "Pawn", "P,p", " ", 100); /* Set the FEN string for the starting position */ game->start_fen = strdup("rnebmk1wbenr/1ppppp1pppp1/6f5/p5p4p/P5P4P/6F5/1PPPPP1PPPP1/RNEBMK1WBENR w - -"); game->name = strdup("Courier chess"); game->finalise_variant(); return game; } game_t *create_berolina_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step NE, NW"); move_flag_t fc = game->movegen.define_piece_move("step N"); move_flag_t f2 = game->movegen.define_piece_move("step 2NE, 2NW"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); game->name = strdup("Berolina Chess"); game->finalise_variant(); return game; } game_t *create_knightmate_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fk, fk, 0, pz, "", "Man", "M,m", "M", 300); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 450); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fn, fn, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBM", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1"); game->name = strdup("Knightmate"); game->finalise_variant(); return game; } game_t *create_shatar_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fq = ff | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; uint32_t mf = PF_NOMATE | PF_SHAK; uint32_t sf = PF_SHAK; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, mf, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, sf, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, sf, pz, "", "Berse", "J,j", "J", 725); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "J", "Pawn", "P,p", " ", 100); game->add_rule(RF_USE_SHAKMATE); game->start_fen = strdup("rnbjkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBJKBNR w - - 0 1"); //game->start_fen = strdup("rnbjkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBJKBNR w - - 0 1"); game->xb_setup = strdup("(PNBR..........J......Kpnbr..........j......k) 8x8+0_fairy"); game->name = strdup("Shatar"); game->finalise_variant(); return game; } game_t *create_spartan_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); move_flag_t fw2 = game->movegen.define_piece_move("step 2N"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = fb | fn; move_flag_t fg = fr | ff; move_flag_t flm = game->movegen.define_piece_move("aleap (-1,0)|(1,0)|(1,1)|(-1,1)|(1,-1)|(-1,-1)|(2,2)|(-2,2)|(2,-2)|(-2,-2)"); move_flag_t flc = game->movegen.define_piece_move("leap (1,1)|(2,2)"); move_flag_t fc = game->movegen.define_piece_move("leap (1,0)|(2,0)"); move_flag_t fbh = game->movegen.define_piece_move("step NE,NW"); move_flag_t fbc = game->movegen.define_piece_move("step N"); move_flag_t fb2 = game->movegen.define_piece_move("leap (2,2)") | fbh; uint32_t kf = PF_ROYAL; bitboard_t rank2 = bitboard_t::board_rank[1]; bitboard_t rank7 = bitboard_t::board_rank[6]; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 475); game->add_piece_type( fg, fg, 0, pz, "", "General", "G,g", "G", 725); game->add_piece_type( fw, fw, 0, pz, "", "Warlord", "W,w", "W", 825); game->add_piece_type(flm, flc, 0, pz, "", "Lieutenant", "L,l", "L", 300); game->add_piece_type( fc, fc, 0, pz, "", "Captain", "C,c", "C", 300); game->add_piece_type(fwp, fwc, 0, pp, "QRBN", "Pawn", "P,p", " ", 100); game->add_piece_type(fbh, fbc, 0, pp, "KGWLC","Hoplite", "H,h", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", rank2, fw2); game->add_special_move("h", rank7, fb2); /* Spartans can have two kings */ game->set_maximum_number_of_kings(BLACK, 2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->start_fen = strdup("lgkcckwl/hhhhhhhh/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ -"); game->name = strdup("Spartan chess"); game->add_rule(RF_KING_DUPLECHECK); game->finalise_variant(); return game; } game_t *create_super_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fe = fn | fr; move_flag_t fs = fb | fn; move_flag_t fa = fb | fr | fn; move_flag_t fv = game->movegen.define_piece_move("leap (1,0)|(1,1)|(1,2)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fe, fe, 0, pz, "", "Empress","E,e", "E", 900); game->add_piece_type(fs, fs, 0, pz, "", "Princess","S,s","S", 875); game->add_piece_type(fa, fa, 0, pz, "", "Amazon", "A,a", "A",1225); game->add_piece_type(fv, fv, 0, pz, "", "Veteran","V,v", "V", 750); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBNAESV", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->set_maximum_number_of_pieces("Q", WHITE, 1); game->set_maximum_number_of_pieces("A", WHITE, 1); game->set_maximum_number_of_pieces("E", WHITE, 1); game->set_maximum_number_of_pieces("S", WHITE, 1); game->set_maximum_number_of_pieces("V", WHITE, 1); game->set_maximum_number_of_pieces("R", WHITE, 2); game->set_maximum_number_of_pieces("N", WHITE, 2); game->set_maximum_number_of_pieces("B", WHITE, 2); game->set_maximum_number_of_pieces("Q", BLACK, 1); game->set_maximum_number_of_pieces("A", BLACK, 1); game->set_maximum_number_of_pieces("E", BLACK, 1); game->set_maximum_number_of_pieces("S", BLACK, 1); game->set_maximum_number_of_pieces("V", BLACK, 1); game->set_maximum_number_of_pieces("R", BLACK, 2); game->set_maximum_number_of_pieces("N", BLACK, 2); game->set_maximum_number_of_pieces("B", BLACK, 2); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); game->xb_setup = strdup("(PNBRQ..SE.......V.AKpnbrq..se.......v.ak) 8x8+9_fairy"); game->name = strdup("Super Chess"); game->finalise_variant(); return game; } game_t *create_test_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fa = fb | fn; move_flag_t fc = fr | fn; move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); move_flag_t fw2 = game->movegen.define_piece_move("step 2N"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fg = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1)"); move_flag_t fd = game->movegen.define_piece_move("leap (2,0)"); move_flag_t fe = game->movegen.define_piece_move("leap (2,2)"); move_flag_t fh = game->movegen.define_piece_move("leap ((0,1)+(1,1))&(2,1)"); move_flag_t fz = game->movegen.define_piece_move("leap (3,2)"); move_flag_t flm = game->movegen.define_piece_move("leap (0,0)|(1,1)|(1,0)|(2,0)|(2,1)|(2,2)"); move_flag_t flc = game->movegen.define_piece_move("leap ((1,1)|(1,0))+((1,1)|(1,0))"); move_flag_t fdk = fr | ff; move_flag_t fdh = fb | fw; uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N"); game->add_piece_type( fh, fh, 0, pz, "", "Horse", "H,h", "H"); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B"); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R"); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q"); game->add_piece_type( fa, fa, 0, pz, "", "Archbishop", "A,a", "A"); game->add_piece_type( fc, fc, 0, pz, "", "Chancellor", "C,c", "C"); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K"); game->add_piece_type( ff, ff, 0, pz, "", "Ferz", "F,f", "F"); game->add_piece_type( fw, fw, 0, pz, "", "Wazir", "W,w", "W"); game->add_piece_type( fz, fz, 0, pz, "", "Zebra", "Z,z", "Z"); game->add_piece_type( flc, flc,0, pz, "", "Lion", "L,l", "L"); game->add_piece_type( fs, fs, 0, pz, "", "Silver general", "S,s", "S"); game->add_piece_type( fg, fg, 0, pz, "", "Gold general", "G,g", "G"); game->add_piece_type( fd, fd, 0, pz, "", "Dabbabah", "D,d", "D"); //game->add_piece_type( fe, fe, 0, pz, "", "Elephant", "E,e", "E"); game->add_piece_type(fdk, fdk, 0, pz, "", "Dragon king","J,j", "J"); game->add_piece_type(fdh, fdh, 0, pz, "", "Dragon horse","I,i", "I"); game->add_piece_type(fwp, fwc, pf, pp, "QACRBN", "Pawn", "P,p", " "); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, fw2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"); game->name = strdup("test"); game->finalise_variant(); return game; } game_t *create_shogi_game(const char *) { int files = 9; int ranks = 9; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("aleap (1,2)|(-1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fl = game->movegen.define_piece_move("step 8N"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fg = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1)"); move_flag_t fdk = fr | ff; move_flag_t fdh = fb | fw; uint32_t kf = PF_ROYAL; uint32_t pf = PF_DROPNOMATE | PF_DROPONEFILE; bitboard_t pp[2]; bitboard_t pz[2] = { bitboard_t::board_rank[8]|bitboard_t::board_rank[7]|bitboard_t::board_rank[6], bitboard_t::board_rank[0]|bitboard_t::board_rank[1]|bitboard_t::board_rank[2] }; float m_scale = 0.3f; game->add_piece_type( fp, fp, pf, pz, "+", "Pawn", "P,p", "P", int( 80*m_scale));// 50); game->add_piece_type( fl, fl, 0, pz, "+", "Lance", "L,l", "L", int(225*m_scale));// 200); game->add_piece_type( fn, fn, 0, pz, "+", "Knight", "N,n", "N", int(250*m_scale));// 250); game->add_piece_type( fs, fs, 0, pz, "+", "Silver general", "S,s", "S", int(375*m_scale));// 350); game->add_piece_type( fb, fb, 0, pz, "+", "Bishop", "B,b", "B", int(575*m_scale));// 550); game->add_piece_type( fr, fr, 0, pz, "+", "Rook", "R,r", "R", int(650*m_scale));// 650); game->add_piece_type( fg, fg, 0, pp, "", "Gold general", "G,g", "G", int(450*m_scale));// 400); game->add_piece_type( fk, fk, kf, pp, "", "King", "K,k", "K"); game->add_piece_type( fg, fg, 0, pp, "", "Promoted pawn", "+P,+p", "+P", int(530*m_scale));// 270); game->add_piece_type( fg, fg, 0, pp, "", "Promoted lance", "+L,+l", "+L", int(480*m_scale));// 300); game->add_piece_type( fg, fg, 0, pp, "", "Promoted knight", "+N,+n", "+N", int(500*m_scale));// 330); game->add_piece_type( fg, fg, 0, pp, "", "Promoted silver", "+S,+s", "+S", int(490*m_scale));// 370); game->add_piece_type(fdh, fdh, 0, pp, "", "Dragon horse", "+B,+b", "+B", int(825*m_scale));// 700); game->add_piece_type(fdk, fdk, 0, pp, "", "Dragon king", "+R,+r", "+R", int(950*m_scale));// 800); game->start_fen = strdup("lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL [-] w 0 1"); game->name = strdup("shogi"); game->add_rule(RF_ALLOW_DROPS); game->add_rule(RF_KEEP_CAPTURE); /* TODO: perpetual checking is not allowed */ game->perpetual = -ILLEGAL; game->repeat_claim = 3; game->finalise_variant(); return game; } game_t *create_minishogi_game(const char *) { #define kind uint64_t unsigned int files = 5; unsigned int ranks = 5; if (8*sizeof(kind) < files*ranks) return NULL; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fg = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1)"); move_flag_t fdk = fr | ff; move_flag_t fdh = fb | fw; uint32_t kf = PF_ROYAL; uint32_t pf = PF_DROPNOMATE | PF_DROPONEFILE; bitboard_t pp[2]; bitboard_t pz[2] = { bitboard_t::board_rank[4], bitboard_t::board_rank[0] }; game->add_piece_type( fp, fp, pf, pz, "+", "Pawn", "P,p", "P", 100); game->add_piece_type( fs, fs, 0, pz, "+", "Silver general", "S,s", "S", 400); game->add_piece_type( fb, fb, 0, pz, "+", "Bishop", "B,b", "B", 450); game->add_piece_type( fr, fr, 0, pz, "+", "Rook", "R,r", "R", 500); game->add_piece_type( fg, fg, 0, pp, "", "Gold general", "G,g", "G", 450); game->add_piece_type( fk, fk, kf, pp, "", "King", "K,k", "K"); game->add_piece_type( fg, fg, 0, pp, "", "Promoted pawn", "+P,+p", "+P", 550); game->add_piece_type( fg, fg, 0, pp, "", "Promoted silver", "+S,+s", "+S", 500); game->add_piece_type(fdh, fdh, 0, pp, "", "Dragon horse", "+B,+b", "+B", 650); game->add_piece_type(fdk, fdk, 0, pp, "", "Dragon king", "+R,+r", "+R", 700); game->start_fen = strdup("rbsgk/4p/5/P4/KGSBR [-] w 0 1"); game->xb_setup = strdup("(P.BR.S...G.+.++.+Kp.br.s...g.+.++.+k) 5x5+5_shogi"); game->name = strdup("minishogi"); game->add_rule(RF_ALLOW_DROPS); game->add_rule(RF_KEEP_CAPTURE); /* FIXME: repetition is scored as a loss, but it should be scored as a loss *for white*, * unless it is a check-by-repetition. */ game->rep_score = -ILLEGAL; game->repeat_claim = 3; game->finalise_variant(); return game; #undef kind } game_t *create_shoshogi_game(const char *) { int files = 9; int ranks = 9; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("aleap (1,2)|(-1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fl = game->movegen.define_piece_move("step 8N"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fg = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)|(0,-1)|(1,1)|(-1,1)"); move_flag_t fe = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)|(1,1)|(-1,1)|(1,-1)|(-1,-1)"); move_flag_t fdk = fr | ff; move_flag_t fdh = fb | fw; uint32_t kf = PF_ROYAL; uint32_t pf = PF_DROPNOMATE | PF_DROPONEFILE; bitboard_t pp[2]; bitboard_t pz[2] = { bitboard_t::board_rank[8]|bitboard_t::board_rank[7]|bitboard_t::board_rank[6], bitboard_t::board_rank[0]|bitboard_t::board_rank[1]|bitboard_t::board_rank[2] }; game->add_piece_type( fp, fp, pf, pz, "+", "Pawn", "P,p", "P", 75); game->add_piece_type( fl, fl, 0, pz, "+", "Lance", "L,l", "L", 250); game->add_piece_type( fn, fn, 0, pz, "+", "Knight", "N,n", "N", 300); game->add_piece_type( fs, fs, 0, pz, "+", "Silver general", "S,s", "S", 400); game->add_piece_type( fb, fb, 0, pz, "+", "Bishop", "B,b", "B", 650); game->add_piece_type( fr, fr, 0, pz, "+", "Rook", "R,r", "R", 750); game->add_piece_type( fe, fe, 0, pz, "+", "Drunk elephant", "E,e", "E", 550); game->add_piece_type( fg, fg, 0, pp, "", "Gold general", "G,g", "G", 450); game->add_piece_type( fk, fk, kf, pp, "", "King", "K,k", "K"); game->add_piece_type( fg, fg, 0, pp, "", "Promoted pawn", "+P,+p", "+P", 450); game->add_piece_type( fg, fg, 0, pp, "", "Promoted lance", "+L,+l", "+L", 450); game->add_piece_type( fg, fg, 0, pp, "", "Promoted knight", "+N,+n", "+N", 450); game->add_piece_type( fg, fg, 0, pp, "", "Promoted silver", "+S,+s", "+S", 450); game->add_piece_type( fk, fk, kf, pp, "", "Crown prince", "+E,+e", "+E", 700); game->add_piece_type(fdh, fdh, 0, pp, "", "Dragon horse", "+B,+b", "+B", 775); game->add_piece_type(fdk, fdk, 0, pp, "", "Dragon king", "+R,+r", "+R", 900); game->start_fen = strdup("lnsgkgsnl/1r2e2b1/ppppppppp/9/9/9/PPPPPPPPP/1B2E2R1/LNSGKGSNL [-] w 0 1"); game->xb_setup = strdup("(PNBRLSE..G.+.++.++Kpnbrlse..g.+.++.++k) 9x9+0_shogi"); game->name = strdup("shoshogi"); game->add_rule(RF_USE_BARERULE); game->bare_king_score = -LEGALWIN; game->repeat_claim = 3; game->finalise_variant(); return game; } game_t *create_tori_game(const char *) { int files = 7; int ranks = 7; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t flq = game->movegen.define_piece_move("step 6N,6SE,SW"); // Left Quail move_flag_t frq = game->movegen.define_piece_move("step 6N,6SW,SE"); // Right Quail move_flag_t fs = game->movegen.define_piece_move("step N"); move_flag_t ff = game->movegen.define_piece_move("aleap (-1,-1)|(-1,0)|(-1,1)|(0,1)|(1,1)|(1,0)|(1,-1)"); move_flag_t fc = game->movegen.define_piece_move("aleap (-1,-1)|(0,-1)|(-1,1)|(1,1)|(0,1)|(1,-1)"); move_flag_t fe = game->movegen.define_piece_move("step 6NE,6NW,6S,2SE,2SW") | fk; move_flag_t fp = game->movegen.define_piece_move("aleap (-1,-1)|(1,-1)|(0,2)"); move_flag_t fg = game->movegen.define_piece_move("aleap (2,2)|(-2,2)|(0,-2)"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_DROPNOMATE | PF_DROPONEFILE; bitboard_t pz[2]; bitboard_t pp[2] = { bitboard_t::board_rank[6]|bitboard_t::board_rank[5], bitboard_t::board_rank[0]|bitboard_t::board_rank[1] }; game->add_piece_type( fk, fk, kf, pz, "", "Phoenix", "K,k", "K"); game->add_piece_type( ff, ff, 0, pp, "+", "Falcon", "F,f", "F"); game->add_piece_type( fc, fc, 0, pz, "", "Crane", "C,c", "C"); game->add_piece_type(flq, flq, 0, pz, "", "Left quail", "L,l", "L"); game->add_piece_type(frq, frq, 0, pz, "", "Right quail","R,r", "R"); game->add_piece_type( fp, fp, 0, pz, "", "Pheasant", "P,p", "P"); int st = game->add_piece_type( fs, fs, pf, pp, "+", "Swallow", "S,s", "S"); game->add_piece_type( fg, fg, 0, pz, "", "Goose", "+S,+s","+S"); game->add_piece_type( fe, fe, 0, pz, "", "Eagle", "+F,+f","+F"); game->pt.piece_drop_file_maximum[st] = 2; game->start_fen = strdup("rpckcpl/3f3/sssssss/2s1S2/SSSSSSS/3F3/LPCKCPR [-] w 0 1"); game->xb_setup = strdup("(S.....FLR.C+.....+.PKs.....flr.c+.....+.pk) 7x7+6_shogi"); game->name = strdup("Tori Shogi"); game->add_rule(RF_ALLOW_DROPS); game->add_rule(RF_KEEP_CAPTURE); // TODO: repetition rules game->rep_score = -ILLEGAL; game->finalise_variant(); /* Promotions are never optional */ for (int n = 0; npt.num_piece_types; n++) { game->pt.optional_promotion_zone[WHITE][n].clear(); game->pt.optional_promotion_zone[BLACK][n].clear(); } return game; } game_t *create_chinese_game(const char *) { int files = 9; int ranks = 10; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fc = game->movegen.define_piece_move("hop (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap ((0,1)+(1,1))&(1,2)"); move_flag_t fe = game->movegen.define_piece_move("leap ((1,1)+(1,1))&(2,2)"); move_flag_t ff = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)"); move_flag_t fp = game->movegen.define_piece_move("aleap (1,0)|(-1,0)|(0,1)"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; game->add_piece_type(fn, fn, 0, pz, "", "Horse", "H,h", "H", 400); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 950); game->add_piece_type(fr, fc, 0, pz, "", "Cannon", "C,c", "C", 450); int gi = game->add_piece_type(ff, ff, 0, pz, "", "Guard", "A,a", "A", 75); int ei = game->add_piece_type(fe, fe, 0, pz, "", "Elephant", "E,e", "E", 75); int pi = game->add_piece_type(fp, fp, 0, pz, "", "Pawn", "P,p", "P", 100); int ki = game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); /* Zones for restricted piece movement */ bitboard_t south = bitboard_t::board_south; bitboard_t north = bitboard_t::board_north;; bitboard_t pawnf; for (int f = 0; f::board_file[f]; /* Guards and kings are restricted to the castles */ bitboard_t palace; for (int r = 0; r<=2; r++) for (int f = 3; f<=5; f++) { palace.set(f + 9*r); palace.set(f + 9*(9-r)); } game->pt.prison[WHITE][ki] = palace & south; game->pt.prison[BLACK][ki] = palace & north; game->pt.prison[WHITE][gi] = palace & south; game->pt.prison[BLACK][gi] = palace & north; game->pt.prison[WHITE][ei] = south; game->pt.prison[BLACK][ei] = north; game->pt.prison[WHITE][pi] = pawnf | north; game->pt.prison[BLACK][pi] = pawnf | south; game->add_rule(RF_KING_TABOO | RF_KING_TRAPPED | RF_USE_CHASERULE); game->start_fen = strdup("rheakaehr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RHEAKAEHR w - - 0 1"); game->name = strdup("XiangQi"); game->fifty_limit = 0; game->stale_score = -LEGALWIN; //game->rep_score = -LEGALWIN; game->finalise_variant(); return game; } game_t *create_grand_game(const char *) { int files = 10; int ranks = 10; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fq = fb | fr; move_flag_t fc = fb | fn; move_flag_t fm = fr | fn; move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); move_flag_t fw2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pi[2] = {bitboard_t::board_rank[2], bitboard_t::board_rank[7]}; bitboard_t pp[2] = {bitboard_t::board_rank[7]|bitboard_t::board_rank[8]|bitboard_t::board_rank[9], bitboard_t::board_rank[0]|bitboard_t::board_rank[1]|bitboard_t::board_rank[2]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 275); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B", 350); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 475); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 950); game->add_piece_type( fc, fc, 0, pz, "", "Cardinal", "A,a", "A", 825); game->add_piece_type( fm, fm, 0, pz, "", "Marshal", "C,c", "C", 875); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, pf, pp, "QCARBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, fw2); game->set_maximum_number_of_pieces("Q", WHITE, 1); game->set_maximum_number_of_pieces("C", WHITE, 1); game->set_maximum_number_of_pieces("A", WHITE, 1); game->set_maximum_number_of_pieces("R", WHITE, 2); game->set_maximum_number_of_pieces("N", WHITE, 2); game->set_maximum_number_of_pieces("B", WHITE, 2); game->set_maximum_number_of_pieces("Q", BLACK, 1); game->set_maximum_number_of_pieces("C", BLACK, 1); game->set_maximum_number_of_pieces("A", BLACK, 1); game->set_maximum_number_of_pieces("R", BLACK, 2); game->set_maximum_number_of_pieces("N", BLACK, 2); game->set_maximum_number_of_pieces("B", BLACK, 2); game->start_fen = strdup("r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R w - -"); game->name = strdup("Grand chess"); game->finalise_variant(); return game; } // http://www.chessvariants.org/index/msdisplay.php?itemid=MSopulentchess game_t *create_opulent_game(const char *) { int files = 10; int ranks = 10; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)|leap(1,0)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,3)|leap(1,1)"); move_flag_t fl = game->movegen.define_piece_move("leap (0,2)|(0,3)|leap(1,1)"); move_flag_t fN = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fq = fb | fr; move_flag_t fa = fb | fN; move_flag_t fc = fr | fN; move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); move_flag_t fw2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pi[2] = {bitboard_t::board_rank[2], bitboard_t::board_rank[7]}; bitboard_t pp[2] = {bitboard_t::board_rank[7]|bitboard_t::board_rank[8]|bitboard_t::board_rank[9], bitboard_t::board_rank[0]|bitboard_t::board_rank[1]|bitboard_t::board_rank[2]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 300); game->add_piece_type( fw, fw, 0, pz, "", "Wizard", "W,w", "W", 300); game->add_piece_type( fl, fl, 0, pz, "", "Lion", "L,l", "L", 300); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B", 350); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 475); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 950); game->add_piece_type( fa, fa, 0, pz, "", "Archbishop","A,a", "A", 825); game->add_piece_type( fc, fc, 0, pz, "", "Chancellor","C,c", "C", 875); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, pf, pp, "QACRBNWL", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, fw2); game->set_maximum_number_of_pieces("Q", WHITE, 1); game->set_maximum_number_of_pieces("C", WHITE, 1); game->set_maximum_number_of_pieces("A", WHITE, 1); game->set_maximum_number_of_pieces("R", WHITE, 2); game->set_maximum_number_of_pieces("N", WHITE, 2); game->set_maximum_number_of_pieces("B", WHITE, 2); game->set_maximum_number_of_pieces("W", WHITE, 2); game->set_maximum_number_of_pieces("L", WHITE, 2); game->set_maximum_number_of_pieces("Q", BLACK, 1); game->set_maximum_number_of_pieces("M", BLACK, 1); game->set_maximum_number_of_pieces("C", BLACK, 1); game->set_maximum_number_of_pieces("R", BLACK, 2); game->set_maximum_number_of_pieces("N", BLACK, 2); game->set_maximum_number_of_pieces("B", BLACK, 2); game->set_maximum_number_of_pieces("W", BLACK, 2); game->set_maximum_number_of_pieces("L", BLACK, 2); game->start_fen = strdup("rw6wr/clbnqknbla/pppppppppp/10/10/10/10/PPPPPPPPPP/CLBNQKNBLA/RW6WR w - -"); game->xb_setup = strdup("(PNBRQ..AC....W.......LKpnbrq..ac....w.......lk) 10x10+0_grand"); game->name = strdup("Opulent chess"); game->finalise_variant(); return game; } game_t *create_greatshatranj_game(const char *) { int files = 10; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fg = game->movegen.define_piece_move("leap (1,0)|(1,1)|(2,0)|(2,2)"); move_flag_t fm = game->movegen.define_piece_move("leap (1,0)|(2,0)|(1,2)"); move_flag_t fh = game->movegen.define_piece_move("leap (1,1)|(2,2)|(1,2)"); move_flag_t fe = game->movegen.define_piece_move("leap (1,1)|(2,2)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,0)|(2,0)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type( fg, fg, 0, pz, "", "General", "G,g", "G", 650); game->add_piece_type( fm, fm, 0, pz, "", "Minister", "M,m", "M", 650); game->add_piece_type( fh, fh, 0, pz, "", "High Priest","H,h","H", 650); game->add_piece_type( fe, fe, 0, pz, "", "Elephant", "E,e", "E", 300); game->add_piece_type( fw, fw, 0, pz, "", "Woody", "W,w", "W", 300); game->add_piece_type( fk, fk, 0, pz, "", "Soldier", "S,s", "S", 300); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, 0, pp, "NGMHEWS","Pawn", "P,p", " ", 100); game->set_maximum_number_of_pieces("N", WHITE, 2); game->set_maximum_number_of_pieces("G", WHITE, 1); game->set_maximum_number_of_pieces("M", WHITE, 1); game->set_maximum_number_of_pieces("H", WHITE, 1); game->set_maximum_number_of_pieces("E", WHITE, 2); game->set_maximum_number_of_pieces("W", WHITE, 2); game->set_maximum_number_of_pieces("N", BLACK, 2); game->set_maximum_number_of_pieces("G", BLACK, 1); game->set_maximum_number_of_pieces("M", BLACK, 1); game->set_maximum_number_of_pieces("H", BLACK, 1); game->set_maximum_number_of_pieces("E", BLACK, 2); game->set_maximum_number_of_pieces("W", BLACK, 2); game->start_fen = strdup("wnegkmhenw/pppppppppp/10/10/10/10/PPPPPPPPPP/WNEGKMHENW w KQkq - 0 1"); game->xb_setup = strdup("(PN....E...S..HWGMKpn....e...s..hwgmk)"); game->name = strdup("Great Shatranj"); game->finalise_variant(); return game; } game_t *create_capablanca_game(const char *) { int files = 10; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fq = fb | fr; move_flag_t fa = fb | fn; move_flag_t fc = fr | fn; move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); move_flag_t fw2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 275); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B", 350); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 475); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 950); game->add_piece_type( fa, fa, 0, pz, "", "Archbishop","A,a", "A", 825); game->add_piece_type( fc, fc, 0, pz, "", "Chancellor","C,c", "C", 875); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, pf, pp, "QACRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, fw2); game->deduce_castle_flags(WHITE, 5, 8, 9); game->deduce_castle_flags(WHITE, 5, 2, 0); game->deduce_castle_flags(BLACK, 75, 78, 79); game->deduce_castle_flags(BLACK, 75, 72, 70); game->start_fen = strdup("rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq -"); game->name = strdup("Capablanca chess"); game->finalise_variant(); return game; } game_t *create_gothic_game(const char *shortname) { game_t *game = create_capablanca_game(shortname); if (game) { free(game->start_fen); free(game->name); game->start_fen = strdup("rnbqckabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQCKABNR w KQkq -"); game->name = strdup("Capablanca chess (Gothic)"); } return game; } game_t *create_embassy_game(const char *shortname) { game_t *game = create_capablanca_game(shortname); if (game) { free(game->start_fen); free(game->name); game->start_fen = strdup("rnbqkcabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQKCABNR w KQkq -"); game->xb_setup = strdup("(PNBRQ..AC............Kpnbrq..ac............k) 10x8+0_capablanca"); game->name = strdup("Capablanca chess (Embassy)"); game->deduce_castle_flags(WHITE, 4, 1, 0); game->deduce_castle_flags(WHITE, 4, 7, 9); game->deduce_castle_flags(BLACK, 74, 71, 70); game->deduce_castle_flags(BLACK, 74, 77, 79); } return game; } game_t *create_micro_game(const char *) { #define kind uint64_t int files = 5; int ranks = 5; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fq = fb | fr; move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[4], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type( fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, 0, pp, "QRBN", "Pawn", "P,p", " ", 100); game->start_fen = strdup("kqbnr/ppppp/5/PPPPP/KQBNR w - -"); game->xb_setup = strdup("(PNBRQKpnbrqk) 5x5+0_fairy"); game->name = strdup("Gardner minichess"); // http://www.chessvariants.org/small.dir/hpmini.html game->finalise_variant(); return game; #undef kind } game_t *create_losalamos_game(const char *) { int files = 6; int ranks = 6; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fq = fb | fr; move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fwp = game->movegen.define_piece_move("step N"); move_flag_t fwc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[5], bitboard_t::board_rank[0]}; game->add_piece_type( fn, fn, 0, pz, "", "Knight", "N,n", "N", 575); game->add_piece_type( fr, fr, 0, pz, "", "Rook", "R,r", "R", 650); game->add_piece_type( fq, fq, 0, pz, "", "Queen", "Q,q", "Q",1220); game->add_piece_type( fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fwp, fwc, 0, pp, "QRN", "Pawn", "P,p", " ", 100); game->start_fen = strdup("rnqknr/pppppp/6/6/PPPPPP/RNQKNR w - -"); game->xb_setup = strdup("(PN.RQ................Kpn.rq................k) 6x6+0_fairy"); game->name = strdup("Los Alamos chess"); game->finalise_variant(); return game; } game_t *create_pocketknight_game(const char *shortname) { game_t *game = create_standard_game(shortname); if (game) { free(game->start_fen); free(game->name); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR [Nn] w KQkq -"); game->xb_setup = strdup("(PNBRQKpnbrqk) 8x8+2_bughouse"); // Hack: we need a holding-size of 2, otherwise the knight drops out. game->name = strdup("Pocket Knight Chess"); game->add_rule(RF_ALLOW_DROPS); } return game; } game_t *create_kingofthehill_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 2N"); uint32_t kf = PF_ROYAL | PF_CAPTUREFLAG; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[6]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 325); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, pf, pp, "QRBN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 4, 6, 7); game->deduce_castle_flags(WHITE, 4, 2, 0); game->deduce_castle_flags(BLACK, 60, 62, 63); game->deduce_castle_flags(BLACK, 60, 58, 56); for (side_t side=WHITE; side<=BLACK; side++) { game->place_flag(side, 27); game->place_flag(side, 28); game->place_flag(side, 35); game->place_flag(side, 36); } game->add_rule(RF_CAPTURE_ANY_FLAG); game->start_fen = strdup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); game->xb_setup = strdup("(PNBRQKpnbrqk) 8x8+0_fairy"); game->name = strdup("King of the hill"); game->finalise_variant(); return game; } game_t *create_sittuyin_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fm = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_PROMOTEWILD; bitboard_t diag = bitboard_t::board_diagonal[7] | bitboard_t::board_antidiagonal[7]; bitboard_t pz[2]; bitboard_t pp[2] = { diag & diag.board_homeland[BLACK], diag & diag.board_homeland[WHITE] }; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fs, fs, 0, pz, "", "Silver general", "S,s", "S", 275); game->add_piece_type(fm, fm, 0, pz, "", "Ferz", "F,f", "F", 100); int ri = game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); int pi = game->add_piece_type(fp, fc, pf, pp, "F", "Pawn", "P,p", " ", 55); game->add_rule(RF_FORCE_DROPS | RF_PROMOTE_IN_PLACE | RF_QUIET_PROMOTION | RF_PROMOTE_BY_MOVE); game->set_maximum_number_of_pieces("F", WHITE, 1); game->set_maximum_number_of_pieces("F", BLACK, 1); for (int n = 0; npt.num_piece_types; n++) { if (n == ri) { game->pt.drop_zone[WHITE][n] = bitboard_t::board_rank[0]; game->pt.drop_zone[BLACK][n] = bitboard_t::board_rank[7]; } else { game->pt.drop_zone[WHITE][n] = bitboard_t::board_rank[0] | bitboard_t::board_rank[1] | bitboard_t::board_rank[2]; game->pt.drop_zone[BLACK][n] = bitboard_t::board_rank[7] | bitboard_t::board_rank[6] | bitboard_t::board_rank[5]; } game->pt.optional_promotion_zone[WHITE][n] = game->pt.promotion_zone[WHITE][n]; game->pt.optional_promotion_zone[BLACK][n] = game->pt.promotion_zone[BLACK][n]; } /* Only moves out of the promotion zone should promote */ game->pt.entry_promotion_zone[WHITE][pi].clear(); game->pt.entry_promotion_zone[BLACK][pi].clear(); /* Set the FEN string for the starting position */ game->start_fen = strdup("8/8/4pppp/pppp4/4PPPP/PPPP4/8/8[KFRRSSNNkfrrssnn] w - - 0 1"); game->xb_setup = strdup("(PN.R.F....SKpn.r.f....sk) 8x8+6_bughouse"); game->name = strdup("Sittuyin"); game->finalise_variant(); for (int n = 0; npt.num_piece_types; n++) { game->pt.optional_promotion_zone[WHITE][n] = game->pt.promotion_zone[WHITE][n]; game->pt.optional_promotion_zone[BLACK][n] = game->pt.promotion_zone[BLACK][n]; } return game; } game_t *create_makruk_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fm = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[5], bitboard_t::board_rank[2]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fs, fs, 0, pz, "", "Silver general", "S,s", "S", 275); game->add_piece_type(fm, fm, 0, pz, "", "Met", "M,m", "M", 150); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "M", "Pawn", "P,p", " ", 80); /* Set the FEN string for the starting position */ game->start_fen = strdup("rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w - -"); game->name = strdup("Makruk"); game->finalise_variant(); /* Sixth-rank promotions are never optional */ for (int n = 0; npt.num_piece_types; n++) { game->pt.optional_promotion_zone[WHITE][n].clear(); game->pt.optional_promotion_zone[BLACK][n].clear(); } return game; } game_t *create_aiwok_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fa = game->movegen.define_piece_move("leap (1,1)|(1,2)") | fr; move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[5], bitboard_t::board_rank[2]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fs, fs, 0, pz, "", "Silver general", "S,s", "S", 275); game->add_piece_type(fa, fa, 0, pz, "", "Ai-Wok", "A,a", "A",1050); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "A", "Pawn", "P,p", " ", 80); /* Set the FEN string for the starting position */ game->start_fen = strdup("rnsaksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKASNR w - -"); game->xb_setup = strdup("(PN.R...A..SKpn.r...a..sk) 8x8+0_makruk"); game->name = strdup("Ai-Wok"); game->finalise_variant(); /* Sixth-rank promotions are never optional */ for (int n = 0; npt.num_piece_types; n++) { game->pt.optional_promotion_zone[WHITE][n].clear(); game->pt.optional_promotion_zone[BLACK][n].clear(); } return game; } game_t *create_asean_game(const char *) { int files = 8; int ranks = 8; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fm = game->movegen.define_piece_move("leap (1,1)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fs = game->movegen.define_piece_move("aleap (0,1)|(1,1)|(1,-1)|(-1,-1)|(-1,1)"); move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); uint32_t kf = PF_ROYAL; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[7], bitboard_t::board_rank[0]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 325); game->add_piece_type(fs, fs, 0, pz, "", "Bishop", "B,b", "B", 275); game->add_piece_type(fm, fm, 0, pz, "", "Queen", "Q,q", "Q", 150); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 500); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); game->add_piece_type(fp, fc, 0, pp, "QRBN", "Pawn", "P,p", " ", 80); /* Set the FEN string for the starting position */ game->start_fen = strdup("rnbqkbnr/8/pppppppp/8/8/PPPPPPPP/8/RNBQKBNR w - -"); game->name = strdup("ASEAN"); game->finalise_variant(); return game; } game_t *create_omega_game(const char *) { int files = 12; int ranks = 10; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); move_flag_t fb = game->movegen.define_piece_move("slide (A,D)"); move_flag_t fr = game->movegen.define_piece_move("slide (H,V)"); move_flag_t fn = game->movegen.define_piece_move("leap (1,2)"); move_flag_t fk = game->movegen.define_piece_move("leap (1,0)|(1,1)"); move_flag_t fh = game->movegen.define_piece_move("leap (0,1)|(0,2)|(2,2)"); move_flag_t fw = game->movegen.define_piece_move("leap (1,1)|(1,3)"); move_flag_t fq = fb | fr; move_flag_t fp = game->movegen.define_piece_move("step N"); move_flag_t fc = game->movegen.define_piece_move("step NE, NW"); move_flag_t f2 = game->movegen.define_piece_move("step 3N"); uint32_t kf = PF_ROYAL; uint32_t pf = PF_SET_EP | PF_TAKE_EP; bitboard_t pz[2]; bitboard_t pp[2] = {bitboard_t::board_rank[9], bitboard_t::board_rank[0]}; bitboard_t pi[2] = {bitboard_t::board_rank[1], bitboard_t::board_rank[8]}; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N", 250); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B", 400); game->add_piece_type(fw, fw, 0, pz, "", "Wizard", "W,w", "W", 350); game->add_piece_type(fh, fh, 0, pz, "", "Champion", "C,c","C", 375); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R", 600); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q", 975); game->add_piece_type(fk, fk, kf, pz, "", "King", "K,k", "K", 0); int ind = game->add_piece_type(fp, fc, pf, pp, "QRBCWN", "Pawn", "P,p", " ", 100); /* Add special move types for pawns on their initial squares */ game->add_special_move("P", pi, f2); game->deduce_castle_flags(WHITE, 6, 8, 9); game->deduce_castle_flags(WHITE, 6, 4, 2); game->deduce_castle_flags(BLACK, 114, 116, 117); game->deduce_castle_flags(BLACK, 114, 112, 110); /* Now hack the movement tables to off-set the wizard squares by one rank */ bitboard_t ws = bitboard_t::board_corner; bitboard_t bb; for (int n=0; n<16; n++) { bitboard_t::board_rank[n] &= ~ws; bitboard_t::board_file[n] &= ~ws; } for (int n=0; n<32; n++) { bitboard_t::board_diagonal[n] &= ~ws; bitboard_t::board_antidiagonal[n] &= ~ws; } bitboard_t::board_dark ^= ws; bitboard_t::board_light ^= ws; /* Remap the wizard squares on the diagonals */ bitboard_t::diagonal_nr[0]--; bitboard_t::diagonal_nr[files*ranks-1]++; bitboard_t::anti_diagonal_nr[files-1]--; bitboard_t::anti_diagonal_nr[files*(ranks-1)]++; for (int n=0; n<32; n++) { bitboard_t::board_diagonal[n].clear(); bitboard_t::board_antidiagonal[n].clear(); } for (int sq=0; sq::diagonal_nr[sq]; bitboard_t::board_diagonal[n].set(sq); n = bitboard_t::anti_diagonal_nr[sq]; bitboard_t::board_antidiagonal[n].set(sq); } /* Adjust leaper tables */ for (int n=0; n<8; n++) game->movegen.step_mask[n] &=~ws; for (int n=0; nmovegen.number_of_leapers; n++) { for (int s=0; smovegen.leaper[n][s] &= ~ws; } for (int n=0; nmovegen.number_of_aleapers; n++) { for (int s=0; smovegen.aleaper[WHITE][n][s] &= ~ws; game->movegen.aleaper[BLACK][n][s] &= ~ws; } } for (int n=0; nmovegen.number_of_steppers; n++) { for (int s=0; smovegen.stepper_step[n][WHITE][s] &= ~ws; game->movegen.stepper_step[n][BLACK][s] &= ~ws; } } /* Correct leaper tables for wizard squares */ bb = ws & bitboard_t::board_south; while (!bb.is_empty()) { int sq = bb.bitscan(); bb.reset(sq); for (int n=0; nmovegen.number_of_leapers; n++) { bitboard_t moves = game->movegen.leaper[n][sq] >> files; game->movegen.leaper[n][sq] = moves; while (!moves.is_empty()) { int s2 = moves.bitscan(); moves.reset(s2); game->movegen.leaper[n][s2].set(sq); } } } bb = ws & bitboard_t::board_north; while (!bb.is_empty()) { int sq = bb.bitscan(); bb.reset(sq); for (int n=0; nmovegen.number_of_leapers; n++) { bitboard_t moves = game->movegen.leaper[n][sq] << files; game->movegen.leaper[n][sq] = moves; while (!moves.is_empty()) { int s2 = moves.bitscan(); moves.reset(s2); game->movegen.leaper[n][s2].set(sq); } } } /* Pawns can never reach wizard squares */ game->pt.prison[WHITE][ind] &= ~ws; game->pt.prison[BLACK][ind] &= ~ws; /* Map squares on the 12x12 board to the 12x10 bitboard representation */ game->virtual_files = 12; game->virtual_ranks = 12; for (int sq = 0; sq<12*12; sq++) game->square_to_bit[sq] = -1; game->top_left = 12*11; for (int r = 0; rsquare_to_bit[sq] = bit; } bb = (bitboard_t::board_east_edge | bitboard_t::board_west_edge) & ~ws; while (!bb.is_empty()) { int sq = bb.bitscan(); bb.reset(sq); game->remove_square(sq); } game->start_fen = strdup("wcrnbqkbnrcw/*pppppppppp*/*10*/*10*/*10*/*10*/*10*/*10*/*PPPPPPPPPP*/WCRNBQKBNRCW w KQkq -"); game->start_fen = strdup("w**********w/*crnbqkbnrc*/*pppppppppp*/*10*/*10*/*10*/*10*/*10*/*10*/*PPPPPPPPPP*/*CRNBQKBNRC*/W**********W w KQkq -"); game->xb_setup = strdup("(PNBRQ..C.W...........Kpnbrq..c.w...........k) 12x12+0_fairy"); game->name = strdup("Omega chess"); game->finalise_variant(); /* Relabel squares so their names are consistent with the 12x12 board rather than the 12x10 */ for (int n=0; n<128; n++) if (square_names[n]) square_names[n][0] = 0; for (int r = 0; r<12; r++) { for (int f = 0; f<12; f++) { //printf("% 4d ", game->square_to_bit[f+r*12]); if (game->square_to_bit[f+r*12] >= 0) { snprintf(square_names[game->square_to_bit[f+r*12]], 10, "%c%d", 'a'+f, 1+r); //printf("%4s", square_names[game->square_to_bit[f+r*12]]); } else { //printf(" "); } } //printf("\n"); } keep_labels = true; return game; } template struct file_piece_description_t { bitboard_t special_zone[2]; bitboard_t promotion_zone[MAX_PZ][2]; bitboard_t optional_promotion_zone[2]; bitboard_t prison_zone[2]; bitboard_t block_zone[2]; bitboard_t drop_zone[2]; bitboard_t entry_promotion_zone[2]; char *name; char *abbr; char *symbol; char *promotion[MAX_PZ]; char *demotion; char *allowed_victim; move_flag_t move, capture, special, initial; uint32_t flags; int value; int max[2]; bool set_capture; bool set_prison; bool set_block; bool set_optional_promotion; bool set_drop_zone; bool set_entry_promotion_zone; int pz_count; }; template struct board_zone_description_t { char *name; bitboard_t zone; }; template static void add_piece_to_game(game_template_t *game, const file_piece_description_t *pd) { move_flag_t cflags = pd->move; if (pd->set_capture) cflags = pd->capture; int id = game->add_piece_type(pd->move, cflags, pd->flags, pd->promotion_zone[0], pd->promotion[0], pd->name, pd->symbol, pd->abbr, pd->value); if (pd->special) { char *s = strdup(pd->symbol); char *p = strchr(s, ','); if (p) *p = 0; game->add_special_move(s, pd->special_zone, pd->special); free(s); } if (pd->initial) { char *s = strdup(pd->symbol); char *p = strchr(s, ','); if (p) *p = 0; game->add_initial_move(s, pd->initial); free(s); } if (pd->max[WHITE] > 0) game->pt.piece_maximum[id][WHITE] = pd->max[WHITE]; if (pd->max[BLACK] > 0) game->pt.piece_maximum[id][BLACK] = pd->max[BLACK]; if (pd->set_prison) { game->pt.prison[WHITE][id] = pd->prison_zone[WHITE]; game->pt.prison[BLACK][id] = pd->prison_zone[BLACK]; } if (pd->set_block) { game->pt.block[WHITE][id] = pd->block_zone[WHITE]; game->pt.block[BLACK][id] = pd->block_zone[BLACK]; } if (pd->set_drop_zone) { game->pt.drop_zone[WHITE][id] = pd->drop_zone[WHITE]; game->pt.drop_zone[BLACK][id] = pd->drop_zone[BLACK]; } for (int k=1; kpz_count; k++) { game->pt.promotion[id][k].zone[WHITE] = pd->promotion_zone[k][WHITE]; game->pt.promotion[id][k].zone[BLACK] = pd->promotion_zone[k][BLACK]; game->pt.promotion[id][k].string = strdup(pd->promotion[k]); } if (pd->set_optional_promotion) { game->pt.optional_promotion_zone[WHITE][id] = pd->optional_promotion_zone[WHITE]; game->pt.optional_promotion_zone[BLACK][id] = pd->optional_promotion_zone[BLACK]; game->pt.promotion_zone[WHITE][id] |= pd->optional_promotion_zone[WHITE]; game->pt.promotion_zone[BLACK][id] |= pd->optional_promotion_zone[BLACK]; game->pt.pzset[id] = true; } if (pd->set_entry_promotion_zone) { game->pt.entry_promotion_zone[WHITE][id] = pd->entry_promotion_zone[WHITE]; game->pt.entry_promotion_zone[BLACK][id] = pd->entry_promotion_zone[BLACK]; } if (pd->demotion) game->pt.demotion_string[id] = strdup(pd->demotion); if (pd->allowed_victim) game->pt.allowed_victim[id] = strdup(pd->allowed_victim); } template game_template_t *parse_game_description(FILE *f, const char *variant_name, int files, int ranks) { board_zone_description_t zone[8*sizeof(kind)+2]; file_piece_description_t *pd = NULL; int num_zones = 0; game_template_t *game = new game_template_t; game->set_board_size(files, ranks); game->name = strdup(variant_name); char line[4096]; zone[num_zones].name = strdup("empty"); zone[num_zones].zone.clear(); num_zones++; zone[num_zones].name = strdup("all"); zone[num_zones].zone = bitboard_t::board_all; num_zones++; while (!feof(f)) { if (fgets(line, sizeof line, f) == 0) continue; /* Strip away comments */ char *s = strstr(line, "#"); if (s) s[0] = '\0'; /* Snip end-of-line */ s = strstr(line, "\n"); if (s) s[0] = '\0'; /* Strip trailing space */ s = line+strlen(line)-1; while (s > line && isspace(s[0])) { s[0] = '\0'; s--; } /* Skip empty lines */ if (s[0] == '\0') continue; /* New variant - we are done */ if (strstr(line, "Variant:") == line) { break; } /* Set board size - can't occur more than once!*/ if (strstr(line, "Board:") == line) { delete game; return NULL; } /* Starting position (FEN) */ if (strstr(line, "FEN:") == line) { char *s = line+4; s = strstr(s, "\""); if (!s) continue; s++; char *eof = strstr(s, "\""); if (!eof) continue; eof[0] = '\0'; game->start_fen = strdup(s); if (game->xb_setup == NULL) game->xb_setup = strdup("(PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk)"); continue; } /* Piece description for XBoard */ if ((strstr(line, "XBoard pieces:") == line) || (strstr(line, "WinBoard pieces:") == line)) { char *s = strstr(line, ":") + 1; while (*s && isspace(*s)) s++; s = strstr(s, "\""); if (!s) continue; s++; char *eof = strstr(s, "\""); if (!eof) continue; if (strstr(s, " ")) eof = strstr(s, " "); eof[0] = '\0'; size_t len = eof - s; free(game->xb_setup); game->xb_setup = (char *)malloc(len+4); snprintf(game->xb_setup, len+3, "(%s)", s); continue; } /* Piece description for XBoard */ if ((strstr(line, "XBoard parent:") == line) || (strstr(line, "WinBoard parent:") == line)) { char *s = line+14; s = strstr(s, "\""); if (!s) continue; s++; char *eof = strstr(s, "\""); if (!eof) continue; eof[0] = '\0'; free(game->xb_parent); game->xb_parent = (char *)malloc(51); snprintf(game->xb_parent, 50, "%s", s); continue; } /* Define a region of the board */ if (strstr(line, "Zone:") == line) { char *name = line+5; while(isspace(*name)) name++; char *s = strstr(name, "="); char *sq; if (!s) continue; sq = s+1; s[0] = '\0'; s--; while(isspace(*s)) s--; s[1] = '\0'; zone[num_zones].name = strdup(name); s = strtok(sq, ","); while(isspace(*s)) s++; char file; int rank; sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; zone[num_zones].zone.clear(); zone[num_zones].zone.set(game->pack_rank_file(rank, file)); while ((s = strtok(NULL, ","))) { sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; zone[num_zones].zone.set(game->pack_rank_file(rank, file)); } num_zones++; continue; } /* Exclude a region on the board (for the purpose of move generation) */ if (strstr(line, "Exclude:") == line) { char *s = line+8; while(isspace(*s)) s++; char *sq = s; char file; int rank; s = strtok(sq, ","); while(isspace(*s)) s++; sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; game->remove_square(game->pack_rank_file(rank, file)); while ((s = strtok(NULL, ","))) { sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; game->remove_square(game->pack_rank_file(rank, file)); } continue; } /* Capture-the-flag, white flag (for black to capture) */ if (strstr(line, "WhiteFlag:") == line) { char *s = line+10; while(isspace(*s)) s++; char *sq = s; char file; int rank; s = strtok(sq, ","); sscanf(sq, "%c%d", &file, &rank); file -= 'a'; rank--; game->place_flag(BLACK, game->pack_rank_file(rank, file)); while ((s = strtok(NULL, ","))) { sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; game->place_flag(BLACK, game->pack_rank_file(rank, file)); } continue; } /* Capture-the-flag, black flag (for white to capture) */ if (strstr(line, "BlackFlag:") == line) { char *s = line+10; while(isspace(*s)) s++; char *sq = s; char file; int rank; s = strtok(sq, ","); sscanf(sq, "%c%d", &file, &rank); file -= 'a'; rank--; game->place_flag(WHITE, game->pack_rank_file(rank, file)); while ((s = strtok(NULL, ","))) { sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; game->place_flag(WHITE, game->pack_rank_file(rank, file)); } continue; } /* Define a new piece */ if (strstr(line, "Piece:") == line) { if (pd) { assert(game); add_piece_to_game(game, pd); free(pd->name); free(pd->abbr); free(pd->symbol); for (int k=0; kpromotion[k]); free(pd->demotion); free(pd); } pd = (file_piece_description_t *)calloc(1, sizeof *pd); s = line + 6; while(isspace(*s)) s++; while(*s == ' ') s++; pd->name = strdup(s); for (side_t side = WHITE; side < NUM_SIDES; side++) { pd->prison_zone[side] = bitboard_t::board_all; pd->block_zone[side] = bitboard_t::board_empty; pd->drop_zone[side] = bitboard_t::board_all; } continue; } if (strstr(line, "Move:") == line && pd) { s = line + 5; while(*s && isspace(*s)) s++; pd->move |= game->movegen.define_piece_move(s); continue; } if (strstr(line, "Drop zone:") == line && pd) { s = line + 7; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; ndrop_zone[WHITE] = zone[n].zone; break; } s = p; while (isspace(*s)) s++; p = strstr(p, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; ndrop_zone[BLACK] = zone[n].zone; break; } pd->set_drop_zone = true; /* Do not override the drop table to remove the graveyard */ pd->flags |= PF_DROPDEAD; } if (strstr(line, "Prison:") == line && pd) { s = line + 7; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; nprison_zone[WHITE] = zone[n].zone; break; } s = p; while (*s && isspace(*s)) s++; /* Find the corresponding zone */ for(int n=0; nprison_zone[BLACK] = zone[n].zone; break; } pd->set_prison = true; } if (strstr(line, "Block:") == line && pd) { s = line + 7; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; nblock_zone[WHITE] = zone[n].zone; break; } s = p; while (*s && isspace(*s)) s++; /* Find the corresponding zone */ for(int n=0; nblock_zone[BLACK] = zone[n].zone; break; } pd->set_block = true; } if (strstr(line, "Special:") == line && pd) { s = line + 8; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; nspecial_zone[WHITE] = zone[n].zone; break; } s = p; while (isspace(*s)) s++; p = strstr(p, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; nspecial_zone[BLACK] = zone[n].zone; break; } while (isspace(*p)) p++; pd->special |= game->movegen.define_piece_move(p); continue; } if (strstr(line, "Initial:") == line && pd) { s = line + 8; while(*s && isspace(*s)) s++; pd->initial |= game->movegen.define_piece_move(s); continue; } if (strstr(line, "Capture:") == line && pd) { s = line + 8; while(isspace(*s)) s++; pd->capture |= game->movegen.define_piece_move(s); pd->set_capture = true; continue; } if (strstr(line, "Castle:") == line && pd) { side_t side = WHITE; int from, to, rfrom; char file; int rank; s = line + 7; while(isspace(*s)) s++; /* Field 1: side */ if (strstr(s, "black") == s) side = BLACK; s += 5; /* From square */ while(isspace(*s)) s++; sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; from = game->pack_rank_file(rank, file); /* Rook from-square */ char *rs = strstr(s, "with"); if (!rs) continue; rs+=4; while(isspace(*rs)) rs++; sscanf(rs, "%c%d", &file, &rank); file -= 'a'; rank--; rfrom = game->pack_rank_file(rank, file); /* To square */ s = strstr(s, "-"); while (s && s < rs) { s++; while(isspace(*s)) s++; sscanf(s, "%c%d", &file, &rank); file -= 'a'; rank--; to = game->pack_rank_file(rank, file); /* Calculate castle masks */ game->deduce_castle_flags(side, from, to, rfrom); s = strstr(s, ","); } continue; } if (strstr(line, "Symbol:") == line && pd) { s = line + 7; while (isspace(*s)) s++; char *p = strstr(s, "\""); if (!p) continue; p++; s = strstr(p, "\""); if (!s) continue; s++; s[-1] = '\0'; pd->abbr = strdup(p); p = strstr(s, "\""); if (!p) continue; p++; s = strstr(p, "\""); if (!s) continue; s++; s[-1] = '\0'; pd->symbol = strdup(p); continue; } if (strstr(line, "Promotion:") == line && pd) { s = line + 10; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; npromotion_zone[pd->pz_count][WHITE] = zone[n].zone; break; } s = p; while (isspace(*s)) s++; p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; npromotion_zone[pd->pz_count][BLACK] = zone[n].zone; break; } p = strstr(p, "\""); p++; s = strstr(p, "\""); if (s) s[0] = '\0'; pd->promotion[pd->pz_count] = strdup(p); pd->pz_count++; continue; } if (strstr(line, "Optional promotion:") == line && pd) { s = line + 19; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; npromotion_zone[pd->pz_count][WHITE] = zone[n].zone; pd->optional_promotion_zone[WHITE] |= zone[n].zone; break; } s = p; while (isspace(*s)) s++; p = strstr(s, ","); if (p) { p[0] = '\0'; p++; } /* Find the corresponding zone */ for(int n=0; npromotion_zone[pd->pz_count][BLACK] = zone[n].zone; pd->optional_promotion_zone[BLACK] |= zone[n].zone; break; } pd->set_optional_promotion = true; if (p) { p = strstr(p, "\""); if(!p) continue; p++; s = strstr(p, "\""); if (s) s[0] = '\0'; pd->promotion[pd->pz_count] = strdup(p); } else continue; pd->pz_count++; continue; } if (strstr(line, "Entry promotion:") == line && pd) { s = line + 16; while (isspace(*s)) s++; char *p = strstr(s, ","); if (!p) continue; p[0] = '\0'; p++; /* Find the corresponding zone */ for(int n=0; nentry_promotion_zone[WHITE] = zone[n].zone; break; } s = p; while (isspace(*s)) s++; p = strstr(s, ","); if (p) { p[0] = '\0'; p++; } /* Find the corresponding zone */ for(int n=0; nentry_promotion_zone[BLACK] = zone[n].zone; break; } pd->set_entry_promotion_zone = true; continue; } if (strstr(line, "Demotion:") == line && pd) { s = line + 9; while (isspace(*s)) s++; char *p = s; p = strstr(p, "\""); if(!p) continue; p++; s = strstr(p, "\""); if (s) s[0] = '\0'; pd->demotion = strdup(p); continue; } if (strstr(line, "Allowed victims:") == line && pd) { s = line + 16; while (isspace(*s)) s++; char *p = s; p = strstr(p, "\""); if(!p) continue; p++; s = strstr(p, "\""); if (s) s[0] = '\0'; pd->allowed_victim = strdup(p); continue; } if (strstr(line, "Flags:") == line && pd) { char *p; s = line + 6; while(isspace(*s)) s++; while((p = strtok(s, ","))) { s = NULL; while(isspace(*p)) p++; while(*p == ' ') p++; if (strstr(p, "royal") == p) { pd->flags |= PF_ROYAL; } else if (strstr(p, "castle") == p) { /* Ignore, deprecated */ } else if (strstr(p, "set_ep") == p) { pd->flags |= PF_SET_EP; } else if (strstr(p, "take_ep") == p) { pd->flags |= PF_TAKE_EP; } else if (strstr(p, "drop_no_check") == p) { pd->flags |= PF_DROPNOCHECK; } else if (strstr(p, "drop_no_mate") == p) { pd->flags |= PF_DROPNOMATE; } else if (strstr(p, "drop_one_file") == p) { pd->flags |= PF_DROPONEFILE; } else if (strstr(p, "drop_dead") == p) { pd->flags |= PF_DROPDEAD; } else if (strstr(p, "no_mate") == p) { pd->flags |= PF_NOMATE; } else if (strstr(p, "shak") == p) { pd->flags |= PF_SHAK; } else if (strstr(p, "assimilate") == p) { pd->flags |= PF_ASSIMILATE; } else if (strstr(p, "no_retaliate") == p) { pd->flags |= PF_NO_RETALIATE; } else if (strstr(p, "endangered") == p) { pd->flags |= PF_ENDANGERED; } else if (strstr(p, "iron") == p) { pd->flags |= PF_IRON; } else if (strstr(p, "capture_flag") == p) { pd->flags |= PF_CAPTUREFLAG; } } continue; } if (strstr(line, "Value:") == line && pd) { sscanf(line+6, "%d", &pd->value); continue; } if (strstr(line, "Max:") == line && pd) { if (strstr(line, ",")) sscanf(line+5, "%d,%d", &pd->max[0], &pd->max[1]); else { sscanf(line+5, "%d", &pd->max[0]); pd->max[1] = pd->max[0]; } continue; } if (strstr(line, "Rule:") == line) { s = line + 5; while(isspace(*s)) s++; if (strstr(s, "checkmate") == s) { if (strstr(s, "loss")) { game->mate_score = LEGALWIN; } else if (strstr(s, "win")) { game->mate_score = -LEGALWIN; } else if (strstr(s, "draw")) { game->mate_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->mate_score = ILLEGAL; } } if (strstr(s, "stalemate") == s) { if (strstr(s, "loss")) { game->stale_score = LEGALWIN; } else if (strstr(s, "win")) { game->stale_score = -LEGALWIN; } else if (strstr(s, "draw")) { game->stale_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->stale_score = ILLEGAL; } } if (strstr(s, "repeat") == s) { int count = -1; if (sscanf(s, "repeat%d", &count) == 1 && count >= 0) { game->repeat_claim = count-1; if (game->repeat_claim <= 0) game->repeat_claim = INT_MAX; if (strstr(s, "loss")) { game->rep_score = -LEGALWIN; } else if (strstr(s, "win")) { game->rep_score = LEGALWIN; } else if (strstr(s, "draw")) { game->rep_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->rep_score = -ILLEGAL; } } } if (strstr(s, "check") == s) { int count = -1; if (sscanf(s, "check%d", &count) == 1 && count >= 1) { game->check_limit = count; if (strstr(s, "loss")) { game->check_score = LEGALWIN; } else if (strstr(s, "win")) { game->check_score = -LEGALWIN; } else if (strstr(s, "draw")) { game->check_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->check_score = -ILLEGAL; } } } if (strstr(s, "perpetual") == s) { if (strstr(s, "loss")) { game->perpetual = -LEGALWIN; } else if (strstr(s, "win")) { game->perpetual = LEGALWIN; } else if (strstr(s, "draw")) { game->perpetual = LEGALDRAW; } else if (strstr(s, "illegal")) { game->perpetual = -ILLEGAL; } else if (strstr(s, "repeat")) { game->perpetual = ILLEGAL+1; } } if (strstr(s, "loneking") == s) { if (strstr(s, "loss")) { game->bare_king_score = -LEGALWIN; } else if (strstr(s, "win")) { game->bare_king_score = LEGALWIN; } else if (strstr(s, "draw")) { game->bare_king_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->bare_king_score = ILLEGAL; } } if (strstr(s, "nopieces") == s) { if (strstr(s, "loss")) { game->no_piece_score = -LEGALWIN; } else if (strstr(s, "win")) { game->no_piece_score = LEGALWIN; } else if (strstr(s, "draw")) { game->no_piece_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->no_piece_score = ILLEGAL; } } if (strstr(s, "captureanyflag") == s) { game->board.rule_flags |= RF_CAPTURE_ANY_FLAG; if (strstr(s, "loss")) { game->flag_score = LEGALWIN; } else if (strstr(s, "win")) { game->flag_score = -LEGALWIN; } else if (strstr(s, "draw")) { game->flag_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->flag_score = ILLEGAL; } } if (strstr(s, "captureallflags") == s) { game->board.rule_flags |= RF_CAPTURE_ALL_FLAG; if (strstr(s, "loss")) { game->flag_score = LEGALWIN; } else if (strstr(s, "win")) { game->flag_score = -LEGALWIN; } else if (strstr(s, "draw")) { game->flag_score = LEGALDRAW; } else if (strstr(s, "illegal")) { game->flag_score = ILLEGAL; } } if (strstr(s, "movecountdraw") == s) { char *p = strstr(s, "="); if (p) { p++; int n = 50; sscanf(p, "%d", &n); n = (2*n)+1; if (n>127) n = 127; game->fifty_limit = n; } } if (strstr(s, "taboo")) game->board.rule_flags |= RF_KING_TABOO; if (strstr(s, "keep capture")) game->board.rule_flags |= RF_KEEP_CAPTURE; if (strstr(s, "return capture")) game->board.rule_flags |= RF_RETURN_CAPTURE; if (strstr(s, "forced capture")) game->board.rule_flags |= RF_FORCE_CAPTURE; if (strstr(s, "duplecheck")) game->board.rule_flags |= RF_KING_DUPLECHECK; if (strstr(s, "allow pickup")) game->board.rule_flags |= RF_ALLOW_PICKUP; if (strstr(s, "allow drop")) game->board.rule_flags |= RF_ALLOW_DROPS; if (strstr(s, "force drop")) game->board.rule_flags |= RF_FORCE_DROPS; if (strstr(s, "gate drop")) game->board.rule_flags |= RF_GATE_DROPS; if (strstr(s, "promote here")) game->board.rule_flags |= RF_PROMOTE_IN_PLACE; if (strstr(s, "promote drop")) game->board.rule_flags |= RF_PROMOTE_ON_DROP; if (strstr(s, "special init")) game->board.rule_flags |= RF_SPECIAL_IS_INIT; if (strstr(s, "bare rule")) game->board.rule_flags |= RF_USE_BARERULE; if (strstr(s, "chase rule")) game->board.rule_flags |= RF_USE_CHASERULE; if (strstr(s, "shak rule")) game->board.rule_flags |= RF_USE_SHAKMATE; if (strstr(s, "check any king")) game->board.rule_flags |= RF_CHECK_ANY_KING; if (strstr(s, "en-passant check")) game->board.rule_flags |= RF_NO_MOVE_PAST_CHECK; if (strstr(s, "quiet promotion")) game->board.rule_flags |= RF_QUIET_PROMOTION; if (strstr(s, "promote by move")) game->board.rule_flags |= RF_PROMOTE_BY_MOVE; } } /* Add the last remaining piece */ if (pd) { add_piece_to_game(game, pd); free(pd->name); free(pd->abbr); free(pd->symbol); for (int n=0; npz_count; n++) free(pd->promotion[n]); free(pd->demotion); free(pd); pd = NULL; } /* Free memory */ int n; for (n=0; nfinalise_variant(); return game; } game_t *create_game_from_file(const char *filename, const char *variant_name) { game_t *game = NULL; int files = 0; int ranks = 0; FILE *f; f = fopen(filename, "r"); if (!f) return game; char line[4096]; bool found_variant = false; while (!feof(f)) { if (fgets(line, sizeof line, f) == 0) continue; /* Strip away comments */ char *s = strstr(line, "#"); if (s) s[0] = '\0'; /* Snip end-of-line */ s = strstr(line, "\n"); if (s) s[0] = '\0'; /* Strip trailing space */ s = line+strlen(line)-1; while (s > line && isspace(s[0])) { s[0] = '\0'; s--; } /* New variant */ if (strstr(line, "Variant:") == line) { if (game) break; /* We're done loading the variant we're looking for */ if (strstr(line+8, variant_name)) { /* We've found the variant we're looking for */ found_variant = true; } continue; } /* Have we found the correct variant? */ if (!found_variant) continue; /* Set board size */ if (strstr(line, "Board:") == line) { s = line+6; sscanf(s, "%dx%d", &files, &ranks); /* Check whether the input is valid */ if (files > 16 || ranks > 16 || (files*ranks > 128)) { fclose(f); char msg[256]; int n = 0; if (files > 16) n += snprintf(msg+n, 256-n, "Board has too many files."); if (ranks > 16) n += snprintf(msg+n, 256-n, "Board has too many ranks."); if (files*ranks>128) n += snprintf(msg+n, 256-n, "Board has too many files."); if (game->xboard_output) game->xboard_output("tellusererror %s\n", msg); return NULL; } if (files * ranks <= 64) game = parse_game_description(f, variant_name, files, ranks); else game = parse_game_description(f, variant_name, files, ranks); break; } continue; } fclose(f); return game; } #endif SjaakII/include/xstring.h000644 000765 000024 00000000734 12465222762 016375 0ustar00eglebbkstaff000000 000000 #ifndef XSTRING_H #define XSTRING_H #include #include static inline char *trim(char *s) { char *p = s; while (*p && isspace(*p)) p++; if (p!= s) { for (size_t n = 0; n<=strlen(p); n++) s[n] = p[n]; } return s; } static inline char *chomp(char *s) { char *p; p = strstr(s, "\n"); if (p) *p = '\0'; return s; } static inline bool streq(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; } #endif SjaakII/src/chess.cc000644 000765 000024 00000005223 12433153270 015274 0ustar00eglebbkstaff000000 000000 #define __STDC_FORMAT_MACROS 1 #include #include #include #include #include #include #include "bitboard.h" #include "board.h" #include "movegen.h" #include "aligned_malloc.h" #include "hashkey.h" #include "game.h" #include "variants.h" uint64_t perft(game_t *game, int depth, int root) { movelist_t movelist; side_t me = game->get_side_to_move(); uint64_t nodes = 0; int n; if (depth == 0) return 1; /* Check if previous move left the player in check */ game->generate_moves(&movelist); for (n=0; nplaymove(movelist.move[n]); if (!game->player_in_check(me)) /* Don't count illegal moves */ count = perft(game, depth-1, root - 1); nodes += count; if (root > 0) printf("%8s %10"PRIu64" %10"PRIu64"\n", move_to_string(movelist.move[n], NULL), count, nodes); game->takeback(); } return nodes; } #define streq(s1, s2) (strcmp((s1), (s2)) == 0) game_t *create_variant_game(const char *variant_name) { if (streq(variant_name, "normal")) { return create_standard_game(); } else if (streq(variant_name, "losalamos")) { return create_losalamos_game(); } else if (streq(variant_name, "micro")) { return create_micro_game(); } else if (streq(variant_name, "capablanca")) { return create_capablanca_game(); } else if (streq(variant_name, "create_pocketknight_game")) { return create_pocketknight_game(); } return NULL; } int main(void) { movelist_t movelist; initialise_hash_keys(); game_t *game = create_variant_game("normal"); if (!game) { printf("Could not create game object!\n"); exit(0); } game->start_new_game(); //game->setup_fen_position("rnbqkbnr/1pp1pppp/p7/3P4/8/8/PP1PPPPP/RNBQKBNR b KQkq -"); //game->setup_fen_position("rnbqkbnr/ppp1pppp/8/3P4/8/8/PP1PPPPP/RNBQKBNR b KQkq -"); uint64_t t = get_timer(); game->print_bitboards(); game->print_board(); game->generate_moves(&movelist); movelist.print(); //printf("%d\n", game->eval()); for (int n = 1; n<4+1; n++) { uint64_t nodes = perft(game, n, 0); uint64_t tt = get_timer(); if (tt == t) tt++; printf("%2d %10lld %5.2f %12.2fnps\n", n, (long long int)nodes, (double)(tt - t)/1000000.0,nodes*1.0e6/(tt-t)); t = tt; } exit(0); move_t move = movelist.move[0]; printf("%s\n", move_to_string(move)); game->playmove(move); game->print_bitboards(); game->takeback(); game->print_bitboards(); return 0; } SjaakII/src/eval/pst.cc000644 000765 000024 00000003456 12433153270 015732 0ustar00eglebbkstaff000000 000000 #include #include "pieces.h" #include "board.h" #include "pst.h" int psq_map[NUM_SIDES][128]; /* Base centralisation score for each square */ int centre_table[128]; /* Base advancement score for each square */ int advance_table[128]; /* Extra advancement score for proximity to promotion zone, for pawns */ int promo_table[128]; /* End game bonus for driving the defending king to the edge */ int lone_king_table[128]; /* Base shield score, for pawn shields */ int shield_table[128]; /* Initialise the base tables for the current board size */ void initialise_base_evaluation_tables(int files, int ranks) { int r, f; int c = files; if (ranks > c) c = ranks; int aa = 0; for (r = 0; r files/2+1 && (r == 1)) shield_table[s] = 1; lone_king_table[s] = 6*(abs(2*r - ranks + 1) + abs(2*f - files + 1)); psq_map[WHITE][s] = s; psq_map[BLACK][s] = f + (ranks-1 - r) * files; } } for (r = 0; r. */ #include #include #include "evalhash.h" #include "bool.h" /* FIXME: tune this */ #define NUM_BUCKETS 2 static inline size_t map_key_to_index(uint64_t key, size_t nelem) { return key & (nelem - 1); } static inline uint64_t map_key_to_lock(uint64_t key) { return key & ~0xffffll; } static int16_t extract_score(uint64_t data) { uint16_t uscore = data & 0xffff; return (int16_t)uscore; } eval_hash_table_t *create_eval_hash_table(size_t nelem) { eval_hash_table_t *table = calloc(1, sizeof *table); table->number_of_elements = nelem; table->data = calloc(nelem + NUM_BUCKETS, sizeof *table->data); return table; } void destroy_eval_hash_table(eval_hash_table_t *table) { if (table) { free(table->data); free(table); } } bool query_eval_table_entry(eval_hash_table_t *table, uint64_t key, int16_t *score) { size_t index, b; uint64_t lock; if (!table) return false; /* Map the key onto the array index, check if entry is there */ index = map_key_to_index(key, table->number_of_elements); lock = map_key_to_lock(key); //printf("0x%016llx 0x%016llx\n", key, lock); for (b=0; bdata[index + b].data; if (map_key_to_lock(data) == lock) { *score = extract_score(data); return true; } } return false; } void store_eval_hash_entry(eval_hash_table_t *table, uint64_t key, int16_t score) { size_t index, b; uint64_t lock, data; if (!table) return; /* Map the key onto the array index, check if entry is there */ index = map_key_to_index(key, table->number_of_elements); lock = map_key_to_lock(key); /* Find out where to store the entry. * If it already exists in one of the buckets we simply store it there * after moving the bucket to the start of the queue. * If it isn't present yet we shift the entire queue back one place and * create a new entry at the start. */ for (b = 0; bdata[index+b].data) == lock) { if (b) { eval_hash_t h = table->data[index]; table->data[index] = table->data[index+b]; table->data[index+b] = h; } goto store; } } /* If we made it here we need to create a new entry at the start of the * queue. */ for (b=NUM_BUCKETS-1; b>0; b--) table->data[index+b] = table->data[index+b-1]; store: data = lock | ((uint16_t)score); table->data[index].data = data; #ifdef DEBUG_EVHASH table->data[index].key = key; #endif } SjaakII/src/hash/hashkey.c000644 000765 000024 00000003456 12433153270 016411 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "hashkey.h" #include "genrand.h" uint64_t piece_key[MAX_PIECE_TYPES][2][128]; uint64_t hold_key[MAX_PIECE_TYPES][2][128]; uint64_t side_to_move_key; uint64_t flag_key[2][8]; uint64_t en_passant_key[128]; uint64_t genrand64(void) { return ((uint64_t)genrandui())<<32 | genrandui(); } void initialise_hash_keys(void) { int colour, piece, square; sgenrand(0x1422CE55); for (piece = 0; piece < MAX_PIECE_TYPES; piece++) for (colour = 0; colour<2; colour++) for (square = 0; square<128; square++) piece_key[piece][colour][square] = genrand64(); for (square = 0; square<128; square++) en_passant_key[square] = genrand64(); for (colour = 0; colour<2; colour++) for (square = 0; square<8; square++) flag_key[colour][square] = genrand64(); side_to_move_key = genrand64(); for (piece = 0; piece < MAX_PIECE_TYPES; piece++) for (colour = 0; colour<2; colour++) for (square = 0; square<128; square++) hold_key[piece][colour][square] = square ? genrand64() : 0; } SjaakII/src/hash/hashtable.c000644 000765 000024 00000010275 12452217646 016716 0ustar00eglebbkstaff000000 000000 /* Jazz, a program for playing chess * Copyright (C) 2009, 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "hashtable.h" #include "prefetch.h" #define HASH_BUCKETS 3 static inline size_t map_key_to_index(uint64_t key, size_t nelem) { return key & ( nelem - 1); } static inline uint32_t map_key_to_lock(uint64_t key) { return key >> 32; } hash_table_t *create_hash_table(size_t nelem) { hash_table_t *table; if (!nelem) return NULL; table = calloc(1, sizeof *table); table->data = calloc(nelem + HASH_BUCKETS, sizeof *table->data); table->number_of_elements = nelem; return table; } void destroy_hash_table(hash_table_t *table) { if (table) { free(table->data); free(table); } } /* Encrypt transposition table entry using the "xor trick" for lockless * hashing. */ static void crypt(hash_table_entry_t *hash) { uint64_t *h; assert(sizeof(hash_table_entry_t) == 3*sizeof(uint64_t)); h = (uint64_t *)hash; //h[1] ^= h[2]; h[0] ^= h[1]; } //static lock_t hash_lock = 0; void store_table_entry(hash_table_t *table, uint64_t key, int depth, int score, unsigned int flags, move_t best_move) { hash_table_entry_t *data; hash_table_entry_t *worst_data = NULL; size_t index, b; uint32_t lock; if (!table) return; /* Map the key onto the array index, check if entry is there */ index = map_key_to_index(key, table->number_of_elements); lock = map_key_to_lock(key); worst_data = data = table->data + index; //acquire_lock(&hash_lock); /* Check all buckets */ for (b=1; bdepth || hash.generation < table->generation || hash.generation == 0) { worst_data = data+b; } } data = worst_data; data->lock = lock; data->depth = depth; data->score = score; data->flags = flags; data->best_move = best_move; data->generation = table->generation; crypt(data); table->write_count++; //release_lock(&hash_lock); } bool retrieve_table(hash_table_t *table, uint64_t key, int *depth, int *score, unsigned int *flags, move_t *best_move) { size_t index, b; uint32_t lock; if (!table) return false; /* Map the key onto the array index, check if entry is there */ index = map_key_to_index(key, table->number_of_elements); lock = map_key_to_lock(key); //acquire_lock(&hash_lock); /* Check all buckets */ b = 0; for (b = 0; bdata[index+b]; crypt(&hash); if (hash.lock == lock) { hash.generation = table->generation; *depth = hash.depth; *score = hash.score; *flags = hash.flags; *best_move = hash.best_move; //release_lock(&hash_lock); return true; } } //release_lock(&hash_lock); return false; } void prepare_hashtable_search(hash_table_t *table) { table->generation++; table->write_count = 0; } void prefetch_hashtable(hash_table_t *table, uint64_t key) { size_t index; if (table) { index = map_key_to_index(key, table->number_of_elements); prefetch(table->data+index); } } int count_unused_table_entries(hash_table_t *table) { size_t n; int count; if (!table) return 0; count = 0; for (n=0; nnumber_of_elements; n++) { if (table->data[n].generation == 0) count++; } return count; } SjaakII/src/misc/aligned_malloc.c000644 000765 000024 00000002052 12452217646 017717 0ustar00eglebbkstaff000000 000000 #include #include #include "aligned_malloc.h" #include "assert.h" typedef struct { void *real_ptr; void *align_ptr; } aligned_memory_pool_t; static aligned_memory_pool_t *pool = NULL; static int pool_size = 0; static int pool_free = 0; /* Allocate aligned memory blocks. */ void *aligned_malloc(size_t size, size_t align) { uint8_t *real_ptr; void *align_ptr; assert((align & (align-1)) == 0); if (pool_free == pool_size) { pool_size = 2*(pool_size)+1; pool = realloc(pool, pool_size * sizeof *pool); } real_ptr = malloc(size + align-1); align_ptr = (void *) ((uint64_t)(real_ptr + align-1) & ~(align-1)); pool[pool_free].real_ptr = real_ptr; pool[pool_free].align_ptr = align_ptr; pool_free++; return align_ptr; } /* Free an aligned memory block */ void aligned_free(void *ptr) { int n; for (n=0; n. */ #include "ansi.h" #if defined _WIN32 || defined _WIN64 #undef DATADIR #include "compilerdef.h" #include #include void ansi_code(const char *s) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); WORD attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; int ansi_code = 0; CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(hConsole, &csbi)) attr = csbi.wAttributes; sscanf(s + 2, "%d", &ansi_code); switch (ansi_code) { case 0: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; break; case 1: attr |= FOREGROUND_INTENSITY; break; case 30: attr &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); break; case 37: attr |= (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); break; case 44: attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED); attr |= BACKGROUND_BLUE; break; case 45: attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED); attr |= BACKGROUND_BLUE | BACKGROUND_RED; break; case 46: attr &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED); attr |= BACKGROUND_BLUE | BACKGROUND_GREEN; break; default: break; } SetConsoleTextAttribute(hConsole, attr); } #else #include void ansi_code(const char *s) { printf("%s", s); } #endif SjaakII/src/misc/cfgpath.c000644 000765 000024 00000027256 13014616475 016414 0ustar00eglebbkstaff000000 000000 /** * @file cfgpath.h * @brief Cross platform methods for obtaining paths to configuration files. * * Copyright (C) 2013 Adam Nielsen * * This code is placed in the public domain. You are free to use it for any * purpose. If you add new platform support, please contribute a patch! * * Example use: * * char cfgdir[256]; * get_user_config_file(cfgdir, sizeof(cfgdir), "myapp"); * if (cfgdir[0] == 0) { * printf("Unable to find home directory.\n"); * return 1; * } * printf("Saving configuration file to %s\n", cfgdir); * * A number of constants are also defined: * * - MAX_PATH: Maximum length of a path, in characters. Used to allocate a * char array large enough to hold the returned path. * * - PATH_SEPARATOR_CHAR: The separator between folders. This will be either a * forward slash or a backslash depending on the platform. This is a * character constant. * * - PATH_SEPARATOR_STRING: The same as PATH_SEPARATOR_CHAR but as a C string, * to make it easier to append to other string constants. */ #ifdef __APPLE__ #define __unix__ #endif #if defined _MSC_VER # define _CRT_SECURE_NO_WARNINGS #endif #undef DATADIR #ifdef __unix__ #include #include #include #define MAX_PATH 512 /* arbitrary value */ #define PATH_SEPARATOR_CHAR '/' #define PATH_SEPARATOR_STRING "/" #elif defined(WIN32) || defined(_WIN32) #include /* MAX_PATH is defined by the Windows API */ #define PATH_SEPARATOR_CHAR '\\' #define PATH_SEPARATOR_STRING "\\" #else #error cfgpath.h functions have not been implemented for your platform! Please send patches. #endif /** Get an absolute path to a single configuration file, specific to this user. * * This function is useful for programs that need only a single configuration * file. The file is unique to the user account currently logged in. * * Output is typically: * * Windows: C:\Users\jcitizen\AppData\Roaming\appname.ini * unix: /home/jcitizen/.config/appname.conf * Mac: * * @param out * Buffer to write the path. On return will contain the path, or an empty * string on error. * * @param maxlen * Length of out. Must be >= MAX_PATH. * * @param appname * Short name of the application. Avoid using spaces or version numbers, and * use lowercase if possible. * * @post The file may or may not exist. * @post The folder holding the file is created if needed. */ void get_user_config_file(char *out, size_t maxlen, const char *appname) { #ifdef __unix__ const char *out_orig = out; char *home = getenv("XDG_CONFIG_HOME"); unsigned int config_len = 0; if (!home) { home = getenv("HOME"); if (!home) { // Can't find home directory out[0] = 0; return; } config_len = strlen(".config/"); } unsigned int home_len = strlen(home); unsigned int appname_len = strlen(appname); const int ext_len = strlen(".conf"); /* first +1 is "/", second is terminating null */ if (home_len + 1 + config_len + appname_len + ext_len + 1 > maxlen) { out[0] = 0; return; } memcpy(out, home, home_len); out += home_len; *out = '/'; out++; if (config_len) { memcpy(out, ".config/", config_len); out += config_len; /* Make the .config folder if it doesn't already exist */ mkdir(out_orig, 0755); } memcpy(out, appname, appname_len); out += appname_len; memcpy(out, ".conf", ext_len); out += ext_len; *out = 0; #elif defined(WIN32) unsigned int appname_len; if (maxlen < MAX_PATH) { out[0] = 0; return; } if (!SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, out))) { out[0] = 0; return; } /* We don't try to create the AppData folder as it always exists already */ appname_len = (unsigned int)strlen(appname); if (strlen(out) + 1 + appname_len + strlen(".ini") + 1 > maxlen) { out[0] = 0; return; } strcat(out, "\\"); strcat(out, appname); strcat(out, ".ini"); #endif } /** Get an absolute path to a configuration folder, specific to this user. * * This function is useful for programs that need to store multiple * configuration files. The output is a folder (which may not exist and will * need to be created) suitable for storing a number of files. * * The returned path will always end in a platform-specific trailing slash, so * that a filename can simply be appended to the path. * * Output is typically: * * Windows: C:\Users\jcitizen\AppData\Roaming\appname\ * unix: /home/jcitizen/.config/appname/ * Mac: * * @param out * Buffer to write the path. On return will contain the path, or an empty * string on error. * * @param maxlen * Length of out. Must be >= MAX_PATH. * * @param appname * Short name of the application. Avoid using spaces or version numbers, and * use lowercase if possible. * * @post The folder is created if needed. */ void get_user_config_folder(char *out, size_t maxlen, const char *appname) { #ifdef __unix__ const char *out_orig = out; char *home = getenv("XDG_CONFIG_HOME"); unsigned int config_len = 0; if (!home) { home = getenv("HOME"); if (!home) { // Can't find home directory out[0] = 0; return; } config_len = strlen(".config/"); } unsigned int home_len = strlen(home); unsigned int appname_len = strlen(appname); /* first +1 is "/", second is trailing "/", third is terminating null */ if (home_len + 1 + config_len + appname_len + 1 + 1 > maxlen) { out[0] = 0; return; } memcpy(out, home, home_len); out += home_len; *out = '/'; out++; if (config_len) { memcpy(out, ".config/", config_len); out += config_len; /* Make the .config folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); } memcpy(out, appname, appname_len); out += appname_len; /* Make the .config/appname folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); *out = '/'; out++; *out = 0; #elif defined(WIN32) unsigned int appname_len; if (maxlen < MAX_PATH) { out[0] = 0; return; } if (!SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, out))) { out[0] = 0; return; } /* We don't try to create the AppData folder as it always exists already */ appname_len = (unsigned int)strlen(appname); if (strlen(out) + 1 + appname_len + 1 + 1 > maxlen) { out[0] = 0; return; } strcat(out, "\\"); strcat(out, appname); /* Make the AppData\appname folder if it doesn't already exist */ CreateDirectoryA(out, NULL); strcat(out, "\\"); #endif } /** Get an absolute path to a data storage folder, specific to this user. * * This function is useful for programs that need to store larger amounts of * data specific to each user. The output is a folder (which may not exist and * will need to be created) suitable for storing a number of data files. * * This path should be used for persistent, important data files the user would * want to keep. Do not use this path for temporary files, cache files, or * other files that can be recreated if they are deleted. Use * get_user_cache_folder() for those instead. * * The returned path will always end in a platform-specific trailing slash, so * that a filename can simply be appended to the path. * * Output is typically: * * Windows: C:\Users\jcitizen\AppData\Roaming\appname-data\ * unix: /home/jcitizen/.local/share/appname/ * Mac: * * @param out * Buffer to write the path. On return will contain the path, or an empty * string on error. * * @param maxlen * Length of out. Must be >= MAX_PATH. * * @param appname * Short name of the application. Avoid using spaces or version numbers, and * use lowercase if possible. * * @post The folder is created if needed. */ void get_user_data_folder(char *out, size_t maxlen, const char *appname) { #ifdef __unix__ const char *out_orig = out; char *home = getenv("XDG_DATA_HOME"); unsigned int config_len = 0; if (!home) { home = getenv("HOME"); if (!home) { // Can't find home directory out[0] = 0; return; } config_len = strlen(".local/share/"); } unsigned int home_len = strlen(home); unsigned int appname_len = strlen(appname); /* first +1 is "/", second is trailing "/", third is terminating null */ if (home_len + 1 + config_len + appname_len + 1 + 1 > maxlen) { out[0] = 0; return; } memcpy(out, home, home_len); out += home_len; *out = '/'; out++; if (config_len) { memcpy(out, ".local/share/", config_len); out += config_len; /* Make the .local/share folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); } memcpy(out, appname, appname_len); out += appname_len; /* Make the .local/share/appname folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); *out = '/'; out++; *out = 0; #elif defined(WIN32) /* No distinction under Windows */ get_user_config_folder(out, maxlen, appname); #endif } /** Get an absolute path to a temporary storage folder, specific to this user. * * This function is useful for programs that temporarily need to store larger * amounts of data specific to each user. The output is a folder (which may * not exist and will need to be created) suitable for storing a number of * temporary files. The files may be lost or deleted when the program * terminates. * * This path should be used for temporary, unimportant data files that can * safely be deleted after the program terminates. Do not use this path for * any important files the user would want to keep. Use get_user_data_folder() * for those instead. * * The returned path will always end in a platform-specific trailing slash, so * that a filename can simply be appended to the path. * * Output is typically: * * Windows: C:\Users\jcitizen\AppData\Local\appname\ * unix: /home/jcitizen/.cache/appname/ * Mac: * * @param out * Buffer to write the path. On return will contain the path, or an empty * string on error. * * @param maxlen * Length of out. Must be >= MAX_PATH. * * @param appname * Short name of the application. Avoid using spaces or version numbers, and * use lowercase if possible. * * @post The folder is created if needed. */ void get_user_cache_folder(char *out, size_t maxlen, const char *appname) { #ifdef __unix__ const char *out_orig = out; char *home = getenv("XDG_CACHE_HOME"); unsigned int config_len = 0; if (!home) { home = getenv("HOME"); if (!home) { // Can't find home directory out[0] = 0; return; } config_len = strlen(".cache/"); } unsigned int home_len = strlen(home); unsigned int appname_len = strlen(appname); /* first +1 is "/", second is trailing "/", third is terminating null */ if (home_len + 1 + config_len + appname_len + 1 + 1 > maxlen) { out[0] = 0; return; } memcpy(out, home, home_len); out += home_len; *out = '/'; out++; if (config_len) { memcpy(out, ".cache/", config_len); out += config_len; /* Make the .cache folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); } memcpy(out, appname, appname_len); out += appname_len; /* Make the .cache/appname folder if it doesn't already exist */ *out = '\0'; mkdir(out_orig, 0755); *out = '/'; out++; *out = 0; #elif defined(WIN32) unsigned int appname_len; if (maxlen < MAX_PATH) { out[0] = 0; return; } if (!SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, out))) { out[0] = 0; return; } /* We don't try to create the AppData folder as it always exists already */ appname_len = (unsigned int)strlen(appname); if (strlen(out) + 1 + appname_len + 1 + 1 > maxlen) { out[0] = 0; return; } strcat(out, "\\"); strcat(out, appname); /* Make the AppData\appname folder if it doesn't already exist */ CreateDirectoryA(out, NULL); strcat(out, "\\"); #endif } SjaakII/src/misc/genrand.c000644 000765 000024 00000010347 12462373750 016411 0ustar00eglebbkstaff000000 000000 /* A C-program for MT19937: Integer version (1998/4/6) */ /* genrand() generates one pseudorandom unsigned integer (32bit) */ /* which is uniformly distributed among 0 to 2^32-1 for each */ /* call. sgenrand(seed) set initial values to the working area */ /* of 624 words. Before genrand(), sgenrand(seed) must be */ /* called once. (seed is any 32-bit integer except for 0). */ /* Coded by Takuji Nishimura, considering the suggestions by */ /* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ /* Modified by Evert Glebbeek 1999-2015 for use in his programs. */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later */ /* version. */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* See the GNU Library General Public License for more details. */ /* You should have received a copy of the GNU Library General */ /* Public License along with this library; if not, write to the */ /* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ /* 02111-1307 USA */ /* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ /* When you use this, send an email to: matumoto@math.keio.ac.jp */ /* with an appropriate reference to your work. */ /* REFERENCE */ /* M. Matsumoto and T. Nishimura, */ /* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */ /* Pseudo-Random Number Generator", */ /* ACM Transactions on Modeling and Computer Simulation, */ /* Vol. 8, No. 1, January 1998, pp 3--30. */ #include #include "genrand.h" /* Period parameters */ #define N 624 #define M 397 #define MATRIX_A 0x9908b0df /* constant vector a */ #define UPPER_MASK 0x80000000 /* most significant w-r bits */ #define LOWER_MASK 0x7fffffff /* least significant r bits */ /* Tempering parameters */ #define TEMPERING_MASK_B 0x9d2c5680 #define TEMPERING_MASK_C 0xefc60000 #define TEMPERING_SHIFT_U(y) (y >> 11) #define TEMPERING_SHIFT_S(y) (y << 7) #define TEMPERING_SHIFT_T(y) (y << 15) #define TEMPERING_SHIFT_L(y) (y >> 18) static unsigned int mt[N]; /* the array for the state vector */ static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ /* initializing the array with a NONZERO seed */ void sgenrand(unsigned int seed) { /* setting initial seeds to mt[N] using */ /* the generator Line 25 of Table 1 in */ /* [KNUTH 1981, The Art of Computer Programming */ /* Vol. 2 (2nd Ed.), pp102] */ mt[0]= seed & 0xffffffff; for (mti=1; mti= N) { /* generate N words at one time */ int kk; if (mti == N+1) /* if sgenrand() has not been called, */ sgenrand(4357); /* a default initial seed is used */ for (kk=0;kk> 1) ^ mag01[y & 0x1]; } for (;kk> 1) ^ mag01[y & 0x1]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; mti = 0; } y = mt[mti++]; y ^= TEMPERING_SHIFT_U(y); y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; y ^= TEMPERING_SHIFT_L(y); return y; } float genrandf(void) { return genrandui()/(float)UINT_MAX; } SjaakII/src/misc/keypressed.c000644 000765 000024 00000006013 12464145566 017150 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "bool.h" #include "keypressed.h" #if defined _WIN32 || defined _WIN64 #define WINDOWS #endif #if defined __unix__ || defined __APPLE__ #define UNIX #endif #ifdef WINDOWS #undef DATADIR #include #include #ifndef min #define min(x,y) ( ((x)<(y))?(x):(y) ) #endif #endif #ifdef UNIX #include #include #include #endif #if defined WINDOWS || defined UNIX const bool ponder_ok = true; #else const bool ponder_ok = false; #endif /* Determine whether there is input waiting in the standard input stream. * A variation of this code is present in at least OliThink, Beowulf, * Crafty and Stockfish. * This version adapted from Stockfish. */ bool keyboard_input_waiting(void) { #ifdef WINDOWS static bool virgin = true; static bool pipe = false; static HANDLE inh; DWORD dw; #if defined(FILE_CNT) if (stdin->_cnt > 0) return stdin->_cnt; #endif if (virgin) { virgin = false; 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 true; return dw; } else { INPUT_RECORD rec[256]; DWORD recCnt; DWORD i; // Count the number of unread input records, including keyboard, // mouse, and window-resizing input records. GetNumberOfConsoleInputEvents(inh, &dw); if (dw <= 0) return false; // Read data from console without removing it from the buffer if (!PeekConsoleInput(inh, rec, min(dw, 256), &recCnt)) return false; // Search for at least one keyboard event for (i = 0; i < recCnt; i++) if (rec[i].EventType == KEY_EVENT) return true; return false; } #elif defined UNIX struct timeval timeout; fd_set readfds; 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 #warning cannot determine keyboard output! return false; #endif } SjaakII/src/misc/pipe2.c000644 000765 000024 00000010040 13014616475 015776 0ustar00eglebbkstaff000000 000000 #include #include #include #include #include #include #include #include #include #include "pipe2.h" /* Bidirectional pipe. * See * http://www.unixwiz.net/techtips/remap-pipe-fds.html * and * http://www.linuxquestions.org/questions/programming-9/where-do-i-get-a-bidirectional-popen-pipe-320699/ * Also: * http://stackoverflow.com/questions/13710003/execvp-fork-how-to-catch-unsuccessful-executions */ pipe2_t *p2open(const char *cmd, char *const argv[]) { pipe2_t *p2 = NULL; int writepipe[2] = {-1,-1}, /* parent -> child */ readpipe [2] = {-1,-1}; /* child -> parent */ pid_t child_pid; /* Open read and write pipes */ if (pipe(readpipe) < 0) goto done; if (pipe(writepipe) < 0) { close(readpipe[0]); close(readpipe[1]); goto done; } /* Convenient defines to make code easier to read */ #define PARENT_READ readpipe[0] #define CHILD_WRITE readpipe[1] #define CHILD_READ writepipe[0] #define PARENT_WRITE writepipe[1] int lifeline[2]; if (pipe(lifeline) < 0) { close(readpipe[0]); close(readpipe[1]); close(writepipe[0]); close(writepipe[1]); goto done; } /* Spawn child process */ child_pid = fork(); /* Failed to launch child process */ if (child_pid < 0) { close(readpipe[0]); close(readpipe[1]); close(writepipe[0]); close(writepipe[1]); goto done; } /* Child process */ if (child_pid == 0) { close(lifeline[0]); fcntl(lifeline[1], F_SETFD, FD_CLOEXEC); /* Close parent file descriptors */ close(PARENT_WRITE); close(PARENT_READ); /* Attach input and output pipes to stdin, stdout */ dup2(CHILD_READ, STDIN_FILENO); close(CHILD_READ); dup2(CHILD_WRITE, STDOUT_FILENO); close(CHILD_WRITE); execvp(cmd, argv); /* Write error code to the lifeline so the parent will know execvp * has failed. */ write(lifeline[1], &errno, sizeof errno); exit(EXIT_FAILURE); } /* Monitor the lifeline */ close(lifeline[1]); char buf[10]; ssize_t res = read(lifeline[0], buf, 10); if (res > 0) { /* Received error code from child process */ close(lifeline[0]); close(readpipe[0]); close(readpipe[1]); close(writepipe[0]); close(writepipe[1]); goto done; } close(lifeline[0]); /* Set up parent end of the pipe */ close(CHILD_READ); close(CHILD_WRITE); FILE *in, *out; if (!(in=fdopen(PARENT_READ,"r"))) { close(PARENT_READ); close(PARENT_WRITE); goto done; } if (!(out=fdopen(PARENT_WRITE,"w"))) { fclose(out); close(PARENT_WRITE); goto done; } /* Turn off buffering for pipes */ setvbuf(in, NULL, _IONBF, 0); setvbuf(out, NULL, _IONBF, 0); p2 = malloc(sizeof *p2); p2->in_fd = PARENT_READ; p2->in = in; p2->out_fd = PARENT_WRITE; p2->out = out; p2->pid = child_pid; done: return p2; } int p2close(pipe2_t *pipe) { if (pipe) { int res1 = fclose(pipe->in); int res2 = fclose(pipe->out); if (res1==EOF || res2==EOF) return EOF; int status; while (waitpid(pipe->pid,&status,0)<0) if (errno != EINTR) return EOF; } return 0; } char *p2gets(char *s, size_t n, pipe2_t *pipe) { return fgets(s, n, pipe->in); } static bool input_waiting(FILE *file) { if (!file) return false; struct timeval timeout; fd_set readfds; FD_ZERO(&readfds); FD_SET(fileno(file), &readfds); /* Set to timeout immediately */ timeout.tv_sec = 0; timeout.tv_usec = 0; int ready = select(fileno(file)+1, &readfds, 0, 0, &timeout); return (FD_ISSET(fileno(file), &readfds)); } bool p2_input_waiting(pipe2_t *pipe) { int status; if (!pipe) return false; if (waitpid(pipe->pid, &status, WNOHANG) != 0) return false; return input_waiting(pipe->in); } size_t p2write(pipe2_t *pipe, const void *ptr, size_t size) { return fwrite(ptr, size, 1, pipe->out); } SjaakII/src/misc/snprintf.c000644 000765 000024 00000002172 13014616475 016631 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014, 2015 Evert Glebbeek * * This source file written by Martin Sedlak, modified by Evert Glebbeek. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined _MSC_VER # include "compilerdef.h" # include # include # include # if _MSC_VER < 1900 int snprintf(char *buf, size_t size, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = vsnprintf(buf, size, fmt, ap); va_end(ap); return res; } # endif #endif SjaakII/src/misc/softexp.c000644 000765 000024 00000003027 12452217646 016460 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "softexp.h" /* Stupid implementation of the exponential function. * First brings its argument in the range [0, 1] while collecting * appropriate integer powers of e, then uses the Pade approximation to * evaluate the exponential on the interval [0, 1]. * * Included here because for obscure reasons cross-compiled 64 bit Windows * binaries crash in Windows when the libc exp() function is called. */ double myexp(double x) { double x2, x3, x4; const double ee = 2.71828182845904523536028747135266250; double y = 1.; while (x > 1.) { y *= ee; x -= 1.0; } while (x < 0.) { y /= ee; x += 1.0; } x2 = x*x; x3 = x2*x; x4 = x2*x2; y *= (1. + 4./7.*x+1./7.*x2+2./105.*x3+1./840*x4) / (1 - 3./7.*x + 1./14. * x2 - 1/210.*x3); return y; } SjaakII/src/misc/sprt.c000644 000765 000024 00000006001 13014616475 015751 0ustar00eglebbkstaff000000 000000 #include #include "assert.h" #include "sprt.h" double elo_to_winprob(double elo, double draw_elo) { return 1. / (1. + pow(10., (-elo + draw_elo) / 400.)); } double elo_to_lossprob(double elo, double draw_elo) { return elo_to_winprob(-elo, draw_elo); } double probability_to_elo(double p_win, double p_loss) { assert(p_win >= 0. && p_win < 1.); assert(p_loss >= 0. && p_loss < 1.); return 200. * log10(p_win/p_loss * (1.-p_loss)/(1.-p_win)); } double probability_to_drawelo(double p_win, double p_loss) { assert(p_win >= 0. && p_win < 1.); assert(p_loss >= 0. && p_loss < 1.); return 200. * log10((1. - p_loss)/p_loss * (1.-p_win)/(1.-p_win)); } sprt_result_t sprt(int n_wins, int n_loss, int n_draw, double elo0, double elo1, double a, double b) { double elo, draw_elo; int n = n_wins + n_loss + n_draw; double p_win = (double)n_wins / n; double p_loss = (double)n_loss / n; double p_draw = (double)n_draw / n; if (n_wins > 0 && n_loss > 0 && n_draw > 0) { elo = probability_to_elo(p_win, p_loss); draw_elo = probability_to_drawelo(p_win, p_loss); } else { return SPRT_UNKNOWN; } double p_win0 = elo_to_winprob (elo0, draw_elo); double p_loss0 = elo_to_lossprob(elo0, draw_elo); double p_draw0 = 1. - p_win0 - p_loss0; double p_win1 = elo_to_winprob (elo1, draw_elo); double p_loss1 = elo_to_lossprob(elo1, draw_elo); double p_draw1 = 1. - p_win1 - p_loss1; double lower = log(b / (1. - a)); double upper = log((1. - b) / a); double llr = n_wins * log(p_win1 / p_win0 ) + n_loss * log(p_loss1 / p_loss0) + n_draw * log(p_draw1 / p_draw0); if (llr < lower) return SPRT_REJECT; if (llr > upper) return SPRT_ACCEPT; return SPRT_UNKNOWN; } void print_sprt(FILE *file, int n_wins, int n_loss, int n_draw, double elo0, double elo1, double a, double b) { double elo, draw_elo; int n = n_wins + n_loss + n_draw; double p_win = (double)n_wins / n; double p_loss = (double)n_loss / n; double p_draw = (double)n_draw / n; if (n_wins > 0 && n_loss > 0 && n_draw > 0) { elo = probability_to_elo(p_win, p_loss); draw_elo = probability_to_drawelo(p_win, p_loss); } else { return; } double p_win0 = elo_to_winprob (elo0, draw_elo); double p_loss0 = elo_to_lossprob(elo0, draw_elo); double p_draw0 = 1. - p_win0 - p_loss0; double p_win1 = elo_to_winprob (elo1, draw_elo); double p_loss1 = elo_to_lossprob(elo1, draw_elo); double p_draw1 = 1. - p_win1 - p_loss1; double lower = log(b / (1. - a)); double upper = log((1. - b) / a); double llr = n_wins * log(p_win1 / p_win0 ) + n_loss * log(p_loss1 / p_loss0) + n_draw * log(p_draw1 / p_draw0); fprintf(file, "SPRT: %g [%g %g], a=%g b=%g elo0=%g elo1=%g, drawelo=%g\n", llr, lower, upper, a, b, elo0, elo1, draw_elo); if (llr < lower) fprintf(file, "SPRT: rejected\n"); if (llr > upper) fprintf(file, "SPRT: accepted\n"); } SjaakII/src/rules/bitboard.cc000644 000765 000024 00000004006 12433153270 017105 0ustar00eglebbkstaff000000 000000 #if 0 #include "bitboard.h" /* Specialisation for 64 bits: use optimised functions */ template<> inline int bitboard_t::popcount() const { return popcount64(bb); } template<> inline int bitboard_t::bitscan() const { return bitscan64(bb); } template<> inline int bitboard_t::popcount() const { return popcount128(bb); } template<> inline int bitboard_t::bitscan() const { return bitscan128(bb); } template<> inline bool bitboard_t::onebit() const { return onebit128(bb); } template<> inline bitboard_t bitboard_t::operator << (const int bits) const { return bitboard_t(shl128(bb, bits)); } template<> inline bitboard_t bitboard_t::operator >> (const int bits) const { return bitboard_t(shr128(bb, bits)); } template<> inline bool bitboard_t::test(int bit) const { return test128(bb, bit); } #ifndef __LP64__ template<> inline bitboard_t::bitboard_t() { bb = u128(0, 0); } template<> inline void bitboard_t::set(int bit) { if (bit < 64) bb |= u128(1ull < bit, 0); else bb |= u128(0, 1ull < (bit-64)); } template<> inline void bitboard_t::reset(int bit) { if (bit < 64) bb &= ~u128(1ull < bit, 0); else bb &= ~u128(0, 1ull < (bit-64)); } template<> inline bool bitboard_t::test(int bit) const { return test128(bb, bit); } template<> inline bool bitboard_t::is_empty() const { return is_zero128(bb); } template<> inline uint32_t bitboard_t::get_row(int row) const { typedef union { uint128_t i128; uint64_t i64[2]; } uuint128_t; uuint128_t b; b.i128 = shr128(bb, row * board_files); return b.i64[0] & rank_mask; } template<> inline uint32_t bitboard_t::get_file(int file) const { uint32_t file_bits = 0; int n; int bit = file; for (n=0; n #include "compilerdef.h" #include "game.h" static void printfstderr(const char *msg, ...) { static char buf[65536]; va_list ap; va_start(ap, msg); vsnprintf(buf, sizeof buf, msg, ap); va_end(ap); fprintf(stderr, "%s", buf); } void (*default_iteration_output)(const char *, ...) = NULL; void (*default_uci_output)(const char *, ...) = NULL; void (*default_xboard_output)(const char *, ...) = NULL; void (*default_error_output)(const char *, ...) = printfstderr; size_t default_hash_size = HASH_TABLE_SIZE; SjaakII/src/rules/move.cc000644 000765 000024 00000020337 13017310267 016272 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess variants * Copyright (C) 2011 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "compilerdef.h" #include "move.h" #include "squares.h" char piece_symbol_string[MAX_PIECE_TYPES+1]; char piece_psymbol_string[MAX_PIECE_TYPES+1]; char piece_drop_string[MAX_PIECE_TYPES+1]; const char *move_to_lan_string(move_t move, bool castle_san, bool castle_kxr, char *buffer) { static char static_buffer[64]; char *s = static_buffer; char dash = '-'; if (buffer) s = buffer; s[0] = '\0'; const char *p = ""; if (is_promotion_move(move) || is_gate_move(move)) { p = piece_symbol_string + get_move_promotion_piece(move); if (piece_symbol_string[get_move_piece(move)] == '+') p = "+"; } if (!is_drop_move(move) && get_move_from(move) == get_move_to(move) && !is_capture_move(move) && !is_castle_move(move) && !is_promotion_move(move) && !is_drop_move(move)) { snprintf(s, sizeof static_buffer, "@@@@"); return s; } if (is_double_capture_move(move)) { int from = get_move_from(move); int to = get_move_to(move); int first = 1; int c1, c2; uint16_t p = 0; if (get_move_swaps(move)) first = 0; p = get_move_pickup(move, first); c1 = decode_pickup_square(p); p = get_move_pickup(move, first+1); c2 = decode_pickup_square(p); if (c2 == to) { snprintf(s, 256, "%s%s,%s%s", square_names[from], square_names[c1], square_names[c1], square_names[to]); } else { snprintf(s, 256, "%s%s,%s%s,%s%s", square_names[from], square_names[c1], square_names[c1], square_names[c2], square_names[c2], square_names[to]); } return s; } if (is_capture_move(move) && get_move_capture_square(move) != get_move_to(move)) { int from = get_move_from(move); int to = get_move_to(move); int cap = get_move_capture_square(move); char fp = piece_symbol_string[get_move_piece(move)]; if (fp != ' ') { snprintf(s, 256, "%s%s,%s%s", square_names[from], square_names[cap], square_names[cap], square_names[to]); return s; } } if (is_castle_move(move)) { int from = get_move_from(move); int to = get_move_to(move); if (castle_san) { if (unpack_file(to) >= div_file) snprintf(s, sizeof static_buffer, "%s", kingside_castle); else snprintf(s, sizeof static_buffer, "%s", queenside_castle); } else if (castle_kxr) { snprintf(s, sizeof static_buffer, "%s%s%c", square_names[from], square_names[get_castle_move_from2(move)], tolower(*p)); } else { snprintf(s, sizeof static_buffer, "%s%s%c", square_names[from], square_names[to], tolower(*p)); if (abs(from-to) == 1) { int from2 = get_castle_move_from2(move); int to2 = get_castle_move_to2(move); snprintf(s, sizeof static_buffer, "%s%s,%s%s", square_names[from], square_names[to], square_names[from2], square_names[to2]); } } if (is_gate_move(move)) { if (get_move_drop_square(move) != from) { snprintf(s, sizeof static_buffer, "%s%s%c", square_names[get_castle_move_from2(move)], square_names[from], tolower(*p)); } else { snprintf(s, sizeof static_buffer, "%s%s%c", square_names[from], square_names[to], tolower(*p)); } } return s; } else if (is_drop_move(move)) { snprintf(s, sizeof static_buffer, "%c@%s", piece_drop_string[get_move_piece(move)], square_names[get_move_to(move)]); /* Promotion drop? */ uint16_t h = get_move_holding(move); int piece = decode_holding_piece(h); if (piece != get_move_piece(move)) { p = piece_symbol_string + get_move_piece(move); snprintf(s, sizeof static_buffer, "%c@%s%c", piece_drop_string[piece], square_names[get_move_to(move)], tolower(*p)); } return s; } else if (is_pickup_move(move)) //snprintf(s, sizeof static_buffer, "%c^%s", piece_drop_string[get_move_piece(move)], square_names[get_move_from(move)]); snprintf(s, sizeof static_buffer, "@@%s", square_names[get_move_from(move)]); else snprintf(s, sizeof static_buffer, "%s%s%c", square_names[get_move_from(move)], square_names[get_move_to(move)], tolower(*p)); //if (is_gate_move(move)) // snprintf(s + strlen(s), sizeof static_buffer-strlen(s), "%c", tolower(*p)); return s; } const char *move_to_string(move_t move, char *buffer) { static char static_buffer[256]; char *s = static_buffer; char dash = '-'; if (buffer) s = buffer; s[0] = '\0'; int n = 0; /* In normal chess (and Capablanca), "O-O" is king-side castling, "O-O-O" is queen-side. * This holds true in FRC and CRC games, but it is not true in Janus chess. * This is a problem, because simply testing whether the king starts out on the left-side of the board * would break FRC/CRC variants. */ if (is_castle_move(move)) { int to = unpack_file(get_move_to(move)); if (to >= div_file) snprintf(s, 256, "%s", kingside_castle); else snprintf(s, 256, "%s", queenside_castle); if (is_gate_move(move)) { snprintf(s + strlen(s), 256-strlen(s), "/%c%s", piece_symbol_string[get_move_promotion_piece(move)], square_names[get_move_drop_square(move)]); } return s; } int p = get_move_piece(move); if (is_drop_move(move)) { int to = get_move_to(move); char fp = piece_symbol_string[p]; snprintf(s, 256, "%c@%s", fp, square_names[to]); return s; } if (is_pickup_move(move)) { int from = get_move_from(move); char fp = piece_symbol_string[p]; snprintf(s, 256, "%c^%s", fp, square_names[from]); return s; } if (is_double_capture_move(move)) { int from = get_move_from(move); int to = get_move_to(move); char fp = piece_symbol_string[get_move_piece(move)]; int first = 1; int c1, c2; uint16_t p = 0; if (get_move_swaps(move)) first = 0; p = get_move_pickup(move, first); c1 = decode_pickup_square(p); p = get_move_pickup(move, first+1); c2 = decode_pickup_square(p); if (c2 == to) { snprintf(s, 256, "%c%sx%sx%s", fp, square_names[from], square_names[c1], square_names[to]); } else { snprintf(s, 256, "%c%sx%sx%s-%s", fp, square_names[from], square_names[c1], square_names[c2], square_names[to]); } return s; } if (is_capture_move(move) && get_move_capture_square(move) != get_move_to(move)) { int from = get_move_from(move); int to = get_move_to(move); int cap = get_move_capture_square(move); char fp = piece_symbol_string[get_move_piece(move)]; if (fp != ' ') { snprintf(s, 256, "%c%sx%s-%s", fp, square_names[from], square_names[cap], square_names[to]); return s; } } /* Normal move or capture */ if (is_capture_move(move)) dash = 'x'; int from, to; char fp = '\0', tp = '\0'; if (is_promotion_move(move)) tp = piece_symbol_string[get_move_promotion_piece(move)]; from = get_move_from(move); to = get_move_to(move); fp = piece_symbol_string[p]; snprintf(s, 256, "%c%s%c%s%c", fp, square_names[from], dash, square_names[to], tp); if (fp == '+') { if (tp) tp = '+'; snprintf(s, 256, "%c%c%s%c%s%c", fp, piece_psymbol_string[p], square_names[from], dash, square_names[to], tp); } if (is_gate_move(move)) snprintf(s + strlen(s), 256-strlen(s), "/%c", piece_symbol_string[get_move_promotion_piece(move)]); return s; } SjaakII/src/rules/san.cc000644 000765 000024 00000013376 13017310267 016112 0ustar00eglebbkstaff000000 000000 #include #include "san.h" #include "squares.h" #include "compilerdef.h" const char *move_to_short_string(move_t move, const movelist_t *movelist, char *buffer, bool san_castle) { static char static_buffer[256]; char *s = buffer; const char *gate_token = ""; const char *token = ""; const char *origin = ""; char piece = ' '; char tp = '\0'; if (!s) s = static_buffer; if (move == 0) { snprintf(s, sizeof static_buffer, "(pass)"); return s; } int to = get_move_to(move); int from = is_drop_move(move) ? to : get_move_from(move); int p = get_move_piece(move); piece = piece_symbol_string[p]; if (san_castle && is_castle_move(move)) { int f = unpack_file(to); if (f >= div_file) snprintf(s, sizeof static_buffer, "%s", kingside_castle); else snprintf(s, sizeof static_buffer, "%s", queenside_castle); if (is_gate_move(move)) { snprintf(s + strlen(s), 256-strlen(s), "/%c%s", piece_symbol_string[get_move_promotion_piece(move)], square_names[get_move_drop_square(move)]); } return s; } if (abs(from-to) == 1 && is_castle_move(move)) { int from2 = get_castle_move_from2(move); int to2 = get_castle_move_to2(move); snprintf(s, sizeof static_buffer, "%cx%s-%s", piece, square_names[from2], square_names[to]); return s; } if (is_capture_move(move)) token = "x"; if (is_drop_move(move)) token = "@"; if (is_promotion_move(move) || is_gate_move(move)) tp = piece_symbol_string[get_move_promotion_piece(move)]; if (is_gate_move(move)) gate_token = "/"; if (is_double_capture_move(move)) { int from = get_move_from(move); int to = get_move_to(move); char fp = piece; int first = 1; int c1, c2; uint16_t p = 0; if (get_move_swaps(move)) first = 0; p = get_move_pickup(move, first); c1 = decode_pickup_square(p); p = get_move_pickup(move, first+1); c2 = decode_pickup_square(p); if (c2 == to) { snprintf(s, 256, "%c%sx%sx%s", fp, square_names[from], square_names[c1], square_names[to]); } else { snprintf(s, 256, "%c%sx%sx%s-%s", fp, square_names[from], square_names[c1], square_names[c2], square_names[to]); } return s; } if (piece != ' ' && is_capture_move(move) && !is_double_capture_move(move) && get_move_capture_square(move)!=get_move_to(move)) { int from = get_move_from(move); int to = get_move_to(move); int cap = get_move_capture_square(move); char fp = piece_symbol_string[get_move_piece(move)]; /* TODO: disambiguate moves */ snprintf(s, 256, "%c%sx%s-%s", fp, "", square_names[cap], square_names[to]); return s; } if (is_drop_move(move)) piece = piece_drop_string[p]; if (is_pickup_move(move)) { piece = piece_drop_string[p]; token = "^"; to = from; goto disambiguous; } if (is_drop_move(move)) goto disambiguous; /* Slightly special case: pawn capture */ if (piece == ' ' && is_capture_move(move)) { origin = file_names[unpack_file(from)]; } else if (movelist) { /* The information we have now might be ambiguous - check */ int count = 0; int n; for (n=0; nnum_moves; n++) { if (is_drop_move(movelist->move[n])) continue; if (is_capture_move(movelist->move[n]) && !is_capture_move(move)) continue; if (!is_capture_move(movelist->move[n]) && is_capture_move(move)) continue; if (is_double_capture_move(movelist->move[n]) && !is_double_capture_move(move)) continue; if (!is_double_capture_move(movelist->move[n]) && is_double_capture_move(move)) continue; if (get_move_piece(move) == get_move_piece(movelist->move[n]) && to == get_move_to(movelist->move[n])) { if (is_promotion_move(move) && is_promotion_move(movelist->move[n])) count += tp == piece_symbol_string[get_move_promotion_piece(movelist->move[n])]; else if (!is_promotion_move(move) && !is_promotion_move(movelist->move[n])) { if (is_gate_move(move) && is_gate_move(movelist->move[n])) count += tp == piece_symbol_string[get_move_promotion_piece(movelist->move[n])]; else if (!is_gate_move(move) && !is_gate_move(movelist->move[n])) count++; } } } if (count <= 1) goto disambiguous; /* Try to disambiguate by file */ count = 0; for (n=0; nnum_moves; n++) { if (is_drop_move(movelist->move[n])) continue; if (get_move_piece(move) == get_move_piece(movelist->move[n]) && to == get_move_to(movelist->move[n]) && unpack_file(from) == unpack_file(get_move_from(movelist->move[n]))) count++; } if (count == 1) { origin = file_names[unpack_file(from)]; goto disambiguous; } /* Try to disambiguate by rank */ count = 0; for (n=0; nnum_moves; n++) { if (is_drop_move(movelist->move[n])) continue; if (get_move_piece(move) == get_move_piece(movelist->move[n]) && to == get_move_to(movelist->move[n]) && unpack_rank(from) == unpack_rank(get_move_from(movelist->move[n]))) count++; } if (count == 1) { origin = rank_names[unpack_rank(from)]; goto disambiguous; } /* Give up, list whole square */ origin = square_names[from]; } disambiguous: snprintf(s, 15, "%c%s%s%s%s%c", piece, origin, token, square_names[to], gate_token, tp); if (piece == '+') { if (tp) tp = '+'; snprintf(s, 15, "%c%c%s%s%s%s%c", piece, piece_psymbol_string[p], origin, token, square_names[to], gate_token, tp); } return s; } SjaakII/src/rules/squares.cc000644 000765 000024 00000006377 12465222762 017027 0ustar00eglebbkstaff000000 000000 #include #include #include #include #include "compilerdef.h" #include "squares.h" char *square_names[128] = { NULL }; char *file_names[16] = { NULL }; char *rank_names[16] = { NULL }; uint8_t packed_file_rank[128]; int rank_offset = 1; const char *kingside_castle; const char *queenside_castle; static bool virgin = true; int div_file = 0; bool keep_labels = false; static int board_files = 0; static int board_ranks = 0; void initialise_square_names(int files, int ranks) { board_files = files; board_ranks = ranks; /* Initialise square names and file/rank masks */ for (int n=0; n<128; n++) { if (!virgin) free(square_names[n]); square_names[n] = NULL; } for (int n=0; n<16; n++) { if (!virgin) { free(rank_names[n]); free(file_names[n]); } rank_names[n] = NULL; file_names[n] = NULL; } virgin = false; for (int f=0; f. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pipe2.h" #include "sprt.h" #include "timer.h" #include "genrand.h" #define streq(s1, s2) (strcmp((s1), (s2)) == 0) typedef enum side_t { NONE=-1, WHITE, BLACK, NUM_SIDES } side_t; typedef struct { pipe2_t *f; char *name; uint32_t state; int64_t clock; /* Clock time, in msec */ int ping; int moves; /* Number of moves played */ int id; int win; int draw; int nv; int depth, score, time; char **variants; char *fen; char *move; char *result_str; } program_t; static struct { char *move; int depth, score, time; } *history = NULL; static int history_size = 0; static int moves_played = 0; /* Program state flags */ #define PF_INIT 0x00000001 #define PF_DEAD 0x00000002 #define PF_PING 0x00000004 #define PF_TIME 0x00000008 #define PF_VARS 0x00000010 #define PF_FORCE 0x00000020 #define PF_THINK 0x00000040 #define PF_USERMOVE 0x00000080 #define PF_FORFEIT 0x00000100 #define PF_CLAIMD 0x00000200 #define PF_CLAIMW 0x00000400 #define PF_CLAIMB 0x00000800 #define PF_CLAIM (PF_CLAIMD | PF_CLAIMW | PF_CLAIMB) #define PF_RESIGN 0x00001000 #define PF_FLAG 0x00002000 #define PF_MEMORY 0x00004000 #define PF_SJEF 0x00010000 #define PF_SYNC 0x00020000 #define PF_FEN 0x00040000 #define PF_MOVE 0x00080000 #define PF_UNLOG 0x00100000 static program_t *prog[3]; static program_t *referee; chess_clock_t chess_clock; static uint64_t start_time = 10000; /* Msec */ static int moves_per_tc = 40; static double time_inc = 0; static int min_time_per_move = 0; static bool check_children; static FILE *logfile = NULL; static char *buf; #define BUF_SIZE 65536 bool child_is_alive(program_t * const prog) { int status; pid_t result = waitpid(prog->f->pid, &status, WNOHANG); if (result == 0) { return true; } else if (result == -1) { //perror(NULL); } // Child exited return false; } static void child_signal_handler(int sig) { /* Reap zombie processes */ int saved_errno = errno; while (waitpid((pid_t)(-1), NULL, WNOHANG) > 0) {} errno = saved_errno; /* If the referee died, we terminate immediately */ if (!child_is_alive(referee)) { fprintf(stderr, "*** Referee died, exit.\n"); exit(EXIT_FAILURE); } check_children = true; if (sig == SIGCHLD) signal(sig, child_signal_handler); } static void record_move(const char *move) { if (moves_played >= history_size) { history_size += 200; history = realloc(history, history_size * sizeof *history); } while (*move && isspace(*move)) move++; history[moves_played].move = strdup(move); moves_played++; } static void clear_history(void) { for (int n=0; nstate & PF_UNLOG)) { if (newline) fprintf(logfile, "%d> ", prog->id); newline = false; fprintf(logfile, "%s", buf); if (strstr(buf, "\n")) newline = true; fflush(logfile); } if (strlen(buf)) { ssize_t res = p2write(prog->f, buf, strlen(buf)); if (res < 0) { int e = errno; perror(NULL); if (logfile && !(prog->state & PF_UNLOG)) fprintf(logfile, "Write error to program %d (%s): %s\n", prog->id, prog->name, strerror(e)); exit(EXIT_FAILURE); } } } void msleep(int msec) { struct timespec timeout; timeout.tv_sec = msec / 1000; timeout.tv_nsec = (msec % 1000)*1000000; nanosleep(&timeout, NULL); } void wait_input(program_t **prog, int nprog, int musec) { struct timeval timeout; fd_set readfds, errfds; FD_ZERO(&readfds); FD_ZERO(&errfds); for (int n = 0; nf->in_fd; FD_SET(fd, &readfds); FD_SET(fd, &errfds); } //printf("Waiting for input...\n"); timeout.tv_sec = musec / 1000000; timeout.tv_usec = musec % 1000000; select(32, &readfds, NULL, &errfds, &timeout); //printf("Received input\n"); } void move_to_program(program_t *prog, const char *move) { send_to_program(prog, "%s%s\n", (prog->state & PF_USERMOVE) ? "usermove " : "", move); } /* Synchronise communication with a program */ bool synchronise(program_t *prog) { if (prog->state & PF_PING) { prog->ping++; prog->state &= ~PF_SYNC; send_to_program(prog, "ping %d\n", prog->ping); while(child_is_alive(prog) && !(prog->state & PF_SYNC)) { while(!p2_input_waiting(prog->f)); while(p2_input_waiting(prog->f)) { if (!p2gets(buf, BUF_SIZE, prog->f)) perror(NULL); parse_engine_input(prog, buf); } } } else { prog->state |= PF_SYNC; } return (prog->state & PF_SYNC); } const char *get_fen(void) { if (referee->state & PF_SJEF) { referee->state |= PF_UNLOG; synchronise(referee); referee->state |= PF_FEN; send_to_program(referee, "fen\n"); synchronise(referee); referee->state &= ~PF_FEN; referee->state &= ~PF_UNLOG; return referee->fen; } return NULL; } const char *get_san_move(const char *move) { if (referee->state & PF_SJEF) { synchronise(referee); referee->state |= PF_MOVE; send_to_program(referee, "san %s\n", move); synchronise(referee); referee->state &= ~PF_MOVE; return referee->move; } return move; } bool move_to_referee(const char *move) { while (*move && isspace(*move)) move++; synchronise(referee); referee->state &= ~PF_FORFEIT; move_to_program(referee, move); synchronise(referee); return !(referee->state & PF_FORFEIT); } static void parse_engine_input(program_t *prog, char *input) { char *line = input; char *token; char *next = NULL; if (!input || input[0] == '\0') return; line = input; while ((token = strchr(line, '\r'))) *token = '\n'; /* Remove leading white space */ while (isspace(*line)) line++; /* Break up lines at linebreaks */ next = strstr(line, "\n"); if (next) { next[0] = '\0'; next++; } if (logfile) { fprintf(logfile, "%d< %s\n", prog->id, line); fflush(logfile); } /* Remove comments */ token = strstr(line, "#"); if (token) *token = '\0'; /* Remove trailing white space */ token = line+strlen(line); while (token>line && isspace(token[-1])) { token--; *token='\0'; } /* Skip empty line */ if (!line[0]) goto done; //printf(">%s<\n", line); /* Parse engine input */ if (strstr(line, "move") == line) { /* Stop the clock */ uint64_t time = peek_timer(&chess_clock); /* Store the move in the game history */ record_move(line+5); const char *movestr = history[moves_played-1].move; history[moves_played-1].score = prog->score; history[moves_played-1].depth = prog->depth; history[moves_played-1].time = (int)time; /* Play the move on the board. If it is illegal, the engine forfeits the game. */ bool legal = move_to_referee(movestr); prog->clock -= time; prog->moves++; prog->state &= ~PF_THINK; /* Update the engine's clock */ prog->clock += time_inc; if (moves_per_tc && prog->moves > moves_per_tc-1 && (prog->moves % moves_per_tc) == 0) { if (logfile) fprintf(logfile, "Time control: %"PRIi64" -> %"PRIu64"\n", prog->clock, prog->clock+start_time); prog->clock += start_time; } if (prog->clock < 0) { prog->state |= PF_FLAG; prog->clock = 0; } if (legal) { if (logfile) { fprintf(logfile, "Move '%s' legal, time = %"PRIu64" (%"PRIu64" remaining), move %d/%d\n", movestr, time, prog->clock, prog->moves, 40); fflush(logfile); } } else { prog->state |= PF_FORFEIT; if (logfile) { fprintf(logfile, "Illegal move: '%s'\n", movestr); fflush(logfile); } } } else if (strstr(line, "#") == line) { /* Comment, ignored */ } else if (strstr(line, "tellusererror") == line) { fprintf(stderr, "%s\n", line+9); } else if (strstr(line, "telluser") == line) { printf("%s\n", line+9); } else if (strstr(line, "resign") == line) { prog->state &= ~PF_THINK; prog->state |= PF_RESIGN; } else if (strstr(line, "1-0") == line) { prog->state &= ~PF_THINK; prog->state |= PF_CLAIMW; free(prog->result_str); prog->result_str = strdup(line); } else if (strstr(line, "0-1") == line) { prog->state &= ~PF_THINK; prog->state |= PF_CLAIMB; free(prog->result_str); prog->result_str = strdup(line); } else if (strstr(line, "1/2-1/2") == line) { prog->state &= ~PF_THINK; prog->state |= PF_CLAIMD; free(prog->result_str); prog->result_str = strdup(line); } else if (strstr(line, "draw") == line) { } else if (strstr(line, "pong") == line) { int pong = 0; sscanf(line, "pong %d", &pong); if (pong == prog->ping) prog->state |= PF_SYNC; } else if (strstr(line, "Illegal move")) { /* Verify claim. * Referees are trusted, engines that make illegal move claims are forfeited. */ prog->state &= ~PF_THINK; prog->state |= PF_FORFEIT; if (logfile) { char *movestr = strstr(line, ":"); if (movestr) { movestr++; while (*movestr && isspace(*movestr)) movestr++; } if (!movestr) movestr = line; movestr = strdup(movestr); const char *fen = get_fen(); if (!fen) fen = "(unknown)"; if (prog == referee) fprintf(logfile, "Referee rejected move %s in position %s\n", movestr, fen); else fprintf(logfile, "False illegal move claim '%s' by %s in position %s\n", movestr, prog->name, fen); free(movestr); fflush(logfile); } } else if (strstr(line, "Error") == line) { } else if (strstr(line, "feature") == line) { /* Parse the different feature options */ char *ss = strstr(line, " "); while (ss && ss[0]) { char *feature; char *value; feature = ss; while (*feature && isspace(*feature)) feature++; value = strstr(feature, "="); if (!value) break; value[0] = '\0'; value++; /* String or numeric? */ if (value[0] == '"') { value++; char *s = value; while (*s && *s != '"') s++; s[0] = '\0'; ss = s+1; } else { char *s = value; while (*s && !isspace(*s)) s++; ss = s+1; } //printf("'%s' '%s'\n", feature, value); bool accept = false; if (streq(feature, "myname")) { int len = strlen(value); prog->name = realloc(prog->name, len+1); int n; for (n=0; nname[n] = value[n]; prog->name[len] = '\0'; accept = true; if (logfile) { fprintf(logfile, "Set name '%s' for program %d\n", prog->name, prog->id); fflush(logfile); } } else if (streq(feature, "sigint")) { accept = true; } else if (streq(feature, "setboard")) { accept = true; } else if (streq(feature, "memory")) { accept = true; prog->state |= PF_MEMORY; } else if (streq(feature, "time")) { accept = true; prog->state |= PF_TIME; } else if (streq(feature, "sjef")) { accept = true; prog->state |= PF_SJEF; } else if (streq(feature, "variants")) { accept = true; prog->state |= PF_VARS; int nv = 1; for (char *s = value; *s; s++) nv += (*s == ','); prog->nv = nv; prog->variants = calloc(nv, sizeof *prog->variants); char *str = value, *s; int n = 0; while ((s = strtok(str, ","))) { str = NULL; prog->variants[n++] = strdup(s); //printf("%d '%s'\n", n, prog->variants[n-1]); } } else if (streq(feature, "ping")) { accept = true; prog->state |= PF_PING; } else if (streq(feature, "usermove")) { accept = true; prog->state |= PF_USERMOVE; } else if (streq(feature, "done")) { accept = true; int res; sscanf(value, "%d", &res); if (res == 0) prog->state |= PF_INIT; else prog->state &= ~PF_INIT; } send_to_program(prog, "%s %s\n", accept?"accepted":"rejected", feature); } } else { /* Thinking or ponder output? */ if (prog->state & PF_FEN) { free(prog->fen); prog->fen = strdup(line); } if (prog->state & PF_MOVE) { char *s = line; while (*s && isspace(*s)) s++; free(prog->move); prog->move = strdup(s); } /* Thinking output */ int depth, score, time; if (sscanf(line, "%d %d %d", &depth, &score, &time) == 3) { prog->depth = depth; prog->score = score; prog->time = time; } } done: if (next) parse_engine_input(prog, next); } void start_new_game(program_t *prog, const char *variant_name) { uint32_t clearf = PF_THINK | PF_FORFEIT | PF_CLAIM | PF_RESIGN | PF_FLAG; prog->state &= ~clearf; prog->clock = start_time; prog->moves = 0; send_to_program(prog, "new\n"); if (!streq(variant_name, "normal")) send_to_program(prog, "variant %s\n", variant_name); /* Randomise initial moves */ send_to_program(prog, "random\n"); /* Switch engines to "force" mode, switch off pondering. */ send_to_program(prog, "force\neasy\npost\n"); prog->state |= PF_FORCE; } int main(int argc, char **argv) { FILE *pgnf = NULL; char *fcp = NULL; char *scp = NULL; char *ref = NULL; char *variant_name = "normal"; char *logfilename = NULL; char *epdfile = NULL; char finit[65536]; char sinit[65536]; char *finit_str = finit; char *sinit_str = sinit; char host[255]; bool use_sprt = false; double elo0 = 0.0; double elo1 = 4.0; double a = 0.05; double b = 0.05; int mg = 1; int epd_count = 0; int lpi = 0; int new_random_fen = 0; unsigned memory = 64; gethostname(host, sizeof host); char *s = strstr(host, "."); if (s) *s = '\0'; finit_str[0] = '\0'; sinit_str[0] = '\0'; buf = malloc(BUF_SIZE); /* Parse command-line options. */ if (argc>1) { int n; for (n=1; n= argc) { fprintf(stderr, "error: -variant passed, but no variant specified"); exit(EXIT_FAILURE); } n++; variant_name = argv[n]; } else if (strstr(argv[n], "-mg")) { if (n+1 >= argc) { fprintf(stderr, "error: number of games/match not specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%d", &mg); } else if (strstr(argv[n], "-lpi")) { if (n+1 >= argc) { fprintf(stderr, "error: position index not specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%d", &lpi); } else if (strstr(argv[n], "-nrf")) { if (n+1 >= argc) { fprintf(stderr, "error: random position frequency not specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%d", &new_random_fen); } else if (strstr(argv[n], "-fcp")) { if (n+1 >= argc) { fprintf(stderr, "error: first program not specified\n"); exit(EXIT_FAILURE); } n++; fcp = argv[n]; } else if (strstr(argv[n], "-scp")) { if (n+1 >= argc) { fprintf(stderr, "error: second program not specified\n"); exit(EXIT_FAILURE); } n++; scp = argv[n]; } else if (strstr(argv[n], "-referee")) { if (n+1 >= argc) { fprintf(stderr, "error: referee program not specified\n"); exit(EXIT_FAILURE); } n++; ref = argv[n]; } else if (strstr(argv[n], "-finit")) { if (n+1 >= argc) { fprintf(stderr, "error: init line for first program not specified\n"); exit(EXIT_FAILURE); } n++; snprintf(finit_str, sizeof finit - (finit_str - finit), "%s\n", argv[n]); finit_str = finit + strlen(finit); } else if (strstr(argv[n], "-sinit")) { if (n+1 >= argc) { fprintf(stderr, "error: init line for second program not specified\n"); exit(EXIT_FAILURE); } n++; snprintf(sinit_str, sizeof sinit - (sinit_str - sinit), "%s\n", argv[n]); sinit_str = sinit + strlen(sinit); } else if (strstr(argv[n], "-tc")) { /* Either just a total time in [minutes:]seconds, or moves/time+increment */ if (n+1 >= argc) { fprintf(stderr, "error: second program not specified\n"); exit(EXIT_FAILURE); } n++; char *s = argv[n]; char *p = s; if (strstr(s, "/")) { p = strstr(s, "/"); sscanf(s, "%d", &moves_per_tc); p[0] = '\0'; p++; } int minutes = 0, seconds=0, msec=0; if (strstr(p,":")) { sscanf(p, "%d:%d", &minutes, &seconds); seconds += 60*minutes; } else { sscanf(p, "%d", &seconds); } if (strstr(p, ".")) { char *pp = strstr(p, "."); double m; sscanf(pp, "%lg", &m); msec = 1000. * m; } start_time = seconds * 1000 + msec; if (strstr(p, "+")) { p = strstr(p, "+"); p++; sscanf(p, "%lg", &time_inc); time_inc *= 1000; } } else if (strstr(argv[n], "-inc")) { if (n+1 >= argc) { fprintf(stderr, "error: no increment specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%lg", &time_inc); time_inc *= 1000; } else if (strstr(argv[n], "-mps")) { if (n+1 >= argc) { fprintf(stderr, "error: moves per session not specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%d", &moves_per_tc); } else if (strstr(argv[n], "-mtpm") ){ if (n+1 >= argc) { fprintf(stderr, "error: minimum time per move not specified\n"); exit(EXIT_FAILURE); } n++; sscanf(argv[n], "%d", &min_time_per_move); } else if (strstr(argv[n], "-sgf")) { if (n+1 >= argc) { fprintf(stderr, "error: no safe file specified\n"); exit(EXIT_FAILURE); } n++; pgnf = fopen(argv[n], "a"); } else if (strstr(argv[n], "-sprt")) { use_sprt = true; } else if (strstr(argv[n], "-sprt_better")) { /* Test if program 1 is better than program 2 */ use_sprt = true; elo0 = 0; elo1 = 4; } else if (strstr(argv[n], "-sprt_worse")) { /* Test if program 1 is a regression against program 2 */ use_sprt = true; elo0 = -4; elo1 = 0; } else if (strstr(argv[n], "-memory")) { if (n+1 < argc && argv[n+1][0] != '-') { sscanf(argv[n+1], "%d", &memory); if (memory < 1) memory = 1; n++; } } else if (strstr(argv[n], "-log")) { logfilename = "sjef.log"; if (n+1 < argc && argv[n+1][0] != '-') { logfilename = argv[n+1]; n++; } } else if (strstr(argv[n], "-epd")) { if (n+1 >= argc) { fprintf(stderr, "error: no EPD file specified\n"); exit(EXIT_FAILURE); } n++; epdfile = argv[n]; } else { fprintf(stderr, "Unknown option: %s\n", argv[n]); exit(EXIT_FAILURE); } } } if (ref == NULL) ref = strdup("sjaakii"); if (moves_per_tc < 0) moves_per_tc = 0; if (time_inc < 0) time_inc = 0; if (!fcp || !scp) { fprintf(stderr, "error: need two programs to play\n"); exit(EXIT_FAILURE); } if (!mg) { printf("No games to play\n"); exit(0); } /* Start a game from a (random) position in the epd file. * Count the number of positions in the file. */ if (epdfile) { FILE *f = fopen(epdfile, "r"); if (f) { while(fgets(buf, BUF_SIZE, f)) epd_count++; sgenrand(time(NULL)); fclose(f); } else { fprintf(stderr, "error: cannot read EPD file %s\n", epdfile); exit(EXIT_FAILURE); } } if (logfilename) logfile = fopen(logfilename, "w"); pipe2_t *first, *second, *third; char **args = calloc(64, sizeof *args); int max_args = 64; int num_args = 0; /* Parse argument list */ num_args = 0; for (char *p = ref; p; ) { while (isspace(*p)) p++; if (!*p) break; if (num_args >= max_args) { max_args+=16; args = realloc(args, max_args*sizeof *args); } args[num_args++] = p; if(*p == '"' || *p == '\'') p = strchr(++args[num_args-1], *p); else p = strchr(p, ' '); if (p == NULL) break; *p++ = '\0'; } args[num_args] = NULL; /* Start the referee program */ printf("Starting referee %s\n", ref); fflush(stdout); third = p2open(args[0], args); if (!third) { printf("*** Failed to start referee %s\n", args[0]); exit(EXIT_FAILURE); } referee = calloc(1, sizeof *referee); referee->f = third; referee->name = strdup(args[0]); referee->state = PF_INIT; referee->id = 0; referee->win = 0; referee->draw = 0; send_to_program(referee, "xboard\nprotover 2\nforce\n"); while(child_is_alive(referee) && (referee->state & PF_INIT)) { while(!p2_input_waiting(referee->f)); while(p2_input_waiting(referee->f)) { if (!p2gets(buf, BUF_SIZE, referee->f)) perror(NULL); parse_engine_input(referee, buf); } } printf("Referee is %s\n", referee->name); /* Test if the referee supports the requested variant */ bool variant_ok = false; for (int n=0; nnv && !variant_ok; n++) if (streq(referee->variants[n], variant_name)) variant_ok = true; if (!variant_ok) { fprintf(stderr, "*** Referee does not understand variant '%s'\n", variant_name); exit(EXIT_FAILURE); } check_children = true; signal(SIGCHLD, child_signal_handler); /* Parse argument list and start first program */ num_args = 0; for (char *p = fcp; p; ) { while (isspace(*p)) p++; if (!*p) break; if (num_args >= max_args) { max_args+=16; args = realloc(args, max_args*sizeof *args); } args[num_args++] = p; if(*p == '"' || *p == '\'') p = strchr(++args[num_args-1], *p); else p = strchr(p, ' '); if (p == NULL) break; *p++ = '\0'; } args[num_args] = NULL; printf("Starting first program %s\n", fcp); first = p2open(fcp, args); if (!first) { printf("*** Failed to start first program %s\n", args[0]); exit(EXIT_FAILURE); } /* Parse argument list and start second program */ num_args = 0; for (char *p = scp; p; ) { while (isspace(*p)) p++; if (!*p) break; if (num_args >= max_args) { max_args+=16; args = realloc(args, max_args*sizeof *args); } args[num_args++] = p; if(*p == '"' || *p == '\'') p = strchr(++args[num_args-1], *p); else p = strchr(p, ' '); if (p == NULL) break; *p++ = '\0'; } args[num_args] = NULL; printf("Starting second program %s\n", scp); second = p2open(scp, args); if (!second) { printf("*** Failed to start second program %s\n", args[0]); exit(EXIT_FAILURE); } prog[0] = calloc(1, sizeof *prog[0]); prog[1] = calloc(1, sizeof *prog[1]); prog[0]->f = first; prog[0]->name = strdup(fcp); prog[0]->state = PF_INIT; prog[0]->id = 1; prog[0]->win = 0; prog[0]->draw = 0; prog[1]->f = second; prog[1]->name = strdup(scp); prog[1]->state = PF_INIT; prog[1]->id = 2; prog[1]->win = 0; prog[1]->draw = 0; prog[2] = referee; /* Send setup */ send_to_program(prog[0], "xboard\nprotover 2\n"); send_to_program(prog[1], "xboard\nprotover 2\n"); /* Parse feature options */ for (int i=0; i<2; i++) { printf("Intialising program %d...", i+1); fflush(stdout); while(child_is_alive(prog[i]) && (prog[i]->state & PF_INIT)) { while(!p2_input_waiting(prog[i]->f)); while(p2_input_waiting(prog[i]->f)) { if (!p2gets(buf, BUF_SIZE, prog[i]->f)) perror(NULL); parse_engine_input(prog[i], buf); } } printf("done\n"); } /* Verify that both programs can play the variant in question */ for (int k=0; k<2; k++) { variant_ok = false; for (int n=0; nnv && !variant_ok; n++) if (streq(prog[k]->variants[n], variant_name)) variant_ok = true; if (!variant_ok) { fprintf(stderr, "*** Program %d (%s) does not understand variant '%s'\n", k+1, prog[k]->name, variant_name); exit(EXIT_FAILURE); } } /* Set memory size */ for (int k=0; k<2; k++) if (prog[k]->state & PF_MEMORY) send_to_program(prog[k], "memory %d\n", memory); /* Send init strings (if needed) */ if (logfile) fprintf(logfile, "init1 = '%s'\ninit2 = '%s'\n", finit, sinit); if (finit[0]) send_to_program(prog[0], "%s", finit); if (sinit[0]) send_to_program(prog[1], "%s", sinit); int n; char tc_string[256]; snprintf(tc_string, sizeof tc_string, "%d/%"PRIu64":%02"PRIu64"+%g", moves_per_tc, (start_time/60000), (start_time%60000) / 1000, time_inc/1000); printf("Time control: %s\n", tc_string); if (logfile) fprintf(logfile, "Time control: %s\n", tc_string); if (moves_per_tc * time_inc) printf("Warning: both moves per session and increment specified\n"); int epd_pos = 0; if (lpi > 0) epd_pos = lpi; if (new_random_fen) epd_pos = genrandf()*epd_count; for (int n=0; n<3; n++) synchronise(prog[n]); for (n=0; nname, prog[black]->name, n+1, mg); /* Inform the engines about the new variant game */ for (int k=0; k<3; k++) start_new_game(prog[k], variant_name); clear_history(); /* Setup time control */ send_to_program(prog[0], "level %d %"PRIu64":%02"PRIu64" %d\n", moves_per_tc, (start_time)/(60000), (start_time)%(60000)/1000, (int)time_inc); send_to_program(prog[1], "level %d %"PRIu64":%02"PRIu64" %d\n", moves_per_tc, (start_time)/(60000), (start_time)%(60000)/1000, (int)time_inc); /* Send initial position */ if (epdfile && epd_count) { FILE *f = fopen(epdfile, "r"); int n; for (n=0; n<=epd_pos; n++) fgets(buf, BUF_SIZE, f); fclose(f); char *fen = strdup(buf); char *s = strstr(fen, "\n"); if (s) *s = '\0'; send_to_program(prog[0], "setboard %s\n", fen); send_to_program(prog[1], "setboard %s\n", fen); send_to_program(referee, "setboard %s\n", fen); if (logfile) fprintf(logfile, "Loaded position #%d from %s (FEN: %s)\n", epd_pos, epdfile, fen); free(fen); } /* Play out the game */ bool game_is_decided = false; while (!game_is_decided) { char s[4096]; if (logfile) fflush(logfile); wait_input(prog, 2, 100); /* Check whether the engines are both still alive */ if (check_children) { bool done = false; for (int i=0; i<3; i++) { if(!child_is_alive(prog[i])) { prog[i]->state |= PF_DEAD; done = true; printf("Child %d died\n", prog[i]->id); } } if (done) break; check_children = false; } /* Parse input from the engines engines */ for (int i=0; i<3; i++) { while(p2_input_waiting(prog[i]->f)) { if (!p2gets(buf, BUF_SIZE, prog[i]->f)) perror(NULL); parse_engine_input(prog[i], buf); } } /* If either program has performed an illegal move or claim, it has forfeited the game */ if ((prog[0]->state & PF_FORFEIT) || (prog[1]->state & PF_FORFEIT)) { game_is_decided = true; break; } /* If either program resigned, we're likewise done */ if ((prog[0]->state & PF_RESIGN) || (prog[1]->state & PF_RESIGN)) { game_is_decided = true; break; } /* A program claimed the game was over, see whether we agree but abort the game anyway */ if ((prog[0]->state & PF_CLAIM) || (prog[1]->state & PF_CLAIM)) { game_is_decided = true; if (logfile) { synchronise(referee); bool end = (referee->state & PF_CLAIM); int desc = 3; if (referee->state & PF_CLAIMD) desc = 2; if (referee->state & PF_CLAIMW) desc = 0; if (referee->state & PF_CLAIMB) desc = 1; const char *side_string[4] = { "1-0", "0-1", "draw", "-" }; fprintf(logfile, "Claim that game ended, referee %s (%s)\n", end? "agrees": "disagrees", side_string[desc]); fprintf(logfile, "Claim made in position %s\n", get_fen()); } break; } /* If neither program is thinking... */ if (!(prog[0]->state & PF_THINK) && !(prog[1]->state & PF_THINK)) { /* Check for end-of-game conditions */ /* Check for out-of-time */ if (min_time_per_move == 0 && prog[0]->clock*prog[1]->clock <=0) { game_is_decided = true; if (prog[0]->clock <= prog[1]->clock) prog[0]->state |= PF_FLAG; if (prog[1]->clock <= prog[0]->clock) prog[1]->state |= PF_FLAG; } side_t stm = moves_played & 1; int ptm = (stm + white) & 1; /* Update program's clock */ int time = prog[ptm]->clock; if (time < min_time_per_move) time = min_time_per_move+9; send_to_program(prog[ptm^1], "otim %g\n", prog[ptm^1]->clock/10.); send_to_program(prog[ptm], "time %g\n", time/10.); /* Send the last move played in the game (as needed) */ prog[ptm]->state |= PF_THINK; if (moves_played) move_to_program(prog[ptm], history[moves_played-1].move); /* Switch off force mode if needed */ if (prog[ptm]->state & PF_FORCE) { prog[ptm]->state &= ~PF_FORCE; send_to_program(prog[ptm], "go\n"); } /* Start the referee's clock */ start_clock(&chess_clock); } } char *result_str = "*"; if (game_is_decided) { if ((prog[white]->state & PF_RESIGN)) { result_str = "0-1 {White resigns}"; prog[black]->win++; } else if ((prog[black]->state & PF_RESIGN)) { result_str = "1-0 {Black resigns}"; prog[white]->win++; } else if ((prog[white]->state & PF_FORFEIT)) { result_str = "0-1 {White forfeits due to illegal move or illegal move claim}"; prog[black]->win++; } else if ((prog[black]->state & PF_FORFEIT)) { result_str = "1-0 {Black forfeits due to illegal move or illegal move claim}"; prog[white]->win++; } else if (!min_time_per_move && (prog[0]->state & PF_FLAG) && (prog[1]->state & PF_FLAG)) { result_str = "1/2-1/2 {Both flags fell}"; prog[0]->draw++; prog[1]->draw++; } else if (!min_time_per_move && prog[white]->state & PF_FLAG) { result_str = "0-1 {White lost on time}"; prog[black]->win++; } else if (!min_time_per_move && prog[black]->state & PF_FLAG) { result_str = "1-0 {Black lost on time}"; prog[white]->win++; } else { synchronise(referee); if (referee->state & PF_CLAIMD) { prog[0]->draw++; prog[1]->draw++; } if (referee->state & PF_CLAIMW) prog[white]->win++; if (referee->state & PF_CLAIMB) prog[BLACK]->win++; if (referee->result_str) result_str = referee->result_str; } if (use_sprt) { sprt_result_t sprt_result = sprt(prog[0]->win, prog[1]->win, prog[0]->draw, elo0, elo1, a, b); if (logfile) print_sprt(logfile, prog[0]->win, prog[1]->win, prog[0]->draw, elo0, elo1, a, b); print_sprt(stdout, prog[0]->win, prog[1]->win, prog[0]->draw, elo0, elo1, a, b); if (sprt_result != SPRT_UNKNOWN) break; } } if (logfile) fprintf(logfile, "Result: %s\n", result_str); printf("Game result: %s\n", result_str); /* Inform players that game has ended */ send_to_program(prog[0], "result %s\n", result_str); send_to_program(prog[1], "result %s\n", result_str); /* Write the game to a .pgn file */ if (pgnf) { start_new_game(referee, variant_name); char *short_result_str = strdup(result_str); char *s = strstr(short_result_str, " "); char *reason = NULL; if (s) { *s = '\0'; reason = s+1; } const char *fen = get_fen(); fprintf(pgnf, "[Event \"Computer Match\"]\n" "[Site \"%s\"]\n" "[Date \"\"]\n" "[Round \"%d\"]\n" "[White \"%s\"]\n" "[Black \"%s\"]\n" "[Referee \"%s\"]\n" "[Result \"%s\"]\n" "[TimeControl \"%s\"]\n" "[Variant \"%s\"]\n", host, n+1, prog[white]->name, prog[black]->name, referee->name, short_result_str, tc_string, variant_name); if (fen) fprintf(pgnf, "[FEN \"%s\"]\n", fen); fprintf(pgnf, "\n"); int l = 0; for (int n=0; n= 80) { fprintf(pgnf, "\n"); l = 0; } l += strlen(s); fprintf(pgnf, "%s", s); snprintf(s, sizeof s, "{%+.2f/%d %.2f} ", history[n].score/100., history[n].depth, history[n].time/1000.); if ( (l + strlen(s)) >= 80) { fprintf(pgnf, "\n"); l = 0; } l += strlen(s); fprintf(pgnf, "%s", s); } if (reason) fprintf(pgnf, "\n%s %s\n\n", reason, short_result_str); else fprintf(pgnf, "\n%s\n\n", short_result_str); fflush(pgnf); free(short_result_str); } } printf("Match result: + %d - %d = %d (%.1f-%.1f)\n", prog[0]->win, prog[1]->win, prog[0]->draw, 1.0*prog[0]->win+0.5*prog[0]->draw, 1.0*prog[1]->win+0.5*prog[1]->draw); int wins = prog[0]->win; int losses = prog[1]->win; int draws = prog[0]->draw; double games = wins + losses + draws; double winning_fraction = (wins + 0.5*draws) / games; double elo_difference = -log10(1.0/winning_fraction-1.0)*400.0; double los = 0.5 + 0.5 * erf((wins-losses)/sqrt(2.0*(wins+losses))); printf("Elo difference: %+g\n", elo_difference); printf("LOS: % g\n", los); if (pgnf) fclose(pgnf); /* Shutdown. Avoid waiting indefinitely by setting an alarm. A timeout of 10s should be plenty. */ alarm(10); /* Flush buffers */ for (int i=0; i<2; i++) { while(p2_input_waiting(prog[i]->f)) { p2gets(buf, BUF_SIZE, prog[i]->f); } } /* Shut down children */ signal(SIGCHLD, SIG_IGN); send_to_program(referee, "quit\n"); send_to_program(prog[0], "quit\n"); send_to_program(prog[1], "quit\n"); msleep(10); wait_input(prog, 2, 50000); if (check_children) { check_children = false; for (int i=0; i<2; i++) { if(!(prog[i]->state & PF_DEAD) && child_is_alive(prog[i])) { kill(prog[i]->f->pid, SIGTERM); if (logfile) fprintf(logfile, "Send terminate signal to %s (%d)\n", prog[i]->name, prog[i]->id); } else { prog[i]->state |= PF_DEAD; if (logfile) fprintf(logfile, "%s (%d) exited\n", prog[i]->name, prog[i]->id); } } } for (int i=0; i<2; i++) { if(!(prog[i]->state & PF_DEAD)) { msleep(10); if (child_is_alive(prog[i])) { if (logfile) fprintf(logfile, "Send kill signal to %s (%d)\n", prog[i]->name, prog[i]->id); kill(prog[i]->f->pid, SIGKILL); while (child_is_alive(prog[i])); } } } if (logfile) fclose(logfile); return 0; } SjaakII/src/test.cc000644 000765 000024 00000012217 12433153270 015147 0ustar00eglebbkstaff000000 000000 #include #include "bitboard.h" #include "board.h" #include "movegen.h" #include "aligned_malloc.h" #include "game.h" int main(void) { game_template_t *game = new game_template_t; bitboard_t large(u128(1,0)); bitboard_t bb((uint64_t)0x06ll); bitboard_t b(3); bitboard_t b2; bitboard_t b3; bitboard_t tiny(0); bitboard_t::initialise_bitboards(4,4); bitboard_t::initialise_bitboards(4,4); bitboard_t::initialise_bitboards(11,11); movegen_t::initialise(); movegen_t::initialise(); movegen_t::initialise(); movegen_t::initialise_slider_tables(); movegen_t::initialise_slider_tables(); movegen_t::initialise_slider_tables(); movegen_t::initialise_super_tables(); movegen_t::initialise_super_tables(); movegen_t::initialise_super_tables(); game->set_board_size(8,8); //large = large << 16; move_flag_t fb = movegen_t::define_piece_move("slide (A,D)"); move_flag_t fr = movegen_t::define_piece_move("slide (H,V)"); move_flag_t fq = fb | fr; move_flag_t fn = movegen_t::define_piece_move("leap (1,2)"); move_flag_t fk = movegen_t::define_piece_move("leap (1,0)|(1,1)"); bitboard_t pz[2]; game->add_piece_type(fn, fn, 0, pz, "", "Knight", "N,n", "N"); game->add_piece_type(fb, fb, 0, pz, "", "Bishop", "B,b", "B"); game->add_piece_type(fr, fr, 0, pz, "", "Rook", "R,r", "R"); game->add_piece_type(fq, fq, 0, pz, "", "Queen", "Q,q", "Q"); game->add_piece_type(fk, fk, 0, pz, "", "King", "K,k", "K"); movegen_t::initialise_super_tables(); movegen_t::deduce_castle_flags(WHITE, 4, 6, 7); movegen_t::deduce_castle_flags(WHITE, 4, 2, 0); movegen_t::deduce_castle_flags(BLACK, 60, 62, 63); movegen_t::deduce_castle_flags(BLACK, 60, 58, 56); b2 = b << 1; printf("%d %d %d\n", large.onebit(), large.popcount(), large.bitscan()); printf("%d %d %d\n", bb.onebit(), bb.popcount(), bb.bitscan()); printf("%d %d %d %d\n", b.onebit(), b.popcount(), b.bitscan(), b.test(4)); printf("%d %d %d %d\n", b2.onebit(), b2.popcount(), b2.bitscan(), b2.test(4)); b2 = b2 >> 1; printf("%d\n", b2 == b); printf("%d %d %d\n", b3.onebit(), b3.popcount(), b3.bitscan()); printf("0x%016llx\n", bitboard_t::board_light.value()); for (int n = 0; n<8; n++) printf("%08x\n", large.get_rank(n)); printf("\n"); for (int n = 0; n<8; n++) printf("%08x\n", large.get_file(n)); #if 0 uint32_t occ = (1<<1)|(1<<2); int where = 0; large = u128(occ, 0); tiny ^= tiny; printf("\n"); for (int n = 0; n movegen; printf("\n"); for (int n = 0; n occb; occb.set(0); occb.set(3); occb.set(2*8); occb.set(14); occb.set(36); occb.set(36 + 2*9); occb.set(36 - 7); occb.set(36 + 2*7); bitboard_t moves = movegen_t::generate_slider_move_bitboard(MF_SLIDER_H|MF_SLIDER_V, WHITE, 0, occb); bitboard_t dmoves = movegen_t::generate_hopper_move_bitboard(MF_HOPPER_D|MF_HOPPER_A, WHITE, 36, occb); printf("\nOccupied:\n"); occb.print(); printf("\n"); moves.print(); printf("\n"); dmoves.print(); printf("%08x\n", fr); printf("%08x\n", fk); printf("%d\n", get_leaper_index(fk)); movegen_t::super[36].print(); bitboard_t knightm = movegen_t::generate_leaper_move_bitboard(fn, WHITE, 36, occb); printf("\n"); movegen_t::leaper[get_leaper_index(fk)][0].print(); printf("\n"); movegen_t::leaper[get_leaper_index(fk)][36].print(); printf("\n"); knightm.print(); //printf("\n"); //occb.fill_north().print(); //printf("\n"); //occb.fill_south().print(); #if 0 for (to = 0; to < tiny.board_ranks*tiny.board_files; to++) { printf("\n"); for (int n = 0; n. */ #if defined _MSC_VER # define USE_GETTICKCOUNT # include # undef min #else # include # if defined HAVE_CLOCK_GETTIME && defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L && defined _POSIX_TIMERS && _POSIX_TIMERS>0 && defined _POSIX_MONOTONIC_CLOCK # define USE_CLOCK_GETTIME # include # else # include # endif #endif #include #include #include "assert.h" #include "timer.h" #include "keypressed.h" /* returns the current value of the counter, in micro seconds */ uint64_t get_timer(void) { #ifdef USE_CLOCK_GETTIME struct timespec t; clock_gettime(0, &t); return t.tv_sec*1000000 + t.tv_nsec/1000; #elif defined USE_GETTICKCOUNT return (uint64_t)GetTickCount() * 1000; #else struct timeval t; gettimeofday(&t, NULL); return t.tv_sec*1000000 + t.tv_usec; #endif } static int min(int x, int y) { return (xstart_time = get_timer(); clock->extra_time = 0; } /* Get the time elapsed since the last call to start_clock(), in ms */ int peek_timer(const chess_clock_t *clock) { return (int)((get_timer() - clock->start_time) / 1000); } /* Calculate time allocated for this a move */ int get_chess_clock_time_for_move(const chess_clock_t *clock) { int num_moves = clock->movestogo; int time_for_move; if (clock->pondering) { if (!keyboard_input_waiting()) return INT_MAX; else return 0; } if (clock->time_per_move) return clock->time_per_move; if (clock->check_clock == NULL) return INT_MAX; /* No set number of moves to be played within the time limit, make some * sort of estimate for how many moves we may want to play in the time * remaining. */ if (num_moves == 0) { if (clock->root_moves_played < 20) num_moves = 15+clock->root_moves_played; else num_moves = 20; } if (num_moves < 0) num_moves = 10; /* Base time for this move: the total time left divided by the number of * moves. */ time_for_move = clock->time_left/num_moves; /* We want to preserve a small buffer to avoid time losses */ if (num_moves < 5) time_for_move = (int)(time_for_move*0.95); /* Adjust: we can spare at least half the increment */ if (clock->time_left > clock->time_inc/2) time_for_move += clock->time_inc/2; /* We may have allocated some extra time for this move, for instance * because our best move failed low. */ time_for_move += min(clock->extra_time, clock->time_left); /* If we're short on time and have an increment, then play quickly so we * gain some extra time to finish the game. */ if (clock->time_left < clock->time_inc * 5) time_for_move = 3*clock->time_inc/4; return min(time_for_move, clock->time_left - clock->time_left/8); } /* Time management: check if a fixed time per move has passed since the * clock was started. */ static bool check_time_per_move_clock(const chess_clock_t *clock) { int time_passed = peek_timer(clock); if (time_passed >= clock->time_per_move) return true; return false; } static bool check_time_for_game(const chess_clock_t *clock) { int time_passed = peek_timer(clock); int time_for_move = get_chess_clock_time_for_move(clock); /* If we've expended our time for this move, then abort */ if (time_passed >= time_for_move) return true; return false; } static bool check_keyboard(const chess_clock_t *clock) { return keyboard_input_waiting(); } void set_time_per_move(chess_clock_t *clock, int msec) { clock->time_per_move = msec; clock->check_clock = check_time_per_move_clock; } void set_ponder_timer(chess_clock_t *clock) { clock->check_clock = check_keyboard; } void set_infinite_time(chess_clock_t *clock) { clock->check_clock = NULL; } void set_time_for_game(chess_clock_t *clock) { clock->time_per_move = 0; clock->check_clock = check_time_for_game; } SjaakII/src/xboard.cc000644 000765 000024 00000424357 13014616475 015472 0ustar00eglebbkstaff000000 000000 /* Sjaak, a program for playing chess * Copyright (C) 2011, 2014 Evert Glebbeek * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define __STDC_FORMAT_MACROS 1 #include "compilerdef.h" #include #include #include #include #include #if defined _MSC_VER # include #endif #include "sjaak.h" #include "xstring.h" #include "keypressed.h" #include "cfgpath.h" #include "test_suite.h" #ifdef __APPLE__ #define __unix__ #endif #ifdef __unix__ #include #include #endif #ifdef HAVE_READLINE #include #include #endif #if defined __LP64__ || defined _WIN64 #define ARCHSTR "(x86_64)" #elif defined __i386__ || defined _WIN32 #define ARCHSTR "(i386)" #elif defined POWERPC #define ARCHSTR "(powerpc)" #else #define ARCHSTR "(unknown)" #endif #ifndef SJAAKIIVERSION #define SJAAKIIVERSION "(version unknown) " #endif #define VERSIONSTR SJAAKIIVERSION #ifdef DEBUGMODE #undef VERSIONSTR #define VERSIONSTR SJAAKIIVERSION " (debug)" #endif #define PROGNAME "Sjaak II" #define TIME_BUFFER 100 typedef struct help_topic_t { const char *topic; const char *cmd; const char *text; } help_topic_t; typedef struct position_signature_t { const char *fen; int depth; uint64_t nodes; } position_signature_t; static position_signature_t perftests[] = { // Martin Sedlak's test positions // (http://www.talkchess.com/forum/viewtopic.php?t=47318) // avoid illegal ep { "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1", 6, 1134888 }, { "8/8/8/8/k1p4R/8/3P4/3K4 w - - 0 1", 6, 1134888 }, { "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1", 6, 1015133 }, { "8/b2p2k1/8/2P5/8/4K3/8/8 b - - 0 1", 6, 1015133 }, // en passant capture checks opponent: { "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1", 6, 1440467 }, { "8/5k2/8/2Pp4/2B5/1K6/8/8 w - d6 0 1", 6, 1440467 }, // short castling gives check: { "5k2/8/8/8/8/8/8/4K2R w K - 0 1", 6, 661072 }, { "4k2r/8/8/8/8/8/8/5K2 b k - 0 1", 6, 661072 }, // long castling gives check: { "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1", 6, 803711 }, { "r3k3/8/8/8/8/8/8/3K4 b q - 0 1", 6, 803711 }, // castling (including losing cr due to rook capture): { "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1", 4, 1274206 }, { "r3k2r/7b/8/8/8/8/1B4BQ/R3K2R b KQkq - 0 1", 4, 1274206 }, // castling prevented: { "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1", 4, 1720476 }, { "r3k2r/8/5Q2/8/8/3q4/8/R3K2R w KQkq - 0 1", 4, 1720476 }, // promote out of check: { "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1", 6, 3821001 }, { "3K4/8/8/8/8/8/4p3/2k2R2 b - - 0 1", 6, 3821001 }, // discovered check: { "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1", 5, 1004658 }, { "5K2/8/1Q6/2N5/8/1p2k3/8/8 w - - 0 1", 5, 1004658 }, // promote to give check: { "4k3/1P6/8/8/8/8/K7/8 w - - 0 1", 6, 217342 }, { "8/k7/8/8/8/8/1p6/4K3 b - - 0 1", 6, 217342 }, // underpromote to check: { "8/P1k5/K7/8/8/8/8/8 w - - 0 1", 6, 92683 }, { "8/8/8/8/8/k7/p1K5/8 b - - 0 1", 6, 92683 }, // self stalemate: { "K1k5/8/P7/8/8/8/8/8 w - - 0 1", 6, 2217 }, { "8/8/8/8/8/p7/8/k1K5 b - - 0 1", 6, 2217 }, // stalemate/checkmate: { "8/k1P5/8/1K6/8/8/8/8 w - - 0 1", 7, 567584 }, { "8/8/8/8/1k6/8/K1p5/8 b - - 0 1", 7, 567584 }, // double check: { "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1", 4, 23527 }, { "8/5k2/8/5N2/5Q2/2K5/8/8 w - - 0 1", 4, 23527 }, // short castling impossible although the rook never moved away from its corner { "1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1", 5, 1063513 }, { "4k2r/8/8/7r/8/8/1B6/1K6 w k - 0 1", 5, 1063513 }, // long castling impossible although the rook never moved away from its corner { "1k6/8/8/8/R7/1n6/8/R3K3 b Q - 0 1", 5, 346695 }, { "r3k3/8/1N6/r7/8/8/8/1K6 w q - 0 1", 5, 346695 }, // From the Wiki { "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", 4, 4085603 }, { "rnbqkb1r/pp1p1ppp/2p5/4P3/2B5/8/PPP1NnPP/RNBQK2R w KQkq - 0 6", 3, 53392 }, // Shortened form of the third position below { "8/7p/p5pb/4k3/P1pPn3/8/P5PP/1rB2RK1 b - d3 0 28", 4, 67197 }, // Some FRC postions by Reinhard Scharnagl // (http://www.talkchess.com/forum/viewtopic.php?t=55274) // We have each of them twice, to get the number of moves at the root // correct too. { "r1k1r2q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K1R2Q w KQkq - 0 1", 1, 23 }, { "r1k2r1q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K2R1Q w KQkq - 0 1", 1, 28 }, { "8/8/8/4B2b/6nN/8/5P2/2R1K2k w Q - 0 1", 1, 34 }, { "2r5/8/8/8/8/8/6PP/k2KR3 w K - 0 1", 1, 17 }, { "4r3/3k4/8/8/8/8/6PP/qR1K1R2 w KQ - 0 1", 1, 19 }, { "r1k1r2q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K1R2Q w KQkq - 0 1", 2, 522 }, { "r1k2r1q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K2R1Q w KQkq - 0 1", 2, 738 }, { "8/8/8/4B2b/6nN/8/5P2/2R1K2k w Q - 0 1", 2, 318 }, { "2r5/8/8/8/8/8/6PP/k2KR3 w K - 0 1", 2, 242 }, { "4r3/3k4/8/8/8/8/6PP/qR1K1R2 w KQ - 0 1", 2, 628 }, { "r1k1r2q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K1R2Q w KQkq - 0 1", 5, 7096972 }, { "r1k2r1q/p1ppp1pp/8/8/8/8/P1PPP1PP/R1K2R1Q w KQkq - 0 1", 5, 15194841 }, { "8/8/8/4B2b/6nN/8/5P2/2R1K2k w Q - 0 1", 5, 3223406 }, { "2r5/8/8/8/8/8/6PP/k2KR3 w K - 0 1", 5, 985298 }, { "4r3/3k4/8/8/8/8/6PP/qR1K1R2 w KQ - 0 1", 5, 8992652 }, // John Merlino's test positions, some of these take a long time, only do them // in debug mode. #ifdef DEBUGMODE { "r3k2r/8/8/8/3pPp2/8/8/R3K1RR b KQkq e3 0 1", 6, 485647607 }, { "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 6, 706045033 }, { "8/7p/p5pb/4k3/P1pPn3/8/P5PP/1rB2RK1 b - d3 0 28", 6, 38633283 }, { "8/3K4/2p5/p2b2r1/5k2/8/8/1q6 b - - 1 67", 7, 493407574 }, { "rnbqkb1r/ppppp1pp/7n/4Pp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3", 6, 244063299 }, { "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", 5, 193690690 }, { "8/p7/8/1P6/K1k3p1/6P1/7P/8 w - -", 8, 8103790 }, { "n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - -", 6, 71179139 }, { "r3k2r/p6p/8/B7/1pp1p3/3b4/P6P/R3K2R w KQkq -", 6, 77054993 }, { "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", 7, 178633661 }, { "8/5p2/8/2k3P1/p3K3/8/1P6/8 b - -", 8, 64451405 }, { "r3k2r/pb3p2/5npp/n2p4/1p1PPB2/6P1/P2N1PBP/R3K2R w KQkq -", 5, 29179893 }, #endif { NULL, 0, 0 } }; /* Test positions, for Spartan chess */ static position_signature_t spartan_perftests[] = { { "5Q1k/8/7K/8/8/8/5h2/8 b - -", 5, 53727 }, { "k7/g6k/1c6/7K/2B1Q3/8/8/1R6 b - - 34 75", 5, 6293168 }, { "lgkcckw1/hhhhhhhh/8/8/3l4/5P2/PPPPPKPP/RNBQ1BNR w KQ - 3 3", 5, 1190857 }, { "1R1k1w2/h5kh/h1g1B2h/3c3h/3Q1PN1/8/2P3PP/R6K b - - 0 1", 5, 23939987 }, { "3k4/2h1k2h/5Q2/2hR4/3h4/8/PP3P1P/3w1K2 b - 0 7", 5, 3195587 }, { "4k2R/4hk2/2c2hh1/h3gh2/3h4/1B3P2/PP3P1P/2R2K2 b - 0 7", 5, 6440892 }, { "3k4/2h2k1R/5Q2/g4h2/3h4/8/PP3P1P/3w1K2 b - 1 6", 5, 8066536 }, { NULL, 0, 0 } }; /* Test positions, for Shogi */ static position_signature_t shogi_perftests[] = { { "8l/1l+R2P3/p2pBG1pp/kps1p4/Nn1P2G2/P1P1P2PP/1PS6/1KSG3+r1/LN2+p3L [Sbgnppp] b 0 62", 3, 2552846 }, { "lnsgkgsnl/1r5b1/pppppp1pp/6p2/9/2P6/PP1PPPPPP/1B5R1/LNSGKGSNL [-] w 0 1", 4, 2000286 }, { "l6+Rl/3s1s3/p4p1pp/1G1G2p2/1p3kBN1/4P1P2/PP+p2R1PP/4KS3/1+b6L[PPPNNSppnlgg] b 1 47", 1, 5 }, { NULL, 0, 0 } }; /* Test positions, for Xiangqi */ static position_signature_t xiangqi_perftests[] = { { "rheakaehr/9/1c7/p1p1p1p1p/7c1/4C4/P1P1P1P1P/1C7/9/RHEAKAEHR b - 4 2", 4, 411539 }, { "2e1ka1h1/2P1h1HC1/9/p3pc3/8p/9/4P2c1/4E4/9/2EAKA3 b 2 26", 2, 60 }, { "rheakaehr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RHEAKAEHR w - - 0 1", 5, 133312995 }, { NULL, 0, 0 } }; /* Test positions, for Seirawan */ static position_signature_t seirawan_perftests[] = { { "Q3k2r/2qnbppp/1ppppn2/5bB1/3P4/2N1PN2/PP2BPPP/R3K2R [HEhe] b KQBCFGkcdfg - 0 2", 1, 5 }, { "rQbqkbnr/p1pppppp/8/8/2p5/8/PP1PPPPP/RNB1KBNR [HEhe] b KQBCFGkqcdfg - 0 3", 1, 30 }, { "6k1/5ppp/1p6/1P6/4e3/8/rb1BKPbP/1R6 [-] w - - 8 1", 4, 97954 }, { "1h1qkber/rQ1bp1pp/3p1n2/2pP2N1/p7/P3B3/1PP2PPP/RE2KBHR [-] w KQk - 1 16", 4, 1966909 }, { NULL, 0, 0 } }; /* Test positions, for Sittuyin */ static position_signature_t sittuyin_perftests[] = { { "8/6k1/6p1/3s2P1/3npR2/2r5/p2N2F1/3K4[-] b 0 49", 4, 395063 }, { "8/5R2/2P5/5S2/3sFs2/P3k3/2K5/8[-] w 3 69", 4, 62239 }, { NULL, 0, 0 } }; /* Test positions, for Crazyhouse */ static position_signature_t crazyhouse_perftests[] = { { "2kr1b1r/p1p1ppp1/2q1b1pn/2p1P3/2PpN3/3P2NP/PP3PP1/R1BQ1RK1[Bn] b - c3 0 16 ", 4, 12608213 }, { "rnb1kbnr/Ppp1pppp/p7/3q4/8/8/PPPP1PPP/RNBQKBNR[p] w KQkq - 0 4", 4, 5225501 }, { "rQ~b1kbnr/1pp1pppp/p7/3q4/8/8/PPPP1PPP/RNBQKBNR[Np] b KQkq - 0 4", 4, 12471091 }, { NULL, 0, 0 } }; /* Benchmark positions. For now we just use the Stockfish ones */ static position_signature_t benchtests[] = { { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 0, 0 }, { "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", 0, 0 }, { "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", 0, 0 }, { "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", 0, 0 }, { "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", 0, 0 }, { "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", 0, 0 }, { "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", 0, 0 }, { "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", 0, 0 }, { "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", 0, 0 }, { "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", 0, 0 }, { "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", 0, 0 }, { "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", 0, 0 }, { "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", 0, 0 }, { "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", 0, 0 }, { "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", 0, 0 }, { "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", 0, 0 }, { NULL, 0, 0 } }; static help_topic_t help_topic[] = { /* .........|.........|.........|.........|.........|.........|.........|.........| */ { "all", NULL, NULL }, { "analyse", "analyse, analyze", " Analyse the current position.\n" }, { "bench", "test benchmark [depth]", " Perform a benchmark test to the specified depth. The returned node-count\n" " can be used as a validation that the program is working correctly while\n" " the reported time can be used as a performance measure on the current\n" " system.\n" }, { "board", "board [on|off]", " Print the current board position, or toggles automatic printing of the\n" " current position on and off.\n" }, { "book", "book [filename|off]", " Set the name of the (polyglot) opening book file to use.\n" " 'book off' switches off the opening book.\n" }, #ifdef SMP { "cores", "cores N, threads N", " Use N cores/threads for the search.\n" }, #endif { "fen", NULL, " Print the FEN representation of the current board position.\n" }, { "force", NULL, " Switch to 'force' mode: Sjaak will play neither side in the game until\n" " it receives a 'go' command\n" }, { "go", NULL, " Switch off force mode, tell Sjaak to start playing from the current position.\n" }, { "help", "help [topic]", " Display a list of help topics, or detailed information about a particular\n" " topic.\n" }, { "hint", NULL, " Print the move that Sjaak is pondering on. If there is no ponder move, no\n" " hint is displayed.\n" }, { "history", NULL, " Print the game history to the current point.\n" }, { "level", "level moves time inc", " Set time control options: number of moves per session, time per session\n" " (either in minutes or minutes:seconds) and time increment (in seconds)\n" }, { "load", "load filename", " Load variant descriptions from the specified file\n" }, { "maxnodes", "maxnodes n", " Set the maximum number of nodes that can be searched before a move is\n" " returned. Setting maxnodes to 0 disables it and restores normal time\n" " control.\n" }, { "memory", "memory [MB]", " Set the (approximate) amount of memory the program can use, in MB. The\n" " actual amount of memory used will be different from this and may be\n" " slightly larger or smaller.\n" }, { "multipv", "multipv n", " Set the number of full evaluation/variations to find in analysis mode.\n"}, { "new", NULL, " Start a new game from the starting position.\n" " In xboard mode the current variant is reset to 'normal'.\n" }, { "perft", "perft [depth] [print depth]", " Perform a 'perft' (performance test) on the current position: count all\n" " positions that can result from this position to the specified depth.\n" " If 'print depth' is specified then the total will be sub-divided per move\n" " upto 'print depth'\n" }, { "ponder", "ponder [on|off]", " Switches ponder mode on or off (Sjaak will think while it is not on move)\n" }, { "post", "(no)post, post off", " Display thinking output (post) or not (nopost/post off)\n" }, { "prompt", "prompt [on|off]", " Switch the prompt on or off\n" }, { "quit", "quit, exit, bye", " Exit Sjaak\n" }, { "random", "random [on|off]", " Switch randomisation of opening moves on or off.\n" }, { "readepd", "readepd filename", " Read (and setup) a position from an EPD file.\n" }, { "rules", NULL, " Summarise the rules of the game and the movement of the pieces in human-radable text.\n" }, { "setboard", "setboard FEN", " Setup a position on the board from a FEN string.\n" }, { "sd", "sd depth", " Specify the maximum search depth\n" }, { "settings", NULL, " Show current settings\n" }, { "shell", "!command", " Run a shell command.\n" }, { "skill", "skill level", " Set the skill level for engine play.\n"}, { "st", "st time", " Specify the maximum time (in seconds) to think on a single move\n" }, { "takeback", "takeback, remove", " Reverses the last two moves in the game, if any.\n" }, { "test", "test [movegen|benchmark [depth]|legal movegen|chase|see |wac|sts]", " Perform tests on the move generator, the search or various evaluation\n" " components. Can also run a number of build-in test suites.\n" }, { "time", "time csec", " Set the remaining time on the engine's clock, in centi-seconds.\n" }, { "ucci", "ucci [on|off]", " Toggle UCCI mode on or off. In UCCI mode commands from the UCCI protocol\n" " are recognised in addition to the standard commands described here.\n" " 'ucci' is equivalent to 'ucci on'\n"}, { "uci", "uci [on|off]", " Toggle UCI mode on or off. In UCI mode commands from the UCI protocol\n" " are recognised in addition to the standard commands described here.\n" " 'uci' is equivalent to 'uci on'\n"}, { "undo", NULL, " Unmakes the last move in the game, if any. If it is Sjaak's turn, it will\n" " start thinking again immediately.\n" }, { "unload", "unload filename", " Unload variant descriptions from the specified file\n" }, { "usi", "usi [on|off]", " Toggle USI mode on or off. In USI mode commands from the USI protocol\n" " are recognised in addition to the standard commands described here.\n" " 'usi' is equivalent to 'usi on'\n"}, { "variant", "variant name", " Set the name of the variant that will be selected when a new game is started.\n" " If no name is specified a list of known variants is displayed.\n" }, { "variants", NULL, " List all known variants.\n" }, { "xboard", "xboard [on|off]", " Switch to xboard mode: prompt off, don't display board, don't print SAN\n" " moves, don't trap interrupts (ctrl-C).\n" " 'xboard' is equivalent to 'xboard on'\n" }, { "MOVE", NULL, " Play a move on the board. Moves can be entered as 'long algebraic'\n" " (e2e4, g1f3) or SAN (e4, Nf3).\n" }, { NULL, NULL, NULL } }; #ifdef HAVE_READLINE static const char *xcmd_list[] = { "moves", "longmoves", "pseudomoves", "pieces", "pieceinfo", NULL, }; #endif static int option_ms = MATE_SEARCH_ENABLE_DROP; static int draw_count = 0; static int resign_count = 0; static int random_ply_count = 10; static int multipv = 1; static eval_t random_amplitude = 20; static eval_t draw_threshold = 0; static eval_t resign_threshold = -500; static bool user_variants_first = true; static bool mask_dark_squares = false; static bool send_piece_descriptions = true; static bool report_fail_low = false; static bool report_fail_high = false; static bool repetition_claim = true; static bool castle_oo = true; static bool remember_eval_file = false; static bool prompt = true; static bool show_board = true; static bool san = true; static bool trapint = true; static int tc_moves = 40; static int tc_time = 60000; static int tc_inc = 0; static char configfile[1024] = { 0 }; static char *fairy_file = NULL; static char *eval_file = NULL; static char deferred[256]; static int lift_sqr = 0; static int skill_level = LEVEL_NORMAL; static struct { const char *label, *name; bool *opt; bool def; bool neg; bool resend_options; } boolean_settings[] = { { "Send 'piece' descriptions", "send_piece_descriptions", &send_piece_descriptions, send_piece_descriptions, false, false }, { "Mark holes in board", "mask_dark_squares", &mask_dark_squares, mask_dark_squares, true, false }, { "List user-defined variants before buildin variants", "user_variants_first", &user_variants_first, user_variants_first, false, true }, { "Report fail low", "report_fail_low", &report_fail_low, report_fail_low, false, false }, { "Report fail high", "report_fail_high", &report_fail_high, report_fail_high, false, false }, { "Claim repetitions", "repetition_claim", &repetition_claim, repetition_claim, false, false }, { "Send O-O/O-O-O for castling", "castle_oo", &castle_oo, castle_oo, false, false }, { "Remember evaluation parameter file", "remember_eval_file", &remember_eval_file, remember_eval_file, false, false }, { NULL, NULL, NULL, false, false, false }, }; static struct { const char *label; int value; int *var; } combo_skill[] = { { "Clueless", LEVEL_RANDOM, &skill_level }, { "Random", LEVEL_BEAL, &skill_level }, { "Static", LEVEL_STATIC, &skill_level }, { "Normal", LEVEL_NORMAL, &skill_level }, }; #ifdef __APPLE__ #define __unix__ #endif #ifdef __unix__ static sig_t old_signal_handler; void interrupt_computer(int i) { abort_search = true; if (old_signal_handler) old_signal_handler(i); } #endif #ifdef HAVE_READLINE static bool stdin_is_terminal(void) { #ifdef __unix__ return isatty(fileno(stdin)); #else return true; #endif } #endif static FILE *f = NULL; static char *buf; static bool may_ponder = false; static bool in_play = true; static void log_engine_output(const char *msg, ...) ATTRIBUTE_UNUSED; static void log_engine_output(const char *msg, ...) { va_list ap; va_start(ap, msg); vsnprintf(buf, 65535, msg, ap); va_end(ap); if (f) { fprintf(f, " %s", buf); fflush(f); } } static void log_xboard_output(const char *msg, ...) { va_list ap; static bool newline = true; va_start(ap, msg); vsnprintf(buf, 65535, msg, ap); va_end(ap); if (f) { if (newline) fprintf(f, "> "); newline = false; fprintf(f, "%s", buf); if (strstr(buf, "\n")) newline = true; } printf("%s", buf); } struct variant_file_list_t { const char *filename; const char *shortname; const char *longname; int files, ranks; size_t line_number; }; static variant_file_list_t *custom_variants = NULL; static int num_custom_variants = 0; static int max_custom_variants = 0; static variant_file_list_t *new_variant_file_entry(void) { variant_file_list_t *file = NULL; if (num_custom_variants >= max_custom_variants) { max_custom_variants = max_custom_variants + 8; custom_variants = (variant_file_list_t *)realloc(custom_variants, max_custom_variants*sizeof *custom_variants); } file = custom_variants + num_custom_variants; num_custom_variants++; return file; } static void invalidate_variant_file(const char *filename) { if (!filename) return; /* Skip if the file has already been indexed. * TODO: we need a way to invalidate a previously scanned file. */ for (int n=0; n 0; n++) { if (!streq(custom_variants[n].filename, filename)) continue; free((void *)custom_variants[n].filename); free((void *)custom_variants[n].shortname); free((void *)custom_variants[n].longname); custom_variants[n].filename = NULL; custom_variants[n].shortname = NULL; custom_variants[n].longname = NULL; custom_variants[n] = custom_variants[num_custom_variants-1]; n--; num_custom_variants--; } } static void scan_variant_file(const char *filename) { /* Skip if the file has already been indexed. * TODO: we need a way to invalidate a previously scanned file. */ for (int n=0; n line && isspace(s[0])) { s[0] = '\0'; s--; } /* New variant */ if (strstr(line, "Variant:") == line) { s = line + 8; while (isspace(*s)) s++; free((void *)name); name = strdup(s); continue; } if (strstr(line, "Board:") == line) { variant_file_list_t *file = new_variant_file_entry(); int files, ranks; sscanf(line + 6, "%dx%d", &files, &ranks); file->filename = strdup(filename); file->longname = strdup(name); file->files = files; file->ranks = ranks; file->line_number = line_number; char *s = strdup(name); file->shortname = s; /* Truncate the long name into something XBoard can handle */ while (*s) { *s = tolower(*s); if (isspace(*s)) *s = '_'; if (!isalnum(s[1])) { if (!isspace(s[1]) && s[1] != '-') s[1] = 0; if (*s == '_') *s = 0; } s++; } } line_number++; } free((void *)name); //for (int n = 0; nmove_string_to_move(s); if (move == 0) { //fprintf(stderr, "\n"); if (f) { fprintf(f, "Report illegal move %s in position:\n", move_str); //print_bitboards_file(f, game->board); } return false; } game->playmove(move); } else { return false; } return true; } static void send_legal_move_targets(game_t *game, int from, const movelist_t *external_movelist = NULL) { int move_board[256]; char fen[4096]; if (!game) return; memset(move_board, 0, sizeof move_board); movelist_t movelist; /* First, generate the list of moves for this position */ if (external_movelist) { movelist.clear(); for(int n = 0; nnum_moves; n++) movelist.push(external_movelist->move[n]); } else { game->generate_legal_moves(&movelist); } move_t move; bool set = false; while ((move = movelist.next_move())) { if (is_drop_move(move)) continue; if (get_move_from(move) != from) continue; int s = game->bit_to_square[get_move_to(move)]; if (s < 0) continue; int p = get_move_piece(move); int piece = piece_symbol_string[p]; bool multi = is_double_capture_move(move) || (piece != ' ' && is_capture_move(move) && get_move_capture_square(move)!=get_move_to(move)); if (multi) { int n = get_move_pickups(move); for (int c=0; cbit_to_square[square]] = 4; } } if (is_castle_move(move) && abs(get_move_from(move) - get_move_to(move)) == 1) { int square = get_castle_move_from2(move); move_board[game->bit_to_square[square]] = 4; } if (move_board[s] == 5) move_board[s] = 3; if (move_board[s] == 4) continue; if (move_board[s] == 3) continue; if (is_promotion_move(move)) move_board[s] = 5; else if (move_board[s] == 0 && is_capture_move(move)) move_board[s] = 2; else move_board[s] = 1; set = set || move_board[s]; } if (!set) { /* Second part in a multi-step move */ movelist.rewind(); while ((move = movelist.next_move())) { int p = get_move_piece(move); int piece = piece_symbol_string[p]; bool multi = is_double_capture_move(move) || (piece != ' ' && is_capture_move(move) && get_move_capture_square(move)!=get_move_to(move)); if (get_move_to(move) == from) { int s = game->bit_to_square[from]; if (s>=0) move_board[s] = 1; } if (!multi) continue; int n = get_move_pickups(move); bool mark = false; for (int c=0; cbit_to_square[square]; if (is_double_capture_move(move)) move_board[s] = 2; else move_board[s] = 1; } int s = game->bit_to_square[get_move_to(move)]; if (is_double_capture_move(move)) move_board[s] = 2; else move_board[s] = 1; } } //log_xboard_output("# %s\n# Set: %d\n", game->make_fen_string(), set); int n = 0; int ranks = (game->virtual_ranks > 0) ? game->virtual_ranks : game->ranks; int files = (game->virtual_files > 0) ? game->virtual_files : game->files; for (int r = ranks-1; r>=0; r--) { int count = 0; for (int f = 0; f < files; f++) { int s = f + r * files; /* Empty? */ if (move_board[s] == 0) { count++; continue; } /* Not empty, do we have a count? */ if (count) n += snprintf(fen+n, 4096 - n, "%d", count); count = 0; const char *colour_string = " YRMCB"; n += snprintf(fen+n, 4096-n, "%c", colour_string[move_board[s]]); } if (count) n += snprintf(fen+n, 4096 - n, "%d", count); if (r) n += snprintf(fen+n, 4096 - n, "/"); } log_xboard_output("highlight %s\n", fen); } /* Play a sequence of moves from the initial position */ void replay_game(game_t *game, char *moves) { movelist_t movelist; char *s; /* First rewind to the beginning if not there yet */ while (game->moves_played) game->takeback(); s = moves; while(s && *s) { char move_str[10] = { 0 }; while (*s == ' ') s++; for (int n = 0; n<10; n++) { if (*s == ' ' || *s == 0) break; move_str[n] = *s; s++; } bool result = input_move(game, move_str); if (!result) { fprintf(stderr, "Bad move %s in movelist!\n", move_str); break; } } } static void set_time_from_string(game_t *game, const char *timestr) { float milliseconds; sscanf(timestr, "%g", &milliseconds); milliseconds *= 10; /* Reserve a bit of time for when we reach the time control, so we don't get penalised by a time * loss. We don't need this for a Fischer clock. */ if (milliseconds > TIME_BUFFER) milliseconds -= TIME_BUFFER; game->clock.time_left = int(milliseconds); game->move_clock[game->moves_played] = game->clock.time_left; set_time_for_game(&game->clock); } static void set_timecontrol_from_string(game_t *game, const char *tcstr) { int moves, milliseconds; float minutes, seconds, inc; /* Set defaults */ set_time_per_move(&game->clock, 5000); game->clock.movestotc = 0; game->clock.movestogo = 0; game->clock.time_inc = 0; if (strstr(tcstr, ":")) { sscanf(tcstr, "%d %g:%g %g", &moves, &minutes, &seconds, &inc); } else { sscanf(tcstr, "%d %g %g", &moves, &minutes, &inc); seconds = 0; } seconds += minutes*60; milliseconds = int(seconds * 1000); /* Reserve a bit of time for when we reach the time control, so we don't get penalised by a time * loss. We don't need this for a Fischer clock. */ if (inc == 0 && milliseconds > TIME_BUFFER) milliseconds -= TIME_BUFFER; game->clock.movestotc = moves; game->clock.movestogo = moves; game->clock.time_inc = int(inc*1000); game->clock.time_left = milliseconds; game->move_clock[game->moves_played] = game->clock.time_left; set_time_for_game(&game->clock); tc_moves = game->clock.movestotc; tc_time = game->clock.time_left; tc_inc = game->clock.time_inc; } static bool interrupt_ponder(game_t *) { return keyboard_input_waiting(); } static bool keyboard_input_on_move(game_t *game) { if (deferred[0]) return true; if (abort_search) return true; static char ponder_input[65536]; bool input_waiting = keyboard_input_waiting(); bool read_input = input_waiting && fgets(ponder_input, sizeof ponder_input, stdin); if (read_input) { chomp(ponder_input); trim(ponder_input); if (strstr(ponder_input, "random")) { sgenrand((unsigned int)time(NULL)); game->random_ok = true; game->random_key = genrandui(); game->random_amplitude = random_amplitude; game->random_ply_count = random_ply_count; } else if (strstr(ponder_input, "easy")) { may_ponder = false; } else if (strstr(ponder_input, "hard")) { may_ponder = true; } else if (strstr(ponder_input, "nopost") || strstr(ponder_input, "post off") == ponder_input) { game->set_xboard_output_function(NULL); } else if (strstr(ponder_input, "post")) { game->set_xboard_output_function(log_xboard_output); } else if (strstr(ponder_input, "?") == ponder_input) { return true; } else if (strstr(ponder_input, "stop") == ponder_input) { return true; } else if (strstr(ponder_input, "draw")) { if (game->draw_count > draw_count) log_xboard_output("offer draw\n"); } else if (strstr(ponder_input, "new")) { snprintf(deferred, sizeof deferred, "%s", ponder_input); return true; } else if (strstr(ponder_input, "otim")) { } else if (strstr(ponder_input, "ping")) { snprintf(deferred, sizeof deferred, "%s", ponder_input); return true; } else if (strstr(ponder_input, "result")) { return true; } else if (strstr(ponder_input, "time")) { set_time_from_string(game, ponder_input+5); } else if (strstr(ponder_input, "level") == ponder_input) { set_timecontrol_from_string(game, ponder_input+6); } else if (strstr(ponder_input, "st ")) { float tpm = 5; sscanf(ponder_input+3, "%g", &tpm); int time_per_move = int(tpm * 1000); set_time_per_move(&game->clock, time_per_move); } else if (strstr(ponder_input, "force") == ponder_input) { in_play = false; } else if (strstr(ponder_input, "pause")) { uint64_t start_pause = get_timer(); /* Sleep until keyboard input */ while(!fgets(ponder_input, sizeof ponder_input, stdin) || !strstr(ponder_input, "resume")); uint64_t stop_pause = get_timer(); game->clock.start_time += stop_pause - start_pause; } else if (streq(ponder_input, "quit")) { if (game) delete game; free(buf); exit(0); } else { snprintf(deferred, sizeof deferred, "%s", ponder_input); } } return false; } static bool (*uci_clock_handler)(const struct chess_clock_t *clock); static bool uci_keyboard_input_on_ponder(game_t *game) { if (!game->pondering) return true; static char ponder_input[65536]; if (keyboard_input_waiting() && fgets(ponder_input, sizeof ponder_input, stdin)) { if (f) { fprintf(f, "< %s\n", ponder_input); fflush(f); } if (strstr(ponder_input, "ponderhit")) { game->clock.check_clock = uci_clock_handler; game->pondering = false; game->check_keyboard = NULL; return false; } else if (strstr(ponder_input, "isready") == ponder_input) { log_xboard_output("readyok\n"); return false; } else if (strstr(ponder_input, "stop") == ponder_input) { game->pondering = false; return true; } else if (streq(ponder_input, "quit")) { if (game) delete game; free(buf); exit(0); } } return false; } static bool keyboard_input_analyse(game_t *game) { static char ponder_input[65536]; bool restart = false; if (game->analyse_move != 0) return true; if (!game->analysing) return false; if (keyboard_input_waiting() && fgets(ponder_input, sizeof ponder_input, stdin)) { chomp(ponder_input); trim(ponder_input); if (strstr(ponder_input, "undo")) { if (game->analyse_moves_played) { game->analyse_undo++; restart = true; } } else if (strstr(ponder_input, "new")) { game->analyse_new = true; restart = true; } else if (strstr(ponder_input, "exit")) { game->analysing = false; restart = true; } else if (strstr(ponder_input, "setboard")) { char *p = strstr(ponder_input, " "); while (p && *p && p[0]==' ') p++; free((void *)game->analyse_fen); game->analyse_fen = strdup(p); restart = true; } else if (strstr(ponder_input, ".") == ponder_input) { } else if (strstr(ponder_input, "hint")) { } else if (strstr(ponder_input, "bk")) { } else if (streq(ponder_input, "quit")) { if (game) delete game; free(buf); exit(0); } else if (strstr(ponder_input, "lift")) { for (int square = 0; square < game->ranks*game->files; square++) { if (streq(ponder_input+5, square_names[square])) { send_legal_move_targets(game, square, &game->analyse_movelist); lift_sqr = square; break; } } } else if (strstr(ponder_input, "put")) { /* Send promotion options, for promotion moves */ char choice_str[256]; int cs = 0; const char *keep = ""; movelist_t movelist; movelist.clear(); for(int n = 0; nanalyse_movelist.num_moves; n++) movelist.push(game->analyse_movelist.move[n]); move_t move; while ((move = movelist.next_move())) { if (get_move_from(move) != lift_sqr) continue; for (int square = 0; square < game->ranks*game->files; square++) { if (streq(ponder_input+4, square_names[square])) { if (get_move_to(move) != square) continue; if (!is_promotion_move(move)) { int p = get_move_piece(move); keep = game->get_piece_notation(p); break; } int p = get_move_promotion_piece(move); cs += snprintf(choice_str+cs, sizeof(choice_str) - cs, "%s", game->get_piece_notation(p)); break; } } } if (cs) { cs+=snprintf(choice_str+cs, sizeof(choice_str) - cs, "%s", keep); for (int n=0; nmove_string_to_move(s, &game->analyse_movelist); game->analyse_move = move; if (move == 0) log_xboard_output("Illegal move: %s\n", ponder_input); else restart = true; } } } return restart; } static uint64_t perft(game_t *game, int depth, int root, bool legal = false) { movelist_t *movelist = game->movelist + depth; side_t me = game->get_side_to_move(); uint64_t nodes = 0; int n; if (depth == 0) return 1; /* Check if previous move left the player in check */ if (legal) game->generate_legal_moves(movelist); else game->generate_moves(movelist); for (n=0; nnum_moves; n++) { uint64_t count = 0; game->playmove(movelist->move[n]); if (legal || !game->player_in_check(me)) { /* Don't count illegal moves */ game->test_move_game_check(); count = perft(game, depth-1, root - 1); } nodes += count; if (root > 0) printf("%8s %10" PRIu64 " %10" PRIu64 "\n", move_to_string(movelist->move[n], NULL), count, nodes); game->takeback(); if (abort_search) break; } return nodes; } static bool run_movegen_test(const char *name, const char *variant, const position_signature_t *suite, bool legal) { game_t *game = NULL; int n = 0; while (suite[n].fen) { uint64_t nodes; game = create_variant_game(variant); game->start_new_game(); game->setup_fen_position(suite[n].fen); printf("."); fflush(stdout); nodes = perft(game, suite[n].depth, 0, legal); if (nodes != suite[n].nodes) { printf("\n"); printf("*** Failed at %s position %d (%s):\n", name, n, suite[n].fen); printf(" Expected %" PRIu64 " nodes at depth %d, got %" PRIu64 " nodes\n", suite[n].nodes, suite[n].depth, nodes); return false; } delete game; n++; } return true; } static void test_movegen(bool legal = false) { if (!run_movegen_test("Chess", "chess", perftests, legal)) return; if (!run_movegen_test("Spartan", "spartan", spartan_perftests, legal)) return; if (!run_movegen_test("Shogi", "shogi", shogi_perftests, legal)) return; if (!run_movegen_test("XiangQi", "xiangqi", xiangqi_perftests, legal)) return; if (!run_movegen_test("Seirawan", "seirawan", seirawan_perftests, legal)) return; if (!run_movegen_test("Sittuyin", "sittuyin", sittuyin_perftests, legal)) return; if (!run_movegen_test("Crazyhouse", "crazyhouse", crazyhouse_perftests, legal)) return; printf("\nOk.\n"); } static uint64_t test_benchmark(int depth) { game_t *game = NULL; int n = 0; uint64_t nodes = 0; uint64_t t = get_timer(); unsigned long long int nodes_searched = 0; #ifdef TRACK_PRUNING_STATISTICS memset(branches_pruned_by_move, 0, sizeof branches_pruned_by_move); #endif while (benchtests[n].fen) { game = create_variant_game("chess"); game->start_new_game(); game->random_ok = false; game->output_iteration = NULL; game->uci_output = NULL; game->xboard_output = NULL; //printf("%s\n", benchtests[n].fen); game->setup_fen_position(benchtests[n].fen); printf("."); fflush(stdout); #ifdef __unix__ if (trapint) old_signal_handler = signal(SIGINT, interrupt_computer); #endif game->think(depth); #ifdef __unix__ if (trapint) signal(SIGINT, old_signal_handler); #endif nodes_searched = game->clock.nodes_searched; delete game; if (abort_search) { printf("\n*** Aborted"); break; } //printf("%"PRIu64"\n", nodes); nodes += nodes_searched; n++; } uint64_t tt = get_timer(); printf("\n"); printf("%" PRIu64 " nodes searched\n", nodes); printf("Elapsed time %" PRIu64 " ms\n", (tt - t) / 1000); printf("%g nodes / s\n", 1.0e6*nodes / (tt - t)); return (tt - t); } #ifdef SMP /* Test the SMP search. * Test position is the first from Hyatt (1994), after White's 9th move in a game * Mchess Pro - Cray Blitz. */ static void test_smp(int cores, int depth) { game_t *game = NULL; int n = get_number_of_threads(); uint64_t t_start[2]; uint64_t t_end[2]; unsigned long long int moves_searched[2]; char *fen = "r2qkbnr/ppp2p1p/2n5/3P4/2BP1pb1/2N2p2/PPPQ2PP/R1B2RK1/ b - - 2 9";//benchtests[1].fen; init_threads(1); game = create_variant_game("chess") game->start_new_game(); game->random_ok = false; setup_fen_position(game, fen); print_board(game->board); printf("Single core analysis:\n"); t_start[0] = get_timer(); computer_play(game, depth); t_end[0] = get_timer(); moves_searched[0] = game->moves_searched; end_game(game); init_threads(cores); printf("Analysis using %d cores:\n", cores); game = create_variant_game("chess") game->start_new_game(); game->random_ok = false; setup_fen_position(game, fen); t_start[1] = get_timer(); computer_play(game, depth); t_end[1] = get_timer(); moves_searched[1] = game->moves_searched; end_game(game); init_threads(n); printf("\n"); printf("Single core: %-10"PRIu64" nodes searched\n", moves_searched[0]); printf("Multi-core: %-10"PRIu64" nodes searched\n", moves_searched[1]); printf("Single core time %"PRIu64" ms\n", (t_end[0]-t_start[0]) / 1000); printf("Multi-core time %"PRIu64" ms\n", (t_end[1]-t_start[1]) / 1000); printf("Relative node counts (multi/single): %.2f\n", (float)moves_searched[1] / moves_searched[0]); printf("Parallel speed-up (time-to-depth): %.2f\n", (float)(t_end[0]-t_start[0]) / (t_end[1]-t_start[1])); } #endif static void run_test_suite(uint64_t time, const char *tests[]) { movelist_t legal_moves; movelist_t best_moves; int n = 0; int position_searched = 0; int position_correct = 0; int score = 0; int max_positions = 0; while (tests[max_positions]) max_positions++; while (tests[n]) { const char *best_move_string = strstr(tests[n], "bm "); if (!best_move_string) { n++; continue; } best_move_string += 3; position_searched++; printf("(%d/%d) %s:\n", position_searched, max_positions, tests[n]); game_t *game = create_variant_game("chess"); game->start_new_game(); game->setup_fen_position(tests[n]); game->generate_legal_moves(&legal_moves); best_moves.clear(); const char *move_score_string = strstr(tests[n], "c0"); if (move_score_string == NULL || strstr(move_score_string, "=") == NULL) { const char *s = best_move_string; while (*s && *s != ';') { char move_str[32] = { 0 }; char *p = move_str; while (*s && isspace(*s)) s++; while (*s && !isspace(*s) && *s != ';') { if (*s != '+') { *p = *s; p++; } s++; } move_t move = game->move_string_to_move(move_str, &legal_moves); if (move) { best_moves.push(move); best_moves.score[best_moves.num_moves-1] = 10; } } } else { const char *s = move_score_string + 4; while (*s && *s != ';') { char move_str[32] = { 0 }; int score = 10; char *p = move_str; while (*s && isspace(*s)) s++; while (*s && !isspace(*s) && *s != '=') { if (*s != '+') { *p = *s; p++; } s++; } if (*s == '=') { s++; sscanf(s, "%d", &score); while (*s && !isspace(*s)) s++; } move_t move = game->move_string_to_move(move_str, &legal_moves); if (move) { best_moves.push(move); best_moves.score[best_moves.num_moves-1] = score; } } } set_time_per_move(&game->clock, (int)time); game->think(MAX_SEARCH_DEPTH); move_t move = game->get_last_move(); //printf("%s\n", move_to_string(move)); //best_moves.print(); bool correct = false; for (int k=0; krep_score == LEGALDRAW) log_xboard_output("1/2-1/2 {%d-fold repetition}\n", game->repeat_claim+1); else { side_t me = game->get_side_to_move(); if (game->get_rules() & RF_USE_CHASERULE) { if (game->player_in_check(me)) { if (game->rep_score < 0) { if (game->get_side_to_move() == BLACK) log_xboard_output("0-1 {White chases}\n"); else log_xboard_output("1-0 {Black chases}\n"); } else { if (game->get_side_to_move() == BLACK) log_xboard_output("1-0 {White chases}\n"); else log_xboard_output("0-1 {Black chases}\n"); } } else { if (game->rep_score < 0) { if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {Black chases}\n"); else log_xboard_output("1-0 {White chases}\n"); } else { if (game->get_side_to_move() == WHITE) log_xboard_output("1-0 {Black chases}\n"); else log_xboard_output("0-1 {White chases}\n"); } } } else { if (game->rep_score < 0) { if (game->get_side_to_move() == BLACK) log_xboard_output("0-1 {White repeats}\n"); else log_xboard_output("1-0 {Black repeats}\n"); } else { if (game->get_side_to_move() == BLACK) log_xboard_output("1-0 {White repeats}\n"); else log_xboard_output("0-1 {Black repeats}\n"); } } } break; case SEARCH_GAME_ENDED_MATE: if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {Black mates}\n"); else log_xboard_output("1-0 {White mates}\n"); break; case SEARCH_GAME_ENDED_CHECK_COUNT: if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {Black checks %d times}\n", game->check_limit); else log_xboard_output("1-0 {White checks %d times}\n", game->check_limit); break; case SEARCH_GAME_ENDED_LOSEBARE: if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {Bare king}\n"); else log_xboard_output("1-0 {Bare king}\n"); break; case SEARCH_GAME_ENDED_WINBARE: if (game->get_side_to_move() == WHITE) log_xboard_output("1-0 {Bare king}\n"); else log_xboard_output("0-1 {Bare king}\n"); break; case SEARCH_GAME_ENDED_STALEMATE: if (game->stale_score == 0) log_xboard_output("1/2-1/2 {Stalemate}\n"); else if (game->stale_score < 0) { if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {Black mates}\n"); else log_xboard_output("1-0 {White mates}\n"); } else { if (game->get_side_to_move() == WHITE) log_xboard_output("1-0 {Stalemate}\n"); else log_xboard_output("0-1 {Stalemate}\n"); } break; case SEARCH_GAME_ENDED_INSUFFICIENT: log_xboard_output("1/2-1/2 {insufficient material}\n"); break; case SEARCH_GAME_ENDED_FORFEIT: log_xboard_output("Illegal move (may not be used to give mate): %s\n", input); game->takeback(); //if (game->get_side_to_move() == WHITE) // log_xboard_output("1-0 {Black forfeits}\n"); //else // log_xboard_output("0-1 {White forfeits}\n"); break; case SEARCH_GAME_ENDED_INADEQUATEMATE: log_xboard_output("1/2-1/2 {Mate, but no shak}\n"); break; case SEARCH_GAME_ENDED_FLAG_CAPTURED: if (game->flag_score == 0) log_xboard_output("1/2-1/2 {Flag captured}\n"); else if (game->flag_score < 0) { if (game->side_captured_flag(BLACK)) log_xboard_output("0-1 {Black captures the flag}\n"); else log_xboard_output("1-0 {White captures the flag}\n"); } else { if (game->side_captured_flag(BLACK)) log_xboard_output("1-0 {Black captures the flag}\n"); else log_xboard_output("0-1 {White captures the flag}\n"); } break; case SEARCH_GAME_ENDED_NOPIECES: if (game->no_piece_score == 0) { log_xboard_output("1/2-1/2 {No pieces remaining}\n"); } else if (game->no_piece_score < 0) { if (game->get_side_to_move() == WHITE) log_xboard_output("0-1 {No white pieces remaining}\n"); else log_xboard_output("1-0 {No black pieces remaining}\n"); } else { if (game->get_side_to_move() == WHITE) log_xboard_output("1-0 {No white pieces remaining}\n"); else log_xboard_output("0-1 {No black pieces remaining}\n"); } break; case SEARCH_GAME_ENDED: log_xboard_output("telluser game ended (unknown reason)\n"); break; } } static void read_config_file(FILE *cf) { static char buf[65536]; while (!feof(cf)) { if (fgets(buf, sizeof buf, cf) == 0) continue; char *eol = strstr(buf, "\n"); if (eol) *eol = 0; if (strstr(buf, "user_alias") == buf) { char *s = strchr(buf, '='); if (s) { s++; while (*s && isspace(*s)) s++; if (*s) { free(user_alias); user_alias = strdup(s); } } } else if (strstr(buf, "fairy_file") == buf) { char *s = strchr(buf, '='); if (s) { s++; while (*s && isspace(*s)) s++; if (*s) { free(fairy_file); fairy_file = strdup(s); } } } else if (strstr(buf, "eval_file") == buf) { char *s = strchr(buf, '='); if (s) { s++; while (*s && isspace(*s)) s++; if (*s) { free(eval_file); eval_file = strdup(s); } } } else if (strstr(buf, "option_mate_search") == buf) { char *s = strchr(buf, '='); if (s) { int i = 1; s++; sscanf(s, "%d", &i); if (i < MATE_SEARCH_DISABLED) i = MATE_SEARCH_DISABLED; if (i > MATE_SEARCH_ENABLED) i = MATE_SEARCH_ENABLED; option_ms = i; } } else if (strstr(buf, "skill_level") == buf) { char *s = strchr(buf, '='); if (s) { int i = 1; s++; sscanf(s, "%d", &i); if (i < LEVEL_RANDOM) i = LEVEL_RANDOM; if (i >= LEVEL_NUM_LEVELS) i = LEVEL_NORMAL; skill_level = (level_t)i; } } else if (strstr(buf, "draw_count") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); draw_count = i; } } else if (strstr(buf, "resign_count") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); resign_count = i; } } else if (strstr(buf, "random_ply_count") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); random_ply_count = i; } } else if (strstr(buf, "random_amplitude") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); random_amplitude = i; } } else if (strstr(buf, "multipv") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); multipv = abs(i); if (multipv < 1) multipv = 1; } } else if (strstr(buf, "draw_threshold") == buf) { char *s = strchr(buf, '='); if (s) { int i = 0; s++; sscanf(s, "%d", &i); draw_threshold = abs(i); } } else if (strstr(buf, "resign_threshold") == buf) { char *s = strchr(buf, '='); if (s) { int i = LEGALWIN; s++; sscanf(s, "%d", &i); resign_threshold = -abs(i); } } else { for (int n = 0; boolean_settings[n].name; n++) { if (strstr(buf, boolean_settings[n].name) == buf) { char *s = strchr(buf, '='); if (s) { int i = boolean_settings[n].def; s++; sscanf(s, "%d", &i); *boolean_settings[n].opt = i != 0; } } } } } } static void write_config_file(void) { FILE *cf = fopen(configfile, "w"); if (!cf) return; if (user_alias && strlen(user_alias)) fprintf(cf, "user_alias = %s\n", user_alias); if (fairy_file && strlen(fairy_file)) fprintf(cf, "fairy_file = %s\n", fairy_file); if (eval_file && strlen(eval_file) && remember_eval_file) fprintf(cf, "eval_file = %s\n", eval_file); if (option_ms != MATE_SEARCH_ENABLE_DROP) fprintf(cf, "option_mate_search = %d\n", option_ms); if (skill_level != LEVEL_NORMAL) fprintf(cf, "skill_level = %d\n", skill_level); for (int n = 0; boolean_settings[n].name; n++) { if (*boolean_settings[n].opt != boolean_settings[n].def) fprintf(cf, "%s = %d\n", boolean_settings[n].name, *boolean_settings[n].opt); } fprintf(cf, "multipv = %d\n", multipv); fprintf(cf, "draw_count = %d\n", draw_count); fprintf(cf, "draw_threshold = %d\n", draw_threshold); fprintf(cf, "resign_count = %d\n", resign_count); fprintf(cf, "resign_threshold = %d\n", resign_threshold); fprintf(cf, "random_ply_count = %d\n", random_ply_count); fprintf(cf, "random_amplitude = %d\n", random_amplitude); fclose(cf); } #ifdef HAVE_READLINE /* Generator function for command completion. STATE lets us know whether * to start from scratch; without any state (i.e. STATE == 0), then we * start at the top of the list. */ char *command_generator (const char *text, int state) { static int list_index, len, ind; const char *name = NULL; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; ind = 0; len = strlen (text); } /* Return the next name which partially matches from the command list. */ while ((name = help_topic[list_index].topic)) { list_index++; if (strncmp (name, text, len) == 0) return strdup(name); } while ((name = xcmd_list[ind])) { ind++; if (strncmp (name, text, len) == 0) return strdup(name); } /* If no names matched, then return NULL. */ return (char *)NULL; } /* Generator function for command completion. STATE lets us know whether * to start from scratch; without any state (i.e. STATE == 0), then we * start at the top of the list. */ char *variant_generator (const char *text, int state) { static int list_index, len; const char *name = NULL; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next name which partially matches from the command list. */ while (list_index < num_standard_variants && (name = standard_variants[list_index].name)) { list_index++; if (strncmp (name, text, len) == 0) return strdup(name); } if (list_index >= num_standard_variants) { int n_alias = sizeof aliases / sizeof *aliases; int index = list_index - num_standard_variants; while (index < n_alias && (name = aliases[index].alias)) { list_index++; index++; if (strncmp (name, text, len) == 0) return strdup(name); } if (index >= n_alias) { index -= n_alias; while (indexload_eval_parameters(inf); fclose(inf); } } } int main(int argc, char **argv) { bool input_interrupt_search = true; bool xboard_mode = false; bool uci_mode = false; bool uci_kxr = true; char uci_dialect = 'c'; int uci_timeunit = 1; side_t my_colour = BLACK; game_t *game = NULL; char input[65536]; char prompt_str[256]; int depth = MAX_SEARCH_DEPTH; size_t hash_size = HASH_TABLE_SIZE; char *pgbook_file = NULL; int time_per_move = 0; int nps = -1; get_user_config_file(configfile, sizeof configfile, "sjaakii"); #if defined SMP && defined _WIN32 pthread_win32_process_attach_np(); pthread_win32_thread_attach_np(); #endif snprintf(normal_alias, sizeof(normal_alias), "chess"); bool load_variant_file = true; for (int n = 1; n= argc) { fprintf(stderr, "error: no book specified\n"); exit(0); } n++; pgbook_file = strdup(argv[n]); printf("Using opening book %s\n", pgbook_file); //pgbook_file = strdup("bigbook.bin"); } else if (strstr(argv[n], "-eval")) { if (n+1 >= argc) { fprintf(stderr, "error: no evaluation parameter file specified\n"); exit(0); } n++; eval_file = strdup(argv[n]); printf("Using eval parameters from %s\n", eval_file); } else if (strstr(argv[n], "-log") || strstr(argv[n], "-newlog")) { const char *logfile = "sjaak.log"; if (n+1 < argc && argv[n+1][0] != '-') { logfile = argv[n+1]; n++; } const char *mode = "a"; if (strstr(argv[n], "-newlog")) mode = "w"; if (!f) f = fopen(logfile, mode); } else if (strstr(argv[n], "-xboard")) { xboard_mode = true; uci_mode = false; } else if (strstr(argv[n], "-uci")) { xboard_mode = false; uci_mode = true; uci_dialect = 'c'; free((void *)variant_name); variant_name = strdup("chess"); } else if (strstr(argv[n], "-usi")) { xboard_mode = false; uci_mode = true; uci_dialect = 's'; free((void *)variant_name); variant_name = strdup("shogi"); } else if (strstr(argv[n], "-ucci")) { xboard_mode = false; uci_mode = true; uci_dialect = 'C'; free((void *)variant_name); variant_name = strdup("xiangqi"); } else if (strstr(argv[n], "-no_user_variants")) { load_variant_file = false; } else if (strstr(argv[n], "-variant")) { if (n+1 < argc) { variant_name = strdup(argv[n+1]); n++; } } else if (strstr(argv[n], "-normal")) { if (n+1 < argc) { snprintf(normal_alias, sizeof(normal_alias), "%s", argv[n+1]); n++; } } else if (strstr(argv[n], "-") == argv[n]) { fprintf(stderr, "Unknown option: %s\n", argv[n]); exit(0); } else { scan_variant_file(argv[n]); } } #ifdef DATADIR if (load_variant_file) scan_variant_file(DATADIR"/variants.txt"); #endif buf = (char *)malloc(65536); printf("%s version %s\n", PROGNAME, VERSIONSTR " " ARCHSTR); printf("Type 'help' for a list of commands and help topics\n"); initialise_hash_keys(); #ifdef SMP int ncore = get_number_of_cores(); printf("Machine has %d core%s\n", ncore, (ncore > 1)? "s" : ""); atexit(kill_threads); printf("Locks are implemented as %s\n", LOCK_DESCRIPTION); #endif #ifdef HAVE_READLINE if (stdin_is_terminal()) { rl_initialize(); //rl_read_init_file(readline_path); using_history(); stifle_history(256); //read_history(history_path); //atexit(write_history_file_on_exit); rl_attempted_completion_function = complete_sjaakii_command; } #endif /* Turn off buffering for stdout and stdin */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); if (xboard_mode) { free((void *)variant_name); variant_name = NULL; } snprintf(fairy_alias, sizeof fairy_alias, "chess"); if (!variant_name) variant_name = strdup("chess"); /* Read persistent settings */ FILE *cf = fopen(configfile, "r"); if (cf) { if (f) fprintf(f, "Reading config file '%s'\n", configfile); read_config_file(cf); fclose(cf); } else { if (f) fprintf(f, "Cannot read config file '%s'\n", configfile); } atexit(write_config_file); /* Load variant description file */ if (fairy_file) scan_variant_file(fairy_file); game = create_variant_game(variant_name); if (game == NULL) { printf("Failed to start variant '%s', defaulting to 'normal'\n", variant_name); free((void *)variant_name); variant_name = strdup("normal"); game = create_variant_game(variant_name); } game->start_new_game(); load_evaluation_parameters(game, eval_file); game->set_default_output_function(log_xboard_output); game->set_xboard_output_function(NULL); game->set_uci_output_function(NULL); if (xboard_mode) { uci_mode = false; prompt = false; show_board = false; san = false; trapint = false; if (game && xboard_mode) rank_offset = (game->ranks != 10); relabel_chess_square_names(); game->set_xboard_output_function(log_xboard_output); game->set_uci_output_function(NULL); game->set_default_output_function(NULL); log_xboard_output("\n"); } if (uci_mode) { prompt = false; show_board = false; san = false; trapint = false; in_play = false; rank_offset = 1; uci_timeunit = 1; if (uci_dialect == 'C') uci_timeunit = 1000; if (game) delete game; game = create_variant_game(variant_name); game->set_transposition_table_size(hash_size); game->start_new_game(); load_evaluation_parameters(game, eval_file); if (uci_dialect == 's') relabel_shogi_square_names(); game->set_xboard_output_function(NULL); game->set_default_output_function(NULL); game->set_uci_output_function(log_xboard_output); } while (true) { input[0] = '\0'; prompt_str[0] = '\0'; if (show_board && game) game->print_board(); if (prompt) { int n = 0; n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "#"); if (game) { n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "[%s] ", game->get_name()); n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "%d", (int)game->get_moves_played()); n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "%s", (game->get_side_to_move() == WHITE)?"w":"b"); if (!in_play) n += snprintf(prompt_str+n, (sizeof prompt_str) - n, " (f)"); if (game->player_in_check(game->get_side_to_move())) n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "+"); } else { n += snprintf(prompt_str+n, (sizeof prompt_str) - n, "[-]"); } n += snprintf(prompt_str+n, (sizeof prompt_str) - n, ">"); #ifdef HAVE_READLINE if (!stdin_is_terminal()) #endif printf("%s", prompt_str); } //if (!(may_ponder && game && game->ponder_move)|| keyboard_input_waiting()) { if (deferred[0]) { snprintf(input, sizeof input, "%s", deferred); deferred[0] = 0; } else if (keyboard_input_waiting() || (!may_ponder && !(game && game->analysing))) { #ifdef HAVE_READLINE if (stdin_is_terminal()) { char *readline_input = readline(prompt_str); if (readline_input && strlen(readline_input)) add_history(readline_input); snprintf(input, sizeof input, "%s", readline_input); free(readline_input); } else #endif if (!fgets(input, sizeof input, stdin)) break; } chomp(input); trim(input); /* Strip trailing whitespace */ char *e = input + strlen(input)-1; while (e > input && isspace(*e)) { *(e--) = '\0'; } #ifdef HAVE_READLINE if (!stdin_is_terminal()) { char *s = strchr(input, '#'); if (s) *s = '\0'; } #endif if (f) { fprintf(f, "< %s\n", input); fflush(f); } if (streq(input, "xboard") || streq(input, "xboard on")) { uci_mode = false; xboard_mode = true; prompt = false; show_board = false; san = false; trapint = false; if (game && xboard_mode) rank_offset = (game->ranks != 10); relabel_chess_square_names(); game->set_xboard_output_function(log_xboard_output); game->set_uci_output_function(NULL); game->set_default_output_function(NULL); log_xboard_output("\n"); } else if (streq(input, "xboard off")) { xboard_mode = false; prompt = true; show_board = true; san = true; trapint = true; rank_offset = 1; relabel_chess_square_names(); log_xboard_output("\n"); game->set_xboard_output_function(NULL); game->set_default_output_function(log_xboard_output); } else if (streq(input, "interrupt off")) { input_interrupt_search = false; } else if (streq(input, "interrupt on")) { input_interrupt_search = true; } else if (streq(input, "uci") || streq(input, "uci on") || streq(input, "ucci") || streq(input, "ucci on") || streq(input, "usi") || streq(input, "usi on")) { uci_mode = true; uci_dialect = input[1]; prompt = false; show_board = false; san = false; trapint = false; in_play = false; rank_offset = 1; if (input[2] == 'c') uci_dialect = 'C'; uci_timeunit = 1; if (uci_dialect == 'C') uci_timeunit = 1000; free((void *)variant_name); switch (uci_dialect) { case 's': variant_name = strdup("shogi"); break; case 'C': variant_name = strdup("xiangqi"); break; default: variant_name = strdup("chess"); } if (game) delete game; game = create_variant_game(variant_name); game->set_transposition_table_size(hash_size); game->start_new_game(); load_evaluation_parameters(game, eval_file); if (input[1] == 's') relabel_shogi_square_names(); game->set_xboard_output_function(NULL); game->set_default_output_function(NULL); game->set_uci_output_function(log_xboard_output); log_xboard_output("\n"); log_xboard_output("id name %s %s %s\n", PROGNAME, VERSIONSTR, ARCHSTR); log_xboard_output("id author Evert Glebbeek\n"); //log_xboard_output("option name OwnBook type check default true\n"); /* Hash size: 1MB - 1 GB */ const char *option_name = " name"; if (uci_dialect == 'C') option_name = ""; log_xboard_output("option%s Hash type spin default 48 min 1 max 4096\n", option_name); #ifdef SMP log_xboard_output("option%s Cores type spin default 1 min 1 max %d\n", option_name, MAX_THREADS); #endif log_xboard_output("option%s Ponder type check default true\n", option_name); log_xboard_output("option%s UCI_Variant type combo default %s", option_name, variant_name); log_xboard_output(" var %s var chess960", standard_variants[0].name); for (int n = 1; nset_default_output_function(log_xboard_output); game->set_uci_output_function(NULL); relabel_chess_square_names(); } else if (streq(input, "isready") && uci_mode) { log_xboard_output("readyok\n"); } else if ((strstr(input, "setoption usemillisec") == input) && uci_mode) { uci_timeunit = 1000; if (strstr(input, "true")) uci_timeunit = 1; } else if ((strstr(input, "setoption name Hash") == input || strstr(input, "setoption Hash") == input) && uci_mode) { /* TODO: set the size of the hash table */ unsigned long long memory_size = hash_size * sizeof(hash_table_entry_t); char *s = strstr(input, "value"); if (s) { sscanf(s+6, "%llu", &memory_size); /* Convert to bytes */ memory_size <<= 20; /* Reserve default */ if (memory_size > 10<<20) memory_size -= 5<<20; } else { sscanf(input+13, "%llu", &memory_size); /* Convert to bytes */ memory_size <<= 20; /* Reserve default */ if (memory_size > 10<<20) memory_size -= 5<<20; } hash_size = size_t(memory_size / sizeof(hash_table_entry_t)); game->set_transposition_table_size(hash_size); } else if ((strstr(input, "setoption name UCI_Variant") == input || strstr(input, "setoption UCI_Variant") == input) && uci_mode) { char *s = strstr(input, "value"); if (s) { s += 6; while (*s && isspace(*s)) s++; if (s[0]) { /* Trim trailing spaces */ char *p = s + strlen(s)-1; while (*p == ' ') {*p = 0; p--; } free((void *)variant_name); variant_name = strdup(s); if (game) delete game; game = create_variant_game(variant_name); game->set_transposition_table_size(hash_size); game->start_new_game(); load_evaluation_parameters(game, eval_file); if (uci_dialect == 's') relabel_shogi_square_names(); uci_kxr = false; if (streq(variant_name, "chess960")) uci_kxr = true; } } else { /* TODO: select variants in ucci mode */ } } else if (strstr(input, "setoption name UCI_Chess960") == input && uci_mode) { char *s = strstr(input, "value"); if (s) { s += 6; uci_kxr = false; if (strstr(s, "true") == s) uci_kxr = true; } } else if (strstr(input, "setoption name Ponder") == input && uci_mode) { #ifdef SMP } else if (strstr(input, "setoption name Cores") == input && uci_mode) { int threads = 0; char *s = strstr(input, "value"); if (s) { s += 6; sscanf(s, "%d", &threads); kill_threads(); if (threads > 0) init_threads(threads); if (show_board) printf("Started %d threads\n", get_number_of_threads()); } #endif } else if ((streq(input, "ucinewgame") || streq(input, "uccinewgame") || streq(input, "usinewgame")) && uci_mode) { if (game) delete game; game = create_variant_game(variant_name); game->set_transposition_table_size(hash_size); game->start_new_game(); load_evaluation_parameters(game, eval_file); if (uci_dialect == 's') relabel_shogi_square_names(); num_games++; if (f) fprintf(f, "# game %d\n", num_games); } else if (strstr(input, "banmoves") && uci_mode) { } else if (strstr(input, "position") && uci_mode) { char *p = strstr(input, " "); while (p && *p && p[0]==' ') p++; assert(game); if (strstr(p, "startpos") == p) { /* Game from start position */ /* We need to return to the first node in the game struct and * then replay all the moves from the beginning to the current * position. */ p = strstr(p, "moves"); if (p && *p) p+=6; replay_game(game, p); } else if (strstr(p, "fen") == p) { /* Game from FEN string */ p+=4; game->setup_fen_position(p); p = strstr(p, "moves"); if (p && *p) p+=6; replay_game(game, p); } else if (strstr(p, "sfen") == p) { /* Game from sFEN string */ /* Translate SFEN to FEN */ char fen[256] = { 0 }; int n = 0; p += 5; /* Skip space */ while (*p && isspace(*p)) p++; /* Copy position verbatim */ while (*p && !isspace(*p)) { fen[n++] = *p; p++; } while (*p && isspace(*p)) p++; char side = *p; while (*p && !isspace(*p)) p++; while (*p && isspace(*p)) p++; /* Flip white and black in side to move */ side = (side == 'w') ? 'b' : 'w'; /* Copy pieces in holdings */ n += snprintf(fen+n, (sizeof fen) - n, " ["); if (*p != '-') { while (*p && !isspace(*p)) { int c = 1; if (isdigit(*p)) { sscanf(p, "%d", &c); p++; } while (c>0) { fen[n++] = *p; c--; } p++; } } n += snprintf(fen+n, (sizeof fen) - n, "] %c", side); int ply = 1; sscanf(p, "%d", &ply); n += snprintf(fen+n, (sizeof fen) - n, " 0 %d", (ply+1)/2); game->setup_fen_position(fen); p = strstr(p, "moves"); if (p && *p) p+=6; replay_game(game, p); } } else if (streq(input, "stop") && uci_mode) { } else if (strstr(input, "go") == input && uci_mode) { const char *p = ""; int depth = MAX_SEARCH_DEPTH; char *s; /* Set defaults */ set_time_per_move(&game->clock, 5000); game->clock.movestogo = 0; game->clock.time_inc = 0; /* parse options */ if ((s = strstr(input, "movetime"))) { sscanf(s+9, "%d", &time_per_move); set_time_per_move(&game->clock, time_per_move * uci_timeunit); } if ((s = strstr(input, " movestogo"))) { sscanf(s+10, "%d", &game->clock.movestogo); } if ((s = strstr(input, "infinite"))) { set_infinite_time(&game->clock); } if ((s = strstr(input, " time"))) { int time_left; sscanf(s+5, "%d", &time_left); game->clock.time_left = time_left * uci_timeunit; set_time_for_game(&game->clock); } if ((s = strstr(input, "wtime"))) { int time_left; sscanf(s+6, "%d", &time_left); if (my_colour == WHITE || (uci_dialect == 's' && my_colour == BLACK)) { game->clock.time_left = time_left * uci_timeunit; set_time_for_game(&game->clock); } } if ((s = strstr(input, "btime"))) { int time_left; sscanf(s+6, "%d", &time_left); if (my_colour == BLACK || (uci_dialect == 's' && my_colour == WHITE)) { game->clock.time_left = time_left * uci_timeunit; set_time_for_game(&game->clock); } } if ((s = strstr(input, " increment"))) { int inc; sscanf(s+5, "%d", &inc); game->clock.time_inc = inc * uci_timeunit; } if ((s = strstr(input, "winc"))) { int inc; sscanf(s+5, "%d", &inc); if (my_colour == WHITE || (uci_dialect == 's' && my_colour == BLACK)) { game->clock.time_inc = inc * uci_timeunit; } } if ((s = strstr(input, "binc"))) { int inc; sscanf(s+5, "%d", &inc); if (my_colour == BLACK || (uci_dialect == 's' && my_colour == WHITE)) { game->clock.time_inc = inc * uci_timeunit; } } if ((s = strstr(input, "mate"))) { /* Just do a normal N-ply search instead */ if (uci_dialect == 's') { /* Tsume search, not implemented */ log_xboard_output("checkmate notimplemented\n"); continue; } else { sscanf(s+5, "%d", &depth); set_infinite_time(&game->clock); } } if ((s = strstr(input, "depth"))) { sscanf(s+6, "%d", &depth); set_infinite_time(&game->clock); } if ((s = strstr(input, "byoyomi"))) { /* Traditional Shogi time-control (USI) */ int bt = 0; sscanf(s+7, "%d", &bt); bt *= uci_timeunit; if (bt > game->clock.time_left) { game->clock.time_inc = bt; set_time_for_game(&game->clock); } else { set_time_per_move(&game->clock, bt); game->clock.movestogo = 1; } } uci_clock_handler = game->clock.check_clock; game->check_keyboard = NULL; game->pondering = false; game->ponder_move = 0; if ((s = strstr(input, "ponder"))) { game->check_keyboard = uci_keyboard_input_on_ponder; game->ponder_move = game->move_list[game->moves_played-1]; game->pondering = true; game->clock.check_clock = NULL; } if (game->think(depth) == SEARCH_OK) { move_t move = game->get_last_move(); log_xboard_output("bestmove %s", move_to_lan_string(move, false, uci_kxr)); if (game->ponder_move) log_xboard_output(" ponder %s", move_to_lan_string(game->ponder_move, false, uci_kxr)); log_xboard_output("\n"); } game->clock.check_clock = uci_clock_handler; game->pondering = false; } else if (strstr(input, "ponderhit") && uci_mode) { /* Nothing special yet... */ } else if (strstr(input, "gameover") && uci_mode) { } else if (strstr(input, "help") == input) { print_help(input+4); } else if (strstr(input, "#") == input) { } else if (strstr(input, "echo ") == input) { log_xboard_output("%s", input+5); } else if (strstr(input, "protover") == input) { send_options_to_xboard(); log_xboard_output("feature done=1\n"); } else if (strstr(input, "accepted") == input) { } else if (strstr(input, "rejected") == input) { } else if (streq(input, "variant") || streq(input, "variants")) { printf("Known variants:\n"); size_t max_len = 0; for (int n = 0; n max_len) max_len = l; } for (int n = 0; n max_len) max_len = l; } for (int n = 0; n0; c--) printf(" "); printf("(%dx%d", standard_variants[n].files, standard_variants[n].ranks); if (standard_variants[n].holdings) printf("+%d", standard_variants[n].holdings); printf(")\n"); } for (int n = 0; n0; c--) printf(" "); printf("(%dx%d", custom_variants[n].files, custom_variants[n].ranks); printf(")\t[%s]\n", custom_variants[n].filename); } } else if (strstr(input, "load") == input) { char *s = input+5; char *p; while (isspace(*s)) s++; p = s + strlen(s)-1; while (p > s && isspace(*p)) { *p = '\0'; p--; } invalidate_variant_file(s); scan_variant_file(s); for (int n = 0; n s && isspace(*p)) { *p = '\0'; p--; } invalidate_variant_file(s); } else if (strstr(input, "variant ") == input || streq(input, "new")) { bool override_fairy = false; if (strstr(input, "variant")) { char *s = input + 7; while (*s && isspace(*s)) s++; if (s[0]) { /* Trim trailing spaces */ char *p = s + strlen(s)-1; while (*p == ' ') {*p = 0; p--; } free((void *)variant_name); variant_name = NULL; if (user_alias && strstr(user_alias, s) == user_alias) { char *p = strstr(user_alias, "="); if (p) { p++; variant_name = trim(strdup(p)); override_fairy = true; } } if (!variant_name) variant_name = strdup(s); /* Remove size-override for variant fairy */ s = strstr((char *)variant_name, "fairy"); if (s && s != variant_name && s[-1] == '_') { snprintf((char *)variant_name, strlen(variant_name), "fairy"); } } } else { /* "new" should switch to variant "normal" in XBoard mode. */ if (xboard_mode) { free((void *)variant_name); variant_name = strdup("normal"); } } /* Backup randomisation settings */ bool rok = game->random_ok; unsigned int rk = game->random_key; eval_t ra = game->random_amplitude; size_t rpc = game->random_ply_count; if (game) delete game; game = create_variant_game(variant_name); if (!game) { log_xboard_output("Error (cannot start variant game): '%s'\n", variant_name); } else { if (xboard_mode) log_xboard_output("# New game '%s'\n", game->get_name()); //game->book = open_opening_book(pgbook_file); game->set_transposition_table_size(hash_size); game->start_new_game(); if (xboard_mode && (game->xb_setup || streq(variant_name, "fairy") || override_fairy)) { const char *fen = game->start_fen; if (mask_dark_squares) fen = filter_dark_squares_fen(game->start_fen); if ((game->xb_setup == NULL || game->xb_setup[0] == 0) && (streq(variant_name, "fairy") || override_fairy)) { const char *parent = find_real_variant_name(variant_name); if (streq(parent, "chess")) parent = "fairy"; log_xboard_output("setup () %dx%d+%d_%s %s\n", game->files, game->ranks, game->holdsize, parent, fen); } else { if (game->xb_setup && game->xb_setup[strlen(game->xb_setup)-1] == ')') { const char *parent_name = "fairy"; if (game->xb_parent && game->xb_parent[0]) parent_name = game->xb_parent; log_xboard_output("setup %s %dx%d+%d_%s %s\n", game->xb_setup ? game->xb_setup : "()", game->files, game->ranks, game->holdsize, parent_name, fen); } else log_xboard_output("setup %s %s\n", game->xb_setup ? game->xb_setup : "()", fen); } if (send_piece_descriptions) game->write_piece_descriptions(true); } /* Restore random settings */ game->random_ok = rok; game->random_key = rk; game->random_amplitude = ra; game->random_ply_count = rpc; if (strstr(input, "new")) { my_colour = BLACK; depth = MAX_SEARCH_DEPTH; num_games++; in_play = true; if (f) fprintf(f, "# game %d\n", num_games); game->random_ok = false; } } if (game && xboard_mode) rank_offset = (game->ranks != 10); relabel_chess_square_names(); load_evaluation_parameters(game, eval_file); } else if (strstr(input, "option") == input) { #if 0 if (strstr(input+7, "Opening book (polyglot)")) { char *s = strstr(input, "="); char *eol = input + strlen(input)-1; if (s) s++; /* Strip leading and trailing spaces */ while (*s && isspace(*s)) s++; while (isspace(*eol)) { *eol='\0'; eol--; } log_xboard_output("# %s\n", s); free(pgbook_file); pgbook_file = strdup(s); if (game) { close_opening_book(game->book); game->book = open_opening_book(pgbook_file); } } #endif #if 0 } else if (strstr(input, "book") == input) { char *s = input+4; char *eol = input + strlen(input)-1; if (s) s++; /* Strip leading and trailing spaces */ while (*s && isspace(*s)) s++; while (isspace(*eol)) { *eol='\0'; eol--; } if (streq(s, "off")) { free(pgbook_file); pgbook_file = NULL; if (game) { close_opening_book(game->book); game->book = NULL; } } else { free(pgbook_file); pgbook_file = strdup(s); if (game) { close_opening_book(game->book); game->book = open_opening_book(pgbook_file); } } #endif if (strstr(input+7, "MultiPV")) { char *s = strstr(input, "="); if (s) { int i = multipv; sscanf(s+1, "%d", &i); multipv = i; } } if (strstr(input+7, "Draw offer threshold")) { char *s = strstr(input, "="); if (s) { int i = draw_threshold; sscanf(s+1, "%d", &i); draw_threshold = i; } } if (strstr(input+7, "Moves before draw offer (0 to disable)")) { char *s = strstr(input, "="); if (s) { int i = draw_count; sscanf(s+1, "%d", &i); draw_count = i; } } if (strstr(input+7, "Resign threshold")) { char *s = strstr(input, "="); if (s) { int i = resign_threshold; sscanf(s+1, "%d", &i); resign_threshold = -abs(i); } } if (strstr(input+7, "Moves before resigning (0 to disable)")) { char *s = strstr(input, "="); if (s) { int i = resign_count; sscanf(s+1, "%d", &i); resign_count = i; } } if (strstr(input+7, "Randomise opening moves")) { char *s = strstr(input, "="); if (s) { int i = random_ply_count; sscanf(s+1, "%d", &i); random_ply_count = i; } } if (strstr(input+7, "Random amplitude (0 to disable)")) { char *s = strstr(input, "="); if (s) { int i = random_amplitude; sscanf(s+1, "%d", &i); random_amplitude = i; } } if (strstr(input+7, "Mate search")) { char *s = strstr(input, "="); if (s) { s++; if (strstr(s, "Disabled") == s) { option_ms = MATE_SEARCH_DISABLED; } else if (strstr(s, "Enabled for drop games") == s) { option_ms = MATE_SEARCH_ENABLE_DROP; } else if (strstr(s, "Enabled") == s) { option_ms = MATE_SEARCH_ENABLED; } } } if (strstr(input+7, "Level")) { char *s = strstr(input, "="); if (s) { s++; skill_level = LEVEL_NORMAL; for (int n=0; n> 1); nelem |= (nelem >> 2); nelem |= (nelem >> 4); nelem |= (nelem >> 8); nelem |= (nelem >> 16); nelem |= (nelem >> 32*(sizeof(size_t)>4)); nelem >>= 1; nelem++; hash_size = nelem; game->set_transposition_table_size(hash_size); } else if (strstr(input, "analyze") || strstr(input, "analyse")) { if (game) { in_play = false; //game->set_xboard_output_function(log_xboard_output); game->analysing = true; } } else if (strstr(input, "multipv")) { sscanf(input+7, "%d", &multipv); if (multipv < 1) multipv = 1; } else if (strstr(input, "force setboard")) { char *p = strstr(input+7, " "); while (p && *p && p[0]==' ') p++; if (game) game->setup_fen_position(p); in_play = false; } else if (strstr(input, "force") == input) { game->draw_count = 0; game->resign_count = 0; in_play = false; } else if (strstr(input, "undo") == input) { if (game->draw_count) game->draw_count--; if (game->resign_count) game->resign_count--; game->takeback(); game->clock.time_left = game->move_clock[game->moves_played]; } else if (strstr(input, "rules") == input) { if (game) game->print_rules(); } else if (strstr(input, "wikirules") == input) { if (game) game->print_wiki_rules(); } else if (strstr(input, "pieceinfo") == input) { game->print_pieces(); } else if (strstr(input, "remove") || strstr(input, "takeback")) { if (game->draw_count) game->draw_count--; if (game->resign_count) game->resign_count--; game->takeback(); game->clock.time_left = game->move_clock[game->moves_played]; game->takeback(); game->clock.time_left = game->move_clock[game->moves_played]; } else if (strstr(input, "setboard") == input) { char *p = strstr(input, " "); while (p && *p && p[0]==' ') p++; if (game) game->setup_fen_position(p); if (!xboard_mode) in_play = false; } else if (strstr(input, "perft") == input) { if (game) { int depth = 6; int root = 0; char *s = input + 5; while (*s && isspace(*s)) s++; if (*s) { sscanf(s, "%d", &depth); while(*s && isdigit(*s)) s++; } while (*s && isspace(*s)) s++; if (*s) { sscanf(s, "%d", &root); } #ifdef __unix__ if (trapint) old_signal_handler = signal(SIGINT, interrupt_computer); #endif abort_search = false; uint64_t t = get_timer(); for (int n = 1; nstart_new_game(); load_evaluation_parameters(game, eval_file); game->setup_fen_position("8/k7/3p4/p2P1p2/P2P1P2/8/8/K7 w - - bm Kb1"); play_state_t status = game->think(depth); delete game; } else if (strstr(input, "test wac") == input) { run_test_suite(1000, wac_test); } else if (strstr(input, "test sts") == input) { run_test_suite(1000, sts_test); } else if (strstr(input, "test static_qs") == input) { printf("%d\n", game->static_qsearch(LEGALWIN)); } else if (strstr(input, "test see") == input) { move_t move = game->move_string_to_move(input+8); printf("%d\n", game->see(move)); } else if (strstr(input, "test smp") == input) { #ifdef SMP int cores = ncore/2; int depth = 12; char *s = input + 8; while (*s && isspace(*s)) s++; if (*s == '-') { if (*s) sscanf(s, "- %d", &depth); } else { if (*s) sscanf(s, "%d %d", &cores, &depth); } test_smp(cores, depth); #endif } else if (strstr(input, "test chase") == input) { chase_state_t state = game->test_chase(); switch (state) { case NO_CHASE: printf("No chase\n"); break; case DRAW_CHASE: printf("Legal chase (draw)\n"); break; case LOSE_CHASE: printf("Illegal chase (lose)\n"); break; case WIN_CHASE: printf("Illegal chase (win)\n"); break; } } else if (strstr(input, "test") == input) { printf("Unknown test: %s\n", input+4); } else if (strstr(input, "sd inf")) { depth = MAX_SEARCH_DEPTH; } else if (strstr(input, "sd ") == input) { sscanf(input, "sd %d", &depth); } else if (strstr(input, "threads") || strstr(input, "cores")) { #ifdef SMP int threads = 0; char *s = input; while (*s && isalpha(*s)) s++; sscanf(s, "%d", &threads); kill_threads(); if (threads > 0) init_threads(threads); if (show_board) printf("Started %d threads\n", get_number_of_threads()); #endif } else if (streq(input, "playother")) { if (game) { my_colour = next_side[game->get_side_to_move()]; in_play = true; /* Correctly set the number of moves to go */ if (game->clock.movestotc) { size_t moves_played = game->get_moves_played() / 2; if (my_colour == WHITE) moves_played += (game->get_moves_played() % 2); moves_played %= game->clock.movestotc; game->clock.movestogo = game->clock.movestotc - (int)moves_played - game->start_move_count/2; } } } else if (streq(input, "go")) { if (game) { my_colour = game->get_side_to_move(); in_play = true; /* Correctly set the number of moves to go */ if (game->clock.movestotc) { size_t moves_played = game->get_moves_played() / 2; if (my_colour == WHITE) moves_played += (game->get_moves_played() % 2); moves_played %= game->clock.movestotc; game->clock.movestogo = game->clock.movestotc - (int)moves_played - game->start_move_count/2; } } } else if (strstr(input, "st inf") == input) { if (game) set_infinite_time(&game->clock); } else if (strstr(input, "st ") == input) { float tpm = 5; sscanf(input+3, "%g", &tpm); time_per_move = int(tpm * 1000); set_time_per_move(&game->clock, time_per_move); if (nps > 0) game->clock.max_nodes = nps * time_per_move; } else if (strstr(input, "computer") == input) { } else if (strstr(input, "name") == input) { } else if (strstr(input, "rating") == input) { } else if (strstr(input, "random") == input) { sgenrand((unsigned int)time(NULL)); if (strstr(input, "on")) game->random_ok = true; else if (strstr(input, "off")) game->random_ok = false; else game->random_ok = !game->random_ok; game->random_key = genrandui(); game->random_amplitude = random_amplitude; game->random_ply_count = random_ply_count; } else if (strstr(input, "easy") || streq(input, "ponder off")) { may_ponder = false; } else if (strstr(input, "hard") || streq(input, "ponder on")) { may_ponder = true; } else if (strstr(input, "nopost") == input || strstr(input, "post off") == input) { game->set_xboard_output_function(NULL); } else if (strstr(input, "post") == input) { game->set_xboard_output_function(log_xboard_output); } else if (strstr(input, "trace on")) { game->trace = true; } else if (strstr(input, "trace off")) { game->trace = false; } else if (strstr(input, "?") == input) { } else if (strstr(input, "hint") == input) { if (game && game->ponder_move) log_xboard_output("Hint: %s\n", move_to_lan_string(game->ponder_move)); } else if (strstr(input, "otim") == input) { } else if (strstr(input, "ping") == input) { log_xboard_output("pong %s\n", input+5); } else if (strstr(input, "draw") == input) { /* Process draw offer */ if (game && game->draw_count > draw_count) log_xboard_output("offer draw\n"); } else if (strstr(input, "result") == input) { } else if (strstr(input, "time") == input) { set_time_from_string(game, input+5); } else if (strstr(input, "nps")) { /* This is a convoluted mess... */ sscanf(input+4, "%d", &nps); /* If we have a fixed time per move, then we can set a maximum number of nodes. * Otherwise... things get complicated. */ game->clock.max_nodes = 0; if (time_per_move > 0 && nps > 0) game->clock.max_nodes = nps * time_per_move; } else if (strstr(input, "maxnodes")) { unsigned int nodes = 0; sscanf(input+9, "%u", &nodes); game->clock.max_nodes = nodes; } else if (strstr(input, "level") == input) { set_timecontrol_from_string(game, input+6); } else if (streq(input, "quit") || streq(input, "exit") || streq(input, "bye")) { exit(0); } else if (streq(input, "eval")) { if (game) { printf("Static evaluation: %d\n", game->eval()); } } else if (strstr(input, "prompt on") == input) { prompt = true; } else if (strstr(input, "prompt off") == input) { printf("\n"); prompt = false; } else if (strstr(input, "board on") == input) { show_board = true; } else if (strstr(input, "board off") == input) { show_board = false; } else if (strstr(input, "san on") == input) { san = true; } else if (strstr(input, "san off") == input) { san = false; } else if (streq(input, "pst")) { if (game) game->print_pst(); } else if (streq(input, "bitboards")) { if (game) game->print_bitboards(); } else if (streq(input, "board")) { if (game) game->print_board(); } else if (streq(input, "settings")) { printf("Xboard mode : %s\n", xboard_mode ? "on" : "off"); printf("UCI mode : %s\n", uci_mode ? "on" : "off"); printf("UCI dialect : %c\n", uci_dialect); printf("UCI timeunit : %dms\n", uci_timeunit); printf("\n"); printf("Prompt : %s\n", prompt ? "on" : "off"); printf("Board : %s\n", show_board ? "on" : "off"); printf("SAN notation : %s\n", san ? "on" : "off"); printf("Trap SIGINT : %s\n", trapint ? "on" : "off"); printf("\n"); printf("Time control : %d moves in %ds + %dms\n", tc_moves, tc_time, tc_inc); printf("Time per move : %ds\n", time_per_move); printf("Max depth : %d\n", depth); printf("Max nodes : %llu\n", (unsigned long long) (game ? game->clock.max_nodes : 0)); printf("MultiPV : %d\n", multipv); } else if (streq(input, "pieces")) { if (game) game->write_piece_descriptions(); } else if (strstr(input, "lep") == input) { const char *fname = input + strlen("lep"); while (*fname && isspace(*fname)) fname++; load_evaluation_parameters(game, fname); } else if (streq(input, "show eval parameters")) { if (game) game->print_eval_parameters(); } else if (strstr(input, "show attackers ") == input) { const char *s = input + 15; while (*s && isspace(*s)) s++; int file, rank; int n = 0; file = *s - 'a'; s++; sscanf(s, "%d %n", &rank, &n); s += n; rank -= rank_offset; /* Xboard counts ranks from 0 for large boards */ int square = game->pack_rank_file(rank, file); game->print_attacker_bitboard(square); } else if (strstr(input, "show attacks ") == input) { const char *s = input + 13; while (*s && isspace(*s)) s++; int file, rank; int n = 0; file = *s - 'a'; s++; sscanf(s, "%d %n", &rank, &n); s += n; rank -= rank_offset; /* Xboard counts ranks from 0 for large boards */ int square = game->pack_rank_file(rank, file); game->print_attack_bitboard(square); } else if (strstr(input, "san ") == input) { char *movestr = input+4; while (*movestr && isspace(*movestr)) movestr++; if (game && *movestr) { movelist_t movelist; move_t move = 0; game->generate_legal_moves(&movelist); for (int k=0; kcastle_san_ok, false), movestr) || streq(move_to_lan_string(movelist.move[k], false, false), movestr) || streq(move_to_lan_string(movelist.move[k], false, true ), movestr)) { move = movelist.move[k]; log_xboard_output("%s", move_to_short_string(move, &movelist, NULL, game->castle_san_ok)); break; } } if (move) { game->playmove(move); if (!strchr(piece_symbol_string, '+') && game->player_in_check(game->get_side_to_move())) log_xboard_output("+"); log_xboard_output("\n"); game->takeback(); } else { log_xboard_output("%s\n", movestr); } } } else if (streq(input, "fen")) { if (game) printf("%s\n", game->make_fen_string()); } else if (streq(input, "moves")) { if (game) { movelist_t movelist; game->generate_legal_moves(&movelist); int k; printf("%d moves\n", movelist.num_moves); for (k=0; kgenerate_legal_moves(&movelist); int k; printf("%d moves\n", movelist.num_moves); for (k=0; kcastle_san_ok)); printf("%-10s ", move_to_lan_string(movelist.move[k], false, false)); printf("%-10s\n", move_to_lan_string(movelist.move[k], false, true)); } } printf("\n"); } else if (streq(input, "pseudomoves")) { if (game) { movelist_t movelist; game->generate_moves(&movelist); int k; printf("%d moves\n", movelist.num_moves); for (k=0; kmoves_played; move_t *history = new move_t[nm]; for (int n=0; nmove_list[n]; while (game->moves_played) game->takeback(); for (int n=0; ngenerate_legal_moves(&movelist); if (n % 2 == 0) printf("%d. ", 1+n/2); printf("%s ", move_to_short_string(game->move_list[n], &movelist)); game->playmove(history[n]); } printf("\n"); delete[] history; } } else if (strstr(input, "lift")) { lift_sqr = -1; movelist_t movelist; game->generate_legal_moves(&movelist); for (int square = 0; square < game->ranks*game->files; square++) { if (streq(input+5, square_names[square])) { send_legal_move_targets(game, square, &movelist); lift_sqr = square; break; } } /* Send "choice" string if all moves are promotion moves with the * same set of options. */ movelist.rewind(); move_t move; uint32_t pmask = 0; movelist.rewind(); while ((move = movelist.next_move())) { if (get_move_from(move) != lift_sqr) continue; if (!is_promotion_move(move)) continue; pmask |= 1 << get_move_promotion_piece(move); } for (int to_sqr = 0; pmask && to_sqr < game->files*game->ranks; to_sqr++) { movelist.rewind(); uint32_t mask = 0; while ((move = movelist.next_move())) { if (get_move_to(move) != to_sqr) continue; if (get_move_from(move) != lift_sqr) continue; if (!is_promotion_move(move)) continue; mask |= 1 << get_move_promotion_piece(move); } if (!mask) continue; if (mask != pmask) pmask= 0; } if (pmask) { char choice_str[256]; int cs = 0; while (pmask) { int bit = game->get_most_valuable_piece_id(pmask); if (bit < 0) break; pmask ^= 1<get_piece_notation(bit)); } for (int n=0; ngenerate_legal_moves(&movelist); move_t move; while ((move = movelist.next_move())) { if (get_move_from(move) != lift_sqr) continue; for (int square = 0; square < game->ranks*game->files; square++) { if (streq(input+4, square_names[square])) { if (get_move_to(move) != square) continue; if (!is_promotion_move(move)) { int p = get_move_piece(move); keep = game->get_piece_notation(p); if (isspace(keep[0])) keep = game->get_piece_abbreviation(WHITE, p); break; } int p = get_move_promotion_piece(move); cs += snprintf(choice_str+cs, sizeof(choice_str) - cs, "%s", game->get_piece_notation(p)); break; } } } if (cs) { cs+=snprintf(choice_str+cs, sizeof(choice_str) - cs, "%s", keep); for (int n=0; nsetup_fen_position(s); game->print_board(); } } fclose(f); } else { printf("Can't open file: %s\n", input+8); } } else if (prompt && input[0] == '!') { if (system(input+1) == -1) printf("Cannot execute command %s\n", input+1); } else if (input[0]) { /* Can the input be interpreted as a move? Or is it an unknown * command? */ if (!input_move(game, input)) { log_xboard_output("Error (Illegal move or unknown command): %s\n", input); if (f) fprintf(f, "In position '%s'\n", game->make_fen_string()); } else { if (!uci_mode) report_game_status(game, game->get_game_end_state(), input); } } /* Should the computer play a move? */ if (game && in_play && !game->analysing && !uci_mode) { memset(deferred, 0, sizeof deferred); game->check_keyboard = keyboard_input_on_move; if (!input_interrupt_search) game->check_keyboard = NULL; if (game->get_side_to_move() == my_colour) { /* Check whether NPS time control is sane. */ if (nps>-1 && !time_per_move) { log_xboard_output("telluser Warning: specifying NPS without fixed time per move is not implemented\n"); game->clock.max_nodes = nps; } game->clock.pondering = false; //log_engine_output("Searching position %s\n", make_fen_string(game, NULL)); movelist_t legal_moves; if (san) game->generate_legal_moves(&legal_moves); #ifdef __unix__ if (trapint) old_signal_handler = signal(SIGINT, interrupt_computer); #endif game->move_clock[game->moves_played] = game->clock.time_left; game->show_fail_low = report_fail_low; game->show_fail_high = report_fail_high; game->repetition_claim = repetition_claim; game->resign_threshold = resign_threshold; game->draw_threshold = draw_threshold; game->multipv = 1; game->option_ms = option_ms; game->level = level_t(skill_level); play_state_t status = game->think(depth); #ifdef __unix__ if (trapint) signal(SIGINT, old_signal_handler); #endif game->clock.time_left -= peek_timer(&game->clock); game->clock.time_left += tc_inc; game->move_clock[game->moves_played] = game->clock.time_left; if (!xboard_mode && game->clock.check_clock) { int time = std::max(0, game->clock.time_left); int min = time / 60000; float sec = float((time - min * 60000) / 1000.); log_xboard_output("Time remaining %d:%.2f\n", min, sec); } if (status == SEARCH_OK) { move_t move = game->get_last_move(); /* Offer draw of resign, as appropriate */ if (draw_count>0 && game->draw_count > draw_count) { game->draw_count /= 2; /* Wait a while before offering again */ log_xboard_output("offer draw\n"); } else if (resign_count>0 && game->resign_count > resign_count) log_xboard_output("resign\n"); if (san) { log_xboard_output("move %s\n", move_to_short_string(move, &legal_moves, NULL, game->castle_san_ok)); } else { char *s = strdup(move_to_lan_string(move, castle_oo && game->castle_san_ok, false)); char *p = strstr(s, ","); if (p) { /* Multi-leg moves should be sent in two goes. */ *p = 0; log_xboard_output("move %s,\n", s); log_xboard_output("move %s\n", p+1); } else { log_xboard_output("move %s\n", s); } free(s); } if (game->clock.movestogo) { game->clock.movestogo--; if (!game->clock.movestogo) { game->clock.movestogo = game->clock.movestotc; game->clock.time_left += tc_time; } } report_game_status(game, game->get_game_end_state(), input); } else { report_game_status(game, game->get_game_end_state(), input); in_play = false; } } else if (may_ponder && game->ponder_move) { /* Ponder */ bool (*old_keyboard_handler)(struct game_t *game) = game->check_keyboard; bool (*old_clock_handler)(const struct chess_clock_t *clock) = game->clock.check_clock; game->check_keyboard = interrupt_ponder; game->clock.check_clock = NULL; game->ponder(); game->check_keyboard = old_keyboard_handler; game->clock.check_clock = old_clock_handler; } } if (game && game->analysing) { bool (*old_keyboard_handler)(struct game_t *game) = game->check_keyboard; bool (*old_clock_handler)(const struct chess_clock_t *clock) = game->clock.check_clock; game->analyse_fen = NULL; game->analyse_new = false; game->analyse_undo = 0; game->analyse_moves_played = int(game->moves_played); game->multipv = multipv; game->level = LEVEL_NORMAL; game->check_keyboard = keyboard_input_analyse; game->clock.check_clock = NULL; game->analyse(); game->check_keyboard = old_keyboard_handler; game->clock.check_clock = old_clock_handler; if (game->analyse_new) game->start_new_game(); for (int n=0; nanalyse_undo; n++) game->takeback(); if (game->analyse_fen) { game->setup_fen_position(game->analyse_fen); free((void *)game->analyse_fen); game->analyse_fen = NULL; } } } if (prompt) printf("\n"); return 0; }