First post (and FailHigh question!)

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

tpetzke
Posts: 686
Joined: Thu Mar 03, 2011 4:57 pm
Location: Germany

Re: First post (and FailHigh question!)

Post by tpetzke »

Hi Chan,

there is no bug and the engine did not see beyond its horizon. The Mate in 12 was found at general depth 18 but "selected depth" 24. So it extended certain lines up to depth 24 and to see there a Mate in 12 is expected.

I know it is a Mate in 11 and the engine would probably have found it when it was allowed to search deeper. If it has secured a Mate in 12 it will not search beyond depth 24 (The line that involves the Mate in 11 was still to much reduced here).

I know that Bobs method works like mine as long as a single search is performed. But I don't clear the hash table between moves so if you and your opponent have played a move and now you find in the hash table a Mate in X where it should be a Mate in X-1 (because 2 half moves into that direction already have been played). There are ways to deal with it, like looking at the age of the hash entry and correct it if the entry is from an older search. And maybe as Sven wrote alpha-beta auto corrects the longer Mate distance anyway. So I don't say that the way crafty does it does not work. It probably does since Cray Blitz times.

I just have a different design in my engine. My Mate scores are adjusted from the transposition table class because the usage of this class is the reason that any adjustment is needed at all. So it takes care of the problem it creates. If this design requires a few additional CPU cycles that's fine.

Thomas...
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: First post (and FailHigh question!)

Post by syzygy »

tpetzke wrote:I know that Bobs method works like mine as long as a single search is performed. But I don't clear the hash table between moves so if you and your opponent have played a move and now you find in the hash table a Mate in X where it should be a Mate in X-1 (because 2 half moves into that direction already have been played).
As long as mate scores in the hash table are relative to the hashed position and not relative to the root position, there is no problem if the root position changes.

Even if the root position does not change, mate scores stored should be relative to the hashed position, because different paths from the root position to the hashed position can have different lengths.

Bob's method (which I am 99.99% sure is storing mate scores relative to the hashed position) does not need any special treatment of mate scores depending on the age of the entry. All that is needed is an adjustment from "tree score" (relative to the root position) to "hash score" (relative to the hashed position) when storing, and the other way around when retrieving.
I just have a different design in my engine.
Your comment accurately describes Bob's method (unless I'm wrong about his method, which I very much doubt):

Code: Select all

/***********************************************************************************************
*   Adjust a possible mate score to the ply where it is probed from
*   e.g. in the hash a mate score 'mate in 3' resulting from ply 10 is stored = mate at ply 16
*   if the hash is hit at ply 12 then the store mate in 3 must be translated into mate at ply 18
************************************************************************************************/
The easy optimisation (in respect of TT entry size) is to store "hashScore +/- hashScoreFromPly" when hashScore is a mate value. Then when you retrieve you do the same as what you do now, but add/subtract not plyDiff but hashProbeFromPly.
tpetzke
Posts: 686
Joined: Thu Mar 03, 2011 4:57 pm
Location: Germany

Re: First post (and FailHigh question!)

Post by tpetzke »

Thanks for the insight. Your explanation makes sense. So my implementation it is not so different after all (probably the reason that it works).

Thomas...
User avatar
Desperado
Posts: 879
Joined: Mon Dec 15, 2008 11:45 am

Re: First post (and FailHigh question!)

Post by Desperado »

Hi Natale,

i do not intend to repeat things already mentioned in this thread.
But i like to give some hints for hunting and finally solving the problem.

What i really miss in this thread is some useful advice to catch the real
problem. All i was reading about is assuming this and that and again, this and that ...

It is still unanswered if it is a bug, a search instability, or maybe a combination of both. Further this problem isnt necessarily a
trivial task to solve and many of the more experienced people here will know the difficulty which goes hand in hand with a proper solution, espeacially if the algorithm is reimplemented from scratch again.

