Example #1
0
    def rook_position_bonus(self, rook, side):
        position = self.position
        pawns_us = position.pieces[Pt.piece(Pt.P, side)]
        pawns_them = position.pieces[Pt.piece(Pt.P, side ^ 1)]
        bonus = 0
        fill = south_attack(rook, FULL_BOARD) | north_attack(rook, FULL_BOARD)
        if pawns_us & fill and not pawns_them & fill:
            # rook supporting passed pawn bonus
            bonus += 5
        if not pawns_us & fill:
            if pawns_them & fill:
                # semi-open file
                bonus += 20
            else:
                # open file
                bonus += 45

        rank_fill = east_attack(rook, FULL_BOARD) | west_attack(
            rook, FULL_BOARD)
        bonus += count_bits(rank_fill & pawns_them) * 8

        # connected rooks, gets bonused once for each rook so halving it
        if self.piece_attacks[Pt.piece(Pt.R, side)] & rook:
            bonus += 15

        return bonus
Example #2
0
    def set_position(self, position):
        self.position = position
        self.piece_attacks = [0] * 13
        self.all_attacks = [0] * 2
        self.double_attacks = [0] * 2

        occ = self.position.occupied[Side.WHITE] | self.position.occupied[
            Side.BLACK]
        for side in [Side.WHITE, Side.BLACK]:
            pinned = self.position.pinned[Pt.piece(Pt.K, side)]
            king_us = self.position.pieces[Pt.piece(Pt.K, side)]
            for sq in range(64):
                pt = self.position.squares[sq]
                if pt == Pt.NULL: continue
                pt_side = Pt.get_side(pt)
                bt = Pt.base_type(pt)
                if pt_side == side:
                    sq_bb = 1 << sq
                    if bt == Pt.P:
                        attacks = pawn_attack(sq_bb, pt_side)
                    else:
                        attacks = piece_attack(bt, sq_bb, occ)
                    if pinned & sq_bb:
                        attacks &= line_sqs(bit_position(king_us), sq)
                    self.piece_attacks[pt] |= attacks
                    self.double_attacks[
                        side] |= self.all_attacks[side] & attacks
                    self.all_attacks[side] |= attacks
        return self
Example #3
0
    def unprotected_penalty(self, side, pins):
        position = self.position
        us = position.occupied[side]
        them = position.occupied[side ^ 1]
        free = (us | them) ^ FULL_BOARD
        us_attacked = attacked_pieces(position, side)
        penalty = 0
        for pt in Pt.piece_types(side=side):
            num = count_bits(position.pieces[pt] & us_attacked)
            penalty += num * 10

            defended = us_attacked & position.pieces[pt] & self.all_attacks[
                side]
            for defended_piece in iterate_pieces(defended):
                if defended_piece & self.piece_attacks[Pt.piece(Pt.P,
                                                                side=side)]:
                    # defended by pawn
                    penalty -= MG_PIECES[Pt.P] * .25
                else:
                    penalty -= MG_PIECES[Pt.P] * .125

            if Pt.base_type(pt) == Pt.P:
                continue

            # possible to get attack from pawn. Penalty regardless if defended
            for p in iterate_pieces(position.pieces[pt]):
                if side == Side.WHITE:
                    pawn_attack_sqs = (north_east(p) | north_west(p)) & free
                else:
                    pawn_attack_sqs = (south_east(p) | south_west(p)) & free

                for pawn_attack_sq in iterate_pieces(pawn_attack_sqs):
                    if side == Side.WHITE:
                        pawn_from_sqs = (pawn_attack_sq << 8)
                        if pawn_attack_sq & RANKS[4]:
                            pawn_from_sqs |= (pawn_attack_sq << 16)
                        pawn_from_sqs &= free << 8
                    else:
                        pawn_from_sqs = (pawn_attack_sq >> 8)
                        if pawn_attack_sq & RANKS[3]:
                            pawn_from_sqs |= (pawn_attack_sq >> 16)
                        pawn_from_sqs &= free >> 8

                    pawn_from_sqs &= position.pieces[Pt.piece(Pt.P,
                                                              side=side ^ 1)]
                    if pawn_from_sqs:
                        # penalty += (MG_PIECES[Pt.base_type(pt)] / 8) - 20
                        # more penalty if the piece is pinned
                        if p in pins:
                            penalty += (MG_PIECES[Pt.base_type(pt)] / 3) - 20
                            # more penalty if the pawn is supported on the attack square
                            if self.all_attacks[side ^ 1] & pawn_attack_sq or \
                               self.all_attacks[side] & pawn_attack_sq == 0:
                                penalty += (MG_PIECES[Pt.base_type(pt)] /
                                            3) - 20

        return int(penalty * 1 / 4)
