Skip to content

Commit

Permalink
Hostage chess prison. Add new capture type 'prison'. Prison for black…
Browse files Browse the repository at this point in the history
… playes contains white pieces. Before return piece back to board, player makes hostage exchange. Exchange rules are defened in array 'hostageExchange' of the variant.
  • Loading branch information
Cab committed Feb 14, 2024
1 parent 6c8fb46 commit af71024
Show file tree
Hide file tree
Showing 14 changed files with 435 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/ffishjs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ namespace ffish {

bool captures_to_hand(std::string uciVariant) {
const Variant* v = get_variant(uciVariant);
return v->capturesToHand;
return v->captureType != OUT;
}

std::string starting_fen(std::string uciVariant) {
Expand Down
54 changes: 54 additions & 0 deletions src/movegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ namespace {
for (PieceSet promotions = pos.promotion_piece_types(c); promotions;)
{
PieceType pt = pop_msb(promotions);
if (pos.prison_pawn_promotion() && pos.count_in_prison(~c, pt) == 0) {
continue;
}
if (!pos.promotion_limit(pt) || pos.promotion_limit(pt) > pos.count(c, pt))
moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), to - D, to, pt);
}
Expand Down Expand Up @@ -130,6 +133,50 @@ namespace {
return moveList;
}

template<Color Us, GenType Type>
ExtMove* generate_exchanges(const Position& pos, ExtMove* moveList, PieceType pt, Bitboard b) {
assert(Type != CAPTURES);
static_assert(SQUARE_BITS >= PIECE_TYPE_BITS, "not enough bits for exchange move");
Color opp = ~Us;
if (pos.count_in_prison(opp, pt) > 0) {
PieceSet rescue = NO_PIECE_SET;
for (PieceSet r = pos.rescueFor(pt); r; ) {
PieceType ex = pop_lsb(r);
if (pos.count_in_prison(Us, ex) > 0) {
rescue |= ex;
}
}
if (rescue == NO_PIECE_SET) {
return moveList;
}
// Restrict to valid target
b &= pos.drop_region(Us, pt);
// Add to move list
if (pos.drop_promoted() && pos.promoted_piece_type(pt)) {
Bitboard b2 = b;
if (Type == QUIET_CHECKS)
b2 &= pos.check_squares(pos.promoted_piece_type(pt));
while (b2) {
auto to = pop_lsb(b2);
for (PieceSet r = rescue; r; ) {
PieceType ex = pop_lsb(r);
*moveList++ = make_exchange(to, ex, pt, pos.promoted_piece_type(pt));
}
}
}
if (Type == QUIET_CHECKS)
b &= pos.check_squares(pt);
while (b) {
auto to = pop_lsb(b);
for (PieceSet r = rescue; r; ) {
PieceType ex = pop_lsb(r);
*moveList++ = make_exchange(to, ex, pt, pt);
}
}
}
return moveList;
}

template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {

Expand Down Expand Up @@ -356,6 +403,9 @@ namespace {
for (PieceSet ps = pos.promotion_piece_types(Us); ps;)
{
PieceType ptP = pop_msb(ps);
if (pos.prison_pawn_promotion() && pos.count_in_prison(~Us, ptP) == 0) {
continue;
}
if (!pos.promotion_limit(ptP) || pos.promotion_limit(ptP) > pos.count(Us, ptP))
for (Bitboard promotions = pawnPromotions; promotions; )
moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), from, pop_lsb(promotions), ptP);
Expand Down Expand Up @@ -408,6 +458,10 @@ namespace {
if (pos.piece_drops() && Type != CAPTURES && (pos.can_drop(Us, ALL_PIECES) || pos.two_boards()))
for (PieceSet ps = pos.piece_types(); ps;)
moveList = generate_drops<Us, Type>(pos, moveList, pop_lsb(ps), target & ~pos.pieces(~Us));
// generate exchange
if (pos.capture_type() == PRISON && Type != CAPTURES && pos.has_exchange())
for (PieceSet ps = pos.piece_types(); ps;)
moveList = generate_exchanges<Us, Type>(pos, moveList, pop_lsb(ps), target & ~pos.pieces(~Us));

// Castling with non-king piece
if (!pos.count<KING>(Us) && Type != CAPTURES && pos.can_castle(Us & ANY_CASTLING))
Expand Down
2 changes: 1 addition & 1 deletion src/movepick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Move MovePicker::next_move(bool skipQuiets) {
case QSEARCH_TT:
case PROBCUT_TT:
++stage;
assert(pos.legal(ttMove) == MoveList<LEGAL>(pos).contains(ttMove) || pos.virtual_drop(ttMove));
assert(pos.legal(ttMove) == MoveList<LEGAL>(pos).contains(ttMove) || pos.virtual_drop(ttMove) || exchange_piece(ttMove));
return ttMove;

case CAPTURE_INIT:
Expand Down
70 changes: 69 additions & 1 deletion src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ namespace {
return value == "win" || value == "loss" || value == "draw" || value == "none";
}

template <> bool set(const std::string& value, CapturingRule& target) {
target = value == "out" ? OUT
: value == "hand" ? HAND
: value == "prison" ? PRISON
: OUT;
return value == "out" || value == "hand" || value == "prison";
}

template <> bool set(const std::string& value, MaterialCounting& target) {
target = value == "janggi" ? JANGGI_MATERIAL
: value == "unweighted" ? UNWEIGHTED_MATERIAL
Expand Down Expand Up @@ -187,6 +195,54 @@ namespace {
target |= pt;
}

void parse_hostage_exchanges(Variant *v, std::string &map, bool DoCheck) {
bool readPiece = true;
size_t idx = -1;
PieceSet mask = NO_PIECE_SET;
for (int i = 0; i < map.size(); ++i) {
char token = map[i];
if (token == ' ') {
if (!readPiece) {
if (idx >= 0) {
v->hostageExchange[idx] = mask;
}
readPiece = true;
}
continue;
}
if (readPiece) {
mask = NO_PIECE_SET;
idx = v->pieceToChar.find(toupper(token));
if (idx == std::string::npos) {
if (DoCheck) {
std::cerr << "hostageExchange - Invalid piece type: " << token << std::endl;
}
return;
}
readPiece = false;
} else if (token == ':') {
if (mask != NO_PIECE_SET) {
if (DoCheck) {
std::cerr << "hostageExchange - Invalid syntax: " << map << std::endl;
}
return;
}
} else {
size_t idx2 = v->pieceToChar.find(toupper(token));
if (idx2 == std::string::npos) {
if (DoCheck) {
std::cerr << "hostageExchange - Invalid hostage piece type: " << token << std::endl;
}
return;
}
mask = mask | PieceType(idx2);
}
}
if (idx >= 0) {
v->hostageExchange[idx] = mask;
}
}

} // namespace

template <bool DoCheck>
Expand All @@ -207,6 +263,7 @@ template <bool Current, class T> bool VariantParser<DoCheck>::parse_attribute(co
: std::is_same<T, MaterialCounting>() ? "MaterialCounting"
: std::is_same<T, CountingRule>() ? "CountingRule"
: std::is_same<T, ChasingRule>() ? "ChasingRule"
: std::is_same<T, CapturingRule>() ? "CapturingRule"
: std::is_same<T, EnclosingRule>() ? "EnclosingRule"
: std::is_same<T, Bitboard>() ? "Bitboard"
: std::is_same<T, CastlingRights>() ? "CastlingRights"
Expand Down Expand Up @@ -453,7 +510,18 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
parse_attribute("mustDropType", v->mustDropType, v->pieceToChar);
parse_attribute("pieceDrops", v->pieceDrops);
parse_attribute("dropLoop", v->dropLoop);
parse_attribute("capturesToHand", v->capturesToHand);

bool capturesToHand = false;
parse_attribute<false>("capturesToHand", capturesToHand);
if (capturesToHand) v->captureType = HAND;

parse_attribute("captureType", v->captureType);
// hostage price
const auto& it_host_p = config.find("hostageExchange");
if (it_host_p != config.end()) {
parse_hostage_exchanges(v, it_host_p->second, DoCheck);
}
parse_attribute("prisonPawnPromotion", v->prisonPawnPromotion);
parse_attribute("firstRankPawnDrops", v->firstRankPawnDrops);
parse_attribute("promotionZonePawnDrops", v->promotionZonePawnDrops);
parse_attribute("enclosingDrop", v->enclosingDrop);
Expand Down
Loading

0 comments on commit af71024

Please sign in to comment.