├── .github └── workflows │ └── test.yml ├── .gitignore ├── .scalafmt.conf ├── LICENSE ├── README.md ├── benchmarks └── src │ └── main │ └── scala │ └── HuffmanPgnBench.scala ├── build.sbt ├── jitpack.yml ├── project ├── build.properties └── plugins.sbt └── src ├── main └── scala │ ├── BitOps.scala │ ├── clock │ ├── Encoder.scala │ ├── EndTimeEstimator.scala │ ├── LinearEstimator.scala │ └── LowBitTruncator.scala │ └── game │ ├── Bitboard.scala │ ├── Board.scala │ ├── Encoder.scala │ ├── Huffman.scala │ ├── Magic.scala │ ├── Move.scala │ ├── MoveList.scala │ ├── ZobristHash.scala │ └── model.scala └── test └── scala ├── ClockHistoryTest.scala ├── HuffmanPgnTest.scala └── PerftTest.scala /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-java@v4 11 | with: 12 | distribution: temurin 13 | java-version: 21 14 | cache: sbt 15 | - name: Setup sbt 16 | uses: sbt/setup-sbt@v1 17 | - run: sbt test 18 | - run: sbt benchmarks/compile 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bsp/ 2 | target/ 3 | project/metals.sbt 4 | project/project/ 5 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.8.4" 2 | runner.dialect = scala3 3 | 4 | align.preset = more 5 | maxColumn = 110 6 | spaces.inImportCurlyBraces = true 7 | rewrite.rules = [SortModifiers] 8 | rewrite.redundantBraces.stringInterpolation = true 9 | 10 | rewrite.scala3.convertToNewSyntax = yes 11 | rewrite.scala3.removeOptionalBraces = yes 12 | rewrite.rules = [AvoidInfix] 13 | docstrings.style = keep // don't format comment 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chess clock and move compression algorithms for lichess.org 2 | 3 | [![](https://jitpack.io/v/lichess-org/compression.svg)](https://jitpack.io/#lichess-org/compression) 4 | 5 | ## Disclaimer 6 | 7 | This library was migrated from the Java language to the Scala language. 8 | Only the language syntax changed to Scala; the design and paradigms of the Java program were kept. 9 | This is not how Scala code should be written, it is not idiomatic. 10 | 11 | ## Blog posts 12 | 13 | - [A better game clock history](https://lichess.org/blog/WOEVrjAAALNI-fWS/a-better-game-clock-history) 14 | - [Developer update: 275% improved game compression](https://lichess.org/blog/Wqa7GiAAAOIpBLoY/developer-update-275-improved-game-compression) 15 | 16 | ## Benchmarks 17 | 18 | ```bash 19 | sbt 'benchmarks/jmh:run -i 5 -wi 3 -f1 -t1 org.lichess.compression.benchmark.*' 20 | ``` 21 | 22 | ## License 23 | 24 | This library is licensed under the GNU Affero General Public License 3 or 25 | any later version at your option. See LICENSE for the full license text. 26 | -------------------------------------------------------------------------------- /benchmarks/src/main/scala/HuffmanPgnBench.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.benchmark 2 | 3 | import org.openjdk.jmh.annotations.* 4 | import org.openjdk.jmh.infra.Blackhole 5 | import java.util.concurrent.TimeUnit 6 | 7 | import org.lichess.compression.game.Encoder 8 | 9 | @State(Scope.Thread) 10 | @BenchmarkMode(Array(Mode.AverageTime)) 11 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 12 | @Measurement(iterations = 15, timeUnit = TimeUnit.SECONDS, time = 3) 13 | @Warmup(iterations = 15, timeUnit = TimeUnit.SECONDS, time = 3) 14 | @Fork(value = 3) 15 | class HuffmanPgnBench: 16 | 17 | @Benchmark 18 | def encode(blackhole: Blackhole) = fixtures foreach { pgnMoves => 19 | blackhole.consume(Encoder.encode(pgnMoves)) 20 | } 21 | 22 | @Benchmark 23 | def decode(blackhole: Blackhole) = encodedFixtures foreach { case (encoded, plies) => 24 | blackhole.consume(Encoder.decode(encoded, plies)) 25 | } 26 | 27 | val fixtures = List( 28 | "d3 d5 g3 e6 Bg2 Nf6 Nf3 Be7 O-O O-O Re1 a6 e4 c5 e5 Nfd7 d4 Nb6 dxc5 Bxc5 Nc3 N8d7 a4 Be7 a5 Nc4 b3 Ncxe5 Nxe5 Nxe5 Rxe5 Bd6 Re1 Bd7 Bf4 Bc6 Bxd6 Qxd6 Na4 Rad8 Nb6 Rfe8 Ra4 Bxa4 bxa4 Qc5 Qa1 Qxa5 Qd4 Rd6 Nc4 Qb4 Nxd6 Qxd4 Nxe8 Qd2 Rb1 Qxc2 Rxb7 Qxa4 Rb8 Kf8 Nd6+ Ke7 Nf5+ Kf6 Nh4 Qd1+ Bf1 Qd4 Kg2 a5 Rb7 a4 Rxf7+ Kxf7 Nf3 Ke7 Ne5 Kd6 Nf3 Qc4 Nd4 Qc3 Nf5+ Ke5 Ne3 Kf6 Nxd5+ exd5", 29 | "e4 e6 Nf3 c5 g3 a6 Bg2 Nc6 O-O d6 h3 Be7 Nc3 Qc7 d4 cxd4 Nxd4 Nxd4 Qxd4 Bf6 Qd1 e5 Nd5 Qc6 Nxf6+ Nxf6 Re1 O-O Bg5 Nd7 f4 exf4 Bxf4 Ne5 Bxe5 dxe5 a3 Be6 b4 Rad8 Qe2 Rd4 Rad1 Rfd8 Rxd4 Rxd4 c3 Rc4 Qc2 f6 Rd1 Qc7 a4 Rxb4 Rc1 Rc4 Bf1 Rc5 c4 Qb6 Qd2 Rxc4+ Kh1 Rxc1 Qxc1 Qc6 Qd1 Qxe4+ Bg2 Qd4 Qc1", 30 | "e4 g6 Nf3 Bg7 d4 e6 Nc3 Ne7 Be3 O-O Be2 d6 O-O b6 Qd2 Bb7 Bh6 c5 Bxg7 Kxg7 dxc5 bxc5 Rad1 d5 exd5 exd5 Rfe1 d4 Nb5 a6 Na3 Kg8 c3 Nbc6 cxd4 cxd4 Nxd4 Nxd4 Qxd4 Qxd4 Rxd4 Nc6 Rd7 Rab8 Red1 Ne5 Re7 Nc6 Rc7 Nb4 Rdd7 Be4 Bc4 Bd5 Bxd5 Nxd5 Rxd5 Rxb2 Rc2 Rfb8 f3 Rxc2 Nxc2 Rb2 Rd8+ Kg7 Rc8 Rxa2 Nb4 Ra4 Nd5 Ra1+ Kf2 a5 Kg3 a4 Ra8 a3 Nc3 Rc1 Nb5 Rb1 Nxa3 Ra1", 31 | "e4 g6 d4 Bg7 e5 e6 f4 Ne7 Nf3 d5 Nc3 O-O Be3 Nd7 Bd3 b6 Nb5 c5 Nd6 cxd4 Nxd4 Nxe5 fxe5 Bxe5 Nxc8 Rxc8 O-O Nc6 Nxc6 Rxc6 c3 Bc7 Bd4 Qd6 Qg4 Qxh2+ Kf2 e5 Rh1 Qf4+ Qxf4 exf4 Rh6 Re6 Rah1 f5 Rxh7 Bd8 Rh8+ Kf7 R1h7+ Ke8 Bb5+ Rc6 Bxc6#", 32 | "e4 c5 Nf3 Nc6 Bc4 Nf6 Nc3 e5 O-O d6 a3 Nd4 Nxd4 exd4 Nd5 Be6 Nxf6+ Qxf6 Bxe6 Qxe6 c3 d3 Qa4+ Ke7 e5 d5 Re1 c4 Qb4+ Kd7 Qxb7+ Ke8 Qxa8+ Kd7 Qxa7+ Kc8 b3 Qf5 bxc4 dxc4 Qa8+ Kc7 Rb1 Bc5 Rb7+ Kc6 Qa6+ Kd5 Rf1 Re8 Rc7 Rxe5 Qc6#", 33 | "b3 d5 Bb2 Nf6 g3 e5 Bg2 Nc6 d3 Be6 e3 Qd6 Nd2 O-O-O Ne2 g6 c4 dxc4 bxc4 Bg7 O-O Nd7 d4 exd4 exd4 Nb4 Ne4 Qe7 d5 Bg4 Bxg7 Rhg8 Bc3 Bxe2 Qxe2 f5 Bxb4 Qxb4 Rab1 Qa3 Nc3 Rde8 Qb2 Qa6 Qb5 Qa3 Qb4 Qa6 a4 Nb6 Qb5 Qxb5 axb5 Nxc4 d6 Nd2 Bxb7+ Kxb7 Rbe1", 34 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 d4 g6 Bc4 Bg7 Bg5 O-O O-O Be6 Bxe6 fxe6 Re1 Nc6 d5 e5 dxc6", 35 | "e3 e5 Nc3 Nf6 Bc4 d5 Bb3 c5 Ba4+ Bd7 Bxd7+ Qxd7 d4 exd4 exd4 cxd4 Qxd4 Qe6+ Be3 Ne4 Nf3 Nxc3 Qxc3 Nc6 Nd4 Nxd4 Qxd4 a5 a3 b5 O-O Be7 Rad1 O-O Qxd5 Qxd5 Rxd5 b4 Rd7 Rfe8 axb4 Bxb4 c3 Be7 Rfd1 a4 Ra1 h6 Rd4 Bf6 Rdxa4 Rxa4 Rxa4 g5 g3 h5 Kg2 Kg7 h4 gxh4 gxh4 Bxh4 Rxh4 Kg6 b4 Re5 Bd4 Rg5+ Kf3 f5 c4 Rg4 Rxg4+ fxg4+ Kg3 Kg5 b5 h4+ Kg2 h3+ Kh2 Kh4 Be5 Kg5 b6 Kf5 Bg3 Ke4 b7 Kf3 b8=Q Ke2 Qd8 Kf1 Qd1#", 36 | "Nf3 f6 d3 e5 Nbd2 g5 e4 g4 Ng1 Bb4 Qxg4 d5 Qh5+ Kf8 a3 dxe4 axb4 exd3 Bxd3 e4 Bc4 f5 Qf7#", 37 | "e4 d5 exd5 Qxd5 Nc3 Qa5 b3 c6 Bb2 Nf6 Be2 Bf5 Nf3 e6 O-O Nbd7 a3 Qc7 a4 Bd6 d3 Ng4 h3 Ngf6 Ne4 Nxe4 dxe4 Bg6 Bd3 f6 Nh4 Bf7 g3 Qb6 Kg2 g5 Nf3 h5 Nh2 Qc7 Qf3 Ne5 Qxf6 Nxd3 Qxh8+ Kd7 Qg7 Nxb2 Qxf7+ Kc8 Qe8+ Qd8 Qxe6+ Kc7 Qf7+ Be7 Qxh5 Qd2 Qf7 Qd6 Rfb1 Rf8 Qg7 Qf6 Qxf6 Bxf6 Ra2 c5 Raxb2 Bxb2 Rxb2 Rd8 c4 Rd3", 38 | "e4 c6 e5 d6 exd6 exd6 d4 g6 Bd2 Bg7 Bc3 Ne7 Bc4 Be6 b3 b5 Bxe6 fxe6 d5 Nxd5 Bxg7 Rg8 Bh6 Qf6 c3 Nd7 Qf3 O-O-O Ne2 Qxf3 gxf3 Ne5 Nd4 Rde8 f4 Nd3+ Kf1 Kb7 Nd2 N5xf4 Bxf4 Nxf4 Ne4 Rgf8 Nxd6+ Kb6 Nxe8 Rxe8 Re1 c5 Rxe6+ Nxe6 Nxe6 Rxe6 Rg1 a5 Rg4 Kc6 Rg5 Kd6 Kg2 Re5 Rg4 h5 Rxg6+ Kd5 Ra6 c4 Rxa5 Kc6 b4 Rg5+ Kf1 Rg8 Ra6+", 39 | "f4 d5 Nf3 Nc6 e3 e6 Bb5 Nf6 d3 Bd7 O-O a6 Ba4 b5 Bb3 Bd6 c3 O-O Bc2 h6 e4 dxe4 dxe4 Be7 e5 Nd5 Qd3 g6 f5 exf5 Bxh6 Re8 Qxd5 Be6 Qxd8 Raxd8 Nbd2 Rd5 Rae1 Bc5+ Kh1 Red8 Nb3 Bb6 Bg5 R8d7 Bf6 a5 Rd1 a4 Nc1 Be3 Nd3 Na5 Nb4 Rxd1 Rxd1 Rxd1+ Bxd1 Nc4 Nd3 Na5 Bd8 Bb6 a3 Bc4 Nb4 Kf8 Nd4 Ke8 Bf6 Bxd4 cxd4 Kd7 d5 Nb7 Bf3 Nc5 Nc6 Nd3 Na5 Nxb2 Nxc4 bxc4 e6+ fxe6 Bxb2 exd5 Bxd5 Kd6 Bxc4 g5 Bb5 c6 Bxa4 Kd5 Bc2 f4 Bc1 Ke5 Kg1 c5 Kf2 g4 g3 f3 Be3 Kd5 Bf4 c4 a4 Kc5 Be5 Kb4 a5", 40 | "e4 e5 Nc3 Nf6 d3 c6 f4 exf4 Bxf4 d5 e5 d4 Nce2 Nd5 Bd2 c5 c4 Ne3 Bxe3 dxe3 Nf3 Be7 Nc3 Bg4 Be2 O-O O-O Nc6 Nd5 Nd4 Nxd4 cxd4 Bxg4 Bc5 e6 fxe6 Bxe6+ Kh8 Rxf8+ Qxf8 Qf3 Qd6 Bf5 Rf8 Qe4 g6 Bg4 a5 Bf3 Ba7 a3 Bb8 g3 Ba7 b4 h5 c5 Qd7 Qxg6 Qh7 Qxh7+ Kxh7 Bxh5 Bb8 Rf1 Rd8 Nf6+ Kh6 g4 Kg5 Kg2 axb4 axb4 Rc8 Ne4+ Kh6 Rf6+ Kg7 g5 Be5 Rf7+ Kg8 Rxb7 Rf8 Bf3 Ra8 Ng3 Ra2+ Ne2 Bf4 h4 Kf8 b5 Rd2 c6 Rxd3 b6", 41 | "d4 d5 h3 e6 a3 Nf6 Nf3 b6 e3 c5 c4 cxd4 Qxd4 Nc6 Qd1 Be7 cxd5 Nxd5 Bb5 Bb7 Bxc6+ Bxc6 e4 Nf6 Qxd8+ Rxd8 Nc3 O-O e5 Bxf3 gxf3 Nd5 Nxd5 Rxd5 f4 f6 Be3 fxe5 fxe5 Rxe5 O-O-O Bf6 Kb1 Rb5 Rd2 Rd8 Rhd1 Rf8 Ka2 a5 Rd6 e5 b4 axb4 axb4 Ra8+ Kb3 Be7 Rxb6 Rxb6 Bxb6 Rb8 Ba5 Rb5 Ka4 Rb7 Re1 Bd6 Rd1 Rd7 Rc1 Kf7 Rg1 g6 h4 Ke6 Rg5 Rf7 Bb6 Rf4 h5 Kf6 Rg2 gxh5 Ba5 Rg4 Rh2 h4 f3 Rf4 Rh3 Kf5 Kb5 e4 fxe4+ Kxe4 Kc6 Be5 b5 Rf6+ Kd7 Rd6+ Ke7 Rd5 Rxh4+ Kf3 Rb4 Bd6+ Ke6 Bxb4 Kxd5 Bxa5 Kc6 h5 Kb7 h4 Ka6 Bd8 b6 Bxb6 Kxb6 h3", 42 | "d4 d5 c4 c5 e3 e6 cxd5 cxd4 dxe6 dxe3 Qxd8+ Kxd8 exf7 exf2+ Kxf2 Nf6 Bg5 Be6 Nf3 Bxf7 Nc3 Be7 Be2 Nc6 Rhd1+ Kc7 Bf4+ Kc8 Bb5 Bc5+ Kf1 Nb4 a3 Nc2 Rac1 Ne3+ Bxe3 Bxe3 Rc2 a6 Re2 axb5 Rxe3 Bc4+ Kg1 Re8 Rxe8+ Nxe8 Ne5 h6 Nxc4 bxc4 Rd4 b5 Nxb5 Kb7 Nd6+ Kb6 Nxc4+ Kc5", 43 | "e4 e5 Bc4 Qf6 d3 Bc5 Qf3 d6 Nc3 c6 Bg5 Qxf3 Nxf3 h6 Bh4 g5 Bg3 Nf6 h4 g4 Nd2 h5 f3 Nbd7 fxg4 Nxg4 Rf1 Ndf6 Nf3 Be6 Ng5 Bxc4 dxc4 O-O-O Bf2 Nxf2 Rxf2 Bxf2+ Kxf2 Ng4+ Kg1 Rdf8 Rf1 f6 Ne6 Kd7 Nxf8+ Rxf8 Rf5 Ke6 Rxh5 Ne3 Rh7 Nxc4 b3 Ne3 Rxb7 Nxc2 Rxa7 Rh8 g3 Rg8 Kh2 Ne3 Ra6 c5 Nb5 Rd8 h5 Ng4+ Kh3 f5 exf5+ Kxf5 Rxd6 Rxd6 Nxd6+ Kg5 a4 Nf6 a5 Nd7 Ne4+ Kxh5 a6 Kg6 a7 Nb6 Nxc5 Kf5 Nd7 e4 Nxb6 e3 a8=Q e2 Qf3+", 44 | "e4 c6 Bc4 d5 exd5 cxd5 Bb3 Nf6 c3 Nc6 d3 e5 h3 Bd6 Ne2 Be6 O-O O-O f4 e4 dxe4 Nxe4 Nd4 Kh8 f5 Nxd4 cxd4 Bd7 Bxd5 Bc6 Bxc6 bxc6 Nc3 Nxc3 bxc3 c5 d5 c4 Be3 Re8 Bd4 Be5 Bxe5 Rxe5 Qd4 Qb6 Qxb6 axb6 d6 Rd5 Rf4 b5 Rd4 Rxd4 cxd4 Rd8 Rb1 g6 Rxb5 Rxd6 fxg6 fxg6 Rc5 Rxd4 a4 c3 a5 Ra4 Rxc3 Rxa5 g4 Ra2 Rc7 h5 Rc6 Kg7 gxh5 gxh5 Rc4 Kg6 h4 Kf5 Rc5+ Kg4 Rc4+ Kg3 Re4 Ra1+ Re1 Rxe1#", 45 | "e4 e5 Nf3 Nc6 Bb5 Nf6 Bxc6 dxc6 d3 Bd6 O-O O-O Nbd2 b5 Qe2 a5 h3 Bd7 a4 b4 b3 c5 Nc4 Be7 Ncxe5 Be6 Bb2 Qc8 Rad1 h6 Kh2 Re8 Ng1 Nh7 f4 f6 Nef3 Bf7 f5 Bd6+ Kh1 c6 Bc1 Bc7 Qf2 Bh5 g4 Bf7 Nh4 Ng5 Bf4 Bxf4 Qxf4 Re7 Ng2 Qc7 Qxc7 Rxc7 h4 Nh7 Kh2 Re8 Kg3 Rd7 Nf4 Nf8 Nf3 Red8 g5 hxg5 hxg5 fxg5 Nxg5 Be8 Rh1 g6 Rh6 Kg7 Rdh1 Kf6 Nf3 Rg7 Kg4 Ke7 e5 Kd7 f6 Rf7 e6+ Kc7 exf7 Bxf7 Rh7 Rd7 Rxf7 Rxf7 Kg5 Kd6 Ne6", 46 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e5 Nb3 a6 Nc3 b5 Nd5 Bb4+ c3 Bf8 Be3 Nf6 h3 Nxe4 Bd3 f5 Bxe4 fxe4 O-O Be7 Bc5 Bxc5 Nxc5 d6 Nxe4 Be6 Ne3 O-O Qxd6 Qxd6 Nxd6 e4 a3 Ne5 Nxe4 Nd3 Rad1 Nxb2 Rd2 Nc4 Nxc4 Bxc4 Nd6 Bxf1 Kxf1 Rad8 Ne4 Rxd2 Nxd2 Rc8 c4 bxc4 Ne4 c3 Nd6 c2 Ne4 c1=Q+ Ke2 Rc2+ Kf3", 47 | "d4 b6 e4 Bb7 Ba6 g6 Bxb7 Na6 Bxa8 Qxa8 Nc3 c6 Qe2 b5 Nf3 Qb7 O-O b4 Nd1 c5 dxc5 Nxc5 c3 Qxe4 Qxe4 Nxe4 Ne3 bxc3 bxc3 Nxc3 Bb2 Ne2+ Kh1 f5 Rfe1 Nf4 g3 Nh5 Ng5 Bh6 f4 Bxg5 fxg5 h6 Nd5 hxg5 Bxh8 Kf7 Bb2 e6 Nc3 Ngf6 Rad1 Ne4 Nxe4 fxe4 Rxd7+ Ke8 Rxa7 Nxg3+ hxg3 g4 Rc1 g5 Rc8#", 48 | "g3 c5 Bg2 d6 Nf3 e6 O-O Nf6 d3 Be7 Nbd2 h6 b3 a6 Bb2 Nbd7 Ne4 Nxe4 dxe4 Rb8 e5 d5 Nd2 b6 c4 dxc4 Nxc4 b5 Nd6+ Kf8 Bc6 Bb7 Bxb7 Qc7 Bg2 Bg5 Ne4 Be7 f4 Nb6 Rc1 Rd8 Qc2 Nd5 Nxc5 Ne3 Nxe6+ fxe6 Qxc7 Nxf1 Rxf1", 49 | "e4 b6 d3 Bb7 Nc3 d6 Be3 e5 Nf3 f6 Be2 g5 O-O h5 Qd2 g4 Ne1 Qd7 f3 Bh6 fxg4 Bxe3+ Qxe3 hxg4 Qg3 Qh7 Bxg4 Nh6 h3 Nxg4 Qxg4 Qg8", 50 | "e4 e6 Nf3 c6 Nc3 d5 exd5 cxd5 d4 Nf6 Bd3 Be7 Ne5 O-O O-O Nbd7 f4 a6 Kh1 b5 a3 Bb7 Qf3 h6 Qg3 Nh7 Ng4 f5 Nxh6+ Kh8 Qg6 gxh6 Qxe6 Rf6 Qe2 Rg6 Bxf5 Rg7 Qe6 Ndf6 Re1 Bd6 Qe3", 51 | "c4 c6 Nc3 e6 e4 Nf6 Nf3 Be7 d4 d5 cxd5 exd5 e5 Nfd7 Bd3 Na6 a3 Nc7 O-O Nb6 h3 Be6 Re1 Nd7 Qc2 c5", 52 | "e4 c6 d3 d5 Nc3 dxe4 dxe4 Nd7 Nf3 Ngf6 Bd3 e6 O-O Bd6 Re1 Qc7 Bg5 Ng4 g3 h6 Bd2 Nde5 Nxe5 Nxe5 Bf4 Nxd3 Bxd6 Qxd6 Qxd3 Qxd3 cxd3 O-O Kf1", 53 | "e4 e5 Qf3 Nf6 a4 Nc6 a5 Nxa5 Rxa5 c6 Rxe5+ Be7 Qg3 d6 Rxe7+ Qxe7 Qxg7 Rg8 Qh6 Qxe4+ Kd1 Bg4+ f3", 54 | "e4 e6 Nf3 d5 e5 c5 d3 Nc6 Nbd2 Nge7 b3 Ng6 Bb2 Qc7 Qe2 Be7 O-O-O O-O Ne4 dxe4 Qxe4 b5 d4 c4 bxc4 bxc4 Bxc4 Na5 Bd3 Bb7 Nh4 Bxe4 Bxe4 Rac8", 55 | "e4 e5 Nf3 Bc5 c3 f6 d4 Be7 Be3 Nc6 Nbd2 Nxd4 cxd4 exd4 Nxd4 Bb4 Be2 Qe7 O-O c5 Nf5 Qe5 Nc4 Qxe4 Nxg7+ Kd8 Qd6 Ne7 Ne6+ Ke8 Nc7+ Kf8 Bh6+ Kf7 Bh5+ Ng6 Bxg6+ Kxg6 Bf4 Qxc4 Nxa8 Qf7 Qd3+ f5 Qd6+ Qf6", 56 | "d4 d5 Bg5 Nd7 g3 Ngf6 Bh3 e6 Nc3 Be7 Bxf6 Nxf6 e3 a6 Nge2 b5 O-O O-O Qb1 c5 dxc5 Bxc5 b4 Bb6 a4 Bb7 a5 Bc7 Nd1 e5 c3 e4 f4 Bc8 Bxc8 Rxc8 Nd4 Bb8 Nf2 Qd7 Ra3 Ng4 Nxg4 Qxg4 Rf2 h5 Kg2 h4 Raa2 h3+ Kh1 Ba7 Rac2 Bxd4 cxd4 Rxc2 Qxc2 Rc8 Qb2", 57 | "d4 d5 f3 e6 Nh3 f5 g3 Nf6 e3 Be7 f4 c5 Qd2 cxd4 Qxd4 Nc6 Qd2 Bd7 Nc3 O-O Qe2 d4 Bd2 dxc3 Bxc3 b6 O-O-O Bc5 Kb1 b5 Bd4 Bxd4 exd4 Be8 c4 Bf7 d5 Ne7 d6 Nc6 cxb5 Nb8 d7 a6 Rc1 Nfxd7 Qd2 Nf6 Qxd8 Rxd8 bxa6 Nbd7 Bb5 Rxa6 Bxa6 Nc5 Bc4 Rc8 b4 Nce4 Bb3 Rd8 Rhd1 h6 Rxd8+ Kh7 Rdd1 Kg6 Rg1 Nd7 Rgf1 Nb6 Nf2 Nd6 Nd3 Nb5 Nc5 Nd5 Bxd5 exd5 Nd7 d4 Ne5+ Kf6 Nxf7 Kxf7 Rfd1 Nc3+ Rxc3 dxc3 Rc1 c2+ Rxc2 Kg6 Rc5 Kf6 h3 g6 Rc6+ Kf7 g4 fxg4 hxg4 g5 fxg5", 58 | "e4 e5 Nf3 Nc6 Bb5 d6 Bxc6+ bxc6 d4 Nf6 dxe5 dxe5 Qxd8+ Kxd8 Nxe5 Bd6 Nxf7+ Ke8 Nxh8 Bb7 f4 Ke7 e5 Bb4+ Nc3 Bxc3+ bxc3 Ne4 O-O Nxc3 Bd2 Ne2+ Kh1 Rxh8 f5 Rf8 Rae1 Nd4 Bg5+ Ke8 f6 g6 e6 Nf5 e7 Rf7 g4 Nxe7 Rxe7+ Kf8 Rxf7+ Kxf7 Re1 c5+ Kg1 Bd5", 59 | "e4 e6 c4 d5 exd5 exd5 cxd5 Nf6 b3 Nxd5 Bb2 Nf6 Nf3 Be7 Be2 O-O O-O h6 Re1 Re8 d4 Nbd7 Nc3 Nb6 Qc2 c6 Rad1 Nbd5 Ne5 Be6 Bd3", 60 | "e4 e5 Nc3 d6 Bc4 f5 exf5 Bxf5 d4 e4 d5 Nf6 Nge2 Nbd7 Ng3 Bg6 h4 Ne5 Be2 Bf7 h5 g6 h6 Nfg4 Be3 Nxe3 fxe3 Qf6 Qd2 Qe7 O-O-O O-O-O Rdf1 a6 Rf2 Be8 Rhf1 Bd7 Ngxe4 Ng4 Bxg4 Bxg4 Nf6 Bxh6 Nxg4 Bg5 Qe2 Rhe8", 61 | "d4 d5 Nf3 Nf6 e3 Bg4 h3 Bh5 g4 Bg6 Ne5 Nbd7 Nxg6 hxg6 Bg2 e6 a3 Nb6 g5 Nh5 e4 c6 exd5 Nxd5 Bxd5 cxd5 Nd2 Qxg5 Nf3 Qd8 Bg5 Be7 Bxe7 Qxe7 Qd3 O-O O-O-O Rac8 Ne5 Qc7 Rhg1 Nf4 Qd2 Nxh3 Rg3 Nxf2 Qxf2 Qb6 c3 Qb3 Kb1", 62 | "e4 c6 Nc3 d6 d4 g6 f4 Bg7 Nf3 Nf6 Bd3 O-O O-O Bg4 Qe1 Nbd7 Be3 c5 Rd1 cxd4 Bxd4 Qa5 h3 Bxf3 Rxf3 e5 Be3 exf4 Bxf4 Ne5 Bxe5 dxe5 Nd5 Qxe1+ Rxe1 Nxd5 exd5 f5 Bc4 e4 Rb3 b6 d6+ Kh8 d7 Rad8 Bb5 a6 Bc6 b5 Rd1 Rf6 Kf2 Rxc6 a4 Rc7 axb5 Rdxd7 Rxd7 Rxd7 bxa6 Bd4+ Ke2 Ra7 Rb8+ Kg7 Rb7+ Rxb7 axb7 Ba7 c4 Kf7 b4 Ke7 c5 Kd7 b5 Kc7 c6 Bb8 Ke3 Kb6 Kd4 Kxb5 Kd5 Kb6 Ke6 Kxc6 Kf7 f4 Kg7 e3 Kxh7 e2 Kxg6 e1=Q Kg5 Qg3+ Kf5 Qxg2 h4 f3", 63 | "e3 e6 b3 d5 Bb2 c5 h3 Nf6 g4 Bd6 h4 d4 h5 dxe3 dxe3 Nc6 h6 g6 g5 Rg8 Bxf6 Qb6 Nc3 e5 Nd5 Qa5+ Qd2 Qa3 Nf3 Nb4 Rd1 Be6 Bb5+ Nc6 Bxc6+ bxc6 Nc3 Be7 Ne4 Bxf6 Nxf6+ Ke7 Nxg8+ Rxg8 Qd7+ Kf8 Qd8#", 64 | "Nc3 Nf6 e4 d5 e5 d4 exf6 dxc3 fxg7 cxd2+ Qxd2 Qxd2+ Bxd2 Bxg7 O-O-O O-O Ne2 Nc6 Bc3 Bg4 f3 Bf5 Bxg7 Kxg7 Ng3 Bg6 Bd3 Rad8 Be4 Bxe4 Nxe4 b6 h4 e6 c3 Ne5 Rh3 Rxd1+ Kxd1 Rd8+ Kc2 f6 h5 Kf7 Rg3 Nc4 f4 Nd6 Nxd6+ cxd6 Kd3 f5 Rg5 Rg8 Kd4 e5+ Kd5", 65 | "e4 d5 exd5 Qxd5 Nc3 Qd7 Bc4 e6 Nf3 Bd6 O-O h6 d4 c6 Ne5 Bxe5 dxe5 Qxd1 Rxd1 Ne7 b4 O-O Rd3 b5 Rg3 Nf5 Rg4 bxc4 Ne4 Nd7 Rb1 Ba6 a4", 66 | "d4 d5 Nf3 Nd7 c3 e6 Bf4 Ngf6 e3 c5 Bd3 c4 Bc2 b5 Nbd2 b4 Qe2 a5 e4 a4 O-O dxe4 Nxe4 Ba6 Nd6+ Bxd6 Bxd6 b3 Bb1 Nd5 Re1 Ne7", 67 | "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Bc4 Bg7 f3 O-O Be3 Nc6 Qd2 Bd7 O-O-O Rc8 Bb3 a6 g4 Ne5 Rdf1 Nc4 Bxc4 Rxc4 Bh6 b5 Bxg7 Kxg7 g5 Nh5 b3 Rc5 f4 Qc7 Nd5 Rxd5 exd5 Rc8 f5 Qc3 Qxc3 Rxc3", 68 | "e3 Nf6 d4 g6 c4 Bg7 Nc3 d6 Nf3 O-O Be2 Bg4 h3 Bxf3 Bxf3 c6 O-O d5 Bd2 dxc4 Be2 b5 b3 cxb3 axb3 Nd5 Nxd5 Qxd5 Qc2 c5 dxc5 Bxa1 Bxb5 Bg7 Bc4 Qc6 Rd1 Qxc5 Bxf7+ Rxf7 Qxc5 Nd7 Qc7 Nf6 Rc1 Raf8 f3 Nd5 Qc5 Rf5 Qd4 Bxd4 exd4 Nf4 Bxf4 Rxf4 Rc4 R4f7 Ra4 Rf6 Rxa7 Rb6 Ra3 Rd8 Ra4 Rxb3 Kf2 Rd3 d5 R3xd5 Re4 e5 Kg3 Kf7 f4 exf4+ Rxf4+ Ke6 Re4+ Re5 Rf4 Rf5 Rg4 Kf6 Kh2 Rd3 Rd4 Rf2 Rd6+ Rxd6 Kg3 Rdd2 Kh4 Rxg2", 69 | "e4 e6 d4 d5 exd5 exd5 Nf3 Nf6 Be2 h6 Nc3 c6 Bd2 Bf5 a3 Bd6 Nh4 Bh7 Be3 Ne4 Nxe4 dxe4 g3 O-O Ng2 f5 Bc4+ Kh8 Nf4 Qb6 Bb3 a5 O-O Nd7 Ne6 Rf6 d5 c5 c4", 70 | "c4 e5 e4 Nc6 d3 a6 Nf3 d6 a3 Bg4 Be2 Qf6 O-O h5 Nc3 Be6 Nd5 Bxd5 cxd5 Nd4 Nxd4 exd4 Bxh5 g6 Bf3 O-O-O Bg4+ Kb8 b4 Bh6 b5 a5 Bd2 b6 Bxh6 Rxh6 a4 Ne7 Qc2 Qe5 h3 f5 Bf3 f4 Bg4", 71 | "e4 c6 Nf3 d5 exd5 cxd5 d4 Nc6 c4 dxc4 Bxc4 e6 O-O Nf6 Re1 Bb4 Nc3 O-O Bd2 a6 a3 Ba5 b4 Bb6 Be3", 72 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e5 Nxc6 bxc6 Nc3 Bc5 Be3 Bxe3 fxe3 Qh4+ g3 Qd8 Qd2 Nf6 O-O-O O-O h3 a5 g4 h6 Qg2 Qb6 g5 Qxe3+ Kb1 hxg5 Be2 Rb8 h4 Qxc3 b3 a4 Bc4 a3 Kc1 Qa1+ Kd2 Qxa2 hxg5 Nh7 Qh3 Re8 Qxh7+ Kf8 Qh8+ Ke7 Qxg7 Rf8 Qf6+ Ke8 g6 fxg6 Rh8 Rxh8", 73 | "e4 e5 Nf3 Nf6 Bc4 Bc5 O-O O-O Nxe5 Nxe4 Re1 Qe8 d4 Bb4 Rxe4 d6 Bxf7+ Rxf7 Nxf7 Qxf7 Qe2 Qg6 Re8+ Kf7 Qe7#", 74 | "e4 g6 d4 d6 Nf3 Bg7 Be3 b6 c4 Ba6 b3 e6 Nc3 Ne7 Bd3 c5 d5 Bxc3+ Nd2 Bxa1 Qxa1 exd5 Qxh8+ Kd7 Qf6 dxc4 bxc4 Ng8 Qxf7+ Kc6 Qd5+ Kd7 e5 Nc6 Qxd6+ Kc8 Qxc6+ Qc7 Qe6+ Kb8 Be4 Bb7 Qxg8+ Qc8 Qxc8+ Bxc8 Bxa8 Kxa8 O-O Be6 f4 Kb7 g4", 75 | "e4 e6 d4 d5 e5 Nc6 c3 g6 Nf3 Bh6 Bxh6 Nxh6 Bd3 O-O Qd2 Nf5 h4 Qe7 h5 g5 Bxf5 exf5 Nxg5 f6 Nf3 Qg7 g3 f4 Qxf4 fxe5 Qg5 Qxg5 Nxg5 exd4 cxd4 Nxd4 Na3 Nf3+ Kf1 Nxg5 Kg2 Bh3+ Rxh3 Nxh3 Kxh3 Rxf2 b3 c5 Nb5 c4 bxc4 dxc4 Rc1 Rxa2 Rxc4 Ra5 Nd6 Rxh5+ Kg4", 76 | "e4 e6 d4 d6 Nc3 Nd7 Nf3 Ne7 Bd3 Ng6 Be3 Be7 Qd2 c6 O-O-O O-O h4 b5 h5 Nh8 Kb1 a5 e5 d5 Bh6 Re8 Rh3 f5 exf6 Nxf6 Rg3 Bf8 Ne5 Nf7 Nxf7 Kxf7 Bg5 h6 Bg6+ Kg8 Bxe8 Qxe8 Bxf6 Qxh5 Qf4 Qf5 Qxh6 Kf7", 77 | "d4 d6 e4 e5 dxe5 dxe5 Qxd8+ Kxd8 Nf3 Nc6 Bb5 Bd7 Nc3 f6 O-O g5 h3 h5 a3 a6 Bxc6 Bxc6 Rd1+ Ke8 Nd5 Bd6 c4 Ne7 Nxf6+ Kf7 Nd5 g4 Ng5+ Ke8 h4 Ng6 Nf6+ Ke7 Nd5+ Bxd5 cxd5 Nf4 Bxf4 exf4 Ne6 c6 Rac1 f3 g3 Rh6 Ng5 cxd5 exd5 Bc7 Re1+ Kd6 Rcd1 Bd8 Re6+ Rxe6 Nxe6", 78 | "e4 d5 exd5 Qxd5 Nc3 Qe5+ Qe2 Qxe2+ Bxe2 Bf5 Nd5 Kd8 c3 c6 Ne3 e6 Nxf5 exf5 Nf3 g6 Bc4 Ke7 O-O Nf6 Re1+ Kd7 Ne5+ Kc7 Nxf7 Rg8 Ng5 Rh8 Ne6+ Kc8 d3 Nbd7 Bg5 Be7", 79 | "e4 e5 Nf3 d6 d4 exd4 Qxd4 Nf6 Nc3 Nc6 Bb5 Bd7 Bxc6 Bxc6 O-O Be7 Re1 O-O Bf4 Nd7 Qd2 Bf6 Nd4 Ne5 Bxe5 Bxe5 Nxc6 bxc6 Rab1 g6 Ne2 Qh4 f4 Bg7 g3 Qf6 c3 a6 Nd4 c5 Nf3 Rab8 Re2 a5 Rc1 a4 a3 Rb3 Rc2 Rfb8 Qd5 Qe6 Qd3", 80 | "e4 e5 Nc3 Qe7 Bc4 c6 d3 h6 Nge2 a5 a3 g5 Ng3 Bg7 Nf5 Qf6 O-O d6 Bd2 Bxf5 exf5 Qxf5 Ne4 d5 Nd6+ Kd7 Nxf5 Nf6 Nxg7 dxc4 dxc4 Kc8 Nf5 Nbd7 Bc3 Rd8 Nd6+ Kb8 Nxf7 Rf8 Nxe5", 81 | "e4 e6 d4 c5 d5 exd5 Qxd5 d6 Bc4 Be6 Qd3 d5 exd5 Bxd5 Bxd5 Nf6 Bxb7 Qxd3 cxd3 Bd6 Bxa8 O-O Bf3 Nbd7 Nh3 Ne5 Be2 Rb8 Nc3 a6 O-O Nc6 b3 Nb4 Bb2 Nc2 Rac1 Nd4 Ne4 Nxe2+ Kh1 Nxc1 Rxc1 Nxe4 dxe4 Re8 f3 f6 Nf2 Rc8", 82 | "e4 c5 b3 Nc6 Bb2 d6 Bb5 Nf6 Qe2 e5 f4 a6 Bxc6+ bxc6 fxe5 dxe5 Nf3 Bd6 O-O O-O d3 Re8 Nbd2", 83 | "g3 d5 Bg2 e6 e4 Nf6 exd5 exd5 d4 Nc6 c3 Be6 h3 Be7 Bf3 Qd7 g4 O-O-O Ne2 a6 Nf4 Bd6 Be3 Rde8 Nd2 g5 Ng2 h6 h4 gxh4 Nxh4", 84 | "b4 e6 Bb2 d5 a3 Nf6 e3 Be7 Bxf6 O-O Bxe7 Qxe7 d4 b6 Nd2 Bb7 c4 Nd7 cxd5 exd5 Ngf3 c5 bxc5 bxc5 Nb3 c4 Nbd2 Bc6 Nb1 Rab8 Nc3 Rb3 Qc2 Rfb8 Rb1 Qxa3 Rxb3 Rxb3 Be2 Rxc3 Qb1 Rc1+ Qxc1 Qxc1+ Bd1 Qc3+ Nd2 a5 O-O a4 Nb1 Qb2 Bxa4 Bxa4 g3 c3 Kg2 c2 Na3 Qxa3", 85 | "e4 d6 Nf3 Bd7 e5 Qc8 Ng5 Nc6 Nxf7 Kxf7 exd6 cxd6 Bc4+ Ke8 Bxg8 Rxg8 O-O Nd8 Qh5+ g6 Qxh7 Rg7 Qh4 Be6 d3 Bf7 Nc3 g5 Qa4+ Nc6 d4 Be6 d5 Bd7 dxc6 Bxc6 Qd4 g4 Bf4 Rf7 Bg3 Bg7 Qd3 Bd7 Qh7", 86 | "c4 e6 Nf3 Nf6 Nc3 d5 d4 Be7 Bg5 Nbd7 e3 c6 c5 h6 Bh4 a5 a3 O-O Bd3 b6 b4 axb4 axb4 Rxa1 Qxa1 bxc5 bxc5 Nh7 Bxe7 Qxe7 Qb1 Nhf6 O-O e5 dxe5 Nxe5 Nxe5 Qxe5 Ne2", 87 | "d4 d5 c4 Nc6 Nc3 e5 e3 exd4 exd4 dxc4 Bxc4 Nf6 Nge2 Bb4 O-O Bg4 f3 Bh5 a3 Bd6 b4 O-O Qb3 Bg6 Bb2 Nxd4 Nxd4 Bxh2+ Kxh2 Qxd4 Ne4 Qd7 Rad1 Qf5 Ng3 Qf4 Rd4 Qh6+ Kg1 Rfe8 Rg4 Nxg4 fxg4 Qe3+ Kh2 Qxb3 Bxb3 Rad8 Nh5 Rd2 Bc1 Rd3 Bc4 Rc3 Bb5 c6 Ba4 b5 Bb2 Rce3 Bd1 a6 Nxg7 Bd3 Rf3", 88 | "e4 e5 f4 d6 fxe5 dxe5 Nf3 Nc6 Bc4 Be7 O-O Nf6 Ng5 O-O Qf3 Nd4 Qh3 h6 Rxf6 Bxh3 Nxf7 Ne2+ Kh1 Rxf7 Bxf7+ Kh8 gxh3 Bxf6 d3", 89 | "c4 e5 Nc3 Nf6 g3 Nc6 d3 d5 cxd5 Nxd5 Bg2 Be6 h4 Bb4 Bd2 Nxc3 Bxc6+ bxc6 bxc3 Ba3 h5 Qd5 Nf3 e4 dxe4 Qxe4 Rh4 Qf5", 90 | "e4 e5 Nf3 Nc6 Bc4 Bc5 d3 Nf6 Nc3 d6 Bg5 Bg4 Nd5 Nd4 h3 Bxf3 gxf3 c6 Nxf6+ gxf6 Be3 Ne6 Qd2 Qb6 O-O-O Rg8 Rhg1 O-O-O Bxe6+ fxe6 Bxc5 Qxc5 Qe3 Qxe3+ fxe3 Kd7 f4 Ke7 f5 d5 Rxg8 Rxg8 fxe6 Kxe6 exd5+ cxd5 Rd2 Rg3 Rh2 Rxe3 Kd2 Rg3 b3 d4 c3 Kd5 c4+ Ke6 a3 f5 b4 e4 dxe4 fxe4 h4 Rxa3 Rg2 Ra2+", 91 | "e4 e5 Nf3 Nc6 Bb5 Nf6 d3 a6 Ba4 b5 Bb3 Na5 c3 c6 Bc2 Bc5 b4 Bxf2+ Kxf2 Ng4+ Ke2 O-O bxa5", 92 | "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Be3 e5 Nb3 Be6 f3 Be7 Qd2 Nbd7 g4 Qc7 O-O-O Rc8 g5 Nh5 Kb1 b5 Nd5 Bxd5 exd5 O-O Na5 Nb8 Bd3 f5 gxf6 Bxf6 Rhg1 Kh8 Qg2 Qxa5 Qg4 Nf4 Bxf4 exf4 Qh5 h6 Qg6 Kg8 Rde1 Be5 Qh7+ Kf7 Qf5+ Ke7 Rxg7+ Kd8 Rxe5 dxe5 Qxe5 Re8 Qd6+ Nd7 Qxd7#", 93 | "e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Be7 d4 exd4 Nxd4 Nxd4 Qxd4 O-O Nc3 d6 h3 h6 Bf4 b6 e5 c5 Qc4 Be6 Qa4 Nd5 Nxd5 Bxd5 Rad1 Bh4 Rxd5 Qe7 Rxd6 Qe6 Bxh6 Rfd8 Rxd8+ Rxd8 Qxh4 gxh6 Qxd8+ Kg7", 94 | "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6 dxc6 Nxe5 Bd6 Nf3 Nf6 Nc3 Bc5 h3 O-O O-O b5 d4 Bb4 Bg5 Be7 Qd2", 95 | "Nf3 c5 g3 Nc6 Bg2 e5 Nc3 e4 Nh4 d5 b3 Be7 Bb2 Bxh4 gxh4 Qxh4 Nxd5 Qd8 Bxg7 Qxd5 Bxh8 f6 e3 Ne5 Qh5+ Kf8 d4 Bg4 Qh4 Nf3+ Bxf3 Bxf3 Bxf6 Bxh1 O-O-O Bf3 Be5", 96 | "e4 e5 Bc4 c6 Nf3 d5 exd5 cxd5 Bb3 e4 d3 exf3 Qxf3 Be6 Nc3 Nf6 Bg5 Bg4 Bxf6 Bxf3 Bxd8 Kxd8 gxf3 Bc5 Bxd5 Nc6 Bxf7 Rf8 Bb3 Rxf3 O-O-O Rxf2 Ne4 Be3+ Kb1 Nd4 Nxf2 Bxf2 Rhf1 Be3 Rfe1 Bg5 Rf1 Kc7 Rde1 a6 Rf7+ Kb6 c3", 97 | "f4 e6 b3 c6 Bb2 d5 Nf3 Nf6 e3 Nbd7 c4 b6 d3 Bb7 Be2 c5 O-O Qc7 Nbd2 Be7 Qc1 O-O Ne5 Nxe5 fxe5 Nd7 d4 Bg5 Rf3 Rad8 Rg3 Bh6 Nf3 dxc4 Bxc4 cxd4 Bxd4 Nc5 Ng5 Qe7 h4 Kh8 Qc2 g6", 98 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 Bc4 Bg7 Be3 e6 Nc3 Nge7 O-O O-O f4 a6 a4 Nxd4 Bxd4 Nc6 Bxg7 Kxg7 f5 exf5 exf5 Qe7 f6+ Qxf6 Rxf6 Kxf6 Nd5+ Kg7 Qf3 f5 Qf4 b5 axb5 axb5 Rxa8 Bb7 Rxf8 Kxf8 Bxb5 Kg7 Bxc6 Bxc6 Qe5+ Kf7 Qe7+ Kg8 Nf6+ Kh8 Qxh7#", 99 | "e4 e5 Nf3 Nc6 Bb5 d6 Nc3 f5 d4 fxe4 Nxe4 Bg4 d5 a6 Bxc6+ bxc6 dxc6 Nf6 Nxf6+ Qxf6 Bg5 Qg6 Qd2 Qe4+ Qe3 Bxf3 gxf3 Qxc6", 100 | "e4 c5 d3 d6 f4 Nf6 Nf3 g6 Nc3 Bg7 Bd2 O-O Be2 Nc6 O-O a6 h3 b5 g4 Qb6 Qc1 b4 Nd1 a5 Kh2 a4 f5 Bb7 Nf2 a3 b3 e6 Rb1 exf5 gxf5 Ne5 Nxe5 dxe5 Be3 Qc7 Ng4 Nxg4+ Bxg4 Rfd8 Bh6 f6 Bxg7 Qxg7 Rg1 g5 h4 h6 hxg5 hxg5 Kg3 Kf7 Bh5+ Ke7 Bg6 Rh8 Qe3 Rh6 Qxc5+ Kd7 Rh1 Rah8 Qb5+ Kc7 Qc4+ Kb8 Kg2 Rxh1 Rxh1 Rxh1 Kxh1 Qh6+ Kg2", 101 | "e4 d5 exd5 Qxd5 Nc3 Qa5 d4 c6 Nf3 Bf5 Bd3 e6 Bxf5 Qxf5 O-O Qa5 Bd2 Nd7 Qe2 Ngf6 a3 Qc7 Rfe1 Bd6 h3 O-O Ne4 Nxe4 Qxe4 Nf6 Qh4 Rfe8 c4 Be7 Bf4 Qd8 Be5 Nd7 Qe4 Nxe5 dxe5 Qb6 b4 c5 b5 Red8 a4 a5 Red1 Qc7 Qe2 b6 Qc2 h6 Rxd8+ Rxd8 Rd1 Rxd1+ Qxd1 Qd8 Qxd8+ Bxd8 Kf1 f6 Ke2 Bc7 exf6 gxf6 g4 Kf7 Ke3 Kg6 Ke4 Kf7 Nh4 Bd6 f4 Be7 f5", 102 | "d4 Nf6 c4 e6 Nf3 Bb4+ Bd2 Qe7 g3 Bxd2+ Qxd2 Nc6 Bg2 d5 O-O O-O b3 Ne4 Qc2 f5 Nbd2 Bd7 Nxe4 fxe4 Ne5 Nxd4 Qc3 Nxe2+", 103 | "e4 c5 f4 Nc6 Nf3 d6 d3 Bd7 Be3 Qc7 Nbd2 O-O-O a3 e6 g3 Be7 Bg2 f6 O-O h5 b4 h4 bxc5 hxg3 hxg3 dxc5 Rb1 g5 fxg5 f5 Bf4 e5 Be3 f4 Bf2 fxg3 Bxg3 Bxg5 Nc4 Nf6 Nxg5 Rdg8 Qd2 Ng4", 104 | "e4 e6 Nf3 g6 d4 Bh6 Bd3 d6 O-O Bd7 c4 Qe7 Nc3 Nc6 d5 exd5 exd5 Ne5 Re1 f6 Bxh6 Nxh6 Qd2 O-O-O Nd4 f5 Qxh6 Ng4 Rxe7 Rhe8 Qxh7 Rxe7 Qxe7 Re8 Qg5 f4 Qxf4 Nxh2 Kxh2 Rh8+ Kg1 Rh5 Bxg6 Re5 Qf8+", 105 | "d4 d5 Nf3 Nf6 e3 Nc6 Be2 Bf5 a3 e6 O-O Bd6 Ne1 e5 c3 e4 f4 O-O c4 Qe7 Nc3 Be6 cxd5 Bxd5 Bg4 Bc4 Rf2 Kh8", 106 | "e3 e5 d4 Nc6 d5 Nb4 e4 a6 a3 Nxd5 exd5 c6 c4 cxd5 cxd5 d6 Nc3 b6 Nge2 Bb7 g3 Nf6 Bg2 Qc8 Bg5 Be7 Rc1 h6 Bxf6 Bxf6 Ne4 Qd8 Nxf6+ Qxf6 O-O O-O f4 exf4 Nxf4 Qe7 Re1 Qc7 Rxc7 Rae8 Rxe8 Rxe8 Rxb7 Re5 Bh3 Kh7 Qc2+ g6 Rxf7+ Kg8 Qxg6+ Kh8 Qg7#", 107 | "e4 c6 Nf3 Qc7 d4 Nf6 Bd3 d6 O-O Bg4 c4 Nbd7 Nbd2 e5 d5 Be7 Qc2 h5 h3 Bxf3 Nxf3 O-O b3 cxd5 exd5 Nc5 Bf5 e4 Ne1 a5 Be3 b6 Bxc5 Qxc5 Bxe4 Qd4 Bd3 Qxa1 Nf3", 108 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 d4 e6 Bd3 Bb4 O-O O-O Be3 Nd5 a3 Bxc3 bxc3 Nxc3 Qd2 Nd5 Ne5 Nxe3 Qxe3 Nd7 Qh3 g6 Ng4 h5 Ne5 Nxe5 dxe5 Qg5 f4 Qh6 Qg3 Bd7 f5 exf5 Bxf5 Bxf5 Rxf5 Kh7 Rff1 Rae8 Rab1 b6 Rbc1 c5 Rcd1 Qg7 Rd7 Rxe5 h3 Rf5 Rxf5 gxf5 Qf3 Qd4+ Rxd4 cxd4 Qxf5+", 109 | "e4 d5 exd5 Qxd5 Nc3 Qa5 d4 e5 Nf3 Bg4 Be2 Bb4 Bd2 Nc6 Nxe5 Bxe2 Qxe2 Nxe5 dxe5 O-O-O a3 Ne7 O-O Bxc3 Bxc3 Qb6 Rad1 Nd5 Bd4 Nf4 Qg4+ Qe6 Qxf4 Rd5 Bc3 Rhd8 Rxd5 Rxd5 h3 h6 Qg4 Qxg4 hxg4 Kd7 Re1 c5 f4 g6 Kf2 b5 Re2", 110 | "e4 c5 f4 g6 Nf3 Bg7 a3 f6 c3 Qc7 e5 fxe5 fxe5 Bxe5 Nxe5 Qxe5+ Be2 d5 O-O Nf6 d4 Qd6 Bf4 Qb6 Be5 O-O dxc5 Qxc5+ Bd4 Qd6 Nd2 Nc6 Bf2 Bf5 Nf3 Ng4 Nd4 Qxh2#", 111 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 Be2 e6 O-O Be7 d4 O-O Bg5 b6 h3 Ba6 Bxa6 Nxa6 a3 c5 Qd3 cxd4 Qxd4 Qxd4 Nxd4 Rac8 Rad1 h6 Bh4 Nc5 Rfe1 g5 Bg3 Nh5 Bh2 Bd8 Ndb5 a6", 112 | "e4 c5 d3 Nc6 f4 e5 c3 d6 Nf3 Bg4 Be2 Bxf3 Bxf3 Nf6 O-O Be7 a4 O-O Na3 a6 Be3 Rc8 Nc4 b5 axb5 axb5 Nd2 Qd7 Qe2 h6 Ra6 b4 Rfa1 Rc7 Nc4 Rb7 fxe5 dxe5 Rxc6 Qxc6 Nxe5 Qe6 Nc4 Rbb8 e5", 113 | "e4 e5 Nf3 d6 Bc4 Bg4 O-O Qf6 h3 Bxf3 Qxf3 Qxf3 gxf3 Nc6 Nc3 Nd4 Bd3 c6 Ne2 Nxf3+ Kg2 Nh4+ Kg3 Ng6 Bc4 Nh6 d4 Be7 dxe5 Bh4+ Kh2 dxe5 Be3 b5 Bb3 O-O Bxh6 gxh6 Ng3", 114 | "g3 c6 Bg2 d5 e3 e6 Ne2 Nf6 d4 Be7 O-O O-O b3 h6 c4 b6 cxd5 exd5 Nbc3 Bb4 Bb2 Bxc3 Bxc3 Bf5 Nf4 Ne4 Bb4 Re8 f3 Nd6 Bh3 Bxh3 Nxh3 a5 Bxd6 Qxd6 Re1 c5 dxc5 bxc5 Nf4 d4 exd4 Rxe1+ Qxe1", 115 | "d4 d5 c4 c6 Nf3 Bg4 cxd5 cxd5 Qb3 Qd7 Ne5 Qc8 Nxg4 Qxg4 Qxb7 Qd7 Qxa8 Qc7 Nc3 e6 Bd2 Bb4 Rc1 Nf6 a3 Ba5 b4 Bb6 Nxd5 Qxc1+ Bxc1 O-O Nxf6+ gxf6 Bh6 Re8 e3 e5 Bb5 Rc8 O-O exd4 exd4 Bxd4 Qf3 f5 Qxf5 Rd8 Bd3", 116 | "c4 e5 g3 Nf6 Bg2 Nc6 e3 d6 Nc3 Be6 b3 Rb8 Nge2 a6 d4 exd4 exd4 Bf5 O-O Be7 Bg5 O-O Bxf6 Bxf6 d5 Ne5 Nd4 Bg6 f4 Nd7 Nde2 h6 Rc1 Bf5 Nd4 Bxd4+ Qxd4 Qf6 Qd2 Qd4+ Qxd4", 117 | "Nf3 d5 g3 Nf6 Bg2 c5 O-O Nc6 d4 cxd4 Nxd4 e5 Nxc6 bxc6 c4 Bb7 cxd5 cxd5 Nc3 Bc5 Bg5 Bd4 Bxf6 gxf6 Qb3 Bxc3 Qxb7 Bd4 Bxd5 O-O Qxa8 Qxa8 Bxa8 Rxa8 Rab1 Rb8 b4 Kf8 e3 Bc3 b5 Ke7 Rfc1 Ba5 a4 Rd8 Rd1 Rc8 Rbc1 Rb8 Rc6 Bb6 Kg2 Rg8 e4 h5 h4 Rg4 f3 Rg8", 118 | "e4 e6 f4 Ne7 Nf3 d5 exd5 exd5 Be2 Nf5 O-O Be7 d4 O-O Nc3 Nh4 Nxh4 Bxh4 Be3 Bf6 Qd3 c6 Bd2 Nd7 Rab1 Qc7 Nd1 c5 dxc5 Qxc5+ Be3 d4 Bf2 Qc7 Bxd4 Bxd4+ Qxd4 Nf6 Bd3 Bd7 Qf2 Bc6 Nc3 Qb6 Qxb6 axb6 a3 Nd5 Nxd5 Bxd5 c4 Bc6 f5 f6 Rbe1 Rfd8 Be4 Rd2 Bxc6 bxc6 Rf2 Rd3 Rf3 Rd2 Rf2 Rd4 Rf3 Kf7 Rb3 b5 cxb5 cxb5 Rxb5 Ra7 Ra1 Rd2 a4 Rad7 a5 Re7", 119 | "d4 Nf6 c4 g6 Nc3 Bg7 e4 O-O e5 Ne8 f4 d6 Nf3 dxe5 fxe5 f6 Be2 fxe5 dxe5 Qxd1+ Bxd1 Nc6 Bf4 e6 Bg3 Bd7 Bc2 Rxf3 gxf3 Nd4 O-O-O Nxf3 Rhf1 Nxe5 Bxe5 Bxe5 Nb5 Bxb5 cxb5 Nf6 Kb1 Nd5 Bb3 Ne3", 120 | "d4 b6 Nf3 Bb7 c4 Nf6 Nc3 g6 g3 Bg7 Bg2 O-O Nh4 d5 cxd5 Nxd5 Nxd5 Bxd5 e4 Bb7 Be3 e6 O-O Nd7 Rc1 Rc8 Qa4 a5 Qb5 Nf6 f3 Ra8 a4 Ba6 Qc6 Bxf1 Bxf1 Rc8 Ba6 Ra8 Bb7 Ra7 e5 Qxd4 Bxd4 Ne8 Qd7 Rxb7 Bc3 f6 Qe7 c5 Qxe6+ Kh8 exf6 Nxf6", 121 | "d4 f5 Nf3 Nf6 h4 e6 Ng5 d5 e3 c6 c3 Be7 Nd2 b5 Ndf3 a5 Ne5 Ne4 Ngf7 Qc7 Nxh8 Bf6 Qh5+ Kf8 Qxh7 Bxe5 Ng6+ Kf7 Nxe5+ Kf8 Qh8+ Ke7 Qxg7+ Kd8 Qf8#", 122 | "Nc3 Nf6 e4 d6 f4 g6 Nf3 Bg7 Bc4 c5 O-O O-O d3 Nc6 Kh1 a6 a3 b5 Ba2 Bb7 Nd5 Qc7 c4 Rad8 Rb1 b4 Nxc7", 123 | "d4 d5 c4 e6 a3 Nf6 Nc3 Be7 Bf4 Nh5 Be5 f6 Bg3 Nxg3 fxg3 dxc4 e4 c5 d5 O-O Bxc4 a6 Nf3 b5 Be2 c4 O-O Qb6+ Kh1 e5 Nh4 Rd8 Bg4 Bb7 Nf5 Bf8 Ne3 Nd7 Be6+ Kh8 Nf5 Nc5 Qg4 Nxe6 dxe6 Qxe6 Rad1 g6 Ne3 Qxg4 Nxg4 Rxd1 Rxd1 f5 exf5 gxf5 Nxe5 Bg7 Nf7+ Kg8 Rd7 Bc6 Rc7 Be8 Nd6 Rd8 Nxf5 Rd1+ Nxd1", 124 | "e4 e5 Nc3 Bb4 a3 Ba5 b4 Bb6 Na4 Nc6 Nxb6 axb6 Nf3 d6 Bc4 Bg4 h3 Bxf3 Qxf3 Qf6 Qb3 Nd4 Qa2 Qg6 O-O b5 Bd5 c6 Bb3 Nf6 d3 O-O Be3 Nf3+ Kh1 Nh4 Rg1 Qh5 c4 Ng6 Bd1 Qh4", 125 | "d4 g6 e3 Nf6 Bd3 d6 h3 Bg7 f4 b6 c4 Bb7 Nf3 O-O O-O c5 d5 Qc8 Nh4 e6 e4 h6 Nc3 a6 f5 gxf5 exf5 exf5 Nxf5 Qe8", 126 | "e4 e5 Nf3 d6 Bc4 Bg4 Nc3 Bxf3 Qxf3 Nf6 Nd5 Nbd7 Nxf6+ Nxf6 d3 Qd7 Bg5 Be7 Bxf6 O-O-O Bxe7 Qxe7 Bxf7 Rhf8 Qf5+ Qd7 Qxh7 Qxf7 Qh3+ Kb8 O-O-O Qf4+ Kb1", 127 | "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6 bxc6 Nxe5 Qe7 d4 d6 Nf3 Qxe4+ Be3 Nf6 Nc3 Qg6 O-O Bf5 Nh4 Qh5 Qxh5 Nxh5 Nxf5 g6 Ng3 Nxg3 fxg3 Bg7 Rae1 O-O", 128 | "e4 a6 Nf3 b5 Be2 Bb7 e5 Nc6 O-O e6 d4 Bb4 c3 Ba5 Bg5 Nge7 a4 h6 Bxe7 Qxe7 axb5 axb5 Bxb5 O-O Nbd2 Bb6 Qc2 Ra5 Bd3 Rfa8 Rxa5 Rxa5 Nc4 Ra2 Nxb6 cxb6 Qb3 Ra8 Qxb6 d5 Qb3 g5 h3 Na5 Qc2 Ba6 Bxa6 Rxa6 Qa4 Qb7", 129 | "e4 d5 exd5 Qxd5 Qf3 Qe6+ Qe3 Nc6 Qxe6 Bxe6 c3 Bf5 d4 O-O-O Be3 e5 dxe5 Nxe5 f3 b6 Nd2 Nd3+ Bxd3 Bxd3 Kf2 Nf6 Ne2 Bd6 Nb3 Rhe8 Rad1 Bc4 Nbc1 Nd5 Bd4 c5 Bxg7 Bxe2 Nxe2 Kc7 Rxd5 Bg3+ hxg3 Rxd5 Bh6 a5 Bf4+ Kc6 Rh6+ Kb7 Rxh7 Re7 g4 Red7 g3 Rd1 Be3 Ra1 a3 Ra2 Bc1 Ra1 g5 Kc6 Rh6+ Kb5 b3 Rb1 c4+ Ka6", 130 | "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 h3 Bg7 Nf3 O-O Bd3 a6 a4 Re8 O-O Nbd7 Bf4 Qc7 Rc1 Nh5 Bh2 Ne5 Nxe5 Bxe5 Bxe5 Rxe5 f4 Re8 e5 dxe5 f5 c4 Ne4 Bxf5 Rxf5 gxf5 Qxh5 fxe4 Qg5+ Kh8 Qf6+ Kg8 Bxe4 Qe7 Qf5 f6 Rc3 Kh8 d6 Qf7 Rxc4", 131 | "e4 e5 Bc4 Nf6 Nc3 Bb4 Nd5 Nxd5 Bxd5 O-O a3 Ba5 b4 Bb6 Qe2 c6 Bb3 d5 exd5 cxd5 Nf3 Bg4 h3 Bh5 g4 Bg6 d3 Nc6 Be3", 132 | "d4 d5 c4 Nf6 Nc3 Bf5 Nf3 Nc6 e3 e6 Be2 Bb4 O-O O-O cxd5 Nxd5 Bd2 a5 a3 Nxc3 bxc3 Bd6 c4 Qf6 c5 Be7 Rc1 Qg6 Kh1 Rfe8 Qb3 Bf6 Qxb7 Be4 Qxc7 e5 Qd7 exd4 exd4 Nxd4 Rfe1", 133 | "c4 e5 Nc3 Nf6 d3 Ng4 e4 f5 exf5 Bb4 Qxg4 h5 Qe2 d5 Qxe5+ Qe7 Qxe7+ Kxe7 Bd2 Bxf5 Nxd5+ Kd6 Nxb4 b5 g3 bxc4 Bg2 cxd3 Bxa8 Re8+ Kd1 h4 gxh4 c5 Nxd3 c4 Nf4 Ke5 Nge2 c3 bxc3 Nd7 Bc6 Kd6 Bxd7 Kxd7 Rb1 a5 Rb5 a4 Rxf5 a3 Ra5 Rb8 Rg1 Rb1+ Kc2 Rb6 Rxg7+ Kd6 Rg6+ Kc7 Rxb6 Kxb6 Rxa3 Kb5 c4+ Kxc4 Rd3 Kc5 Ne6+ Kc4 Rd8 Kb5 Kc3 Ka4 Kc4 Ka3 Rb8 Kxa2 Kc3 Ka1 Kc2", 134 | "c4 Nf6 Nc3 c5 e3 d6 d4 cxd4 exd4 a6 Nf3 Bg4 Be2 Bxf3 Bxf3 Nc6 O-O e6 Bg5 Be7 a3 O-O b4 e5 b5 axb5 cxb5 Nxd4 Bxb7 Rb8 Ba6 Qa5 Bd2 Nb3 Ra2 Nxd2 Qxd2 Qb6 a4 Nh5 a5 Qc5 b6 Nf4 Rb1 Qc6 f3 d5 Bb5 Qc5+ Kf1 d4 Ne4", 135 | "d3 Nf6 g3 g6 Bg2 Bg7 Nf3 O-O e3 d6 h4 h5 Nh2 c5 Nc3 a6 f3 Nc6 g4 hxg4 fxg4 Qd7 Bd2 Nxg4 Nxg4 Qxg4 Qxg4 Bxg4 Bh3 Bh5 Ne2 e6 Nf4 Bf3 Rg1 e5 Ne2 Nb4 Bxb4 cxb4 Ng3 f5 Rf1 Bg4 Bxg4 fxg4 O-O-O Rxf1 Rxf1 Rf8 Rxf8+ Bxf8 Ne4 Be7 Ng5 Bxg5 hxg5 Kf7 Kd2 Ke6 Ke2 Kf5 Kf2 Kxg5 Kg3 e4 dxe4 Kf6 Kxg4 b5 Kf4 a5 e5+ dxe5+ Ke4 a4", 136 | "e4 e5 Nf3 Nc6 Bc4 d6 Nc3 Nf6 d4 exd4 Nxd4 Bd7 Bf4 Be7 O-O O-O Nf3 Nh5 Be3 Bg4 h3 Bxf3 Qxf3 Ne5 Qxh5 Nxc4 Bc1 Bf6 Re1 Ne5 Bf4 Ng6 Re3 Bd4 Rg3 Nxf4 Qg4 Ng6 Rd1 Bxc3 bxc3 Qe7 Rd5 Qe6 Rg5 Qxg4 R3xg4 f6 Rh5 Ne5 Rf4 g6 Rhh4 g5", 137 | "d4 d5 c4 dxc4 Nc3 Nf6 Nf3 e6 a3 Nc6 g3 Be7 Bg2 O-O O-O Bd6 e4 e5 dxe5 Nxe5 Nxe5 Bxe5 Qxd8 Rxd8 Bg5 Rd6 Nd5 Nxd5 exd5 b5 Be7 Rd7 Bb4 Rxd5 Bxd5 Ba6 Bxa8", 138 | "e4 e5 Nf3 Nc6 c3 Nf6 d3 d6 Bg5 Bg4 h3 Bxf3 Qxf3 Be7 Bxf6 Bxf6 Nd2 Qe7 a3 O-O-O b4 Bg5 Nb3 b6 a4 h5 b5 Na5 Nxa5 bxa5 Be2 Qe6 O-O", 139 | "d4 e6 c4 d5 Nf3 c6 Nc3 Bd6 e3 f5 Bd3 Nf6 Qc2 O-O O-O Bd7 c5 Bc7 b4 Be8 Ng5 Ne4 Nxe6 Qh4 Nxf8 Qxh2#", 140 | "e4 b6 d4 Bb7 Bd3 e6 c4 Bb4+ Nc3 Bxc3+ bxc3 h6 Nf3 Nf6 Qe2 O-O O-O d6 h3 Nbd7 a4 e5 Re1 a5 Nh2 Nh7 d5 Nc5 Bc2 Bc8 f4 Qh4 Rf1 Nf6 fxe5 dxe5 Nf3 Qg3 Kh1", 141 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Bc4 Nf6 d3 Bg4 f3 Bf5 Be3 e6 Nge2 c6 Ng3 Bg6 Qd2 Bd6 Nce4 Nxe4 Nxe4 Bc7 Bb3 Ba5 c3 Bxe4 fxe4 O-O O-O-O b5 h4 Bb6 d4 Nd7 g4 c5 g5 cxd4 Bxd4 Bxd4 Qxd4 Nb8 Qe3 Qc7" 142 | ).map(_ `split` " ") 143 | 144 | val encodedFixtures = fixtures.map(pgnMoves => (Encoder.encode(pgnMoves), pgnMoves.size)) 145 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | val commonSettings = Seq( 2 | scalacOptions := Seq( 3 | "-encoding", 4 | "utf-8", 5 | "-explaintypes", 6 | "-Wunused:all", 7 | "-feature", 8 | "-language:postfixOps", 9 | "-indent", 10 | "-rewrite", 11 | "-source:future-migration", 12 | "-release:21" 13 | ) 14 | ) 15 | 16 | lazy val root = (project in file(".")) 17 | .settings( 18 | commonSettings, 19 | scalaVersion := "3.7.0", 20 | name := "compression", 21 | organization := "org.lichess", 22 | version := "3.1.1", 23 | resolvers += "lila-maven".at("https://raw.githubusercontent.com/ornicar/lila-maven/master"), 24 | libraryDependencies += "org.specs2" %% "specs2-core" % "4.17.0" % Test 25 | ) 26 | 27 | lazy val benchmarks = (project in file("benchmarks")) 28 | .settings( 29 | commonSettings, 30 | name := "compression-benchmarks", 31 | scalaVersion := "3.7.0", 32 | publish / skip := true, 33 | libraryDependencies ++= Seq( 34 | "org.openjdk.jmh" % "jmh-core" % "1.37" % "compile", 35 | "org.openjdk.jmh" % "jmh-generator-annprocess" % "1.37" % "compile" 36 | ) 37 | ) 38 | .dependsOn(root) 39 | .enablePlugins(JmhPlugin) 40 | 41 | publishTo := Some(Resolver.file("file", new File(sys.props.getOrElse("publishTo", "")))) 42 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk21 3 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.10.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") 2 | -------------------------------------------------------------------------------- /src/main/scala/BitOps.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression 2 | 3 | object BitOps: 4 | private val BitMasks: Array[Int] = 5 | Array.tabulate(32)(i => (1 << i) - 1) 6 | 7 | def writeSigned(values: Array[Int], writer: Writer): Unit = 8 | values.foreach(n => writeSigned(n, writer)) 9 | 10 | def writeSigned(n: Int, writer: Writer): Unit = 11 | // zigzag encode 12 | writeUnsigned((n << 1) ^ (n >> 31), writer) 13 | 14 | def writeUnsigned(n: Int, writer: Writer): Unit = 15 | if (n & ~0x1f) == 0 then writer.writeBits(n, 6) 16 | else 17 | writer.writeBits(n | 0x20, 6) 18 | var remaining = n >>> 5 19 | while (remaining & ~0x07) != 0 do 20 | writer.writeBits(remaining | 0x08, 4) 21 | remaining >>>= 3 22 | // While loop terminated, so 4th bit is 0 23 | writer.writeBits(remaining, 4) 24 | 25 | def readUnsigned(reader: Reader): Int = 26 | var n = reader.readBits(6) 27 | if n > 0x1f then 28 | n &= 0x1f 29 | var curShift = 5 30 | var curVal = 0 31 | while 32 | curVal = reader.readBits(4) 33 | curVal > 0x07 34 | do 35 | n |= (curVal & 0x07) << curShift 36 | curShift += 3 37 | n |= curVal << curShift 38 | n 39 | 40 | def readSigned(reader: Reader): Int = 41 | val n = readUnsigned(reader) 42 | (n >>> 1) ^ -(n & 1) // zigzag decode 43 | 44 | def readSigned(reader: Reader, numMoves: Int): Array[Int] = 45 | Array.tabulate(numMoves) { _ => 46 | val n = readUnsigned(reader) 47 | (n >>> 1) ^ -(n & 1) // zigzag decode 48 | } 49 | 50 | import java.nio.ByteBuffer 51 | 52 | class Reader(bytes: Array[Byte]): 53 | private val bb = ByteBuffer.wrap(bytes) 54 | private var numRemainingBits = 0 55 | private var pendingBits = 0 56 | 57 | private def readNext(): Unit = 58 | if bb.remaining >= 4 then 59 | pendingBits = bb.getInt 60 | numRemainingBits = 32 61 | else 62 | numRemainingBits = bb.remaining * 8 63 | pendingBits = (bb.get & 0xff) << (numRemainingBits - 8) 64 | var s = numRemainingBits - 16 65 | while s >= 0 do 66 | pendingBits |= (bb.get & 0xff) << s 67 | s -= 8 68 | 69 | def readBits(numReqBits: Int): Int = 70 | if numRemainingBits >= numReqBits then 71 | numRemainingBits -= numReqBits 72 | (pendingBits >>> numRemainingBits) & BitMasks(numReqBits) 73 | else 74 | val res = pendingBits & BitMasks(numRemainingBits) 75 | val neededBits = numReqBits - numRemainingBits 76 | readNext() 77 | (res << neededBits) | readBits(neededBits) 78 | 79 | class Writer(initialCapacity: Int = 10): 80 | private var buffer = Array[Int](initialCapacity) 81 | private var index: Int = 0 82 | private var numRemainingBits = 32 83 | private var pendingBits = 0 84 | 85 | def writeBits(data: Int, numBits: Int): Unit = 86 | val maskedData = data & BitMasks(numBits) 87 | numRemainingBits -= numBits 88 | if numRemainingBits >= 0 then pendingBits |= maskedData << numRemainingBits 89 | else 90 | if index == buffer.length then buffer = java.util.Arrays.copyOf(buffer, index + (index >> 1) + 5) 91 | buffer(index) = pendingBits | (maskedData >>> -numRemainingBits); 92 | numRemainingBits += 32 93 | pendingBits = maskedData << numRemainingBits 94 | index += 1 95 | 96 | def toArray(): Array[Byte] = 97 | val numPendingBytes = (39 - numRemainingBits) >> 3 98 | val bb = ByteBuffer.allocate(4 * index + numPendingBytes) 99 | for i <- 0 until index do bb.putInt(buffer(i)) 100 | if numPendingBytes == 4 then bb.putInt(pendingBits) 101 | else for i <- 0 until numPendingBytes do bb.put((pendingBits >>> (24 - i * 8)).toByte) 102 | bb.array() 103 | 104 | end BitOps 105 | -------------------------------------------------------------------------------- /src/main/scala/clock/Encoder.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.clock 2 | 3 | import java.util.Arrays 4 | import org.lichess.compression.BitOps 5 | 6 | object Encoder: 7 | 8 | def encode(centis: Array[Int], startTime: Int): Array[Byte] = 9 | if centis.isEmpty then return Array.emptyByteArray 10 | 11 | val encoded = Arrays.copyOf(centis, centis.length) 12 | val truncatedStart = LowBitTruncator.truncate(startTime) 13 | 14 | LowBitTruncator.truncate(encoded) 15 | LinearEstimator.encode(encoded, truncatedStart) 16 | EndTimeEstimator.encode(encoded, truncatedStart) 17 | 18 | val writer = BitOps.Writer() 19 | BitOps.writeUnsigned(encoded.length - 1, writer) 20 | BitOps.writeSigned(encoded, writer) 21 | LowBitTruncator.writeDigits(centis, writer) 22 | 23 | writer.toArray() 24 | 25 | def decode(bytes: Array[Byte], startTime: Int): Array[Int] = 26 | if bytes.isEmpty then return Array.emptyIntArray 27 | 28 | val reader = BitOps.Reader(bytes) 29 | val truncatedStart = LowBitTruncator.truncate(startTime) 30 | 31 | val numMoves = BitOps.readUnsigned(reader) + 1 32 | val decoded = BitOps.readSigned(reader, numMoves) 33 | 34 | EndTimeEstimator.decode(decoded, truncatedStart) 35 | LinearEstimator.decode(decoded, truncatedStart) 36 | LowBitTruncator.decode(decoded, reader) 37 | 38 | decoded 39 | -------------------------------------------------------------------------------- /src/main/scala/clock/EndTimeEstimator.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.clock 2 | 3 | object EndTimeEstimator: 4 | 5 | def encode(vals: Array[Int], startTime: Int): Unit = 6 | val maxIdx = vals.length - 1 7 | if maxIdx < 32 then vals(maxIdx) -= startTime - ((startTime * maxIdx) >>> 5) 8 | 9 | def decode(vals: Array[Int], startTime: Int): Unit = 10 | val maxIdx = vals.length - 1 11 | if maxIdx < 32 then vals(maxIdx) += startTime - ((startTime * maxIdx) >>> 5) 12 | -------------------------------------------------------------------------------- /src/main/scala/clock/LinearEstimator.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.clock 2 | 3 | object LinearEstimator: 4 | 5 | def encode(dest: Array[Int], startTime: Int): Unit = 6 | val maxIdx = dest.length - 1 7 | encode(dest, -1, startTime, maxIdx, dest(maxIdx)) 8 | 9 | def decode(dest: Array[Int], startTime: Int): Unit = 10 | val maxIdx = dest.length - 1 11 | decode(dest, -1, startTime, maxIdx, dest(maxIdx)) 12 | 13 | private def encode(dest: Array[Int], startIdx: Int, start: Int, endIdx: Int, end: Int): Unit = 14 | val midIdx = (startIdx + endIdx) >> 1 15 | if startIdx == midIdx then return 16 | 17 | val mid = dest(midIdx) 18 | dest(midIdx) = mid - ((start + end) >> 1) 19 | 20 | encode(dest, startIdx, start, midIdx, mid) 21 | encode(dest, midIdx, mid, endIdx, end) 22 | 23 | private def decode(dest: Array[Int], startIdx: Int, start: Int, endIdx: Int, end: Int): Unit = 24 | val midIdx = (startIdx + endIdx) >> 1 25 | if startIdx == midIdx then return 26 | 27 | dest(midIdx) += (start + end) >> 1 28 | val mid = dest(midIdx) 29 | 30 | decode(dest, startIdx, start, midIdx, mid) 31 | decode(dest, midIdx, mid, endIdx, end) 32 | -------------------------------------------------------------------------------- /src/main/scala/clock/LowBitTruncator.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.clock 2 | 3 | import org.lichess.compression.BitOps 4 | 5 | object LowBitTruncator: 6 | private val CENTI_CUTOFF = 1000 7 | 8 | def truncate(centis: Array[Int]): Unit = 9 | centis.indices.foreach(i => centis(i) >>= 3) 10 | 11 | def truncate(centi: Int): Int = 12 | centi >> 3 13 | 14 | def writeDigits(centis: Array[Int], writer: BitOps.Writer): Unit = 15 | val maxIdx = centis.length - 1 16 | centis.indices.init.foreach { i => 17 | if centis(i) < CENTI_CUTOFF then writer.writeBits(centis(i), 3) 18 | } 19 | writer.writeBits(centis(maxIdx), 3) 20 | 21 | def decode(trunced: Array[Int], reader: BitOps.Reader): Unit = 22 | val maxIdx = trunced.length - 1 23 | trunced.indices.foreach { i => 24 | val rounded = trunced(i) << 3 25 | trunced(i) = 26 | if rounded < CENTI_CUTOFF || i == maxIdx then rounded | reader.readBits(3) 27 | else rounded | 3 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/game/Bitboard.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | import scala.annotation.tailrec 4 | import scala.collection.mutable.HashSet 5 | 6 | object Bitboard: 7 | val ALL: Long = -1L 8 | 9 | val RANKS: Array[Long] = Array.tabulate(8)(i => 0xffL << (i * 8)) 10 | val FILES: Array[Long] = Array.tabulate(8)(i => 0x0101010101010101L << i) 11 | 12 | private val KNIGHT_DELTAS = Array(17, 15, 10, 6, -17, -15, -10, -6) 13 | private val BISHOP_DELTAS = Array(7, -7, 9, -9) 14 | private val ROOK_DELTAS = Array(1, -1, 8, -8) 15 | private val KING_DELTAS = Array(1, 7, 8, 9, -1, -7, -8, -9) 16 | private val WHITE_PAWN_DELTAS = Array(7, 9) 17 | private val BLACK_PAWN_DELTAS = Array(-7, -9) 18 | 19 | val KNIGHT_ATTACKS: Array[Long] = Array.ofDim(64) 20 | val KING_ATTACKS: Array[Long] = Array.ofDim(64) 21 | val WHITE_PAWN_ATTACKS: Array[Long] = Array.ofDim(64) 22 | val BLACK_PAWN_ATTACKS: Array[Long] = Array.ofDim(64) 23 | 24 | val BETWEEN: Array[Array[Long]] = Array.ofDim(64, 64) 25 | val RAYS: Array[Array[Long]] = Array.ofDim(64, 64) 26 | 27 | private val ATTACKS: Array[Long] = Array.ofDim(88772) 28 | 29 | def slidingAttacks(square: Int, occupied: Long, deltas: Array[Int]): Long = 30 | def attackLoop(deltaIndex: Int, acc: Long): Long = 31 | if deltaIndex >= deltas.length then acc 32 | else 33 | @tailrec 34 | def deltaLoop(sq: Int, tempAcc: Long): Long = 35 | val newSq = sq + deltas(deltaIndex) 36 | if newSq < 0 || newSq >= 64 || Square.distance(newSq, sq) > 2 then 37 | attackLoop(deltaIndex + 1, tempAcc) 38 | else 39 | val newAcc = tempAcc | (1L << newSq) 40 | if (Bitboard.contains(occupied, newSq)) attackLoop(deltaIndex + 1, newAcc) 41 | else deltaLoop(newSq, newAcc) 42 | deltaLoop(square, acc) 43 | attackLoop(0, 0L) 44 | 45 | private def initMagics(square: Int, magic: Magic, shift: Int, deltas: Array[Int]): Unit = 46 | @tailrec 47 | def updateAttacks(subset: Long): Unit = 48 | val attack = slidingAttacks(square, subset, deltas) 49 | val idx = ((magic.factor * subset) >>> (64 - shift)).toInt + magic.offset 50 | assert(ATTACKS(idx) == 0 || ATTACKS(idx) == attack) 51 | ATTACKS(idx) = attack 52 | val nextSubset = (subset - magic.mask) & magic.mask 53 | if (nextSubset != 0) updateAttacks(nextSubset) 54 | 55 | updateAttacks(0L) 56 | 57 | for i <- 0 until 64 58 | do 59 | KNIGHT_ATTACKS(i) = slidingAttacks(i, Bitboard.ALL, KNIGHT_DELTAS) 60 | KING_ATTACKS(i) = slidingAttacks(i, Bitboard.ALL, KING_DELTAS) 61 | WHITE_PAWN_ATTACKS(i) = slidingAttacks(i, Bitboard.ALL, WHITE_PAWN_DELTAS) 62 | BLACK_PAWN_ATTACKS(i) = slidingAttacks(i, Bitboard.ALL, BLACK_PAWN_DELTAS) 63 | 64 | initMagics(i, Magic.ROOK(i), 12, ROOK_DELTAS) 65 | initMagics(i, Magic.BISHOP(i), 9, BISHOP_DELTAS) 66 | 67 | for 68 | a <- 0 until 64 69 | b <- 0 until 64 70 | do 71 | if contains(slidingAttacks(a, 0, ROOK_DELTAS), b) then 72 | BETWEEN(a)(b) = slidingAttacks(a, 1L << b, ROOK_DELTAS) & 73 | slidingAttacks(b, 1L << a, ROOK_DELTAS) 74 | RAYS(a)(b) = (1L << a) | (1L << b) | 75 | slidingAttacks(a, 0, ROOK_DELTAS) & 76 | slidingAttacks(b, 0, ROOK_DELTAS) 77 | else if contains(slidingAttacks(a, 0, BISHOP_DELTAS), b) then 78 | BETWEEN(a)(b) = slidingAttacks(a, 1L << b, BISHOP_DELTAS) & 79 | slidingAttacks(b, 1L << a, BISHOP_DELTAS) 80 | RAYS(a)(b) = (1L << a) | (1L << b) | 81 | slidingAttacks(a, 0, BISHOP_DELTAS) & 82 | slidingAttacks(b, 0, BISHOP_DELTAS) 83 | 84 | def bishopAttacks(square: Int, occupied: Long): Long = 85 | val magic = Magic.BISHOP(square) 86 | ATTACKS(((magic.factor * (occupied & magic.mask)) >>> (64 - 9)).toInt + magic.offset) 87 | 88 | def rookAttacks(square: Int, occupied: Long): Long = 89 | val magic = Magic.ROOK(square) 90 | ATTACKS(((magic.factor * (occupied & magic.mask)) >>> (64 - 12)).toInt + magic.offset) 91 | 92 | def queenAttacks(square: Int, occupied: Long): Long = 93 | bishopAttacks(square, occupied) ^ rookAttacks(square, occupied) 94 | 95 | def pawnAttacks(white: Boolean, square: Int): Long = 96 | if white then WHITE_PAWN_ATTACKS(square) else BLACK_PAWN_ATTACKS(square) 97 | 98 | def lsb(b: Long): Int = 99 | assert(b != 0) 100 | java.lang.Long.numberOfTrailingZeros(b) 101 | 102 | def msb(b: Long): Int = 103 | assert(b != 0) 104 | 63 - java.lang.Long.numberOfLeadingZeros(b) 105 | 106 | def moreThanOne(b: Long): Boolean = 107 | (b & (b - 1L)) != 0 108 | 109 | def contains(b: Long, sq: Int): Boolean = 110 | (b & (1L << sq)) != 0 111 | 112 | def squareSet(b: Long): Set[Int] = 113 | var remaining = b 114 | val set = HashSet[Int]() 115 | while remaining != 0 do 116 | val sq = lsb(remaining) 117 | set.add(sq) 118 | remaining &= remaining - 1L 119 | set.toSet 120 | -------------------------------------------------------------------------------- /src/main/scala/game/Board.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | import scala.collection.mutable.HashMap 4 | import scala.util.control.Breaks.{ break, breakable } 5 | 6 | final class Board( 7 | var pawns: Long = 0xff00000000ff00L, 8 | var knights: Long = 0x4200000000000042L, 9 | var bishops: Long = 0x2400000000000024L, 10 | var rooks: Long = 0x8100000000000081L, 11 | var queens: Long = 0x800000000000008L, 12 | var kings: Long = 0x1000000000000010L, 13 | var white: Long = 0xffffL, 14 | var black: Long = 0xffff000000000000L, 15 | var turn: Boolean = true, 16 | var epSquare: Int = 0, 17 | var castlingRights: Long = 0x8100000000000081L 18 | ): 19 | var occupied = white | black 20 | var incrementalHash: Int = ZobristHash.hashPieces(this) ^ ZobristHash.hashTurn(this) 21 | 22 | def copy(): Board = 23 | val board = 24 | Board(pawns, knights, bishops, rooks, queens, kings, white, black, turn, epSquare, castlingRights) 25 | board.occupied = occupied 26 | board.incrementalHash = incrementalHash 27 | board 28 | 29 | private def isOccupied(square: Int): Boolean = 30 | Bitboard.contains(this.occupied, square) 31 | 32 | private def discard(square: Int): Unit = 33 | if isOccupied(square) then 34 | val mask = 1L << square 35 | val role = roleAt(square) 36 | role match 37 | case Role.PAWN => this.pawns ^= mask 38 | case Role.KNIGHT => this.knights ^= mask 39 | case Role.BISHOP => this.bishops ^= mask 40 | case Role.ROOK => this.rooks ^= mask 41 | case Role.QUEEN => this.queens ^= mask 42 | case Role.KING => this.kings ^= mask 43 | val color = whiteAt(square) 44 | if color then this.white ^= mask else this.black ^= mask 45 | 46 | this.occupied ^= mask 47 | this.incrementalHash ^= ZobristHash.hashPiece(square, color, role) 48 | 49 | private def put(square: Int, color: Boolean, role: Role): Unit = 50 | discard(square) 51 | val mask = 1L << square 52 | role match 53 | case Role.PAWN => this.pawns ^= mask 54 | case Role.KNIGHT => this.knights ^= mask 55 | case Role.BISHOP => this.bishops ^= mask 56 | case Role.ROOK => this.rooks ^= mask 57 | case Role.QUEEN => this.queens ^= mask 58 | case Role.KING => this.kings ^= mask 59 | 60 | if color then this.white ^= mask else this.black ^= mask 61 | 62 | this.occupied ^= mask 63 | this.incrementalHash ^= ZobristHash.hashPiece(square, color, role) 64 | 65 | def roleAt(square: Int): Role = 66 | if Bitboard.contains(this.pawns, square) then Role.PAWN 67 | else if Bitboard.contains(this.knights, square) then Role.KNIGHT 68 | else if Bitboard.contains(this.bishops, square) then Role.BISHOP 69 | else if Bitboard.contains(this.rooks, square) then Role.ROOK 70 | else if Bitboard.contains(this.queens, square) then Role.QUEEN 71 | else if Bitboard.contains(this.kings, square) then Role.KING 72 | else null 73 | 74 | def whiteAt(square: Int): Boolean = 75 | Bitboard.contains(this.white, square) 76 | 77 | def zobristHash(): Int = 78 | this.incrementalHash ^ ZobristHash.hashCastling(this) ^ ZobristHash.hashEnPassant(this) 79 | 80 | def pieceMap(): Map[Int, Piece] = 81 | val map = HashMap[Int, Piece]() 82 | var occupied = this.occupied 83 | while occupied != 0 do 84 | val sq = Bitboard.lsb(occupied) 85 | map.put(sq, Piece(whiteAt(sq), roleAt(sq))) 86 | occupied &= occupied - 1L 87 | map.toMap 88 | 89 | def play(move: Move): Unit = 90 | this.epSquare = 0 91 | move.`type` match 92 | case Move.NORMAL => 93 | if move.role == Role.PAWN && Math.abs(move.from - move.to) == 16 then 94 | val theirPawns = them() & this.pawns 95 | if theirPawns != 0 then 96 | val sq = move.from + (if this.turn then 8 else -8) 97 | if (Bitboard.pawnAttacks(this.turn, sq) & theirPawns) != 0 then this.epSquare = sq 98 | 99 | if this.castlingRights != 0 then 100 | if move.role == Role.KING then this.castlingRights &= Bitboard.RANKS(if this.turn then 7 else 0) 101 | else if move.role == Role.ROOK then this.castlingRights &= ~(1L << move.from) 102 | 103 | if move.capture then this.castlingRights &= ~(1L << move.to) 104 | 105 | discard(move.from) 106 | put(move.to, this.turn, if move.promotion != null then move.promotion else move.role) 107 | 108 | case Move.CASTLING => 109 | this.castlingRights &= Bitboard.RANKS(if this.turn then 7 else 0) 110 | val rookTo = Square.combine(if move.to < move.from then Square.D1 else Square.F1, move.to) 111 | val kingTo = Square.combine(if move.to < move.from then Square.C1 else Square.G1, move.from) 112 | discard(move.from) 113 | discard(move.to) 114 | put(rookTo, this.turn, Role.ROOK) 115 | put(kingTo, this.turn, Role.KING) 116 | 117 | case Move.EN_PASSANT => 118 | discard(Square.combine(move.to, move.from)) 119 | discard(move.from) 120 | put(move.to, this.turn, Role.PAWN) 121 | 122 | this.turn = !this.turn 123 | this.incrementalHash ^= ZobristHash.POLYGLOT(780) 124 | 125 | def us(): Long = 126 | byColor(this.turn) 127 | 128 | def them(): Long = 129 | byColor(!this.turn) 130 | 131 | def byColor(white: Boolean): Long = 132 | if white then this.white else this.black 133 | 134 | private def getKing(white: Boolean): Int = 135 | Bitboard.lsb(this.kings & byColor(white)) 136 | 137 | private def sliderBlockers(king: Int): Long = 138 | val snipers = them() & ( 139 | Bitboard.rookAttacks(king, 0) & (this.rooks ^ this.queens) | 140 | Bitboard.bishopAttacks(king, 0) & (this.bishops ^ this.queens) 141 | ) 142 | 143 | var blockers = 0L 144 | var remainingSnipers = snipers 145 | while remainingSnipers != 0 do 146 | val sniper = Bitboard.lsb(remainingSnipers) 147 | val between = Bitboard.BETWEEN(king)(sniper) & this.occupied 148 | if !Bitboard.moreThanOne(between) then blockers |= between 149 | remainingSnipers &= remainingSnipers - 1L 150 | blockers 151 | 152 | def isCheck(): Boolean = 153 | attacksTo(getKing(this.turn), !this.turn) != 0 154 | 155 | private def attacksTo(sq: Int, attacker: Boolean): Long = 156 | attacksTo(sq, attacker, this.occupied) 157 | 158 | private def attacksTo(sq: Int, attacker: Boolean, occupied: Long): Long = 159 | byColor(attacker) & ( 160 | Bitboard.rookAttacks(sq, occupied) & (this.rooks ^ this.queens) | 161 | Bitboard.bishopAttacks(sq, occupied) & (this.bishops ^ this.queens) | 162 | Bitboard.KNIGHT_ATTACKS(sq) & this.knights | 163 | Bitboard.KING_ATTACKS(sq) & this.kings | 164 | Bitboard.pawnAttacks(!attacker, sq) & this.pawns 165 | ) 166 | 167 | def legalMoves(moves: MoveList): Unit = 168 | moves.clear() 169 | if this.epSquare != 0 then genEnPassant(moves) 170 | 171 | val king = getKing(this.turn) 172 | val checkers = attacksTo(king, !this.turn) 173 | if checkers == 0 then 174 | val target = ~us() 175 | genNonKing(target, moves) 176 | genSafeKing(king, target, moves) 177 | genCastling(king, moves) 178 | else genEvasions(king, checkers, moves) 179 | 180 | val blockers = sliderBlockers(king) 181 | 182 | if blockers != 0 || this.epSquare != 0 then moves.retain(m => isSafe(king, m, blockers)) 183 | 184 | def hasLegalEnPassant(): Boolean = 185 | if this.epSquare == 0 then false 186 | else 187 | val moves = MoveList(2) 188 | genEnPassant(moves) 189 | 190 | val king = getKing(this.turn) 191 | val blockers = sliderBlockers(king) 192 | moves.anyMatch(m => isSafe(king, m, blockers)) 193 | 194 | private def genNonKing(mask: Long, moves: MoveList): Unit = 195 | genPawn(mask, moves) 196 | 197 | var knights = us() & this.knights 198 | while knights != 0 do 199 | val from = Bitboard.lsb(knights) 200 | var targets = Bitboard.KNIGHT_ATTACKS(from) & mask 201 | while targets != 0 do 202 | val to = Bitboard.lsb(targets) 203 | moves.pushNormal(this, Role.KNIGHT, from, isOccupied(to), to) 204 | targets &= targets - 1L 205 | knights &= knights - 1L 206 | 207 | var bishops = us() & this.bishops 208 | while bishops != 0 do 209 | val from = Bitboard.lsb(bishops) 210 | var targets = Bitboard.bishopAttacks(from, this.occupied) & mask 211 | while targets != 0 do 212 | val to = Bitboard.lsb(targets) 213 | moves.pushNormal(this, Role.BISHOP, from, isOccupied(to), to) 214 | targets &= targets - 1L 215 | bishops &= bishops - 1L 216 | 217 | var rooks = us() & this.rooks 218 | while rooks != 0 do 219 | val from = Bitboard.lsb(rooks) 220 | var targets = Bitboard.rookAttacks(from, this.occupied) & mask 221 | while targets != 0 do 222 | val to = Bitboard.lsb(targets) 223 | moves.pushNormal(this, Role.ROOK, from, isOccupied(to), to) 224 | targets &= targets - 1L 225 | rooks &= rooks - 1L 226 | 227 | var queens = us() & this.queens 228 | while queens != 0 do 229 | val from = Bitboard.lsb(queens) 230 | var targets = Bitboard.queenAttacks(from, this.occupied) & mask 231 | while targets != 0 do 232 | val to = Bitboard.lsb(targets) 233 | moves.pushNormal(this, Role.QUEEN, from, isOccupied(to), to) 234 | targets &= targets - 1L 235 | queens &= queens - 1L 236 | 237 | private def genSafeKing(king: Int, mask: Long, moves: MoveList): Unit = 238 | var targets = Bitboard.KING_ATTACKS(king) & mask 239 | while targets != 0 do 240 | val to = Bitboard.lsb(targets) 241 | if attacksTo(to, !this.turn) == 0 then moves.pushNormal(this, Role.KING, king, isOccupied(to), to) 242 | targets &= targets - 1L 243 | 244 | private def genEvasions(king: Int, checkers: Long, moves: MoveList): Unit = 245 | var sliders = checkers & (this.bishops ^ this.rooks ^ this.queens) 246 | var attacked = 0L 247 | while sliders != 0 do 248 | val slider = Bitboard.lsb(sliders) 249 | attacked |= Bitboard.RAYS(king)(slider) ^ (1L << slider) 250 | sliders &= sliders - 1L 251 | 252 | genSafeKing(king, ~us() & ~attacked, moves) 253 | 254 | if checkers != 0 && !Bitboard.moreThanOne(checkers) then 255 | val checker = Bitboard.lsb(checkers) 256 | val target = Bitboard.BETWEEN(king)(checker) | checkers 257 | genNonKing(target, moves) 258 | 259 | private def genPawn(mask: Long, moves: MoveList): Unit = 260 | var capturers = us() & this.pawns 261 | while capturers != 0 do 262 | val from = Bitboard.lsb(capturers) 263 | var targets = Bitboard.pawnAttacks(this.turn, from) & them() & mask 264 | while targets != 0 do 265 | val to = Bitboard.lsb(targets) 266 | addPawnMoves(from, true, to, moves) 267 | targets &= targets - 1L 268 | capturers &= capturers - 1L 269 | 270 | var singleMoves = 271 | ~this.occupied & (if this.turn then (this.white & this.pawns) << 8 else (this.black & this.pawns) >>> 8) 272 | var doubleMoves = 273 | ~this.occupied & (if this.turn then singleMoves << 8 else singleMoves >>> 8) & Bitboard.RANKS( 274 | if this.turn then 3 else 4 275 | ) 276 | 277 | singleMoves &= mask 278 | doubleMoves &= mask 279 | 280 | while singleMoves != 0 do 281 | val to = Bitboard.lsb(singleMoves) 282 | val from = to + (if this.turn then -8 else 8) 283 | addPawnMoves(from, false, to, moves) 284 | singleMoves &= singleMoves - 1L 285 | 286 | while doubleMoves != 0 do 287 | val to = Bitboard.lsb(doubleMoves) 288 | val from = to + (if this.turn then -16 else 16) 289 | moves.pushNormal(this, Role.PAWN, from, false, to) 290 | doubleMoves &= doubleMoves - 1L 291 | 292 | private def addPawnMoves(from: Int, capture: Boolean, to: Int, moves: MoveList): Unit = 293 | if Square.rank(to) == (if this.turn then 7 else 0) then 294 | moves.pushPromotion(this, from, capture, to, Role.QUEEN) 295 | moves.pushPromotion(this, from, capture, to, Role.KNIGHT) 296 | moves.pushPromotion(this, from, capture, to, Role.ROOK) 297 | moves.pushPromotion(this, from, capture, to, Role.BISHOP) 298 | else moves.pushNormal(this, Role.PAWN, from, capture, to) 299 | 300 | private def genEnPassant(moves: MoveList): Unit = 301 | var pawns = us() & this.pawns & Bitboard.pawnAttacks(!this.turn, this.epSquare) 302 | while pawns != 0 do 303 | val pawn = Bitboard.lsb(pawns) 304 | moves.pushEnPassant(this, pawn, this.epSquare) 305 | pawns &= pawns - 1L 306 | 307 | private def genCastling(king: Int, moves: MoveList): Unit = 308 | var rooks = this.castlingRights & Bitboard.RANKS(if this.turn then 0 else 7) 309 | while rooks != 0 do 310 | val rook = Bitboard.lsb(rooks) 311 | val path = Bitboard.BETWEEN(king)(rook) 312 | if (path & this.occupied) == 0 then 313 | val kingTo = Square.combine(if rook < king then Square.C1 else Square.G1, king) 314 | var kingPath = Bitboard.BETWEEN(king)(kingTo) | (1L << kingTo) | (1L << king) 315 | breakable: 316 | while kingPath != 0 do 317 | val sq = Bitboard.lsb(kingPath) 318 | if attacksTo(sq, !this.turn, this.occupied ^ (1L << king)) != 0 then break 319 | kingPath &= kingPath - 1L 320 | if kingPath == 0 then moves.pushCastle(this, king, rook) 321 | rooks &= rooks - 1L 322 | 323 | private def isSafe(king: Int, move: Move, blockers: Long): Boolean = 324 | move.`type` match 325 | case Move.NORMAL => 326 | !Bitboard.contains(us() & blockers, move.from) || Square.aligned(move.from, move.to, king) 327 | case Move.EN_PASSANT => 328 | var occupied = this.occupied 329 | occupied ^= (1L << move.from) 330 | occupied ^= (1L << Square.combine(move.to, move.from)) 331 | occupied |= (1L << move.to) 332 | (Bitboard.rookAttacks(king, occupied) & them() & (this.rooks ^ this.queens)) == 0 && 333 | (Bitboard.bishopAttacks(king, occupied) & them() & (this.bishops ^ this.queens)) == 0 334 | case _ => true 335 | -------------------------------------------------------------------------------- /src/main/scala/game/Encoder.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | import org.lichess.compression.BitOps 4 | 5 | object Encoder: 6 | 7 | private val moveList = new ThreadLocal[MoveList]: 8 | override def initialValue(): MoveList = MoveList() 9 | 10 | private val SAN_RE = 11 | "([NBKRQ])?([a-h])?([1-8])?x?([a-h][1-8])(?:=([NBRQK]))?[\\+#]?".r 12 | 13 | private def charToRole(c: Char): Role = c match 14 | case 'N' => Role.KNIGHT 15 | case 'B' => Role.BISHOP 16 | case 'R' => Role.ROOK 17 | case 'Q' => Role.QUEEN 18 | case 'K' => Role.KING 19 | case _ => throw IllegalArgumentException() 20 | 21 | def encode(pgnMoves: Array[String]): Array[Byte] = 22 | val writer = BitOps.Writer() 23 | val board = Board() 24 | val legals = moveList.get() 25 | 26 | for pgnMove <- pgnMoves do 27 | var role: Role = null 28 | var promotion: Role = null 29 | var from: Long = Bitboard.ALL 30 | var to: Int = 0 31 | 32 | if pgnMove.startsWith("O-O-O") then 33 | role = Role.KING 34 | from = board.kings 35 | to = Bitboard.lsb(board.rooks & Bitboard.RANKS(if (board.turn) 0 else 7)) 36 | else if pgnMove.startsWith("O-O") then 37 | role = Role.KING 38 | from = board.kings 39 | to = Bitboard.msb(board.rooks & Bitboard.RANKS(if (board.turn) 0 else 7)) 40 | else 41 | SAN_RE.findFirstMatchIn(pgnMove) match 42 | case None => 43 | return null 44 | case Some(matcher) => 45 | val roleStr = matcher.group(1) 46 | role = if (roleStr == null) Role.PAWN else charToRole(roleStr.charAt(0)) 47 | if matcher.group(2) != null then from &= Bitboard.FILES(matcher.group(2).charAt(0) - 'a') 48 | if matcher.group(3) != null then from &= Bitboard.RANKS(matcher.group(3).charAt(0) - '1') 49 | 50 | to = Square.square(matcher.group(4).charAt(0) - 'a', matcher.group(4).charAt(1) - '1') 51 | 52 | if matcher.group(5) != null then promotion = charToRole(matcher.group(5).charAt(0)) 53 | 54 | board.legalMoves(legals) 55 | legals.sort() 56 | var foundMatch = false 57 | for i <- 0 until legals.getSize() do 58 | val legal = legals.get(i) 59 | if legal.role == role && legal.to == to && legal.promotion == promotion && Bitboard.contains( 60 | from, 61 | legal.from 62 | ) 63 | then 64 | if foundMatch then return null 65 | Huffman.write(i, writer) 66 | board.play(legal) 67 | foundMatch = true 68 | 69 | if !foundMatch then return null 70 | 71 | writer.toArray() 72 | end encode 73 | 74 | case class DecodeResult( 75 | pgnMoves: Array[String], 76 | board: Board, 77 | halfMoveClock: Int, 78 | positionHashes: Array[Byte], 79 | lastUci: String 80 | ) 81 | 82 | def decode(input: Array[Byte], plies: Int): DecodeResult = 83 | val reader = BitOps.Reader(input) 84 | val output = Array.ofDim[String](plies) 85 | val board = Board() 86 | val legals = moveList.get() 87 | var lastUci: String = null 88 | var lastZeroingPly = -1 89 | var lastIrreversiblePly = -1 90 | val positionHashes = Array.ofDim[Byte](3 * (plies + 1)) 91 | setHash(positionHashes, -1, board.zobristHash()) 92 | 93 | for i <- 0 to plies do 94 | if 0 < i || i < plies then board.legalMoves(legals) 95 | 96 | if 0 < i then if board.isCheck() then output(i - 1) += (if legals.isEmpty then "#" else "+") 97 | 98 | if i < plies then 99 | val moveIndex = Huffman.read(reader) 100 | legals.partialSort(moveIndex + 1) 101 | val move = legals.get(moveIndex) 102 | output(i) = san(move, legals) 103 | board.play(move) 104 | 105 | if move.isZeroing then lastZeroingPly = i 106 | if move.isIrreversible then lastIrreversiblePly = i 107 | setHash(positionHashes, i, board.zobristHash()) 108 | 109 | if i + 1 == plies then lastUci = move.uci() 110 | end for 111 | 112 | DecodeResult( 113 | output, 114 | board, 115 | plies - 1 - lastZeroingPly, 116 | positionHashes.slice(0, 3 * (plies - lastIrreversiblePly)), 117 | lastUci 118 | ) 119 | end decode 120 | 121 | private def san(move: Move, legals: MoveList): String = move.`type` match 122 | case Move.NORMAL | Move.EN_PASSANT => 123 | val builder = StringBuilder(6) 124 | builder.append(move.role.symbol) 125 | 126 | if move.role != Role.PAWN then 127 | var file = false 128 | var rank = false 129 | var others = 0L 130 | 131 | for i <- 0 until legals.getSize() do 132 | val other = legals.get(i) 133 | if other.role == move.role && other.to == move.to && other.from != move.from then 134 | others |= 1L << other.from 135 | 136 | if others != 0 then 137 | if (others & Bitboard.RANKS(Square.rank(move.from))) != 0 then file = true 138 | if (others & Bitboard.FILES(Square.file(move.from))) != 0 then rank = true 139 | else file = true 140 | 141 | if file then builder.append((Square.file(move.from) + 'a').toChar) 142 | if rank then builder.append((Square.rank(move.from) + '1').toChar) 143 | else if move.capture then builder.append((Square.file(move.from) + 'a').toChar) 144 | 145 | if move.capture then builder.append('x') 146 | 147 | builder.append((Square.file(move.to) + 'a').toChar) 148 | builder.append((Square.rank(move.to) + '1').toChar) 149 | if move.promotion != null then builder.append(s"=${move.promotion.symbol}") 150 | 151 | builder.toString() 152 | 153 | case Move.CASTLING => 154 | if move.from < move.to then "O-O" else "O-O-O" 155 | 156 | case _ => "--" 157 | end san 158 | 159 | private def setHash(buffer: Array[Byte], ply: Int, hash: Int): Unit = 160 | val base = buffer.length - 3 * (ply + 1 + 1) 161 | buffer(base) = (hash >>> 16).toByte 162 | buffer(base + 1) = (hash >>> 8).toByte 163 | buffer(base + 2) = hash.toByte 164 | end Encoder 165 | -------------------------------------------------------------------------------- /src/main/scala/game/Huffman.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | import org.lichess.compression.BitOps 4 | 5 | object Huffman: 6 | def write(value: Int, writer: BitOps.Writer): Unit = 7 | val symbol = CODES(value) 8 | writer.writeBits(symbol.code, symbol.bits) 9 | 10 | def read(reader: BitOps.Reader): Int = 11 | def traverse(node: Node): Node = 12 | if node.zero == null && node.one == null then node 13 | else 14 | val bit = reader.readBits(1) 15 | if bit == 0 then traverse(node.zero) 16 | else traverse(node.one) 17 | traverse(ROOT).leaf 18 | 19 | private case class Symbol(code: Int, bits: Int) 20 | 21 | private class Node(val zero: Node, val one: Node, val leaf: Int): 22 | def this(leaf: Int) = this(null, null, leaf) 23 | def this(zero: Node, one: Node) = this(zero, one, -1) 24 | 25 | private def buildTree(code: Int, bits: Int): Node = 26 | assert(bits <= 32) 27 | (0 to 0xff).find(i => CODES(i).code == code && CODES(i).bits == bits) match 28 | case Some(i) => Node(i) 29 | case None => Node(buildTree(code << 1, bits + 1), buildTree((code << 1) | 1, bits + 1)) 30 | 31 | private def b(s: String): Int = Integer.parseInt(s, 2) 32 | 33 | private val CODES = Array[Symbol]( 34 | Symbol(b("00"), 2), // 0: 225883932 (20.71%) 35 | Symbol(b("100"), 3), // 1: 134956126 (12.37%) 36 | Symbol(b("1101"), 4), // 2: 89041269 (8.16%) 37 | Symbol(b("1010"), 4), // 3: 69386238 (6.36%) 38 | Symbol(b("0101"), 4), // 4: 57040790 (5.23%) 39 | Symbol(b("11101"), 5), // 5: 44974559 (4.12%) 40 | Symbol(b("10111"), 5), // 6: 36547155 (3.35%) 41 | Symbol(b("01110"), 5), // 7: 31624920 (2.90%) 42 | Symbol(b("01100"), 5), // 8: 28432772 (2.61%) 43 | Symbol(b("01000"), 5), // 9: 26540493 (2.43%) 44 | Symbol(b("111101"), 6), // 10: 24484873 (2.24%) 45 | Symbol(b("111001"), 6), // 11: 23058034 (2.11%) 46 | Symbol(b("111100"), 6), // 12: 23535272 (2.16%) 47 | Symbol(b("110011"), 6), // 13: 20482457 (1.88%) 48 | Symbol(b("110010"), 6), // 14: 20450172 (1.87%) 49 | Symbol(b("110000"), 6), // 15: 18316057 (1.68%) 50 | Symbol(b("101101"), 6), // 16: 17214833 (1.58%) 51 | Symbol(b("101100"), 6), // 17: 16964761 (1.56%) 52 | Symbol(b("011111"), 6), // 18: 16530028 (1.52%) 53 | Symbol(b("011011"), 6), // 19: 15369510 (1.41%) 54 | Symbol(b("010011"), 6), // 20: 14178440 (1.30%) 55 | Symbol(b("011010"), 6), // 21: 14275714 (1.31%) 56 | Symbol(b("1111111"), 7), // 22: 13353306 (1.22%) 57 | Symbol(b("1111101"), 7), // 23: 12829602 (1.18%) 58 | Symbol(b("1111110"), 7), // 24: 13102592 (1.20%) 59 | Symbol(b("1111100"), 7), // 25: 11932647 (1.09%) 60 | Symbol(b("1110000"), 7), // 26: 10608657 (0.97%) 61 | Symbol(b("1100011"), 7), // 27: 10142459 (0.93%) 62 | Symbol(b("0111101"), 7), // 28: 8294594 (0.76%) 63 | Symbol(b("0100101"), 7), // 29: 7337490 (0.67%) 64 | Symbol(b("0100100"), 7), // 30: 6337744 (0.58%) 65 | Symbol(b("11100010"), 8), // 31: 5380717 (0.49%) 66 | Symbol(b("11000101"), 8), // 32: 4560556 (0.42%) 67 | Symbol(b("01111001"), 8), // 33: 3913313 (0.36%) 68 | Symbol(b("111000111"), 9), // 34: 3038767 (0.28%) 69 | Symbol(b("110001001"), 9), // 35: 2480514 (0.23%) 70 | Symbol(b("011110001"), 9), // 36: 1951026 (0.18%) 71 | Symbol(b("011110000"), 9), // 37: 1521451 (0.14%) 72 | Symbol(b("1110001100"), 10), // 38: 1183252 (0.11%) 73 | Symbol(b("1100010000"), 10), // 39: 938708 (0.09%) 74 | Symbol(b("11100011010"), 11), // 40: 673339 (0.06%) 75 | Symbol(b("11000100010"), 11), // 41: 513153 (0.05%) 76 | Symbol(b("111000110110"), 12), // 42: 377299 (0.03%) 77 | Symbol(b("110001000110"), 12), // 43: 276996 (0.03%) 78 | Symbol(b("1110001101110"), 13), // 44: 199682 (0.02%) 79 | Symbol(b("1100010001110"), 13), // 45: 144602 (0.01%) 80 | Symbol(b("11100011011110"), 14), // 46: 103313 (0.01%) 81 | Symbol(b("11000100011110"), 14), // 47: 73046 (0.01%) 82 | Symbol(b("111000110111110"), 15), // 48: 52339 (0.00%) 83 | Symbol(b("110001000111110"), 15), // 49: 36779 (0.00%) 84 | Symbol(b("1110001101111110"), 16), // 50: 26341 (0.00%) 85 | Symbol(b("1100010001111110"), 16), // 51: 18719 (0.00%) 86 | Symbol(b("11000100011111111"), 17), // 52: 13225 (0.00%) 87 | Symbol(b("111000110111111111"), 18), // 53: 9392 (0.00%) 88 | Symbol(b("111000110111111101"), 18), // 54: 6945 (0.00%) 89 | Symbol(b("110001000111111100"), 18), // 55: 4893 (0.00%) 90 | Symbol(b("1110001101111111100"), 19), // 56: 3698 (0.00%) 91 | Symbol(b("1100010001111111011"), 19), // 57: 2763 (0.00%) 92 | Symbol(b("11100011011111111011"), 20), // 58: 2114 (0.00%) 93 | Symbol(b("11100011011111110010"), 20), // 59: 1631 (0.00%) 94 | Symbol(b("11100011011111110000"), 20), // 60: 1380 (0.00%) 95 | Symbol(b("111000110111111110101"), 21), // 61: 1090 (0.00%) 96 | Symbol(b("111000110111111100110"), 21), // 62: 887 (0.00%) 97 | Symbol(b("111000110111111100010"), 21), // 63: 715 (0.00%) 98 | Symbol(b("110001000111111101001"), 21), // 64: 590 (0.00%) 99 | Symbol(b("110001000111111101000"), 21), // 65: 549 (0.00%) 100 | Symbol(b("1110001101111111101000"), 22), // 66: 477 (0.00%) 101 | Symbol(b("1110001101111111000110"), 22), // 67: 388 (0.00%) 102 | Symbol(b("1100010001111111010111"), 22), // 68: 351 (0.00%) 103 | Symbol(b("1100010001111111010101"), 22), // 69: 319 (0.00%) 104 | Symbol(b("11100011011111111010011"), 23), // 70: 262 (0.00%) 105 | Symbol(b("11100011011111110011110"), 23), // 71: 236 (0.00%) 106 | Symbol(b("11100011011111110001110"), 23), // 72: 200 (0.00%) 107 | Symbol(b("11100011011111110001111"), 23), // 73: 210 (0.00%) 108 | Symbol(b("11000100011111110101100"), 23), // 74: 153 (0.00%) 109 | Symbol(b("111000110111111100111011"), 24), // 75: 117 (0.00%) 110 | Symbol(b("111000110111111110100100"), 24), // 76: 121 (0.00%) 111 | Symbol(b("111000110111111100111111"), 24), // 77: 121 (0.00%) 112 | Symbol(b("111000110111111100111010"), 24), // 78: 115 (0.00%) 113 | Symbol(b("110001000111111101011011"), 24), // 79: 95 (0.00%) 114 | Symbol(b("110001000111111101010011"), 24), // 80: 75 (0.00%) 115 | Symbol(b("110001000111111101010001"), 24), // 81: 67 (0.00%) 116 | Symbol(b("1110001101111111001110011"), 25), // 82: 55 (0.00%) 117 | Symbol(b("1110001101111111001110001"), 25), // 83: 50 (0.00%) 118 | Symbol(b("1110001101111111001110010"), 25), // 84: 55 (0.00%) 119 | Symbol(b("1100010001111111010100101"), 25), // 85: 33 (0.00%) 120 | Symbol(b("1100010001111111010110100"), 25), // 86: 33 (0.00%) 121 | Symbol(b("1100010001111111010100001"), 25), // 87: 30 (0.00%) 122 | Symbol(b("11100011011111110011111011"), 26), // 88: 32 (0.00%) 123 | Symbol(b("11100011011111110011111001"), 26), // 89: 28 (0.00%) 124 | Symbol(b("11100011011111110011111010"), 26), // 90: 29 (0.00%) 125 | Symbol(b("11100011011111110011111000"), 26), // 91: 27 (0.00%) 126 | Symbol(b("11000100011111110101101011"), 26), // 92: 21 (0.00%) 127 | Symbol(b("111000110111111110100101111"), 27), // 93: 15 (0.00%) 128 | Symbol(b("110001000111111101011010100"), 27), // 94: 9 (0.00%) 129 | Symbol(b("110001000111111101011010101"), 27), // 95: 10 (0.00%) 130 | Symbol(b("111000110111111100111000010"), 27), // 96: 12 (0.00%) 131 | Symbol(b("111000110111111100111000011"), 27), // 97: 12 (0.00%) 132 | Symbol(b("110001000111111101010010011"), 27), // 98: 8 (0.00%) 133 | Symbol(b("1110001101111111101001010011"), 28), // 99: 7 (0.00%) 134 | Symbol(b("1100010001111111010100100101"), 28), // 100: 2 (0.00%) 135 | Symbol(b("1110001101111111001110000011"), 28), // 101: 4 (0.00%) 136 | Symbol(b("1110001101111111001110000010"), 28), // 102: 5 (0.00%) 137 | Symbol(b("1110001101111111001110000000"), 28), // 103: 5 (0.00%) 138 | Symbol(b("11100011011111110011100000010"), 29), // 104 139 | Symbol(b("11000100011111110101000001001"), 29), // 105: 5 (0.00%) 140 | Symbol(b("11100011011111110011100000011"), 29), // 106: 1 (0.00%) 141 | Symbol(b("11000100011111110101000001000"), 29), // 107: 1 (0.00%) 142 | Symbol(b("11000100011111110101000000011"), 29), // 108 143 | Symbol(b("110001000111111101010000011110"), 30), // 109: 1 (0.00%) 144 | Symbol(b("111000110111111110100101100110"), 30), // 110: 2 (0.00%) 145 | Symbol(b("111000110111111110100101010111"), 30), // 111: 1 (0.00%) 146 | Symbol(b("110001000111111101010000001101"), 30), // 112: 1 (0.00%) 147 | Symbol(b("111000110111111110100101100010"), 30), // 113 148 | Symbol(b("110001000111111101010000001000"), 30), // 114 149 | Symbol(b("110001000111111101010000000101"), 30), // 115: 1 (0.00%) 150 | Symbol(b("110001000111111101010000000000"), 30), // 116 151 | Symbol(b("110001000111111101010000001010"), 30), // 117 152 | Symbol(b("110001000111111101010010001101"), 30), // 118 153 | Symbol(b("110001000111111101010010010011"), 30), // 119 154 | Symbol(b("110001000111111101010010010010"), 30), // 120 155 | Symbol(b("110001000111111101010010010001"), 30), // 121 156 | Symbol(b("110001000111111101010010010000"), 30), // 122 157 | Symbol(b("110001000111111101010010001011"), 30), // 123 158 | Symbol(b("110001000111111101010010001010"), 30), // 124 159 | Symbol(b("110001000111111101010010001001"), 30), // 125 160 | Symbol(b("110001000111111101010010001000"), 30), // 126 161 | Symbol(b("110001000111111101010010000111"), 30), // 127 162 | Symbol(b("110001000111111101010010000110"), 30), // 128 163 | Symbol(b("110001000111111101010010000011"), 30), // 129 164 | Symbol(b("110001000111111101010010000010"), 30), // 130 165 | Symbol(b("110001000111111101010000011011"), 30), // 131 166 | Symbol(b("110001000111111101010000011010"), 30), // 132 167 | Symbol(b("110001000111111101010000011001"), 30), // 133 168 | Symbol(b("110001000111111101010000011000"), 30), // 134 169 | Symbol(b("110001000111111101010000010101"), 30), // 135 170 | Symbol(b("110001000111111101010000010100"), 30), // 136 171 | Symbol(b("110001000111111101010010000101"), 30), // 137 172 | Symbol(b("110001000111111101010010000100"), 30), // 138 173 | Symbol(b("110001000111111101010000011111"), 30), // 139 174 | Symbol(b("110001000111111101010000011101"), 30), // 140 175 | Symbol(b("110001000111111101010000011100"), 30), // 141 176 | Symbol(b("110001000111111101010010000001"), 30), // 142 177 | Symbol(b("110001000111111101010010000000"), 30), // 143 178 | Symbol(b("110001000111111101010000001111"), 30), // 144 179 | Symbol(b("110001000111111101010000001110"), 30), // 145 180 | Symbol(b("110001000111111101010000001100"), 30), // 146 181 | Symbol(b("110001000111111101010000010111"), 30), // 147 182 | Symbol(b("110001000111111101010000010110"), 30), // 148 183 | Symbol(b("110001000111111101010000001001"), 30), // 149 184 | Symbol(b("110001000111111101010000000100"), 30), // 150 185 | Symbol(b("110001000111111101010000000011"), 30), // 151 186 | Symbol(b("110001000111111101010000000010"), 30), // 152 187 | Symbol(b("110001000111111101010000000001"), 30), // 153 188 | Symbol(b("110001000111111101010000001011"), 30), // 154 189 | Symbol(b("110001000111111101010010001111"), 30), // 155 190 | Symbol(b("110001000111111101010010001110"), 30), // 156 191 | Symbol(b("110001000111111101010010001100"), 30), // 157 192 | Symbol(b("1110001101111111101001010111101"), 31), // 158 193 | Symbol(b("1110001101111111101001010111111"), 31), // 159 194 | Symbol(b("1110001101111111101001010100010"), 31), // 160 195 | Symbol(b("1110001101111111101001011011111"), 31), // 161 196 | Symbol(b("1110001101111111101001010100100"), 31), // 162 197 | Symbol(b("1110001101111111101001010111001"), 31), // 163 198 | Symbol(b("1110001101111111101001011011010"), 31), // 164 199 | Symbol(b("1110001101111111101001011010010"), 31), // 165 200 | Symbol(b("1110001101111111101001011010000"), 31), // 166 201 | Symbol(b("1110001101111111101001010111010"), 31), // 167 202 | Symbol(b("1110001101111111101001010001011"), 31), // 168 203 | Symbol(b("1110001101111111101001010001010"), 31), // 169 204 | Symbol(b("1110001101111111101001010001001"), 31), // 170 205 | Symbol(b("1110001101111111101001010001000"), 31), // 171 206 | Symbol(b("1110001101111111101001010000111"), 31), // 172 207 | Symbol(b("1110001101111111101001010000110"), 31), // 173 208 | Symbol(b("1110001101111111101001010000101"), 31), // 174 209 | Symbol(b("1110001101111111101001010000100"), 31), // 175 210 | Symbol(b("1110001101111111101001011010111"), 31), // 176 211 | Symbol(b("1110001101111111101001011010110"), 31), // 177 212 | Symbol(b("1110001101111111101001011010101"), 31), // 178 213 | Symbol(b("1110001101111111101001011010100"), 31), // 179 214 | Symbol(b("1110001101111111101001010110111"), 31), // 180 215 | Symbol(b("1110001101111111101001010110110"), 31), // 181 216 | Symbol(b("1110001101111111101001010010101"), 31), // 182 217 | Symbol(b("1110001101111111101001010010100"), 31), // 183 218 | Symbol(b("1110001101111111101001010110101"), 31), // 184 219 | Symbol(b("1110001101111111101001010110100"), 31), // 185 220 | Symbol(b("1110001101111111101001010010111"), 31), // 186 221 | Symbol(b("1110001101111111101001010010110"), 31), // 187 222 | Symbol(b("1110001101111111101001010110001"), 31), // 188 223 | Symbol(b("1110001101111111101001010110000"), 31), // 189 224 | Symbol(b("1110001101111111101001010010011"), 31), // 190 225 | Symbol(b("1110001101111111101001010010010"), 31), // 191 226 | Symbol(b("1110001101111111101001011101101"), 31), // 192 227 | Symbol(b("1110001101111111101001011101100"), 31), // 193 228 | Symbol(b("1110001101111111101001011101011"), 31), // 194 229 | Symbol(b("1110001101111111101001011101010"), 31), // 195 230 | Symbol(b("1110001101111111101001011100111"), 31), // 196 231 | Symbol(b("1110001101111111101001011100110"), 31), // 197 232 | Symbol(b("1110001101111111101001010010001"), 31), // 198 233 | Symbol(b("1110001101111111101001010010000"), 31), // 199 234 | Symbol(b("1110001101111111101001011100011"), 31), // 200 235 | Symbol(b("1110001101111111101001011100010"), 31), // 201 236 | Symbol(b("1110001101111111101001011100001"), 31), // 202 237 | Symbol(b("1110001101111111101001011100000"), 31), // 203 238 | Symbol(b("1110001101111111101001011101001"), 31), // 204 239 | Symbol(b("1110001101111111101001011101000"), 31), // 205 240 | Symbol(b("1110001101111111101001010001111"), 31), // 206 241 | Symbol(b("1110001101111111101001010001110"), 31), // 207 242 | Symbol(b("1110001101111111101001010000011"), 31), // 208 243 | Symbol(b("1110001101111111101001010000010"), 31), // 209 244 | Symbol(b("1110001101111111101001010001101"), 31), // 210 245 | Symbol(b("1110001101111111101001010001100"), 31), // 211 246 | Symbol(b("1110001101111111101001011001111"), 31), // 212 247 | Symbol(b("1110001101111111101001011001110"), 31), // 213 248 | Symbol(b("1110001101111111101001010000001"), 31), // 214 249 | Symbol(b("1110001101111111101001010000000"), 31), // 215 250 | Symbol(b("1110001101111111101001011011001"), 31), // 216 251 | Symbol(b("1110001101111111101001011011000"), 31), // 217 252 | Symbol(b("1110001101111111101001011100101"), 31), // 218 253 | Symbol(b("1110001101111111101001011100100"), 31), // 219 254 | Symbol(b("1110001101111111101001010101101"), 31), // 220 255 | Symbol(b("1110001101111111101001010101100"), 31), // 221 256 | Symbol(b("1110001101111111101001010110011"), 31), // 222 257 | Symbol(b("1110001101111111101001010110010"), 31), // 223 258 | Symbol(b("1110001101111111101001010101001"), 31), // 224 259 | Symbol(b("1110001101111111101001010101000"), 31), // 225 260 | Symbol(b("1110001101111111101001011101111"), 31), // 226 261 | Symbol(b("1110001101111111101001011101110"), 31), // 227 262 | Symbol(b("1110001101111111101001011001011"), 31), // 228 263 | Symbol(b("1110001101111111101001011001010"), 31), // 229 264 | Symbol(b("1110001101111111101001011000011"), 31), // 230 265 | Symbol(b("1110001101111111101001011000010"), 31), // 231 266 | Symbol(b("1110001101111111101001010101011"), 31), // 232 267 | Symbol(b("1110001101111111101001010101010"), 31), // 233 268 | Symbol(b("1110001101111111101001011001001"), 31), // 234 269 | Symbol(b("1110001101111111101001011001000"), 31), // 235 270 | Symbol(b("1110001101111111101001011000111"), 31), // 236 271 | Symbol(b("1110001101111111101001011000110"), 31), // 237 272 | Symbol(b("1110001101111111101001011000001"), 31), // 238 273 | Symbol(b("1110001101111111101001011000000"), 31), // 239 274 | Symbol(b("1110001101111111101001010111100"), 31), // 240 275 | Symbol(b("1110001101111111101001010100111"), 31), // 241 276 | Symbol(b("1110001101111111101001010100110"), 31), // 242 277 | Symbol(b("1110001101111111101001010111110"), 31), // 243 278 | Symbol(b("1110001101111111101001010100011"), 31), // 244 279 | Symbol(b("1110001101111111101001010100001"), 31), // 245 280 | Symbol(b("1110001101111111101001010100000"), 31), // 246 281 | Symbol(b("1110001101111111101001011011110"), 31), // 247 282 | Symbol(b("1110001101111111101001010100101"), 31), // 248 283 | Symbol(b("1110001101111111101001011011101"), 31), // 249 284 | Symbol(b("1110001101111111101001011011100"), 31), // 250 285 | Symbol(b("1110001101111111101001010111000"), 31), // 251 286 | Symbol(b("1110001101111111101001011011011"), 31), // 252 287 | Symbol(b("1110001101111111101001011010001"), 31), // 253 288 | Symbol(b("1110001101111111101001011010011"), 31), // 254 289 | Symbol(b("1110001101111111101001010111011"), 31) // 255 290 | ) 291 | private val ROOT = buildTree(0, 0) 292 | -------------------------------------------------------------------------------- /src/main/scala/game/Magic.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | case class Magic(mask: Long, factor: Long, offset: Int) 4 | 5 | object Magic: 6 | val ROOK: Array[Magic] = Array( 7 | Magic(0x000101010101017eL, 0x00280077ffebfffeL, 26304), 8 | Magic(0x000202020202027cL, 0x2004010201097fffL, 35520), 9 | Magic(0x000404040404047aL, 0x0010020010053fffL, 38592), 10 | Magic(0x0008080808080876L, 0x0040040008004002L, 8026), 11 | Magic(0x001010101010106eL, 0x7fd00441ffffd003L, 22196), 12 | Magic(0x002020202020205eL, 0x4020008887dffffeL, 80870), 13 | Magic(0x004040404040403eL, 0x004000888847ffffL, 76747), 14 | Magic(0x008080808080807eL, 0x006800fbff75fffdL, 30400), 15 | Magic(0x0001010101017e00L, 0x000028010113ffffL, 11115), 16 | Magic(0x0002020202027c00L, 0x0020040201fcffffL, 18205), 17 | Magic(0x0004040404047a00L, 0x007fe80042ffffe8L, 53577), 18 | Magic(0x0008080808087600L, 0x00001800217fffe8L, 62724), 19 | Magic(0x0010101010106e00L, 0x00001800073fffe8L, 34282), 20 | Magic(0x0020202020205e00L, 0x00001800e05fffe8L, 29196), 21 | Magic(0x0040404040403e00L, 0x00001800602fffe8L, 23806), 22 | Magic(0x0080808080807e00L, 0x000030002fffffa0L, 49481), 23 | Magic(0x00010101017e0100L, 0x00300018010bffffL, 2410), 24 | Magic(0x00020202027c0200L, 0x0003000c0085fffbL, 36498), 25 | Magic(0x00040404047a0400L, 0x0004000802010008L, 24478), 26 | Magic(0x0008080808760800L, 0x0004002020020004L, 10074), 27 | Magic(0x00101010106e1000L, 0x0001002002002001L, 79315), 28 | Magic(0x00202020205e2000L, 0x0001001000801040L, 51779), 29 | Magic(0x00404040403e4000L, 0x0000004040008001L, 13586), 30 | Magic(0x00808080807e8000L, 0x0000006800cdfff4L, 19323), 31 | Magic(0x000101017e010100L, 0x0040200010080010L, 70612), 32 | Magic(0x000202027c020200L, 0x0000080010040010L, 83652), 33 | Magic(0x000404047a040400L, 0x0004010008020008L, 63110), 34 | Magic(0x0008080876080800L, 0x0000040020200200L, 34496), 35 | Magic(0x001010106e101000L, 0x0002008010100100L, 84966), 36 | Magic(0x002020205e202000L, 0x0000008020010020L, 54341), 37 | Magic(0x004040403e404000L, 0x0000008020200040L, 60421), 38 | Magic(0x008080807e808000L, 0x0000820020004020L, 86402), 39 | Magic(0x0001017e01010100L, 0x00fffd1800300030L, 50245), 40 | Magic(0x0002027c02020200L, 0x007fff7fbfd40020L, 76622), 41 | Magic(0x0004047a04040400L, 0x003fffbd00180018L, 84676), 42 | Magic(0x0008087608080800L, 0x001fffde80180018L, 78757), 43 | Magic(0x0010106e10101000L, 0x000fffe0bfe80018L, 37346), 44 | Magic(0x0020205e20202000L, 0x0001000080202001L, 370), 45 | Magic(0x0040403e40404000L, 0x0003fffbff980180L, 42182), 46 | Magic(0x0080807e80808000L, 0x0001fffdff9000e0L, 45385), 47 | Magic(0x00017e0101010100L, 0x00fffefeebffd800L, 61659), 48 | Magic(0x00027c0202020200L, 0x007ffff7ffc01400L, 12790), 49 | Magic(0x00047a0404040400L, 0x003fffbfe4ffe800L, 16762), 50 | Magic(0x0008760808080800L, 0x001ffff01fc03000L, 0), 51 | Magic(0x00106e1010101000L, 0x000fffe7f8bfe800L, 38380), 52 | Magic(0x00205e2020202000L, 0x0007ffdfdf3ff808L, 11098), 53 | Magic(0x00403e4040404000L, 0x0003fff85fffa804L, 21803), 54 | Magic(0x00807e8080808000L, 0x0001fffd75ffa802L, 39189), 55 | Magic(0x007e010101010100L, 0x00ffffd7ffebffd8L, 58628), 56 | Magic(0x007c020202020200L, 0x007fff75ff7fbfd8L, 44116), 57 | Magic(0x007a040404040400L, 0x003fff863fbf7fd8L, 78357), 58 | Magic(0x0076080808080800L, 0x001fffbfdfd7ffd8L, 44481), 59 | Magic(0x006e101010101000L, 0x000ffff810280028L, 64134), 60 | Magic(0x005e202020202000L, 0x0007ffd7f7feffd8L, 41759), 61 | Magic(0x003e404040404000L, 0x0003fffc0c480048L, 1394), 62 | Magic(0x007e808080808000L, 0x0001ffffafd7ffd8L, 40910), 63 | Magic(0x7e01010101010100L, 0x00ffffe4ffdfa3baL, 66516), 64 | Magic(0x7c02020202020200L, 0x007fffef7ff3d3daL, 3897), 65 | Magic(0x7a04040404040400L, 0x003fffbfdfeff7faL, 3930), 66 | Magic(0x7608080808080800L, 0x001fffeff7fbfc22L, 72934), 67 | Magic(0x6e10101010101000L, 0x0000020408001001L, 72662), 68 | Magic(0x5e20202020202000L, 0x0007fffeffff77fdL, 56325), 69 | Magic(0x3e40404040404000L, 0x0003ffffbf7dfeecL, 66501), 70 | Magic(0x7e80808080808000L, 0x0001ffff9dffa333L, 14826) 71 | ) 72 | 73 | val BISHOP: Array[Magic] = Array( 74 | Magic(0x0040201008040200L, 0x007fbfbfbfbfbfffL, 5378), 75 | Magic(0x0000402010080400L, 0x0000a060401007fcL, 4093), 76 | Magic(0x0000004020100a00L, 0x0001004008020000L, 4314), 77 | Magic(0x0000000040221400L, 0x0000806004000000L, 6587), 78 | Magic(0x0000000002442800L, 0x0000100400000000L, 6491), 79 | Magic(0x0000000204085000L, 0x000021c100b20000L, 6330), 80 | Magic(0x0000020408102000L, 0x0000040041008000L, 5609), 81 | Magic(0x0002040810204000L, 0x00000fb0203fff80L, 22236), 82 | Magic(0x0020100804020000L, 0x0000040100401004L, 6106), 83 | Magic(0x0040201008040000L, 0x0000020080200802L, 5625), 84 | Magic(0x00004020100a0000L, 0x0000004010202000L, 16785), 85 | Magic(0x0000004022140000L, 0x0000008060040000L, 16817), 86 | Magic(0x0000000244280000L, 0x0000004402000000L, 6842), 87 | Magic(0x0000020408500000L, 0x0000000801008000L, 7003), 88 | Magic(0x0002040810200000L, 0x000007efe0bfff80L, 4197), 89 | Magic(0x0004081020400000L, 0x0000000820820020L, 7356), 90 | Magic(0x0010080402000200L, 0x0000400080808080L, 4602), 91 | Magic(0x0020100804000400L, 0x00021f0100400808L, 4538), 92 | Magic(0x004020100a000a00L, 0x00018000c06f3fffL, 29531), 93 | Magic(0x0000402214001400L, 0x0000258200801000L, 45393), 94 | Magic(0x0000024428002800L, 0x0000240080840000L, 12420), 95 | Magic(0x0002040850005000L, 0x000018000c03fff8L, 15763), 96 | Magic(0x0004081020002000L, 0x00000a5840208020L, 5050), 97 | Magic(0x0008102040004000L, 0x0000020008208020L, 4346), 98 | Magic(0x0008040200020400L, 0x0000804000810100L, 6074), 99 | Magic(0x0010080400040800L, 0x0001011900802008L, 7866), 100 | Magic(0x0020100a000a1000L, 0x0000804000810100L, 32139), 101 | Magic(0x0040221400142200L, 0x000100403c0403ffL, 57673), 102 | Magic(0x0002442800284400L, 0x00078402a8802000L, 55365), 103 | Magic(0x0004085000500800L, 0x0000101000804400L, 15818), 104 | Magic(0x0008102000201000L, 0x0000080800104100L, 5562), 105 | Magic(0x0010204000402000L, 0x00004004c0082008L, 6390), 106 | Magic(0x0004020002040800L, 0x0001010120008020L, 7930), 107 | Magic(0x0008040004081000L, 0x000080809a004010L, 13329), 108 | Magic(0x00100a000a102000L, 0x0007fefe08810010L, 7170), 109 | Magic(0x0022140014224000L, 0x0003ff0f833fc080L, 27267), 110 | Magic(0x0044280028440200L, 0x007fe08019003042L, 53787), 111 | Magic(0x0008500050080400L, 0x003fffefea003000L, 5097), 112 | Magic(0x0010200020100800L, 0x0000101010002080L, 6643), 113 | Magic(0x0020400040201000L, 0x0000802005080804L, 6138), 114 | Magic(0x0002000204081000L, 0x0000808080a80040L, 7418), 115 | Magic(0x0004000408102000L, 0x0000104100200040L, 7898), 116 | Magic(0x000a000a10204000L, 0x0003ffdf7f833fc0L, 42012), 117 | Magic(0x0014001422400000L, 0x0000008840450020L, 57350), 118 | Magic(0x0028002844020000L, 0x00007ffc80180030L, 22813), 119 | Magic(0x0050005008040200L, 0x007fffdd80140028L, 56693), 120 | Magic(0x0020002010080400L, 0x00020080200a0004L, 5818), 121 | Magic(0x0040004020100800L, 0x0000101010100020L, 7098), 122 | Magic(0x0000020408102000L, 0x0007ffdfc1805000L, 4451), 123 | Magic(0x0000040810204000L, 0x0003ffefe0c02200L, 4709), 124 | Magic(0x00000a1020400000L, 0x0000000820806000L, 4794), 125 | Magic(0x0000142240000000L, 0x0000000008403000L, 13364), 126 | Magic(0x0000284402000000L, 0x0000000100202000L, 4570), 127 | Magic(0x0000500804020000L, 0x0000004040802000L, 4282), 128 | Magic(0x0000201008040200L, 0x0004010040100400L, 14964), 129 | Magic(0x0000402010080400L, 0x00006020601803f4L, 4026), 130 | Magic(0x0002040810204000L, 0x0003ffdfdfc28048L, 4826), 131 | Magic(0x0004081020400000L, 0x0000000820820020L, 7354), 132 | Magic(0x000a102040000000L, 0x0000000008208060L, 4848), 133 | Magic(0x0014224000000000L, 0x0000000000808020L, 15946), 134 | Magic(0x0028440200000000L, 0x0000000001002020L, 14932), 135 | Magic(0x0050080402000000L, 0x0000000401002008L, 16588), 136 | Magic(0x0020100804020000L, 0x0000004040404040L, 6905), 137 | Magic(0x0040201008040200L, 0x007fff9fdf7ff813L, 16076) 138 | ) 139 | -------------------------------------------------------------------------------- /src/main/scala/game/Move.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | final class Move( 4 | var `type`: Int = 0, 5 | var role: Role = null, 6 | var from: Int = 0, 7 | var capture: Boolean = false, 8 | var to: Int = 0, 9 | var promotion: Role = null, 10 | var score: Int = 0 11 | ): 12 | 13 | def set( 14 | board: Board, 15 | `type`: Int, 16 | role: Role, 17 | from: Int, 18 | capture: Boolean, 19 | to: Int, 20 | promotion: Role 21 | ): Unit = 22 | this.`type` = `type` 23 | this.role = role 24 | this.from = from 25 | this.capture = capture 26 | this.to = to 27 | this.promotion = promotion 28 | val defendingPawns = Bitboard.pawnAttacks(board.turn, to) & board.pawns & board.them() 29 | val moveValue = pieceValue(board, role, to) - pieceValue(board, role, from) 30 | 31 | this.score = (if promotion == null then 0 else promotion.index << 26) + 32 | (if capture then 1 << 25 else 0) + 33 | ((if defendingPawns == 0 then 6 else 5 - role.index) << 22) + 34 | (512 + moveValue << 12) + 35 | (to << 6) + 36 | from 37 | 38 | def uci(): String = 39 | val toSquare = 40 | if `type` == Move.CASTLING then Square.combine(if to < from then Square.C1 else Square.G1, from) else to 41 | val builder = StringBuilder(if this.promotion == null then 4 else 5) 42 | builder.append((Square.file(from) + 'a').toChar) 43 | builder.append((Square.rank(from) + '1').toChar) 44 | builder.append((Square.file(toSquare) + 'a').toChar) 45 | builder.append((Square.rank(toSquare) + '1').toChar) 46 | if this.promotion != null then builder.append(this.promotion.symbol.toLowerCase) 47 | builder.toString 48 | 49 | def isZeroing: Boolean = capture || role == Role.PAWN 50 | 51 | def isIrreversible: Boolean = isZeroing || `type` == Move.CASTLING 52 | 53 | private def pieceValue(board: Board, role: Role, square: Int): Int = 54 | Move.PSQT(role.index)(if board.turn then Square.mirror(square) else square) 55 | 56 | object Move: 57 | val NORMAL = 0 58 | val EN_PASSANT = 1 59 | val CASTLING = 2 60 | 61 | private val PSQT: Array[Array[Int]] = Array( 62 | Array(0, 0, 0, 0, 0, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 10, 10, 20, 30, 30, 20, 10, 10, 5, 5, 10, 63 | 25, 25, 10, 5, 5, 0, 0, 0, 20, 21, 0, 0, 0, 5, -5, -10, 0, 0, -10, -5, 5, 5, 10, 10, -31, -31, 10, 10, 64 | 5, 0, 0, 0, 0, 0, 0, 0, 0), 65 | Array(-50, -40, -30, -30, -30, -30, -40, -50, -40, -20, 0, 0, 0, 0, -20, -40, -30, 0, 10, 15, 15, 10, 0, 66 | -30, -30, 5, 15, 20, 20, 15, 5, -30, -30, 0, 15, 20, 20, 15, 0, -30, -30, 5, 10, 15, 15, 11, 5, -30, 67 | -40, -20, 0, 5, 5, 0, -20, -40, -50, -40, -30, -30, -30, -30, -40, -50), 68 | Array(-20, -10, -10, -10, -10, -10, -10, -20, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, 69 | -10, 5, 5, 10, 10, 5, 5, -10, -10, 0, 10, 10, 10, 10, 0, -10, -10, 10, 10, 10, 10, 10, 10, -10, -10, 5, 70 | 0, 0, 0, 0, 5, -10, -20, -10, -10, -10, -10, -10, -10, -20), 71 | Array(0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 10, 10, 10, 10, 10, 5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 72 | 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 5, 5, 0, 73 | 0, 0), 74 | Array(-20, -10, -10, -5, -5, -10, -10, -20, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -5, 0, 75 | 5, 5, 5, 5, 0, -5, 0, 0, 5, 5, 5, 5, 0, -5, -10, 5, 5, 5, 5, 5, 0, -10, -10, 0, 5, 0, 0, 0, 0, -10, -20, 76 | -10, -10, -5, -5, -10, -10, -20), 77 | Array(-30, -40, -40, -50, -50, -40, -40, -30, -30, -40, -40, -50, -50, -40, -40, -30, -30, -40, -40, -50, 78 | -50, -40, -40, -30, -30, -40, -40, -50, -50, -40, -40, -30, -20, -30, -30, -40, -40, -30, -30, -20, -10, 79 | -20, -20, -20, -20, -20, -20, -10, 20, 20, 0, 0, 0, 0, 20, 20, 0, 30, 10, 0, 0, 10, 30, 0) 80 | ) 81 | -------------------------------------------------------------------------------- /src/main/scala/game/MoveList.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | final class MoveList(capacity: Int = 256): 4 | private val buffer = Array.tabulate(capacity)(_ => Move()) 5 | private val comparator = java.util.Comparator.comparingInt[Move](_.score).reversed() 6 | 7 | private var size = 0 8 | 9 | def clear(): Unit = size = 0 10 | 11 | def getSize(): Int = size 12 | 13 | def get(i: Int): Move = 14 | require(i < size) 15 | buffer(i) 16 | 17 | def isEmpty: Boolean = size == 0 18 | 19 | def pushNormal(board: Board, role: Role, from: Int, capture: Boolean, to: Int): Unit = 20 | buffer(size).set(board, Move.NORMAL, role, from, capture, to, null) 21 | size += 1 22 | 23 | def pushPromotion(board: Board, from: Int, capture: Boolean, to: Int, promotion: Role): Unit = 24 | buffer(size).set(board, Move.NORMAL, Role.PAWN, from, capture, to, promotion) 25 | size += 1 26 | 27 | def pushCastle(board: Board, king: Int, rook: Int): Unit = 28 | buffer(size).set(board, Move.CASTLING, Role.KING, king, false, rook, null) 29 | size += 1 30 | 31 | def pushEnPassant(board: Board, capturer: Int, to: Int): Unit = 32 | buffer(size).set(board, Move.EN_PASSANT, Role.PAWN, capturer, true, to, null) 33 | size += 1 34 | 35 | def sort(): Unit = 36 | java.util.Arrays.sort[Move](buffer, 0, size, comparator) 37 | 38 | def anyMatch(predicate: Move => Boolean): Boolean = buffer.take(size).exists(predicate) 39 | 40 | def retain(predicate: Move => Boolean): Unit = 41 | var i = 0 42 | while i < size do 43 | if !predicate(buffer(i)) then swapRemove(i) 44 | else i += 1 45 | 46 | private def swapRemove(i: Int): Unit = 47 | require(i < size) 48 | size -= 1 49 | swap(i, size) 50 | 51 | private def swap(i: Int, j: Int): Unit = 52 | val tmp = buffer(i) 53 | buffer(i) = buffer(j) 54 | buffer(j) = tmp 55 | 56 | def partialSort(last: Int): Unit = 57 | require(last <= size) 58 | makeHeap(last) 59 | for i <- last until size do 60 | if comparator.compare(buffer(i), buffer(0)) < 0 then 61 | swap(0, i) 62 | adjustHeap(0, last) 63 | sortHeap(last) 64 | 65 | private def makeHeap(last: Int): Unit = 66 | if last >= 2 then 67 | for parent <- (last - 2) / 2 until -1 by -1 do 68 | adjustHeap(parent, last) 69 | 70 | private def adjustHeap(holeIndex: Int, len: Int): Unit = 71 | require(len <= size) 72 | require(holeIndex < size) 73 | var leftChild = holeIndex * 2 + 1 74 | var holeDest = holeIndex 75 | val tmp = buffer(holeDest) 76 | while leftChild < len do 77 | if leftChild + 1 < len then 78 | leftChild = leftChild + (if comparator.compare(buffer(leftChild), buffer(leftChild + 1)) < 0 then 1 else 0) 79 | if comparator.compare(tmp, buffer(leftChild)) < 0 then 80 | buffer(holeDest) = buffer(leftChild) 81 | holeDest = leftChild 82 | leftChild = leftChild * 2 + 1 83 | else 84 | leftChild = len 85 | buffer(holeDest) = tmp 86 | 87 | private def sortHeap(last: Int): Unit = 88 | for i <- last - 1 until 0 by -1 do 89 | swap(0, i) 90 | adjustHeap(0, i) 91 | 92 | def pretty(): String = 93 | val builder = StringBuilder() 94 | for i <- 0 until size do 95 | val m = buffer(i) 96 | builder.append(s"${m.uci()} ") 97 | // builder.append(s"${m.role} ${m.from} ${m.to} ${m.capture} ${m.promotion.getOrElse("")}") 98 | builder.toString() 99 | -------------------------------------------------------------------------------- /src/main/scala/game/ZobristHash.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | object ZobristHash: 4 | def hashBoard(board: Board): Int = 5 | hashPieces(board) ^ hashCastling(board) ^ hashTurn(board) ^ hashEnPassant(board) 6 | 7 | def hashPieces(board: Board): Int = 8 | var hash = 0 9 | var occupied = board.occupied 10 | while (occupied != 0) 11 | val sq = Bitboard.lsb(occupied) 12 | hash ^= hashPiece(sq, board.whiteAt(sq), board.roleAt(sq)) 13 | occupied &= occupied - 1L 14 | hash 15 | 16 | def hashPiece(square: Int, color: Boolean, role: Role): Int = 17 | val index = role.index * 2 + (if (color) 1 else 0) 18 | POLYGLOT(64 * index + square) 19 | 20 | def hashCastling(board: Board): Int = 21 | var hash = 0 22 | val cr = board.castlingRights 23 | if (Bitboard.contains(cr, Square.H1)) hash ^= POLYGLOT(768) 24 | if (Bitboard.contains(cr, Square.A1)) hash ^= POLYGLOT(768 + 1) 25 | if (Bitboard.contains(cr, Square.H8)) hash ^= POLYGLOT(768 + 2) 26 | if (Bitboard.contains(cr, Square.A8)) hash ^= POLYGLOT(768 + 3) 27 | hash 28 | 29 | def hashTurn(board: Board): Int = 30 | if (board.turn) POLYGLOT(780) else 0 31 | 32 | def hashEnPassant(board: Board): Int = 33 | if (board.hasLegalEnPassant()) POLYGLOT(772 + Square.file(board.epSquare)) else 0 34 | 35 | val POLYGLOT: Array[Int] = Array( 36 | 0x9d3924, 0x2af739, 0x44db01, 0x9c15f7, 0x758344, 0x3290ac, 0x0fbbad, 0xe83a90, 0x0d7e76, 0x1a0838, 37 | 0x9605d5, 0xd021ff, 0x40bdf1, 0x011355, 0x5db483, 0x239f8b, 0x05d1a1, 0x679f84, 0x7449bb, 0x7d11cd, 38 | 0x82c770, 0xf3218f, 0x331478, 0x4bb38d, 0xaa649c, 0x8dbd98, 0x87d207, 0x19f3c7, 0xb4ab30, 0x7b0500, 39 | 0xc9452c, 0x24aa6c, 0x4c9f34, 0x14a68f, 0xa71b9b, 0x03488b, 0x637b2b, 0x09d1bc, 0x357566, 0x735e2b, 40 | 0x187270, 0x1fcbac, 0xd310a7, 0xbf983f, 0x9f74d1, 0x51ebdc, 0x5c82c5, 0xfcf7fe, 0x3253a7, 0x8c74c3, 41 | 0xb9bc6c, 0x7ef48f, 0x11d505, 0x6568fc, 0x4de0b0, 0x96d693, 0x42e240, 0x6d2bdc, 0x42880b, 0x5f0f4a, 42 | 0x39f890, 0x93c5b5, 0x63dc35, 0xec16ca, 0x5355f9, 0x07fb9f, 0x509341, 0x7bcbc3, 0x19fc8a, 0x637a77, 43 | 0x8249a4, 0x79ad69, 0x14acba, 0xf145b6, 0xdabf2a, 0x24c3c9, 0xbb6e29, 0x0ce26c, 0xa49cd1, 0xe99d66, 44 | 0x27e6ad, 0x8535f0, 0x54b3f4, 0x72b12c, 0xee954d, 0x9a85ac, 0x70ac4c, 0xf9b89d, 0x87b3e2, 0xa366e5, 45 | 0xae4a93, 0x1920c0, 0x87bf02, 0x092237, 0xff07f6, 0x8de8dc, 0x9c1633, 0xb3f22c, 0x390e5f, 0x5bfea5, 46 | 0x1e1032, 0x9a74ac, 0x4f80f7, 0x6304d0, 0x2171e6, 0x5b9b63, 0x506aac, 0x1881af, 0x650308, 0xdfd395, 47 | 0xef927d, 0x7b32f7, 0xb9fd76, 0x05a7e8, 0xb5889c, 0x4a750a, 0xcf464c, 0xf53863, 0x3c79a0, 0xede6c8, 48 | 0x799e81, 0x86536b, 0x97d737, 0xa24663, 0x043fca, 0x920e44, 0x70eb09, 0x73a192, 0x56436c, 0xefac4b, 49 | 0xbb2157, 0x45f200, 0x930f80, 0xff6712, 0xae623f, 0xdd2c5b, 0x7eed12, 0x22fe54, 0xc91800, 0x808bd6, 50 | 0xdec468, 0x1bede3, 0x435396, 0xaa969b, 0xa87832, 0x65942c, 0xded2d6, 0x21f085, 0xb41593, 0x91b859, 51 | 0x10cff3, 0x28aed1, 0xc5cc1d, 0x5648f6, 0x2d2550, 0x9bc5a3, 0xef2f05, 0xaf2042, 0x480412, 0xaef3af, 52 | 0x19afe5, 0x525938, 0xf4f076, 0x113796, 0xbce5d2, 0x9da424, 0x066f70, 0x4dc4de, 0x51039a, 0xc07a3f, 53 | 0xb46ee9, 0xb3819a, 0x21a007, 0x2df16f, 0x763c4a, 0xf793c4, 0xd7288e, 0xde336a, 0x0bf692, 0x2c604a, 54 | 0x4850e7, 0xcfc447, 0xb05ca3, 0x9ae182, 0xa4fc4b, 0xe75517, 0x69b97d, 0xf9b5b7, 0xfc6a82, 0x9c684c, 55 | 0x8ec97d, 0x6703df, 0xc547f5, 0x78e376, 0xfe9a44, 0x08bd35, 0x9315e5, 0x94061b, 0xdf1d9f, 0x3bba57, 56 | 0xd2b7ad, 0xf7a255, 0xd7f4f2, 0xd95be8, 0x336f52, 0xa74049, 0xa2f61b, 0x4f2a5c, 0x87d380, 0x16b9f7, 57 | 0x7ba248, 0xf3a678, 0x39b0bf, 0xfcaf55, 0x18fcf6, 0x4c0563, 0x40e087, 0x8cffa9, 0x68ca39, 0x7a1ee9, 58 | 0x9d1d60, 0x3810e3, 0x32095b, 0x35cab6, 0xa90b24, 0x77a225, 0x513e5e, 0x4361c0, 0xd941ac, 0x528f7c, 59 | 0x52ab92, 0x9d1dfa, 0x722ff1, 0x1d1260, 0x7a249a, 0x04208f, 0x5a110c, 0x0cd9a4, 0x56fd23, 0x284c84, 60 | 0x04feab, 0x742e1e, 0x9a9632, 0x881b82, 0x506e67, 0xb0183d, 0x0ed9b9, 0x5e11e8, 0xf67864, 0x1b85d4, 61 | 0xdab9fe, 0x0d151d, 0xa865a5, 0x93c425, 0x99e7af, 0x48cbff, 0x7f9b6a, 0x58627e, 0x2cd16e, 0xd363ef, 62 | 0x0ce2a3, 0x1a804a, 0x907f30, 0x501f65, 0x37624a, 0x957baf, 0x3a6c27, 0xd49503, 0x088e04, 0xf943ae, 63 | 0x6c3b8e, 0x364f6f, 0xd60f6d, 0x56963b, 0x16f50e, 0xef1955, 0x565601, 0xecb539, 0xbac7a9, 0xb344c4, 64 | 0x65d349, 0xb4b81b, 0xb42206, 0x071582, 0x7a13f1, 0xbc4097, 0x59b978, 0x99170a, 0x6f4233, 0x325928, 65 | 0xd0e436, 0x565c31, 0x30f561, 0xd873db, 0x7bd94e, 0xc7d9f1, 0x947ae0, 0xc8c938, 0x3a9bf5, 0xd9a11f, 66 | 0x0fd220, 0xb3f256, 0xb03031, 0x35dd37, 0xe9f608, 0xebfafa, 0x9255ab, 0xb9ab4c, 0x693501, 0xc62c58, 67 | 0xcd454f, 0xbbe83f, 0xdc842b, 0xba8914, 0xa3bc94, 0xe9f676, 0x09c7e5, 0x852f54, 0x8107fc, 0x098954, 68 | 0x23b70e, 0xc330de, 0x4715ed, 0xa8d7e4, 0x0572b9, 0xb57d2e, 0xe8d9ec, 0x2fe4b1, 0x11317b, 0x7fbf21, 69 | 0x1725ca, 0x964e91, 0x3e2b8b, 0xbe7444, 0xf85b2b, 0x49353f, 0x1dd01a, 0x1fca8a, 0xfc7c95, 0x18a6a9, 70 | 0xcccb70, 0x3bdbb9, 0xaa70b5, 0xe94c39, 0xb7a0b1, 0xd4dba8, 0x2e18bc, 0x2de096, 0xb9c11d, 0x64972d, 71 | 0x94628d, 0xdbc0d2, 0xd2733c, 0x7e75d9, 0x6ced19, 0x97fcaa, 0x7b7749, 0x8547ed, 0x79999c, 0xcffe19, 72 | 0x829626, 0x92fae2, 0x63e22c, 0xc678b6, 0x587388, 0x0981dc, 0x9f6578, 0x9ff38f, 0xe479ee, 0xe7f28e, 73 | 0x56c074, 0x5544f7, 0x7b3f01, 0x121536, 0x7f5126, 0x7a7695, 0x3d5774, 0x8a1b08, 0x7b4a38, 0x950113, 74 | 0x4da897, 0x3bc36e, 0x5d0a12, 0x7f9d1a, 0xda3a36, 0xdcdd7d, 0x368333, 0xce6834, 0xab9090, 0x43954b, 75 | 0xb438c2, 0x10dcd7, 0xdbc27a, 0x9b3cdb, 0xb67b78, 0xbfced1, 0xa9119b, 0x1fff7a, 0xac12fb, 0xaf08da, 76 | 0x1b0cab, 0xb559eb, 0xc37b45, 0xc3a9dc, 0xf3b8b6, 0x9fc477, 0x67378d, 0x6dd856, 0xa319ce, 0x073973, 77 | 0x8a8e84, 0xe1925c, 0x74c04b, 0x4dda48, 0x9d266d, 0x7440fb, 0x133285, 0xd6bf7b, 0x4838d6, 0x1e1523, 78 | 0x8f8419, 0x72c883, 0xd7a023, 0x94ebc8, 0x9fc10d, 0xde68a2, 0xa44cfe, 0x9d1d84, 0x51d2b1, 0x2fd7e4, 79 | 0x65ca5b, 0xdd69a0, 0x604d51, 0x73aa8a, 0x1a8c1e, 0xaac40a, 0x764dbe, 0x1e99b9, 0x2c5e9d, 0x3a938f, 80 | 0x26e6db, 0x469356, 0xc8763c, 0x3f6c6a, 0x7f7cc3, 0x9bfb22, 0x89039d, 0x8fe88b, 0xa09e8c, 0xfa7e39, 81 | 0xd6b6d0, 0xdfea21, 0xb67c1f, 0xca1e37, 0x1cfc8b, 0xd18d85, 0x4ed0fe, 0xe4dbf0, 0x1761f9, 0x53898e, 82 | 0x734de8, 0x2680b1, 0x298af2, 0x7983ee, 0x66c1a2, 0x9e17e4, 0xedb454, 0x50b704, 0x4cc317, 0x66b483, 83 | 0x219b97, 0x261e4e, 0x1fe2cc, 0xd7504d, 0xb9571f, 0x1ddc03, 0xcf3f46, 0xf4f5d0, 0x38b652, 0x36f60e, 84 | 0xeb3593, 0x9c4cd6, 0xaf0c31, 0x258e5a, 0x8b889d, 0xf4d145, 0xd4347f, 0xe699ed, 0x2472f6, 0xc2a1e7, 85 | 0xab4f64, 0x637675, 0xa59e0b, 0x116d00, 0x2cf9c8, 0x0b090a, 0xabeedd, 0x58efc1, 0xc6e57a, 0x2eab8c, 86 | 0x14a195, 0x7c0828, 0xd74bbe, 0x804456, 0xebe9ea, 0x03219a, 0x49787f, 0xa1e930, 0x5b45e5, 0xb49c3b, 87 | 0xd4490a, 0x12a8f2, 0x001f83, 0x1877b5, 0xa2853b, 0x993e1d, 0xb35980, 0x252f59, 0xd23c8e, 0x1bda04, 88 | 0x21e0bd, 0x3b097a, 0x8d14de, 0xf95cff, 0x387170, 0xca672b, 0x64c8e5, 0x241260, 0x106c09, 0x7fba19, 89 | 0x7884d9, 0x0647df, 0x63573f, 0x4fc8e9, 0x1db956, 0xb8d912, 0xa2ebee, 0xd9f1f3, 0xefed53, 0x2e6d02, 90 | 0xa9aa4d, 0xb64be8, 0x70cb6a, 0x98f076, 0xbf8447, 0x94c325, 0x3e003e, 0xb925a6, 0x61bdd1, 0xbf8d51, 91 | 0x240ab5, 0xfc8761, 0xef02cd, 0xa1082c, 0x8215e5, 0xd39bb9, 0x273825, 0x61cf4f, 0x1b6bac, 0x758f45, 92 | 0x959f58, 0xb063e9, 0x60e8ed, 0x7b6497, 0xfd080d, 0x8c90fd, 0x106f72, 0x797603, 0xa4ec01, 0x733ea7, 93 | 0xb4d8f7, 0x9e21f4, 0x9d765e, 0xd30c08, 0x5d9433, 0x1a4e48, 0x6ffe73, 0xddf957, 0x64d0e2, 0x08dd9b, 94 | 0x087e79, 0xe328e2, 0x1c2559, 0x720bf5, 0xb0774d, 0x443f64, 0x4112cf, 0xd813f2, 0x660d32, 0x59ac2c, 95 | 0xe84696, 0x93b633, 0xc0c0f5, 0xcaf21e, 0x572777, 0x506c11, 0xd83cc2, 0x4a29c6, 0xed2df2, 0xb5635c, 96 | 0x22af00, 0x52e762, 0x9aeba3, 0x944f6d, 0x6c47be, 0x6ad047, 0xa5b1cf, 0x7c45d8, 0x5092ef, 0x9338e6, 97 | 0x455a4b, 0x6b02e6, 0x6b17b2, 0xd1e0cc, 0xde0c89, 0x50065e, 0x9c1169, 0x78edef, 0x6dc93d, 0xee97f4, 98 | 0x32ab0e, 0x3a6853, 0x31865c, 0x67fef9, 0x1f2b1d, 0xb69e38, 0xaa9119, 0xf43c73, 0xfb4a3d, 0x3550c2, 99 | 0x371f77, 0x6bfa9a, 0xcd04f3, 0xe32735, 0x9f9150, 0x049a7f, 0xfcb6be, 0x08de8a, 0x8f9887, 0xb5b407, 100 | 0x230e34, 0x43ed7f, 0x3a88a0, 0x21874b, 0x1bdea1, 0x53c065, 0xe34a1d, 0xd6b04d, 0x5e9027, 0x2c046f, 101 | 0xb10bb4, 0x3fa9dd, 0x0e09b8, 0x10e8b3, 0x9eedec, 0xd4c718, 0x81536d, 0x91b534, 0xec8177, 0x190e71, 102 | 0xb592bf, 0x89c350, 0xac042e, 0xb49b52, 0xfb152f, 0x3e666e, 0x3b544e, 0xe805a1, 0x24b33c, 0xe74733, 103 | 0x0a804d, 0x57e330, 0x4ae7d6, 0x2d8d54, 0xd1e649, 0x8a328a, 0x07a3ae, 0x84547d, 0x990a98, 0x1a4ff1, 104 | 0xf6f7fd, 0x30c05b, 0x8d2636, 0x46c9fe, 0xccec0a, 0x4e9d28, 0x19ebb0, 0x4659d2, 0x963ef2, 0x74f851, 105 | 0x5a0f54, 0x037270, 0xc7f6aa, 0x352787, 0x9853ea, 0xabbdcd, 0xcf05da, 0x49cad4, 0x7a4c10, 0xd9e92a, 106 | 0x13ae97, 0x730499, 0x4e4b70, 0xff5772, 0x55b634, 0xb86222, 0xcac09a, 0xdaf8e9, 0xb5fdfc, 0x310cb3, 107 | 0xe87fbb, 0x2102ae, 0xf8549e, 0x07a69a, 0xc4c118, 0xf9f489, 0x1af3db, 0xf5b4b0, 0x962ace, 0x046e3e, 108 | 0xf05d12, 0x964781, 0x9c2ed4, 0x522e23, 0x177e00, 0x2bc60a, 0x222bbf, 0x486289, 0x7dc778, 0x8af387, 109 | 0x1fab64, 0xe4d942, 0x9da058, 0x24c0e3, 0x233003, 0xd586bd, 0x5e5637, 0x7eba72, 0x0a56a5, 0xd79476, 110 | 0x9e4c12, 0x17efee, 0x1d95b0, 0x93cbe0, 0x65fa4f, 0xd5f9e8, 0xc2b5a0, 0x593002, 0xce2f86, 0x7ca972, 111 | 0x278533, 0xc61bb3, 0x150f36, 0x9f6a41, 0x64a53d, 0x142de4, 0x0c3352, 0x0a9c32, 0xe6c421, 0x71f1ce, 112 | 0xf1bcc3, 0xe728e8, 0x96fbf8, 0x81a154, 0x5fa786, 0x56986e, 0x917f1d, 0xd20d8c, 0x31d71d, 0xf165b5, 113 | 0xa57e63, 0x1ef6e6, 0x70cc73, 0xe21a6b, 0x003a93, 0x1c99de, 0xcf3145, 0xd0e442, 0x77c621, 0x67a34d, 114 | 0xf8d626 115 | ) 116 | end ZobristHash 117 | -------------------------------------------------------------------------------- /src/main/scala/game/model.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | case class Piece(white: Boolean, role: Role) 4 | 5 | enum Role(val index: Int, val symbol: String): 6 | case PAWN extends Role(0, "") 7 | case KNIGHT extends Role(1, "N") 8 | case BISHOP extends Role(2, "B") 9 | case ROOK extends Role(3, "R") 10 | case QUEEN extends Role(4, "Q") 11 | case KING extends Role(5, "K") 12 | 13 | object Square: 14 | val A1 = 0 15 | val C1 = 2 16 | val D1 = 3 17 | val F1 = 5 18 | val G1 = 6 19 | val H1 = 7 20 | val A8 = 56 21 | val H8 = 63 22 | 23 | def square(file: Int, rank: Int): Int = file ^ (rank << 3) 24 | 25 | def file(square: Int): Int = square & 7 26 | 27 | def rank(square: Int): Int = square >>> 3 28 | 29 | def mirror(square: Int): Int = square ^ 0x38 30 | 31 | def combine(a: Int, b: Int): Int = square(file(a), rank(b)) 32 | 33 | def distance(a: Int, b: Int): Int = Math.max(Math.abs(file(a) - file(b)), Math.abs(rank(a) - rank(b))) 34 | 35 | def aligned(a: Int, b: Int, c: Int): Boolean = Bitboard.contains(Bitboard.RAYS(a)(b), c) 36 | -------------------------------------------------------------------------------- /src/test/scala/ClockHistoryTest.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.clock 2 | 3 | import org.specs2.mutable.* 4 | 5 | case class Centis(centis: Int) extends AnyVal: 6 | def *(f: Int) = Centis(centis * f) 7 | def -(c: Int) = Centis(centis - c) 8 | case class ByteArray(value: Array[Byte]) extends AnyVal: 9 | def isEmpty = value.isEmpty 10 | 11 | object clockHistory: 12 | 13 | def writeSide(start: Centis, times: Vector[Centis], flagged: Boolean) = 14 | val timesToWrite = if (flagged) times.dropRight(1) else times 15 | ByteArray(Encoder.encode(timesToWrite.iterator.map(_.centis).to(Array), start.centis)) 16 | 17 | def readSide(start: Centis, ba: ByteArray, flagged: Boolean) = 18 | val decoded: Vector[Centis] = 19 | Encoder.decode(ba.value, start.centis) 20 | .iterator.map(Centis.apply).to(Vector) 21 | if (flagged) decoded :+ Centis(0) else decoded 22 | 23 | class BinaryClockHistoryTest extends Specification: 24 | 25 | val hour = Centis(60 * 60 * 100) 26 | val day = hour * 24 27 | 28 | def beLike(comp: Vector[Centis]) = (acts: Vector[Centis]) => { 29 | acts.size must_== comp.size 30 | (comp zip acts) forall: 31 | case (c, a) => a.centis must beCloseTo(c.centis +/- 4) 32 | } 33 | 34 | "binary clock history" should: 35 | 36 | "handle empty vectors" in: 37 | clockHistory.writeSide(Centis(720000), Vector.empty, false).isEmpty must beTrue 38 | 39 | "handle singleton vectors" in: 40 | val times = Vector(Centis(1234)) 41 | val bytes = clockHistory.writeSide(Centis(12345), times, false) 42 | val restored = clockHistory.readSide(Centis(12345), bytes, false) 43 | 44 | restored must beLike(times) 45 | 46 | "restorable" in: 47 | val times = Vector( 48 | 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 49 | 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 199, 333, 567, 666, 2000, 30 50 | ).map(t => Centis(21000 - 10 * t)) 51 | val bytes = clockHistory.writeSide(hour * 2, times, false) 52 | val restored = clockHistory.readSide(hour * 2, bytes, false) 53 | restored must beLike(times) 54 | 55 | "restore correspondence" in: 56 | val times = Vector(118, 204, 80, 191, 75, 230, 48, 258).map(t => day * 2 - t) 57 | val bytes = clockHistory.writeSide(day * 2, times, false) 58 | val restored = clockHistory.readSide(day * 2, bytes, false) 59 | restored must beLike(times) 60 | 61 | "not drift" in: 62 | val times = Vector(5009, 4321, 2999, 321, 3044, 21, 2055, 77).map(Centis.apply) 63 | var restored = Vector.empty[Centis] 64 | val start = Centis(6000) 65 | for (end <- times) 66 | val binary = clockHistory.writeSide(start, restored :+ end, false) 67 | restored = clockHistory.readSide(start, binary, false) 68 | restored must beLike(times) 69 | -------------------------------------------------------------------------------- /src/test/scala/HuffmanPgnTest.scala: -------------------------------------------------------------------------------- 1 | package org.lichess.compression.game 2 | 3 | import org.specs2.mutable.* 4 | 5 | class HuffmanPgnTest extends Specification: 6 | 7 | def hexToBytes(str: String) = 8 | str.grouped(2).map(cc => Integer.parseInt(cc, 16).toByte).toArray 9 | 10 | def base64ToBytes(str: String): Array[Byte] = 11 | java.util.Base64.getDecoder.decode(str) 12 | 13 | "game compression" should: 14 | "compress and decompress" in: 15 | forall(fixtures) { pgn => 16 | // val pgn = "e4" 17 | val pgnMoves = pgn.split(" ") 18 | val encoded = Encoder.encode(pgnMoves) 19 | val decoded = Encoder.decode(encoded, pgnMoves.size) 20 | pgnMoves must_== decoded.pgnMoves 21 | } 22 | 23 | "stable format" in: 24 | forall(v1 zip fixtures) { case (encoded, pgn) => 25 | val pgnMoves = pgn.split(" ") 26 | val decoded = Encoder.decode(base64ToBytes(encoded), pgnMoves.size) 27 | pgnMoves must_== decoded.pgnMoves 28 | } 29 | 30 | "least surprise" in: 31 | val n = 22 32 | val decoded = Encoder.decode(Array.fill(n)(0.toByte), n) 33 | decoded.pgnMoves.mkString( 34 | " " 35 | ) must_== "e4 e5 Nf3 Nf6 Nxe5 Nxe4 Nxf7 Kxf7 d4 Nxf2 Kxf2 d5 Nc3 Nc6 Nxd5 Qxd5 Kg1 Nxd4 Qxd4 Qxd4+ Be3 Qxe3#" 36 | 37 | "unmoved rooks" in: 38 | import scala.jdk.CollectionConverters.* 39 | val pgnMoves = "d4 h5 c4 Rh6 Nf3 Rh8".split(" ") 40 | val encoded = Encoder.encode(pgnMoves) 41 | 42 | val d1 = Encoder.decode(encoded, 0) 43 | Bitboard.squareSet(d1.board.castlingRights) must_== Set(0, 7, 56, 63) 44 | 45 | val d2 = Encoder.decode(encoded, pgnMoves.size) 46 | Bitboard.squareSet(d2.board.castlingRights) must_== Set(0, 7, 56) 47 | 48 | "half-move clock" in: 49 | val pgnMoves = 50 | "e4 e5 Nf3 Nc6 Nc3 Nf6 Bb5 d6 O-O Be7 d4 exd4 Nxd4 Bd7 Bg5 O-O Nxc6 bxc6 Bd3 h6 Bh4 Ne8 Bxe7 Qxe7 Qf3 Nf6 Rfe1 Rfe8" 51 | .split(" ") 52 | val encoded = Encoder.encode(pgnMoves) 53 | val halfMoveClocks = 54 | List(0, 0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 0, 0, 0, 1, 2, 3, 0, 0, 1, 0, 1, 2, 0, 0, 1, 2, 3, 4) 55 | (0 to pgnMoves.size).map(Encoder.decode(encoded, _).halfMoveClock) must_== halfMoveClocks 56 | 57 | "last uci" in: 58 | val pgnMoves = 59 | "e4 e5 Nf3 Nc6 Bc4 Nf6 d4 exd4 O-O Bc5 e5 d5 exf6 dxc4 Re1+ Be6 Ng5 Qxf6 Nxe6 Qxe6".split(" ") 60 | val encoded = Encoder.encode(pgnMoves) 61 | 62 | val empty = Encoder.decode(encoded, 0) 63 | Option(empty.lastUci) must_== None 64 | 65 | val decoded = Encoder.decode(encoded, pgnMoves.size) 66 | Option(decoded.lastUci) must_== Some("f6e6") 67 | 68 | "position hash 1. e4 d5 2. e5 f5 3. Ke2 Kf7" in: 69 | val pgnMoves = "e4 d5 e5 f5 Ke2 Kf7".split(" ") 70 | val encoded = Encoder.encode(pgnMoves) 71 | 72 | // initial position 73 | val d0 = Encoder.decode(encoded, 0) 74 | d0.positionHashes must_== hexToBytes("463b96") 75 | 76 | // 1. e4 77 | val d1 = Encoder.decode(encoded, 1) 78 | d1.positionHashes must_== hexToBytes("823c9b") 79 | 80 | // 1. e4 d5 81 | val d2 = Encoder.decode(encoded, 2) 82 | d2.positionHashes must_== hexToBytes("0756b9") 83 | 84 | // 1. e4 d5 2. e5 85 | val d3 = Encoder.decode(encoded, 3) 86 | d3.positionHashes must_== hexToBytes("662faf") 87 | 88 | // 1. e4 d5 2. e5 f5 (en passant matters) 89 | val d4 = Encoder.decode(encoded, 4) 90 | d4.positionHashes must_== hexToBytes("22a48b") 91 | 92 | // 1. e4 d5 2. e5 f5 3. Ke2 93 | val d5 = Encoder.decode(encoded, 5) 94 | d5.positionHashes must_== hexToBytes("652a60" + "22a48b") 95 | 96 | // 1. e4 d5 2. e5 f5 3. Ke2 Kf7 97 | val d6 = Encoder.decode(encoded, 6) 98 | d6.positionHashes must_== hexToBytes("00fdd3" + "652a60" + "22a48b") 99 | 100 | "position hash 1. a4 b5 2. h4 b4 3. c4 bxc3 4. Ra3" in: 101 | val pgnMoves = "a4 b5 h4 b4 c4 bxc3 Ra3".split(" ") 102 | val encoded = Encoder.encode(pgnMoves) 103 | 104 | // 1. a4 b5 2. h4 b4 3. c4 105 | val d5 = Encoder.decode(encoded, 5) 106 | d5.positionHashes must_== hexToBytes("3c8123") 107 | 108 | // 1. a4 b5 2. h4 b4 3. c4 bxc3 4. Ra3 109 | val d7 = Encoder.decode(encoded, 7) 110 | d7.positionHashes must_== hexToBytes("5c3f9b" + "93d326") 111 | 112 | "position hash threefold" in: 113 | // https://lichess.org/V0m3eSGN 114 | val pgnMoves = 115 | "Nf3 d5 d4 c5 dxc5 e6 c4 Bxc5 Nc3 Nf6 e3 O-O cxd5 Nxd5 Nxd5 Qxd5 Qxd5 exd5 Be2 Nc6 a3 Bf5 b4 Bb6 Bb2 Rfd8 Rd1 Rac8 O-O Ne7 Nd4 Bg6 Rc1 Rxc1 Rxc1 Nf5 Bf3 Kf8 Nb3 Nxe3 Bd4 Nc2 Bxb6 axb6 Bd1 Re8 Bxc2 Bxc2 Nd4 Bd3 f3 Bc4 Kf2 Re5 g4 g6 Rc3 Ke7 Re3 Kf6 h4 Rxe3 Kxe3 Ke5 f4+ Kd6 g5 Ke7 Nf3 Ke6 Nd4+ Ke7 Nf3 Ke6 Nd4+ Ke7" 116 | .split(" ") 117 | val encoded = Encoder.encode(pgnMoves) 118 | val decoded = Encoder.decode(encoded, pgnMoves.size) 119 | 120 | val threefold = "966379" 121 | val ncheck = "65afff" 122 | val ke6 = "1bc865" 123 | val nf3 = "e804e3" 124 | val g5 = "ef8a0b" 125 | decoded.positionHashes must_== hexToBytes( 126 | threefold + ncheck + ke6 + nf3 + threefold + ncheck + ke6 + nf3 + threefold + g5 127 | ) 128 | 129 | "position hash compat" in: 130 | // https://lichess.org/DoqH1EQP 131 | val pgnMoves = 132 | "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nc6 Nc3 g6 Be3 Bg7 Bc4 Nf6 f3 O-O Qd2 Nd7 O-O-O a5 g4 Nce5 Be2 a4 a3 Nb6 h4 Nbc4 Bxc4 Nxc4 Qf2 Qb6 b3 Nxe3 Qxe3 e5 Nf5 Qxe3+ Nxe3 axb3 cxb3 Rxa3 Kb2 Ra6 h5 h6 hxg6 fxg6 Ned5 Rxf3 Ne7+ Kf7 Nxc8 Ke6 Nxd6 Rf2+ Kb1 Rxd6 Nd5 Rc6 Rc1 Rxc1+ Rxc1 Re2 Rc7 Rxe4 Nb6 Bf8 Rxb7 Rb4 Rb8 Rxb3+ Kc2 Rb5 Rxf8 Rxb6 Rg8 Kf6 Rf8+ Kg5 Rh8 Rd6 Re8 Kxg4 Rxe5 g5 Re3 Kf5" 133 | .split(" ") 134 | val encoded = Encoder.encode(pgnMoves) 135 | val decoded = Encoder.decode(encoded, pgnMoves.size) 136 | decoded.positionHashes must_== base64ToBytes("oB9I1h1e6YDy") 137 | 138 | "work with all black legal moves in YycayYfM" in: 139 | // Exclude compression as cause of issues with https://lichess.org/YycayYfM 140 | val prefix = 141 | "e4 c6 Nf3 d5 exd5 cxd5 d4 Nc6 c3 Nf6 Bf4 Bg4 Be2 e6 Nbd2 Bd6 Bxd6 Qxd6 O-O O-O Re1 a6 Ne5 Bxe2 Qxe2 Nd7 Nxd7 Qxd7 a4 Rab8 Nf3 b5 axb5 axb5 Ne5 Nxe5 Qxe5 b4 c4 dxc4 Rac1 Rbc8 Qa5 Qb7 Re2 c3 bxc3 bxc3 Rec2 Qe4 Qe5 Qxe5 dxe5 Rc5 f4 Rfc8 Kf2 f6 exf6 gxf6 Ke3" 142 | val legals = 143 | "Kh8 Kf8 Kg7 Kf7 Rf8 Re8 Rd8 Rb8 Ra8 R8c7 R8c6 R5c7 R5c6 Rh5 Rg5 Rf5 Re5+ Rd5 Rb5 Ra5 Rc4 h6 f5 e5 h5" 144 | .split(" ") 145 | forall(legals) { legal => 146 | val pgnMoves = (prefix + " " + legal).split(" ") 147 | val encoded = Encoder.encode(pgnMoves) 148 | val decoded = Encoder.decode(encoded, pgnMoves.size) 149 | pgnMoves must_== decoded.pgnMoves 150 | } 151 | 152 | "work with CwdQG2Es" in: 153 | // Exclude compression as cause of https://github.com/ornicar/lila/issues/5594 154 | val prefix = 155 | "c4 e5 g3 h5 Nc3 h4 Bg2 Nf6 d3 Bb4 Bd2 d6 Nf3 h3 Bf1 Nc6 e3 Bg4 Be2 d5 Nxd5 Nxd5 cxd5 Qxd5 Bxb4 Nxb4 Qa4+ c6 Qxb4 Bxf3 Bxf3 Qxf3 Rg1 O-O-O Qe4 Qf6 O-O-O Rd5 f4 Rhd8 Rgf1 Qe6 Kb1 f5 Qc4 e4 d4 Kb8 Rc1 Qe7 Rg1 Qd7 Qc2 Re8 Qe2 Ra5 g4 g6 gxf5 gxf5 Qh5 Rd8 Qh6 c5 Rg7 Qa4 a3 Qb3 Qf6 Rc8 Qd6+ Ka8" 156 | val pgnMoves = s"$prefix Rxc5 Raxc5".split(" ") 157 | val encoded = Encoder.encode(pgnMoves) 158 | val decoded = Encoder.decode(encoded, pgnMoves.size) 159 | pgnMoves must_== decoded.pgnMoves 160 | 161 | "pass perft test" in: 162 | // Running the entire suite can take minutes. 163 | 164 | PerftTest.trickyBatch(); 165 | 166 | /* 167 | PerftTest.batch0(); 168 | PerftTest.batch1(); 169 | PerftTest.batch2(); 170 | PerftTest.batch3(); 171 | PerftTest.batch4(); 172 | 173 | PerftTest.batch5(); 174 | PerftTest.batch6(); 175 | PerftTest.batch7(); 176 | PerftTest.batch8(); 177 | PerftTest.batch9(); 178 | 179 | PerftTest.batch10(); 180 | PerftTest.batch11(); 181 | PerftTest.batch12(); 182 | PerftTest.batch13(); 183 | PerftTest.batch14(); 184 | 185 | PerftTest.batch15(); 186 | PerftTest.batch16(); 187 | */ 188 | 189 | PerftTest.batch17(); 190 | 191 | // These tests throw a RuntimeError if they fail. 192 | true must_== true 193 | 194 | val fixtures = List( 195 | "d3 d5 g3 e6 Bg2 Nf6 Nf3 Be7 O-O O-O Re1 a6 e4 c5 e5 Nfd7 d4 Nb6 dxc5 Bxc5 Nc3 N8d7 a4 Be7 a5 Nc4 b3 Ncxe5 Nxe5 Nxe5 Rxe5 Bd6 Re1 Bd7 Bf4 Bc6 Bxd6 Qxd6 Na4 Rad8 Nb6 Rfe8 Ra4 Bxa4 bxa4 Qc5 Qa1 Qxa5 Qd4 Rd6 Nc4 Qb4 Nxd6 Qxd4 Nxe8 Qd2 Rb1 Qxc2 Rxb7 Qxa4 Rb8 Kf8 Nd6+ Ke7 Nf5+ Kf6 Nh4 Qd1+ Bf1 Qd4 Kg2 a5 Rb7 a4 Rxf7+ Kxf7 Nf3 Ke7 Ne5 Kd6 Nf3 Qc4 Nd4 Qc3 Nf5+ Ke5 Ne3 Kf6 Nxd5+ exd5", 196 | "e4 e6 Nf3 c5 g3 a6 Bg2 Nc6 O-O d6 h3 Be7 Nc3 Qc7 d4 cxd4 Nxd4 Nxd4 Qxd4 Bf6 Qd1 e5 Nd5 Qc6 Nxf6+ Nxf6 Re1 O-O Bg5 Nd7 f4 exf4 Bxf4 Ne5 Bxe5 dxe5 a3 Be6 b4 Rad8 Qe2 Rd4 Rad1 Rfd8 Rxd4 Rxd4 c3 Rc4 Qc2 f6 Rd1 Qc7 a4 Rxb4 Rc1 Rc4 Bf1 Rc5 c4 Qb6 Qd2 Rxc4+ Kh1 Rxc1 Qxc1 Qc6 Qd1 Qxe4+ Bg2 Qd4 Qc1", 197 | "e4 g6 Nf3 Bg7 d4 e6 Nc3 Ne7 Be3 O-O Be2 d6 O-O b6 Qd2 Bb7 Bh6 c5 Bxg7 Kxg7 dxc5 bxc5 Rad1 d5 exd5 exd5 Rfe1 d4 Nb5 a6 Na3 Kg8 c3 Nbc6 cxd4 cxd4 Nxd4 Nxd4 Qxd4 Qxd4 Rxd4 Nc6 Rd7 Rab8 Red1 Ne5 Re7 Nc6 Rc7 Nb4 Rdd7 Be4 Bc4 Bd5 Bxd5 Nxd5 Rxd5 Rxb2 Rc2 Rfb8 f3 Rxc2 Nxc2 Rb2 Rd8+ Kg7 Rc8 Rxa2 Nb4 Ra4 Nd5 Ra1+ Kf2 a5 Kg3 a4 Ra8 a3 Nc3 Rc1 Nb5 Rb1 Nxa3 Ra1", 198 | "e4 g6 d4 Bg7 e5 e6 f4 Ne7 Nf3 d5 Nc3 O-O Be3 Nd7 Bd3 b6 Nb5 c5 Nd6 cxd4 Nxd4 Nxe5 fxe5 Bxe5 Nxc8 Rxc8 O-O Nc6 Nxc6 Rxc6 c3 Bc7 Bd4 Qd6 Qg4 Qxh2+ Kf2 e5 Rh1 Qf4+ Qxf4 exf4 Rh6 Re6 Rah1 f5 Rxh7 Bd8 Rh8+ Kf7 R1h7+ Ke8 Bb5+ Rc6 Bxc6#", 199 | "e4 c5 Nf3 Nc6 Bc4 Nf6 Nc3 e5 O-O d6 a3 Nd4 Nxd4 exd4 Nd5 Be6 Nxf6+ Qxf6 Bxe6 Qxe6 c3 d3 Qa4+ Ke7 e5 d5 Re1 c4 Qb4+ Kd7 Qxb7+ Ke8 Qxa8+ Kd7 Qxa7+ Kc8 b3 Qf5 bxc4 dxc4 Qa8+ Kc7 Rb1 Bc5 Rb7+ Kc6 Qa6+ Kd5 Rf1 Re8 Rc7 Rxe5 Qc6#", 200 | "b3 d5 Bb2 Nf6 g3 e5 Bg2 Nc6 d3 Be6 e3 Qd6 Nd2 O-O-O Ne2 g6 c4 dxc4 bxc4 Bg7 O-O Nd7 d4 exd4 exd4 Nb4 Ne4 Qe7 d5 Bg4 Bxg7 Rhg8 Bc3 Bxe2 Qxe2 f5 Bxb4 Qxb4 Rab1 Qa3 Nc3 Rde8 Qb2 Qa6 Qb5 Qa3 Qb4 Qa6 a4 Nb6 Qb5 Qxb5 axb5 Nxc4 d6 Nd2 Bxb7+ Kxb7 Rbe1", 201 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 d4 g6 Bc4 Bg7 Bg5 O-O O-O Be6 Bxe6 fxe6 Re1 Nc6 d5 e5 dxc6", 202 | "e3 e5 Nc3 Nf6 Bc4 d5 Bb3 c5 Ba4+ Bd7 Bxd7+ Qxd7 d4 exd4 exd4 cxd4 Qxd4 Qe6+ Be3 Ne4 Nf3 Nxc3 Qxc3 Nc6 Nd4 Nxd4 Qxd4 a5 a3 b5 O-O Be7 Rad1 O-O Qxd5 Qxd5 Rxd5 b4 Rd7 Rfe8 axb4 Bxb4 c3 Be7 Rfd1 a4 Ra1 h6 Rd4 Bf6 Rdxa4 Rxa4 Rxa4 g5 g3 h5 Kg2 Kg7 h4 gxh4 gxh4 Bxh4 Rxh4 Kg6 b4 Re5 Bd4 Rg5+ Kf3 f5 c4 Rg4 Rxg4+ fxg4+ Kg3 Kg5 b5 h4+ Kg2 h3+ Kh2 Kh4 Be5 Kg5 b6 Kf5 Bg3 Ke4 b7 Kf3 b8=Q Ke2 Qd8 Kf1 Qd1#", 203 | "Nf3 f6 d3 e5 Nbd2 g5 e4 g4 Ng1 Bb4 Qxg4 d5 Qh5+ Kf8 a3 dxe4 axb4 exd3 Bxd3 e4 Bc4 f5 Qf7#", 204 | "e4 d5 exd5 Qxd5 Nc3 Qa5 b3 c6 Bb2 Nf6 Be2 Bf5 Nf3 e6 O-O Nbd7 a3 Qc7 a4 Bd6 d3 Ng4 h3 Ngf6 Ne4 Nxe4 dxe4 Bg6 Bd3 f6 Nh4 Bf7 g3 Qb6 Kg2 g5 Nf3 h5 Nh2 Qc7 Qf3 Ne5 Qxf6 Nxd3 Qxh8+ Kd7 Qg7 Nxb2 Qxf7+ Kc8 Qe8+ Qd8 Qxe6+ Kc7 Qf7+ Be7 Qxh5 Qd2 Qf7 Qd6 Rfb1 Rf8 Qg7 Qf6 Qxf6 Bxf6 Ra2 c5 Raxb2 Bxb2 Rxb2 Rd8 c4 Rd3", 205 | "e4 c6 e5 d6 exd6 exd6 d4 g6 Bd2 Bg7 Bc3 Ne7 Bc4 Be6 b3 b5 Bxe6 fxe6 d5 Nxd5 Bxg7 Rg8 Bh6 Qf6 c3 Nd7 Qf3 O-O-O Ne2 Qxf3 gxf3 Ne5 Nd4 Rde8 f4 Nd3+ Kf1 Kb7 Nd2 N5xf4 Bxf4 Nxf4 Ne4 Rgf8 Nxd6+ Kb6 Nxe8 Rxe8 Re1 c5 Rxe6+ Nxe6 Nxe6 Rxe6 Rg1 a5 Rg4 Kc6 Rg5 Kd6 Kg2 Re5 Rg4 h5 Rxg6+ Kd5 Ra6 c4 Rxa5 Kc6 b4 Rg5+ Kf1 Rg8 Ra6+", 206 | "f4 d5 Nf3 Nc6 e3 e6 Bb5 Nf6 d3 Bd7 O-O a6 Ba4 b5 Bb3 Bd6 c3 O-O Bc2 h6 e4 dxe4 dxe4 Be7 e5 Nd5 Qd3 g6 f5 exf5 Bxh6 Re8 Qxd5 Be6 Qxd8 Raxd8 Nbd2 Rd5 Rae1 Bc5+ Kh1 Red8 Nb3 Bb6 Bg5 R8d7 Bf6 a5 Rd1 a4 Nc1 Be3 Nd3 Na5 Nb4 Rxd1 Rxd1 Rxd1+ Bxd1 Nc4 Nd3 Na5 Bd8 Bb6 a3 Bc4 Nb4 Kf8 Nd4 Ke8 Bf6 Bxd4 cxd4 Kd7 d5 Nb7 Bf3 Nc5 Nc6 Nd3 Na5 Nxb2 Nxc4 bxc4 e6+ fxe6 Bxb2 exd5 Bxd5 Kd6 Bxc4 g5 Bb5 c6 Bxa4 Kd5 Bc2 f4 Bc1 Ke5 Kg1 c5 Kf2 g4 g3 f3 Be3 Kd5 Bf4 c4 a4 Kc5 Be5 Kb4 a5", 207 | "e4 e5 Nc3 Nf6 d3 c6 f4 exf4 Bxf4 d5 e5 d4 Nce2 Nd5 Bd2 c5 c4 Ne3 Bxe3 dxe3 Nf3 Be7 Nc3 Bg4 Be2 O-O O-O Nc6 Nd5 Nd4 Nxd4 cxd4 Bxg4 Bc5 e6 fxe6 Bxe6+ Kh8 Rxf8+ Qxf8 Qf3 Qd6 Bf5 Rf8 Qe4 g6 Bg4 a5 Bf3 Ba7 a3 Bb8 g3 Ba7 b4 h5 c5 Qd7 Qxg6 Qh7 Qxh7+ Kxh7 Bxh5 Bb8 Rf1 Rd8 Nf6+ Kh6 g4 Kg5 Kg2 axb4 axb4 Rc8 Ne4+ Kh6 Rf6+ Kg7 g5 Be5 Rf7+ Kg8 Rxb7 Rf8 Bf3 Ra8 Ng3 Ra2+ Ne2 Bf4 h4 Kf8 b5 Rd2 c6 Rxd3 b6", 208 | "d4 d5 h3 e6 a3 Nf6 Nf3 b6 e3 c5 c4 cxd4 Qxd4 Nc6 Qd1 Be7 cxd5 Nxd5 Bb5 Bb7 Bxc6+ Bxc6 e4 Nf6 Qxd8+ Rxd8 Nc3 O-O e5 Bxf3 gxf3 Nd5 Nxd5 Rxd5 f4 f6 Be3 fxe5 fxe5 Rxe5 O-O-O Bf6 Kb1 Rb5 Rd2 Rd8 Rhd1 Rf8 Ka2 a5 Rd6 e5 b4 axb4 axb4 Ra8+ Kb3 Be7 Rxb6 Rxb6 Bxb6 Rb8 Ba5 Rb5 Ka4 Rb7 Re1 Bd6 Rd1 Rd7 Rc1 Kf7 Rg1 g6 h4 Ke6 Rg5 Rf7 Bb6 Rf4 h5 Kf6 Rg2 gxh5 Ba5 Rg4 Rh2 h4 f3 Rf4 Rh3 Kf5 Kb5 e4 fxe4+ Kxe4 Kc6 Be5 b5 Rf6+ Kd7 Rd6+ Ke7 Rd5 Rxh4+ Kf3 Rb4 Bd6+ Ke6 Bxb4 Kxd5 Bxa5 Kc6 h5 Kb7 h4 Ka6 Bd8 b6 Bxb6 Kxb6 h3", 209 | "d4 d5 c4 c5 e3 e6 cxd5 cxd4 dxe6 dxe3 Qxd8+ Kxd8 exf7 exf2+ Kxf2 Nf6 Bg5 Be6 Nf3 Bxf7 Nc3 Be7 Be2 Nc6 Rhd1+ Kc7 Bf4+ Kc8 Bb5 Bc5+ Kf1 Nb4 a3 Nc2 Rac1 Ne3+ Bxe3 Bxe3 Rc2 a6 Re2 axb5 Rxe3 Bc4+ Kg1 Re8 Rxe8+ Nxe8 Ne5 h6 Nxc4 bxc4 Rd4 b5 Nxb5 Kb7 Nd6+ Kb6 Nxc4+ Kc5", 210 | "e4 e5 Bc4 Qf6 d3 Bc5 Qf3 d6 Nc3 c6 Bg5 Qxf3 Nxf3 h6 Bh4 g5 Bg3 Nf6 h4 g4 Nd2 h5 f3 Nbd7 fxg4 Nxg4 Rf1 Ndf6 Nf3 Be6 Ng5 Bxc4 dxc4 O-O-O Bf2 Nxf2 Rxf2 Bxf2+ Kxf2 Ng4+ Kg1 Rdf8 Rf1 f6 Ne6 Kd7 Nxf8+ Rxf8 Rf5 Ke6 Rxh5 Ne3 Rh7 Nxc4 b3 Ne3 Rxb7 Nxc2 Rxa7 Rh8 g3 Rg8 Kh2 Ne3 Ra6 c5 Nb5 Rd8 h5 Ng4+ Kh3 f5 exf5+ Kxf5 Rxd6 Rxd6 Nxd6+ Kg5 a4 Nf6 a5 Nd7 Ne4+ Kxh5 a6 Kg6 a7 Nb6 Nxc5 Kf5 Nd7 e4 Nxb6 e3 a8=Q e2 Qf3+", 211 | "e4 c6 Bc4 d5 exd5 cxd5 Bb3 Nf6 c3 Nc6 d3 e5 h3 Bd6 Ne2 Be6 O-O O-O f4 e4 dxe4 Nxe4 Nd4 Kh8 f5 Nxd4 cxd4 Bd7 Bxd5 Bc6 Bxc6 bxc6 Nc3 Nxc3 bxc3 c5 d5 c4 Be3 Re8 Bd4 Be5 Bxe5 Rxe5 Qd4 Qb6 Qxb6 axb6 d6 Rd5 Rf4 b5 Rd4 Rxd4 cxd4 Rd8 Rb1 g6 Rxb5 Rxd6 fxg6 fxg6 Rc5 Rxd4 a4 c3 a5 Ra4 Rxc3 Rxa5 g4 Ra2 Rc7 h5 Rc6 Kg7 gxh5 gxh5 Rc4 Kg6 h4 Kf5 Rc5+ Kg4 Rc4+ Kg3 Re4 Ra1+ Re1 Rxe1#", 212 | "e4 e5 Nf3 Nc6 Bb5 Nf6 Bxc6 dxc6 d3 Bd6 O-O O-O Nbd2 b5 Qe2 a5 h3 Bd7 a4 b4 b3 c5 Nc4 Be7 Ncxe5 Be6 Bb2 Qc8 Rad1 h6 Kh2 Re8 Ng1 Nh7 f4 f6 Nef3 Bf7 f5 Bd6+ Kh1 c6 Bc1 Bc7 Qf2 Bh5 g4 Bf7 Nh4 Ng5 Bf4 Bxf4 Qxf4 Re7 Ng2 Qc7 Qxc7 Rxc7 h4 Nh7 Kh2 Re8 Kg3 Rd7 Nf4 Nf8 Nf3 Red8 g5 hxg5 hxg5 fxg5 Nxg5 Be8 Rh1 g6 Rh6 Kg7 Rdh1 Kf6 Nf3 Rg7 Kg4 Ke7 e5 Kd7 f6 Rf7 e6+ Kc7 exf7 Bxf7 Rh7 Rd7 Rxf7 Rxf7 Kg5 Kd6 Ne6", 213 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e5 Nb3 a6 Nc3 b5 Nd5 Bb4+ c3 Bf8 Be3 Nf6 h3 Nxe4 Bd3 f5 Bxe4 fxe4 O-O Be7 Bc5 Bxc5 Nxc5 d6 Nxe4 Be6 Ne3 O-O Qxd6 Qxd6 Nxd6 e4 a3 Ne5 Nxe4 Nd3 Rad1 Nxb2 Rd2 Nc4 Nxc4 Bxc4 Nd6 Bxf1 Kxf1 Rad8 Ne4 Rxd2 Nxd2 Rc8 c4 bxc4 Ne4 c3 Nd6 c2 Ne4 c1=Q+ Ke2 Rc2+ Kf3", 214 | "d4 b6 e4 Bb7 Ba6 g6 Bxb7 Na6 Bxa8 Qxa8 Nc3 c6 Qe2 b5 Nf3 Qb7 O-O b4 Nd1 c5 dxc5 Nxc5 c3 Qxe4 Qxe4 Nxe4 Ne3 bxc3 bxc3 Nxc3 Bb2 Ne2+ Kh1 f5 Rfe1 Nf4 g3 Nh5 Ng5 Bh6 f4 Bxg5 fxg5 h6 Nd5 hxg5 Bxh8 Kf7 Bb2 e6 Nc3 Ngf6 Rad1 Ne4 Nxe4 fxe4 Rxd7+ Ke8 Rxa7 Nxg3+ hxg3 g4 Rc1 g5 Rc8#", 215 | "g3 c5 Bg2 d6 Nf3 e6 O-O Nf6 d3 Be7 Nbd2 h6 b3 a6 Bb2 Nbd7 Ne4 Nxe4 dxe4 Rb8 e5 d5 Nd2 b6 c4 dxc4 Nxc4 b5 Nd6+ Kf8 Bc6 Bb7 Bxb7 Qc7 Bg2 Bg5 Ne4 Be7 f4 Nb6 Rc1 Rd8 Qc2 Nd5 Nxc5 Ne3 Nxe6+ fxe6 Qxc7 Nxf1 Rxf1", 216 | "e4 b6 d3 Bb7 Nc3 d6 Be3 e5 Nf3 f6 Be2 g5 O-O h5 Qd2 g4 Ne1 Qd7 f3 Bh6 fxg4 Bxe3+ Qxe3 hxg4 Qg3 Qh7 Bxg4 Nh6 h3 Nxg4 Qxg4 Qg8", 217 | "e4 e6 Nf3 c6 Nc3 d5 exd5 cxd5 d4 Nf6 Bd3 Be7 Ne5 O-O O-O Nbd7 f4 a6 Kh1 b5 a3 Bb7 Qf3 h6 Qg3 Nh7 Ng4 f5 Nxh6+ Kh8 Qg6 gxh6 Qxe6 Rf6 Qe2 Rg6 Bxf5 Rg7 Qe6 Ndf6 Re1 Bd6 Qe3", 218 | "c4 c6 Nc3 e6 e4 Nf6 Nf3 Be7 d4 d5 cxd5 exd5 e5 Nfd7 Bd3 Na6 a3 Nc7 O-O Nb6 h3 Be6 Re1 Nd7 Qc2 c5", 219 | "e4 c6 d3 d5 Nc3 dxe4 dxe4 Nd7 Nf3 Ngf6 Bd3 e6 O-O Bd6 Re1 Qc7 Bg5 Ng4 g3 h6 Bd2 Nde5 Nxe5 Nxe5 Bf4 Nxd3 Bxd6 Qxd6 Qxd3 Qxd3 cxd3 O-O Kf1", 220 | "e4 e5 Qf3 Nf6 a4 Nc6 a5 Nxa5 Rxa5 c6 Rxe5+ Be7 Qg3 d6 Rxe7+ Qxe7 Qxg7 Rg8 Qh6 Qxe4+ Kd1 Bg4+ f3", 221 | "e4 e6 Nf3 d5 e5 c5 d3 Nc6 Nbd2 Nge7 b3 Ng6 Bb2 Qc7 Qe2 Be7 O-O-O O-O Ne4 dxe4 Qxe4 b5 d4 c4 bxc4 bxc4 Bxc4 Na5 Bd3 Bb7 Nh4 Bxe4 Bxe4 Rac8", 222 | "e4 e5 Nf3 Bc5 c3 f6 d4 Be7 Be3 Nc6 Nbd2 Nxd4 cxd4 exd4 Nxd4 Bb4 Be2 Qe7 O-O c5 Nf5 Qe5 Nc4 Qxe4 Nxg7+ Kd8 Qd6 Ne7 Ne6+ Ke8 Nc7+ Kf8 Bh6+ Kf7 Bh5+ Ng6 Bxg6+ Kxg6 Bf4 Qxc4 Nxa8 Qf7 Qd3+ f5 Qd6+ Qf6", 223 | "d4 d5 Bg5 Nd7 g3 Ngf6 Bh3 e6 Nc3 Be7 Bxf6 Nxf6 e3 a6 Nge2 b5 O-O O-O Qb1 c5 dxc5 Bxc5 b4 Bb6 a4 Bb7 a5 Bc7 Nd1 e5 c3 e4 f4 Bc8 Bxc8 Rxc8 Nd4 Bb8 Nf2 Qd7 Ra3 Ng4 Nxg4 Qxg4 Rf2 h5 Kg2 h4 Raa2 h3+ Kh1 Ba7 Rac2 Bxd4 cxd4 Rxc2 Qxc2 Rc8 Qb2", 224 | "d4 d5 f3 e6 Nh3 f5 g3 Nf6 e3 Be7 f4 c5 Qd2 cxd4 Qxd4 Nc6 Qd2 Bd7 Nc3 O-O Qe2 d4 Bd2 dxc3 Bxc3 b6 O-O-O Bc5 Kb1 b5 Bd4 Bxd4 exd4 Be8 c4 Bf7 d5 Ne7 d6 Nc6 cxb5 Nb8 d7 a6 Rc1 Nfxd7 Qd2 Nf6 Qxd8 Rxd8 bxa6 Nbd7 Bb5 Rxa6 Bxa6 Nc5 Bc4 Rc8 b4 Nce4 Bb3 Rd8 Rhd1 h6 Rxd8+ Kh7 Rdd1 Kg6 Rg1 Nd7 Rgf1 Nb6 Nf2 Nd6 Nd3 Nb5 Nc5 Nd5 Bxd5 exd5 Nd7 d4 Ne5+ Kf6 Nxf7 Kxf7 Rfd1 Nc3+ Rxc3 dxc3 Rc1 c2+ Rxc2 Kg6 Rc5 Kf6 h3 g6 Rc6+ Kf7 g4 fxg4 hxg4 g5 fxg5", 225 | "e4 e5 Nf3 Nc6 Bb5 d6 Bxc6+ bxc6 d4 Nf6 dxe5 dxe5 Qxd8+ Kxd8 Nxe5 Bd6 Nxf7+ Ke8 Nxh8 Bb7 f4 Ke7 e5 Bb4+ Nc3 Bxc3+ bxc3 Ne4 O-O Nxc3 Bd2 Ne2+ Kh1 Rxh8 f5 Rf8 Rae1 Nd4 Bg5+ Ke8 f6 g6 e6 Nf5 e7 Rf7 g4 Nxe7 Rxe7+ Kf8 Rxf7+ Kxf7 Re1 c5+ Kg1 Bd5", 226 | "e4 e6 c4 d5 exd5 exd5 cxd5 Nf6 b3 Nxd5 Bb2 Nf6 Nf3 Be7 Be2 O-O O-O h6 Re1 Re8 d4 Nbd7 Nc3 Nb6 Qc2 c6 Rad1 Nbd5 Ne5 Be6 Bd3", 227 | "e4 e5 Nc3 d6 Bc4 f5 exf5 Bxf5 d4 e4 d5 Nf6 Nge2 Nbd7 Ng3 Bg6 h4 Ne5 Be2 Bf7 h5 g6 h6 Nfg4 Be3 Nxe3 fxe3 Qf6 Qd2 Qe7 O-O-O O-O-O Rdf1 a6 Rf2 Be8 Rhf1 Bd7 Ngxe4 Ng4 Bxg4 Bxg4 Nf6 Bxh6 Nxg4 Bg5 Qe2 Rhe8", 228 | "d4 d5 Nf3 Nf6 e3 Bg4 h3 Bh5 g4 Bg6 Ne5 Nbd7 Nxg6 hxg6 Bg2 e6 a3 Nb6 g5 Nh5 e4 c6 exd5 Nxd5 Bxd5 cxd5 Nd2 Qxg5 Nf3 Qd8 Bg5 Be7 Bxe7 Qxe7 Qd3 O-O O-O-O Rac8 Ne5 Qc7 Rhg1 Nf4 Qd2 Nxh3 Rg3 Nxf2 Qxf2 Qb6 c3 Qb3 Kb1", 229 | "e4 c6 Nc3 d6 d4 g6 f4 Bg7 Nf3 Nf6 Bd3 O-O O-O Bg4 Qe1 Nbd7 Be3 c5 Rd1 cxd4 Bxd4 Qa5 h3 Bxf3 Rxf3 e5 Be3 exf4 Bxf4 Ne5 Bxe5 dxe5 Nd5 Qxe1+ Rxe1 Nxd5 exd5 f5 Bc4 e4 Rb3 b6 d6+ Kh8 d7 Rad8 Bb5 a6 Bc6 b5 Rd1 Rf6 Kf2 Rxc6 a4 Rc7 axb5 Rdxd7 Rxd7 Rxd7 bxa6 Bd4+ Ke2 Ra7 Rb8+ Kg7 Rb7+ Rxb7 axb7 Ba7 c4 Kf7 b4 Ke7 c5 Kd7 b5 Kc7 c6 Bb8 Ke3 Kb6 Kd4 Kxb5 Kd5 Kb6 Ke6 Kxc6 Kf7 f4 Kg7 e3 Kxh7 e2 Kxg6 e1=Q Kg5 Qg3+ Kf5 Qxg2 h4 f3", 230 | "e3 e6 b3 d5 Bb2 c5 h3 Nf6 g4 Bd6 h4 d4 h5 dxe3 dxe3 Nc6 h6 g6 g5 Rg8 Bxf6 Qb6 Nc3 e5 Nd5 Qa5+ Qd2 Qa3 Nf3 Nb4 Rd1 Be6 Bb5+ Nc6 Bxc6+ bxc6 Nc3 Be7 Ne4 Bxf6 Nxf6+ Ke7 Nxg8+ Rxg8 Qd7+ Kf8 Qd8#", 231 | "Nc3 Nf6 e4 d5 e5 d4 exf6 dxc3 fxg7 cxd2+ Qxd2 Qxd2+ Bxd2 Bxg7 O-O-O O-O Ne2 Nc6 Bc3 Bg4 f3 Bf5 Bxg7 Kxg7 Ng3 Bg6 Bd3 Rad8 Be4 Bxe4 Nxe4 b6 h4 e6 c3 Ne5 Rh3 Rxd1+ Kxd1 Rd8+ Kc2 f6 h5 Kf7 Rg3 Nc4 f4 Nd6 Nxd6+ cxd6 Kd3 f5 Rg5 Rg8 Kd4 e5+ Kd5", 232 | "e4 d5 exd5 Qxd5 Nc3 Qd7 Bc4 e6 Nf3 Bd6 O-O h6 d4 c6 Ne5 Bxe5 dxe5 Qxd1 Rxd1 Ne7 b4 O-O Rd3 b5 Rg3 Nf5 Rg4 bxc4 Ne4 Nd7 Rb1 Ba6 a4", 233 | "d4 d5 Nf3 Nd7 c3 e6 Bf4 Ngf6 e3 c5 Bd3 c4 Bc2 b5 Nbd2 b4 Qe2 a5 e4 a4 O-O dxe4 Nxe4 Ba6 Nd6+ Bxd6 Bxd6 b3 Bb1 Nd5 Re1 Ne7", 234 | "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Bc4 Bg7 f3 O-O Be3 Nc6 Qd2 Bd7 O-O-O Rc8 Bb3 a6 g4 Ne5 Rdf1 Nc4 Bxc4 Rxc4 Bh6 b5 Bxg7 Kxg7 g5 Nh5 b3 Rc5 f4 Qc7 Nd5 Rxd5 exd5 Rc8 f5 Qc3 Qxc3 Rxc3", 235 | "e3 Nf6 d4 g6 c4 Bg7 Nc3 d6 Nf3 O-O Be2 Bg4 h3 Bxf3 Bxf3 c6 O-O d5 Bd2 dxc4 Be2 b5 b3 cxb3 axb3 Nd5 Nxd5 Qxd5 Qc2 c5 dxc5 Bxa1 Bxb5 Bg7 Bc4 Qc6 Rd1 Qxc5 Bxf7+ Rxf7 Qxc5 Nd7 Qc7 Nf6 Rc1 Raf8 f3 Nd5 Qc5 Rf5 Qd4 Bxd4 exd4 Nf4 Bxf4 Rxf4 Rc4 R4f7 Ra4 Rf6 Rxa7 Rb6 Ra3 Rd8 Ra4 Rxb3 Kf2 Rd3 d5 R3xd5 Re4 e5 Kg3 Kf7 f4 exf4+ Rxf4+ Ke6 Re4+ Re5 Rf4 Rf5 Rg4 Kf6 Kh2 Rd3 Rd4 Rf2 Rd6+ Rxd6 Kg3 Rdd2 Kh4 Rxg2", 236 | "e4 e6 d4 d5 exd5 exd5 Nf3 Nf6 Be2 h6 Nc3 c6 Bd2 Bf5 a3 Bd6 Nh4 Bh7 Be3 Ne4 Nxe4 dxe4 g3 O-O Ng2 f5 Bc4+ Kh8 Nf4 Qb6 Bb3 a5 O-O Nd7 Ne6 Rf6 d5 c5 c4", 237 | "c4 e5 e4 Nc6 d3 a6 Nf3 d6 a3 Bg4 Be2 Qf6 O-O h5 Nc3 Be6 Nd5 Bxd5 cxd5 Nd4 Nxd4 exd4 Bxh5 g6 Bf3 O-O-O Bg4+ Kb8 b4 Bh6 b5 a5 Bd2 b6 Bxh6 Rxh6 a4 Ne7 Qc2 Qe5 h3 f5 Bf3 f4 Bg4", 238 | "e4 c6 Nf3 d5 exd5 cxd5 d4 Nc6 c4 dxc4 Bxc4 e6 O-O Nf6 Re1 Bb4 Nc3 O-O Bd2 a6 a3 Ba5 b4 Bb6 Be3", 239 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e5 Nxc6 bxc6 Nc3 Bc5 Be3 Bxe3 fxe3 Qh4+ g3 Qd8 Qd2 Nf6 O-O-O O-O h3 a5 g4 h6 Qg2 Qb6 g5 Qxe3+ Kb1 hxg5 Be2 Rb8 h4 Qxc3 b3 a4 Bc4 a3 Kc1 Qa1+ Kd2 Qxa2 hxg5 Nh7 Qh3 Re8 Qxh7+ Kf8 Qh8+ Ke7 Qxg7 Rf8 Qf6+ Ke8 g6 fxg6 Rh8 Rxh8", 240 | "e4 e5 Nf3 Nf6 Bc4 Bc5 O-O O-O Nxe5 Nxe4 Re1 Qe8 d4 Bb4 Rxe4 d6 Bxf7+ Rxf7 Nxf7 Qxf7 Qe2 Qg6 Re8+ Kf7 Qe7#", 241 | "e4 g6 d4 d6 Nf3 Bg7 Be3 b6 c4 Ba6 b3 e6 Nc3 Ne7 Bd3 c5 d5 Bxc3+ Nd2 Bxa1 Qxa1 exd5 Qxh8+ Kd7 Qf6 dxc4 bxc4 Ng8 Qxf7+ Kc6 Qd5+ Kd7 e5 Nc6 Qxd6+ Kc8 Qxc6+ Qc7 Qe6+ Kb8 Be4 Bb7 Qxg8+ Qc8 Qxc8+ Bxc8 Bxa8 Kxa8 O-O Be6 f4 Kb7 g4", 242 | "e4 e6 d4 d5 e5 Nc6 c3 g6 Nf3 Bh6 Bxh6 Nxh6 Bd3 O-O Qd2 Nf5 h4 Qe7 h5 g5 Bxf5 exf5 Nxg5 f6 Nf3 Qg7 g3 f4 Qxf4 fxe5 Qg5 Qxg5 Nxg5 exd4 cxd4 Nxd4 Na3 Nf3+ Kf1 Nxg5 Kg2 Bh3+ Rxh3 Nxh3 Kxh3 Rxf2 b3 c5 Nb5 c4 bxc4 dxc4 Rc1 Rxa2 Rxc4 Ra5 Nd6 Rxh5+ Kg4", 243 | "e4 e6 d4 d6 Nc3 Nd7 Nf3 Ne7 Bd3 Ng6 Be3 Be7 Qd2 c6 O-O-O O-O h4 b5 h5 Nh8 Kb1 a5 e5 d5 Bh6 Re8 Rh3 f5 exf6 Nxf6 Rg3 Bf8 Ne5 Nf7 Nxf7 Kxf7 Bg5 h6 Bg6+ Kg8 Bxe8 Qxe8 Bxf6 Qxh5 Qf4 Qf5 Qxh6 Kf7", 244 | "d4 d6 e4 e5 dxe5 dxe5 Qxd8+ Kxd8 Nf3 Nc6 Bb5 Bd7 Nc3 f6 O-O g5 h3 h5 a3 a6 Bxc6 Bxc6 Rd1+ Ke8 Nd5 Bd6 c4 Ne7 Nxf6+ Kf7 Nd5 g4 Ng5+ Ke8 h4 Ng6 Nf6+ Ke7 Nd5+ Bxd5 cxd5 Nf4 Bxf4 exf4 Ne6 c6 Rac1 f3 g3 Rh6 Ng5 cxd5 exd5 Bc7 Re1+ Kd6 Rcd1 Bd8 Re6+ Rxe6 Nxe6", 245 | "e4 d5 exd5 Qxd5 Nc3 Qe5+ Qe2 Qxe2+ Bxe2 Bf5 Nd5 Kd8 c3 c6 Ne3 e6 Nxf5 exf5 Nf3 g6 Bc4 Ke7 O-O Nf6 Re1+ Kd7 Ne5+ Kc7 Nxf7 Rg8 Ng5 Rh8 Ne6+ Kc8 d3 Nbd7 Bg5 Be7", 246 | "e4 e5 Nf3 d6 d4 exd4 Qxd4 Nf6 Nc3 Nc6 Bb5 Bd7 Bxc6 Bxc6 O-O Be7 Re1 O-O Bf4 Nd7 Qd2 Bf6 Nd4 Ne5 Bxe5 Bxe5 Nxc6 bxc6 Rab1 g6 Ne2 Qh4 f4 Bg7 g3 Qf6 c3 a6 Nd4 c5 Nf3 Rab8 Re2 a5 Rc1 a4 a3 Rb3 Rc2 Rfb8 Qd5 Qe6 Qd3", 247 | "e4 e5 Nc3 Qe7 Bc4 c6 d3 h6 Nge2 a5 a3 g5 Ng3 Bg7 Nf5 Qf6 O-O d6 Bd2 Bxf5 exf5 Qxf5 Ne4 d5 Nd6+ Kd7 Nxf5 Nf6 Nxg7 dxc4 dxc4 Kc8 Nf5 Nbd7 Bc3 Rd8 Nd6+ Kb8 Nxf7 Rf8 Nxe5", 248 | "e4 e6 d4 c5 d5 exd5 Qxd5 d6 Bc4 Be6 Qd3 d5 exd5 Bxd5 Bxd5 Nf6 Bxb7 Qxd3 cxd3 Bd6 Bxa8 O-O Bf3 Nbd7 Nh3 Ne5 Be2 Rb8 Nc3 a6 O-O Nc6 b3 Nb4 Bb2 Nc2 Rac1 Nd4 Ne4 Nxe2+ Kh1 Nxc1 Rxc1 Nxe4 dxe4 Re8 f3 f6 Nf2 Rc8", 249 | "e4 c5 b3 Nc6 Bb2 d6 Bb5 Nf6 Qe2 e5 f4 a6 Bxc6+ bxc6 fxe5 dxe5 Nf3 Bd6 O-O O-O d3 Re8 Nbd2", 250 | "g3 d5 Bg2 e6 e4 Nf6 exd5 exd5 d4 Nc6 c3 Be6 h3 Be7 Bf3 Qd7 g4 O-O-O Ne2 a6 Nf4 Bd6 Be3 Rde8 Nd2 g5 Ng2 h6 h4 gxh4 Nxh4", 251 | "b4 e6 Bb2 d5 a3 Nf6 e3 Be7 Bxf6 O-O Bxe7 Qxe7 d4 b6 Nd2 Bb7 c4 Nd7 cxd5 exd5 Ngf3 c5 bxc5 bxc5 Nb3 c4 Nbd2 Bc6 Nb1 Rab8 Nc3 Rb3 Qc2 Rfb8 Rb1 Qxa3 Rxb3 Rxb3 Be2 Rxc3 Qb1 Rc1+ Qxc1 Qxc1+ Bd1 Qc3+ Nd2 a5 O-O a4 Nb1 Qb2 Bxa4 Bxa4 g3 c3 Kg2 c2 Na3 Qxa3", 252 | "e4 d6 Nf3 Bd7 e5 Qc8 Ng5 Nc6 Nxf7 Kxf7 exd6 cxd6 Bc4+ Ke8 Bxg8 Rxg8 O-O Nd8 Qh5+ g6 Qxh7 Rg7 Qh4 Be6 d3 Bf7 Nc3 g5 Qa4+ Nc6 d4 Be6 d5 Bd7 dxc6 Bxc6 Qd4 g4 Bf4 Rf7 Bg3 Bg7 Qd3 Bd7 Qh7", 253 | "c4 e6 Nf3 Nf6 Nc3 d5 d4 Be7 Bg5 Nbd7 e3 c6 c5 h6 Bh4 a5 a3 O-O Bd3 b6 b4 axb4 axb4 Rxa1 Qxa1 bxc5 bxc5 Nh7 Bxe7 Qxe7 Qb1 Nhf6 O-O e5 dxe5 Nxe5 Nxe5 Qxe5 Ne2", 254 | "d4 d5 c4 Nc6 Nc3 e5 e3 exd4 exd4 dxc4 Bxc4 Nf6 Nge2 Bb4 O-O Bg4 f3 Bh5 a3 Bd6 b4 O-O Qb3 Bg6 Bb2 Nxd4 Nxd4 Bxh2+ Kxh2 Qxd4 Ne4 Qd7 Rad1 Qf5 Ng3 Qf4 Rd4 Qh6+ Kg1 Rfe8 Rg4 Nxg4 fxg4 Qe3+ Kh2 Qxb3 Bxb3 Rad8 Nh5 Rd2 Bc1 Rd3 Bc4 Rc3 Bb5 c6 Ba4 b5 Bb2 Rce3 Bd1 a6 Nxg7 Bd3 Rf3", 255 | "e4 e5 f4 d6 fxe5 dxe5 Nf3 Nc6 Bc4 Be7 O-O Nf6 Ng5 O-O Qf3 Nd4 Qh3 h6 Rxf6 Bxh3 Nxf7 Ne2+ Kh1 Rxf7 Bxf7+ Kh8 gxh3 Bxf6 d3", 256 | "c4 e5 Nc3 Nf6 g3 Nc6 d3 d5 cxd5 Nxd5 Bg2 Be6 h4 Bb4 Bd2 Nxc3 Bxc6+ bxc6 bxc3 Ba3 h5 Qd5 Nf3 e4 dxe4 Qxe4 Rh4 Qf5", 257 | "e4 e5 Nf3 Nc6 Bc4 Bc5 d3 Nf6 Nc3 d6 Bg5 Bg4 Nd5 Nd4 h3 Bxf3 gxf3 c6 Nxf6+ gxf6 Be3 Ne6 Qd2 Qb6 O-O-O Rg8 Rhg1 O-O-O Bxe6+ fxe6 Bxc5 Qxc5 Qe3 Qxe3+ fxe3 Kd7 f4 Ke7 f5 d5 Rxg8 Rxg8 fxe6 Kxe6 exd5+ cxd5 Rd2 Rg3 Rh2 Rxe3 Kd2 Rg3 b3 d4 c3 Kd5 c4+ Ke6 a3 f5 b4 e4 dxe4 fxe4 h4 Rxa3 Rg2 Ra2+", 258 | "e4 e5 Nf3 Nc6 Bb5 Nf6 d3 a6 Ba4 b5 Bb3 Na5 c3 c6 Bc2 Bc5 b4 Bxf2+ Kxf2 Ng4+ Ke2 O-O bxa5", 259 | "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Be3 e5 Nb3 Be6 f3 Be7 Qd2 Nbd7 g4 Qc7 O-O-O Rc8 g5 Nh5 Kb1 b5 Nd5 Bxd5 exd5 O-O Na5 Nb8 Bd3 f5 gxf6 Bxf6 Rhg1 Kh8 Qg2 Qxa5 Qg4 Nf4 Bxf4 exf4 Qh5 h6 Qg6 Kg8 Rde1 Be5 Qh7+ Kf7 Qf5+ Ke7 Rxg7+ Kd8 Rxe5 dxe5 Qxe5 Re8 Qd6+ Nd7 Qxd7#", 260 | "e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Be7 d4 exd4 Nxd4 Nxd4 Qxd4 O-O Nc3 d6 h3 h6 Bf4 b6 e5 c5 Qc4 Be6 Qa4 Nd5 Nxd5 Bxd5 Rad1 Bh4 Rxd5 Qe7 Rxd6 Qe6 Bxh6 Rfd8 Rxd8+ Rxd8 Qxh4 gxh6 Qxd8+ Kg7", 261 | "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6 dxc6 Nxe5 Bd6 Nf3 Nf6 Nc3 Bc5 h3 O-O O-O b5 d4 Bb4 Bg5 Be7 Qd2", 262 | "Nf3 c5 g3 Nc6 Bg2 e5 Nc3 e4 Nh4 d5 b3 Be7 Bb2 Bxh4 gxh4 Qxh4 Nxd5 Qd8 Bxg7 Qxd5 Bxh8 f6 e3 Ne5 Qh5+ Kf8 d4 Bg4 Qh4 Nf3+ Bxf3 Bxf3 Bxf6 Bxh1 O-O-O Bf3 Be5", 263 | "e4 e5 Bc4 c6 Nf3 d5 exd5 cxd5 Bb3 e4 d3 exf3 Qxf3 Be6 Nc3 Nf6 Bg5 Bg4 Bxf6 Bxf3 Bxd8 Kxd8 gxf3 Bc5 Bxd5 Nc6 Bxf7 Rf8 Bb3 Rxf3 O-O-O Rxf2 Ne4 Be3+ Kb1 Nd4 Nxf2 Bxf2 Rhf1 Be3 Rfe1 Bg5 Rf1 Kc7 Rde1 a6 Rf7+ Kb6 c3", 264 | "f4 e6 b3 c6 Bb2 d5 Nf3 Nf6 e3 Nbd7 c4 b6 d3 Bb7 Be2 c5 O-O Qc7 Nbd2 Be7 Qc1 O-O Ne5 Nxe5 fxe5 Nd7 d4 Bg5 Rf3 Rad8 Rg3 Bh6 Nf3 dxc4 Bxc4 cxd4 Bxd4 Nc5 Ng5 Qe7 h4 Kh8 Qc2 g6", 265 | "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 Bc4 Bg7 Be3 e6 Nc3 Nge7 O-O O-O f4 a6 a4 Nxd4 Bxd4 Nc6 Bxg7 Kxg7 f5 exf5 exf5 Qe7 f6+ Qxf6 Rxf6 Kxf6 Nd5+ Kg7 Qf3 f5 Qf4 b5 axb5 axb5 Rxa8 Bb7 Rxf8 Kxf8 Bxb5 Kg7 Bxc6 Bxc6 Qe5+ Kf7 Qe7+ Kg8 Nf6+ Kh8 Qxh7#", 266 | "e4 e5 Nf3 Nc6 Bb5 d6 Nc3 f5 d4 fxe4 Nxe4 Bg4 d5 a6 Bxc6+ bxc6 dxc6 Nf6 Nxf6+ Qxf6 Bg5 Qg6 Qd2 Qe4+ Qe3 Bxf3 gxf3 Qxc6", 267 | "e4 c5 d3 d6 f4 Nf6 Nf3 g6 Nc3 Bg7 Bd2 O-O Be2 Nc6 O-O a6 h3 b5 g4 Qb6 Qc1 b4 Nd1 a5 Kh2 a4 f5 Bb7 Nf2 a3 b3 e6 Rb1 exf5 gxf5 Ne5 Nxe5 dxe5 Be3 Qc7 Ng4 Nxg4+ Bxg4 Rfd8 Bh6 f6 Bxg7 Qxg7 Rg1 g5 h4 h6 hxg5 hxg5 Kg3 Kf7 Bh5+ Ke7 Bg6 Rh8 Qe3 Rh6 Qxc5+ Kd7 Rh1 Rah8 Qb5+ Kc7 Qc4+ Kb8 Kg2 Rxh1 Rxh1 Rxh1 Kxh1 Qh6+ Kg2", 268 | "e4 d5 exd5 Qxd5 Nc3 Qa5 d4 c6 Nf3 Bf5 Bd3 e6 Bxf5 Qxf5 O-O Qa5 Bd2 Nd7 Qe2 Ngf6 a3 Qc7 Rfe1 Bd6 h3 O-O Ne4 Nxe4 Qxe4 Nf6 Qh4 Rfe8 c4 Be7 Bf4 Qd8 Be5 Nd7 Qe4 Nxe5 dxe5 Qb6 b4 c5 b5 Red8 a4 a5 Red1 Qc7 Qe2 b6 Qc2 h6 Rxd8+ Rxd8 Rd1 Rxd1+ Qxd1 Qd8 Qxd8+ Bxd8 Kf1 f6 Ke2 Bc7 exf6 gxf6 g4 Kf7 Ke3 Kg6 Ke4 Kf7 Nh4 Bd6 f4 Be7 f5", 269 | "d4 Nf6 c4 e6 Nf3 Bb4+ Bd2 Qe7 g3 Bxd2+ Qxd2 Nc6 Bg2 d5 O-O O-O b3 Ne4 Qc2 f5 Nbd2 Bd7 Nxe4 fxe4 Ne5 Nxd4 Qc3 Nxe2+", 270 | "e4 c5 f4 Nc6 Nf3 d6 d3 Bd7 Be3 Qc7 Nbd2 O-O-O a3 e6 g3 Be7 Bg2 f6 O-O h5 b4 h4 bxc5 hxg3 hxg3 dxc5 Rb1 g5 fxg5 f5 Bf4 e5 Be3 f4 Bf2 fxg3 Bxg3 Bxg5 Nc4 Nf6 Nxg5 Rdg8 Qd2 Ng4", 271 | "e4 e6 Nf3 g6 d4 Bh6 Bd3 d6 O-O Bd7 c4 Qe7 Nc3 Nc6 d5 exd5 exd5 Ne5 Re1 f6 Bxh6 Nxh6 Qd2 O-O-O Nd4 f5 Qxh6 Ng4 Rxe7 Rhe8 Qxh7 Rxe7 Qxe7 Re8 Qg5 f4 Qxf4 Nxh2 Kxh2 Rh8+ Kg1 Rh5 Bxg6 Re5 Qf8+", 272 | "d4 d5 Nf3 Nf6 e3 Nc6 Be2 Bf5 a3 e6 O-O Bd6 Ne1 e5 c3 e4 f4 O-O c4 Qe7 Nc3 Be6 cxd5 Bxd5 Bg4 Bc4 Rf2 Kh8", 273 | "e3 e5 d4 Nc6 d5 Nb4 e4 a6 a3 Nxd5 exd5 c6 c4 cxd5 cxd5 d6 Nc3 b6 Nge2 Bb7 g3 Nf6 Bg2 Qc8 Bg5 Be7 Rc1 h6 Bxf6 Bxf6 Ne4 Qd8 Nxf6+ Qxf6 O-O O-O f4 exf4 Nxf4 Qe7 Re1 Qc7 Rxc7 Rae8 Rxe8 Rxe8 Rxb7 Re5 Bh3 Kh7 Qc2+ g6 Rxf7+ Kg8 Qxg6+ Kh8 Qg7#", 274 | "e4 c6 Nf3 Qc7 d4 Nf6 Bd3 d6 O-O Bg4 c4 Nbd7 Nbd2 e5 d5 Be7 Qc2 h5 h3 Bxf3 Nxf3 O-O b3 cxd5 exd5 Nc5 Bf5 e4 Ne1 a5 Be3 b6 Bxc5 Qxc5 Bxe4 Qd4 Bd3 Qxa1 Nf3", 275 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 d4 e6 Bd3 Bb4 O-O O-O Be3 Nd5 a3 Bxc3 bxc3 Nxc3 Qd2 Nd5 Ne5 Nxe3 Qxe3 Nd7 Qh3 g6 Ng4 h5 Ne5 Nxe5 dxe5 Qg5 f4 Qh6 Qg3 Bd7 f5 exf5 Bxf5 Bxf5 Rxf5 Kh7 Rff1 Rae8 Rab1 b6 Rbc1 c5 Rcd1 Qg7 Rd7 Rxe5 h3 Rf5 Rxf5 gxf5 Qf3 Qd4+ Rxd4 cxd4 Qxf5+", 276 | "e4 d5 exd5 Qxd5 Nc3 Qa5 d4 e5 Nf3 Bg4 Be2 Bb4 Bd2 Nc6 Nxe5 Bxe2 Qxe2 Nxe5 dxe5 O-O-O a3 Ne7 O-O Bxc3 Bxc3 Qb6 Rad1 Nd5 Bd4 Nf4 Qg4+ Qe6 Qxf4 Rd5 Bc3 Rhd8 Rxd5 Rxd5 h3 h6 Qg4 Qxg4 hxg4 Kd7 Re1 c5 f4 g6 Kf2 b5 Re2", 277 | "e4 c5 f4 g6 Nf3 Bg7 a3 f6 c3 Qc7 e5 fxe5 fxe5 Bxe5 Nxe5 Qxe5+ Be2 d5 O-O Nf6 d4 Qd6 Bf4 Qb6 Be5 O-O dxc5 Qxc5+ Bd4 Qd6 Nd2 Nc6 Bf2 Bf5 Nf3 Ng4 Nd4 Qxh2#", 278 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Nf3 Nf6 Be2 e6 O-O Be7 d4 O-O Bg5 b6 h3 Ba6 Bxa6 Nxa6 a3 c5 Qd3 cxd4 Qxd4 Qxd4 Nxd4 Rac8 Rad1 h6 Bh4 Nc5 Rfe1 g5 Bg3 Nh5 Bh2 Bd8 Ndb5 a6", 279 | "e4 c5 d3 Nc6 f4 e5 c3 d6 Nf3 Bg4 Be2 Bxf3 Bxf3 Nf6 O-O Be7 a4 O-O Na3 a6 Be3 Rc8 Nc4 b5 axb5 axb5 Nd2 Qd7 Qe2 h6 Ra6 b4 Rfa1 Rc7 Nc4 Rb7 fxe5 dxe5 Rxc6 Qxc6 Nxe5 Qe6 Nc4 Rbb8 e5", 280 | "e4 e5 Nf3 d6 Bc4 Bg4 O-O Qf6 h3 Bxf3 Qxf3 Qxf3 gxf3 Nc6 Nc3 Nd4 Bd3 c6 Ne2 Nxf3+ Kg2 Nh4+ Kg3 Ng6 Bc4 Nh6 d4 Be7 dxe5 Bh4+ Kh2 dxe5 Be3 b5 Bb3 O-O Bxh6 gxh6 Ng3", 281 | "g3 c6 Bg2 d5 e3 e6 Ne2 Nf6 d4 Be7 O-O O-O b3 h6 c4 b6 cxd5 exd5 Nbc3 Bb4 Bb2 Bxc3 Bxc3 Bf5 Nf4 Ne4 Bb4 Re8 f3 Nd6 Bh3 Bxh3 Nxh3 a5 Bxd6 Qxd6 Re1 c5 dxc5 bxc5 Nf4 d4 exd4 Rxe1+ Qxe1", 282 | "d4 d5 c4 c6 Nf3 Bg4 cxd5 cxd5 Qb3 Qd7 Ne5 Qc8 Nxg4 Qxg4 Qxb7 Qd7 Qxa8 Qc7 Nc3 e6 Bd2 Bb4 Rc1 Nf6 a3 Ba5 b4 Bb6 Nxd5 Qxc1+ Bxc1 O-O Nxf6+ gxf6 Bh6 Re8 e3 e5 Bb5 Rc8 O-O exd4 exd4 Bxd4 Qf3 f5 Qxf5 Rd8 Bd3", 283 | "c4 e5 g3 Nf6 Bg2 Nc6 e3 d6 Nc3 Be6 b3 Rb8 Nge2 a6 d4 exd4 exd4 Bf5 O-O Be7 Bg5 O-O Bxf6 Bxf6 d5 Ne5 Nd4 Bg6 f4 Nd7 Nde2 h6 Rc1 Bf5 Nd4 Bxd4+ Qxd4 Qf6 Qd2 Qd4+ Qxd4", 284 | "Nf3 d5 g3 Nf6 Bg2 c5 O-O Nc6 d4 cxd4 Nxd4 e5 Nxc6 bxc6 c4 Bb7 cxd5 cxd5 Nc3 Bc5 Bg5 Bd4 Bxf6 gxf6 Qb3 Bxc3 Qxb7 Bd4 Bxd5 O-O Qxa8 Qxa8 Bxa8 Rxa8 Rab1 Rb8 b4 Kf8 e3 Bc3 b5 Ke7 Rfc1 Ba5 a4 Rd8 Rd1 Rc8 Rbc1 Rb8 Rc6 Bb6 Kg2 Rg8 e4 h5 h4 Rg4 f3 Rg8", 285 | "e4 e6 f4 Ne7 Nf3 d5 exd5 exd5 Be2 Nf5 O-O Be7 d4 O-O Nc3 Nh4 Nxh4 Bxh4 Be3 Bf6 Qd3 c6 Bd2 Nd7 Rab1 Qc7 Nd1 c5 dxc5 Qxc5+ Be3 d4 Bf2 Qc7 Bxd4 Bxd4+ Qxd4 Nf6 Bd3 Bd7 Qf2 Bc6 Nc3 Qb6 Qxb6 axb6 a3 Nd5 Nxd5 Bxd5 c4 Bc6 f5 f6 Rbe1 Rfd8 Be4 Rd2 Bxc6 bxc6 Rf2 Rd3 Rf3 Rd2 Rf2 Rd4 Rf3 Kf7 Rb3 b5 cxb5 cxb5 Rxb5 Ra7 Ra1 Rd2 a4 Rad7 a5 Re7", 286 | "d4 Nf6 c4 g6 Nc3 Bg7 e4 O-O e5 Ne8 f4 d6 Nf3 dxe5 fxe5 f6 Be2 fxe5 dxe5 Qxd1+ Bxd1 Nc6 Bf4 e6 Bg3 Bd7 Bc2 Rxf3 gxf3 Nd4 O-O-O Nxf3 Rhf1 Nxe5 Bxe5 Bxe5 Nb5 Bxb5 cxb5 Nf6 Kb1 Nd5 Bb3 Ne3", 287 | "d4 b6 Nf3 Bb7 c4 Nf6 Nc3 g6 g3 Bg7 Bg2 O-O Nh4 d5 cxd5 Nxd5 Nxd5 Bxd5 e4 Bb7 Be3 e6 O-O Nd7 Rc1 Rc8 Qa4 a5 Qb5 Nf6 f3 Ra8 a4 Ba6 Qc6 Bxf1 Bxf1 Rc8 Ba6 Ra8 Bb7 Ra7 e5 Qxd4 Bxd4 Ne8 Qd7 Rxb7 Bc3 f6 Qe7 c5 Qxe6+ Kh8 exf6 Nxf6", 288 | "d4 f5 Nf3 Nf6 h4 e6 Ng5 d5 e3 c6 c3 Be7 Nd2 b5 Ndf3 a5 Ne5 Ne4 Ngf7 Qc7 Nxh8 Bf6 Qh5+ Kf8 Qxh7 Bxe5 Ng6+ Kf7 Nxe5+ Kf8 Qh8+ Ke7 Qxg7+ Kd8 Qf8#", 289 | "Nc3 Nf6 e4 d6 f4 g6 Nf3 Bg7 Bc4 c5 O-O O-O d3 Nc6 Kh1 a6 a3 b5 Ba2 Bb7 Nd5 Qc7 c4 Rad8 Rb1 b4 Nxc7", 290 | "d4 d5 c4 e6 a3 Nf6 Nc3 Be7 Bf4 Nh5 Be5 f6 Bg3 Nxg3 fxg3 dxc4 e4 c5 d5 O-O Bxc4 a6 Nf3 b5 Be2 c4 O-O Qb6+ Kh1 e5 Nh4 Rd8 Bg4 Bb7 Nf5 Bf8 Ne3 Nd7 Be6+ Kh8 Nf5 Nc5 Qg4 Nxe6 dxe6 Qxe6 Rad1 g6 Ne3 Qxg4 Nxg4 Rxd1 Rxd1 f5 exf5 gxf5 Nxe5 Bg7 Nf7+ Kg8 Rd7 Bc6 Rc7 Be8 Nd6 Rd8 Nxf5 Rd1+ Nxd1", 291 | "e4 e5 Nc3 Bb4 a3 Ba5 b4 Bb6 Na4 Nc6 Nxb6 axb6 Nf3 d6 Bc4 Bg4 h3 Bxf3 Qxf3 Qf6 Qb3 Nd4 Qa2 Qg6 O-O b5 Bd5 c6 Bb3 Nf6 d3 O-O Be3 Nf3+ Kh1 Nh4 Rg1 Qh5 c4 Ng6 Bd1 Qh4", 292 | "d4 g6 e3 Nf6 Bd3 d6 h3 Bg7 f4 b6 c4 Bb7 Nf3 O-O O-O c5 d5 Qc8 Nh4 e6 e4 h6 Nc3 a6 f5 gxf5 exf5 exf5 Nxf5 Qe8", 293 | "e4 e5 Nf3 d6 Bc4 Bg4 Nc3 Bxf3 Qxf3 Nf6 Nd5 Nbd7 Nxf6+ Nxf6 d3 Qd7 Bg5 Be7 Bxf6 O-O-O Bxe7 Qxe7 Bxf7 Rhf8 Qf5+ Qd7 Qxh7 Qxf7 Qh3+ Kb8 O-O-O Qf4+ Kb1", 294 | "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6 bxc6 Nxe5 Qe7 d4 d6 Nf3 Qxe4+ Be3 Nf6 Nc3 Qg6 O-O Bf5 Nh4 Qh5 Qxh5 Nxh5 Nxf5 g6 Ng3 Nxg3 fxg3 Bg7 Rae1 O-O", 295 | "e4 a6 Nf3 b5 Be2 Bb7 e5 Nc6 O-O e6 d4 Bb4 c3 Ba5 Bg5 Nge7 a4 h6 Bxe7 Qxe7 axb5 axb5 Bxb5 O-O Nbd2 Bb6 Qc2 Ra5 Bd3 Rfa8 Rxa5 Rxa5 Nc4 Ra2 Nxb6 cxb6 Qb3 Ra8 Qxb6 d5 Qb3 g5 h3 Na5 Qc2 Ba6 Bxa6 Rxa6 Qa4 Qb7", 296 | "e4 d5 exd5 Qxd5 Qf3 Qe6+ Qe3 Nc6 Qxe6 Bxe6 c3 Bf5 d4 O-O-O Be3 e5 dxe5 Nxe5 f3 b6 Nd2 Nd3+ Bxd3 Bxd3 Kf2 Nf6 Ne2 Bd6 Nb3 Rhe8 Rad1 Bc4 Nbc1 Nd5 Bd4 c5 Bxg7 Bxe2 Nxe2 Kc7 Rxd5 Bg3+ hxg3 Rxd5 Bh6 a5 Bf4+ Kc6 Rh6+ Kb7 Rxh7 Re7 g4 Red7 g3 Rd1 Be3 Ra1 a3 Ra2 Bc1 Ra1 g5 Kc6 Rh6+ Kb5 b3 Rb1 c4+ Ka6", 297 | "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 h3 Bg7 Nf3 O-O Bd3 a6 a4 Re8 O-O Nbd7 Bf4 Qc7 Rc1 Nh5 Bh2 Ne5 Nxe5 Bxe5 Bxe5 Rxe5 f4 Re8 e5 dxe5 f5 c4 Ne4 Bxf5 Rxf5 gxf5 Qxh5 fxe4 Qg5+ Kh8 Qf6+ Kg8 Bxe4 Qe7 Qf5 f6 Rc3 Kh8 d6 Qf7 Rxc4", 298 | "e4 e5 Bc4 Nf6 Nc3 Bb4 Nd5 Nxd5 Bxd5 O-O a3 Ba5 b4 Bb6 Qe2 c6 Bb3 d5 exd5 cxd5 Nf3 Bg4 h3 Bh5 g4 Bg6 d3 Nc6 Be3", 299 | "d4 d5 c4 Nf6 Nc3 Bf5 Nf3 Nc6 e3 e6 Be2 Bb4 O-O O-O cxd5 Nxd5 Bd2 a5 a3 Nxc3 bxc3 Bd6 c4 Qf6 c5 Be7 Rc1 Qg6 Kh1 Rfe8 Qb3 Bf6 Qxb7 Be4 Qxc7 e5 Qd7 exd4 exd4 Nxd4 Rfe1", 300 | "c4 e5 Nc3 Nf6 d3 Ng4 e4 f5 exf5 Bb4 Qxg4 h5 Qe2 d5 Qxe5+ Qe7 Qxe7+ Kxe7 Bd2 Bxf5 Nxd5+ Kd6 Nxb4 b5 g3 bxc4 Bg2 cxd3 Bxa8 Re8+ Kd1 h4 gxh4 c5 Nxd3 c4 Nf4 Ke5 Nge2 c3 bxc3 Nd7 Bc6 Kd6 Bxd7 Kxd7 Rb1 a5 Rb5 a4 Rxf5 a3 Ra5 Rb8 Rg1 Rb1+ Kc2 Rb6 Rxg7+ Kd6 Rg6+ Kc7 Rxb6 Kxb6 Rxa3 Kb5 c4+ Kxc4 Rd3 Kc5 Ne6+ Kc4 Rd8 Kb5 Kc3 Ka4 Kc4 Ka3 Rb8 Kxa2 Kc3 Ka1 Kc2", 301 | "c4 Nf6 Nc3 c5 e3 d6 d4 cxd4 exd4 a6 Nf3 Bg4 Be2 Bxf3 Bxf3 Nc6 O-O e6 Bg5 Be7 a3 O-O b4 e5 b5 axb5 cxb5 Nxd4 Bxb7 Rb8 Ba6 Qa5 Bd2 Nb3 Ra2 Nxd2 Qxd2 Qb6 a4 Nh5 a5 Qc5 b6 Nf4 Rb1 Qc6 f3 d5 Bb5 Qc5+ Kf1 d4 Ne4", 302 | "d3 Nf6 g3 g6 Bg2 Bg7 Nf3 O-O e3 d6 h4 h5 Nh2 c5 Nc3 a6 f3 Nc6 g4 hxg4 fxg4 Qd7 Bd2 Nxg4 Nxg4 Qxg4 Qxg4 Bxg4 Bh3 Bh5 Ne2 e6 Nf4 Bf3 Rg1 e5 Ne2 Nb4 Bxb4 cxb4 Ng3 f5 Rf1 Bg4 Bxg4 fxg4 O-O-O Rxf1 Rxf1 Rf8 Rxf8+ Bxf8 Ne4 Be7 Ng5 Bxg5 hxg5 Kf7 Kd2 Ke6 Ke2 Kf5 Kf2 Kxg5 Kg3 e4 dxe4 Kf6 Kxg4 b5 Kf4 a5 e5+ dxe5+ Ke4 a4", 303 | "e4 e5 Nf3 Nc6 Bc4 d6 Nc3 Nf6 d4 exd4 Nxd4 Bd7 Bf4 Be7 O-O O-O Nf3 Nh5 Be3 Bg4 h3 Bxf3 Qxf3 Ne5 Qxh5 Nxc4 Bc1 Bf6 Re1 Ne5 Bf4 Ng6 Re3 Bd4 Rg3 Nxf4 Qg4 Ng6 Rd1 Bxc3 bxc3 Qe7 Rd5 Qe6 Rg5 Qxg4 R3xg4 f6 Rh5 Ne5 Rf4 g6 Rhh4 g5", 304 | "d4 d5 c4 dxc4 Nc3 Nf6 Nf3 e6 a3 Nc6 g3 Be7 Bg2 O-O O-O Bd6 e4 e5 dxe5 Nxe5 Nxe5 Bxe5 Qxd8 Rxd8 Bg5 Rd6 Nd5 Nxd5 exd5 b5 Be7 Rd7 Bb4 Rxd5 Bxd5 Ba6 Bxa8", 305 | "e4 e5 Nf3 Nc6 c3 Nf6 d3 d6 Bg5 Bg4 h3 Bxf3 Qxf3 Be7 Bxf6 Bxf6 Nd2 Qe7 a3 O-O-O b4 Bg5 Nb3 b6 a4 h5 b5 Na5 Nxa5 bxa5 Be2 Qe6 O-O", 306 | "d4 e6 c4 d5 Nf3 c6 Nc3 Bd6 e3 f5 Bd3 Nf6 Qc2 O-O O-O Bd7 c5 Bc7 b4 Be8 Ng5 Ne4 Nxe6 Qh4 Nxf8 Qxh2#", 307 | "e4 b6 d4 Bb7 Bd3 e6 c4 Bb4+ Nc3 Bxc3+ bxc3 h6 Nf3 Nf6 Qe2 O-O O-O d6 h3 Nbd7 a4 e5 Re1 a5 Nh2 Nh7 d5 Nc5 Bc2 Bc8 f4 Qh4 Rf1 Nf6 fxe5 dxe5 Nf3 Qg3 Kh1", 308 | "e4 d5 exd5 Qxd5 Nc3 Qd8 Bc4 Nf6 d3 Bg4 f3 Bf5 Be3 e6 Nge2 c6 Ng3 Bg6 Qd2 Bd6 Nce4 Nxe4 Nxe4 Bc7 Bb3 Ba5 c3 Bxe4 fxe4 O-O O-O-O b5 h4 Bb6 d4 Nd7 g4 c5 g5 cxd4 Bxd4 Bxd4 Qxd4 Nb8 Qe3 Qc7" 309 | ) 310 | 311 | val v1 = List( 312 | "7qasJezzPJK15lj9CbbYheEA63S9DE37qYM/HcONsibhbJM/2xJqSwr/nVAX79Rn3x/vsAA=", 313 | "KjTb/Zzt6FTIF/lVyHjtbeOzYeV9uhNzDfuV/699pPx/1XWiwVs31MA=", 314 | "Mp0orWLvti0lxmh6kBmGf5IqTYEAdXvgx/3Jnivwhju9A6ImWcvOcc9n1FmEwA==", 315 | "MhU6x0SImzC1OgAhmyHHSZLcNtUGucvp9TLlpoA=", 316 | "PDdknk9du7oA11Y1tRdCpolRK+yysDyJ9z1Q", 317 | "s3sbOnTq9vX15Npv7x4fJ97xFroPTbOLG+n9Q3639s5WH/7BQA==", 318 | "Hw15mn6XrBtZGTjK0A==", 319 | "Ugpnwa0n6QjIy8kUOHvF4vWAfNWGLlSpu4AGDXj+CUAfbz9YqN7Jq1sqLU2rqQmcfA==", 320 | "19Sn95fufWzCaFjfjA==", 321 | "Hw3FxvG9IvO817llUSMRAfV5xvfz/7Ez4kfrQWMgfg1NZz1n1D9gW0AqLQ==", 322 | "LM7AHuV0bWvXpOGmfCsWvct0pUMzO1AV0XTqwBX68pFIq0XP0rvp2g==", 323 | "zkmxSlt9/tqJ03f9B/Lbv8WnSSTNFLf11Wuz8dxe1//QCb9wv4i1/3JwIZWzXWaTwgMK9vGUr/ZVqt91WbzrlUA=", 324 | "CGtPQ+eYf80+X/0C63rNmndIuJCBKzcx5b4u4nm3ha8Ul3updJ5mjhqLG4eUexdgdHNLHYWqpOg=", 325 | "k8XmH/XqWTUtzb0FfAEODSU9gT2plavcE2l1IS+2kEcMmOdTr4P22vtsvWFnPMz9Zf3Ly1mBM0VfUV4TK/ZCZzu3AA==", 326 | "k+fO9m7ugAzYRzplfkRc++fc6BmXoIFCkQ9zO7c6", 327 | "BUL1i68rpzjsWPUJ2Pdk92pZJ6nCGB72yZwpuLCz0C7bn9wXPruaw0gbdav0rZ1ffFWg6A==", 328 | "LOthhPsUt3Z477iYLiHYfBdAEvKxfMyDjTPrr0K7AhTjndfDLSzmoMa8tqkb30g=", 329 | "AnSG1kzU5Yq/3W1r8pc++W3e8G/rdkvOFn93x/v+rkwd0PU11u9ULwgHmeCdv3BHXteI712hWYNcoA==", 330 | "PDfxJxP78de/6HhPgcrkATDkByNP67npuEF2FeGmmgiA", 331 | "mArWBeET583tvuNqHEQkAbLGO4J/OeGnlzebr+lIgaBv7cA=", 332 | "ty73VQUu24W1KDl7tqeOC+TKoueDkJ+WLfYnAkA=", 333 | "MF7OrJjpv1PueNH5uAZ4958JYA==", 334 | "KnCPQhSuzsnzxJbZrPH/mK3pkFuPLbPWc98A", 335 | "ysrQlz6bg1S5bgjzeeXJQA==", 336 | "LFb0KkXcNZ3f2PBtQhABf4A=", 337 | "Bg1PYNwxwIj/tXo=", 338 | "KmPn/CTPMXaQz7Uae8eQ4uevEMA=", 339 | "A618/WLNgHe98/vso2VXG9HicZOPF3sqzyMg", 340 | "kvv8yK3ZWdJ+Iw/Bbb+r1P9r7H0w3LUN/gza+Y7c9wRAHZA=", 341 | "k/0760KrEmRDhIzLFWHxrVuiH9Kr3+50x038r7eBXSSkP99u68l7WrvrGTlFQf6QKoMIXXUdWNE7AA==", 342 | "Andh9gQNgU0xiZBfIqcgy86PbN9q/+SAuig=", 343 | "K+8IExxnDUMbfTwtmne8O0su", 344 | "CVSUMTl6Sc8/JxJ+pfX1Xa7lah9meFtCcE9FwUA=", 345 | "kDe8/E+orRd88dwxtGhpTW9ARh5rEb/mYc4gmoA=", 346 | "LNob+KTfOVRolcRnunH82UEhJgrmfdm1znTvc+Bj8NIM1PQC1GHviouxfWOuL2gdrjoXyqS4", 347 | "VeEdjtGlsxcoa8Utvm92IsOEa866aNNLKkLxsA==", 348 | "qCefpoSA3lN73H4DrIi8nznXtZhWpe47p0iSzXItfUA=", 349 | "Hw3Gq73PyY3AB+unnhf0ucss", 350 | "kN+zTf+9SWT92zEowhgma9xPUYw=", 351 | "PBf5IfZfEMt13fvnNv5H38D16TuLwbXtaevbTQ==", 352 | "WGHCpp5fvgWYfrjTWMjJL8VKerZcArLrTuM12HsGwZ756ZZKwtz6OLf+Qdct0L7EveqUsA==", 353 | "KPwAiyY70yvnGX5k+YV/2S7n/VpPi9aA", 354 | "yCVgblV+5/46gWBj3m0/sxpu/kw2mIS118A=", 355 | "LI2BEkK/7hW4MfwSJF6A", 356 | "PDfxISLpH9w5P6PcHee9xw1ecXry8azxeIh/+MhwB+5iKMg=", 357 | "AKXNwe8rYUQnjecT", 358 | "Mijq//lj3oo9eBUDTsjcpBiZAnhnUgB7tu4A", 359 | "KPzZxNGQT1m2uxXaT8vP57BM2Av+FPkDJoxyeA/00A==", 360 | "KKlErsNbydzbX/cHneNuv2SepoijLP4Gl79+gA==", 361 | "ijQARzN4ef+W+bKHatW6bfjv17zlvQoB7dL/+vg0jvcLgA==", 362 | "Hw3/RL0zFev0EarLk7tLPO80aL0=", 363 | "Ar8BUYsLNYtZ8NrZA8/24fP35eGc1r1t+1nluSNy5A==", 364 | "Cc1+r2sEPuqnP64hvbaJANL98J6A", 365 | "KMeJCkLZxEJA0vfbz905zbTT635tQhDf3+rg", 366 | "PP2up0ynHt0htYcq6g==", 367 | "t3q/4EMWf6sv5lPOqqKb4I+GgA==", 368 | "wW+5xenkBqqSUBxw51HLWE/rnlVRT4+Bu+oW3I33qN1Q", 369 | "FjvmdkNJVB5+ZBO/Nf5b4Uxzexnq5edsx8Q=", 370 | "yrTf297tPr69zzvTWISECwPNwfgD4A==", 371 | "k+bvxZIbt0+pHi14k996oAV+85eGH/GdzCbIcfcaV/D8k/fEctOPbA==", 372 | "DGoG0zKWZ3Ri5UseardCAA==", 373 | "yNGyvxLz6rDSYlDJxgWfgA==", 374 | "AlLU3aquoXcZKTWrbo8hJWj7XP/hAA6+w958dn9g7uphyLQ=", 375 | "AnSvMbff/5P/TQW75A==", 376 | "PBf5IW1ElYqorh374xIv7ux2P9VkmXDA7Va/h1bMsZ3SKFeGgA==", 377 | "AnTzvDYHVmTFtxHydu/Lk5/R09nkJDQ=", 378 | "AncICtSWO5R+idYO", 379 | "3LKlNwT2x3SA/CYnfbjy7NIBG+uA", 380 | "BfkxwR9+wWh31do2pC7h8q0UHVH7PMny+KA=", 381 | "zqzheaUYv1au7CqrC6wsSW2s5sduyr8H+CVj0A==", 382 | "PDfxH171aqzx7y0S4PCb6pBTbe/ANIaTdQNE", 383 | "And38kC6LaCaV9dzuwA=", 384 | "PKX6DR2s9l8qbzZz61Zn797XGvrxshsFz90X9xBb37yb9ep6zlfxLfcaoNAnkA==", 385 | "Hw3FMUvXQy/dXu2s53zuhEtcTXf/L/Sbj4VvmbL2fO3CQcHee6F+b+dpVve4", 386 | "g1JW3bIVjsu9rz7XYKZw", 387 | "PGobF3d9dE6jaGJP+yCEu4/aR8akFFy9/A==", 388 | "Kn0wuuDGvJvHmuyxQQ/1x+HAO8UkcZjngA==", 389 | "kDS9c7zX14f2YOa7LSctZcc=", 390 | "Ux1gmtqcnDGFbj2m5uEdY0B7B+72GGdzdIhJn6oBaA==", 391 | "LIpO1gr+zHwWm1oZ9k3NHFbX7YYLoA==", 392 | "Hw15mlUuGWs52FMgbi2Yvmg5Ozu8QAm27/++feV2Nt6X9AA=", 393 | "Hw3FHmesQ7pDTLDtujGKhNkGkXmsdhDMyRnh5cA=", 394 | "PGsBef76/gAFdX35X7zII3VJOs4uYA==", 395 | "Hw15muXlxnv/mwEfKIGTLPmq1XxK/8zA", 396 | "PK38njqKyEf191+aZvDR72/lnF/8dzoBkn6w", 397 | "Aqu5uYBJFv8cH6q7TUjQb/8RscA=", 398 | "tbvq0pKw8Tzx/STLSbF/O4rP0loMSBOOaA==", 399 | "k+fZcHe+tUTJaqve4V5x/9ap3TPtZ8+tgdUuwA==", 400 | "yLDopokZvj0PPspQvBnxtK6xgB+8AA==", 401 | "3cIv1EaJIPehs793eyvZ5VQ8Zk3LI/z/bQtjLOzg7X38sag=", 402 | "K/7LAczy54Y4TVa+ROYn6Te8FIUzvFVZQ80G84nF3TV/0rGqxuSgf2qwdHA=", 403 | "g15tDk7MXCB+RJJpSKXTU5mQT0rd7YA=", 404 | "mE7/kser33E8CE1uut9fzQ/YuIeYrV5rMEMzQFv4bztZNgA=", 405 | "ngW6+L9e7v9CaENOjMwrsImE/A==", 406 | "qC/W53U/M5PmXjfyr08sfeYA", 407 | "k+K/u9Xh5jwkDGkzLdbe+5+1SbPF3/4vZld0Ijj8zZE4F/53rml7GgA=", 408 | "CXyx/+8NAXes0vnfG37yxKzsZOOcGP8F4Pg=", 409 | "mSl6sd++fLz19Kjl78v5vnFugwA=", 410 | "Aqu6BVSbVSeQl77Iw/9A", 411 | "AncJDmtxgp+9rG1IYJki3A==", 412 | "Gb5fqN92K1wXqTIAHat075BvSXYarXeHUwYg", 413 | "HwRZVB+8EdSBwxO/gzJtnefuJvzikOAkk+JY6wGuu/Zu/od+w8/219zQAA==", 414 | "g03g3QQ/JqZVa0zBYvmN6pCPo4piXKbAZFJ5z+5+qiA=", 415 | "BTers9z+p1zbk+G99SNLW0A=", 416 | "k+Te7N29ZINd882eyt4Z2flvRzHg3AbA", 417 | "yNKyHpcbseyQ9JfUnydJ2nmfh1iScNlQLWudN++/rRBXFoCqZnNZmIlSFheA", 418 | "ylZ7XEFp1gDUqZO4fsQ0DPFR8xWqmf/i6mveCeIu9/bz0A==", 419 | "7P9qO6ilfsxp1Y9nwnl4ANJG6VfJ8tD1/nKGAKG64EL7t9EIIXXqlKA=", 420 | "Albpi7O7qd0nDJ2QVpxky7SfzOwR+vIuDGbH2F3+qA==", 421 | "k+GyV5v1e84dMcCE22afckIDmA==", 422 | "AjRJW+QgSMR9fZwwbXHpXXA=", 423 | "lJ/o9i7j3b+eVTb/jdx/YA==", 424 | "MBXVjuQzk9ZbSKw/IzSfmL9/8WX+4bn+", 425 | "Hw15XVdxZr3eLz6xem8XLy8NLfv1/u0n/U0ifPQ=" 426 | ) 427 | --------------------------------------------------------------------------------