Now some hints how you can systematically explore the real problem and be aware that it isnt necessarily "in the corner" you might think it is. Later some more words on what i mean with that point.

Because it looks like you are in an early stage of development, you should concept things in a way you can enable and disable different components easily. Let's go...

1. Search Instabilities

I will argue in a way if it was only a search instability problem here. So what you can / should do is to enable / disable step by step following parts.

- if you have TT in QS -> disable it
- if you have TT-pruning in PV -> disable it
- you can test failLo / failHi TT prunings seperately
- use mate distance pruning, to reduce "more" search instabilities,
it is useful anyway and may speedup tracing the problem.
- Another issue maybe ( but i dont think it is your case ), that
heavily reduction and pruning techniques are causing such an effect.
( if you have some kind of pruning disable it )
- change your testcase ( look at mating problems with KQK, KRK ) and observe if you can find a similar behaviour. You can get useful information
that way. ( Try to use a test case where you find the same behaviour, but
where you can imagine with human skills what the move sequence should be to the mate )

2. Bugs

- check your mate-scoring algorithm like Sven already described. It is
a (the) correct way of handling mate scores.
- check the search for valid score inside the bounds you are allowing
- make your pvs lightweight as possible, check signs, returning conditions
and if the logic works the way you want to have it.

EG: what happend to me when re-implementing Nemo search code...

Code: Select all


  while(moves)
  {
    ...
    if( value >= bestValue ) bestValue = value;
    if( value >= beta ) break; // ohoh ;-)
  }

  // Minimax Worked proper with break,
  // even AlphaBeta does, but with TT the code
  // above was a problem of course.

  StoreBestValueToTT(...)

  return bestValue;
There will always be some new exciting bugs to find with new implementation,
even if you did it correct a 100 times before. You dont want to know how
long it took to get this thing, because i was thinking of old verified code and suddenly my mating tests showed very strange behaviour.

There are many more bugs to find, but in another section...

3. Other Issues

Of course you should start with the most obvious things, but extend
your debugging on other components if you dont have success with
the already mentioned. The following examples are choosen arbitrary.

eg: - you already hash material scores, so your evluation may produce values that are simply wrong in some cases and are passed back through
the search tree.

eg: - you may simply have a datatype overflow or uninitialized variable
in TT slot or somewhere else which is influencing the search logic.

eg: - using transmove / moveordering issue, where you use invalid
moves somehow, producing wrong lines and wrong values and so
on... A bug source for this might be a function like ("moveIsValid()")
or sth. like that.

eg: thinking of "king-capture" issues, are they possible in your design
what happens with buggy board states ...

and so on... ( the main point of the examples is, that you should consider
other components too
)

I'm not sure if it is your first engine but if it is you will rewrite it many
times again. The more experience you get with hunting such kind of bugs
the faster you can look into problems upcoming when rewriting your code.
A good investigation to think where the cause for this problem may come from. ( There are many more sources not mentioned here )

Finally i guess it is a combination of bug(s) and search instability from what i was reading so far in your postings.

Hmm :?, there was an additional point i wanted to mention, but i have forgotten what it was :lol:.

Maybe another point is, the more code snippets you provide the more
concret the help can be, and the road of speculation can be left very soon.
Another thing from my experience is, that posting some snippets here often is enough to read it more concentrated before submitting it,
which is often enough to catch things which you already read 100 times before in your development environment. More input, more help...

So, i wish you good luck and go for it :wink:

Best, Michael

PS: sry for my quick and dirty english, just wanted to write down some
thoughts on this.
Chan Rasjid
Posts: 588
Joined: Thu Mar 09, 2006 4:47 pm
Location: Singapore

Re: First post (and FailHigh question!)

Post by Chan Rasjid »

Hello Patrice,
Patrice Duhamel wrote:Hello,

Don't know if it helps, but for me one part of the problem with wrong mate scores was in null moves pruning.
In my condition to prevent zugzwang positions, I used the total number of non pawn pieces instead of the value for the current player.

