Beispiel #1
0
    def research_detect(self, image):
        """
        Args:
            image (PIL.Image.Image): Screenshots
        """
        projects = []
        for name, series in zip(get_research_name(image), get_research_series(image)):
            project = ResearchProject(name=name, series=series)
            logger.attr('Project', project)
            projects.append(project)

        self.projects = projects
Beispiel #2
0
    def triggered_bug(self):
        """
        The game does not calculate emotion correctly, which is a bug in AzurLane.
        After a long run, we have to restart the game to update it.
        """
        logger.attr('Emotion_bug',
                    f'{self.total_reduced}/{self.BUG_THRESHOLD}')
        if self.total_reduced >= self.BUG_THRESHOLD:
            self.total_reduced = 0
            return True

        return False
Beispiel #3
0
    def daily_execute(self, remain, fleet):
        """
        Args:
            remain (int): Remain daily challenge count.
            fleet (int): Index of fleet to use.

        Returns:
            bool: True if success, False if daily locked.
        """
        logger.hr(f'Daily {self.daily_current}')
        logger.attr('Fleet', fleet)

        def daily_enter_check():
            return self.appear(DAILY_ENTER_CHECK)

        def daily_end():
            if self.appear(BATTLE_PREPARATION, interval=2):
                self.device.click(BACK_ARROW)
            return self.appear(DAILY_ENTER_CHECK) or self.appear(BACK_ARROW)

        self.ui_click(click_button=DAILY_ENTER,
                      check_button=daily_enter_check,
                      appear_button=DAILY_CHECK)
        if self.appear(DAILY_LOCKED):
            logger.info('Daily locked')
            self.ui_click(click_button=BACK_ARROW, check_button=DAILY_CHECK)
            self.device.sleep((1, 1.2))
            return False

        button = DAILY_MISSION_LIST[
            self.config.DAILY_CHOOSE[self.daily_current] - 1]
        for n in range(remain):
            logger.hr(f'Count {n + 1}')
            self.ui_click(click_button=button,
                          check_button=self.combat_appear,
                          appear_button=daily_enter_check,
                          additional=self.handle_combat_automation_confirm
                          if not self.daily_auto_checked else None)
            self.daily_auto_checked = True
            self.ui_ensure_index(fleet,
                                 letter=OCR_DAILY_FLEET_INDEX,
                                 prev_button=DAILY_FLEET_PREV,
                                 next_button=DAILY_FLEET_NEXT,
                                 fast=False,
                                 skip_first_screenshot=True)
            self.combat(emotion_reduce=False,
                        save_get_items=False,
                        expected_end=daily_end,
                        balance_hp=False)

        self.ui_click(click_button=BACK_ARROW, check_button=DAILY_CHECK)
        self.device.sleep((1, 1.2))
        return True
 def _get_medals(self):
     """
     Returns:
         np.array: [[x1, y1], [x2, y2]], location of the medal icon upper-left corner.
     """
     left_column = self.image_crop((489, 256, 1120, 572))
     medals = TEMPLATE_MEDAL_ICON.match_multi(left_column,
                                              similarity=0.5,
                                              threshold=5)
     medals = Points([(0., m.area[1]) for m in medals]).group(threshold=5)
     logger.attr('Medals_icon', len(medals))
     return medals
    def _opponent_sort(self, method="max_exp"):
        """
        Args:
            method: EXERCISE_CHOOSE_MODE

        Returns:
            list[int]: List of opponent index, such as [2, 1, 0, 3].
                       Attack one by one.
        """
        order = np.argsort([-x.get_priority(method) for x in self.opponents])
        logger.attr('Order', str(order))
        return order
Beispiel #6
0
    def reward_loop(self):
        logger.hr('Reward loop')
        while 1:
            if self.config.triggered_app_restart():
                self.app_restart()

            self.reward()

            logger.info('Reward loop wait')
            logger.attr('Reward_loop_wait',
                        f'{self.config.REWARD_INTERVAL} min')
            self.device.sleep(self.config.REWARD_INTERVAL * 60)
