Пример #1
0
def mobility(board: Board):
    mobility = 0
    if board.turn == WHITE:
        mobility -= sum(1 for m in board.generate_pseudo_legal_moves()
                        if not board.is_into_check(m))
        board.push(Move.null())
        mobility += sum(1 for m in board.generate_pseudo_legal_moves()
                        if not board.is_into_check(m))
        board.pop()
    else:
        mobility += sum(1 for m in board.generate_pseudo_legal_moves()
                        if not board.is_into_check(m))
        board.push(Move.null())
        mobility -= sum(1 for m in board.generate_pseudo_legal_moves()
                        if not board.is_into_check(m))
        board.pop()
    return mobility
Пример #2
0
class TTEntry():
    best: Move = Move.null()
    depth: float = 0
    value: float = 0
    type: int = 0
    null: bool = True

    @classmethod
    def default(cls):
        return TTEntry()

    def is_null(self) -> bool:
        return self.null == True
Пример #3
0
def play_one_game(pwhite, pblack, rnd):
    """

    :type pwhite: NNPLayer
    :type pblack: NNPLayer
    :type rnd: int
    """
    board: BoardOptim = BoardOptim.from_chess960_pos(rnd % 960)
    pwhite.board = board
    pblack.board = board

    try:
        while True:  # and board.fullmove_number < 150
            if not pwhite.makes_move(rnd):
                break
            if not pblack.makes_move(rnd):
                break

            if is_debug():
                board.write_pgn(pwhite, pblack, os.path.join(os.path.dirname(__file__), "last.pgn"), rnd)
    except:
        last = board.move_stack[-1] if board.move_stack else Move.null()
        logging.warning("Final move: %s %s %s", last, last.from_square, last.to_square)
        logging.warning("Final position:\n%s", board.unicode())
        raise
    finally:
        if board.move_stack:
            board.write_pgn(pwhite, pblack, os.path.join(os.path.dirname(__file__), "last.pgn"), rnd)

    result = board.result(claim_draw=True)

    badp = 0
    badc = 0
    if isinstance(pwhite, NNPLayer):
        badp += pwhite.invalid_moves
        pwhite.invalid_moves = 0
        badc += 1

    if isinstance(pblack, NNPLayer):
        badp += pblack.invalid_moves
        pblack.invalid_moves = 0
        badc += 1

    badp = badp / badc

    logging.info("Game #%d/%d:\t%s by %s,\t%d moves, %d%% bad", rnd, rnd % 960, result, board.explain(),
                 board.fullmove_number, badp)

    return result
Пример #4
0
    def _push(self, move: chess.Move) -> None:
        self._updateJustMovedCells(False)

        turn = self.board.turn

        if self._isCellAccessible(self.cellWidgetAtSquare(move.from_square)) \
                and move.promotion is None and self.isPseudoLegalPromotion(move):
            w = self.cellWidgetAtSquare(move.to_square)

            promotionDialog = _PromotionDialog(parent=self,
                                               color=turn,
                                               order=self._flipped)
            if not self._flipped and turn:
                promotionDialog.move(self.mapToGlobal(w.pos()))
            else:
                promotionDialog.move(
                    self.mapToGlobal(
                        QtCore.QPoint(w.x(),
                                      w.y() - 3 * w.height())))
            promotionDialog.setFixedWidth(w.width())
            promotionDialog.setFixedHeight(4 * w.height())

            exitCode = promotionDialog.exec_()
            if exitCode == _PromotionDialog.Accepted:
                move.promotion = promotionDialog.chosenPiece
            elif exitCode == _PromotionDialog.Rejected:
                self.foreachCells(CellWidget.unhighlight,
                                  lambda w: w.setChecked(False))
                return

        if not self.board.is_legal(move) or move.null():
            # //HACK this is the only way for a move to be illegal (without playtesting)
            # self.illegalClassicalMove.emit(move)
            self.entangle_move = True
            # raise IllegalMove(f"illegal move {move} by ")
        # logging.debug(f"\n{self.board.lan(move)} ({move.from_square} -> {move.to_square})")

        san = self.board.san(move)
        self.board.push(move)
        # logging.debug(f"\n{self.board}\n")

        self._updateJustMovedCells(True)
        self.popStack.clear()

        self.foreachCells(CellWidget.unmark, CellWidget.unhighlight)
        self.synchronizeAndUpdateStyles()

        self.moveMade.emit(move)
        self.movePushed.emit(san)
