- [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Modern](https://en.wikipedia.org/wiki/Modern_Chess_(chess_variant)), [Chancellor](https://en.wikipedia.org/wiki/Chancellor_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess), [Gothic](https://www.chessvariants.com/large.dir/gothicchess.html), [Capablanca random chess](https://en.wikipedia.org/wiki/Capablanca_Random_Chess)
- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html), [Centaur](https://www.chessvariants.com/large.dir/contest/royalcourt.html)
- [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess)
-- [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html), Capablanca-Crazyhouse
+- [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Bughouse](https://en.wikipedia.org/wiki/Bughouse_chess), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html), Capablanca-Crazyhouse
- [Seirawan](https://en.wikipedia.org/wiki/Seirawan_chess), Seirawan-Crazyhouse
- [Amazon](https://en.wikipedia.org/wiki/Amazon_(chess)), [Chigorin](https://en.wikipedia.org/wiki/Chigorin_Chess), [Almost chess](https://en.wikipedia.org/wiki/Almost_Chess)
- [Hoppel-Poppel](http://www.chessvariants.com/diffmove.dir/hoppel-poppel.html), New Zealand
- [Three-check](https://en.wikipedia.org/wiki/Three-check_chess), Five-check
- [Los Alamos](https://en.wikipedia.org/wiki/Los_Alamos_chess)
- [Horde](https://en.wikipedia.org/wiki/Dunsany%27s_Chess#Horde_Chess)
+- [Knightmate](https://www.chessvariants.com/diffobjective.dir/knightmate.html)
### Shogi variants
- [Minishogi](https://en.wikipedia.org/wiki/Minishogi), [EuroShogi](https://en.wikipedia.org/wiki/EuroShogi), [Judkins shogi](https://en.wikipedia.org/wiki/Judkins_shogi)
+ 4 * (kingFlankAttack - kingFlankDefense)
+ 3 * kingFlankAttack * kingFlankAttack / 8
+ mg_value(mobility[Them] - mobility[Us])
- - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand() || pos.xiangqi_general()) / (1 + pos.check_counting())
+ - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand() || pos.king_type() == WAZIR) / (1 + pos.check_counting())
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
- 6 * mg_value(score) / 8
// Penalty if king flank is under attack, potentially moving toward the king
score -= FlankAttacks * kingFlankAttack * (1 + 5 * pos.captures_to_hand() + pos.check_counting());
- if (pos.check_counting() || pos.xiangqi_general())
+ if (pos.check_counting() || pos.king_type() == WAZIR)
score += make_score(0, mg_value(score) / 2);
// For drop games, king danger is independent of game phase
if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
{
Square ksq = pos.square<KING>(Us);
- Bitboard b = pos.attacks_from<KING>(ksq, Us) & target;
- if (pos.xiangqi_general())
- b &= PseudoAttacks[Us][WAZIR][ksq];
+ Bitboard b = ( (pos.attacks_from(Us, KING, ksq) & pos.pieces())
+ | (pos.moves_from(Us, KING, ksq) & ~pos.pieces())) & target;
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, ksq, pop_lsb(&b));
Bitboard b = pos.moves_from(us, pt, from) & ~pos.pieces();
- if (pt == KING)
- {
+ if (pt == KING && pos.king_type() == KING)
b &= ~PseudoAttacks[~us][QUEEN][pos.square<KING>(~us)];
- if (pos.xiangqi_general())
- b &= PseudoAttacks[us][WAZIR][from];
- }
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, us, from, pop_lsb(&b));
if (sliders & (pos.pieces(CANNON, BANNER) | pos.pieces(HORSE, ELEPHANT)))
{
Bitboard target = pos.board_bb() & ~pos.pieces(us);
- Bitboard b = pos.attacks_from<KING>(ksq, us) & target;
- if (pos.xiangqi_general())
- b &= PseudoAttacks[us][WAZIR][ksq];
+ Bitboard b = ( (pos.attacks_from(us, KING, ksq) & pos.pieces())
+ | (pos.moves_from(us, KING, ksq) & ~pos.pieces())) & target;
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
}
// Generate evasions for king, capture and non capture moves
- Bitboard b = pos.attacks_from<KING>(ksq, us) & ~pos.pieces(us) & ~sliderAttacks;
- if (pos.xiangqi_general())
- b &= PseudoAttacks[us][WAZIR][ksq];
+ Bitboard b = ( (pos.attacks_from(us, KING, ksq) & pos.pieces())
+ | (pos.moves_from(us, KING, ksq) & ~pos.pieces())) & ~pos.pieces(us) & ~sliderAttacks;
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
set(it->second, target);
}
+void VariantParser::parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar) {
+ const auto& it = config.find(key);
+ if (it != config.end())
+ {
+ char token;
+ size_t idx;
+ std::stringstream ss(it->second);
+ if (ss >> token && (idx = pieceToChar.find(toupper(token))) != std::string::npos)
+ target = PieceType(idx);
+ }
+}
+
Variant* VariantParser::parse() {
Variant* v = new Variant();
v->reset_pieces();
parse_attribute("castlingKingsideFile", v->castlingKingsideFile);
parse_attribute("castlingQueensideFile", v->castlingQueensideFile);
parse_attribute("castlingRank", v->castlingRank);
- // castling rook piece type
- const auto& it_castling_rook_piece = config.find("castlingRookPiece");
- if (it_castling_rook_piece != config.end())
- {
- char token;
- size_t idx;
- std::stringstream ss(it_castling_rook_piece->second);
- if (ss >> token && (idx = v->pieceToChar.find(toupper(token))) != std::string::npos)
- v->castlingRookPiece = PieceType(idx);
- }
+ parse_attribute("castlingRookPiece", v->castlingRookPiece, v->pieceToChar);
+ parse_attribute("kingType", v->kingType, v->pieceToChar);
parse_attribute("checking", v->checking);
parse_attribute("mustCapture", v->mustCapture);
parse_attribute("mustDrop", v->mustDrop);
parse_attribute("seirawanGating", v->seirawanGating);
parse_attribute("cambodianMoves", v->cambodianMoves);
parse_attribute("flyingGeneral", v->flyingGeneral);
- parse_attribute("xiangqiGeneral", v->xiangqiGeneral);
parse_attribute("xiangqiSoldier", v->xiangqiSoldier);
// game end
parse_attribute("nMoveRule", v->nMoveRule);
while (ss >> token && ((idx = v->pieceToChar.find(toupper(token))) != std::string::npos || token == '*'))
v->extinctionPieceTypes.insert(PieceType(token == '*' ? 0 : idx));
}
- // flag piece type
- const auto& it_flag_pt = config.find("flagPiece");
- if (it_flag_pt != config.end())
- {
- char token;
- size_t idx;
- std::stringstream ss(it_flag_pt->second);
- if (ss >> token && (idx = v->pieceToChar.find(toupper(token))) != std::string::npos)
- v->flagPiece = PieceType(idx);
- }
+ parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
parse_attribute("whiteFlag", v->whiteFlag);
parse_attribute("blackFlag", v->blackFlag);
parse_attribute("flagMove", v->flagMove);
private:
Config config;
template <class T> void parse_attribute(const std::string& key, T& target);
+ void parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar);
};
#endif // #ifndef PARSER_H_INCLUDED
\ No newline at end of file
for (PieceType pt : piece_types())
if (board_bb(c, pt) & s)
{
+ PieceType move_pt = pt == KING ? king_type() : pt;
// Consider asymmetrical move of horse
- if (pt == HORSE || pt == BANNER)
+ if (move_pt == HORSE || move_pt == BANNER)
{
- Bitboard horses = PseudoAttacks[~c][pt][s] & pieces(c, pt);
+ Bitboard horses = PseudoAttacks[~c][move_pt][s] & pieces(c, pt);
while (horses)
{
Square s2 = pop_lsb(&horses);
- if (attacks_bb(c, pt, s2, occupied) & s)
+ if (attacks_bb(c, move_pt, s2, occupied) & s)
b |= s2;
}
}
else
- b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt);
+ b |= attacks_bb(~c, move_pt, s, occupied) & pieces(c, pt);
}
// Consider special move of neang in cambodian chess
b |= pieces(c, FERS) & gates(c) & fers_sq;
}
- if (xiangqi_general())
- b ^= b & pieces(KING) & ~PseudoAttacks[~c][WAZIR][s];
-
if (unpromoted_soldier(c, s))
b ^= b & pieces(SOLDIER) & ~PseudoAttacks[~c][SHOGI_PAWN][s];
if (type_of(m) != NORMAL || is_gating(m))
return MoveList<LEGAL>(*this).contains(m);
- // Xiangqi general
- if (xiangqi_general() && type_of(pc) == KING && !(PseudoAttacks[us][WAZIR][from] & to))
- return false;
-
// Xiangqi soldier
if (type_of(pc) == SOLDIER && unpromoted_soldier(us, from) && file_of(from) != file_of(to))
return false;
File castling_queenside_file() const;
Rank castling_rank(Color c) const;
PieceType castling_rook_piece() const;
+ PieceType king_type() const;
bool checking_permitted() const;
bool must_capture() const;
bool must_drop() const;
bool gating() const;
bool seirawan_gating() const;
bool cambodian_moves() const;
- bool xiangqi_general() const;
bool unpromoted_soldier(Color c, Square s) const;
// winning conditions
int n_move_rule() const;
return var->castlingRookPiece;
}
+inline PieceType Position::king_type() const {
+ assert(var != nullptr);
+ return var->kingType;
+}
+
inline bool Position::checking_permitted() const {
assert(var != nullptr);
return var->checking;
return var->cambodianMoves;
}
-inline bool Position::xiangqi_general() const {
- assert(var != nullptr);
- return var->xiangqiGeneral;
-}
-
inline bool Position::unpromoted_soldier(Color c, Square s) const {
assert(var != nullptr);
return var->xiangqiSoldier && relative_rank(c, s, var->maxRank) <= RANK_5;
}
inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
- return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
+ return attacks_bb(c, pt == KING ? king_type() : pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
- return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
+ return moves_bb(c, pt == KING ? king_type() : pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
inline Bitboard Position::attackers_to(Square s) const {
v->checking = false;
return v;
}
+ Variant* knightmate_variant() {
+ Variant* v = fairy_variant_base();
+ v->add_piece(COMMONER, 'm');
+ v->remove_piece(KNIGHT);
+ v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
+ v->kingType = KNIGHT;
+ v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP};
+ return v;
+ }
Variant* losers_variant() {
Variant* v = fairy_variant_base();
v->checkmateValue = VALUE_MATE;
SQ_C7, SQ_D7, SQ_E7);
v->mobilityRegion[WHITE][KING] = white_castle;
v->mobilityRegion[BLACK][KING] = black_castle;
+ v->kingType = WAZIR;
v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
//v->nFoldValue = VALUE_MATE;
v->perpetualCheckIllegal = true;
v->flyingGeneral = true;
- v->xiangqiGeneral = true;
return v;
}
#ifdef LARGEBOARDS
add("newzealand", newzealand_variant());
add("kingofthehill", kingofthehill_variant());
add("racingkings", racingkings_variant());
+ add("knightmate", knightmate_variant());
add("losers", losers_variant());
add("giveaway", giveaway_variant());
add("antichess", antichess_variant());
File castlingQueensideFile = FILE_C;
Rank castlingRank = RANK_1;
PieceType castlingRookPiece = ROOK;
+ PieceType kingType = KING;
bool checking = true;
bool mustCapture = false;
bool mustDrop = false;
bool seirawanGating = false;
bool cambodianMoves = false;
bool flyingGeneral = false;
- bool xiangqiGeneral = false;
bool xiangqiSoldier = false;
// game end
int nMoveRule = 50;
# castlingQueensideFile: destination file of king after queenside castling [File] (default: c)
# castlingRank: relative rank of castling [Rank] (default: 1)
# castlingRookPiece: piece type that participates in castling [PieceType] (default: r)
+# kingType: piece type defining moves of king/royal piece (default: k)
# checking: allow checks [bool] (default: true)
# mustCapture: captures are mandatory (check evasion still takes precedence) [bool] (default: false)
# mustDrop: drops are mandatory (e.g., for Sittuyin setup phase) [bool] (default: false)
# seirawanGating: allow gating of pieces in hand like in S-Chess, requires "gating = true" [bool] (default: false)
# cambodianMoves: enable special moves of cambodian chess, requires "gating = true" [bool] (default: false)
# flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false)
-# xiangqiGeneral: restrict king to wazir-like movements [bool] (default: false)
# xiangqiSoldier: restrict soldier to shogi pawn movements on first five ranks [bool] (default: false)
# nMoveRule: move count for 50/n-move rule [int] (default: 50)
# nFoldRule: move count for 3/n-fold repetition rule [int] (default: 3)
expect perft.exp loop startpos 5 4888832 > /dev/null
expect perft.exp chessgi startpos 5 4889167 > /dev/null
expect perft.exp racingkings startpos 5 9472927 > /dev/null
+ expect perft.exp knightmate startpos 5 3249033 > /dev/null
expect perft.exp losers startpos 5 2723795 > /dev/null
expect perft.exp antichess startpos 5 2732672 > /dev/null
expect perft.exp giveaway startpos 5 2732672 > /dev/null