Now there are other variations with mate scores in few endgames, and it's visible with all search options disabled (using only PVS + Quiescence), but I don't think it's a problem.
You did mentioned earlier that your program has an old bug about wrong mate scores that you could not resolve - so you disabled mate score adjustment. You cannot do a half-measured hashing of mate scores. I suggest as a temporary measure, you do not store any +/- mate scores in your TT; so you also need not worry about retrieving mate scores otherwise your program cannot run!

Also I don't really know what you mean by wrong mate scores - maybe your engine says mate-in-6 but somehow there is no mate to be found; therefore "wrong mate scores." It is not unusual that we sometimes need to wait out a period before a bug could be resolved. I waited for months before I found my KPK bug.

Also, I am not sure how nullmove may be related to wrong or amplified mate scores.

Best Regards,
Rasjid.
Chan Rasjid
Posts: 588
Joined: Thu Mar 09, 2006 4:47 pm
Location: Singapore

Re: First post (and FailHigh question!)

Post by Chan Rasjid »

Hello Thomas,
tpetzke wrote:Hi Chan,

there is no bug and the engine did not see beyond its horizon. The Mate in 12 was found at general depth 18 but "selected depth" 24. So it extended certain lines up to depth 24 and to see there a Mate in 12 is expected.

I know it is a Mate in 11 and the engine would probably have found it when it was allowed to search deeper. If it has secured a Mate in 12 it will not search beyond depth 24 (The line that involves the Mate in 11 was still to much reduced here).

I know that Bobs method works like mine as long as a single search is performed. But I don't clear the hash table between moves so if you and your opponent have played a move and now you find in the hash table a Mate in X where it should be a Mate in X-1 (because 2 half moves into that direction already have been played). There are ways to deal with it, like looking at the age of the hash entry and correct it if the entry is from an older search. And maybe as Sven wrote alpha-beta auto corrects the longer Mate distance anyway. So I don't say that the way crafty does it does not work. It probably does since Cray Blitz times.

I just have a different design in my engine. My Mate scores are adjusted from the transposition table class because the usage of this class is the reason that any adjustment is needed at all. So it takes care of the problem it creates. If this design requires a few additional CPU cycles that's fine.

Thomas...
My engine has now been restored to normal but strictly set to be without QS and any depth extension/reduction; this is an analysis:
info depth 17, score +782 cp, time 27099 ms, nodes 108458842 nps 4002318, pv d6d5 h1g2 d1e2 g2g3 d5e4 g3h3 e4f5 h3g3 f5e5 g3g2 e5f4 g2h3 e2f3
depth 18, score +782 cp, d6d5
info depth 18, score +782 cp, time 32188 ms, nodes 128041150 nps 3977915, pv d6d5 h1g2 d1e2 g2g3 d5e4 g3h3 e4f5 h3g3 f5e5 g3g2 e2g4
+mate FH depth 19 score 7955
depth 20, score +7969 cp, d6d5
depth 20, score +7979 cp, d6e5
info depth 20, +mate 11, time 39251 ms, nodes 152857105 nps 3894349, pv d6e5 h1h2 e5f4 h2h3 f4g5
info depth 22, +mate 11, time 46953 ms, nodes 176180304 nps 3752269, pv d6e5 h1h2 e5f4 h2h3 f4g5 h3g3 d1g4 g3g2 g5h4 g2h2 g4h3 h2h1 h4g3 h1g1 d3e5 g1h1 h3g2 h1g1 e5f3
Matesearch found no better mate 7979, depth 22, d6e5
info depth 24, +mate 11, time 89729 ms, nodes 349128049 nps 3890916, pv d6e5 h1h2 e5f4 h2h3 f4g5 h3g3 d1g4 g3g2 g5h4 g2h2 g4h3 h2h1 h4g3 h1g1 d3e5 g1h1 h3g2 h1g1 e5f3
At depth 19, there was the first +mate fail high of 7955 (inf - 45) or mate23. So it sees much beyond its usual depth horizon. Then research starts with the depth set to the next even depth 20. It finds mate11 on the 2nd move, the shortest mate but before depth 22. The shortened PV was the first five half-moves of the PV from depth 22 - so it is correct and it is an exact score.

