Skip to content

Commit

Permalink
Support petrified (fairy-stockfish#713)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianfab authored Sep 15, 2023
1 parent cd412cc commit 1b5a789
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
parse_attribute("blastOnCapture", v->blastOnCapture);
parse_attribute("blastImmuneTypes", v->blastImmuneTypes, v->pieceToChar);
parse_attribute("mutuallyImmuneTypes", v->mutuallyImmuneTypes, v->pieceToChar);
parse_attribute("petrifyOnCapture", v->petrifyOnCapture);
parse_attribute("petrifyOnCaptureTypes", v->petrifyOnCaptureTypes, v->pieceToChar);
parse_attribute("petrifyBlastPieces", v->petrifyBlastPieces);
parse_attribute("doubleStep", v->doubleStep);
parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]);
Expand Down
31 changes: 19 additions & 12 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,9 @@ bool Position::legal(Move m) const {
occupied &= ~square_bb(capture_square(kto));
if (capture(m) && blast_on_capture())
occupied &= ~((attacks_bb<KING>(kto) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | kto);
// Petrifying a pseudo-royal piece is illegal
if (capture(m) && (var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && (st->pseudoRoyals & from))
return false;
Bitboard pseudoRoyals = st->pseudoRoyals & pieces(sideToMove);
Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~sideToMove);
if (is_ok(from) && (pseudoRoyals & from))
Expand All @@ -1145,14 +1148,18 @@ bool Position::legal(Move m) const {
// Self-explosions are illegal
if (pseudoRoyals & ~occupied)
return false;
// Petrifiable pseudo-royals can't capture
Bitboard attackerCandidatesTheirs = occupied & ~square_bb(kto);
for (PieceSet ps = var->petrifyOnCaptureTypes & extinction_piece_types(); ps;)
attackerCandidatesTheirs &= ~pieces(~us, pop_lsb(ps));
// Check for legality unless we capture a pseudo-royal piece
if (!(pseudoRoyalsTheirs & ~occupied))
while (pseudoRoyals)
{
Square sr = pop_lsb(pseudoRoyals);
// Touching pseudo-royal pieces are immune
if ( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
&& (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto))))
&& (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs))
return false;
}
// Look for duple check
Expand All @@ -1169,18 +1176,14 @@ bool Position::legal(Move m) const {
Square sr = pop_lsb(pseudoRoyalCandidates);
// Touching pseudo-royal pieces are immune
if (!( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
&& (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto)))))
&& (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs)))
allCheck = false;
}
if (allCheck)
return false;
}
}

// Petrifying the king is illegal
if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) == KING)
return false;