Beispiel #7
0
    def meow_train(self):
        """
        Performs both retrieving a trained meowfficer and queuing
        meowfficer boxes for training

        Pages:
            in: page_meowfficer
            out: page_meowfficer
        """
        logger.hr('Meowfficer train', level=1)

        # Retrieve capacity to determine whether able to collect
        current, remain, total = MEOWFFICER_CAPACITY.ocr(self.device.image)
        logger.attr('Meowfficer_capacity_remain', remain)

        # Read box count, utilized in other helper funcs
        self._box_count = MEOWFFICER_BOX_COUNT.ocr(self.device.image)

        logger.attr('MeowfficerTrain_Mode', self.config.MeowfficerTrain_Mode)
        collected = False
        if self.config.MeowfficerTrain_Mode == 'seamlessly':
            # Enter
            self.ui_click(MEOWFFICER_TRAIN_ENTER,
                          check_button=MEOWFFICER_TRAIN_START,
                          additional=self.meow_additional,
                          retry_wait=3,
                          confirm_wait=0,
                          skip_first_screenshot=True)
            # Collect
            if remain > 0:
                collected = self.meow_collect(collect_all=True)
            # Queue
            self.meow_queue(ascending=False)
            # Exit
            self.meow_menu_close()
        else:
            # Enter
            self.ui_click(MEOWFFICER_TRAIN_ENTER,
                          check_button=MEOWFFICER_TRAIN_START,
                          additional=self.meow_additional,
                          retry_wait=3,
                          confirm_wait=0,
                          skip_first_screenshot=True)
            # Collect
            if remain > 0:
                collected = self.meow_collect(
                    collect_all=self.meow_is_sunday())
            # Queue
            self.meow_queue(ascending=False)
            # Exit
            self.meow_menu_close()

        return collected
Beispiel #8
0
    def get_remain(self, mode):
        """
        Args:
            mode (str): easy, normal, hard

        Returns:
            int:
        """
        ocr = raid_ocr(raid=self.config.RAID_NAME, mode=mode)
        remain, _, _ = ocr.ocr(self.device.image)
        logger.attr(f'{mode.capitalize()} Remain', remain)
        return remain
Beispiel #9
0
    def zone_type_select(self, types=('SAFE', 'DANGEROUS')):
        """
        Args:
            types (tuple[str], list[str], str): Zone types, or a list of them.
                Available types: DANGEROUS, SAFE, OBSCURE, ABYSSAL, STRONGHOLD, ARCHIVE.
                Try the the first selection in type list, if not available, try the next one.
                Do nothing if no selection satisfied input.

        Returns:
            bool: If success.

        Pages:
            in: is_zone_pinned
            out: is_zone_pinned
        """
        if not self.zone_has_switch():
            logger.info('Zone has no type to select, skip')
            return True

        if isinstance(types, str):
            types = [types]

        def get_button(selection_):
            for typ in types:
                typ = 'SELECT_' + typ
                for sele in selection_:
                    if typ == sele.name:
                        return sele
            return None

        pinned = self.get_zone_pinned_name()
        if pinned in types:
            logger.info(f'Already selected at {pinned}')
            return True

        for _ in range(3):
            self.zone_select_enter()
            selection = self.ensure_zone_select_expanded()
            logger.attr('Zone_selection', selection)

            button = get_button(selection)
            if button is None:
                logger.warning(
                    'No such zone type to select, fallback to default')
                types = ('SAFE', 'DANGEROUS')
                button = get_button(selection)

            self.zone_select_execute(button)
            if self.pinned_to_name(button) == self.get_zone_pinned_name():
                return True

        logger.warning('Failed to select zone type after 3 trial')
        return False
Beispiel #10
0
    def is_combat_loading(self):
        """
        Returns:
            bool:
        """
        left = color_bar_percentage(self.device.image, area=LOADING_BAR.area, prev_color=(99, 150, 255))
        right = color_bar_percentage(self.device.image, area=LOADING_BAR.area, prev_color=(225, 225, 225), reverse=True)
        if 0.15 < left < 0.95 and right > 0.15 and left + right <= 1.2:
            logger.attr('Loading', f'{int(left * 100)}%({int(right * 100)}%)')
            return True

        return False