Пример #5
0
def zugzwang(engine: SimpleEngine, puzzle: Puzzle) -> bool:
    for node in puzzle.mainline[1::2]:
        board = node.board()
        if board.is_check():
            continue
        if len(list(board.legal_moves)) > 15:
            continue

        score = score_of(engine, board, not puzzle.pov)

        rev_board = node.board()
        rev_board.push(Move.null())
        rev_score = score_of(engine, rev_board, not puzzle.pov)

        if win_chances(score) < win_chances(rev_score) - 0.3:
            return True

    return False
Пример #6
0
    def push(self, move):
        backrank = BB_RANK_1 if self.turn == WHITE else BB_RANK_8
        is_legal = True
        dest_sq = move.to_square
        clear_squares = 0
        # Observations that are made as a result of this move are encoded by this var
        observation = np.zeros((8, 8, 13), dtype='float32')

        # TODO: Add observation from failed castling
        # TODO: Add observation from sliding pawn move

        # Castling is legal if the squares between the king and rook are empty
        if self.is_kingside_castling(move):
            cols = BB_FILE_F | BB_FILE_G
            squares = cols & backrank
            is_legal = (squares & self.occupied) == 0
        elif self.is_queenside_castling(move):
            cols = BB_FILE_B | BB_FILE_C | BB_FILE_D
            squares = cols & backrank
            is_legal = (squares & self.occupied) == 0
        elif BB_SQUARES[move.from_square] & self.pawns:
            # Pawn moves that are straight need to go to empty squares
            if move.from_square % 8 == move.to_square % 8:
                is_legal = (BB_SQUARES[move.to_square] & self.occupied) == 0
            # Pawn moves that are diagonal need to be captures (accounts for ep)
            else:
                is_legal = self.is_capture(move)
        elif (BB_SQUARES[move.from_square] &
              (self.bishops | self.rooks | self.queens)):
            # Returns the new destination and a mask for all squares that were revealed to be empty
            dest_sq, clear_squares = self.adjust_sliding_move(
                move.from_square, move.to_square)

        true_move = Move(move.from_square, dest_sq, promotion=move.promotion)
        if not is_legal:
            true_move = Move.null()
            # The square the pawn is moving to is empty
            if BB_SQUARES[move.from_square] & self.pawns:
                observation[square_rank(move.to_square)][square_file(
                    move.to_square)][12] = 1

        capture = None

        if true_move != Move.null():

            # Updates visible board. Moves pieces from from_square to to_square
            # Special case for promotion and castling needed
            visible = self.visible_state[self.turn]
            if true_move.promotion is None:
                visible.set_piece_at(true_move.to_square,
                                     visible.piece_at(true_move.from_square))
            else:
                visible._set_piece_at(true_move.to_square, true_move.promotion,
                                      self.turn, True)
            visible.remove_piece_at(true_move.from_square)
            if self.is_castling(move):
                if self.is_kingside_castling(move):
                    rook_from = H1 if self.turn else H8
                    rook_to = F1 if self.turn else F8
                else:
                    rook_from = A1 if self.turn else A8
                    rook_to = D1 if self.turn else D8
                visible.set_piece_at(rook_to, visible.piece_at(rook_from))
                visible.remove_piece_at(rook_from)

            capture = self.is_capture(true_move)
            # Update our visible board to be empty on any squares we moved through
            if clear_squares:
                observation = self.get_current_state(self.turn, clear_squares)

            from_rank = square_rank(true_move.from_square)
            from_file = square_file(true_move.from_square)
            to_rank = square_rank(true_move.to_square)
            to_file = square_file(true_move.to_square)
            if capture:
                # If you capture something, update your opponent's visibility
                self.visible_state[not self.turn].remove_piece_at(
                    true_move.to_square)

                for i in range(6, 12):
                    # We observe a -1 for all their pieces on that square
                    observation[to_rank][to_file][i] = -1
                    # Our opponent observes a 1 for all our pieces on that square
                    self.observation[not self.turn][to_rank][to_file][i] = 1

        self.observation[self.turn] = observation

        super().push(true_move)

        # Return reward for move
        if true_move == Move.null():
            return -5
        elif capture:
            return 5
        else:
            return 0
