My program can't mate KQK or KRK!

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

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

My program can't mate KQK or KRK!

Post by Chan Rasjid »

Hello,

My program has a hashing bug that I could not resolve after weeks of attempts. If anyone had come across a similar situation in the development of their engine, I would like to know the solution.

With TT hashing enabled, my program plays normally from the start position and does not do strange moves. But when given KQK or KRK positions, it fails to mate and will concede a repetition/fifty-moves draw.

But with TT disabled, it does successfully mate with KQK or KRK. With KRK, it will take some moves to force the lone king into the corner. When it detects mate, matesearch() will be called starting at root. The root search iterates through even depth 2/4/6/8.. to seek the best mate. It will take almost zero second for it to find the next better mate move: mate in 5/4/3/2/1 - checkmate.

My matesearch() code is here:
EDIT: at root, the 1st call to search_mate() has :- best = alpha = positve mate score with a bestmove.

Code: Select all

/* Mate Search
 * =======
 * Mate search starts with a +mate score X which may not be the best mate.
 * Root search iterates through even depth 2, 4, 6, 8, ...If any improvement in the mate score is found,
 * then it is the best mate and search returns. 
 * After depth N where INFI - N + 1 == X,  then it means there is no better mate than X 
 * and search also returns.
 */

int search_mate(const int depth, int alpha, int beta, const int side, const int xside,
        const int ply, check_t check_s, int followPV) {
    const int alpha0 = alpha;
    const int incheck = check_s.type;
    int i, x, nummove, sorted_front;
    int movepc, ep, movedone;
    int best, xeval;
    move_t *move, bestmove, ttmove = 0;
    move_t list[256];
    /* even depth == engine side  */

    assert(depth > 0);
    assert&#40;ply & 1 ? beta <= M_MATE &#58; alpha >= P_MATE&#41;;
    assert&#40;!is_sq_attacked&#40;king&#91;xside&#93;, side&#41;);
    pv&#91;ply&#93;&#91;0&#93; = 0;
    maxply = MAX&#40;maxply, ply&#41;;
    if &#40;ply > MAX_PLY&#41; &#123;
        assert&#40;0&#41;;
        sendlog&#40;"matesearch maxply reached %d, depth %d\n", ply, depth&#41;;
        return mat&#91;side&#93; - mat&#91;xside&#93; - 2;
    &#125;

    ++fnodes;
    ++nodes;

    /* may run out of time */
    if &#40;nodes & timeoutNodeMask || notimeout || !searchInfo->bestmove || searchinfi&#41;
        ;
    else &#123;
        timeout&#40;);
    &#125;

    best = -INFI;
    bestmove = 0;
    movedone = 0;
    xeval = -INFI;

#if !ENABLE_HASH
    ttdata->type = 0;
    ttdata->move = 0;
#else
    probehash&#40;ply&#41;;
    ++numprobe;
    if &#40;ttdata->type && ttdata->depth >= depth&#41; &#123;
        ++probehit;
        switch &#40;ttdata->type&#41; &#123;
            case EX&#58;
                assert&#40;ttdata->score ^ -INFI&#41;;
                ttmove = ttdata->move;/* return if not rpt3; a temp bad hack  */
                break;
            case LB&#58;
                if &#40;ttdata->score >= beta&#41; &#123;
                    assert&#40;ttdata->score ^ INFI&#41;;
                    return ttdata->score;
                &#125;
                assert&#40;ttdata->score != -INFI&#41;;
                break;
            case UB&#58;
                assert&#40;pv&#91;ply&#93;&#91;0&#93; == 0&#41;;
                if &#40;ttdata->score <= alpha&#41; &#123;
                    assert&#40;ttdata->score ^ INFI&#41;;
                    return ttdata->score;
                &#125;
                break;
            default&#58;
                assert&#40;0&#41;;
        &#125;
    &#125;

    if &#40;ttdata->type&#41; &#123;
        xeval = ttdata->eval;
    &#125;
#endif

    move = gen_all&#40;list, side, &check_s&#41;;
    nummove = move - list;
    if (!nummove&#41; &#123;
        goto MATE;
    &#125;

    sorted_front = 0;
    if &#40;followPV && pv&#91;0&#93;&#91;ply&#93;) &#123;
        swapMoveFront&#40;list, pv&#91;0&#93;&#91;ply&#93;);
        sorted_front = 1;
    &#125; else &#123;
        followPV = 0;
        if &#40;ttdata->type && ttdata->move&#41; &#123;
            swapMoveFront&#40;list, ttdata->move&#41;;
            sorted_front = 1;
        &#125;
    &#125;

    assert&#40;*move == 0&#41;;
    assert&#40;list&#91;nummove&#93; == 0&#41;;
    assert&#40;depth > 0&#41;;
    for &#40;move = list; *move; ++move&#41; &#123;
        assert&#40;* &#40;list + nummove - 1&#41;);
        assert&#40;* &#40;list + nummove&#41; == 0&#41;;
        if (!sorted_front&#41; &#123;
            if (!incheck&#41; &#123;
                if &#40;movedone < 32&#41; &#123;
                    bubbleBestMoveHistory&#40;move, list + nummove, history&#91;side&#93;);
                &#125;
            &#125; else &#123;
                swapBestMove&#40;move&#41;;
            &#125;
        &#125;

        sorted_front = 0;
        if &#40;is_move_valid&#40;*move, side, incheck&#41;)
            ;
        else &#123;
            followPV = 0;
            continue;
        &#125;

        movepc = MOVE_PC&#40;*move&#41;;
        ep = currstack->epRights && TO&#40;*move&#41; == currstack->ep && movepc == Pawn;
        currstack->move = *move;
        makemove&#40;*move, side&#41;;

        if &#40;currstack->fifty < 100&#41; &#123;
            for &#40;i = 4; i <= currstack->fifty; i += 2&#41; &#123;
                if &#40;currstack->hash ^ &#40;currstack - i&#41;->hash&#41;
                    ;
                else &#123;
                   /* repetition */
                    x = 0;
                    pv&#91;ply + 1&#93;&#91;0&#93; = 0;
                    goto SET_BEST;
                &#125;
            &#125;
        &#125; else &#123;
            x = 0;
            pv&#91;ply + 1&#93;&#91;0&#93; = 0;
            goto SET_BEST;
        &#125;
        
        if &#40;ttmove&#41;&#123;
            /* !!! TEMP; return TT exact hit  */
            unmake&#40;*move, side&#41;;
            pv&#91;ply&#93;&#91;0&#93; = ttmove;
            pv&#91;ply&#93;&#91;1&#93; = 0;
            return ttdata->score;
        &#125;

        //validate ep;
        if &#40;ep && is_sq_attacked_brq&#40;king&#91;side&#93;, xside&#41;) &#123;
            unmake&#40;*move, side&#41;;
            continue;
        &#125;

        assert&#40;!is_sq_attacked&#40;king&#91;side&#93;, xside&#41;);

        if &#40;depth == 1&#41; &#123;
            assert&#40;ply & 1&#41;;
            /* seeks mate only. returns if not mate. */
            x = mat&#91;side&#93; - mat&#91;xside&#93; + 2;
            assert&#40;x >= beta&#41;;
            unmake&#40;*move, side&#41;;
            storehash&#40;x, depth, LB, *move, xeval&#41;;
            return x;
        &#125;

        getSideIncheck&#40;&check_s, xside&#41;;
        if &#40;check_s.type&#41; &#123;
            SET_CHECKBIT&#40;*move&#41;; /* may hashed with TT move */
        &#125;
        assert&#40;!&#40;ply & 1&#41; ? alpha >= P_MATE &#58; 1&#41;;

        if &#40;depth == 2 && !check_s.type&#41; &#123;
            /* seek only mate. */
            x = mat&#91;side&#93; - mat&#91;xside&#93; + 2;
            pv&#91;ply + 1&#93;&#91;0&#93; = 0;
            assert&#40;x <= alpha&#41;;
            goto SET_BEST;
        &#125;

#if 0        
        if (!&#40;ply & 1&#41; && INFI - &#40;ply + 1&#41; == alpha + 2 && !check_s.type&#41; &#123;
            x = mat&#91;side&#93; - mat&#91;xside&#93; + 2;
            pv&#91;ply + 1&#93;&#91;0&#93; = 0;
            goto SET_BEST;
        &#125;
#endif

        x = -search_mate&#40;depth - 1, -beta, -alpha, xside, side, ply + 1, check_s, followPV&#41;;
        assert&#40;x ^ INFI&#41;;

SET_BEST&#58;

        unmake&#40;*move, side&#41;;
        ++movedone;
        followPV = 0;
        assert&#40;x >= -INFI && x <= INFI&#41;;

        if &#40;x > best&#41; &#123;
            best = x;
            bestmove = *move;
            if &#40;best > alpha&#41; &#123;
                if &#40;best >= beta&#41; &#123;
                    assert&#40;best ^ -INFI&#41;;
                    ++history&#91;side&#93;&#91;movepc&#93;&#91;TO&#40;bestmove&#41;&#93;;
                    break;
                &#125;
                alpha = best;
                pv&#91;ply&#93;&#91;0&#93; = *move;
                pv_t *dst = pv&#91;ply&#93; + 1;
                pv_t *src = pv&#91;ply + 1&#93;;
                while ((*&#40;dst++) = *&#40;src++)));
                assert&#40;debugPV&#40;pv&#91;ply&#93;, side, ply&#41;);
                assert&#40;depth & 1 ? beta <= M_MATE &#58; alpha >= P_MATE&#41;;
                /* may have lesser mate from TT hit ??? */
                if (!&#40;ply & 1&#41; && best == INFI - &#40;ply + 1&#41;) &#123;
                    storehash&#40;best + ply, MAX_DEPTH, EX, bestmove, -INFI&#41;;
                    return best;
                &#125;
            &#125;
        &#125;
    &#125;/* end for loop*/

    if &#40;best ^ -INFI&#41; &#123;
        assert&#40;bestmove&#41;;
        assert&#40;is_move_generated&#40;list, bestmove&#41;);
        if &#40;best > M_MATE && best < P_MATE&#41; &#123;
            if &#40;best ^ 0&#41; &#123;
                storehash&#40;best, depth, best <= alpha0 ? UB &#58; &#40;best >= beta ? LB &#58; EX&#41;, bestmove, xeval&#41;;
            &#125;
        &#125; else if &#40;best <= M_MATE&#41; &#123;
            /* even ply has -mate, odd ply +mate  */
            storehash&#40;best - ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125; else &#123;
            storehash&#40;best + ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125;
        assert&#40;best ^ INFI&#41;;
        return best;
    &#125; else &#123;
MATE&#58;
        assert&#40;!bestmove&#41;;
        if &#40;incheck&#41; &#123;
            storehash&#40;-INFI, MAX_DEPTH, EX, 0, -INFI&#41;;
            return -INFI + ply;
        &#125;
        assert&#40;depth >= 1&#41;;
        /* stalemate*/
        storehash&#40;1, MAX_DEPTH, EX, 0, -INFI&#41;;
        return 1;
    &#125;
&#125;
Best Regards,
Rasjid.
AlvaroBegue
Posts: 931
Joined: Tue Mar 09, 2010 3:46 pm
Location: New York
Full name: Álvaro Begué (RuyDos)

Re: My program can't mate KQK or KRK!

Post by AlvaroBegue »

Does your program claim to be able to mate, but it never gets there because it doesn't find the shortest mate?

I do three simple things that result in pretty stable behavior in these situations:
(1) Use -8000+moves_from_root as score for getting mated.
(2) Store only "better than 7500" or "worse than -7500" in the transposition table, instead of the actual mating score.
(3) Before every search, store every position since the last irreversible move as a dead draw in the transposition table, with a high depth associated to it.

(1) just makes sense if you want your program to find the shortest mate. The problem is that the score assigned to each position changes when the root changes. I got (2) as the solution to that problem from Bruce Moreland's pages. It is very simple and works well enough. I am sure something smarter could be done, but it's not worth the effort.

I ran into some situations where my program would repeat a position thinking the score was something other than 0, because the transposition tables remember scores that were obtained with a different history. It's possible I had some other bug, but (3) improved the behavior dramatically.
elcabesa
Posts: 855
Joined: Sun May 23, 2010 1:32 pm

Re: My program can't mate KQK or KRK!

Post by elcabesa »

maybe I'm wrong but I think you should "correct" the values you save in the hashTable.

when you save the value:

Code: Select all

if ( abs&#40;val&#41; >= MATE_IN_MAX_PLY )
			&#123;
			// convert to relative value!
			if ( val < 0 )
			&#123;
				val -= ply;
			&#125;
			else
			&#123;
				val += ply;
			&#125;
		&#125;
and when you retrieve it

Code: Select all

inline score getValue&#40; int ply ) const
    &#123;
        if ( abs&#40;value&#41; >= MATE_IN_MAX_PLY )
        &#123;
            return value < 0 ? value + ply &#58; value - ply;
        &#125;
        return value;
    &#125;

in this way when you save in the transposition table a mate in 4 it's a mate in 4 every time it was checked whathever long is the path to reach the position.
Chan Rasjid
Posts: 588
Joined: Thu Mar 09, 2006 4:47 pm
Location: Singapore

Re: My program can't mate KQK or KRK!

Post by Chan Rasjid »

Hello Alvaro,
AlvaroBegue wrote:Does your program claim to be able to mate, but it never gets there because it doesn't find the shortest mate?

I do three simple things that result in pretty stable behavior in these situations:
(1) Use -8000+moves_from_root as score for getting mated.
(2) Store only "better than 7500" or "worse than -7500" in the transposition table, instead of the actual mating score.
(3) Before every search, store every position since the last irreversible move as a dead draw in the transposition table, with a high depth associated to it.

(1) just makes sense if you want your program to find the shortest mate. The problem is that the score assigned to each position changes when the root changes. I got (2) as the solution to that problem from Bruce Moreland's pages. It is very simple and works well enough. I am sure something smarter could be done, but it's not worth the effort.

I ran into some situations where my program would repeat a position thinking the score was something other than 0, because the transposition tables remember scores that were obtained with a different history. It's possible I had some other bug, but (3) improved the behavior dramatically.
I post two pgn files. For the initial KRK game before the TT is filled, it mates somehow.

The second is by just rewinding the game to the start and repeat with the TT tables not cleared. The log for the second games are all +mate scores from 8000 - 127 = +7873 showing the engine is relying on the TT mate scores. From here, I have no clue...

I will think about the other things you wrote.

Thanks.
[Event "Computer Chess Game"]
[Site "mycomputer"]
[Date "2013.03.12"]
[Round "-"]
[White "Cowrie Ver 1.0"]
[Black "Octochess revision 4984"]
[Result "1-0"]
[TimeControl "60/30"]
[FEN "8/8/8/8/3k4/8/8/K6R w - - 1 1"]
[SetUp "1"]

{--------------
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . k . . . .
. . . . . . . .
. . . . . . . .
K . . . . . . R
white to play
--------------}
1. Re1 Kc3 2. Ka2 Kd2 3. Rb1 Kd3 4. Kb3 Kd2 5. Kc4 Ke3 6. Rb2 Ke4 7. Rh2
Ke5 8. Rh6 Kf4 9. Rh4+ Ke3 10. Rd4 Ke2 11. Re4+ Kf2 12. Kd4 Kf3 13. Kd3 Kf2
14. Re3 Kf1 15. Re2 Kg1 16. Ke4 Kh1 17. Kf3 Kg1 18. Re5 Kf1 19. Kg3 Kg1 20.
Re1#
{Xboard adjudication: Checkmate} 1-0
repeat game:
[Event "Computer Chess Game"]
[Site "mycomputer"]
[Date "2013.03.12"]
[Round "-"]
[White "Cowrie Ver 1.0"]
[Black "Octochess revision 4984"]
[Result "1/2-1/2"]
[TimeControl "60/30"]
[FEN "8/8/8/8/3k4/8/8/K6R w - - 1 1"]
[SetUp "1"]

{--------------
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . k . . . .
. . . . . . . .
. . . . . . . .
K . . . . . . R
white to play
--------------}
1. Rh5 Kc4 2. Rh2 Kd4 3. Rh5 Kc4 4. Rh4+ Kd5 5. Rh7 Ke5 6. Kb2 Kf5 7. Ka2
Kg6 8. Re7 Kf6 9. Rb7 Ke5 10. Re7+ Kf6 11. Rh7 Kg6 12. Re7 Kf6
{Draw by repetition} 1/2-1/2
log: all scores are +mate scores.
Cowrie Chess Version 1.0, 3rd Feb 2010
Hash Size 256 MB, Pawn Hash Size 16 MB
node-mask, timeout 0x3ffff, poll 0x3fffff
Number valid moves 16
depth start 6
!!! 16 Repeat PV - returns
1 h1h5 score( 7945) depth(32) pvL( 1) ply(16) nps( 4309588) pc(3) cttime(25) ctpoll(0)
sec(0.43, used 0.12) nodes(q 0.0%, h 29.2%, f 70.8%) FH(13.0%) evasion(4.7%) invalid(1.1%)
Root-red(6.90%, rsc 10.00%) Root-research(0.69%) BranchF(0.89%)
Hash-hit(full 78.9%, qs 5.5%, p( 100.0%, cover 0.0%), ev (99.2%) matOWrite( 0.14) draw(0)
ext(1.59%) see(qs 0.00%, delay 7.55%) zero-wnd(14.20%, rsc 6.44%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(2.98%)
EV call(0.46%) lazy(0.00%) cut(0.00%) fl(0.00%)
1 d4c4
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7943 h5h2
Number valid moves 17
depth start 2
!!! 16 Repeat PV - returns
2 h5h2 score( 7943) depth(34) pvL( 1) ply(24) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.47, used 0.01) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(0.6%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 66.6%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(0.04%) lazy(0.00%) cut(0.00%) fl(0.00%)
2 c4d4
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7945 h2h5
Number valid moves 17
depth start 2
!!! 16 Repeat PV - returns
3 h2h5 score( 7945) depth(34) pvL( 1) ply( 7) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.47, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(11.8%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 99.0%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(2.25%) lazy(0.00%) cut(0.00%) fl(0.00%)
3 d4c4
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7943 h5h2
!!! bestmove cancelled - draw
Number valid moves 17
depth start 2
!!! 16 Repeat PV - returns
4 h5h4 score( 7943) depth(32) pvL( 1) ply( 9) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.48, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(3.0%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 74.9%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(1.09%) lazy(0.00%) cut(0.00%) fl(0.00%)
4 c4d5
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7945 h4h7
Number valid moves 17
depth start 2
!!! 16 Repeat PV - returns
5 h4h7 score( 7945) depth(34) pvL( 1) ply(17) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.49, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(4.1%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 73.1%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(0.80%) lazy(0.00%) cut(0.00%) fl(0.00%)
5 d5e5
node-mask, timeout 0x3ffff, poll 0x7fffff
Number valid moves 17
depth start 3
!!! 16 Repeat PV - returns
6 a1b2 score( 7945) depth(32) pvL( 1) ply(11) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.49, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(13.3%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 91.0%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(1.64%) lazy(0.00%) cut(0.00%) fl(0.00%)
6 e5f5
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7951 b2a2
Number valid moves 22
depth start 2
!!! 16 Repeat PV - returns
7 b2a2 score( 7951) depth(34) pvL( 1) ply(11) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.50, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(5.8%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 83.0%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(0.40%) lazy(0.00%) cut(0.00%) fl(0.00%)
7 f5g6
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7957 h7e7
Number valid moves 19
depth start 2
!!! 16 Repeat PV - returns
8 h7e7 score( 7957) depth(34) pvL( 1) ply(17) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.51, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(0.9%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 62.6%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(0.17%) lazy(0.00%) cut(0.00%) fl(0.00%)
8 g6f6
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7947 e7b7
Number valid moves 19
depth start 2
!!! 16 Repeat PV - returns
9 e7b7 score( 7947) depth(34) pvL( 1) ply(23) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.51, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(3.1%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 68.8%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.61) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(0.39%) lazy(0.00%) cut(0.00%) fl(0.00%)
9 f6e5
node-mask, timeout 0x3ffff, poll 0x7fffff
Number valid moves 19
depth start 6
!!! 16 Repeat PV - returns
10 b7e7 score( 7945) depth(32) pvL( 1) ply(11) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.52, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(10.5%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 89.5%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(1.08%) lazy(0.00%) cut(0.00%) fl(0.00%)
10 e5f6
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7947 e7b7
!!! bestmove cancelled - draw
Number valid moves 19
depth start 2
!!! 16 Repeat PV - returns
11 e7h7 score( 7945) depth(32) pvL( 1) ply( 9) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.53, used 0.00) nodes(q 0.0%, h 2.9%, f 97.1%) FH(0.0%) evasion(32.4%) invalid(2.2%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(0.00%)
Hash-hit(full 76.8%, qs 50.0%, p( 100.0%, cover 0.0%), ev (100.0%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs 0.00%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(2.13%) lazy(0.00%) cut(0.00%) fl(0.00%)
11 f6g6
node-mask, timeout 0x3ffff, poll 0x7fffff
!!! Root start with TT matesearch score 7957 h7e7
Number valid moves 19
depth start 2
!!! 16 Repeat PV - returns
12 h7e7 score( 7957) depth(34) pvL( 1) ply(16) nps( 4309588) pc(3) cttime(18) ctpoll(0)
sec(0.54, used 0.00) nodes(q 0.0%, h 0.0%, f 100.0%) FH(0.0%) evasion(2.8%) invalid(0.0%)
Root-red(0.00%, rsc -nan%) Root-research(0.00%) BranchF(-nan%)
Hash-hit(full 77.9%, qs -nan%, p( 100.0%, cover 0.0%), ev (-nan%) matOWrite( 0.00) draw(0)
ext(0.00%) see(qs -nan%, delay 0.00%) zero-wnd(0.00%, rsc -nan%)
null(0.0% hit -nan% verify -nan% ok -nan% ) lmr(0.00%, rsc -nan%) ply1_rsc(0) fut(0.00%) killer(0.00%)
EV call(1.34%) lazy(0.00%) cut(0.00%) fl(0.00%)
Best Regards,
Rasjid.
Chan Rasjid
Posts: 588
Joined: Thu Mar 09, 2006 4:47 pm
Location: Singapore

Re: My program can't mate KQK or KRK!

Post by Chan Rasjid »

Hello Marco,
elcabesa wrote:maybe I'm wrong but I think you should "correct" the values you save in the hashTable.

when you save the value:

Code: Select all

if ( abs&#40;val&#41; >= MATE_IN_MAX_PLY )
			&#123;
			// convert to relative value!
			if ( val < 0 )
			&#123;
				val -= ply;
			&#125;
			else
			&#123;
				val += ply;
			&#125;
		&#125;
and when you retrieve it

Code: Select all

inline score getValue&#40; int ply ) const
    &#123;
        if ( abs&#40;value&#41; >= MATE_IN_MAX_PLY )
        &#123;
            return value < 0 ? value + ply &#58; value - ply;
        &#125;
        return value;
    &#125;

in this way when you save in the transposition table a mate in 4 it's a mate in 4 every time it was checked whathever long is the path to reach the position.
This is what I do. I use the Crafty method when it comes to storing mate scores - by adjusting with a +/- ply.

Retrieving will add/subtract the ply of the current node.

Best Regards,
Rasjid.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: My program can't mate KQK or KRK!

Post by bob »

The most common cause of this is failing to "correct" the score you store in the tt.

You can't simply store the current score, you have to correct it to be a "mate in N from the current ply" as opposed to "mate in N from the root position." If you fail to do this, then your program will keep announcing mate, but never deliver on it.

The idea is simple:

1. When you store a tt entry and the score is a mate-type score, before you store it, correct it based on the current ply.

example. Assume you use MATE as 9999.

You have found a forced mate in 5 moves (9 plies total), which sets the score to 9999 - 9 or 9990. If you store a tt entry at ply=3, you want to store 9992, rather than 9990, because this position is a mate in 4 moves from the current position, not a mate in 5.

Later, you somehow reach this position somewhere else in the tree. First, let's assume a hit at ply=3 on the next iteration. You retrieve this position, with a score of 9992, you subtract the two plies (subtract current ply - 1) which corrects it to mate in 5, which is correct.

Or, assume you reach this position later at ply=4 (as opposed to ply=2). Now you correct the 9992 to 9988 (subtract ply-1 again but now ply=4) so that you get a score of 9988, which is the correct "mate in 6 from the root since you somehow wasted 2 plies of time to reach a mate-in-4 position.

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

Re: My program can't mate KQK or KRK!

Post by Chan Rasjid »

Hello Bob,
bob wrote:The most common cause of this is failing to "correct" the score you store in the tt.

You can't simply store the current score, you have to correct it to be a "mate in N from the current ply" as opposed to "mate in N from the root position." If you fail to do this, then your program will keep announcing mate, but never deliver on it.

The idea is simple:

1. When you store a tt entry and the score is a mate-type score, before you store it, correct it based on the current ply.

example. Assume you use MATE as 9999.

You have found a forced mate in 5 moves (9 plies total), which sets the score to 9999 - 9 or 9990. If you store a tt entry at ply=3, you want to store 9992, rather than 9990, because this position is a mate in 4 moves from the current position, not a mate in 5.

Later, you somehow reach this position somewhere else in the tree. First, let's assume a hit at ply=3 on the next iteration. You retrieve this position, with a score of 9992, you subtract the two plies (subtract current ply - 1) which corrects it to mate in 5, which is correct.

Or, assume you reach this position later at ply=4 (as opposed to ply=2). Now you correct the 9992 to 9988 (subtract ply-1 again but now ply=4) so that you get a score of 9988, which is the correct "mate in 6 from the root since you somehow wasted 2 plies of time to reach a mate-in-4 position.

Hope that helps...
I think I am aware of most aspects of chess programming as I have followed this forum for many years.

I am actually using the method of Crafty when it comes to storing mate scores; storing the 'distance-to-mate' (score + ply) and not the mate from root. On retrieval from TT, it would be an adjusted mate score for distance-to-mate from root.

I think the likely cause of the bug could be about storing +mate scores as either LOWER or EXACT. My normal search returns whenever a +mate score is found; there is no purpose in searching further for the best mate as it is not certain it is the PV. If +mate is propagated to the root, then it is first stored as :
storehash(score + ply, MAX_DEPTH, LOWER, bestmove,...);
It is a lower bound as it could be better.

Then the program calls search_mate() at root iterating through even depth 2/5/6/8... to get the best mate. When best mate is found, it will be stored as EXACT.

I don't know how things go wrong. I don't think there is mistake in 'correcting' for mate scores. But I will again take an 'incisive' look at my codes again.

Best Regards,
Rasjid.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: My program can't mate KQK or KRK!

Post by bob »

Chan Rasjid wrote:Hello Bob,
bob wrote:The most common cause of this is failing to "correct" the score you store in the tt.

You can't simply store the current score, you have to correct it to be a "mate in N from the current ply" as opposed to "mate in N from the root position." If you fail to do this, then your program will keep announcing mate, but never deliver on it.

The idea is simple:

1. When you store a tt entry and the score is a mate-type score, before you store it, correct it based on the current ply.

example. Assume you use MATE as 9999.

You have found a forced mate in 5 moves (9 plies total), which sets the score to 9999 - 9 or 9990. If you store a tt entry at ply=3, you want to store 9992, rather than 9990, because this position is a mate in 4 moves from the current position, not a mate in 5.

Later, you somehow reach this position somewhere else in the tree. First, let's assume a hit at ply=3 on the next iteration. You retrieve this position, with a score of 9992, you subtract the two plies (subtract current ply - 1) which corrects it to mate in 5, which is correct.

Or, assume you reach this position later at ply=4 (as opposed to ply=2). Now you correct the 9992 to 9988 (subtract ply-1 again but now ply=4) so that you get a score of 9988, which is the correct "mate in 6 from the root since you somehow wasted 2 plies of time to reach a mate-in-4 position.

Hope that helps...
I think I am aware of most aspects of chess programming as I have followed this forum for many years.

I am actually using the method of Crafty when it comes to storing mate scores; storing the 'distance-to-mate' (score + ply) and not the mate from root. On retrieval from TT, it would be an adjusted mate score for distance-to-mate from root.

I think the likely cause of the bug could be about storing +mate scores as either LOWER or EXACT. My normal search returns whenever a +mate score is found; there is no purpose in searching further for the best mate as it is not certain it is the PV. If +mate is propagated to the root, then it is first stored as :
storehash(score + ply, MAX_DEPTH, LOWER, bestmove,...);
It is a lower bound as it could be better.

Then the program calls search_mate() at root iterating through even depth 2/5/6/8... to get the best mate. When best mate is found, it will be stored as EXACT.

I don't know how things go wrong. I don't think there is mistake in 'correcting' for mate scores. But I will again take an 'incisive' look at my codes again.

Best Regards,
Rasjid.
Note that you have to correct ALL entries. EXACT, UPPER and LOWER. I found that screwing up any of this would lead to search instability and incorrect mate announcements. With the correction done as in Crafty, I never see this sort of problem. The mate score always drops move to move as it should...

Note that I have looked at my code MANY times over the years, and while I don't remember when it was fixed the last time, I certainly got it wrong on several occasions. :) And I try to avoid touching any of that today for fear of "fixing it again"... :)
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: My program can't mate KQK or KRK!

Post by Sven »

Chan Rasjid wrote:I think I am aware of most aspects of chess programming as I have followed this forum for many years.

I am actually using the method of Crafty when it comes to storing mate scores; storing the 'distance-to-mate' (score + ply) and not the mate from root. On retrieval from TT, it would be an adjusted mate score for distance-to-mate from root.
What are the values of M_MATE and P_MATE? I would assume that M_MATE is a high negative number indicating "mated in zero" while P_MATE is something like -M_MATE and therefore a high positive number. Am I right?

If my assumption is correct then I do not understand how you store mate scores. If we look at this code snippet:

Code: Select all

   &#125;/* end for loop*/

    if &#40;best ^ -INFI&#41; &#123;
        assert&#40;bestmove&#41;;
        assert&#40;is_move_generated&#40;list, bestmove&#41;);
        if &#40;best > M_MATE && best < P_MATE&#41; &#123;
            if &#40;best ^ 0&#41; &#123;
                storehash&#40;best, depth, best <= alpha0 ? UB &#58; &#40;best >= beta ? LB &#58; EX&#41;, bestmove, xeval&#41;;
            &#125;
        &#125; else if &#40;best <= M_MATE&#41; &#123;
            /* even ply has -mate, odd ply +mate  */
            storehash&#40;best - ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125; else &#123;
            storehash&#40;best + ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125;
        assert&#40;best ^ INFI&#41;;
        return best;
    &#125; else &#123;
MATE&#58; 
then what is the meaning of the condition "if (best <= M_MATE)", and why does

Code: Select all

            if &#40;best ^ 0&#41; &#123;
                storehash&#40;best, depth, best <= alpha0 ? UB &#58; &#40;best >= beta ? LB &#58; EX&#41;, bestmove, xeval&#41;;
            &#125;
NOT adjust the mate score according to the "distance to mate" principle?

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

Re: My program can't mate KQK or KRK!

Post by Chan Rasjid »

Hello Sven,
Sven Schüle wrote:
Chan Rasjid wrote:I think I am aware of most aspects of chess programming as I have followed this forum for many years.

I am actually using the method of Crafty when it comes to storing mate scores; storing the 'distance-to-mate' (score + ply) and not the mate from root. On retrieval from TT, it would be an adjusted mate score for distance-to-mate from root.
What are the values of M_MATE and P_MATE? I would assume that M_MATE is a high negative number indicating "mated in zero" while P_MATE is something like -M_MATE and therefore a high positive number. Am I right?

If my assumption is correct then I do not understand how you store mate scores. If we look at this code snippet:

Code: Select all

   &#125;/* end for loop*/

    if &#40;best ^ -INFI&#41; &#123;
        assert&#40;bestmove&#41;;
        assert&#40;is_move_generated&#40;list, bestmove&#41;);
        if &#40;best > M_MATE && best < P_MATE&#41; &#123;
            if &#40;best ^ 0&#41; &#123;
                storehash&#40;best, depth, best <= alpha0 ? UB &#58; &#40;best >= beta ? LB &#58; EX&#41;, bestmove, xeval&#41;;
            &#125;
        &#125; else if &#40;best <= M_MATE&#41; &#123;
            /* even ply has -mate, odd ply +mate  */
            storehash&#40;best - ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125; else &#123;
            storehash&#40;best + ply, MAX_DEPTH, best <= alpha0 ? UB &#58; &#40;best < beta ? EX &#58; LB&#41;, bestmove, -INFI&#41;;
        &#125;
        assert&#40;best ^ INFI&#41;;
        return best;
    &#125; else &#123;
MATE&#58; 
then what is the meaning of the condition "if (best <= M_MATE)", and why does

Code: Select all

            if &#40;best ^ 0&#41; &#123;
                storehash&#40;best, depth, best <= alpha0 ? UB &#58; &#40;best >= beta ? LB &#58; EX&#41;, bestmove, xeval&#41;;
            &#125;
NOT adjust the mate score according to the "distance to mate" principle?

Sven

Code: Select all

#define INFI 8000
#define MAX_PLY 127
#define M_MATE (-INFI + MAX_PLY&#41;
#define P_MATE &#40;INFI - MAX_PLY&#41;
best means best score.

So (best > M_MATE && best < p_MATE)
are non-mate scores; 0 means material/rep3/fifty draw and it is not hashed.

best <= M_MATE are losing mate scores, best >= P_MATE are winning mate scores.

7999 means mate-in-1.

I have checked my storehash codes many times and still can't see where the problem comes from.

Best Regards,
Rasjid.