Example #4
0
 def safe_attacks(self, bt, stm):
     # if attacked by lower weight piece, it doesn't count
     lower_wts = (pt for pt in [Pt.P, Pt.N, Pt.B, Pt.R, Pt.Q] if pt < bt)
     opp_attacks = 0
     for piece_type in lower_wts:
         opp_pt = Pt.piece(piece_type, stm ^ 1)
         opp_attacks |= self.piece_attacks[opp_pt]
     attacks = self.piece_attacks[Pt.piece(bt, stm)]
     attacks &= invert(opp_attacks)
     # attacks &= invert(self.position.occupied[stm])
     return attacks
Example #5
0
 def mobility(self, bt, stm):
     # attacks count, safe attacks count
     pos = self.pos
     pt = Pt.piece(bt, stm)
     attacks = self.base_evaluator.piece_attacks[pt]
     safe_attacks = self.base_evaluator.safe_attacks(bt, stm)
     return [count_bits(attacks) / 16, count_bits(safe_attacks) / 16]
Example #6
0
 def bad_bishop_penalty(self, minor, side):
     position = self.position
     color = minor & DARK_SQUARES
     pawns_us = position.pieces[Pt.piece(Pt.P, side)]
     if color == 0: pawns = pawns_us & WHITE_SQUARES
     else: pawns = pawns_us & DARK_SQUARES
     return count_bits(pawns) * 8
Example #7
0
 def get_pairs(self, bt, side):
     # exist 1, exist 2, 1 coords, 2 coords
     pos = self.pos
     pt = Pt.piece(bt, side)
     features = []
     b = pos.pieces[pt]
     count = count_bits(b)
     if count == 1:
         if b & LEFT_SIDE:
             features.extend([1, 0])
             features.extend(self.sq(b, side))
             features.extend(self.sq(0, side))
         else:
             features.extend([0, 1])
             features.extend(self.sq(0, side))
             features.extend(self.sq(b, side))
     elif count >= 2:
         features.extend([1, 1])
         features.extend(self.sq(ls1b(b), side))
         b = reset_ls1b(b)
         features.extend(self.sq(ls1b(b), side))
     else:
         features.extend([0, 0])
         features.extend(self.sq(0, side))
         features.extend(self.sq(0, side))
     return features
Example #8
0
 def queens(self, side):
     # coords, count
     queen_t = Pt.piece(Pt.Q, side)
     queens = self.pos.pieces[queen_t]
     f = self.sq(ls1b(queens), side)
     f.extend([count_bits(queens)])
     return f
Example #9
0
 def BRN_pairs(self, bt, side):
     # B R and N pairs
     # ordered exist flags, coords, count
     pt = Pt.piece(bt, side)
     b = self.pos.pieces[pt]
     features = self.get_pairs(bt, side)
     features.extend([count_bits(b)])
     return features
Example #10
0
 def all_pawn_attack_potentials(self, side):
     """Return attack potentials of pawns of side `side`"""
     position = self.position
     pawns = position.pieces[Pt.piece(Pt.P, side)]
     potential = 0
     for pawn in iterate_pieces(pawns):
         potential |= self.pawn_attack_potential(pawn, side)
     return potential
