├── .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/#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 |
--------------------------------------------------------------------------------