# Computer Chess Club Archives

## Messages

### Subject: SEE and possible EXChess bug

Author: Gian-Carlo Pascutto

Date: 07:29:42 04/01/01

```Hi all,

in the process of using EXChess's source to design my SEE
after, I ran into something which I suspect is either a bug
in EXChess, or a misunderstanding from my part about how SEE
works.

Below is the critical section.

int swap(int sq, position p, int side, int from)
{
int val = value[p.sq[sq].type], lsq;

// initalize attackers arrays
attackers[0][0].v = 0; attcks[0] = 0;
attackers[1][0].v = 0; attcks[1] = 0;

// put in the first capture
attackers[side][0].v = value[p.sq[from].type];
attcks[side]++;
p.sq[from] = empty;

// find all the attcks on sq
dia_attcks(sq, 0, &p);
knt_attcks(sq, &p);
hor_attcks(sq, 0, &p);

attackers[0][attcks[0]].v = 0;
attackers[1][attcks[1]].v = 0;

// swap off the attacks, starting with other side
// and with the least value
int swapside = side^1;
int count[2] = { 0, 0 }, bestval;

if(attcks[swapside]) bestval = val - attackers[side][0].v;
else bestval = val;

count[side]++; // for the first capture

while(count[swapside] < attcks[swapside]) {
SSort(&attackers[swapside][count[swapside]],
&attackers[swapside][attcks[swapside]-1]);
if(swapside == side) {
val += attackers[swapside^1][count[swapside^1]-1].v;
if(count[side^1] >= attcks[side^1] && val > bestval)
bestval = val;
} else {
val -= attackers[swapside^1][count[swapside^1]-1].v;
if(val > bestval)
{ bestval= val; }
}
// square from which the capture came
lsq = attackers[swapside][count[swapside]].s;
// add in any revealed attacks due to this capture
if((FILE(lsq) == FILE(sq) || RANK(lsq) == RANK(sq)) && lsq)
hor_attcks(sq, lsq, &p);
else if(p.sq[lsq].type != KNIGHT && lsq) dia_attcks(sq, lsq, &p);
count[swapside]++;
swapside ^= 1;
}

return bestval;
}

If you look at this code you see the program keeps exchanging off
attackers untill one side is exhausted. New best values are remembered
if the side that was originally to move can stand pat either after
a recapture by the opponent, or after a capture when we know the
opponent has no more recaptures.

I think this will cause problems when, for example, a bishop and a
pawn are attacking a pawn defended by a queen.

Correct would be:

pawn takes pawn, standpat, score +100

but I think the algorithm does:

pawn takes pawn, queen takes pawn, bishop takes queen and standpat, score +900

because it does not allow a standpat for the opponent.

Am I missing something here? I added the (IMHO) corrected algorithm
to Sjeng and the results aren't as good as I hoped they would be, even
if using SEE to prune losing captures.

My SEE function is below (note that I don't do Xray attacks yet):

int see(int color, int square, int from)
{
int sside;
int caps[2];
int value;
int origpiece;
int ourbestvalue;
int hisbestvalue;

/* reset data */
see_num_attackers[WHITE] = 0;
see_num_attackers[BLACK] = 0;

/* remove original capturer from board, exposing his first xray-er */
origpiece = board[from];
board[from] = npiece;

see_num_attackers[color]++;
see_attackers[color][0].piece = origpiece;
see_attackers[color][0].square = from;

/* calculate all attackers to square */
setup_attackers(square);

/* initially we gain the piece we are capturing */
value = abs(material[board[square]]);

/* free capture ? */
if (!see_num_attackers[!color])
{
board[from] = origpiece;
return value;
}
else
{
/* we can never get a higher SEE score than the piece we just captured */
/* so that is the current best value for our opponent */
/* we arent sure of anything yet, so -INF */
hisbestvalue = value;
ourbestvalue = -INF;
}

caps[color] = 1;
caps[!color] = 0;

sside = !color;

/* continue as long as there are attackers */
while (caps[sside] < see_num_attackers[sside])
{
/* resort capturelist of sside to put lowest attacker in next position */
findlowest(sside, caps[sside]);

if (sside == color)
{
/* capturing more */
/* we capture the opponents recapturer */
value += abs(material[see_attackers[!sside][caps[!sside]-1].piece]);

/* if the opp ran out of attackers we can stand pat now! */
if (see_num_attackers[!sside] <= caps[!sside] && value > ourbestvalue)
ourbestvalue = value;

/* our opponent can always stand pat now */
if (value < hisbestvalue) hisbestvalue = value;
}
else
{
/* recapture by opp */
/* we lose whatever we captured with in last iteration */
value -= abs(material[see_attackers[!sside][caps[!sside]-1].piece]);

/* we can stand pat if we want to now */
/* our best score goes up, opponent is unaffected */

if (value > ourbestvalue)
{
ourbestvalue = value;
}

if (see_num_attackers[!sside] <= caps[!sside] && value < hisbestvalue)
hisbestvalue = value;
}

/* keep track of capture count */
caps[sside]++;

/* switch sides */
sside ^= 1;

}

/* restore capturer */
board[from] = origpiece;

/* we return our best score now, keeping in mind that
it can never we better than the best for our opponent */
return (ourbestvalue > hisbestvalue) ? hisbestvalue : ourbestvalue;
}

--
GCP

```