Example #11
0
def lowest_attacker(pos, square, side=None, highest_attacker=Pt.NULL):
    """Finds lowest piece of `side` attacking a square. `highest_attacker` means
    skip attacks of higher or equal value of given pt"""
    position = pos
    side = position.side_to_move() if side is None else side
    highest_attacker = Pt.K + 1 if highest_attacker == Pt.NULL else highest_attacker

    # pawn
    if Pt.P < highest_attacker:
        possible_from_squares = shift_sw(square, side) | shift_se(square, side)
        piece_type = Pt.piece(Pt.P, side)
        attackers = position.pieces[piece_type] & possible_from_squares
        if attackers:
            return piece_type, ls1b(attackers)

    # knight
    if Pt.N < highest_attacker:
        piece_type = Pt.piece(Pt.N, side)
        possible_from_squares = knight_attack(square)
        attackers = position.pieces[piece_type] & possible_from_squares
        if attackers:
            return piece_type, ls1b(attackers)

    # sliders
    for piece_type in [Pt.B, Pt.R]:
        if piece_type < highest_attacker:
            pt = Pt.piece(piece_type, side)
            occ = position.occupied[side] | position.occupied[side ^ 1]
            attacks = piece_attack(piece_type, square, occ)

            b = attacks & position.pieces[pt]
            if b: return pt, ls1b(b)

            if Pt.Q < highest_attacker:
                qn = Pt.piece(Pt.Q, side)
                b = attacks & position.pieces[qn]
                if b: return qn, ls1b(b)

    # king
    if Pt.K < highest_attacker:
        piece_type = Pt.piece(Pt.K, side)
        possible_from_squares = king_attack(square)
        attackers = position.pieces[piece_type] & possible_from_squares
        if attackers:
            return piece_type, attackers
Example #12
0
    def pawns_evaluation(self, side):
        position = self.position
        us, them = side, side ^ 1
        pawns_us = position.pieces[Pt.piece(Pt.P, us)]
        pawns_them = position.pieces[Pt.piece(Pt.P, them)]

        score = 0

        files = set(get_file(p) for p in iterate_pieces(pawns_us))

        protected = (shift_ne(pawns_us, us)
                     | shift_nw(pawns_us, us)) & pawns_us
        phalanx = (shift_east(pawns_us, us)
                   | shift_west(pawns_us, us)) & pawns_us
        connected = protected | phalanx

        isolated = 0
        opposed = 0
        for f in files:
            fl = 1 if f == 0 else f - 1
            fr = 6 if f == 7 else f + 1
            if not (FILES[fl] & pawns_us) | (FILES[fr] & pawns_us):
                isolated |= FILES[f] & pawns_us
            if FILES[f] & pawns_them:
                opposed |= FILES[f] & pawns_us

        doubled = 0
        for f in files:
            doubled |= reset_ls1b(pawns_us & FILES[f])

        score -= count_bits(isolated & opposed) * 30
        score -= count_bits(isolated ^ opposed) * 40
        for c in iterate_pieces(connected):
            val = 2**get_rank(c, us)
            if c & phalanx:
                val += val - (2**(get_rank(c, us) - 1))
            if c & opposed:
                val >> 1
            score += val
        score -= count_bits(doubled) * 25
        # TODO: add backward pawns
        # don't recount these if backward
        score -= count_bits(pawns_us ^ protected) * 15
        return score
Example #13
0
 def king_safety_squares(self, side):
     """squares surrounding king plus one rank further"""
     position = self.position
     king_sq = position.pieces[Pt.piece(Pt.K, side)]
     attacks = king_attack(king_sq)
     if side == Side.WHITE:
         attacks |= attacks << 8
     else:
         attacks |= attacks >> 8
     return attacks
Example #14
0
 def pawn_exists(self, side):
     # Double pawns disappear but hopefully the counts make up for it
     # I'd think this is better than filling in empty slots
     pos = self.pos
     pt = Pt.piece(Pt.P, side)
     features = [0] * 8
     pawns = pos.pieces[pt]
     for p in iterate_pieces(pawns):
         features[get_file(p)] = 1
     return features
