return b ^ SquareBB[s];
}
+inline Bitboard operator-(Bitboard b, Square s) {
+ assert(s >= SQ_A1 && s <= SQ_H8);
+ return b & ~SquareBB[s];
+}
+
inline Bitboard& operator|=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b |= SquareBB[s];
return b ^= SquareBB[s];
}
+inline Bitboard& operator-=(Bitboard& b, Square s) {
+ assert(s >= SQ_A1 && s <= SQ_H8);
+ return b &= ~SquareBB[s];
+}
+
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}
}
+/// promotion_zone_bb() returns a bitboard representing the squares on all the ranks
+/// in front of and on the given relative rank, from the point of view of the given color.
+/// For instance, promotion_zone_bb(BLACK, RANK_7) will return the 16 squares on ranks 1 and 2.
+
+inline Bitboard promotion_zone_bb(Color c, Rank r) {
+ return ForwardRanksBB[c][relative_rank(c, r)] | rank_bb(relative_rank(c, r));
+}
+
+
/// forward_file_bb() returns a bitboard representing all the squares along the line
/// in front of the given one, from the point of view of the given color:
/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s)
return moveList;
}
+ template<Color Us, bool Checks>
+ ExtMove* generate_drops(const Position& pos, ExtMove* moveList, PieceType pt, Bitboard b) {
+ if (pos.count_in_hand(Us, pt))
+ {
+ if (pt == PAWN)
+ b &= ~(promotion_zone_bb(Us, pos.promotion_rank()) | rank_bb(relative_rank(Us, RANK_1)));
+ if (Checks)
+ b &= pos.check_squares(pt);
+ while (b)
+ *moveList++ = make_drop(pop_lsb(&b), pt);
+ }
+
+ return moveList;
+ }
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
for (PieceType pt = PieceType(PAWN + 1); pt < KING; ++pt)
moveList = generate_moves<Checks>(pos, moveList, Us, pt, target);
+ // generate drops
+ if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES))
+ for (PieceType pt = PAWN; pt < KING; ++pt)
+ moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
-typedef Stats<int16_t, 10368, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
+typedef Stats<int16_t, 10368, COLOR_NB, int(SQUARE_NB + 1) * int(SQUARE_NB)> ButterflyHistory;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
put_piece(Piece(idx), sq);
++sq;
}
+ // Set flag for promoted pieces
+ else if (piece_drops() && !drop_loop() && token == '~')
+ promotedPieces |= SquareBB[sq - 1];
+ // Stop before pieces in hand
+ else if (token == '[')
+ break;
}
+ // Pieces in hand
+ if (!isspace(token))
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == ']')
+ continue;
+ else if ((idx = piece_to_char().find(token)) != string::npos)
+ add_to_hand(color_of(Piece(idx)), type_of(Piece(idx)));
+ }
// 2. Active color
ss >> token;
ss << emptyCnt;
if (f <= FILE_H)
+ {
ss << piece_to_char()[piece_on(make_square(f, r))];
+
+ // Set promoted pieces
+ if (piece_drops() && is_promoted(make_square(f, r)))
+ ss << "~";
+ }
}
if (r > RANK_1)
bool Position::legal(Move m) const {
assert(is_ok(m));
+ assert(type_of(m) != DROP || piece_drops());
Color us = sideToMove;
Square from = from_sq(m);
}
}
+ // illegal drops
+ if (piece_drops() && type_of(m) == DROP)
+ return pieceCountInHand[us][type_of(moved_piece(m))] && empty(to_sq(m));
+
// game end
if (is_variant_end())
return false;
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent. Castling moves are checked
// for legality during move generation.
- if (type_of(piece_on(from)) == KING)
+ if (type_of(moved_piece(m)) == KING)
return type_of(m) == CASTLING || !(attackers_to(to) & pieces(~us));
// A non-king move is legal if the king is not under attack after the move.
- return !(attackers_to(ksq, (pieces() ^ from) | to) & pieces(~us) & ~SquareBB[to]);
+ return !( attackers_to(ksq, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to)
+ & pieces(~us) & ~SquareBB[to]);
}
Square to = to_sq(m);
// Is there a direct check?
- if (st->checkSquares[type_of(piece_on(from))] & to)
+ if (st->checkSquares[type_of(moved_piece(m))] & to)
return true;
// Is there a discovered check?
- if ( (st->blockersForKing[~sideToMove] & from)
+ if ( type_of(m) != DROP
+ && (st->blockersForKing[~sideToMove] & from)
&& ( !aligned(from, to, square<KING>(~sideToMove))
|| (attackers_to(square<KING>(~sideToMove), (pieces() ^ from) | to) & pieces(sideToMove))))
return true;
switch (type_of(m))
{
case NORMAL:
+ case DROP:
return false;
case PROMOTION:
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
- Piece pc = piece_on(from);
+ Piece pc = moved_piece(m);
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(pc) == us);
st->pawnKey ^= Zobrist::psq[captured][capsq];
}
else
+ {
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
+ if (piece_drops() && !is_promoted(to))
+ st->nonPawnMaterial[us] += PieceValue[MG][captured];
+ }
// Update board and piece lists
remove_piece(captured, capsq);
+ if (piece_drops())
+ {
+ st->capturedpromoted = is_promoted(to);
+ Piece pieceToHand = is_promoted(to) ? make_piece(~color_of(captured), PAWN) : ~captured;
+ add_to_hand(color_of(pieceToHand), type_of(pieceToHand));
+ st->psq += PSQT::psq[pieceToHand][SQ_NONE];
+ k ^= Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1]
+ ^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]];
+ promotedPieces -= to;
+ }
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
}
// Update hash key
- k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
+ if (type_of(m) == DROP)
+ k ^= Zobrist::psq[pc][to]
+ ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)] - 1]
+ ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)]];
+ else
+ k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// Reset en passant square
if (st->epSquare != SQ_NONE)
}
// Update castling rights if needed
- if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
+ if (type_of(m) != DROP && st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
{
int cr = castlingRightsMask[from] | castlingRightsMask[to];
k ^= Zobrist::castling[st->castlingRights & cr];
}
// Move the piece. The tricky Chess960 castling is handled earlier
- if (type_of(m) != CASTLING)
+ if (type_of(m) == DROP)
+ {
+ drop_piece(pc, to);
+ st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1];
+ }
+ else if (type_of(m) != CASTLING)
move_piece(pc, from, to);
// If the moving piece is a pawn do some special extra work
remove_piece(pc, to);
put_piece(promotion, to);
+ if (piece_drops() && !drop_loop())
+ promotedPieces = promotedPieces | to;
// Update hash keys
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
}
// Update pawn hash key and prefetch access to pawnsTable
- st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
+ if (type_of(m) == DROP)
+ st->pawnKey ^= Zobrist::psq[pc][to];
+ else
+ st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
// Set capture piece
st->capturedPiece = captured;
+ if (piece_drops() && !captured)
+ st->capturedpromoted = false;
// Update the key with the final value
st->key = k;
// Calculate checkers bitboard (if move gives check)
st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
+ if (type_of(m) != DROP && is_promoted(from))
+ promotedPieces = (promotedPieces - from) | to;
+
sideToMove = ~sideToMove;
// Update king attacks used for fast check detection
Square to = to_sq(m);
Piece pc = piece_on(to);
- assert(empty(from) || type_of(m) == CASTLING);
+ assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING);
assert(type_of(st->capturedPiece) != KING);
if (type_of(m) == PROMOTION)
remove_piece(pc, to);
pc = make_piece(us, PAWN);
put_piece(pc, to);
+ if (piece_drops() && !drop_loop())
+ promotedPieces -= to;
}
if (type_of(m) == CASTLING)
}
else
{
- move_piece(pc, to, from); // Put the piece back at the source square
+ if (type_of(m) == DROP)
+ undrop_piece(pc, to); // Remove the dropped piece
+ else
+ move_piece(pc, to, from); // Put the piece back at the source square
+ if (piece_drops() && !drop_loop() && is_promoted(to))
+ promotedPieces = (promotedPieces - to) | from;
if (st->capturedPiece)
{
}
put_piece(st->capturedPiece, capsq); // Restore the captured piece
+ if (piece_drops())
+ {
+ remove_from_hand(~color_of(st->capturedPiece),
+ !drop_loop() && st->capturedpromoted ? PAWN : type_of(st->capturedPiece));
+ if (!drop_loop() && st->capturedpromoted)
+ promotedPieces |= to;
+ }
}
}
Square from = from_sq(m);
Square to = to_sq(m);
- Piece pc = piece_on(from);
+ Piece pc = moved_piece(m);
Piece captured = piece_on(to);
Key k = st->key ^ Zobrist::side;
if (captured)
+ {
k ^= Zobrist::psq[captured][to];
+ if (piece_drops())
+ {
+ Piece removeFromHand = !drop_loop() && is_promoted(to) ? make_piece(~color_of(captured), PAWN) : ~captured;
+ k ^= Zobrist::inHand[removeFromHand][pieceCountInHand[color_of(removeFromHand)][type_of(removeFromHand)] + 1]
+ ^ Zobrist::inHand[removeFromHand][pieceCountInHand[color_of(removeFromHand)][type_of(removeFromHand)]];
+ }
+ }
+ if (type_of(m) == DROP)
+ return k ^ Zobrist::psq[pc][to] ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)]]
+ ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)] - 1];
return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
}
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
- int end = std::min(st->rule50, st->pliesFromNull);
+ int end = piece_drops() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull);
if (end < 4)
return false;
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
assert(0 && "pos_is_ok: Kings");
- if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
- || pieceCount[make_piece(WHITE, PAWN)] > 8
- || pieceCount[make_piece(BLACK, PAWN)] > 8)
+ if ( (pieces(PAWN) & Rank8BB)
+ || pieceCount[make_piece(WHITE, PAWN)] > 16
+ || pieceCount[make_piece(BLACK, PAWN)] > 16)
assert(0 && "pos_is_ok: Pawns");
if ( (pieces(WHITE) & pieces(BLACK))
|| (pieces(WHITE) | pieces(BLACK)) != pieces()
- || popcount(pieces(WHITE)) > 16
- || popcount(pieces(BLACK)) > 16)
+ || popcount(pieces(WHITE)) > 32
+ || popcount(pieces(BLACK)) > 32)
assert(0 && "pos_is_ok: Bitboards");
for (PieceType p1 = PAWN; p1 <= KING; ++p1)
Bitboard blockersForKing[COLOR_NB];
Bitboard pinners[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB];
+ bool capturedpromoted;
};
/// A list to keep track of the position states along the setup moves (from the
bool checking_permitted() const;
bool must_capture() const;
bool piece_drops() const;
+ bool drop_loop() const;
// winning conditions
Value stalemate_value(int ply = 0) const;
Value checkmate_value(int ply = 0) const;
bool is_variant_end() const;
bool is_variant_end(Value& result, int ply = 0) const;
+ // Variant-specific properties
+ int count_in_hand(Color c, PieceType pt) const;
+
// Position representation
Bitboard pieces() const;
Bitboard pieces(PieceType pt) const;
Bitboard byColorBB[COLOR_NB];
int pieceCount[PIECE_NB];
Square pieceList[PIECE_NB][16];
- int pieceCountInHand[COLOR_NB][PIECE_TYPE_NB];
- Bitboard promotedPieces;
int index[SQUARE_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Color sideToMove;
Thread* thisThread;
StateInfo* st;
+
+ // variant-specific
const Variant* var;
bool chess960;
+ int pieceCountInHand[COLOR_NB][PIECE_TYPE_NB];
+ Bitboard promotedPieces;
+ bool is_promoted(Square s) const;
+ void add_to_hand(Color c, PieceType pt);
+ void remove_from_hand(Color c, PieceType pt);
+ void drop_piece(Piece pc, Square s);
+ void undrop_piece(Piece pc, Square s);
};
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
return var->pieceDrops;
}
+inline bool Position::drop_loop() const {
+ assert(var != nullptr);
+ return var->dropLoop;
+}
+
inline Value Position::stalemate_value(int ply) const {
assert(var != nullptr);
Value v = var->stalemateValue;
}
inline Piece Position::moved_piece(Move m) const {
+ if (type_of(m) == DROP)
+ return make_piece(sideToMove, dropped_piece_type(m));
return board[from_sq(m)];
}
inline bool Position::capture_or_promotion(Move m) const {
assert(is_ok(m));
- return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
+ return type_of(m) != NORMAL ? type_of(m) != DROP && type_of(m) != CASTLING : !empty(to_sq(m));
}
inline bool Position::capture(Move m) const {
do_move(m, newSt, gives_check(m));
}
+inline int Position::count_in_hand(Color c, PieceType pt) const {
+ return pieceCountInHand[c][pt];
+}
+
+inline void Position::add_to_hand(Color c, PieceType pt) {
+ pieceCountInHand[c][pt]++;
+ pieceCountInHand[c][ALL_PIECES]++;
+}
+
+inline void Position::remove_from_hand(Color c, PieceType pt) {
+ pieceCountInHand[c][pt]--;
+ pieceCountInHand[c][ALL_PIECES]--;
+}
+
+inline bool Position::is_promoted(Square s) const {
+ return promotedPieces & s;
+}
+
+inline void Position::drop_piece(Piece pc, Square s) {
+ assert(pieceCountInHand[color_of(pc)][type_of(pc)]);
+ put_piece(pc, s);
+ remove_from_hand(color_of(pc), type_of(pc));
+}
+
+inline void Position::undrop_piece(Piece pc, Square s) {
+ remove_piece(pc, s);
+ board[s] = NO_PIECE;
+ add_to_hand(color_of(pc), type_of(pc));
+ assert(pieceCountInHand[color_of(pc)][type_of(pc)]);
+}
+
#endif // #ifndef POSITION_H_INCLUDED
typedef uint64_t Key;
typedef uint64_t Bitboard;
-constexpr int MAX_MOVES = 256;
+constexpr int MAX_MOVES = 512;
constexpr int MAX_PLY = 128;
/// A move needs 16 bits to be stored
PROMOTION_STRAIGHT = PROMOTION,
PROMOTION_LEFT = 4 << 12,
PROMOTION_RIGHT = 5 << 12,
+ DROP = 6 << 12,
};
enum Color {
}
inline Square from_sq(Move m) {
+ if (type_of(m) == DROP)
+ return SQ_NONE;
if (type_of(m) == PROMOTION)
{
Square to = to_sq(m);
return Move(T + (from << 6) + to);
}
+constexpr Move make_drop(Square to, PieceType pt) {
+ return Move(DROP + (pt << 6) + to);
+}
+
+constexpr PieceType dropped_piece_type(Move m) {
+ return PieceType((m >> 6) & 63);
+}
+
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}
if (type_of(m) == CASTLING && !pos.is_chess960())
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
- string move = UCI::square(from) + UCI::square(to);
+ string move = (type_of(m) == DROP ? std::string{pos.piece_to_char()[type_of(pos.moved_piece(m))], '@'}
+ : UCI::square(from)) + UCI::square(to);
if (type_of(m) == PROMOTION)
move += pos.piece_to_char()[make_piece(BLACK, promotion_type(m))];
v->pieceDrops = true;
return v;
} ();
+ const Variant* loop = [&]{
+ Variant* v = new Variant();
+ v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[] w KQkq - 0 1";
+ v->pieceDrops = true;
+ v->dropLoop = true;
+ return v;
+ } ();
insert(std::pair<std::string, const Variant*>(std::string("chess"), chess));
insert(std::pair<std::string, const Variant*>(std::string("makruk"), makruk));
insert(std::pair<std::string, const Variant*>(std::string("asean"), asean));
insert(std::pair<std::string, const Variant*>(std::string("losers"), losers));
insert(std::pair<std::string, const Variant*>(std::string("3check"), threecheck));
insert(std::pair<std::string, const Variant*>(std::string("5check"), fivecheck));
- //insert(std::pair<std::string, const Variant*>(std::string("crazyhouse"), crazyhouse));
+ insert(std::pair<std::string, const Variant*>(std::string("crazyhouse"), crazyhouse));
+ insert(std::pair<std::string, const Variant*>(std::string("loop"), loop));
}
void VariantMap::clear_all() {
bool checking = true;
bool mustCapture = false;
bool pieceDrops = false;
+ bool dropLoop = false;
// game end
Value stalemateValue = VALUE_DRAW;
Value checkmateValue = -VALUE_MATE;