Exemplo n.º 1
0
    def under_attack(self, sq, wtm):
        """determine whether a square is attacked by the given side"""
        # pawn attacks
        if wtm:
            if (self._is_pc_at('P', sq - 0x11)
                    or self._is_pc_at('P', sq - 0xf)):
                return True
        else:
            if (self._is_pc_at('p', sq + 0x11)
                    or self._is_pc_at('p', sq + 0xf)):
                return True

        #  knight attacks
        npc = 'N' if wtm else 'n'
        for d in piece_moves['n']:
            if self._is_pc_at(npc, sq + d):
                return True

        # king attacks
        kpc = 'K' if wtm else 'k'
        for d in piece_moves['k']:
            if self._is_pc_at(kpc, sq + d):
                return True

        # bishop/queen attacks
        for d in piece_moves['b']:
            cur_sq = sq + d
            while valid_sq(cur_sq):
                if self.board[cur_sq] != '-':
                    if wtm:
                        if self.board[cur_sq] in ['B', 'Q']:
                            return True
                    else:
                        if self.board[cur_sq] in ['b', 'q']:
                            return True
                    # square blocked
                    break
                cur_sq += d

        # rook/queen attacks
        for d in piece_moves['r']:
            cur_sq = sq + d
            while valid_sq(cur_sq):
                if self.board[cur_sq] != '-':
                    if wtm:
                        if self.board[cur_sq] in ['R', 'Q']:
                            return True
                    else:
                        if self.board[cur_sq] in ['r', 'q']:
                            return True
                    # square blocked
                    break
                cur_sq += d

        return False
Exemplo n.º 2
0
 def _any_pc_moves(self, sq, pc):
     if pc == 'P':
         if self.board[sq + 0x10] == '-':
             return True
         if self._pawn_cap_at(sq + 0xf):
             return True
         if self._pawn_cap_at(sq + 0x11):
             return True
     elif pc == 'p':
         if self.board[sq - 0x10] == '-':
             return True
         if self._pawn_cap_at(sq - 0xf):
             return True
         if self._pawn_cap_at(sq - 0x11):
             return True
     else:
         for d in piece_moves[pc.lower()]:
             cur_sq = sq + d
             while valid_sq(cur_sq):
                 topc = self.board[cur_sq]
                 if topc == '-' or piece_is_white(topc) != self.wtm:
                     return True
                 if not pc in sliding_pieces or self.board[cur_sq] != '-':
                     break
                 cur_sq += d
Exemplo n.º 3
0
    def check_pseudo_legal(self):
        """Tests if a move is pseudo-legal, that is, legal ignoring the
        fact that a capture must be made if possible. Also sets en passant
        flags for this move. This is used for long algebraic moves,
        but not san, which does these checks implicitly."""

        if self.pc == '-' or piece_is_white(self.pc) != self.pos.wtm:
            raise IllegalMoveError('can only move own pieces')

        if self.is_capture and piece_is_white(self.capture) == self.pos.wtm:
            raise IllegalMoveError('cannot capture own piece')

        diff = self.to - self.fr
        if self.pc == 'p':
            if self.pos.board[self.to] == '-':
                if diff == -0x10:
                    pass
                elif diff == -0x20 and rank(self.fr) == 6:
                    self.new_ep = self.fr - 0x10
                    if self.pos.board[self.new_ep] != '-':
                        raise IllegalMoveError('bad en passant')
                elif diff in [-0x11, -0xf] and self.to == self.pos.ep:
                    self.is_ep = True
                else:
                    raise IllegalMoveError('bad pawn push')
            else:
                if not diff in [-0x11, -0xf]:
                    raise IllegalMoveError('bad pawn capture')
        elif self.pc == 'P':
            if self.pos.board[self.to] == '-':
                if diff == 0x10:
                    pass
                elif diff == 0x20 and rank(self.fr) == 1:
                    self.new_ep = self.fr + 0x10
                    if self.pos.board[self.new_ep] != '-':
                        raise IllegalMoveError('bad en passant')
                elif diff in [0x11, 0xf] and self.to == self.pos.ep:
                    self.is_ep = True
                else:
                    raise IllegalMoveError('bad pawn push')
            else:
                if not diff in [0x11, 0xf]:
                    raise IllegalMoveError('bad pawn capture')
        else:
            if self.pc in sliding_pieces:
                d = dir(self.fr, self.to)
                if d == 0 or not d in piece_moves[self.pc.lower()]:
                    raise IllegalMoveError('piece cannot make that move')
                # now check if there are any pieces in the way
                cur_sq = self.fr + d
                while cur_sq != self.to:
                    assert (valid_sq(cur_sq))
                    if self.pos.board[cur_sq] != '-':
                        raise IllegalMoveError('sliding piece blocked')
                    cur_sq += d
            else:
                if not diff in piece_moves[self.pc.lower()]:
                    raise IllegalMoveError('piece cannot make that move')
