/// Mate with KX vs K. This function is used to evaluate positions with /// king and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge /// of the board, and for keeping the distance between the two kings small. public Value KXK(Position pos) { Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); Debug.Assert(0 == pos.checkers()); // Eval is never called when in check // Stalemate detection with lone king if (pos.side_to_move() == weakSide && 0 == (new MoveList(pos, GenTypeS.LEGAL)).size()) { return(ValueS.VALUE_DRAW); } Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide, PieceTypeS.PAWN) * ValueS.PawnValueEg + PushToEdges[loserKSq] + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]; if (pos.count(strongSide, PieceTypeS.QUEEN) != 0 || pos.count(strongSide, PieceTypeS.ROOK) != 0 || (pos.count(strongSide, PieceTypeS.BISHOP) != 0 && pos.count(strongSide, PieceTypeS.KNIGHT) != 0) || pos.bishop_pair(strongSide)) { result += ValueS.VALUE_KNOWN_WIN; } return(strongSide == pos.side_to_move() ? result : -result); }
/// TranspositionTable::resize() sets the size of the transposition table, /// measured in megabytes. Transposition table consists of a power of 2 number /// of clusters and each cluster consists of ClusterSize number of TTEntry. public void resize(UInt64 mbSize) { Debug.Assert(BitBoard.msb((mbSize << 20) / 16) < 32); uint size = ClusterSize << BitBoard.msb((mbSize << 20) / 64); if (hashMask == size - ClusterSize) { return; } hashMask = size - ClusterSize; try { table = new TTEntry[size]; for (int i = 0; i < table.Length; i++) { table[i] = new TTEntry(); } } catch (Exception) { System.Console.Error.WriteLine("Failed to allocate " + mbSize + "MB for transposition table."); throw new Exception("Failed to allocate " + mbSize + "MB for transposition table."); } }
/// Entry::shelter_storm() calculates shelter and storm penalties for the file /// the king is on, as well as the two adjacent files. public Value shelter_storm(Position pos, Square ksq, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Value safety = Pawns.MaxSafetyBonus; Bitboard b = pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.in_front_bb(Us, Types.rank_of(ksq)) | BitBoard.rank_bb_square(ksq)); Bitboard ourPawns = b & pos.pieces_color(Us); Bitboard theirPawns = b & pos.pieces_color(Them); Rank rkUs, rkThem; File kf = Math.Max(FileS.FILE_B, Math.Min(FileS.FILE_G, Types.file_of(ksq))); for (File f = kf - 1; f <= kf + 1; ++f) { b = ourPawns & BitBoard.file_bb_file(f); rkUs = b != 0 ? Types.relative_rank_square(Us, BitBoard.backmost_sq(Us, b)) : RankS.RANK_1; b = theirPawns & BitBoard.file_bb_file(f); rkThem = b != 0 ? Types.relative_rank_square(Us, BitBoard.frontmost_sq(Them, b)) : RankS.RANK_1; if ((MiddleEdges & BitBoard.SquareBB[Types.make_square(f, rkThem)]) != 0 && Types.file_of(ksq) == f && Types.relative_rank_square(Us, ksq) == rkThem - 1) { safety += 200; } else { safety -= ShelterWeakness[rkUs] + StormDanger[rkUs == RankS.RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; } } return(safety); }
public KPKPosition(uint idx) { wksq = (Square)((idx >> 0) & 0x3F); bksq = (Square)((idx >> 6) & 0x3F); us = (Color)((idx >> 12) & 0x01); psq = Types.make_square((File)((idx >> 13) & 0x03), (Rank)(RankS.RANK_7 - (idx >> 15))); result = Result.UNKNOWN; // Check if two pieces are on the same square or if a king can be captured if (BitBoard.square_distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq || (us == ColorS.WHITE && (BitBoard.StepAttacksBB[PieceTypeS.PAWN][psq] & BitBoard.SquareBB[bksq]) != 0)) { result = Result.INVALID; } else if (us == ColorS.WHITE) { // Immediate win if pawn can be promoted without getting captured if (Types.rank_of(psq) == RankS.RANK_7 && wksq != psq + SquareS.DELTA_N && (BitBoard.square_distance(bksq, psq + SquareS.DELTA_N) > 1 || (BitBoard.StepAttacksBB[PieceTypeS.KING][wksq] & BitBoard.SquareBB[psq + SquareS.DELTA_N]) != 0)) { result = Result.WIN; } } // Immediate draw if it is a stalemate or a king captures undefended pawn else if (0 == (BitBoard.StepAttacksBB[PieceTypeS.KING][bksq] & ~(BitBoard.StepAttacksBB[PieceTypeS.KING][wksq] | BitBoard.StepAttacksBB[PieceTypeS.PAWN][psq])) || (BitBoard.StepAttacksBB[PieceTypeS.KING][bksq] & BitBoard.SquareBB[psq] & ~BitBoard.StepAttacksBB[PieceTypeS.KING][wksq]) != 0) { result = Result.DRAW; } }
// init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. public static void init_eval_info(Position pos, EvalInfo ei, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Square Down = (Us == ColorS.WHITE ? SquareS.DELTA_S : SquareS.DELTA_N); ei.pinnedPieces[Us] = pos.pinned_pieces(Us); Bitboard b = ei.attackedBy[Them][PieceTypeS.KING] = pos.attacks_from_square_piecetype(pos.king_square(Them), PieceTypeS.KING); ei.attackedBy[Us][PieceTypeS.ALL_PIECES] = ei.attackedBy[Us][PieceTypeS.PAWN] = ei.pi.pawn_attacks(Us); // Init king safety tables only if we are going to use them if (pos.count(Us, PieceTypeS.QUEEN) != 0 && pos.non_pawn_material(Us) > ValueS.QueenValueMg + ValueS.PawnValueMg) { ei.kingRing[Them] = b | BitBoard.shift_bb(b, Down); b &= ei.attackedBy[Us][PieceTypeS.PAWN]; ei.kingAttackersCount[Us] = (b != 0) ? Bitcount.popcount_Max15(b) : 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; } else { ei.kingRing[Them] = 0; ei.kingAttackersCount[Us] = 0; } }
// evaluate_outposts() evaluates bishop and knight outpost squares public static Score evaluate_outposts(Position pos, EvalInfo ei, Square s, PieceType Pt, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Debug.Assert(Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT); // Initial bonus based on square Value bonus = Outpost[Pt == PieceTypeS.BISHOP ? 1 : 0][Types.relative_square(Us, s)]; // Increase bonus if supported by pawn, especially if the opponent has // no minor piece which can trade with the outpost piece. if (bonus != 0 && (ei.attackedBy[Us][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0) { if (0 == pos.pieces_color_piecetype(Them, PieceTypeS.KNIGHT) && 0 == (BitBoard.squares_of_color(s) & pos.pieces_color_piecetype(Them, PieceTypeS.BISHOP))) { bonus += bonus + bonus / 2; } else { bonus += bonus / 2; } } return(Types.make_score(bonus, bonus)); }
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. public static int generate_quiet_checks(Position pos, ExtMove[] mlist, int mPos) { Debug.Assert(0 == pos.checkers()); Color us = pos.side_to_move(); CheckInfo ci = new CheckInfo(pos); Bitboard dc = ci.dcCandidates; while (dc != 0) { Square from = BitBoard.pop_lsb(ref dc); PieceType pt = Types.type_of_piece(pos.piece_on(from)); if (pt == PieceTypeS.PAWN) { continue; // Will be generated togheter with direct checks } Bitboard b = pos.attacks_from_piece_square((Piece)pt, from) & ~pos.pieces(); if (pt == PieceTypeS.KING) { b &= ~BitBoard.PseudoAttacks[PieceTypeS.QUEEN][ci.ksq]; } while (b != 0) { mlist[mPos++].move = Types.make_move(from, BitBoard.pop_lsb(ref b)); } } return(us == ColorS.WHITE ? generate_all(pos, mlist, mPos, ~pos.pieces(), ColorS.WHITE, GenTypeS.QUIET_CHECKS, ci) : generate_all(pos, mlist, mPos, ~pos.pieces(), ColorS.BLACK, GenTypeS.QUIET_CHECKS, ci)); }
public static int generate_promotions(ExtMove[] mlist, int mPos, Bitboard pawnsOn7, Bitboard target, CheckInfo ci, GenType Type, Square Delta) { Bitboard b = BitBoard.shift_bb(pawnsOn7, Delta) & target; while (b != 0) { Square to = BitBoard.pop_lsb(ref b); if (Type == GenTypeS.CAPTURES || Type == GenTypeS.EVASIONS || Type == GenTypeS.NON_EVASIONS) { mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.QUEEN); } if (Type == GenTypeS.QUIETS || Type == GenTypeS.EVASIONS || Type == GenTypeS.NON_EVASIONS) { mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.ROOK); mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.BISHOP); mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.KNIGHT); } // Knight promotion is the only promotion that can give a direct check // that's not already included in the queen promotion. if (Type == GenTypeS.QUIET_CHECKS && (BitBoard.StepAttacksBB[PieceS.W_KNIGHT][to] & BitBoard.SquareBB[ci.ksq]) != 0) { mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.KNIGHT); } } return(mPos); }
// polyglot_key() returns the PolyGlot hash key of the given position public static Key polyglot_key(Position pos) { Key key = 0; Bitboard b = pos.pieces(); while (b != 0) { Square s = BitBoard.pop_lsb(ref b); Piece pc = pos.piece_on(s); // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 int pieceOfs = 2 * (Types.type_of_piece(pc) - 1) + ((Types.color_of(pc) == ColorS.WHITE) ? 1 : 0); key ^= PG[psq + (64 * pieceOfs + s)]; } b = (ulong)pos.can_castle_castleright(CastlingRightS.ANY_CASTLING); while (b != 0) { key ^= PG[castle + BitBoard.pop_lsb(ref b)]; } if (pos.ep_square() != SquareS.SQ_NONE) { key ^= PG[enpassant + Types.file_of(pos.ep_square())]; } if (pos.side_to_move() == ColorS.WHITE) { key ^= PG[turn + 0]; } return(key); }
/// KBP vs KB. There are two rules: if the defending king is somewhere along the /// path of the pawn, and the square of the king is not of the same color as the /// stronger side's bishop, it's a draw. If the two bishops have opposite color, /// it's almost always a draw. public ScaleFactor KBPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); Square pawnSq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square strongerBishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square weakerBishopSq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square weakerKingSq = pos.king_square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away if (Types.file_of(weakerKingSq) == Types.file_of(pawnSq) && Types.relative_rank_square(strongSide, pawnSq) < Types.relative_rank_square(strongSide, weakerKingSq) && (Types.opposite_colors(weakerKingSq, strongerBishopSq) || Types.relative_rank_square(strongSide, weakerKingSq) <= RankS.RANK_6)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // Case 2: Opposite colored bishops if (Types.opposite_colors(strongerBishopSq, weakerBishopSq)) { // We assume that the position is drawn in the following three situations: // // a. The pawn is on rank 5 or further back. // b. The defending king is somewhere in the pawn's path. // c. The defending bishop attacks some square along the pawn's path, // and is at least three squares away from the pawn. // // These rules are probably not perfect, but in practice they work // reasonably well. if (Types.relative_rank_square(strongSide, pawnSq) <= RankS.RANK_5) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { Bitboard path = BitBoard.forward_bb(strongSide, pawnSq); if ((path & pos.pieces_color_piecetype(weakSide, PieceTypeS.KING)) != 0) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } if (((pos.attacks_from_square_piecetype(weakerBishopSq, PieceTypeS.BISHOP) & path) != 0) && BitBoard.square_distance(weakerBishopSq, pawnSq) >= 3) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
// evaluate_unstoppable_pawns() scores the most advanced among the passed and // candidate pawns. In case opponent has no pieces but pawns, this is somewhat // related to the possibility that pawns are unstoppable. public static Score evaluate_unstoppable_pawns(Position pos, Color us, EvalInfo ei) { Bitboard b = ei.pi.passed_pawns(us) | ei.pi.candidate_pawns(us); if (0 == b || pos.non_pawn_material(Types.notColor(us)) != 0) { return(ScoreS.SCORE_ZERO); } return(Unstoppable * (Types.relative_rank_square(us, BitBoard.frontmost_sq(us, b)))); }
/// KR vs KN. The attacking side has slightly better winning chances than /// in KR vs KB, particularly if the king and the knight are far apart. public Value KRKN(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.KnightValueMg, 0)); Square bksq = pos.king_square(weakSide); Square bnsq = pos.list(weakSide, PieceTypeS.KNIGHT)[0]; Value result = (PushToEdges[bksq] + PushAway[BitBoard.square_distance(bksq, bnsq)]); return(strongSide == pos.side_to_move() ? result : -result); }
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move for /// the defending side in the search, this is usually sufficient to win KQ vs KR. public Value KQKR(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Value result = ValueS.QueenValueEg - ValueS.RookValueEg + PushToEdges[loserKSq] + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]; return(strongSide == pos.side_to_move() ? result : -result); }
/// KNP vs KB. If knight can block bishop from taking pawn, it's a win. /// Otherwise the position is drawn. public ScaleFactor KNPKB(Position pos) { Square pawnSq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square bishopSq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square weakerKingSq = pos.king_square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if ((BitBoard.forward_bb(strongSide, pawnSq) & pos.attacks_from_square_piecetype(bishopSq, PieceTypeS.BISHOP)) != 0) { return(BitBoard.square_distance(weakerKingSq, pawnSq)); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
public static int generate_castling(Position pos, ExtMove[] mlist, int mPos, Color us, CheckInfo ci, CastlingRight Cr, bool Checks, bool Chess960) { bool KingSide = (Cr == CastlingRightS.WHITE_OO || Cr == CastlingRightS.BLACK_OO); if (pos.castling_impeded(Cr) || 0 == pos.can_castle_castleright(Cr)) { return(mPos); } // After castling, the rook and king final positions are the same in Chess960 // as they would be in standard chess. Square kfrom = pos.king_square(us); Square rfrom = pos.castling_rook_square(Cr); Square kto = Types.relative_square(us, KingSide ? SquareS.SQ_G1 : SquareS.SQ_C1); Bitboard enemies = pos.pieces_color(Types.notColor(us)); Debug.Assert(0 == pos.checkers()); Square K = Chess960 ? kto > kfrom ? SquareS.DELTA_W : SquareS.DELTA_E : KingSide ? SquareS.DELTA_W : SquareS.DELTA_E; for (Square s = kto; s != kfrom; s += K) { if ((pos.attackers_to(s) & enemies) != 0) { return(mPos); } } // 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 && (BitBoard.attacks_bb_SBBPT(kto, pos.pieces() ^ BitBoard.SquareBB[rfrom], PieceTypeS.ROOK) & pos.pieces_color_piecetype(Types.notColor(us), PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0) { return(mPos); } Move m = Types.make(kfrom, rfrom, MoveTypeS.CASTLING); if (Checks && !pos.gives_check(m, ci)) { return(mPos); } mlist[mPos++].move = m; return(mPos); }
public ScaleFactor KRPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); // Test for a rook pawn if ((pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.FileABB | BitBoard.FileHBB)) != 0) { Square ksq = pos.king_square(weakSide); Square bsq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square psq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Rank rk = Types.relative_rank_square(strongSide, psq); Square push = Types.pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on // the same color square as the bishop then there is a chance of // a fortress. Depending on the king position give a moderate // reduction or a stronger one if the defending king is near the // corner but not trapped there. if (rk == RankS.RANK_5 && !Types.opposite_colors(bsq, psq)) { int d = BitBoard.square_distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) { return(24); } else { return(48); } } // When the pawn has moved to the 6th rank we can be fairly sure // it's drawn if the bishop attacks the square in front of the // pawn from a reasonable distance and the defending king is near // the corner if (rk == RankS.RANK_6 && BitBoard.square_distance(psq + 2 * push, ksq) <= 1 && (BitBoard.PseudoAttacks[PieceTypeS.BISHOP][bsq] & BitBoard.SquareBB[(psq + push)]) != 0 && BitBoard.file_distance(bsq, psq) >= 2) { return(8); } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank /// and the defending king prevents the pawn from advancing, the position is drawn. public ScaleFactor KNPK(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.KnightValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D Square pawnSq = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.PAWN)[0]); Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); if (pawnSq == SquareS.SQ_A7 && BitBoard.square_distance(SquareS.SQ_A8, weakKingSq) <= 1) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without /// a bitbase. The function below returns drawish scores when the pawn is /// far advanced with support of the king, while the attacking king is far /// away. public Value KRKP(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 1)); Square wksq = Types.relative_square(strongSide, pos.king_square(strongSide)); Square bksq = Types.relative_square(strongSide, pos.king_square(weakSide)); Square rsq = Types.relative_square(strongSide, pos.list(strongSide, PieceTypeS.ROOK)[0]); Square psq = Types.relative_square(strongSide, pos.list(weakSide, PieceTypeS.PAWN)[0]); Square queeningSq = Types.make_square(Types.file_of(psq), RankS.RANK_1); Value result; // If the stronger side's king is in front of the pawn, it's a win if (wksq < psq && Types.file_of(wksq) == Types.file_of(psq)) { result = ValueS.RookValueEg - (BitBoard.square_distance(wksq, psq)); } // If the weaker side's king is too far from the pawn and the rook, // it's a win else if (BitBoard.square_distance(bksq, psq) >= 3 + ((pos.side_to_move() == weakSide)?1:0) && BitBoard.square_distance(bksq, rsq) >= 3) { result = ValueS.RookValueEg - (BitBoard.square_distance(wksq, psq)); } // If the pawn is far advanced and supported by the defending king, // the position is drawish else if (Types.rank_of(bksq) <= RankS.RANK_3 && BitBoard.square_distance(bksq, psq) == 1 && Types.rank_of(wksq) >= RankS.RANK_4 && BitBoard.square_distance(wksq, psq) > 2 + ((pos.side_to_move() == strongSide)?1:0)) { result = 80 - 8 * BitBoard.square_distance(wksq, psq); } else { result = (Value)(200) - 8 * (BitBoard.square_distance(wksq, psq + SquareS.DELTA_S) - BitBoard.square_distance(bksq, psq + SquareS.DELTA_S) - BitBoard.square_distance(psq, queeningSq)); } return(strongSide == pos.side_to_move() ? result : -result); }
public Engine(string[] args) { inOut.WriteLine(Misc.engine_info(), MutexAction.ATOMIC); Uci.init(Options); BitBoard.init(); Position.init(); Bitbases.init_kpk(); Search.init(); Pawns.init(); Eval.init(); Threads.init(); TT.resize((ulong)Options["Hash"].getInt()); Uci.loop(args); Threads.exit(); }
public Result classify(KPKPosition[] db, Color Us) { // White to Move: If one move leads to a position classified as WIN, the result // of the current position is WIN. If all moves lead to positions classified // as DRAW, the current position is classified as DRAW, otherwise the current // position is classified as UNKNOWN. // // Black to Move: If one move leads to a position classified as DRAW, the result // of the current position is DRAW. If all moves lead to positions classified // as WIN, the position is classified as WIN, otherwise the current position is // classified as UNKNOWN. Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Result r = Result.INVALID; Bitboard b = BitBoard.StepAttacksBB[PieceTypeS.KING][Us == ColorS.WHITE ? wksq : bksq]; while (b != 0) { r |= (Us == ColorS.WHITE) ? db[Bitbases.index(Them, bksq, BitBoard.pop_lsb(ref b), psq)].result : db[Bitbases.index(Them, BitBoard.pop_lsb(ref b), wksq, psq)].result; } if (Us == ColorS.WHITE && Types.rank_of(psq) < RankS.RANK_7) { Square s = (psq + SquareS.DELTA_N); r |= db[Bitbases.index(ColorS.BLACK, bksq, wksq, s)].result; // Single push if (Types.rank_of(psq) == RankS.RANK_2 && s != wksq && s != bksq) { r |= db[Bitbases.index(ColorS.BLACK, bksq, wksq, s + SquareS.DELTA_N)].result; // Double push } } if (Us == ColorS.WHITE) { return(result = (r & Result.WIN) != 0 ? Result.WIN : (r & Result.UNKNOWN) != 0 ? Result.UNKNOWN : Result.DRAW); } else { return(result = (r & Result.DRAW) != 0 ? Result.DRAW : (r & Result.UNKNOWN) != 0 ? Result.UNKNOWN : Result.WIN); } }
// evaluate_threats() assigns bonuses according to the type of attacking piece // and the type of attacked one. public static Score evaluate_threats(Position pos, EvalInfo ei, Color Us, bool Trace) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Bitboard b, weakEnemies; Score score = ScoreS.SCORE_ZERO; // Enemies not defended by a pawn and under our attack weakEnemies = pos.pieces_color(Them) & ~ei.attackedBy[Them][PieceTypeS.PAWN] & ei.attackedBy[Us][PieceTypeS.ALL_PIECES]; // Add a bonus according if the attacking pieces are minor or major if (weakEnemies != 0) { b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.PAWN] | ei.attackedBy[Us][PieceTypeS.KNIGHT] | ei.attackedBy[Us][PieceTypeS.BISHOP]); if (b != 0) { score += Threat[0][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))]; } b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.ROOK] | ei.attackedBy[Us][PieceTypeS.QUEEN]); if (b != 0) { score += Threat[1][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))]; } b = weakEnemies & ~ei.attackedBy[Them][PieceTypeS.ALL_PIECES]; if (b != 0) { score += BitBoard.more_than_one(b) ? Hanging[Us != pos.side_to_move()?1:0] * Bitcount.popcount_Max15(b) : Hanging[Us == pos.side_to_move()?1:0]; } } if (Trace) { Tracing.terms[Us][TermsS.THREAT] = score; } return(score); }
/// KQ vs KP. In general, this is a win for the stronger side, but there are a /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files /// with a king positioned next to it can be a draw, so in that case, we only /// use the distance between the kings. public Value KQKP(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 1)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square pawnSq = pos.list(weakSide, PieceTypeS.PAWN)[0]; Value result = (PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]); if (Types.relative_rank_square(weakSide, pawnSq) != RankS.RANK_7 || BitBoard.square_distance(loserKSq, pawnSq) != 1 || 0 == ((BitBoard.FileABB | BitBoard.FileCBB | BitBoard.FileFBB | BitBoard.FileHBB) & BitBoard.SquareBB[pawnSq])) { result += ValueS.QueenValueEg - ValueS.PawnValueEg; } return(strongSide == pos.side_to_move() ? result : -result); }
public static Bitboard sliding_attack(Square[] deltas, Square sq, Bitboard occupied) { Bitboard attack = 0; for (int i = 0; i < 4; ++i) { for (Square s = sq + deltas[i]; Types.is_ok_square(s) && BitBoard.square_distance(s, s - deltas[i]) == 1; s += deltas[i]) { attack |= SquareBB[s]; if ((occupied & SquareBB[s]) != 0) { break; } } } return(attack); }
/// K and two or more pawns vs K. There is just a single rule here: If all pawns /// are on the same rook file and are blocked by the defending king, it's a draw. public ScaleFactor KPsK(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.VALUE_ZERO); Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 2); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); Square ksq = pos.king_square(weakSide); Bitboard pawns = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN); Square psq = pos.list(strongSide, PieceTypeS.PAWN)[0]; // If all pawns are ahead of the king, on a single rook file and // the king is within one file of the pawns, it's a draw. if (0 == (pawns & ~BitBoard.in_front_bb(weakSide, Types.rank_of(ksq))) && !((pawns & ~BitBoard.FileABB) != 0 && (pawns & ~BitBoard.FileHBB) != 0) && BitBoard.file_distance(ksq, psq) <= 1) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
public static int generate_moves(Position pos, ExtMove[] mlist, int mPos, Color us, Bitboard target, CheckInfo ci, PieceType Pt, bool Checks) { Debug.Assert(Pt != PieceTypeS.KING && Pt != PieceTypeS.PAWN); Square[] pieceList = pos.list(us, Pt); int pl = 0; for (Square from = pieceList[pl]; from != SquareS.SQ_NONE; from = pieceList[++pl]) { if (Checks) { if ((Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.ROOK || Pt == PieceTypeS.QUEEN) && 0 == (BitBoard.PseudoAttacks[Pt][from] & target & ci.checkSq[Pt])) { continue; } if (ci.dcCandidates != 0 && (ci.dcCandidates & BitBoard.SquareBB[from]) != 0) { continue; } } Bitboard b = pos.attacks_from_square_piecetype(from, Pt) & target; if (Checks) { b &= ci.checkSq[Pt]; } while (b != 0) { mlist[mPos++].move = Types.make_move(from, BitBoard.pop_lsb(ref b)); } } return(mPos); }
/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed /// pawns and the defending king is actively placed, the position is drawish. public ScaleFactor KRPPKRP(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 1)); Square wpsq1 = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square wpsq2 = pos.list(strongSide, PieceTypeS.PAWN)[1]; Square bksq = pos.king_square(weakSide); // Does the stronger side have a passed pawn? if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) { return(ScaleFactorS.SCALE_FACTOR_NONE); } Rank r = Math.Max(Types.relative_rank_square(strongSide, wpsq1), Types.relative_rank_square(strongSide, wpsq2)); if (BitBoard.file_distance(bksq, wpsq1) <= 1 && BitBoard.file_distance(bksq, wpsq2) <= 1 && Types.relative_rank_square(strongSide, bksq) > r) { switch (r) { case RankS.RANK_2: return(10); case RankS.RANK_3: return(10); case RankS.RANK_4: return(15); case RankS.RANK_5: return(20); case RankS.RANK_6: return(40); default: Debug.Assert(false); break; } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side /// to move is in check. Returns a pointer to the end of the move list. public static int generate_evasions(Position pos, ExtMove[] mlist, int mPos) { Debug.Assert(pos.checkers() != 0); Color us = pos.side_to_move(); Square ksq = pos.king_square(us); Bitboard sliderAttacks = 0; Bitboard sliders = pos.checkers() & ~pos.pieces_piecetype(PieceTypeS.KNIGHT, PieceTypeS.PAWN); // Find all the squares attacked by slider checkers. We will remove them from // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders != 0) { Square checksq = BitBoard.pop_lsb(ref sliders); sliderAttacks |= BitBoard.LineBB[checksq][ksq] ^ BitBoard.SquareBB[checksq]; } // Generate evasions for king, capture and non capture moves Bitboard b = pos.attacks_from_square_piecetype(ksq, PieceTypeS.KING) & ~pos.pieces_color(us) & ~sliderAttacks; while (b != 0) { mlist[mPos++].move = Types.make_move(ksq, BitBoard.pop_lsb(ref b)); } if (BitBoard.more_than_one(pos.checkers())) { return(mPos); // Double check, only a king move can save the day } // Generate blocking evasions or captures of the checking piece Square checksq2 = BitBoard.lsb(pos.checkers()); Bitboard target = BitBoard.between_bb(checksq2, ksq) | BitBoard.SquareBB[checksq2]; return(us == ColorS.WHITE ? generate_all(pos, mlist, mPos, target, ColorS.WHITE, GenTypeS.EVASIONS) : generate_all(pos, mlist, mPos, target, ColorS.BLACK, GenTypeS.EVASIONS)); }
public static int generate_all(Position pos, ExtMove[] mlist, int mPos, Bitboard target, Color us, GenType Type, CheckInfo ci = null) { bool Checks = Type == GenTypeS.QUIET_CHECKS; mPos = generate_pawn_moves(pos, mlist, mPos, target, ci, us, Type); mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.KNIGHT, Checks); mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.BISHOP, Checks); mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.ROOK, Checks); mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.QUEEN, Checks); if (Type != GenTypeS.QUIET_CHECKS && Type != GenTypeS.EVASIONS) { Square ksq = pos.king_square(us); Bitboard b = pos.attacks_from_square_piecetype(ksq, PieceTypeS.KING) & target; while (b != 0) { mlist[mPos++].move = Types.make_move(ksq, BitBoard.pop_lsb(ref b)); } } if (Type != GenTypeS.CAPTURES && Type != GenTypeS.EVASIONS && pos.can_castle_color(us) != 0) { if (pos.is_chess960() != 0) { mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.KING_SIDE)).right, Checks, true); mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.QUEEN_SIDE)).right, Checks, true); } else { mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.KING_SIDE)).right, Checks, false); mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.QUEEN_SIDE)).right, Checks, false); } } return(mPos); }
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// defending king towards a corner square of the right color. public Value KBNK(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.KnightValueMg + ValueS.BishopValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a // bishop that cannot reach the above squares, we flip the kings in order // to drive the enemy toward corners A8 or H1. if (Types.opposite_colors(bishopSq, SquareS.SQ_A1)) { winnerKSq = Types.notSquare(winnerKSq); loserKSq = Types.notSquare(loserKSq); } Value result = ValueS.VALUE_KNOWN_WIN + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return(strongSide == pos.side_to_move() ? result : -result); }
/// move_to_san() takes a position and a legal Move as input and returns its /// short algebraic notation representation. public static string move_to_san(Position pos, Move m) { if (m == MoveS.MOVE_NONE) { return("(none)"); } if (m == MoveS.MOVE_NULL) { return("(null)"); } Debug.Assert((new MoveList(pos, GenTypeS.LEGAL).contains(m))); Bitboard others, b; string san = ""; Color us = pos.side_to_move(); Square from = Types.from_sq(m); Square to = Types.to_sq(m); Piece pc = pos.piece_on(from); PieceType pt = Types.type_of_piece(pc); if (Types.type_of_move(m) == MoveTypeS.CASTLING) { san = to > from ? "O-O" : "O-O-O"; } else { if (pt != PieceTypeS.PAWN) { san = "" + PieceToChar[ColorS.WHITE][pt]; // Upper case // A disambiguation occurs if we have more then one piece of type 'pt' // that can reach 'to' with a legal move. others = b = (pos.attacks_from_piece_square(pc, to) & pos.pieces_color_piecetype(us, pt)) ^ BitBoard.SquareBB[from]; while (b != 0) { Square s = BitBoard.pop_lsb(ref b); if (!pos.legal(Types.make_move(s, to), pos.pinned_pieces(us))) { others ^= BitBoard.SquareBB[s]; } } if (0 == others) { /* Disambiguation is not needed */ } else if (0 == (others & BitBoard.file_bb_square(from))) { san += Types.file_to_char(Types.file_of(from)); } else if (0 == (others & BitBoard.rank_bb_square(from))) { san += Types.rank_to_char(Types.rank_of(from)); } else { san += Types.square_to_string(from); } } else if (pos.capture(m)) { san = "" + Types.file_to_char(Types.file_of(from)); } if (pos.capture(m)) { san += 'x'; } san += Types.square_to_string(to); if (Types.type_of_move(m) == MoveTypeS.PROMOTION) { san += "=" + PieceToChar[ColorS.WHITE][Types.promotion_type(m)]; } } if (pos.gives_check(m, new CheckInfo(pos))) { StateInfo st = new StateInfo(); pos.do_move(m, st); san += (new MoveList(pos, GenTypeS.LEGAL)).size() > 0 ? "+" : "#"; pos.undo_move(m); } return(san); }