/*
Texel - A UCI chess engine.
Copyright (C) 2012-2014 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/>.
*/
/*
* moveGen.hpp
*
* Created on: Feb 25, 2012
* Author: petero
*/
#ifndef MOVEGEN_HPP_
#define MOVEGEN_HPP_
#include "move.hpp"
#include "position.hpp"
#include "util/util.hpp"
#include <cassert>
//#define MOVELIST_DEBUG
#ifdef MOVELIST_DEBUG
# include <set>
# include "textio.hpp"
#endif
/** A stack-allocated move list object. */
class MoveList {
public:
MoveList();
void clear();
Move& operator[](int i);
const Move& operator[](int i) const;
void addMove(int from, int to, int promoteTo);
/** Remove all moves that are not included in searchMoves. */
void filter(const std::vector<Move>& searchMoves);
int size;
private:
static const int MAX_MOVES = 256;
int buf[sizeof(Move[MAX_MOVES])/sizeof(int)];
};
/**
* Generates move lists (pseudo-legal, legal, check evasions, captures).
*/
class MoveGen {
public:
MoveGen() = delete;
/**
* Generate and return a list of pseudo-legal moves.
* Pseudo-legal means that the moves don't necessarily defend from check threats.
*/
template <bool wtm>
static void pseudoLegalMoves(const Position& pos, MoveList& moveList);
static void pseudoLegalMoves(const Position& pos, MoveList& moveList);
/**
* Generate and return a list of pseudo-legal check evasion moves.
* Pseudo-legal means that the moves don't necessarily defend from check threats.
*/
template <bool wtm>
static void checkEvasions(const Position& pos, MoveList& moveList);
static void checkEvasions(const Position& pos, MoveList& moveList);
/** Generate captures, checks, and possibly some other moves that are too hard to filter out. */
template <bool wtm>
static void pseudoLegalCapturesAndChecks(const Position& pos, MoveList& moveList);
static void pseudoLegalCapturesAndChecks(const Position& pos, MoveList& moveList);
/** Generate and return a list of pseudo-legal capture moves. */
template <bool wtm>
static void pseudoLegalCaptures(const Position& pos, MoveList& moveList);
static void pseudoLegalCaptures(const Position& pos, MoveList& moveList);
/** Return true if the side to move is in check. */
static bool inCheck(const Position& pos);
/** Return true if making a move delivers check to the opponent */
static bool givesCheck(const Position& pos, const Move& m);
/** Return true if the side to move can take the opponents king. */
static bool canTakeKing(Position& pos);
/** Return true if a square is attacked by the opposite side. */
static bool sqAttacked(const Position& pos, int sq);
static bool sqAttacked(const Position& pos, int sq, U64 occupied);
template <bool wtm> static bool sqAttacked(const Position& pos, int sq, U64 occupied);
/**
* 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.
*/
static void removeIllegal(Position& pos, MoveList& moveList);
/** Return true if the pseudo-legal move "move" is legal is position "pos".
* isInCheck must be equal to inCheck(pos). */
static bool isLegal(Position& pos, const Move& move, bool isInCheck);
private:
/** Return the next piece in a given direction, starting from sq. */
static int nextPiece(const Position& pos, int sq, int delta);
/** Like nextPiece(), but handles board edges. */
static int nextPieceSafe(const Position& pos, int sq, int delta);
template <bool wtm>
static void addPawnMovesByMask(MoveList& moveList, U64 mask, int delta, bool allPromotions);
static void addPawnDoubleMovesByMask(MoveList& moveList, U64 mask, int delta);
static void addMovesByMask(MoveList& moveList, int sq0, U64 mask);
};
inline
MoveList::MoveList()
: size(0) {
}
inline void
MoveList::clear() {
size = 0;
}
inline Move&
MoveList::operator[](int i) {
return ((Move*)&buf[0])[i];
}
inline const Move&
MoveList::operator[](int i) const {
return ((Move*)&buf[0])[i];
}
inline void
MoveList::addMove(int from, int to, int promoteTo) {
Move& m = (*this)[size++];
new (&m) Move(from, to, promoteTo, 0);
}
inline void
MoveGen::pseudoLegalMoves(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
pseudoLegalMoves<true>(pos, moveList);
else
pseudoLegalMoves<false>(pos, moveList);
}
inline void
MoveGen::checkEvasions(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
checkEvasions<true>(pos, moveList);
else
checkEvasions<false>(pos, moveList);
}
inline void
MoveGen::pseudoLegalCapturesAndChecks(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
pseudoLegalCapturesAndChecks<true>(pos, moveList);
else
pseudoLegalCapturesAndChecks<false>(pos, moveList);
}
inline void
MoveGen::pseudoLegalCaptures(const Position& pos, MoveList& moveList) {
if (pos.isWhiteMove())
pseudoLegalCaptures<true>(pos, moveList);
else
pseudoLegalCaptures<false>(pos, moveList);
}
inline bool
MoveGen::inCheck(const Position& pos) {
int kingSq = pos.getKingSq(pos.isWhiteMove());
return sqAttacked(pos, kingSq);
}
inline bool
MoveGen::canTakeKing(Position& pos) {
pos.setWhiteMove(!pos.isWhiteMove());
bool ret = inCheck(pos);
pos.setWhiteMove(!pos.isWhiteMove());
return ret;
}
inline bool
MoveGen::sqAttacked(const Position& pos, int sq) {
const U64 occupied = pos.occupiedBB();
return sqAttacked(pos, sq, occupied);
}
inline bool
MoveGen::sqAttacked(const Position& pos, int sq, U64 occupied) {
return pos.isWhiteMove() ? sqAttacked<true>(pos, sq, occupied)
: sqAttacked<false>(pos, sq, occupied);
}
template <bool wtm>
inline bool
MoveGen::sqAttacked(const Position& pos, int sq, U64 occupied) {
using OtherColor = ColorTraits<!wtm>;
if ((BitBoard::knightAttacks[sq] & pos.pieceTypeBB(OtherColor::KNIGHT)) != 0)
return true;
if ((BitBoard::kingAttacks[sq] & pos.pieceTypeBB(OtherColor::KING)) != 0)
return true;
if (wtm) {
if ((BitBoard::wPawnAttacks[sq] & pos.pieceTypeBB(OtherColor::PAWN)) != 0)
return true;
} else {
if ((BitBoard::bPawnAttacks[sq] & pos.pieceTypeBB(OtherColor::PAWN)) != 0)
return true;
}
U64 bbQueen = pos.pieceTypeBB(OtherColor::QUEEN);
if ((BitBoard::bishopAttacks(sq, occupied) & (pos.pieceTypeBB(OtherColor::BISHOP) | bbQueen)) != 0)
return true;
if ((BitBoard::rookAttacks(sq, occupied) & (pos.pieceTypeBB(OtherColor::ROOK) | bbQueen)) != 0)
return true;
return false;
}
inline int
MoveGen::nextPiece(const Position& pos, int sq, int delta) {
while (true) {
sq += delta;
int p = pos.getPiece(sq);
if (p != Piece::EMPTY)
return p;
}
assert(false);
return -1;
}
inline int
MoveGen::nextPieceSafe(const 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;
}
assert(false);
return -1;
}
template <bool wtm>
inline void
MoveGen::addPawnMovesByMask(MoveList& moveList, U64 mask, int delta, bool allPromotions) {
using MyColor = ColorTraits<wtm>;
if (mask == 0)
return;
U64 promMask = mask & BitBoard::maskRow1Row8;
mask &= ~promMask;
while (promMask != 0) {
int sq = BitBoard::extractSquare(promMask);
int sq0 = sq + delta;
moveList.addMove(sq0, sq, MyColor::QUEEN);
moveList.addMove(sq0, sq, MyColor::KNIGHT);
if (allPromotions) {
moveList.addMove(sq0, sq, MyColor::ROOK);
moveList.addMove(sq0, sq, MyColor::BISHOP);
}
}
while (mask != 0) {
int sq = BitBoard::extractSquare(mask);
moveList.addMove(sq + delta, sq, Piece::EMPTY);
}
}
inline void
MoveGen::addPawnDoubleMovesByMask(MoveList& moveList, U64 mask, int delta) {
while (mask != 0) {
int sq = BitBoard::extractSquare(mask);
moveList.addMove(sq + delta, sq, Piece::EMPTY);
}
}
inline void
MoveGen::addMovesByMask(MoveList& moveList, int sq0, U64 mask) {
while (mask != 0) {
int sq = BitBoard::extractSquare(mask);
moveList.addMove(sq0, sq, Piece::EMPTY);
}
}
#endif /* MOVEGEN_HPP_ */