Allow castling with one non-rook piece type and add New Zealand chess (#52).
bench: 4470822
### Chess variants
- [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)
+- [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
- [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)
+- [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
- [Antichess](https://lichess.org/variant/antichess), [Giveaway](http://www.chessvariants.com/diffobjective.dir/giveaway.old.html), [Losers](https://www.chessclub.com/help/Wild17), [Codrus](http://www.binnewirtz.com/Schlagschach1.htm)
- [Extinction](https://en.wikipedia.org/wiki/Extinction_chess), [Kinglet](https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_Chess)
- [King of the Hill](https://en.wikipedia.org/wiki/King_of_the_Hill_(chess)), [Racing Kings](https://en.wikipedia.org/wiki/V._R._Parton#Racing_Kings)
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(token)) != std::string::npos)
+ v->castlingRookPiece = PieceType(idx);
+ }
parse_attribute("checking", v->checking);
parse_attribute("mustCapture", v->mustCapture);
parse_attribute("mustDrop", v->mustDrop);
p->sliderCapture = {};
return p;
}
+ PieceInfo* kniroo_piece() {
+ PieceInfo* p = rook_piece();
+ p->name = "kniroo";
+ PieceInfo* p2 = knight_piece();
+ p->merge(p2);
+ delete p2;
+ p->stepsCapture = {};
+ p->sliderQuiet = {};
+ return p;
+ }
+ PieceInfo* rookni_piece() {
+ PieceInfo* p = rook_piece();
+ p->name = "rookni";
+ PieceInfo* p2 = knight_piece();
+ p->merge(p2);
+ delete p2;
+ p->stepsQuiet = {};
+ p->sliderCapture = {};
+ return p;
+ }
PieceInfo* shogi_pawn_piece() {
PieceInfo* p = new PieceInfo();
p->name = "shogi_pawn";
add(AMAZON, amazon_piece());
add(KNIBIS, knibis_piece());
add(BISKNI, biskni_piece());
+ add(KNIROO, kniroo_piece());
+ add(ROOKNI, rookni_piece());
add(SHOGI_PAWN, shogi_pawn_piece());
add(LANCE, lance_piece());
add(SHOGI_KNIGHT, shogi_knight_piece());
{
Square rsq;
Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, ROOK);
+ Piece rook = make_piece(c, castling_rook_piece());
token = char(toupper(token));
for (Color c : {WHITE, BLACK})
if ((gates(c) & pieces(KING)) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand()))
{
- Bitboard castling_rooks = gates(c) & pieces(ROOK);
+ Bitboard castling_rooks = gates(c) & pieces(castling_rook_piece());
while (castling_rooks)
set_castling_right(c, pop_lsb(&castling_rooks));
}
Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(), castling_rank(sideToMove));
Square rto = kto + (rfrom > kfrom ? WEST : EAST);
- return (PseudoAttacks[sideToMove][ROOK][rto] & square<KING>(~sideToMove))
- && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
+ return (PseudoAttacks[sideToMove][type_of(piece_on(rfrom))][rto] & square<KING>(~sideToMove))
+ && (attacks_bb(sideToMove, type_of(piece_on(rfrom)), rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
assert(false);
if (type_of(m) == CASTLING)
{
assert(type_of(pc) != NO_PIECE_TYPE);
- assert(captured == make_piece(us, ROOK));
+ assert(captured == make_piece(us, castling_rook_piece()));
Square rfrom, rto;
do_castling<true>(us, from, to, rfrom, rto);
{
if (type_of(pc) == KING && file_of(to) == FILE_E)
{
- Bitboard castling_rooks = pieces(us, ROOK)
+ Bitboard castling_rooks = pieces(us, castling_rook_piece())
& rank_bb(castling_rank(us))
& (file_bb(FILE_A) | file_bb(max_file()));
while (castling_rooks)
set_castling_right(us, pop_lsb(&castling_rooks));
}
- else if (type_of(pc) == ROOK)
+ else if (type_of(pc) == castling_rook_piece())
{
if ( (file_of(to) == FILE_A || file_of(to) == max_file())
&& piece_on(make_square(FILE_E, castling_rank(us))) == make_piece(us, KING))
rto = to + (kingSide ? WEST : EAST);
// Remove both pieces first since squares could overlap in Chess960
- Piece castling_piece = piece_on(Do ? from : to);
- remove_piece(castling_piece, Do ? from : to);
- remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
+ Piece castlingKingPiece = piece_on(Do ? from : to);
+ Piece castlingRookPiece = piece_on(Do ? rfrom : rto);
+ remove_piece(castlingKingPiece, Do ? from : to);
+ remove_piece(castlingRookPiece, Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
- put_piece(castling_piece, Do ? to : from);
- put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
+ put_piece(castlingKingPiece, Do ? to : from);
+ put_piece(castlingRookPiece, Do ? rto : rfrom);
}
if (!can_castle(cr))
continue;
- if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
+ if ( piece_on(castlingRookSquare[cr]) != make_piece(c, castling_rook_piece())
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|| (count<KING>(c) && (castlingRightsMask[square<KING>(c)] & cr) != cr))
assert(0 && "pos_is_ok: Castling");
File castling_kingside_file() const;
File castling_queenside_file() const;
Rank castling_rank(Color c) const;
+ PieceType castling_rook_piece() const;
bool checking_permitted() const;
bool must_capture() const;
bool must_drop() const;
return relative_rank(c, var->castlingRank, max_rank());
}
+inline PieceType Position::castling_rook_piece() const {
+ assert(var != nullptr);
+ return var->castlingRookPiece;
+}
+
inline bool Position::checking_permitted() const {
assert(var != nullptr);
return var->checking;
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg,
FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
- ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg,
+ ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg,
CannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, WazirValueMg, CommonerValueMg, CentaurValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
- ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg,
+ ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg,
CannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, WazirValueEg, CommonerValueEg, CentaurValueEg }
AmazonValueMg = 3000, AmazonValueEg = 3000,
KnibisValueMg = 1100, KnibisValueEg = 1200,
BiskniValueMg = 750, BiskniValueEg = 700,
+ KnirooValueMg = 900, KnirooValueEg = 900,
+ RookniValueMg = 900, RookniValueEg = 900,
ShogiPawnValueMg = 90, ShogiPawnValueEg = 100,
LanceValueMg = 350, LanceValueEg = 250,
ShogiKnightValueMg = 350, ShogiKnightValueEg = 300,
enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN,
FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS,
- ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI,
+ ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, KNIROO, ROOKNI,
SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, SOLDIER, HORSE, ELEPHANT, WAZIR, COMMONER, CENTAUR, KING,
ALL_PIECES = 0,
v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS};
return v;
}
+ Variant* newzealand_variant() {
+ Variant* v = chess_variant();
+ v->remove_piece(ROOK);
+ v->remove_piece(KNIGHT);
+ v->add_piece(ROOKNI, 'r');
+ v->add_piece(KNIROO, 'n');
+ v->castlingRookPiece = ROOKNI;
+ v->promotionPieceTypes = {QUEEN, ROOKNI, BISHOP, KNIROO};
+ return v;
+ }
Variant* kingofthehill_variant() {
Variant* v = fairy_variant_base();
v->flagPiece = KING;
add("shatranj", shatranj_variant());
add("amazon", amazon_variant());
add("hoppelpoppel", hoppelpoppel_variant());
+ add("newzealand", newzealand_variant());
add("kingofthehill", kingofthehill_variant());
add("racingkings", racingkings_variant());
add("losers", losers_variant());
File castlingKingsideFile = FILE_G;
File castlingQueensideFile = FILE_C;
Rank castlingRank = RANK_1;
+ PieceType castlingRookPiece = ROOK;
bool checking = true;
bool mustCapture = false;
bool mustDrop = false;
# amazon (=queen+knight)
# knibis
# biskni
+# kniroo
+# rookni
# shogi_pawn
# lance
# shogi_knight
# castlingKingsideFile: destination file of king after kingside castling [File] (default: g)
# 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)
# 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)
expect perft.exp amazon startpos 5 9319911 > /dev/null
expect perft.exp makruk startpos 5 6223994 > /dev/null
expect perft.exp shatranj startpos 5 1164248 > /dev/null
+ expect perft.exp hoppelpoppel startpos 5 5056643 > /dev/null
+ expect perft.exp newzealand startpos 5 4987426 > /dev/null
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