// Connect-n
if (pos.connect_n() > 0)
{
- std::vector<Direction> connect_directions;
+ for (const Direction& d : pos.getConnectDirections())
- if (pos.connect_horizontal())
- {
- connect_directions.push_back(EAST);
- }
- if (pos.connect_vertical())
- {
- connect_directions.push_back(NORTH);
- }
- if (pos.connect_diagonal())
- {
- connect_directions.push_back(NORTH_EAST);
- connect_directions.push_back(SOUTH_EAST);
- }
- for (Direction d : connect_directions)
{
// Find sufficiently large gaps
Bitboard b = pos.board_bb() & ~pos.pieces(Them);
parse_attribute("mandatoryPiecePromotion", v->mandatoryPiecePromotion);
parse_attribute("pieceDemotion", v->pieceDemotion);
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("doubleStep", v->doubleStep);
parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]);
// Check for limitations
if (v->pieceDrops && (v->arrowGating || v->duckGating || v->staticGating || v->pastGating))
std::cerr << "pieceDrops and arrowGating/duckGating are incompatible." << std::endl;
+
// Options incompatible with royal kings
if (v->pieceTypes & KING)
{
std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl;
}
}
+ // Options incompatible with royal kings OR pseudo-royal kings. Possible in theory though:
+ // 1. In blast variants, moving a (pseudo-)royal blastImmuneType into another piece is legal.
+ // 2. In blast variants, capturing a piece next to a (pseudo-)royal blastImmuneType is legal.
+ // 3. Moving a (pseudo-)royal mutuallyImmuneType into a square threatened by the same type is legal.
+ if ((v->extinctionPseudoRoyal) || (v->pieceTypes & KING))
+ {
+ if (v->blastImmuneTypes)
+ std::cerr << "Can not use kings or pseudo-royal with blastImmuneTypes." << std::endl;
+ if (v->mutuallyImmuneTypes)
+ std::cerr << "Can not use kings or pseudo-royal with mutuallyImmuneTypes." << std::endl;
+ }
}
return v;
}
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) &&
+ (mutually_immune_types() & type_of(moved_piece(m))) &&
+ (type_of(moved_piece(m)) == type_of(piece_on(to)))
+ )
+ return false;
+
// En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after
// the move is made.
if (cambodian_moves() && type_of(pc) == ROOK && (square<KING>(them) & gates(them) & attacks_bb<ROOK>(to)))
st->gatesBB[them] ^= square<KING>(them);
+
// Remove the blast pieces
if (captured && (blast_on_capture() || var->petrifyOnCapture))
{
std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch));
st->demotedBycatch = st->promotedBycatch = 0;
- Bitboard blast = blast_on_capture() ? (attacks_bb<KING>(to) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | to
- : type_of(pc) != PAWN ? square_bb(to) : Bitboard(0);
+ Bitboard blastImmune = 0;
+ for (PieceSet ps = blast_immune_types(); ps;){
+ PieceType pt = pop_lsb(ps);
+ 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);
while (blast)
{
Square bsq = pop_lsb(blast);
if (connect_n() > 0)
{
Bitboard b;
- std::vector<Direction> connect_directions;
- if (connect_horizontal())
- {
- connect_directions.push_back(EAST);
- }
- if (connect_vertical())
- {
- connect_directions.push_back(NORTH);
- }
- if (connect_diagonal())
- {
- connect_directions.push_back(NORTH_EAST);
- connect_directions.push_back(SOUTH_EAST);
- }
- for (Direction d : connect_directions)
+ for (Direction d : var->connect_directions)
{
b = pieces(~sideToMove);
for (int i = 1; i < connect_n() && b; i++)
bool mandatory_piece_promotion() const;
bool piece_demotion() const;
bool blast_on_capture() const;
+ PieceSet blast_immune_types() const;
+ PieceSet mutually_immune_types() const;
bool endgame_eval() const;
Bitboard double_step_region(Color c) const;
Bitboard triple_step_region(Color c) const;
bool connect_horizontal() const;
bool connect_vertical() const;
bool connect_diagonal() const;
+ const std::vector<Direction>& getConnectDirections() const;
CheckCount checks_remaining(Color c) const;
MaterialCounting material_counting() const;
return var->blastOnCapture;
}
+inline PieceSet Position::blast_immune_types() const {
+ assert(var != nullptr);
+ return var->blastImmuneTypes;
+}
+
+inline PieceSet Position::mutually_immune_types() const {
+ assert(var != nullptr);
+ return var->mutuallyImmuneTypes;
+}
+
inline bool Position::endgame_eval() const {
assert(var != nullptr);
return var->endgameEval && !count_in_hand(ALL_PIECES) && count<KING>() == 2;
return var->connectDiagonal;
}
+inline const std::vector<Direction>& Position::getConnectDirections() const {
+ assert(var != nullptr);
+ return var->connect_directions;
+}
inline CheckCount Position::checks_remaining(Color c) const {
return st->checksRemaining[c];
}
inline Value Position::material_counting_result() const {
- auto weigth_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); };
+ auto weight_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); };
int materialCount;
Value result;
switch (var->materialCounting)
{
case JANGGI_MATERIAL:
- materialCount = weigth_count(ROOK, 13)
- + weigth_count(JANGGI_CANNON, 7)
- + weigth_count(HORSE, 5)
- + weigth_count(JANGGI_ELEPHANT, 3)
- + weigth_count(WAZIR, 3)
- + weigth_count(SOLDIER, 2)
+ materialCount = weight_count(ROOK, 13)
+ + weight_count(JANGGI_CANNON, 7)
+ + weight_count(HORSE, 5)
+ + weight_count(JANGGI_ELEPHANT, 3)
+ + weight_count(WAZIR, 3)
+ + weight_count(SOLDIER, 2)
- 1;
result = materialCount > 0 ? VALUE_MATE : -VALUE_MATE;
break;
return v;
}
+ // Atomar chess
+ // https://web.archive.org/web/20230519082613/https://chronatog.com/wp-content/uploads/2021/09/atomar-chess-rules.pdf
+ Variant* atomar_variant() {
+ Variant* v = nocheckatomic_variant()->init();
+ v->blastImmuneTypes = piece_set(COMMONER);
+ v->mutuallyImmuneTypes = piece_set(COMMONER);
+ return v;
+ }
+
#ifdef ALLVARS
// Duck chess
Variant* duck_variant() {
add("isolation7x7", isolation7x7_variant());
add("snailtrail", snailtrail_variant());
add("fox-and-hounds", fox_and_hounds_variant());
+ add("atomar", atomar_variant());
#ifdef ALLVARS
add("duck", duck_variant());
#endif
break;
}
+ connect_directions.clear();
+ if (connectHorizontal)
+ {
+ connect_directions.push_back(EAST);
+ }
+ if (connectVertical)
+ {
+ connect_directions.push_back(NORTH);
+ }
+ if (connectDiagonal)
+ {
+ connect_directions.push_back(NORTH_EAST);
+ connect_directions.push_back(SOUTH_EAST);
+ }
+
return this;
}
bool mandatoryPiecePromotion = false;
bool pieceDemotion = false;
bool blastOnCapture = false;
+ PieceSet blastImmuneTypes = NO_PIECE_SET;
+ PieceSet mutuallyImmuneTypes = NO_PIECE_SET;
bool petrifyOnCapture = false;
bool doubleStep = true;
Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB};
int nnueMaxPieces;
bool endgameEval = false;
bool shogiStylePromotions = false;
+ std::vector<Direction> connect_directions;
void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') {
// Avoid ambiguous definition by removing existing piece with same letter
# mandatoryPiecePromotion: piece promotion (and demotion if enabled) is mandatory [bool] (default: false)
# pieceDemotion: enable demotion of pieces (e.g., Kyoto shogi) [bool] (default: false)
# 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)
# doubleStep: enable pawn double step [bool] (default: true)
# doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2)