// mutuallyImmuneTypes (diplomacy in Atomar)-- In no-check Atomic, kings can be beside each other, but in Atomar, this prevents them from actually taking.
// Generalized to allow a custom set of pieces that can't capture a piece of the same type.
if (capture(m) &&
Expand Down Expand Up @@ -1405,7 +1408,7 @@ bool Position::gives_check(Move m) const {

// Is there a direct check?
if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION && type_of(m) != CASTLING
&& !(var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN))
&& !((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m)))
{
PieceType pt = type_of(moved_piece(m));
if (pt == JANGGI_CANNON)
Expand Down Expand Up @@ -1433,7 +1436,7 @@ bool Position::gives_check(Move m) const {
return true;

// Petrified piece can't give check
if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN)
if ((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m))
return false;

// Is there a check by special diagonal moves?
Expand Down Expand Up @@ -1946,7 +1949,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {


// Remove the blast pieces
if (captured && (blast_on_capture() || var->petrifyOnCapture))
if (captured && (blast_on_capture() || var->petrifyOnCaptureTypes))
{
std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch));
st->demotedBycatch = st->promotedBycatch = 0;
Expand All @@ -1956,7 +1959,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
blastImmune |= pieces(pt);
};
Bitboard blast = blast_on_capture() ? ((attacks_bb<KING>(to) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | to)
& (pieces() ^ blastImmune) : type_of(pc) != PAWN ? square_bb(to) : Bitboard(0);
& (pieces() ^ blastImmune) : var->petrifyOnCaptureTypes & type_of(pc) ? square_bb(to) : Bitboard(0);
while (blast)
{
Square bsq = pop_lsb(blast);
Expand Down Expand Up @@ -2018,7 +2021,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
}

// Make a wall square where the piece was
if (bsq == to ? var->petrifyOnCapture : var->petrifyBlastPieces)
if (bsq == to ? bool(var->petrifyOnCaptureTypes & type_of(bpc)) : var->petrifyBlastPieces)
{
st->wallSquares |= bsq;
byTypeBB[ALL_PIECES] |= bsq;
Expand Down Expand Up @@ -2116,7 +2119,7 @@ void Position::undo_move(Move m) {
byTypeBB[ALL_PIECES] ^= st->wallSquares ^ st->previous->wallSquares;

// Add the blast pieces
if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCapture))
if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCaptureTypes))
{
Bitboard blast = attacks_bb<KING>(to) | to;
while (blast)
Expand Down Expand Up @@ -2435,6 +2438,10 @@ bool Position::see_ge(Move m, Value threshold) const {
if (swap <= 0)
return true;

// Petrification ends SEE
if (var->petrifyOnCaptureTypes & type_of(moved_piece(m)) && capture(m))
return false;

Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()) ^ to;
Color stm = color_of(moved_piece(m));
Bitboard attackers = attackers_to(to, occupied);
Expand Down
17 changes: 17 additions & 0 deletions src/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ namespace {
v->extinctionPieceTypes = piece_set(ALL_PIECES);
return v;
}
// Petrified
// Sideways pawns + petrification on capture
// https://www.chess.com/variants/petrified
Variant* petrified_variant() {
Variant* v = pawnsideways_variant()->init();
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->extinctionPseudoRoyal = true;
v->petrifyOnCaptureTypes = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
return v;
}
// Atomic chess without checks (ICC rules)
// https://www.chessclub.com/help/atomic
Variant* nocheckatomic_variant() {
Expand Down Expand Up @@ -493,6 +507,7 @@ namespace {

#ifdef ALLVARS
// Duck chess
// https://duckchess.com/
Variant* duck_variant() {
Variant* v = chess_variant_base()->init();
v->remove_piece(KING);
Expand Down Expand Up @@ -1805,6 +1820,7 @@ void VariantMap::init() {
add("kinglet", kinglet_variant());
add("threekings", threekings_variant());
add("horde", horde_variant());
add("petrified", petrified_variant());
add("nocheckatomic", nocheckatomic_variant());
add("atomic", atomic_variant());
add("atomar", atomar_variant());
Expand Down Expand Up @@ -2013,6 +2029,7 @@ Variant* Variant::conclude() {
&& !makpongRule
&& !connectN
&& !blastOnCapture
&& !petrifyOnCaptureTypes
&& !capturesToHand
&& !twoBoards
&& !restrictedMobility
Expand Down
2 changes: 1 addition & 1 deletion src/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ struct Variant {
bool blastOnCapture = false;
PieceSet blastImmuneTypes = NO_PIECE_SET;
PieceSet mutuallyImmuneTypes = NO_PIECE_SET;
bool petrifyOnCapture = false;
PieceSet petrifyOnCaptureTypes = NO_PIECE_SET;
bool petrifyBlastPieces = false;
bool doubleStep = true;
Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB};
Expand Down
4 changes: 2 additions & 2 deletions src/variants.ini
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
# blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false)
# blastImmuneTypes: pieces completely immune to explosions (even at ground zero) [PieceSet] (default: none)
# mutuallyImmuneTypes: pieces that can't capture another piece of same types (e.g., kings (commoners) in atomar) [PieceSet] (default: none)
# petrifyOnCapture: non-pawn pieces are turned into wall squares when capturing [bool] (default: false)
# petrifyOnCaptureTypes: defined pieces are turned into wall squares when capturing [PieceSet] (default: -)
# petrifyBlastPieces: if petrify and blast combined, should pieces destroyed in the blast be petrified? [bool] (default: false)
# doubleStep: enable pawn double step [bool] (default: true)
# doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2)
Expand Down Expand Up @@ -1656,7 +1656,7 @@ startFen = lnsgkgsnl/1rci1uab1/p1p1p1p1p/9/9/9/P1P1P1P1P/1BAU1ICR1/LNSGKGSNL[-]
pawn = -
customPiece1 = p:fmWfceFifmnD
pawnTypes = p
petrifyOnCapture = true
petrifyOnCaptureTypes = pnbrq
enPassantRegion = -

#https://en.wikipedia.org/wiki/Nim
Expand Down

0 comments on commit 1b5a789

Please sign in to comment.