@marisvolk

Как вынести вычисления ходов в отдельный поток на Tkinter?

В текущей реализации шашек на tkinter, несмотря на то, что вроде как вынес просчёт ходов в отдельный поток, тормозит интерфейс при вычислении компьютером оптимального хода (при средней глубине просчёта).
Вот ключевые методы:
def mouse_down(self, event: Event):
        '''Событие нажатия мышки'''

        x, y = (event.x - BOARD_BORDER) // CELL_SIZE, (event.y - BOARD_BORDER) // CELL_SIZE
        # Если точка не внутри поля
        if not (self.__field.is_within(x, y)): return

        if MULTIPLAYER:
        ...
        else:
            if not (self.__player_turn): return

            if (PLAYER_SIDE == SideType.WHITE):
                player_checkers = WHITE_CHECKERS
            elif (PLAYER_SIDE == SideType.BLACK):
                player_checkers = BLACK_CHECKERS
            else:
                return

            # Если нажатие по шашке игрока, то выбрать её
            if (self.__field.type_at(x, y) in player_checkers):
                self.__selected_cell = Point(x, y)
                self.__draw()
            elif (self.__player_turn):
                move = Move(self.__selected_cell.x, self.__selected_cell.y, x, y)
                # Если нажатие по ячейке, на которую можно походить
                if (move in self.__get_moves_list(PLAYER_SIDE)):
                    self.__handle_player_turn(move)
                    # Если не ход игрока, то ход противника
                    if not (self.__player_turn):
                        self.test_field = self.__field
                        self.handle_enemy_turn_async()
                ...
    def handle_enemy_turn_async(game_instance):
        def run():
            game_instance.__handle_enemy_turn()

        threading.Thread(target=run).start()

    def __handle_enemy_turn(self):
        '''Обработка хода противника (компьютера)'''
        self.__player_turn = False
        self.calc = True
        optimal_moves_list = self.__predict_optimal_moves(SideType.opposite(PLAYER_SIDE))
        self.calc = False
        for move in optimal_moves_list:
            self.__handle_move(self.__field, move)
        self.__player_turn = True
        self.__check_for_game_over()

    def __predict_optimal_moves(self, side: SideType) -> list[Move]:
        '''Предсказать оптимальный ход'''
        best_result = 0
        optimal_moves = []
        self.test_field = Field.copy(self.__field)
        predicted_moves_list = self.__get_predicted_moves_list(side)
        if (predicted_moves_list):
            field_copy = Field.copy(self.__field)
            self.test_field = Field.copy(self.__field)
            field_copy_test = Field.copy(self.test_field)
            for moves in predicted_moves_list:
                for move in moves:
                    self.__handle_move(self.test_field, move, draw=False)
                try:
                    if (side == SideType.WHITE):
                        result = self.test_field.white_score / self.test_field.black_score
                    elif (side == SideType.BLACK):
                        result = self.test_field.black_score / self.test_field.white_score
                except ZeroDivisionError:
                    result = inf

                if (result > best_result):
                    best_result = result
                    optimal_moves.clear()
                    optimal_moves.append(moves)
                elif (result == best_result):
                    optimal_moves.append(moves)

                self.test_field = Field.copy(field_copy_test)

        optimal_move = []
        if (optimal_moves):
            # Фильтрация хода
            for move in choice(optimal_moves):
                if (side == SideType.WHITE and self.__field.type_at(move.from_x, move.from_y) in BLACK_CHECKERS):
                    break
                elif (side == SideType.BLACK and self.__field.type_at(move.from_x, move.from_y) in WHITE_CHECKERS):
                    break
                optimal_move.append(move)
        return optimal_move

    def __get_predicted_moves_list(self, side: SideType, current_prediction_depth: int = 0,
                                   all_moves_list: list[Move] = [], current_moves_list: list[Move] = [],
                                   required_moves_list: list[Move] = []) -> list[Move]:
        '''Предсказать все возможные ходы'''
        if (current_moves_list):
            all_moves_list.append(current_moves_list)
        else:
            all_moves_list.clear()

        if (required_moves_list):
            moves_list = required_moves_list
        else:
            now = False
            moves_list = self.__get_moves_list(side)

        if (moves_list and current_prediction_depth < MAX_DEPTH):
            field_copy = Field.copy(self.test_field)
            for move in moves_list:
                field_for_check = Field.copy(self.test_field)
                has_killed_checker = self.__handle_move(self.test_field, move, draw=False)
                required_moves_list = list(filter(
                    lambda required_move: move.to_x == required_move.from_x and move.to_y == required_move.from_y,
                    self.__get_required_moves_list(side, self.test_field)))
                # Если есть ещё ход этой же шашкой
                if (has_killed_checker and required_moves_list):
                    self.__get_predicted_moves_list(side, current_prediction_depth, all_moves_list,
                                                    current_moves_list + [move], required_moves_list)
                else:
                    self.__get_predicted_moves_list(SideType.opposite(side), current_prediction_depth + 1,
                                                    all_moves_list, current_moves_list + [move])

                self.test_field = Field.copy(field_copy)

        return all_moves_list
  • Вопрос задан
  • 47 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы