constexpr Score WeakLever = S( 0, 56);
constexpr Score WeakUnopposed = S(13, 27);
+ // Bonus for blocked pawns at 5th or 6th rank
- constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) };
++ constexpr Score BlockedPawn[RANK_NB - 5] = { S(-11, -4), S(-3, 4) };
+
constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)
};
e->passedPawns[Us] |= s;
// Score this pawn
- if (support | phalanx)
+ if ((support | phalanx) && (r < pos.promotion_rank() || !pos.mandatory_pawn_promotion()))
{
- int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2 * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1)
- int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
++ int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1)
+ 21 * popcount(support);
+ if (r >= RANK_4 && pos.count<PAWN>(Us) > popcount(pos.board_bb()) / 4)
+ v = popcount(support | phalanx) * 50 / (opposed ? 2 : 1);
score += make_score(v, v * (r - 2) / 4);
}
if (!support)
score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
+
+ if (blocked && r > RANK_4)
+ score += BlockedPawn[r-4];
}
+ // Double pawn evaluation if there are no non-pawn pieces
+ if (pos.count<ALL_PIECES>(Us) == pos.count<PAWN>(Us))
+ score = score * 2;
+
+ const Square* pl_shogi = pos.squares<SHOGI_PAWN>(Us);
+
+ ourPawns = pos.pieces(Us, SHOGI_PAWN);
+ theirPawns = pos.pieces(Them, SHOGI_PAWN);
+
+ // Loop through all shogi pawns of the current color and score each one
+ while ((s = *pl_shogi++) != SQ_NONE)
+ {
+ assert(pos.piece_on(s) == make_piece(Us, SHOGI_PAWN));
+
+ neighbours = ourPawns & adjacent_files_bb(s);
+
+ if (!neighbours)
+ score -= Isolated / 2;
+ }
+
return score;
}
// 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 no pawn capture is possible
++ // 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'));
+
- if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
- || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
++ // 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);
+ }
+
+ // counting rules
+ if (st->countingLimit && st->rule50)
+ {
+ st->countingPly = st->rule50;
+ st->rule50 = 0;
+ }
- chess960 = isChess960;
+ chess960 = isChess960 || v->chess960;
thisThread = th;
set_state(st);