From what I now learned, I expect most programs to see mate much earlier. But from analysis with stockfish and texel, it seems (I can't read their output too well) their engine do not see beyond their depth horizon. I don't know why. But I would be surprised if the peculiarity of my engine is a bug or a defect.

I know Ronald has explained about Crafty's way of hashing mate scores. But I think giving a simple concrete example may be helpful to some.

Say search enters a new node, probes the TT and found an exact hit and the entry score is (infi - 5). What it means is that there is a mate line (shortest ?) and the mate node is 5 plies away from the current node. TT entries are all about hashing a chess position. So even if this entry was hashed from a search 6 game moves away, it does not matter. It is all about a chess position, the current position (which is identical to the position 6 game moves away when it was hashed), having a mate line and the mate node is 5 plies away.

Our TT hashing don't store real PV because of memory issue. If we did, we could even know the actual pv for this mate. But our TT only has the info "5 plies". Now if our current node is 12 plies from the root, we know the mate node is 17 plies from the root. So there is no need to search this current node, search just returns an adjusted score of (infi - 17).

Hope it helps.

Best Regards,
Rasjid.
Patrice Duhamel
Posts: 193
Joined: Sat May 25, 2013 11:17 am
Location: France
Full name: Patrice Duhamel

Re: First post (and FailHigh question!)

Post by Patrice Duhamel »

Chan Rasjid wrote: You did mentioned earlier that your program has an old bug about wrong mate scores that you could not resolve - so you disabled mate score adjustment. You cannot do a half-measured hashing of mate scores. I suggest as a temporary measure, you do not store any +/- mate scores in your TT; so you also need not worry about retrieving mate scores otherwise your program cannot run!
In previous version of my engine (Cheese) mate score adjustment was not used.

The bug I found with null moves fixed the problem, or a part of the problem, because now with mate score adjustment I only have wrong mate scores on few positions.
Chan Rasjid wrote: Also I don't really know what you mean by wrong mate scores - maybe your engine says mate-in-6 but somehow there is no mate to be found; therefore "wrong mate scores." It is not unusual that we sometimes need to wait out a period before a bug could be resolved. I waited for months before I found my KPK bug.
By "wrong mate scores" I mean 2 things :
- for example a mate in 30 in the position given by Natale, where we should only found mates in 11, 12 or 13
- or sometimes, a mate in 11 is found, and the next depth searched find a mate in 12, and it changes again next depth...
Chan Rasjid wrote: Also, I am not sure how nullmove may be related to wrong or amplified mate scores.
I can't explain the relation with null move prunning, but this fixed the main problem.
I may have other problems somewhere.

I thought that this could help Natale, but I'm not sure if he have null moves.

Thanks.
Chan Rasjid
Posts: 588
Joined: Thu Mar 09, 2006 4:47 pm
Location: Singapore

Re: First post (and FailHigh question!)

Post by Chan Rasjid »

Hello Patrice,
Patrice Duhamel wrote:
Chan Rasjid wrote: You did mentioned earlier that your program has an old bug about wrong mate scores that you could not resolve - so you disabled mate score adjustment. You cannot do a half-measured hashing of mate scores. I suggest as a temporary measure, you do not store any +/- mate scores in your TT; so you also need not worry about retrieving mate scores otherwise your program cannot run!
In previous version of my engine (Cheese) mate score adjustment was not used.

The bug I found with null moves fixed the problem, or a part of the problem, because now with mate score adjustment I only have wrong mate scores on few positions.
Chan Rasjid wrote: Also I don't really know what you mean by wrong mate scores - maybe your engine says mate-in-6 but somehow there is no mate to be found; therefore "wrong mate scores." It is not unusual that we sometimes need to wait out a period before a bug could be resolved. I waited for months before I found my KPK bug.
By "wrong mate scores" I mean 2 things :
- for example a mate in 30 in the position given by Natale, where we should only found mates in 11, 12 or 13
- or sometimes, a mate in 11 is found, and the next depth searched find a mate in 12, and it changes again next depth...
Chan Rasjid wrote: Also, I am not sure how nullmove may be related to wrong or amplified mate scores.
I can't explain the relation with null move prunning, but this fixed the main problem.
I may have other problems somewhere.

I thought that this could help Natale, but I'm not sure if he have null moves.

Thanks.
Not to long ago, I was struggling to debug why my program failed to mate KRK/KQK positions. I started a thread that grew long and finally the bug was resolved. I probably have gone through what you called "wrong mate scores". I give what could be the likely causes.

A) TT bound type :
1) score <= alpha is UPPER.
2) score >= beta is LOWER.
3) score > alpha and score < beta is EXACT.
These are absolute rules without exceptions.