Beispiel #11
0
    def combat_status(self, save_get_items=False, expected_end=None):
        """
        Args:
            save_get_items (bool):
            expected_end (str): with_searching, no_searching, in_stage.
        """
        logger.info('Combat status')
        logger.attr(
            'expected_end',
            expected_end.__name__ if callable(expected_end) else expected_end)
        exp_info = False  # This is for the white screen bug in game
        while 1:
            self.device.screenshot()

            # Combat status
            if not exp_info and self.handle_get_ship(
                    save_get_items=save_get_items):
                continue
            if self.handle_get_items(save_get_items=save_get_items):
                continue
            if self.handle_battle_status(save_get_items=save_get_items):
                continue
            if self.handle_popup_confirm():
                continue
            if self.handle_exp_info():
                exp_info = True
                continue
            if self.handle_urgent_commission(save_get_items=save_get_items):
                continue
            if self.handle_story_skip():
                continue

            # End
            if self.handle_in_stage():
                break
            if expected_end is None:
                if self.handle_in_map_with_enemy_searching():
                    break
            if isinstance(expected_end, str):
                if expected_end == 'in_stage' and self.handle_in_stage():
                    break
                if expected_end == 'with_searching' and self.handle_in_map_with_enemy_searching(
                ):
                    break
                if expected_end == 'no_searching' and self.handle_in_map_no_enemy_searching(
                ):
                    break
                if expected_end == 'in_ui' and self.appear(BACK_ARROW,
                                                           offset=(20, 20)):
                    break
            if callable(expected_end):
                if expected_end():
                    break
    def handle_guild(self):
        """
        ALAS handler function for guild reward loop

        Returns:
            bool: If executed

        Pages:
            in: page_main
            out: page_main
        """
        if not self.config.ENABLE_GUILD_LOGISTICS and not self.config.ENABLE_GUILD_OPERATIONS:
            return False

        now = datetime.now()
        guild_record = datetime.strptime(self.config.config.get(*GUILD_RECORD),
                                         self.config.TIME_FORMAT)
        update = guild_record + timedelta(seconds=self.guild_interval)
        attr = f'{GUILD_RECORD[0]}_{GUILD_RECORD[1]}'
        logger.attr(f'{attr}', f'Record time: {guild_record}')
        logger.attr(f'{attr}', f'Next update: {update}')
        if not now > update:
            return False
        if not self.appear(GUILD_RED_DOT, offset=(30, 30)):
            logger.info('Guild red dot not appears, skip current check')
            self.config.record_save(option=GUILD_RECORD)
            return False

        self.ui_ensure(page_guild)

        # Lobby
        self.guild_lobby()

        # Logistics
        if self.config.ENABLE_GUILD_LOGISTICS \
                and not self.config.record_executed_since(option=RECORD_OPTION_LOGISTICS, since=RECORD_SINCE_LOGISTICS):
            # Save record when guild mission has been finished this week
            # If finished, only need to check guild logostics once a day
            # If not finished, check it in every guild reward
            if self.guild_logistics():
                self.config.record_save(option=RECORD_OPTION_LOGISTICS)

        # Operation
        if self.config.ENABLE_GUILD_OPERATIONS \
                and not self.config.record_executed_since(option=RECORD_OPTION_DISPATCH, since=RECORD_SINCE_DISPATCH):
            # Check guild operation 4 times a day
            self.guild_operations()

        self.guild_interval_reset()
        self.config.record_save(option=GUILD_RECORD)
        self.ui_goto_main()
        return True
Beispiel #13
0
    def meow_choose(self, count) -> bool:
        """
        Pages:
            in: page_meowfficer
            out: MEOWFFICER_BUY

        Args:
            count (int): 0 to 15.

        Returns:
            bool: If success.
        """
        remain, bought, total = MEOWFFICER.ocr(self.device.image)
        logger.attr('Meowfficer_remain', remain)

        # Check buy status
        if total != BUY_MAX:
            logger.warning(
                f'Invalid meowfficer buy limit: {total}, revise to {BUY_MAX}')
            total = BUY_MAX
            bought = total - remain
        if bought > 0:
            if bought >= count:
                logger.info(f'Already bought {bought} today, stopped')
                return False
            else:
                count -= bought
                logger.info(
                    f'Already bought {bought} today, only need to buy {count} more'
                )

        # Check coins
        coins = MEOWFFICER_COINS.ocr(self.device.image)
        if (coins < BUY_PRIZE) and (remain < total):
            logger.info('Not enough coins to buy one, stopped')
            return False
        elif (count - int(remain == total)) * BUY_PRIZE > coins:
            count = coins // BUY_PRIZE + int(remain == total)
            logger.info(f'Current coins only enough to buy {count}')

        self.ui_click(MEOWFFICER_BUY_ENTER,
                      check_button=MEOWFFICER_BUY,
                      additional=self.meow_additional,
                      retry_wait=3,
                      confirm_wait=0,
                      skip_first_screenshot=True)
        self.ui_ensure_index(count,
                             letter=MEOWFFICER_CHOOSE,
                             prev_button=MEOWFFICER_BUY_PREV,
                             next_button=MEOWFFICER_BUY_NEXT,
                             skip_first_screenshot=True)
        return True
