Today, let us cover pawn promotion, castling, and en passant. First, let’s begin with pawn promotion. Take a look at the existing move class, and the make_move and undo_move functions:

class move {
public:
    std::pair<int, int&gt; init_pos;
    std::pair<int, int&gt; final_pos;
    int captured_piece;
    
    move(std::pair<int, int&gt; init_pos, std::pair<int, int&gt; final_pos,
                                int captured_piece) {
        this-&gt;init_pos = init_pos;
        this-&gt;final_pos = final_pos;
        this-&gt;captured_piece = captured_piece;
    }
    
    move(std::pair<int, int&gt; init_pos, std::pair<int, int&gt; final_pos) {
        this-&gt;init_pos = init_pos;
        this-&gt;final_pos = final_pos;
        this-&gt;captured_piece = BL;
    }
};

We’ll add another entry to this, and call it promoted_piece. It is set to BLANK if the move is not a pawn promotion move, else it is set to the promoted piece. Add a new constructor as well:

move(std::pair<int, int&gt; init_pos, std::pair<int, int&gt; final_pos,
int captured_piece, int promoted_piece) {
        this-&gt;init_pos = init_pos;
        this-&gt;final_pos = final_pos;
        this-&gt;captured_piece = captured_piece;
        this-&gt;promoted_piece = promoted_piece;
}

As for make_move, set the final position to the promoted piece instead of the pawn, like so:

void make_move(move m) {
    board[m.final_pos.first][m.final_pos.second] 
    	= board[m.init_pos.first][m.init_pos.second];          
    board[m.init_pos.first][m.init_pos.second]
        = BLANK;
    if(m.promoted_piece != BL) {
        board[m.final_pos.first][m.final_pos.second] 
    		= m.promoted_piece;
    }
    side_to_play = (side_to_play == WHITE ? BLACK : WHITE);
}

For undo_move, check to see if promoted piece is BLANK. If not, set the initial piece to the pawn.

void undo_move(move m) {
    board[m.init_pos.first][m.init_pos.second] 
    	= board[m.final_pos.first][m.final_pos.second];
    board[m.final_pos.first][m.final_pos.second]
    	= m.captured_piece;
    if(m.promoted_piece != BL) {
        board[m.init_pos.first][m.init_pos.second]
        = side_to_play == WHITE ? BP : WP;
    }
    side_to_play = (side_to_play == WHITE ? BLACK : WHITE);
}

Now let’s move onto something a bit more challenging: castle moves. There are many ways to handle this, but the way I chose is to simply add a special castling code for each type of castle; hence making castling an entirely different kind of move.

enum {
    NO_CASTLE = 0, WHITE_QUEEN_SIDE_CASTLE, WHITE_KING_SIDE_CASTLE,
    BLACK_QUEEN_SIDE_CASTLE, BLACK_KING_SIDE_CASTLE
};

...

class move {
public:
    ...
    
    int castle_code;
    
    ...
    
    // New constructor for castling moves
    move(int castle_code) {
        this->castle_code = castle_code;
        this->init_pos = {INVALID, INVALID};
        this->final_pos = {INVALID, INVALID};
        this->captured_piece = INVALID;
        this->promoted_piece = INVALID;
    }
};

Apart from this, there is an another fundamental change we need to make. A castle is not allowed if the king or rook has already moved. This condition is dealt by having a series of booleans which carry the permissions for each of the four castles.

class chessboard {
private:
    ...
    
    bool whiteQcastle, prevWhiteQcastle;
    bool whiteKcastle, prevWhiteKcastle;
    bool blackQcastle, prevBlackQcastle;
    bool blackKcastle, prevBlackKcastle;

    ...
};

The variables prepended with ‘prev’ exist purely for us to undo the moves and restore the original castling permissions. When making a move, if the piece to be moved is a rook or a king, we alter the castling permissions on the board accordingly(and preserve the old state in the prev variables). Look below:

void make_move(move m) {
        int curr_piece = board[m.init_pos.first][m.init_pos.second];
        // Store the current castling permissions
        prevWhiteKcastle = whiteKcastle;
        prevWhiteQcastle = whiteQcastle;
        prevBlackKcastle = blackKcastle;
        prevBlackQcastle = blackQcastle;
        
        // Set the permissions
        if(curr_piece == WK) {
            whiteKcastle = false;
            whiteQcastle = false;
        }
        if(curr_piece == BK) {
            blackKcastle = false;
            blackQcastle = false;
        }
        if(curr_piece == WR) {
            if(m.init_pos.second == 7) {
                whiteKcastle = false;
            } else if(m.init_pos.second == 0) {
                whiteQcastle = false;
            }
        }
      
        ...
}

Finally, to see which move to make, we simply look at the castling code and make the respective changes on the board. Undoing it is just the opposite of this(see if you can come up with the code; open this link to confirm).

// Make a castling move
if(m.castle_code != NO_CASTLE) {
    switch(m.castle_code) {
        case WHITE_QUEEN_SIDE_CASTLE:
            board[7][2] = BL;
            board[7][3] = BL;
            board[7][0] = WR;
            board[7][4] = WK;
            break;
        ...
    }
    ...
}

Now, that we have dealt with how to play/apply the move, let’s see how to generate them.

auto generate_all_moves() {
    std::vector <move&gt; movelist;
        
    // Castles
    if(side_to_play == WHITE &amp;&amp; whiteQcastle == true 
                &amp;&amp; board[7][1] == BL &amp;&amp; board[7][2] == BL 
                            &amp;&amp; board[7][3] == BL) {
        movelist.push_back(move(WHITE_QUEEN_SIDE_CASTLE));
    }
    // The other 3 castles can be generated in a similar manner.
    ...    
}

You might have noticed that the castling moves we generate might not be perfectly legal(the squares along the path may be attacked or the king may be under check). Since these are related to the problem of filtering out legal moves from pseudo-legal ones, we’ll deal with this issue while solving that.

Anyway, I created a sample position where we can check out if the castling and pawn promotion work properly(the code is here and the output here).

Now on to en passant. We set a flag called is_enpassant in our chessboard if a pawn is moved by 2 squares. Then while move generation, if this flag is set; we make the necessary adjustments to the board in the make_move function. Why don’t you try this yourself? Will you that in the next article. See you soon 🙂

Advertisements

About the Author Pranav Deshpande

Software Developer interested in AI, Reinforcement Learning and game programming.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s