/// move_to_uci() converts a move to a string in coordinate notation /// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print /// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 /// mode. Internally castling moves are always encoded as "king captures rook". public static string move_to_uci(Move m, bool chess960) { Square from = Types.from_sq(m); Square to = Types.to_sq(m); if (m == MoveS.MOVE_NONE) { return("(none)"); } if (m == MoveS.MOVE_NULL) { return("0000"); } if (Types.type_of_move(m) == MoveTypeS.CASTLING && !chess960) { to = Types.make_square((to > from ? FileS.FILE_G : FileS.FILE_C), Types.rank_of(from)); } string move = Types.square_to_string(from) + Types.square_to_string(to); if (Types.type_of_move(m) == MoveTypeS.PROMOTION) { move += PieceToChar[ColorS.BLACK][Types.promotion_type(m)]; // Lower case } return(move); }
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; } }
/// 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); }
/// 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); }
/// Bitboards::pretty() returns an ASCII representation of a bitboard to be /// printed to standard output. This is sometimes useful for debugging. public static String pretty(Bitboard b) { StringBuilder sb = new StringBuilder("+---+---+---+---+---+---+---+---+"); sb.Append(Types.newline); for (Rank r = RankS.RANK_8; r >= RankS.RANK_1; --r) { for (File f = FileS.FILE_A; f <= FileS.FILE_H; ++f) { sb.Append((b & SquareBB[Types.make_square(f, r)]) != 0 ? "| X " : "| "); } sb.Append("|"); sb.Append(Types.newline); sb.Append("+---+---+---+---+---+---+---+---+"); sb.Append(Types.newline); } return(sb.ToString()); }
/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops public ScaleFactor KBPPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); Square wbsq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square bbsq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; if (!Types.opposite_colors(wbsq, bbsq)) { return(ScaleFactorS.SCALE_FACTOR_NONE); } Square ksq = pos.king_square(weakSide); Square psq1 = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square psq2 = pos.list(strongSide, PieceTypeS.PAWN)[1]; Rank r1 = Types.rank_of(psq1); Rank r2 = Types.rank_of(psq2); Square blockSq1, blockSq2; if (Types.relative_rank_square(strongSide, psq1) > Types.relative_rank_square(strongSide, psq2)) { blockSq1 = psq1 + Types.pawn_push(strongSide); blockSq2 = Types.make_square(Types.file_of(psq2), Types.rank_of(psq1)); } else { blockSq1 = psq2 + Types.pawn_push(strongSide); blockSq2 = Types.make_square(Types.file_of(psq1), Types.rank_of(psq2)); } switch (BitBoard.file_distance(psq1, psq2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly // controls some square in the frontmost pawn's path. if (Types.file_of(ksq) == Types.file_of(blockSq1) && Types.relative_rank_square(strongSide, ksq) >= Types.relative_rank_square(strongSide, blockSq1) && Types.opposite_colors(ksq, wbsq)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { return(ScaleFactorS.SCALE_FACTOR_NONE); } case 1: // Pawns on adjacent files. It's a draw if the defender firmly controls the // square in front of the frontmost pawn's path, and the square diagonally // behind this square on the file of the other pawn. if (ksq == blockSq1 && Types.opposite_colors(ksq, wbsq) && (bbsq == blockSq2 || (pos.attacks_from_square_piecetype(blockSq2, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0 || Math.Abs(r1 - r2) >= 2)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else if (ksq == blockSq2 && Types.opposite_colors(ksq, wbsq) && (bbsq == blockSq1 || (pos.attacks_from_square_piecetype(blockSq1, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { return(ScaleFactorS.SCALE_FACTOR_NONE); } default: // The pawns are not on the same file or adjacent files. No scaling. return(ScaleFactorS.SCALE_FACTOR_NONE); } }
/// KRP vs KR. This function knows a handful of the most important classes of /// drawn positions, but is far from perfect. It would probably be a good idea /// to add more knowledge in the future. /// /// It would also be nice to rewrite the actual code for this function, /// which is mostly copied from Glaurung 1.x, and isn't very pretty. public ScaleFactor KRPKR(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 0)); // Assume strongSide is white and the pawn is on files A-D Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); Square wrsq = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.ROOK)[0]); Square wpsq = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.PAWN)[0]); Square brsq = normalize(pos, strongSide, pos.list(weakSide, PieceTypeS.ROOK)[0]); File f = Types.file_of(wpsq); Rank r = Types.rank_of(wpsq); Square queeningSq = Types.make_square(f, RankS.RANK_8); int tempo = (pos.side_to_move() == strongSide ? 1 : 0); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. if (r <= RankS.RANK_5 && BitBoard.square_distance(bksq, queeningSq) <= 1 && wksq <= SquareS.SQ_H5 && (Types.rank_of(brsq) == RankS.RANK_6 || (r <= RankS.RANK_3 && Types.rank_of(wrsq) != RankS.RANK_6))) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. if (r == RankS.RANK_6 && BitBoard.square_distance(bksq, queeningSq) <= 1 && Types.rank_of(wksq) + tempo <= RankS.RANK_6 && (Types.rank_of(brsq) == RankS.RANK_1 || (0 == tempo && Math.Abs(Types.file_of(brsq) - f) >= 3))) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } if (r >= RankS.RANK_6 && bksq == queeningSq && Types.rank_of(brsq) == RankS.RANK_1 && (0 == tempo || BitBoard.square_distance(wksq, wpsq) >= 2)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // and the black rook is behind the pawn. if (wpsq == SquareS.SQ_A7 && wrsq == SquareS.SQ_A8 && (bksq == SquareS.SQ_H7 || bksq == SquareS.SQ_G7) && Types.file_of(brsq) == FileS.FILE_A && (Types.rank_of(brsq) <= RankS.RANK_3 || Types.file_of(wksq) >= FileS.FILE_D || Types.rank_of(wksq) <= RankS.RANK_5)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. if (r <= RankS.RANK_5 && bksq == wpsq + SquareS.DELTA_N && BitBoard.square_distance(wksq, wpsq) - tempo >= 2 && BitBoard.square_distance(wksq, brsq) - tempo >= 2) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // Pawn on the 7th rank supported by the rook from behind usually wins if the // attacking king is closer to the queening square than the defending king, // and the defending king cannot gain tempi by threatening the attacking rook. if (r == RankS.RANK_7 && f != FileS.FILE_A && Types.file_of(wrsq) == f && wrsq != queeningSq && (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, queeningSq) - 2 + tempo) && (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, wrsq) + tempo)) { return((ScaleFactor)(ScaleFactorS.SCALE_FACTOR_MAX - 2 * BitBoard.square_distance(wksq, queeningSq))); } // Similar to the above, but with the pawn further back if (f != FileS.FILE_A && Types.file_of(wrsq) == f && wrsq < wpsq && (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, queeningSq) - 2 + tempo) && (BitBoard.square_distance(wksq, wpsq + SquareS.DELTA_N) < BitBoard.square_distance(bksq, wpsq + SquareS.DELTA_N) - 2 + tempo) && (BitBoard.square_distance(bksq, wrsq) + tempo >= 3 || (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, wrsq) + tempo && (BitBoard.square_distance(wksq, wpsq + SquareS.DELTA_N) < BitBoard.square_distance(bksq, wrsq) + tempo)))) { return((ScaleFactor)(ScaleFactorS.SCALE_FACTOR_MAX - 8 * BitBoard.square_distance(wpsq, queeningSq) - 2 * BitBoard.square_distance(wksq, queeningSq))); } // If the pawn is not far advanced, and the defending king is somewhere in // the pawn's path, it's probably a draw. if (r <= RankS.RANK_4 && bksq > wpsq) { if (Types.file_of(bksq) == Types.file_of(wpsq)) { return(10); } if (Math.Abs(Types.file_of(bksq) - Types.file_of(wpsq)) == 1 && BitBoard.square_distance(wksq, bksq) > 2) { return(24 - 2 * BitBoard.square_distance(wksq, bksq)); } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KB and one or more pawns vs K. It checks for draws with rook pawns and /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// will be used. public ScaleFactor KBPsK(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.BishopValueMg); Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 1); // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. Bitboard pawns = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN); File pawnFile = Types.file_of(pos.list(strongSide, PieceTypeS.PAWN)[0]); // All pawns are on a single rook file ? if ((pawnFile == FileS.FILE_A || pawnFile == FileS.FILE_H) && 0 == (pawns & ~BitBoard.file_bb_file(pawnFile))) { Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square queeningSq = Types.relative_square(strongSide, Types.make_square(pawnFile, RankS.RANK_8)); Square kingSq = pos.king_square(weakSide); if (Types.opposite_colors(queeningSq, bishopSq) && BitBoard.square_distance(queeningSq, kingSq) <= 1) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } // If all the pawns are on the same B or G file, then it's potentially a draw if ((pawnFile == FileS.FILE_B || pawnFile == FileS.FILE_G) && 0 == (pos.pieces_piecetype(PieceTypeS.PAWN) & ~BitBoard.file_bb_file(pawnFile)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide, PieceTypeS.PAWN) >= 1) { // Get weakSide pawn that is closest to the home rank Square weakPawnSq = BitBoard.backmost_sq(weakSide, pos.pieces_color_piecetype(weakSide, PieceTypeS.PAWN)); Square strongKingSq = pos.king_square(strongSide); Square weakKingSq = pos.king_square(weakSide); Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left if (Types.relative_rank_square(strongSide, weakPawnSq) == RankS.RANK_7 && (pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN) & BitBoard.SquareBB[(weakPawnSq + Types.pawn_push(weakSide))]) != 0 && (Types.opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide, PieceTypeS.PAWN) == 1)) { int strongKingDist = BitBoard.square_distance(weakPawnSq, strongKingSq); int weakKingDist = BitBoard.square_distance(weakPawnSq, weakKingSq); // It's a draw if the weak king is on its back two ranks, within 2 // squares of the blocking pawn and the strong king is not // closer. (I think this rule only fails in practically // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) if (Types.relative_rank_square(strongSide, weakKingSq) >= RankS.RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } } return(ScaleFactorS.SCALE_FACTOR_NONE); }