A simple method to check if a move delivers checks.

Discussion of chess software programming and technical issues.

Moderator: Ras

OliverBr
Posts: 846
Joined: Tue Dec 18, 2007 9:38 pm
Location: Munich, Germany
Full name: Dr. Oliver Brausch

A simple method to check if a move delivers checks.

Post by OliverBr »

Starting with all direct checks I wrote the following:

Code: Select all

int isDirectCheck(Move m) {
	int t = TO(m), c = ONMV(m), p = PROM(m) ? PROM(m) : PIECE(m), evilK = P.king[c^1], occ = BOARD;
	switch (p) {
		case PAWN:
			if (PCAP(evilK, c^1) & BIT[t]) return 1; else break;
		case KNIGHT:
			if (nmoves[evilK] & BIT[t]) return 1; else break;
		case BISHOP:
			if (BATT(evilK, occ) & BIT[t]) return 1; else break;
		case ROOK:
			if (RATT(evilK, occ) & BIT[t]) return 1; else break;
		case QUEEN:
			if (BATT(evilK, occ) & BIT[t]) return 1;
			if (RATT(evilK, occ) & BIT[t]) return 1;
	}
	return 0;
}
PCAP, nmoves, BATT, RATT being the BitBoard-representations for all attacks the EvilKing could do if he were Pawn, Knight, etc..
I haven't tested it yet, but it looks quite simple. Have I missed something?
OliThink GitHub: https://github.com/olithink
Nice arcticle about OlIThink: https://www.chessengeria.eu/post/olithink-oldie-goldie
Chess Engine OliThink Homepage: http://brausch.org/home/chess
mar
Posts: 2665
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: A simple method to check if a move delivers checks.

Post by mar »

OliverBr wrote: Thu Sep 18, 2025 1:41 pm Have I missed something?
discovered checks
OliverBr
Posts: 846
Joined: Tue Dec 18, 2007 9:38 pm
Location: Munich, Germany
Full name: Dr. Oliver Brausch

Re: A simple method to check if a move delivers checks.

Post by OliverBr »

mar wrote: Fri Sep 19, 2025 12:10 am
OliverBr wrote: Thu Sep 18, 2025 1:41 pm Have I missed something?
discovered checks
Yes... there are the next step and a little more "demanding". Fortunately OliThink has already "Xray-Bitboards, so there it should be doable. We don't have forget that there can be discovered check with "en-passant", which is tricky :)
OliThink GitHub: https://github.com/olithink
Nice arcticle about OlIThink: https://www.chessengeria.eu/post/olithink-oldie-goldie
Chess Engine OliThink Homepage: http://brausch.org/home/chess
chessbit
Posts: 26
Joined: Fri Dec 29, 2023 4:47 pm
Location: Belgium
Full name: thomas albert

Re: A simple method to check if a move delivers checks.

Post by chessbit »

OliverBr wrote: Thu Sep 18, 2025 1:41 pm Starting with all direct checks I wrote the following:

Code: Select all

int isDirectCheck(Move m) {
	int t = TO(m), c = ONMV(m), p = PROM(m) ? PROM(m) : PIECE(m), evilK = P.king[c^1], occ = BOARD;
	switch (p) {
		case PAWN:
			if (PCAP(evilK, c^1) & BIT[t]) return 1; else break;
		case KNIGHT:
			if (nmoves[evilK] & BIT[t]) return 1; else break;
		case BISHOP:
			if (BATT(evilK, occ) & BIT[t]) return 1; else break;
		case ROOK:
			if (RATT(evilK, occ) & BIT[t]) return 1; else break;
		case QUEEN:
			if (BATT(evilK, occ) & BIT[t]) return 1;
			if (RATT(evilK, occ) & BIT[t]) return 1;
	}
	return 0;
}
PCAP, nmoves, BATT, RATT being the BitBoard-representations for all attacks the EvilKing could do if he were Pawn, Knight, etc..
I haven't tested it yet, but it looks quite simple. Have I missed something?
I don't know if you have a good reason to separate the direct checks and the discoveries, but it would be more efficient to combine them.
Also, the way you access data with arrays etc is quite inefficient, but since I don't know your code and what you're trying to achieve, I will ignore that.
For pawns and knights, I do the same as you, but you could do rougly something like this:

Code: Select all

uint64 checks = 0
IF pawn =>           checks |= PCAP(evilK, c^1) & BIT[t]
ELSE IF knight =>  checks |= nmoves[evilK] & BIT[t]

