if ( pos.captures_to_hand()
|| pos.count_in_hand(c, ALL_PIECES)
|| (pos.extinction_value() != VALUE_NONE && !pos.extinction_pseudo_royal())
- || (pos.capture_the_flag_piece() && pos.count(c, pos.capture_the_flag_piece())))
+ || (pos.flag_region(c) && pos.count(c, pos.flag_piece(c))))
return false;
// Restricted pieces
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0);
- if (pos.capture_the_flag(Us))
- score += make_score(200, 200) * popcount(behind & safe & pos.capture_the_flag(Us));
+ if (pos.flag_region(Us))
+ score += make_score(200, 200) * popcount(behind & safe & pos.flag_region(Us));
if constexpr (T)
Trace::add(SPACE, Us, score);
Score score = SCORE_ZERO;
// Capture the flag
- if (pos.capture_the_flag(Us))
+ if (pos.flag_region(Us))
{
- PieceType ptCtf = pos.capture_the_flag_piece();
- Bitboard ctfPieces = pos.pieces(Us, ptCtf);
- Bitboard ctfTargets = pos.capture_the_flag(Us) & pos.board_bb();
+ Bitboard ctfPieces = pos.pieces(Us, pos.flag_piece(Us));
+ Bitboard ctfTargets = pos.flag_region(Us) & pos.board_bb();
Bitboard onHold = 0;
Bitboard onHold2 = 0;
Bitboard processed = 0;
// Traverse all paths of the CTF pieces to the CTF targets.
// Put squares that are attacked or occupied on hold for one iteration.
// This reflects that likely a move will be needed to block or capture the attack.
+ // If all piece types are eligible, use the king path as a proxy for distance.
+ PieceType ptCtf = pos.flag_piece(Us) == ALL_PIECES ? KING : pos.flag_piece(Us);
for (int dist = 0; (ctfPieces || onHold || onHold2) && (ctfTargets & ~processed); dist++)
{
int wins = popcount(ctfTargets & ctfPieces);
// Orient a square according to perspective (rotates by 180 for black)
// Missing kings map to index 0 (SQ_A1)
inline Square HalfKAv2Variants::orient(Color perspective, Square s, const Position& pos) {
- return s != SQ_NONE ? to_variant_square( perspective == WHITE || (pos.capture_the_flag(BLACK) & Rank8BB) ? s
+ return s != SQ_NONE ? to_variant_square( perspective == WHITE || (pos.flag_region(BLACK) & Rank8BB) ? s
: flip_rank(s, pos.max_rank()), pos) : SQ_A1;
}
parse_attribute("extinctionPieceTypes", v->extinctionPieceTypes, v->pieceToChar);
parse_attribute("extinctionPieceCount", v->extinctionPieceCount);
parse_attribute("extinctionOpponentPieceCount", v->extinctionOpponentPieceCount);
- parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
+ parse_attribute("flagPiece", v->flagPiece[WHITE], v->pieceToChar);
+ parse_attribute("flagPiece", v->flagPiece[BLACK], v->pieceToChar);
+ parse_attribute("flagPieceWhite", v->flagPiece[WHITE], v->pieceToChar);
+ parse_attribute("flagPieceBlack", v->flagPiece[BLACK], v->pieceToChar);
+ parse_attribute("flagRegion", v->flagRegion[WHITE]);
+ parse_attribute("flagRegion", v->flagRegion[BLACK]);
parse_attribute("flagRegionWhite", v->flagRegion[WHITE]);
parse_attribute("flagRegionBlack", v->flagRegion[BLACK]);
parse_attribute("flagPieceCount", v->flagPieceCount);
}
}
// capture the flag
- if ( capture_the_flag_piece()
- && flag_move()
- && (
- (popcount(capture_the_flag(sideToMove) & pieces(sideToMove, capture_the_flag_piece()))>=flag_piece_count()) // opponent has >= number of pieces needed to win
- || //-or-
- (
- (flag_piece_blocked_win()) //flagPieceBlockedWin variant option true
- && //-and-
- (capture_the_flag(sideToMove) & pieces(sideToMove, capture_the_flag_piece())) //at least one piece in flag zone
- && //-and-
- !(capture_the_flag(sideToMove) & ~pieces()) //no empty squares in flag zone
- )
- )
- )
- {
- result =
- (
- (
- (popcount(capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece()))>=flag_piece_count()) // you have >= number of pieces needed to win
- || //-or-
- (
- (flag_piece_blocked_win()) //flagPieceBlockedWin variant option true
- && //-and-
- (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece())) //at least one piece in flag zone
- && //-and-
- !(capture_the_flag(~sideToMove) & ~pieces()) //no empty squares in flag zone
- )
- )
- &&
- (sideToMove == WHITE) //opponent is white
- )
- ? VALUE_DRAW : mate_in(ply); //then it's a draw, otherwise, win
+ // A flag win by the side to move is only possible if flagMove is enabled
+ // and they already reached the flag region the move before.
+ // In the case both colors reached it, it is a draw if white was first.
+ if (flag_move() && flag_reached(sideToMove))
+ {
+ result = sideToMove == WHITE && flag_reached(BLACK) ? VALUE_DRAW : mate_in(ply);
return true;
}
- if ( capture_the_flag_piece()
- && (!flag_move() || capture_the_flag_piece() == KING) //if black doesn't get an extra move to draw, or flag piece is king,
- && ( //-and-
- (popcount(capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece()))>=flag_piece_count()) // you have >= number of pieces needed to win
- || //-or-
- (
- (flag_piece_blocked_win()) //flagPieceBlockedWin variant option true
- && //-and-
- (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece())) //at least one piece in flag zone
- && //-and-
- !(capture_the_flag(~sideToMove) & ~pieces()) //no empty squares in flag zone
- )
- )
- )
+ // A direct flag win is possible if the opponent does not get an extra flag move
+ // or we can detect early for kings that they won't be able to reach the flag region
+ // Note: This condition has to be after the above, since both might be true e.g. in racing kings.
+ if ( (!flag_move() || flag_piece(sideToMove) == KING) // we can do early win detection only for the king
+ && flag_reached(~sideToMove))
{
bool gameEnd = true;
- // Check whether king can move to CTF zone
+ // Check whether king can move to CTF zone (racing kings) to draw
if ( flag_move() && sideToMove == BLACK && !checkers() && count<KING>(sideToMove)
- && (capture_the_flag(sideToMove) & attacks_from(sideToMove, KING, square<KING>(sideToMove))))
+ && (flag_region(sideToMove) & attacks_from(sideToMove, KING, square<KING>(sideToMove))))
{
- assert(capture_the_flag_piece() == KING);
- gameEnd = true;
+ assert(flag_piece(sideToMove) == KING);
for (const auto& m : MoveList<NON_EVASIONS>(*this))
- if (type_of(moved_piece(m)) == KING && (capture_the_flag(sideToMove) & to_sq(m)) && legal(m))
+ if (type_of(moved_piece(m)) == KING && (flag_region(sideToMove) & to_sq(m)) && legal(m))
{
gameEnd = false;
break;
int extinction_piece_count() const;
int extinction_opponent_piece_count() const;
bool extinction_pseudo_royal() const;
- PieceType capture_the_flag_piece() const;
- Bitboard capture_the_flag(Color c) const;
+ PieceType flag_piece(Color c) const;
+ Bitboard flag_region(Color c) const;
bool flag_move() const;
+ bool flag_reached(Color c) const;
bool check_counting() const;
- int flag_piece_count() const;
- bool flag_piece_blocked_win() const;
int connect_n() const;
CheckCount checks_remaining(Color c) const;
MaterialCounting material_counting() const;
return var->extinctionPseudoRoyal;
}
-inline PieceType Position::capture_the_flag_piece() const {
+inline PieceType Position::flag_piece(Color c) const {
assert(var != nullptr);
- return var->flagPiece;
+ return var->flagPiece[c];
}
-inline Bitboard Position::capture_the_flag(Color c) const {
+inline Bitboard Position::flag_region(Color c) const {
assert(var != nullptr);
return var->flagRegion[c];
}
return var->flagMove;
}
-inline int Position::flag_piece_count() const {
+inline bool Position::flag_reached(Color c) const {
assert(var != nullptr);
- return var->flagPieceCount;
-}
-
-inline bool Position::flag_piece_blocked_win() const {
- assert(var != nullptr);
- return var->flagPieceBlockedWin;
+ return (flag_region(c) & pieces(c, flag_piece(c)))
+ && ( popcount(flag_region(c) & pieces(c, flag_piece(c))) >= var->flagPieceCount
+ || (var->flagPieceBlockedWin && !(flag_region(c) & ~pieces())));
}
inline bool Position::check_counting() const {
}
}
- probCutBeta = beta + (209 + 20 * !!pos.capture_the_flag_piece() + 50 * pos.captures_to_hand()) * (1 + pos.check_counting() + pos.extinction_single_piece()) - 44 * improving;
+ probCutBeta = beta + (209 + 20 * !!pos.flag_region(~pos.side_to_move()) + 50 * pos.captures_to_hand()) * (1 + pos.check_counting() + pos.extinction_single_piece()) - 44 * improving;
// Step 9. ProbCut (~4 Elo)
// If we have a good enough capture and a reduced search returns a value
continue;
// Prune moves with negative SEE (~20 Elo)
- if (!pos.variant()->duckGating && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.capture_the_flag_piece()) * lmrDepth * lmrDepth)))
+ if (!pos.variant()->duckGating && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.flag_region(pos.side_to_move())) * lmrDepth * lmrDepth)))
continue;
}
}
// https://lichess.org/variant/kingOfTheHill
Variant* kingofthehill_variant() {
Variant* v = chess_variant_base()->init();
- v->flagPiece = KING;
+ v->flagPiece[WHITE] = v->flagPiece[BLACK] = KING;
v->flagRegion[WHITE] = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
v->flagRegion[BLACK] = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
v->flagMove = false;
Variant* racingkings_variant() {
Variant* v = chess_variant_base()->init();
v->startFen = "8/8/8/8/8/8/krbnNBRK/qrbnNBRQ w - - 0 1";
- v->flagPiece = KING;
+ v->flagPiece[WHITE] = v->flagPiece[BLACK] = KING;
v->flagRegion[WHITE] = Rank8BB;
v->flagRegion[BLACK] = Rank8BB;
v->flagMove = true;
v->add_piece(CUSTOM_PIECE_2, 'f', "mF"); //Fox
v->startFen = "1h1h1h1h/8/8/8/8/8/8/4F3 w - - 0 1";
v->stalemateValue = -VALUE_MATE;
- v->flagPiece = CUSTOM_PIECE_2;
+ v->flagPiece[WHITE] = CUSTOM_PIECE_2;
v->flagRegion[WHITE] = Rank8BB;
return v;
}
v->mandatoryPiecePromotion = true;
v->immobilityIllegal = false;
v->shogiPawnDropMateIllegal = false;
- v->flagPiece = KING;
+ v->flagPiece[WHITE] = v->flagPiece[BLACK] = KING;
v->flagRegion[WHITE] = Rank4BB;
v->flagRegion[BLACK] = Rank1BB;
v->dropNoDoubled = NO_PIECE_TYPE;
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
- v->flagPiece = BREAKTHROUGH_PIECE;
+ v->flagPiece[WHITE] = v->flagPiece[BLACK] = BREAKTHROUGH_PIECE;
v->flagRegion[WHITE] = Rank8BB;
v->flagRegion[BLACK] = Rank1BB;
return v;
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
- v->flagPiece = KNIGHT;
+ v->flagPiece[WHITE] = v->flagPiece[BLACK] = KNIGHT;
v->flagRegion[WHITE] = make_bitboard(SQ_E5);
v->flagRegion[BLACK] = make_bitboard(SQ_E5);
v->flagMove = true;
PieceSet extinctionPieceTypes = NO_PIECE_SET;
int extinctionPieceCount = 0;
int extinctionOpponentPieceCount = 0;
- PieceType flagPiece = NO_PIECE_TYPE;
+ PieceType flagPiece[COLOR_NB] = {ALL_PIECES, ALL_PIECES};
Bitboard flagRegion[COLOR_NB] = {};
int flagPieceCount = 1;
bool flagPieceBlockedWin = false;
&& checkmateValue == -VALUE_MATE
&& stalemateValue == VALUE_DRAW
&& !materialCounting
- && !flagPiece
+ && !(flagRegion[WHITE] || flagRegion[BLACK])
&& !mustCapture
&& !checkCounting
&& !makpongRule
# extinctionPieceTypes: list of piece types for extinction rules, e.g., pnbrq (* means all) (default: )
# extinctionPieceCount: piece count at which the game is decided by extinction rule (default: 0)
# extinctionOpponentPieceCount: opponent piece count required to adjudicate by extinction rule (default: 0)
-# flagPiece: piece type for capture the flag win rule [PieceType] (default: -)
+# flagPiece: piece type for capture the flag win rule [PieceType] (default: *)
+# flagPieceWhite: piece type for capture the flag win rule [PieceType] (default: *)
+# flagPieceBlack: piece type for capture the flag win rule [PieceType] (default: *)
+# flagRegion: target region for capture the flag win rule [Bitboard] (default: )
# flagRegionWhite: white's target region for capture the flag win rule [Bitboard] (default: )
# flagRegionBlack: black's target region for capture the flag win rule [Bitboard] (default: )
-# flagMove: black gets one more move after white captures the flag [bool] (default: false)
-# checkCounting: enable check count win rule (check count is communicated via FEN, see 3check) [bool] (default: false)
# flagPieceCount: number of flag pieces that have to be in the flag zone [int] (default: 1)
-# flagPieceBlockedWin: for flagPieceCount > 1. if at least one piece in flag zone and all others occupied by opponent pieces, win. [bool] (default: false)
+# flagPieceBlockedWin: for flagPieceCount > 1, win if at least one flag piece in flag zone and all others occupied by pieces [bool] (default: false)
+# flagMove: the other side gets one more move after one reaches the flag zone [bool] (default: false)
+# checkCounting: enable check count win rule (check count is communicated via FEN, see 3check) [bool] (default: false)
# connectN: number of aligned pieces for win [int] (default: 0)
# materialCounting: enable material counting rules [MaterialCounting] (default: none)
# countingRule: enable counting rules [CountingRule] (default: none)
[atomic-giveaway-hill:giveaway]
blastOnCapture = true
flagPiece = k
-flagRegionWhite = d4 e4 d5 e5
-flagRegionBlack = d4 e4 d5 e5
+flagRegion = d4 e4 d5 e5
# Crazyhouse with mandatory captures, using crazyhouse as a template
[coffeehouse:crazyhouse]
expect perft.exp janggi "fen 1n1kaabn1/cr2N4/5C1c1/p1pNp3p/9/9/P1PbP1P1P/3r1p3/4A4/R1BA1KB1R b - - 0 1" 4 76763 > /dev/null
expect perft.exp janggi "fen 1Pbcka3/3nNn1c1/N2CaC3/1pB6/9/9/5P3/9/4K4/9 w - - 0 23" 4 151202 > /dev/null
expect perft.exp jesonmor startpos 3 27960 > /dev/null
+ expect perft.exp jesonmor "fen nn1nnn1nn/9/3n1n3/9/9/9/3N1N3/9/NN1NNN1NN w - - 4 3" 3 37564 > /dev/null
# non-chess
expect perft.exp flipello10 startpos 7 55180 > /dev/null