/// 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); }
/// 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 static Score evaluate(Position pos, Pawns.Entry e, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); 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 b, p, doubled; Square s; File f; Rank r; bool passed, isolated, opposed, connected, backward, candidate, unsupported; Score value = ScoreS.SCORE_ZERO; Square[] pl = pos.list(Us, PieceTypeS.PAWN); int plPos = 0; Bitboard ourPawns = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN); Bitboard theirPawns = pos.pieces_color_piecetype(Them, PieceTypeS.PAWN); e.passedPawns[Us] = e.candidatePawns[Us] = 0; e.kingSquares[Us] = SquareS.SQ_NONE; e.semiopenFiles[Us] = 0xFF; e.pawnAttacks[Us] = BitBoard.shift_bb(ourPawns, Right) | BitBoard.shift_bb(ourPawns, Left); e.pawnsOnSquares[Us][ColorS.BLACK] = Bitcount.popcount_Max15(ourPawns & BitBoard.DarkSquares); e.pawnsOnSquares[Us][ColorS.WHITE] = pos.count(Us, PieceTypeS.PAWN) - e.pawnsOnSquares[Us][ColorS.BLACK]; // Loop through all pawns of the current color and score each pawn while ((s = pl[plPos++]) != SquareS.SQ_NONE) { Debug.Assert(pos.piece_on(s) == Types.make_piece(Us, PieceTypeS.PAWN)); f = Types.file_of(s); // This file cannot be semi-open e.semiopenFiles[Us] &= ~(1 << f); // Previous rank p = BitBoard.rank_bb_square(s - Types.pawn_push(Us)); // Our rank plus previous one b = BitBoard.rank_bb_square(s) | p; // Flag the pawn as passed, isolated, doubled, // unsupported or connected (but not the backward one). connected = (ourPawns & BitBoard.adjacent_files_bb(f) & b) != 0; unsupported = (0 == (ourPawns & BitBoard.adjacent_files_bb(f) & p)); isolated = (0 == (ourPawns & BitBoard.adjacent_files_bb(f))); doubled = ourPawns & BitBoard.forward_bb(Us, s); opposed = (theirPawns & BitBoard.forward_bb(Us, s)) != 0; passed = (0 == (theirPawns & BitBoard.passed_pawn_mask(Us, s))); // Test for backward pawn. // If the pawn is passed, isolated, or connected it cannot be // backward. If there are friendly pawns behind on adjacent files // or if it can capture an enemy pawn it cannot be backward either. if ((passed | isolated | connected) || (ourPawns & BitBoard.pawn_attack_span(Them, s)) != 0 || (pos.attacks_from_pawn(s, Us) & theirPawns) != 0) { backward = false; } else { // We now know that there are no friendly pawns beside or behind this // pawn on adjacent files. We now check whether the pawn is // backward by looking in the forward direction on the adjacent // files, and picking the closest pawn there. b = BitBoard.pawn_attack_span(Us, s) & (ourPawns | theirPawns); b = BitBoard.pawn_attack_span(Us, s) & BitBoard.rank_bb_square(BitBoard.backmost_sq(Us, b)); // If we have an enemy pawn in the same or next rank, the pawn is // backward because it cannot advance without being captured. backward = ((b | BitBoard.shift_bb(b, Up)) & theirPawns) != 0; } Debug.Assert(opposed | passed | (BitBoard.pawn_attack_span(Us, s) & theirPawns) != 0); // A not-passed pawn is a candidate to become passed, if it is free to // advance and if the number of friendly pawns beside or behind this // pawn on adjacent files is higher than or equal to the number of // enemy pawns in the forward direction on the adjacent files. candidate = !(opposed | passed | backward | isolated) && (b = BitBoard.pawn_attack_span(Them, s + Types.pawn_push(Us)) & ourPawns) != 0 && Bitcount.popcount_Max15(b) >= Bitcount.popcount_Max15(BitBoard.pawn_attack_span(Us, s) & theirPawns); // Passed pawns will be properly scored in evaluation because we need // full attack info to evaluate passed pawns. Only the frontmost passed // pawn on each file is considered a true passed pawn. if (passed && 0 == doubled) { e.passedPawns[Us] |= BitBoard.SquareBB[s]; } // Score this pawn if (isolated) { value -= Isolated[opposed ? 1 : 0][f]; } if (unsupported && !isolated) { value -= UnsupportedPawnPenalty; } if (doubled != 0) { value -= Types.divScore(Doubled[f], BitBoard.rank_distance(s, BitBoard.lsb(doubled))); } if (backward) { value -= Backward[opposed ? 1 : 0][f]; } if (connected) { value += Connected[f][Types.relative_rank_square(Us, s)]; } if (candidate) { value += CandidatePassed[Types.relative_rank_square(Us, s)]; if (0 == doubled) { e.candidatePawns[Us] |= BitBoard.SquareBB[s]; } } } // In endgame it's better to have pawns on both wings. So give a bonus according // to file distance between left and right outermost pawns. if (pos.count(Us, PieceTypeS.PAWN) > 1) { b = (Bitboard)(e.semiopenFiles[Us] ^ 0xFF); value += PawnsFileSpan * (BitBoard.msb(b) - BitBoard.lsb(b)); } return(value); }
// init_magics() computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we // use the so called "fancy" approach. public static void init_magics(PieceType pt, Bitboard[][] attacks, Bitboard[] magics, Bitboard[] masks, uint[] shifts, Square[] deltas, Fn index) { int[][] MagicBoosters = new int[2][] { new int[] { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, new int[] { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; RKISS rk = new RKISS(); Bitboard[] occupancy = new UInt64[4096], reference = new UInt64[4096]; Bitboard edges, b; int i, size, booster; for (Square s = SquareS.SQ_A1; s <= SquareS.SQ_H8; s++) { // Board edges are not considered in the relevant occupancies edges = ((BitBoard.Rank1BB | BitBoard.Rank8BB) & ~BitBoard.rank_bb_square(s)) | ((BitBoard.FileABB | BitBoard.FileHBB) & ~BitBoard.file_bb_square(s)); // Given a square 's', the mask is the bitboard of sliding attacks from // 's' computed on an empty board. The index must be big enough to contain // all the attacks for each possible subset of the mask and so is 2 power // the number of 1s of the mask. Hence we deduce the size of the shift to // apply to the 64 or 32 bits word to get the index. masks[s] = sliding_attack(deltas, s, 0) & ~edges; shifts[s] = 32 - (uint)Bitcount.popcount_Max15(masks[s]); // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. b = 0; size = 0; do { occupancy[size] = b; reference[size] = sliding_attack(deltas, s, b); size++; b = (b - masks[s]) & masks[s]; } while (b != 0); // Set the offset for the table of the next square. We have individual // table sizes for each square with "Fancy Magic Bitboards". attacks[s] = new Bitboard[size]; booster = MagicBoosters[0][Types.rank_of(s)]; // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. do { do { magics[s] = rk.magic_rand(booster); }while (Bitcount.popcount_Max15((magics[s] * masks[s]) >> 56) < 6); Array.Clear(attacks[s], 0, size); // A good magic must map every possible occupancy to an index that // looks up the correct sliding attack in the attacks[s] database. // Note that we build up the database for square 's' as a side // effect of verifying the magic. for (i = 0; i < size; i++) { Bitboard attack = attacks[s][index(s, occupancy[i], pt)]; if (attack != 0 && attack != reference[i]) { break; } Debug.Assert(reference[i] != 0); //attack = reference[i]; attacks[s][index(s, occupancy[i], pt)] = reference[i]; } } while (i != size); } }