Example #15
0
    def mobility(self, side, PINNED):
        """Bonus for legal moves not attacked by lower weight piece. Pinned pieces
        have restricted mobility"""
        position = self.position
        mobility = 0
        piece_types = [Pt.P, Pt.N, Pt.B, Pt.R, Pt.Q]

        pinned_piece_types = []
        if PINNED[Pt.piece(Pt.K, side)]:
            pinned_piece_types = [
                position.squares[bit_position(p)]
                for p in iterate_pieces(PINNED[Pt.piece(Pt.K, side)])
            ]

        for base_pt in piece_types:
            pt = Pt.piece(base_pt, side)
            safe_attacks = self.safe_attacks(base_pt, side)
            mobility_factor = base_pt if base_pt < Pt.K else 1
            mobility += count_bits(safe_attacks) * mobility_factor

        return mobility
Example #16
0
 def lowest_attacker(self, side):
     f = []
     for sqind in range(64):
         sq = 1 << sqind
         lowest = Pt.NULL
         pieces = [Pt.P, Pt.N, Pt.B, Pt.R, Pt.Q, Pt.K]
         if self.base_evaluator.all_attacks[side]:
             lowest = pieces[-1] + 1
             for bt in pieces:
                 pt = Pt.piece(bt, side)
                 if self.base_evaluator.piece_attacks[pt] & sq:
                     lowest = bt
                     break
         f.append((7 - lowest) / 6)  # lower pt gives higher "score"
     return f
Example #17
0
    def minor_outpost_bonus(self, minor, side, potentials):
        position = self.position
        if minor == 0: return 0
        us = side
        them = side ^ 1
        potential_them = potentials[them]
        potential_us = potentials[us]
        base_type = Pt.base_type(minor)
        pawns_us = position.pieces[Pt.piece(Pt.P, side)]
        outpost_ranks = [
            RANKS[3] | RANKS[4] | RANKS[5], RANKS[2] | RANKS[3] | RANKS[4]
        ]
        outpost_squares = outpost_ranks[side] & invert(
            potential_them) & potential_us
        if outpost_squares:
            # reachable squares
            if base_type == Pt.N: bonus = 12
            else: bonus = 7

            # successfully outposted
            if minor & outpost_squares: bonus += 35

            return bonus
        return 0
Example #18
0
def get_feats(pos):
    krights = 0
    if preserved_kingside_castle_rights(pos.position_flags, Side.W):
        krights |= G1 | F1
    if preserved_queenside_castle_rights(pos.position_flags, Side.W):
        krights |= C1 | D1
    if preserved_kingside_castle_rights(pos.position_flags, Side.B):
        krights |= G8 | F8
    if preserved_kingside_castle_rights(pos.position_flags, Side.B):
        krights |= C8 | D8
    krights_plane = bitfield(krights)
    stm_plane = np.zeros(64).reshape(
        (8, 8)) if pos.side_to_move() == S.W else np.ones(64).reshape((8, 8))
    wp_plane = bitfield(pos.pieces[Pt.piece(Pt.P, S.W)])
    bp_plane = bitfield(pos.pieces[Pt.piece(Pt.P, S.B)])
    wn_plane = bitfield(pos.pieces[Pt.piece(Pt.N, S.W)])
    bn_plane = bitfield(pos.pieces[Pt.piece(Pt.N, S.B)])
    wb_plane = bitfield(pos.pieces[Pt.piece(Pt.B, S.W)])
    bb_plane = bitfield(pos.pieces[Pt.piece(Pt.B, S.B)])
    wr_plane = bitfield(pos.pieces[Pt.piece(Pt.R, S.W)])
    br_plane = bitfield(pos.pieces[Pt.piece(Pt.R, S.B)])
    wq_plane = bitfield(pos.pieces[Pt.piece(Pt.Q, S.W)])
    bq_plane = bitfield(pos.pieces[Pt.piece(Pt.Q, S.B)])
    wk_plane = bitfield(pos.pieces[Pt.piece(Pt.K, S.W)])
    bk_plane = bitfield(pos.pieces[Pt.piece(Pt.K, S.B)])
    return np.stack([
        krights_plane, stm_plane, wp_plane, bp_plane, wn_plane, bn_plane,
        wb_plane, bb_plane, wr_plane, br_plane, wq_plane, bq_plane, wk_plane,
        bk_plane
    ],
                    axis=-1)  # 8x8x14
