def update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.grids.update(image=self.device.image)
            return True

        try:
            self.grids = Grids(self.device.image, config=self.config)
        except Exception as e:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                raise e

        # Catch perspective error
        known_exception = self.info_bar_count()
        if len(self.grids.horizontal) > self.map.shape[1] + 2 or len(self.grids.vertical) > self.map.shape[0] + 2:
            if not known_exception:
                logger.info('Perspective Error. Too many lines')
            self.grids.correct = False
        if len(self.grids.horizontal) <= 3 or len(self.grids.vertical) <= 3:
            if not known_exception:
                logger.info('Perspective Error. Too few lines')
            self.grids.correct = False

        if not self.grids.correct:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                self.grids.save_error_image()

        # Set camera position
        if self.grids.left_edge:
            x = 0 + self.grids.center_grid[0]
        elif self.grids.right_edge:
            x = self.map.shape[0] - self.grids.shape[0] + self.grids.center_grid[0]
        else:
            x = self.camera[0]
        if self.grids.lower_edge:
            y = 0 + self.grids.center_grid[1]
        elif self.grids.upper_edge:
            y = self.map.shape[1] - self.grids.shape[1] + self.grids.center_grid[1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.info(f'      camera corrected: {location2node(self.camera)} -> {location2node((x, y))}')
        self.camera = (x, y)
        self.show_camera()
Example #2
0
    def handle_boss_appear_refocus(self):
        """

        """
        appear = False
        for data in self.map.spawn_data:
            if data.get('battle') == self.battle_count and data.get('boss', 0):
                logger.info('Catch camera re-positioning after boss appear')
                appear = True

        if self.config.POOR_MAP_DATA:
            self.device.screenshot()
            grids = Grids(self.device.image, config=self.config)
            grids.predict()
            grids.show()
            for grid in grids:
                if grid.is_boss:
                    logger.info(
                        'Catch camera re-positioning after boss appear')
                    appear = True
                    for g in self.map:
                        g.wipe_out()
                    break

        if appear:
            camera = self.camera
            self.ensure_edge_insight()
            logger.info('Refocus to previous camera position.')
            self.focus_to(camera)
            return True
        else:
            return False
Example #3
0
class Camera(InfoHandler):
    map: CampaignMap
    camera = (0, 0)

    def _map_swipe(self, vector, drop_threshold=0.1):
        """
        Args:
            vector(tuple, np.ndarray): float
            drop_threshold(float): swipe distance lower than this will be drop, because a closing swipe will be treat
                as a click in game.
        Returns:
            bool: if camera moved.
        """
        x, y = vector
        if abs(x) > drop_threshold or abs(y) > drop_threshold:
            # Linear fit
            # x = x * 200
            # y = y * 140
            if self.config.CAMERA_SWIPE_MULTIPLY_X is not None and self.config.CAMERA_SWIPE_MULTIPLY_Y is not None:
                if callable(self.config.CAMERA_SWIPE_MULTIPLY_X):
                    x = self.config.CAMERA_SWIPE_MULTIPLY_X(x)
                else:
                    x = x * self.config.CAMERA_SWIPE_MULTIPLY_X
                if callable(self.config.CAMERA_SWIPE_MULTIPLY_Y):
                    y = self.config.CAMERA_SWIPE_MULTIPLY_X(y)
                else:
                    y = y * self.config.CAMERA_SWIPE_MULTIPLY_Y
            else:
                # Function fit
                x, y = swipe_multiply_2d(x, y)

            vector = (-int(x), -int(y))
            self.device.swipe(vector)
            self.device.sleep(0.3)
            self.update()
        else:
            self.update(camera=False)

    def map_swipe(self, vector):
        """
        Swipe to a grid using relative position.
        Remember to update before calling this.

        Args:
            vector(tuple): int
        Returns:
            bool: if camera moved.
        """
        logger.info('Map swipe: %s' % str(vector))
        vector = np.array(vector)
        self.camera = tuple(vector + self.camera)
        vector = np.array([0.5, 0.5]) - np.array(
            self.grids.center_offset) + vector
        try:
            self._map_swipe(vector)
        except PerspectiveError as e:
            self.handle_camera_outside_map(e)

    def focus_to_grid_center(self):
        """
        Re-focus to the center of a grid.

        Returns:
            bool: Map swiped.
        """
        if np.any(
                np.abs(self.grids.center_offset -
                       0.5) > self.config.MAP_GRID_CENTER_TOLERANCE):
            logger.info('Re-focus to grid center.')
            self.map_swipe((0, 0))
            return True

        return False

    def update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.grids.update(image=self.device.image)
            return True

        try:
            self.grids = Grids(self.device.image, config=self.config)
        except Exception as e:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                raise e

        # Catch perspective error
        known_exception = self.info_bar_count()
        if len(self.grids.horizontal) > self.map.shape[1] + 2 or len(
                self.grids.vertical) > self.map.shape[0] + 2:
            if not known_exception:
                logger.info('Perspective Error. Too many lines')
            self.grids.correct = False
        if len(self.grids.horizontal) <= 3 or len(self.grids.vertical) <= 3:
            if not known_exception:
                logger.info('Perspective Error. Too few lines')
            self.grids.correct = False

        if not self.grids.correct:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                self.grids.save_error_image()

        # Set camera position
        if self.grids.left_edge:
            x = 0 + self.grids.center_grid[0]
        elif self.grids.right_edge:
            x = self.map.shape[0] - self.grids.shape[
                0] + self.grids.center_grid[0]
        else:
            x = self.camera[0]
        if self.grids.lower_edge:
            y = 0 + self.grids.center_grid[1]
        elif self.grids.upper_edge:
            y = self.map.shape[1] - self.grids.shape[
                1] + self.grids.center_grid[1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.info(
                f'      camera corrected: {location2node(self.camera)} -> {location2node((x, y))}'
            )
        self.camera = (x, y)
        self.show_camera()

    def predict(self, is_carrier_scan=False):
        self.grids.predict()
        self.map.update(grids=self.grids,
                        camera=self.camera,
                        is_carrier_scan=is_carrier_scan)

    def show_camera(self):
        logger.info('                Camera: %s' % location2node(self.camera))

    def ensure_edge_insight(self, reverse=False, preset=None):
        """
        Swipe to bottom left until two edges insight.
        Edges are used to locate camera.

        Args:
            reverse (bool): Reverse swipes.
            preset (tuple(int)): Set in map swipe manually.

        Returns:
            list[tuple]: Swipe record.
        """
        logger.info('Ensure edge in sight.')
        record = []

        while 1:
            try:
                if len(record) == 0:
                    self.update()
                    if preset is not None:
                        self.map_swipe(preset)
                        record.append(preset)

                x = 0 if self.grids.left_edge or self.grids.right_edge else 3
                y = 0 if self.grids.lower_edge or self.grids.upper_edge else 2

                if len(record) > 0:
                    # Swipe even if two edges insight, this will avoid some embarrassing camera position.
                    self.map_swipe((x, y))

            except PerspectiveError as e:
                self.handle_camera_outside_map(e)
                continue

            record.append((x, y))

            if x == 0 and y == 0:
                break

        if reverse:
            logger.info('Reverse swipes.')
            for vector in record[::-1]:
                x, y = vector
                if x != 0 and y != 0:
                    self.map_swipe((-x, -y))

        return record

    def handle_camera_outside_map(self, e):
        msg = str(e).split(':')[1].strip()
        logger.info(f'Camera outside map: {msg}')
        dic = {
            'to the left': (2, 0),
            'to the right': (-2, 0),
            'to the lower': (0, 2),
            'to the upper': (0, -2)
        }
        self._map_swipe(dic[msg])

    def focus_to(self, location, swipe_limit=(3, 2)):
        """Focus camera on a grid

        Args:
            location: grid
            swipe_limit(tuple): (x, y). Limit swipe in (-x, -y, x, y).
        """
        location = location_ensure(location)
        logger.info('Focus to: %s' % location2node(location))

        vector = np.array(location) - self.camera
        vector, sign = np.abs(vector), np.sign(vector)
        while 1:

            swipe = (
                vector[0] if vector[0] < swipe_limit[0] else swipe_limit[0],
                vector[1] if vector[1] < swipe_limit[1] else swipe_limit[1])
            self.map_swipe(tuple(sign * swipe))

            vector -= swipe
            if np.all(np.abs(vector) <= 0):
                break

    def full_scan(self,
                  battle_count=None,
                  mystery_count=0,
                  siren_count=0,
                  carrier_count=0,
                  is_carrier_scan=False):
        """Scan the hole map.

        Args:
            battle_count:
            mystery_count:
            siren_count:
            carrier_count:
            is_carrier_scan:
        """
        logger.info('Full scan start')
        self.map.reset_fleet()

        queue = self.map.camera_data
        if battle_count == 0:
            queue.add(self.map.camera_data_spawn_point)

        while len(queue) > 0:
            if self.map.missing_is_none(battle_count, mystery_count,
                                        siren_count, carrier_count):
                if battle_count == 0 and queue.count != queue.delete(
                        self.map.camera_data_spawn_point).count:
                    logger.info('Continue scanning spawn points.')
                    pass
                else:
                    logger.info('All spawn found, Early stopped.')
                    break
            queue = queue.sort_by_camera_distance(self.camera)
            self.focus_to(queue[0])
            self.predict(is_carrier_scan=is_carrier_scan)
            queue = queue[1:]

        if battle_count is not None:
            self.map.missing_predict(battle_count=battle_count,
                                     mystery_count=mystery_count,
                                     siren_count=siren_count,
                                     carrier_count=carrier_count)
        self.map.show()

    def in_sight(self, location, sight=None):
        """Make sure location in camera sight

        Args:
            location:
            sight (tuple): Such as (-3, -1, 3, 2).
        """
        location = location_ensure(location)
        logger.info('In sight: %s' % location2node(location))
        if sight is None:
            sight = self.map.camera_sight

        diff = np.array(location) - self.camera
        if diff[1] > sight[3]:
            y = diff[1] - sight[3]
        elif diff[1] < sight[1]:
            y = diff[1] - sight[1]
        else:
            y = 0
        if diff[0] > sight[2]:
            x = diff[0] - sight[2]
        elif diff[0] < sight[0]:
            x = diff[0] - sight[0]
        else:
            x = 0
        self.focus_to((self.camera[0] + x, self.camera[1] + y))

    def convert_map_to_grid(self, location):
        """If self.grids doesn't contain this location, focus camera on the location and re-convert it.

        Args:
            location: Grid instance in self.map.

        Returns:
            Grid: Grid instance in self.grids.
        """
        location = location_ensure(location)

        grid = np.array(location) - self.camera + self.grids.center_grid
        logger.info('Convert_map_to_grid')
        logger.info(f'Map: {location2node(location)}, '
                    f'Camera: {location2node(self.camera)}, '
                    f'Center: {location2node(self.grids.center_grid)}, '
                    f'grid: {location2node(grid)}')
        if grid in self.grids:
            return self.grids[grid]
        else:
            logger.warning('Convert_map_to_grid Failed.')
            self.grids.save_error_image()
            self.focus_to(location)
            grid = np.array(location) - self.camera + self.grids.center_grid
            return self.grids[grid]

    def full_scan_find_boss(self):
        logger.info('Full scan find boss.')
        self.map.reset_fleet()

        queue = self.map.select(may_boss=True)
        while len(queue) > 0:
            queue = queue.sort_by_camera_distance(self.camera)
            self.in_sight(queue[0])
            self.predict()
            queue = queue[1:]

            boss = self.map.select(is_boss=True)
            boss = boss.add(self.map.select(may_boss=True, is_enemy=True))
            if boss:
                logger.info(f'Boss found: {boss}')
                self.map.show()
                return True

        logger.warning('No boss found.')
        return False
Example #4
0

class Config:
    """
    Paste the config of map file here
    """
    pass


cfg = AzurLaneConfig().merge(Config())

# Folder to save temp images
folder = './screenshots/temp/'
# Put Screenshot here
file = './screenshots/TEMPLATE_AMBUSH_EVADE_FAILED.png'

i = Image.open(file).convert('RGB')
grids = Grids(i, cfg)
grids.predict()
grids.show()

for grid in grids:
    # Find more relative_crop area in module/map/grid_predictor.py
    # This one is for `predict_enemy_genre`
    piece = grid.get_relative_image((-1, -1, 1, 0), output_shape=(120, 60))

    file = '%s_%s_%s.png' % (int(
        time.time()), grid.location[0], grid.location[1])
    file = os.path.join(folder, file)
    piece.save(file)
Example #5
0
    }
    EDGE_LINES_FIND_PEAKS_PARAMETERS = {
        'height': (255 - 24, 255),
        'prominence': 10,
        'distance': 50,
        # 'width': (0, 7),
        'wlen': 1000
    }
    # Parameters for cv2.HoughLines
    INTERNAL_LINES_HOUGHLINES_THRESHOLD = 75
    EDGE_LINES_HOUGHLINES_THRESHOLD = 75
    # Parameters for lines pre-cleansing
    HORIZONTAL_LINES_THETA_THRESHOLD = 0.005
    VERTICAL_LINES_THETA_THRESHOLD = 18
    TRUST_EDGE_LINES = False  # True to use edge to crop inner, false to use inner to crop edge
    # Parameters for perspective calculating
    VANISH_POINT_RANGE = ((540, 740), (-3000, -1000))
    DISTANCE_POINT_X_RANGE = ((-3200, -1600),)
    # Parameters for line cleansing
    COINCIDENT_POINT_ENCOURAGE_DISTANCE = 3
    ERROR_LINES_TOLERANCE = (-10, 10)
    MID_DIFF_RANGE_H = (129 - 3, 129 + 3)
    MID_DIFF_RANGE_V = (129 - 3, 129 + 3)
    """


cfg = AzurLaneConfig().merge(Config())
grids = Grids(Image.open(file), cfg)
grids.predict()
grids.show()