Exemplo n.º 4
0
def init_direction_table():
    for r in range(8):
        for f in range(8):
            sq = 0x10 * r + f
            for d in piece_moves['q']:
                cur_sq = sq + d
                while valid_sq(cur_sq):
                    assert (0 <= cur_sq - sq + 0x7f <= 0xff)
                    if direction_table[cur_sq - sq + 0x7f] != 0:
                        assert (d == direction_table[cur_sq - sq + 0x7f])
                    else:
                        direction_table[cur_sq - sq + 0x7f] = d
                    cur_sq += d
Exemplo n.º 5
0
 def _is_legal_ep(self, ep):
     # According to Geurt Gijssen's "An Arbiter's Notebook" #110,
     # if an en passant capture that is otherwise legal is not
     # permitted because it would leave the king in check,
     # then for the puposes of claiming a draw by repetition, the
     # position is identical to one where there is no such en
     # passant capture.  So we have to test the legality of
     # en passant captures.
     if self.wtm:
         if (valid_sq(ep - 0x11) and self.board[ep - 0x11] == 'P'
                 and Move(self, ep - 0x11, ep, is_ep=True).is_legal()):
             return True
         elif (valid_sq(ep - 0xf) and self.board[ep - 0xf] == 'P'
               and Move(self, ep - 0xf, ep, is_ep=True).is_legal()):
             return True
     else:
         if (valid_sq(ep + 0xf) and self.board[ep + 0xf] == 'p'
                 and Move(self, ep + 0xf, ep, is_ep=True).is_legal()):
             return True
         elif (valid_sq(ep + 0x11) and self.board[ep + 0x11] == 'p'
               and Move(self, ep + 0x11, ep, is_ep=True).is_legal()):
             return True
     return False
Exemplo n.º 6
0
 def get_from_sqs(self, pc, sq):
     '''given a piece (not including a pawn) and a destination square,
     return a list of all legal source squares'''
     ret = []
     is_sliding = pc in sliding_pieces
     for d in piece_moves[pc.lower()]:
         cur_sq = sq
         while 1:
             cur_sq += d
             if not valid_sq(cur_sq):
                 break
             if self.board[cur_sq] == pc:
                 if Move(self, cur_sq, sq).is_legal():
                     ret.append(cur_sq)
             if not (self.board[cur_sq] == '-' and is_sliding):
                 break
     return ret
