S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171),
S(112,178), S(114,185), S(114,187), S(119,221) }
};
+ constexpr Score MaxMobility = S(150, 200);
+ constexpr Score DropMobility = S(10, 10);
+ // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
+ // squares of the same color as our bishop.
+ constexpr Score BishopPawns[int(FILE_NB) / 2] = {
+ S(3, 8), S(3, 9), S(1, 8), S(3, 7)
+ };
+
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
else
- sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
+ sf = std::min(sf, 36 + 7 * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide))) - 4 * !pawnsOnBothFlanks;
-
+
sf -= 4 * !pawnsOnBothFlanks;
}
// 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, castling_rook_piece());
- 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, castling_rank(c)); 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, castling_rank(c)); 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'), castling_rank(c));
- else
- continue;
+ else
+ continue;
- set_castling_right(c, rsq);
+ // Set gates (and skip castling rights)
+ if (gating())
+ {
+ st->gatesBB[c] |= rsq;
+ if (token == 'K' || token == 'Q')
+ st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, castling_rank(c));
+ // Do not set castling rights for gates unless there are no pieces in hand,
+ // which means that the file is referring to a chess960 castling right.
+ else if (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand())
+ continue;
+ }
+
+ if (castling_enabled())
+ set_castling_right(c, rsq);
+ }
+
+ // Set castling rights for 960 gating variants
+ if (gating() && castling_enabled())
+ for (Color c : {WHITE, BLACK})
+ if ((gates(c) & pieces(KING)) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand()))
+ {
+ Bitboard castling_rooks = gates(c) & pieces(castling_rook_piece());
+ while (castling_rooks)
+ set_castling_right(c, pop_lsb(&castling_rooks));
+ }
+
+ // counting limit
+ if (counting_rule() && isdigit(ss.peek()))
+ ss >> st->countingLimit;
+
+ // 4. En passant square.
+ // Ignore if square is invalid or not on side to move relative rank 6.
+ 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'));
+
+ // En passant square will be considered only if
+ // a) side to move have a pawn threatening epSquare
+ // b) there is an enemy pawn in front of epSquare
+ // c) there is no piece on epSquare or behind epSquare
+ bool enpassant;
+ enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
+ && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
+ if (!enpassant)
+ st->epSquare = SQ_NONE;
+ }
}
- // 4. En passant square.
- // Ignore if square is invalid or not on side to move relative rank 6.
- bool enpassant = false;
+ // Check counter for nCheck
+ ss >> std::skipws >> token >> std::noskipws;
- if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
+ if (check_counting())
{
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
-
- // En passant square will be considered only if
- // a) side to move have a pawn threatening epSquare
- // b) there is an enemy pawn in front of epSquare
- // c) there is no piece on epSquare or behind epSquare
- enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
- && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
- && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
+ if (ss.peek() == '+')
+ {
+ st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
+ ss >> token >> token;
+ st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
+ }
+ else
+ {
+ // If check count is not provided, assume that the next check wins
+ st->checksRemaining[WHITE] = CheckCount(1);
+ st->checksRemaining[BLACK] = CheckCount(1);
+ ss.putback(token);
+ }
}
-
- if (!enpassant)
- st->epSquare = SQ_NONE;
+ else
+ ss.putback(token);
// 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> gamePly;
+ if (sfen)
+ {
+ // Pieces in hand for SFEN
+ int handCount = 1;
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == '-')
+ continue;
+ else if (isdigit(token))
+ {
+ handCount = token - '0';
+ while (isdigit(ss.peek()) && ss >> token)
+ handCount = 10 * handCount + (token - '0');
+ }
+ else if ((idx = piece_to_char().find(token)) != string::npos)
+ {
+ for (int i = 0; i < handCount; i++)
+ add_to_hand(Piece(idx));
+ handCount = 1;
+ }
+ }
+ // 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);
+ }
- chess960 = isChess960;
+ // counting rules
+ if (st->countingLimit && st->rule50)
+ {
+ st->countingPly = st->rule50;
+ st->rule50 = 0;
+ }
+
+ chess960 = isChess960 || v->chess960;
thisThread = th;
set_state(st);
+ st->accumulator.state[WHITE] = Eval::NNUE::INIT;
+ st->accumulator.state[BLACK] = Eval::NNUE::INIT;
assert(pos_is_ok());
++gamePly;
++st->rule50;
++st->pliesFromNull;
+ if (st->countingLimit)
+ ++st->countingPly;
// Used by NNUE
- st->accumulator.computed_accumulation = false;
+ st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
+ st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
auto& dp = st->dirtyPiece;
dp.dirty_num = 1;