1074 lines
47 KiB
Java

/*
CuckooChess - A java chess program.
Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package chess;
import java.util.List;
/**
*
* @author petero
*/
public final class MoveGen {
static final MoveGen instance;
static {
instance = new MoveGen();
}
public final static class MoveList {
public final Move[] m;
public int size;
MoveList() {
m = new Move[MAX_MOVES];
this.size = 0;
}
public final void filter(List<Move> searchMoves) {
int used = 0;
for (int i = 0; i < size; i++)
if (searchMoves.contains(m[i]))
m[used++] = m[i];
size = used;
}
}
/**
* Generate and return a list of pseudo-legal moves.
* Pseudo-legal means that the moves doesn't necessarily defend from check threats.
*/
public final MoveList pseudoLegalMoves(Position pos) {
MoveList moveList = getMoveListObj();
final long occupied = pos.whiteBB | pos.blackBB;
if (pos.whiteMove) {
// Queen moves
long squares = pos.pieceTypeBB[Piece.WQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.WROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.WBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(true);
long m = BitBoard.kingAttacks[sq] & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
final int k0 = 4;
if (sq == k0) {
final long OO_SQ = 0x60L;
final long OOO_SQ = 0xEL;
if (((pos.getCastleMask() & (1 << Position.H1_CASTLE)) != 0) &&
((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 + 3) == Piece.WROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 + 1)) {
setMove(moveList, k0, k0 + 2, Piece.EMPTY);
}
if (((pos.getCastleMask() & (1 << Position.A1_CASTLE)) != 0) &&
((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 - 4) == Piece.WROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 - 1)) {
setMove(moveList, k0, k0 - 2, Piece.EMPTY);
}
}
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.WKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.WPAWN];
long m = (pawns << 8) & ~occupied;
if (addPawnMovesByMask(moveList, pos, m, -8, true)) return moveList;
m = ((m & BitBoard.maskRow3) << 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, pos, m, -16);
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -7, true)) return moveList;
m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -9, true)) return moveList;
} else {
// Queen moves
long squares = pos.pieceTypeBB[Piece.BQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.BROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.BBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(false);
long m = BitBoard.kingAttacks[sq] & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
final int k0 = 60;
if (sq == k0) {
final long OO_SQ = 0x6000000000000000L;
final long OOO_SQ = 0xE00000000000000L;
if (((pos.getCastleMask() & (1 << Position.H8_CASTLE)) != 0) &&
((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 + 3) == Piece.BROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 + 1)) {
setMove(moveList, k0, k0 + 2, Piece.EMPTY);
}
if (((pos.getCastleMask() & (1 << Position.A8_CASTLE)) != 0) &&
((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 - 4) == Piece.BROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 - 1)) {
setMove(moveList, k0, k0 - 2, Piece.EMPTY);
}
}
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.BKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.BPAWN];
long m = (pawns >>> 8) & ~occupied;
if (addPawnMovesByMask(moveList, pos, m, 8, true)) return moveList;
m = ((m & BitBoard.maskRow6) >>> 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, pos, m, 16);
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 9, true)) return moveList;
m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 7, true)) return moveList;
}
return moveList;
}
/**
* Generate and return a list of pseudo-legal check evasion moves.
* Pseudo-legal means that the moves doesn't necessarily defend from check threats.
*/
public final MoveList checkEvasions(Position pos) {
MoveList moveList = getMoveListObj();
final long occupied = pos.whiteBB | pos.blackBB;
if (pos.whiteMove) {
long kingThreats = pos.pieceTypeBB[Piece.BKNIGHT] & BitBoard.knightAttacks[pos.wKingSq];
long rookPieces = pos.pieceTypeBB[Piece.BROOK] | pos.pieceTypeBB[Piece.BQUEEN];
if (rookPieces != 0)
kingThreats |= rookPieces & BitBoard.rookAttacks(pos.wKingSq, occupied);
long bishPieces = pos.pieceTypeBB[Piece.BBISHOP] | pos.pieceTypeBB[Piece.BQUEEN];
if (bishPieces != 0)
kingThreats |= bishPieces & BitBoard.bishopAttacks(pos.wKingSq, occupied);
kingThreats |= pos.pieceTypeBB[Piece.BPAWN] & BitBoard.wPawnAttacks[pos.wKingSq];
long validTargets = 0;
if ((kingThreats != 0) && ((kingThreats & (kingThreats-1)) == 0)) { // Exactly one attacking piece
int threatSq = BitBoard.numberOfTrailingZeros(kingThreats);
validTargets = kingThreats | BitBoard.squaresBetween[pos.wKingSq][threatSq];
}
validTargets |= pos.pieceTypeBB[Piece.BKING];
// Queen moves
long squares = pos.pieceTypeBB[Piece.WQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) &
~pos.whiteBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.WROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & ~pos.whiteBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.WBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.whiteBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(true);
long m = BitBoard.kingAttacks[sq] & ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.WKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.whiteBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.WPAWN];
long m = (pawns << 8) & ~occupied;
if (addPawnMovesByMask(moveList, pos, m & validTargets, -8, true)) return moveList;
m = ((m & BitBoard.maskRow3) << 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, pos, m & validTargets, -16);
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns << 7) & BitBoard.maskAToGFiles & ((pos.blackBB & validTargets) | epMask);
if (addPawnMovesByMask(moveList, pos, m, -7, true)) return moveList;
m = (pawns << 9) & BitBoard.maskBToHFiles & ((pos.blackBB & validTargets) | epMask);
if (addPawnMovesByMask(moveList, pos, m, -9, true)) return moveList;
} else {
long kingThreats = pos.pieceTypeBB[Piece.WKNIGHT] & BitBoard.knightAttacks[pos.bKingSq];
long rookPieces = pos.pieceTypeBB[Piece.WROOK] | pos.pieceTypeBB[Piece.WQUEEN];
if (rookPieces != 0)
kingThreats |= rookPieces & BitBoard.rookAttacks(pos.bKingSq, occupied);
long bishPieces = pos.pieceTypeBB[Piece.WBISHOP] | pos.pieceTypeBB[Piece.WQUEEN];
if (bishPieces != 0)
kingThreats |= bishPieces & BitBoard.bishopAttacks(pos.bKingSq, occupied);
kingThreats |= pos.pieceTypeBB[Piece.WPAWN] & BitBoard.bPawnAttacks[pos.bKingSq];
long validTargets = 0;
if ((kingThreats != 0) && ((kingThreats & (kingThreats-1)) == 0)) { // Exactly one attacking piece
int threatSq = BitBoard.numberOfTrailingZeros(kingThreats);
validTargets = kingThreats | BitBoard.squaresBetween[pos.bKingSq][threatSq];
}
validTargets |= pos.pieceTypeBB[Piece.WKING];
// Queen moves
long squares = pos.pieceTypeBB[Piece.BQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) &
~pos.blackBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.BROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & ~pos.blackBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.BBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & ~pos.blackBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(false);
long m = BitBoard.kingAttacks[sq] & ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.BKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.blackBB & validTargets;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.BPAWN];
long m = (pawns >>> 8) & ~occupied;
if (addPawnMovesByMask(moveList, pos, m & validTargets, 8, true)) return moveList;
m = ((m & BitBoard.maskRow6) >>> 8) & ~occupied;
addPawnDoubleMovesByMask(moveList, pos, m & validTargets, 16);
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns >>> 9) & BitBoard.maskAToGFiles & ((pos.whiteBB & validTargets) | epMask);
if (addPawnMovesByMask(moveList, pos, m, 9, true)) return moveList;
m = (pawns >>> 7) & BitBoard.maskBToHFiles & ((pos.whiteBB & validTargets) | epMask);
if (addPawnMovesByMask(moveList, pos, m, 7, true)) return moveList;
}
/* Extra debug checks
{
ArrayList<Move> allMoves = pseudoLegalMoves(pos);
allMoves = MoveGen.removeIllegal(pos, allMoves);
HashSet<String> evMoves = new HashSet<String>();
for (Move m : moveList)
evMoves.add(TextIO.moveToUCIString(m));
for (Move m : allMoves)
if (!evMoves.contains(TextIO.moveToUCIString(m)))
throw new RuntimeException();
}
*/
return moveList;
}
/** Generate captures, checks, and possibly some other moves that are too hard to filter out. */
public final MoveList pseudoLegalCapturesAndChecks(Position pos) {
MoveList moveList = getMoveListObj();
long occupied = pos.whiteBB | pos.blackBB;
if (pos.whiteMove) {
int bKingSq = pos.getKingSq(false);
long discovered = 0; // Squares that could generate discovered checks
long kRookAtk = BitBoard.rookAttacks(bKingSq, occupied);
if ((BitBoard.rookAttacks(bKingSq, occupied & ~kRookAtk) &
(pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WROOK])) != 0)
discovered |= kRookAtk;
long kBishAtk = BitBoard.bishopAttacks(bKingSq, occupied);
if ((BitBoard.bishopAttacks(bKingSq, occupied & ~kBishAtk) &
(pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WBISHOP])) != 0)
discovered |= kBishAtk;
// Queen moves
long squares = pos.pieceTypeBB[Piece.WQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied));
if ((discovered & (1L<<sq)) == 0) m &= (pos.blackBB | kRookAtk | kBishAtk);
m &= ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.WROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied);
if ((discovered & (1L<<sq)) == 0) m &= (pos.blackBB | kRookAtk);
m &= ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.WBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied);
if ((discovered & (1L<<sq)) == 0) m &= (pos.blackBB | kBishAtk);
m &= ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(true);
long m = BitBoard.kingAttacks[sq];
m &= ((discovered & (1L<<sq)) == 0) ? pos.blackBB : ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
final int k0 = 4;
if (sq == k0) {
final long OO_SQ = 0x60L;
final long OOO_SQ = 0xEL;
if (((pos.getCastleMask() & (1 << Position.H1_CASTLE)) != 0) &&
((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 + 3) == Piece.WROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 + 1)) {
setMove(moveList, k0, k0 + 2, Piece.EMPTY);
}
if (((pos.getCastleMask() & (1 << Position.A1_CASTLE)) != 0) &&
((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 - 4) == Piece.WROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 - 1)) {
setMove(moveList, k0, k0 - 2, Piece.EMPTY);
}
}
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.WKNIGHT];
long kKnightAtk = BitBoard.knightAttacks[bKingSq];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.whiteBB;
if ((discovered & (1L<<sq)) == 0) m &= (pos.blackBB | kKnightAtk);
m &= ~pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
// Captures
long pawns = pos.pieceTypeBB[Piece.WPAWN];
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
long m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -7, false)) return moveList;
m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -9, false)) return moveList;
// Discovered checks and promotions
long pawnAll = discovered | BitBoard.maskRow7;
m = ((pawns & pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB);
if (addPawnMovesByMask(moveList, pos, m, -8, false)) return moveList;
m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB);
addPawnDoubleMovesByMask(moveList, pos, m, -16);
// Normal checks
m = ((pawns & ~pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB);
if (addPawnMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -8, false)) return moveList;
m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB);
addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -16);
} else {
int wKingSq = pos.getKingSq(true);
long discovered = 0; // Squares that could generate discovered checks
long kRookAtk = BitBoard.rookAttacks(wKingSq, occupied);
if ((BitBoard.rookAttacks(wKingSq, occupied & ~kRookAtk) &
(pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BROOK])) != 0)
discovered |= kRookAtk;
long kBishAtk = BitBoard.bishopAttacks(wKingSq, occupied);
if ((BitBoard.bishopAttacks(wKingSq, occupied & ~kBishAtk) &
(pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BBISHOP])) != 0)
discovered |= kBishAtk;
// Queen moves
long squares = pos.pieceTypeBB[Piece.BQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied));
if ((discovered & (1L<<sq)) == 0) m &= pos.whiteBB | kRookAtk | kBishAtk;
m &= ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.BROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied);
if ((discovered & (1L<<sq)) == 0) m &= pos.whiteBB | kRookAtk;
m &= ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.BBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied);
if ((discovered & (1L<<sq)) == 0) m &= pos.whiteBB | kBishAtk;
m &= ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// King moves
{
int sq = pos.getKingSq(false);
long m = BitBoard.kingAttacks[sq];
m &= ((discovered & (1L<<sq)) == 0) ? pos.whiteBB : ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
final int k0 = 60;
if (sq == k0) {
final long OO_SQ = 0x6000000000000000L;
final long OOO_SQ = 0xE00000000000000L;
if (((pos.getCastleMask() & (1 << Position.H8_CASTLE)) != 0) &&
((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 + 3) == Piece.BROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 + 1)) {
setMove(moveList, k0, k0 + 2, Piece.EMPTY);
}
if (((pos.getCastleMask() & (1 << Position.A8_CASTLE)) != 0) &&
((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) &&
(pos.getPiece(k0 - 4) == Piece.BROOK) &&
!sqAttacked(pos, k0) &&
!sqAttacked(pos, k0 - 1)) {
setMove(moveList, k0, k0 - 2, Piece.EMPTY);
}
}
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.BKNIGHT];
long kKnightAtk = BitBoard.knightAttacks[wKingSq];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & ~pos.blackBB;
if ((discovered & (1L<<sq)) == 0) m &= pos.whiteBB | kKnightAtk;
m &= ~pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// Pawn moves
// Captures
long pawns = pos.pieceTypeBB[Piece.BPAWN];
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
long m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 9, false)) return moveList;
m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 7, false)) return moveList;
// Discovered checks and promotions
long pawnAll = discovered | BitBoard.maskRow2;
m = ((pawns & pawnAll) >>> 8) & ~(pos.whiteBB | pos.blackBB);
if (addPawnMovesByMask(moveList, pos, m, 8, false)) return moveList;
m = ((m & BitBoard.maskRow6) >>> 8) & ~(pos.whiteBB | pos.blackBB);
addPawnDoubleMovesByMask(moveList, pos, m, 16);
// Normal checks
m = ((pawns & ~pawnAll) >>> 8) & ~(pos.whiteBB | pos.blackBB);
if (addPawnMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 8, false)) return moveList;
m = ((m & BitBoard.maskRow6) >>> 8) & ~(pos.whiteBB | pos.blackBB);
addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 16);
}
return moveList;
}
public final MoveList pseudoLegalCaptures(Position pos) {
MoveList moveList = getMoveListObj();
long occupied = pos.whiteBB | pos.blackBB;
if (pos.whiteMove) {
// Queen moves
long squares = pos.pieceTypeBB[Piece.WQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.WROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.WBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.WKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// King moves
int sq = pos.getKingSq(true);
long m = BitBoard.kingAttacks[sq] & pos.blackBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.WPAWN];
m = (pawns << 8) & ~(pos.whiteBB | pos.blackBB);
m &= BitBoard.maskRow8;
if (addPawnMovesByMask(moveList, pos, m, -8, false)) return moveList;
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -7, false)) return moveList;
m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, -9, false)) return moveList;
} else {
// Queen moves
long squares = pos.pieceTypeBB[Piece.BQUEEN];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Rook moves
squares = pos.pieceTypeBB[Piece.BROOK];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.rookAttacks(sq, occupied) & pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Bishop moves
squares = pos.pieceTypeBB[Piece.BBISHOP];
while (squares != 0) {
int sq = BitBoard.numberOfTrailingZeros(squares);
long m = BitBoard.bishopAttacks(sq, occupied) & pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
squares &= squares-1;
}
// Knight moves
long knights = pos.pieceTypeBB[Piece.BKNIGHT];
while (knights != 0) {
int sq = BitBoard.numberOfTrailingZeros(knights);
long m = BitBoard.knightAttacks[sq] & pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
knights &= knights-1;
}
// King moves
int sq = pos.getKingSq(false);
long m = BitBoard.kingAttacks[sq] & pos.whiteBB;
if (addMovesByMask(moveList, pos, sq, m)) return moveList;
// Pawn moves
long pawns = pos.pieceTypeBB[Piece.BPAWN];
m = (pawns >>> 8) & ~(pos.whiteBB | pos.blackBB);
m &= BitBoard.maskRow1;
if (addPawnMovesByMask(moveList, pos, m, 8, false)) return moveList;
int epSquare = pos.getEpSquare();
long epMask = (epSquare >= 0) ? (1L << epSquare) : 0L;
m = (pawns >>> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 9, false)) return moveList;
m = (pawns >>> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask);
if (addPawnMovesByMask(moveList, pos, m, 7, false)) return moveList;
}
return moveList;
}
/**
* Return true if the side to move is in check.
*/
public static final boolean inCheck(Position pos) {
int kingSq = pos.getKingSq(pos.whiteMove);
return sqAttacked(pos, kingSq);
}
/**
* Return the next piece in a given direction, starting from sq.
*/
private static final int nextPiece(Position pos, int sq, int delta) {
while (true) {
sq += delta;
int p = pos.getPiece(sq);
if (p != Piece.EMPTY)
return p;
}
}
/** Like nextPiece(), but handles board edges. */
private static final int nextPieceSafe(Position pos, int sq, int delta) {
int dx = 0, dy = 0;
switch (delta) {
case 1: dx=1; dy=0; break;
case 9: dx=1; dy=1; break;
case 8: dx=0; dy=1; break;
case 7: dx=-1; dy=1; break;
case -1: dx=-1; dy=0; break;
case -9: dx=-1; dy=-1; break;
case -8: dx=0; dy=-1; break;
case -7: dx=1; dy=-1; break;
}
int x = Position.getX(sq);
int y = Position.getY(sq);
while (true) {
x += dx;
y += dy;
if ((x < 0) || (x > 7) || (y < 0) || (y > 7)) {
return Piece.EMPTY;
}
int p = pos.getPiece(Position.getSquare(x, y));
if (p != Piece.EMPTY)
return p;
}
}
/**
* Return true if making a move delivers check to the opponent
*/
public static final boolean givesCheck(Position pos, Move m) {
boolean wtm = pos.whiteMove;
int oKingSq = pos.getKingSq(!wtm);
int oKing = wtm ? Piece.BKING : Piece.WKING;
int p = Piece.makeWhite(m.promoteTo == Piece.EMPTY ? pos.getPiece(m.from) : m.promoteTo);
int d1 = BitBoard.getDirection(m.to, oKingSq);
switch (d1) {
case 8: case -8: case 1: case -1: // Rook direction
if ((p == Piece.WQUEEN) || (p == Piece.WROOK))
if ((d1 != 0) && (MoveGen.nextPiece(pos, m.to, d1) == oKing))
return true;
break;
case 9: case 7: case -9: case -7: // Bishop direction
if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) {
if ((d1 != 0) && (MoveGen.nextPiece(pos, m.to, d1) == oKing))
return true;
} else if (p == Piece.WPAWN) {
if (((d1 > 0) == wtm) && (pos.getPiece(m.to + d1) == oKing))
return true;
}
break;
default:
if (d1 != 0) { // Knight direction
if (p == Piece.WKNIGHT)
return true;
}
}
int d2 = BitBoard.getDirection(m.from, oKingSq);
if ((d2 != 0) && (d2 != d1) && (MoveGen.nextPiece(pos, m.from, d2) == oKing)) {
int p2 = MoveGen.nextPieceSafe(pos, m.from, -d2);
switch (d2) {
case 8: case -8: case 1: case -1: // Rook direction
if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) ||
(p2 == (wtm ? Piece.WROOK : Piece.BROOK)))
return true;
break;
case 9: case 7: case -9: case -7: // Bishop direction
if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) ||
(p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP)))
return true;
break;
}
}
if ((m.promoteTo != Piece.EMPTY) && (d1 != 0) && (d1 == d2)) {
switch (d1) {
case 8: case -8: case 1: case -1: // Rook direction
if ((p == Piece.WQUEEN) || (p == Piece.WROOK))
if ((d1 != 0) && (MoveGen.nextPiece(pos, m.from, d1) == oKing))
return true;
break;
case 9: case 7: case -9: case -7: // Bishop direction
if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) {
if ((d1 != 0) && (MoveGen.nextPiece(pos, m.from, d1) == oKing))
return true;
}
break;
}
}
if (p == Piece.WKING) {
if (m.to - m.from == 2) { // O-O
if (MoveGen.nextPieceSafe(pos, m.from, -1) == oKing)
return true;
if (MoveGen.nextPieceSafe(pos, m.from + 1, wtm ? 8 : -8) == oKing)
return true;
} else if (m.to - m.from == -2) { // O-O-O
if (MoveGen.nextPieceSafe(pos, m.from, 1) == oKing)
return true;
if (MoveGen.nextPieceSafe(pos, m.from - 1, wtm ? 8 : -8) == oKing)
return true;
}
} else if (p == Piece.WPAWN) {
if (pos.getPiece(m.to) == Piece.EMPTY) {
int dx = Position.getX(m.to) - Position.getX(m.from);
if (dx != 0) { // en passant
int epSq = m.from + dx;
int d3 = BitBoard.getDirection(epSq, oKingSq);
switch (d3) {
case 9: case 7: case -9: case -7:
if (MoveGen.nextPiece(pos, epSq, d3) == oKing) {
int p2 = MoveGen.nextPieceSafe(pos, epSq, -d3);
if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) ||
(p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP)))
return true;
}
break;
case 1:
if (MoveGen.nextPiece(pos, Math.max(epSq, m.from), d3) == oKing) {
int p2 = MoveGen.nextPieceSafe(pos, Math.min(epSq, m.from), -d3);
if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) ||
(p2 == (wtm ? Piece.WROOK : Piece.BROOK)))
return true;
}
break;
case -1:
if (MoveGen.nextPiece(pos, Math.min(epSq, m.from), d3) == oKing) {
int p2 = MoveGen.nextPieceSafe(pos, Math.max(epSq, m.from), -d3);
if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) ||
(p2 == (wtm ? Piece.WROOK : Piece.BROOK)))
return true;
}
break;
}
}
}
}
return false;
}
/**
* Return true if the side to move can take the opponents king.
*/
public static final boolean canTakeKing(Position pos) {
pos.setWhiteMove(!pos.whiteMove);
boolean ret = inCheck(pos);
pos.setWhiteMove(!pos.whiteMove);
return ret;
}
/**
* Return true if a square is attacked by the opposite side.
*/
public static final boolean sqAttacked(Position pos, int sq) {
if (pos.whiteMove) {
if ((BitBoard.knightAttacks[sq] & pos.pieceTypeBB[Piece.BKNIGHT]) != 0)
return true;
if ((BitBoard.kingAttacks[sq] & pos.pieceTypeBB[Piece.BKING]) != 0)
return true;
if ((BitBoard.wPawnAttacks[sq] & pos.pieceTypeBB[Piece.BPAWN]) != 0)
return true;
long occupied = pos.whiteBB | pos.blackBB;
long bbQueen = pos.pieceTypeBB[Piece.BQUEEN];
if ((BitBoard.bishopAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.BBISHOP] | bbQueen)) != 0)
return true;
if ((BitBoard.rookAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.BROOK] | bbQueen)) != 0)
return true;
} else {
if ((BitBoard.knightAttacks[sq] & pos.pieceTypeBB[Piece.WKNIGHT]) != 0)
return true;
if ((BitBoard.kingAttacks[sq] & pos.pieceTypeBB[Piece.WKING]) != 0)
return true;
if ((BitBoard.bPawnAttacks[sq] & pos.pieceTypeBB[Piece.WPAWN]) != 0)
return true;
long occupied = pos.whiteBB | pos.blackBB;
long bbQueen = pos.pieceTypeBB[Piece.WQUEEN];
if ((BitBoard.bishopAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.WBISHOP] | bbQueen)) != 0)
return true;
if ((BitBoard.rookAttacks(sq, occupied) & (pos.pieceTypeBB[Piece.WROOK] | bbQueen)) != 0)
return true;
}
return false;
}
/**
* Remove all illegal moves from moveList.
* "moveList" is assumed to be a list of pseudo-legal moves.
* This function removes the moves that don't defend from check threats.
*/
public static final void removeIllegal(Position pos, MoveList moveList) {
int length = 0;
UndoInfo ui = new UndoInfo();
boolean isInCheck = inCheck(pos);
final long occupied = pos.whiteBB | pos.blackBB;
int kSq = pos.getKingSq(pos.whiteMove);
long kingAtks = BitBoard.rookAttacks(kSq, occupied) | BitBoard.bishopAttacks(kSq, occupied);
int epSquare = pos.getEpSquare();
if (isInCheck) {
kingAtks |= pos.pieceTypeBB[pos.whiteMove ? Piece.BKNIGHT : Piece.WKNIGHT];
for (int mi = 0; mi < moveList.size; mi++) {
Move m = moveList.m[mi];
boolean legal;
if ((m.from != kSq) && ((kingAtks & (1L<<m.to)) == 0) && (m.to != epSquare)) {
legal = false;
} else {
pos.makeMove(m, ui);
pos.setWhiteMove(!pos.whiteMove);
legal = !inCheck(pos);
pos.setWhiteMove(!pos.whiteMove);
pos.unMakeMove(m, ui);
}
if (legal)
moveList.m[length++].copyFrom(m);
}
} else {
for (int mi = 0; mi < moveList.size; mi++) {
Move m = moveList.m[mi];
boolean legal;
if ((m.from != kSq) && ((kingAtks & (1L<<m.from)) == 0) && (m.to != epSquare)) {
legal = true;
} else {
pos.makeMove(m, ui);
pos.setWhiteMove(!pos.whiteMove);
legal = !inCheck(pos);
pos.setWhiteMove(!pos.whiteMove);
pos.unMakeMove(m, ui);
}
if (legal)
moveList.m[length++].copyFrom(m);
}
}
moveList.size = length;
}
private final static boolean addPawnMovesByMask(MoveList moveList, Position pos, long mask,
int delta, boolean allPromotions) {
if (mask == 0)
return false;
long oKingMask = pos.pieceTypeBB[pos.whiteMove ? Piece.BKING : Piece.WKING];
if ((mask & oKingMask) != 0) {
int sq = BitBoard.numberOfTrailingZeros(mask & oKingMask);
moveList.size = 0;
setMove(moveList, sq + delta, sq, Piece.EMPTY);
return true;
}
long promMask = mask & BitBoard.maskRow1Row8;
mask &= ~promMask;
while (promMask != 0) {
int sq = BitBoard.numberOfTrailingZeros(promMask);
int sq0 = sq + delta;
if (sq >= 56) { // White promotion
setMove(moveList, sq0, sq, Piece.WQUEEN);
setMove(moveList, sq0, sq, Piece.WKNIGHT);
if (allPromotions) {
setMove(moveList, sq0, sq, Piece.WROOK);
setMove(moveList, sq0, sq, Piece.WBISHOP);
}
} else { // Black promotion
setMove(moveList, sq0, sq, Piece.BQUEEN);
setMove(moveList, sq0, sq, Piece.BKNIGHT);
if (allPromotions) {
setMove(moveList, sq0, sq, Piece.BROOK);
setMove(moveList, sq0, sq, Piece.BBISHOP);
}
}
promMask &= (promMask - 1);
}
while (mask != 0) {
int sq = BitBoard.numberOfTrailingZeros(mask);
setMove(moveList, sq + delta, sq, Piece.EMPTY);
mask &= (mask - 1);
}
return false;
}
private final static void addPawnDoubleMovesByMask(MoveList moveList, Position pos,
long mask, int delta) {
while (mask != 0) {
int sq = BitBoard.numberOfTrailingZeros(mask);
setMove(moveList, sq + delta, sq, Piece.EMPTY);
mask &= (mask - 1);
}
}
private final static boolean addMovesByMask(MoveList moveList, Position pos, int sq0, long mask) {
long oKingMask = pos.pieceTypeBB[pos.whiteMove ? Piece.BKING : Piece.WKING];
if ((mask & oKingMask) != 0) {
int sq = BitBoard.numberOfTrailingZeros(mask & oKingMask);
moveList.size = 0;
setMove(moveList, sq0, sq, Piece.EMPTY);
return true;
}
while (mask != 0) {
int sq = BitBoard.numberOfTrailingZeros(mask);
setMove(moveList, sq0, sq, Piece.EMPTY);
mask &= (mask - 1);
}
return false;
}
private final static void setMove(MoveList moveList, int from, int to, int promoteTo) {
Move m = moveList.m[moveList.size++];
m.from = from;
m.to = to;
m.promoteTo = promoteTo;
m.score = 0;
}
// Code to handle the Move cache.
private Object[] moveListCache = new Object[200];
private int moveListsInCache = 0;
private static final int MAX_MOVES = 256;
private final MoveList getMoveListObj() {
MoveList ml;
if (moveListsInCache > 0) {
ml = (MoveList)moveListCache[--moveListsInCache];
ml.size = 0;
} else {
ml = new MoveList();
for (int i = 0; i < MAX_MOVES; i++)
ml.m[i] = new Move(0, 0, Piece.EMPTY);
}
return ml;
}
/** Return all move objects in moveList to the move cache. */
public final void returnMoveList(MoveList moveList) {
if (moveListsInCache < moveListCache.length) {
moveListCache[moveListsInCache++] = moveList;
}
}
}