Passed Pawn Move Generation

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Edsel Apostol
Posts: 803
Joined: Mon Jul 17, 2006 5:53 am
Full name: Edsel Apostol

Passed Pawn Move Generation

Post by Edsel Apostol »

Generating a certain kind of move is much faster in my engine than when I just generate all moves and then filter them through the move loop according to its certain criteria. I have move generators for tactical moves, non tactical moves, non tactical checks, and legal evasion. I'm planning to add a passed move move generation.

What would be the best way to generate passed pawn moves? Take note that it must not be a tactical move, that means it shouldn't include captures, promotions and checks. Here is my first try, though I have not tested it yet.

Code: Select all

/* returns a bitboard with all bits above b filled up (including b) */
uint64 fillUp2(uint64 b) {
    b |= b << 8;
    b |= b << 16;
    return &#40;b | &#40;b << 32&#41;);
&#125;

/* returns a bitboard with all bits below b filled down &#40;including b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;

// shift the parameter b with i places to the left
uint64 shiftLeft&#40;uint64 b, uint32 i&#41; &#123;return &#40;b << i&#41;;&#125;

// shift the parameter b with i places to the right
uint64 shiftRight&#40;uint64 b, uint32 i&#41; &#123;return &#40;b >> i&#41;;&#125;

static uint64 (*FillPtr2&#91;&#93;)&#40;uint64&#41; = &#123;&fillUp2, &fillDown2&#125;;
static uint64 (*ShiftPtr&#91;&#93;)&#40;uint64, uint32&#41; = &#123;&shiftLeft, &shiftRight&#125;;

/* this generates non tactical non checking passed pawn moves only */
void genPassedPawnMoves&#40;const position_t *pos, sort_t *sort&#41; &#123;
    static const int Shift&#91;&#93; = &#123;9, 7&#125;;
    uint64 pc_bits, mv_bits, pawns, xpawns, mask, dcc;
    int from, to;

    ASSERT&#40;pos != NULL&#41;;
    ASSERT&#40;sort != NULL&#41;;

    sort->size = 0;
    pawns = pos->pawns & pos->color&#91;pos->side&#93;;
    xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
    dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;
    mask = (~Rank8ByColorBB&#91;pos->side&#93; & ~((*FillPtr2&#91;pos->side^1&#93;)((*ShiftPtr&#91;pos->side^1&#93;)&#40;pos->pawns, 8&#41;)))
        & ~(((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side^1&#93;) & ~FileABB&#41;
        | ((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side&#93;) & ~FileHBB&#41;)
        & ~PawnCaps&#91;pos->kpos&#91;pos->side^1&#93;&#93;&#91;pos->side^1&#93;;

    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 8&#41; & mask;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = &#40;PawnMoves&#91;to&#93;&#91;pos->side^1&#93; & pawns&#41; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenOneForward&#40;from, to&#41;;
        &#125;
    &#125;
    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 16&#41; & Rank4ByColorBB&#91;pos->side&#93; & mask
    & ((*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 8&#41; & (*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 16&#41;);
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenTwoForward&#40;from, to&#41;;
        &#125;
    &#125;
&#125;
Any tips on how to optimize this? How would you do it if you we're to do such a move generator?

P.S. A1 = 0, H8 = 63, WHITE = 0, BLACK = 1
P.S.2 I'm using the sides color as index to Shift pointer as LEFT = 0 and RIGHT = 1 as they directly corresponds to WHITE and BLACK
Edsel Apostol
Posts: 803
Joined: Mon Jul 17, 2006 5:53 am
Full name: Edsel Apostol

Re: Passed Pawn Move Generation

Post by Edsel Apostol »

Correction:

Code: Select all

pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & ~dcc;
should be this:

Code: Select all

pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & FileMask&#91;to&#93; & ~dcc;
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: Passed Pawn Move Generation

Post by Gerd Isenberg »

Edsel Apostol wrote:Generating a certain kind of move is much faster in my engine than when I just generate all moves and then filter them through the move loop according to its certain criteria. I have move generators for tactical moves, non tactical moves, non tactical checks, and legal evasion. I'm planning to add a passed move move generation.

What would be the best way to generate passed pawn moves? Take note that it must not be a tactical move, that means it shouldn't include captures, promotions and checks. Here is my first try, though I have not tested it yet.

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;including b&#41; */
uint64 fillUp2&#40;uint64 b&#41; &#123;
    b |= b << 8;
    b |= b << 16;
    return &#40;b | &#40;b << 32&#41;);
&#125;

/* returns a bitboard with all bits below b filled down &#40;including b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;

// shift the parameter b with i places to the left
uint64 shiftLeft&#40;uint64 b, uint32 i&#41; &#123;return &#40;b << i&#41;;&#125;

// shift the parameter b with i places to the right
uint64 shiftRight&#40;uint64 b, uint32 i&#41; &#123;return &#40;b >> i&#41;;&#125;

static uint64 (*FillPtr2&#91;&#93;)&#40;uint64&#41; = &#123;&fillUp2, &fillDown2&#125;;
static uint64 (*ShiftPtr&#91;&#93;)&#40;uint64, uint32&#41; = &#123;&shiftLeft, &shiftRight&#125;;

/* this generates non tactical non checking passed pawn moves only */
void genPassedPawnMoves&#40;const position_t *pos, sort_t *sort&#41; &#123;
    static const int Shift&#91;&#93; = &#123;9, 7&#125;;
    uint64 pc_bits, mv_bits, pawns, xpawns, mask, dcc;
    int from, to;

    ASSERT&#40;pos != NULL&#41;;
    ASSERT&#40;sort != NULL&#41;;

    sort->size = 0;
    pawns = pos->pawns & pos->color&#91;pos->side&#93;;
    xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
    dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;
    mask = (~Rank8ByColorBB&#91;pos->side&#93; & ~((*FillPtr2&#91;pos->side^1&#93;)((*ShiftPtr&#91;pos->side^1&#93;)&#40;pos->pawns, 8&#41;)))
        & ~(((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side^1&#93;) & ~FileABB&#41;
        | ((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side&#93;) & ~FileHBB&#41;)
        & ~PawnCaps&#91;pos->kpos&#91;pos->side^1&#93;&#93;&#91;pos->side^1&#93;;

    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 8&#41; & mask;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = &#40;PawnMoves&#91;to&#93;&#91;pos->side^1&#93; & pawns&#41; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenOneForward&#40;from, to&#41;;
        &#125;
    &#125;
    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 16&#41; & Rank4ByColorBB&#91;pos->side&#93; & mask
    & ((*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 8&#41; & (*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 16&#41;);
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenTwoForward&#40;from, to&#41;;
        &#125;
    &#125;
&#125;
Any tips on how to optimize this? How would you do it if you we're to do such a move generator?

P.S. A1 = 0, H8 = 63, WHITE = 0, BLACK = 1
P.S.2 I'm using the sides color as index to Shift pointer as LEFT = 0 and RIGHT = 1 as they directly corresponds to WHITE and BLACK
In a staged MG I would probably do it target rankwise (only rank 7, 6, 5). Then you may even unroll the serialization loops of those rank-sets (usually empty or very low populated), by using a target rank (byte) as index to address a pre-calculated move-list.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Passed Pawn Move Generation

Post by mcostalba »

Below is the case for a single color only (side to move == WHITE), I am not a fan of function pointers to handle color asymmetry.

Not tested.

It is not clear if with "passed pawn moves" you mean moves of pawns that are already passed, move of a non passed pawn that make the pawn become passed, or a union of the boths.

In my example I will consider the last case that is the most correct IMHO.

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;EXCLUDING b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b >>= 8;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125; 


pawns = pos->pawns & pos->color&#91;pos->side&#93;;
xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;

// After the move a passed pawn must not be in the mask area
mask = fillDown2&#40;xpawns&#41;;
mask |= (&#40;mask >> 1&#41; & ~FileABB&#41; | (&#40;mask << 1&#41; & ~FileHBB&#41;;

// Pawn pushes
pawns &= ~dcc;
single_push_pawns = &#40;pawns >> 8&#41; & ~pos->occupied;
double_push_pawns = &#40;single_push_pawns >> 8&#41; & ~pos->occupied & ~mask & Rank4ByColorBB&#91;pos->side&#93;;
single_push_pawns &= ~mask;

// Pawn that are passed or became passed with a single push
mv_single_bits = single_push_pawns << 8;

// The same but for double push
mv_double_bits = double_push_pawns << 16;


...then serialize mv_bits as usual


BTW are you sure your code checks the single push in non occupied squares only ?
Edsel Apostol
Posts: 803
Joined: Mon Jul 17, 2006 5:53 am
Full name: Edsel Apostol

Re: Passed Pawn Move Generation

Post by Edsel Apostol »

mcostalba wrote:Below is the case for a single color only (side to move == WHITE), I am not a fan of function pointers to handle color asymmetry.

Not tested.

It is not clear if with "passed pawn moves" you mean moves of pawns that are already passed, move of a non passed pawn that make the pawn become passed, or a union of the boths.

In my example I will consider the last case that is the most correct IMHO.

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;EXCLUDING b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b >>= 8;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125; 


pawns = pos->pawns & pos->color&#91;pos->side&#93;;
xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;

// After the move a passed pawn must not be in the mask area
mask = fillDown2&#40;xpawns&#41;;
mask |= (&#40;mask >> 1&#41; & ~FileABB&#41; | (&#40;mask << 1&#41; & ~FileHBB&#41;;

// Pawn pushes
pawns &= ~dcc;
single_push_pawns = &#40;pawns >> 8&#41; & ~pos->occupied;
double_push_pawns = &#40;single_push_pawns >> 8&#41; & ~pos->occupied & ~mask & Rank4ByColorBB&#91;pos->side&#93;;
single_push_pawns &= ~mask;

// Pawn that are passed or became passed with a single push
mv_single_bits = single_push_pawns << 8;

// The same but for double push
mv_double_bits = double_push_pawns << 16;


...then serialize mv_bits as usual


BTW are you sure your code checks the single push in non occupied squares only ?
Hi Marco,

Yes, I mean the last case, where the to square must be considered as passed, so it doesn't matter if it already is a passed pawn before moving or not.

And yes, my single push is not correct as it might push into non pawns. I assumed that my mask is already not occupied by pieces. :oops:

By the way, you're code I think is for black as my A1 is 0 and H8 is 63. I am also not sure but I think that your code will fail if there is a doubled pawn in a file, in that case only the pawn ahead is a passed pawn and the one behind it is not. It also doesn't take into consideration if the move will give direct check.

I'm planning to use this move generation in conjunction with tactical and checking moves and I don't want it to generate moves identical to what is generated by those mentioned.

Your code give me ideas, I will improve on that. I will post it here later.
Edsel Apostol
Posts: 803
Joined: Mon Jul 17, 2006 5:53 am
Full name: Edsel Apostol

Re: Passed Pawn Move Generation

Post by Edsel Apostol »

Gerd Isenberg wrote:
Edsel Apostol wrote:Generating a certain kind of move is much faster in my engine than when I just generate all moves and then filter them through the move loop according to its certain criteria. I have move generators for tactical moves, non tactical moves, non tactical checks, and legal evasion. I'm planning to add a passed move move generation.

What would be the best way to generate passed pawn moves? Take note that it must not be a tactical move, that means it shouldn't include captures, promotions and checks. Here is my first try, though I have not tested it yet.

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;including b&#41; */
uint64 fillUp2&#40;uint64 b&#41; &#123;
    b |= b << 8;
    b |= b << 16;
    return &#40;b | &#40;b << 32&#41;);
&#125;

/* returns a bitboard with all bits below b filled down &#40;including b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;

// shift the parameter b with i places to the left
uint64 shiftLeft&#40;uint64 b, uint32 i&#41; &#123;return &#40;b << i&#41;;&#125;

// shift the parameter b with i places to the right
uint64 shiftRight&#40;uint64 b, uint32 i&#41; &#123;return &#40;b >> i&#41;;&#125;

static uint64 (*FillPtr2&#91;&#93;)&#40;uint64&#41; = &#123;&fillUp2, &fillDown2&#125;;
static uint64 (*ShiftPtr&#91;&#93;)&#40;uint64, uint32&#41; = &#123;&shiftLeft, &shiftRight&#125;;

/* this generates non tactical non checking passed pawn moves only */
void genPassedPawnMoves&#40;const position_t *pos, sort_t *sort&#41; &#123;
    static const int Shift&#91;&#93; = &#123;9, 7&#125;;
    uint64 pc_bits, mv_bits, pawns, xpawns, mask, dcc;
    int from, to;

    ASSERT&#40;pos != NULL&#41;;
    ASSERT&#40;sort != NULL&#41;;

    sort->size = 0;
    pawns = pos->pawns & pos->color&#91;pos->side&#93;;
    xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
    dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;
    mask = (~Rank8ByColorBB&#91;pos->side&#93; & ~((*FillPtr2&#91;pos->side^1&#93;)((*ShiftPtr&#91;pos->side^1&#93;)&#40;pos->pawns, 8&#41;)))
        & ~(((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side^1&#93;) & ~FileABB&#41;
        | ((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side&#93;) & ~FileHBB&#41;)
        & ~PawnCaps&#91;pos->kpos&#91;pos->side^1&#93;&#93;&#91;pos->side^1&#93;;

    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 8&#41; & mask;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = &#40;PawnMoves&#91;to&#93;&#91;pos->side^1&#93; & pawns&#41; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenOneForward&#40;from, to&#41;;
        &#125;
    &#125;
    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 16&#41; & Rank4ByColorBB&#91;pos->side&#93; & mask
    & ((*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 8&#41; & (*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 16&#41;);
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenTwoForward&#40;from, to&#41;;
        &#125;
    &#125;
&#125;
Any tips on how to optimize this? How would you do it if you we're to do such a move generator?

P.S. A1 = 0, H8 = 63, WHITE = 0, BLACK = 1
P.S.2 I'm using the sides color as index to Shift pointer as LEFT = 0 and RIGHT = 1 as they directly corresponds to WHITE and BLACK
In a staged MG I would probably do it target rankwise (only rank 7, 6, 5). Then you may even unroll the serialization loops of those rank-sets (usually empty or very low populated), by using a target rank (byte) as index to address a pre-calculated move-list.
Hi Gerd,

Can you give a pseudo code for this. I can't seem to grasp the idea from your description.
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: Passed Pawn Move Generation

Post by Gerd Isenberg »

Edsel Apostol wrote: Hi Gerd,

Can you give a pseudo code for this. I can't seem to grasp the idea from your description.
Hi Edsel,
for instance white pawns to the seventh rank, where you even don't need to consider sentries:

Code: Select all

pawnPushTargets7thRank = &#40;whitePawns << 8&#41; & emptySquares & 0x00ff000000000000;
unsigned index = pawnPushTargets7thRank >> 48;
movecpy &#40;movelist, precalcpawnPushes&#91;rank7&#93;&#91;index &#93;.moves, precalcpawnPushes&#91;rank7&#93;&#91;index &#93;.nmoves&#41;;
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Passed Pawn Move Generation

Post by mcostalba »

Edsel Apostol wrote: By the way, you're code I think is for black as my A1 is 0 and H8 is 63. I am also not sure but I think that your code will fail if there is a doubled pawn in a file, in that case only the pawn ahead is a passed pawn and the one behind it is not. It also doesn't take into consideration if the move will give direct check.
Yes you are correct, this should be ok.

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;EXCLUDING b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b >>= 8;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;


pawns = pos->pawns & pos->color&#91;pos->side&#93;;
xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;

// In case of two pawns in the same file consider only
// the more advanced as passed.
pawns &= ~fillDown2&#40;pawns&#41;;

// After the move a passed pawn must not be in the mask area.
// We don't want direct check moves.
mask = fillDown2&#40;xpawns&#41; | &#40;kposBB >> 8&#41;;
mask |= (&#40;mask >> 1&#41; & ~FileABB&#41; | (&#40;mask << 1&#41; & ~FileHBB&#41;;
mask &= ~&#40;kposBB >> 8&#41;;

// Pawn pushes
pawns &= ~dcc;
single_push_pawns = &#40;pawns >> 8&#41; & ~pos->occupied;
double_push_pawns = &#40;single_push_pawns >> 8&#41; & ~pos->occupied & ~mask & Rank4ByColorBB&#91;pos->side&#93;;
single_push_pawns &= ~mask;

// Pawn that are passed or became passed with a single push
mv_single_bits = single_push_pawns << 8;

// The same but for double push
mv_double_bits = double_push_pawns << 16;


...then serialize mv_bits as usual 
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Passed Pawn Move Generation

Post by mcostalba »

Well, my previous version was almost correct ;-) it had a very very small bug !

Hope this is teh final one:

Code: Select all

/* returns a bitboard with all bits below b filled up &#40;EXCLUDING b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b >>= 8;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;


pawns = pos->pawns & pos->color&#91;pos->side&#93;;
xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;

// In case of two pawns in the same file consider only the ahead one
pawns &= ~fillDown2&#40;pawns&#41;;

// Build up the after-move mask area. We don't want direct check moves.
kchecks = kposBB >> 8;
mask = fillDown2&#40;xpawns&#41;;
mask |= (&#40;mask >> 1&#41; & ~FileABB&#41; | (&#40;mask << 1&#41; & ~FileHBB&#41;;
mask |= (&#40;kchecks >> 1&#41; & ~FileABB&#41; | (&#40;kchecks << 1&#41; & ~FileHBB&#41;;

// Pawn pushes
pawns &= ~dcc;
single_push_pawns = &#40;pawns >> 8&#41; & ~pos->occupied;
double_push_pawns = &#40;single_push_pawns >> 8&#41; & ~pos->occupied & ~mask & Rank4ByColorBB&#91;pos->side&#93;;
single_push_pawns &= ~mask;

// Pawn that are passed or became passed with a single push
mv_single_bits = single_push_pawns << 8;

// The same but for double push
mv_double_bits = double_push_pawns << 16;


...then serialize mv_bits as usual 
Edsel Apostol
Posts: 803
Joined: Mon Jul 17, 2006 5:53 am
Full name: Edsel Apostol

Re: Passed Pawn Move Generation

Post by Edsel Apostol »

Thanks Marco. I almost didn't notice it. That last code wouldn't generate a passed pawn if there is an enemy king in front of it. This last one seems correct.
mcostalba wrote:Well, my previous version was almost correct ;-) it had a very very small bug !

Hope this is teh final one:

Code: Select all

/* returns a bitboard with all bits below b filled up &#40;EXCLUDING b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b >>= 8;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;


pawns = pos->pawns & pos->color&#91;pos->side&#93;;
xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;

// In case of two pawns in the same file consider only the ahead one
pawns &= ~fillDown2&#40;pawns&#41;;

// Build up the after-move mask area. We don't want direct check moves.
kchecks = kposBB >> 8;
mask = fillDown2&#40;xpawns&#41;;
mask |= (&#40;mask >> 1&#41; & ~FileABB&#41; | (&#40;mask << 1&#41; & ~FileHBB&#41;;
mask |= (&#40;kchecks >> 1&#41; & ~FileABB&#41; | (&#40;kchecks << 1&#41; & ~FileHBB&#41;;

// Pawn pushes
pawns &= ~dcc;
single_push_pawns = &#40;pawns >> 8&#41; & ~pos->occupied;
double_push_pawns = &#40;single_push_pawns >> 8&#41; & ~pos->occupied & ~mask & Rank4ByColorBB&#91;pos->side&#93;;
single_push_pawns &= ~mask;

// Pawn that are passed or became passed with a single push
mv_single_bits = single_push_pawns << 8;

// The same but for double push
mv_double_bits = double_push_pawns << 16;


...then serialize mv_bits as usual 
By the way, here's my corrected version:

Code: Select all

/* this generates non tactical non checking passed pawn moves only */
void genPassedPawnMoves&#40;const position_t *pos, sort_t *sort&#41; &#123;
    static const int Shift&#91;&#93; = &#123;9, 7&#125;;
    uint64 pc_bits, mv_bits, pawns, xpawns, mask, dcc;
    int from, to;

    ASSERT&#40;pos != NULL&#41;;
    ASSERT&#40;sort != NULL&#41;;

    sort->size = 0;
    pawns = pos->pawns & pos->color&#91;pos->side&#93;;
    xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
    dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;
    mask = Rank8ByColorBB&#91;pos->side&#93; & ((*FillPtr2&#91;pos->side^1&#93;)((*ShiftPtr&#91;pos->side^1&#93;)&#40;pos->pawns, 8&#41;))
        & (*FillPtr&#91;pos->side^1&#93;)(((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side^1&#93;) & ~FileABB&#41;
        | ((*ShiftPtr&#91;pos->side^1&#93;)&#40;xpawns, Shift&#91;pos->side&#93;) & ~FileHBB&#41;)
        & PawnCaps&#91;pos->kpos&#91;pos->side^1&#93;&#93;&#91;pos->side^1&#93;;

    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 8&#41; & ~mask & (*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 8&#41;;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = &#40;PawnMoves&#91;to&#93;&#91;pos->side^1&#93; & pawns&#41; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenOneForward&#40;from, to&#41;;
        &#125;
    &#125;
    mv_bits = (*ShiftPtr&#91;pos->side&#93;)&#40;pawns, 16&#41; & Rank4ByColorBB&#91;pos->side&#93; & ~mask
    & ((*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 8&#41; & (*ShiftPtr&#91;pos->side&#93;)(~pos->occupied, 16&#41;);
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & FileMask&#91;to&#93; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenTwoForward&#40;from, to&#41;;
        &#125;
    &#125;
&#125;
And here's an equivalent code if for WHITE only:

Code: Select all

    sort->size = 0;
    pawns = pos->pawns & pos->color&#91;pos->side&#93;;
    xpawns = pos->pawns & pos->color&#91;pos->side^1&#93;;
    dcc = discoveredCheckCandidates&#40;pos, pos->side&#41;;
    mask =
        Rank8ByColorBB&#91;pos->side&#93; // do not include promotions
        & fillDown2&#40;pos->pawns >> 8&#41; // it must be on open file
        & fillDown&#40;(&#40;xpawns >> 7&#41; & ~FileABB&#41; | (&#40;xpawns >> 9&#41; & ~FileHBB&#41;) // no enemy pawns to attack its way
        & PawnCaps&#91;pos->kpos&#91;pos->side^1&#93;&#93;&#91;pos->side^1&#93;; // it must not give check

    mv_bits = &#40;pawns << 8&#41; & ~mask & ~&#40;pos->occupied << 8&#41;;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = (&#40;BitMask&#91;to&#93; >> 8&#41; & pawns&#41; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenOneForward&#40;from, to&#41;;
        &#125;
    &#125;
    mv_bits = &#40;pawns << 16&#41; & Rank4ByColorBB&#91;pos->side&#93; & ~mask & ~&#40;pos->occupied << 8&#41; & ~&#40;pos->occupied << 16&#41;;
    while &#40;mv_bits&#41; &#123;
        to = popFirstBit&#40;&mv_bits&#41;;
        pc_bits = pawns & Rank2ByColorBB&#91;pos->side&#93; & FileMask&#91;to&#93; & ~dcc;
        while &#40;pc_bits&#41; &#123;
            from = popFirstBit&#40;&pc_bits&#41;;
            sort->list&#91;sort->size++&#93;.m = GenTwoForward&#40;from, to&#41;;
        &#125;
    &#125;
Here's the complete bit utility used:

Code: Select all

/* returns a bitboard with all bits above b filled up &#40;excluding b&#41; */
uint64 fillUp&#40;uint64 b&#41; &#123;
    b |= b << 8;
    b |= b << 16;
    return &#40;b | &#40;b << 32&#41;) << 8;
&#125;

/* returns a bitboard with all bits below b filled down &#40;excluding b&#41; */
uint64 fillDown&#40;uint64 b&#41; &#123;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;) >> 8;
&#125;

/* returns a bitboard with all bits above b filled up &#40;including b&#41; */
uint64 fillUp2&#40;uint64 b&#41; &#123;
    b |= b << 8;
    b |= b << 16;
    return &#40;b | &#40;b << 32&#41;);
&#125;

/* returns a bitboard with all bits below b filled down &#40;including b&#41; */
uint64 fillDown2&#40;uint64 b&#41; &#123;
    b |= b >> 8;
    b |= b >> 16;
    return &#40;b | &#40;b >> 32&#41;);
&#125;

// shift the parameter b with i places to the left
uint64 shiftLeft&#40;uint64 b, uint32 i&#41; &#123;return &#40;b << i&#41;;&#125;

// shift the parameter b with i places to the right
uint64 shiftRight&#40;uint64 b, uint32 i&#41; &#123;return &#40;b >> i&#41;;&#125;

static uint64 (*FillPtr&#91;&#93;)&#40;uint64&#41; = &#123;&fillUp, &fillDown&#125;;
static uint64 (*FillPtr2&#91;&#93;)&#40;uint64&#41; = &#123;&fillUp2, &fillDown2&#125;;
static uint64 (*ShiftPtr&#91;&#93;)&#40;uint64, uint32&#41; = &#123;&shiftLeft, &shiftRight&#125;;
Last edited by Edsel Apostol on Thu Aug 27, 2009 12:14 pm, edited 1 time in total.