Exemplo n.º 7
0
 def _any_pc_moves(self, sq, pc):
     if pc == 'P':
         if self.board[sq + 0x10] == '-':
             if Move(self, sq, sq + 0x10).is_legal():
                 return True
             if rank(sq) == 1 and self.board[sq + 0x20] == '-' and Move(
                     self, sq, sq + 0x20).is_legal():
                 return True
         if self._pawn_cap_at(sq + 0xf) and Move(
                 self, sq, sq + 0xf, is_ep=sq + 0xf == self.ep).is_legal():
             return True
         if self._pawn_cap_at(sq + 0x11) and Move(
                 self, sq, sq + 0x11, is_ep=sq + 0x11
                 == self.ep).is_legal():
             return True
     elif pc == 'p':
         if self.board[sq - 0x10] == '-':
             if Move(self, sq, sq - 0x10).is_legal():
                 return True
             if rank(sq) == 6 and self.board[sq - 0x20] == '-' and Move(
                     self, sq, sq - 0x20).is_legal():
                 return True
         if self._pawn_cap_at(sq - 0xf) and Move(
                 self, sq, sq - 0xf, is_ep=sq - 0xf == self.ep).is_legal():
             return True
         if self._pawn_cap_at(sq - 0x11) and Move(
                 self, sq, sq - 0x11, is_ep=sq - 0x11
                 == self.ep).is_legal():
             return True
     else:
         for d in piece_moves[pc.lower()]:
             cur_sq = sq + d
             # we don't need to check castling because if castling
             # is legal, some other king move must be also
             while valid_sq(cur_sq):
                 topc = self.board[cur_sq]
                 if topc == '-' or piece_is_white(topc) != self.wtm:
                     mv = Move(self, sq, cur_sq)
                     if mv.is_legal():
                         return True
                 if not pc in sliding_pieces or self.board[cur_sq] != '-':
                     break
                 cur_sq += d
Exemplo n.º 8
0
 def _is_pc_at(self, pc, sq):
     return valid_sq(sq) and self.board[sq] == pc
Exemplo n.º 9
0
 def _pawn_cap_at(self, sq):
     if not valid_sq(sq):
         return False
     pc = self.board[sq]
     return pc != '-' and piece_is_white(pc) != self.wtm
Exemplo n.º 10
0
    def set_pos(self, fen, detect_check=True):
        """Set the position from Forsyth-Fdwards notation.  The format
        is intentionally interpreted strictly; better to give the user an
        error than take in bad data."""
        try:
            # rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
            m = self.set_pos_re.match(fen)
            if not m:
                raise BadFenError('does not look like FEN')
            (pos, side, castle_flags, ep, fifty_count,
             full_moves) = [m.group(i) for i in range(1, 7)]

            ranks = pos.split('/')
            ranks.reverse()
            self.hash = 0
            self.material = [0, 0]
            for (r, rank_str) in enumerate(ranks):
                sq = 0x10 * r
                for c in rank_str:
                    d = '12345678'.find(c)
                    if d >= 0:
                        sq += d + 1
                    else:
                        assert (valid_sq(sq))
                        self.board[sq] = c
                        self.hash ^= zobrist.piece_hash(sq, c)
                        self.material[piece_is_white(c)] += \
                            piece_material[c.lower()]
                        if c == 'k':
                            if self.king_pos[0] is not None:
                                # multiple kings
                                raise BadFenError()
                            self.king_pos[0] = sq
                        elif c == 'K':
                            if self.king_pos[1] is not None:
                                # multiple kings
                                raise BadFenError()
                            self.king_pos[1] = sq
                        elif c.lower() == 'p':
                            if rank(sq) in [0, 7]:
                                # pawn on 1st or 8th rank
                                raise BadFenError()
                        sq += 1
                if sq & 0xf != 8:
                    # wrong row length
                    raise BadFenError()

            if None in self.king_pos:
                # missing king
                raise BadFenError()

            self.wtm = side == 'w'
            if self.wtm:
                self.hash ^= zobrist.side_hash

            if castle_flags == '-':
                self.castle_flags = 0
            else:
                (w_oo, w_ooo, b_oo, b_ooo) = (False, False, False, False)
                for c in castle_flags:
                    if c == 'K':
                        if self.board[E1] != 'K' or self.board[H1] != 'R':
                            raise BadFenError()
                        if w_oo:
                            raise BadFenError()
                        w_oo = True
                    elif c == 'Q':
                        if self.board[E1] != 'K' or self.board[A1] != 'R':
                            raise BadFenError()
                        if w_ooo:
                            raise BadFenError()
                        w_ooo = True
                    elif c == 'k':
                        if self.board[E8] != 'k' or self.board[H8] != 'r':
                            raise BadFenError()
                        if b_oo:
                            raise BadFenError()
                        b_oo = True
                    elif c == 'q':
                        if self.board[E8] != 'k' or self.board[A8] != 'r':
                            raise BadFenError()
                        if b_ooo:
                            raise BadFenError()
                        b_ooo = True
                self.castle_flags = to_castle_flags(w_oo, w_ooo, b_oo, b_ooo)
            self.hash ^= zobrist.castle_hash(self.castle_flags)

            self.fifty_count = int(fifty_count, 10)
            self.ply = 2 * (int(full_moves, 10) - 1) + int(not self.wtm)
            self.start_ply = self.ply  # 0 for new games

            if ep == '-':
                self.ep = None
            else:
                # only set ep if there is a legal capture
                # XXX: this is even stricter than X-FEN; maybe
                # we should not check for true legality, but only
                # whether there is an enemy pawn on an adjacent
                # square?
                self.ep = 'abcdefgh'.index(ep[0]) + \
                    0x10 * '1234567'.index(ep[1])
                if rank(self.ep) not in (2, 5):
                    raise BadFenError('bad en passant square')
                self.hash ^= zobrist.ep_hash(self.ep)
                # legality checking needs a value for in_check
                self.in_check = None
                if not self._is_legal_ep(self.ep):
                    # undo the en passant square
                    self.ep = None
                    self.hash ^= zobrist.ep_hash(self.ep)

            #assert(self.hash == self._compute_hash())
            self.history.set_hash(self.ply, self.hash)

            if detect_check:
                self.detect_check()
                if self.is_checkmate or self.is_stalemate \
                        or self.is_draw_nomaterial:
                    raise BadFenError('got a terminal position')

        except AssertionError:
            raise
