Implementing SEE

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: Implementing SEE

Post by lucasart »

cms271828 wrote:Hi

I've been away from chess programming for a while, but I'm still stuck on SEE.
Every time I try to resolve something I seem to open a new can of worms.

I planned on using an iterative static exchange evaluation following the psuedo code:

Code: Select all

int staticexchangeevaluationw ( const int from , const int to ) 
{
    int iteration ;
    int valuelist[ 3 2 ] ;
    int next ;
    int valueattacker ;

    // iteration for White
    iteration=0;
    valuelist[iteration] = boardvalue ( to ) ;
    next = from ;
    generateattackers ( next , to ) ;
    if ( blackattackers( ) == 0 ) return boardvalue ( t o ) ;

    valueattacker = boardvalue ( from ) ;

    // forward−iterationloop : ( 1 ) fill the valuelist
    while ( true ) 
    {
        // iteration for Black
        iteration ++;
        valuelist[ iteration ]= valueattacker −valuelist [iteration− 1 ] ;
        next = nextblackattacker ( ) ;
        updatexrayattackers ( next , t o ) ;

        if ( whiteattackers( ) == 0 ) break ;

        valueattacker = board value ( next ) ;

        //iteration for White
       iteration ++;
       valuelist [iteration ]= valueattacker −valuelist [iteration − 1 ] ;
       next = nextwhiteattacker ( ) ;
       updatexrayattackers( next , t o ) ;

       if ( blackattackers( ) == 0 ) break ;

       valueattacker = board value ( next ) ;
    }

    // reverse −iteration loop : ( 2 ) evaluate the valuelist
    while ( true ) 
    {
        if( iteration == 0 ) return valuelistt [ 0 ] ;

        if ( valuelist [iteration ] > − valuelist [ iiteration −1])
        {
            valuelist [iteration −1] = −valuelist [iteration ] ;
        }

        iteration −−;
    }
}
I'm not sure about generating attackers, the problem is more trickier than it seems.
I have had a few ideas but they all seem problematic.

Any advice would be grateful, thanks.
Here's mine if it helps. It's basically the one of StockFish, that I also improved to handle promotions and en passant captures. I tested it, and it seems to work well

Code: Select all

