put_piece(Piece(idx), sq);
++sq;
}
+ // Promoted shogi pieces
+ else if (token == '+')
+ {
+ ss >> token;
+ idx = piece_to_char().find(token);
+ unpromotedBoard[sq] = Piece(idx);
+ promotedPieces |= SquareBB[sq];
+ put_piece(make_piece(color_of(Piece(idx)), promoted_piece_type(type_of(Piece(idx)))), sq);
+ ++sq;
+ }
+ // Set flag for promoted pieces
+ else if (captures_to_hand() && !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)));
++ add_to_hand(Piece(idx));
+ }
// 2. Active color
ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK);
+ // Invert side to move for SFEN
+ if (sfen)
+ sideToMove = ~sideToMove;
ss >> token;
- // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
- // Shredder-FEN that uses the letters of the columns on which the rooks began
- // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
- // if an inner rook is associated with the castling right, the castling tag is
- // replaced by the file letter of the involved rook, as for the Shredder-FEN.
- while ((ss >> token) && !isspace(token))
+ // 3-4. Skip parsing castling and en passant flags if not present
+ st->epSquare = SQ_NONE;
+ if (!isdigit(ss.peek()) && !sfen)
{
- Square rsq;
- Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, ROOK);
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
+ // if an inner rook is associated with the castling right, the castling tag is
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
+ while ((ss >> token) && !isspace(token))
+ {
+ Square rsq;
+ Color c = islower(token) ? BLACK : WHITE;
+ Piece rook = make_piece(c, ROOK);
- token = char(toupper(token));
+ token = char(toupper(token));
- if (token == 'K')
- for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
+ if (token == 'K')
+ for (rsq = make_square(FILE_MAX, relative_rank(c, RANK_1, max_rank())); piece_on(rsq) != rook; --rsq) {}
- else if (token == 'Q')
- for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
+ else if (token == 'Q')
+ for (rsq = make_square(FILE_A, relative_rank(c, RANK_1, max_rank())); piece_on(rsq) != rook; ++rsq) {}
- else if (token >= 'A' && token <= 'H')
- rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
+ else if (token >= 'A' && token <= 'A' + max_file())
+ rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1, max_rank()));
- else
- continue;
+ else
+ continue;
+
+ set_castling_right(c, rsq);
+ }
+
+ // counting limit
+ if (counting_rule() && isdigit(ss.peek()))
+ ss >> st->countingLimit;
- set_castling_right(c, rsq);
+ // 4. En passant square. Ignore if no pawn capture is possible
+ else if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
+ && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
+ {
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+
+ if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
+ || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
+ st->epSquare = SQ_NONE;
+ }
}
- // 4. En passant square. Ignore if no pawn capture is possible
- if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == '3' || row == '6')))
- {
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ // Check counter for nCheck
+ ss >> std::skipws >> token >> std::noskipws;
- if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
- || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
- st->epSquare = SQ_NONE;
+ if (max_check_count() && ss.peek() == '+')
+ {
+ st->checksGiven[WHITE] = CheckCount(std::max(max_check_count() - std::max(token - '0', 0), 0));
+ ss >> token >> token;
+ st->checksGiven[BLACK] = CheckCount(std::max(max_check_count() - std::max(token - '0', 0), 0));
}
else
- st->epSquare = SQ_NONE;
+ ss.putback(token);
// 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> gamePly;
+ if (sfen)
+ {
+ // Pieces in hand for SFEN
+ 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)));
++ add_to_hand(Piece(idx));
+ }
+ // Move count is in ply for SFEN
+ ss >> std::skipws >> gamePly;
+ gamePly = std::max(gamePly - 1, 0);
+ }
+ else
+ {
+ ss >> std::skipws >> st->rule50 >> gamePly;
+
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
+ // handle also common incorrect FEN with fullmove = 0.
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ }
- // Convert from fullmove starting from 1 to gamePly starting from 0,
- // handle also common incorrect FEN with fullmove = 0.
- gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ // counting rules
+ if (st->countingLimit && st->rule50)
+ {
+ st->countingPly = st->rule50;
+ st->rule50 = 0;
+ }
chess960 = isChess960;
thisThread = th;
si->key = si->materialKey = 0;
si->pawnKey = Zobrist::noPawns;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
- si->psq = SCORE_ZERO;
- si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
+ si->checkersBB = count<KING>(sideToMove) ? attackers_to(square<KING>(sideToMove), ~sideToMove) : 0;
set_check_info(si);
// Update board and piece lists
remove_piece(captured, capsq);
+ if (captures_to_hand())
+ {
+ st->capturedpromoted = is_promoted(to);
+ Piece pieceToHand = !is_promoted(to) ? ~captured
+ : unpromotedCaptured ? ~unpromotedCaptured
+ : make_piece(~color_of(captured), PAWN);
- add_to_hand(color_of(pieceToHand), type_of(pieceToHand));
- st->psq += PSQT::psq[pieceToHand][SQ_NONE];
++ add_to_hand(pieceToHand);
+ k ^= Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1]
+ ^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]];
+ promotedPieces -= to;
+ }
+ unpromotedBoard[to] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
// Reset rule 50 draw counter
st->rule50 = 0;
}
+ else if (type_of(m) == PIECE_PROMOTION)
+ {
+ Piece promotion = make_piece(us, promoted_piece_type(type_of(pc)));
+
+ remove_piece(pc, to);
+ put_piece(promotion, to);
+ promotedPieces |= to;
+ unpromotedBoard[to] = pc;
+
+ // Update hash keys
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
+ st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
+ ^ Zobrist::psq[pc][pieceCount[pc]];
+
- // Update incremental score
- st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to];
-
+ // Update material
+ st->nonPawnMaterial[us] += PieceValue[MG][promotion] - PieceValue[MG][pc];
+ }
+ else if (type_of(m) == PIECE_DEMOTION)
+ {
+ Piece demotion = unpromoted_piece_on(from);
+
+ remove_piece(pc, to);
+ put_piece(demotion, to);
+ promotedPieces ^= from;
+ unpromotedBoard[from] = NO_PIECE;
+
+ // Update hash keys
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[demotion][to];
+ st->materialKey ^= Zobrist::psq[demotion][pieceCount[demotion]-1]
+ ^ Zobrist::psq[pc][pieceCount[pc]];
+
- // Update incremental score
- st->psq += PSQT::psq[demotion][to] - PSQT::psq[pc][to];
-
+ // Update material
+ st->nonPawnMaterial[us] += PieceValue[MG][demotion] - PieceValue[MG][pc];
+ }
- // Update incremental scores
- st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
-
// Set capture piece
st->capturedPiece = captured;
+ st->unpromotedCapturedPiece = captured ? unpromotedCaptured : NO_PIECE;
+ if (captures_to_hand() && !captured)
+ st->capturedpromoted = false;
// Update the key with the final value
st->key = k;
}
put_piece(st->capturedPiece, capsq); // Restore the captured piece
+ if (captures_to_hand())
+ {
- remove_from_hand(~color_of(st->capturedPiece),
- !drop_loop() && st->capturedpromoted ? (st->unpromotedCapturedPiece ? type_of(st->unpromotedCapturedPiece)
- : PAWN)
- : type_of(st->capturedPiece));
++ remove_from_hand(!drop_loop() && st->capturedpromoted ? (st->unpromotedCapturedPiece ? ~st->unpromotedCapturedPiece
++ : make_piece(~color_of(st->capturedPiece), PAWN))
++ : ~st->capturedPiece);
+ if (!drop_loop() && st->capturedpromoted)
+ promotedPieces |= to;
+ }
+ if (st->unpromotedCapturedPiece)
+ unpromotedBoard[to] = st->unpromotedCapturedPiece;
}
}
int castlingRights;
int rule50;
int pliesFromNull;
+ int countingPly;
+ int countingLimit;
+ CheckCount checksGiven[COLOR_NB];
- Score psq;
Square epSquare;
// Not copied when making a move (will be recomputed anyhow)
Bitboard castlingPath[CASTLING_RIGHT_NB];
int gamePly;
Color sideToMove;
+ Score psq;
Thread* thisThread;
StateInfo* st;
+
+ // variant-specific
+ const Variant* var;
bool chess960;
+ int pieceCountInHand[COLOR_NB][PIECE_TYPE_NB];
+ Bitboard promotedPieces;
- void add_to_hand(Color c, PieceType pt);
- void remove_from_hand(Color c, PieceType pt);
++ void add_to_hand(Piece pc);
++ void remove_from_hand(Piece pc);
+ void drop_piece(Piece pc_hand, Piece pc_drop, Square s);
+ void undrop_piece(Piece pc_hand, Piece pc_drop, Square s);
};
+ namespace PSQT {
- extern Score psq[PIECE_NB][SQUARE_NB];
++ extern Score psq[PIECE_NB][SQUARE_NB + 1];
+ }
+
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
+inline const Variant* Position::variant() const {
+ assert(var != nullptr);
+ return var;
+}
+
+inline Rank Position::max_rank() const {
+ assert(var != nullptr);
+ return var->maxRank;
+}
+
+inline File Position::max_file() const {
+ assert(var != nullptr);
+ return var->maxFile;
+}
+
+inline Bitboard Position::board_bb() const {
+ assert(var != nullptr);
+ return board_size_bb(var->maxFile, var->maxRank);
+}
+
+inline const std::set<PieceType>& Position::piece_types() const {
+ assert(var != nullptr);
+ return var->pieceTypes;
+}
+
+inline const std::string Position::piece_to_char() const {
+ assert(var != nullptr);
+ return var->pieceToChar;
+}
+
+inline Rank Position::promotion_rank() const {
+ assert(var != nullptr);
+ return var->promotionRank;
+}
+
+inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
+ assert(var != nullptr);
+ return var->promotionPieceTypes;
+}
+
+inline bool Position::sittuyin_promotion() const {
+ assert(var != nullptr);
+ return var->sittuyinPromotion;
+}
+
+inline PieceType Position::promoted_piece_type(PieceType pt) const {
+ assert(var != nullptr);
+ return var->promotedPieceType[pt];
+}
+
+inline bool Position::mandatory_piece_promotion() const {
+ assert(var != nullptr);
+ return var->mandatoryPiecePromotion;
+}
+
+inline bool Position::piece_demotion() const {
+ assert(var != nullptr);
+ return var->pieceDemotion;
+}
+
+inline bool Position::endgame_eval() const {
+ assert(var != nullptr);
+ return var->endgameEval;
+}
+
+inline bool Position::double_step_enabled() const {
+ assert(var != nullptr);
+ return var->doubleStep;
+}
+
+inline bool Position::first_rank_double_steps() const {
+ assert(var != nullptr);
+ return var->firstRankDoubleSteps;
+}
+
+inline bool Position::castling_enabled() const {
+ assert(var != nullptr);
+ return var->castling;
+}
+
+inline bool Position::castling_dropped_piece() const {
+ assert(var != nullptr);
+ return var->castlingDroppedPiece;
+}
+
+inline File Position::castling_kingside_file() const {
+ assert(var != nullptr);
+ return var->castlingKingsideFile;
+}
+
+inline File Position::castling_queenside_file() const {
+ assert(var != nullptr);
+ return var->castlingQueensideFile;
+}
+
+inline bool Position::checking_permitted() const {
+ assert(var != nullptr);
+ return var->checking;
+}
+
+inline bool Position::must_capture() const {
+ assert(var != nullptr);
+ return var->mustCapture;
+}
+
+inline bool Position::must_drop() const {
+ assert(var != nullptr);
+ return var->mustDrop;
+}
+
+inline bool Position::piece_drops() const {
+ assert(var != nullptr);
+ return var->pieceDrops;
+}
+
+inline bool Position::drop_loop() const {
+ assert(var != nullptr);
+ return var->dropLoop;
+}
+
+inline bool Position::captures_to_hand() const {
+ assert(var != nullptr);
+ return var->capturesToHand;
+}
+
+inline bool Position::first_rank_drops() const {
+ assert(var != nullptr);
+ return var->firstRankDrops;
+}
+
+inline bool Position::drop_on_top() const {
+ assert(var != nullptr);
+ return var->dropOnTop;
+}
+
+inline Bitboard Position::drop_region(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
+}
+
+inline Bitboard Position::drop_region(Color c, PieceType pt) const {
+ Bitboard b = drop_region(c) & board_bb();
+
+ // Connect4-style drops
+ if (drop_on_top())
+ b &= shift<NORTH>(pieces()) | Rank1BB;
+ // Pawns on back ranks
+ if (pt == PAWN)
+ {
+ b &= ~promotion_zone_bb(c, promotion_rank(), max_rank());
+ if (!first_rank_drops())
+ b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
+ }
+ // Doubled shogi pawns
+ if (pt == SHOGI_PAWN && !shogi_doubled_pawn())
+ for (File f = FILE_A; f <= max_file(); ++f)
+ if (file_bb(f) & pieces(c, pt))
+ b &= ~file_bb(f);
+ // Sittuyin rook drops
+ if (pt == ROOK && sittuyin_rook_drop())
+ b &= rank_bb(relative_rank(c, RANK_1, max_rank()));
+
+ return b;
+}
+
+inline bool Position::sittuyin_rook_drop() const {
+ assert(var != nullptr);
+ return var->sittuyinRookDrop;
+}
+
+inline bool Position::drop_opposite_colored_bishop() const {
+ assert(var != nullptr);
+ return var->dropOppositeColoredBishop;
+}
+
+inline bool Position::drop_promoted() const {
+ assert(var != nullptr);
+ return var->dropPromoted;
+}
+
+inline bool Position::shogi_doubled_pawn() const {
+ assert(var != nullptr);
+ return var->shogiDoubledPawn;
+}
+
+inline bool Position::immobility_illegal() const {
+ assert(var != nullptr);
+ return var->immobilityIllegal;
+}
+
+inline int Position::n_move_rule() const {
+ assert(var != nullptr);
+ return var->nMoveRule;
+}
+
+inline int Position::n_fold_rule() const {
+ assert(var != nullptr);
+ return var->nFoldRule;
+}
+
+inline Value Position::stalemate_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->stalemateValue, ply);
+}
+
+inline Value Position::checkmate_value(int ply) const {
+ assert(var != nullptr);
+ // Check for illegal mate by shogi pawn drop
+ if ( var->shogiPawnDropMateIllegal
+ && !(checkers() & ~pieces(SHOGI_PAWN))
+ && !st->capturedPiece
+ && st->pliesFromNull > 0
+ && (st->materialKey != st->previous->materialKey))
+ {
+ return mate_in(ply);
+ }
+ // Check for shatar mate rule
+ if (var->shatarMateRule)
+ {
+ // Mate by knight is illegal
+ if (!(checkers() & ~pieces(KNIGHT)))
+ return mate_in(ply);
+
+ StateInfo* stp = st;
+ while (stp->checkersBB)
+ {
+ // Return mate score if there is at least one shak in series of checks
+ if (stp->shak)
+ return convert_mate_value(var->checkmateValue, ply);
+
+ if (stp->pliesFromNull < 2)
+ break;
+
+ stp = stp->previous->previous;
+ }
+ // Niol
+ return VALUE_DRAW;
+ }
+ // Return mate value
+ return convert_mate_value(var->checkmateValue, ply);
+}
+
+inline Value Position::bare_king_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->bareKingValue, ply);
+}
+
+inline Value Position::extinction_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->extinctionValue, ply);
+}
+
+inline bool Position::bare_king_move() const {
+ assert(var != nullptr);
+ return var->bareKingMove;
+}
+
+inline const std::set<PieceType>& Position::extinction_piece_types() const {
+ assert(var != nullptr);
+ return var->extinctionPieceTypes;
+}
+
+inline PieceType Position::capture_the_flag_piece() const {
+ assert(var != nullptr);
+ return var->flagPiece;
+}
+
+inline Bitboard Position::capture_the_flag(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteFlag : var->blackFlag;
+}
+
+inline bool Position::flag_move() const {
+ assert(var != nullptr);
+ return var->flagMove;
+}
+
+inline CheckCount Position::max_check_count() const {
+ assert(var != nullptr);
+ return var->maxCheckCount;
+}
+
+inline int Position::connect_n() const {
+ assert(var != nullptr);
+ return var->connectN;
+}
+
+inline CheckCount Position::checks_given(Color c) const {
+ return st->checksGiven[c];
+}
+
+inline CountingRule Position::counting_rule() const {
+ assert(var != nullptr);
+ return var->countingRule;
+}
+
+inline bool Position::is_immediate_game_end() const {
+ Value result;
+ return is_immediate_game_end(result);
+}
+
+inline bool Position::is_game_end(Value& result, int ply) const {
+ return is_immediate_game_end(result, ply) || is_optional_game_end(result, ply);
+}
+
inline Color Position::side_to_move() const {
return sideToMove;
}
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::add_to_hand(Piece pc) {
++ pieceCountInHand[color_of(pc)][type_of(pc)]++;
++ pieceCountInHand[color_of(pc)][ALL_PIECES]++;
++ psq += PSQT::psq[pc][SQ_NONE];
+}
+
- inline void Position::remove_from_hand(Color c, PieceType pt) {
- pieceCountInHand[c][pt]--;
- pieceCountInHand[c][ALL_PIECES]--;
++inline void Position::remove_from_hand(Piece pc) {
++ pieceCountInHand[color_of(pc)][type_of(pc)]--;
++ pieceCountInHand[color_of(pc)][ALL_PIECES]--;
++ psq -= PSQT::psq[pc][SQ_NONE];
+}
+
+inline void Position::drop_piece(Piece pc_hand, Piece pc_drop, Square s) {
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]);
+ put_piece(pc_drop, s);
- remove_from_hand(color_of(pc_hand), type_of(pc_hand));
++ remove_from_hand(pc_hand);
+}
+
+inline void Position::undrop_piece(Piece pc_hand, Piece pc_drop, Square s) {
+ remove_piece(pc_drop, s);
+ board[s] = NO_PIECE;
- add_to_hand(color_of(pc_hand), type_of(pc_hand));
++ add_to_hand(pc_hand);
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]);
+}
+
#endif // #ifndef POSITION_H_INCLUDED