Example #19
0
    def king_zone_attack_bonus(self, king_zones, side):
        """Attackers of enemy king zone, each weighted by piece weight, including
        xray attacks."""
        position = self.position
        us = side
        them = side ^ 1
        their_king = position.pieces[Pt.piece(Pt.K, them)]
        bonus = 0

        if them == Side.WHITE:
            camp = (RANKS[0] | RANKS[1] | RANKS[2] | RANKS[3] | RANKS[4])
        else:
            camp = (RANKS[3] | RANKS[4] | RANKS[5] | RANKS[6] | RANKS[7])

        king_file = get_file(their_king)
        if 0 <= king_file <= 2: files = range(0, 4)
        elif 3 <= king_file <= 4: files = range(2, 6)
        else: files = range(4, 8)
        flank = 0
        for f in files:
            flank |= FILES[f]
        flank &= camp

        # defended by their king only
        king_defended = self.piece_attacks[Pt.piece(Pt.K, them)] & invert(
            self.double_attacks[them])
        bonus += count_bits(king_defended) * 8

        # attacks directly around their king
        bonus += count_bits(self.all_attacks[us] & king_attack(their_king)) * 4
        # bonus for the more coordinated attacks in the king zone
        bonus += count_bits(self.double_attacks[us] & king_zones[them]) * 4

        # king flank huddling bonus
        bonus += count_bits(self.all_attacks[us] & flank) * 4
        # extra bonus for double attacks in flank, not defended by pawn
        bonus += count_bits(
            self.all_attacks[us] & flank & self.double_attacks[us]
            & invert(self.piece_attacks[Pt.piece(Pt.P, them)])) * 7

        # safe positions to check from
        safe = invert(self.all_attacks[them] | position.occupied[us])

        # safe positions to check from b/c protected by their queen only
        safe2 = safe | self.piece_attacks[Pt.piece(Pt.Q, them)] & invert(
            self.double_attacks[them]) & self.double_attacks[us]

        bishop_rays = pseudo_attacks(Pt.B, bit_position(their_king))
        rook_rays = pseudo_attacks(Pt.R, bit_position(their_king))

        # potential safe knight check squares
        safe_n_checks = knight_attack(their_king) & safe2 & self.piece_attacks[
            Pt.piece(Pt.N, us)]
        if safe_n_checks: bonus += 25

        # potential safe bishop check squares
        safe_b_checks = bishop_rays & safe2 & self.piece_attacks[Pt.piece(
            Pt.B, them)]
        if safe_b_checks: bonus += 15

        # potential safe rook check squares
        safe_r_checks = rook_rays & safe2 & self.piece_attacks[Pt.piece(
            Pt.R, them)]
        if safe_b_checks: bonus += 20

        # potential safe queen check squares
        safe_q_checks = (bishop_rays
                         | rook_rays) & safe & self.piece_attacks[Pt.piece(
                             Pt.Q, them)]
        if safe_b_checks: bonus += 20

        # safe queen contact checks
        safe_q_contact_checks = king_defended & self.double_attacks[
            us] & self.piece_attacks[Pt.piece(Pt.Q, us)]
        if safe_q_contact_checks: bonus += 35

        # attack more difficult without the queen
        if position.pieces[Pt.piece(Pt.Q, us)] == 0:
            bonus /= 2

        return int(bonus)