static int full_see(const Board *B, int fsq, int tsq)
// SEE from StockFish: adapted and improved to handle promotions
{
	assert(square_ok(fsq) && square_ok(tsq));

	int stm = color_on(B, fsq);	// side to move
	U64 attackers, stm_attackers;
	int swap_list[32], sl_idx = 1;
	occ_t occ = B->st->occ;

	int piece = B->piece_on[fsq], capture;

	const bool is_prank = test_bit(PPromotionRank[stm], tsq);
	static const int see_val[NB_PIECE+1] = {MP, MN, MB, MR, MQ, 200*MP, 0};

	// Handle en-passant
	if (B->st->epsq == tsq && B->piece_on[fsq] == Pawn) {
		clear_occ(&occ, stm ? tsq+8 : tsq-8);
		capture = Pawn;
	} else
		capture = B->piece_on[tsq];

	assert(capture != King);

	swap_list[0] = see_val[capture];
	clear_occ(&occ, fsq);
	attackers = calc_attackers(B, tsq, &occ);

	// Handle promotion
	if (piece == Pawn && is_prank) {
		swap_list[0] += see_val[Queen] - see_val[Pawn];
		capture = Queen;
	} else
		capture = B->piece_on[fsq];

	// If the opponent has no attackers we are finished
	stm = opp_color(stm);
	stm_attackers = attackers & B->all[stm];
	if (!stm_attackers)
		return swap_list[0];

	/* The destination square is defended, which makes things rather more difficult to compute. We
	 * proceed by building up a "swap list" containing the material gain or loss at each stop in a
	 * sequence of captures to the destination square, where the sides alternately capture, and
	 * always capture with the least valuable piece. After each capture, we look for new X-ray
	 * attacks from behind the capturing piece. */
	do {
		/* Locate the least valuable attacker for the side to move. The loop below looks like it is
		 * potentially infinite, but it isn't. We know that the side to move still has at least one
		 * attacker left. */
		for (piece = Pawn; !(stm_attackers & B->b[stm][piece]); piece++)
			assert&#40;piece < King&#41;;

		// remove the piece &#40;from wherever it came&#41;
		clear_occ&#40;&occ, first_bit&#40;stm_attackers & B->b&#91;stm&#93;&#91;piece&#93;));
		// scan for new X-ray attacks through the vacated square
		attackers |= &#40;get_all_RQ&#40;B&#41; & rook_attack&#40;&occ, tsq&#41;) | &#40;get_all_BQ&#40;B&#41; & bishop_attack&#40;&occ, tsq&#41;);
		// cut out pieces we've already done
		attackers &= occ.all;

		// add the new entry to the swap list &#40;beware of promoting pawn captures&#41;
		assert&#40;sl_idx < 32&#41;;
		swap_list&#91;sl_idx&#93; = -swap_list&#91;sl_idx - 1&#93; + see_val&#91;capture&#93;;
		if &#40;piece == Pawn && is_prank&#41; &#123;
			swap_list&#91;sl_idx&#93; += see_val&#91;Queen&#93; - see_val&#91;Pawn&#93;;
			capture = Queen;
		&#125; else
			capture = piece;
		sl_idx++;

		stm = opp_color&#40;stm&#41;;
		stm_attackers = attackers & B->all&#91;stm&#93;;

		// Stop after a king capture
		if &#40;piece == King && stm_attackers&#41; &#123;
			assert&#40;sl_idx < 32&#41;;
			swap_list&#91;sl_idx++&#93; = see_val&#91;King&#93;;
			break;
		&#125;
	&#125; while &#40;stm_attackers&#41;;

	/* Having built the swap list, we negamax through it to find the best achievable score from the
	 * point of view of the side to move */
	while (--sl_idx&#41;
		swap_list&#91;sl_idx-1&#93; = min&#40;-swap_list&#91;sl_idx&#93;, swap_list&#91;sl_idx-1&#93;);

	return swap_list&#91;0&#93;;
&#125;
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Implementing SEE

Post by hgm »

cms271828 wrote:If the black bishop is played, it would be wrong to use the white queen next.
So the problem is updating x, I have 3 ideas:
Such 'foe-backed' attackers are in any case very troublesome, because they guarantee the presence of tactics outside the square. Plus it can change the optimal order: suppose there was not only a black Bishop behind your white, but a BQ battery:

[d]7q/6b1/8/4B3/2N2P2/2p3Q1/N7/1n6 w

The black Pawn can be won through 1. Nxc3 Nxc3 2.Qxc3. While if you would use 'optimal' ordering, B before Q, 1. Nxc3 Nxc 3. Bxc3 would unleash the battery with 3... Bxc3and you are down a Knight (and after 4. Qxc3 Qxc3 you are down a Queen). In this exchange the white Bishop has more value as a blocker than as an attacker!

This is why I switched to BLIND. If I can grab a higher piece, or an unprotected one, I am happy, and do it. But if I am left with a non-futile capture after he recaptures, SEE becomes so inaccurate that it is hardly worth it. I want to see it proven in QS in that case.
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Implementing SEE

Post by hgm »

if you really want tohave alaugh, here is Joker's:

Code: Select all

int SEE&#40;int color, int move&#41;
&#123;
    int a, b, e, w, piece, his, mine, block, from, to, x, y, h, v, ipiece=0;
    static int seq;

    block =  &#40;seq += 0x100&#41; ^ 0x80000000;  /* fresh codes for used/blocked */
/*printf&#40;"SEE\n");*/
    /* start doing the given capture, and set up window */
    to   = move & 0x77;
    from = move>>8 & 0x77;
/*printf&#40;"to = %02x\n", to&#41;;*/
    if&#40;!&#40;move&0x8000&#41;) 
    &#123;   /* offensive SEE&#58; start with given capture */
        a = -1;                   /* if losing, don't care how bad */
        ipiece = board&#91;from&#93;;
        e = b = move>>16 & 0xFF;  /* from MMV pre-sort */
        used&#91;ipiece-WHITE&#93; = seq; /* mark as used      */
        w = qval&#91;ipiece-WHITE&#93;;   /* what he is gonna capture next */
    &#125; else
    &#123;   /* defensive SEE&#58; let him start, and see how bad it is */
        a = -100; e = b = ipiece = 0;
        w = qval&#91;board&#91;to&#93;-WHITE&#93;;
    &#125;

    /* generate first recapture of opponent, preppare his LVA list         */
    his = First&#91;COLOR-color&#93;;  /* Pieces-only attacker list, in LVA order  */
    /* Prepend relevant Pawns to list. Other pieces are already sorted.    */
    from = to - color + 47;                          /* first pawn square  */
    piece = board&#91;from&#93;;                                                   
    if&#40;&#40;piece & &#40;PAWNS|COLOR&#41;) == (&#40;PAWNS|COLOR&#41;^color&#41;)      /* his pawn  */
        his = piece;          /* prepend to list &#40;next-field already set&#41;  */

    from += 2;                                      /* second pawn square  */
    piece = board&#91;from&#93;;     
    if&#40;&#40;piece & &#40;PAWNS|COLOR&#41;) != (&#40;PAWNS|COLOR&#41;^color&#41;)  /* not his pawn  */
    &#123;    piece = his; his = next&#91;his-WHITE&#93;; &#125;      /* take next piece     */
/*printf&#40;"from = %02x, piece = %02x\n", from, piece&#41;;*/
    /* 'piece' now holds first piece, 'mine' the second (+ rest of list&#41;   */
retry&#58;  do &#123;
SDB1
            if&#40;&#40;x=pos&#91;piece-WHITE&#93;)>=0 &&        /* was already captured */
                used&#91;piece-WHITE&#93;!=seq &&        /* captured in this SEE */
                &#40;code&#91;piece-WHITE&#93; & &#40;h=capt_code&#91;to-x&#93;) ))
            &#123;   /* pseudo-hit&#58; aligned for capture */
/*printf&#40;"pseudo hit\n");*/
                if&#40;h & C_CONTACT&#41; break; /* contact attack, always hit   */
                v = delta_vec&#91;to-x&#93;;
                y=to;
                while&#40;&#40;h=board&#91;y-=v&#93;)==DUMMY                 /* scan ray */
                       || used&#91;h-WHITE&#93;==seq&#41;
                       ;     /* ignore used pieces */
                if&#40;x==y&#41; break;          /* ray is clear, distant hit    */
                /* ray is blocked. mark blocker so we can retry later    */
                used&#91;h-WHITE&#93; = block + piece;
            &#125;
            if&#40;!&#40;piece = his&#41;) return b;         /* out of moves         */
            his = next&#91;his-WHITE&#93;;               /* remaining list       */
        &#125; while&#40;1&#41;;
    LVA = piece; if&#40;w>9&#41; return&#40;-100&#41;;
    /* for first capture, we require legality. So test for pin on King   */
    y = x = pos&#91;BLACK-color&#93;; /* opponent's King */
    if&#40;capt_code&#91;pos&#91;piece-WHITE&#93;-x&#93; & C_QUEEN&#41;
    &#123;
/*printf&#40;"aligned with King at %02x h= %02x & %02x\n", x, h, C_QUEEN&#41;;*/
        v = delta_vec&#91;pos&#91;piece-WHITE&#93;-x&#93;;
        while&#40;board&#91;x+=v&#93; == DUMMY || board&#91;x&#93; == piece
                                   || board&#91;x&#93; == ipiece&#41;;
/*printf&#40;"ray ends at %02x, code=%02x & %02x\n", x, h, code&#91;board&#91;x&#93;-WHITE&#93;);*/
        if&#40;!&#40;board&#91;x&#93; & COLOR^color&#41; &&
           capt_code&#91;y-x&#93; & code&#91;board&#91;x&#93;-WHITE&#93;)/* piece was pinned     */
        &#123;   LVA = 0;                             /* try next             */
            if&#40;!&#40;piece = his&#41;) return b;         /* out of moves         */
            his = next&#91;his-WHITE&#93;;               /* remaining list       */
            goto retry;
        &#125;
    &#125;
        if&#40;&#40;used&#91;piece-WHITE&#93; & ~0xFF&#41; == block&#41;   /* blocked something! */
        &#123;
            h = used&#91;piece-WHITE&#93; & 0xFF;          /* unblocked piece    */
            if&#40;&#40;h&COLOR&#41; != color&#41;          /* rewind LVA list for X-ray */
                 his  = h;
            else if&#40;mine==0 || kind&#91;mine-WHITE&#93; > kind&#91;h-WHITE&#93;)
                 mine = h; /* for opponent make sure rewind is backwards */
        &#125;
SDB2&#40;"he") 
    used&#91;piece-WHITE&#93; = seq; /* mark as used */
    e -= w;
    w = qval&#91;piece-WHITE&#93;;
    if&#40;e+w<=a&#41; return a; /* victim not valuable enough to get even, futile */
    if&#40;a<e&#41; a=e;         /* in any case we have score e&#58; if we stop now.   */

    /* look if continuing with capture is better&#58; our first LVA recapture  */
    mine = First&#91;color&#93;;       /* pieces-only attacker list, in LVA order  */
    /* Prepend relevant Pawns to list. Other pieces are already sorted.    */
    from = to + color - 49;                          /* first pawn square  */
    piece = board&#91;from&#93;;                                                   
    if&#40;&#40;piece & &#40;PAWNS|COLOR&#41;) == &#40;PAWNS|color&#41;)               /* my pawn  */
        mine = piece;         /* prepend to list &#40;next-field already set&#41;  */

    from += 2;                                      /* second pawn square  */
    piece = board&#91;from&#93;;     
    if&#40;&#40;piece & &#40;PAWNS|COLOR&#41;) != &#40;PAWNS|color&#41;)           /* not my pawn  */
    &#123;    piece = mine; mine = next&#91;mine-WHITE&#93;; &#125;           /* next piece  */

    /* 'piece' now holds first piece, 'mine' the second piece              */

    do &#123;/* Now Pawns are added to both lists, loop for remaining captures  */
/*printf&#40;"hoofdloop\n");*/

        /* validate capture with 'piece', or find other one from my list   */
        do &#123;
SDB1 
            if&#40;&#40;x=pos&#91;piece-WHITE&#93;)>=0 &&        /* was already captured */
                used&#91;piece-WHITE&#93;!=seq &&        /* captured in this SEE */
                &#40;code&#91;piece-WHITE&#93; & &#40;h=capt_code&#91;to-x&#93;) ))
            &#123;   /* pseudo-hit&#58; aligned for capture */
                if&#40;h & C_CONTACT&#41; break; /* contact attack, always hit   */
                v = delta_vec&#91;to-x&#93;;
                y = to;
                while&#40;&#40;h=board&#91;y-=v&#93;)==DUMMY                 /* scan ray */
                       || used&#91;h-WHITE&#93;==seq&#41;;     /* ignore used pieces */
                if&#40;x==y&#41; break;          /* ray is clear, distant hit    */
                /* ray is blocked. mark blocker so we can retry later    */
                used&#91;h-WHITE&#93; = block + piece;
            &#125;
            if&#40;!&#40;piece = mine&#41;) return a;        /* out of moves         */
            mine = next&#91;mine-WHITE&#93;;             /* remaining list       */
        &#125; while&#40;1&#41;;
        if&#40;w>9&#41; return&#40;b&#41;;

        if&#40;&#40;used&#91;piece-WHITE&#93; & ~0xFF&#41; == block&#41;   /* blocked something! */
        &#123;
            h = used&#91;piece-WHITE&#93; & 0xFF;          /* unblocked piece    */
            if&#40;&#40;h&COLOR&#41; == color&#41;          /* rewind LVA list for X-ray */
                 mine = h;                  
            else if&#40;his==0 || kind&#91;his-WHITE&#93; > kind&#91;h-WHITE&#93;)
                 his  = h; /* for opponent make sure rewind is backwards */
        &#125;
SDB2&#40;"I ")
        used&#91;piece-WHITE&#93; = seq;

        e += w;
        w = qval&#91;piece-WHITE&#93;;
        if&#40;e-w>=b&#41; return b;
        if&#40;e<b&#41; b=e;

        /* next recapture of opponent from LVA list */
        do &#123;
            if&#40;!&#40;piece = his&#41;) return b;         /* out of moves         */
            his = next&#91;his-WHITE&#93;;               /* remaining list       */
SDB1 
            if&#40;&#40;x=pos&#91;piece-WHITE&#93;)<0&#41; continue; /* was already captured */
            if&#40;used&#91;piece-WHITE&#93;==seq&#41; continue; /* captured in this SEE */
            if&#40;code&#91;piece-WHITE&#93; & &#40;h=capt_code&#91;to-x&#93;))
            &#123;   /* pseudo-hit&#58; aligned for capture */
                if&#40;h & C_CONTACT&#41; break; /* contact attack, always hit   */
                v = delta_vec&#91;to-x&#93;;
                y = to;
                while&#40;&#40;h=board&#91;y-=v&#93;)==DUMMY                 /* scan ray */
                       || used&#91;h-WHITE&#93;==seq&#41;;     /* ignore used pieces */
                if&#40;x==y&#41; break;          /* ray is clear, distant hit    */
                /* ray is blocked. mark blocker so we can retry later    */
                used&#91;h-WHITE&#93; = block + piece;
            &#125;
        &#125; while&#40;1&#41;;
        if&#40;w>9&#41; return&#40;a&#41;;

        if&#40;&#40;used&#91;piece-WHITE&#93; & ~0xFF&#41; == block&#41;   /* blocked something! */
        &#123;
            h = used&#91;piece-WHITE&#93; & 0xFF;          /* unblocked piece    */
/*printf&#40;"unblock %x\n", h&#41;;*/
            if&#40;&#40;h&COLOR&#41; != color&#41;          /* rewind LVA list for X-ray */
                 his  = h;                  
            else if&#40;mine==0 || kind&#91;mine-WHITE&#93; > kind&#91;h-WHITE&#93;)
                 mine = h; /* for opponent make sure rewind is backwards */
        &#125;
SDB2&#40;"he")
        used&#91;piece-WHITE&#93; = seq;

        e -= w;
        w = qval&#91;piece-WHITE&#93;;
        if&#40;e+w<=a&#41; return a;
        if&#40;a<e&#41; a=e;

        /* unhook next recapture of us from LVA list &#40;to conform to  */
        /* situation after prepending Pawns, so we can loop&#41;         */
        if&#40;!&#40;piece = mine&#41;) return a;        /* out of moves         */
        mine = next&#91;mine-WHITE&#93;;             /* remaining list       */

    &#125; while&#40;1&#41;;

&#125;
User avatar
Don
Posts: 5106
Joined: Tue Apr 29, 2008 4:27 pm

Re: Implementing SEE

Post by Don »

cms271828 wrote:Hi

I've been away from chess programming for a while, but I'm still stuck on SEE.
Every time I try to resolve something I seem to open a new can of worms.
I suggest you start with a recursive version, don't worry about speed, get it debugged, then build a fast iterative version. Call BOTH versions while getting the iterative version debugged to make sure they are returning the same answer.

The recursive version is easier to understand and will get you started.

I planned on using an iterative static exchange evaluation following the psuedo code:

Code: Select all

int staticexchangeevaluationw ( const int from , const int to ) 
&#123;
    int iteration ;
    int valuelist&#91; 3 2 &#93; ;
    int next ;
    int valueattacker ;

    // iteration for White
    iteration=0;
    valuelist&#91;iteration&#93; = boardvalue ( to ) ;
    next = from ;
    generateattackers ( next , to ) ;
    if ( blackattackers&#40; ) == 0 ) return boardvalue ( t o ) ;

    valueattacker = boardvalue ( from ) ;

    // forward&#8722;iterationloop &#58; ( 1 ) fill the valuelist
    while ( true ) 
    &#123;
        // iteration for Black
        iteration ++;
        valuelist&#91; iteration &#93;= valueattacker &#8722;valuelist &#91;iteration&#8722; 1 &#93; ;
        next = nextblackattacker ( ) ;
        updatexrayattackers ( next , t o ) ;

        if ( whiteattackers&#40; ) == 0 ) break ;

        valueattacker = board value ( next ) ;

        //iteration for White
       iteration ++;
       valuelist &#91;iteration &#93;= valueattacker &#8722;valuelist &#91;iteration &#8722; 1 &#93; ;
       next = nextwhiteattacker ( ) ;
       updatexrayattackers&#40; next , t o ) ;

       if ( blackattackers&#40; ) == 0 ) break ;

       valueattacker = board value ( next ) ;
    &#125;

    // reverse &#8722;iteration loop &#58; ( 2 ) evaluate the valuelist
    while ( true ) 
    &#123;
        if&#40; iteration == 0 ) return valuelistt &#91; 0 &#93; ;

        if ( valuelist &#91;iteration &#93; > &#8722; valuelist &#91; iiteration &#8722;1&#93;)
        &#123;
            valuelist &#91;iteration &#8722;1&#93; = &#8722;valuelist &#91;iteration &#93; ;
        &#125;

        iteration &#8722;&#8722;;
    &#125;
&#125;
I'm not sure about generating attackers, the problem is more trickier than it seems.
I have had a few ideas but they all seem problematic.

Any advice would be grateful, thanks.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Implementing SEE

Post by mcostalba »

Desperado wrote:Hi Marco,

well it is always the same when i give code chunks to the public. I get unhappy
with my code, because i am looking more intensive on it, and maybe more carefully.

Although it was on my _improve someday_ todo list, i think now is a better time.
The first step I take when I want to optimize something is to measure it. So, in case of SEE I have just done a quick measure of what we are talking about regarding the loop cycles needed to resolve a capture.

Here are the results:

Code: Select all

29% of cases side to move captures and opponent has no defendants
47% of cases opponent defends and side to move cannot recapture
16% opponent defends with one piece and side to move can recapture
8% all the other more complex cases together
So I would say that any optimization should attack especially the first 2 points and an optimization of the last point simply it is not worth.

Regarding promotion I'd guess that you don't have a clear ELO advantage if you include them in the SEE also because (queen) promotions are usually always tried by the search and never pruned.

For what I have written before optimization of point c is an optimization of a rare case, so the effect is small, but the added code complexity could be big.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Implementing SEE

Post by mcostalba »

lucasart wrote: Here's mine if it helps. It's basically the one of StockFish, that I also improved to handle promotions and en passant captures.
I'd think that the en passant case was already handled in the original Stokfish version.... ;-)
User avatar
Desperado
Posts: 879
Joined: Mon Dec 15, 2008 11:45 am

Re: Implementing SEE

Post by Desperado »

Hello Marco,

just a few minutes ago i tried your proposal given in your first post.
Unfortunatelly it will not work with my program flow.

But to be more specific, i just ask you if i really understand what
you had in mind.

original:
======

Code: Select all

...
attadef   = attackers&#40;pos,dst,white&#41; | attackers&#40;pos,dst,black&#41; | fromSet/*pawnPush*/; 
gain&#91;d=0&#93; = vSee&#91;pos->sq&#91;dst&#93;&#93;; 
...
change:
======

Code: Select all

...
attadef = attackers&#40;pos,dst,sidePick&#41;;
gain&#91;d=0&#93;=vSee&#91;pos->sq&#91;dst&#93;&#93;;

if&#40;attadef==0&#41; return&#40;gain&#91;0&#93;);
attadef |= attackers&#40;pos,dst,pos->ctm&#41; | fromSet;
...
The reason why it does not work is that xray attacks are missing
for the initial move.So, if that was your intention, it is no option for my
code.I need to enter the loop because of the xrays.

easy improvement:
=============

what can be done, and maybe flagged as option is the "easy capture"
possibility. That would mean to order the initilization block a little
bit different like...

Code: Select all


 ...

 aPiece    = pos->sq&#91;unpackSrc&#40;mve&#41;&#93;;
 gain&#91;d=0&#93; = vSee&#91;pos->sq&#91;dst&#93;&#93;;

 if&#40;vSee&#91;aPiece&#93;</*=*/gain&#91;0&#93;) return&#40;gain&#91;0&#93;); //option;
 
 sidePick  = pos->ctm ^ side;
 fromSet   = onebit&#40;unpackSrc&#40;mve&#41;);
 occ       = OCCUPIED;
 attadef   = attackers&#40;pos,dst,white&#41; | attackers&#40;pos,dst,black&#41; | fromSet

 ...
Michael
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Implementing SEE

Post by mcostalba »

Desperado wrote:Hello Marco,

just a few minutes ago i tried your proposal given in your first post.
Unfortunatelly it will not work with my program flow.

But to be more specific, i just ask you if i really understand what
you had in mind.

original:
======

Code: Select all

...
attadef   = attackers&#40;pos,dst,white&#41; | attackers&#40;pos,dst,black&#41; | fromSet/*pawnPush*/; 
gain&#91;d=0&#93; = vSee&#91;pos->sq&#91;dst&#93;&#93;; 
...
change:
======

Code: Select all

...
attadef = attackers&#40;pos,dst,sidePick&#41;;
gain&#91;d=0&#93;=vSee&#91;pos->sq&#91;dst&#93;&#93;;

if&#40;attadef==0&#41; return&#40;gain&#91;0&#93;);
attadef |= attackers&#40;pos,dst,pos->ctm&#41; | fromSet;
...
The reason why it does not work is that xray attacks are missing
for the initial move.So, if that was your intention, it is no option for my
code.I need to enter the loop because of the xrays.

easy improvement:
=============

what can be done, and maybe flagged as option is the "easy capture"
possibility. That would mean to order the initilization block a little
bit different like...

Code: Select all


 ...

 aPiece    = pos->sq&#91;unpackSrc&#40;mve&#41;&#93;;
 gain&#91;d=0&#93; = vSee&#91;pos->sq&#91;dst&#93;&#93;;

 if&#40;vSee&#91;aPiece&#93;</*=*/gain&#91;0&#93;) return&#40;gain&#91;0&#93;); //option;
 
 sidePick  = pos->ctm ^ side;
 fromSet   = onebit&#40;unpackSrc&#40;mve&#41;);
 occ       = OCCUPIED;
 attadef   = attackers&#40;pos,dst,white&#41; | attackers&#40;pos,dst,black&#41; | fromSet

 ...
Michael
You have to remove the original piece from the board before calculating attacks, so something along this lines:

occ ^= fromSet;
attadef = attackers(pos,occ,dst,sidePick)


Regarding what you call "easy capture" it is called "SEE sign" in SF because it does not return the correct SEE value, but just the correct SEE sign: if capture is a gain / equal or if it is a loss. Yes can be handy when you just need to detect if a given move is a bad captures.

I'll be mostly off-line for the week, so sorry if I don't answer you anymore.
User avatar
Desperado
Posts: 879
Joined: Mon Dec 15, 2008 11:45 am

Re: Implementing SEE

Post by Desperado »

Code: Select all

//* easySEE&#58; not sure that i will use it
 if&#40;vSee&#91;aPiece&#93;<gain&#91;0&#93;) &#123;return&#40;gain&#91;0&#93;);&#125;

Code: Select all

int seeMove&#40;pos_t *pos,mv_t mve&#41;
&#123;
 int gain&#91;32&#93;, d ,aPiece,sidePick,dst = unpackDst&#40;mve&#41;;
 ui64_t mayXray,fromSet,occ,attadef;
 
 if&#40;moveIsOO&#40;mve&#41;) return&#40;0&#41;;
 if&#40;moveIsEP&#40;mve&#41;) return&#40;100&#41;;

 aPiece    = pos->sq&#91;unpackSrc&#40;mve&#41;&#93;;
 gain&#91;d=0&#93; = vSee&#91;pos->sq&#91;dst&#93;&#93;;
 /*easySEE, see above*/
 sidePick  = pos->ctm ^ side;
 fromSet   = onebit&#40;unpackSrc&#40;mve&#41;);
 occ       = OCCUPIED;
 attadef   = attackers&#40;pos,dst,white&#41; | attackers&#40;pos,dst,black&#41; | fromSet;

 mayXray   =   occ
	         ^ &#40;pos->bb&#91;wnEnum&#93; | pos->bb&#91;bnEnum&#93;
             |  pos->bb&#91;wkEnum&#93; | pos->bb&#91;bkEnum&#93;);

 do
 &#123;
  d++; gain&#91;d&#93; = vSee&#91;aPiece&#93; - gain&#91;d-1&#93;;

  //* improvement&#58; standpat!
  if&#40;-maxN&#40;-gain&#91;d-1&#93;,gain&#91;d&#93;)>0&#41; break;

  attadef^= fromSet;
  occ    ^= fromSet;

  if&#40;fromSet & mayXray&#41;
  &#123;
   attadef |= attackSlider&#40;pos,occ,dst,white&#41;;
   attadef |= attackSlider&#40;pos,occ,dst,black&#41;;
  &#125;

  for&#40;aPiece=wpEnum+&#40;sidePick&#41;;aPiece<=wkEnum+&#40;sidePick&#41;;aPiece+=2&#41;
	 if&#40;fromSet = pos->bb&#91;aPiece&#93; & attadef&#41;
		&#123;fromSet &=- fromSet;sidePick^=side;break;&#125;

 &#125;while&#40;fromSet&#41;;

 while&#40;--d&#41; gain&#91;d-1&#93; = -maxN&#40;-gain&#91;d-1&#93;,gain&#91;d&#93;); return&#40;gain&#91;0&#93;);
&#125;
ok,think the "standpat" option was what i was looking for with "point c".
Now i am happy and can enjoy the sunday with my familiy :) 8-)

compact + easy i think. Have a nice sunday everyone

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

Re: Implementing SEE

Post by Desperado »

mcostalba wrote: You have to remove the original piece from the board before calculating attacks, so something along this lines:

occ ^= fromSet;
attadef = attackers(pos,occ,dst,sidePick)


Regarding what you call "easy capture" it is called "SEE sign" in SF because it does not return the correct SEE value, but just the correct SEE sign: if capture is a gain / equal or if it is a loss. Yes can be handy when you just need to detect if a given move is a bad captures.

I'll be mostly off-line for the week, so sorry if I don't answer you anymore.
ok, i am aware of that, but that would require a change of my "attackers()" function
which uses OCCUPIED , not a temporary occupancy (occ) as
parameter like "attackSlider()".

Second point is, that i wonder if it is really profitable _after_ introduced the standpat condition,
which will catch that case too,and is a more general solution.

Many thx anyway.

Michael