Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2775,23 +2775,24 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
}
}
// capture the flag
// A flag win by the side to move is only possible if flagMove is enabled
// A flag win by the side to move is only possible if flagMove or flagPieceSafe are 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))
// In the case both colors reached it, it is a draw for flagPieceSafe or if white's king was first (special case for racing kings).
if ((flag_move() || var->flagPieceSafe) && flag_reached(sideToMove))
{
result = sideToMove == WHITE && flag_reached(BLACK) ? VALUE_DRAW : mate_in(ply);
result = ((flag_move() && sideToMove == WHITE && flag_piece(~sideToMove) == KING)
|| (var->flagPieceSafe && !flag_move())) && flag_reached(~sideToMove) ? VALUE_DRAW : mate_in(ply);
return true;
}
// 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
if ( (!flag_move() || var->flagPieceSafe || 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 (racing kings) to draw
if ( flag_move() && sideToMove == BLACK && !checkers() && count<KING>(sideToMove)
if ( flag_move() && flag_piece(sideToMove) == KING && sideToMove == BLACK && !checkers() && count<KING>(sideToMove)
&& (flag_region(sideToMove) & attacks_from(sideToMove, KING, square<KING>(sideToMove))))
{
assert(flag_piece(sideToMove) == KING);
Expand Down
30 changes: 8 additions & 22 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -1064,34 +1064,20 @@ inline bool Position::flag_reached(Color c) const {
(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())));

if (simpleResult&&var->flagPieceSafe)

// When flagPieceSafe and flagMove are combined, it means that only unsafe pieces cause an extra move
if (simpleResult && var->flagPieceSafe && (!flag_move() || c == ~sideToMove))
{
Bitboard piecesInFlagZone = flag_region(c) & pieces(c, flag_piece(c));
int potentialPieces = (popcount(piecesInFlagZone));
/*
There isn't a variant that uses it, but in the hypothetical game where the rules say I need 3
pieces in the flag zone and they need to be safe: If I have 3 pieces there, but one is under
threat, I don't think I can declare victory. If I have 4 there, but one is under threat, I
think that's victory.
*/
while (piecesInFlagZone)
int potentialPieces = popcount(piecesInFlagZone);
// If we are exactly at the required piece count, all pieces in the flag zone need to be safe
while (piecesInFlagZone && potentialPieces == var->flagPieceCount)
{
Square sr = pop_lsb(piecesInFlagZone);
Bitboard flagAttackers = attackers_to(sr, ~c);

if ((potentialPieces < var->flagPieceCount) || (potentialPieces >= var->flagPieceCount + 1)) break;
while (flagAttackers)
{
Square currentAttack = pop_lsb(flagAttackers);
if (legal(make_move(currentAttack, sr)))
{
potentialPieces--;
break;
}
}
if (flagAttackers)
return false;
}
return potentialPieces >= var->flagPieceCount;
}
return simpleResult;
}
Expand Down
9 changes: 7 additions & 2 deletions src/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,16 +883,19 @@ namespace {
v->add_piece(GOLD, 'h');
v->add_piece(FERS, 'e');
v->add_piece(WAZIR, 'g');
v->add_piece(KING, 'l');
v->add_piece(COMMONER, 'l');
v->startFen = "gle/1c1/1C1/ELG[-] w 0 1";
v->promotionRegion[WHITE] = Rank4BB;
v->promotionRegion[BLACK] = Rank1BB;
v->mandatoryPiecePromotion = true;
v->immobilityIllegal = false;
v->shogiPawnDropMateIllegal = false;
v->flagPiece[WHITE] = v->flagPiece[BLACK] = KING;
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->flagPiece[WHITE] = v->flagPiece[BLACK] = COMMONER;
v->flagRegion[WHITE] = Rank4BB;
v->flagRegion[BLACK] = Rank1BB;
v->flagPieceSafe = true;
v->dropNoDoubled = NO_PIECE_TYPE;
v->nFoldValue = VALUE_DRAW;
v->perpetualCheckIllegal = false;
Expand Down Expand Up @@ -1484,6 +1487,8 @@ namespace {
v->flagRegion[WHITE] = make_bitboard(SQ_E5);
v->flagRegion[BLACK] = make_bitboard(SQ_E5);
v->flagMove = true;
// we could remove the useless extra move when the flag piece can not be captured
// v->flagPieceSafe = true;
return v;
}
// Courier chess
Expand Down
1 change: 1 addition & 0 deletions src/variants.ini
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@
# 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)
# flagPieceSafe: the flag piece must be safe to win [bool] (default: false)
# when flagPieceSafe and flagMove are combined, it means that only unsafe flag pieces cause an extra move.
# 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)
# connectPieceTypes: pieces evaluated for connection rule [PieceSet] (default: *)
Expand Down
6 changes: 6 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,12 @@ def test_is_immediate_game_end(self):
self._check_immediate_game_end("ataxx", "PPPpppp/pppPPPp/pPPPPPP/PPPPPPp/ppPPPpp/pPPPPpP/pPPPPPP b - - 99 50", [], True, -sf.VALUE_MATE)
self._check_immediate_game_end("ataxx", "PPPpppp/pppPPPp/pPP*PPP/PP*P*Pp/ppP*Ppp/pPPPPpP/pPPPPPP b - - 99 50", [], True, -sf.VALUE_MATE)

# dobutsu flag rules
self._check_immediate_game_end("dobutsu", "1L1/1g1/1G1/1l1[] w - - 0 1", ["b2a2"], True, sf.VALUE_MATE)
self._check_immediate_game_end("dobutsu", "1L1/1g1/1G1/1l1[] w - - 0 1", ["b4a4"], True, -sf.VALUE_MATE)
self._check_immediate_game_end("dobutsu", "1L1/1g1/1G1/1l1[] w - - 0 1", ["b2b3"], True, sf.VALUE_DRAW)
self._check_immediate_game_end("dobutsu", "1L1/1g1/1G1/1l1[] w - - 0 1", ["b4b3"], False)

def _check_optional_game_end(self, variant, fen, moves, game_end, game_result=None):
with self.subTest(variant=variant, fen=fen, game_end=game_end, game_result=game_result):
result = sf.is_optional_game_end(variant, fen, moves)
Expand Down
2 changes: 2 additions & 0 deletions tests/perft.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ if [[ $1 == "all" || $1 == "variant" ]]; then
expect perft.exp losers startpos 4 152955 > /dev/null
expect perft.exp kinglet startpos 4 197742 > /dev/null
expect perft.exp threekings startpos 4 199514 > /dev/null
expect perft.exp dobutsu startpos 6 71677 > /dev/null
expect perft.exp dobutsu "fen g1e/1cL/lC1/E1G[] w - - 4 3" 4 2290 > /dev/null

# pockets
expect perft.exp crazyhouse startpos 4 197281 > /dev/null
Expand Down
Loading