Exemplo n.º 11
0
 def piece_hash(self, sq, pc):
     assert ((0xf << 7) & sq == 0)
     assert (valid_sq(sq))
     return self._piece[(self._piece_index[pc] << 7) | sq]
Exemplo n.º 12
0
    def set_pos(self, fen):
        """Set the position from Forsyth-Fdwards notation.  The format
        is intentionally interpreted strictly; better to give the user an
        error than take in bad data."""
        try:
            # rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1
            m = self.set_pos_re.match(fen)
            if not m:
                raise BadFenError('does not look like FEN')
            (pos, side, castle_flags, ep, fifty_count,
             full_moves) = [m.group(i) for i in range(1, 7)]

            ranks = pos.split('/')
            ranks.reverse()
            self.hash = 0
            self.material = [0, 0]
            for (r, rank_str) in enumerate(ranks):
                sq = 0x10 * r
                for c in rank_str:
                    d = '12345678'.find(c)
                    if d >= 0:
                        sq += d + 1
                    else:
                        assert (valid_sq(sq))
                        self.board[sq] = c
                        self.hash ^= zobrist.piece_hash(sq, c)
                        self.material[piece_is_white(c)] += \
                            piece_material[c.lower()]
                        if c.lower() == 'p':
                            if rank(sq) in [0, 7]:
                                # pawn on 1st or 8th rank
                                raise BadFenError()
                        sq += 1
                if sq & 0xf != 8:
                    # wrong row length
                    raise BadFenError()

            self.wtm = side == 'w'
            if self.wtm:
                self.hash ^= zobrist.side_hash

            self.fifty_count = int(fifty_count, 10)
            self.ply = 2 * (int(full_moves, 10) - 1) + int(not self.wtm)
            self.start_ply = self.ply  # 0 for new games

            if ep == '-':
                self.ep = None
            else:
                # only set ep if there is a legal capture
                # XXX: this is even stricter than X-FEN; maybe
                # we should not check for true legality, but only
                # whether there is an enemy pawn on an adjacent
                # square?
                self.ep = 'abcdefgh'.index(ep[0]) + \
                    0x10 * '1234567'.index(ep[1])
                if rank(self.ep) not in (2, 5):
                    raise BadFenError('bad en passant square')
                self.hash ^= zobrist.ep_hash(self.ep)

            #assert(self.hash == self._compute_hash())
            self.history.set_hash(self.ply, self.hash)

        except AssertionError:
            raise