1

I have generated pseudo-legal moves, and now need to check if they are legal. I believe the fastest way of doing this is comparing the kings position to a bitboard or list of all attacked squares, and making sure it doesn't overlap. My questions are:

  • How would I account for pins?
  • What is the fastest way of generating the list of attacked squares?

Also, if anyone has written a similar algorithm to take a set of pseudo legal moves and return a list of only legal moves, could you copy the code or a GitHub link so I can use it for reference.

1
  • Not really an answer to the question, but making a related point: In case you use the moves to run an engine one simple option is to simply ignore checks and let the search handle it. I.e. if you made an illegal move, you'll notice one ply later and can handle it there.
    – koedem
    Commented Aug 31, 2023 at 8:59

1 Answer 1

0

The easiest way to generate legal moves is to just play out every pseudo-legal move and see if the king in check afterward. The fastest way to check for check (no pun intended) is with the "I see you, you see me" method. For example, to see if the king is in check from a rook, you would generate rook moves from the king's position and see if any of them move to a square with an enemy rook (or a queen).

I recommend doing this before you move on to faster methods if you don't have working legal move generation yet, since you can use this to catch lots of bugs while your code isn't too complicated.

However, this is quite slow as you can imagine, and there is a much faster way to do it. The first thing you do is generate the enemy's attacked squares. This unfortunately is not very fast; you have to do pseudo legal move generation for your opponent as well. You can AND their moves for each piece to an attacks bitboard. At the beginning of your function you will need to start with a variable checkSquare which will be equal to zero. If the current enemy pieces' move bitboard && your king then make checkSquare equal to the index of the current enemy piece, unless checkSquare is already nonzero, in which case you are in double check and can set checkSquare to say, 64.

IMPORTANT NOTE: make sure to allow your opponent to capture their own pieces in this special move generation, otherwise your program will think taking a defended piece with your king is legal. This can be done by fiddling with the piece color bitboards.

After this, you generate moves based on checkSquare. If checkSquare is 64, you are in double check and your only moves will be pseudoLegalKingMoves && !attacks.

Otherwise, you will have to find all of the pinned pieces. You can find some info about this here.

If there is only one check, you will look at the piece type of the enemy checker. If it is a knight or pawn, the only legal moves will be capturing the checking piece or the king moves as described earlier. If it is a sliding piece, you will need to generate all the squares in between your king and checkSquare (IIRC a lookup table is fastest for this) then AND all your pseudo legal moves with the in-between squares. No pinned piece can move while in check, so you can actually save yourself the effort of generating moves for pinned pieces by testing pins before pseudo-legal move generation.

IMPORTANT NOTE: en-passant can be tricky: for example, a common bug is one where a king is on the fifth rank and an opponent pawn get double pushed. En-passant might look legal to the program but actually be pinned by a rook. Additionally, en-passant doesn't actually move to the square of the captured pawn so en-passanting a checking pawn to evade a check might not even be considered by an engine.

If there is no check, for each pinned piece you can AND its moves with the diagonal/file/rank on which it is pinned. Like I said, a lookup table really helps with this, but none of that matters if you are generating sliding piece moves with loops, in which case you can also consider switching pseudo-legal move generation to lookup tables. And of course, for king moves you can just do the same thing as before.

Lastly, make sure to test your code against stockfish in some common test positions. Good luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.