//now check for all slider attacks (takes care of direct checks and discoveries at the same time)
if (ROOK_XRAYS[evilK] & (rooks | queens))        checks |= RATT(evilK, occ) & (rooks | queens);
if (BISHOP_XRAYS[evilK] & (bishops | queens))   checks |= BATT(evilK, occ) & (bishops | queens);
The XRAYS are the 4 directions starting from the enemy king, to avoid the lookup if the sliders are not in the vision. I recently added this in my engine and it improved by ~2-3%.
mar
Posts: 2665
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: A simple method to check if a move delivers checks.

Post by mar »

you can only do direct checks only after you make the move, but if you want to test whether the move gives check before you make it (which I assume was what the OP was trying to achieve),
then you have to handle discovered checks, en passant as well as promotions and even castling, the king himself can give a discovered check etc.
(the latter is vastly more complicated but can be useful in a real engine)
abulmo2
Posts: 474
Joined: Fri Dec 16, 2016 11:04 am
Location: France
Full name: Richard Delorme

Re: A simple method to check if a move delivers checks.

Post by abulmo2 »

OliverBr wrote: Thu Sep 18, 2025 1:41 pm Have I missed something?
Castling ? This is the only king move that can give a direct chess, through the rook actually.
Richard Delorme
PK
Posts: 908
Joined: Mon Jan 15, 2007 11:23 am
Location: Warsza

Re: A simple method to check if a move delivers checks.

Post by PK »