Beispiel #14
0
    def missing_get(self,
                    battle_count,
                    mystery_count=0,
                    siren_count=0,
                    carrier_count=0):
        try:
            missing = self.spawn_data[battle_count].copy()
        except IndexError:
            missing = self.spawn_data[-1].copy()
        may = {'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0, 'carrier': 0}
        missing['enemy'] -= battle_count - siren_count
        missing['mystery'] -= mystery_count
        missing['siren'] -= siren_count
        missing['carrier'] = carrier_count - self.select(is_enemy=True,
                                                         may_enemy=False).count
        for grid in self:
            for attr in ['enemy', 'mystery', 'siren', 'boss']:
                if grid.__getattribute__(
                        'is_' + attr) and grid.__getattribute__('may_' + attr):
                    missing[attr] -= 1

        for grid in self:
            if not grid.is_fleet and not grid.is_mystery and not grid.is_siren:
                continue

            cover = [(0, -1)]
            if grid.is_current_fleet:
                cover.append((0, -2))

            for upper in cover:
                upper = tuple(np.array(grid.location) + upper)
                if upper in self:
                    upper = self[upper]
                    for attr in ['enemy', 'mystery', 'siren', 'boss']:
                        if upper.__getattribute__(
                                'may_' +
                                attr) and not upper.__getattribute__('is_' +
                                                                     attr):
                            may[attr] += 1
                    if upper.may_carrier:
                        may['carrier'] += 1

        logger.attr(
            'enemy_missing', ', '.join([
                f'{k[:2].upper()}:{str(v).rjust(2)}'
                for k, v in missing.items() if k != 'battle'
            ]))
        logger.attr(
            'enemy_may____', ', '.join([
                f'{k[:2].upper()}:{str(v).rjust(2)}' for k, v in may.items()
            ]))
        return may, missing
Beispiel #15
0
    def __init__(self, main_image, fleet_image, index):
        self.index = index
        self.power = self.get_power(image=main_image)
        self.level = self.get_level(image=fleet_image)
        self.priority = self.get_priority()

        # [OPPONENT_1] ( 8256) 120 120 120 | (12356) 100  80  80
        level = [str(x).rjust(3, ' ') for x in self.level]
        power = ['(' + str(x).rjust(5, ' ') + ')' for x in self.power]
        logger.attr(
            'OPPONENT_%s, %s' %
            (index, str(np.round(self.priority, 3)).ljust(5, '0')),
            ' '.join([power[0]] + level[:3] + ['|'] + [power[1]] + level[3:]))
Beispiel #16
0
    def ocr(self, image):
        start_time = time.time()

        image_list = [self.pre_process(i) for i in image]
        result_list = self.cnocr.ocr_for_single_lines(image_list)
        result_list = [self.after_process(result) for result in result_list]

        if len(self.buttons) == 1:
            result_list = result_list[0]
        logger.attr(name='%s %ss' % (self.name, str(round(time.time() - start_time, 3)).ljust(5, '0')),
                    text=str(result_list))

        return result_list
    def check_s3_enemy(self):
        if self.battle_count == 0:
            self.s3_enemy_count = 0
        elif self.battle_count >= 5:
            self.withdraw()

        current = self.map.select(is_enemy=True, enemy_scale=2) \
            .add(self.map.select(is_enemy=True, enemy_scale=1)) \
            .count
        logger.attr('S2_enemy', current)

        if self.s3_enemy_count >= self.config.C122MediumLeveling_LargeEnemyTolerance and current == 0:
            self.withdraw()
    def check_s3_enemy(self):
        if self.battle_count == 0:
            self.s3_enemy_count = 0
        elif self.battle_count >= 5:
            self.withdraw()

        current = self.map.select(is_enemy=True, enemy_scale=2)\
            .add(self.map.select(is_enemy=True, enemy_scale=1))\
            .count
        logger.attr('S2_enemy', current)

        if self.s3_enemy_count >= self.config.C122_S3_TOLERANCE and current == 0:
            self.withdraw()
Beispiel #19
0
    def research_sort_cheapest(self):
        """
        Returns:
            list: A list of str and int, such as [2, 3, 0, 'reset']
        """
        FILTER.load(FILTER_STRING_CHEAPEST)
        priority = FILTER.apply(self.projects)
        priority = self._research_check_filter(priority)

        logger.attr(
            'Cheapest_sort',
            ' > '.join([str(self.projects[index]) if isinstance(index, int) else index for index in priority]))
        return priority
Beispiel #20
0
    def enhance_ships_order(self, favourite=None):
        """
        Info:
            Target ships in order of specified
            type listing by ENHANCE_ORDER_STRING

        Pages:
            in: page_dock
            out: page_dock

        Args:
            favourite (bool):

        Returns:
            int: total enhanced
        """
        if favourite is None:
            favourite = self.config.ENHANCE_FAVOURITE

        logger.hr('Enhancement by type')
        total = 0

        ship_types = [s.strip().lower() for s in self.config.ENHANCE_ORDER_STRING.split('>')]
        enable_simple = True
        if ship_types == ['']:
            enable_simple = False
            ship_types = [None]
        logger.attr('Enhance Order', ship_types)

        for ship_type in ship_types:
            index = 0 # Helper variable only for _enhance_choose_simple
            logger.info(f'Favourite={favourite}, Ship Type={ship_type}')
            if not self._enhance_enter(favourite=favourite, ship_type=ship_type):
                logger.hr(f'Dock Empty by ship type {ship_type}')
                continue

            while 1:
                if enable_simple:
                    choose_result, index = self._enhance_choose_simple(current_index=index)
                else:
                    choose_result = self._enhance_choose()
                if not choose_result:
                    break
                self._enhance_confirm()
                total += 10
                if total >= self._retire_amount:
                    break
            self.ui_back(DOCK_FILTER)

        self._enhance_quit()
        return total
 def is_combat_loading(self):
     """
     Returns:
         bool:
     """
     similarity, location = TEMPLATE_COMBAT_LOADING.match_result(
         self.device.image.crop((0, 620, 1280, 720)))
     if similarity > 0.85:
         loading = (location[0] + 38 - LOADING_BAR.area[0]) / (
             LOADING_BAR.area[2] - LOADING_BAR.area[0])
         logger.attr('Loading', f'{int(loading * 100)}%')
         return True
     else:
         return False
Beispiel #22
0
 def research_has_finished(self):
     """
     Finished research should be auto-focused to the center, but sometimes didn't, due to an unknown game bug.
     This method will handle that.
     Returns:
         bool: True if a research finished
     """
     index = get_research_finished(self.device.image)
     if index is not None:
         logger.attr('Research_finished', index)
         self._research_finished_index = index
         return True
     else:
         return False
Beispiel #23
0
    def wait(self, fleet=(1, 2)):
        """
        Args:
            fleet (int, tuple):
        """
        self.update()
        recovered_time = self.recovered_time(fleet=fleet)
        while 1:
            if datetime.now() > recovered_time:
                break

            logger.attr('Emotion recovered', recovered_time)
            self.config.EMOTION_LIMIT_TRIGGERED = True
            sleep(60)
Beispiel #24
0
def research_detect(image):
    """
    Args:
        image (np.ndarray): Screenshot

    Return:
        list[ResearchProject]:
    """
    projects = []
    for name, series in zip(get_research_name(image), get_research_series(image)):
        project = ResearchProject(name=name, series=series)
        logger.attr('Project', project)
        projects.append(project)
    return projects
Beispiel #25
0
def get_os_reset_remain():
    """
    Returns:
        int: number of days before next opsi reset
    """
    from module.logger import logger

    next_reset = get_os_next_reset()
    now = datetime.now()
    logger.attr('OpsiNextReset', next_reset)

    remain = int((next_reset - now).total_seconds() // 86400)
    logger.attr('ResetRemain', remain)
    return remain
Beispiel #26
0
    def ui_goto(self, destination, skip_first_screenshot=False):
        """
        Args:
            destination (Page):
            skip_first_screenshot (bool):
        """
        # Iter
        visited = [self.ui_current]
        visited = set(visited)
        while 1:
            new = visited.copy()
            for page in visited:
                for link in page.links.keys():
                    if link in visited:
                        continue
                    link.parent = page
                    new.add(link)
            if len(new) == len(visited):
                break
            visited = new

        # Find path
        if destination.parent is None:
            return []
        route = [destination]
        while 1:
            destination = destination.parent
            if destination is not None:
                route.append(destination)
            else:
                break
        route.reverse()
        if len(route) < 2:
            logger.warning('No page route found.')
        logger.attr('UI route', ' - '.join([p.name for p in route]))

        # Click
        for p1, p2 in zip(route[:-1], route[1:]):
            # self.ui_click(source=p1, destination=p2)
            self.ui_click(
                click_button=p1.links[p2],
                check_button=p2.check_button,
                offset=(20, 20),
                skip_first_screenshot=skip_first_screenshot)
            self.ui_current = p2
            skip_first_screenshot = True

        # Reset
        for page in visited:
            page.parent = None
Beispiel #27
0
    def get_current_zone(self):
        """
        Returns:
            Zone:
        """
        if not self.is_in_map():
            logger.warning('Trying to get zone name, but not in OS map')
            raise ScriptError('Trying to get zone name, but not in OS map')

        name = self.get_zone_name()
        logger.info(f'Map name processed: {name}')
        self.zone = self.name_to_zone(name)
        logger.attr('Zone', self.zone)
        return self.zone
Beispiel #28
0
    def check_screen_size(self):
        width, height = self.device.window_size()
        if height > width:
            width, height = height, width

        logger.attr('Screen_size', f'{width}x{height}')

        if width == 1280 and height == 720:
            return True
        else:
            logger.warning(f'Not supported screen size: {width}x{height}')
            logger.warning('Alas requires 1280x720')
            logger.hr('Script end')
            exit(1)
Beispiel #29
0
    def set(self, status, main, skip_first_screenshot=True):
        """
        Args:
            status (str):
            main (ModuleBase):
            skip_first_screenshot (bool):

        Returns:
            bool:
        """
        self.get_data(status)

        counter = 0
        changed = False
        warning_show_timer = Timer(5, count=10).start()
        click_timer = Timer(1, count=3)
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                main.device.screenshot()

            # Detect
            current = self.get(main=main)
            logger.attr(self.name, current)

            # End
            if current == status:
                return changed

            # Warning
            if current == 'unknown':
                if warning_show_timer.reached():
                    logger.warning(f'Unknown {self.name} switch')
                    warning_show_timer.reset()
                    if counter >= 1:
                        logger.warning(f'{self.name} switch {status} asset has evaluated to unknown too many times, '
                                       f'asset should be re-verified')
                        return False
                    counter += 1
                continue

            # Click
            if click_timer.reached():
                click_status = status if self.is_choice else current
                main.device.click(self.get_data(click_status)['click_button'])
                click_timer.reset()
                changed = True

        return changed
Beispiel #30
0
    def ui_get_current_page(self):
        self.device.screenshot()
        for page in self.ui_pages:
            if self.ui_page_appear(page=page):
                logger.attr('UI', page.name)
                self.ui_current = page
                return page

        logger.info('Unknown ui page')
        if self.appear_then_click(GOTO_MAIN, offset=(20, 20)):
            logger.info('Goto page_main')
            self.ui_current = page_unknown
            self.ui_goto(page_main, skip_first_screenshot=True)

        if hasattr(self, 'ui_current'):
            logger.warning(
                f'Unrecognized ui_current, using previous: {self.ui_current}')
        else:
            logger.info('Unable to goto page_main')
            logger.attr('DEVICE_SCREENSHOT_METHOD',
                        self.config.DEVICE_SCREENSHOT_METHOD)
            logger.attr('DEVICE_CONTROL_METHOD',
                        self.config.DEVICE_CONTROL_METHOD)
            logger.attr('SERVER', self.config.SERVER)
            logger.warning('Starting from current page is not supported')
            logger.warning(
                f'Supported page: {[str(page) for page in self.ui_pages]}')
            logger.warning(
                f'Supported page: Any page with a "HOME" button on the upper-right'
            )
            if not self.device.app_is_running():
                raise GameNotRunningError('Game not running')
            else:
                exit(1)