From: ianfab Date: Sat, 14 Jul 2018 17:33:36 +0000 (+0200) Subject: Support giveaway, antichess, extinction, and kinglet chess X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=0cfe34af00a3848f1b439c38298007b93e27c51a;p=fairystockfish.git Support giveaway, antichess, extinction, and kinglet chess Includes implementation of - positions without kings - castling with non-king piece - winning condition "extinction" - piece type commoner bench: 4948195 --- diff --git a/src/bitboard.cpp b/src/bitboard.cpp index f43eb82..52f42fb 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -167,6 +167,7 @@ void Bitboards::init() { { 15, 17 }, // shogi knight { -1, 1, 15, 17 }, // euroshogi knight { -8, -1, 1, 7, 8, 9 }, // gold + { -9, -8, -7, -1, 1, 7, 8, 9 }, // commoner { -9, -8, -7, -1, 1, 7, 8, 9 } // king }; int stepsQuiet[][13] = { @@ -190,6 +191,7 @@ void Bitboards::init() { { 15, 17 }, // shogi knight { -1, 1, 15, 17 }, // euroshogi knight { -8, -1, 1, 7, 8, 9 }, // gold + { -9, -8, -7, -1, 1, 7, 8, 9 }, // commoner { -9, -8, -7, -1, 1, 7, 8, 9 } // king }; Direction sliderCapture[][9] = { @@ -213,6 +215,7 @@ void Bitboards::init() { {}, // shogi knight {}, // euroshogi knight {}, // gold + {}, // commoner {} // king }; Direction sliderQuiet[][9] = { @@ -236,6 +239,7 @@ void Bitboards::init() { {}, // shogi knight {}, // euroshogi knight {}, // gold + {}, // commoner {} // king }; int sliderDistCapture[] = { @@ -259,6 +263,7 @@ void Bitboards::init() { 0, // shogi knight 0, // euroshogi knight 0, // gold + 0, // commoner 0 // king }; int sliderDistQuiet[] = { @@ -282,6 +287,7 @@ void Bitboards::init() { 0, // shogi knight 0, // euroshogi knight 0, // gold + 0, // commoner 0 // king }; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 5abb88a..43f30bc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -264,13 +264,13 @@ namespace { mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); // Initialise attackedBy bitboards for kings and pawns - attackedBy[Us][KING] = pos.attacks_from(Us, pos.square(Us)); + attackedBy[Us][KING] = pos.count(Us) ? pos.attacks_from(Us, pos.square(Us)) : 0; attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN]; // Init our king safety tables only if we are going to use them - if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg) + if (pos.count(Us) && pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg) { kingRing[Us] = attackedBy[Us][KING]; if (relative_rank(Us, pos.square(Us)) == RANK_1) @@ -336,7 +336,8 @@ namespace { mobility[Us] += make_score(300, 300) * (mob - 2) / (10 + mob); // Penalty if the piece is far from the king - score -= KingProtector[Pt - 2] * distance(s, pos.square(Us)); + if (pos.count(Us)) + score -= KingProtector[Pt - 2] * distance(s, pos.square(Us)); if (Pt == BISHOP || Pt == KNIGHT) { @@ -393,7 +394,7 @@ namespace { score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))]; // Penalty when trapped by the king, even more if the king cannot castle - else if (mob <= 3) + else if (mob <= 3 && pos.count(Us)) { File kf = file_of(pos.square(Us)); if ((kf < FILE_E) == (file_of(s) < kf)) @@ -424,6 +425,9 @@ namespace { constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); + if (!pos.count(Us)) + return SCORE_ZERO; + const Square ksq = pos.square(Us); Bitboard weak, b, b1, b2, safe, unsafeChecks; @@ -638,7 +642,7 @@ namespace { constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); auto king_proximity = [&](Color c, Square s) { - return std::min(distance(pos.square(c), s), 5); + return pos.count(c) ? std::min(distance(pos.square(c), s), 5) : 5; }; Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; @@ -817,8 +821,9 @@ namespace { template Score Evaluation::initiative(Value eg) const { - int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - - distance(pos.square(WHITE), pos.square(BLACK)); + int outflanking = !pos.count(WHITE) || !pos.count(BLACK) ? 0 + : distance(pos.square(WHITE), pos.square(BLACK)) + - distance(pos.square(WHITE), pos.square(BLACK)); bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); diff --git a/src/movegen.cpp b/src/movegen.cpp index 5f0cb89..4425bd7 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -35,7 +35,7 @@ namespace { // After castling, the rook and king final positions are the same in Chess960 // as they would be in standard chess. - Square kfrom = pos.square(us); + Square kfrom = pos.count(us) ? pos.square(us) : make_square(FILE_E, us == WHITE ? RANK_1 : RANK_8); Square rfrom = pos.castling_rook_square(Cr); Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1); Bitboard enemies = pos.pieces(~us); @@ -45,15 +45,18 @@ namespace { const Direction step = Chess960 ? kto > kfrom ? WEST : EAST : KingSide ? WEST : EAST; - for (Square s = kto; s != kfrom; s += step) - if (pos.attackers_to(s) & enemies) - return moveList; + if (type_of(pos.piece_on(kfrom)) == KING) + { + for (Square s = kto; s != kfrom; s += step) + if (pos.attackers_to(s) & enemies) + return moveList; - // Because we generate only legal castling moves we need to verify that - // when moving the castling rook we do not discover some hidden checker. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & pos.pieces(~us))) - return moveList; + // Because we generate only legal castling moves we need to verify that + // when moving the castling rook we do not discover some hidden checker. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & pos.pieces(~us))) + return moveList; + } Move m = make(kfrom, rfrom); @@ -134,7 +137,7 @@ namespace { b2 &= target; } - if (Type == QUIET_CHECKS) + if (Type == QUIET_CHECKS && pos.count(Them)) { Square ksq = pos.square(Them); @@ -274,7 +277,7 @@ namespace { for (PieceType pt = PAWN; pt < KING; ++pt) moveList = generate_drops(pos, moveList, pt, target & ~pos.pieces(~Us)); - if (Type != QUIET_CHECKS && Type != EVASIONS) + if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count(Us)) { Square ksq = pos.square(Us); Bitboard b = pos.attacks_from(Us, ksq) & target; diff --git a/src/position.cpp b/src/position.cpp index 2de52df..618c82a 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -374,7 +374,7 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, void Position::set_castling_right(Color c, Square rfrom) { - Square kfrom = square(c); + Square kfrom = count(c) ? square(c) : make_square(FILE_E, c == WHITE ? RANK_1 : RANK_8); CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; CastlingRight cr = (c | cs); @@ -400,13 +400,13 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info(StateInfo* si) const { - si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinners[BLACK]); - si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinners[WHITE]); + si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), count(WHITE) ? square(WHITE) : SQ_NONE, si->pinners[BLACK]); + si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), count(BLACK) ? square(BLACK) : SQ_NONE, si->pinners[WHITE]); - Square ksq = square(~sideToMove); + Square ksq = count(~sideToMove) ? square(~sideToMove) : SQ_NONE; for (PieceType pt = PAWN; pt < KING; ++pt) - si->checkSquares[pt] = attacks_from(~sideToMove, pt, ksq); + si->checkSquares[pt] = ksq != SQ_NONE ? attacks_from(~sideToMove, pt, ksq) : 0; si->checkSquares[KING] = 0; } @@ -422,7 +422,7 @@ void Position::set_state(StateInfo* si) const { si->pawnKey = Zobrist::noPawns; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->psq = SCORE_ZERO; - si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); + si->checkersBB = count(sideToMove) ? attackers_to(square(sideToMove)) & pieces(~sideToMove) : 0; set_check_info(si); @@ -585,6 +585,9 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners Bitboard blockers = 0; pinners = 0; + if (s == SQ_NONE) + return blockers; + // Snipers are sliders that attack 's' when a piece is removed Bitboard snipers = sliders & attackers_to(s, 0) @@ -629,10 +632,10 @@ bool Position::legal(Move m) const { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - Square ksq = square(us); + Square ksq = count(us) ? square(us) : SQ_NONE; assert(color_of(moved_piece(m)) == us); - assert(piece_on(square(us)) == make_piece(us, KING)); + assert(!count(us) || piece_on(square(us)) == make_piece(us, KING)); // illegal moves to squares outside of board if (rank_of(to) > max_rank() || file_of(to) > max_file()) @@ -680,7 +683,7 @@ bool Position::legal(Move m) const { assert(piece_on(capsq) == make_piece(~us, PAWN)); assert(piece_on(to) == NO_PIECE); - return !(attackers_to(ksq, occupied) & pieces(~us) & occupied); + return !count(us) || !(attackers_to(ksq, occupied) & pieces(~us) & occupied); } // If the moving piece is a king, check whether the destination @@ -690,7 +693,7 @@ bool Position::legal(Move m) const { return type_of(m) == CASTLING || !(attackers_to(to) & pieces(~us)); // A non-king move is legal if the king is not under attack after the move. - return !( attackers_to(ksq, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to) + return !count(us) || !( attackers_to(ksq, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to) & pieces(~us) & ~SquareBB[to]); } @@ -780,6 +783,10 @@ bool Position::gives_check(Move m) const { Square from = from_sq(m); Square to = to_sq(m); + // No check possible without king + if (!count(~sideToMove)) + return false; + // Is there a direct check? if (st->checkSquares[type_of(moved_piece(m))] & to) return true; @@ -869,7 +876,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (type_of(m) == CASTLING) { - assert(pc == make_piece(us, KING)); + assert(type_of(pc) != NO_PIECE_TYPE); assert(captured == make_piece(us, ROOK)); Square rfrom, rto; @@ -1129,10 +1136,11 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 - remove_piece(make_piece(us, KING), Do ? from : to); + Piece castling_piece = piece_on(Do ? from : to); + remove_piece(castling_piece, Do ? from : to); remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(make_piece(us, KING), Do ? to : from); + put_piece(castling_piece, Do ? to : from); put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } @@ -1447,8 +1455,8 @@ bool Position::pos_is_ok() const { constexpr bool Fast = true; // Quick (default) or full check? if ( (sideToMove != WHITE && sideToMove != BLACK) - || piece_on(square(WHITE)) != make_piece(WHITE, KING) - || piece_on(square(BLACK)) != make_piece(BLACK, KING) + || (count(WHITE) && piece_on(square(WHITE)) != make_piece(WHITE, KING)) + || (count(BLACK) && piece_on(square(BLACK)) != make_piece(BLACK, KING)) || ( ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) assert(0 && "pos_is_ok: Default"); @@ -1456,9 +1464,8 @@ bool Position::pos_is_ok() const { if (Fast) return true; - if ( pieceCount[make_piece(WHITE, KING)] != 1 - || pieceCount[make_piece(BLACK, KING)] != 1 - || attackers_to(square(~sideToMove)) & pieces(sideToMove)) + if ( pieceCount[make_piece(~sideToMove, KING)] + && (attackers_to(square(~sideToMove)) & pieces(sideToMove))) assert(0 && "pos_is_ok: Kings"); if ( (pieces(PAWN) & Rank8BB) diff --git a/src/position.h b/src/position.h index 8331e6c..926c871 100644 --- a/src/position.h +++ b/src/position.h @@ -106,7 +106,9 @@ public: Value stalemate_value(int ply = 0) const; Value checkmate_value(int ply = 0) const; Value bare_king_value(int ply = 0) const; + Value extinction_value(int ply = 0) const; bool bare_king_move() const; + const std::set& extinction_piece_types() const; Bitboard capture_the_flag(Color c) const; bool flag_move() const; CheckCount max_check_count() const; @@ -127,6 +129,7 @@ public: Piece piece_on(Square s) const; Square ep_square() const; bool empty(Square s) const; + int count(Color c, PieceType pt) const; template int count(Color c) const; template int count() const; template const Square* squares(Color c) const; @@ -340,11 +343,24 @@ inline Value Position::bare_king_value(int ply) const { : v; } +inline Value Position::extinction_value(int ply) const { + assert(var != nullptr); + Value v = var->extinctionValue; + return v == VALUE_MATE ? mate_in(ply) + : v == -VALUE_MATE ? mated_in(ply) + : v; +} + inline bool Position::bare_king_move() const { assert(var != nullptr); return var->bareKingMove; } +inline const std::set& Position::extinction_piece_types() const { + assert(var != nullptr); + return var->extinctionPieceTypes; +} + inline Bitboard Position::capture_the_flag(Color c) const { assert(var != nullptr); return c == WHITE ? var->whiteFlag : var->blackFlag; @@ -385,13 +401,23 @@ inline bool Position::is_variant_end(Value& result, int ply) const { result = -bare_king_value(ply); return true; } + // extinction + if (extinction_value() != VALUE_NONE) + { + for (PieceType pt : extinction_piece_types()) + if (!count(WHITE, pt) || !count(BLACK, pt)) + { + result = !count(sideToMove, pt) ? extinction_value(ply) : -extinction_value(ply); + return true; + } + } // capture the flag - if (!flag_move() && (capture_the_flag(~sideToMove) & square(~sideToMove))) + if (count(~sideToMove) && !flag_move() && (capture_the_flag(~sideToMove) & square(~sideToMove))) { result = mated_in(ply); return true; } - if (flag_move() && (capture_the_flag(sideToMove) & square(sideToMove))) + if (count( sideToMove) && flag_move() && (capture_the_flag( sideToMove) & square( sideToMove))) { result = mate_in(ply); return true; @@ -447,6 +473,10 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); } +inline int Position::count(Color c, PieceType pt) const { + return pieceCount[make_piece(c, pt)]; +} + template inline int Position::count(Color c) const { return pieceCount[make_piece(c, Pt)]; } diff --git a/src/psqt.cpp b/src/psqt.cpp index a6a9cf7..ae83b84 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -26,11 +26,13 @@ Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, FersValueMg, AlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, - ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg }, + ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, + CommonerValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, FersValueEg, AlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, - ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg } + ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, + CommonerValueEg } }; namespace PSQT { diff --git a/src/types.h b/src/types.h index 94ff7a4..93868ea 100644 --- a/src/types.h +++ b/src/types.h @@ -209,6 +209,7 @@ enum Value : int { ShogiKnightValueMg = 300, ShogiKnightValueEg = 300, EuroShogiKnightValueMg = 400, EuroShogiKnightValueEg = 400, GoldValueMg = 600, GoldValueEg = 600, + CommonerValueMg = 600, CommonerValueEg = 600, MidgameLimit = 15258, EndgameLimit = 3915 }; @@ -218,7 +219,7 @@ const int PIECE_TYPE_BITS = 5; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS) enum PieceType { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, FERS, MET = FERS, ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS, CHANCELLOR, - AMAZON, KNIBIS, BISKNI, SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, KING, + AMAZON, KNIBIS, BISKNI, SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, COMMONER, KING, ALL_PIECES = 0, PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS diff --git a/src/variant.cpp b/src/variant.cpp index cc57253..fb9e908 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -128,6 +128,51 @@ void VariantMap::init() { v->mustCapture = true; return v; } (); + const Variant* giveaway = [&]{ + Variant* v = new Variant(); + v->remove_piece(KING); + v->add_piece(COMMONER, 'k'); + v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT}; + v->stalemateValue = VALUE_MATE; + v->extinctionValue = VALUE_MATE; + v->extinctionPieceTypes = {ALL_PIECES}; + v->mustCapture = true; + return v; + } (); + const Variant* antichess = [&]{ + Variant* v = new Variant(); + v->remove_piece(KING); + v->add_piece(COMMONER, 'k'); + v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1"; + v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT}; + v->stalemateValue = VALUE_MATE; + v->extinctionValue = VALUE_MATE; + v->extinctionPieceTypes = {ALL_PIECES}; + v->castling = false; + v->mustCapture = true; + return v; + } (); + const Variant* extinction = [&]{ + Variant* v = new Variant(); + v->remove_piece(KING); + v->add_piece(COMMONER, 'k'); + v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT}; + v->extinctionValue = -VALUE_MATE; + v->extinctionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT, PAWN}; + return v; + } (); + const Variant* kinglet = [&]{ + Variant* v = new Variant(); + v->remove_piece(KING); + v->add_piece(COMMONER, 'k'); + v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + v->promotionPieceTypes = {COMMONER}; + v->extinctionValue = -VALUE_MATE; + v->extinctionPieceTypes = {PAWN}; + return v; + } (); const Variant* threecheck = [&]{ Variant* v = new Variant(); v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+3 0 1"; @@ -263,6 +308,10 @@ void VariantMap::init() { add("kingofthehill", kingofthehill); add("racingkings", racingkings); add("losers", losers); + add("giveaway", giveaway); + add("antichess", antichess); + add("extinction", extinction); + add("kinglet", kinglet); add("3check", threecheck); add("5check", fivecheck); add("crazyhouse", crazyhouse); diff --git a/src/variant.h b/src/variant.h index 8644161..a79ffa7 100644 --- a/src/variant.h +++ b/src/variant.h @@ -54,7 +54,9 @@ struct Variant { Value stalemateValue = VALUE_DRAW; Value checkmateValue = -VALUE_MATE; Value bareKingValue = VALUE_NONE; + Value extinctionValue = VALUE_NONE; bool bareKingMove = false; + std::set extinctionPieceTypes = {}; Bitboard whiteFlag = 0; Bitboard blackFlag = 0; bool flagMove = false;