Example #20
0
    def evaluate(self, position=None, debug=False):
        # if ' '.join(map(str, position.moves)) == "e2-e4 e7-e6 Qd1-f3":
        #     debug = True

        if position:
            self.set_position(position)

        position = self.position

        evals = defaultdict(lambda: [0, 0])

        # Check for mate
        # if position.is_mate():
        #     return -1000000

        # TODO: implement stalemate

        evaluations = [0, 0]

        counts = piece_counts(position)

        POTENTIALS_BB = [
            self.all_pawn_attack_potentials(Side.WHITE),
            self.all_pawn_attack_potentials(Side.BLACK)
        ]
        KING_ZONE_BB = [
            self.king_safety_squares(Side.WHITE),
            self.king_safety_squares(Side.BLACK)
        ]

        q_discoverers, q_pinned, q_sliding_checkers = self.position.get_discoveries_and_pins(
            Pt.Q)
        PINNED = self.position.pinned
        PINNED[Pt.piece(Pt.Q,
                        Side.WHITE)] = q_pinned[Pt.piece(Pt.Q, Side.WHITE)]
        PINNED[Pt.piece(Pt.Q,
                        Side.BLACK)] = q_pinned[Pt.piece(Pt.Q, Side.BLACK)]
        DISCOVERERS = self.position.discoverers

        phase = get_phase(self.position)

        for side in [Side.WHITE, Side.BLACK]:

            side_str = "WHITE" if side == Side.WHITE else "BLACK"

            # count material
            for base_type in [Pt.P, Pt.N, Pt.B, Pt.R, Pt.Q, Pt.K]:
                piece_type = Pt.piece(base_type, side)

                if base_type is not Pt.K:
                    value = counts[piece_type] * material_eval(
                        phase, counts, base_type, side)
                    if debug:
                        evals["Material %s" %
                              (HUMAN_PIECE[piece_type])][side] += value
                    evaluations[side] += value

                # Positional bonuses and penalties:

                # ..rook considerations
                if base_type == Pt.R:
                    for rook in iterate_pieces(position.pieces[piece_type]):
                        value = self.rook_position_bonus(rook, side)
                        if debug:
                            evals["Rook Position %s" %
                                  (HUMAN_PIECE[piece_type])][side] += value
                        evaluations[side] += value

                # ..minor outpost, minor behind pawn
                if base_type in [Pt.B, Pt.N]:
                    for minor in iterate_pieces(position.pieces[piece_type]):
                        value = self.minor_outpost_bonus(
                            base_type, side, POTENTIALS_BB)
                        if debug:
                            evals["Minor Outpost %s" %
                                  (HUMAN_PIECE[piece_type])][side] += value
                        evaluations[side] += value

                        if base_type == Pt.B:
                            value = self.bad_bishop_penalty(minor, side)
                            if debug:
                                evals["Bad Bishop Penalty %s" %
                                      (HUMAN_PIECE[piece_type])][side] += value
                            evaluations[side] -= value

                    value = self.minor_behind_pawn(piece_type, side)
                    if debug:
                        evals["Minor Behind Pawn %s" %
                              (HUMAN_PIECE[piece_type])][side] += value
                    evaluations[side] += value

                # ..pawn structure
                if base_type == Pt.P:
                    value = self.pawns_evaluation(side)
                    if debug: evals["Pawn Structure"][side] += value
                    evaluations[side] += value

                    value = self.pawn_potential_penalty(side, POTENTIALS_BB)
                    if debug: evals["Pawn Potential Penalty"][side] += value
                    evaluations[side] -= value

                # ..piece-square table adjustments
                if base_type in [Pt.P, Pt.N, Pt.B, Pt.K]:
                    value = psqt_value(piece_type, position, side)
                    if debug: evals["PSQT adjustments"][side] += value
                    evaluations[side] += value

            # center attacks bonus
            value = self.center_attacks_bonus(side)
            if debug: evals["Center Attack Bonus"][side] += value
            evaluations[side] += value

            # weak/hanging pieces penalties
            # for ep in next_en_prise(side):
            #     pt, *rest = ep
            #     bt = Pt.base_type(pt)
            #     value = (MG_PIECES[bt] / MG_PIECES[Pt.P]) * 30
            #     if debug: evals["En-prise penalties %s" % (HUMAN_PIECE[bt])][side] += value
            #     evaluations[side] -= value

            # unprotected
            # value = unprotected_penalty(side, pinned + q_pinned)
            # if debug: evals["Weak/Hanging penalties"][side] += value
            # evaluations[side] -= value

            # threats
            value = self.threats(side, PINNED)
            if debug: evals["Threats bonus"][side] += value
            evaluations[side] += value

            value = count_bits(PINNED[Pt.piece(Pt.K, side)]) * 15
            if debug: evals["Pins to King penalty"][side] += value
            evaluations[side] -= value

            value = count_bits(DISCOVERERS[Pt.piece(Pt.K, side ^ 1)]) * 150
            if debug: evals["Discovery threats to King bonus"][side] += value
            evaluations[side] += value

            value = count_bits(q_pinned[Pt.piece(Pt.Q, side)]) * 10
            if debug: evals["Pins to Queen penalty"][side] += value
            evaluations[side] -= value

            value = count_bits(q_discoverers[Pt.piece(Pt.Q, side ^ 1)]) * 100
            if debug: evals["Discovery threats to Queen bonus"][side] += value
            evaluations[side] += value

            # mobility, taking pins to king into account
            value = self.mobility(side, PINNED)
            if debug: evals["Mobility"][side] += value
            evaluations[side] += value

            # king safety, castle readiness
            value = 0
            if side == Side.WHITE:
                if white_can_castle_kingside(position.position_flags,
                                             self.all_attacks[Side.BLACK],
                                             position.occupied[Side.WHITE]):
                    value += (2 - count_bits(position.occupied[Side.WHITE] &
                                             (F1 | G1))) * 4
                elif white_can_castle_queenside(position.position_flags,
                                                self.all_attacks[Side.BLACK],
                                                position.occupied[Side.WHITE]):
                    value += (3 - count_bits(position.occupied[Side.WHITE] &
                                             (D1 | C1 | B1))) * 4
            else:
                if black_can_castle_kingside(
                        position.position_flags, self.all_attacks[Side.WHITE],
                        position.occupied[Side.BLACK] ^ FULL_BOARD):
                    value += (2 - count_bits(position.occupied[Side.BLACK] &
                                             (F8 | G8))) * 4
                elif black_can_castle_queenside(
                        position.position_flags, self.all_attacks[Side.WHITE],
                        position.occupied[Side.BLACK] ^ FULL_BOARD):
                    value += (3 - count_bits(position.occupied[Side.BLACK] &
                                             (D8 | C8 | B8))) * 4
            if debug: evals["Castling readiness"][side] += value
            evaluations[side] += value

            # .. pawn cover of own king
            value = self.pawn_cover_bonus(KING_ZONE_BB, side)
            if debug: evals["Pawn cover"][side] += value
            evaluations[side] += value

            # .. king attack bonuses
            value = self.king_zone_attack_bonus(KING_ZONE_BB, side)
            if debug: evals["King Attack"][side] += value
            evaluations[side] += value

        if debug: pprint.pprint(dict(evals))
        # pprint.pprint(evals.items())

        res_value = int(evaluations[Side.WHITE] - evaluations[Side.BLACK])
        if debug: print("EVAL", res_value)
        if position.white_to_move():
            return res_value
        else:
            return -res_value
