- Rook drops are limited to back ranks.
- Promotion is allowed on main diagonals within opponent's half
or when only one pawn is left. The player must not have a general.
- Promoting pawns stay on same square or move like a general.
- Pawn promotions may not capture or give check.
/// in front of the given one, from the point of view of the given color. For instance,
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
+inline Bitboard forward_ranks_bb(Color c, Rank r) {
+ return ForwardRanksBB[c][r];
+}
+
inline Bitboard forward_ranks_bb(Color c, Square s) {
return ForwardRanksBB[c][rank_of(s)];
}
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
- if (!Bitbases::probe(wksq, psq, bksq, us))
- return VALUE_DRAW;
+ Value result;
+ if ( pos.promotion_rank() == RANK_8
+ && pos.promotion_piece_types().find(QUEEN) != pos.promotion_piece_types().end())
+ {
+ if (!Bitbases::probe(wksq, psq, bksq, us))
+ return VALUE_DRAW;
- Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq));
+ result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq));
+ }
+ else
+ {
+ // Non-standard promotion, evaluation unclear
+ result = PawnValueEg + Value(rank_of(psq));
+ }
return strongSide == pos.side_to_move() ? result : -result;
}
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
- return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
+ if ( pos.promotion_rank() == RANK_8
+ && pos.promotion_piece_types().find(QUEEN) != pos.promotion_piece_types().end())
+ return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
+ else
+ return SCALE_FACTOR_NONE;
}
for (File f = FILE_A; f <= pos.max_file(); ++f)
if (file_bb(f) & pos.pieces(Us, pt))
b &= ~file_bb(f);
+ if (pt == ROOK && pos.sittuyin_rook_drop())
+ b &= rank_bb(relative_rank(Us, RANK_1, pos.max_rank()));
if (Checks)
b &= pos.check_squares(pt);
while (b)
moveList = make_promotions<Us, Type, Up >(pos, moveList, pop_lsb(&b3));
}
+ // Sittuyin promotions
+ if (pos.sittuyin_promotion() && (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS))
+ {
+ Bitboard pawns = pos.pieces(Us, PAWN);
+ // Pawns need to be on diagonals on opponent's half if there is more than one pawn
+ if (pos.count<PAWN>(Us) > 1)
+ pawns &= ( PseudoAttacks[Us][BISHOP][make_square(FILE_A, relative_rank(Us, RANK_1, pos.max_rank()))]
+ | PseudoAttacks[Us][BISHOP][make_square(pos.max_file(), relative_rank(Us, RANK_1, pos.max_rank()))])
+ & forward_ranks_bb(Us, relative_rank(Us, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
+ while (pawns)
+ {
+ Square from = pop_lsb(&pawns);
+ for (PieceType pt : pos.promotion_piece_types())
+ {
+ if (pos.count(Us, pt))
+ continue;
+ Bitboard b = (pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from;
+ if (Type == EVASIONS)
+ b &= target;
+
+ while (b)
+ *moveList++ = make<PROMOTION>(from, pop_lsb(&b), pt);
+ }
+ }
+ }
+
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s);
lever = theirPawns & PseudoAttacks[Us][PAWN][s];
- leverPush = theirPawns & PseudoAttacks[Us][PAWN][s + Up];
+ leverPush = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : 0;
doubled = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? ourPawns & (s - Up) : 0;
neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s);
// A pawn is backward when it is behind all pawns of the same color
// on the adjacent files and cannot be safely advanced.
- backward = !(ourPawns & pawn_attack_span(Them, s + Up))
+ backward = relative_rank(Them, s, pos.max_rank()) > RANK_1
+ && !(ourPawns & pawn_attack_span(Them, s + Up))
&& (stoppers & (leverPush | (s + Up)));
// Passed pawns will be properly scored in evaluation because we need
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s;
- else if ( stoppers == SquareBB[s + Up]
+ else if ( relative_rank(Them, s, pos.max_rank()) > RANK_1
+ && stoppers == SquareBB[s + Up]
&& relative_rank(Us, s, pos.max_rank()) >= RANK_5)
{
b = shift<Up>(supported) & ~theirPawns;
assert(color_of(moved_piece(m)) == us);
assert(!count<KING>(us) || piece_on(square<KING>(us)) == make_piece(us, KING));
- // illegal moves to squares outside of board
+ // Illegal moves to squares outside of board
if (!(board_bb() & to))
return false;
- // illegal checks
- if (!checking_permitted() && gives_check(m))
+ // Illegal checks
+ if ((!checking_permitted() || (sittuyin_promotion() && type_of(m) == PROMOTION)) && gives_check(m))
return false;
- // illegal quiet moves
+ // Illegal quiet moves
if (must_capture() && !capture(m))
{
if (checkers())
}
}
- // illegal non-drop moves
+ // Illegal non-drop moves
if (must_drop() && type_of(m) != DROP && count_in_hand(us, ALL_PIECES))
{
if (checkers())
}
}
- // illegal drop move
+ // Illegal drop move
if (drop_opposite_colored_bishop() && type_of(m) == DROP)
{
if (type_of(moved_piece(m)) != BISHOP)
return false;
}
- // no legal moves from target square
+ // No legal moves from target square
if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(moves_bb(us, type_of(moved_piece(m)), to, 0) & board_bb()))
return false;
- // game end
+ // Game end
if (is_variant_end())
return false;
Square to = to_sq(m);
Piece pc = moved_piece(m);
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
+ if (to == from)
+ {
+ assert(type_of(m) == PROMOTION && sittuyin_promotion());
+ captured = NO_PIECE;
+ }
Piece unpromotedCaptured = unpromoted_piece_on(to);
assert(color_of(pc) == us);
{
Piece promotion = make_piece(us, promotion_type(m));
- assert(relative_rank(us, to, max_rank()) == promotion_rank());
+ assert(relative_rank(us, to, max_rank()) == promotion_rank() || sittuyin_promotion());
assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING);
remove_piece(pc, to);
Square to = to_sq(m);
Piece pc = piece_on(to);
- assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING);
+ assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING || (type_of(m) == PROMOTION && sittuyin_promotion()));
assert(type_of(st->capturedPiece) != KING);
if (type_of(m) == PROMOTION)
{
- assert(relative_rank(us, to, max_rank()) == promotion_rank());
+ assert(relative_rank(us, to, max_rank()) == promotion_rank() || sittuyin_promotion());
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) < KING);
const std::string piece_to_char() const;
Rank promotion_rank() const;
const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
+ bool sittuyin_promotion() const;
PieceType promoted_piece_type(PieceType pt) const;
bool mandatory_piece_promotion() const;
bool endgame_eval() const;
bool first_rank_drops() const;
bool drop_on_top() const;
Bitboard drop_region(Color c) const;
+ bool sittuyin_rook_drop() const;
bool drop_opposite_colored_bishop() const;
bool immobility_illegal() const;
// winning conditions
return var->promotionPieceTypes;
}
+inline bool Position::sittuyin_promotion() const {
+ assert(var != nullptr);
+ return var->sittuyinPromotion;
+}
+
inline PieceType Position::promoted_piece_type(PieceType pt) const {
assert(var != nullptr);
return var->promotedPieceType[pt];
return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
}
+inline bool Position::sittuyin_rook_drop() const {
+ assert(var != nullptr);
+ return var->sittuyinRookDrop;
+}
+
inline bool Position::drop_opposite_colored_bishop() const {
assert(var != nullptr);
return var->dropOppositeColoredBishop;
}
inline bool is_ok(Move m) {
- return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
+ return from_sq(m) != to_sq(m) || type_of(m) == PROMOTION; // Catch MOVE_NULL and MOVE_NONE
}
#endif // #ifndef TYPES_H_INCLUDED
v->castlingDroppedPiece = true;
return v;
}
+ Variant* sittuyin_variant() {
+ Variant* v = makruk_variant();
+ v->startFen = "8/8/4pppp/pppp4/4PPPP/PPPP4/8/8[KFRRSSNNkfrrssnn] w - - 0 1";
+ v->remove_piece(MET);
+ v->add_piece(MET, 'f');
+ v->mustDrop = true;
+ v->pieceDrops = true;
+ v->capturesToHand = false;
+ v->whiteDropRegion = Rank1BB | Rank2BB | Rank3BB;
+ v->blackDropRegion = Rank8BB | Rank7BB | Rank6BB;
+ v->sittuyinRookDrop = true;
+ v->promotionRank = RANK_1; // no regular promotions
+ v->sittuyinPromotion = true;
+ v->immobilityIllegal = false;
+ return v;
+ }
Variant* euroshogi_variant() {
Variant* v = fairy_variant_base();
v->reset_pieces();
add("chessgi", chessgi_variant());
add("pocketknight", pocketknight_variant());
add("placement", placement_variant());
+ add("sittuyin", sittuyin_variant());
add("euroshogi", euroshogi_variant());
add("judkinshogi", judkinsshogi_variant());
add("minishogi", minishogi_variant());
std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
Rank promotionRank = RANK_8;
std::set<PieceType, std::greater<PieceType> > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT };
+ bool sittuyinPromotion = false;
PieceType promotedPieceType[PIECE_TYPE_NB] = {};
bool mandatoryPiecePromotion = false;
bool endgameEval = false;
bool dropOnTop = false;
Bitboard whiteDropRegion = AllSquares;
Bitboard blackDropRegion = AllSquares;
+ bool sittuyinRookDrop = false;
bool dropOppositeColoredBishop = false;
bool immobilityIllegal = false;
// game end
expect perft.exp minishogi startpos 5 533203 > /dev/null
expect perft.exp horde startpos 6 5396554 > /dev/null
expect perft.exp placement startpos 4 1597696 > /dev/null
+expect perft.exp sittuyin startpos 3 580096 > /dev/null
+expect perft.exp sittuyin "fen 8/8/6R1/s3r3/P5R1/1KP3p1/1F2kr2/8[-] b - - 0 72" 4 657824 > /dev/null
rm perft.exp