B) TT hash draft: always hash the exact depth parameter that was passed to search. Again without exception unless we really know what we are doing.

My KRK/KQK bug was because I broke the above rules and conditions.

Mate scores are unusual in a sense. If at depth 22, even within internal nodes and you get mate11 (infi -21) , that's the best you can ever get and you just hash and return the score. At root, we can stop the iteration and play the best move found. So the smart thing I did was to do:
HashStore(Infi - 21 + ply, MAX_DEPTH, ...) and MAX_DEPTH = 64. I hashed all mate scores with depth 64! I did other things and so my program somehow could never find mate for KRK/KQK.

Then Gert Muller saw my code snippet and commented that he never treated mate scores any different from non-mate scores when it comes to hashing. This is the key - just follow exactly the rules for bound types and hash draft. Of course there must be ply adjustment for mate scores which you are already well aware of.

Then there is such a thing called "mate-distance-pruning". I am not sure what it is. The name suggest we may meddle with things and our program may "prune away" and fail to see "correct mate scores."

There are too many ways we could introduce bugs and opportunity abounds. But one other serious bug is wrong triangular pv[][]. If after every search that stockfish does and about to play its move, its pv[][] is replaced by the one that comes from cowrie chess, then it would be a huge issue. So I have a routine:
_BOOL debugPV(move_t *pv, int side, int ply);
It is called with assert(debugPV()) whenever root finds a new pv. The routine make() the moves and do a moveGen() and checked that the move was indeed there.

Hope the above helps.

Best Regards,
Rasjid.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: First post (and FailHigh question!)

Post by Sven »

Chan Rasjid wrote:Then there is such a thing called "mate-distance-pruning". I am not sure what it is. The name suggest we may meddle with things and our program may "prune away" and fail to see "correct mate scores."
CPW explanation of "Mate Distance Pruning"

Avoids searching useless subtrees after a forced mate has been found. Useful in analysis, although it has been reported that it has no measurable impact on playing strength.

Sven
xmas79
Posts: 286
Joined: Mon Jun 03, 2013 7:05 pm
Location: Italy

Re: First post (and FailHigh question!)

Post by xmas79 »

Hi all again,
i'm still very busy at work, but I finally debugged my position and results are:

Short verion: No bug.





Long version: please copy & paste in a notepad without word wrap...

Code: Select all

