{ 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] = {
{ 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] = {
{}, // shogi knight
{}, // euroshogi knight
{}, // gold
+ {}, // commoner
{} // king
};
Direction sliderQuiet[][9] = {
{}, // shogi knight
{}, // euroshogi knight
{}, // gold
+ {}, // commoner
{} // king
};
int sliderDistCapture[] = {
0, // shogi knight
0, // euroshogi knight
0, // gold
+ 0, // commoner
0 // king
};
int sliderDistQuiet[] = {
0, // shogi knight
0, // euroshogi knight
0, // gold
+ 0, // commoner
0 // king
};
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<KING>(Us, pos.square<KING>(Us));
+ attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(Us, pos.square<KING>(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<KING>(Us) && pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg)
{
kingRing[Us] = attackedBy[Us][KING];
if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
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<KING>(Us));
+ if (pos.count<KING>(Us))
+ score -= KingProtector[Pt - 2] * distance(s, pos.square<KING>(Us));
if (Pt == BISHOP || Pt == KNIGHT)
{
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<KING>(Us))
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
+ if (!pos.count<KING>(Us))
+ return SCORE_ZERO;
+
const Square ksq = pos.square<KING>(Us);
Bitboard weak, b, b1, b2, safe, unsafeChecks;
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
auto king_proximity = [&](Color c, Square s) {
- return std::min(distance(pos.square<KING>(c), s), 5);
+ return pos.count<KING>(c) ? std::min(distance(pos.square<KING>(c), s), 5) : 5;
};
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
template<Tracing T>
Score Evaluation<T>::initiative(Value eg) const {
- int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
+ int outflanking = !pos.count<KING>(WHITE) || !pos.count<KING>(BLACK) ? 0
+ : distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
+ - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide);
// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
- Square kfrom = pos.square<KING>(us);
+ Square kfrom = pos.count<KING>(us) ? pos.square<KING>(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);
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<CASTLING>(kfrom, rfrom);
b2 &= target;
}
- if (Type == QUIET_CHECKS)
+ if (Type == QUIET_CHECKS && pos.count<KING>(Them))
{
Square ksq = pos.square<KING>(Them);
for (PieceType pt = PAWN; pt < KING; ++pt)
moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
- if (Type != QUIET_CHECKS && Type != EVASIONS)
+ if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
{
Square ksq = pos.square<KING>(Us);
Bitboard b = pos.attacks_from<KING>(Us, ksq) & target;
void Position::set_castling_right(Color c, Square rfrom) {
- Square kfrom = square<KING>(c);
+ Square kfrom = count<KING>(c) ? square<KING>(c) : make_square(FILE_E, c == WHITE ? RANK_1 : RANK_8);
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
CastlingRight cr = (c | cs);
void Position::set_check_info(StateInfo* si) const {
- si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinners[BLACK]);
- si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinners[WHITE]);
+ si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), count<KING>(WHITE) ? square<KING>(WHITE) : SQ_NONE, si->pinners[BLACK]);
+ si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), count<KING>(BLACK) ? square<KING>(BLACK) : SQ_NONE, si->pinners[WHITE]);
- Square ksq = square<KING>(~sideToMove);
+ Square ksq = count<KING>(~sideToMove) ? square<KING>(~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;
}
si->pawnKey = Zobrist::noPawns;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->psq = SCORE_ZERO;
- si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
+ si->checkersBB = count<KING>(sideToMove) ? attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove) : 0;
set_check_info(si);
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)
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
- Square ksq = square<KING>(us);
+ Square ksq = count<KING>(us) ? square<KING>(us) : SQ_NONE;
assert(color_of(moved_piece(m)) == us);
- assert(piece_on(square<KING>(us)) == make_piece(us, KING));
+ assert(!count<KING>(us) || piece_on(square<KING>(us)) == make_piece(us, KING));
// illegal moves to squares outside of board
if (rank_of(to) > max_rank() || file_of(to) > max_file())
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
- return !(attackers_to(ksq, occupied) & pieces(~us) & occupied);
+ return !count<KING>(us) || !(attackers_to(ksq, occupied) & pieces(~us) & occupied);
}
// If the moving piece is a king, check whether the destination
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<KING>(us) || !( attackers_to(ksq, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to)
& pieces(~us) & ~SquareBB[to]);
}
Square from = from_sq(m);
Square to = to_sq(m);
+ // No check possible without king
+ if (!count<KING>(~sideToMove))
+ return false;
+
// Is there a direct check?
if (st->checkSquares[type_of(moved_piece(m))] & to)
return true;
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;
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);
}
constexpr bool Fast = true; // Quick (default) or full check?
if ( (sideToMove != WHITE && sideToMove != BLACK)
- || piece_on(square<KING>(WHITE)) != make_piece(WHITE, KING)
- || piece_on(square<KING>(BLACK)) != make_piece(BLACK, KING)
+ || (count<KING>(WHITE) && piece_on(square<KING>(WHITE)) != make_piece(WHITE, KING))
+ || (count<KING>(BLACK) && piece_on(square<KING>(BLACK)) != make_piece(BLACK, KING))
|| ( ep_square() != SQ_NONE
&& relative_rank(sideToMove, ep_square()) != RANK_6))
assert(0 && "pos_is_ok: Default");
if (Fast)
return true;
- if ( pieceCount[make_piece(WHITE, KING)] != 1
- || pieceCount[make_piece(BLACK, KING)] != 1
- || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
+ if ( pieceCount[make_piece(~sideToMove, KING)]
+ && (attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove)))
assert(0 && "pos_is_ok: Kings");
if ( (pieces(PAWN) & Rank8BB)
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<PieceType>& extinction_piece_types() const;
Bitboard capture_the_flag(Color c) const;
bool flag_move() const;
CheckCount max_check_count() const;
Piece piece_on(Square s) const;
Square ep_square() const;
bool empty(Square s) const;
+ int count(Color c, PieceType pt) const;
template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) 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<PieceType>& 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;
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<KING>(~sideToMove)))
+ if (count<KING>(~sideToMove) && !flag_move() && (capture_the_flag(~sideToMove) & square<KING>(~sideToMove)))
{
result = mated_in(ply);
return true;
}
- if (flag_move() && (capture_the_flag(sideToMove) & square<KING>(sideToMove)))
+ if (count<KING>( sideToMove) && flag_move() && (capture_the_flag( sideToMove) & square<KING>( sideToMove)))
{
result = mate_in(ply);
return true;
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
}
+inline int Position::count(Color c, PieceType pt) const {
+ return pieceCount[make_piece(c, pt)];
+}
+
template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[make_piece(c, Pt)];
}
{ 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 {
ShogiKnightValueMg = 300, ShogiKnightValueEg = 300,
EuroShogiKnightValueMg = 400, EuroShogiKnightValueEg = 400,
GoldValueMg = 600, GoldValueEg = 600,
+ CommonerValueMg = 600, CommonerValueEg = 600,
MidgameLimit = 15258, EndgameLimit = 3915
};
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
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";
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);
Value stalemateValue = VALUE_DRAW;
Value checkmateValue = -VALUE_MATE;
Value bareKingValue = VALUE_NONE;
+ Value extinctionValue = VALUE_NONE;
bool bareKingMove = false;
+ std::set<PieceType> extinctionPieceTypes = {};
Bitboard whiteFlag = 0;
Bitboard blackFlag = 0;
bool flagMove = false;