Example #21
0
 def king(self, side):
     # coords
     king_t = Pt.piece(Pt.K, side)
     k = self.pos.pieces[king_t]
     return self.sq(k, side)
Example #22
0
    def threats(self, side, PINNED):
        position = self.position
        occupied = position.occupied[Side.WHITE] | position.occupied[
            Side.BLACK]
        free = occupied ^ FULL_BOARD
        rank2 = RANKS[1] if side == Side.WHITE else RANKS[6]

        us = side
        them = side ^ 1

        bonus = 0
        penalty = 0
        # them_no_qk = position.occupied[side ^ 1] \
        #     ^ position.pieces[Pt.piece(Pt.Q, side ^ 1)] \
        #     ^ position.pieces[Pt.piece(Pt.K, side ^ 1)]
        # loose_pieces = them_no_qk & ((self.all_attacks[side] | self.all_attacks[side]) ^ FULL_BOARD)
        # if loose_pieces:
        #     bonus += 25

        # The following is copied as in stockfish:

        # non-pawn enemies attacked by pawn
        weak = (position.occupied[them] ^ position.pieces[Pt.piece(Pt.P, them)]) \
               & (self.piece_attacks[Pt.piece(Pt.P, us)])

        if weak:
            # our pawns protected by us or not attacked by them
            b = position.pieces[Pt.piece(Pt.P, us)] & (
                self.all_attacks[us] | invert(self.all_attacks[side]))

            safe_threats = (shift_ne(b, us) | shift_nw(b, us)) & weak

            if weak ^ safe_threats:
                bonus += 70

            for threatened_piece in iterate_pieces(safe_threats):
                bonus += 150
                if Pt.base_type(
                        position.squares[bit_position(threatened_piece)]) in [
                            Pt.R, Pt.Q
                        ]:
                    bonus += 50

        # non-pawn enemies defended by pawn
        defended = (position.occupied[them] ^ position.pieces[Pt.piece(Pt.P, them)]) \
               & (self.piece_attacks[Pt.piece(Pt.P, them)])

        # enemies not defended by a pawn and under our attack
        weak = position.occupied[them] \
               & invert(self.piece_attacks[Pt.piece(Pt.P, them)]) \
               & self.all_attacks[us]

        if defended | weak:
            # minor attacks
            minor_attack = self.piece_attacks[Pt.piece(
                Pt.N, us)] | self.piece_attacks[Pt.piece(Pt.B, us)]
            b = (defended | weak) & minor_attack
            for attacked in iterate_pieces(b):
                attacked_type = Pt.base_type(
                    position.squares[bit_position(attacked)])
                if attacked_type == Pt.N:
                    bonus += 10
                if attacked_type > Pt.N:
                    bonus += 56
                if attacked_type == Pt.Q:
                    bonus += 40

            # rook attacks
            b = (position.pieces[Pt.piece(Pt.Q, them)]
                 | weak) & self.piece_attacks[Pt.piece(Pt.R, us)]
            for attacked in iterate_pieces(b):
                attacked_type = Pt.base_type(
                    position.squares[bit_position(attacked)])
                if attacked_type > Pt.P and attacked_type != Pt.R:
                    bonus += 40

            # hanging
            bonus += 44 * count_bits(weak & invert(self.all_attacks[them]))

            # king attacks
            b = weak & self.piece_attacks[Pt.piece(Pt.K, us)]
            more_than_one = reset_ls1b(b) > 0
            if more_than_one: bonus += 18  # 120 for endgame
            elif b: bonus += 6  # 60 for endgame

        # bonus for pawn push that attacks pieces
        # pawns already attacking were considered earlier above
        b = position.pieces[Pt.piece(Pt.P, us)]
        b = shift_north(b | (shift_north(b & rank2, us) & free), us)
        b &= free & (self.all_attacks[us] | invert(self.all_attacks[them]))
        b = (shift_ne(b, us) | shift_nw(b, us)) & invert(
            self.piece_attacks[Pt.piece(Pt.P, us)])
        b2 = PINNED[Pt.piece(Pt.K, side ^ 1)] | PINNED[Pt.piece(
            Pt.Q, side ^ 1)]
        bonus += count_bits(b & b2
                            & position.occupied[them]) * 70 + count_bits(
                                b & invert(b2) & position.occupied[them]) * 20

        return bonus
Example #23
0
 def pawn_count(self, side):
     pt = Pt.piece(Pt.P, side)
     return [count_bits(self.pos.pieces[pt]) / 8]
Example #24
0
 def minor_behind_pawn(self, minors, side):
     position = self.position
     pawns_in_front = position.pieces[Pt.piece(Pt.P, side)] & shift_north(
         minors, side)
     pawns_in_front &= (RANKS[2] | RANKS[3] | RANKS[4] | RANKS[5])
     return count_bits(pawns_in_front) * 8
Example #25
0
 def pawn_cover_bonus(self, king_zones, side):
     position = self.position
     pawn_type = Pt.piece(Pt.P, side)
     pawn_cover = king_zones[side] & position.pieces[pawn_type]
     # increase this too much, king will move to get close to e4,d4 pawns! or only do this when king is castled?
     return count_bits(pawn_cover) * 6