b &= ~file_bb(f);
if (pt == ROOK && pos.sittuyin_rook_drop())
b &= rank_bb(relative_rank(Us, RANK_1, pos.max_rank()));
+
+ // Add to move list
+ if (pos.drop_promoted() && pos.promoted_piece_type(pt))
+ {
+ Bitboard b2 = b;
+ if (Checks)
+ b2 &= pos.check_squares(pos.promoted_piece_type(pt));
+ while (b2)
+ *moveList++ = make_drop(pop_lsb(&b2), pt, pos.promoted_piece_type(pt));
+ }
if (Checks)
b &= pos.check_squares(pt);
while (b)
- *moveList++ = make_drop(pop_lsb(&b), pt);
+ *moveList++ = make_drop(pop_lsb(&b), pt, pt);
}
return moveList;
Bitboard b1 = ( (pos.attacks_from(us, pt, from) & pos.pieces())
| (pos.moves_from(us, pt, from) & ~pos.pieces())) & target;
- PieceType pt_promotion = pos.promoted_piece_type(pt);
- Bitboard b2 = pt_promotion ? b1 : 0;
+ Bitboard b2 = pos.promoted_piece_type(pt) ? b1 : 0;
+ Bitboard b3 = pos.piece_demotion() && pos.is_promoted(from) ? b1 : 0;
if (Checks)
{
b1 &= pos.check_squares(pt);
- if (pt_promotion)
- b2 &= pos.check_squares(pt_promotion);
+ if (b2)
+ b2 &= pos.check_squares(pos.promoted_piece_type(pt));
+ if (b3)
+ b3 &= pos.check_squares(type_of(pos.unpromoted_piece_on(from)));
}
// Restrict target squares considering promotion zone
- if (pt_promotion)
+ if (b2 | b3)
{
Bitboard promotion_zone = promotion_zone_bb(us, pos.promotion_rank(), pos.max_rank());
if (pos.mandatory_piece_promotion())
b1 &= promotion_zone & from ? 0 : ~promotion_zone;
if (!(promotion_zone & from))
+ {
b2 &= promotion_zone;
+ b3 &= promotion_zone;
+ }
}
while (b1)
// Shogi-style piece promotions
while (b2)
*moveList++ = make<PIECE_PROMOTION>(from, pop_lsb(&b2));
+
+ // Piece demotions
+ while (b3)
+ *moveList++ = make<PIECE_DEMOTION>(from, pop_lsb(&b3));
}
return moveList;
if (type_of(m) != NORMAL)
return MoveList<LEGAL>(*this).contains(m);
+ // Handle the case where a mandatory piece promotion/demotion is not taken
+ if ( mandatory_piece_promotion()
+ && (is_promoted(from) ? piece_demotion() : promoted_piece_type(type_of(pc)) != NO_PIECE_TYPE)
+ && (promotion_zone_bb(us, promotion_rank(), max_rank()) & (SquareBB[from] | to)))
+ return false;
+
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) != NO_PIECE_TYPE)
return false;
return false;
// Is there a direct check?
- if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && (st->checkSquares[type_of(moved_piece(m))] & to))
+ if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION && (st->checkSquares[type_of(moved_piece(m))] & to))
return true;
// Is there a discovered check?
case PIECE_PROMOTION:
return attacks_bb(sideToMove, promoted_piece_type(type_of(moved_piece(m))), to, pieces() ^ from) & square<KING>(~sideToMove);
+ case PIECE_DEMOTION:
+ return attacks_bb(sideToMove, type_of(unpromoted_piece_on(from)), to, pieces() ^ from) & square<KING>(~sideToMove);
+
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we
// need to handle is the unusual case of a discovered check through
// Update hash key
if (type_of(m) == DROP)
+ {
+ Piece pc_hand = make_piece(us, in_hand_piece_type(m));
k ^= Zobrist::psq[pc][to]
- ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)] - 1]
- ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)]];
+ ^ Zobrist::inHand[pc_hand][pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] - 1]
+ ^ Zobrist::inHand[pc_hand][pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]];
+ }
else
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) == DROP)
{
- drop_piece(pc, to);
+ drop_piece(make_piece(us, in_hand_piece_type(m)), pc, to);
st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1];
if (type_of(pc) != PAWN)
st->nonPawnMaterial[us] += PieceValue[MG][pc];
// Update material
st->nonPawnMaterial[us] += PieceValue[MG][promotion] - PieceValue[MG][pc];
}
+ else if (type_of(m) == PIECE_DEMOTION)
+ {
+ Piece demotion = unpromoted_piece_on(from);
+
+ remove_piece(pc, to);
+ put_piece(demotion, to);
+ promotedPieces ^= from;
+ unpromotedBoard[from] = NO_PIECE;
+
+ // Update hash keys
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[demotion][to];
+ st->materialKey ^= Zobrist::psq[demotion][pieceCount[demotion]-1]
+ ^ Zobrist::psq[pc][pieceCount[pc]];
+
+ // Update incremental score
+ st->psq += PSQT::psq[demotion][to] - PSQT::psq[pc][to];
+
+ // Update material
+ st->nonPawnMaterial[us] += PieceValue[MG][demotion] - PieceValue[MG][pc];
+ }
// Update incremental scores
st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
// Update information about promoted pieces
if (type_of(m) != DROP && is_promoted(from))
promotedPieces = (promotedPieces - from) | to;
+ else if (type_of(m) == DROP && in_hand_piece_type(m) != dropped_piece_type(m))
+ promotedPieces = promotedPieces | to;
if (type_of(m) != DROP && unpromoted_piece_on(from))
{
unpromotedBoard[to] = unpromotedBoard[from];
unpromotedBoard[from] = NO_PIECE;
}
+ else if (type_of(m) == DROP && in_hand_piece_type(m) != dropped_piece_type(m))
+ unpromotedBoard[to] = make_piece(us, in_hand_piece_type(m));
sideToMove = ~sideToMove;
unpromotedBoard[to] = NO_PIECE;
promotedPieces -= to;
}
+ else if (type_of(m) == PIECE_DEMOTION)
+ {
+ remove_piece(pc, to);
+ unpromotedBoard[from] = pc;
+ pc = make_piece(us, promoted_piece_type(type_of(pc)));
+ put_piece(pc, to);
+ promotedPieces |= from;
+ }
if (type_of(m) == CASTLING)
{
else
{
if (type_of(m) == DROP)
- undrop_piece(pc, to); // Remove the dropped piece
+ undrop_piece(make_piece(us, in_hand_piece_type(m)), pc, to); // Remove the dropped piece
else
move_piece(pc, to, from); // Put the piece back at the source square
if (captures_to_hand() && !drop_loop() && is_promoted(to))
- promotedPieces = (promotedPieces - to) | from;
+ {
+ promotedPieces = (promotedPieces - to);
+ if (type_of(m) != DROP)
+ promotedPieces |= from;
+ }
if (unpromoted_piece_on(to))
{
- unpromotedBoard[from] = unpromotedBoard[to];
+ if (type_of(m) != DROP)
+ unpromotedBoard[from] = unpromotedBoard[to];
unpromotedBoard[to] = NO_PIECE;
}
}
}
if (type_of(m) == DROP)
- return k ^ Zobrist::psq[pc][to] ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)]]
- ^ Zobrist::inHand[pc][pieceCountInHand[color_of(pc)][type_of(pc)] - 1];
+ {
+ Piece pc_hand = make_piece(sideToMove, in_hand_piece_type(m));
+ return k ^ Zobrist::psq[pc][to] ^ Zobrist::inHand[pc_hand][pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]]
+ ^ Zobrist::inHand[pc_hand][pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] - 1];
+ }
return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
}
bool sittuyin_promotion() const;
PieceType promoted_piece_type(PieceType pt) const;
bool mandatory_piece_promotion() const;
+ bool piece_demotion() const;
bool endgame_eval() const;
bool double_step_enabled() const;
bool first_rank_double_steps() const;
Bitboard drop_region(Color c) const;
bool sittuyin_rook_drop() const;
bool drop_opposite_colored_bishop() const;
+ bool drop_promoted() const;
bool shogi_doubled_pawn() const;
bool immobility_illegal() const;
// winning conditions
// Piece specific
bool pawn_passed(Color c, Square s) const;
bool opposite_bishops() const;
+ bool is_promoted(Square s) const;
// Doing and undoing moves
void do_move(Move m, StateInfo& newSt);
bool chess960;
int pieceCountInHand[COLOR_NB][PIECE_TYPE_NB];
Bitboard promotedPieces;
- bool is_promoted(Square s) const;
void add_to_hand(Color c, PieceType pt);
void remove_from_hand(Color c, PieceType pt);
- void drop_piece(Piece pc, Square s);
- void undrop_piece(Piece pc, Square s);
+ void drop_piece(Piece pc_hand, Piece pc_drop, Square s);
+ void undrop_piece(Piece pc_hand, Piece pc_drop, Square s);
};
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
return var->mandatoryPiecePromotion;
}
+inline bool Position::piece_demotion() const {
+ assert(var != nullptr);
+ return var->pieceDemotion;
+}
+
inline bool Position::endgame_eval() const {
assert(var != nullptr);
return var->endgameEval;
return var->dropOppositeColoredBishop;
}
+inline bool Position::drop_promoted() const {
+ assert(var != nullptr);
+ return var->dropPromoted;
+}
+
inline bool Position::shogi_doubled_pawn() const {
assert(var != nullptr);
return var->shogiDoubledPawn;
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
}
+inline bool Position::is_promoted(Square s) const {
+ return promotedPieces & s;
+}
+
inline bool Position::is_chess960() const {
return chess960;
}
pieceCountInHand[c][ALL_PIECES]--;
}
-inline bool Position::is_promoted(Square s) const {
- return promotedPieces & s;
-}
-
-inline void Position::drop_piece(Piece pc, Square s) {
- assert(pieceCountInHand[color_of(pc)][type_of(pc)]);
- put_piece(pc, s);
- remove_from_hand(color_of(pc), type_of(pc));
+inline void Position::drop_piece(Piece pc_hand, Piece pc_drop, Square s) {
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]);
+ put_piece(pc_drop, s);
+ remove_from_hand(color_of(pc_hand), type_of(pc_hand));
}
-inline void Position::undrop_piece(Piece pc, Square s) {
- remove_piece(pc, s);
+inline void Position::undrop_piece(Piece pc_hand, Piece pc_drop, Square s) {
+ remove_piece(pc_drop, s);
board[s] = NO_PIECE;
- add_to_hand(color_of(pc), type_of(pc));
- assert(pieceCountInHand[color_of(pc)][type_of(pc)]);
+ add_to_hand(color_of(pc_hand), type_of(pc_hand));
+ assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)]);
}
#endif // #ifndef POSITION_H_INCLUDED
PROMOTION = 3 << (2 * SQUARE_BITS),
DROP = 4 << (2 * SQUARE_BITS),
PIECE_PROMOTION = 5 << (2 * SQUARE_BITS),
+ PIECE_DEMOTION = 6 << (2 * SQUARE_BITS),
};
+constexpr int MOVE_TYPE_BITS = 4;
+
enum Color {
WHITE, BLACK, COLOR_NB = 2
};
MidgameLimit = 15258, EndgameLimit = 3915
};
-const int PIECE_TYPE_BITS = 5; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS)
+constexpr int PIECE_TYPE_BITS = 5; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS)
enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN,
static_assert(PIECE_TYPE_BITS <= 6, "PIECE_TYPE uses more than 6 bit");
static_assert(!(PIECE_TYPE_NB & (PIECE_TYPE_NB - 1)), "PIECE_TYPE_NB is not a power of 2");
+static_assert(2 * SQUARE_BITS + MOVE_TYPE_BITS + 2 * PIECE_TYPE_BITS <= 32, "Move encoding uses more than 32 bits");
+
enum Piece {
NO_PIECE,
PIECE_NB = 2 * PIECE_TYPE_NB
}
inline PieceType promotion_type(Move m) {
- return type_of(m) == PROMOTION ? PieceType((m >> (2 * SQUARE_BITS + 4)) & (PIECE_TYPE_NB - 1)) : NO_PIECE_TYPE;
+ return type_of(m) == PROMOTION ? PieceType((m >> (2 * SQUARE_BITS + MOVE_TYPE_BITS)) & (PIECE_TYPE_NB - 1)) : NO_PIECE_TYPE;
}
inline Move make_move(Square from, Square to) {
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = NO_PIECE_TYPE) {
- return Move((pt << (2 * SQUARE_BITS + 4)) + T + (from << SQUARE_BITS) + to);
+ return Move((pt << (2 * SQUARE_BITS + MOVE_TYPE_BITS)) + T + (from << SQUARE_BITS) + to);
}
-constexpr Move make_drop(Square to, PieceType pt) {
- return Move((pt << (2 * SQUARE_BITS + 4)) + DROP + to);
+constexpr Move make_drop(Square to, PieceType pt_in_hand, PieceType pt_dropped) {
+ return Move((pt_in_hand << (2 * SQUARE_BITS + MOVE_TYPE_BITS + PIECE_TYPE_BITS)) + (pt_dropped << (2 * SQUARE_BITS + MOVE_TYPE_BITS)) + DROP + to);
}
constexpr PieceType dropped_piece_type(Move m) {
- return PieceType((m >> (2 * SQUARE_BITS + 4)) & (PIECE_TYPE_NB - 1));
+ return PieceType((m >> (2 * SQUARE_BITS + MOVE_TYPE_BITS)) & (PIECE_TYPE_NB - 1));
+}
+
+constexpr PieceType in_hand_piece_type(Move m) {
+ return PieceType((m >> (2 * SQUARE_BITS + MOVE_TYPE_BITS + PIECE_TYPE_BITS)) & (PIECE_TYPE_NB - 1));
}
inline bool is_ok(Move m) {
#endif
}
+/// UCI::dropped_piece() generates a piece label string from a Move.
+
+string UCI::dropped_piece(const Position& pos, Move m) {
+ assert(type_of(m) == DROP);
+ if (dropped_piece_type(m) == pos.promoted_piece_type(in_hand_piece_type(m)))
+ // Dropping as promoted piece
+ return std::string{'+', pos.piece_to_char()[in_hand_piece_type(m)]};
+ else
+ return std::string{pos.piece_to_char()[dropped_piece_type(m)]};
+}
+
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
/// The only special case is castling, where we print in the e1g1 notation in
if (type_of(m) == CASTLING && !pos.is_chess960())
to = make_square(to > from ? pos.castling_kingside_file() : pos.castling_queenside_file(), rank_of(from));
- string move = (type_of(m) == DROP ? std::string{pos.piece_to_char()[type_of(pos.moved_piece(m))],
- Options["Protocol"] == "usi" ? '*' : '@'}
+ string move = (type_of(m) == DROP ? UCI::dropped_piece(pos, m) + (Options["Protocol"] == "usi" ? '*' : '@')
: UCI::square(pos, from)) + UCI::square(pos, to);
if (type_of(m) == PROMOTION)
move += pos.piece_to_char()[make_piece(BLACK, promotion_type(m))];
-
- if (type_of(m) == PIECE_PROMOTION)
+ else if (type_of(m) == PIECE_PROMOTION)
move += '+';
+ else if (type_of(m) == PIECE_DEMOTION)
+ move += '-';
return move;
}
void loop(int argc, char* argv[]);
std::string value(Value v);
std::string square(const Position& pos, Square s);
+std::string dropped_piece(const Position& pos, Move m);
std::string move(const Position& pos, Move m);
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
Move to_move(const Position& pos, std::string& str);
v->shogiPawnDropMateIllegal = true;
return v;
}
+ Variant* kyotoshogi_variant() {
+ Variant* v = minishogi_variant();
+ v->add_piece(LANCE, 'l');
+ v->add_piece(SHOGI_KNIGHT, 'n');
+ v->startFen = "p+nks+l/5/5/5/+LSK+NP[-] w 0 1";
+ v->promotionRank = RANK_1;
+ v->mandatoryPiecePromotion = true;
+ v->pieceDemotion = true;
+ v->dropPromoted = true;
+ v->promotedPieceType[LANCE] = GOLD;
+ v->promotedPieceType[SILVER] = BISHOP;
+ v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
+ v->promotedPieceType[SHOGI_PAWN] = ROOK;
+ v->promotedPieceType[GOLD] = NO_PIECE_TYPE;
+ v->promotedPieceType[BISHOP] = NO_PIECE_TYPE;
+ v->promotedPieceType[ROOK] = NO_PIECE_TYPE;
+ v->immobilityIllegal = false;
+ v->shogiPawnDropMateIllegal = false;
+ v->shogiDoubledPawn = true;
+ return v;
+ }
Variant* dobutsu_variant() {
Variant* v = minishogi_variant();
v->maxRank = RANK_4;
add("placement", placement_variant());
add("sittuyin", sittuyin_variant());
add("minishogi", minishogi_variant());
+ add("kyotoshogi", kyotoshogi_variant());
add("dobutsu", dobutsu_variant());
add("gorogoro", gorogoroshogi_variant());
add("judkinshogi", judkinsshogi_variant());
bool sittuyinPromotion = false;
PieceType promotedPieceType[PIECE_TYPE_NB] = {};
bool mandatoryPiecePromotion = false;
+ bool pieceDemotion = false;
bool endgameEval = false;
bool doubleStep = true;
bool firstRankDoubleSteps = false;
Bitboard blackDropRegion = AllSquares;
bool sittuyinRookDrop = false;
bool dropOppositeColoredBishop = false;
+ bool dropPromoted = false;
bool shogiDoubledPawn = true;
bool immobilityIllegal = false;
// game end