def missing_predict(self,
                        battle_count,
                        mystery_count=0,
                        siren_count=0,
                        carrier_count=0,
                        mode='normal'):
        if self.poor_map_data:
            return False

        may, missing = self.missing_get(battle_count, mystery_count,
                                        siren_count, carrier_count, mode)

        # predict
        for upper in self.map_covered:
            for attr in ['enemy', 'mystery', 'siren', 'boss']:
                if upper.__getattribute__('may_' + attr) and missing[
                        attr] > 0 and missing[attr] == may[attr]:
                    logger.info('Predict %s to be %s' %
                                (location2node(upper.location), attr))
                    upper.__setattr__('is_' + attr, True)
            if carrier_count:
                if upper.may_carrier and missing['carrier'] > 0 and missing[
                        'carrier'] == may['carrier']:
                    logger.info('Predict %s to be enemy' %
                                location2node(upper.location))
                    upper.__setattr__('is_enemy', True)
    def find_path(self, location, step=0):
        location = location_ensure(location)

        path = self._find_path(location)
        if path is None or not len(path):
            logger.warning('No path found. Return destination.')
            return [location]
        logger.info('Full path: %s' % '[' +
                    ', '.join([location2node(grid) for grid in path]) + ']')

        portal_path = []
        index = [0]
        for i, loca in enumerate(zip(path[:-1], path[1:])):
            if self[loca[0]].is_portal and self[
                    loca[0]].portal_link == loca[1]:
                index += [i, i + 1]
        index.append(len(path))
        for start, end in zip(index[:-1], index[1:]):
            if end - start == 1 and self[path[start]].is_portal and self[
                    path[start]].portal_link == path[end]:
                continue
            local_path = path[start:end + 1]
            local_path = self._find_route_node(local_path, step=step)
            portal_path += local_path
            logger.info('Path: %s' % '[' +
                        ', '.join([location2node(grid)
                                   for grid in local_path]) + ']')
        path = portal_path

        return path
    def find_path(self, location, step=0):
        location = location_ensure(location)

        path = self._find_path(location)
        if path is None or not len(path):
            logger.warning('No path found. Return destination.')
            return [location]

        logger.info('Path: %s' % '[' +
                    ', '.join([location2node(grid) for grid in path]) + ']')
        path = self._find_route_node(path, step=step)
        logger.info('Path: %s' % '[' +
                    ', '.join([location2node(grid) for grid in path]) + ']')

        return path
    def _load_map_data(self, text):
        if not len(self.grids.keys()):
            grids = np.array([loca for loca, _ in self._parse_text(text)])
            self.shape = location2node(tuple(np.max(grids, axis=0)))

        for loca, data in self._parse_text(text):
            self.grids[loca].decode(data)
 def show_connection(self):
     logger.info('   ' + ' '.join([' ' + chr(x + 64 + 1) for x in range(self.shape[0] + 1)]))
     for y in range(self.shape[1] + 1):
         text = str(y + 1).rjust(2) + ' ' + ' '.join(
             [location2node(self[(x, y)].connection) if (x, y) in self and self[(x, y)].connection else '  ' for x in
              range(self.shape[0] + 1)])
         logger.info(text)
    def get_file_lines(self):
        """
        Returns:
            list(str): Python code in map file.
        """
        header = """
            from module.campaign.campaign_base import CampaignBase
            from module.map.map_base import CampaignMap
            from module.map.map_grids import SelectedGrids, RoadGrids
            from module.logger import logger
        """
        lines = []

        # Import
        for head in header.strip().split('\n'):
            lines.append(head.strip())
        lines.append('')
        lines.append('')

        # Map
        lines.append(f'MAP = CampaignMap(\'{self.chapter_name}\')')
        lines.append(f'MAP.shape = \'{location2node(self.shape)}\'')
        lines.append(
            f'MAP.camera_data = {[location2node(loca) for loca in camera_2d(self.shape, sight=(-3, -1, 3, 2))]}')
        lines.append(f'MAP.camera_data_spawn_point = []')
        lines.append('MAP.map_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join([self.map_data[(x, y)] for x in range(self.shape[0] + 1)]))
        lines.append('\"\"\"')
        lines.append('MAP.weight_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join(['10'] * (self.shape[0] + 1)))
        lines.append('\"\"\"')
        lines.append('MAP.spawn_data = [')
        for battle in self.spawn_data:
            lines.append('    ' + str(battle) + ',')
        lines.append(']')
        for y in range(self.shape[1] + 1):
            lines.append(', '.join([location2node((x, y)) for x in range(self.shape[0] + 1)]) + ', \\')
        lines.append('    = MAP.flatten()')
        lines.append('')
        lines.append('')

        # Config
        lines.append('class Config:')
        lines.append('    pass')
        lines.append('')
        lines.append('')

        # Campaign
        lines.append('class Campaign(CampaignBase):')
        lines.append('    MAP = MAP')
        lines.append('')
        lines.append('    def battle_0(self):')
        lines.append('        return self.battle_default()')
        lines.append('')
        lines.append(f'    def battle_{self.data["boss_refresh"]}(self):')
        lines.append('        return self.fleet_boss.clear_boss()')

        return lines
    def extract_map_size(self, server='zh-CN'):
        LOADER.server = server
        data = LOADER.load('sharecfgdata/world_chapter_template.lua')
        out = {}
        for full_index, chapter in data.items():
            if not full_index // 1000000 == 1 or not chapter['map_sight']:
                continue
            index = (full_index % 1000000) // 1000
            if index < 10:
                index -= 1
            shape = self.parse_map_data(chapter['grids'])
            out[index] = location2node(shape)

        return out
    def shape(self, scale):
        self._shape = node2location(scale.upper())
        for y in range(self._shape[1] + 1):
            for x in range(self._shape[0] + 1):
                grid = GridInfo()
                grid.location = (x, y)
                self.grids[(x, y)] = grid

        # camera_data can be generate automatically, but it's better to set it manually.
        self.camera_data = [location2node(loca) for loca in camera_2d((0, 0, *self._shape), sight=self.camera_sight)]
        self.camera_data_spawn_point = []
        # weight_data set to 10.
        for grid in self:
            grid.weight = 10.
    def extract_map_size(self, server='zh-CN'):
        folder = os.path.join(self.folder, server, 'sharecfg')
        data = load_lua_by_function(folder, 'world_chapter_template.lua')
        out = {}
        for full_index, chapter in data.items():
            if not full_index // 1000000 == 1 or not chapter['map_sight']:
                continue
            index = (full_index % 1000000) // 1000
            if index < 10:
                index -= 1
            shape = self.parse_map_data(chapter['grids'])
            out[index] = location2node(shape)

        return out
Beispiel #10
0
    def missing_predict(self,
                        battle_count,
                        mystery_count=0,
                        siren_count=0,
                        carrier_count=0):
        if self.poor_map_data:
            return False

        may, missing = self.missing_get(battle_count, mystery_count,
                                        siren_count, carrier_count)

        # predict
        for grid in self:
            if not grid.is_fleet and not grid.is_mystery:
                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 missing[
                                attr] > 0 and missing[attr] == may[attr]:
                            logger.info('Predict %s to be %s' %
                                        (location2node(upper.location), attr))
                            upper.__setattr__('is_' + attr, True)
                    if carrier_count:
                        if upper.may_carrier and missing[
                                'carrier'] > 0 and missing['carrier'] == may[
                                    'carrier']:
                            logger.info('Predict %s to be enemy' %
                                        location2node(upper.location))
                            upper.__setattr__('is_enemy', True)
Beispiel #11
0
    def shape(self, scale):
        self._shape = node2location(scale.upper())
        for y in range(self._shape[1] + 1):
            for x in range(self._shape[0] + 1):
                grid = GridInfo()
                grid.location = (x, y)
                self.grids[(x, y)] = grid

        # camera_data can be generate automatically, but it's better to set it manually.
        self.camera_data = [
            location2node(loca)
            for loca in camera_2d(self._shape, sight=self.camera_sight)
        ]
        # weight_data set to 10.
        for grid in self:
            grid.weight = 10.
        # Initialize grid connection.
        self.grid_connection_initial()
Beispiel #12
0
    def get_file_lines(self, has_modified_campaign_base):
        """
        Args:
            has_modified_campaign_base (bool): If target folder has modified campaign_base.py

        Returns:
            list(str): Python code in map file.
        """
        if IS_WAR_ARCHIVES:
            base_import = 'from ..campaign_war_archives.campaign_base import CampaignBase'
        elif has_modified_campaign_base:
            base_import = 'from .campaign_base import CampaignBase'
        else:
            base_import = 'from module.campaign.campaign_base import CampaignBase'

        header = f"""
            {base_import}
            from module.map.map_base import CampaignMap
            from module.map.map_grids import SelectedGrids, RoadGrids
            from module.logger import logger
        """
        lines = []

        # Import
        for head in header.strip().split('\n'):
            lines.append(head.strip())
        if self.chapter_name[-1].isdigit():
            chap, stage = self.chapter_name[:-1], self.chapter_name[-1]
            if stage != '1':
                lines.append(
                    f'from .{chap.lower()}1 import Config as ConfigBase')
        lines.append('')

        # Map
        lines.append(f'MAP = CampaignMap(\'{self.chapter_name}\')')
        lines.append(f'MAP.shape = \'{location2node(self.shape)}\'')
        camera_data = camera_2d(get_map_active_area(self.map_data),
                                sight=(-3, -1, 3, 2))
        lines.append(
            f'MAP.camera_data = {[location2node(loca) for loca in camera_data]}'
        )
        camera_sp = camera_spawn_point(
            camera_data,
            sp_list=[k for k, v in self.map_data.items() if v == 'SP'])
        lines.append(
            f'MAP.camera_data_spawn_point = {[location2node(loca) for loca in camera_sp]}'
        )
        if self.MAP_HAS_PORTAL:
            lines.append(f'MAP.portal_data = {self.portal}')
        lines.append('MAP.map_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join(
                [self.map_data[(x, y)] for x in range(self.shape[0] + 1)]))
        lines.append('\"\"\"')
        if self.map_data_loop is not None:
            lines.append('MAP.map_data_loop = \"\"\"')
            for y in range(self.shape[1] + 1):
                lines.append('    ' + ' '.join([
                    self.map_data_loop[(x, y)]
                    for x in range(self.shape[0] + 1)
                ]))
            lines.append('\"\"\"')
        lines.append('MAP.weight_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join(['50'] * (self.shape[0] + 1)))
        lines.append('\"\"\"')
        if self.MAP_HAS_LAND_BASED:
            lines.append(f'MAP.land_based_data = {self.land_based}')
        lines.append('MAP.spawn_data = [')
        for battle in self.spawn_data:
            lines.append('    ' + str(battle) + ',')
        lines.append(']')
        if self.spawn_data_loop is not None:
            lines.append('MAP.spawn_data_loop = [')
            for battle in self.spawn_data_loop:
                lines.append('    ' + str(battle) + ',')
            lines.append(']')
        for y in range(self.shape[1] + 1):
            lines.append(', '.join(
                [location2node((x, y))
                 for x in range(self.shape[0] + 1)]) + ', \\')
        lines.append('    = MAP.flatten()')
        lines.append('')
        lines.append('')

        # Config
        if self.chapter_name[-1].isdigit():
            chap, stage = self.chapter_name[:-1], self.chapter_name[-1]
            if stage != '1':
                lines.append('class Config(ConfigBase):')
            else:
                lines.append('class Config:')
        else:
            lines.append('class Config:')
        lines.append('    # ===== Start of generated config =====')
        if len(self.MAP_SIREN_TEMPLATE):
            lines.append(f'    MAP_SIREN_TEMPLATE = {self.MAP_SIREN_TEMPLATE}')
            lines.append(
                f'    MOVABLE_ENEMY_TURN = {tuple(self.MOVABLE_ENEMY_TURN)}')
            lines.append(f'    MAP_HAS_SIREN = True')
            lines.append(
                f'    MAP_HAS_MOVABLE_ENEMY = {self.MAP_HAS_MOVABLE_ENEMY}')
        lines.append(f'    MAP_HAS_MAP_STORY = {self.MAP_HAS_MAP_STORY}')
        lines.append(f'    MAP_HAS_FLEET_STEP = {self.MAP_HAS_FLEET_STEP}')
        lines.append(f'    MAP_HAS_AMBUSH = {self.MAP_HAS_AMBUSH}')
        if self.MAP_HAS_PORTAL:
            lines.append(f'    MAP_HAS_PORTAL = {self.MAP_HAS_PORTAL}')
        if self.MAP_HAS_LAND_BASED:
            lines.append(f'    MAP_HAS_LAND_BASED = {self.MAP_HAS_LAND_BASED}')
        for n in range(1, 4):
            if self.__getattribute__(f'STAR_REQUIRE_{n}') != n:
                lines.append(f'    STAR_REQUIRE_{n} = 0')
        lines.append('    # ===== End of generated config =====')
        lines.append('')
        lines.append('')

        # Campaign
        lines.append('class Campaign(CampaignBase):')
        lines.append('    MAP = MAP')
        lines.append('')
        lines.append('    def battle_0(self):')
        if len(self.MAP_SIREN_TEMPLATE):
            lines.append('        if self.clear_siren():')
            lines.append('            return True')
            lines.append('')
        lines.append('        return self.battle_default()')
        lines.append('')
        lines.append(f'    def battle_{self.data["boss_refresh"]}(self):')
        if self.data["boss_refresh"] >= 5:
            lines.append('        return self.fleet_boss.clear_boss()')
        else:
            lines.append('        return self.clear_boss()')

        return lines
Beispiel #13
0
    def __init__(self, data, data_loop):
        self.data = data
        self.data_loop = data_loop
        self.chapter_name = data['chapter_name'].replace('–', '-')
        self.name = data['name']
        self.profiles = data['profiles']
        self.map_id = data['id']

        try:
            self.spawn_data = self.parse_spawn_data(data)
            if data_loop is not None:
                self.spawn_data_loop = self.parse_spawn_data(data_loop)
                if len(self.spawn_data) == len(self.spawn_data_loop) \
                        and all([s1 == s2 for s1, s2 in zip(self.spawn_data, self.spawn_data_loop)]):
                    self.spawn_data_loop = None
            else:
                self.spawn_data_loop = None

            # map_data
            # {0: {0: 6, 1: 8, 2: False, 3: 0}, ...}
            self.map_data = self.parse_map_data(data['grids'])
            self.shape = tuple(np.max(list(self.map_data.keys()), axis=0))
            if self.data_loop is not None:
                self.map_data_loop = self.parse_map_data(data_loop['grids'])
                if all([
                        d1 == d2 for d1, d2 in zip(self.map_data.values(),
                                                   self.map_data_loop.values())
                ]):
                    self.map_data_loop = None
            else:
                self.map_data_loop = None

            # portal
            self.portal = []
            if self.map_id in MAP_EVENT_LIST:
                for event_id in MAP_EVENT_LIST[
                        self.map_id]['event_list'].values():
                    event = MAP_EVENT_TEMPLATE[event_id]
                    for effect in event['effect'].values():
                        if effect[0] == 'jump':
                            address = event['address']
                            address = location2node((address[1], address[0]))
                            target = location2node((effect[2], effect[1]))
                            self.portal.append((address, target))

            # land_based
            # land_based = {{6, 7, 1}, ...}
            # Format: {y, x, rotation}
            land_based_rotation_dict = {
                1: 'up',
                2: 'down',
                3: 'left',
                4: 'right'
            }
            self.land_based = []
            if isinstance(data['land_based'], dict):
                for lb in data['land_based'].values():
                    y, x, r = lb.values()
                    if r not in land_based_rotation_dict:
                        continue
                    self.land_based.append(
                        [location2node((x, y)), land_based_rotation_dict[r]])

            # config
            self.MAP_SIREN_TEMPLATE = []
            self.MOVABLE_ENEMY_TURN = set()
            for siren_id in data['ai_expedition_list'].values():
                if siren_id == 1:
                    continue
                exped_data = EXPECTATION_DATA[siren_id]
                name = exped_data['icon']
                name = DIC_SIREN_NAME_CHI_TO_ENG.get(name, name)
                if name not in self.MAP_SIREN_TEMPLATE:
                    self.MAP_SIREN_TEMPLATE.append(name)
                self.MOVABLE_ENEMY_TURN.add(int(exped_data['ai_mov']))
            self.MAP_HAS_MOVABLE_ENEMY = bool(len(self.MOVABLE_ENEMY_TURN))
            self.MAP_HAS_MAP_STORY = len(data['story_refresh_boss']) > 0
            self.MAP_HAS_FLEET_STEP = bool(data['is_limit_move'])
            self.MAP_HAS_AMBUSH = bool(data['is_ambush']) or bool(
                data['is_air_attack'])
            self.MAP_HAS_PORTAL = bool(len(self.portal))
            self.MAP_HAS_LAND_BASED = bool(len(self.land_based))
            for n in range(1, 4):
                self.__setattr__(f'STAR_REQUIRE_{n}',
                                 data[f'star_require_{n}'])
        except Exception as e:
            for k, v in data.items():
                print(f'{k} = {v}')
            raise e
Beispiel #14
0
 def __str__(self):
     return location2node(self.location)
Beispiel #15
0
    def get_file_lines(self):
        """
        Returns:
            list(str): Python code in map file.
        """
        header = """
            from module.campaign.campaign_base import CampaignBase
            from module.map.map_base import CampaignMap
            from module.map.map_grids import SelectedGrids, RoadGrids
            from module.logger import logger
        """
        lines = []

        # Import
        for head in header.strip().split('\n'):
            lines.append(head.strip())
        if self.chapter_name[-1].isdigit():
            chap, stage = self.chapter_name[:-1], self.chapter_name[-1]
            if stage != '1':
                lines.append(
                    f'from .{chap.lower()}1 import Config as ConfigBase')
        lines.append('')

        # Map
        lines.append(f'MAP = CampaignMap(\'{self.chapter_name}\')')
        lines.append(f'MAP.shape = \'{location2node(self.shape)}\'')
        lines.append(
            f'MAP.camera_data = {[location2node(loca) for loca in camera_2d(self.shape, sight=(-3, -1, 3, 2))]}'
        )
        lines.append(f'MAP.camera_data_spawn_point = []')
        lines.append('MAP.map_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join(
                [self.map_data[(x, y)] for x in range(self.shape[0] + 1)]))
        lines.append('\"\"\"')
        lines.append('MAP.weight_data = \"\"\"')
        for y in range(self.shape[1] + 1):
            lines.append('    ' + ' '.join(['50'] * (self.shape[0] + 1)))
        lines.append('\"\"\"')
        lines.append('MAP.spawn_data = [')
        for battle in self.spawn_data:
            lines.append('    ' + str(battle) + ',')
        lines.append(']')
        for y in range(self.shape[1] + 1):
            lines.append(', '.join(
                [location2node((x, y))
                 for x in range(self.shape[0] + 1)]) + ', \\')
        lines.append('    = MAP.flatten()')
        lines.append('')
        lines.append('')

        # Config
        if self.chapter_name[-1].isdigit():
            chap, stage = self.chapter_name[:-1], self.chapter_name[-1]
            if stage != '1':
                lines.append('class Config(ConfigBase):')
            else:
                lines.append('class Config:')
        else:
            lines.append('class Config:')
        # lines.append('    pass')
        if len(self.MAP_SIREN_TEMPLATE):
            lines.append(f'    MAP_SIREN_TEMPLATE = {self.MAP_SIREN_TEMPLATE}')
            lines.append(
                f'    MOVABLE_ENEMY_TURN = {tuple(self.MOVABLE_ENEMY_TURN)}')
            lines.append(f'    MAP_HAS_SIREN = True')
        lines.append(f'    MAP_HAS_MAP_STORY = {self.MAP_HAS_MAP_STORY}')
        lines.append(f'    MAP_HAS_FLEET_STEP = {self.MAP_HAS_FLEET_STEP}')
        lines.append('')
        lines.append('')

        # Campaign
        lines.append('class Campaign(CampaignBase):')
        lines.append('    MAP = MAP')
        lines.append('')
        lines.append('    def battle_0(self):')
        if len(self.MAP_SIREN_TEMPLATE):
            lines.append('        if self.clear_siren():')
            lines.append('            return True')
            lines.append('')
        lines.append('        return self.battle_default()')
        lines.append('')
        lines.append(f'    def battle_{self.data["boss_refresh"]}(self):')
        if self.data["boss_refresh"] >= 5:
            lines.append('        return self.fleet_boss.clear_boss()')
        else:
            lines.append('        return self.clear_boss()')

        return lines
Beispiel #16
0
    def __init__(self, data, data_loop):
        self.data = data
        self.data_loop = data_loop
        self.chapter_name = data['chapter_name'].replace('–', '-')
        self.name = data['name']
        self.profiles = data['profiles']
        self.map_id = data['id']
        try:
            battle_count = max(data['boss_refresh'],
                               max(data['enemy_refresh'].keys()))
        except ValueError:
            battle_count = 0
        self.spawn_data = [{
            'battle': index
        } for index in range(battle_count + 1)]
        try:
            # spawn_data
            for index, count in data['enemy_refresh'].items():
                if count:
                    spawn = self.spawn_data[index]
                    spawn['enemy'] = spawn.get('enemy', 0) + count
            if ''.join([str(item) for item in data['elite_refresh'].values()
                        ]) != '100':  # Some data is incorrect
                for index, count in data['elite_refresh'].items():
                    if count:
                        spawn = self.spawn_data[index]
                        spawn['enemy'] = spawn.get('enemy', 0) + count
            for index, count in data['ai_refresh'].items():
                if count:
                    spawn = self.spawn_data[index]
                    spawn['siren'] = spawn.get('siren', 0) + count
            for index, count in data['box_refresh'].items():
                if count:
                    spawn = self.spawn_data[index]
                    spawn['mystery'] = spawn.get('mystery', 0) + count
            try:
                self.spawn_data[data['boss_refresh']]['boss'] = 1
            except IndexError:
                pass

            # map_data
            # {0: {0: 6, 1: 8, 2: False, 3: 0}, ...}
            self.map_data = self.parse_map_data(data['grids'])
            self.shape = tuple(np.max(list(self.map_data.keys()), axis=0))
            if self.data_loop is not None:
                self.map_data_loop = self.parse_map_data(data_loop['grids'])
                if all([
                        d1 == d2 for d1, d2 in zip(self.map_data.values(),
                                                   self.map_data_loop.values())
                ]):
                    self.map_data_loop = None
            else:
                self.map_data_loop = None

            # portal
            self.portal = []
            if self.map_id in MAP_EVENT_LIST:
                for event_id in MAP_EVENT_LIST[
                        self.map_id]['event_list'].values():
                    event = MAP_EVENT_TEMPLATE[event_id]
                    for effect in event['effect'].values():
                        if effect[0] == 'jump':
                            address = event['address']
                            address = location2node((address[1], address[0]))
                            target = location2node((effect[2], effect[1]))
                            self.portal.append((address, target))

            # config
            self.MAP_SIREN_TEMPLATE = []
            self.MOVABLE_ENEMY_TURN = set()
            for siren_id in data['ai_expedition_list'].values():
                if siren_id == 1:
                    continue
                exped_data = EXPECTATION_DATA[siren_id]
                name = exped_data['icon']
                name = DIC_SIREN_NAME_CHI_TO_ENG.get(name, name)
                self.MAP_SIREN_TEMPLATE.append(name)
                self.MOVABLE_ENEMY_TURN.add(int(exped_data['ai_mov']))
            self.MAP_HAS_MOVABLE_ENEMY = bool(len(self.MOVABLE_ENEMY_TURN))
            self.MAP_HAS_MAP_STORY = len(data['story_refresh_boss']) > 0
            self.MAP_HAS_FLEET_STEP = bool(data['is_limit_move'])
            self.MAP_HAS_AMBUSH = bool(data['is_ambush']) or bool(
                data['is_air_attack'])
            self.MAP_HAS_PORTAL = bool(len(self.portal))
            for n in range(1, 4):
                self.__setattr__(f'STAR_REQUIRE_{n}',
                                 data[f'star_require_{n}'])
        except Exception as e:
            for k, v in data.items():
                print(f'{k} = {v}')
            raise e