idx    func         key      oldhash              oldvalue   newhash              newvalue   ply     depth     old best move  new best move      fen                                Current Line 
... 
295    InsertHash   207800   0x7A46155C335B2BB9   600        0x7A46155C335B2BB9   31991      12      3         ------         Kf2-g3             8/8/8/8/8/3N4/5K2/3B3k w - -       1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2 6. Be2-d1 Kh2-h1 
296    CheckHash    84508    0x1E0C1E5B2DD94A1C   31993                                      14      1         Bg4-e2                            8/8/8/8/6B1/3N2K1/8/6k1 w - -      1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2 6. Be2-g4 Kh2-h1 7. Kf2-g3 Kh1-g1
297    InsertHash   456672   0x62CD69D87571081E   -600       0x52937EE31471081C   -31992     13      2         Kf1-e1         ------             8/8/8/8/6B1/3N2K1/8/7k b - -       1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2 6. Be2-g4 Kh2-h1 7. Kf2-g3
298    InsertHash   143616   0x0F780EBB7BF23103   600        0x0F780EBB7BF23103   31991      12      3         ------         Kf2-g3             8/8/8/8/6B1/3N4/5K2/7k w - -       1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2 6. Be2-g4 Kh2-h1
299    InsertHash   41108    0x45B1279F747F5F68   -600       0x528A28727AEF5F6B   -31990     11      4         Kg1-f1         ------             8/8/8/8/6B1/3N4/5K1k/8 b - -       1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2 6. Be2-g4
300    InsertHash   1488     0x38370F7F052805D3   600        0x38370F7F052805D3   31989      10      5         ------         Be2-g4             8/8/8/8/8/3N4/4BK1k/8 w - -        1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2 Kh1-h2
301    InsertHash   169028   0x65C529B604356BBB   -600       0x65C529B604356BBB   -31988      9      6         Kh1-h2         ------             8/8/8/8/8/3N4/4BK2/7k b - -        1. Bd1-e2 Kh1-g1 2. Kd6-c5 Kg1-h1 3. Kc5-d4 Kh1-g1 4. Kd4-e3 Kg1-h1 5. Ke3-f2
...
477    CheckHash    169028   0x65C529B604356BBB   -31988                                     13      2         ------                            8/8/8/8/8/3N4/4BK2/7k b - -        1. Bd1-e2 Kh1-g1 2. Kd6-e5 Kg1-h1 3. Ke5-f4 Kh1-h2 4. Kf4-f3 Kh2-h3 5. Nd3-f4 Kh3-h2 6. Kf3-f2 Kh2-h1 7. Nf4-d3
478    InsertHash   462840   0x71135DE459670FF9   600        0x71135DE459670FF9   31987      12      3         ------         Nf4-d3             8/8/8/8/5N2/8/4BK2/7k w - -        1. Bd1-e2 Kh1-g1 2. Kd6-e5 Kg1-h1 3. Ke5-f4 Kh1-h2 4. Kf4-f3 Kh2-h3 5. Nd3-f4 Kh3-h2 6. Kf3-f2 Kh2-h1
479    InsertHash   368236   0x2CE17B2D587A6191   -600       0x2CE17B2D587A6191   -31986     11      4         Kh2-h1         ------             8/8/8/8/5N2/8/4BK1k/8 b - -        1. Bd1-e2 Kh1-g1 2. Kd6-e5 Kg1-h1 3. Ke5-f4 Kh1-h2 4. Kf4-f3 Kh2-h3 5. Nd3-f4 Kh3-h2 6. Kf3-f2
480    InsertHash   267996   0x58645D8A7FDC16DF   600        0x58645D8A7FDC16DF   31985      10      5         ------         Kf3-f2             8/8/8/8/5N2/5K2/4B2k/8 w - -       1. Bd1-e2 Kh1-g1 2. Kd6-e5 Kg1-h1 3. Ke5-f4 Kh1-h2 4. Kf4-f3 Kh2-h3 5. Nd3-f4 Kh3-h2
...
1014   CheckHash    368236   0x2CE17B2D587A6191   -31986                                     13      2         ------                            8/8/8/8/5N2/8/4BK1k/8 b - -        1. Kd6-e5 Kh1-g1 2. Ke5-d4 Kg1-f1 3. Kd4-e3 Kf1-g2 4. Ke3-e2 Kg2-g1 5. Nd3-f4 Kg1-h1 6. Ke2-f2 Kh1-h2 7. Bd1-e2
1015   InsertHash   270736   0x336247C76F142193   600        0x336247C76F142193   31985      12      3         ------         Bd1-e2             8/8/8/8/5N2/8/5K1k/3B4 w - -       1. Kd6-e5 Kh1-g1 2. Ke5-d4 Kg1-f1 3. Kd4-e3 Kf1-g2 4. Ke3-e2 Kg2-g1 5. Nd3-f4 Kg1-h1 6. Ke2-f2 Kh1-h2
1016   InsertHash   438276   0x6E90610E6E094FFB   600        0x6E90610E6E094FFB   31984      11      4         Kh1-h2         ------             8/8/8/8/5N2/8/5K2/3B3k b - -       1. Kd6-e5 Kh1-g1 2. Ke5-d4 Kg1-f1 3. Kd4-e3 Kf1-g2 4. Ke3-e2 Kg2-g1 5. Nd3-f4 Kg1-h1 6. Ke2-f2
... 
1030   CheckHash    169028   0x65C529B604356BBB   -31988                                     11      4         ------                            8/8/8/8/8/3N4/4BK2/7k b - -        1. Kd6-e5 Kh1-g1 2. Ke5-d4 Kg1-f1 3. Kd4-e3 Kf1-g2 4. Ke3-e2 Kg2-h2 5. Ke2-f2 Kh2-h1 6. Bd1-e2 
1031   InsertHash   207800   0x7A46155C335B2BB9   31991      0x7A46155C335B2BB9   31987      10      5         Kf2-g3         Bd1-e2             8/8/8/8/8/3N4/5K2/3B3k w - -       1. Kd6-e5 Kh1-g1 2. Ke5-d4 Kg1-f1 3. Kd4-e3 Kf1-g2 4. Ke3-e2 Kg2-h2 5. Ke2-f2 Kh2-h1 
... 
1067   CheckHash    438276   0x6E90610E6E094FFB   -31984                                     13      2         ------                            8/8/8/8/5N2/8/5K2/3B3k b - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-h1 3. Ke4-f3 Kh1-h2 4. Bd1-c2 Kh2-h1 5. Kf3-f2 Kh1-h2 6. Nd3-f4 Kh2-h1 7. Bc2-d1
1068   InsertHash   162424   0x6B6C76B73AB27A79   600        0x6B6C76B73AB27A79   31983      12      3         ------         Bc2-d1             8/8/8/8/5N2/8/2B2K2/7k w - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-h1 3. Ke4-f3 Kh1-h2 4. Bd1-c2 Kh2-h1 5. Kf3-f2 Kh1-h2 6. Nd3-f4 Kh2-h1
1069   InsertHash   60396    0x369E507E3BAF1411   -600       0x369E507E3BAF1411   -31982     11      4         Kh2-h1         ------             8/8/8/8/5N2/8/2B2K1k/8 b - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-h1 3. Ke4-f3 Kh1-h2 4. Bd1-c2 Kh2-h1 5. Kf3-f2 Kh1-h2 6. Nd3-f4
1070   InsertHash   356432   0x2248242C66FD7053   600        0x2248242C66FD7053   31981      10      5         ------         Nd3-f4             8/8/8/8/8/3N4/2B2K1k/8 w - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-h1 3. Ke4-f3 Kh1-h2 4. Bd1-c2 Kh2-h1 5. Kf3-f2 Kh1-h2
1071   InsertHash   516548   0x7FBA02E567E01E3B   -600       0x7FBA02E567E01E3B   -31980      9      6         Kh1-h2         ------             8/8/8/8/8/3N4/2B2K2/7k b - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-h1 3. Ke4-f3 Kh1-h2 4. Bd1-c2 Kh2-h1 5. Kf3-f2 
... 
1269   CheckHash    516548   0x7FBA02E567E01E3B   -31980                                      9      6         ------                            8/8/8/8/8/3N4/2B2K2/7k b - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-g2 3. Ke4-e3 Kg2-h2 4. Ke3-f2 Kh2-h1 5. Bd1-c2 
1270   InsertHash   207800   0x7A46155C335B2BB9   31987      0x7A46155C335B2BB9   31979       8      7         Bd1-e2         Bd1-c2             8/8/8/8/8/3N4/5K2/3B3k w - -       1. Kd6-e5 Kh1-g1 2. Ke5-e4 Kg1-g2 3. Ke4-e3 Kg2-h2 4. Ke3-f2 Kh2-h1 
idx: progessive check/insert number
func: insert or check
key: my hash index
oldhash: old complete hash key in the slot if insert, or simply complete hash key in the slot if check
oldvalue: old hash position value in the slot if insert, or simply hash position value if check. Value is alread ply corrected
newhash: new hash when insert
newvalue: new value when insert. Value is alread ply corrected
ply: current ply
depth: actually is the draft
old best move: old best move in the hash if insert, or simply best move if check
new best move: new best move if insert
fen: fen of the position that is querying/inserting
current line: line executed until this position

