### Regional and historical games
- [Shatranj](https://en.wikipedia.org/wiki/Shatranj), [Courier](https://en.wikipedia.org/wiki/Courier_chess)
-- [Makruk](https://en.wikipedia.org/wiki/Makruk), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Ai-Wok
+- [Makruk](https://en.wikipedia.org/wiki/Makruk), [Ouk Chatrang](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess), [Kar Ouk](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Ai-Wok
- [Sittuyin](https://en.wikipedia.org/wiki/Sittuyin)
- [Shatar](https://en.wikipedia.org/wiki/Shatar), [Jeson Mor](https://en.wikipedia.org/wiki/Jeson_Mor)
- [Shogi](https://en.wikipedia.org/wiki/Shogi)
score += pieces<WHITE>(pt) - pieces<BLACK>(pt);
// Evaluate pieces in hand once attack tables are complete
- if (pos.piece_drops() || pos.gating())
+ if (pos.piece_drops() || pos.seirawan_gating())
for (PieceType pt = PAWN; pt < KING; ++pt)
score += hand<WHITE>(pt) - hand<BLACK>(pt);
*moveList++ = make<T>(from, to);
// Gating moves
- if (pos.gating() && (pos.gates(us) & from))
+ if (pos.seirawan_gating() && (pos.gates(us) & from))
for (PieceType pt_gating = PAWN; pt_gating <= KING; ++pt_gating)
if (pos.count_in_hand(us, pt_gating) && (pos.drop_region(us, pt_gating) & from))
*moveList++ = make_gating<T>(from, to, pt_gating, from);
- if (pos.gating() && T == CASTLING && (pos.gates(us) & to))
+ if (pos.seirawan_gating() && T == CASTLING && (pos.gates(us) & to))
for (PieceType pt_gating = PAWN; pt_gating <= KING; ++pt_gating)
if (pos.count_in_hand(us, pt_gating) && (pos.drop_region(us, pt_gating) & to))
*moveList++ = make_gating<T>(from, to, pt_gating, to);
moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, from, pos.castling_rook_square(OOO));
}
+ // Special moves
+ if (pos.cambodian_moves() && pos.gates(Us))
+ {
+ if (Type != CAPTURES && Type != EVASIONS && (pos.pieces(Us, KING) & pos.gates(Us)))
+ {
+ Square from = pos.square<KING>(Us);
+ Bitboard b = PseudoAttacks[WHITE][KNIGHT][from] & rank_bb(rank_of(from + (Us == WHITE ? NORTH : SOUTH)))
+ & target & ~pos.pieces();
+ while (b)
+ moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, pop_lsb(&b));
+ }
+
+ Bitboard b = pos.pieces(Us, FERS) & pos.gates(Us);
+ while (b)
+ {
+ Square from = pop_lsb(&b);
+ Square to = from + 2 * (Us == WHITE ? NORTH : SOUTH);
+ if (is_ok(to) && (target & to))
+ moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, to);
+ }
+ }
+
return moveList;
}
parse_attribute("shogiDoubledPawn", v->shogiDoubledPawn);
parse_attribute("immobilityIllegal", v->immobilityIllegal);
parse_attribute("gating", v->gating);
+ parse_attribute("seirawanGating", v->seirawanGating);
+ parse_attribute("cambodianMoves", v->cambodianMoves);
// game end
parse_attribute("nMoveRule", v->nMoveRule);
parse_attribute("nFoldRule", v->nFoldRule);
st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, relative_rank(c, castling_rank(), max_rank()));
// Do not set castling rights for gates unless there are no pieces in hand,
// which means that the file is referring to a chess960 castling right.
- else if (count_in_hand(c, ALL_PIECES) || captures_to_hand())
+ else if (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand())
continue;
}
// Set castling rights for 960 gating variants
if (gating() && castling_enabled())
for (Color c : {WHITE, BLACK})
- if ((gates(c) & pieces(KING)) && !castling_rights(c) && (count_in_hand(c, ALL_PIECES) || captures_to_hand()))
+ 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);
while (castling_rooks)
// Check counter for nCheck
ss >> std::skipws >> token >> std::noskipws;
- if (check_counting() && ss.peek() == '+')
+ if (check_counting())
{
- st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
- ss >> token >> token;
- st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
+ if (ss.peek() == '+')
+ {
+ st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
+ ss >> token >> token;
+ st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
+ }
+ else
+ {
+ // If check count is not provided, assume that the next check wins
+ st->checksRemaining[WHITE] = CheckCount(1);
+ st->checksRemaining[BLACK] = CheckCount(1);
+ ss.putback(token);
+ }
}
else
ss.putback(token);
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt];
- if (piece_drops() || gating())
+ if (piece_drops() || seirawan_gating())
si->key ^= Zobrist::inHand[pc][pieceCountInHand[c][pt]];
}
}
// pieces in hand
- if (piece_drops() || gating())
+ if (piece_drops() || seirawan_gating())
{
ss << '[';
for (Color c : {WHITE, BLACK})
if (can_castle(WHITE_OOO))
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
- if (gating() && gates(WHITE) && (count_in_hand(WHITE, ALL_PIECES) || captures_to_hand()))
+ if (gating() && gates(WHITE) && (!seirawan_gating() || count_in_hand(WHITE, ALL_PIECES) || captures_to_hand()))
for (File f = FILE_A; f <= max_file(); ++f)
if (gates(WHITE) & file_bb(f))
ss << char('A' + f);
if (can_castle(BLACK_OOO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
- if (gating() && gates(BLACK) && (count_in_hand(BLACK, ALL_PIECES) || captures_to_hand()))
+ if (gating() && gates(BLACK) && (!seirawan_gating() || count_in_hand(BLACK, ALL_PIECES) || captures_to_hand()))
for (File f = FILE_A; f <= max_file(); ++f)
if (gates(BLACK) & file_bb(f))
ss << char('a' + f);
Bitboard b = 0;
for (PieceType pt : piece_types())
b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt);
+
+ // Consider special move of neang in cambodian chess
+ if (cambodian_moves())
+ {
+ Square fers_sq = s + 2 * (c == WHITE ? SOUTH : NORTH);
+ if (is_ok(fers_sq))
+ b |= pieces(c, FERS) & gates(c) & fers_sq;
+ }
+
return b;
}
{
case NORMAL:
case DROP:
+ case SPECIAL:
return false;
case PROMOTION:
st->gatesBB[us] ^= to_sq(m);
if (gates(them) & to)
st->gatesBB[them] ^= to;
- if (!count_in_hand(us, ALL_PIECES) && !captures_to_hand())
+ if (seirawan_gating() && !count_in_hand(us, ALL_PIECES) && !captures_to_hand())
st->gatesBB[us] = 0;
}
bool shogi_doubled_pawn() const;
bool immobility_illegal() const;
bool gating() const;
+ bool seirawan_gating() const;
+ bool cambodian_moves() const;
// winning conditions
int n_move_rule() const;
int n_fold_rule() const;
return var->gating;
}
+inline bool Position::seirawan_gating() const {
+ assert(var != nullptr);
+ return var->seirawanGating;
+}
+
+inline bool Position::cambodian_moves() const {
+ assert(var != nullptr);
+ return var->cambodianMoves;
+}
+
inline int Position::n_move_rule() const {
assert(var != nullptr);
return var->nMoveRule;
DROP = 4 << (2 * SQUARE_BITS),
PIECE_PROMOTION = 5 << (2 * SQUARE_BITS),
PIECE_DEMOTION = 6 << (2 * SQUARE_BITS),
+ SPECIAL = 7 << (2 * SQUARE_BITS),
};
constexpr int MOVE_TYPE_BITS = 4;
v->countingRule = MAKRUK_COUNTING;
return v;
}
+ Variant* cambodian_variant() {
+ Variant* v = makruk_variant();
+ v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w DEde - 0 1";
+ v->gating = true;
+ v->cambodianMoves = true;
+ return v;
+ }
+ Variant* karouk_variant() {
+ Variant* v = cambodian_variant();
+ v->checkCounting = true;
+ return v;
+ }
Variant* asean_variant() {
Variant* v = chess_variant();
v->remove_piece(BISHOP);
v->add_piece(CHANCELLOR, 'e');
v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KQBCDFGkqbcdfg - 0 1";
v->gating = true;
+ v->seirawanGating = true;
v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
add("standard", chess_variant());
add("fairy", fairy_variant()); // fairy variant used for endgame code initialization
add("makruk", makruk_variant());
+ add("cambodian", cambodian_variant());
+ add("karouk", karouk_variant());
add("asean", asean_variant());
add("ai-wok", aiwok_variant());
add("shatranj", shatranj_variant());
bool shogiDoubledPawn = true;
bool immobilityIllegal = false;
bool gating = false;
+ bool seirawanGating = false;
+ bool cambodianMoves = false;
// game end
int nMoveRule = 50;
int nFoldRule = 3;
# dropPromoted: [bool] (default: false)
# shogiDoubledPawn: allow shogi pawns to be doubled [bool] (default: true)
# immobilityIllegal: [bool] (default: false)
-# gating: [bool] (default: false)
+# gating: maintain squares on backrank with extra rights in castling field of FEN [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)
# nMoveRule: [int] (default: 50)
# nFoldRule: [int] (default: 3)
# nFoldValue: [Value] (default: draw)