Publius does it like that (not the cleanest code I'm afraid)

Code: Select all

// Detect whether a move gives check
// without making it on the board
bool Position::MoveGivesCheck(const Move move) {

    Bitboard checks, occ;

    // Collect information about the move
    Color color = GetSideToMove();
    Square fromSquare = GetFromSquare(move);
    Square toSquare = GetToSquare(move);
    PieceType hunterType = PieceTypeOnSq(fromSquare);
    PieceType preyType = PieceTypeOnSq(toSquare);

    // Handle promotion
    if (IsMovePromotion(move))
        hunterType = GetPromotedPiece(move);

    // Locate enemy king
    Square kingSquare = KingSq(~color);

    // Direct checks by a pawn
    if (hunterType == Pawn) {
        checks = ForwardOf(SidesOf(Paint(kingSquare)), ~color);
        if (checks & Paint(toSquare)) return true;
    }

    // Init occupancy bitboard
    occ = Occupied();

    // Remove pawn in case of promotion,
    // otherwise we will not detech checks
    // along the same ray as the promoting move
    if (IsMovePromotion(move))
        occ ^= Paint(fromSquare);

    // Direct checks by a knight
    if (hunterType == Knight) {
        checks = GenerateMoves.Knight(kingSquare);
        if (checks & Paint(toSquare)) return true;
    }

    // Direct diagonal checks
    if (hunterType == Bishop || hunterType == Queen) {
        checks = GenerateMoves.Bish(occ, kingSquare);
        if (checks & Paint(toSquare)) return true;
    }

    // Direct orthogonal checks
    if (hunterType == Rook || hunterType == Queen) {
        checks = GenerateMoves.Rook(occ, kingSquare);
        if (checks & Paint(toSquare)) return true;
    }

    // Prepare occupancy map after the move...
    occ = Occupied() ^ (Paint(fromSquare) | Paint(toSquare));

    // ...remembering to take captures into account
    if (preyType != noPieceType)
        occ ^= Paint(toSquare);

    // Diagonal discovered checks
    checks = GenerateMoves.Bish(occ, kingSquare);
    if (checks & MapDiagonalMovers(color)) return true;

    // Orthogonal discovered checks
    checks = GenerateMoves.Rook(occ, kingSquare);
    if (checks & MapStraightMovers(color)) return true;

    // Checks discovered by en passant capture
    if (GetTypeOfMove(move) == tEnPassant) {
        int dir = (GetSideToMove() == White ? -8 : 8);
        occ ^= Paint(toSquare + dir);

        checks = GenerateMoves.Bish(occ, kingSquare);
        if (checks & MapDiagonalMovers(color)) return true;

        checks = GenerateMoves.Rook(occ, kingSquare);
        if (checks & MapStraightMovers(color)) return true;
    }

    // Checks discovered by castling
    // (we make and unmake a move, as it's rare enough
    // and writing out correct conditions would be hard)
    if (GetTypeOfMove(move) == tCastle) {
        UndoData undo;
        DoMove(move, &undo);
        bool isInCheck = IsInCheck();
        UndoMove(move, &undo);
        return isInCheck;
    }

    return false;
}
mar
Posts: 2665
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: A simple method to check if a move delivers checks.

Post by mar »

PK wrote: Sat Sep 20, 2025 12:53 am Publius does it like that (not the cleanest code I'm afraid)
looks pretty nice, my code for this is quite ugly

the comment for castling implies that castling can give a discovered check, but I'd argue that castling in chess (and even chess 960)
can only give a direct check (rook)
User avatar
hgm
Posts: 28386
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: A simple method to check if a move delivers checks.

Post by hgm »

I always do something like:

Code: Select all

int evilKing = location[KING + xstm];
int relativeKingLocation = evilKing - toSqr;
int possible = alignment[relativeKingLocation]; // there exist moves that go from to-square to enemy King
int hit = possible & captureCode[movedPiece]; // the moved piece has such a move
if(hit) { // on an empty board this would be a check
  if(possible & CONTACT) return CONTACT_CHECK;
  int step = baseStep[relativeKingLocation];
  int sqr = evilKing; // assuming we still need evilKing later, for testing for discovered checks
  while(board[sqr-=step] == EMPTY) {}
  if(sqr == toSqr) return DISTANT_CHECK;
}
return 0;
The advantage is that the outer if-statement is easily predictable, as most moves do not deliver check. (Unlike a switch statement on mover type, which is an almost certain misprediction.) Which also means that the code within the if-clause is commonly not executed, so that its speed is not very critical. The ray scan, which would be needed for distant checks is done starting at the King, to make it terminate very quickly during most of the game, where the King is surrounded by friendly pieces (such as a Pawn shield).

I suppose the if-clause could be optimized further (eliminating a possibly mispredicted branch) as:

Code: Select all

if(hit) { // on an empty board this would be a check
  int step = baseStep[relativeKingLocation];
  int sqr = evilKing;
  int stop = moveType[hit]; // 0 for contact attacks, 1 for distant attacks
  while(board[sqr-=step] < stop) {} // this would always terminate immediate for contact checks (assuming 0 means EMPTY).
  if(sqr == toSqr) return stop + 1; // returns 1 for contact check, 2 for distant check
}
The moveType[hit] table could be replaced by an expression ((possible & LEAPS) != 0) to relieve load (and cache) pressure.

In engines where I have a neighbor table which is already updated at this point, the ray scan would of course not be needed, and you would get something like:

Code: Select all

if(hit) { // on an empty board this would be a check
  if(possible & LEAPS) return CONTACT_CHECK; // necessary to catch Knight jumps
  int dir = direction[relativeKingLocation]; // direction number 0...7
  int sqr = nearestNeighbor[evilKing][dir];
  if(sqr == toSqr) return DISTANT_CHECK;
}
For discoverd checks it works similarly; the primary filter here is alignment of the enemy King and the from-square:

Code: Select all

int relativeKingLocation = evilKing - fromSqr;
int hit = aligned[relativeKingLocation] & SLIDES; // orthogonally or diagonally aligned
if(hit) { // on an empty board this would be a check
  int dir = direction[relativeKingLocation]; // direction number 0...7
  int sqr = nearestNeighbor[evilKing][dir];
  int discoveredPiece = board[sqr]; // new neighbor of enemy King
  if(discoverdPiece & xstm) return 0; // is enemy
  if(captureCode[discoveredPiece] & hit) return DISCOVERED_CHECK; // and slides in this direction
}
return 0;
If the SLIDES bits in the elements of the alignment[] table would not only distinguish orthogonal and diagonal, but also the piece color (i.e. white and black rooks would use different bits in their captureCode), we could save a branch (and make us less dependent on how pieces are encoded) by omitting the explicit testing for an enemy piece: we could just AND the condition of the final test with slideMask[stm], which would mask away the SLIDER bits of wrongly colored pieces.

Good point about the castling, BTW. I don't think any of my engines does that. Of course this is pretty much a 'never happens' case. In most games an engine would never think about castling, as this was already done while still in book.
OliverBr
Posts: 846
Joined: Tue Dec 18, 2007 9:38 pm
Location: Munich, Germany
Full name: Dr. Oliver Brausch

Re: A simple method to check if a move delivers checks.

Post by OliverBr »

Hi chessbit,
If I may, I would directly go into this question:
chessbit wrote: Fri Sep 19, 2025 6:47 pm I don't know if you have a good reason to separate the direct checks and the discoveries, but it would be more efficient to combine them.
Without checking, I guess in 98%+ of the positions there are only direct checks and no discovered checks. So maybe this makes it easier for the compiler to optimize?
OliThink GitHub: https://github.com/olithink
Nice arcticle about OlIThink: https://www.chessengeria.eu/post/olithink-oldie-goldie
Chess Engine OliThink Homepage: http://brausch.org/home/chess