Engine configuration:
AB-search, no Q-search, no-null move, no pruning, no evertything else. Only AB-search and hashtable.

Notice that ply+depth always equals to 15, as in this configuration engine sees the first mate at ply 15 (and not in 14 as the original post with Q-search enabled.)

Explanation:
I debugged all insert/check hash entries at ply 15 that produced a mate score (retrieved mate score or inserted) and dumped everthing I thought could be useful.

So, at ply 15, after 295 check/insert it inserts a mate in 9 plies starting from this FEN:
[d]8/8/8/8/8/3N4/5K2/3B3k w - -
Mating sequence is: 1. Kg3 Kg1 2. Be2 Kh1 3. Nf2+ Kg1 4. Nh3+ Kh1 5. Bf3# which is a mate in 9 plies, so the score stored in the hash (31991) is correct.
The next position analyzed at idx 296 is a mate in 7 plies (and can easily be seen by the engine since we are at ply 15), and that position procudes a back-up (and a raise of the mate score) until ply 9 (idx 301).
Then after another bunch of positions engine checks for hash 0x65C529B604356BBB scored -31988 which has been already seen at idx 301. Position is the following:
[d]8/8/8/8/8/3N4/4BK2/7k b - -
and mating sequence this time is: 1. ... Kh2 2. Bg4 Kh1 3. Nc1 Kh2 4. Ne2 Kh1 5. Ng3+ Kh2 6. Nf1+ Kh1 7. Bf3# which is a mate i 12 plies, so the score stored in the hash (-31988) is correct.
That also means engine have a hash hit and a new back-up starts, raising mate scores from ply 13 up to ply 10! Now hashtable has scores up to mate in 15 plies (31985).
Then after a few positions the engine checks for hash 0x2CE17B2D587A6191 seen at idx 479 with a mated score of -31986. Notice how consistent are stored/retrieved scores besides different checking depth/ply.
Then at idx 1030 engine checks hash 0x65C529B604356BBB stored at idx 301, produce a single back-up and here there is the first replace at idx 1031 of a hash entry with a "bubbled" mate score.
The same happens at idx 1067, producing at idx 1071 a score of mated in 20 plies (-31980).
And the big finale, idx 1269 retrieves a score of -31980 and backs-up a score of 31979, mate in 21 plies, well beyond 15 plies.

This is one of the flow I traced successfully. The same thing is happening all over the tree. That's why engine fails high when BETA is less than INF. When BETA is +INF no cut-offs happens and normal score is then reported.

That's all.



Bye,
Natl.