/// 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); }
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); } }
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); }
/// 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); }
/// 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); }
// evaluate_passed_pawns() evaluates the passed pawns of the given color public static Score evaluate_passed_pawns(Position pos, EvalInfo ei, Color Us, bool Trace) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Bitboard b, squaresToQueen, defendedSquares, unsafeSquares; Score score = ScoreS.SCORE_ZERO; b = ei.pi.passed_pawns(Us); while (b != 0) { Square s = BitBoard.pop_lsb(ref b); Debug.Assert(pos.pawn_passed(Us, s)); int r = (int)(Types.relative_rank_square(Us, s) - RankS.RANK_2); int rr = r * (r - 1); // Base bonus based on rank Value mbonus = (17 * rr), ebonus = (7 * (rr + r + 1)); if (rr != 0) { Square blockSq = s + Types.pawn_push(Us); // Adjust bonus based on kings proximity ebonus += (BitBoard.square_distance(pos.king_square(Them), blockSq) * 5 * rr) - (BitBoard.square_distance(pos.king_square(Us), blockSq) * 2 * rr); // If blockSq is not the queening square then consider also a second push if (Types.relative_rank_square(Us, blockSq) != RankS.RANK_8) { ebonus -= (BitBoard.square_distance(pos.king_square(Us), blockSq + Types.pawn_push(Us)) * rr); } // If the pawn is free to advance, increase bonus if (pos.empty(blockSq)) { squaresToQueen = BitBoard.forward_bb(Us, s); // If there is an enemy rook or queen attacking the pawn from behind, // add all X-ray attacks by the rook or queen. Otherwise consider only // the squares in the pawn's path attacked or occupied by the enemy. if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { unsafeSquares = squaresToQueen; } else { unsafeSquares = squaresToQueen & (ei.attackedBy[Them][PieceTypeS.ALL_PIECES] | pos.pieces_color(Them)); } if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { defendedSquares = squaresToQueen; } else { defendedSquares = squaresToQueen & ei.attackedBy[Us][PieceTypeS.ALL_PIECES]; } // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. int k = 0 == unsafeSquares? 15 : 0 == (unsafeSquares & BitBoard.SquareBB[blockSq]) ? 9 : 0; // If the path to queen is fully defended, assign a big bonus. // Otherwise assign a smaller bonus if the block square is defended. if (defendedSquares == squaresToQueen) { k += 6; } else if ((defendedSquares & BitBoard.SquareBB[blockSq]) != 0) { k += 4; } mbonus += (k * rr); ebonus += (k * rr); } } // rr != 0 if (pos.count(Us, PieceTypeS.PAWN) < pos.count(Them, PieceTypeS.PAWN)) { ebonus += ebonus / 4; } score += Types.make_score(mbonus, ebonus); } if (Trace) { Tracing.terms[Us][TermsS.PASSED] = apply_weight(score, Weights[EvalWeightS.PassedPawns]); } // Add the scores to the middle game and endgame eval return(Eval.apply_weight(score, Weights[EvalWeightS.PassedPawns])); }
public static int generate_pawn_moves(Position pos, ExtMove[] mlist, int mPos, Bitboard target, CheckInfo ci, Color Us, GenType Type) { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Bitboard TRank8BB = (Us == ColorS.WHITE ? BitBoard.Rank8BB : BitBoard.Rank1BB); Bitboard TRank7BB = (Us == ColorS.WHITE ? BitBoard.Rank7BB : BitBoard.Rank2BB); Bitboard TRank3BB = (Us == ColorS.WHITE ? BitBoard.Rank3BB : BitBoard.Rank6BB); Square Up = (Us == ColorS.WHITE ? SquareS.DELTA_N : SquareS.DELTA_S); Square Right = (Us == ColorS.WHITE ? SquareS.DELTA_NE : SquareS.DELTA_SW); Square Left = (Us == ColorS.WHITE ? SquareS.DELTA_NW : SquareS.DELTA_SE); Bitboard b1, b2, dc1, dc2, emptySquares = 0; Bitboard pawnsOn7 = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN) & ~TRank7BB; Bitboard enemies = (Type == GenTypeS.EVASIONS ? pos.pieces_color(Them) & target : Type == GenTypeS.CAPTURES ? target : pos.pieces_color(Them)); // Single and double pawn pushes, no promotions if (Type != GenTypeS.CAPTURES) { emptySquares = (Type == GenTypeS.QUIETS || Type == GenTypeS.QUIET_CHECKS ? target : ~pos.pieces()); b1 = BitBoard.shift_bb(pawnsNotOn7, Up) & emptySquares; b2 = BitBoard.shift_bb(b1 & TRank3BB, Up) & emptySquares; if (Type == GenTypeS.EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } if (Type == GenTypeS.QUIET_CHECKS) { b1 &= pos.attacks_from_pawn(ci.ksq, Them); b2 &= pos.attacks_from_pawn(ci.ksq, Them); // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we // don't generate captures. Note that a possible discovery check // promotion has been already generated among captures. if ((pawnsNotOn7 & ci.dcCandidates) != 0) { dc1 = BitBoard.shift_bb(pawnsNotOn7 & ci.dcCandidates, Up) & emptySquares & ~BitBoard.file_bb_square(ci.ksq); dc2 = BitBoard.shift_bb(dc1 & TRank3BB, Up) & emptySquares; b1 |= dc1; b2 |= dc2; } } while (b1 != 0) { Square to = BitBoard.pop_lsb(ref b1); mlist[mPos++].move = Types.make_move(to - Up, to); } while (b2 != 0) { Square to = BitBoard.pop_lsb(ref b2); mlist[mPos++].move = Types.make_move(to - Up - Up, to); } } // Promotions and underpromotions if (pawnsOn7 != 0 && (Type != GenTypeS.EVASIONS || (target & TRank8BB) != 0)) { if (Type == GenTypeS.CAPTURES) { emptySquares = ~pos.pieces(); } if (Type == GenTypeS.EVASIONS) { emptySquares &= target; } mPos = generate_promotions(mlist, mPos, pawnsOn7, enemies, ci, Type, Right); mPos = generate_promotions(mlist, mPos, pawnsOn7, enemies, ci, Type, Left); mPos = generate_promotions(mlist, mPos, pawnsOn7, emptySquares, ci, Type, Up); } // Standard and en-passant captures if (Type == GenTypeS.CAPTURES || Type == GenTypeS.EVASIONS || Type == GenTypeS.NON_EVASIONS) { b1 = BitBoard.shift_bb(pawnsNotOn7, Right) & enemies; b2 = BitBoard.shift_bb(pawnsNotOn7, Left) & enemies; while (b1 != 0) { Square to = BitBoard.pop_lsb(ref b1); mlist[mPos++].move = Types.make_move(to - Right, to); } while (b2 != 0) { Square to = BitBoard.pop_lsb(ref b2); mlist[mPos++].move = Types.make_move(to - Left, to); } if (pos.ep_square() != SquareS.SQ_NONE) { Debug.Assert(Types.rank_of(pos.ep_square()) == Types.relative_rank_rank(Us, RankS.RANK_6)); // An en passant capture can be an evasion only if the checking piece // is the double pushed pawn and so is in the target. Otherwise this // is a discovery check and we are forced to do otherwise. if (Type == GenTypeS.EVASIONS && (target & BitBoard.SquareBB[(pos.ep_square() - Up)]) == 0) { return(mPos); } b1 = pawnsNotOn7 & pos.attacks_from_pawn(pos.ep_square(), Them); Debug.Assert(b1 != 0); while (b1 != 0) { mlist[mPos++].move = Types.make(BitBoard.pop_lsb(ref b1), pos.ep_square(), MoveTypeS.ENPASSANT); } } } return(mPos); }