Problems with TT, sometimes makes blunder moves

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

AlvaroBegue
Posts: 931
Joined: Tue Mar 09, 2010 3:46 pm
Location: New York
Full name: Álvaro Begué (RuyDos)

Re: Problems with TT, sometimes makes blunder moves

Post by AlvaroBegue »

I am going to start a third front in this war (but probably not engage further in the conflict).

I don't think performance or flexibility are the main things to strive for in code (although they are good to have). The main measure to optimize is readability. This is probably a result of having spent a lot of time debugging other people's code under time pressure, sometimes in the middle of the night.

I have to say that HGM's code scores fairly low in readability. I do understand how the using "slot^=1" in between the sequence points introduced by "&&" works, but it takes some effort to get through it.

I would much rather see something like this:

Code: Select all

int64_t find_in_bucket(int64_t key) {
  int64_t entry = key & hashMask;
  for &#40;int slot = 0; slot < 4; ++slot&#41; &#123;
    if &#40;hashTable&#91;entry ^ slot&#93; == key&#41; // ^ instead of + to stay inside a cache line
      return entry ^ slot;
  &#125;
  return -1; // meaning "not found"
&#125;
mar
Posts: 2554
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: Problems with TT, sometimes makes blunder moves

Post by mar »

Sven Schüle wrote: I am certainly able to understand C code, even if it looks like that:

Code: Select all

int slot=0;
HashEntry *entry = hashTable + &#40;key & hashMask&#41;;
if&#40;entry&#91;slot&#93;.lock == key || entry&#91;slot^=1&#93;.lock == key ||
   entry&#91;slot^=2&#93;.lock == key || entry&#91;slot^=1&#93;.lock == key&#41; &#123; // hit
  entry += slot;
  ...
&#125; else &#123; // miss
  slot = -1;
&#125;
I simply don't like it that way. A simple loop does the same since the compiler will unroll it due to the number of slots being a compile time constant. Updating the slot variable with some XORs may be a nice hacker trick but why invest energy if an obvious solution is available?

Code: Select all

inline HashEntry * ttProbe&#40;uint64_t key&#41; &#123;
    HashEntry * entry = hashTable + &#40;key & hashMask&#41;;
    for &#40;int slot = 0; slot < 4; slot++) &#123;
        if &#40;entry&#91;slot&#93;.lock == key&#41; &#123;
            return &&#40;entry&#91;slot&#93;);
        &#125;
    &#125;
    return 0;
&#125;
You like to omit things, don't you?
Those ... in hgm's listing do some logic, where do you handle bounds?

Why mislead people? That's not how tt probing works and you know that very well.

(also will it still unroll with extra code inside?)
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Problems with TT, sometimes makes blunder moves

Post by Sven »

mar wrote:
Sven Schüle wrote: I am certainly able to understand C code, even if it looks like that:

Code: Select all

int slot=0;
HashEntry *entry = hashTable + &#40;key & hashMask&#41;;
if&#40;entry&#91;slot&#93;.lock == key || entry&#91;slot^=1&#93;.lock == key ||
   entry&#91;slot^=2&#93;.lock == key || entry&#91;slot^=1&#93;.lock == key&#41; &#123; // hit
  entry += slot;
  ...
&#125; else &#123; // miss
  slot = -1;
&#125;
I simply don't like it that way. A simple loop does the same since the compiler will unroll it due to the number of slots being a compile time constant. Updating the slot variable with some XORs may be a nice hacker trick but why invest energy if an obvious solution is available?

Code: Select all

inline HashEntry * ttProbe&#40;uint64_t key&#41; &#123;
    HashEntry * entry = hashTable + &#40;key & hashMask&#41;;
    for &#40;int slot = 0; slot < 4; slot++) &#123;
        if &#40;entry&#91;slot&#93;.lock == key&#41; &#123;
            return &&#40;entry&#91;slot&#93;);
        &#125;
    &#125;
    return 0;
&#125;
You like to omit things, don't you?
Those ... in hgm's listing do some logic, where do you handle bounds?

Why mislead people? That's not how tt probing works and you know that very well.

(also will it still unroll with extra code inside?)
I don't understand your comment. HGM's code has lots of "..." in it so what did I omit that he didn't? I don't see where his code snippet (which was obviously not meant as a complete solution for TT probing - the probing code in my own engines is slightly longer as well) handles bounds, he only shows how one could deal with accessing TT entries without introducing an additional "cluster" level, which is perfectly fine for me (only the implementation wasn't ...). Providing a completely functional TT probing code was not the point of HGM, nor was it mine. So it was also not my intention to "mislead" anyone, I don't think anyone could have actually understood my comments that way. If you did so then I apologize for that.
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Problems with TT, sometimes makes blunder moves

Post by hgm »

Well, this seems just standard C idiom. I often see code (from others) that looks like

Code: Select all

if&#40;condition1 && &#40;kind=1&#41; ||
   condition2 && &#40;kind=2&#41; ||
   condition3 && &#40;kind=3&#41;   ) &#123;
&#125;
where you need to do very similar things for all 3 conditions (assumed to be complex expressions), but still will need to know for some details which condition triggered it. The probing code I showed is just a streamlined version of that, where the side effect required to identify the condition can be done inside the [] of the array index, so that it doesn't need an extra && operator.

Requiring maintainability is one thing, but requiring maintainability by someone not fluent in the langage in question quite another. The latter path would lead you to allowing only use of assignment statements and (possibly conditional) gotos, because using 'for' and 'while' constructs might make the code too difficult to maintain for people that only know BASIC...

It is a sad fact of life that advanced algorithms are usually more complex than inefficient straightforward ones. Qsort is more difficult to understand than bubble sort. In Chess programming speed is one of the important goals; if you write the most beautiful, elegant and maintainable code for a Chess engine, but to do it you need to use the most inefficient and simplistic algorithms to achieve that, so that it will never do more than 10knps, it will be of zero use to anyone.

Hash probing is an important performance bottleneck. (E.g. micro-Max 1.6, without hash table, does 6 Mnps, micro-Max 4.8 only 1 Mnsp on the same machine.) So it doesn't seem entirely crazy to pay at least some minimal attention to avoid handling it in a needlessly inefficient way. To the uninitiated it might seem that looping through the 4 entries of a cache line is as good a method as any other, but in fact it isn't: cache lines of 64 bytes are loaded from memory in burst of eight 8-byte words, with a significant delay between them in terms of current CPU clock speeds. So each iteration of the loop will likely be waiting for the next memory word to arrive, before it can test for a key match. So there is an incentive to minimize the number of comparisons you need to find the hash hit on probing. And for the case of a miss, where you cannot avoid going through all possible storage locations, to minimize the number of possible storage locations. You gain significant speed by not storing a position in an arbitrary location in a large bucket, so that you have to search without a clue through the bucket to find it back on a later probe.

The XOR method of stepping through the entries is not just to be quirky; it reflects the way the memory hardware fetches the words from memory in a burst access. If you read from address 64*N + 5 (say) you get the words in the order 5, 4, 7, 6, 1, 0, 3, 2 (i.e. as a normal counter that counts 0-7 XORed with the low bits of the originally requested address). So if you have a replacement scheme that can arrange it such that you can guess where in the bucket the entry you are looking for will be with (say) an 80% probability (and it is not possible that this then would always be in the first entry of the bucket!), so that you want to start probing somewhere in the middle of the bucket, you have to deal with the funny order the words will arrive to the CPU. Then, when your first guess (5) was wrong, but the sought position happens to be in the entry formed by words 6 & 7, you won't have to needlessly wait for words 2 & 3 to arrive, which would happen if you tested for a hit on (2 & 3) before you tested for a hit on (6 & 7). This is why my sample code tested the entries in the order it did.

So it seems to me that you blame complexity due to use of a more performant algorithm on the style of the code. This is like looking at qsort, and complaining that bubble sort is much more readable and easy to understand. Which no doubt is true, but misses the point. The value of qsort can only be understood on an entirely different level.

The code Alvaro gives indeed does access the elements in the same order as I used. To me it doesn't seem more readable than the unrolled version; it defenitely requires more code lines. And it still doesn't fit Sven's original requirement that it should also work with buckets of 3. (Which mine did not either.) OTOH, a probing like 0, 2, 3 or 1, 3, 2 (with almost-replace entries in 0 and 1, and depth-preferred in 2 and 3), depending on the 'primary hit location being 0 or 1, and limiting the number of probes to 3, is still easiest done with an unrolled loop. (This would use a mask = hashSize - 2.)
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Problems with TT, sometimes makes blunder moves

Post by hgm »

Actually it might be competitive to have a scheme that can rule out hits in secondary locations based on the first word read from the bucket. E.g. store 5 bytes of the signature of the primary entry, and then the 4th byte of the signature of the two always-replace entries. As the low-order 3 bytes of the hash key are used to derive the index, and thus are redundant information, so you only need a 5-byte signature. The depth-preferred entries then only have to store the top 4 bytes of the key as signature. By first comparing if the 4th byte of the key matches, you would only get a false hit (which will be rejected when you get the remaining 4 bytes in the other memory word) in 0.4% of the cases.
tttony
Posts: 268
Joined: Sun Apr 24, 2011 12:33 am

Re: Problems with TT, sometimes makes blunder moves

Post by tttony »

Well I just killed the bug and guess what, it wasn't the TT :D

It was something that I'm shame to tell :oops: but anyway

Apart from adding the TT, I also had to implement a new move gen, to put the TT move first in the list, but I just forgot to init the hash_move variable, so when in the capture or quiet stage

Code: Select all

// loop ...
if &#40;curr_move == hash_move&#41;
   continue;
So, some moves were skiped

Weird that in debug will not blunder but in release

Here is the log from the HGM position, I let all night running

Code: Select all

FEN&#58; 4k3/8/8/8/8/8/4P3/4K3 w - - 0 1 

Skiull 0.2 x64 popcnt&#58;
   1	00&#58;00	           7	7		+1,20	e2-e4
   2	00&#58;00	          24	24		+1,10	e2-e4 Ke8-d7
   3	00&#58;00	          93	93		+1,20	e2-e4 Ke8-d7 Ke1-d2
   4	00&#58;00	         219	219		+1,10	e2-e4 Ke8-d7 Ke1-d2 Kd7-c6
   5	00&#58;00	         639	639		+1,20	e2-e4 Ke8-d7 Ke1-d2 Kd7-c6 Kd2-c3
   6	00&#58;00	       1.733	1.733		+1,00	e2-e4 Ke8-d7 Ke1-d2 Kd7-d6 Kd2-c3 Kd6-e5
   7	00&#58;00	       6.526	6.526		+1,40	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4
   8	00&#58;00	      12.361	12.361		+1,40	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6
   9	00&#58;00	      26.845	26.845		+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-c6 Kc3-d4 Kc6-d6 e2-e4 Kd6-c6
  10	00&#58;00	      70.582	70.582		+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-c6 Kc3-d4 Kc6-d6 e2-e4 Kd6-e6 e4-e5 Ke6-f5
  11	00&#58;00	     178.325	11.145.313	+1,45	Ke1-d2 Ke8-d7 e2-e4 Kd7-c6 Kd2-c3 Kc6-c5 Kc3-d3
  12	00&#58;00	     480.472	10.222.809	+1,45	Ke1-d2 Ke8-d7 e2-e4 Kd7-c6 Kd2-c3 Kc6-c5 Kc3-d3 Kc5-c6 Kd3-d4 Kc6-d6 e4-e5+ Kd6-c6
  13	00&#58;00	   1.389.870	11.118.960	+1,50	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-c6 Kc3-d4 Kc6-d6 e2-e4 Kd6-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-f4 e5-e6
  14	00&#58;00	   3.947.335	10.526.227	+1,50	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-c6 Kc3-d4 Kc6-b6 e2-e4 Kb6-c6 e4-e5 Kc6-d7 Kd4-d5 Kd7-e7 e5-e6 Ke7-f6
  15	00&#58;01	  16.400.867	10.513.376	+1,50	Ke1-d2 Ke8-f8 e2-e3 Kf8-e7 Kd2-c3 Ke7-d6 Kc3-d4 Kd6-e6 e3-e4 Ke6-f6 e4-e5+ Kf6-f5 Kd4-d5 Kf5-f4 e5-e6
  16	00&#58;04	  44.658.199	10.149.591	+1,50	Ke1-d2 Ke8-d7 e2-e4 Kd7-c6 Kd2-c3 Kc6-c5 Kc3-d3 Kc5-c6 Kd3-d4 Kc6-d6 e4-e5+ Kd6-d7 Kd4-d5 Kd7-e7
  17	00&#58;16	 169.401.854	10.593.575	+1,50	Ke1-d2 Ke8-d7 e2-e4 Kd7-d6 Kd2-c2 Kd6-c5 Kc2-d3 Kc5-b5 Kd3-d4 Kb5-c6 e4-e5 Kc6-d7 Kd4-e3 Kd7-c7 Ke3-d4
  18	01&#58;05	 669.401.218	10.158.604	+1,60	Ke1-f2 Ke8-d7 Kf2-f3 Kd7-c6 e2-e4 Kc6-b5 e4-e5 Kb5-c5 Kf3-e4 Kc5-b6 e5-e6 Kb6-c7 Ke4-f4 Kc7-d6
  19	05&#58;19	3.467.365.343	10.864.988	+1,55	Ke1-f2 Ke8-d7 Kf2-e3 Kd7-e7 Ke3-d3 Ke7-d6 Kd3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+ Kd6-c6 Kd4-c4 Kc6-d7 Kc4-d4 Kd7-e6 Kd4-e4 Ke6-d7 Ke4-d4
  20	20&#58;16	12.123.140.333	9.967.155	+1,60	Ke1-f2 Ke8-d7 e2-e4 Kd7-c6 Kf2-e3 Kc6-c5 e4-e5 Kc5-d5 Ke3-f4 Kd5-c6 e5-e6 Kc6-d6 Kf4-f5
  21	2&#58;39&#58;12	104.628.803.057	10.953.025	+1,70	Ke1-f2 Ke8-d7 Kf2-e3 Kd7-c6 Ke3-d4 Kc6-d6 e2-e4 Kd6-e6 e4-e5 Ke6-d7 Kd4-d5 Kd7-e7 e5-e6 Ke7-e8 Kd5-d6 Ke8-d8 Kd6-c5 Kd8-e7 Kc5-d5 Ke7-e8 Kd5-d4
  22	7&#58;51&#58;20	288.935.243.308	10.216.749	+1,60	Ke1-f2 Ke8-d7 e2-e4 Kd7-c6 Kf2-e2 Kc6-b5 e4-e5 Kb5-c5 Ke2-e3 Kc5-b5 e5-e6 Kb5-c6 Ke3-e4 Kc6-d6 Ke4-f5 Kd6-e7 Kf5-e5 Ke7-d8

In the pv is not the promotion, the kings just dance

Right now it's playing vs 0.1 500 games 1m 1s

Code: Select all

1&#58; Skiull 0.2 x64 170.5/307 
2&#58; Skiull 0.1 x64 136.5/307
Now I'm going to add more bugs... I mean more features :D

Thanks all for the help
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Problems with TT, sometimes makes blunder moves

Post by hgm »

Well, Fairy-Max also needed 24 plies before it would see the promotion. So that you don't have it at 22 ply is no reason for worry.

The question, though, is why you only reach 22 ply even after a very long search. It does seem that your TT is not working well. Look at the node counts: Fairy-Max needed under 6M nodes to see the mates. And you see that the node count hardly goes up after 14 ply, because by that time all positions are in the table. (Note that the node count is cumulative over iterations!) Only when it sees the promotion it burns a lot of notes. (This is logical; it has to completely restructure the tree at that point, because of the change in root score.)
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Problems with TT, sometimes makes blunder moves

Post by Sven »

hgm wrote:Well, Fairy-Max also needed 24 plies before it would see the promotion. So that you don't have it at 22 ply is no reason for worry.

The question, though, is why you only reach 22 ply even after a very long search. It does seem that your TT is not working well. Look at the node counts: Fairy-Max needed under 6M nodes to see the mates. And you see that the node count hardly goes up after 14 ply, because by that time all positions are in the table. (Note that the node count is cumulative over iterations!) Only when it sees the promotion it burns a lot of notes. (This is logical; it has to completely restructure the tree at that point, because of the change in root score.)
The huge tree size could also result from other problems like missing (or wrong) detection of stalemate, repetition draw, or draw by insufficient material (K-K). Of course bad move ordering (caused by not working TT) might have an even bigger influence. But I would start by checking these low-hanging fruits like draw detection. In an earlier post I saw an incredibly huge search output of the (TT-less) version Skiull 0.1 for a KPK position where the white pawn could be captured in the next ply so that no single PV should be longer than two plies with existing draw detection ...

Since this KPK endgame has a branching factor of certainly less than 7 (perft(11) = 1516730534 suggests a BF of 6.8) a proper alpha-beta search should probably have an EBF of less than 3 (sqrt(6.8) = 2.6). From the search output above I derive an EBF of at least 3.14 for iteration 20 (probably more since the output usually refers to the time of finding the PV move and not the end of the whole iteration). That seems way too much for that simple endgame.

Jumbo single-core needs 25 iterations to find the promotion. For depth 20 it needs less than 800000 nodes.
User avatar
hgm
Posts: 27788
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Problems with TT, sometimes makes blunder moves

Post by hgm »

Well, Fairy-Max has no draw detection. At least not for insufficient material. And when I try it with Fairy-Max 4.8V, which has no detection of in-search repeats, it finds the promotion at 26 ply and 8.39M nodes (4 sec). I am not even sure anymore what Fairy-Max I used to get the other result.

So draw detection might help some, but it is certainly not a major factor.

Fairy-Max also has no move ordering, other than hash move.
tttony
Posts: 268
Joined: Sun Apr 24, 2011 12:33 am

Re: Problems with TT, sometimes makes blunder moves

Post by tttony »

I tested that position with (key & size), I don't use slots and the table just fill 1024 from 1677720 entries, with (key % size) see the mate in 32 in the depth 30

Also it does not detect repetitions and material draw

Code: Select all

FEN&#58; 4k3/8/8/8/8/8/4P3/4K3 w - - 5 1 

Skiull 0.2 x64 popcnt&#58;
   1	00&#58;00	           7	7	+1,20	e2-e4
   2	00&#58;00	          24	24	+1,10	e2-e4 Ke8-d7
   3	00&#58;00	          93	93	+1,20	e2-e4 Ke8-d7 Ke1-d2
   4	00&#58;00	         219	219	+1,10	e2-e4 Ke8-d7 Ke1-d2 Kd7-c6
   5	00&#58;00	         630	630	+1,20	e2-e4 Ke8-d7 Ke1-d2 Kd7-c6 Kd2-c3
   6	00&#58;00	       1.541	1.541	+1,00	e2-e4 Ke8-d7 Ke1-d2 Kd7-d6 Kd2-c3 Kd6-e5
   7	00&#58;00	       4.964	4.964	+1,40	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4
   8	00&#58;00	       6.910	6.910	+1,40	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6
   9	00&#58;00	      10.351	10.351	+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+
  10	00&#58;00	      14.556	14.556	+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+ Kd6-c6
  11	00&#58;00	      21.805	21.805	+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+ Kd6-e6 Kd4-e4
  12	00&#58;00	      31.644	31.644	+1,45	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5
  13	00&#58;00	      52.395	52.395	+1,50	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-f4 e5-e6
  14	00&#58;00	      93.181	93.181	+1,50	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-f4 e5-e6 Kf4-e3
  15	00&#58;00	     162.060	10.804.000	+1,55	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-g6 Kd5-e4 Kg6-f7 Ke4-d4
  16	00&#58;00	     228.592	7.373.935	+1,55	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-g6 Kd5-e4 Kg6-f7 Ke4-d5 Kf7-g6
  17	00&#58;00	     318.944	10.288.516	+1,55	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-g6 Kd5-e4 Kg6-f7 Ke4-f5 Kf7-e7 Kf5-e4
  18	00&#58;00	     469.570	7.573.710	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-f5 Kd4-d5 Kf5-g6 e5-e6 Kg6-f6 Kd5-d6 Kf6-g6 e6-e7 Kg6-f7
  19	00&#58;00	     758.440	8.155.269	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-d7 e2-e4 Kd7-e6 e4-e5 Ke6-e7 Kd4-d5 Ke7-d7 e5-e6+ Kd7-c7
  20	00&#58;00	   1.187.283	7.610.788	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+ Kd6-e6 Kd4-e4 Ke6-d7 Ke4-d3
  21	00&#58;00	   1.718.191	7.881.610	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-c6 e2-e4 Kc6-d6 e4-e5+ Kd6-e6 Kd4-e4 Ke6-d7 Ke4-d3 Kd7-c6 Kd3-e3 Kc6-c5 Ke3-f4 Kc5-c6 Kf4-e4 Kc6-c7 e5-e6
  22	00&#58;00	   3.517.611	7.516.263	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-d6 Kc3-d4 Kd6-e6 e2-e4 Ke6-d6
  23	00&#58;00	   5.941.215	7.616.942	+1,60	Ke1-d2 Ke8-d7 Kd2-c3 Kd7-e6 e2-e4 Ke6-e5 Kc3-d3 Ke5-d6 Kd3-c2 Kd6-c5 Kc2-c3 Kc5-c6 Kc3-b2 Kc6-b5 Kb2-b3 Kb5-c6 Kb3-c2
  24	00&#58;01	   7.762.582	7.428.308	+1,60	Ke1-d2 Ke8-d8 e2-e4 Kd8-c7 Kd2-e3 Kc7-d6 Ke3-d2 Kd6-c5 Kd2-e2 Kc5-d4 Ke2-f3 Kd4-e5 Kf3-e3 Ke5-f6 Ke3-d2 Kf6-e5 Kd2-d3
  25	00&#58;05	  49.170.060	8.518.721	+1,70	Ke1-d2 Ke8-d8 e2-e4 Kd8-c7 e4-e5 Kc7-c6 Kd2-e3 Kc6-d5 Ke3-f4 Kd5-e6 Kf4-e4 Ke6-d7 Ke4-d5 Kd7-e7 e5-e6 Ke7-e8 Kd5-d4 Ke8-e7 Kd4-e5
  26	00&#58;13	 104.294.185	7.738.105	+9,70	Ke1-d2 Ke8-d8 Kd2-d3 Kd8-d7 Kd3-e4 Kd7-e6 e2-e3 Ke6-f6 Ke4-d5 Kf6-e7 Kd5-e5
  27	00&#58;29	 240.815.241	8.237.506	+10,00	Ke1-d2 Ke8-d8 Kd2-d3 Kd8-d7 e2-e4 Kd7-c6 Kd3-c3 Kc6-c5 Kc3-d3 Kc5-c6 Kd3-c3 Kc6-c5 Kc3-d3 Kc5-c6 Kd3-c3 Kc6-c5 Kc3-d3 Kc5-c6 Kd3-e3
  28	00&#58;48	 384.502.569	7.852.119	+10,30	Ke1-d2 Ke8-d8 Kd2-d3 Kd8-d7 Kd3-c3 Kd7-e6
  29	01&#58;58	 984.296.288	8.293.351	+10,50	Ke1-d2 Ke8-d8 Kd2-d3 Kd8-d7 Kd3-e4 Kd7-e6 e2-e3 Ke6-f6 Ke4-d5 Kf6-e7
  30	03&#58;35	1.702.893.180	7.889.499	+M32	Ke1-d2 Ke8-d8 Kd2-d3 Kd8-d7 Kd3-d4 Kd7-d6
  31	05&#58;52	2.864.575.706	8.125.396	+M29	Ke1-f2 Ke8-d7 Kf2-e3 Kd7-e7 Ke3-e4 Ke7-e6 e2-e3 Ke6-d6 Ke4-f5 Kd6-d7
  32	09&#58;19	4.386.817.291	7.844.796	+M28	Ke1-f2 Ke8-d7 Kf2-e3 Kd7-e7 Ke3-e4 Ke7-f6 Ke4-f4 Kf6-g6 Kf4-e5 Kg6-f7 Ke5-d6 Kf7-f6 e2-e4 Kf6-f7 e4-e5 Kf7-e8