Пример #7
0
    def negamax(self, depth: float, colour: int, alpha: float, beta: float) -> float:
        initial_alpha = alpha

        # (* Transposition Table Lookup; self.node is the lookup key for ttEntry *)
        tt_entry = self.tt_lookup(self.node)
        if not tt_entry.is_null() and tt_entry.depth >= depth:
            if tt_entry.type == EXACT:
                return tt_entry.value
            elif tt_entry.type == LOWERBOUND:
                alpha = max(alpha, tt_entry.value)
            elif tt_entry.type == UPPERBOUND:
                beta = min(beta, tt_entry.value)

            if alpha >= beta:
                return tt_entry.value
            
            if self.node.is_legal(tt_entry.best):
                moves = itertools.chain([tt_entry.best], filter(lambda x: x != tt_entry.best, self.ordered_moves()))
            else:
                moves = self.ordered_moves()
        else:
            moves = self.ordered_moves()

        gameover, checkmate, draw = self.gameover_check_info()

        if gameover:
            return colour * self.evaluate(depth, checkmate, draw)

        if depth < 1:
            return self.qsearch(alpha, beta, depth, colour, gameover, checkmate, draw)

        current_pos_is_check = self.node.is_check()
        if not current_pos_is_check and depth >= 3 and abs(alpha) < MATE_VALUE and abs(beta) < MATE_VALUE:
            # MAKE A NULL MOVE
            self.node.push(Move.null())  
            # PERFORM A LIMITED SEARCH
            value = - self.negamax(depth - 3, -colour, -beta, -beta + 1)
            # UNMAKE NULL MOVE
            self.node.pop()

            if value >= beta:
                return beta

        do_prune = self.pruning(depth, colour, alpha, beta, current_pos_is_check)

        best_move = Move.null()
        search_pv = True
        value = float("-inf")
        for move_idx, move in enumerate(moves):
            if move_idx == 0:
                best_move = move
            gives_check = self.node.gives_check(move)
            is_capture = self.node.is_capture(move)
            is_promo = bool(move.promotion)
            depth_reduction = search_reduction_factor(
                move_idx, current_pos_is_check, gives_check, is_capture, is_promo, depth)
                
            if do_prune:
                if not gives_check and not is_capture: 
                    continue

            self.node.push(move)
            
            if search_pv:
                r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha)
                value = max(value, r)
            else:
                r = -self.negamax(depth - depth_reduction, -colour, -alpha-1, -alpha)
                if (r > alpha): # // in fail-soft ... & & value < beta) is common
                    r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha) #// re-search
                value = max(value, r)

            self.node.pop()

            if value > alpha:
                alpha = value
                best_move = move
            alpha = max(alpha, value)
            if alpha >= beta:
                break
            search_pv = False

        # (* Transposition Table Store; self.node is the lookup key for ttEntry *)
        # ttEntry = TTEntry()
        tt_entry.value = value
        if value <= initial_alpha:
            tt_entry.type = UPPERBOUND
        elif value >= beta:
            tt_entry.type = LOWERBOUND
        else:
            tt_entry.type = EXACT
        tt_entry.depth = depth
        tt_entry.best = best_move
        tt_entry.null = False
        self.tt_store(self.node, tt_entry)

        return value
Пример #8
0
from chess import Board, Move, SQUARE_NAMES, KING

MOVE_NULL = Move.null()


class State:
    def __init__(self, board: Board, moves_left: int, parent: 'State',
                 move: Move, is_checkmate: bool, hf, zhf):
        # Core
        self.board = board
        self.moves_left = moves_left
        self.is_checkmate = is_checkmate

        # Pointer
        self.parent = parent
        self.move = move

        # Id
        self.zhf = zhf
        try:
            self.id = zhf.update(self.parent.id, self.parent.board, self.move,
                                 self.moves_left)
        except AttributeError:
            self.id = zhf.hash(self.board, self.moves_left)

        # Score
        self.hf = hf
        self.score = hf.score(self)

    def __hash__(self) -> int:
        return self.id