Repository: SJTUwbl/MaCA Branch: master Commit: e8503f565a6b Files: 56 Total size: 381.0 KB Directory structure: gitextract_qb55n16v/ ├── .gitignore ├── README.md ├── agent/ │ ├── airc_homo_rule/ │ │ └── agent.py │ ├── base_agent.py │ ├── fix_rule/ │ │ ├── agent.py │ │ ├── agent_core.py │ │ ├── license.lic │ │ ├── product.key │ │ ├── pyshield.key │ │ ├── pyshield.lic │ │ └── pytransform.py │ ├── fix_rule_no_att/ │ │ ├── agent.py │ │ ├── agent_core.py │ │ ├── license.lic │ │ ├── product.key │ │ ├── pyshield.key │ │ ├── pyshield.lic │ │ └── pytransform.py │ └── simple/ │ ├── agent.py │ └── dqn.py ├── common/ │ └── agent_process.py ├── configuration/ │ ├── reward.py │ └── system.py ├── doc/ │ └── tutorial.md ├── environment/ │ ├── error_log.py │ ├── interface.py │ ├── license.lic │ ├── product.key │ ├── pyshield.key │ ├── pyshield.lic │ ├── pytransform.py │ ├── render/ │ │ ├── __init__.py │ │ ├── render_pic.py │ │ └── render_pic2.py │ └── world/ │ ├── __init__.py │ ├── config.py │ ├── detection_calc.py │ ├── em_battle.py │ ├── load_map.py │ ├── log.py │ ├── position_calc.py │ ├── replay.py │ └── strike_calc.py ├── fight.py ├── fight_mp.py ├── log/ │ └── readme ├── model/ │ └── README ├── obs_construct/ │ ├── airc_homo_rule/ │ │ └── construct.py │ └── simple/ │ └── construct.py ├── replay.py ├── tournament/ │ ├── config.ini │ ├── config_gen.py │ └── tournament_mp.py ├── train/ │ └── simple/ │ ├── dqn.py │ └── main.py └── utility.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log .static_storage/ .media/ local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # Windows: Thumbs.db ehthumbs.db Desktop.ini data .idea *.csv/application/api_demo_pygame.py /application/pygameTest.py *.csv /application/api_demo2.py /application/api_demo_pygame.py /application/rule_test.py /environment/render/render_th.py /application/array.py /environment/encryption/pyarmor /environment/encryption/.pyarmor_capsule.zip /environment/encryption/.pyarmor_config *.macalog *.pkl /tournament/result ================================================ FILE: README.md ================================================ # MaCA ![](https://img.shields.io/badge/language-python-green.svg) ![](https://img.shields.io/badge/platform-windows-green.svg) ![](https://img.shields.io/badge/platform-linux-green.svg) ![](https://img.shields.io/badge/platform-mac-green.svg) ![](https://img.shields.io/badge/stability-experimental-green.svg) Multi-agent Combat Arena (MaCA) is a heterogeneous multi-agent distributed decision and control technology reasearch platform produced by CETC-TFAI team. It focuses on the application of AI technologies e.g. reinforcement learning in multi-agent cooperation and confrontation ![](https://leonfg.github.io/maca/resource/maca.gif) ## System Requirements - Linux 64-bit or Mac OS with Python 3.6 - numpy 1.14.2 or later - pygame 1.9.3 or later There is no limitation on agents' structure. You can write rule-based algorithms or use deep learning frameworks. ## Quick Start Guide ### Installation ```bash pip install -U numpy pandas pygame git clone https://github.com/CETC-TFAI/MaCA.git cd MaCA export PYTHONPATH=$(pwd)/environment:$PYTHONPATH ``` ### Run a combat between two agents [fight_mp.py](fight_mp.py) can execute two agents. It uses two instances of a fixed-rule agent to fight each other by default. ```bash python fight_mp.py ``` You can specify agents and map by input arguments. In addition an agent should provide a call interface follows the MaCa platform specification. ### Replay MaCa can record runtime log while playing and training. Use [replay.py](replay.py) to perform a replay. First, run [fight_mp.py](fight_mp.py) and enable log record function ```bash python fight_mp.py --log ``` Then, run [replay.py](replay.py) to replay the log ```bash python replay.py fix_rule_vs_fix_rule ``` The log structure of MaCA is a set of .macalog files, they will be saved in path "log/log-name/". When you run the [replay.py](replay.py), You must input a "log-name" as the parameter to specify which log you want to replay. For more information, see [tutorial](/doc/tutorial.md) ================================================ FILE: agent/airc_homo_rule/agent.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Xunyun Liu @contact: xunyunliu@gmail.com @software: PyCharm @file: agent.py @time: @desc: rule based agent """ from agent.base_agent import BaseAgent import copy import random import utility as ut import numpy as np DETECTOR_NUM = 0 FIGHTER_NUM = 10 COURSE_NUM = 16 ATTACK_IND_NUM = (DETECTOR_NUM + FIGHTER_NUM) * 2 + 1 # long missile attack + short missile attack + no attack ACTION_NUM = COURSE_NUM * ATTACK_IND_NUM LONG_MISSILE_RANGE = 120 SHORT_MISSILE_RANGE = 50 SQUARE_SIZE = 50 class Agent(BaseAgent): def __init__(self): """ Init this agent :param size_x: battlefield horizontal size :param size_y: battlefield vertical size :param detector_num: detector quantity of this side :param fighter_num: fighter quantity of this side """ BaseAgent.__init__(self) self.obs_ind = 'airc_homo_rule' self.last_random_action_set = {} self.to_explore_set = {} self.explore_destination_set = {} self.rule_set = 'attack' # if not os.path.exists('model/simple/model.pkl'): # print('Error: agent simple model data not exist!') # exit(1) # self.fighter_model = dqn.RLFighter(ACTION_NUM) def set_map_info(self, size_x, size_y, detector_num, fighter_num): self.size_x = size_x self.size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num # some eligibility checks if self.detector_num != 0: print('Error: agents using airc_homo_rule can only be used in a homogeneous environment!') exit(1) if self.size_x % SQUARE_SIZE != 0 or self.size_y % SQUARE_SIZE != 0: print('Error, the map size is not a multiple of SQUARE_SIZE') exit(1) # initialise data structures for map exploration self.to_explore_set = set(range((size_x // SQUARE_SIZE) * (size_y // SQUARE_SIZE))) # initialise last_random_action_set for i in range(self.fighter_num): # it doesn't matter what the initial value is as it will be overwritten in the first round self.last_random_action_set[i] = [0, 1, 0, 0] def __reset(self): pass def get_action(self, obs_dict, step_cnt): """ get actions :param detector_obs_list: :param fighter_obs_list: :param joint_obs_dict: :param step_cnt: :return: """ # initialise the explore_destination_set based on the birthplace of the agent if step_cnt == 1: for i in range(self.fighter_num): self.explore_destination_set[i] = (self.size_x - obs_dict[i]['pos_x'], obs_dict[i]['pos_y']) detector_action = [] fighter_action = [] # loop over the fighter list to generate actions for index in range(self.fighter_num): # [0, 1, 0, 0] is just a placeholder tentative_action = np.array([0, 1, 0, 0], dtype=np.int32) if obs_dict[index]['alive']: try: if self.rule_set == 'random': tentative_action = self.choose_action_random(index, step_cnt) elif self.rule_set == 'random_attack': tentative_action = self.choose_action_random_attack(obs_dict, index, step_cnt) elif self.rule_set == 'explore': tentative_action = self.choose_action_explore(obs_dict, index) elif self.rule_set == 'attack': tentative_action = self.choose_action_attack(obs_dict, index) elif self.rule_set == 'still': tentative_action = self.choose_action_still(step_cnt) except AttributeError: print("agent is missing a rule_set attribute. Now flying towards right as default.") # action formation # true_action[0] = int(360 / COURSE_NUM * int(tmp_action[0] / ATTACK_IND_NUM)) # true_action[3] = int(tmp_action[0] % ATTACK_IND_NUM) # radar and interference logic tentative_action[1] = step_cnt % 10 + 1 tentative_action[2] = step_cnt % 10 + 1 tmp_action = {} tmp_action['course'] = tentative_action[0] tmp_action['r_iswork'] = True tmp_action['r_fre_point'] = 2 tmp_action['j_iswork'] = True tmp_action['j_fre_point'] = 2 if tentative_action[3] > self.fighter_num: tmp_action['hit_target'] = tentative_action[3] - self.fighter_num tmp_action['missile_type'] = 2 else: tmp_action['hit_target'] = tentative_action[3] tmp_action['missile_type'] = 1 fighter_action.append(copy.deepcopy(tmp_action)) # fighter_action = np.array(fighter_action) return detector_action, fighter_action # the logic of engagement with enemies def engage_enemy(self, tmp_action, obs_dict, index): # default -- no attack allowed tmp_action[3] = 0 if 'target_list' in obs_dict[index]: # flying towards the nearest enemy tmp_action[0] = obs_dict[index]['target_list'][0]['angle'] target_id = obs_dict[index]['target_list'][0]['id'] target_distance = obs_dict[index]['target_list'][0]['distance'] # check if the nearest enemy has been attacked strike_list = obs_dict['strike_list'] strike_target_id_set = set(ele['target_id'] for ele in strike_list) if target_id in strike_target_id_set: return tmp_action # perform attack while having enough ammunition if obs_dict[index]['s_missile_left'] + obs_dict[index]['l_missile_left'] == 0: return tmp_action elif target_distance <= SHORT_MISSILE_RANGE: if obs_dict[index]['s_missile_left'] > 0: tmp_action[3] = target_id + 10 else: tmp_action[3] = target_id elif SHORT_MISSILE_RANGE < target_distance <= LONG_MISSILE_RANGE: if obs_dict[index]['l_missile_left'] > 0: tmp_action[3] = target_id return tmp_action def choose_action_random(self, index, step_cnt): if step_cnt % 20 == 1: self.last_random_action_set[index][0] = random.randint(0, 359) return self.last_random_action_set[index] def choose_action_random_attack(self, obs_dict, index, step_cnt): random_action = self.choose_action_random(index, step_cnt) return self.engage_enemy(random_action, obs_dict, index) def choose_action_explore(self, obs_dict, index): # maintain the data structure for map exploration area_x = int(obs_dict[index]['pos_x'] / SQUARE_SIZE) area_y = int(obs_dict[index]['pos_y'] / SQUARE_SIZE) explored_index = area_x * (self.size_y / SQUARE_SIZE) + area_y if explored_index >= (self.size_x / SQUARE_SIZE) * (self.size_y / SQUARE_SIZE): print('explored_index out of range!') explored_index = (self.size_x / SQUARE_SIZE) * (self.size_y / SQUARE_SIZE) - 1 if explored_index in self.to_explore_set: self.to_explore_set.remove(explored_index) if len(self.to_explore_set) == 0: self.to_explore_set = set(range(round((self.size_x / SQUARE_SIZE) * (self.size_y / SQUARE_SIZE)))) # if agent has a destination to explore if index in self.explore_destination_set: # delete the destination when getting close enough if ut.distance(obs_dict[index]['pos_x'], obs_dict[index]['pos_y'], self.explore_destination_set[index][0], self.explore_destination_set[index][1]) <= 5: # print('trying to delete the key') del self.explore_destination_set[index] # print('delete succesfully') # if the agent has no destination, randomly select a destination from the to_explore_set if index not in self.explore_destination_set: if len(self.to_explore_set) == 0: print('Error! to_explore_set is empty') selected_ele = random.sample(self.to_explore_set, 1) # print('selected_ele: ', selected_ele) ele_index_x = int(selected_ele[0] / (self.size_y / SQUARE_SIZE)) # print('ele_index_x: ', ele_index_x) ele_index_y = int(selected_ele[0] % (self.size_y / SQUARE_SIZE)) # print('ele_index_y', ele_index_y) ele_pos_x = int(ele_index_x * SQUARE_SIZE + SQUARE_SIZE / 2) ele_pos_y = int(ele_index_y * SQUARE_SIZE + SQUARE_SIZE / 2) # print('ele_pos_x: ', ele_pos_x, 'ele_pos_y', ele_pos_y) self.explore_destination_set[index] = (ele_pos_x, ele_pos_y) # print('self.explore_destination_set[index]: ', self.explore_destination_set[index]) # print('angle: ', # ut.angle(obs_dict[index]['pos_x'], obs_dict[index]['pos_y'], self.explore_destination_set[index][0], # self.explore_destination_set[index][1])) # navigating towards the explore destination return [ut.angle(obs_dict[index]['pos_x'], obs_dict[index]['pos_y'], self.explore_destination_set[index][0], self.explore_destination_set[index][1]), 1, 0, 0] def choose_action_attack(self, obs_dict, index): return self.engage_enemy(self.choose_action_explore(obs_dict, index), obs_dict, index) @staticmethod def choose_action_still(step_cnt): if 1 <= step_cnt % 40 <= 10: return [0, 1, 0, 0] elif 11 <= step_cnt % 40 <= 20: return [90, 1, 0, 0] elif 21 <= step_cnt % 40 <= 30: return [180, 1, 0, 0] else: return [270, 1, 0, 0] ================================================ FILE: agent/base_agent.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: base_agent.py @time: 2018/3/22 0022 14:13 @desc: base class of agent """ class BaseAgent: def __init__(self): """ init """ self.obs_ind = 'raw' self.size_x = 0 self.size_y = 0 self.detector_num = 0 self.fighter_num = 0 def get_obs_ind(self): """ get obs info :return: obs const class path. """ return self.obs_ind ================================================ FILE: agent/fix_rule/agent.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- from pytransform import pyarmor_runtime pyarmor_runtime() """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: agent.py @time: 2018/3/13 0013 10:51 @desc: rule based agent """ from agent.fix_rule.agent_core import Agent as ag class Agent: def __init__(self): """ Init this agent """ self.agent_core = ag() def set_map_info(self, size_x, size_y, detector_num, fighter_num): return self.agent_core.set_map_info(size_x, size_y, detector_num, fighter_num) def get_action(self, obs_dict, step_cnt): """ get actions :param detector_obs_list: :param fighter_obs_list: :param joint_obs_dict: :param step_cnt: :return: """ return self.agent_core.get_action(obs_dict, step_cnt) def get_obs_ind(self): return self.agent_core.get_obs_ind() ================================================ FILE: agent/fix_rule/agent_core.py ================================================ __pyarmor__(__name__, __file__, b'\xe7\x50\x8c\x64\x26\x42\xd6\x01\xb9\x4e\x3f\xdf\x40\x99\x74\xef\xb8\xc9\x91\x76\x3b\x40\xaa\xbe\x5d\xc5\xbd\x32\x0b\xf3\x42\x40\xe3\xce\x4f\x75\x5d\x9f\x4c\x04\x35\x6e\xf5\xb9\xdb\xfc\x44\xa8\x11\xd0\xdb\xb1\x0f\x62\xe2\x75\xc1\x38\x66\x9c\x65\x70\xe1\x0d\x2b\x57\x65\xd2\x85\x6d\x0e\x9d\xb5\x16\x6d\x19\x4c\x08\x05\x50\xe0\xd6\x28\xd8\xe5\x1b\x68\xb7\x96\xd4\x21\x0e\x87\x99\x7d\xc0\x18\x75\xb0\x67\x5d\x77\xba\xb0\xc6\xdf\x31\x69\x39\x6e\xd1\x45\x73\xec\xd7\xec\x65\x93\xcb\x46\x43\xd3\x68\x99\x09\xa8\x80\xd9\x28\xa0\xf6\xc2\xa2\xe5\xc3\xe7\x36\x9b\xec\xf2\x0f\x2a\xf1\x9d\xb6\x41\xae\x5c\x09\x7b\x21\x59\x72\xee\xc1\x98\x30\x0e\xb3\xe9\x7e\x44\x8a\x9b\x52\x69\x33\xac\x8f\x31\xb4\x23\xde\xcc\xe2\xad\xb2\x31\x53\x7a\x9b\xb1\x57\xd3\x1c\x71\x51\x17\x75\x19\x03\x24\x0c\x7a\x01\x93\x06\x9a\x0b\x6e\xee\xa6\xc5\xb4\xc2\x8d\x0a\x69\x52\xbf\xd2\xaf\xd4\xc5\xa7\x60\xb1\x44\xd6\xfa\xbb\xc3\x19\x93\x06\x7f\x83\x00\x59\x12\xb4\xaf\x74\x36\xa9\x34\x5a\x43\xc5\xd2\xdf\xa7\x6e\x78\x20\x07\x75\x3f\xae\x92\x75\x22\xd7\xc6\xc7\x4c\xc5\x2a\x22\x8d\xb0\x44\xe9\xeb\xf4\x69\x59\x8c\x53\x63\x94\xa7\x15\x85\x68\xfa\xc7\x20\x49\x9e\x36\x1e\x2b\x40\xe2\x98\x34\xa8\x51\x55\xfc\xc9\x13\xee\x24\x60\x57\x6c\xdf\xad\x56\x49\x4e\x38\x9d\x4d\x49\xda\x4d\x2e\xfe\xe2\x09\x0b\x42\xaf\x41\x88\x2a\x47\x73\xa7\x5f\x24\xcf\x2c\x1d\x84\xe7\x99\x71\x01\x66\xce\xdd\xaa\xb3\xf3\xfe\xab\xf7\x11\x30\x95\xbd\xc0\xfa\xbb\xa7\x7c\x06\xbe\xb6\x9c\x18\x0c\xac\x47\x43\xdd\x4a\x65\x10\x58\x25\x7a\x93\x57\x0d\x03\xeb\x9b\x25\x1c\xec\x3b\xb7\x91\x12\xac\xad\x85\xf6\x3b\x99\x7d\x4b\xe8\x89\x28\x3a\xd5\xa7\x92\x64\x06\xcb\xa3\x14\x52\xf2\x1b\x3e\xa3\x4a\x01\x0d\x59\x18\x76\x35\x10\x06\x55\x18\xcd\x7e\x34\x24\x70\x5d\xfc\xd1\x0a\x68\x16\xeb\xb1\x70\xb2\x6b\x17\xe3\xa9\x7a\xc5\xc1\x34\x37\x3b\xae\xe3\x00\xe0\x09\xf2\x1e\xb4\xca\xa2\x20\xef\xf8\xd9\x5b\x06\xd2\xa3\x50\xdb\xdd\x20\xfe\x0f\xc1\x43\x52\x48\x3c\xf9\x61\x17\xdb\x1a\xd2\xb3\xee\x0a\xe4\xf4\x9d\x8c\xe3\x37\xdd\x3d\x74\xf9\xaf\xe3\x99\x15\x41\x7d\xcd\x03\x08\x92\x92\x8b\xc1\x4d\x83\x4f\x89\x2f\x04\xf0\xcf\xae\x25\x5a\x92\x62\xa6\x94\x92\xdf\xe1\xa6\xe4\x15\x7b\xe2\x3b\xa8\x42\x1a\xe8\xfb\xa0\x38\x71\x46\xfa\xde\x4d\xad\x43\x60\x96\x6a\xd8\x03\x87\xa2\x30\x50\x2e\x68\xfb\xfa\xa4\xb9\x8c\xfd\x3a\x94\xe7\x82\x42\xce\x89\xc8\x8c\xa2\x17\x93\x75\x23\x46\xdf\xb6\x28\x32\x2d\xcf\x6d\xe0\xfb\x8e\xdd\x7f\xbe\x79\x67\x17\xae\x74\xc9\x0e\xec\x81\x52\x38\x1e\x78\x4b\x97\xe8\x61\xae\x16\x32\x97\xdb\x2b\xbe\x41\x00\xd4\xd6\x3a\xe0\xac\xd3\x85\x02\x95\x4a\x1d\x32\xab\xbb\x7b\x4c\xb0\x35\x26\x39\x31\x1b\xcc\xe8\xa6\xb3\xc8\x8d\x77\x42\x95\x26\x6d\xef\x37\xbf\x6c\x28\xa2\x21\xaf\xdd\xd4\x58\xcf\x52\xa0\x2f\x94\xf1\xe9\x2d\xef\xd4\x58\xf5\xa5\x79\xed\xb5\x9a\x7b\x95\xd0\x0c\xea\x45\x1c\xa0\xe8\x6f\x76\xfa\xcf\x8c\x63\x6a\x7a\xc7\x73\x36\x85\x4f\x7b\xde\x9f\x37\x18\x99\x0f\x97\x21\xd4\x78\x02\x8b\xbc\x55\x7a\x88\x32\x78\x13\xc6\x4d\xbf\x12\x9c\x52\xea\x83\x9e\x79\x33\xde\x63\x93\x1f\xfe\xf1\x15\x64\x87\x38\x26\x1b\x83\x5e\x80\x70\xfc\x85\x3d\x7c\xae\x2d\x4f\x0d\x13\xf4\xb4\xa4\x94\x3c\x6b\x5c\x85\xdc\xd4\xc7\xd2\x85\x3a\x9c\x10\x28\x25\x1f\xc3\x95\xd4\x11\x88\xe1\x3a\x5a\xfc\xc0\xde\x86\x4f\x7a\x8a\xb2\x06\x1a\x53\xbb\x46\x72\xe4\xec\xb8\x68\x4c\xa2\xf9\xda\xf5\x17\x6a\xb2\x76\xd3\x92\x36\x70\x6b\xcd\xeb\xa7\x22\x4f\x14\x48\xc2\x96\x10\xff\x36\xe1\xf3\xcc\x81\xec\xe2\x58\xc4\x56\x3c\x6a\x50\xb3\x5a\xb0\x59\xae\xce\x26\x54\xb0\xf6\x05\xf3\xb1\x7e\x8e\x2a\xa8\xc6\x2e\x22\x59\x55\x80\x2f\xe6\x6d\xee\xb7\xcd\xcc\xb1\x57\x67\xef\x7a\x5e\xf5\x23\xad\x2b\xaf\x7a\xcb\x4e\xad\x59\x46\xd7\xe1\xd0\x71\xad\xe6\xe9\x86\x41\x55\x15\x77\x86\x8c\xab\x4c\xf9\xa0\x3b\x20\x6b\x38\xc5\x68\x64\x24\x00\xca\x9d\xff\x20\x74\x60\xb4\x39\x6c\xc9\x9e\x99\xa7\x86\x0b\x92\xc6\xe4\xe4\x8d\x1f\x14\xe7\x08\x4e\x2b\x69\x3f\xdb\x71\x76\xaf\x2f\x40\xe7\x2a\xd8\x74\x06\x4a\xcb\xc4\x6c\x52\x08\xc8\xe3\x0e\x52\x8d\x3d\x5d\x2b\xfb\x32\x37\x5a\x8d\xed\x15\xe8\x64\xde\x88\xf7\x39\x73\x82\x91\xc5\x00\xa1\xe1\x4c\x8c\xd2\x38\x07\xff\xe5\xae\x9e\x17\xc4\xb2\xd6\xee\x53\x59\xcb\x17\xdf\xbe\x25\x63\xa1\x77\x3e\x31\xcf\xc8\x2f\x82\x3b\xcd\x20\xa6\xce\x49\x9d\x35\x53\xd5\x49\xc2\x5d\x4f\xd3\xaf\x12\x13\x6f\xff\x36\x26\xa7\x7b\xc4\xbe\xf3\x38\xe1\x6c\xd8\xb1\xe3\xa5\xd4\x8b\x32\xfd\xf6\xa0\x82\x0e\xd5\xc0\xa8\x0d\xf7\xc5\x23\x9a\x0f\xa2\xda\xa9\x31\x24\xf6\xe7\xe7\x60\x92\xf3\x86\xc6\x0f\x19\x67\xd3\x3e\x4e\x2c\x3f\xf4\x0e\xa8\xb1\xa5\xb5\xfe\xc6\x1b\xb8\xf4\x2e\x01\x2d\x7d\x3d\x9d\x7a\xa3\x18\x22\x48\xd0\x3c\xd7\xdd\x7e\x5f\x03\x9f\x93\xc7\x76\x67\x13\x0d\x46\x1a\xde\xdd\xd1\xd3\x6b\x84\xa1\x94\x58\x3f\xc7\x63\x5f\xca\xb6\xd8\x29\x29\x1f\xb5\x0e\xb2\x62\x62\x82\xe1\x60\x09\x3b\x0e\x5e\x4b\x3f\xe4\xe3\x3e\x28\x7a\x27\xbc\x13\xc3\xe5\x3b\x9d\xea\x47\xf6\x30\x89\xb1\x5e\xc4\x60\xc1\x2c\x40\x76\x3c\x50\x9e\xed\xc4\x2e\xf2\x8d\xca\xe1\x81\xff\xf0\xaf\xd4\xbe\x9f\x61\x58\x7c\xa3\x27\xb4\x06\x44\x43\xfe\x60\x76\x7c\x23\x01\x72\x86\x3d\x2f\xdc\xef\xd6\xd2\xc6\x53\xbe\xf6\x80\xfa\xdc\xe7\x0b\x1b\x8a\x5b\x0f\xd5\x20\xaa\x4b\x03\xe2\x42\x75\x56\x44\x8a\x4c\x8d\xe6\xa4\x58\x95\x6f\x01\x42\x45\x27\xc5\x00\x45\xba\x51\xad\x7f\x25\x15\x01\x9b\x5f\xdd\xf8\xeb\x5b\x35\x9f\xb5\x60\x70\x9e\x07\x70\x5b\xff\x15\xf2\x3d\xb7\xc6\xfb\xbe\xcf\x5b\xf5\x20\xdc\x09\xea\x28\x27\x66\xd7\xab\x34\xd1\x38\x2a\x62\x2a\x2f\xab\x58\xf1\xf4\x42\x2c\x5f\x10\x58\xf7\xd8\x36\x88\x25\x37\x93\xeb\x16\xab\xbd\x1a\x40\xc8\x44\xfa\x0f\xe4\x00\xfa\x98\x90\xb6\x7c\xc8\x7f\x5a\xb3\x83\xf7\x77\x61\xe6\xb9\x69\xf3\x96\x7a\xd0\xa0\xf8\xf9\xec\x57\x8c\xe3\xed\x1f\xea\x74\xb2\xcc\x14\xfb\xdb\x17\x5d\x89\x61\x96\x54\x1f\x9c\x6a\x63\x39\x80\x84\x01\x7c\x18\xad\x64\x1d\x0e\xae\xd9\x6a\x07\x2f\x21\x50\x26\x93\x0d\x68\xae\xe0\xc6\xe3\x1d\x18\xe4\x52\x2f\xe0\xae\x7f\xc1\x9c\x12\xf4\x42\x46\xdb\x5d\x9d\x31\x8a\xa9\xeb\x14\x20\x96\xf6\x4f\x08\xfe\x6a\xcf\x65\x7e\xb7\x97\xe9\xbc\xb3\xd1\xa9\x13\xaa\x70\x66\x73\x7d\x96\x42\xe5\x10\xa5\xb5\x36\x15\x12\x3e\x10\x60\x52\x39\xf2\x93\xc8\xaf\x8e\xe5\x83\x0b\xda\x64\xa4\x83\x9a\x0f\x83\x07\x7b\x0f\x93\x58\x29\xd6\x4b\x73\xd3\xb3\x71\xc5\xd0\xc2\x99\xdd\x5d\x41\x28\xa9\x3e\x57\x48\x2e\xdf\x33\x0e\xc7\xaa\xcb\x8b\x59\xfc\xd4\xd9\x42\x45\x37\x77\x03\xfb\x8f\x6e\x33\x29\x54\x01\xfa\xf7\x59\x8b\x3c\xa3\x9e\xee\x12\xc5\xb1\x82\x66\xcc\xfb\xcb\x5b\xee\x3d\xcc\x7c\x5d\xc2\xdb\x7f\xeb\x94\x33\xb7\xf9\x44\x48\x72\x32\xc9\xd7\x19\x2a\x7e\x30\x50\x1e\x68\x77\x77\xb1\xd4\xc9\x09\xe2\x07\x29\xfd\xf5\x1c\xc1\xa2\x02\x5d\x11\x33\xa7\x96\x8a\x90\x29\x5d\xfa\xb4\x3d\x82\x02\x4c\xf2\x59\x18\x79\x45\x21\x8c\x1b\x2b\x5b\x8a\xe5\x07\x00\xd7\x48\x79\xf0\xcc\xd0\xc9\x7a\x0f\x22\x3b\x4e\xdb\x15\x06\xc2\x3f\x9f\xe5\x25\x02\x9f\xdf\x26\x20\xec\xb5\xb6\x87\x30\x1f\xde\x48\x5b\x60\x8c\x13\x85\x20\xc1\x0f\xf4\x49\x90\x31\x11\xed\x23\xdd\xf9\xa5\xe6\x9e\xbc\x2e\x68\xf5\x95\x2b\x2b\x03\x65\xf4\xf9\x0d\x9d\xc4\x2a\xb0\xac\x14\x25\xa3\x78\xdf\x8b\xc6\x99\xfa\x67\x0f\xda\x5a\x63\x4e\x3c\x29\xe3\x25\x90\xd3\x93\x92\xdf\x51\xb8\xd1\x1e\x3f\x9f\xe8\x8b\x5f\xe4\xc7\x7b\x36\xba\xf6\x29\x3a\x08\x28\x53\xc6\x90\xa3\x6d\x44\x40\x3a\xd4\x37\xf7\xbc\x6d\x44\x19\xba\x00\xcb\x26\x02\x61\x6f\x42\xb5\x35\x1f\x22\x60\x69\x1e\x1c\x82\xff\x17\xa0\xe2\xb9\xd2\xdd\xbc\xd2\xe0\xce\xa4\x37\xb9\x59\xec\x52\x5e\x7b\x7c\x07\xc5\x48\x04\x0f\x8f\x6d\x06\x5c\x89\x32\x05\x00\x18\x91\x7e\xbe\x64\xc5\xfc\xf5\x3d\x22\xde\x34\xe3\xa3\xda\x9a\x39\x2e\x1b\x94\x6f\x8d\x3e\xbb\x2f\x6a\xd3\xfb\xa0\x2d\xf4\xaa\x66\x6a\x7d\xf4\x79\xe5\x3b\x84\xfd\xc6\x58\x76\x1e\xb3\xa6\xe6\x22\xb8\xcf\x65\x96\x0e\xe7\xcc\xb3\xbc\xf3\xe6\xa2\x3d\xf8\xa0\x04\x5b\x71\x72\x29\xa4\x79\xbf\xf1\x2e\x69\xe8\x65\x77\x54\x58\x8e\xf8\x28\x2d\x46\xbf\xe2\x1d\xe7\xb2\x08\xea\xa5\x38\xbc\x5b\x66\xa9\x37\xa2\x14\xe7\xc3\x89\x4d\xf4\xf7\xb6\xac\xee\x18\xad\xdd\xe3\xa4\x6c\x4a\xab\x1a\x8b\xee\xf9\x27\xbc\x25\xe2\xab\xf3\xd9\x3e\xba\x45\xe9\xb4\xa1\x98\xb0\x5e\xd5\x0f\x6d\x23\x1c\xb0\x1b\x57\xa6\x4b\x05\x06\x6d\x96\x57\xf3\x61\x8b\x10\x7d\x26\xd8\x54\xca\x53\x6b\xb6\x67\x08\x5b\xc6\x65\x45\x7c\x0b\x20\xcc\x63\x73\x45\x76\xe1\x4a\x9b\x90\x92\x25\x94\xac\x95\x06\xca\x7b\x6f\xec\x2d\x56\xf0\x81\xb2\x97\x31\x51\xec\x85\x32\x20\x5f\x3e\x45\x8d\x80\x11\xed\x33\xbd\x75\xe6\xdb\xb3\x51\x78\xa6\x75\x05\xb4\x67\xd3\xc1\x19\x90\x89\x49\xf4\xbf\x39\x9d\xfd\x4a\x05\x90\x0e\x54\xf8\x43\x2d\xeb\xa2\xfd\xfe\x88\xe0\xa5\xa3\x35\x45\x27\x49\x90\xae\x84\x0a\x74\x8d\x30\x2f\xde\x37\x1a\xc7\xd2\xd8\x7e\x30\x51\xb7\x3b\xf7\xb8\xe4\x13\xfb\x7f\xbb\x06\xea\xac\xa4\xd7\x6b\xc1\x20\x49\xf4\x2d\xa4\xb7\x67\x1f\x4c\xe4\x87\xc9\x3b\x01\xb8\xd9\x34\xe9\x80\xb0\xda\xf6\x10\xcd\xb4\xe0\xac\x1f\x6b\x26\x6c\xb9\xc3\x95\xd2\x76\x13\x52\xc9\x86\xee\x56\x07\x6d\x9d\xdf\x5c\x10\xd4\x82\x61\xb2\xa8\x2c\xf6\x40\x20\xdc\x46\x33\xf9\x92\xce\xa8\xf0\x84\xec\x24\x77\xa1\xe7\xed\x4c\x38\xd9\x64\x3d\xdd\x97\x3a\xe4\x48\x83\x94\xda\x9c\xb0\x6c\x67\xf9\xaf\x84\x7e\x6c\x90\x63\x46\x22\x1e\x25\xcc\x2a\xb4\x92\x8e\xb4\x8c\xda\x97\xb6\xc9\xe8\xf4\x7f\xcb\x1e\x98\x59\x0e\x1d\x2f\x55\xad\x8d\xeb\xa0\x33\x43\xf0\xdd\x57\x81\xb3\xa8\x19\xd8\xb8\x9c\x40\xd3\x8e\xa8\x42\x97\xb9\xea\xf3\x4b\x9f\x50\x59\xc0\x26\xeb\x1a\xfe\x43\xba\x90\x3a\xb9\x95\x22\x3d\x3f\xb0\xb5\x5b\x80\xba\xb8\xc1\xfd\x09\xf1\x8f\x4a\xc7\x60\x19\x41\xee\x30\x0d\xdc\x77\x7b\x7a\x3b\xfe\xe5\x2c\x44\xa7\xdf\x1b\xd3\x72\x2b\x36\x92\x06\xe3\xef\x3a\x4f\x44\xe2\xe9\xed\x3d\x41\x44\x2d\x89\x9b\xb4\x81\xa6\x6e\x22\x4e\x29\x67\xe8\x74\x86\x31\x4b\xdd\xde\xb3\x0e\x40\x47\xe4\xdc\xe4\x92\xf5\xf6\x2e\xd9\xc5\x86\xf3\x1b\x48\x30\xcc\x8a\x09\xd6\x61\x38\xc4\x94\xf0\x00\x62\xad\xb9\x5d\x57\x31\xf5\xa7\xff\x9c\x78\xc8\x47\xf1\x37\x9a\x8b\x92\x8e\xd0\x03\x8c\x6c\x7f\x15\x3d\xa0\xba\xc3\x69\x8a\x88\xa2\x7f\x7f\xe0\xc8\x78\x89\x6f\xa6\xfc\xc3\x71\x4e\x76\x9a\xdf\xfb\x89\x7e\x55\x96\x53\x34\xb3\xa2\x78\xf8\x2b\x87\x6b\x26\xa0\xb0\xf6\x14\x64\xd7\x69\x20\xfc\x54\x22\x7a\x9f\xb6\x72\xdc\xdd\x75\xbc\x68\xd2\x1f\x56\xac\xb1\x17\x0f\x49\x6e\x33\xf6\x12\x29\x44\x79\x11\xec\xea\x9c\x6d\x4d\xe7\x48\x29\xf9\x74\xa3\x91\x5a\xb7\x20\x59\x75\x99\x76\xcf\xa7\xca\xb5\x51\xee\xe7\x20\x64\x1e\x32\xd2\x00\xa4\x9f\x14\x5f\x1b\xd1\x4f\xc7\x1f\xe0\x87\xdf\xad\x6b\x56\xa8\xc8\x45\x9a\xbf\xf2\xff\xe5\x0f\x09\x46\x85\xdb\xe1\x5d\x77\x63\x45\x74\x55\x4d\x9e\x5c\xdb\x9b\x04\x2b\x86\x63\xe7\xe1\xf3\x82\x9d\x80\x76\x9b\xd4\x5b\x42\xbc\x47\x61\x50\xa8\xd9\x22\x9d\x7e\x35\x10\xf5\xeb\x0d\x0d\xb3\x74\xc4\x18\xd3\x82\x28\x15\x1a\xff\xdc\x81\x26\x72\x99\x07\x37\x9f\xc6\xff\xa4\x5b\x54\xb5\x2f\xc4\x79\x01\x1e\x8b\x54\x41\x9c\x41\x2e\x1c\x62\x98\x4d\xb7\x4b\x60\x62\xb6\x24\xf9\x33\x42\x26\xe3\xfc\xc7\x74\x9a\xfe\x02\x7e\x6d\x26\xac\xb6\x8e\xbd\xa7\x7a\x5d\xb0\xdf\x79\x81\xc1\x9b\x86\x8f\x07\x2a\x12\xa1\x4e\x15\xeb\x35\xf9\xc2\xe0\x9e\xad\xe7\x74\x32\x3c\x93\x0f\xcb\xc6\x3b\xd3\x65\xe7\x7a\xf1\x05\xa2\x10\x43\xe9\x2b\x9e\xc6\x15\xfe\xde\x9f\xf5\x57\x5e\x0c\x0b\xf6\xdf\x55\x52\xf5\x90\x91\x02\x60\xf1\xf1\xc6\x59\x17\x86\x61\x3c\x26\x2a\x73\x97\xe8\x2a\xa0\x37\xa4\x06\xa4\x95\xcc\xba\xca\x75\xaa\x17\x84\x4b\xa7\x15\x25\x28\xfc\xea\x1c\xba\x7c\x01\x6e\x56\x1d\x7f\x3e\x73\xbf\x04\x56\xf4\x73\xaa\xea\x53\xd6\x4e\xca\x49\xe1\x2f\xb8\x34\x76\x78\x3e\x7c\x41\xd4\x59\x7e\x75\x3e\xfb\x21\xcf\x5c\x6f\x64\x79\x90\xab\x3b\x96\x96\x62\x96\x6d\x53\x32\x4b\x41\x63\xc6\x11\x2e\xe2\xef\x47\x52\xd6\x0d\xa1\x06\xce\xea\x81\xb2\x18\x22\x26\xd0\x2b\x97\xb4\xe2\xc2\xf9\x61\xcb\x11\xec\xbf\x88\xfa\x29\xb6\xcb\x93\x11\x4f\x00\x10\xff\x2d\xa3\xb0\xef\x11\xc8\xe3\xbe\xfe\xd3\x2c\xbd\x02\xf1\xbf\x76\x93\x58\x69\x53\xcb\xc5\xd1\xfb\x95\x09\xd2\xa7\xbc\x2a\x1f\x02\x91\x5f\x6a\xe1\x8f\xf4\x40\x5c\xb9\x58\xfb\xcf\x21\xa0\x65\xb6\x11\x47\x73\x16\xbf\x75\x21\x08\x53\x7e\xdd\xd3\x62\x19\xe4\xb5\x99\xea\x67\xfb\x58\x45\xc3\x46\x9e\x8c\x68\x50\x99\x5b\x1a\xc5\xd0\x48\xbd\x1f\x62\xfa\x1f\x47\xc1\x1e\x00\x60\xaf\x11\xa4\x76\x86\xce\xea\x50\xa0\x1a\xe6\x36\x96\xc7\x64\x8b\x18\x91\x9f\xbf\xe8\x9b\x19\x24\xb6\x57\x5a\xaa\x84\x58\x87\xe0\x14\xb5\x76\x1b\x7a\x1a\xad\x00\xc7\x96\x8b\x3f\xa2\x33\x91\x2e\x57\x4b\x10\x34\x51\x04\xa8\x78\xfa\x99\xf2\x6e\x4c\x32\x6e\xc5\x52\xdb\x4b\x58\xe4\x74\x74\x56\x18\x26\xb5\x1b\xe5\x10\x5b\x35\x4e\xf0\x5b\x02\x90\x6b\x22\x7e\x60\x90\x5c\x78\xc5\xd0\x6d\xb5\x93\x50\x35\x59\xd5\x67\xe8\x0f\x26\x74\xc7\xf5\x9d\xc8\xd6\x30\x86\x83\xe3\x2e\xb9\xe1\x95\x7f\xe9\x96\xb9\xf9\xb5\xa0\x33\x56\x9f\xe7\xdf\xce\x28\xf3\x06\xdc\x1f\x7b\x73\x8a\xa1\x99\xd5\x3b\x18\x90\xbb\xba\x9f\x08\x66\xf2\x3e\xdc\xea\x29\xda\x58\xb3\xc1\xeb\xdf\xf0\xf7\x7b\xc9\x1e\x9c\x66\x09\x6c\x97\x83\x9f\xea\x80\x9c\x5e\x1a\x7e\x89\x92\x41\x50\xb9\xd0\x0f\xe5\x63\x3a\x05\xf9\x6e\x0f\x6f\x90\x56\x1d\x26\x51\xc6\x1c\x91\x86\x84\xbd\xa5\x69\xb6\x33\x3e\x94\xea\x2f\xa2\x94\x8b\x05\x00\x5a\xbb\xa4\x5a\x91\xf9\x32\x02\xb7\xb1\xf9\x60\x92\x03\x72\x66\x46\xd1\xf1\xdf\xfa\x90\x51\x94\x5e\x1b\xba\xe9\x65\x6a\x68\x4f\x71\x54\xbe\x93\x09\x3d\x23\x14\xdd\x0a\xb7\x89\xad\x87\x59\x99\xc2\xe3\xe7\x1d\x8a\xf5\x1c\xe3\xdd\x72\x62\xce\x11\x44\x8f\x18\x3d\xfd\x36\xc3\x1d\x78\x89\xe4\xfb\x57\x6a\x14\xa5\xdc\xb1\x32\x19\x59\x19\xae\x90\xfd\x22\xa6\xf2\xf5\x9a\xb7\xea\x9e\xef\xbd\x9d\xde\x5c\x11\xe4\x23\xd5\xc5\x56\x59\x11\xf3\x5c\x7b\xdd\xfa\x6e\x15\x93\xea\x26\xd2\xee\xde\xbd\xc7\xfa\xd6\x51\x72\xaa\x8c\xeb\x62\x10\x67\x14\x50\xed\xa1\xe3\x3a\x3b\x79\x23\x3f\x2d\xca\xc3\x3b\x5b\x6c\x90\xc2\xa7\x0a\xf7\x47\x77\x6d\xd2\x83\xe3\x2d\x04\x19\x18\xd7\x62\x80\xdf\xdf\x69\x06\xea\x1c\xe5\x75\x09\xfa\x7f\x99\xcb\xb3\x88\x28\xbc\x38\xfe\x58\xfc\xac\x3e\x33\xe5\x7a\xc3\x8d\x88\x86\x0b\x0d\xda\xf6\xf9\xff\x55\x20\xae\x64\x4a\x83\x6f\xd7\x1d\xf6\xac\x7c\x09\xde\x15\xc3\xb1\xec\x08\x1f\x23\x3f\xdb\x09\x41\x8c\xf8\x96\x62\xc7\x75\x21\xce\x19\x05\xae\xe7\x59\x66\xdf\x05\x34\x84\x93\x3e\x5a\xe2\x01\x39\x2d\xaa\xe4\x74\xa0\xd0\x03\x0d\xf3\x2f\x75\xdc\x2e\x32\xb2\x0e\xbc\x47\xbc\x95\xd6\x28\xf8\x12\xc9\x6b\x45\xb3\x36\x6f\x39\xfd\xae\x9c\xea\xae\x84\x23\x64\xa5\x3a\xea\x9c\xe4\xa8\x74\xa4\xf9\x4c\x59\x2d\x67\x03\x58\xa2\x61\x33\x1f\x13\x01\x30\x1f\xb5\x5e\x4d\x6b\x95\x45\x49\x41\x99\x99\xae\xd9\x3d\x62\x5b\x6f\x73\xce\x95\x67\x38\x41\xd5\x02\xed\x88\x51\xc3\x3a\x6b\x32\x19\x36\x08\x97\xe5\xf3\x53\xb9\x2c\xda\x2c\x7e\x57\xd7\xbc\xe0\x64\x0d\xa6\x22\x7b\xf0\xb4\xad\xc1\x4e\xba\xb8\x12\x2a\x0b\x7b\x43\xee\x79\x01\x90\xa0\x8e\x5e\xc1\x74\xb2\xee\x7b\x96\x98\x51\xfc\x58\xd2\x69\xcd\x5e\x52\x79\x95\x9a\x24\xc7\x93\x6f\x2c\x7a\x0f\xf1\x20\x3e\xca\x45\x53\xc9\xe7\x78\xd3\xa2\x64\x52\x1e\x7c\x77\xa1\x6f\x42\x64\x07\x9d\x59\xe0\xb8\x72\x93\xa6\x2d\x01\xf9\x99\x4b\x8f\xb6\xe7\x91\x4f\xc2\xb9\x03\x4f\xec\x16\x34\x84\x75\x58\xef\x44\xa0\x30\x4f\x9c\xe1\x9e\xce\xfe\x76\x2c\xb5\x2b\xf4\x13\x01\x5b\x7f\x26\xee\x12\x8d\x3c\xdf\x74\x4c\x1e\x73\xac\x83\x7c\x2e\x58\x58\xfb\x37\x7f\x6f\x02\x74\x0b\xf1\x95\x3a\xb1\x22\x2f\x93\x3d\x1f\x2a\x1a\x48\x51\x61\xfc\xf7\x3e\x57\x06\x3a\x8b\x91\x92\xf3\x3d\x95\x57\x1e\xb6\xd5\x82\x94\x6f\x26\xe1\xb1\xd8\x16\x10\x84\xc9\xde\xaf\x23\x1a\x6d\x92\x89\xbb\x1e\xe4\x22\xe6\xd7\x5d\x78\x76\xcd\xc0\xe9\xd9\x39\xb2\x97\x99\x4a\xa7\x70\x4f\x64\x2b\xa7\x24\x9b\xcd\x2b\x68\x06\xc6\x63\xc0\xaf\xac\x9f\xb7\x12\xe1\x8e\xea\x9c\x8e\x05\xa6\xe2\x0b\xad\x78\x8d\xc0\x83\xf6\xcd\x81\x7a\xca\xe7\x94\x96\xaa\x95\x31\x2c\xf1\x58\x5f\x92\x86\xc3\x0e\xa1\xfe\x00\xf5\x59\x66\x29\x39\xb1\x7b\x12\x59\x90\x42\xb1\xdd\x2c\x95\xa5\xc4\x0e\x5d\xf5\x7b\xc4\xe8\x42\x65\x54\xc2\xac\x62\xc0\xb0\x95\xec\x78\xf2\x45\xfe\x88\xee\x1c\xf1\x86\x80\x5c\x81\x22\xb3\x46\xa0\x8a\x28\xec\x9b\xae\xb6\xf9\x8d\x4f\x5b\x7d\xc5\x02\x22\x21\xce\x11\x18\x78\x11\x58\xdc\xbb\x18\x39\x02\x7c\x55\xd1\x33\x15\x72\x13\x5c\xc5\x29\xfd\x0c\xdd\x10\xf6\x29\xae\x5f\xe5\x34\xfa\xce\x3f\xdb\x1a\x42\xe5\x01\x66\xdd\x9e\x74\x95\x98\xfc\x10\x45\x05\x2b\x08\x84\x3c\x0f\x5f\xff\x09\x8e\x37\x7b\x65\xaa\xf0\xc2\x38\xb6\x70\x98\xdb\x67\xc2\xf1\x6d\xaa\xa9\xf3\xa9\x70\xda\x42\xe6\x41\xa6\x6e\xb8\x57\x4e\x67\x3e\xc4\x5b\x33\xcd\x5f\x34\x9c\x9b\x39\xab\x67\xb5\xd9\x82\x35\xc4\xc9\x8e\xdb\x5d\xdd\xe7\x9e\x8b\xb8\x1c\xf8\xb4\x17\x2e\x66\x0e\x36\xbb\x3a\x4e\xc0\xf8\x4d\x89\x23\xe2\xb6\x0d\x74\x46\x05\x97\x26\x55\xf0\x71\x3b\x80\x13\x2d\x99\xa4\x7f\xbc\xbf\x6a\xfe\x13\x6a\x9b\x46\xb3\x3d\xc8\x44\x92\xe1\xc7\x22\x35\x78\x4a\xe0\x10\x44\x7e\x89\xf2\xd6\x4d\x83\xd0\xf5\xab\x92\x56\x0a\x72\x0a\x3c\x72\x73\xc2\xb3\xef\xdf\x6d\x79\xdb\x6d\x90\x15\x95\x4a\x4c\x8f\x69\x4b\x40\x12\xf8\x7c\x72\xa5\x2a\x91\xa2\x27\x5c\x32\x14\x53\xd1\xf2\xf0\xaf\xfe\xbe\x3e\x82\x6b\x45\x80\xa5\x57\x41\xa9\x5b\xa4\x59\x03\x3c\x75\xb2\x77\x8e\xfd\x8f\xfd\x80\xe5\x4f\xb4\x8c\xd7\x00\xc6\x9b\x54\xa3\xca\x36\xb0\x01\x99\xc5\x02\x00\x39\x3e\x6b\x16\x7e\x2f\xa8\x36\x33\xa0\x35\x85\x02\x08\xe9\x1f\x66\x12\x96\xb1\xf6\xbc\xb6\x1e\x5b\x44\x20\x57\x4a\xb3\xb7\x48\x5d\x40\x21\x87\x69\xd1\x91\xd7\xff\xd6\x7c\xb7\x68\x38\x36\xe9\xa0\x39\x6a\x85\x8f\x24\x0d\xfc\x0e\xfd\x83\x8e\xf2\xcf\x5a\xa9\x06\x13\x9f\xdb\x36\xe6\x6e\xcc\x59\x23\x3d\x20\xf9\x37\x87\x6b\x84\x82\xb7\x34\xda\xc5\x06\x5d\x75\xea\x47\xeb\x77\xac\x98\x46\x4d\xb3\xaf\x47\x12\x78\xd4\x40\xb1\x2a\x43\x92\x1f\x9d\x48\x86\x8a\x0c\x18\xc9\x37\x2d\x79\x46\x39\x13\x26\xfe\xf9\x78\xd7\xdb\x92\x2d\xe6\x53\x2d\x3d\xd3\x35\x9c\x86\x0b\xd6\xfc\xfb\xad\x25\x01\xfc\xa4\xa2\xc0\x44\x25\x6e\x31\x10\x2f\xb6\x78\xd3\xfe\xac\x72\xf7\x3f\x4b\xb2\x84\x1d\xd4\x9b\x68\x55\xd9\x9d\x96\x68\x3e\x9b\x32\xdc\x46\xfb\x1c\xf2\x69\x51\x21\xf3\xe0\x21\x3c\x9c\xff\xa8\xd6\x05\x70\x7e\xb8\x3f\xef\x1a\x97\x7d\xf8\x18\xb3\xb3\x70\x80\xdf\x32\xf1\xd5\xac\xd5\x61\x47\x25\x95\x13\x60\x3d\x2e\x40\x23\x92\xd1\x42\xee\x05\x72\xa4\xd2\x6e\x72\xe3\x7e\xf4\x54\xb2\x2b\x57\x3c\xa4\x23\x38\xcd\x8b\x87\xed\xe5\x30\x2a\x0f\x2d\xdc\xc0\xdc\x1e\x50\xdd\x53\xff\x5f\x62\x9a\x68\x1b\x11\xbe\x59\xb5\xf4\xd3\xf6\x0e\xbb\x62\x3e\xf2\xcb\x44\xfe\x13\x09\x5c\xb1\x6b\x1c\xb7\x5f\xd6\xd5\xb7\xa4\xf2\x96\x0a\x91\x59\xf2\xa4\xc6\xde\xbe\x8f\xf2\x24\x5a\x84\x5f\xd7\x63\x75\x7c\x56\xf0\x85\x12\x0e\x47\xba\x57\xd7\x80\xf3\x56\xed\x04\x4d\xab\x0b\x7e\xdd\xbf\xd8\xf3\xa9\x4c\x41\xa0\x46\x1f\x34\x7d\xbb\x83\xaa\xdf\xa5\x17\xc0\x93\x5b\xa7\x5e\xa0\xae\x32\x3e\x87\x54\x01\xb8\xdf\x98\xd0\x79\x4b\xc5\x83\x3c\xbf\xdf\x30\x23\xd6\xcc\xf0\xc4\x4a\x07\x76\x0b\xb9\x4f\x43\xc5\xa3\x85\xca\x59\x7a\x23\xa7\x44\x09\x21\x01\x92\x5e\x0f\x49\x22\xb0\x9d\xca\xea\x10\x2c\x5a\xe1\x98\x3d\x2f\x59\xfb\x4b\xb7\xb6\xb7\x66\x4e\xcc\xea\x29\xa6\xa9\x3d\x12\xa1\x4f\xb1\x15\xed\xbf\x06\xef\xdb\x1d\xdb\xe9\xfd\x0c\x51\x35\x98\xbb\x12\xdb\x6a\x82\x5c\x29\xfc\x06\x73\xef\xd3\x25\xa1\x39\x49\xc2\xd4\x54\xc1\x13\x57\x71\xc1\xea\xa3\x63\x1f\xcb\xee\xb1\xbd\xc9\x74\x6e\x89\xbc\x6e\x28\xac\x54\x72\x3a\xba\xad\xfe\x14\x49\x04\x56\x56\xa8\x53\x2b\x74\x7b\xc7\x5f\xab\xaa\x60\xe5\x8c\xe9\x43\xda\x20\x65\x1c\xe8\xf9\x97\x01\xec\x7c\xa3\x3b\x48\x0a\x5e\xaf\x41\xf0\xe5\x5e\x62\x4f\x2e\x5b\xb8\x2e\x92\x23\x59\xe8\xb1\x79\x6a\xa1\x5d\x26\xdf\xf3\x3d\x30\xf2\x2f\xe8\x92\x45\xa4\xa5\xbc\x91\xe9\x72\x32\x0a\xa7\x4e\x43\x1e\xe9\xf1\x87\xad\x3e\x99\x94\x58\x82\xa0\x99\x63\xde\xe9\xf9\x61\xaf\x89\xd1\x47\x98\x2e\xca\x17\x61\xaa\x89\x6c\xc9\xde\x1b\xae\x00\xcf\x75\x8d\x73\x75\x76\x97\x7a\x57\xa4\x4a\x43\xd9\x2b\x03\xcc\xbb\xa2\x92\x38\x86\x5d\x0e\x62\x84\x4c\xaf\x2f\x79\x47\xd8\x0e\x9c\x9e\x60\x04\x09\x95\xe2\x35\xb3\xc2\xe2\xf7\x0b\x78\xa6\x79\x25\xb4\x5d\x79\xa5\x21\xdb\xf1\xf7\xcf\x9a\x49\x30\x7f\x9e\x90\x92\x80\xae\x0d\xae\x68\x64\x35\x83\x35\x90\x8c\x34\x8e\xe7\xb2\x51\x6b\xf7\xd2\xd0\x79\xcd\x61\x0e\x25\x64\xc2\x9c\xec\x5e\x76\x62\xf9\xc7\x25\x1a\xa0\xe5\x1c\x40\xe9\x4c\x2e\x55\x1f\x0c\xd3\x38\x6a\x71\x2b\xbf\x43\x48\x4a\x9a\xfd\x4b\x64\x4a\x09\x9f\x4b\xc1\x32\x52\x86\x79\x71\x6b\x60\xfa\x59\x3c\x1e\x35\x00\x9b\x81\x32\x6c\x35\x31\x18\xcc\x2f\x5a\x18\x84\x07\xe5\x9b\xc5\xc9\xee\xef\x8f\x8d\xaf\x8f\x92\xce\x1b\x6f\xee\xdb\xad\xa2\xfd\x1a\x6c\x7b\xfc\x04\xcc\x92\xe5\x40\xa1\xca\x70\x91\x4a\x2a\xb5\x92\x5b\x91\x1c\x04\x9f\xff\xdd\x7e\x02\xcc\x68\xea\x8e\xcb\x8d\xb5\x17\x37\x99\x1f\xd8\xbd\x25\xf8\xb0\x9a\x99\xad\x1d\x4d\x33\xf6\xec\xba\xf9\x1f\xa4\x7d\x24\x73\x53\x56\xda\xd1\x93\xfb\x21\xc5\x43\xd9\x00\x10\xf5\xb7\x3b\x3f\x05\x84\x6f\xb3\x8d\xcc\xa6\x6f\xb8\x2e\x66\x7b\x71\x9a\x3d\xc0\x15\x3e\x77\x61\xc0\x37\x02\x3c\xe7\x68\xcc\x3b\x57\x50\x6f\x2f\xdb\x35\x39\xe5\xd0\x37\xd4\x84\x8e\xa5\x98\xef\xfb\x01\x33\x3f\xc0\xec\x80\xe0\x74\xe0\x4b\xda\x86\x93\xcb\xa3\x31\x05\xe1\xfa\x6e\xd7\x5e\x2d\x10\x1e\xbc\xdb\xfa\x4d\xf7\xc1\xf1\x51\xf7\x31\x2b\x10\xfa\x34\x1d\x76\x36\x58\x9c\xca\x90\x96\x04\x79\xcd\xa6\x9b\xcf\x99\x09\x56\x79\x09\xfa\x28\x9a\x46\x21\x13\xdd\x2e\xf2\x51\xe8\x2f\x7b\x53\x4a\x03\x92\x0e\xc8\xb2\x55\xdc\x16\x3c\xb0\x09\x9e\xe5\x32\xc8\x09\xd7\xca\xcf\x27\x92\x44\x2e\x9c\xcd\x85\x19\x02\x57\x31\x77\x64\x05\x6c\x08\x91\xae\xa6\xf9\x2f\x5c\x97\x72\x32\x4c\x75\xf7\xef\x53\x27\x11\xb2\x25\xd1\x65\x85\x73\x13\xf4\xce\xa4\x08\xa4\xa4\x47\x3f\xce\xa4\x2d\xab\x41\x1b\x9b\x8a\x23\xa8\xd7\xa0\xa6\x1a\x85\xcc\xd1\x8b\xff\x0e\x6b\x1e\xf7\x90\x67\xf3\x54\x97\x48\x63\xec\x18\x5d\x24\x5b\x62\xed\xf2\x31\xc1\x45\x5d\xed\xbe\x42\xb3\x50\x01\xd0\x5d\x5d\x8b\x2e\x99\xe2\xef\x88\xce\xfc\x8a\x8a\x30\x1a\x6e\x12\xac\xdb\xee\x28\x73\xcf\xfc\x08\x83\xe3\x98\x36\x97\xbe\x9b\xee\x13\x54\x0e\x3e\x3f\x96\xd9\xbb\x37\xf4\xa1\x7d\xdd\x8d\x05\xbc\x62\x0b\x4e\x02\x4c\xe8\xa9\x40\xa6\xaf\xd4\xa9\x61\x1e\x74\x5e\x04\xfe\xee\x2b\x47\x1e\xd8\x73\x10\x3c\xea\x02\x6b\x6e\x64\xef\x36\x81\x31\x38\x77\x6f\xf7\x71\x79\xd8\x7a\xf1\xdb\xa6\x24\xa3\x2a\x08\x5b\xa6\xaa\x3b\xf2\x6c\x0d\x8d\xdd\xbb\xad\x77\x68\xdd\x2d\x06\x58\xb5\x5f\xbe\xe2\xbc\xb5\x7b\x43\xf7\xa0\x26\x80\xc1\x53\x46\xf0\x0c\xa1\x4f\x15\x33\x91\x45\x94\x1b\x8e\x20\xe8\x31\x45\x49\xdf\xc9\xb2\xa9\xa3\x93\x5c\x9f\x11\xa0\xd2\xf7\xc5\xf2\x89\x42\x88\xcf\x78\x35\x34\xdc\x64\xe4\x1d\x4d\xb1\xc1\x0f\x6b\xc5\xb3\xe0\x34\xfa\x91\x43\xbf\x60\xc1\x14\x89\x1a\x87\xf2\xdc\xd8\x56\x2a\xe6\x6d\x9e\x22\xf2\xf1\x11\xe6\x36\x07\xd8\xb1\xb4\x9d\xee\xaa\x72\x14\xe3\xf2\xfd\x95\x67\x8d\x72\x98\x85\xab\xe2\xab\x7e\x88\xcd\xb8\xc6\x1a\x0b\xec\x18\x09\x5a\xb5\x06\xdb\x88\x64\xbb\xf1\xdd\x5a\xb9\xe8\x36\x56\xcb\x0f\xa4\x5a\x72\x77\xae\x4e\x0d\x75\x1e\x4f\x22\x79\x01\xdc\xbf\x52\x00\xf3\xa6\xe2\x2d\x08\x9d\xcd\x60\xb3\xdf\x5c\xa1\x2b\x4f\x6f\x39\xd5\xb4\x15\x93\x7c\x62\x00\x37\x5d\x86\xf5\xab\x98\xdb\x62\xa6\x6b\xf3\xae\x44\xd8\x91\xd6\xa3\x24\x0c\x37\x32\x06\x02\xc8\x95\x9d\x12\x43\x0e\xdf\x1b\x02\x85\x0e\x02\x32\xfb\xa7\xe1\xca\xc4\xdd\x9d\x69\x4e\xb7\x33\x33\xa0\x81\x0a\x06\xe3\xa7\x1d\x04\x12\x97\x4f\x25\x7f\x75\xcf\x85\x4c\xd7\x5b\x97\xd7\x84\xf0\x62\x54\xe4\x60\x0b\xa5\x0b\x31\x67\x85\x55\xa5\xb9\x0f\x37\xb1\x71\x33\xda\x83\xcd\x6d\x56\xd6\xeb\x81\x0a\xae\xed\x27\xcb\xe7\xfa\x80\x67\x34\xc3\xf2\x31\x4b\xd1\x28\x2b\x7f\x27\x55\xe4\x20\xd0\x70\x51\x27\x38\x34\xae\x34\x51\x47\x93\xe1\x1c\x17\x23\x16\x2e\xdd\xb2\xc0\xa4\x9e\xe1\xc1\x5d\x0d\xe1\xed\x32\x9e\x72\xff\x7b\x3e\x8f\xc0\xd2\x57\x9a\xb7\x28\x29\x33\x22\x1f\x4e\xd5\x0e\xd9\x65\x4b\xb6\xc0\xa8\xf3\xa3\xfb\x98\x85\xb3\xbe\xbf\x9d\xcd\x51\xf7\x3f\xa3\x03\xbc\xba\x5f\x44\x14\xc4\x11\x8a\xb1\xeb\xad\xc8\x04\x48\x67\x1b\x96\x00\x81\xb6\xcc\xbe\x2a\x7f\x50\x4f\x99\x18\x83\x23\x72\xee\xb6\x5c\xb6\xef\xdb\x4b\xce\x9c\xec\xc4\xa3\x33\xa7\x23\xd9\x12\x96\x48\x93\x6a\x50\x7b\x58\x3d\x70\x8a\x8b\x01\xd2\x56\x86\xf8\x95\xab\x40\xd5\xee\x01\xb9\x0d\x56\xe2\x00\x53\x16\x25\x46\xe4\x93\xce\xa0\xbb\x9a\xc3\x2f\xd1\x44\x6d\xb6\x90\xfb\x01\x70\x19\xc2\xb2\x30\xf2\xe7\x36\x8a\x17\x18\x51\x24\xc3\xf2\xb7\x7c\xfc\x29\x52\xbd\x5f\x58\xd1\x3d\xf6\x1b\x38\xf8\x00\xea\x59\x0e\xc7\xd3\x84\x5f\x15\x1e\x1d\x11\xa4\xbd\xb3\xb7\x39\xa0\x10\xc9\x10\xd5\x86\xc9\x6e\xac\xed\x7d\xff\x97\x96\x19\xa9\x0c\x1a\x0a\x4f\x01\xe4\xe4\x48\xa8\xfe\x24\x69\x23\x4f\x82\x17\x25\xb0\x8f\x33\xd8\xe2\xca\xd6\xcb\x19\x58\x31\x9f\xd2\x41\xfb\xf7\xd4\x73\xfc\x2a\x6d\x29\x03\x5a\x17\x21\x7b\x94\x12\x62\xae\xfb\x4b\x1e\x25\xe1\xeb\xdf\xad\x8a\x81\x6d\xb7\x38\xe6\x52\x66\x08\x0a\xc8\x43\xf7\xdf\x5e\x1c\x87\x34\xb7\xa9\x7b\x99\xed\xf5\xb2\x8e\x6b\xb5\x29\x4a\x98\x35\x3d\xa6\x05\x97\x2b\x8a\x41\xfa\x61\x18\x58\x93\xe5\x26\x55\x01\xfd\x34\x02\xbb\x72\x53\xe8\x48\x19\x84\xe3\x84\xe8\x07\xde\x26\xed\xce\x6a\x9e\x58\x82\xb6\x53\x20\x09\xed\x6c\xe7\x30\xd5\x54\xb0\xc8\x31\x4d\x4b\xb4\xda\xce\xc6\x49\x80\x67\x1f\xb5\x1d\x3f\x3f\xa1\xbc\x68\x42\x2f\x87\x2b\xf6\x25\xbc\x97\xec\x2c\xfe\xab\x13\x1f\xcf\x61\x1b\xd4\x50\xe3\x2e\x44\xcb\xe3\x92\xf0\x77\x93\x13\xb3\x5d\xbd\x14\x51\x30\xa3\xbd\x2a\x64\xf4\x5a\xb4\x7d\xd9\xa1\xf2\xfe\xd7\x51\x44\x0c\xbe\xbf\x63\x27\x10\x56\x23\x66\xa3\x51\x09\xd9\xbf\x18\xed\xcc\x03\x31\x8f\x4a\xaa\x86\xea\xe9\x0c\x2b\x66\xe2\x9d\xf2\xfd\xf1\xe3\xaa\x90\xfa\xc5\x2f\xa7\xb8\x5f\x2a\x11\xc4\x9c\x9d\xdb\x2d\xb8\x2d\x72\x16\x16\x96\x3b\x4b\x5f\xa3\xe8\x15\xf0\x7a\x38\xcf\x0a\xe4\x2d\x27\x19\xd0\x05\x4d\x28\xe9\x0d\xc2\x69\xec\x13\xf6\x27\x0b\x26\x3b\x12\x09\x9d\x30\xf4\x8b\xc5\xef\x8a\x19\xdd\xdb\xc8\x6c\x8f\x65\x08\x9a\x80\x94\xfd\x9d\x47\x45\x37\xa1\xb2\x74\x0e\x88\x38\x24\xf5\x85\xe0\x01\x48\x35\xc6\x6f\x57\xd9\x1c\x8a\x44\x10\xcf\xc3\x7c\x02\x74\x37\x85\x77\x85\x0a\x66\x7b\x48\xc8\x33\xa5\x3c\xf4\x72\x0f\x59\x89\xed\xf1\x35\xc6\xe0\x10\x7d\x6a\xc8\xb1\xd7\x12\xb8\x2b\x66\x9c\xf5\x70\x72\x7e\x63\x7b\x12\xe1\xe6\x79\x99\xa9\xb9\xdd\x66\xf8\x01\xc5\x76\x9e\x3b\x2d\x70\x65\x7e\x20\x03\x05\x57\xa9\x04\xe1\x87\x21\x46\xa6\x5b\xea\x5a\x5f\x3d\x4b\x20\xb6\x74\xcb\x2a\x32\xa3\x58\x5a\x29\xc1\x1a\xe9\xfe\xcf\xfd\x60\x6c\xfc\x54\x88\xa3\x4f\x8c\x7f\x3a\x62\x4c\x67\x4b\x6e\xd1\x98\x28\xb1\x9b\x5d\xfe\x98\x12\x66\x2a\x72\xe4\x96\xb5\x25\x30\x74\x4a\xf9\xd8\x42\xee\x4a\x54\xf7\x64\xcf\x75\x98\x9b\xa7\x9b\xc2\x7b\xba\x7e\x30\xd0\xe6\x32\x0c\x3c\x88\x69\xfe\x0f\x89\x21\x3e\x8c\xc9\xef\x67\x37\x14\x98\x27\x12\xe9\xba\xeb\x12\x9e\xd3\x47\xfc\x25\x78\xa7\x90\xc3\x2b\x08\xae\x39\x86\xd0\xac\xdd\xc7\x6b\xcc\xe0\x3b\x51\x11\x59\x7d\x46\x19\xf5\xbc\xbc\xb8\x6a\x74\xa6\x9f\xd8\x44\x51\x0d\xf4\xed\x89\xc1\x44\xe1\xfb\x1d\x0a\x91\x9c\x3f\x87\xb5\xc3\x4f\xde\xc1\x93\x27\x6b\xb7\x6c\xb2\x5a\xda\xc9\xe2\xe0\x3d\x5a\xa0\x2b\x49\xe7\xc6\xbb\x48\x4d\x8a\x69\xbe\x17\x08\x99\x1a\x16\x10\xc7\xa8\xce\x8c\x0b\x3a\x24\xd1\x66\xb0\xd9\xc7\xf4\x22\xfa\x5c\xad\xdc\xc0\x25\x53\x5d\xc6\xaa\x8c\xc5\x89\xf7\x83\xa7\x54\xbe\xde\x47\xe2\x9b\x74\x31\xf1\xc3\x3d\x60\x20\xf3\x6d\xd0\xd7\xfe\x3b\xcb\x54\xcc\x3c\xff\xed\x25\x32\x9c\xf9\x22\xe1\xbd\xa7\x14\x56\x02\xec\x6b\x8d\x92\xe7\x4f\x22\x73\x0e\x1a\xce\x5f\x33\xea\xfc\xba\x05\x6b\xd1\x9c\x41\x0e\x8b\xb9\xed\x57\xc1\x65\x67\x26\x57\xa5\x9f\x6f\x07\xef\xca\x0b\xb0\xc5\xf3\x20\x24\xb7\xc0\x02\x33\x7d\x7a\xf4\x8a\x00\x3b\x89\x06\x5c\x6b\xf2\x17\x13\xed\x4f\xd7\x63\xfd\x7b\x59\x4d\x8e\x38\xc1\xe1\x10\x8d\x8b\xfe\xb5\xde\xd7\x98\x7c\xe3\x1b\xe2\x16\x17\x72\x55\xb7\x36\x0b\xbe\x90\x87\x9c\x52\x12\x3a\x79\x8c\xaf\x1e\xea\x66\x47\x20\x28\x70\x14\x74\x06\xc2\xd0\x03\xab\x26\xe7\xbd\xb2\xab\x70\x6e\x77\x7c\x99\x36\x25\x1c\x7f\x7d\xf0\x9c\x89\xb2\x5c\x8e\x7a\xb6\xf7\xf7\x45\x62\x38\x59\x0b\xa5\x80\x04\x0f\x7e\x6d\x96\x07\x59\x92\x73\xb9\x93\x07\x00\xde\x92\x8f\xfd\xc8\xb9\x9c\xae\x26\x05\xfb\x3b\x14\xfb\xd0\xb5\xc5\xae\xd9\xb1\x91\x4f\x7b\xe1\x0a\xa5\xf1\x11\xf3\x77\x20\x72\x77\x19\xca\x9e\xee\x29\xf0\xc2\x04\x73\x23\x8c\x53\xa4\xca\x14\xd4\x5d\x9a\x96\x9f\x41\xd5\x12\x1e\x6b\xd8\xb6\xe3\x1d\xdf\x8d\xd7\xe6\x54\x55\x02\x2c\xc6\xfe\x61\x17\x09\x5c\x51\x5a\xb9\x2d\xc8\xa6\x6f\x5f\xd1\x2e\xe8\x1d\x0d\xe5\x41\x47\xb0\x0c\x3c\x31\x12\x4d\xdb\x37\x2f\x0c\x6f\x7b\x66\x90\xc8\x17\xed\xe0\x60\x23\x35\xab\xf9\xcd\x93\x9a\x69\xee\x60\x00\x1a\xb7\xf5\xe5\x47\x7a\xcb\x9e\xdc\x75\x5c\x4b\xe1\x1b\xfc\x18\x40\x54\xb4\x96\xab\xdb\xf7\x07\x5b\x92\xc1\x07\xbd\x80\x4a\x90\x7b\x97\xd0\xe3\xad\xfa\x34\xe4\x24\x10\x47\xd0\xdf\xf1\x1a\x30\xa9\xae\x98\x3d\x2d\xb2\x15\xa4\x71\xf0\x7a\x85\x21\xa5\xea\x7b\xc3\x74\x08\x07\xb3\x4f\x80\x01\xd3\x85\x77\x2e\x94\xf7\x32\x69\x25\x73\xf5\x1b\xaa\x44\xd0\xb0\x0a\x32\x8e\xbd\x37\x0c\xaf\x62\x46\x07\xcd\xfa\x8f\x2b\x8a\x0e\xfe\xf8\x33\xde\x69\xd3\xe6\x31\x28\x76\x3d\x90\x47\x88\xf2\xee\x39\x6f\xae\xd0\x82\x82\xa8\xa5\x94\xb1\x75\x27\x2f\x08\xf5\xdf\x53\x41\xf5\xfa\xa0\x26\xcb\x1a\xa8\xb5\xde\xb5\x7e\x2b\xa7\x06\x39\x42\x38\x19\xea\x93\x17\x45\xd5\x25\x1c\x82\x0b\xe0\x0b\x3b\x90\x8b\xeb\x7d\x4b\xa7\xb4\xda\xc5\xe2\xcb\xde\xc4\xb6\x72\xc4\x03\x6d\xab\xe0\xad\xfe\x5a\x86\xbc\x61\x50\xfc\x53\xe1\x51\x6b\x9c\x42\xdb\xc8\x7c\x24\x3f\x48\x31\x5e\x46\x31\x2b\x45\x29\x78\x3d\x54\x02\x08\x60\x58\x42\x5a\x91\xbd\x20\x32\x93\xf2\xd2\x5f\x62\x3f\xd5\x4f\xaf\xde\xd7\x2e\x2d\x0b\x32\x9c\xf7\x60\xa8\x04\xf6\x72\xd8\xbf\xe1\x50\xab\xdf\x05\xd0\x6f\x8c\x98\x9f\x31\x55\x90\x41\xb2\xf0\xee\xb4\xce\x5b\x99\xa5\x19\x08\xbb\x02\x9a\x6c\x22\x7a\x2c\x59\xee\x78\xac\x4a\x10\x3d\x17\x52\x30\x30\x42\xe1\x6a\x04\x7a\xb3\x57\x76\x82\x06\xa2\x8f\x24\x3c\x40\xb2\x50\x6a\x08\x95\x5c\xc4\xc9\x89\x8f\x6a\x32\xe3\x87\x26\xb4\x1a\x69\x30\xe1\xda\x47\xdf\xd8\x4e\xc0\x6f\x52\xa1\x25\xa3\xd2\x4c\x84\x0c\x92\x75\x20\xc1\x61\x0b\x6f\x7e\x4f\x5d\x8a\xda\x2e\x1f\xb8\xe1\x80\x17\xfe\x91\x93\x3d\x30', 1) ================================================ FILE: agent/fix_rule/license.lic ================================================ HSpGTEFHUzoBKkNPREU6UHlhcm1vci1Qcm9qZWN0OHD4jbg3oaZPjPlTyiQyadr7bY7WVZJHSrbRS3HjpzdMKMC4qTYbE97ZIZZ8XWDTHSJrrOVqGCnWjuZ71LGMWNMESpvXwaRyL9WGqf9kmUO9iYDYVWwF8u/DddUuYjq4k/V1JEGKftwPFwieTkza8iiUts7xUu/dHRKLwANb/MU= ================================================ FILE: agent/fix_rule/product.key ================================================ Bې?CAk8a6W H0vY y  }q#fRq+JBwzʱnƐS*TE`wN(RGy0tD&y P:Cq\8SBV] ================================================ FILE: agent/fix_rule/pytransform.py ================================================ # Because ctypes is new from Python 2.5, so pytransform doesn't work # before Python 2.5 # from ctypes import cdll, c_char, c_char_p, c_int, c_void_p, \ pythonapi, py_object, PYFUNCTYPE from ctypes.util import find_library import os import sys import platform import struct # # Global # _pytransform = None _get_error_msg = None # # Options # # How to show error message return from dynamic library _pytransform. # # Each time call a dll function, if something is wrong, _get_error_msg # will return the reason. # # Enable _verbose_mode will print the details got from _get_error_msg. # Disable this options will print only a short message. _verbose_mode = 1 # In debug mode, print trace stack when raise exception. # Otherwise only show a short message. # # Run python with command option "-d", or set environment variable # PYTHONDEBUG. For example # # python -d pyarmor.py xxxx # _debug_mode = sys.flags.debug class PytransformError(Exception): def __init__(self, *args, **kwargs): super(Exception, self).__init__(*args, **kwargs) if _debug_mode: self._print_stack() @classmethod def _print_stack(self): try: from traceback import print_stack except Exception: _debug_mode = 0 sys.stderr.write('Disabled debug mode.\n') else: print_stack() def dllmethod(func): def format_message(msg, *args, **kwargs): if _verbose_mode: s1 = str(args)[:-1] s2 = ', '.join(['%s=%s' % (k, repr(v)) for k, v in kwargs.items()]) sep = ', ' if (s1 and s2) else '' line = 'Call with arguments: %s%s%s)' % (s1, sep, s2) return msg return msg.split('\n')[-1] def wrap(*args, **kwargs): args = [(s.encode() if isinstance(s, str) else s) for s in args] result = func(*args, **kwargs) errmsg = _get_error_msg() if errmsg: raise PytransformError(format_message(errmsg, *args, **kwargs)) return result return wrap @dllmethod def init_pytransform(): major, minor = sys.version_info[0:2] # Python2.5 no sys.maxsize but sys.maxint # bitness = 64 if sys.maxsize > 2**32 else 32 prototype = PYFUNCTYPE(c_int, c_int, c_int, c_void_p) init_module = prototype(('init_module', _pytransform)) init_module(major, minor, pythonapi._handle) @dllmethod def init_runtime(systrace=0, sysprofile=1, threadtrace=0, threadprofile=1): pyarmor_init() prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int) _init_runtime = prototype(('init_runtime', _pytransform)) _init_runtime(systrace, sysprofile, threadtrace, threadprofile) @dllmethod def import_module(modname, filename): global _import_module if _import_module is None: prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) _import_module = prototype(('import_module', _pytransform)) return _import_module(modname, filename) _import_module = None @dllmethod def exec_file(filename): global _exec_file if _exec_file is None: prototype = PYFUNCTYPE(c_int, c_char_p) _exec_file = prototype(('exec_file', _pytransform)) return _exec_file(filename) _exec_file = None @dllmethod def encrypt_project_files(proname, filelist, mode=0): prototype = PYFUNCTYPE(c_int, c_char_p, py_object, c_int) dlfunc = prototype(('encrypt_project_files', _pytransform)) return dlfunc(proname, filelist, mode) @dllmethod def encrypt_files(key, filelist, mode=0): t_key = c_char * 32 prototype = PYFUNCTYPE(c_int, t_key, py_object, c_int) dlfunc = prototype(('encrypt_files', _pytransform)) return dlfunc(t_key(*key), filelist, mode) def generate_project_capsule(licfile): prikey, pubkey, prolic = _generate_project_capsule() capkey = _encode_capsule_key_file(licfile) return prikey, pubkey, capkey, prolic @dllmethod def _generate_project_capsule(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('generate_project_capsule', _pytransform)) return dlfunc() @dllmethod def _encode_capsule_key_file(licfile): prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) dlfunc = prototype(('encode_capsule_key_file', _pytransform)) return dlfunc(licfile, None) @dllmethod def generate_module_key(pubname, key): t_key = c_char * 32 prototype = PYFUNCTYPE(py_object, c_char_p, t_key, c_char_p) dlfunc = prototype(('generate_module_key', _pytransform)) return dlfunc(pubname, t_key(*key), None) @dllmethod def generate_license_file(filename, priname, rcode, start=-1, count=1): prototype = PYFUNCTYPE(c_int, c_char_p, c_char_p, c_char_p, c_int, c_int) dlfunc = prototype(('generate_project_license_files', _pytransform)) return dlfunc(filename, priname, rcode, start, count) @dllmethod def get_registration_code(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_registration_code', _pytransform)) return dlfunc() @dllmethod def get_expired_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_expired_days', _pytransform)) return dlfunc() @dllmethod def get_trial_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_trial_days', _pytransform)) return dlfunc() @dllmethod def version_info(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('version_info', _pytransform)) return dlfunc() def get_hd_sn(): size = 256 t_sn = c_char * size sn = t_sn() if (_pytransform.get_hd_sn(sn, size) == -1): return '' return sn.value.decode() def show_hd_info(): return _pytransform.show_hd_info() def get_license_info(): info = { 'expired': 'Never', 'restrict_mode': 'Enabled', 'HARDDISK': 'Any', 'IFMAC': 'Any', 'IFIPV4': 'Any', 'DOMAIN': 'Any', 'CODE': '', } rcode = get_registration_code().decode() index = 0 if rcode.startswith('*TIME:'): from time import ctime index = rcode.find('\n') info['expired'] = ctime(float(rcode[6:index])) index += 1 if rcode[index:].startswith('*FLAGS:'): info['restrict_mode'] = 'Disabled' index += len('*FLAGS:') + 1 prev = None start = index for k in ['HARDDISK', 'IFMAC', 'IFIPV4', 'DOMAIN', 'FIXKEY', 'CODE']: index = rcode.find('*%s:' % k) if index > -1: if prev is not None: info[prev] = rcode[start:index] prev = k start = index + len(k) + 2 info['CODE'] = rcode[start:] return info # Load _pytransform library def _load_library(path=None): if path is None: path = os.path.dirname(__file__) plat = platform.system().lower() bitness = struct.calcsize('P'.encode()) * 8 libpath = os.path.join(path, 'platforms', '%s%s' % (plat, bitness)) if not os.path.isdir(libpath): libpath = path try: if plat == 'linux': if libpath == '': m = cdll.LoadLibrary(os.path.abspath('_pytransform.so')) else: m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) m.set_option('libc'.encode(), find_library('c').encode()) elif plat == 'darwin': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dylib')) elif plat == 'windows': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dll')) elif plat == 'freebsd': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) else: raise RuntimeError('Platform not supported') except Exception: raise PytransformError('Could not load _pytransform from "%s"', libpath) # Required from Python3.6 m.set_option('byteorder'.encode(), sys.byteorder.encode()) # m.set_option('enable_trace_log'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('enable_encrypt_generator'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('disable_obfmode_encrypt'.encode(), c_char_p(1)) if not os.path.abspath('.') == os.path.abspath(path): m.set_option('pyshield_path'.encode(), path.encode()) return m def pyarmor_init(path=None): global _pytransform global _get_error_msg if _pytransform is None: _pytransform = _load_library(path) _get_error_msg = _pytransform.get_error_msg _get_error_msg.restype = c_char_p init_pytransform() def pyarmor_runtime(path=None): pyarmor_init(path) init_runtime(0, 0, 0, 0) ================================================ FILE: agent/fix_rule_no_att/agent.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- from pytransform import pyarmor_runtime pyarmor_runtime() """ Copyright 2018 Information Science Academy of China Electronics Technology Group Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: agent.py @time: 2018/3/13 0013 10:51 @desc: rule based agent without attack ability """ from agent.fix_rule_no_att.agent_core import Agent as ag class Agent: def __init__(self): """ Init this agent """ self.agent_core = ag() def set_map_info(self, size_x, size_y, detector_num, fighter_num): return self.agent_core.set_map_info(size_x, size_y, detector_num, fighter_num) def get_action(self, obs_dict, step_cnt): """ get actions :param detector_obs_list: :param fighter_obs_list: :param joint_obs_dict: :param step_cnt: :return: """ return self.agent_core.get_action(obs_dict, step_cnt) def get_obs_ind(self): return self.agent_core.get_obs_ind() ================================================ FILE: agent/fix_rule_no_att/agent_core.py ================================================ __pyarmor__(__name__, __file__, b'\xe7\x50\x8c\x64\x26\x42\xd6\x01\xb9\x4e\x3f\xdf\x40\x99\x74\xef\xb8\xc9\x91\x76\x3b\x40\xaa\xbe\x5d\xc5\xbd\x32\x0b\xf3\x42\x40\xe3\xce\x4f\x75\x5d\x9f\x4c\x04\x35\x6e\xf5\xb9\xdb\xfc\x44\xa8\x11\xd0\xdb\xb1\x0f\x62\xe2\x75\xc1\x38\x66\x9c\x65\x70\xe1\x0d\x2b\x57\x65\xd2\x85\x6d\x0e\x9d\xb5\x16\x6d\x19\x4c\x08\x05\x50\xe0\xd6\x28\xd8\xe5\x1b\x68\xb7\x96\xd4\x21\x0e\x87\x99\x7d\xc0\x18\x75\xb0\x67\x5d\x77\xba\xb0\xc6\xdf\x31\x69\x39\x6e\xca\xf0\x57\xec\xb8\x3a\xce\x56\xaf\x00\xa6\x55\x5c\x7e\xa5\x9a\xdd\x2a\xa9\x02\x64\x01\xd9\x77\x1a\x29\xd9\x74\xcf\x5b\x91\xc7\x0b\xda\xe7\xdc\x65\x49\x7c\x95\x35\x85\x75\x0c\x31\x79\x1a\x0e\xc3\x17\x86\x4d\xf0\x69\x33\x2f\x58\x15\x05\x13\x7b\xd9\x9a\x22\x0d\xce\x35\xad\x50\x98\xfc\x5d\x92\x7c\xbc\x4f\x9c\xe5\xf1\xb5\x08\x7f\x61\x2d\x11\x4f\xc9\x21\xd5\x96\x7b\x1b\x22\xeb\xe5\xd8\x44\x2f\x35\xa9\x76\x77\xaf\x82\xd0\xe9\x4f\x19\xe5\xe5\x90\x97\x8e\x68\xfa\x23\x61\xcc\xf0\x2f\x4b\x7a\xc4\x67\x87\x47\xd3\xf3\xf1\x87\x98\xb9\xcd\xea\xe2\x86\x45\x25\xda\xfb\xad\x72\x5f\x64\x27\x02\xb5\x28\xd5\x5e\xeb\x85\x6c\xcb\xed\xc7\x1c\xbb\x0d\x53\x03\xe1\x53\x57\x81\x56\x6b\x46\x8d\x6a\x78\xc6\x28\xcb\x0f\x12\x6f\x0b\x0f\xda\x71\x55\x4d\x6d\x91\x15\x10\x20\x55\x95\xca\x9d\xb5\x26\xda\x64\xfa\xb5\x4e\x11\xb2\x03\xaf\x3f\x38\x0c\x0a\x56\x47\x51\x1f\x89\x1a\x9f\xbc\x42\x90\x52\x98\xc4\x5f\x0e\x54\xeb\x6c\xa9\x11\x85\x6e\x19\x59\xae\x91\xcd\x33\x99\xc6\x0b\x53\x56\xd9\x72\xfe\x0b\x1a\xa7\xd5\xd8\xcc\x01\x7b\x78\x38\x8b\x49\xd1\x0a\xb9\xda\xe7\xbb\x9d\x0f\xc9\xdd\xda\x5b\xc5\x89\x36\x5d\x70\x09\x63\x35\xc0\x3f\xf5\x77\xbe\x32\xa2\xcf\x8e\xc6\xfc\x62\xf8\xe3\x63\xad\x47\x59\x99\xf7\x27\x5d\x7a\x56\x33\xa2\xb9\x64\xde\x6b\xeb\xe8\x04\x40\x14\x2b\xac\x68\x55\x62\xcc\x48\x36\x58\xad\xb4\x23\xc2\xfa\x26\xac\x49\xee\xca\xcc\xdf\x25\x4e\x99\xc9\x83\x31\x58\x5e\xfe\x30\xef\x4b\x78\xb2\x38\x13\x67\x43\x69\xb0\x75\xb3\x1b\xe6\x32\xbd\x42\xc0\x5b\x7b\x0e\x16\xc3\xfe\xb2\x23\xeb\xdd\x78\xa0\xde\x25\xf8\x4d\x43\xc9\xc8\xa9\x13\x5c\x50\x53\xb9\x2d\xc6\x67\x91\x42\x8e\x24\xde\xfe\x8c\x5e\x0a\x2a\x21\xab\xe2\xcc\x46\xea\x7d\x40\x6e\x83\xeb\xb4\x7b\xe0\x01\x37\x5b\x37\x05\xde\x71\xf2\x13\x13\x9c\x25\x23\x62\xa8\x27\x3f\x13\x4d\x65\xf8\x03\x33\xbb\xac\xc6\x56\x5b\x98\x1b\xc5\xac\xda\x3f\xbe\x25\xb8\xc9\xe9\x13\xb3\x29\x3c\x84\x52\x47\x74\xe2\x4d\x50\x73\xfc\xc1\xa3\x7e\xe1\x5e\xdb\x70\xce\xb0\x56\xe8\xc0\x0d\x40\x50\xf2\x60\x26\x92\xf8\x2c\x4a\xe5\xb8\xf2\xfb\x28\x84\x26\xb0\x4a\xc4\xf4\xa3\xd1\xc0\x37\xa1\xfa\x5a\x90\x20\xea\x23\xe9\xf5\xc5\xf3\xd5\x27\xe6\x1f\xab\xee\x90\xa5\x26\x11\x84\x5e\x1b\x37\x98\x6e\x89\x09\xa3\x49\xc8\xdb\x4c\x7e\x79\x8f\x00\x8a\x09\x80\xb4\x5b\xb4\x87\xef\xbb\x87\x99\x6f\x85\xc0\xfc\x24\xff\xe6\xfc\xc1\x4b\x78\x2e\x45\x7b\xe8\x4c\x96\x52\x1b\xdb\x38\x80\x64\xca\x35\x4b\x0b\x0c\xe1\xfb\x3f\x05\x3e\xac\xe7\x86\x44\x22\xa2\x14\x36\x60\x36\x9a\x8e\xe7\x83\xaa\xb1\x4f\xac\x54\x61\xb9\xcc\x4c\x01\x70\x79\x7b\x2a\x6c\xd8\xfd\xf9\xfd\x33\x49\x39\x13\x2b\xa2\xe2\x98\x29\x8e\xe6\x00\xe2\x20\xa6\x7b\x6e\x16\x14\x00\x61\x5c\xbf\xb4\x23\x79\xda\x3a\x6e\x72\xf7\x33\x83\x3a\x6e\xf2\x0a\x3d\xa0\x7a\x8b\xaa\x49\x40\xbe\x38\xeb\xe7\xae\x8a\x12\xad\x08\x69\x1b\x42\x78\x4c\x81\x7e\x23\x3e\xc3\xff\x3a\x33\xa1\x48\x14\xe7\xd2\x63\xb6\xa9\xff\x29\x34\x0a\xd9\x1c\x21\x32\xb9\x36\x72\x5d\x8c\x59\xc1\x75\xb9\x4a\xf9\x51\xf1\x47\x8a\x05\x4d\xe5\xca\x63\xce\xe2\x2f\x0e\xa7\x96\x02\x1d\x90\x6a\x11\x07\x98\x97\x2e\xcc\x00\x87\x6c\xf4\x0c\x87\x87\x77\xac\xa9\x8f\xb2\x2c\x51\x8f\xc1\x31\x6f\x1f\xb0\xe0\xfd\x4c\xcc\xc8\xa6\x36\xd1\x4d\x42\x90\x6d\x95\x4a\x73\x1b\x82\x72\xb1\xc4\x37\xac\xae\xf7\x6f\x76\xa3\x58\xe5\xf2\x9c\xfd\x51\xc8\x5d\x7c\x3e\x10\x79\xcd\x27\x3b\xa0\x95\x02\x5e\x91\xcf\x53\x55\x5d\x24\x0f\x0a\xaa\x4d\x58\x3c\x90\xc4\xfd\x31\xb0\xda\xc2\x62\xe0\x87\x8a\x0f\xa9\x5b\x3d\x1b\x46\xef\x14\x9a\xc0\x53\x92\x09\x0a\x99\x2e\x5e\xd8\x29\xe8\x7c\xde\x17\x71\x7e\xf7\xd0\xdd\xb9\x74\x1d\xd1\xf9\x20\xc9\x82\xa6\x73\x70\xf1\xbf\x33\x59\x0b\x18\x39\xc7\xa8\xb4\x55\x7c\x1f\x54\xa6\x0f\x0d\xb8\x6b\x01\x06\xbb\x93\xfb\xc9\xaa\xc2\x61\x1e\x64\xc2\x92\x9e\xf9\x48\xa4\x2b\x62\xe5\x58\x9d\x5b\x4f\xf6\x20\xab\x03\x2b\x9b\x22\x49\x61\x9a\x9a\x7d\x3d\x04\x4f\x58\xee\x07\x7a\xba\xf5\x32\x28\x47\xab\x99\xd1\x47\x88\x36\x76\xbd\xa2\xd5\xdc\xda\x93\x2f\x2a\xac\x15\x55\xff\x90\x8c\xf7\xdc\xf3\xff\x4a\x28\xef\x0e\x10\xe9\xc0\x13\x70\xa3\xb9\xef\x33\x00\xb8\x10\x27\x35\x89\xd5\x53\xc5\x8a\x65\xb8\x56\x44\xb1\x33\x1e\xce\xe8\xa9\x7d\x9f\x87\x35\x61\x72\x9c\x2f\x2a\x65\x2a\x9d\xad\x44\x3a\x20\xa2\x53\x8f\xd6\x9f\x8a\xab\xd4\x3c\xc5\xe8\x58\xff\xe5\x37\xb2\xcb\x5c\xa3\x50\xfc\x71\x6f\x59\x6a\x41\x26\xb9\x86\x77\x5b\x65\xf0\x3b\xc6\xb2\xca\x63\x10\x67\xb3\x54\x6c\x5d\x77\x8b\x17\xfd\x34\xc1\x48\xa9\x04\xc9\xeb\x08\xf6\xe0\xe4\x0f\xd8\xb6\x47\xe7\xe5\x5a\x0f\xc5\x86\xaf\xa8\xde\x49\x72\xc8\x9e\x74\x97\x32\xd0\x1d\x18\xb8\x41\xc6\x34\x15\x15\x9b\xd2\x37\xb8\xa0\xe4\x3e\x92\x44\xb7\xea\xb8\xb0\x06\x7a\xe8\x18\x9d\x94\xc5\xf0\x19\xd3\x8f\xa4\xb0\x8d\x87\x31\x31\x9f\xa5\x85\x24\x1b\x99\xe2\x7b\x31\xba\x3a\x63\x6a\xf4\xc8\xcb\x7e\x01\x05\x93\x00\xcb\x61\x42\x33\xa4\x6c\x89\x37\xf6\x8e\x8a\xf2\x72\xee\x9c\xd6\xdb\x58\x88\x16\xf2\x6f\x5e\x2b\x89\x57\x53\x2c\x4f\xf8\x85\x23\xd2\x88\xdf\x33\x34\x12\x49\x3c\x20\xf9\xa4\xf4\x4d\x2b\xa5\x33\x50\xf5\x5e\x19\xd6\x71\x11\x66\x0c\x3a\x9c\xed\x7b\xe7\xe1\xdc\xc5\x53\xb1\xdb\xbf\x1b\xf2\xc8\xa5\x4f\x41\x3d\x79\x88\x71\x2a\x08\xb4\xca\xd9\x22\xab\xf9\xfd\x59\xbb\x51\x28\x9a\xf9\x03\xa7\xfb\xf7\xdb\x91\x11\x4a\x58\xa6\xe5\xb6\x4a\xd6\xd9\xf4\x2f\xae\xdd\xf3\x53\x6f\xe0\x3d\xa3\x48\x4d\x96\xdb\x9b\xfe\x76\x23\xd3\xac\xe4\x4e\x1c\x90\xb8\x54\x5e\xea\x90\xb8\xc7\xad\x43\x00\x9d\xe4\x1d\x6d\x3d\xb5\x7d\xf6\xc7\xc5\xe5\x82\xb1\xa0\x97\xc0\x96\x5f\x39\x70\x20\x11\xc8\x73\x97\x1a\x8e\x06\x44\xd1\x5a\x02\x3f\x92\x86\x26\x1f\xff\x69\x89\xc6\x5a\x34\x47\x9c\xf9\xd0\xdc\x6c\xe7\x6a\x47\x1b\x3e\x79\xbb\x57\xbe\x5b\x3a\x22\x43\x1d\x29\xe8\x56\x23\x33\x2e\xd6\x3c\xb1\xa5\x16\xc3\x68\x44\x2d\xfd\x40\xd3\x44\x0b\x09\xa1\x06\x5d\x06\x8a\xea\x4d\xc0\xe5\x02\xc1\xf1\x21\xe4\x4d\xd1\x0f\x1d\xc1\x82\xcb\xdf\x87\x62\xf4\xfe\x64\xa5\xcc\x1c\xb6\x23\x94\x74\xaa\x49\x61\x8f\x9f\x5f\x9c\x28\xb5\x57\x2c\x34\xf3\x62\x47\x51\xad\xed\xd6\x67\x34\x2a\x44\x88\xde\xce\x19\x8c\xc2\x02\x2d\x7f\x2a\x76\xb6\x9d\xb6\x33\x77\xb5\x88\x1c\x8f\xa9\x4b\xc3\x35\xe9\xc8\x10\x69\xfd\x13\x19\x54\x82\xfe\xd1\x83\x47\x5e\xb4\xa3\x14\xfc\xcb\xfe\xcd\x01\xa8\x67\x37\x50\x64\x43\x0f\x0c\x4e\x69\xf3\x48\x02\xf8\xcd\xec\x3c\x34\x01\xa6\x30\x36\xf5\x05\xc9\x4f\x23\x1e\x05\xea\x3d\x2a\x55\xd6\xbc\xe9\xff\x37\x77\xa0\x3b\xb4\x2d\x37\x02\x8b\xd2\x56\xaa\xd9\xea\x3b\xd5\x32\xf1\x1e\x7a\x9b\x49\x1b\x2b\xf2\x03\xcb\x3a\xbf\x4b\x50\x01\x6f\xff\x40\x08\x20\xe9\x3e\x45\xd4\xa2\xb0\xd0\x98\x2c\xb4\x7e\xd0\x22\xfa\x5b\x98\xd7\x67\x90\xa3\xfb\x74\x7c\x2f\xea\xe4\x06\x6e\xb0\xbb\x9f\x6e\x54\x66\x2c\xd3\x69\xac\xb3\xd7\x71\xa8\x5d\xfa\x51\x33\x64\x98\x51\xf5\x52\xd9\xcf\xe7\xc2\x41\xde\xc3\x5c\xf1\x5c\x26\x37\x8f\xeb\xba\x7e\x07\xe0\x08\xb6\x6f\x9a\x89\x97\x1f\xd4\x77\x8a\x71\x69\x08\xd2\xd6\xbd\x61\xe8\x69\x57\x84\x54\x13\xdf\x05\x3f\x62\x61\x25\xf2\x1a\x76\x31\x38\x51\xd3\x33\xdc\x08\x12\x56\xb1\x36\x5d\x9c\xcc\x35\xd7\x4c\xc2\x52\x57\x80\xbd\x19\xe6\x98\x60\x16\xb1\xd9\x28\x03\x47\xb6\xa8\x6c\x9c\xab\xf5\x6d\x41\x36\x18\x90\xe8\x56\x1b\x23\x92\xba\x2c\x11\x3e\x9d\x26\x0b\x99\xce\x0b\x65\x63\xb8\xba\x92\xf6\x1e\xbf\xd4\xdd\x2b\x52\x7a\x34\x41\x59\xa9\x48\x98\x79\xe3\xf0\xd1\xdd\x0b\x10\x03\x5f\x76\x8d\x09\xf8\xc3\x64\x76\x82\xd6\x2d\xd9\xfc\x9d\x4a\x29\xa9\xf0\xd4\xb6\xd0\xab\x78\xf4\xc7\x64\x7d\xd0\x6d\xba\x3a\xb1\xe5\xac\xea\x95\x9d\x9f\x8f\x36\x20\x1d\x78\x0d\x7e\x97\xca\xaf\x8c\x1d\xb2\x33\xde\x52\xd2\xf7\xf6\xf3\xed\x97\x88\xc8\xb6\x81\xca\xb9\x55\x3e\x7f\x93\xca\xe5\x71\x9c\x97\x9e\x2c\xfa\xed\x98\x18\xb0\x2e\xf6\x4e\xec\x9d\xac\xe1\x15\xd5\x70\xb2\x7f\xe0\xc8\x5a\x4e\xb2\x3c\x39\xd8\x93\x4a\x36\xca\x0a\x20\x18\x11\xab\x76\x3a\xcf\x00\x79\x0a\xe0\xf9\x73\xbf\x5c\xd7\xf1\xda\xa3\x55\x21\xfb\x58\x61\xcc\xd0\x32\x11\x4f\xb0\x7a\x3c\xab\xbb\x3b\x49\xff\xe1\x3b\x21\x51\x16\xa1\xe6\x65\x9e\x7b\xc6\x09\xb2\xf9\xdf\x78\xe6\x00\x86\xbe\xda\x60\xbc\x82\x9c\xe8\x03\xc6\x79\xf3\x47\x3b\x36\xec\xcf\xf6\x0c\xeb\x85\x14\x6d\x20\x8a\x5d\x1a\x63\xb9\x4f\x56\x19\x46\xd5\xf9\x42\x6a\x89\x4c\xbe\x91\x9d\xf3\x83\x6d\x02\xee\x2c\xe3\x49\x50\xa2\xbe\x77\xf3\x64\x43\x1f\xdc\x67\x89\x28\x96\x2a\x0a\x6a\x23\x77\x44\xfd\x2f\x6e\xe0\x05\xca\x3d\x39\xfb\x9e\x98\xd8\xee\x7c\xf5\xf7\xf3\x34\x84\x7b\xb9\xf8\xac\x57\x41\x57\x16\x8b\x0b\x56\x15\x2b\xad\x4f\xc3\xb9\xce\x7b\x31\x6c\xca\xad\xf9\x08\x1b\x5e\xa5\x4d\xcd\x16\xbb\x85\x87\x20\xce\xa9\x42\xd4\x7f\x2b\x4b\xd8\x9c\xef\x9a\x87\xb5\xf9\x0d\xf0\xdb\xc0\x43\xa1\x7e\x1c\x8e\xe2\x01\xea\x28\xda\xd3\x6e\xcb\x9b\x5d\x6d\xde\x56\x21\x37\x8e\x7b\x8a\x4a\x7d\x1e\x2f\x1a\xf0\x77\x62\xaa\xc4\x34\x57\xc8\xdc\xdc\xa3\x4e\xb4\x97\x40\x22\xfb\x2c\x9e\x0c\x58\xfb\x98\xbb\x08\x46\xea\x05\x2f\x26\xcf\x3d\xf9\xfa\xc2\x9a\xed\xf9\xfd\xc5\x4c\x95\xe9\xc2\xd1\x5b\x00\x6e\xd3\xc0\xdd\xed\xcf\x53\xcb\x06\x54\x13\x24\x5b\x62\xcc\x71\xe4\x69\xf5\x9b\x8f\x7f\xd3\x3a\xf3\x86\x89\x7a\x6f\x68\x4e\x37\x6d\x6b\xee\x2d\x91\x2e\xd9\xf7\x2d\x7b\x23\x64\x36\xfd\x40\x9f\x6e\xf3\xdf\x55\x86\x57\xd1\x9e\x92\x8b\x15\xf1\xa2\xb1\x12\x4d\xe4\xe1\x02\xa3\x70\xd1\xf6\x45\x2b\xdc\x72\x9f\x69\x19\xb8\xff\x1b\x55\x27\xe7\x66\x1b\xa7\x48\xac\x23\x3e\xe4\x03\x29\x89\x0f\x33\x2d\xab\x9e\x2a\x66\xb0\xf3\x22\x4a\xbc\x59\x76\x42\xf9\x2e\x41\x02\x56\x2a\xbe\xfe\x25\x63\x4a\xcb\xed\x80\x5b\x80\xfd\xd0\xfc\x3d\xf7\xa4\x48\xa4\x89\x51\xf2\xbd\x6e\x8b\x9b\xe2\xd5\xe3\x14\xef\x98\xa1\x01\x72\x57\xbb\xe4\x71\x22\x97\xd0\x99\x0b\x8e\x51\x5c\x6b\xfd\xeb\x0f\x9d\x55\x4e\x33\xde\x24\x80\x35\x68\x67\x44\x8d\x17\xc7\xec\xb3\x7a\xe9\x4d\x53\x6e\x83\x1e\x8d\xc3\x3b\x29\x4a\x34\xb0\xca\xea\xbe\x10\xe7\x6d\x07\xaa\xf8\xf8\x43\x0a\x93\x0a\xfb\xde\x9e\x9a\xd4\x2b\x35\xaa\x7c\x54\x19\x4a\xde\xc3\xd7\x76\x59\xc1\x04\xad\xdb\x51\x6d\x47\x12\x62\xe9\xee\xd6\x86\x5e\x52\x1a\xa2\xcb\x88\xfd\x96\xdf\xbf\x54\xc0\x32\x70\x3a\x80\x76\x5c\xb5\x29\x26\xc8\x61\xe3\x6b\xed\x31\xb4\x33\x78\x23\x68\xbf\xad\x45\x5b\x98\x43\xbd\x93\x25\xd7\x43\x79\x08\x5d\xaf\x92\x28\x25\x62\xe9\x63\x8f\x78\x6f\xfc\xb0\xfa\x64\x8e\x9e\xf6\x06\xb6\xd4\xa5\xcc\x97\x53\xba\x62\xf0\x41\x06\xc0\xf2\x42\x45\x86\x25\xa8\x5c\xb1\xae\x13\x77\xde\x13\xf4\x30\x5a\x38\xfd\x98\xe7\x56\x28\x5c\x61\x56\x1d\x50\xbd\xd4\x4a\xe3\xaa\xeb\xce\xda\xc4\xc7\xe7\xb1\x46\x94\x2c\x51\x66\x99\x8a\xdc\xc1\x1e\xc3\xeb\xf4\xe1\xfa\x65\x3d\x23\x0c\xd2\x37\xdb\x93\x7a\xa1\x65\xc5\x17\x56\xc3\x8e\x84\x4b\x1c\x6a\x85\x83\x77\x6d\x4a\xe3\x39\x93\x0a\xce\x69\xbf\x64\x41\xfa\xf4\xbf\x2b\x92\x9c\xe0\x8a\xab\xab\x7c\x0f\xdc\xf6\x36\xe7\xaa\xbb\x11\x41\x6e\xfb\x80\x1d\xb8\x39\xa4\xea\x69\x38\xc9\x7e\xe1\xcc\xa0\x1a\xd5\xbf\x04\x65\xde\x1c\x06\x26\x8b\xfa\x82\xe4\xb0\x5e\x47\x53\x6d\xe8\xc5\x25\xde\xb8\x6f\x07\x4d\x21\xc7\x64\xb7\x57\xd2\xfd\x7b\xf0\xc1\x35\x59\xbe\xa1\x5b\xcd\xcf\xed\xac\x88\x0e\x69\xf5\xaa\x13\x88\x08\xac\x1d\x3a\xd5\x69\x4c\x22\x78\xe9\x3a\x11\x8e\x2c\x9c\x2d\xae\x9c\xed\xb1\x55\xe8\x14\x69\x44\xe1\x93\x25\x81\x5f\xe4\x1e\x93\xa9\x7f\x38\xad\xe5\xb5\x62\x28\xa7\x82\xf3\x77\x44\xe3\x41\xd9\x91\x59\x10\xac\xe7\xf3\xbc\xd3\xe4\x61\xcd\xa7\xb6\xfc\x60\x69\xf0\x89\x8e\x86\x48\x68\xc2\x72\x9b\xbc\xfc\x17\x53\xad\x40\x90\x30\xce\x8f\xe3\x88\xea\x16\x6b\xbd\xd8\xdc\x08\xe6\x37\xe1\x04\x29\xb6\x2a\xa9\xe5\x88\xa1\x5b\x09\x2e\x07\x64\x48\xc8\x35\x32\x07\x64\x7f\x87\x2c\x45\x46\xa1\x96\xa2\x3c\x8a\xcf\x2c\xb9\x2a\x83\x16\x87\x94\x9b\xc1\x38\x3a\x9b\x1c\x17\xfa\x34\x3a\x94\x47\x93\x3e\x0f\x08\xac\xb6\x64\x6a\x82\xa6\x75\x0f\xda\x44\xcf\xaa\x24\x34\xa6\xd7\x95\x67\x62\x19\xb1\x4d\x9a\xdb\x8c\xfd\x87\xca\xf9\x63\x8f\x32\x97\xb1\xe1\x96\x88\x25\xab\xf9\x7b\x24\x46\x0f\xe8\x8c\x95\x84\x0d\x64\x66\xb1\x4f\x31\xe6\xc0\x5f\x43\x5a\x30\x5e\x2c\xb1\xc8\x72\x8c\x29\xc7\xa4\x74\x41\x95\xd5\xad\x3b\x50\x28\x75\xe7\x70\x5e\xd8\x61\x4f\xff\x05\x77\x01\x1f\x31\xe9\x53\xfb\x1c\x6e\xcc\x18\xdc\x7b\x03\x8c\x29\x03\x55\xa1\x24\xbc\x80\x3e\xa0\xb6\xa8\x0d\xd2\x7f\x20\x3f\x19\x3e\x09\xdc\x19\x01\xa4\x4a\xe6\x07\x8e\x5e\xaa\xdf\x05\x2a\xe0\xc7\x56\xb9\x55\x00\x85\x9a\x21\x8d\xb7\x5a\x14\x08\x0e\xf0\x4b\xaa\xf9\x8a\x57\x77\x2d\x4a\x7c\x1c\x02\x27\x3a\xd5\x15\x89\xb6\x11\x47\x9d\x62\x88\xeb\x97\x7f\x54\xa4\xe6\x3f\x3b\xa4\x37\x61\xef\x97\xd7\x05\x63\xb4\xc3\xb4\x72\xae\x35\x24\x52\x23\x6c\xcf\xc8\x57\x17\xf4\xbc\x20\xde\x43\x74\x8b\x2c\x41\xf2\x68\x74\x64\xbe\x9c\x64\xe1\x93\x48\x5c\x65\xd7\x7e\x07\x46\xd0\xce\xfc\xf9\x2d\xf4\x03\x80\xbc\xe8\x41\x44\x97\xed\x72\x13\x09\xd6\x70\x84\x23\xe7\x1c\x44\x28\x56\x7c\x2e\xb6\xdc\x68\xe3\x4f\x15\x4c\xa1\x66\x03\x72\xa0\x6f\xb0\x34\x50\xba\x16\x33\xef\xed\x79\x70\xea\x20\x1e\xad\x7b\x33\x98\x4b\xd4\x18\x40\xdb\x13\x7b\xdf\x43\x0a\x52\xaa\xae\x00\x64\xa5\x04\xe6\xbc\x70\x8e\xf1\xd2\xbb\x50\x1c\x1e\x1d\x4b\xdc\x6c\x68\x66\x0f\x3c\x5a\xa2\x75\x92\xb3\x94\xc9\x22\xfb\x58\x17\xf6\x69\x28\xed\x6e\x99\xab\x4c\x74\x6e\x22\x1e\xf0\x2a\x40\x4a\xe9\x8f\xd8\xfa\x90\xca\x90\xcf\x1b\xe2\x82\xd4\x93\x18\x58\xdc\x4e\xb5\x12\x78\xed\xf0\xd1\x6b\x0d\xd7\x7d\xd1\x78\x53\xcb\x29\x21\x40\xa4\x2a\xcc\x72\x65\x43\x6a\xf5\x5e\x7d\xd2\xa2\xcd\x7e\xa7\x9f\xae\x44\x43\x1b\xbf\x2e\x10\x94\x4b\xf5\xca\x47\xb3\x82\xef\x1c\xb3\x28\x1a\xcd\x45\x13\x4a\x58\x10\x2f\x4e\x62\x1e\x3d\xcf\xc9\x13\x43\x0c\xdb\x0b\x8c\x44\x9e\xdb\x54\x50\x26\xb1\x87\x6c\x68\x46\x73\xa2\xb3\xf7\x99\xf7\xa5\x01\x3b\x13\xe2\x5a\xe4\xa3\x6b\x3d\xd8\x13\xb3\xa9\x73\xc8\x2e\xf0\xe4\xd2\x75\x83\xa6\xc3\xfd\xd6\x15\xde\x6b\x93\xda\x4e\x88\xd8\x8a\xa8\x5c\xbe\x45\xcc\x8d\x54\xcb\xa3\x0d\x9b\x89\x5f\x53\xd7\xb2\x5c\x6a\xb3\x04\x51\x5e\x9a\x52\xab\xa4\x02\x1f\x18\xca\x1b\x6e\x38\x62\x1b\xbb\x65\x6b\xf3\xf9\x0b\x6d\xa4\x8e\x23\x1b\xd9\xe1\xf7\xcb\x44\x55\xb5\xa7\xb0\xed\x1e\x68\x84\x1d\x69\x38\x31\x27\x94\x59\x72\x4f\xeb\x2b\x4b\x88\xea\x26\x62\x5f\x6b\x2d\x93\x6d\xd9\xca\xbe\xa7\x27\xc4\x8a\xba\xfd\xd4\x58\xe0\x51\x1f\x76\xda\x95\x53\x3e\xf5\x58\xea\xd3\x8c\x1a\x9e\x5f\xc1\xf7\x49\xd6\x1e\x56\x2e\xe1\x2a\x4a\x83\xa6\x64\x52\x0b\x1e\xd3\xe3\x98\xee\x32\x07\x49\x68\xc9\x6d\xc9\x06\x4b\xe8\xb6\x1d\xc7\x38\x22\xec\x65\x3b\x43\xfc\xef\x79\xc8\x9b\x80\xb1\x33\x7d\x94\x34\xbc\xeb\x03\x8b\xf4\x98\x52\x3d\x8b\x02\xf1\xf6\x23\x75\x6a\xb6\xec\x8f\x73\xd3\x71\x86\xf6\xa7\x3b\x86\xd5\x02\xa1\x75\xfd\x7c\x9c\x11\xe4\xab\xb4\xd6\xbb\xc5\xe4\xdc\x85\xec\x6b\xed\x25\xff\xec\xa9\x54\xd9\xe4\xa8\x5f\xd7\x00\x7f\xc0\xbe\xdb\x4b\x66\x0c\x0e\x78\xbd\xa5\x69\x6b\x17\x62\xc3\x2f\x58\x08\x27\x8d\x7e\xaa\x0e\x6d\xe9\xc7\xa4\xc0\xf6\x95\x0a\xe2\x4a\x17\xba\x70\x9c\x69\xa6\xf8\xb0\x07\xcc\xa7\xb6\x3c\x4e\x02\xff\xc6\xb8\x15\x52\xfa\x19\x06\x14\x9a\x50\x50\x52\x37\xe4\xa2\x5c\x25\xf4\x5d\x01\xb8\xf0\xee\x7d\xe5\x37\xcc\xac\xd8\x12\x92\x1e\x78\x69\x45\xc2\xd5\x43\xfa\xd3\xd6\x64\x81\x60\x60\x5f\xe6\x4f\xc9\xe0\x5f\x5a\xbd\x07\xa0\xb9\x22\x89\x21\x0b\x3c\x75\xcc\xa9\x49\x63\x09\x83\xf2\xe3\x4f\x9c\x29\x85\x66\xd2\xd9\x50\x82\x15\xe7\x59\x23\x46\xa5\xdc\x3b\xaa\x35\x37\xb3\x9f\x3c\x7f\x91\xb4\x52\x4a\xa1\xa8\xf6\xc4\xa8\x93\xa1\x8b\xc9\x9a\x8b\x71\xfd\x42\x7c\xbb\x98\xe6\xa5\xad\x76\xb0\x31\x06\xa0\x5a\x21\xf0\x00\x1d\x5c\xd7\x05\xe4\xc0\x1b\x62\x94\xaa\x94\x66\x7a\x06\x5c\x5e\x02\x7d\x91\x1d\xe2\xc8\xb2\x7f\x8f\xf2\xb0\xcb\x2f\xb5\x3a\x6c\xed\x98\x36\x00\x0b\x69\x61\xe4\x2d\x7a\x3f\xde\x7e\x90\x84\x17\x49\x88\x7e\xbe\x94\xdc\x13\x3f\xaf\x82\xec\x70\x5d\x3a\xd1\xf6\x6a\xd4\x61\xc4\x66\x5d\xaf\xc7\xfc\xe1\x37\x9d\xe5\x5b\xea\xdf\xe7\x66\xfe\xd2\x25\x86\xdb\xfb\xa9\xe2\xe4\x17\x36\x42\xa6\x72\x89\x92\xfb\x03\x01\xe1\xef\x62\x06\xfe\x0c\xa5\x79\xd9\x88\x4c\x28\x9b\xbf\x97\x48\xc5\x04\xac\xce\x47\xc0\x54\x22\xda\xcc\x90\xa6\x11\x86\xd6\x50\x39\xaa\x94\x3f\x66\xf6\x98\xd4\xe0\x46\xf6\x27\x72\x14\x1f\xbe\x8f\x24\xde\xcf\x81\x88\x1e\x19\x45\x00\x63\xc2\xf8\x76\x43\x1c\xf9\x8a\xa4\xc6\xa5\x47\x00\x0f\xd1\x2a\x07\x42\xa2\xfb\xa2\xad\x69\xfd\x27\x39\xa1\xfc\xec\xcc\x1e\x24\x7e\x15\xc0\xe1\x7e\x73\x80\x73\x8c\x39\x95\x9c\xad\xc0\x4c\xac\x99\x0b\x11\x38\xa4\x17\x9b\xf4\x63\xcf\x9f\x41\xdd\x89\x0a\xbc\x10\xd0\x4e\x6f\x2e\xc8\xbc\x3b\x75\x65\x33\xa0\xbc\x97\x78\x2f\x63\x59\xd6\x15\x64\x97\x57\x18\xad\x0f\x11\x34\x3e\x06\xef\x97\xf0\x4d\xec\x53\x60\x0d\xea\x43\x20\xd7\xce\xd6\x60\x94\x80\xbc\xe9\x15\x5a\x20\xbb\x85\xe6\x62\xe6\xb5\x98\x05\x35\xe4\xc6\xed\x5a\x0d\xb4\xd4\x97\x9e\x02\x24\x30\x94\x87\xad\xf1\xac\x5e\x18\x4c\x0a\x77\x5b\xf7\xb5\x0a\xf0\xa3\xfe\x18\xfe\x07\x6d\x11\x79\x26\x10\x7f\x1e\x63\x84\x71\x7b\xc9\xc2\x36\xf6\xcc\x93\x24\xe2\x3e\x26\xb6\x55\xda\xa4\xf1\xb4\x21\x38\x60\x8f\x80\x9c\x64\x23\xdc\xf8\xaa\x9f\x01\x39\xce\x94\xad\x42\x31\x9d\xa6\x12\x2d\xba\x66\x00\x1c\xe2\x8e\xbf\xc4\x1c\x4b\xa8\x0d\x74\x63\x1a\xd3\x08\x76\xe9\x4f\x23\x91\x2d\xb4\xb4\x77\xaa\xae\x55\xd6\xb6\x64\x68\x96\xb9\xcb\x43\x85\x7e\x71\x2d\x14\x59\x13\xa5\xca\xf0\xad\x56\x04\x5f\x99\x6e\x63\x1f\x5b\x34\xf9\xd0\x38\x47\x79\x15\x5a\xb3\x59\x45\x5f\x0e\xfd\xde\x8b\x0f\x2f\x88\xd8\xeb\x16\xd0\xaf\xa3\x6a\x67\x0a\x47\x6c\x81\x02\x5b\xc1\x15\xde\x7d\xc4\x99\xf2\x6e\x2d\xfc\x11\x8b\x13\x38\xbe\xdf\x50\xdd\xe0\x44\x35\xbd\x25\x62\xaf\x87\xb5\x47\x1e\x95\x1b\xff\x1e\xa0\x91\x99\x13\xf0\x1e\x34\xc7\xdf\x48\xe7\x16\x1d\xbf\xed\x31\x4c\x77\x76\xb6\xf5\xa7\x56\x61\xe5\xa1\xe8\x15\xbf\xdd\x9e\x91\x6c\xf6\x07\x82\x06\xd1\x35\x25\x57\x8b\xf4\xaf\xef\x3e\xb3\x31\x57\x04\xe4\xf5\x67\x36\xba\x81\xcd\x0a\x8d\x0a\xe4\xb9\xdf\x0b\x84\x2c\x42\x39\xfa\xec\x37\x6f\xa0\x5e\x0e\x83\xee\xe2\x17\x5e\xb4\x17\x23\x0b\xc7\x6a\x48\x4d\x0b\x04\xf0\xbe\xf7\x6f\xa3\x16\x8e\xe6\x0b\x87\x18\x5e\x70\x75\xad\xc0\xc5\xe7\x3d\x89\xc8\xd0\xcd\xfd\xf4\x3e\xa9\xeb\x68\xb5\x04\x30\x7b\x8a\x2e\xa3\x11\x1b\x53\x8f\xb2\x19\x0a\x54\x55\xda\xbb\xec\x7a\x85\xff\xac\x60\xa4\x65\x26\xbf\x8e\x48\xd7\x36\x72\x2d\xcb\xb2\x3f\x18\x1e\xe9\x5e\xfc\x60\x5c\x5e\x98\x2f\xee\x9a\xa7\x91\xe8\xcd\x21\xad\x85\xea\xd4\x3b\x8e\xb2\xb5\x60\xd1\x23\x62\xbc\x2e\xe1\xfd\x6e\x74\x84\x84\x73\xca\x64\x68\x85\x16\xbb\x13\xe2\x4d\x33\xae\x79\x29\x3e\xbc\xa7\x9c\x21\xce\xdf\x6c\xb4\x24\x13\xc1\x82\xcc\xfd\x38\x1c\x85\x6d\x03\x87\x9a\x7b\x3c\x57\x0f\x18\xbe\xf8\xfb\xd0\xa7\x9b\xbb\x55\x7e\xaf\xc3\x7f\xfa\x50\x87\x9a\x60\xc7\x57\x60\x26\x14\x4d\xe7\x7c\x51\x23\xfa\x4c\x5d\x4d\x6d\xf2\xbd\xfd\xfe\x34\x9d\xba\x8f\xde\x71\x99\x27\x61\x80\x57\x33\xde\xce\xfb\xe9\x5f\x92\xba\xb5\xad\x40\xd4\x70\x8d\xb5\x04\xea\x22\xfb\x5b\x98\x2b\x66\x5c\xa4\x8e\x7f\x16\xfe\x90\x29\xa9\xce\x73\xdd\x14\x23\x25\x46\xf2\xbd\x11\x2f\x6d\x9a\x24\xfe\x6d\x09\x02\x96\x86\x4e\x99\x4c\x8c\xdf\xeb\xe9\x10\x55\x0d\xad\x94\xe8\x18\xc3\x41\x22\xa9\xcf\xf5\x9c\xb9\x08\xcf\xf0\x24\x2a\x2a\x4b\x8e\xb0\x8b\x8c\x71\x38\x30\x6a\x84\x14\x60\x73\x90\x20\x0c\x63\xae\x80\x4a\x6f\xef\x2d\xcd\x5e\xe4\x79\x81\x40\x4b\x2c\x70\xe4\x76\xfe\xe0\x74\x61\x71\x72\xed\xd4\xeb\x15\x77\x8a\x5d\xca\x8e\x30\x54\xf8\x67\xcf\xe1\x60\x5a\x7d\x71\x4c\xa3\xde\xd5\x5a\x7d\xc0\x02\x79\xbe\x1c\x6e\xe2\x0a\x10\xb1\xf3\x8a\xe4\x8a\xaa\xce\x13\xc2\xb9\xb5\xa1\x67\x6b\x8e\xae\x3f\xa6\x6d\x52\x0e\xed\x50\x93\x2f\x7b\x81\x84\x04\x6a\x7d\x15\xd2\xf7\xeb\xf6\xfd\xad\x69\x57\x16\x17\xaf\x74\x9b\x0b\x78\xa1\x8d\x3f\x0e\x28\xf8\x4b\x08\x08\xa9\x3f\x05\x1d\xec\xc8\x88\xfc\xa9\x56\xaa\x81\x51\x11\x4c\x45\x89\xe6\x3a\xd3\x78\xc2\xad\xb2\xfc\xd0\x7a\xf0\xa9\x3a\xb5\x34\x5b\xf0\x12\x31\x91\xba\xa0\xac\x23\x6d\x34\xc4\x91\xf1\xec\x57\x39\xba\x92\x31\x5b\xfb\xe5\x87\x9f\xa9\xed\x98\x2d\x86\x6b\xc3\xee\x01\x90\x9b\xbd\xe2\xf1\x79\x12\x16\x90\xe6\x4d\x16\x6b\x8e\xd8\x66\xf1\x0c\xa0\x53\xb9\x7c\xc6\xd2\x1f\x7a\xff\xe5\xc4\x0c\x64\x5f\xe0\x3e\x6e\x26\xac\xe8\x2a\x08\x88\x6c\x9e\x39\xaf\x70\xf8\x80\xbf\x3e\xb8\x64\xb0\xc9\x40\x7a\x7c\xa4\xaa\xc3\xf3\xfc\xfd\x96\x69\x3e\xdb\xec\x56\x11\x11\x41\xd0\x53\x0e\xf3\xfe\x93\xba\x5c\x44\x49\xaf\x60\xa2\xb3\x93\x40\xd6\x95\x7b\x74\x92\x98\x69\x7f\xfe\x62\x9e\x6b\x23\xd3\x10\xab\x3b\x76\xc3\xd3\xf0\x42\x3c\xfe\xcf\x18\x3a\x05\x42\x15\xb1\x8d\x2c\x8a\x7d\x4f\x31\x77\x14\xcd\x5b\xe5\xb7\x99\xff\x61\xdf\xf5\x1d\x1d\xde\x55\x1b\xd9\x35\xf5\xde\xd2\xb2\xa1\xb0\x1d\xd7\xa3\xd9\x96\xd9\x66\xbb\xc0\x04\x1b\x00\x3d\x77\x3b\x01\x21\x75\xfd\x93\x59\xe8\xba\x4e\x11\x07\x3b\xd8\xe6\x3d\x6d\x59\xc3\x20\xf7\xfb\xdb\xa0\x90\x41\x61\x66\xf1\x15\x3f\x9e\xcc\x58\x32\x89\xd5\x60\x9d\xc2\xf5\xcc\xdc\x69\xec\xc2\x3d\xa4\x03\x05\x34\xab\x4a\x20\xd9\x5c\x6e\xe7\x49\xe3\xf5\x04\x81\x63\x28\x30\x32\x8d\xd8\x59\xff\x97\xfe\x33\xbe\x13\xd9\x9e\x75\x57\x29\x29\xf1\x06\x53\xc8\x38\x0e\x3f\x0c\xd8\x83\x66\x38\xd2\x5d\x7a\x29\xea\x53\xba\x33\x39\x75\xf4\xaa\x1a\x6f\xca\x18\x89\xfc\x7a\xb6\x80\xfc\xa1\x03\x58\x8b\xe5\x86\xa2\x15\x8f\xd1\x4e\x5e\xf8\x9d\x10\xe6\x34\x27\xcb\xb8\xcc\xd5\x88\x5c\xad\xaf\x19\x45\x47\xb3\x54\x90\xcb\xa8\x68\xd8\xb9\x2d\xd8\x02\x34\x12\xe4\xe7\x82\x6b\x23\xe2\xcf\xb9\xfe\x76\x25\x6e\xb4\x52\x99\x9d\xba\x98\xec\x0b\xc2\x83\x7b\x47\x8d\x0b\x0b\x01\x7c\xce\x2d\xf4\x80\x57\xeb\xb7\x56\xe0\x0a\x52\xfd\xf4\x2f\xb9\x50\xfc\x5c\x78\xd3\x64\x45\x69\xc2\xc7\x75\xf2\x03\x27\xc4\x52\xd1\xa7\x68\xc0\xb4\xc0\x25\x46\x97\x57\x7e\xde\xaa\x7f\x33\xfd\xc7\xbd\x7c\x36\xc6\xe3\x32\x1e\x73\x2b\x0f\x8e\x21\x12\x8f\x64\x17\x16\xa7\x1a\x74\x7a\xbe\x30\x6c\x58\x6e\xbe\x92\x80\x6e\x46\x28\xd1\x81\x30\x9c\x59\x50\x0a\x91\x19\x43\x29\x12\xb2\x89\x62\xf3\x60\x4a\xcf\xe5\x61\x60\x27\x18\xed\x49\x7d\x32\x05\x57\x9d\xe6\x4a\xa6\xd7\x2a\x86\x04\xf1\x82\x53\x32\x32\x8b\xf6\x2e\x2a\xf7\x7e\xde\xe2\xea\x34\xb4\x84\xe1\xcb\xee\xef\xa5\xd6\x0c\x50\x9a\x5d\x40\x94\x8e\x62\x90\x81\x38\x39\xaf\xa3\xb9\x41\x73\x1e\xec\xf5\xef\xbc\x43\xfc\xca\xae\x1b\x04\x60\xd4\x1d\xed\x79\x47\x06\xec\x86\x12\x20\x33\xb4\xb1\x24\x34\x61\x5d\x48\x5b\x57\x53\xb3\xc3\x9f\x37\xdf\x28\xac\x4e\x55\xe4\x60\x2f\xec\xe6\xf6\x55\x72\xd4\x01\x22\xdb\xfe\x48\xc0\x3d\x5a\x4b', 1) ================================================ FILE: agent/fix_rule_no_att/license.lic ================================================ HSpGTEFHUzoBKkNPREU6UHlhcm1vci1Qcm9qZWN0egMhJ6NNlQdmKa/iVehg0LovCYkZYgMtBOQo6jHlP0slLc0iYjbS9NAnZQslmFBVRWO6JERZBMANwGUlKeAdkFUMv/8JT6WquK7BFnlYcPnuhV6z5zP2O/hWCaJzDGRL4pWU7A1javZizykc2gPVHAnWW1cvuMMbeNlS2tMSCeo= ================================================ FILE: agent/fix_rule_no_att/product.key ================================================ Bې?CAk8a6W H0vY y  }q#fRq+JBwzʱnƐS*TE`wN(RGy0tD&y P:Cq\8SBV] ================================================ FILE: agent/fix_rule_no_att/pytransform.py ================================================ # Because ctypes is new from Python 2.5, so pytransform doesn't work # before Python 2.5 # from ctypes import cdll, c_char, c_char_p, c_int, c_void_p, \ pythonapi, py_object, PYFUNCTYPE from ctypes.util import find_library import os import sys import platform import struct # # Global # _pytransform = None _get_error_msg = None # # Options # # How to show error message return from dynamic library _pytransform. # # Each time call a dll function, if something is wrong, _get_error_msg # will return the reason. # # Enable _verbose_mode will print the details got from _get_error_msg. # Disable this options will print only a short message. _verbose_mode = 1 # In debug mode, print trace stack when raise exception. # Otherwise only show a short message. # # Run python with command option "-d", or set environment variable # PYTHONDEBUG. For example # # python -d pyarmor.py xxxx # _debug_mode = sys.flags.debug class PytransformError(Exception): def __init__(self, *args, **kwargs): super(Exception, self).__init__(*args, **kwargs) if _debug_mode: self._print_stack() @classmethod def _print_stack(self): try: from traceback import print_stack except Exception: _debug_mode = 0 sys.stderr.write('Disabled debug mode.\n') else: print_stack() def dllmethod(func): def format_message(msg, *args, **kwargs): if _verbose_mode: s1 = str(args)[:-1] s2 = ', '.join(['%s=%s' % (k, repr(v)) for k, v in kwargs.items()]) sep = ', ' if (s1 and s2) else '' line = 'Call with arguments: %s%s%s)' % (s1, sep, s2) return msg return msg.split('\n')[-1] def wrap(*args, **kwargs): args = [(s.encode() if isinstance(s, str) else s) for s in args] result = func(*args, **kwargs) errmsg = _get_error_msg() if errmsg: raise PytransformError(format_message(errmsg, *args, **kwargs)) return result return wrap @dllmethod def init_pytransform(): major, minor = sys.version_info[0:2] # Python2.5 no sys.maxsize but sys.maxint # bitness = 64 if sys.maxsize > 2**32 else 32 prototype = PYFUNCTYPE(c_int, c_int, c_int, c_void_p) init_module = prototype(('init_module', _pytransform)) init_module(major, minor, pythonapi._handle) @dllmethod def init_runtime(systrace=0, sysprofile=1, threadtrace=0, threadprofile=1): pyarmor_init() prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int) _init_runtime = prototype(('init_runtime', _pytransform)) _init_runtime(systrace, sysprofile, threadtrace, threadprofile) @dllmethod def import_module(modname, filename): global _import_module if _import_module is None: prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) _import_module = prototype(('import_module', _pytransform)) return _import_module(modname, filename) _import_module = None @dllmethod def exec_file(filename): global _exec_file if _exec_file is None: prototype = PYFUNCTYPE(c_int, c_char_p) _exec_file = prototype(('exec_file', _pytransform)) return _exec_file(filename) _exec_file = None @dllmethod def encrypt_project_files(proname, filelist, mode=0): prototype = PYFUNCTYPE(c_int, c_char_p, py_object, c_int) dlfunc = prototype(('encrypt_project_files', _pytransform)) return dlfunc(proname, filelist, mode) @dllmethod def encrypt_files(key, filelist, mode=0): t_key = c_char * 32 prototype = PYFUNCTYPE(c_int, t_key, py_object, c_int) dlfunc = prototype(('encrypt_files', _pytransform)) return dlfunc(t_key(*key), filelist, mode) def generate_project_capsule(licfile): prikey, pubkey, prolic = _generate_project_capsule() capkey = _encode_capsule_key_file(licfile) return prikey, pubkey, capkey, prolic @dllmethod def _generate_project_capsule(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('generate_project_capsule', _pytransform)) return dlfunc() @dllmethod def _encode_capsule_key_file(licfile): prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) dlfunc = prototype(('encode_capsule_key_file', _pytransform)) return dlfunc(licfile, None) @dllmethod def generate_module_key(pubname, key): t_key = c_char * 32 prototype = PYFUNCTYPE(py_object, c_char_p, t_key, c_char_p) dlfunc = prototype(('generate_module_key', _pytransform)) return dlfunc(pubname, t_key(*key), None) @dllmethod def generate_license_file(filename, priname, rcode, start=-1, count=1): prototype = PYFUNCTYPE(c_int, c_char_p, c_char_p, c_char_p, c_int, c_int) dlfunc = prototype(('generate_project_license_files', _pytransform)) return dlfunc(filename, priname, rcode, start, count) @dllmethod def get_registration_code(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_registration_code', _pytransform)) return dlfunc() @dllmethod def get_expired_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_expired_days', _pytransform)) return dlfunc() @dllmethod def get_trial_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_trial_days', _pytransform)) return dlfunc() @dllmethod def version_info(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('version_info', _pytransform)) return dlfunc() def get_hd_sn(): size = 256 t_sn = c_char * size sn = t_sn() if (_pytransform.get_hd_sn(sn, size) == -1): return '' return sn.value.decode() def show_hd_info(): return _pytransform.show_hd_info() def get_license_info(): info = { 'expired': 'Never', 'restrict_mode': 'Enabled', 'HARDDISK': 'Any', 'IFMAC': 'Any', 'IFIPV4': 'Any', 'DOMAIN': 'Any', 'CODE': '', } rcode = get_registration_code().decode() index = 0 if rcode.startswith('*TIME:'): from time import ctime index = rcode.find('\n') info['expired'] = ctime(float(rcode[6:index])) index += 1 if rcode[index:].startswith('*FLAGS:'): info['restrict_mode'] = 'Disabled' index += len('*FLAGS:') + 1 prev = None start = index for k in ['HARDDISK', 'IFMAC', 'IFIPV4', 'DOMAIN', 'FIXKEY', 'CODE']: index = rcode.find('*%s:' % k) if index > -1: if prev is not None: info[prev] = rcode[start:index] prev = k start = index + len(k) + 2 info['CODE'] = rcode[start:] return info # Load _pytransform library def _load_library(path=None): if path is None: path = os.path.dirname(__file__) plat = platform.system().lower() bitness = struct.calcsize('P'.encode()) * 8 libpath = os.path.join(path, 'platforms', '%s%s' % (plat, bitness)) if not os.path.isdir(libpath): libpath = path try: if plat == 'linux': if libpath == '': m = cdll.LoadLibrary(os.path.abspath('_pytransform.so')) else: m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) m.set_option('libc'.encode(), find_library('c').encode()) elif plat == 'darwin': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dylib')) elif plat == 'windows': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dll')) elif plat == 'freebsd': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) else: raise RuntimeError('Platform not supported') except Exception: raise PytransformError('Could not load _pytransform from "%s"', libpath) # Required from Python3.6 m.set_option('byteorder'.encode(), sys.byteorder.encode()) # m.set_option('enable_trace_log'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('enable_encrypt_generator'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('disable_obfmode_encrypt'.encode(), c_char_p(1)) if not os.path.abspath('.') == os.path.abspath(path): m.set_option('pyshield_path'.encode(), path.encode()) return m def pyarmor_init(path=None): global _pytransform global _get_error_msg if _pytransform is None: _pytransform = _load_library(path) _get_error_msg = _pytransform.get_error_msg _get_error_msg.restype = c_char_p init_pytransform() def pyarmor_runtime(path=None): pyarmor_init(path) init_runtime(0, 0, 0, 0) ================================================ FILE: agent/simple/agent.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: agent.py @time: 2018/3/13 0013 10:51 @desc: rule based agent """ import os from agent.base_agent import BaseAgent from agent.simple import dqn import interface from world import config import copy import random import numpy as np DETECTOR_NUM = 0 FIGHTER_NUM = 10 COURSE_NUM = 16 ATTACK_IND_NUM = (DETECTOR_NUM + FIGHTER_NUM) * 2 + 1 # long missile attack + short missile attack + no attack ACTION_NUM = COURSE_NUM * ATTACK_IND_NUM class Agent(BaseAgent): def __init__(self): """ Init this agent :param size_x: battlefield horizontal size :param size_y: battlefield vertical size :param detector_num: detector quantity of this side :param fighter_num: fighter quantity of this side """ BaseAgent.__init__(self) self.obs_ind = 'simple' if not os.path.exists('model/simple/model.pkl'): print('Error: agent simple model data not exist!') exit(1) self.fighter_model = dqn.RLFighter(ACTION_NUM) def set_map_info(self, size_x, size_y, detector_num, fighter_num): self.size_x = size_x self.size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num def __reset(self): pass def get_action(self, obs_dict, step_cnt): """ get actions :param detector_obs_list: :param fighter_obs_list: :param joint_obs_dict: :param step_cnt: :return: """ detector_action = [] fighter_action = [] for y in range(self.fighter_num): true_action = np.array([0, 1, 0, 0], dtype=np.int32) if obs_dict['fighter'][y]['alive']: true_action = np.array([0, 1, 0, 0], dtype=np.int32) tmp_img_obs = obs_dict['fighter'][y]['screen'] tmp_img_obs = tmp_img_obs.transpose(2, 0, 1) tmp_info_obs = obs_dict['fighter'][y]['info'] tmp_action = self.fighter_model.choose_action(tmp_img_obs, tmp_info_obs) # action formation true_action[0] = int(360 / COURSE_NUM * int(tmp_action[0] / ATTACK_IND_NUM)) true_action[3] = int(tmp_action[0] % ATTACK_IND_NUM) fighter_action.append(copy.deepcopy(true_action)) fighter_action = np.array(fighter_action) return detector_action, fighter_action ================================================ FILE: agent/simple/dqn.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: dqn.py @time: 2018/7/25 0025 13:38 @desc: """ import torch import torch.nn as nn import numpy as np class NetFighter(nn.Module): def __init__(self, n_actions): super(NetFighter, self).__init__() self.conv1 = nn.Sequential( # 100 * 100 * 3 nn.Conv2d( in_channels=5, out_channels=16, kernel_size=5, stride=1, padding=2, ), nn.ReLU(), nn.MaxPool2d(2), ) self.conv2 = nn.Sequential( # 50 * 50 * 16 nn.Conv2d(16, 32, 5, 1, 2), nn.ReLU(), nn.MaxPool2d(2), # 25 * 25 * 32 ) self.info_fc = nn.Sequential( nn.Linear(3, 256), nn.Tanh(), ) self.feature_fc = nn.Sequential( # 25 * 25 * 32 + 256 nn.Linear((25 * 25 * 32 + 256), 512), nn.ReLU(), ) self.decision_fc = nn.Linear(512, n_actions) def forward(self, img, info): img_feature = self.conv1(img) img_feature = self.conv2(img_feature) info_feature = self.info_fc(info) combined = torch.cat((img_feature.view(img_feature.size(0), -1), info_feature.view(info_feature.size(0), -1)), dim=1) feature = self.feature_fc(combined) action = self.decision_fc(feature) return action # Deep Q Network off-policy class RLFighter: def __init__( self, n_actions, ): self.n_actions = n_actions self.gpu_enable = torch.cuda.is_available() self.target_net = NetFighter(self.n_actions) if self.gpu_enable: print('GPU Available!!') self.target_net = self.target_net.cuda() self.target_net.load_state_dict(torch.load('model/simple/model.pkl')) else: self.target_net.load_state_dict(torch.load('model/simple/model.pkl', map_location=lambda storage, loc: storage)) def choose_action(self, img_obs, info_obs): img_obs = torch.unsqueeze(torch.FloatTensor(img_obs), 0) info_obs = torch.unsqueeze(torch.FloatTensor(info_obs), 0) if self.gpu_enable: img_obs = img_obs.cuda() info_obs = info_obs.cuda() actions_value = self.target_net(img_obs, info_obs) action = torch.max(actions_value, 1)[1] if self.gpu_enable: action = action.cpu() action = action.numpy() return action class NetDetector(nn.Module): def __init__(self, n_actions): super(NetDetector, self).__init__() self.conv1 = nn.Sequential( # 100 * 100 * 3 nn.Conv2d( in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=2, ), nn.ReLU(), nn.MaxPool2d(2), ) self.conv2 = nn.Sequential( # 50 * 50 * 16 nn.Conv2d(16, 32, 5, 1, 2), nn.ReLU(), nn.MaxPool2d(2), # 25 * 25 * 32 ) self.info_fc = nn.Sequential( nn.Linear(3, 256), nn.Tanh(), ) self.feature_fc = nn.Sequential( # 25 * 25 * 32 + 256 nn.Linear((25 * 25 * 32 + 256), 512), nn.ReLU(), ) self.decision_fc = nn.Linear(512, n_actions) def forward(self, img, info): img_feature = self.conv1(img) img_feature = self.conv2(img_feature) info_feature = self.info_fc(info) combined = torch.cat((img_feature.view(img_feature.size(0), -1), info_feature.view(info_feature.size(0), -1)), dim=1) feature = self.feature_fc(combined) action = self.decision_fc(feature) return action ================================================ FILE: common/agent_process.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: fight.py @time: 2019/3/12 0009 16:41 @desc: agent sub proc """ import importlib import time from multiprocessing import Process, Queue AGENT_INIT_TIMEOUT = 60 AGENT_RESP_TIMEOUT = 60 class AgentProc(Process): ''' agent子进程 ''' def __init__(self, agent_name, size_x, size_y, detector_num, fighter_num, recv_queue, send_queue): super().__init__() self.agent_name = agent_name self.size_x = size_x self.size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num self.recv_queue = recv_queue self.send_queue = send_queue self.agent = None self.obs_construct = None self.obs_ind = 'raw' def run(self): agent_import_path = 'agent.' + self.agent_name + '.agent' agent_module = importlib.import_module(agent_import_path) self.agent = agent_module.Agent() self.agent.set_map_info(self.size_x, self.size_y, self.detector_num,self.fighter_num) self.obs_ind = self.agent.get_obs_ind() if self.obs_ind != 'raw': obs_path = 'obs_construct.' + self.obs_ind + '.construct' obs_module = importlib.import_module(obs_path) self.obs_construct = obs_module.ObsConstruct(self.size_x, self.size_y, self.detector_num,self.fighter_num) # time.sleep(5) self.send_queue.put('Init OK') self.__decision_proc() def __decision_proc(self): while True: obs_data = self.recv_queue.get() obs_raw_dict = obs_data['obs_raw_dict'] step_cnt = obs_data['step_cnt'] if self.obs_ind == 'raw': obs_dict = obs_raw_dict else: obs_dict = self.obs_construct.obs_construct(obs_raw_dict) detector_action, fighter_action = self.agent.get_action(obs_dict, step_cnt) action_dict = {'detector_action': detector_action, 'fighter_action': fighter_action} self.send_queue.put(action_dict) class AgentCtrl: ''' agent子进程维护 ''' def __init__(self, agent_name, size_x, size_y, detector_num, fighter_num): self.agent_name = agent_name self.size_x = size_x self.size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num self.send_q = None self.recv_q = None self.agent = None def agent_init(self): self.send_q = Queue(1) self.recv_q = Queue(1) self.agent = AgentProc(self.agent_name, self.size_x, self.size_y, self.detector_num, self.fighter_num, self.send_q, self.recv_q) self.agent.start() try: agent_msg = self.recv_q.get(True, AGENT_INIT_TIMEOUT) except: self.terminate() return False else: return True def terminate(self): if self.agent: if self.agent.is_alive(): self.agent.terminate() self.agent = None if self.send_q: self.send_q.close() self.send_q = None if self.recv_q: self.recv_q.close() self.recv_q = None def get_action(self, obs_raw_dict, step_cnt): ''' 获得动作 :param obs_raw_dict: raw obs :param step_cnt: step计数,从1开始 :return: action: 动作信息 :return: result: 0, 正常; 1, 崩溃; 2, 超时 ''' action = [] result = 0 self.send_q.put({'obs_raw_dict': obs_raw_dict, 'step_cnt': step_cnt}) try: action = self.recv_q.get(True, AGENT_RESP_TIMEOUT) except: if not self.agent.is_alive(): # 子进程不存在,崩溃 result = 1 else: # 子进程存在,卡死 result = 2 self.__agent_restart() return action, result def __agent_restart(self): ''' 重启agent。由于重启代表之前成功启动过,所以此处认为重启也会成功。但若重启不成功将导致程序卡死。若后续出现此类问题应重点排查此处。 :return: ''' self.terminate() self.send_q = Queue(1) self.recv_q = Queue(1) self.agent = AgentProc(self.agent_name, self.size_x, self.size_y, self.detector_num, self.fighter_num, self.send_q, self.recv_q) self.agent.start() self.recv_q.get() ================================================ FILE: configuration/reward.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: reward.py @time: 2018/4/19 0019 14:39 @desc: reward config """ class GlobalVar: # reward # detector radar see a detector reward_radar_detector_detector = 20 # detector radar see a fighter reward_radar_detector_fighter = 10 # fighter radar see a detector reward_radar_fighter_detector = 20 # fighter radar see a fighter reward_radar_fighter_fighter = 10 # Missile hit a detector reward_strike_detector_success = 100 # Missile miss a detector reward_strike_detector_fail = 0 # Missile hit a fighter reward_strike_fighter_success = 100 # Missile miss a fighter reward_strike_fighter_fail = 0 # A detector been destroyed reward_detector_destroyed = -100 # A fighter been destroyed reward_fighter_destroyed = -100 # A valid attack action reward_strike_act_valid = 10 # An invalid attack action reward_strike_act_invalid = -10 # Keep alive in a step reward_keep_alive_step = 0 # Round reward:totally win reward_totally_win = 200 # Round reward:totally lose reward_totally_lose = -200 # Round reward:win reward_win = 100 # Round reward:lose reward_lose = -100 # Round reward:draw reward_draw = 0 def get_reward_radar_detector_detector(): return GlobalVar.reward_radar_detector_detector def get_reward_radar_detector_fighter(): return GlobalVar.reward_radar_detector_fighter def get_reward_radar_fighter_detector(): return GlobalVar.reward_radar_fighter_detector def get_reward_radar_fighter_fighter(): return GlobalVar.reward_radar_fighter_fighter def get_reward_strike_detector_success(): return GlobalVar.reward_strike_detector_success def get_reward_strike_detector_fail(): return GlobalVar.reward_strike_detector_fail def get_reward_strike_fighter_success(): return GlobalVar.reward_strike_fighter_success def get_reward_strike_fighter_fail(): return GlobalVar.reward_strike_fighter_fail def get_reward_detector_destroyed(): return GlobalVar.reward_detector_destroyed def get_reward_fighter_destroyed(): return GlobalVar.reward_fighter_destroyed def get_reward_strike_act_valid(): return GlobalVar.reward_strike_act_valid def get_reward_strike_act_invalid(): return GlobalVar.reward_strike_act_invalid def get_reward_keep_alive_step(): return GlobalVar.reward_keep_alive_step def get_reward_win(): return GlobalVar.reward_win def get_reward_lose(): return GlobalVar.reward_lose def get_reward_totally_win(): return GlobalVar.reward_totally_win def get_reward_totally_lose(): return GlobalVar.reward_totally_lose def get_reward_draw(): return GlobalVar.reward_draw ================================================ FILE: configuration/system.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: system.py @time: 2018/4/19 0019 14:46 @desc: system config """ class GlobalVar: # attack effect delay attack_effect_delay = True # hit probability enable hit_prob_enable = True def get_attack_effect_delay(): return GlobalVar.attack_effect_delay def get_hit_prob_enable(): return GlobalVar.hit_prob_enable ================================================ FILE: doc/tutorial.md ================================================ # 多智能体对抗仿真环境MaCA完全入门指南 [TOC] ## 1 前言 中国电子科技集团公司认知与智能技术重点实验室发布的MaCA(Multi-agent Combat Arena)环境,是国内首个可模拟军事作战的轻量级多智能体对抗与训练平台,是多智能体对抗算法研究、训练、测试和评估的绝佳环境,可支持作战场景和规模自定义,智能体数量和种类自定义,智能体特征和属性自定义,支持智能体行为回报规则和回报值自定义等。目前发布的MaCA环境中预设了两种智能体类型,探测单元和攻击单元:探测单元可模拟L、S波段雷达进行全向探测,支持多频点切换;攻击单元具备侦察、探测、干扰、打击等功能,可模拟X波段雷达进行指向性探测,模拟L、S、X频段干扰设备进行阻塞式和瞄准式电子干扰,支持多频点切换。攻击单元还可模拟对敌方进行火力攻击,同时具有无源侦测能力,可模拟多站无源协同定位和辐射源特征识别。 MaCA 环境为研究利用人工智能方法解决大规模多智能体分布式对抗问题提供了很好的支撑,专门面向多智能体深度强化学习开放了 RL-API 接口。环境支持使用 Python 语言进行算法实现,并支持Tensorflow、Pytorch 等常用深度学习框架的集成调用。 ## 2 MaCA安装 ### 2.1 系统要求 - Linux 64-bit 、Mac OS或windows10 x64操作系统 - Python 3.6及后续版本 - Numpy、Pandas、Pygame软件包 ### 2.2 环境安装 1. 安装支撑软件包 ```bash pip install -U numpy pandas pygame ``` 2. 下载环境 ```bash git clone https://github.com/CETC-TFAI/MaCA.git ``` 3. 环境配置 - 命令行形式(适用于Linux、Mac) ```bash cd MaCA export PYTHONPATH=$(pwd)/environment:$PYTHONPATH ``` - Pycharm中配置(适用于Windows、Linux、Mac) - 在Pycharm打开MaCA - 将“[environment](/environment)”文件夹设置为“Sources Root” - 运行MaCA中任何py文件必须将其“Working Directory”设置为MaCA根目录 ## 3 MaCA初探 ### 3.1 运行一个对抗场景 运行[fight_mp.py](/fight_mp.py),即可开启一场基于预置规则的异构对抗 ```bash python fight_mp.py ``` 对抗场景如下图: ![对抗示例](https://leonfg.github.io/maca/resource/maca.gif) ### 3.2 观看回放录像 1. 运行对抗时增加“--log”参数,即可在log路径下生成以“红方_名称vs_蓝方名称”命名的日志文件夹,其中存储本局对抗相关信息。 ```bash python fight_mp.py --log ``` 2. 默认对抗示例的日志名称为“fix_rule_vs_fix_rule”,使用replay.py即可进行日志回放: ```bash python replay.py fix_rule_vs_fix_rule ``` ## 4 MaCA详解 ### 4.1 MaCA架构简介 ![MaCA架构图](https://leonfg.github.io/maca/resource/maca_arch_cn.png) MaCA核心架构如上图所示,由推演引擎环境辅以对抗调度模块,对抗场景库(maps)构成。与外围的决策算法代码相结合,实现多智能体决策训练及对抗。主要模块功能如下: - 推演环境:承担核心推演功能,负责载入作战场景,对外输出原始战场状态数据(raw obs),接收双方决策算法给出的动作控制信息,执行一系列位置更新、电磁传播、攻击决策算法,实现对抗推演,同时具有现实及日志记录功能。 - 调用接口:外部模块与推演环境的交互接口 - 对抗调度:负责启动红蓝对抗,可对对抗多方面参数进行自定义,如双方决策程序指定、对抗局数、场景选择、每局最高持续帧数等等,同时向决策进程转发推演环境输出的状态数据,接收决策进程输出的动作信息 - 决策进程:红蓝双方对抗控制决策以单独子进程形式存在,相互隔离,主要包括两个模块 - 决策算法:智能体决策算法,接收观测数据,输出动作控制数据 - 观测构建:每个决策算法对应的观测信息构建,由原始状态数据生成与决策算法需求对应的观测数据。 MaCA代码结构与上述模块划分一致,具体如下图: ![MaCA代码结构](https://leonfg.github.io/maca/resource/code_arch.png) - [environment/](/environment/):推演环境 - [interface.py](/environment/interface.py):环境调用接口 - [agent/](/agent/):用于存储封装好的可直接调用的智能体决策代码,每个决策代码可单独设立一个文件夹,按照标准封装,系统直接调用 - [fix_rule/](/agent/fix_rule/):基于规则的同构异构对抗决策算法 - [fix_rule_no_att/](/agent/fix_rule_no_att/):基于规则的同构异构对抗决策算法(无攻击能力) - [simple/](/agent/simple/):基于DQN的同构对抗决策示例算法,需使用pytorch - [obs_construct/](/obs_construct/):观测值组织形式自定义,开发者可根据算法需要自定义obs结构 - [simple/](/obs_construct/simple/):基于DQN的同构对抗决策示例对应的obs组织 - [fight_mp.py](/fight_mp.py):对抗调度程序 - [replay.py](/replay.py) 日志回放入口程序 - [maps/](/maps/):对抗场景库 - [1000_1000_2_10_vs_2_10.map](/maps/1000_1000_2_10_vs_2_10.map):预置异构对抗场景 - [1000_1000_fighter10v10.map](/maps/1000_1000_fighter10v10.map):预置同构对抗场景 - [configuration/](/configuration/):配置数据 - [reward.py](/configuration/reward.py):部分预置回报检查点的赋值 - [system.py](/configuration/system.py):部分系统推演规则定义 - [train/](/train):决策算法训练所在路径 - [simple/](/train/simple/):simple决策模型训练代码 - [model/](/model/):决策模型数据存储路径 - [simple/](model/simple/):simple决策模型数据存储路径 - [common/](/common):多模块复用代码 - [agent_process.py](/common/agent_process.py):对抗调度和比赛调度模块公用子进程管理代码 - [log/](/log/):对抗日志存储路径 - [tournament/](/tournament/):多智能体比赛调度及结果统计模块 - [tournament_mp.py](/tournament/tournament_mp.py):比赛调度程序 - [config_gen.py](/tournament/config_gen.py):比赛配置辅助生成工具 ### 4.2 环境调用接口说明 MaCA环境的接口定义在[interface.py ](/environment/interface.py )文件的Environment类中,其接口函数如下: | 接口函数 | 描述 | | ---- | ---- | | init | 环境实例初始化 | | reset | 环境重置 | | step | 环境运行一步 | | get_obs | 获得组合的观测信息 | | get_done | 判断对抗是否结束 | | get_alive_status | 获取各作战单元的存活状态 | | get_map_size | 获取地图大小信息 | | get_unit_num | 获取各类别作战单元数目 | | get_unit_property_list | 获取各作战单元属性信息 | | get_reward | 获取回报 | #### 4.2.1 init 参数: | 序号 | 参数名 | 功能 | 存在性 | 赋值规则 | | ---- | --------------- | --------------------- | ------ | ------------------------------------------------------------ | | 1 | map_path | 地图路径 | 必选 | MaCA根目录下开始的完整地图文件路径,如“maps/1000_1000_fighter10v10.map” | | 2 | side1_obs_ind | 红方使用的obs构建模块 | 必选 | 使用raw obs则赋值为“raw”,使用其他自定义obs构建则输入对应构建模块所在文件夹名 | | 3 | side2_obs_ind | 蓝方使用的obs构建模块 | 必选 | 同side2_obs_ind | | 4 | max_step | 每局最大步长 | 可选 | 正整数值,不赋值此参数则使用默认值5000 | | 5 | render | 是否开启显示 | 可选 | True:开启显示,False:关闭显示。不使用此参数则为默认不开启 | | 6 | render_interval | 每隔几步显示一帧 | 可选 | 正整数值,不赋值此参数则使用默认每步都显示 | | 7 | random_pos | 是否使用随机出生位置 | 可选 | True:开启随机出生,False:关闭示。不使用此参数则为默认红方在左侧蓝方在右侧 | | 8 | log | 日志记录开启指示 | 可选 | False:不开启日志,其他字符串值:日志文件夹名称。不使用此参数则默认不开启 | | 9 | random_seed | 指定随机种子 | 可选 | -1:不指定随机种子,其他正整数:指定随机种子。不使用此参数则默认不指定,由环境生成。该参数主要用于回放时载入原始种子重现对抗场景,fight和train时不需考虑。 | 返回值:无 #### 4.2.2 reset 参数:无 返回值:无 #### 4.2.3 step 参数: | 序号 | 参数名 | 功能 | 存在性 | 赋值规则 | | ---- | --------------------- | ---------------------- | ------ | --------- | | 1 | side1_detector_action | 红方探测单元动作集 | 必选 | 参见动作结构 | | 2 | side1_fighter_action | 红方干扰打击单元动作集 | 必选 | 参见动作结构 | | 3 | side2_detector_action | 蓝方探测单元动作集 | 必选 | 参见动作结构 | | 4 | side2_fighter_action | 蓝方干扰打击单元动作集 | 必选 | 参见动作结构 | #### 4.2.4 get_obs 参数:无 返回值: | 序号 | 返回值名 | 功能 | 数据形式 | | ---- | --------- | ------- | --------------------------------------- | | 1 | side1_obs | 红方obs | 参见raw obs | | 2 | side2_obs | 蓝方obs | 同上 | #### 4.2.5 get_done 参数:无 返回值:无 #### 4.2.6 get_reward 参数:无 返回值: | 序号 | 返回值名 | 功能 | 数据形式 | | ---- | -------------- | ------------------------ | --------- | | 1 | side1_detector | 红方探测单元单步回报 | 整型 | | 2 | side1_fighter | 红方打击干扰单元单步回报 | 整型 | | 3 | side1_round | 红方本局输赢回报 | 整型 | | 4 | side2_detector | 蓝方探测单元单步回报 | 整型 | | 5 | side2_fighter | 蓝方打击干扰单元单步回报 | 整型 | | 6 | side2_round | 蓝方本局输赢回报 | 整型 | ### 4.3 自定义obs信息统一接口 为使得环境能够返回Agent对抗或训练时自定义的状态信息,自组织状态信息的接口统一放在[obs_construct/](/obs_construct/)下,如simple智能体中指定obs_ind为simple,则应在obs_construct模块下构建simple文件夹,每一个信息组织形式文件夹下必须包含construct.py文件,且该文件定义ObsConstruct类及obs_construct成员函数,成员函数接口如下: ```python def obs_construct(self, obs_raw_dict) # obs_raw_dict为raw信息结构 ``` ### 4.4 多智能体对抗决策程序(agent)统一接口 为方便统一对抗调用形式,限定Agent必须包含如下成员、函数接口及固定格式的返回值。 ```python from agent.base_agent import BaseAgent class Agent(BaseAgent): def __init__(self): #初始化接口 BaseAgent.__init__(self) #从BaseAgent继承 self.obs_ind = 'raw' #状态信息形式,’raw’表示原始信息形式 #注:可根据原始信息自行组织新的信息,可参考simple def set_map_info(self, size_x, size_y, detector_num, fighter_num):#读取地图信息 pass #根据需要自行选择函数实现形式 def get_action(self, obs_dict, step_cnt): #obs_dict为状态,step_cnt为当前步数(从1开始) return detector_action, fighter_action ``` ### 4.5 主要数据格式定义 #### 4.5.1 raw obs信息结构 环境底层原始观测数据Raw data Observation结构为字典,包含三部分内容分别为detector_obs_list、fighter_obs_list、join_obs_dict。 detector_obs_list表示探测单元信息,结构为detector数目的list。每一个元素为字典结构,表示当前detector的obs信息,具体内容如下: | Key | Value | | -------------- | ------------------------------------------------------------ | | id | 探测单元id编号,从1开始 | | alive | 攻击单元存活状态 | | pos_x | 横向坐标(位置坐标体系圆点为左上角,水平向右为x,垂直向下为y) | | pos_y | 纵向坐标(位置坐标体系圆点为左上角,水平向右为x,垂直向下为y) | | course | 航向(0-359),水平向右为0度,顺时针旋转 | | r_iswork | 雷达开关状态 | | r_fre_point | 雷达频点 | | r_visible_list | 雷达观测到的敌方单位列表,每一元素字典,表示敌方单位信息,结构为 {‘id’:编号,’type’:类型(0:探测单元,1:攻击单元) ,‘pos_x’:横向坐标,’pos_y’:纵向坐标} | | last_reward | 上一个状态采用动作的回报 | | last_action | 上一个状态采用的动作(字典结构) {‘course’:,’r_iswork’:,’r_fre_point’: } | fighter_obs_list表示攻击单元信息,结构为detector数目的list。每一个元素为字典结构,表示当前detector的obs信息,具体内容如下: | Key | Value | | ------------------ | ------------------------------------------------------------ | | id | 攻击单元id编号,编号从探测单元数目开始 | | alive | 攻击单元存活状态 | | pos_x | 横向坐标(位置坐标体系圆点为左上角,水平向右为x,垂直向下为y) | | pos_y | 纵向坐标(位置坐标体系圆点为左上角,水平向右为x,垂直向下为y) | | course | 航向(0-359),水平向右为0度,顺时针旋转 | | r_iswork | 雷达开关状态 | | r_fre_point | 雷达频点 | | r_visible_list | 雷达观测到的敌方单位列表,每一元素字典,表示敌方单位信息,结构为 {‘id’:编号,’type’:类型(0:探测单元,1:攻击单元) ,‘pos_x’:横向坐标,’pos_y’:纵向坐标} | | j_iswork | 干扰雷达开关 | | j_fre_point | 干扰雷达频点 | | j_recv_list | 雷达被动观测的敌方单位列表,每一元素字典,表示敌方单位信息,结构为 {‘id’:编号,’type’: 类型(0:探测单元,1:攻击单元) ,’direction’:目标相对自身的方向,’r_fp’:频点} | | l_missle_left | 远程导弹剩余数目 | | s_missle_left | 短程导弹剩余数目 | | striking_list | 打击列表[{'attacker_id': 攻击者编号, 'target_id': 目标编号, 'missile_type': 导弹类型(1:远程,2:中程), information_source_type: 信息来源类型(0:L探测单元,1:S探测单元,2:战机,3:被动侦测) step_count: step计数, op_count: 观测点计数, del_ind: 删除指示(默认false)}] | | striking_dict_list | 攻击列表[{‘target_id’:目标编号,‘pos_x’:横向坐标,’pos_y’:纵向坐标,‘type’: 类型(0:探测单元,1:攻击单元) }] | | last_reward | 上一个状态采用动作的回报 | | last_action | 上一个状态采用的动作(字典结构) {‘course’:,’r_iswork’:,’r_fre_point’:,’j_iswork’:,’j_fre_point’;,’hit_target’:,’missle_type’:} | joint_obs_list为表示全局信息的字典{‘strike_list’:打击列表,’passive_detection_enemy_list’:被动侦测列表}。strike_list为每一个作战单元striking_list的并集,passive_detection_enemy_list:[{‘id’:敌方编号,’type’: 类型(0:探测单元,1:攻击单元),‘pos_x’:横向坐标,’pos_y’:纵向坐标}] #### 4.5.2 动作信息结构 以2个探测单元,10个攻击单元为例,则: **detector_action为2*2的数组,每一行表示每一个探测单元的两个动作:** | | 意义 | 取值范围 | | ----- | -------------- | ------------------------------------------------- | | 动作1 | 航向 | [0-359]离散值 | | 动作2 | 雷达开关及频点 | 频点[0,频点总数],0表示雷达关,非0表示开机对应频点 | **fighter_action为10*4的数组,每一行表示每一个攻击单元的四个动作:** | | 意义 | 取值范围 | | ----- | -------------------------- | ------------------------------------------------------------ | | 动作1 | 航向 | [0-359]离散值 | | 动作2 | 雷达开关及频点 | 频点[0,频点总数],0表示关,非0表示开机对应频点 | | 动作3 | 干扰设备开关机频点 | 频点[0,频点总数+1],0表示关,[1,频点总数]表示开机对应频点 频点总数+1:阻塞干扰 | | 动作4 | 是否发射导弹及攻击目标的id | [0,25] 0:表示不发射导弹 1-12: 表示远程导弹攻击敌方目标id 13-24: 表示中程导弹攻击敌方目标id+12 | #### 4.5.3 回报结构 回报信息可以通过Environment 接口中的get_reward获得,具体结构如下: **以2个探测单元,10个攻击单元为例,则:** - detector_reward为(2,)的numpy数组,每一个元素表示每一个探测单元的动作回报 - fighter_reward为(10,)的numpy数组,每一个元素表示每一个攻击单元的动作回报 - round_reward为游戏输赢的回报,此回报只有当一局对抗结束才有效 ## 5 多智能体算法 ### 5.1 算法与MaCA环境交互关系 ![工作流程](https://leonfg.github.io/maca/resource/workflow.png) ### 5.2 算法实现示例 #### 5.2.1 多智能体决策(agent) 决策的功能是接收观测量并处理,选择合适的动作,并与环境交互。这里将多智能体决策程序称为agent,代码存放在agent路径中,子文件夹名称作为该agent名称。其中,agent.base_agent模块定义了所有agent的基类BaseAgent,其中定义了一些基本数据以及观测信息构建类指示函数: ```python def get_obs_ind(self): """ obs indication :return: obs构建程序路径指示字符串。对应obs_construct中同名观测构建代码,若该项赋值为'raw'代表使用原始raw obs data """ '''返回obs_ind的值''' ``` agent程序最主要的方法是get_action,即接收观测量并返回动作。 一个简单的agent应具有如下形式: ```python class SimpleAgent(BaseAgent): def __init__(self): BaseAgent.__init__(self) self.obs_ind = 'simple_agent' # 指示本决策agent需要使用哪个观测构建类 '''初始化数据''' def set_map_info(self, size_x, size_y, detector_num, fighter_num): """ 获取地图信息 :param size_x: 横向尺寸 :param size_y: 纵向尺寸 :param detector_num: 探测单元数量 :param fighter_num: 攻击单元数量 :return: N/A """ '''初始化地图、作战实体相关信息''' def get_action(self, obs_data, step_cnt): """ 获取动作 :param obs_data: obs数据 :param step_cnt: 当前step数 :return: detector_action:侦查单元动作 fighter_action:攻击单元动作 """ '''由观测信息得到动作并输出''' ``` #### 5.2.2 观测信息构建 环境输出的底层观测信息称为raw obs,该数据包括红方或蓝方自身所有可知状态信息。对于不同技术思路的agent来说,可能并不需要所有信息,或者数据构建形式不能满足agent自身需求,此时需要agent编写者基于raw obs进行观测信息重构,环境提供此机制便于开发者自定义观测数据。 观测重构代码应存放于obs_construct/“obs_ind名称”/construct.py,其中“obs_ind名称”应与agent中obs_ind变量赋值相同。一个典型的obs重构代码应具有如下形式: ```python class ObsConstruct: def __init__(self, size_x, size_y, detector_num, fighter_num): """ obs construct init :param size_x: 战场横向尺寸 :param size_y: 战场纵向尺寸 :param detector_num: 探测单元数量 :param fighter_num: 攻击单元数量 """ '''内部数据初始化''' def obs_construct(self, obs_raw_dict): ''' obs构建调用接口 :param obs_raw_dict: raw obs数据 :return: 自定义obs数据 ''' '''接收raw obs,构建新obs''' ``` agent及配套obs construct按上述要求编写完成后,使用fight_mp进行对抗时会自动生成agent所需观测数据提供给agent进行动作决策。 #### 5.2.3 训练 对于使用强化学习等机器学习方法构建的agent程序,需要进行一系列的训练,形成模型数据,才可以实现较好的动作决策。推荐将训练代码存放于“train/agent同名子文件夹”路径下,生成的模型数据存放于“model/agent同名子文件夹”,便于管理使用。 由于不同技术思路下的agent训练过程各不相同,本环境无法提类似fight_mp的统一训练管控入口,需要开发者自己编写训练调度程序,调用环境接口进行训练。对抗执行流程如下: 1. 实例化双方agent 2. 获取双方obs_ind 3. 调用environment.interface.Environment()创建环境 4. environment.interface.get_map_info()获取地图信息 5. 将地图信息传给每个agent各自的set_map_info() 6. 进入step推进的循环 - environment.interface.Environment.get_obs()获取obs信息 - 使用agent的get_action()将obs传输给agent并获取动作指令 - environment.interface.Environment.step()将动作返回环境,进行下一步推演 - 每一step完成后通过environment.interface.Environment.get_reward()获得回报数据,通过每一step完成后通过environment.interface.Environment.get_done()判断本局是否结束。 具体训练流程可参考[train/simple/main.py](/train/simple/main.py) ## 6 小结 中国电子科技集团公司认知与智能技术重点实验室发布的MaCA环境为多智能体对抗算法研究领域带来了一个全新的实验、训练与评估验证平台,为广大研究者提供了更多的交流学习机会。众多军队科研机构,企业科研机构,高等院校都在这个“竞技场”上修炼升级,同台竞技,相互促进,共同提高,将极大地促进军事智能算法研究和重大基础科学问题的解决,实现军事智能跨越式发展和实质性突破,为军事智能未来应用打下坚实基础。 ================================================ FILE: environment/error_log.py ================================================ {'speed': 3, 'r_band': 2, 'j_band': 2, 'l_missile_num': 2, 's_missile_num': 4, 'id': 3, 'alive': True, 'pos_x': 576, 'pos_y': 649, 'course': 20, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'l_missile_left': 0, 's_missile_left': 3, 'j_enable': True, 'j_fp': 0, 'j_receive_list': []} [{'speed': 1, 'r_band': 0, 'id': 1, 'alive': False, 'pos_x': 491, 'pos_y': 333, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 11, 'type': 1, 'pos_x': 464, 'pos_y': 361}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {'speed': 1, 'r_band': 1, 'id': 2, 'alive': False, 'pos_x': 330, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 2, 'type': 0, 'pos_x': 622, 'pos_y': 666}, {'id': 11, 'type': 1, 'pos_x': 363, 'pos_y': 654}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}] {'id': 2, 'alive': False, 'pos_x': 487, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 1, 'type': 0, 'pos_x': 491, 'pos_y': 333}, {'id': 3, 'type': 1, 'pos_x': 520, 'pos_y': 638}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} {'speed': 3, 'r_band': 2, 'j_band': 2, 'l_missile_num': 2, 's_missile_num': 4, 'id': 3, 'alive': True, 'pos_x': 578, 'pos_y': 650, 'course': 20, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'l_missile_left': 0, 's_missile_left': 2, 'j_enable': True, 'j_fp': 0, 'j_receive_list': []} [{'speed': 1, 'r_band': 0, 'id': 1, 'alive': False, 'pos_x': 491, 'pos_y': 333, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 11, 'type': 1, 'pos_x': 464, 'pos_y': 361}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {'speed': 1, 'r_band': 1, 'id': 2, 'alive': False, 'pos_x': 330, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 2, 'type': 0, 'pos_x': 622, 'pos_y': 666}, {'id': 11, 'type': 1, 'pos_x': 363, 'pos_y': 654}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}] {'id': 2, 'alive': False, 'pos_x': 487, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 1, 'type': 0, 'pos_x': 491, 'pos_y': 333}, {'id': 3, 'type': 1, 'pos_x': 520, 'pos_y': 638}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} {'speed': 3, 'r_band': 2, 'j_band': 2, 'l_missile_num': 2, 's_missile_num': 4, 'id': 3, 'alive': True, 'pos_x': 580, 'pos_y': 650, 'course': 19, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'l_missile_left': 0, 's_missile_left': 1, 'j_enable': True, 'j_fp': 0, 'j_receive_list': []} [{'speed': 1, 'r_band': 0, 'id': 1, 'alive': False, 'pos_x': 491, 'pos_y': 333, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 11, 'type': 1, 'pos_x': 464, 'pos_y': 361}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {'speed': 1, 'r_band': 1, 'id': 2, 'alive': False, 'pos_x': 330, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 2, 'type': 0, 'pos_x': 622, 'pos_y': 666}, {'id': 11, 'type': 1, 'pos_x': 363, 'pos_y': 654}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}] {'id': 2, 'alive': False, 'pos_x': 487, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 1, 'type': 0, 'pos_x': 491, 'pos_y': 333}, {'id': 3, 'type': 1, 'pos_x': 520, 'pos_y': 638}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} {'speed': 3, 'r_band': 2, 'j_band': 0, 'l_missile_num': 2, 's_missile_num': 4, 'id': 11, 'alive': True, 'pos_x': 526, 'pos_y': 329, 'course': 336, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'l_missile_left': 0, 's_missile_left': 1, 'j_enable': True, 'j_fp': 0, 'j_receive_list': []} [{'speed': 1, 'r_band': 0, 'id': 1, 'alive': False, 'pos_x': 669, 'pos_y': 333, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 1, 'type': 0, 'pos_x': 350, 'pos_y': 333}, {'id': 3, 'type': 1, 'pos_x': 570, 'pos_y': 310}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {'speed': 1, 'r_band': 1, 'id': 2, 'alive': False, 'pos_x': 487, 'pos_y': 666, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [{'id': 1, 'type': 0, 'pos_x': 491, 'pos_y': 333}, {'id': 3, 'type': 1, 'pos_x': 520, 'pos_y': 638}], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}] {'id': 3, 'alive': True, 'pos_x': 615, 'pos_y': 651, 'course': 0, 'radar_enable': True, 'radar_fp': 1, 'radar_visible_list': [], 'passive_location_ind': False, 'detection_count_list': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'l_missile_left': 0, 's_missile_left': 0, 'j_enable': True, 'j_fp': 0, 'j_receive_list': []} strike_context: {'attacker_id': 11, 'target_id': 3, 'missile_type': 2, 'step_count': 1, 'op_count': 0, 'del_ind': False} Traceback (most recent call last): File "fight.py", line 136, in env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) File "/home/boris/MaCA/environment/interface.py", line 151, in step return self.env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) File "", line 1506, in step File "", line 401, in __step_list File "", line 441, in __under_strike_proc File "", line 693, in __under_strike_proc_delay File "/home/boris/MaCA/environment/world/strike_calc.py", line 97, in strike_judge if missile_type == config.get_s_missile_type() and step_count >= 4: UnboundLocalError: local variable 'step_count' referenced before assignment dis_update dis_update(self, done, step, o_detector_data_obs_list, o_fighter_data_obs_list, e_detector_data_obs_list, e_fighter_data_obs_list, o_detector_data_reward_list, o_fighter_data_reward_list, e_detector_data_reward_list, e_fighter_data_reward_list, o_all_reward, e_all_reward) dis_update_done_font dis_update_done_font(self) dis_update_font dis_update_font(self, step, red_reward, blue_reward, red_detector_num, blue_detector_num, red_fighter_num, blue_fighter_num, red_missile_num, blue_missile_num) dis_update_pos dis_update_pos(self, o_detector_pos_list, o_fighter_pos_list, e_detector_pos_list, e_fighter_pos_list) draw_course draw_course(self, center_point_x, center_point_y, course, course_r) draw_fight_rela draw_fight_rela(self, center_point_x, center_point_y, striking_list, detector_list, fighter_list, line_colour) ================================================ FILE: environment/interface.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- from pytransform import pyarmor_runtime pyarmor_runtime() """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: interface.py @time: 2018/4/10 0010 8:52 @desc: environment interface """ import importlib from world.em_battle import BattleField from world.replay import Replay from world.load_map import Map import world.position_calc as position_calc from render.render_pic import Render class Environment: """ Environment interface """ def __init__(self, map_path, side1_obs_ind, side2_obs_ind, max_step=5000, render=False, render_interval=1, random_pos=False, log=False, random_seed=-1): """ Environment initiation :param size_x: battlefield horizontal size. got from LoadMap.get_map_size :param size_y: battlefield vertical size. got from LoadMap.get_map_size :param side1_detector_list: side 1 detector configuration. got from LoadMap.get_unit_property_list :param side1_fighter_list: side 1 fighter configuration. got from LoadMap.get_unit_property_list :param side2_detector_list: side 2 detector configuration. got from LoadMap.get_unit_property_list :param side2_fighter_list: side 2 fighter configuration. got from LoadMap.get_unit_property_list :param max_step: max step,0:unlimited :param render: display enable control, True: enable display, False: disable display :param render_interval: display interval, skip how many steps to display a frame :param random_pos: start location initial method. False: side 1 on right, side2 on left. True: random position on top, bottom, right and left) :param log: log control,False:disable log,other value:the folder name of log. :param random_seed: random digit,-1:generate a new one,other value:use an exist random digit value """ # load map self.map = Map(map_path) self.size_x, self.size_y = self.map.get_map_size() self.side1_detector_num, self.side1_fighter_num, self.side2_detector_num, self.side2_fighter_num = self.map.get_unit_num() # make env self.side1_detector_list, self.side1_fighter_list, self.side2_detector_list, self.side2_fighter_list = self.map.get_unit_property_list() self.env = BattleField(self.size_x, self.size_y, self.side1_detector_list, self.side1_fighter_list, self.side2_detector_list, self.side2_fighter_list, max_step, render, render_interval, random_pos, log, random_seed) # for item in dir(Render): # print(item) # return # print(help(Render.draw_fight_rela)) # return # import obs construct class if 'raw' == side1_obs_ind: self.side1_obs_path = 'raw' else: self.side1_obs_path = 'obs_construct.' + side1_obs_ind + '.construct' self.agent1_obs_module = importlib.import_module(self.side1_obs_path) self.agent1_obs = self.agent1_obs_module.ObsConstruct(self.size_x, self.size_y, self.side1_detector_num, self.side1_fighter_num) if 'raw' == side2_obs_ind: self.side2_obs_path = 'raw' else: self.side2_obs_path = 'obs_construct.' + side2_obs_ind + '.construct' self.agent2_obs_module = importlib.import_module(self.side2_obs_path) self.agent2_obs = self.agent2_obs_module.ObsConstruct(self.size_x, self.size_y, self.side2_detector_num, self.side2_fighter_num) def get_done(self): """ Get done :return: done: True, False """ # TODO: modified return self.env.get_done() def get_obs(self): """ Get image-based observation :return: side1_obs :return: side2_obs """ side1_obs_raw_dict, side2_obs_raw_dict = self.get_obs_raw() if 'raw' == self.side1_obs_path: side1_obs = side1_obs_raw_dict else: side1_obs = self.agent1_obs.obs_construct(side1_obs_raw_dict) if 'raw' == self.side2_obs_path: side2_obs = side2_obs_raw_dict else: side2_obs = self.agent2_obs.obs_construct(side2_obs_raw_dict) return side1_obs, side2_obs def get_obs_raw(self): """ Get raw data observation :return: side1_detector_data :return: side1_fighter_data :return: side2_detector_data :return: side2_fighter_data detector obs:{'id':id, 'alive': alive status, 'pos_x': horizontal coordinate, 'pos_y': vertical coordinate, 'course': course, 'r_iswork': radar enable status, 'r_fre_point': radar frequency point, 'r_visible_list': radar visible enemy} fighter obs:{'id':id, 'alive': alive status, 'pos_x': horizontal coordinate, 'pos_y': vertical coordinate, 'course': course, 'r_iswork': radar enable status, 'r_fre_point': radar frequency point, 'r_visible_list': radar visible enemy, 'j_iswork': jammer enable status, 'j_fre_point': jammer frequency point, 'j_recv_list': jammer received enemy, 'l_missile_left': long range missile left, 's_missile_left': short range missile left} """ side1_obs_dict = {} side2_obs_dict = {} side1_detector_data_obs_list, side1_fighter_data_obs_list, side1_joint_data_obs_dict, \ side2_detector_data_obs_list, side2_fighter_data_obs_list, side2_joint_data_obs_dict = self.env.get_obs_raw() side1_obs_dict.update({'detector_obs_list': side1_detector_data_obs_list}) side1_obs_dict.update({'fighter_obs_list': side1_fighter_data_obs_list}) side1_obs_dict.update({'joint_obs_dict': side1_joint_data_obs_dict}) side2_obs_dict.update({'detector_obs_list': side2_detector_data_obs_list}) side2_obs_dict.update({'fighter_obs_list': side2_fighter_data_obs_list}) side2_obs_dict.update({'joint_obs_dict': side2_joint_data_obs_dict}) return side1_obs_dict, side2_obs_dict def get_alive_status(self,side1_detector_obs_raw_list,side1_fighter_obs_raw_list,side2_detector_obs_raw_list,side2_fighter_obs_raw_list): return self.env.get_alive_status(side1_detector_obs_raw_list,side1_fighter_obs_raw_list,side2_detector_obs_raw_list,side2_fighter_obs_raw_list) def get_reward(self): """ get reward :return:side1_detector:side1 detector reward,side1_fighter:side1 fighter reward,side1_round: side1 round reward, side2_detector:side2 detector reward,side2_fighter:side2 fighter reward,side2_round: side1 round reward """ return self.env.get_reward() def reset(self): """ Reset environment :return: none """ self.env.reset() def step(self, side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action): """ Run a step :param side1_detector_action: Numpy ndarray [detector_quantity, 2] :param side1_fighter_action: Numpy ndarray [fighter_quantity, 4] :param side2_detector_action: Numpy ndarray [detector_quantity, 2] :param side2_fighter_action: Numpy ndarray [fighter_quantity, 4] :return: True, run succeed, False, run Failed """ return self.env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) def get_map_size(self): """ Get map size :return: size_x: horizontal size :return: size_y: vertical size """ return self.map.get_map_size() def get_unit_num(self): """ Get unit number :return: side1_detector_num :return: side1_fighter_num :return: side2_detector_num :return: side2_fighter_num """ return self.map.get_unit_num() def get_unit_property_list(self): """ Get unit config information :return: side1_detector_list, should be directly forward to Environment init interface :return: side1_fighter_list, should be directly forward to Environment init interface :return: side2_detector_list, should be directly forward to Environment init interface :return: side2_fighter_list, should be directly forward to Environment init interface """ return self.map.get_unit_property_list() def set_surrender(self, side): ''' surrender :param side: side1: 0, side2: 1 :return: ''' return self.env.set_surrender(side) class PlayBack: """ Replay """ def __init__(self, log_name, display_delay_time=0): """ Initial replay class :param log_name: :param display_delay_time: """ self.rp = Replay(log_name, display_delay_time) def start(self): """ Replay begin """ self.rp.start() # Utilities def get_distance(a_x, a_y, b_x, b_y): """ Get distance between two coordinates :param a_x: point a horizontal coordinate :param a_y: point a vertical coordinate :param b_x: point b horizontal coordinate :param b_y: point b vertical coordinate :return: distance value """ return position_calc.get_distance(a_x, a_y, b_x, b_y) def angle_cal(o_x, o_y, e_x, e_y): """ Get a direction (angle) from a point to another point. :param o_x: starting point horizontal coordinate :param o_y: starting point vertical coordinate :param e_x: end point horizontal coordinate :param e_y: end point vertical coordinate :return: angle value """ return position_calc.angle_cal(o_x, o_y, e_x, e_y) ================================================ FILE: environment/license.lic ================================================ FCpGTEFHUzoBKkNPREU6cHJvLTAyZ5BCrprWffXaRJeFaCw1aihFNyaQ/BLeHa7NWyCHrDf43ud9WKhQVbrO3PWhLjHKZEZE9FkBbDV440VAFJ+knAcuTEk5zc3I2eS0qAeZUKjiIb4i91gy9kKIfgyqOIg4cXwgasxk3icpOEj9B/yzhnDMXeZn4WUQGOKIbslIdcM= ================================================ FILE: environment/product.key ================================================ Bې?CAk8a6W H0vY y  }q#fRq+JBwzʱnƐS*TE`wN(RGy0tD&y P:Cq\8SBV] ================================================ FILE: environment/pytransform.py ================================================ # Because ctypes is new from Python 2.5, so pytransform doesn't work # before Python 2.5 # from ctypes import cdll, c_char, c_char_p, c_int, c_void_p, \ pythonapi, py_object, PYFUNCTYPE from ctypes.util import find_library import os import sys import platform import struct # # Global # _pytransform = None _get_error_msg = None # # Options # # How to show error message return from dynamic library _pytransform. # # Each time call a dll function, if something is wrong, _get_error_msg # will return the reason. # # Enable _verbose_mode will print the details got from _get_error_msg. # Disable this options will print only a short message. _verbose_mode = 1 # In debug mode, print trace stack when raise exception. # Otherwise only show a short message. # # Run python with command option "-d", or set environment variable # PYTHONDEBUG. For example # # python -d pyarmor.py xxxx # _debug_mode = sys.flags.debug class PytransformError(Exception): def __init__(self, *args, **kwargs): super(Exception, self).__init__(*args, **kwargs) if _debug_mode: self._print_stack() @classmethod def _print_stack(self): try: from traceback import print_stack except Exception: _debug_mode = 0 sys.stderr.write('Disabled debug mode.\n') else: print_stack() def dllmethod(func): def format_message(msg, *args, **kwargs): if _verbose_mode: s1 = str(args)[:-1] s2 = ', '.join(['%s=%s' % (k, repr(v)) for k, v in kwargs.items()]) sep = ', ' if (s1 and s2) else '' line = 'Call with arguments: %s%s%s)' % (s1, sep, s2) return msg return msg.split('\n')[-1] def wrap(*args, **kwargs): args = [(s.encode() if isinstance(s, str) else s) for s in args] result = func(*args, **kwargs) errmsg = _get_error_msg() if errmsg: raise PytransformError(format_message(errmsg, *args, **kwargs)) return result return wrap @dllmethod def init_pytransform(): major, minor = sys.version_info[0:2] # Python2.5 no sys.maxsize but sys.maxint # bitness = 64 if sys.maxsize > 2**32 else 32 prototype = PYFUNCTYPE(c_int, c_int, c_int, c_void_p) init_module = prototype(('init_module', _pytransform)) init_module(major, minor, pythonapi._handle) @dllmethod def init_runtime(systrace=0, sysprofile=1, threadtrace=0, threadprofile=1): pyarmor_init() prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int) _init_runtime = prototype(('init_runtime', _pytransform)) _init_runtime(systrace, sysprofile, threadtrace, threadprofile) @dllmethod def import_module(modname, filename): global _import_module if _import_module is None: prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) _import_module = prototype(('import_module', _pytransform)) return _import_module(modname, filename) _import_module = None @dllmethod def exec_file(filename): global _exec_file if _exec_file is None: prototype = PYFUNCTYPE(c_int, c_char_p) _exec_file = prototype(('exec_file', _pytransform)) return _exec_file(filename) _exec_file = None @dllmethod def encrypt_project_files(proname, filelist, mode=0): prototype = PYFUNCTYPE(c_int, c_char_p, py_object, c_int) dlfunc = prototype(('encrypt_project_files', _pytransform)) return dlfunc(proname, filelist, mode) @dllmethod def encrypt_files(key, filelist, mode=0): t_key = c_char * 32 prototype = PYFUNCTYPE(c_int, t_key, py_object, c_int) dlfunc = prototype(('encrypt_files', _pytransform)) return dlfunc(t_key(*key), filelist, mode) def generate_project_capsule(licfile): prikey, pubkey, prolic = _generate_project_capsule() capkey = _encode_capsule_key_file(licfile) return prikey, pubkey, capkey, prolic @dllmethod def _generate_project_capsule(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('generate_project_capsule', _pytransform)) return dlfunc() @dllmethod def _encode_capsule_key_file(licfile): prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p) dlfunc = prototype(('encode_capsule_key_file', _pytransform)) return dlfunc(licfile, None) @dllmethod def generate_module_key(pubname, key): t_key = c_char * 32 prototype = PYFUNCTYPE(py_object, c_char_p, t_key, c_char_p) dlfunc = prototype(('generate_module_key', _pytransform)) return dlfunc(pubname, t_key(*key), None) @dllmethod def generate_license_file(filename, priname, rcode, start=-1, count=1): prototype = PYFUNCTYPE(c_int, c_char_p, c_char_p, c_char_p, c_int, c_int) dlfunc = prototype(('generate_project_license_files', _pytransform)) return dlfunc(filename, priname, rcode, start, count) @dllmethod def get_registration_code(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_registration_code', _pytransform)) return dlfunc() @dllmethod def get_expired_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_expired_days', _pytransform)) return dlfunc() @dllmethod def get_trial_days(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('get_trial_days', _pytransform)) return dlfunc() @dllmethod def version_info(): prototype = PYFUNCTYPE(py_object) dlfunc = prototype(('version_info', _pytransform)) return dlfunc() def get_hd_sn(): size = 256 t_sn = c_char * size sn = t_sn() if (_pytransform.get_hd_sn(sn, size) == -1): return '' return sn.value.decode() def show_hd_info(): return _pytransform.show_hd_info() def get_license_info(): info = { 'expired': 'Never', 'restrict_mode': 'Enabled', 'HARDDISK': 'Any', 'IFMAC': 'Any', 'IFIPV4': 'Any', 'DOMAIN': 'Any', 'CODE': '', } rcode = get_registration_code().decode() index = 0 if rcode.startswith('*TIME:'): from time import ctime index = rcode.find('\n') info['expired'] = ctime(float(rcode[6:index])) index += 1 if rcode[index:].startswith('*FLAGS:'): info['restrict_mode'] = 'Disabled' index += len('*FLAGS:') + 1 prev = None start = index for k in ['HARDDISK', 'IFMAC', 'IFIPV4', 'DOMAIN', 'FIXKEY', 'CODE']: index = rcode.find('*%s:' % k) if index > -1: if prev is not None: info[prev] = rcode[start:index] prev = k start = index + len(k) + 2 info['CODE'] = rcode[start:] return info # Load _pytransform library def _load_library(path=None): if path is None: path = os.path.dirname(__file__) plat = platform.system().lower() bitness = struct.calcsize('P'.encode()) * 8 libpath = os.path.join(path, 'platforms', '%s%s' % (plat, bitness)) if not os.path.isdir(libpath): libpath = path try: if plat == 'linux': if libpath == '': m = cdll.LoadLibrary(os.path.abspath('_pytransform.so')) else: m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) m.set_option('libc'.encode(), find_library('c').encode()) elif plat == 'darwin': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dylib')) elif plat == 'windows': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.dll')) elif plat == 'freebsd': m = cdll.LoadLibrary(os.path.join(libpath, '_pytransform.so')) else: raise RuntimeError('Platform not supported') except Exception: raise PytransformError('Could not load _pytransform from "%s"', libpath) # Required from Python3.6 m.set_option('byteorder'.encode(), sys.byteorder.encode()) # m.set_option('enable_trace_log'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('enable_encrypt_generator'.encode(), c_char_p(1)) # # Deprecated from v3.4 # m.set_option('disable_obfmode_encrypt'.encode(), c_char_p(1)) if not os.path.abspath('.') == os.path.abspath(path): m.set_option('pyshield_path'.encode(), path.encode()) return m def pyarmor_init(path=None): global _pytransform global _get_error_msg if _pytransform is None: _pytransform = _load_library(path) _get_error_msg = _pytransform.get_error_msg _get_error_msg.restype = c_char_p init_pytransform() def pyarmor_runtime(path=None): pyarmor_init(path) init_runtime(0, 0, 0, 0) ================================================ FILE: environment/render/__init__.py ================================================ __pyarmor__(__name__, __file__, b'\xec\x50\x8c\x64\x26\x42\xd6\x01\x10\x54\xca\x9c\xb6\x36\x84\x05\x6a\x0b\xf3\xc4\x16\x01\xb0\x49\xdd\x1c\xf4\xf8\x5a\xe1\xe0\xc4\xbe\x1b\x41\x5d\x2c\x21\x77\xb1\x53\x58\xd9\xeb\x22\xb3\x4c\x09\x12\xdb\x19\xce\x41\x3b\xeb\xb0\x0d\x17\x26\x12\xf9\x51\x94\x9e\xf1\xf5\x85\x77\xd0\x70\xdf\xce\x84\x05\x76\x90\x38\xf1\x10\xd7\x44\x53\x87\x32\x78\xa3\x65\xcd\x08\xcf\x45\x20\x53\x90\x31\x2c\x73\xa3\x04\x11\xfb\x6f\xf4\x36\xd1\x62\xd3\x8e\xcd\x56\x1e\xfa\x95\xb6\xcf\x95\xbc\x7b\x99\x14\x01\xd1\x68\x33\xcd\x33\xf0\x81\xb8\xc9\xea\x5c\x41\x40\x4e\xa4\xc6\x33\x22\x8b\xbe\x9f\x5e\x52\x39\x81\x68\x68\x4e\x6b\x3e\x50\xfa\xd3\xe2\x15', 1) ================================================ FILE: environment/render/render_pic.py ================================================ __pyarmor__(__name__, __file__, b'\xe7\x50\x8c\x64\x26\x42\xd6\x01\xb9\x4e\x3f\xdf\x40\x9b\x76\xef\x72\x6c\x30\x53\x5b\x51\x12\xe5\x74\x7d\x05\x68\xb6\x2a\x70\xff\x5a\x6c\xe7\x13\x67\xad\x90\xbb\xcf\x70\x39\x65\x6e\xea\x70\xef\xf9\xcc\xa6\x0c\xbc\x59\xd1\x77\xa1\x9a\x9a\xb6\x68\xa1\x4b\x30\x9d\x0c\xa4\x01\xb7\xfc\x29\xe8\x56\xe1\x9c\x8d\x63\x89\x24\xd9\x30\x14\xfd\x61\x5e\x83\x04\x18\xd3\x5d\xcd\xcc\x5c\x89\xe0\x07\xd2\xcb\xfe\x13\x0a\x78\x56\x02\x8a\x04\x02\x6a\x09\xef\x28\x22\xbb\x60\xf5\x30\xc8\x17\x27\xb2\xa2\xa9\x45\x38\x34\x79\x7c\xae\x0f\xf8\xce\x4f\x4b\x60\xaf\xcb\x51\xc7\xfc\xfb\xb9\xd5\xe6\x91\x62\xac\xd3\x5c\x55\xc3\xef\xbb\xd7\xe4\xe2\xac\xff\x0d\xf5\x8f\xac\xf6\xe9\x1c\x20\xbd\xa5\x8a\x81\x12\x3c\x4e\xb9\x90\xd7\x7e\xf8\xaf\x2b\xe5\xce\x65\x83\xd1\xe2\x99\xcb\x02\x83\x95\xfe\x0b\xa0\x16\x3d\x75\x65\xfb\x34\x5b\x6d\x04\xfa\xd5\x5b\x41\x7e\xe8\x91\x58\xe1\xe1\xd6\xbd\xa4\x6e\x64\x28\x93\x8c\x95\x49\xe4\x32\x1f\xf1\x96\x9e\x70\x58\x34\x18\x1a\x22\x24\x53\xd8\xdf\x59\x8f\x93\x37\x83\x40\xf9\x4c\x2b\x69\xad\x37\xfc\x92\xc9\x45\x3e\x5a\x13\x8f\xbd\x36\xd7\xdf\x8a\xa3\x1c\xdf\x4f\x36\xde\xfa\x23\x33\x36\x92\x40\xef\xdc\xf5\x15\x15\x6e\x35\x81\x54\x5a\x63\xbc\x92\xc8\x8f\x0e\x7f\x9d\x09\x73\x26\x51\xb9\x71\x34\xff\x1b\x58\x31\xdb\x2a\x5c\x4f\xab\xb7\x7a\xcc\x5d\x58\xca\xd4\x7d\x6d\x7d\x1d\x89\x0b\x1a\x41\x76\xed\x90\x0d\xd5\x93\x39\x9a\xa9\x74\x79\x25\x3b\x09\x39\x8b\xff\xc8\x3a\x60\x5b\x64\xa3\xb6\x31\x67\x31\x54\x14\xb2\x7d\x20\x11\x25\x1f\x6d\x0d\x5c\x7b\x59\xcc\x86\x94\xba\x11\x77\xbb\x37\xe7\xa6\xde\x89\x28\x0c\xb3\x4f\xd0\x6e\x84\x3c\x5b\xbd\x9c\x4b\x4d\x36\x1b\x92\x9e\x9d\x08\x8f\x40\x48\x86\x5b\x07\x2b\x1f\x0b\x1c\x68\xa1\xd1\xd4\x96\x90\xf0\x46\x51\x1d\x07\x82\x23\x5f\x23\x4b\xce\x28\x0e\xd2\x49\x87\x6e\xa5\x7e\xc3\x9d\x5f\x48\x44\x86\xdf\xae\x6a\xf1\xc4\x34\x48\xcc\xcc\x42\xc1\x13\x56\x4b\x08\x4c\xe4\x35\x1e\x65\x74\xc8\xa2\x68\x48\x99\xe3\x71\x9a\xa5\xb8\x5e\x02\x96\x8d\xdf\x01\x1e\x43\x9f\x56\xe0\x6a\xdc\xfb\x2a\x64\xde\x02\xea\xc9\xd0\xa3\x1a\xb0\x2a\x85\x14\x49\x6b\x99\x74\xef\xee\xac\x1b\x49\x63\xe9\x6a\x7d\x6e\xea\x70\x59\xef\x6a\x5d\x5c\x5c\x4b\xe9\xb9\xe4\xd8\xc7\x1a\x23\x4a\x54\xf3\x59\x0f\xb8\x0c\xe7\x8d\x72\x37\x16\x1b\xe0\x17\x43\x80\xf9\xb1\x72\x8b\x01\xf7\x1f\x34\x2c\x2a\x09\x47\xef\x49\xbd\xa1\x76\x1c\xd8\x3b\x85\xc0\x41\xf6\x5c\xaf\xf7\x07\x03\xd8\x05\xad\x2d\xb8\x14\x73\x7f\xe8\xb8\xab\x2e\x32\x01\xc8\xc6\xbf\xa1\xf5\xae\xf5\xb1\x47\x80\xe4\xb2\xbd\x7e\xd1\xb0\x7a\x42\x9a\xd3\x46\x26\x4a\x24\xcb\xb9\x8d\xfa\x46\x96\x27\x49\x57\x16\x35\x63\xfe\x90\xe6\x0f\x7e\x6b\x83\x61\x2f\xe9\x03\xd1\x08\x83\x9a\x39\x55\xd0\x62\x17\x6f\x38\x1f\x77\x10\x4f\xe1\x0f\x31\xae\x4e\xa6\x39\x06\xf8\xc9\x46\xca\xfc\x1f\x08\xec\x9f\xc3\xfd\xe6\x62\x64\xff\xae\x5f\x5e\x2a\x64\xbb\x35\x5d\x2f\x4c\x7c\xde\x9f\x05\xd5\x39\xd6\x38\x04\xdb\x03\xe5\xe7\x91\xc6\x4e\xa0\x1d\x14\x83\x74\xee\x0e\xfd\x07\xba\xc6\x15\xf7\x43\xf9\xf5\xfc\x3f\xec\x5c\x94\x6a\x01\xfc\x4d\x10\x9c\xab\x17\xc3\x46\xc3\xf9\x58\x83\x58\x5c\x19\x6a\x4d\xcc\xd1\x4c\x36\x9c\xc4\x7f\xc8\x1e\x81\x38\x25\x1f\x55\x8c\x6f\x20\x26\x29\x8a\x6b\x1e\xab\x71\x88\x33\x6a\x4e\xdd\x4f\xb9\x67\xe7\x49\xcf\xe6\xb2\x9d\x26\xad\xab\x00\x0f\x3b\x44\xab\xc3\x98\x5f\xbc\x5c\xbb\x7e\x25\xa0\xf9\x13\x88\xdf\xc7\x4f\xfa\xcd\x96\xfd\x0d\x12\x9f\x6f\x56\xe2\x59\x3b\x5a\x0a\xf8\xf6\xa6\xbc\xcd\x01\x95\x41\x2e\xa9\x7b\x5c\x05\x7b\xdf\x52\x22\xc8\x7d\xe4\xb2\x41\x0e\x38\xd7\x9f\x81\x63\xf9\x04\xba\x5e\xa4\xe3\x47\xbe\xe5\x25\x9f\x31\xb9\x24\xaa\x34\xd6\x81\xca\xba\xbf\x8b\x8c\x02\x18\xe6\xa1\x59\xbe\xea\x89\xa3\x13\x6b\x6c\xb9\x20\xcc\x58\x87\x12\x74\xe0\x75\x10\x5d\xfd\x31\x6f\x7c\xb6\xc7\xf0\x38\xc7\x9c\x70\x33\xd0\x56\xd8\x2e\x03\x45\x80\xdb\x08\x4d\xc2\x6b\xd0\x90\x95\x0b\xd8\x3f\xb7\x27\x24\xc6\x46\x93\x35\xb8\xa9\x69\x24\x86\xd3\xa0\x11\xb2\x22\xee\xbb\x3f\xa9\x28\x8e\xc9\x2a\x3d\xea\xe7\x6a\x9e\xcc\xc2\x92\x88\xdb\x8d\x32\x91\x22\x16\x79\xb6\x80\x97\x30\x34\x57\x3d\xe3\x73\xa3\x10\x4b\xb6\xb7\x99\xc8\x60\xb8\x8b\x22\x2f\xf2\xd4\xcc\xc5\xa4\xa5\x8c\x49\xf9\xed\xef\x5d\xfa\x77\xa3\xfd\x1c\x4a\xe9\x2a\x49\xea\x7d\xdd\x8d\x3a\x36\x39\x48\x25\x93\xd2\x7b\x7f\x14\x55\x59\x60\x13\x28\x05\x4a\xa1\x85\x69\xe2\x91\x70\x74\x22\x71\x24\x40\x28\xc5\xf9\x11\x74\xf8\x88\x51\x4c\x29\xa0\xcb\x15\xb8\x99\xcc\x9e\xa2\xb4\xec\x2c\x25\x0a\xee\xbd\x61\xbd\x33\x04\x27\xab\xaa\xcb\x08\x1e\x87\x77\x13\x78\xc7\x22\x6a\xae\x9d\x3b\xe6\xcb\x78\x9e\x59\xff\x77\x1c\xf5\xf2\x69\x02\x7d\xfa\xdc\x86\x13\x4d\x18\x3c\x4e\x04\x97\x81\x7c\x41\xe0\x67\x36\xe5\x57\xcb\x36\x51\xeb\xd3\x93\x48\x18\x39\xb9\x9a\xc8\x7e\xac\x54\x0c\x46\xc7\xd6\x2e\x00\xb0\x41\x0f\x59\x50\x8f\xc5\xbe\x50\x8a\x2f\x7e\x79\x1f\x06\x35\x6f\x3f\xd1\x17\x0f\x6c\xe3\x58\x34\x63\x29\x80\x0f\xa6\x43\xe0\x3c\x21\x59\x50\x34\xc9\xab\x94\x3a\x92\x12\x81\x4e\xbc\x0e\x2e\x52\xa6\xc8\x0d\x2e\xb3\x0a\xf5\xee\xe5\x6b\x9b\x71\x62\x04\x11\x4e\x55\xe4\xd5\x6c\xdc\xf9\x9d\xfe\xac\x2a\xa8\x22\x71\x7e\xca\xd6\x3f\xe0\x11\xd3\x43\x0f\xb1\xd9\xb4\x3f\x89\x4c\x15\x74\x94\xf0\x39\x11\x62\x2e\x2d\x8e\x68\xf9\x0b\x0f\x43\x3f\x89\x48\xb1\x43\x2e\xdc\xdf\xb2\x20\x3e\x8b\xcf\xaf\x8e\xaf\x1b\x31\xc6\x58\x61\x5c\x06\x81\xfe\x19\xe3\x22\x53\x1c\x0f\x6d\x14\x34\x51\xd3\x73\xb7\xd2\x9b\xc9\xf8\xc2\xc0\xfb\x7a\xd1\x25\x46\x18\xd1\xea\x4f\xe3\xf5\xfc\xd5\xe6\xb7\xcf\x52\x11\xd2\x17\xc5\x05\x29\xc4\xf9\x06\x3b\x12\x71\xf2\x19\xcc\x4c\xca\xa4\x9c\x9b\xd4\x20\xc5\xfe\x9f\x9f\x2b\x02\x2b\xbf\x1d\x86\x58\xfb\x27\x20\xe6\x45\xba\x7c\xe1\xc4\xa5\x3d\x33\xa2\xea\x2a\xa3\xd5\x0c\xfd\x0d\xbe\xd2\x00\xef\x5b\xe3\x36\xb1\x3d\xd8\x38\x62\x6e\xb9\xcb\x49\x67\xf0\x10\x01\xdb\x67\x30\xfa\xbf\xa6\xea\x15\x2f\x65\x70\x0b\x35\xe7\x7b\x19\x21\xaf\xcc\x76\x97\xf8\x5e\xd1\xd7\x0f\xe1\x6b\x61\x31\xb5\x08\x98\x35\x3d\x0f\x11\xec\xed\x6d\x89\x7e\xa3\x8a\x87\x09\x87\xe9\x8a\x03\xb7\xde\x89\x70\x39\x22\x77\xd9\x7e\xd3\xef\x91\xb5\x81\x62\xa6\x5d\xf3\x74\x97\x37\x40\xc7\xad\x0a\x73\x2a\x88\x76\x6f\xd5\x94\x00\xef\x1e\x41\x3a\x07\xf0\x28\xa2\x27\xdb\x79\xf8\xe5\xa1\xf7\xb6\x91\x48\xf4\x95\xc6\xd4\x4e\x65\x8b\x0a\x47\x23\x8a\xc2\xd7\x32\xe4\x02\x74\x44\x36\xad\xf0\xd5\x8b\xed\xbe\xe3\x5b\xbb\x13\x07\x11\x35\x52\xe2\xd7\x20\x01\x80\x54\xd1\x25\x13\x2c\x9b\x86\x3f\xc3\xbe\xd5\xac\x11\xf5\xf4\xb9\xd1\x53\x62\x7f\x4f\xf5\x62\xe0\xec\x8f\x81\xa8\xf2\xbe\xb4\x54\xbf\x7b\xbd\xcd\xb6\xb4\x94\x21\x94\xbe\xd1\xff\xf9\x37\x25\xfd\xe3\xce\x14\xb2\x1d\x96\x55\x89\x55\x5e\x75\xba\xfe\x08\x44\xc5\x06\xa5\xd5\x5e\x86\x14\x6a\xfe\x08\x47\xab\x31\x4f\x05\xab\x28\x1c\x7e\xdd\x78\x7a\xb2\x81\x27\x18\xf5\xdb\x45\x73\x1b\x69\xec\x86\x6c\x17\xe6\x2c\xde\x70\x97\x79\x2b\xa6\x6a\x05\xd8\x5f\x31\x61\x8d\xa4\x6f\x1b\x17\x49\xe5\x9f\x2c\x03\x14\xd6\x9b\x6f\xb9\x50\xfd\x2e\x18\x2d\xbf\x2f\xec\xd6\xd8\xc3\x87\x1d\x35\x9f\x1f\x43\x80\xdd\x28\xe0\x71\x4b\xd4\x72\xe1\x45\x62\x9c\xa5\x27\x03\xc2\xbe\x27\xed\x47\xc9\xd6\xe4\x18\x65\x08\xe4\x53\x51\x38\x40\xb3\xb2\x2e\x10\xd2\xe7\x06\x53\xbe\xb4\xdb\x91\x2f\xa6\xb6\x01\xb1\xa9\x27\xf0\xa6\xb9\x7c\x06\x32\xf6\xb1\x4f\x78\x4f\x31\xcc\x95\xbc\x62\x44\xb4\x75\x00\x3d\xfb\xbb\xfd\x17\x6f\x3b\x91\x29\xc6\xf4\x05\x0b\x52\xf2\xc3\xfe\x89\x8b\xde\x7e\x06\xb2\x09\x8d\x63\xcd\x6f\x8f\x66\x33\x25\x55\x18\xa7\xc9\xd4\xb5\xba\x62\x33\xbf\xf1\x84\x76\x98\x08\xae\x06\x1a\x56\x42\xd6\x83\xeb\x12\xd5\x1c\x02\xa3\xd6\x22\xb9\x47\x44\x41\x23\x93\xa7\xbe\x73\xa5\x90\x6f\xce\x56\x90\x27\x2a\x65\xda\x63\xff\x0d\xc8\x4f\xa5\xb1\xd1\x96\x84\xe4\x31\xbe\xfb\x4d\x16\x34\x3c\xdc\x47\x93\x84\x83\x89\xc5\x88\x85\x46\x5f\x50\xb5\xee\x1c\x8d\x8c\x30\x3c\x20\x61\x23\xd1\x1b\x39\x4f\xe1\xfd\x81\x8d\x4a\xe2\x33\x59\x68\x58\x90\x28\x64\x3a\xd6\x76\xb0\xf3\x04\x93\x8b\xb5\x99\xcb\xa7\xba\x38\x5b\x62\x39\xda\x5b\x79\x3e\x44\x9e\x5c\xff\xb7\xa7\x51\x69\xf1\x27\x5e\x0a\xa4\xf5\x8b\x4f\xe5\xb3\xe8\xfd\x1b\x95\x4d\x68\xc2\xb2\x8a\xe6\x28\xad\x32\xe6\x08\x58\xc4\x3c\xc5\x76\x51\xb5\xac\xf8\xc7\xf9\xfa\x4b\xe9\xc1\xc0\x72\x0d\x88\xd4\xf3\xc9\x5e\xe0\x2a\x64\x50\xaa\xa8\x5c\xe4\xa5\x63\xeb\x09\xb1\xe7\x5d\x0c\xcf\x07\x7b\x26\x42\xfc\xff\x54\xb8\xdf\x08\xa4\x4f\x87\x27\x24\xbf\x20\x8e\x9b\xd1\xb1\xfd\x49\x26\x6b\x89\x9f\x60\xec\x12\xf3\x30\xa3\x95\xb9\xc9\xcc\x54\xc8\x06\xe6\xda\x7c\x40\x27\x4e\x73\x3c\xb3\x1d\x70\x04\xd1\xba\x5b\x7c\xa1\x4c\x01\xe9\x23\xb6\xfc\x86\x8d\xe4\x93\xab\xea\xd0\xee\xa1\xf1\xaa\xef\x1c\x00\xe2\x05\x9d\xdb\xfa\xa3\xda\x1c\x60\xcd\xcc\x7d\x6c\x2c\x95\x94\x5d\x79\x2d\xdf\x58\x87\x95\x04\x89\x2c\x47\x8c\x28\xaa\x7c\x40\xc4\xfe\x36\x0b\xdf\xb8\x0a\xfe\x1a\x1f\x03\x6e\xde\xbe\xc7\x75\x6d\xf9\xbc\xa7\x10\x74\x70\xc4\xf7\x66\xac\x1e\x9d\x8d\xda\xde\x36\x1b\xd4\x0f\x87\xdf\x90\x30\x56\x90\x21\xa9\xec\x01\x17\x44\xb6\xab\x7f\xad\xf5\xb5\xa0\x40\x8e\x44\x92\xbe\xff\xc6\x7c\x82\x8f\x9f\x46\x9a\xc9\xd1\x50\x6c\xd9\x4c\xeb\x99\x06\xf2\x30\x9a\x7e\xcc\xf0\x1d\xc3\x7c\x97\xfe\x0e\x5e\x63\xd6\x81\xbc\x7f\xca\xf6\xf4\x8e\x7f\x26\x95\x77\x05\x64\x42\x53\x39\x55\xc6\xd5\x59\xf1\x4c\xb0\xd0\xc4\x65\xa6\x54\x1a\xe3\xc1\x62\xec\xa2\xa9\x29\x34\x42\xa9\xfb\xf9\x99\x25\x5b\xef\xfe\x03\x31\x42\x03\xc1\x49\xc7\x6b\x0d\xb4\xf4\x7e\x94\x84\x9c\x45\x44\xcf\xcf\x16\x6b\x51\x46\x9a\xc6\x97\x8e\xfd\x33\x9d\xfd\x9b\xda\xc6\x01\x68\xd2\x09\x84\x6e\x61\x2a\x7b\xff\xda\x3e\x9a\x0b\x4a\x89\x79\x58\xbe\xa7\xc3\xcc\x42\x26\xca\x9d\x25\x6c\xd0\x33\xba\xfc\x22\x52\x48\xc3\x42\xf9\x80\x0f\x85\xe3\x89\xaa\xbd\x20\x72\x31\xe1\xdd\x6f\xa5\xc5\xa6\x52\x44\x99\xd7\x05\x35\xe6\x43\xaf\x1b\xed\x30\xea\xa3\x5c\xa4\xf0\x9c\x8e\x04\x9b\x6a\xc0\x56\xb0\x2d\x00\x6a\x86\x64\x43\xd0\xee\xd4\xdb\xa3\xa3\x42\x6c\x5b\xdf\x10\x81\xf9\xd9\x11\x08\x96\xe6\x9a\x61\x3f\x2a\x1e\xdb\xf6\x64\xeb\xa5\x51\xa5\xea\x00\x24\x1c\x1d\x64\x00\xdb\x77\x70\xa8\xc9\x29\xf0\x36\xf4\xc5\xe2\x61\xda\xab\x78\x9a\x27\x90\xc1\x70\xfa\xbb\xea\xb6\xe4\x0f\xbe\xbf\x60\x98\x96\x15\x2f\xd7\xfc\xb7\x8f\x70\x82\x2a\x6e\x4a\x52\xf5\xcb\x8d\xc3\x8c\xd1\xea\x21\x6f\x1a\xe8\xfa\x57\xec\x02\x73\x79\x10\x8c\x83\x02\x18\xf3\x0a\xb4\xf1\x09\xe6\x08\xa3\x10\x4e\x1b\xcb\x2b\xcd\x3f\x4c\xcd\xfe\x4b\xf0\x0c\xb3\x7f\x86\x43\xec\x07\xf3\x47\x8f\x50\x2e\xb8\x4b\x28\x0c\x20\x47\xf8\x0f\x1f\x36\x00\xb7\xd3\xde\xc4\xdd\x49\xed\x2c\x6b\x17\x79\xdc\xf5\xde\xa4\x5d\x37\xae\xb8\xa8\xb1\x01\x7b\x2d\xb8\x3f\xdd\x15\xe5\x20\x44\xd3\x4c\xc1\xed\x31\x43\x17\x27\xa0\x59\x45\x17\x7b\x04\xd6\x62\x6f\x6c\xc9\xd1\x56\x97\xd7\x7c\x3c\x74\x10\xfd\x38\xba\x41\xa2\xae\xfb\xf0\x1d\xac\x73\x54\x44\xb5\x20\x79\x81\xa9\x05\x63\x3e\x2a\xbe\x35\x3e\x93\x56\x8d\x24\xbf\x3c\x80\x21\xf7\xda\xd8\x84\x51\x8c\xa6\x8c\xd8\x29\x66\xea\x94\xcb\x8a\xc2\x3c\x49\x12\x6a\x78\xce\x45\xa5\x82\xd6\x39\xa3\x69\x4e\x70\xdf\xb4\xd0\x6a\xd7\x42\xaa\x80\x48\xa0\xd9\x1d\xd2\x63\x55\xa5\x07\x11\x57\x3f\xf7\x45\xf1\x72\x47\x5f\x0f\x0f\xdf\xa7\x0f\xc0\x29\x14\xf9\xfd\x37\xc2\x65\x5e\x68\xc6\xe2\xdb\x7e\xb7\x37\xf2\x69\x90\x4c\xb2\xe7\x13\x4e\xc3\xa4\x36\xdd\x98\x80\x62\x20\xb1\x37\xb1\x01\x91\x2a\xa5\xb2\x51\x85\x51\xf2\xfe\x59\x45\xce\x48\x1d\xb0\xb7\x66\x97\x6a\xbd\x02\x0f\x33\x41\x6a\x67\x33\xf8\x01\xe9\x75\x7b\xbd\x94\x2f\x7e\x9a\x28\x97\xdc\x41\x4b\x29\xc5\xc8\xa8\x3a\x0f\xb0\xae\xdb\xcb\xd9\x51\x44\x89\x18\xc1\x9d\x99\x9c\x85\x06\x17\xa4\x38\x53\xb3\x95\x39\xf8\x74\x40\x57\xa0\x3f\xc6\x33\x95\x03\x96\x9e\x32\xd5\xe8\x1a\xd8\x7e\x01\xf7\x50\x31\x97\x08\xe4\x5b\x39\xdd\x02\x32\x50\x1b\xaa\x9f\xe1\x60\xb2\x06\x8a\x74\x01\xd5\xae\xa2\xe8\xb3\x37\xa9\x61\x81\xd5\x77\xd9\xe0\x7e\xc4\x13\x1d\x55\x8d\x38\xad\x65\xdd\x8f\x31\xcd\x64\x02\x5d\xf1\xf9\x32\x47\x31\x60\x24\xcf\x4e\x1d\xbd\x06\xc3\x31\xac\x00\xff\x90\xc9\x58\x46\x0d\xc4\xba\x4b\x5b\x63\x12\xeb\xc9\x95\x87\x2d\x60\xda\xcc\xb9\xf5\xd1\x17\xdd\x77\xc8\x2e\x74\x9e\x28\x34\x36\x2f\x63\x68\x36\x72\x50\xe7\x97\xf7\xfb\x7c\xe8\x40\xdb\x62\xff\x9f\xd1\xb4\x23\xe1\x84\x66\x70\x3b\x1f\x0a\xba\x11\x6e\xc1\x69\x26\x60\x8b\xa8\x1a\x55\xfe\x26\x45\x98\x8b\xa3\x27\xf9\xe2\x82\xa2\x0c\x1d\x28\x9b\xae\xc9\xca\xbc\xdd\x9e\x73\x5f\x73\xd9\x0f\x57\x62\x2e\x12\x22\xfc\x3b\x6b\x73\x40\x12\x64\x53\x76\x8e\x0b\xf0\x04\x45\x08\x0a\x4a\x9d\x56\x11\x5a\x05\x8d\x13\x2f\xcc\xa4\x58\xe3\x99\x1d\xac\x9b\x2f\x04\xb2\x09\x23\xf5\xf7\xbf\xdd\x93\xac\x97\x76\xa5\x77\xc6\x7b\xac\x87\xb1\x23\x4d\xb5\x0b\xea\x20\x1c\x7e\x02\x16\x8a\x2d\xec\x9d\xe8\x55\xa0\x71\x1d\xda\x68\xa2\xfa\x88\x15\xdc\x31\x5b\xc7\x61\xb3\x93\xca\x18\x4c\x94\x2c\x7f\x76\x8b\xa2\x65\xef\x9b\x69\xde\x8a\x6a\xa3\xf3\x27\x7b\x00\x42\xab\x6b\xca\x4d\xf8\x7a\x66\xeb\x19\x37\x0d\x56\x33\x76\x74\x69\x0f\x2b\x10\x7a\x4f\xb3\x43\x12\xeb\x9b\xca\x12\x9f\xf8\x81\xb1\xed\x4d\xdd\xae\xb9\x35\x8f\xab\xf5\x20\xdb\x76\x97\xfe\xd9\xc4\x36\xd1\x92\x1a\xe3\xc6\xd2\x95\xdd\xb0\xe4\x82\x0f\x46\x5f\x52\xe1\x8a\x21\x6f\x77\xa4\x6d\x7c\xb4\x79\xc8\xb7\x53\x30\x2b\x11\xa3\x31\xd2\xfc\x70\xd2\xa0\x7d\xef\x55\x4f\x06\x3c\xf9\xd4\x78\xa9\x12\xf2\xc7\x98\x80\x18\x65\x20\xa5\x9c\xdb\x8b\x26\x38\x6d\xa0\x43\x48\x06\x26\x28\x18\x26\x6c\x28\xaf\xe6\x1b\xb7\xc3\x97\x7e\x9a\x4b\x5e\x1b\xa0\xb5\xe5\x99\xe2\x9f\xb9\x71\x2b\xf7\x0a\x85\x66\xb1\x17\xb9\xa8\x31\x5b\xb4\xd1\xcd\x02\xa0\x75\x7b\x46\xe1\xd2\xf1\x9a\xc1\xbd\x4d\x14\x0a\xa4\xaf\x48\x91\xf6\x65\x14\x5d\xb0\xa6\xf3\xb5\x3a\x15\x3a\x7c\x85\x39\x36\xe9\xe7\x43\x1d\xb3\xcf\xcc\xbc\x27\xf6\x24\x1d\x0b\x6f\x4c\x6e\x1a\x39\xb9\xe3\xeb\xc0\xd5\x41\xa7\xca\x85\x34\xbe\xc1\x1d\xbd\xf0\xb0\x45\x9a\x8a\x7d\x9e\xb3\x9b\xc4\x84\xbc\x99\x95\x78\x3d\xf0\xac\x7b\xb0\x3b\x6e\x22\x3e\xd2\x76\xe8\x96\xae\x6d\xd7\x7d\xfd\xfd\x60\x4d\x68\xee\x83\x52\x5b\x7b\xc3\x9a\xb9\x05\xee\xf7\xd5\x80\x49\x4a\xd8\xd4\x1e\x61\x80\x51\x29\x07\xfa\x83\x3f\xed\xef\xe2\x63\x4d\x20\x83\x68\xc1\x1c\xc5\x3d\xa5\x14\xd8\x10\x21\xab\x34\xf5\x52\x2b\xcb\x80\x21\xba\x07\xb5\x31\xd5\x36\xca\xf3\xcd\x85\xf3\x84\x30\x58\x00\x7a\xaa\x64\xcc\xc7\x62\xa9\x25\x18\x50\xc7\xda\x08\x3b\x71\x60\x6f\x10\x36\xa7\x14\xe3\xe8\x9d\x02\x81\x97\xcc\x12\x1e\x2f\xc9\x15\x4d\x99\x74\x19\x21\xb3\x88\x6b\xba\x0e\x02\x99\x32\xa2\x97\x86\x47\x1b\xfc\x5b\xdf\x28\x55\xd6\xc3\x2c\x84\x42\xb7\x33\x54\x15\x47\xd6\x83\x3f\x80\x01\xe9\x23\x96\xc3\xe1\x79\x06\x21\x48\xa9\xd2\x96\xce\x0f\xfc\xbf\x7a\xed\x2c\x2f\xf2\x00\x40\xcf\x74\xec\xea\xe1\x07\x28\xfd\x01\x7d\xb4\xd5\xf9\x6d\x0b\xb2\xe1\x4b\xfa\xce\x0d\xd0\xe1\xd0\x9a\xf7\x9b\x89\x1a\x78\x52\x1f\xea\x5f\x7f\x13\x08\x98\xb1\x55\xb0\x15\x88\xba\x0a\xfa\x28\xe8\x37\xe8\x49\x6e\x77\x73\x8b\xb3\x3b\x2c\x1b\x08\x8f\xe5\x54\xc8\x1a\xba\x61\x2a\xdd\x6e\xed\x4c\xc4\xd9\x92\x32\x2b\x8b\x38\x0f\x6f\x36\xfb\x81\x8a\x7e\xc7\xdd\x4c\x86\xac\xd0\x35\xb4\xbb\x0b\xe3\x3c\xaf\x0e\x42\x3b\x49\x1c\x2f\x7c\xdc\x33\x25\x48\xe1\x76\x38\x66\x42\x08\xae\x16\xb2\x3c\x15\x91\x0e\x58\x52\xf2\xa3\x0c\x50\xcc\xec\x3a\xc3\xae\x33\x3f\x03\xfb\xa5\xa2\x01\xa9\x97\x46\xf7\x36\x12\x40\xff\x88\xcf\x03\x71\xc2\xf9\x05\xec\xf9\x10\xb9\x4e\x57\xf1\x20\xbd\x71\x49\x7b\x97\xe2\x25\x51\x38\x27\xd3\xd1\x1a\xba\x29\x22\x04\x23\x20\x36\x4a\xe9\x92\x40\xe2\xe2\xdc\x5a\x05\x62\x3c\x97\xfe\x8f\xed\x6f\xf5\x7f\xc8\xf7\x23\x9c\xe4\x0b\x01\x12\x70\xcf\x57\x87\x50\xee\x7a\x4b\x48\xc4\x6a\xbf\x11\xc9\x00\xfe\xd3\xd7\xd5\xaa\x0c\x57\xde\xfc\xb6\x4c\x53\xb7\xe3\x58\xa9\x31\x0c\xf1\xff\x1d\x18\x07\x30\xec\x1b\x56\x7b\x51\xa0\x1e\x8e\x41\x69\xea\x86\x84\x05\xb0\x4d\x5f\xdd\x45\xad\x4e\x3c\xa7\xf5\x4c\x17\x48\x5f\xaf\xf2\xcc\xf2\x7b\x84\x0a\x97\x51\x2a\xa7\x9e\xaf\x81\xcc\x1d\xf0\x73\x35\x81\x45\xda\xb9\xaa\x75\x1d\xe5\xa7\x9e\xf2\xdf\x8b\x4a\x32\x2e\xb5\x38\xe5\xc8\x15\xe9\x05\xa3\x2e\x4c\x8c\x75\x85\x24\x22\x9e\x9f\xf1\x17\x6e\x14\xe0\x41\x10\x1e\x6e\x4f\xcf\x8f\xd9\x12\xee\xbb\x28\x99\x74\xa0\x50\x87\x4d\x26\x66\xb8\xd5\x8b\x44\x57\xa3\x12\x12\x49\x41\x7c\xc0\x9a\x0e\x35\x9e\x94\xa2\x65\xa1\xdd\x0d\xc1\x18\x05\xa2\x59\x01\x0c\x87\xf1\x6a\x32\x33\xef\x4c\x97\x79\x16\xdb\x74\x56\x7a\xab\xfe\x11\x42\xb5\xcf\xf0\xfd\xb0\xc2\xc7\xc0\xce\xca\x7b\x3f\x6e\xd4\x92\xd4\xec\xcb\x0c\xd1\x4d\x04\xc3\xb0\xc0\x89\x4e\xa0\xa2\x9d\xbd\x48\x0d\x67\xbf\x99\x78\x99\x88\x3d\xe3\x58\x14\xe2\x2b\x79\xd5\x72\xa4\xe9\xcc\x09\xb1\xeb\x11\x29\x67\x57\x75\x46\x7b\x3a\x50\x48\x2b\xe4\x03\x2f\x71\x6c\x0f\x9d\x79\x6a\x7e\x59\xea\x0c\xae\xce\x8c\x55\x48\x68\xc2\xfa\x89\xc5\x53\x1d\xf3\x31\x6b\xf8\x7e\x41\xc3\x2d\xd9\x62\x32\x4f\x1d\x12\x5f\x04\xa8\x57\xd0\x3a\xef\xa5\xdb\x13\xc7\x42\x3e\x3c\x1d\x4d\xeb\x0f\xa0\x14\x21\x8e\x1e\x71\x5d\x04\x16\x17\xe8\x85\xaa\x11\x31\x41\x18\x72\xcc\x40\x70\x54\x2b\x36\x4f\x16\xe6\x47\x97\x96\xd6\xc9\x90\x08\x05\x5b\x4e\xb8\xe1\x74\xa6\xbb\xca\xd3\x76\x43\xd3\x74\xfe\x41\x99\x9d\xf0\x51\x7c\x6a\xd7\xa5\x75\x77\xc7\xad\xb7\x85\x94\xfb\x0d\xdf\x03\xa6\xa4\xaa\x03\x42\x2c\x1e\x3d\x80\x3d\x23\xe9\x01\x2b\xf3\x78\x4d\x48\x63\x2b\x59\xe1\x33\x6d\x9f\xad\xa2\xf4\x91\x15\xe5\xf2\xbd\x5b\xdd\x71\x96\x22\x01\x57\x01\x11\xb6\x83\x18\x00\xb6\x08\x52\xd0\xad\xf7\x74\xb4\x38\x6b\x96\x93\x52\x4b\x9b\xc4\x70\x3f\x6b\x3a\xe8\x63\x1b\x56\xeb\x68\x0b\x53\xee\xf7\x4b\xc5\x71\xd5\x78\x96\x73\xd2\x4a\x77\x52\xbf\x73\x32\x0c\x9e\x01\x32\x00\x2e\xd0\x33\x08\x33\x3c\x48\x8f\x02\xc6\xb4\xf9\x26\x66\x22\xc8\x0a\x30\xf2\x01\xa8\x21\x81\x6e\x40\xfa\x48\x0f\x0f\x04\xfb\xad\xca\xcc\xaa\x5c\x7e\xad\x3c\x8c\x3e\x9e\xc8\x14\xa6\x39\x03\x23\x4b\xdc\xb3\xb8\xfb\xa4\x81\xa8\x95\xbd\xb9\x01\xb2\x25\x25\xf1\xfb\x9e\x08\x5b\x60\x0e\xcd\x69\x0b\x51\x5b\xdf\x78\x95\x73\x41\xfd\xfe\xdd\xfb\x40\xba\xcc\x32\x92\x63\x14\xfe\xdf\x12\xce\x78\x04\x62\x23\xe1\xd9\xa5\xa1\xb7\x6e\x45\x0d\xa2\xf0\xfd\x16\x64\xa7\xca\x06\xab\xab\x7e\xef\xd0\xc3\x61\x9d\xd9\xa7\x64\xad\x8c\xdf\xed\x2a\x05\xa0\x19\xa8\x34\xbe\xbe\x18\xf5\x93\xd6\x51\x6a\x3a\x6d\x6c\xe1\x22\x74\xca\xe6\x1a\xb1\xc5\xfc\x1a\x8f\x4e\x56\x06\x16\x29\x25\x72\x1f\x06\x00\x76\x50\x51\xc3\xb4\xd8\x13\xed\xa5\xe5\xad\xd6\x8b\x00\x82\x09\x86\x5b\xfb\xdc\x89\xf8\x18\xea\x96\x09\x3a\xff\x76\xf7\x1a\xc6\x00\xc6\xe3\x4f\x20\x3b\x6e\x3b\xef\x84\x43\xb1\x81\x51\xc6\x00\x41\xf3\x2e\x36\xf1\xf0\x44\x05\x75\xff\x87\x14\x42\xc9\x0a\x92\x57\x3a\x31\x05\x8b\x64\xd8\xe7\x10\x1c\x5b\x9e\x5a\xad\x32\x88\x1d\x12\x7b\xea\x84\xeb\x3c\x7e\x3a\xbe\x40\xfb\xb4\x43\xb2\xd6\x20\xc4\x5c\x7a\x38\xe2\x8a\xd8\xa9\x66\xf8\x2d\x82\x6b\x74\x6d\x12\x73\xb7\x52\x9d\xa1\x1b\xfc\xc8\xcc\xbb\x4b\x1f\x77\x40\x88\x52\x91\xf9\xe5\x62\x17\x7e\xb2\x42\xa8\x1b\x80\x11\xf6\xda\x00\xe8\x53\xf8\xbe\xf4\x4c\xf3\x05\x58\x20\x28\x5c\xf2\x6a\xe5\xdd\xd7\x56\xe1\xd4\x12\x77\xac\x13\xe9\x5e\xbf\x92\x4e\xb9\x25\x12\x46\xf6\xdf\x33\x05\x02\xf8\xf5\x28\x32\x69\xfc\x0e\xf8\x9d\x2a\x13\x07\x6c\x0b\x22\x72\x58\x5d\x80\xde\xb4\x0e\xe3\x6f\xa5\x32\xcc\x6e\x73\xe1\xc6\xd4\x94\xa7\x94\xb1\x8a\x1e\xa1\x24\x4e\x1b\x11\xd7\xf7\xbd\xdc\x54\xb6\xb8\xa2\x51\x4b\x3c\xe9\x73\x74\x39\xc0\x15\xdf\xef\x03\x72\x95\x69\x6f\x8c\xd3\xe4\x68\x8b\x9b\x65\xac\x02\x9a\x49\x39\x0f\x74\x32\x94\x3a\xb2\xd3\xf6\xdf\x74\xfa\x17\x78\x0a\xcf\xb8\xbb\xd4\xcc\x93\x1c\x67\x01\xf4\xf9\xcf\x3e\xcd\xfd\x36\x05\x62\x12\x99\x30\xd5\xb4\x71\xf9\x27\xe4\x3b\x86\x26\x26\x21\x92\x84\x91\x11\x0c\xfb\x5b\xe5\x7c\x5f\xb4\xe6\x1a\xd9\xd8\x0f\x3d\x2d\x8f\x52\xc4\x14\xea\xcc\x14\x72\x3c\xcf\xd6\x43\x1c\xa6\x45\x50\xfc\x6b\xa7\xa2\x6e\x24\xc4\x25\x43\xe8\x9d\xc5\x32\x2e\x48\xba\x44\x02\x69\xaa\x61\x3b\xea\xa1\x6e\x22\x48\x06\xed\x30\xfa\xcf\x79\xd3\xa4\xea\x23\x27\xf6\x3f\x3d\xe9\x49\xfb\x1b\x99\xc8\xc3\x86\xba\x7f\x16\xcf\xfe\xe8\x2b\x64\x9c\x8b\x3f\x1f\xf4\x0f\x62\x79\x98\xc6\x7b\x82\x5e\x80\x8a\x82\xd6\x26\x5e\xbc\xb0\x84\x39\x6b\xd0\x72\x35\x70\x48\xd6\x04\x98\xe8\x1e\xbb\x93\x60\x3e\x47\x97\xbc\x0c\x78\x21\xce\xee\xb8\xf7\x34\x70\xd2\xa1\x55\x4f\xe0\x0c\x93\x77\xa1\x94\x67\x14\x4d\x8a\x41\x37\x9a\x87\x19\xa6\xec\x95\xd9\x3c\x6b\xbe\x1d\x1c\x39\x86\xf9\xd5\x1f\x05\xf6\x72\xdf\xa0\xa7\x90\xeb\x25\xac\xe3\xcc\x63\x2a\x3f\x82\x6e\x5b\x18\xbe\x4f\x04\x5a\xbc\x32\xda\x24\x2f\x21\xcb\xa4\x51\x40\xf4\xfe\x8c\x0b\xda\x82\x9b\xa4\x52\x29\xf9\xbd\x73\x49\x23\xca\x6e\xb1\xd9\xdd\x26\x1a\xd4\xb3\xac\xb7\xee\x48\x1d\x5b\xfa\x5b\x0f\x49\x03\x44\x2c\xc0\x41\x61\x43\x64\x11\x82\xe8\xd9\x8a\xfc\xd4\xf8\xe4\x12\xc1\x0f\x12\x80\x58\xc7\xfc\xc1\xca\xa1\x20\x90\x9a\x1f\x4d\x54\x1c\x2d\xd9\xfa\x2b\x34\x52\x3e\xc3\x77\x06\xb7\x5c\xa5\xb4\xd6\xa5\x1a\xc2\x89\x5c\x74\x93\xe5\x37\xd9\x92\x76\x94\xdb\x1a\x3a\xb0\x17\xd2\x8a\xb6\x11\x5c\xbd\x66\x11\xf1\x70\xd9\x73\x5c\x5e\x25\xe8\xbc\x13\x5e\x73\x3b\x3f\x18\x4c\x83\x4d\xf6\x29\x8e\x93\x51\x35\x89\xa1\x0e\xbd\xcd\xcc\x98\x38\x0b\xf8\x2c\x3f\x62\x96\x82\xa0\x4f\x30\xaf\x9b\x7a\x6b\x9c\x51\xa6\xf2\x20\xd3\xd7\xef\x1f\x8c\xea\xbd\xea\x26\x9a\x36\x45\x1d\x87\x89\xb7\xdc\x0a\xc1\x81\xb5\xc8\xe3\xa1\xdb\x7d\xb8\x52\xcb\x3a\x4f\x87\x4a\xa3\x83\xe8\x64\x76\x85\xe0\xca\xc2\xb5\xf7\x9a\x28\x3c\x79\xe0\x5a\x7c\x27\x6e\x3d\x80\x41\xd6\x6d\x5a\xb7\x74\x86\x36\xfe\xbd\x2c\x24\x6d\x9e\x34\x63\x85\xcd\x9a\x90\x58\x2e\x57\xf1\x5c\xfb\x06\xdb\xdd\x47\xbe\x82\x15\x73\x32\x11\x17\xc1\xe7\x60\xef\x76\x71\x5c\xa7\x3c\x53\x24\xfc\x0e\xe6\x8d\xcd\xdc\x87\x61\xd4\x4b\x14\xc0\xef\xc7\x6b\x48\x5e\x5f\xd3\xa3\x96\xdc\x25\xcd\x4a\x67\x2f\xc8\x51\xee\xa2\x8b\x4b\x57\xc5\xf0\x73\xe9\xf4\x9f\xc2\x94\xeb\x7b\x00\x6a\xd6\x8d\x2c\x57\xc8\xb3\xe6\x1b\x88\xe1\xed\x10\x72\x79\xde\xe2\x2a\xa4\xea\x2e\x62\xae\x84\xef\xa9\xba\xc9\x09\x9c\x92\x97\x56\x15\x2f\x5a\x5f\xa6\x1d\xff\x90\x4e\x18\xa4\xe6\x35\x3b\x0c\xae\x60\xf5\x2a\xdd\x0c\xcd\xe5\x1d\x39\x10\x8a\x77\x27\xff\x0e\xaf\xde\x9a\x2b\xf9\x88\x27\xaf\x28\xb2\xb0\x5e\x82\x96\x8d\xfa\xa7\xa6\xb0\x08\xf2\xd2\x0f\x91\xef\xb2\x72\xfb\x17\x9b\x60\xa1\xef\xfe\x63\x8c\x05\xab\x8d\x8f\xbc\xac\x53\x06\xa2\x51\x3c\xd0\x00\x61\x92\xa6\x41\x6d\x71\x2d\xee\xf6\x60\x4a\x9b\x67\x08\x11\x0d\xde\xd9\x60\x3e\x6a\xed\x8c\x7f\x49\xbe\xaf\x1e\x54\x20\xad\xc8\x56\x37\x94\xf3\xe0\x03\x53\x81\xbf\x98\x21\x47\x30\x87\xd3\x41\x79\xf9\x56\x77\x9f\x3e\xd5\x53\xd6\x04\xf0\x93\x78\x27\x64\x9b\x53\x15\xff\x3e\xa5\xcc\x5b\x90\xba\x63\x54\x78\xe4\x8f\x50\xbc\x53\x1b\x28\x3c\xe1\xd8\x49\x6f\x77\xc5\x59\x38\x98\x95\xe3\x0f\x10\x59\x43\x74\xf0\x42\xc5\xb4\x9e\xd5\x25\xca\xc6\xba\x0b\x34\x4b\x59\x06\x26\x65\x24\xf7\x2f\xbc\x03\xf2\x68\x89\xef\x97\xa7\xb3\x5a\x8d\xf5\xbc\x05\xe8\xca\x79\x37\x68\xbc\xae\x9d\xe5\x2f\xcc\x07\xde\xda\x97\x09\x2c\x1b\x28\x9c\x9e\x60\xca\x08\x0b\x62\x08\xde\x61\x5d\x49\x32\x7d\xe6\x30\x8f\x23\xed\x15\xd5\x6e\xdc\x0e\x4b\x0e\x6d\x89\xb5\x09\x82\x23\x55\xa1\xf7\x4e\x1b\x93\x43\xc7\x32\x56\xf9\x5d\x6a\x4d\x48\x01\x91\xa7\xb1\xf2\x26\xa7\xb5\xb2\xd6\xd0\xbf\x8d\x02\x6a\x2b\x83\xba\x4a\xf3\x0d\xbc\x7f\xba\xa3\x2d\x62\x74\xd5\x54\x89\xfc\x34\x49\xf9\xf1\xe9\xac\x4f\xb1\x1d\xc7\xc4\x6b\x43\x2d\xb1\x30\x9a\x62\x78\x2c\x7b\x9f\x27\xd8\x2d\xd8\x91\xc1\xd1\x89\xc6\x96\x3d\xa4\x8a\x96\x8c\x89\x5b\x83\x3b\x40\x58\x5e\xe9\x8d\xe0\x5e\xeb\x32\x80\x79\x70\x80\xfb\xdf\x35\x9a\xec\x33\x99\x12\x63\x11\xa7\x71\x4d\x08\xb7\x36\xab\x1c\x01\xc9\xca\xe4\x02\xe9\x37\xf6\x24\xa6\xec\x5a\xfa\x8a\x5b\xcf\x45\x3c\xda\xc9\x3b\x88\xbc\x12\xb7\xfc\xfe\x7c\x92\x85\x8b\x07\xdf\x8f\xb7\xcf\xa8\xbc\x16\x34\xa4\x25\xaf\xd2\xb3\x7c\xd3\x49\xa5\x56\xa2\xd4\x3c\x00\x23\xbb\xf6\x42\x23\x64\x59\x4e\xa4\xaf\x2b\x53\x63\xb3\xb3\x3d\x53\x2e\xed\x74\xca\xe2\xf9\x48\xc3\xae\x66\x95\x8f\x82\xcc\xc4\xbf\x22\x51\xbc\xd9\xe8\x27\x4d\x5c\xf7\x58\x9b\xfd\x33\x57\xaf\x93\xa4\xd1\xd8\x56\xb2\xb2\xee\x1e\xfa\x20\x7e\x1e\x24\xce\xf9\x4b\x4f\xc7\xef\x37\x1a\x32\x79\x30\xa0\xf4\xab\xf4\x0e\x0d\xa3\x09\x3f\x53\x16\xb9\x8a\x90\xaa\xef\x2a\x9a\xa9\x14\x2a\xcc\x90\x7e\xce\x7a\xc6\x97\x1e\xba\xa4\xcd\x2f\x37\xd4\xd2\xd4\x88\x3f\x67\xf8\xc9\xc9\x9d\x0d\xed\x9f\xc1\x9f\x75\x59\x60\xa7\x8f\x17\x49\xd9\xfb\x90\xce\xc5\x40\x9b\xe1\x8a\x0e\x61\xa1\x9a\x2a\xc4\x14\xbb\xc3\x28\x4c\x3e\x64\x15\x66\x6a\x43\xf5\x53\x3b\x31\xb7\x36\x33\x77\xbd\x66\x88\xdd\xe0\xb1\x4c\x2c\x21\x88\xbe\xdd\xeb\x26\x2b\x3d\xf6\x95\xf3\xa2\x65\x37\x33\x5a\x3e\xac\x7a\xaa\x2a\x59\x43\xd3\x3d\xbd\x67\xa6\xf5\x75\x84\x78\x51\x5f\x29\x3f\xae\x91\x5b\x6e\x68\x11\xed\x60\x4e\xa5\xf7\x06\xb9\x0d\x68\xa1\xf0\x06\xe2\x6d\xdd\x67\xf9\xae\xfe\xd9\x5a\xbf\x80\xbb\x97\x7b\x25\x54\x01\xb7\x08\xfb\x15\x6a\x4c\x73\x85\x5e\x74\x09\x97\x0e\x08\xde\x3d\x7d\x1a\x97\x92\x90\xe3\xdd\x26\x9e\xeb\xee\xb6\x6c\xa1\x15\xc1\xd6\x91\x47\x12\xcb\x55\x8f\x2e\xf7\xae\x1f\xb3\x60\xdb\xd4\x4f\xa1\xef\x53\xb2\xef\x6b\x59\x8c\x1a\x4e\xaf\x67\x8f\x54\xb5\x63\x03\xd9\x3d\xfa\xe8\x61\x4c\xb2\x1d\x02\xd8\x0a\xf8\x30\xe9\x2c\xfe\x76\x97\xb6\x68\xe1\x08\xe7\x6a\x37\xc3\xb7\x85\x74\x12\x5f\xe9\x31\xc2\xb5\x9c\x33\x3e\x9d\x72\x31\xc4\x68\xc7\x4a\x9a\x4d\xee\x68\x01\x54\xca\xba\x19\x52\x2c\x80\x64\xab\x17\x9a\xf5\x9c\xae\x95\xe4\x96\x8c\x95\x1d\xc5\x46\x7f\xeb\x5f\x99\x55\x63\xdd\x7f\x78\xc0\x4a\x70\x0d\xb0\x92\x7f\xaa\x76\xa1\x02\x0d\x05\xbf\x0b\x06\x1e\x7f\xc2\x61\xb6\x7a\xea\x02\xc5\xb2\xbd\x6f\x8a\x90\x28\x4e\xd7\x55\xfa\x8a\xc8\xe7\x9f\x47\x5f\xdc\x9f\x26\xdc\x63\xca\x27\xa8\x0f\xb9\xf4\xb0\xb8\x72\xcf\xe0\x86\xcf\x6c\x7c\x8e\x6a\xf4\xf8\x44\x43\x3e\x01\xd7\x08\xc8\x90\x0f\x4d\x40\xe6\x67\x0b\xaf\x70\x6a\xcb\x23\xb0\x29\x30\x91\x2e\x5c\x23\x7c\x1d\x98\xb4\x28\x34\x0b\x97\xfb\x6e\x65\xc8\x88\xe3\xbc\x73\xe5\x98\x34\x29\x8c\xd8\x81\x27\x22\x06\xe2\x8a\xc2\xc5\xb4\xf6\x1c\xfa\xf7\xd6\x32\xf9\x31\x68\xb2\x57\x31\x70\x7b\xbe\x50\xb6\x48\xf0\xaa\x61\xbf\xf3\x24\xb9\x82\x5e\xa0\xe3\x94\xb5\x9a\x56\x36\x89\x16\xd1\x74\x12\x67\x37\x8d\x5d\x3d\x55\xda\xca\x7d\x70\x5e\x71\xa0\x55\x13\x80\xe3\x38\xae\x1a\x80\x07\x13\xcc\x25\xad\xfa\x2d\xa5\x4d\xb1\xb9\xee\xc1\x86\xc3\xa0\x79\xa5\xb7\x0a\x1f\x5a\xe7\x8e\x7b\x10\x0f\x10\xe1\x4b\xd6\x42\xeb\xb7\x95\x1d\xf2\x2b\xa3\x22\x37\xa5\x0f\xf2\xe1\x91\x84\xcb\xa9\xd0\x60\xac\xa2\xdb\xd0\xcc\xcb\xc7\xf4\x0c\x59\x8a\xc0\x76\x89\x35\xb5\x02\x2c\x0a\x70\x0c\xb3\x73\xad\xfd\x42\x44\x4b\x31\xc8\x94\x05\x70\xdd\x6b\x8e\xb4\x36\xf9\x6a\xd4\x4e\xee\x54\x2a\x3b\x95\x85\x5f\x43\xdd\x94\x9b\x9d\x3c\x1d\x8f\x68\x80\xa2\xdc\x31\x98\x6b\x84\x6d\x81\xdf\xb5\xae\xe0\x00\xf4\xb1\xca\x5c\x8f\x66\xa8\x0f\xa3\x04\xe2\x61\x1b\x21\x67\x0e\x14\xe4\x56\x5c\x7e\x0d\xd5\x86\x61\x38\xc0\x85\x89\xea\x89\x68\x74\xc0\xa8\xc0\xa1\x59\x6b\xb6\x06\xb3\x23\xb4\xb9\x85\xf1\xce\x69\xd7\x29\x47\xec\xe2\x4a\x8f\x83\xd9\x65\x56\x66\x31\xd1\x10\x23\x7d\x45\xad\xd8\x15\x8f\x99\x14\x61\x34\xc3\x90\xf4\xf0\x49\x88\xa8\xb1\x4e\x7d\x39\xbe\xf9\x61\x62\x59\xff\xe8\x45\xbb\xf8\xcb\xad\x15\x4c\x7b\xd5\x42\xbc\x28\x87\x14\x4e\x8c\xfb\xc0\x62\x3d\x88\x13\x51\x35\x25\x0d\xf4\xd9\xee\xe5\xf3\xee\x84\xf7\x58\x4d\x1c\x4f\x7a\x0b\x8f\x82\xae\x8d\x19\x9b\x33\xfd\xd3\x14\x86\x0e\x73\x9c\x46\xce\x1e\x20\x7f\xa4\xb7\x86\xbd\x75\x57\x34\xc7\x98\x54\x41\x73\x96\x6f\xbf\x1d\x2a\x81\xad\x3b\x8c\x7a\xbb\xe6\x36\x02\x2b\x74\x09\xb7\xc2\x17\xc0\xe5\x57\x76\x5d\x6c\x76\xcf\x49\xdc\x72\x3c\x15\xee\xea\xe4\xb5\x29\x50\x14\x1d\x06\xd4\x06\xe1\xe8\x0c\xab\x1a\x16\x1d\xbf\xec\x41\x5e\xa4\xcc\xa4\x29\x34\xeb\x52\x85\xa2\xcc\x6d\x26\xc2\x76\xfb\xa4\x61\x8d\x65\x8d\xed\x26\x69\x39\xd5\xe6\x8a\x8c\xd7\x29\xe5\xb2\x03\x75\xeb\x74\x44\xfe\x1d\xc4\x02\x8f\x5e\x2d\x1f\x4a\xe0\x51\x59\x95\x1c\x80\x70\x8f\x51\xe2\x12\xbb\x54\x87\x46\x1c\xf1\xf6\x4c\xcd\x99\xb8\xe1\xb2\xc2\x5e\xc4\x1a\xe3\x88\x0b\x02\x7f\xdd\xde\xa0\x1c\x5b\x0d\xd6\xaf\xa1\x7e\x48\x17\xc1\xd4\x65\xd2\xff\x9f\x2a\xe1\xf0\xf4\x4c\xe0\x2f\x99\xb4\x72\x51\x87\xd9\x2b\x57\xd2\x2c\x5c\xbf\x76\xa9\xb4\x38\xaf\x4c\xd9\xd6\x1e\x57\x9e\x43\xc6\x8f\xb0\xdc\xa5\xaf\x1d\xf9\x00\x9a\x32\x78\x75\x59\x1c\x55\x22\xe8\x65\xc3\x14\x41\x4b\x92\xdb\x14\x26\x87\x76\xdb\x8f\x12\x53\xf3\x03\x92\x54\xf4\x60\x12\xbb\xed\x62\x16\x4d\x7f\x9f\x0e\xc6\xc5\x29\xd6\x9d\x99\x11\xfe\xea\x13\x5d\x3d\x04\x91\xa8\x04\x96\x77\x63\x57\x69\x9c\x1d\x07\xfb\x9c\x6a\xa2\x3f\xec\xdc\xb7\x60\x1b\x9e\xcf\x1c\xbd\x51\x5e\x08\x40\x07\x92\x95\x1b\xc6\x0a\xac\xac\xfa\x7e\x7d\xae\x21\x0d\x3c\x08\xca\xa6\xa7\x24\x23\xfe\x7f\x82\x2c\x53\x83\xbd\xd9\x01\xdb\xc6\xb4\x69\x94\x32\x33\x3d\xbf\x46\x1a\xb8\x7e\xe0\xc9\xa0\xa6\x23\xc2\xf8\xd9\x0e\x76\xc9\x3d\x41\x86\xc2\x43\xd9\x17\x83\xe3\x62\xe9\xaa\x9d\x28\x6b\xf5\x2c\x86\x66\xcf\x6b\xfa\xe3\x53\x68\xb4\xa6\x98\x08\x64\xbb\x3e\xaf\xb3\x3a\x83\x50\xd7\x14\xd0\xe9\x65\x97\xe4\xe9\x11\xa8\x8c\x3d\x83\xe9\xd8\xe6\x53\xa6\x6c\xe9\x70\xd1\x35\xbd\x6e\xa6\x50\xa1\xee\xc9\xc2\xc9\x21\x0a\xe1\x8b\xa7\xe1\xfc\x61\x07\xae\x5b\x08\x03\xf2\x24\x80\x5a\x28\x2d\xe9\x5b\xb3\x6f\x60\x55\xbb\x94\x9b\x6e\x75\x8e\x29\xbf\x5f\x10\x65\x35\xad\x6e\x0c\xcc\x45\x15\x5a\x36\x3b\x3d\x40\x62\x63\xa4\xdc\xde\x00\xdc\xde\x0d\x8c\xd3\x50\xf1\x33\x8a\x3c\x0f\xc8\xc6\xd2\x7f\xcb\x7d\x2a\x88\x7d\xf7\x88\x7e\x7f\xd2\x16\x72\x93\x5e\xc6\xbc\x70\x0b\x02\xf4\x22\x5f\xcc\xed\x3d\x3e\x21\x6a\x2c\x52\x4a\xb4\x3b\x51\x0a\xa4\x5f\x41\x61\xc2\xc9\xe8\xb7\x08\x7c\xff\xac\xae\x2c\xff\xb6\x72\xf6\x41\x6d\x6b\xda\xd6\xcb\x4c\xf4\x7e\xf3\x2b\x9e\xe1\x1e\x63\x0f\x08\xed\x20\x65\xb5\x8d\x56\x5c\x6a\xcc\x9d\x29\xfb\x24\x71\x15\x1f\x51\x14\xf1\x01\x0d\x2a\x55\x7e\x37\x5b\x5f\x5d\xa3\x01\x3a\x29\x3d\x80\x4a\xd7\xbd\xb0\x0f\xad\xfc\x66\x62\x49\x68\x84\x73\xd3\xf9\xf1\x41\xaf\x32\x96\x4b\x83\xba\xda\xe6\xc9\xfb\xe5\xf8\x41\x68\xdd\x12\x4d\x46\xc6\x72\xb8\x4d\x5c\x85\x1c\xa8\xce\x7d\x32\xe5\x18\xa4\x11\xb7\x93\x7d\x75\xca\xd0\x64\x06\x6d\xee\x14\x68\xe0\x37\xd5\x0a\x3a\x50\x66\x19\x3c\x9b\xd8\xc4\x80\x18\xed\x9b\x96\x79\x46\xa5\x63\xca\xfe\x26\x4b\xc9\xb8\x15\xec\x5e\xb2\xa7\x9d\x7a\xbf\xcc\xeb\x6b\x3d\x04\x9c\x47\xbd\x80\x50\x05\x34\x8f\xf9\x94\x9a\xb4\xa9\x01\x2d\x71\xfa\x63\x75\x61\x5e\x78\x79\xbb\xd4\x5f\x2e\x99\x0f\x51\x3d\x29\x06\x99\x34\x4f\xf9\x16\x87\x70\xca\x5f\x0b\x36\x06\xd0\x23\xf8\x3c\x09\x96\x1d\xad\x0b\x9f\xf0\xcd\x98\xe8\x96\x7a\xf3\x71\xfc\x92\xed\xf2\xa7\xa0\x44\xfa\xa2\x0d\x15\x92\x96\x5c\xe8\xc3\xa6\x28\xf2\xbf\xc3\x59\x98\xb3\x78\xf5\xb1\x4b\x95\x0b\x5a\xbd\x1a\x3c\xfd\x90\xc6\x80\xa3\xa3\x25\xbf\xcd\xd8\x54\x69\x22\x32\x81\x77\x83\xa4\x67\x8b\x16\xdb\xc7\x88\x2a\xde\xa0\x68\xb8\xdc\xe6\x0f\x6f\x34\x17\xdd\x27\x25\x61\xa5\xe3\xb7\xe1\xfe\xf7\x92\x58\x01\x07\xea\xc0\x23\x09\xa2\xbc\x5a\xcf\x12\x6f\xbd\xfc\x6a\x65\x71\x16\x02\x3f\x4c\x56\x89\xf9\xc6\x99\xd9\x4c\xa5\x70\xef\xcd\x8f\x39\x76\xaf\x1f\xf7\xa7\x7b\x07\x4d\xb9\x89\xaf\x26\x26\xd8\xbc\xcb\xce\x92\x3c\xdc\x0b\x12\x54\x61\xa8\x41\x9a\x75\x2a\x3f\xab\xf0\xe5\x1b\xa6\x43\xdd\x73\x14\x44\x0b\x54\x0d\x31\xd3\xa8\xd0\x3e\xfc\x54\x19\x32\x6e\x75\x31\x91\x07\xf7\xf2\xdb\x1a\x7a\x02\x43\xf2\x49\x8c\x57\xce\x31\x1c\x00\xb2\x08\xf8\xec\x00\xa3\xbc\x96\xab\x0f\xf4\xd8\xc9\xec\x32\x72\x73\x24\xef\x2c\x00\x3f\x6b\xbb\xe3\x0c\xff\xce\xca\xad\x96\x98\x63\xca\x01\x44\x69\x83\x10\x92\x0b\xae\xfa\x71\x20\x11\x5e\xaf\x7a\x92\x63\x4e\x11\xdb\x08\x5c\x3c\x2e\xb6\xf0\xf7\x96\x5e\xed\xd5\x27\x7b\xea\xea\x8c\x8f\xf8\x9f\xcc\xff\x85\x22\x6e\x1a\x47\x4d\x64\xd6\xee\x74\x7d\x83\x59\x34\xba\x1d\x7e\x20\x0c\xa0\x91\x38\x3b\xd2\x98\xb0\xe5\x2d\x6d\x62\x01\x35\x16\x77\x4a\x3c\xaa\x2e\xde\xe1\x32\xbd\x68\x9e\xed\x8a\xa5\xa3\xc1\xb1\xe2\xaa\xa2\x68\x4f\x94\x2e\x21\x55\xfd\xc3\xd2\xd2\xf9\x6e\xd1\x81\x02\x88\x7a\x47\x63\x46\x61\xee\x3c\x44\x6a\xc9\x76\x5a\xea\x67\x78\x80\x5b\x7d\x15\xb2\x73\x65\xbd\x81\x86\xaa\xc0\x0c\x26\x26\x9c\x25\x2c\x50\x82\xa4\x21\x2e\x9a\x0a\xcf\x77\xbc\x4b\x5c\xeb\x77\xdb\xf0\x2a\xe5\x02\x99\x05\xa8\xeb\x15\xa2\x68\x58\xb9\x74\xe8\xdc\x29\x06\xa6\x33\xee\x26\x66\x84\xa3\xcd\x8c\x89\xe6\xba\x5e\x4a\x85\x90\x6a\xfa\x71\x79\x18\x90\x3e\x71\xb1\xd8\x0b\xdb\xb0\x03\x50\x2b\x18\xff\x75\x41\x90\xa7\x8b\x09\x6e\x9a\x6b\xe8\x36\xaa\xab\xad\x1a\xbc\x3a\x9d\x3c\x38\x0c\x35\xa9\x4e\x06\x0b\xe5\x9d\x35\x96\x82\xd0\xa0\xa2\x79\x94\xdd\x14\x83\xdf\xb3\x7c\x2b\xa4\xa4\x2a\x85\x8a\xf3\xbf\x6d\x37\x31\xe7\x74\x9c\x7c\x6c\x9b\x5e\x14\x29\xb5\xdb\xe7\x77\xb6\x99\x37\xe0\xeb\xf0\x2c\x69\x40\x09\xb1\x69\x19\xc3\x5f\x06\x00\x4d\xed\xaa\x2a\xa9\x15\x94\x28\xe7\x32\x33\x29\xcd\x2c\x95\x8e\xa7\xc6\xca\xe8\x06\x29\xb0\x8d\x2e\x2b\x0d\xb6\x11\xb2\x10\x5b\xf6\x1e\xb0\x43\xe2\x78\x13\x90\x1d\xac\x74\x0a\x62\xb4\x81\xbe\x13\x33\x51\x78\xdf\x61\x1e\x96\xdf\xff\xe9\xf0\x39\xd5\xc1\x45\x0e\xfb\x33\x69\x8e\x4d\x70\x0f\xf3\xed\x52\x5f\xb4\xdc\xce\x23\x88\xb8\x19\x5c\x08\x79\xa8\xd4\xc3\x48\x36\x56\xd5\x01\xa1\x3c\x36\xc0\x6c\xc4\x5e\xff\xf4\xcf\x11\x37\x3a\x6e\x8e\xe7\xd0\xf0\xdf\x26\x8e\x79\xb8\x93\xe3\x68\x65\x33\xe1\x7e\xf2\x9b\xcf\x5b\x5c\x8e\x99\x49\x6f\xa4\x42\x73\x36\xcb\x0e\xf2\x74\x48\xbe\xb0\x5f\x84\x98\x83\xa7\x11\x13\x4d\x88\x93\x8b\x1e\x2c\x2c\x5d\x1d\x74\x28\x79\x64\xc7\x83\x5b\x85\x05\x27\xb4\xfc\x54\xd6\x50\x9c\x9b\x1c\x7c\x89\x13\x7c\xff\xd6\x47\xc9\x49\x40\xe8\xef\x53\xdb\x51\xde\xa1\xac\xb6\x95\xc3\xb0\x69\x8e\x98\x54\xd1\x71\xb2\x9f\x00\x1f\xa2\xe8\xf6\x12\xec\xed\xa6\xba\x8e\x0b\x1d\xa0\xaf\xbd\x21\x3b\xbc\x7a\x4f\x00\xe3\x08\x65\xd5\xd0\x05\xde\x94\xb7\x81\x3e\xa0\xd4\xab\x59\x40\xf0\x21\x40\x9d\xb5\xba\x87\x34\xd9\xea\x12\x34\x82\xe4\x7c\x07\x65\xb8\x7f\xd0\x2c\xf9\x88\x40\xf8\xda\xff\xf8\x6a\x3b\xd7\xf6\x97\x56\x36\x0c\xbf\xa9\xe0\xb4\xf6\x40\x08\xdf\x1a\xf8\xd9\x14\xb9\x35\x9f\x0d\xf3\x18\x55\x8d\x47\x23\x74\x27\x45\xf3\x68\x86\x7c\x6a\xd2\x29\x0a\x87\x34\x63\xc2\x98\xa9\x5e\xc5\xab\x94\x54\xc6\x90\x18\x66\xc9\x56\x4e\x82\x3b\x5e\x32\x72\xf1\x69\xa7\xb6\x03\x3d\xa3\x82\x6d\x88\x67\x21\x1d\xfc\x94\x49\xe1\x20\x69\x87\x79\x14\xdf\x81\x69\x2d\x27\x3a\xd8\x86\xc6\xfe\x32\xf0\x9c\x7c\x47\x4b\x58\xf7\xd4\xcd\xe7\x31\xc9\x0c\xd4\x46\xdb\x91\x0d\x95\x1a\x8e\xac\xcb\x09\xbf\xbd\x7c\x26\xd3\xf2\x74\xcd\x83\xe4\xa4\x60\xe5\xb5\x87\x12\x36\x00\xc5\xa4\x25\xc5\xde\x19\xe4\x64\x83\xad\xb6\x50\x52\xd8\x51\xcd\x0c\xad\x6a\x61\xf8\x22\x54\x81\x70\xe5\x2e\xd1\xd2\x77\xfb\x0c\x9d\xcd\xb2\xe2\x5b\x4f\x10\x2f\x27\x3e\x46\xe6\x6f\x87\x45\x80\x55\x72\xb2\x9a\x8e\xf8\xf0\x0f\x03\xd7\x8e\xa2\x48\x41\x52\xbc\xbb\x2b\x78\x05\x11\xb4\x50\x0c\xe8\x6a\x23\x6f\x55\x0c\xca\xc4\x91\xb1\xab\x1b\xb7\xec\x73\x44\x5a\xfb\x4b\x27\xcf\x06\x39\x6b\xcc\xbf\xd2\xea\x8a\xab\x6c\xf5\x52\x18\x61\xb9\x73\x64\x6d\x7f\x52\xd4\xec\xca\xce\x2b\xe7\x22\xd2\xe3\x56\xdd\x24\x6e\x16\xdb\xd8\xda\x98\x6d\x94\x00\x9d\xc8\x1a\x5c\xe6\xef\x88\x40\x05\xf3\x0c\xe7\x8c\x51\x80\x52\x50\x26\xad\x9a\x3a\x61\xff\x61\xb2\x8c\xa0\xa6\x2c\x87\x66\x17\xb1\xb7\x29\x06\x75\x4d\xd3\x8f\x14\x1a\xce\x3d\xbf\xe9\xe9\x7c\x84\xcf\x05\x48\xdb\x58\x16\x7f\x73\xcd\xb0\x3f\xc8\x02\x32\xd9\xc3\xe9\xd5\xc7\x40\x81\xbd\xdb\xa5\x96\xad\x78\x9a\x30\xf7\x73\x7f\xe8\xa3\x6a\x23\x8c\xc5\x0b\xc0\xb0\x46\x10\x0a\x46\x2a\xbf\xd7\xbb\x82\x27\xc9\xfb\xc3\xdf\xd8\x55\x32\x51\xb5\x1f\xf0\x4e\x8f\x3e\xac\x03\x00\xe7\xb6\x22\x59\xce\x64\x99\x1e\xb8\x86\x6b\xd8\xc3\x5c\x3a\x67\x16\xad\xd5\x95\x44\xa5\xc8\xb9\x3d\x5b\x5e\xae\x62\x2d\xfe\xb2\x13\xd6\x77\x69\xf6\x06\xaa\x51\xde\xc6\xd5\xf5\xc1\x3e\x79\x56\x08\x6a\x02\x12\x23\xd9\x1a\x99\xb1\x08\x74\x80\x7f\x86\x42\x14\xbd\xbe\x27\x4b\x63\x36\x30\x3f\x26\x56\x5e\x1c\x89\x38\xc6\xea\x54\x37\x73\x69\xf2\x7d\x50\xb1\x61\x9e\xa7\x0c\xa8\xec\xfa\xe7\x4e\x13\x5f\x8d\x43\x44\x31\x9d\x47\xa0\x30\x26\xce\xbc\x85\x7f\x05\x7b\xb7\xcd\x4d\xe4\x9d\x7d\x66\x04\x3d\xf5\x24\x15\x8a\xb7\x73\xf6\x27\xe1\x45\x90\x4c\x33\x8b\x2f\x61\xf0\xaa\x10\xd1\x66\x68\x5c\x11\x89\x8b\x90\xe8\xb5\xdf\xb5\xa8\xb5\xaf\x14\xea\xd8\xb8\xc8\x34\x98\xb9\x20\x0f\xd2\xdb\x08\x06\x93\xeb\xba\xbd\x6d\xe5\xe7\x70\xb9\x46\x03\x94\x99\x9c\x52\xae\x55\x51\x99\x71\x99\xae\x63\xbc\x48\x2a\xa6\x08\x85\x4d\xf1\xda\x15\xcf\x18\xa4\x97\x1c\xdf\xef\x09\x2d\x45\x66\x78\x20\x45\x4e\xc6\x1c\x0e\x7f\x80\xdc\xf6\x9b\xd4\xbc\x2c\x29\xd8\x5d\xef\x01\x1c\x73\xc7\x95\xf8\x28\x9a\xdd\xca\x3c\x5c\x87\x87\x93\x76\x4c\x7b\xd3\x2e\x0e\x58\xb2\xa6\x7e\xe8\x26\x74\x13\x29\x78\xf9\x13\x8e\xb7\x7d\xb6\xd8\x15\xbd\x84\x8c\x55\x74\x14\x6b\xe6\x9c\xa7\x41\x37\xb1\xe2\x6e\x69\x3b\x61\xa0\x61\xf8\x7e\xb2\x8e\x99\xbd\x8f\x58\x3e\xff\x1d\xb8\x8f\xed\xe4\xd6\x53\x9b\x37\xed\x0f\x85\xaf\x95\x0a\x81\xb8\x80\xfa\xc9\x64\xaf\x98\xca\xdf\x4d\xf4\xcf\xae\xd7\x31\x8d\x1e\x6d\xa2\xaf\x69\xb1\xc7\x8e\xd3\x15\x43\xe1\x65\xa2\x87\x6b\xb6\x39\xd5\x84\x91\xf7\xff\xb4\x30\x12\xb0\x28\xe5\xf5\x05\xa2\xce\x22\x3b\x9f\x2d\x78\x11\x01\xf7\x78\x8e\x94\xee\x58\x7b\x7e\x39\xba\x97\x9e\xaf\x39\x82\x54\x86\x08\xa1\xe2\x23\x61\x57\xc1\x2c\x42\x4d\x06\xc8\x7e\x65\xdb\x7c\xbf\xd3\x49\x41\x67\x66\x2e\x76\x0f\x89\xb8\x17\x46\x49\x83\xc2\xa2\xc5\x9d\x62\x3b\x9e\xee\xd2\x50\x56\x18\x75\xdd\xae\x6c\x91\x97\xb5\x41\x4b\x28\xa5\xf2\x7b\x31\x75\x0c\x4a\xc3\xad\x3e\x52\xea\x5e\x8f\x2f\xa1\xef\xfa\x37\x5f\x45\x3b\x2f\x7f\x16\xa6\x00\x19\xbf\x9d\x2b\xf2\x62\x1d\x06\x14\x85\x65\x23\x75\x6c\xce\xac\xec\x75\x37\x30\xfd\xf4\x8f\x12\x3e\x39\xe2\x8b\xeb\xa6\xde\x47\x91\x6e\x8c\xf4\xd0\x51\x63\x6c\xbb\x48\x8a\x7c\xb7\xbc\xc8\xee\x3a\xda\x93\x7b\x38\x34\xdb\x13\x0d\x9c\xc9\x97\x38\x26\xfc\xcb\x9d\x16\x85\xc2\x48\x42\x68\x5a\xae\x68\xf4\x4c\x99\x48\xc7\x2b\xc5\x13\x22\x57\xee\xad\xa8\xeb\x5b\xa3\x08\x2e\x04\xc2\xab\x33\xe2\x0a\x08\xa2\x47\x72\x7d\x66\xbf\x07\xe9\x2c\x7a\xf4\x34\xef\x1f\xee\x4d\xf6\xac\xf3\xab\x71\xd1\x08\xae\xad\x4f\xf3\x27\x08\x87\x62\x2e\x9e\x19\x9e\x03\x64\xea\x99\xd6\xcf\x72\x4b\xbc\xef\x5f\x4c\x66\xa0\xda\x17\x16\x48\x31\x57\x68\xf7\x60\x59\x06\xa6\x7d\xa2\x09\x6f\xd1\xbb\x3a\xee\x11\x0e\x1f\x98\x1a\x44\x55\x96\xf3\xe3\x22\x4f\xe2\xe7\x8c\xe1\xde\xa4\x6d\x30\x41\x16\xe0\xd3\x12\x15\xb9\xed\x66\x3b\xf9\xeb\x3f\x75\x4a\x01\x2b\x6c\x26\x78\xf9\xf6\x6d\xf2\xff\x69\xfa\x23\x5b\x43\x6a\xb3\xf5\x68\xd8\xa6\x43\x3e\x13\x8e\xef\x09\x47\xeb\xec\x42\x65\x19\x71\x77\x8e\xe7\x54\x2c\xeb\x3e\x1f\xf4\x20\x24\xd7\x43\xb2\x0f\x64\x7c\x00\xdb\x56\xe6\xfc\xf2\xef\x79\x57\xad\xed\x9d\x3e\x1a\x0a\x54\xeb\xb4\xa0\xe2\xb1\x74\x32\x7a\x09\xd6\x19\xe8\xbd\x9e\x21\x6a\xbc\x4c\x9c\xa3\x48\xbb\xa1\x3a\x72\x12\x16\x2e\xb6\x7a\x20\x1c\xab\xfb\xc8\xbd\x1b\xa5\x1f\xef\x1e\xc6\xaa\x06\xf7\x51\x6d\xf9\xec\xc4\x03\xbb\xb2\xbe\x2a\x1f\x30\x99\x35\xe1\x32\x01\xf9\xf3\x1e\x38\x0d\xb3\x02\x67\x07\x52\x30\xa4\xdc\x54\x75\x8f\xe6\x98\xde\x73\x45\x86\x5b\x53\x82\x05\xdf\x62\xdc\x82\xab\xef\xa1\xc7\xad\x9d\x5c\x04\xd3\x5a\xba\x23\x55\x4f\x8a\x55\xda\xb6\x23\x6b\x8c\x92\xb1\x9c\xc2\xd9\x23\x27\xd1\x93\xe3\x56\x38\x02\x0d\x49\x91\xfc\x7a\x24\xf3\xa9\xda\xf7\xce\xf8\x51\x49\x0c\x1d\xbf\x03\xf4\xa0\xa7\xef\xd6\xea\x64\xda\x18\x4f\x30\xb9\xb4\xdf\x03\xd1\x0a\x89\xad\xbe\x07\xf7\x61\x88\xb7\x79\x58\x5d\xac\x11\xd8\x66\xc5\x08\xf7\x93\xa4\x13\x87\x49\xe2\xfa\xfd\x64\x52\xb8\xd2\xfd\x7d\x50\x9c\x4e\x6e\x0e\x8f\xd7\x73\xbe\x61\x7b\x0b\xc1\x67\xee\xad\x1e\xe1\x57\x12\x56\xf8\x8b\xb0\x8b\xfb\xc3\xaf\x00\xd9\x9d\x85\x92\xfa\x50\x70\xf8\x0d\x44\xd6\xa3\xf5\xf5\x3d\xb2\xa7\x0e\xba\xbe\x11\x31\x62\x14\xc6\x8c\xf0\xa7\x12\xb7\x47\x86\x19\x52\xc1\x93\xbd\x80\x0a\x7a\x5e\xe0\xdf\x9d\x88\xab\x05\x6a\x55\x64\xc5\xfa\x50\x61\x0c\xb8\xde\x86\x09\xec\x28\xb1\xde\x89\x8d\xe5\x6f\x4c\x2b\x53\x84\x6b\xe0\x7e\x37\xc7\x88\x6e\x29\xdd\x41\x64\x80\xa5\x9e\x5e\xb4\x1f\x86\x0a\x3e\x99\x38\x56\x99\x80\x4b\x90\x6d\x25\xdd\xf4\xa0\x9d\x9c\xe7\xae\x9f\x03\x39\xc0\x4f\xb9\x7d\x63\xdc\xb4\xb0\x57\xe1\x92\x41\x84\xec\x01\x1a\xc0\x21\x05\x5f\x80\x34\x2c\xb1\xee\x06\x6d\x97\xdc\x8d\xde\x32\x67\x0c\x01\x65\xee\x80\x4e\x3c\x49\xdd\x5d\x92\xa6\xf7\x26\x9f\x2d\x9c\x54\xcc\x4f\x6d\x21\xfe\x93\x8e\x9e\xc4\x5d\x50\xbf\x27\x59\xef\x39\x47\x4e\x65\xed\xcd\xac\xdb\x1d\xd2\xdb\xde\x1b\xc7\xcb\xfd\x51\x28\x3e\xf9\x8e\x66\x8a\x30\xa0\x10\x76\xc6\x82\x74\xd0\x15\xdd\xfc\xed\xc6\x3f\x2a\xca\x9e\xd3\x04\xa5\x6c\x4f\x58\x55\x2a\x43\xc0\x2c\xac\xd4\xbe\x2a\xea\x09\x8e\x92\x8b\x92\x0f\xc1\xb8\x7b\xc8\x57\xca\x08\x84\xe2\x32\x4d\xb7\xb7\xce\x98\x1e\x10\x5b\x90\x49\x37\x24\x35\x0d\x39\x7c\x74\x44\x24\x89\x86\x32\xb2\x31\x51\xbb\x98\x6d\xa3\x6e\x6f\xef\x5f\x37\x77\xa7\x59\xc2\x1c\x67\xcd\x55\x0e\x85\x43\x13\xca\x41\x19\xc4\x67\x9e\x61\x74\x09\x65\xb4\x12\xc1\x1b\xa6\xb9\xc1\x67\xae\xc0\x47\x07\x18\xdc\x1a\x50\x47\x51\x06\x55\xf3\x5e\xe8\x94\xc7\x44\x5e\x4b\xb1\x66\x69\x4f\x2c\xf1\xc7\xe0\x62\x14\x3d\x91\x18\x5f\xb3\xa6\xf7\x9b\xc3\x99\x28\x56\x8d\x10\x54\x8e\xfa\xa8\x9d\xf6\x8e\x79\x0e\x6b\x7e\x96\x12\x42\x5b\x71\xd7\x31\x86\x3e\x73\x10\x82\x94\xe9\x47\x80\x2d\x9d\xf4\x04\xa6\x0e\x29\x2f\x22\x3a\xc5\x36\x1a\xc1\xfd\xdd\xad\x3b\x2f\xa1\xd8\x64\xcd\x55\x7d\xc2\x64\x33\x5e\xa9\x6c\x46\xcf\xe5\xf3\x75\x71\x99\xfe\x37\x49\x96\xcb\xf2\x66\x8f\xf0\x6f\x60\xa2\x83\x7a\xb3\xf7\xa4\x81\x42\x08\x34\x6b\xd3\xa5\x4e\xd6\x46\x29\x7a\x4f\x37\xc9\xfd\xc7\x27\x9b\xf9\x4b\xa3\xfb\x5b\x7b\xa3\x48\xe0\xfa\xb0\x77\xdc\x98\x90\x6e\x99\xd0\xed\x07\x80\x49\x05\xe1\x88\x39\x4d\xac\x18\xf9\xa7\xbf\x4a\x89\x4b\xba\x15\x1b\x86\x78\x22\x99\xe7\x9e\x17\x4c\x53\x40\x98\x3b\x8f\x9a\x86\x03\x60\x9c\xed\xd9\x0f\xa3\x4c\x2e\x96\x88\x9d\xd5\xdb\x19\x1c\xf9\x31\xf4\x24\x02\xbe\x0d\xc8\xd7\x64\x03\xe2\x37\x5f\x69\x12\xc2\xd1\x42\x7c\xdb\xf6\x83\x6f\xf9\xea\xd0\x49\x6a\xf1\x63\x55\x1a\x00\x8f\xbb\xaf\x5d\x1c\x0e\xd1\xa6\x41\x63\x13\x70\x71\x68\x74\xd9\x53\x89\x48\xa5\x2b\x2d\x80\x2e\x3a\xab\xe8\xa7\x6c\x84\x4d\x41\xa3\x59\xd9\xe7\x79\xdf\xb2\x7e\xa1\x29\x8d\xe0\x0b\x9b\x4e\xd8\x9d\x56\xe1\xed\xef\xd1\x95\x55\x52\x0b\x7a\xff\x57\xd3\x4c\xca\x5c\xfa\x31\x09\xa1\x05\x06\x9d\x1a\x0a\x2a\x07\xe4\xd0\x8e\xfd\x30\x84\xb8\xc7\x91\xe7\x1f\xfa\x22\x87\x12\xc5\x37\x02\xe5\xda\xb8\x0c\x3c\x5e\x49\xdb\x53\x36\xb7\xf6\xc5\xf1\x8a\xe7\xcd\x74\x37\x05\x69\xe0\x3d\x4f\xb9\x55\x1a\x8d\x1e\x4f\x72\x93\x31\xba\xe5\xb3\x00\x4b\x1f\xfa\xf7\xdd\xc3\x29\x2d\x08\xf1\x81\x81\x93\xd5\xb5\x77\xbb\x3a\x66\x87\x5c\xa9\xfd\x6f\x64\xe4\x2a\x37\x81\xfd\x32\x29\x70\x2c\x7c\x67\x6f\x78\x59\x46\x0f\x8b\xf0\x73\xeb\xa3\x94\xf4\x81\xb6\x53\x05\xc1\x4a\x3b\x97\x0f\x59\x22\xc4\xc9\x4a\x9a\x9d\x63\x30\x5e\xb8\x28\x8a\xe8\xf5\xed\xa7\x32\x63\xe4\x87\xb4\xff\x78\xb3\x94\x3b\x58\xfc\xaa\x84\x20\xa9\x43\xd8\x77\x67\xe7\x36\x67\x9a\x7d\x6c\x40\x3f\x01\xcd\xf1\x63\x44\xaa\x08\xaf\xb4\x01\x39\xba\x19\x7d\xfb\x72\xab\xaa\x6a\xf3\x01\x09\x04\x50\xe3\x3f\x63\x21\xe6\xa6\x87\x14\x74\xdb\xb8\x03\x79\x69\xac\xb5\x1e\xca\x9a\x90\x8b\x30\x27\x48\x0f\x33\xb3\x53\x28\x22\xe1\x89\x0f\xcf\x46\xb0\x8c\x84\xca\xb4\x3b\xcf\x0f\x7a\xb8\x58\x8c\xf5\xb4\xe0\xb0\xfc\xb5\x03\x9d\x4c\xc3\x32\x0a\xea\x34\xaf\x08\x48\x03\x78\x57\x5f\x68\x43\x3d\xb0\xb9\xfc\x2b\x19\x12\x1f\xaa\x7c\x98\x26\x5e\x25\x8a\xa4\xcc\x26\x47\xd9\x11\x48\x3a\x9b\x9a\x35\xfe\x57\x8d\x7a\xf8\x73\x7a\x4b\xb7\x3c\xb8\x6a\xa4\x9d\x0e\x6e\xd7\xb0\x09\xed\xdf\x07\x6b\x51\xe3\x13\x8e\x2b\x95\xf1\xb7\x7d\xbb\x45\x3a\xeb\xd0\x80\xf9\xf4\xef\xbd\x3c\x43\xd3\x4a\x7d\x5b\x1e\x9e\x64\x67\x54\xfb\x7e\x3c\xcc\x7d\x98\xc2\x21\x85\xbe\x39\x8e\x44\x54\xf5\x16\x83\x60\x0a\xf7\x82\x7b\xec\x88\x0e\xb5\x64\x83\x61\x89\x15\xfc\xa4\x7c\xf4\x34\x59\x66\xe8\xb6\x61\x1d\x97\xfa\xc1\xad\xce\xe4\x59\xc7\xec\x7a\x2e\xbc\x40\xa2\xaf\x9d\xfa\x0b\x47\x5c\xff\xde\x18\x1f\x65\xe3\xa6\xee\x24\xdd\xf7\x34\x7c\x6a\xca\xb2\xa7\x55\x24\x28\x39\xcc\xbd\x50\xc5\xc4\x3d\x2f\xdc\x6c\x2f\x7b\xaa\x14\xe6\x1d\x85\x32\x95\xea\xd6\xcf\xaf\x2b\x04\x6a\xef\xb7\xc8\xe9\xdb\xc1\xce\xd5\x00\x87\x06\xd3\x19\x98\x95\x72\xaf\x2c\x5d\xdc\x91\xae\x54\xf9\xfd\x77\x96\xfe\x80\x6d\xde\x23\x5a\xd2\x23\x8d\xff\x51\x84\x6b\x05\x8a\x0c\x3c\xff\xf9\xc6\x32\x10\xff\x26\xf1\x75\xdb\xc7\x2e\xf4\xe6\x23\x0b\x2d\xd6\x6b\x1b\xcb\x2b\x58\xe4\x8b\x97\x4e\x2f\x01\xe9\x9f\xd6\x33\x42\x9c\x97\xb0\x31\x08\xc5\xa7\xff\x61\x1f\x5a\x6d\xf8\x2a\xdc\xf1\x15\x9b\x46\xfe\xe3\x09\x73\xce\x1f\x7f\xfb\x68\x95\x26\xce\x23\x3d\xa0\x31\x2c\xaf\x12\x85\x91\x0c\x9d\x9e\xd4\xa4\xdc\x2d\x2e\x8c\x8a\x56\x45\xf7\xec\xd9\x33\xac\xcb\xf0\x82\xc7\x8b\x3b\x62\x6f\x9e\xb2\x0a\xc7\xbf\xfe\x87\xe9\xb1\x97\xc6\x75\x53\x37\x1a\xa9\xa8\xa5\x09\x8b\x57\x64\x45\xbc\x58\x56\xc5\xf9\xf6\x02\x91\xcf\x1a\x5e\x67\x4d\x91\x99\xe4\x61\x7d\xa2\x7f\x35\xca\xa8\x68\x52\x9c\x69\x8a\x2a\x39\x9b\xc0\x9c\x15\x8a\xfb\x20\xdd\xc2\xf5\x1b\x7e\xfb\x19\x6e\xff\xac\xab\xba\x61\x98\xdc\xd6\xed\xb7\xf1\xe1\x94\xf2\xe4\xd0\xef\x76\x8f\xe7\xf5\x4e\x57\x5c\xfd\x52\x28\x16\x04\x65\xe1\x1d\xd8\xc1\x66\xeb\x96\x92\x86\xc3\x33\xd6\x10\x5f\x38\x8c\x9e\xc2\x4d\x7b\xb2\x04\xb0\x49\x43\x80\x49\xfa\xff\xce\xb3\xa7\xfb\xe4\x8f\x31\x4a\x07\xd3\x1f\x7e\xd6\xe1\x37\xe2\xce\xb1\x9f\x7f\xcf\xa8\xf5\x21\x49\x54\x73\xc1\xc8\xf0\xee\xe0\x4b\x99\xe0\xec\x80\x15\x3c\xdf\x83\x91\xcb\x5d\xf4\xab\x2c\xc0\x76\xef\x9e\x19\x6b\x18\xb8\x98\x19\x7b\x42\xab\x31\xdd\xe2\x08\x8d\xbe\xcc\xe6\x27\x27\xad\x04\x1c\x68\x71\x35\x4e\x1c\x38\xcf\x34\x12\xc2\xc0\x97\x0a\x42\x02\x0c\xa3\x38\x53\x77\x6e\x96\x22\x2d\x5e\xa7\x4b\xcd\x82\x4f\x00\x16\x7e\xdd\xd7\x0f\xbe\x7c\x81\xe9\xa2\xc5\x17\xad\x34\x09\x9c\xff\xde\x08\x49\x11\x3b\xce\xa0\xdc\x73\x3b\x57\x18\x49\x21\xb3\x2f\x30\xed\x0e\xbe\x08\x50\x08\x72\x14\xeb\xd1\x0f\x03\x94\x6a\x5f\x4c\x51\x70\xeb\xfe\x94\xb1\xc2\xb8\x29\xd3\x9b\x6d\x18\x8d\x8e\xe9\xa9\x39\x2f\x50\x3f\x5f\x36\xff\xdc\x23\x13\xf1\x3a\x2c\x1c\x30\xfe\xce\xf0\x10\x98\x94\x41\xc5\x2d\x87\xb9\xc6\x0e\x11\x3c\x73\xb6\x1b\x8f\xbb\x55\x03\xd0\xbb\x70\x73\x2f\x66\x60\x21\xa9\x62\x3f\x41\x32\xb7\x79\x94\xf5\xe3\x90\x81\xdb\x1f\xf5\xdc\xf9\x0b\x2b\x93\x8f\x69\xa0\x0d\x98\xae\x22\xfe\x96\x98\x08\xd9\x4f\xe1\xdc\xd3\xd4\xbf\x69\xe9\x59\xdd\xfc\x91\x1c\x6a\x7d\xce\x09\x99\x24\x9d\xb4\x3f\x9c\x2e\x15\x3d\x73\x07\x5d\xea\x69\xcf\xec\xb3\xe9\x42\xdc\x62\xbe\xc1\xd0\xd0\x26\x62\xc2\x75\x05\x49\x54\x43\x2e\xf1\x46\x38\x2f\x3b\xc8\x38\xb6\xdf\xa1\x9a\x82\x33\xdd\x68\x41\x9f\x71\xa7\x17\xd6\xc2\x5f\xd6\xe6\xb5\xdc\x55\xcc\x5b\x9c\xac\x78\xbf\x9b\x15\x09\x67\xb9\x27\xe0\x64\x63\xd6\xc1\xfc\xe0\xe1\x72\x98\x4a\x7e\x7c\xca\x80\x2c\x29\x8d\xac\xc7\xf2\xf5\xbc\xbc\xa3\x8f\xba\xbc\xe4\x3a\x32\x0b\xa4\xc3\x00\x0f\x33\xb7\x3a\x50\xbd\x35\x15\x98\x0c\x06\xca\x8a\x7f\x51\x21\xe2\x44\x34\x7f\x34\x9f\x56\x52\x22\x7d\x26\x51\x6c\x09\x48\xe7\x6b\x92\x8d\x32\x1f\x8b\x49\x59\x16\xec\x92\x71\xa8\x19\x91\xc1\x43\x4c\xdb\xf9\x0c\x76\x70\x1a\x3d\x05\xd2\xb7\xc7\x57\x17\x20\xcc\xcb\x3b\x41\x67\xf6\xc9\x37\xf1\xac\x4b\x72\x1a\x3a\x82\x0c\xaf\xe6\x56\x7d\xa5\x97\x26\xa6\x16\x96\xf8\xf8\xcd\x91\x1a\xd0\xb0\x9b\x68\x7e\xfb\x18\x85\x78\x6d\x9d\x88\x43\x18\xc4\x31\x7c\xe3\x66\x77\xd8\x8a\xa9\x0b\x4c\x45\x50\xaa\xea\x8d\xde\x35\x20\xfe\x6e\x09\xc5\xd4\xb0\xc4\xd1\xa1\x63\x8d\x5f\x50\x6e\xfd\x31\x4f\x19\xa9\xb3\x04\x50\x52\x44\x44\x33\x60\x22\x62\xd9\x0b\x4d\x23\xf8\xe8\x18\x98\x44\xda\x14\xfa\xca\x9e\xfd\x0f\x3d\x91\x70\xb8\x6c\xab\x05\x79\xfd\x6b\xe8\xe8\x47\xde\x98\x87\x1b\x23\xbc\x91\x40\xbf\x49\x7a\x64\x1a\x7d\x7f\xa9\x0e\x80\xe1\x44\xb3\xd5\x69\xcf\x0e\x5f\x7f\x72\xa6\x94\xaa\xb0\xb8\x11\x34\xaa\x02\xd5\xeb\x55\x10\xc0\xdc\xa6\x40\x7c\x93\x38\xaa\x4f\xb9\xae\xb6\xed\x48\x0e\x17\xf8\x74\x84\x05\x4f\x60\x7a\x8f\xd8\x81\xad\xcc\x7a\x43\x74\xd2\xe5\x82\x6a\x40\x92\x53\x8e\x51\x58\xaa\x74\xb2\x24\x8e\x38\xd9\xeb\x01\xda\xaa\x16\xdd\xcc\xbc\xf5\x5c\xae\xe6\x6f\xcf\xee\x6b\x84\x8b\x97\x89\x7a\xb2\xc3\x15\x6c\x46\x11\x96\x55\xca\xd5\xc7\x4c\x96\xd4\x02\xdf\x1f\x69\xcb\x46\xb4\x76\xa2\x82\xe0\x9f\x64\x55\x6a\xd8\x57\xcb\xe9\xe8\x99\xbe\xb3\x17\x63\x05\x63\x94\x10\xe9\xe7\x74\x00\x61\x37\xcc\xba\x12\xde\x19\x7a\x15\x32\xd6\x7b\xc7\x41\x36\x4f\x17\x8b\x4c\x7a\xb2\x6f\x8b\x4a\x02\xab\xfa\xb6\xb0\x46\x36\xf4\x4c\x92\x16\x7d\xcb\xb6\x39\xb0\x70\xd6\x6e\xbf\xdc\x51\x88\x60\x1c\x4d\x95\xb4\x6a\x22\x28\xf5\x04\x0a\x8b\x6c\x78\xd1\x30\x01\x4c\xaf\xe6\x01\xb1\x6a\x7f\xd9\x1f\xb8\x3b\x2b\x87\x00\x17\xd5\x06', 1) ================================================ FILE: environment/render/render_pic2.py ================================================ import traceback import pygame import traceback import os import copy from render.render_pic3 import Render as Render3 class Render: def __init__(self, size_x, size_y):#, o_detector_num, o_fighter_num, e_detector_num, e_fighter_num): self.render3 = Render3(size_x, size_y) return pygame.init() self.screen = pygame.display.set_mode((size_x, size_y)) image_path = os.getcwd() + '/environment/render/resource/' self.detector_red = pygame.image.load(image_path + 'detector-red-20.png') self.detector_blue = pygame.image.load(image_path + 'detector-blue-20.png') self.fighter_red = pygame.image.load(image_path + 'fighter-red-20.png') self.fighter_bue = pygame.image.load(image_path + 'fighter-blue-20.png') self.detector_red_rect = self.detector_red.get_rect() self.detector_blue_rect = self.detector_blue.get_rect() self.fighter_red_rect = self.fighter_red.get_rect() self.fighter_blue_rect = self.fighter_blue.get_rect() self.side1_detector = [] self.side1_fighter = [] self.side2_detector = [] self.side2_fighter = [] for index in range(o_detector_num): detector = {} detector['detector'] = copy.deepcopy(self.detector_red) detector['rect'] = copy.deepcopy(self.detector_red_rect) self.side1_detector.append(detector.copy()) for index in range(o_fighter_num): fighter = {} fighter['fighter'] = copy.deepcopy(self.fighter_red) fighter['rect'] = copy.deepcopy(self.fighter_red_rect) self.side1_fighter.append(fighter.copy()) for index in range(e_detector_num): detector = {} detector['detector'] = copy.deepcopy(self.detector_blue) detector['rect'] = copy.deepcopy(self.detector_blue_rect) self.side2_detector.append(detector.copy()) for index in range(e_fighter_num): fighter = {} fighter['fighter'] = copy.deepcopy(self.fighter_blue) fighter['rect'] = copy.deepcopy(self.fighter_blue_rect) self.side2_fighter.append(fighter.copy()) self.state_time = pygame.time.Clock() def dis_update(self, done, step, o_detector_data_obs_list, o_fighter_data_obs_list, e_detector_data_obs_list, e_fighter_data_obs_list, o_detector_data_reward_list, o_fighter_data_reward_list, e_detector_data_reward_list, e_fighter_data_reward_list, o_all_reward, e_all_reward): print('update') print((o_detector_data_obs_list)) print((o_fighter_data_obs_list)) print((o_detector_data_reward_list)) print((o_fighter_data_reward_list)) print((o_all_reward)) # b=a return self.render3.dis_update(done, step, o_detector_data_obs_list, o_fighter_data_obs_list, e_detector_data_obs_list, e_fighter_data_obs_list, o_detector_data_reward_list, o_fighter_data_reward_list, e_detector_data_reward_list, e_fighter_data_reward_list, o_all_reward, e_all_reward) color = 255, 255, 255 speed = [5, 5] self.detector_red_rect = self.detector_red_rect.move(speed) self.detector_blue_rect = self.detector_blue_rect.move(speed) self.screen.fill(color) self.screen.blit(self.detector_red, self.detector_red_rect) self.screen.blit(self.detector_blue, self.detector_blue_rect) pygame.display.update() self.state_time.tick(30) return def dis_update_done_font(self): print('update done font') return self.render3.dis_update_done_font() def dis_update_font(self, step, red_reward, blue_reward, red_detector_num, blue_detector_num, red_fighter_num, blue_fighter_num, red_missile_num, blue_missile_num): print('dis_update_font') return self.render3.dis_update_font(step, red_reward, blue_reward, red_detector_num, blue_detector_num, red_fighter_num, blue_fighter_num, red_missile_num, blue_missile_num) def dis_update_pos(self, o_detector_pos_list, o_fighter_pos_list, e_detector_pos_list, e_fighter_pos_list): print('dis_update_pos') return self.render3.dis_update_pos(o_detector_pos_list, o_fighter_pos_list, e_detector_pos_list, e_fighter_pos_list) def draw_course(self, center_point_x, center_point_y, course, course_r): print('draw_course') return self.render3.draw_course(center_point_x, center_point_y, course, course_r) def draw_fight_rela(self, center_point_x, center_point_y, striking_list, detector_list, fighter_list, line_colour): print('draw_fight_rela') return self.render3.draw_fight_rela(center_point_x, center_point_y, striking_list, detector_list, fighter_list, line_colour) ================================================ FILE: environment/world/__init__.py ================================================ __pyarmor__(__name__, __file__, b'\xec\x50\x8c\x64\x26\x42\xd6\x01\x10\x54\xca\x9c\xb6\x36\x84\x05\x6a\x0b\xf3\xc4\x16\x01\xb0\x49\xdd\x1c\xf4\xf8\x5a\xe1\xe0\xc4\xbe\x1b\x41\x5d\x2c\x21\x77\xb1\x53\x58\xd9\xeb\x22\xb3\x4c\x09\x12\xdb\x19\xce\x41\x3b\xeb\xb0\x0d\x17\x26\x12\xf9\x51\x94\x9e\xf1\xf5\x85\x77\xd0\x70\xdf\xce\x84\x05\x76\x90\x38\xf1\x10\xd7\x44\x53\x87\x32\x78\xa3\x65\xcd\x08\xcf\x45\x20\x53\x90\x31\x2c\x73\xa3\x04\x11\xfb\x6f\xf4\x36\xd1\x62\xd3\x8e\xcd\x57\x1f\xfa\x46\xe3\x9e\x0a\x32\xc9\x4d\x7d\xa1\x37\xba\xe0\xe3\x84\x87\xb1\x25\x00\x11\x98\x1a\x78\xdf\x6f\x4c\xd6\x38\x0a\x58\xb0\x25\xee\xf1\x9f\x45\x5f\x65\x92\xe1\x38\x5d\xe3\x1d', 1) ================================================ FILE: environment/world/config.py ================================================ class GlobalVar: api_raw = 0 api_spatial = 1 band_L = 0 band_S = 1 band_X = 2 band_fp_num = [20, 20, 10] detection_hit_probability = [0.2, 0.3, 0.9, 0.6] hit_detection_source_detector_L = 0 hit_detection_source_detector_S = 1 hit_detection_source_fighter = 2 hit_detection_source_passive = 3 img_obs_reduce_ratio = 10 j_coverage_angle = [45, 30, 10] j_max_range = [400, 400, 300] l_missile_dis = 120 l_missile_hit_probability = 0.8 l_missile_op_dec_ratio = 0.05 l_missile_op_req = 10 l_missile_type = 1 l_missile_num = 2 passive_loc_continuous_detection_req = 5 passive_loc_fighter_num_req = 4 r_coverage_angle_X = 120 # major lobe r_jammed_angle_mlob = [10, 10, 4] r_jammed_range_mlobe = [120, 150, 30] # side lobe r_jammed_range_slobe_aim = [220, 260, 80] r_jammed_range_slobe_block = [300, 320, 120] r_max_range = [400, 400, 180] s_missile_dis = 50 s_missile_hit_probability = 0.9 s_missile_op_dec_ratio = 0.05 s_missile_op_req = 4 s_missile_type = 2 s_missile_num = 4 def get_api_raw(): return GlobalVar.api_raw def get_api_spatial(): return GlobalVar.api_spatial def get_band_L(): return GlobalVar.band_L def get_band_S(): return GlobalVar.band_S def get_band_X(): return GlobalVar.band_X def get_band_fp_num(band): return GlobalVar.band_fp_num[band] def get_detection_hit_probability(typ): return GlobalVar.detection_hit_probability[typ] def get_hit_detection_source_detector_L(): return GlobalVar.hit_detection_source_detector_L def get_hit_detection_source_detector_S(): return GlobalVar.hit_detection_source_detector_S def get_hit_detection_source_fighter(): return GlobalVar.hit_detection_source_fighter def get_hit_detection_source_passive(): return GlobalVar.hit_detection_source_passive def get_img_obs_redu_ratio(): return GlobalVar.img_obs_reduce_ratio def get_j_coverage_angle(band): return GlobalVar.j_coverage_angle[band] def get_j_max_range(band): return GlobalVar.j_max_range[band] def get_l_missile_dis(): return GlobalVar.l_missile_dis def get_l_missile_hit_probability(): return GlobalVar.l_missile_hit_probability def get_l_missile_op_dec_ratio(): return GlobalVar.l_missile_op_dec_ratio def get_l_missile_op_req(): return GlobalVar.l_missile_op_req def get_l_missile_type(): return GlobalVar.l_missile_type def get_l_missile_num(): return GlobalVar.l_missile_num def get_passive_loc_continuous_detection_req(): return GlobalVar.passive_loc_continuous_detection_req def get_passive_loc_fighter_num_req(): return GlobalVar.passive_loc_fighter_num_req def get_r_coverage_angle_X(): return GlobalVar.r_coverage_angle_X def get_r_jammed_angle_mlob(band): return GlobalVar.r_jammed_angle_mlob[band] def get_r_jammed_range_mlobe(band): return GlobalVar.r_jammed_range_mlobe[band] def get_r_jammed_range_slobe_aim(band): return GlobalVar.r_jammed_range_slobe_aim[band] def get_r_jammed_range_slobe_block(band): return GlobalVar.r_jammed_range_slobe_block[band] def get_r_max_range(band): return GlobalVar.r_max_range[band] def get_s_missile_dis(): return GlobalVar.s_missile_dis def get_s_missile_hit_probability(): return GlobalVar.s_missile_hit_probability def get_s_missile_op_dec_ratio(): return GlobalVar.s_missile_op_dec_ratio def get_s_missile_op_req(): return GlobalVar.s_missile_op_req def get_s_missile_type(): return GlobalVar.s_missile_type def get_s_missile_num(): return GlobalVar.s_missile_num ================================================ FILE: environment/world/detection_calc.py ================================================ import copy import world.config as config import world.position_calc as position_calc def passive_detection_calc(self_context, enemy_fighter_context_list): ''' 根据敌方战机干扰机接收情况,更新自身上下文中无源侦测计数器,根据可侦测标准对自身上下文中可侦测指示赋值 需确定的问题:干扰机被动侦测雷达信号,能够全向接收雷达信号还是指向性接收?目前认为干扰机接收信号与干扰信号发射的指向性相同 :param self_context: :自身上下文 :param enemy_fighter_context_list: 敌方战机上下文列表 :return: ''' for index, enemy in enumerate(enemy_fighter_context_list): exist = False for j_receive in enemy['j_receive_list']: if self_context['id'] == j_receive['id']: exist = True if exist: self_context['detection_count_list'][index] += 1 else: self_context['detection_count_list'][index] = 0 def radar_visible_calc(self_context, enemy_detector_context_list, enemy_fighter_context_list): ''' 计算自身雷达的可见敌方目标,将自身上下文中雷达可见目标列表更新 规则: 1. 当一个雷达收到n个干扰机干扰时,在干扰机方向±5°范围内收到主瓣干扰,在其他方向收到副瓣干扰 2. 若有n个干扰机对一个雷达进行干扰,只要有瞄准式干扰存在,则雷达整体受到瞄准式副瓣干扰影响,若无瞄准式干扰存在,则雷达受到阻塞式干扰影响 3. 预警机雷达为全向探测,战机雷达为扇形方向探测 :param self_context:自身上下文 :param enemy_detector_context_list:敌方预警机上下文列表 :param enemy_fighter_context_list:敌方战机上下文列表 :return: 新发现detector数量和fighter数量 ''' radar_list = [] r_band = self_context['r_band'] o_pos_x = self_context['pos_x'] o_pos_y = self_context['pos_y'] course = self_context['course'] r_max_range = config.get_r_max_range(r_band) # 预警机雷达为全向探测 if r_band == config.get_band_L() or r_band == config.get_band_S(): for unit in (enemy_detector_context_list + enemy_fighter_context_list): # 战机已被摧毁 if not unit['alive']: continue e_pos_x = unit['pos_x'] e_pos_y = unit['pos_y'] distance = position_calc.get_distance(o_pos_x, o_pos_y, e_pos_x, e_pos_y) if distance <= r_max_range: radar_list.append(unit) # 战机雷达为扇形方向探测 else: r_coverage_angle_X = config.get_r_coverage_angle_X() r_angle_low_bound = course - r_coverage_angle_X/2 r_angle_up_bound = course + r_coverage_angle_X/2 for unit in (enemy_detector_context_list + enemy_fighter_context_list): # 战机已被摧毁 if not unit['alive']: continue e_pos_x = unit['pos_x'] e_pos_y = unit['pos_y'] distance = position_calc.get_distance(o_pos_x, o_pos_y, e_pos_x, e_pos_y) angle = position_calc.angle_cal(o_pos_x, o_pos_y, e_pos_x, e_pos_y) # the coverage angle cross the horizon line if r_angle_low_bound < 0 and r_angle_up_bound > 0: if distance <= r_max_range and (angle >= (r_angle_low_bound + 360) or angle <= r_angle_up_bound): radar_list.append(unit) elif r_angle_low_bound < 360 and r_angle_up_bound > 360: if distance <= r_max_range and (angle >= r_angle_low_bound or angle <= (r_angle_up_bound - 360)): radar_list.append(unit) else: if distance <= r_max_range and angle >= r_angle_low_bound and angle <= r_angle_up_bound: radar_list.append(unit) # 计算雷达新发现目标的数量,用于确定无人机的奖励 new_detector_count = 0 new_fighter_count = 0 id_list = [] for item in self_context['radar_visible_list']: id_list.append(item['id']) for item in radar_list: if not item['id'] in id_list: if item['id'] > len(enemy_detector_context_list): new_fighter_count += 1 else: new_detector_count += 1 self_context['radar_visible_list'].clear() for radar in radar_list: enemy = {} enemy['id'] = radar['id'] if radar['r_band'] == config.get_band_L() or radar['r_band'] == config.get_band_S(): enemy['type'] = 0 else: enemy['type'] = 1 enemy['pos_x'] = radar['pos_x'] enemy['pos_y'] = radar['pos_y'] self_context['radar_visible_list'].append(enemy) return new_detector_count, new_fighter_count # TODO: uncomplete def under_jam_check(rader_x, radar_y, radar_band, jammer_x, jammer_y, jammer_course): ''' 判断雷达是否处于干扰机的干扰范围内 :param rader_x:雷达机横坐标 :param radar_y: 雷达机纵坐标 :param radar_band: 雷达波段 :param jammer_x: 干扰机横坐标 :param jammer_y: 干扰机纵坐标 :param jammer_course: 干扰机航向 :return: True:处于干扰范围, False: 不处于干扰范围 ''' print('under_jam_check') result = null return result ================================================ FILE: environment/world/em_battle.py ================================================ import copy import random import numpy as np import world.config as config from render.render_pic import Render import configuration.reward as reward import world.strike_calc as strike_calc import world.position_calc as position_calc import world.detection_calc as detection_calc class BattleField: def __init__(self, size_x, size_y, o_detector_list, o_fighter_list, e_detector_list, e_fighter_list, max_step=5000, render=False, render_interval=1, random_pos=False, log=False, random_seed=-1): ''' 环境类初始化 :param size_x: 横向尺寸 :param size_y: 纵向尺寸 :param o_detector_list: 势力1预警机配置 :param o_fighter_list: 势力1战机配置 :param e_detector_list: 势力2预警机配置 :param e_fighter_list: 势力2战机配置 :param max_step:最大step,0:无限制 :param render:显示控制 :param render_interval:render显示间隔 :param random_pos:初始位置配置(False: 势力1在左,势力2在右;True:上下左右随机初始位置,双方相对) :param log: 日志开启指示,False:无日志,其他值:日志文件夹名称 :param random_seed: 随机数种子值,-1:重新生成,其他:种子值 ''' self.battlefield_size_x = size_x self.battlefield_size_y = size_y self.e_detector_num = len(e_detector_list) self.e_fighter_num = len(e_fighter_list) self.o_detector_num = len(o_detector_list) self.o_fighter_num = len(o_fighter_list) self.step_count = 0 self.max_step = max_step self.done = False self.render_flag = render self.render_interval = render_interval self.random_seed = random_seed self.random_pos = random_pos self.log = log self.render_obj = Render(self.battlefield_size_x, self.battlefield_size_y) self.log_obj = None if random_seed != -1: random.seed(random_seed) self.random_obj = random self.e_detector_property_list = e_detector_list self.e_detector_reward = [0] * self.e_detector_num self.e_detector_status_list = [] self.e_fighter_property_list = e_fighter_list self.e_fighter_reward = [0] * self.e_fighter_num self.e_fighter_status_list = [] self.e_passive_detection_enemy_list = [] self.e_strike_list = [] self.e_game_reward = 0 self.o_detector_property_list = o_detector_list self.o_detector_reward = [0] * self.o_detector_num self.o_detector_status_list = [] self.o_fighter_property_list = o_fighter_list self.o_fighter_reward = [0] * self.o_fighter_num self.o_fighter_status_list = [] self.o_passive_detection_enemy_list = [] self.o_strike_list = [] self.o_game_reward = 0 self.side1_detector_action = [] self.side1_fighter_action = [] self.side2_detector_action = [] self.side2_fighter_action = [] pos_x, pos_y, o_course, e_course = position_calc.pos_generate(self.o_detector_num, self.o_fighter_num, self.e_detector_num, self.e_fighter_num, \ self.battlefield_size_x, self.battlefield_size_y, self.random_obj, self.random_pos) for index in range(self.o_detector_num + self.o_fighter_num): status = {} status['id'] = index + 1 status['alive'] = True status['pos_x'] = pos_x[index] status['pos_y'] = pos_y[index] status['course'] = o_course status['radar_enable'] = True status['radar_fp'] = 1 status['radar_visible_list'] = [] status['passive_location_ind'] = False status['detection_count_list'] = [0 for i in range(self.e_fighter_num)] if index >= self.o_detector_num: status['radar_enable'] = False status['l_missile_left'] = config.get_l_missile_num() status['s_missile_left'] = config.get_s_missile_num() status['j_enable'] = False status['j_fp'] = 0 status['j_receive_list'] = [] self.o_fighter_status_list.append(status.copy()) else: self.o_detector_status_list.append(status.copy()) for index in range(self.e_detector_num + self.e_fighter_num): status = {} status['id'] = index + 1 status['alive'] = True status['pos_x'] = pos_x[index+self.o_detector_num+self.o_fighter_num] status['pos_y'] = pos_y[index+self.o_detector_num+self.o_fighter_num] status['course'] = e_course status['radar_enable'] = True status['radar_fp'] = 1 status['radar_visible_list'] = [] status['passive_location_ind'] = False status['detection_count_list'] = [0 for i in range(self.o_fighter_num)] if index >= self.e_detector_num: status['radar_enable'] = False status['l_missile_left'] = config.get_l_missile_num() status['s_missile_left'] = config.get_s_missile_num() status['j_enable'] = False status['j_fp'] = 0 status['j_receive_list'] = [] self.e_fighter_status_list.append(status.copy()) else: self.e_detector_status_list.append(status.copy()) for index in range(self.e_detector_num + self.e_fighter_num): action = {} action['course'] = e_course action['r_iswork'] = True action['r_fre_point'] = 1 if index >= self.e_detector_num: action['r_iswork'] = False action['j_iswork'] = False action['j_fre_point'] = 0 action['hit_target'] = 0 action['missile_type'] = 0 self.side2_fighter_action.append(action.copy()) else: self.side2_detector_action.append(action.copy()) for index in range(self.o_detector_num + self.o_fighter_num): action = {} action['course'] = o_course action['r_iswork'] = True action['r_fre_point'] = 1 if index >= self.o_detector_num: action['r_iswork'] = False action['j_iswork'] = False action['j_fre_point'] = 0 action['hit_target'] = 0 action['missile_type'] = 0 self.side1_fighter_action.append(action.copy()) else: self.side1_detector_action.append(action.copy()) def get_alive_status(self, o_detector_status_list, o_fighter_status_list, e_detector_status_list, e_fighter_status_list): side1_detector_alive_num = 0 side1_fighter_alive_num = 0 side1_l_missile_left = 0 side1_s_missile_left = 0 side2_detector_alive_num = 0 side2_fighter_alive_num = 0 side2_l_missile_left = 0 side2_s_missile_left = 0 for item in o_detector_status_list: if item['alive']: side1_detector_alive_num += 1 for item in o_fighter_status_list: if item['alive']: side1_fighter_alive_num += 1 side1_l_missile_left += item['l_missile_left'] side1_s_missile_left += item['s_missile_left'] for item in e_detector_status_list: if item['alive']: side2_detector_alive_num += 1 for item in e_fighter_status_list: if item['alive']: side2_fighter_alive_num += 1 side2_l_missile_left += item['l_missile_left'] side2_s_missile_left += item['s_missile_left'] return side1_detector_alive_num, side1_fighter_alive_num, side1_l_missile_left, side1_s_missile_left, \ side2_detector_alive_num, side2_fighter_alive_num, side2_l_missile_left, side2_s_missile_left def get_done(self): ''' 获取当前done :return: done: True, False ''' return self.done def get_obs_raw(self): ''' 获取当前obs :return:obs detector data obs:{'id':编号, 'alive': 存活, 'pos_x': 横坐标, 'pos_y': 纵坐标, 'course': 航向, 'r_iswork': 雷达开关, 'r_fre_point': 频点, 'r_visible_list': 雷达可见敌人} fighter data obs:{'id':编号, 'alive': 存活, 'pos_x': 横坐标, 'pos_y': 纵坐标, 'course': 航向, 'r_iswork': 雷达开关, 'r_fre_point': 频点, 'r_visible_list': 雷达可见敌人, 'j_iswork': 干扰机开关, 'j_fre_point': 干扰机频点, 'j_recv_list': 干扰机发现信号源, 'l_missile_left': 远程导弹余量, 's_missile_left': 中程导弹余量} ''' side1_detector_obs_list = [] side1_fighter_obs_list = [] side2_detector_obs_list = [] side2_fighter_obs_list = [] dict_key_list = ['id', 'alive', 'pos_x', 'pos_y', 'course', 'r_iswork', 'r_fre_point', 'r_visible_list'] list_key_list = ['id', 'alive', 'pos_x', 'pos_y', 'course', 'radar_enable', 'radar_fp', 'radar_visible_list'] for index in range(self.o_detector_num): side1_detector_obs_dict = {} for key_index in range(len(dict_key_list)): side1_detector_obs_dict[dict_key_list[key_index]] = self.o_detector_status_list[index][list_key_list[key_index]] side1_detector_obs_dict['last_reward'] = self.o_detector_reward[index] side1_detector_obs_dict['last_action'] = self.side1_detector_action[index] side1_detector_obs_list.append(side1_detector_obs_dict.copy()) for index in range(self.e_detector_num): side2_detector_obs_dict = {} for key_index in range(len(dict_key_list)): side2_detector_obs_dict[dict_key_list[key_index]] = self.e_detector_status_list[index][list_key_list[key_index]] side2_detector_obs_dict['last_reward'] = self.e_detector_reward[index] side2_detector_obs_dict['last_action'] = self.side2_detector_action[index] side2_detector_obs_list.append(side2_detector_obs_dict.copy()) dict_key_list += ['j_iswork', 'j_fre_point', 'j_recv_list', 'l_missile_left', 's_missile_left'] list_key_list += ['j_enable', 'j_fp', 'j_receive_list', 'l_missile_left', 's_missile_left'] for index in range(self.o_fighter_num): side1_fighter_obs_dict = {} for key_index in range(len(dict_key_list)): side1_fighter_obs_dict[dict_key_list[key_index]] = self.o_fighter_status_list[index][list_key_list[key_index]] side1_fighter_obs_dict['striking_list'] = [] side1_fighter_obs_dict['striking_dict_list'] = [] for item in self.o_strike_list: if item['attacker_id'] == self.o_fighter_status_list[index]['id']: side1_fighter_obs_dict['striking_list'].append(item['target_id']) striking_dict = {} striking_dict['target_id'] = item['target_id'] if item['target_id'] > self.e_detector_num: striking_dict['pos_x'] = self.e_fighter_status_list[item['target_id']-self.e_detector_num-1]['pos_x'] striking_dict['pos_y'] = self.e_fighter_status_list[item['target_id']-self.e_detector_num-1]['pos_y'] striking_dict['type'] = 1 else: striking_dict['pos_x'] = self.e_detector_status_list[item['target_id']-1]['pos_x'] striking_dict['pos_y'] = self.e_detector_status_list[item['target_id']-1]['pos_y'] striking_dict['type'] = 0 side1_fighter_obs_dict['striking_dict_list'].append(striking_dict.copy()) side1_fighter_obs_dict['last_reward'] = self.o_fighter_reward[index] side1_fighter_obs_dict['last_action'] = self.side1_fighter_action[index] side1_fighter_obs_list.append(side1_fighter_obs_dict.copy()) for index in range(self.e_fighter_num): side2_fighter_obs_dict = {} for key_index in range(len(dict_key_list)): side2_fighter_obs_dict[dict_key_list[key_index]] = self.e_fighter_status_list[index][list_key_list[key_index]] side2_fighter_obs_dict['striking_list'] = [] side2_fighter_obs_dict['striking_dict_list'] = [] for item in self.e_strike_list: if item['attacker_id'] == self.e_fighter_status_list[index]['id']: side2_fighter_obs_dict['striking_list'].append(item['target_id']) striking_dict = {} striking_dict['target_id'] = item['target_id'] if item['target_id'] > self.o_detector_num: striking_dict['pos_x'] = self.o_fighter_status_list[item['target_id']-self.o_detector_num-1]['pos_x'] striking_dict['pos_y'] = self.o_fighter_status_list[item['target_id']-self.o_detector_num-1]['pos_y'] striking_dict['type'] = 1 else: striking_dict['pos_x'] = self.o_detector_status_list[item['target_id']-1]['pos_x'] striking_dict['pos_y'] = self.o_detector_status_list[item['target_id']-1]['pos_y'] striking_dict['type'] = 0 side2_fighter_obs_dict['striking_dict_list'].append(striking_dict.copy()) side2_fighter_obs_dict['last_reward'] = self.e_fighter_reward[index] side2_fighter_obs_dict['last_action'] = self.side2_fighter_action[index] side2_fighter_obs_list.append(side2_fighter_obs_dict.copy()) side1_joint_obs_list = {} side1_joint_obs_list['strike_list'] = self.o_strike_list side1_joint_obs_list['passive_detection_enemy_list'] = [] side2_joint_obs_list = {} side2_joint_obs_list['strike_list'] = self.e_strike_list side2_joint_obs_list['passive_detection_enemy_list'] = [] return (side1_detector_obs_list, side1_fighter_obs_list, side1_joint_obs_list, side2_detector_obs_list, side2_fighter_obs_list, side2_joint_obs_list) def get_reward(self): # TODO: uncomplete return self.get_reward_list() def get_reward_list(self): ''' 获取当前reward :return:o_detector_reward:o方预警机动作回报,o_fighter_reward:o方战机动作回报,o_game_reward,o方输赢回报 e_detector_reward:e方预警机动作回报,e_fighter_reward:e方战机动作回报,e_game_reward,e方输赢回报 ''' return self.o_detector_reward, self.o_fighter_reward, self.o_game_reward, \ self.e_detector_reward, self.e_fighter_reward, self.e_game_reward def reset(self): self.step_count = 0 self.done = False self.e_detector_reward = [0] * self.e_detector_num self.e_detector_status_list.clear() self.e_fighter_reward = [0] * self.e_fighter_num self.e_fighter_status_list.clear() self.e_passive_detection_enemy_list.clear() self.e_strike_list.clear() self.e_game_reward = 0 self.o_detector_reward = [0] * self.o_detector_num self.o_detector_status_list.clear() self.o_fighter_reward = [0] * self.o_fighter_num self.o_fighter_status_list.clear() self.o_passive_detection_enemy_list.clear() self.o_strike_list.clear() self.o_game_reward = 0 self.side1_detector_action.clear() self.side1_fighter_action.clear() self.side2_detector_action.clear() self.side2_fighter_action.clear() pos_x, pos_y, o_course, e_course = position_calc.pos_generate(self.e_detector_num, self.e_fighter_num, self.o_detector_num, self.o_fighter_num, \ self.battlefield_size_x, self.battlefield_size_y, self.random_obj, False) for index in range(self.o_detector_num + self.o_fighter_num): status = {} status['id'] = index + 1 status['alive'] = True status['pos_x'] = pos_x[index] status['pos_y'] = pos_y[index] status['course'] = o_course status['radar_enable'] = True status['radar_fp'] = 1 status['radar_visible_list'] = [] status['passive_location_ind'] = False status['detection_count_list'] = [0 for i in range(self.e_fighter_num)] if index >= self.o_detector_num: status['radar_enable'] = False status['l_missile_left'] = config.get_l_missile_num() status['s_missile_left'] = config.get_s_missile_num() status['j_enable'] = False status['j_fp'] = 0 status['j_receive_list'] = [] self.o_fighter_status_list.append(status.copy()) else: self.o_detector_status_list.append(status.copy()) for index in range(self.e_detector_num + self.e_fighter_num): status = {} status['id'] = index + 1 status['alive'] = True status['pos_x'] = pos_x[index+self.o_detector_num+self.o_fighter_num] status['pos_y'] = pos_y[index+self.o_detector_num+self.o_fighter_num] status['course'] = e_course status['radar_enable'] = True status['radar_fp'] = 1 status['radar_visible_list'] = [] status['passive_location_ind'] = False status['detection_count_list'] = [0 for i in range(self.o_fighter_num)] if index >= self.e_detector_num: status['radar_enable'] = False status['l_missile_left'] = config.get_l_missile_num() status['s_missile_left'] = config.get_s_missile_num() status['j_enable'] = False status['j_fp'] = 0 status['j_receive_list'] = [] self.e_fighter_status_list.append(status.copy()) else: self.e_detector_status_list.append(status.copy()) for index in range(self.e_detector_num + self.e_fighter_num): action = {} action['course'] = e_course action['r_iswork'] = True action['r_fre_point'] = 1 if index >= self.e_detector_num: action['r_iswork'] = False action['j_iswork'] = False action['j_fre_point'] = 0 action['hit_target'] = 0 action['missile_type'] = 0 self.side2_fighter_action.append(action.copy()) else: self.side2_detector_action.append(action.copy()) for index in range(self.o_detector_num + self.o_fighter_num): action = {} action['course'] = o_course action['r_iswork'] = True action['r_fre_point'] = 1 if index >= self.o_detector_num: action['r_iswork'] = False action['j_iswork'] = False action['j_fre_point'] = 0 action['hit_target'] = 0 action['missile_type'] = 0 self.side1_fighter_action.append(action.copy()) else: self.side1_detector_action.append(action.copy()) # self.env.reset() def set_surrender(self, side): ''' 投降 :param side: 0: side1, 1:side2, 2: both :return: ''' self.done = True if side == 0: self.o_game_reward = reward.get_reward_totally_lose() self.e_game_reward = reward.get_reward_totally_win() if side == 1: self.o_game_reward = reward.get_reward_totally_win() self.e_game_reward = reward.get_reward_totally_lose() if side == 2: self.o_game_reward = reward.get_reward_draw() self.e_game_reward = reward.get_reward_draw() def show(self): obs_data = self.get_obs_raw() self.render_obj.dis_update(self.done, self.step_count, obs_data[0], obs_data[1], obs_data[3], obs_data[4], self.o_detector_reward, self.o_fighter_reward, self.e_detector_reward, self.e_fighter_reward, self.o_game_reward, self.e_game_reward) def step(self, side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action): ''' 接收动作信息执行step,可支持array和list两种形式动作 :param side1_detector_action: :param side1_fighter_action: :param side2_detector_action: :param side2_fighter_action: :return: ''' self.side1_detector_action = copy.deepcopy(side1_detector_action) self.side1_fighter_action = copy.deepcopy(side1_fighter_action) self.side2_detector_action = copy.deepcopy(side2_detector_action) self.side2_fighter_action = copy.deepcopy(side2_fighter_action) self.step_count += 1 # self.env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) # generate detector and fighter context e_detector_context_list = [] o_detector_context_list = [] e_fighter_context_list = [] o_fighter_context_list = [] for index in range(self.e_detector_num): self_context = {} self_context.update(self.e_detector_property_list[index]) self_context.update(self.e_detector_status_list[index]) e_detector_context_list.append(self_context.copy()) for index in range(self.o_detector_num): self_context = {} self_context.update(self.o_detector_property_list[index]) self_context.update(self.o_detector_status_list[index]) o_detector_context_list.append(self_context.copy()) for index in range(self.e_fighter_num): self_context = {} self_context.update(self.e_fighter_property_list[index]) self_context.update(self.e_fighter_status_list[index]) e_fighter_context_list.append(self_context.copy()) for index in range(self.o_fighter_num): self_context = {} self_context.update(self.o_fighter_property_list[index]) self_context.update(self.o_fighter_status_list[index]) o_fighter_context_list.append(self_context.copy()) # update strike list for index in range(self.o_fighter_num): print(side1_fighter_action[index]) if self.o_fighter_status_list[index]['alive'] and side1_fighter_action[index]['hit_target']: target_id = side1_fighter_action[index]['hit_target'] missile_type = side1_fighter_action[index]['missile_type'] enemy_status = None enemy_index = 0 if missile_type == config.get_s_missile_type(): if self.o_fighter_status_list[index]['s_missile_left'] <= 0: continue self.o_fighter_status_list[index]['s_missile_left'] -= 1 if missile_type == config.get_l_missile_type(): if self.o_fighter_status_list[index]['l_missile_left'] <= 0: continue self.o_fighter_status_list[index]['l_missile_left'] -= 1 if target_id > self.e_detector_num + self.e_fighter_num: target_id -= self.e_detector_num + self.e_fighter_num if target_id > self.e_detector_num: enemy_status = self.e_fighter_status_list enemy_index = target_id - self.e_detector_num - 1 else: enemy_status = self.e_detector_status_list enemy_index = target_id - 1 valid = strike_calc.strike_act_validation_and_initiation(missile_type, o_fighter_context_list[index], o_detector_context_list, enemy_status[enemy_index], self.o_strike_list) if valid: self.o_fighter_reward[index] += reward.get_reward_strike_act_valid() else: self.o_fighter_reward[index] += reward.get_reward_strike_act_invalid() for index in range(self.e_fighter_num): if self.e_fighter_status_list[index]['alive'] and side2_fighter_action[index]['hit_target']: target_id = side2_fighter_action[index]['hit_target'] missile_type = side2_fighter_action[index]['missile_type'] enemy_status = None enemy_index = 0 if missile_type == config.get_s_missile_type(): if self.e_fighter_status_list[index]['s_missile_left'] <= 0: continue self.e_fighter_status_list[index]['s_missile_left'] -= 1 if missile_type == config.get_l_missile_type(): if self.e_fighter_status_list[index]['l_missile_left'] <= 0: continue self.e_fighter_status_list[index]['l_missile_left'] -= 1 if target_id > self.o_detector_num + self.o_fighter_num: target_id -= self.o_detector_num + self.o_fighter_num if target_id > self.o_detector_num: enemy_status = self.o_fighter_status_list enemy_index = target_id - self.o_detector_num - 1 else: enemy_status = self.o_detector_status_list enemy_index = target_id - 1 valid = strike_calc.strike_act_validation_and_initiation(missile_type, e_fighter_context_list[index], e_detector_context_list, enemy_status[enemy_index], self.e_strike_list) if valid: self.e_fighter_reward[index] += reward.get_reward_strike_act_valid() else: self.e_fighter_reward[index] += reward.get_reward_strike_act_invalid() # strike judge, update the status list, the strike list and the reward for item in reversed(self.o_strike_list): fighter_index = item['attacker_id'] - self.o_detector_num - 1 fighter_radar_visible_list = self.o_fighter_status_list[fighter_index]['radar_visible_list'] target_index = item['target_id'] - self.e_detector_num - 1 if item['target_id'] > self.e_detector_num \ else item['target_id'] - 1 strike_result = strike_calc.strike_judge(item, fighter_radar_visible_list, self.random_obj) if strike_result == 1: # hit success if item['target_id'] > self.e_detector_num: # missile hit a fighter self.o_fighter_reward[fighter_index] += reward.get_reward_strike_fighter_success() self.e_fighter_status_list[target_index]['alive'] = False self.e_fighter_reward[target_index] += reward.get_reward_fighter_destroyed() else: # missile hit a detector self.o_fighter_reward[fighter_index] += reward.get_reward_strike_detector_success() self.e_detector_status_list[target_index]['alive'] = False self.e_detector_reward[target_index] += reward.get_reward_detector_destroyed() self.o_strike_list.remove(item) if strike_result == -1: self.o_strike_list.remove(item) # hit failed if item['target_id'] > self.e_detector_num: self.o_fighter_reward[fighter_index] += reward.get_reward_strike_fighter_fail() else: self.o_fighter_reward[fighter_index] += reward.get_reward_strike_detector_fail() for item in reversed(self.e_strike_list): fighter_index = item['attacker_id'] - self.e_detector_num - 1 fighter_radar_visible_list = self.e_fighter_status_list[fighter_index]['radar_visible_list'] target_index = item['target_id'] - self.o_detector_num - 1 if item['target_id'] > self.o_detector_num \ else item['target_id'] - 1 strike_result = strike_calc.strike_judge(item, fighter_radar_visible_list, self.random_obj) if strike_result == 1: # hit success if item['target_id'] > self.o_detector_num: # missile hit a fighter self.e_fighter_reward[fighter_index] += reward.get_reward_strike_fighter_success() self.o_fighter_status_list[target_index]['alive'] = False self.o_fighter_reward[target_index] += reward.get_reward_fighter_destroyed() else: #missile hit a detector self.e_fighter_reward[fighter_index] += reward.get_reward_strike_detector_success() self.o_detector_status_list[target_index]['alive'] = False self.o_detector_reward[target_index] += reward.get_reward_detector_destroyed() self.e_strike_list.remove(item) if strike_result == -1: self.e_strike_list.remove(item) # hit failed if item['target_id'] > self.o_detector_num: self.e_fighter_reward[fighter_index] += reward.get_reward_strike_fighter_fail() else: self.e_fighter_reward[fighter_index] += reward.get_reward_strike_detector_fail() # update position and jammer for index in range(self.e_detector_num): if self.e_detector_status_list[index]['alive']: prev_pos_x = self.e_detector_status_list[index]['pos_x'] prev_pos_y = self.e_detector_status_list[index]['pos_y'] course = side2_detector_action[index]['course'] speed = self.e_detector_property_list[index]['speed'] self.e_detector_status_list[index]['pos_x'], self.e_detector_status_list[index]['pos_y'] = \ position_calc.pos_update(prev_pos_x, prev_pos_y, course, speed, self.battlefield_size_x, self.battlefield_size_y) self.e_detector_status_list[index]['course'] = course self.e_detector_status_list[index]['radar_enable'] = side2_detector_action[index]['r_iswork'] self.e_detector_status_list[index]['radar_fp'] = side2_detector_action[index]['r_fre_point'] for index in range(self.e_fighter_num): if self.e_fighter_status_list[index]['alive']: prev_pos_x = self.e_fighter_status_list[index]['pos_x'] prev_pos_y = self.e_fighter_status_list[index]['pos_y'] course = side2_fighter_action[index]['course'] speed = self.e_fighter_property_list[index]['speed'] self.e_fighter_status_list[index]['pos_x'], self.e_fighter_status_list[index]['pos_y'] = \ position_calc.pos_update(prev_pos_x, prev_pos_y, course, speed, self.battlefield_size_x, self.battlefield_size_y) self.e_fighter_status_list[index]['course'] = course self.e_fighter_status_list[index]['radar_enable'] = side2_fighter_action[index]['r_iswork'] self.e_fighter_status_list[index]['radar_fp'] = side2_fighter_action[index]['r_fre_point'] self.e_fighter_status_list[index]['j_enable'] = side2_fighter_action[index]['j_iswork'] self.e_fighter_status_list[index]['j_fp'] = side2_fighter_action[index]['j_fre_point'] self.e_fighter_status_list[index]['radar_fp'] = side2_fighter_action[index]['r_fre_point'] self.e_fighter_status_list[index]['radar_fp'] = side2_fighter_action[index]['r_fre_point'] self.e_fighter_status_list[index]['radar_fp'] = side2_fighter_action[index]['r_fre_point'] self.e_fighter_status_list[index]['radar_fp'] = side2_fighter_action[index]['r_fre_point'] for index in range(self.o_detector_num): if self.o_detector_status_list[index]['alive']: prev_pos_x = self.o_detector_status_list[index]['pos_x'] prev_pos_y = self.o_detector_status_list[index]['pos_y'] course = side1_detector_action[index]['course'] speed = self.o_detector_property_list[index]['speed'] self.o_detector_status_list[index]['pos_x'], self.o_detector_status_list[index]['pos_y'] = \ position_calc.pos_update(prev_pos_x, prev_pos_y, course, speed, self.battlefield_size_x, self.battlefield_size_y) self.o_detector_status_list[index]['course'] = course self.o_detector_status_list[index]['radar_enable'] = side1_detector_action[index]['r_iswork'] self.o_detector_status_list[index]['radar_fp'] = side1_detector_action[index]['r_fre_point'] for index in range(self.o_fighter_num): if self.o_fighter_status_list[index]['alive']: prev_pos_x = self.o_fighter_status_list[index]['pos_x'] prev_pos_y = self.o_fighter_status_list[index]['pos_y'] course = side1_fighter_action[index]['course'] speed = self.o_fighter_property_list[index]['speed'] self.o_fighter_status_list[index]['pos_x'], self.o_fighter_status_list[index]['pos_y'] = \ position_calc.pos_update(prev_pos_x, prev_pos_y, course, speed, self.battlefield_size_x, self.battlefield_size_y) self.o_fighter_status_list[index]['course'] = side1_fighter_action[index]['course'] self.o_fighter_status_list[index]['radar_enable'] = side1_fighter_action[index]['r_iswork'] self.o_fighter_status_list[index]['radar_fp'] = side1_fighter_action[index]['r_fre_point'] self.o_fighter_status_list[index]['j_enable'] = side1_fighter_action[index]['j_iswork'] self.o_fighter_status_list[index]['j_fp'] = side1_fighter_action[index]['j_fre_point'] self.o_fighter_status_list[index]['radar_fp'] = side1_fighter_action[index]['r_fre_point'] self.o_fighter_status_list[index]['radar_fp'] = side1_fighter_action[index]['r_fre_point'] self.o_fighter_status_list[index]['radar_fp'] = side1_fighter_action[index]['r_fre_point'] self.o_fighter_status_list[index]['radar_fp'] = side1_fighter_action[index]['r_fre_point'] # update radar visible list and reward list for index in range(self.e_detector_num): if self.e_detector_status_list[index]['alive']: new_detector_num, new_fighter_num = detection_calc.radar_visible_calc(e_detector_context_list[index], o_detector_context_list, o_fighter_context_list) self.e_detector_reward[index] += reward.get_reward_radar_detector_detector()*new_detector_num + \ reward.get_reward_radar_detector_fighter() *new_fighter_num for index in range(self.e_fighter_num): if self.e_fighter_status_list[index]['alive']: new_detector_num, new_fighter_num = detection_calc.radar_visible_calc(e_fighter_context_list[index], o_detector_context_list, o_fighter_context_list) self.e_fighter_reward[index] += reward.get_reward_radar_fighter_detector()*new_detector_num + \ reward.get_reward_radar_fighter_fighter()*new_fighter_num for index in range(self.o_detector_num): if self.o_detector_status_list[index]['alive']: new_detector_num, new_fighter_num = detection_calc.radar_visible_calc(o_detector_context_list[index], e_detector_context_list, e_fighter_context_list) self.o_detector_reward[index] += reward.get_reward_radar_detector_detector()*new_detector_num + \ reward.get_reward_radar_detector_fighter() *new_fighter_num for index in range(self.o_fighter_num): if self.o_fighter_status_list[index]['alive']: new_detector_num, new_fighter_num = detection_calc.radar_visible_calc(o_fighter_context_list[index], e_detector_context_list, e_fighter_context_list) self.o_fighter_reward[index] += reward.get_reward_radar_fighter_detector()*new_detector_num + \ reward.get_reward_radar_fighter_fighter()*new_fighter_num # 判断是否仍旧存活,并更新奖励 for index in range(self.o_detector_num): if self.o_detector_status_list[index]['alive']: self.o_detector_reward[index] += reward.get_reward_keep_alive_step() for index in range(self.o_fighter_num): if self.o_fighter_status_list[index]['alive']: self.o_fighter_reward[index] += reward.get_reward_keep_alive_step() for index in range(self.e_detector_num): if self.e_detector_status_list[index]['alive']: self.e_detector_reward[index] += reward.get_reward_keep_alive_step() for index in range(self.e_fighter_num): if self.e_fighter_status_list[index]['alive']: self.e_fighter_reward[index] += reward.get_reward_keep_alive_step() # 判断是否回合结束 if self.render_flag: if not self.step_count % self.render_interval: self.show() side1_detector_alive_num, side1_fighter_alive_num, side1_l_missile_left, side1_s_missile_left, side2_detector_alive_num, side2_fighter_alive_num, side2_l_missile_left, side2_s_missile_left = \ self.get_alive_status(self.o_detector_status_list, self.o_fighter_status_list, self.e_detector_status_list, self.e_fighter_status_list) # 完胜 if side1_detector_alive_num == 0 and side1_fighter_alive_num == 0: self.done = True self.e_game_reward = reward.get_reward_totally_win() self.o_game_reward = reward.get_reward_totally_lose() if side2_detector_alive_num == 0 and side2_fighter_alive_num == 0: self.done = True self.o_game_reward = reward.get_reward_totally_win() self.e_game_reward = reward.get_reward_totally_lose() # 导弹余量为0,作战单元多者获胜 if side1_l_missile_left == 0 and side1_s_missile_left == 0 and \ side2_l_missile_left == 0 and side2_s_missile_left == 0: self.done = True if side1_fighter_alive_num > side2_fighter_alive_num: self.o_game_reward = reward.get_reward_win() self.e_game_reward = reward.get_reward_lose() elif side1_fighter_alive_num < side2_fighter_alive_num: self.e_game_reward = reward.get_reward_win() self.o_game_reward = reward.get_reward_lose() else: self.o_game_reward = reward.get_reward_draw() self.e_game_reward = reward.get_reward_draw() # 达到最大step,剩余作战单元数量多的获胜 if self.step_count >= self.max_step: self.done = True if side1_fighter_alive_num > side2_fighter_alive_num: self.o_game_reward = reward.get_reward_win() self.e_game_reward = reward.get_reward_lose() elif side1_fighter_alive_num < side2_fighter_alive_num: self.e_game_reward = reward.get_reward_win() self.o_game_reward = reward.get_reward_lose() else: self.o_game_reward = reward.get_reward_draw() self.e_game_reward = reward.get_reward_draw() return True ================================================ FILE: environment/world/load_map.py ================================================ import json class Map: def __init__(self, map_path): with open(map_path, 'r') as f: self.map_info = json.load(f) def get_map_size(self): return self.map_info['size_x'], self.map_info['size_y'] def get_unit_num(self): return self.map_info['side1_detector_num'], self.map_info['side1_fighter_num'], self.map_info['side2_detector_num'], self.map_info['side2_fighter_num'] def get_unit_property_list(self): return self.map_info['side1_detector_list'], self.map_info['side1_fighter_list'], self.map_info['side2_detector_list'], self.map_info['side2_fighter_list'] ================================================ FILE: environment/world/log.py ================================================ __pyarmor__(__name__, __file__, b'\x84\x50\x8c\x64\x26\x42\xd6\x01\x9b\xd2\x9b\x61\xa4\xcf\x1b\xb9\x58\x14\x4e\xf4\xc9\x56\x7f\x05\x09\x60\x6c\x85\x00\xce\x8a\x18\xd2\xea\x08\x62\x16\x2e\x37\x8f\x4b\x57\x68\xbf\x3f\x4a\x96\xe6\x9f\x2c\xe2\x67\x2b\xdf\xa8\xbf\xd6\x62\x39\x3c\x52\x72\x59\x44\x63\xf7\x7d\x97\x44\x15\x9e\x36\x7d\x0f\x8d\xb7\xf0\xc7\x32\x9e\xde\xd1\xb2\x24\x65\x89\x00\x4e\x49\xaf\x7a\x33\x28\x52\x2c\xe2\x38\xb5\x8c\xf7\x49\xff\x97\xbe\xc9\x87\x34\xb8\xc0\xd2\xa4\x2c\x96\xc6\x52\xf0\xaf\x7f\xc2\x16\xe2\x4a\xbd\xf7\x3d\xea\x0c\x3a\x5e\x10\xe2\x32\xf7\x39\x24\x1e\x67\xaf\x22\xe1\xa5\x48\x4d\x14\x8d\xdc\x15\x9e\x46\x32\x84\x43\xdc\x9d\x19\x23\x85\xba\xa7\xf5\x67\x9f\x59\x78\x6e\xc9\x31\x69\x58\x25\x16\x6b\xf2\x53\x25\x53\x23\x62\x3c\xa1\x7e\x6a\x1f\x83\x14\xaa\x97\x0c\x18\x97\xde\x94\x72\xb2\xa9\xc0\x9e\xb8\xdc\x4c\xcf\xad\x97\xe9\xad\xc4\x42\x31\x63\x84\x2c\x7e\xdd\xca\x06\xcf\xa7\xc4\x4c\xd3\x09\xa3\x50\x0e\x51\x44\x61\x8f\xa6\x1a\x21\xe4\x21\x89\x90\x12\xd0\x96\xd3\x8e\xf5\xc1\x1f\x04\x0e\x95\xa3\xea\xbd\x2b\x2f\x6f\xbf\x21\x63\xad\x0c\x16\x99\xfc\x27\xd6\xe0\x78\x25\x3f\x0c\x77\xe5\xc7\x6d\xe6\xa7\xbd\x12\x5d\x07\x10\xa5\xa0\x0a\x3d\xb0\xcf\x4d\xa3\xaf\x3d\x0c\x08\x47\xc1\xd7\x97\x03\x0a\xcb\xe1\x93\xdf\x36\x07\x0d\x9b\x1d\x40\xb3\x56\xe1\x49\xce\xff\x2d\x98\x67\x9e\x90\xd7\xf6\x98\x6c\x86\x4f\x76\x5a\xfb\x55\xe7\x12\xfd\x66\x5b\x47\xd4\xab\x24\x75\x2c\x37\xa8\x90\x8b\x9d\x2d\x8f\x1c\xa0\x31\x87\xaa\xa9\xd0\x53\x20\x75\x2e\xf0\x2a\xd9\xbd\x51\x35\xed\x54\x97\x05\xfa\xe3\x40\x0d\x33\x0e\x5f\x08\xdd\x28\xff\x9f\x6b\x3b\x1c\x6f\xd2\x92\x82\x6f\xf2\xd7\x2b\xc2\xff\x7a\xe7\x37\x5b\x2c\x68\xf8\xbe\x3c\xeb\x2c\x6c\xa5\xb5\x44\x3d\x76\x66\xaa\x75\xbe\x9a\xcb\x68\x49\x3c\x41\x8b\x83\xf8\x01\x55\x37\x41\xdd\x9a\xea\xbc\x28\xc1\xb7\x42\x48\x1e\xa2\xfc\x49\x54\x5d\x25\x2e\x5d\x7a\x62\xf2\x4c\x36\xdd\x7c\x69\xd9\x99\x13\x7e\x67\x36\x13\xf0\x64\x1c\xa4\x04\xc5\x34\x91\x0b\x90\x5c\x05\x56\x0f\xb4\xe1\xfe\x36\x10\x36\x69\x7e\x35\x8a\xfd\xb0\x2a\xa1\x90\x46\x81\x23\xa9\x66\xc2\xbe\xae\xce\xbe\x89\x77\x74\x4a\x53\xe6\xf1\x4b\x2d\x60\x98\x25\x01\xe3\x6b\x1a\x9a\x40\xb1\x0c\xb8\x36\x06\x05\x27\x4f\xc5\xef\xba\x33\xd4\xd5\x3a\xc2\xcc\xa8\x2b\x07\xf9\x6c\x35\x9d\x38\x04\x47\x91\x4a\x1a\x9e\x15\x14\x6a\xd5\x2a\x2e\xd8\x9a\xfb\x55\xa4\xb8\x42\xbc\x38\xf7\xf2\x3c\x8d\x7d\x5a\xb0\x7c\x13\x04\x46\x53\xa2\x20\xaf\xf2\x32\xf4\x21\x9c\x5d\xe8\x3e\xb9\x20\xad\x20\xba\xa0\x8b\x05\x88\x84\xcf\x8a\xa2\xd7\x0f\x01\x2a\xac\x0a\xa5\xd2\xad\x49\x7e\xf4\x7c\xb1\xd1\x16\x4b\x81\x4d\x36\x7a\xa8\x50\xc4\x65\xbd\x50\x4a\x0b\x15\x30\x14\xd7\x9c\xfa\x33\xc2\xf3\xc8\x18\x7c\xf4\x7d\xa1\x11\xe0\x22\xe4\x4f\x2e\xb1\x61\x15\x03\x5f\xe9\x6c\xeb\x56\x04\xab\xa5\x95\x5b\x37\x40\xe7\x94\x16\x4c\x88\xf4\xe9\x72\xc1\x05\x21\x30\xe7\x68\x3d\x24\x05\x3e\x78\x0a\xf0\x44\xcf\xb5\xcd\x0d\xc6\x2a\xc0\x6a\x4d\x22\x9c\xef\xaa\x54\x40\x2f\x03\x81\x09\xe6\xc1\x10\x6d\x5d\xca\x3d\x81\xaf\xba\x07\xc0\xcf\xcb\x45\xbb\x45\x3a\x42\xc6\xfe\x99\x5c\x4f\xc9\x20\xd7\x8a\xef\xd5\x4b\x21\x1e\xe6\x79\x48\xb4\x94\xc2\xe8\x26\x82\xf1\x98\x2e\x5e\xc2\x9c\xb7\xca\xa1\x91\xc1\x1a\xfa\x80\x2a\x80\x2f\x0b\x0f\xab\xf7\x39\x39\x5e\x1a\x6d\xa8\x1d\xe8\x17\x8e\xad\x0e\xf9\x2d\x5f\x15\x5b\x38\xa9\xb0\x03\xec\x17\x0b\xe0\x4e\x7b\x9c\x8c\x13\x54\x51\xff\xe3\xd8\xd7\x80\xc5\x66\x15\xb7\x18\x39\x08\xe8\xa8\x6a\xf8\xb3\x17\xb5\x7f\xef\x06\x2f\x53\xe8\x35\xfe\xe8\x9d\x3f\x94\xe2\xed\x44\x36\xa2\x83\x2f\x23\x05\x69\xa2\x45\x63\x58\xc6\x99\xb5\xb5\x84\x07\x11\xce\x3c\xfb\x0a\xd0\x2e\xee\x5e\x1c\x79\xac\xe5\x2d\xa7\xa8\x1f\x87\xfb\xfa\xb1\x62\x7c\x12\x90\x62\x59\xa0\x8c\x08\xc3\x8c\xca\xb1\xee\x3a\x3f\x5c\x23\xa9\xb2\xe6\x6f\xc7\x53\xeb\x66\x1f\x6a\x03\xba\x37\xf0\xa9\xb0\x62\x46\x8b\x2d\xee\xe9\x89\xa6\xa7\x2a\x2e\xab\xfd\x59\xc7\x66\xc7\x0e\x72\x3a\xc6\xc1\xf6\x62\x47\x45\xf0\xa9\xef\x4d\x8c\x92\x76\xc7\xd5\xab\xd1\x3d\x20\x7c\xd2\x91\xde\x12\x9f\x3c\x5a\x53\xf1\x4a\x4c\x3a\x25\x86\x2b\xd1\x80\x4e\x11\x90\x7d\x08\x95\x80\x85\x0d\x2a\xb6\x4e\xf5\x42\xae\x60\x6c\x34\xea\x8b\x77\x7b\x8d\xf0\xf2\x12\x7e\xd7\x77\x34\xb1\x55\xb7\xde\x1e\x8b\xef\x94\x22\x56\x66\x8d\x91\x06\x05\xfc\xfa\x81\x00\xef\x88\x80\xa6\x9f\x60\x3b\x6a\xa8\x54\xf7\x53\x30\x3a\x4c\xba\x3f\xff\xe0\x16\x5e\x8b\xca\xc7\x35\x0a\x0d\x67\x21\xd2\x82\x58\x2d\xc2\x59\x63\x54\xe4\x34\x8e\xa2\xdf\xcb\x0c\xb3\xd4\x87\x40\xb2\x23\x05\xcd\x4e\x15\xc9\xd0\xc6\xa8\x22\x19\x58\xff\xfb\xf9\x19\xdf\x85\x60\x1d\x8c\xa7\x06\xd5\x6d\x0d\x1e\x5f\xec\x3e\xfb\x6c\x3c\x01\x16\x57\xb9\x4d\xf3\x2e\xba\x0e\xd6\x5a\x5d\xc9\xe3\x52\xec\x87\x0f\x35\x31\xdf\x37\xd4\x7d\x63\xfe\xd2\xa8\xa4\x64\x1a\x63\x22\x11\xa9\x27\x5d\xd7\xe8\xb1\x9d\x1d\x1e\x7c\x05\x29\x9f\x04\x5d\x55\x0b\x05\xdb\x7e\xf7\x83\x73\x24\xa6\x75\x1f\x2d\xea\x99\x74\x5e\x73\xdd\xd8\x59\xc8\x7e\x11\xcd\x46\x94\x47\xee\x3b\x65\x25\x5a\x64\xf1\xf3\x30\x7e\x24\xf1\x0d\x4c\x74\xa0\x53\xe1\x0e\xc3\xd0\xc1\x3f\x35\xa9\x2d\x87\x1b\x3c\x9c\xdb\x84\x2c\x4a\x0e\xc3\x68\xea\x3b\x71\x56\x3a\x45\xf3\xe2\x5f\xe5\x54\x95\xad\x0c\x7c\xb5\xe1\xbd\xa6\x07\xf6\x36\x36\x14\xd0\x75\x7b\x9d\x76\x85\xc9\x2e\x80\xc7\x42\xe7\xf7\x07\xbc\xfa\x94\x3d\xf4\x82\xf2\x8d\x89\xd5\xca\xb6\x23\xf8\x4b\xee\xd1\x49\xe0\x7f\x83\xbf\xa4\x43\x2b\x8e\x89\x05\xf9\x9c\x88\x2c\x33\x0e\x84\x17\x18\xda\x07\x1e\x15\xc3\x30\xe2\x10\x6e\xc6\x70\xdb\x9a\x2e\xf2\x20\xfd\x24\x34\x03\x06\x87\xda\xfe\xd1\xa8\xeb\x00\xf3\x12\xc8\x04\xc5\x56\x08\xe3\x64\xe0\x9f\x0c\x3e\x56\xf0\x70\xac\x89\x70\x9e\x68\xd8\x00\x92\x47\xe2\xfd\x14\x49\x11\x06\xcf\xbd\x5c\xf6\x56\xfd\x05\xb8\x91\x14\x08\x19\x06\xec\xc0\xc1\x71\x4e\x52\x42\x3e\x49\xe3\x26\xa6\x13\x47\x25\x1e\xbd\x11\xbc\x54\xf6\x15\x52\xd7\x72\xe4\xf7\x14\x6c\x75\x30\x22\xcd\x0c\x88\x3e\x4f\x75\x39\x9c\xc7\x37\xfc\x29\x8a\xff\x49\x36\x70\x14\xce\x1e\xd2\x8e\x1a\xb7\xd4\x53\x7a\x30\x4f\xe4\xcd\xe9\x05\x93\xf4\x19\x79\x91\xf4\x98\x4d\x20\xd3\xfd\x0e\xba\xe0\x72\x9d\x5e\x3d\x3d\x5d\x53\x63\x0c\x48\xa8\x2a\xea\xf4\x2f\xa9\xf4\xb4\xf6\x8e\xfb\x76\xa9\xab\x96\x7f\x79\x22\x7d\x38\xd2\xb6\xe4\xc2\x9f\x0e\x9e\xcd\x13\x5c\xdc\x52\x5f\x46\xab\x45\x4e\x6f\xf3\xdd\x05\x7d\xd9\x3d\x87\x62\x2d\xa4\xa6\xbf\xc3\x4b\x6c\xc2\x1c\xcd\xf4\x01\x83\x17\xf3\xe5\x4f\xd0\xa8\x50\x92\x15\x7d\x7a\x83\x26\x6e\xe0\x52\x81\x32\xba\x4d\x90\x0f\x2f\xfe\x3e\x4f\x32\x14\x36\x91\x17\x79\x03\x25\x3b\x35\xd8\x00\x4b\x3e\x70\x21\x91\x1d\xc3\x87\x90\x34\x3d\x3e\x91\x09\x47\x65\xbd\x5e\x74\x24\xa7\xb4\xb2\x77\x28\xfe\xb0\xde\xcc\xcb\x78\x66\x63\x70\x86\xff\xcb\x41\xf9\x6f\x2d\xbd\xe4\xfe\x1f\x74\x5a\x17\x90\x03\x5f\x48\xe2\x7f\xa6\xae\x31\xa4\x42\x42\xe4\x6f\xd6\xf4\x69\xf3\x0e\xe3\x8d\x49\x30\xa4\xef\xbd\x3c\x44\x64\x62\x98\xcd\xd6\xc2\x74\x1a\xaa\x8a\x89\xaa\xc7\x7e\xd5\x87\x1e\xca\x37\xf1\xf9\x35\xca\x41\xee\xe7\x50\x49\x5c\x35\xd5\x1f\x4b\x49\xbf\x37\x96\xea\x57\x49\x79\x4c\x78\xe7\x6e\xb1\x01\xff\x4e\x2f\x73\xe5\x9a\x50\x6b\x4c\x29\x40\xc9\xf4\xd8\x20\xf4\x70\xd4\x24\xdb\x53\x71\xb9\x09\xc0\xee\x37\xb5\x08\xe5\x32\x18\x40\x22\xcb\xe4\x2c\x8a\x6e\xce\x21\x08\xa7\xac\x55\x86\x7f\x19\xf8\xd7\xcf\x69\x93\x2c\x2a\x6d\xb7\xee\x2b\x55\xe1\xd6\xd5\x59\xca\xdc\x58\xf1\x57\x90\x3e\x78\x72\xfd\x4c\x83\xfc\xd0\x94\x88\xc6\x61\xc4\x81\xbc\x7a\x06\x08\x25\x0b\x37\xb6\x11\x1f\x15\xb7\x00\x5f\x6e\xe8\x80\xbc\x1c\x92\xcc\xa6\x23\x31\x25\x84\xb1\x46\x2a\x78\x95\x87\x99\x58\xb3\x48\x8c\x5d\x58\x26\x3f\x48\xa1\xce\x8c\x9b\x1a\xb5\x63\x58\xdc\x6e\xd8\xdc\xad\xd3\xc5\xda\x8f\x6e\x28\x96\xd7\x56\x78\x61\x1e\x12\xc9\x52\x16\x73\x54\x04\xcb\x6e\x84\x82\x3a\x8a\x29\x51\xcd\xa1\x0e\xa8\x00\x71\x4f\xb9\xff\xfe\x5a\x04\x6a\xdf\x08\x40\x32\x72\xd1\xd3\x01\x18\x00\x9b\xac\x13\x8b\x7b\x1d\x4a\xb2\x54\xa5\xec\x88\x9d\xd2\xae\x1b\x86\x2f\xa5\x70\xa8\x70\x36\xc2\xe5\x95\x3d\x21\x0f\xf7\x49\x21\xa6\x53\x71\x62\x97\x72\xdf\x12\xa5\x11\xa2\x94\xde\xb3\xab\x0c\x27\x88\x19\xe1\xa4\xe4\x62\x58\x68\x82\xdc\x13\xf6\x03\xe7\x27\x1f\x97\x69\x71\x62\x4a\x4f\x15\xda\xe2\x41\x7c\x99\x83\xb1\x1e\x38\x7d\x7b\x45\x32\x9f\x6b\xb9\x4d\x02\xaa\x56\x7b\x76\xbe\x02\xf4\x08\x9d\x93\x95\x6c\xf8\xbf\x8d\xe4\xe5\x99\xf7\xa8\x04\xe4\x39\x26\x6e\x85\x17\x15\x9e\x23\xe8\x3f\x65\xab\xc1\x21\x12\xee\xda\xe0\xac\x0e\xd0\x26\x52\xd4\x45\xe6\x32\xc0\x99\x33\x43\x87\x0b\xd0\xe9\xd8\x20\x55\x6c\xa4\x80\x34\x2c\x07\x8e\x00\xf1\x5f\x59\x54\xb9\xa7\x3c\xb3\x9d\xd0\x30\xaa\xce\x9e\x5c\xc8\x47\xa0\xbd\xf3\xfa\x91\x11\xb7\x75\x30\xa9\x35\x14\x02\x69\xe4\xa5\xcd\xef\xe0\x9d\xb0\x1d\xb4\x21\x8f\x64\x9c\x51\x08\xd1\x50\x9e\x78\x5d\x47\x22\x47\x7d\x72\xcc\x64\xfe\xb2\xd0\x33\x59\x07\x40\xf0\x86\xd1\xe1\xe3\x6d\x65\xba\x29\x0b\x21\x81\xb4\xa0\xee\x83\xb3\x6a\x63\xd9\x7f\xf6\xef\xba\x9c\xf3\x57\x0e\xdb\xf9\xca\x66\x64\xdd\xbe\x02\xac\x44\x74\x77\x6a\x74\xa5\x63\x11\x1c\xbf\xff\xd5\x82\xd7\xcb\x5b\xce\x24\x97\x25\x70\xbe\x7a\xdd\x1b\xc2\x56\x4f\x84\xc7\xf0\x7f\x18\x0c\x63\x87\xd8\xa0\x90\x38\xd5\x02\xb0\xb0\xdd\x9f\x76\x72\x92\x62\x86\xd8\x28\xfa\x46\x88\x1a\x0c\x00\x25\xe6\xec\xf2\xb2\xb2\xe6\xa0\xbb\x75\x24\x57\x38\x2c\x61\xb4\x96\x28\x9e\x95\x57\x30\xae\x16\x33\x47\xdf\x3f\xff\x4e\x16\xaa\xa0\xaf\xaf\x78\x24\x86\xe1\xff\x50\x31\xd7\x96\x36\x10\x8b\x72\xdf\xbb\xd1\x82\x01\xf3\x66\x28\x66\xd8\x01\xbf\x49\xce\xa6\xe6\xac\xc5\x2c\x88\x2d\xc9\x6a\x8c\x61\xfa\x6b\x2d\x07\x9e\x94\xdc\x6f\x0b\xf2\x8d\x17\x2e\x24\x17\xa8\xbb\x70\xfa\x03\x67\x72\x5f\x76\xfb\x69\x4d\x41\xe7\xe3\x08\x15\xe2\x40\x60\x45\xc6\x2c\xb5\xdb\xb5\xb9\xd1\xa6\x46\x6a\x43\x4e\xef\xd4\x76\x35\x43\x18\x58\x98\x3c\x94\x8a\x00\x76\xd8\xe5\x9f\xd5\x3d\x8a\xd6\x11\x6b\x82\x20\x62\x79\x39\x75\x9e\x8e\x0c\x97\x3e\x70\x8a\xa0\x5f\x92\x53\x23\x16\xdf\x7d\xd5\x4d\xed\x48\x85\x54\x09\x53\x5d\x26\xe2\x41\x33\x6e\x89\x1a\x43\xe3\xa9\x40\xc5\xe9\x5a\xe1\x4e\x54\xac\x52\x04\x30\xf0\x3c\x1a\xa4\x62\xf5\x15\xf9\x26\xb1\xdd\xda\x2a\xf7\x9a\x6a\xe0\xb5\x3e\x6d\x93\xe3\xad\x4f\x46\xa3\x2e\xf5\x61\x70\x09\x00\x0f\xe1\x86\x60\xb4\xa0\x98\x5b\x25\xee\x8a\xfd\xea\x19\x68\xb8\xd9\xc3\xf5\x84\x20\x74\x2f\x4f\x75\x21\xe8\xd3\x1e\x24\x36\xf3\xb9\xed\x9e\xdd\xb8\xcd\x75\xdc\xcc\xa6\xfd\x72\x50\x22\xfe\xb7\xe5\x3f\xfb\x54\xdb\x6c\x1a\xd5\x61\x25\x59\xd9\xd6\xd6\x11\x4a\x14\x24\xa3\xb0\xab\x9e\x78\x73\x53\xe0\x17\x82\x17\x6d\x0a\xfb\x3d\x08\x90\x85\xfa\xa8\x18\x80\x3b\x49\xff\x62\x8c\xf7\x7e\x48\xc7\x20\xbe\x5e\x6c\xec\x1b\x7d\x0e\x65\x64\x40\x6a\xf9\xe9\x17\xf6\x37\x4f\xdb\x77\x99\xf7\x1b\xe5\x69\x2e\xdb\x7a\xd6\xaf\xc6\x73\x78\xe3\xa1\x37\x01\xb4\x07\x08\xaa\xf6\xd3\x8f\x79\xdb\x74\x7f\xb9\x7e\x9e\xc5\xe9\x51\x59\xa5\x61\xa0\xfd\x1b\x96\x50\x48\xd5\x40\xa9\x00\x60\xcb\x87\x40\x76\x3e\x71\x8e\x10\x2d\xca\xb7\x27\xab\xc5\xf4\xe5\xc0\xbf\x6f\x8c\x5d\xe5\x02\x98\xec\x9c\x79\x2c\xb9\x7a\xaa\xd2\x36\x99\xb4\xc6\x2a\x7a\x58\x5a\xcb\x74\x55\x98\x5c\x03\x78\x4c\x2b\x65\xc4\xdd\xb9\x49\x9a\x8c\xa5\x3e\xfa\xdc\x42\x3f\x1a\x8e\x94\x10\xaa\x7e\x6a\x95\xab\x65\x60\xef\x04\xb1\x55\xee\xe7\x60\x8e\xf4\xba\x34\xee\x28\x97\x4e\x86\x58\x64\x72\x3c\x46\xda\x9a\xd0\x97\x53\x5b\xe0\x65\xdb\xd2\xaf\x54\x81\x5a\x28\xa6\xf3\x18\x64\x05\x01\x85\x95\x60\x8a\xd3\xbb\x73\x61\x8e\x47\x9d\x11\x17\xea\x5b\x99\xaf\x9c\x4b\x8d\xa0\x36\xd7\xe7\xba\x89\x99\xf6\x98\x5c\xa5\x15\x67\x93\x25\x70\x33\xb1\x8f\x55\xb3\x24\x03\xa1\xc1\x7d\xcf\x78\xb2\x2c\x76\x89\x64\x63\x93\x7b\x95\xf2\x0a\x92\xcd\x4d\xe0\x2b\x41\xfa\x02\x26\xc8\x6e\xda\x61\xa4\xf7\xd4\x4b\x7a\x91\x45\x26\x84\x42\x2e\x82\x9c\xd5\xed\x5a\xc6\x46\xe6\x8e\x84\x61\x20\xf4\xdb\x33\x51\x5e\xa1\x87\x6f\xbf\xab\x12\xe9\x59\x30\x66\xde\x7b\xa5\xfe\x09\x6b\xcf\x21\x5f\xb6\xce\x7e\xea\xa3\xf3\x02\x43\xa9\x9a\x27\x67\x2a\x54\x8b\x22\x44\x92\x7f\x0c\x3c\xb2\xea\x0d\xf7\xcd\xbe\x2b\x0b\x83\x64\x76\x11\x57\x2a\xee\xa7\x98\x82\x17\x88\x8d\x25\x82\x9d\x91\xe6\xec\x7f\xb9\xb2\x70\x0a\x25\x18\x2f\x66\x3e\x97\x96\x7b\xe6\x70\x67\xa7\xd4\xe4\x6b\x19\x8f\xc3\x21\xb5\x5e\x7a\x39\x75\xdd\x26\x0a\xdd\x64\xbf\x29\x8c\xf9\x07\x19\xc4\x39\x44\xec\x28\xef\x30\x6c\xe8\xff\xa9\xbf\xcc\x99\x69\x31\xfc\x62\x36\x5c\x33\x25\x8e\x16\x22\x4b\x88\x76\x91\xa6\x98\x45\x76\xdc\x35\xa2\xad\x0f\xcf\x19\x11\xb8\x0d\xbd\x06\x04\x27\xf7\xc6\xc4\x86\x53\xd8\x7e\xda\x89\xf5\xc1\x9a\x9e\x9f\x1e\x34\x0c\x51\x55\xf2\xa5\x42\x66\xf8\xc7\x01\xd6\xc2\x86\xf5\x3b\xe2\xf7\xd6\x39\x23\x17\x38\x66\xb5\x91\xb3\x9e\xf7\x67\x83\x1e\x8e\x96\x50\xef\x9d\x15\x78\x2b\xb9\x58\xd2\x76\x5a\xac\x2d\x5f\x64\x4e\xa1\x53\x7d\x9a\xca\x1c\x0f\x74\xf3\x63\x42\x35\x99\x55\x6d\x8c\xe0\x10\xfd\x90\x98\xe0\x84\x46\x71\x60\xbc\x94\xb3\x0c\xbe\x78\x85\x5a\x74\x1e\xb0\x5d\xb2\xb1\x85\xfd\x79\x49\x01\x9f\xac\x54\xb9\x67\x6a\x4c\x75\xa5\x7e\x8e\x9d\x8a\x59\x34\xe0\xa1\xd4\xcf\x47\x03\x6e\xd1\x8a\xc0\xb9\x35\xe1\x85\x81\x3e\x7a\xc1\x45\x1e\xad\x2d\x62\xad\x1a\xda\xad\x7b\xa1\x85\x41\xb6\xa0\x75\xf7\x9a\xe4\x52\x6c\x98\x8b\xf3\xc5\x8e\x83\xe7\x26\xab\xf0\x4c\x28\x33\xd4\x5c\x5a\x9d\x11\xe3\x70\xb5\xf1\x70\xa7\xc3\x1a\xac\xbc\x7d\x23\x44\x38\xd6\x48\x8f\x3f\xa8\x6c\x67\xd6\x07\x77\x7b\x0e\x39\x45\x85\xe7\xc4\xd4\xbf\xdc\x28\x77\xe3\x69\xa4\xe3\xab\x95\x14\xcd\x13\x87\x09\x5f\x0c\xc1\x40\x53\x0d\x06\x4f\xab\xed\xfc\x9e\xe8\x73\x30\xe4\x4d\x5b\x6b\x8d\xfd\xf6\xf0\x76\xad\x0e\xec\xe7\xf4\x55\x76\xdf\x4c\x5d\x86\x1a\x05\x91\xdc\x80\x3f\xef\xc6\x65\x21\x44\xa4\x57\x25\xa6\x02\x18\xc0\x9e\x71\xab\x3b\x1c\xf3\xba\xac\xbb\x61\x99\xdd\x5b\xda\x86\xcb\xc5\xa2\x06\xdf\xc4\x5b\x82\x89\x01\x8f\x72\x8e\x1f\x69\xf5\xa8\xc0\x7e\x6e\x66\x42\x83\xd4\x05\x1d\x2f\x7e\xf1\x26\x45\xb7\xc7\x12\x03\x57\xd5\xbd\x5d\x8a\xab\x24\x3d\x91\x5f\xc7\x0b\xe3\xbc\x4a\xb5\xcc\x50\x21\xa6\xf6\xa5\xd2\x1c\x35\xe8\xea\xbb\x9f\x26\x23\x95\x7c\xfc\x46\x0b\x1a\x7e\x40\x0c\xf0\x76\x1a\xa8\x8b\x2e\x17\x6f\x54\xaa\x4c\x1f\x66\xeb\xc2\xf8\x10\x4d\x63\x98\x0b\xbd\x18\x5d\x5d\x10\xb8\x43\x98\x99\x19\x44\x5f\x2b\xf1\x85\x97\x2a\x62\x8b\x35\xd2\x43\x15\x3f\x58\x09\x51\xc9\x84\x0e\xe0\x9c\x2c\x13\x39\x7a\x8a\x81\xc2\xc0\x25\xb8\x91\x78\xd5\x74\xed\x99\x47\x10\xce\xdf\x43\x49\x66\x55\xe3\x7c\xac\x3c\x91\xd6\x8d\x94\x73\x3c\xdc\x7d\x38\xfd\x38\xab\x0b\x25\xb8\x40\x7e\xd7\xb4\x0a\x53\x92\x57\x27\xa5\x7c\xa0\xf8\xfc\x63\x49\x45\xb6\xb1\x5b\x8f\x1e\x47\x2e\x94\x25\x91\x95\xdd\xa5\xb6\x97\xf7\x2b\x40\x59\xda\x68\x74\xcd\xb3\xcd\x92\x96\xfd\x4b\x0f\x47\x80\x98\x10\xba\x86\xf7\x4f\xec\x4c\x92\x6c\xc4\x75\xc8\xed\x53\x4a\x53\xff\xd0\xfa\x08\xde\x94\x92\x0c\x7c\x15\x75\xd3\xe0\xd2\x2a\x94\xfb\xef\x3a\x28\xde\xf9\x7c\xdb\x6b\x9b\x65\x2e\x8f\xc0\x9a\x82\x33\xfa\x40\xbd\xf8\x8d\x40\xf5\x2e\x20\xb6\x77\x6e\x21\xbc\x79\xb3\xb8\x60\x82\xa1\x0a\x17\x37\xb5\xe6\x56\x54\x3a\x20\x37\xc5\x1e\xa3\x43\xb7\xf0\x69\xa9\x03\x6a\x21\x54\x94\x68\x47\x8e\x6c\x48\x92\xc0\x85\xea\x6f\x65\x60\x5e\x52\x52\xec\x00\xa1\x68\xd5\x4a\x46\xc6\x18\xa2\x3c\x49\x50\x00\x6c\xd9\xff\xa1\xcd\x77\x3f\x55\x5b\x9b\x2c\x10\xf1\x5e\x3b\xa7\x75\x5a\x15\x81\x4f\xaf\x91\xea\x49\xd1\x97\x45\x01\x5f\x18\xef\x3e\xd3\xe4\xef\xd4\xda\xda\x72\x26\xa4\xa3\xd4\xce\xe2\x98\xa8\x9c\x23\xc6\x8a\x8c\x9f\x2c\xef\xf8\xae\x62\x84\xcc\x72\x2b\x1d\xac\xa0\x70\x80\xee\x80\x4e\xdf\x05\xc0\x4c\xaa\xe4\xfd\x17\x19\xa1\x0e\xe4\xc0\xbc\x76\x20\x63\xb1\x84\x9a\x9b\xf4\x3b\xff\xa3\x31\x41\xec\x35\x95\x92\x9e\x6e\x31\x80\xb3\xf4\xb7\x39\x3e\x7b\x27\x51\xa8\x9d\xb2\x00\x98\x78\x84\x26\x17\x82\x84\x2f\x69\x5a\xe1\xd3\xb2\x12\xf3\x03\x52\xac\x57\x2b\xaf\xc6\x87\xbd\xc3\xcf\x54\xfe\xec\xd6\xf2\x58\x72\x1c\xbe\x2a\x9f\xc9\x6f\x41\xde\xb3\x06\x3a\x32\x07\x6d\x2a\xd0\xff\x37\xcb\x3e\x51\x8a\xb1\x30\x69\x86\x00\x35\x01\xd8\xb4\xc4\xf9\x60\x77\xb0\x68\x5b\x8b\xc7\x37\x35\x1a\x00\x43\x15\x9e\x00\xcb\xbc\x31\x20\xea\x19\x49\xf5\x5f\x88\x7a\x09\x61\x69\xc3\x04\xa2\x4f\xc0\xc7\xdb\xcc\x15\xf7\xc8\x13\x49\x70\xef\x76\xbe\xc7\xe5\x10\x29\x17\x50\x69\x6f\xff\x03\x7e\x92\x4a\x4f\x58\xe4\x67\x60\x31\xc5\x96\x96\x2a\x26\x2f\x72\x61\x9b\x3f\xe4\x48\x04\x94\x5b\x7e\x47\x58\x4c\x3a\xa8\x0b\xe9\xbd\x2c\xf9\x6f\xc0\xfa\x23\xbb\xb8\xef\x1a\xd9\x37\x3f\xab\xdb\xb5\x7d\xa4\xa0\xee\xc0\xe9\x50\x83\xe5\xbe\x17\xbd\x46\xca\xc8\x6d\x4b\xb9\x71\x29\x63\xd6\xd2\xd7\x40\x87\xfa\xd7\x61\xd9\x5f\x18\x31\x05\xff\xcf\x90\x43\x39\x53\xd6\xb8\x95\x68\xc5\xb2\xcd\xc9\x02\x52\xa9\xa2\x9e\x6f\xf3\x8c\x52\x00\x93\x3a\x24\x2a\x20\x59\x62\x35\xe2\xf6\x82\x3f\xd3\x24\xac\x94\xde\x75\xea\x65\x03\xae\xaa\xa1\xf6\x6d\x20\x11\xb4\x01\x6f\x55\xed\xf7\x9e\x11\x3a\xa8\x48\x64\x2f\xa8\xad\x32\x18\xff\xc2\x17\x0b\x95\x78\x85\x09\xac\x93\x40\x95\x38\x10\x4f\x91\x3d\xf2\xed\xe6\xb2\xaf\x15\x86\x22\x6d\x02\xf6\x3f\x93\xdc\x73\xe2\xdd\x8b\xa1\x95\x97\xe9\x77\xa7\x16\x13\xd1\xa6\x54\xfe\xcd\xa6\x8d\xb8\xe8\x83\x8f\xad\x63\x1e\x38\x35\x32\x58\x2f\xde\x68\x59\x8e\x24\xb0\x30\x50\xc4\x51\x74\x24\x08\x66\x25\xf8\x79\x86\xbc\x11\x12\x15\x15\x63\x7a\x11\xdd\x7a\x1b\xec\x6f\xde\xf7\xcb\x0f\xa1\x77\x08\x30\x3e\x46\x10\x86\x01\x71\x38\xf0\xa7\x95\xc9\x1d\xfd\x9d\x58\x97\x9f\x5c\xfe\x84\x6f\xe7\x3d\x51\x00\x2c\xf8\x0a\xe3\xd9\xdb\x67\x5d\x5c\xf2\x26\xc6\xb0\x83\x38\x48\x2d\xeb\x51\xf9\xaa\x9b\x4a\xbc\x01\xff\x62\xf3\x43\xe2\x82\x65\x95\xeb\x65\xa0\x56\xb0\xea\x97\x7c\xf3\x52\x57\xf8\x10\xdf\xe0\x9c\x32\x44\xef\xf7\x87\xdf\x72\xb0\xd5\xb4\x4e\x6c\x95\x38\xcb\x1a\x47\x6e\xbf\x49\x19\xaa\xfd\xe6\xb6\x71\x3d\xe1\x3c\x7c\x1b\x65\x75\xcb\xb4\xee\x27\x84\x8d\x9c\xc0\xd6\xd6\x5c\x7e\x22\xb8\x1a\x91\xaa\x8d\xd4\x65\x54\xa6\x02\x50\xe4\x96\x78\xcc\xe5\x8b\xd9\xa8\x8c\x9f\xd3\x05\xe8\x6c\x61\xfb\x2b\xd6\xd0\xb5\x0c\x92\x38\x84\x9a\x4f\x39\x67\x49\x46\x18\x26\x58\xbc\x04\x8d\x8e\xa7\x8e\x61\x98\x00\xd9\x90\xa4\xf5\xc2\xda\x85\x39\x42\xb6\x64\xbc\xcf\x25\x13\x5e\x36\xf7\xaf\xff\x99\x3a\xd4\x18\x2a\x63\x33\x79\x2e\x4e\xf0\x8e\x7a\x48\x08\xed\x06\x72\x06\x32\x31\x81\x81\x9d\x6c\xe7\x5f\xb9\xe4\x8e\xa7\xcb\x49\xbe\xdf\x75\x61\x26\x81\x32\x77\x23\x17\x01\x35\x5f\xe3\x70\x81\xa5\x7f\x69\x72\xbe\xcc\xc4\x87\x5a\x0e\x34\x86\x03\xf3\xf0\x92\xa8\xe7\x10\xb9\x00\x37\x1c\x42\x93\x4d\x10\xf0\x53\x72\x4c\xac\x92\x80\x5b\x82\xbe\x9f\xa7\x1d\x5c\x9e\xc6\x4c\xf5\x33\x91\xd8\xaf\x69\x32\xbb\x13\x5c\x11\xd4\x4c\x18\x34\xb4\x5b\x68\x1a\x1d\x12\xf3\xe2\x68\x7e\x65\x24\xa3\x6b\xad\x5e\x0b\x11\x4c\x0e\xd0\x1a\x45\xa6\xfd\xf5\x5a\x04\x04\x2b\x95\x2d\xf3\xb3\x36\xf5\x46\x78\xe3\xad\xce\xf8\x41\xe3\xfd\x73\x07\xe4\xf9\x80\x43\x24\x35\x11\x80\x08\xdd\x53\x01\xe0\x5f\x32\x1e\x72\x90\xcf\xf8\x6d\x93\x3e\xb1\x16\xe9\xd8\x11\xc1\x1a\xb9\x27\xfe\x0e\x07\xeb\x15\x3d\x02\x4a\x50\xb7\x51\x1d\x57\x23\xc2\x2f\x68\xca\xb6\x42\x5a\x00\xf5\xc7\x3d\x72\xce\xba\x46\xbd\x4d\x12\xec\x24\x18\x7a\x48\xb9\xb1\x90\x7f\xea\x6f\x55\xbe\x1c\x51\x46\x1d\x63\x3e\xf6\xf7\xc3\x63\xc9\xeb\xd1\x92\x20\x0d\x60\x21\x0b\x69\x81\x00\xcd\x71\xe3\xdc\x2b\x67\x21\xb4\x4d\x30\x6b\x8c\x3a\x74\x6a\x75\x6f\x58\xc9\xf5\xc7\xb3\x84\xa8\x88\x59\xf9\xb7\x75\x13\xf2\x1a\xa0\x83\xc0\x36\x6b\x4e\x30\x69\x53\x0a\x3c\xc0\x62\x52\x62\x21\x7d\x29\x54\xa3\xe4\x9d\xb2\x90\x78\x5f\x2f\x59\xd2\x72\x32\x90\x55\x05\xa6\x6f\xac\xdc\x96\xa5\xe5\x78\x47\xf3\xdf\x62\xe8\x29\xce\x4b\x3c\xea\x56\xbf\xd7\x16\xac\x04\x98\x68\xcb\x2e\x39\x52\x2f\x83\xbb\x11\x12\xab\xd0\xea\x8c\x7c\x18\x07\x6c\x4f\x3a\xa4\xf9\xa3\x17\x04\xc0\x5a\x59\x2b\x12\x7c\xe6\x0d\x69\x9e\x19\x35\x55\xc2\x82\xd7\xcc\x4d\x87\x6f\x0c\x6d\xa1\x7f\xa2\x1c\xbf\x0f\xc7\x19\x4d\x28\xe5\x35\x92\x0a\x84\x35\xeb\x54\xea\xca\xa2\x93\xb6\x93\x94\x7a\xb3\x60\x12\x6b\xd3\xff\x23\x73\x58\xcf\x9e\x6b\x84\x7d\x1d\x8a\x1f\x17\x7a\x44\x92\x86\xf9\xb7\x9f\x69\xbb\x75\x65\x61\x76\xb9\x20\x82\x7e\xcd\xe2\xec\xd5\x88\x11\xf8\xbb\x36\x25\x5e\x35\xde\xa0\xd2\x21\x4f\xb5\xdb\x82\x9b\x16\xaa\x27\xf9\xa9\x0b\xe1\x8e\xba\x1f\x9e\x6b\xfc\x5e\x5e\x1d\xd7\x88\x38\xdd\xde\xb2\x79\x33\xe4\x19\xf8\xe5\x7b\xb5\xfd\x4e\x06\x54\x26\xb1\xe8\xb5\xc5\x83\x74\xf4\x45\x34\x3e\xb8\x3b\xb0\xd5\xd6\xaf\x61\x1b\xba\x50\x77\x39\xd4\x5d\x21\x4e\xc8\x4f\x19\x1d\x6c\xc7\x1e\xcf\xd7\x42\xad\xbc\xe7\x10\x78\x6f\x3f\xd1\x9e\x57\x31\xca\x3e\xca\x41\xe7\xc4\x9d\x3f\xba\xea\x29\x3f\xf2\xff\x71\xd6\x7d\x6d\x7f\x10\x77\xa6\x8f\xec\x70\x11\x29\x51\x45\x27\xe3\x90\xb4\xf1\x0c\x27\xdc\xf6\xd2\xcc\x47\x64\x01\x21\x9f\xec\xeb\x53\x38\x2c\x9a\x59\x9e\x6b\xea\xfd\x5d\x2a\x3d\x48\x96\x83\x4c\xac\x0e\xd6\x5c\x27\x93\x48\x68\x2a\x60\x12\x36\x94\xce\xf7\x62\x50\x93\x57\xb1\x49\x62\x4f\xbc\x32\x68\xc2\xc4\xb8\xd7\x0d\x86\xed\xb6\xa0\x5e\xb1\x22\x03\x88\x8a\x79\x9c\x8a\x3e\x93\x05\x3f\xde\x0e\xa8\x86\xe9\x06\x2d\x78\x75\x8d\xc6\x55\xfb\x9f\x98\x0e\x21\x57\x1f\xbc\x17\xbb\x4a\x68\x23\x61\xf4\x21\xd3\x38\x8f\xbe\x4c\x72\x5f\xd9\x2c\xf2\x29\x52\x7d\x9b\x8b\xe1\xd2\x41\xab\xd1\x75\x60\x84\x71\x0c\xb4\x3d\xea\x0b\xa0\x80\xc4\xa3\x53\xca\x91\xd7\xac\x95\xbc\x02\x39\x1b\x5e\x56\xff\x7c\x46\x12\x67\xfb\x68\x5e\x0d\xee\x05\x9e\xb2\x89\x7a\xb6\xa3\x77\x95\x5e\x1b\x67\x16\x74\x0f\x7f\xed\x08\xf7\x47\xba\xa2\x2d\xd0\xc6\xdd\xce\x64\x8f\x1f\xe5\x62\x53\x3a\x4f\xa3\x74\x5e\x15\xcc\x97\x1c\x50\x3c\xc7\x36\xe3\xda\x30\x8f\xb0\xf1\xb0\x72\xe6\x11\x51\xb9\x8d\xf4\xf3\xa0\x63\xa6\x5a\x5b\x46\xe9\x66\x51\x1a\x66\xef\x60\xfd\x66\x71\x3e\x9d\xda\xc3\xc5\xe2\xa3\x28\x09\x9d\x98\xda\xe2\xea\xd1\x94\xdf\xf3\xc8\xd9\xa3\xc6\x53\x04\x89\x3c\x08\xed\x22\x96\x9d\xf7\xd7\x25\x67\x73\x06\xaf\x39\x9b\xca\xd3\x6f\xb3\x15\x62\x4e\x57\x7a\x1c\x6b\xe1\xb6\xb2\x40\xf1\x69\x6a\x43\x94\xbb\xdf\x4c\xaa\xd5\x9f\xc0\xc9\xc6\xde\xb4\xe9\x40\x06\x54\xa2\x1b\x1a\x18\xfa\x85\xbd\xd1\x2f\x41\x34\xa5\xc0\xae\x88\xc8\x45\x21\x3a\x68\x7e\x86\x29\xea\x77\x39\xf0\x73\x57\x9f\x66\x01\x78\x6c\x1c\x44\xed\xfa\x6a\xc1\xe6\x57\x8b\x7b\xf3\x86\x07\xc8\x21\xd8\x93\xe5\xcf\x21\x01\x39\x52\xa1\x8b\x15\xe6\x70\xce\x8a\x8e\xba\x5e\xa5\xd5\x17\x98\x79\x69\x65\x6d\x97\x6d\xbf\x62\xe7\x44\xc6\x75\xe1\xea\xf3\xde\x1c\xca\xd3\x22\x48\x5e\x56\x7a\x6e\x32\x6a\x01\x14\xaf\xfc\xf3\x61\x66\x96\x87\xee\x00\xc8\x20\x43\x07\x1c\x3d\xf9\xa9\xda\x01\x83\x40\x23\x67\x8a\x0d\x30\xaa\x36\x5d\x13\x27\xf6\x95\xa6\x8d\x99\x12\x17\x62\x9e\xc2\x77\x70\x16\x24\xb9\x0d\x79\xaa\xc7\xb0\xaa\x48\x38\x03\xf5\xc9\x59\xfb\xa7\xd9\x5c\xd4\xdc\xdd\x5c\x35\xdc\x41\x41\x5a\xe2\x57\x82\x02\x87\x20\x79\x5e\xa6\xc8\x36\x18\xd2\x0d\x0c\xa5\xd9\x1f\x64\xd6\x15\x08\x3a\x51\x13\xdf\x51\xac\xb1\x91\xc6\x8a\x07\xa8\x1e\x87\x41\xbb\x1c\x93\xc8\x34\x01\x82\x33\xa6\x87\x3e\xda\x3c\x7c\xf9\xa4\x05\x28\x33\x0a\x62\xb0\x92\xae\xa8\x90\xfb\x82\x05\xf7\x2b\xe1\xb6\xbc\x46\xf3\x75\xe1\xdf\xf5\x90\xd0\x6c\xfa\xf5\xd2\xdd\xa2\x3f\xbc\x12\x2a\x30\xef\x7e\x4d\x62\x84\xb6\xa5\x28\xb6\xbb\x9e\xc0\xd7\xb8\xf7\x74\x02\xff\x1f\xf1\xdd\x9c\xe1\xd2\xd2\xa5\x9b\x23\xfe\x32\xf5\x26\x0d\x70\x6b\x02\x8e\x40\x41\xde\x7f\x9e\x1f\x38\xd4\xa7\x2e\xc9\x1b\xf6\x07\x84\x25\x9c\x2f\x8d\x6d\x09\x39\x88\xef\xf9\xa4\x01\xbd\x12\x44\x6f\x2e\x56\x4a\x63\x23\x44\x97\xac\x01\x39\x8b\xb5\xec\x30\xfb\xc4\x3b\xe0\x3e\x8f\xaf\xed\x37\x8f\x14\x14\x38\x5e\x49\x92\x45\x59\x78\x3d\xf9\x1a\xc0\xd6\x3b\x4f\xd2\xc2\xc9\x10\xd8\x88\x1e\x9a\x9e\x3e\x7e\x1e\x4a\x42\xcd\x02\x11\x13\x09\x46\xe8\xbc\x6f\xc8\xdb\xb6\x5a\x54\x55\xd2\x69\xd5\x39\xc0\x5f\xdc\x15\xf2\x01\x9b\x5c\x0e\xfe\x4d\x8d\x8e\xd6\xa1\x0f\xf1\xc8\xf4\x63\xc3\x35\x0b\xfb\xaa\xcc\xb6\x06\x6f\x2e\x99\x7f\xb7\xca\xbb\x0a\xca\x9e\x75\x8a\xf3\x09\x85\xb5\xec\x17\x79\x9c\xae\xa1\xf0\xa3\x31\x83\xe1\xc2\x2f\xfc\x2c\x73\x4f\x63\x67\x61\x79\x29\x69\xb5\x1a\xd9\xdf\x4b\x08\xcb\x93\x93\xcd\x54\xfa\xb2\x23\x26\x78\x89\x2c\x8e\x78\x28\x8c\x55\xda\x5f\x75\xec\xee\xb8\x7b\xfb\xb5\xbf\xb3\x59\x62\x20\xda\xef\xeb\x15\xe1\xff\xd6\xff\x83\x73\xed\xff\x12\xd8\x58\x8d\x92\xef\x33\x33\x81\x18\x3a\xed\xc6\x4e\xfb\x60\x94\x8e\xcb\x4e\x34\x91\x2d\x32\xfd\xaa\x58\xec\x18\xaa\x4f\xfe\x78\xfe\x8d\x5f\xe3\xa8\xb4\x9c\x5b\x63\x31\x78\x04\x14\x12\xad\x86\x40\x4c\xb7\x88\xba\xe0\xb2\x0c\x58\x5a\x0e\x3f\x87\x6f\x02\xbb\x30\x8b\x7d\xfd\x40\xf9\xc3\x46\x68\x7d\xa1\x4f\xc5\xd1\x21\xfe\x43\x24\x79\x24\xf8\x3e\xdd\x9f\x20\xc8\x11\xa7\x49\x60\x8e\xb3\x40\xeb\x39\x0c\x7a\x52\x94\x60\x3b\x1a\xf4\xe5\x40\x2a\x94\xcc\x6f\x8c\x2b\x99\xaf\xa3\x35\x37\x6e\x52\x5b\x32\x50\xcd\x8b\x7c\x6f\x3a\xe6\x4d\x9c\x22\x68\x2c\x41\xd3\x61\xe4\x52\x3b\x66\x48\x6b\x55\x44\xb0\x59\x50\x15\x33\x1f\xbc\xf9\x6c\x9b\x41\xbb\xc0\x87\x6e\x48\x45\x61\x33\x27\x1e\xc4\x5f\x9e\x79\x8d\x37\x6d\x92\x3b\xd5\xa4\x5b\xf6\x93\xff\x29\x30\xf3\x31\x70\xb7\x37\x6c\x3b\xe0\xad\x01\x83\x55\x0d\x3b\x7a\x5a\xff\x5e\x49\x05\x11\xfe\xbf\x64\xfd\x24\x12\xac\x82\x48\x15\xc6\x1d\xe2\xba\x6b\x01\x10\x8c\x13\xf2\xf1\xf5\xf6\xd7\x2a\x88\xbb\x5a\x2a\x0d\x7d\x94\xee\xc2\x5e\xbe\x94\x7a\xa6\xf5\xd2\x62\xec\x56\x57\x1d\xf4\xe7\x86\x4e\x0f\x5f\x8c\x3d\xf4\xf0\x74\xe2\xed\xad\x7e\x95\x9b\x9c\xbf\x94\xee\x24\x1d\x5d\x5c\x0e\x57\xc1\x71\xe8\xaf\x2b\xc7\x9d\xf5\x18\x64\xcf\xc6\x1c\xab\x6b\x1d\x58\x57\x42\x07\x5f\x4f\x3a\x1a\x1c\x08\x02\x5a\x71\x5c\x7e\x6a\xe8\xaf\x0b\x62\xa4\x98\x29\x43\xba\xcb\x79\x4b\xab\x49\x1f\xb0\x84\x92\x7d\xf5\x04\xd1\xcf\xae\xfc\x4f\x13\xa6\xed\x23\x33\x7e\x5e\x80\x85\x52\x72\xc8\xfe\xb1\xa7\x3e\x08\x6b\x23\x92\xe7\xbf\x32\xf8\x97\x96\xf7\xfd\x43\xac\x2c\x51\xbe\xac\xe1\x7d\x5a\xf8\xcd\x66\x9f\x63\x5f\x4e\xf4\x1b\xbb\xa8\x64\x41\x96\x84\x3e\xf9\x5b\x3e\xb0\x4f\xeb\xad\x9f\xc2\xb4\x5c\xb8\x1a\xb2\x71\x58\x0b\x8c\xea\xa1\x83\xd5\x09\xa5\xbc\xd3\x3a\x05\xed\x3d\x72\x9d\x22\x74\xbb\xeb\x9e\x0e\x1f\xbd\x82\x41\xf1\x84\xc5\x07\x30\xf7\xe8\x8d\xc7\x4a\x15\x66\x83\x0c\x45\x90\xee\x40\x65\x9a\xad\xc7\x42\xb5\x2a\xba\x23\x94\x73\x8c\x5b\x31\xc4\x61\xa1\xc8\x52\x4a\x6d\x19\xf6\x97\x9b\x48\x38\x5e\x9d\x18\x0e\x44\xd2\xb8\x28\xe9\x7e\x54\x68\xb2\xe4\xa7\x71\xb9\x2f\x78\x0d\x2b\x4e\x9e\x1b\x2e\x96\xd4\xd7\xba\x79\xbc\x77\x2b\x6f\x76\x91\xa1\xcc\x04\xf7\xf9\x2f\x45\xcd\x3f\xd4\x88\x27\x40\xd6\x39\xa6\x1d\x6b\xf1\xc7\x7d\x35\x27\x5d\xcc\x8a\x26\x7b\x87\xf5\x0d\xd6\x2b\xc0\x31\x47\x73\xde\x93\x69\x54\x12\xfd\xe0\xfc\xe3\xe6\xba\x5c\xa2\xf5\x4f\xef\x37\x02\x8c\x6d\x7a\x68\x48\x3a\x44\xc4\x3e\x46\x39\x36\x07\x92\x8e\x3b\xcb\x85\xbc\xe9\xa0\x78\xc1\xde\x9d\x58\x01\x5b\x31\x88\x48\xdb\x2d\xb7\x27\x52\x7c\x7e\x90\x2f\x86\x46\x78\xcf\x41\xbf\x0e\xbf\x11\x9e\x01\x89\xff\x67\x60\xd6\x17\xea\xcd\x3e\x3f\x63\xbf\xc4\xae\x44\x6d\x8e\x48\x30\xa5\x9f\x4a\x28\x0c\xbb\x34\x5a\xc6\x88\x0f\xd4\xd7\x35\xf1\x2d\x02\x24\x1d\xaa\xf6\x68\x26\xc6\x03\x35\x3b\x09\x94\x64\xd7\xc9\x9a\x47\x2b\x76\xc1\x20\x82\xbc\x78\xc5\xbf\x3c\x73\x69\x7d\x0c\x10\x41\x75\xb1\xdc\x44\x71\xb1\xfa\x23\x2d\xd0\x81\xd8\x75\xeb\xe3\x6d\x13\x14\x62\x0f\xaf\xaf\x0e\x81\xcd\xba\xda\x94\x83\x19\x4f\x34\x23\x6a\x5d\xf3\x61\xda\x1f\x4d\x09\x38\xd9\xf3\x35\x67\xe3\x43\xbf\xbc\x87\xb2\xbf\xe1\xda\x66\x8d\xa2\xb5\x50\x66\xbf\x04\xbd\x17\x1e\x98\x8f\xe5\x67\xd7\x7e\x8d\x00\x60\x6a\x7e\x35\x92\xf5\x75\xd8\x4d\x42\xef\x84\x89\x91\xc5\x7b\xbc\x93\xca\xeb\xb2\xda\x8e\x08\x10\xa0\x61\xa4\x59\xbb\x34\x4a\x43\x38\xc6\x25\x87\x4e\xa2\x68\x49\x01\x30\xee\x98\x45\x48\xf9\x92\x47\x4e\xa5\x4d\x12\xe8\x9b\xc2\x0f\x43\x1e\xc1\xf9\x76\xe7\xcf\x5e\x89\x67\x7c\x66\x2a\x73\xac\xc9\xc1\x1b\xd1\xaf\xf0\x33\xa3\x34\x0d\x04\x1e\xf8\xb3\x61\x08\xac\x11\x6d\x84\x4d\x6b\x8f\xc9\xcd\x83\xc3\x8b\x95\x56\x1b\x1b\x7a\xc9\xa5\xc6\xbc\x11\x0e\x4c\x05\x25\x3e\x7d\xc0\x54\x93\x9e\x42\x84\x37\x00\x1e\x2d\x49\xd5\x15\x5f\x97\xfd\xde\x5e\x39\xc0\x63\x91\x60\x96\x07\x81\x39\x64\x56\xdc\x90\x83\x91\x50\x97\xd8\xe4\x73\x0e\xb1\xc4\x5a\x56\xa8\xa0\x1e\x29\x0e\xf4\x1e\x1e\x9d\x06\xd9\x89\x14\x00\x59\x9d\x94\x08\xc9\xf2\x93\xb4\x4a\x08\x22\x47\xf9\x11\xa0\xa0\x76\x17\xd2\xfd\x05\x73\xd1\x32\x3d\xd0\x6e\x15\x62\x9a\xb3\xc1\x3e\x78\x91\x5a\x7b\xbf\xf7\xfe\x61\x87\x12\xcc\xed\xf5\x75\xf6\x39\x93\x9e\xc5\x77\xd0\xc8\x90\xac\x4c\xe8\xb1\xe8\x28\xe9\x76\x14\x21\xba\x2d\x85\xf8\x1b\x57\x8c\x5e\x73\xbd\x9b\x30\xc3\x62\xd0\x3d\x80\xc4\x55\xcd\x89\x87\xe8\x8c\xa3\xf4\x70\x16\x8f\x65\x77\x67\xd0\x87\x01\x7b\x2c\xbd\xd7\xf6\x93\xa3\x58\x93\x7c\xa0\x01\x2a\x47\xf0\x24\xc7\xd5\x5a\x4b\x83\x58\xed\xaa\x00\xbc\xec\xeb\x42\xd8\x3d\x18\xd7\x95\x8f\x82\xe9\x7f\xcf\xdc\xe4\xd7\x63\x03\x6e\xff\x50\xdb\x36\x1c\x0f\xc9\x4f\x0a\x0e\x53\x17\x1a\xa3\x11\x35\xf7\x97\x45\x47\xe7\x8f\x34\x95\x46\xef\x77\xd2\xe1\xc6\x49\x3c\xd1\x44\x5a\xba\x66\x6f\xb3\xde\x48\x7a\xc1\x52\x3b\xae\x5b\xfb\x30\xfb\x33\x60\x5b\x4a\x06\xba\x40\xf4\x34\xe5\xd7\x9e\x32\x6f\x24\x60\xc6\x3e\x58\xc4\x6c\x6f\x61\x49\x40\x50\xe3\x5d\x48\x48\xd4\x8c\x5a\xda\x18\x06\x52\xde\x4f\x7f\x87\x8a\x76\xd5\x56\xe7\xa4\xf8\x67\x6b\x8b\xf7\x13\x8c\x09\x32\xed\xad\xe6\x27\x93\x5a\x7a\x5a\x20\x5c\xe0\xbb\xfc\x9d\xb7\x87\xe2\xa4\xa8\xe9\xb3\x60\xfd\x63\x5f\x8e\x23\x68\x2f\xb9\x28\xe2\x1f\x94\x99\x87\x88\x2c\x5e\x61\x40\x1c\xdf\xb9\x99\xd1\xa1\xc4\x38\xff\xa1\x2d\xf1\x53\xa1\xd7\xbe\xe7\x69\xfd\x95\xcc\x34\x3c\x7d\x8a\x09\xdc\x58\xbd\xd4\x8d\xf3\x8e\x58\xf7\x30\x76\xff\x6e\x68\xdc\x52\x9a\x33\x8d\x3b\x77\xf7\xfd\xac\xe2\x70\xa0\x1d\x5f\xfe\x85\x78\x55\x02\xf1\x39\x52\x53\x9b\x76\x2d\x72\x46\x97\x26\xa0\x31\x4a\x52\x67\x6a\x7d\x2a\xcb\x81\x8f\x41\xbf\xab\x14\x06\x80\xab\x8f\x11\x45\x31\x40\x57\xc2\x92\x2c\x3b\x0d\x99\x32\x5f\x18\xc1\xc2\xed\x6f\xd8\x76\xdc\x42\x88\x0f\x26\x5a\xaa\x89\xcc\xf2\x01\x2a\x42\x69\xa1\xf8\x92\xfc\x11\xf6\xb6\xca\xf8\xb0\x7a\x00\x95\x71\xfc\x81\xe2\xcf\x5a\x0f\x8a\xf7\xb3\xc2\xe0\xce\xc6\x73\x98\xcd\x4e\x71\x3c\x51\xcb\xc0\x03\x63\x2e\xa8\x52\x78\xb1\xb2\xfe\xe3\x4c\x48\xe0\x1b\xa4\xaa\x83\x3f\xd2\x1d\x83\x76\xb3\x5b\xe6\x3e\x7b\xbe\x20\xd1\xd7\xf6\x16\x18\x80\x60\xe6\x3c\x13\xd3\x01\x33\xbb\x07\xa1\x02\x3f\x2f\x48\x7a\x96\x97\x16\xf5\x54\x86\x8a\x1c\x45\x91\x05\x50\x79\xc6\xf4\x7e\x48\xa6\xc8\xfc\x9e\xf3\xbe\x5f\x09\xf0\x16\x28\xaa\x75\x0e\x81\xca\x3f\xc9\x47\x19\xd4\xb8\xfb\x31\x64\x1d\x7b\x48\x6b\xf1\xde\x2d\x17\x6e\x8a\xdf\x77\x3f\x3f\x99\x91\x30\xed\xf1\xc4\x0d\x90\xfc\x65\x27\x58\x24\xfd\x7c\xfb\x11\x88\x1e\x44\xee\xf6\xd2\xe5\x44\xa4\xdd\xc2\xd8\x1a\x12\xb0\x86\xac\xc2\x48\xb4\x22\x9f\x3b\x08\x8f\xfd\x90\x86\x3e\x25\xe0\x52\xc8\x5f\x8f\xdf\x1f\x09\x23\xe5\x90\xdf\xc8\xb8\xec\x85\xca\x08\x77\x9f\x08\x71\x74\x2f\x9d\x84\x1f\x53\x1c\x7c\xb7\x70\xea\x7d\x5d\xf4\x1b\xbe\xa8\x31\x9f\x1f\x2c\x35\x9f\x33\xa4\xa3\x5f\x54\x7d\xa0\x3f\xfc\xbd\x73\xfb\xb9\xac\x10\xe7\xce\xba\x55\xe5\x94\xc6\xfd\xc1\xfa\x15\x41\x0e\xc1\x4e\x4c\xc8\x0b\x1b\xee\x37\x58\x57\x17\x00\x74\x06\x1c\x51\xe6\xbb\xf3\xe6\x43\x3a\xd7\x2b\xf1\x1a\xe2\xbc\x02\x1e\x5f\x01\x78\x4e\x28\x01\x27\xe3\x63\x4b\x81\xac\xfb\x51\x8e\x96\x9a\xd1\x7a\xc8\x51\x00\x44\x35\xd0\xde\x01\x98\x0d\xcb\xf8\xe8\x04\x70\x22\x09\xa3\x2d\xb5\xba\x2b\xe8\x67\xff\x95\x09\x1e\xef\x14\x60\x49\xb5\xa0\x60\xf2\xbd\xc3\x50\x81\xbc\x47\xd6\xdf\xab\xbb\x83\x50\x66\xce\x95\x0f\x4c\x96\x27\xd1\x58\x2e\xe3\x5d\x66\x8c\xc0\x20\x9b\x64\xaf\x9a\x9f\x9c\x71\xd1\x2d\xab\xa7\xe8\xa3\x59\xc8\xaa\xa5\x85\x7a\x47\xcd\x6e\x1c\x9d\xf5\x51\x81\x3e\x95\xe2\x0c\xac\xe2\x5f\xd9\xf3\x1b\x01\x18\x0d\x92\xce\xb8\x76\x70\xa6\x0d\x4f\xa1\x63\xe3\xf5\x5d\x7d\xd9\xd7\x16\x00\xa8\x38\x35\x5c\x35\xfb\xf9\x0c\x17\x60\xaa\xf3\x12\x8e\xb7\x28\x55\xb8\xe9\xdd\x1c\x92\xcd\x9a\x40\x78\x13\xbc\xbf\x23\xe4\x23\x33\x57\xfd\x09\xe2\x7c\x86\xce\xae\x25\x86\xd0\x59\x4f\xb5\x34\xad\x67\xcb\xc7\x5e\xef\xbc\xa5\xf6\x3e\xd9\xc7\x3a\xe4\xc7\xe7\xca\x47\x4f\xe9\xbc\xa2\xb8\x0c\x6b\xee\xdf\x31\xff\xee\x49\x0a\xc8\xb7\x84\x90\xf3\xcf\x93\x71\x90\x42\x77\xa3\xef\xdc\x77\xaa\x18\x8c\x20\xb3\x34\x2d\x6c\xa0\x99\x11\xfc\x41\x10\xc9\xa1\x3c\xd4\xba\xe5\xbc\x8b\x68\x9c\xeb\xd7\x55\xce\x5b\xf8\xfa\xfa\x1b\x9c\xe0\x35\x6b\xf8\xd4\x39\x2e\x5d\xe7\x8c\xbb\xe7\xa0\xb4\xfc\x21\x17\xe1\xfe\xb1\xd5\x15\xb5\xb4\x0b\x5c\x30\x44\x93\x8c\x90\x3c\x88\x1e\x5d\x99\x84\x9d\x8a\x05\xbd\xe5\xad\xb5\x11\xcd\xcc\xbf\x71\x87\xac\xad\x17\x4e\xaf\x15\xb8\xf4\x60\x0c\x95\x94\x2d\x10\x2b\x1f\xc3\x64\x9f\xe6\x10\xc9\xb5\x9f\xaa\x00\xb7\x25\x44\xc0\xf3\xd0\x6c\x85\xb2\x99\x5f\x70\xef\x6d\x3d\xeb\xb0\x37\xec\xaf\x54\xc3\x49\x28\x09\x9d\xad\x6b\xc7\x28\x13\x75\xbc\x52\xb5\x38\xb4\x4f\xa5\xa2\x8e\xb2\xdb\xe7\x44\x7a\xd9\x7b\xbd\x34\x6e\xfb\x73\x15\x51\x07\xd5\x11\x1e\x1b\xe2\x1c\x56\xaa\x0b\x18\xa0\x0e\x43\xe0\x03\x68\x23\x34\xa7\xdf\x2a\x60\x59\x82\x8b\x86\x0f\x0f\x5d\x52\x72\x5e\xae\x26\x4a\x5f\x6d\xdb\xb8\x57\x71\x20\x94\x65\xf9\x83\xb9\x8d\xc4\x58\x69\xb5\x15\x2f\xe3\x1e\x33\x11\xd8\xf0\xdd\x65\x7b\xfd\x0c\xee\xde\x6e\x66\xaf\x48\x13\xa9\xd1\x39\xce\xd8\x7a\xee\xb5\x74\x00\x99\x0f\x6c\x57\xf8\xcf\xde\xeb\x24\x8d\x62\x6f\x52\xcf\x81\x0e\x6f\xc1\x75\xce\x58\xfc\x07\xa2\x3c\xf8\xca\xb1\x3e\x8e\xec\x2f\xba\xba\xe0\xe2\xe9\xd1\xb2\x66\x13\xdd\xcd\xc4\x7b\xb2\x23\x6e\x22\xae\x17\x62\x29\xf5\x7f\x94\x92\x0c\xa0\x55\x34\xc5\xc2\x0f\x34\xc7\xb5\xfd\xf1\xc6\xab\xda\x39\xb6\xbd\x7f\x1f\x5f\xa7\x0e\x8d\x96\x6e\xee\xa5\xfb\x3c\x43\x55\x31\x1a\x81\xf8\xab\x0d\x6e\x21\x99\x5e\x09\x08\xb6\x89\x57\x53\x50\x0e\xb0\x01\x8e\xcc\xb7\x79\xeb\x90\x80\xcb\x26\x81\x7f\x5d\x86\x85\xe4\xf4\xb4\xa7\x90\x5f\x75\x27\x86\x8d\xe8\x13\xdb\x3b\x68\x99\xe6\x1c\xd0\x7e\x4f\xa2\x07\x70\x5c\x45\x1a\x9a\x50\x17\x5d\xbe\x5e\xc8\xbe\xe3\x91\xff\x2e\x3e\x0e\x2b\x8b\xe5\x50\xf7\x02\xf4\xe9\x7b\x6b\x28\x0a\x44\x4a\x98\xa5\xc0\xe0\xcf\x40\x05\x3c\xec\xf1\x36\x00\x8c\xaf\x27\x17\x05\x63\x7e\xf7\x8c\xbf\xb9\xb1\xa6\x47\x03\xe5\xa3\x93\x45\x44\x79\x37\x07\xfa\xc3\xc3\xf4\xfb\xc6\x6d\xf9\xeb\x83\xee\xd4\x28\x9e\x5f\x5a\x71\x38\x95\x0e\x75\x7b\xa4\xa9\x07\xf0\xee\x31\xe4\xee\x8f\xfd\x01\xbd\xdb\xc7\xfe\x63\x65\xd9\x1b\xd1\x90\xed\xee\x96\x78\xac\x14\x4b\xd7\x91\xdf\xc0\xad\xf6\x94\x17\xad\x0b\x6b\x4b\x5d\xe7\x55\x36\xd3\x65\x42\x5a\x94\xf4\x97\xfc\xa8\x1c\x26\xe1\x5c\x78\x3d\x62\x54\xe1\x37\xab\xbe\xe5\x1f\x5f\x8b\x61\xac\xd9\xcf\xf8\x32\x7f\x41\x9e\x0d\x81\x77\xd1\x42\xfc\x65\x81\x14\xa8\xf8\xc5\xfb\xd9\x2b\x56\xdc\x83\x89\x6a\x28\xd8\xe0\x4d\xa8\xab\x13\x5b\x83\x7c\xc6\xf3\xb2\xc1\xee\xc7\xb4\xc1\x00\x23\xf6\x95\xe8\x4c\x26\x49\x8c\xd7\x51\x54\x0d\xce\xf0\x04\x68\x86\x12\x6e\xb2\xf7\x3a\xc2\xfc\xa3\x48\xfa\xa4\x39\x88\x8f\x8e\xf7\xba\xf6\x06\xe3\x65\x91\x96\x0e\x5e\x8e\x8c\x92\x03\x7c\x4f\x81\xdc\x42\xf5\x01\xec\x29\x4f\x4c\x13\x81\x2d\x17\xb7\xb8\x0d\x45\x38\x5c\x46\x6e\x57\x1c\x02\x97\x2a\xa3\xfd\x61\x3b\xa3\x6e\xba\x67\xff\x45\xe7\xcc\xc8\x02\xbf\x70\xe4\xd5\x61\x71\xa4\xfa\x6e\xdf\xe4\x3e\x22\x88\x9f\x39\x09\x14\xac\x0a\x3b\x2d\x59\x62\x76\xc5\x79\x2e\x6f\xfd\xe1\x02\xc6\x28\x69\xbe\x18\x4e\x7c\x19\x90\x1b\x2a\xa9\x14\x2f\x5d\x0a\x02\x32\x29\xbf\xd0\xb8\x5f\x60\x2b\x5c\x75\xb7\x70\x5e\x52\x7f\xaa\x7e\xf0\xdb\x35\x2f\x03\x7c\x27\x53\x5c\x54\xa9\x6e\xb2\xc5\x6f\x62\x4a\xef\xa2\x0c\x20\x7c\x82\x8f\xeb\xe6\x74\x82\x5f\xb8\xe9\x6b\xee\x7e\x81\x4b\xcf\xea\x39\x37\x9a\x4c\x38\x82\xdc\xfb\xb5\x49\x07\x4f\x84\xf0\xa8\xcf\x16\xab\x6c\x52\x9e\xd6\xaf\xf2\x0b\x26\xb9\x75\x8f\xf3\x67\xf2\x9a\xc5\x77\x96\xcb\x57\xda\x42\x8d\x8f\x6f\x2c\x7a\x98\x2a\xf2\x83\x90\x64\xc0\x94\xf1\xbf\x0e\x3a\xe7\xb3\xb6\xce\x92\x6d\x2d\xc4\xd7\x82\x36\x9f\x0c\x39\x26\xcc\x11\x4c\x97\x75\x1d\xc5\x8b\x68\x7e\xb3\x6d\x02\x0e\x6a\x19\xb2\x2e\x13\x09\x6d\x90\x85\xca\xdd\xa9\x28\x0b\x87\x0b\x38\xf4\xd0\x7a\xf2\xfc\xff\x09\xff\x44\x3f\x09\xc6\x60\x87\xd5\x86\xc9\xdd\x1e\x7d\xc5\x6d\x3f\x59\x9a\x5d\xf7\xf6\xa1\xfc\xde\x51\xb8\xfc\x82\xc5\x57\xe6\x37\xe9\x7c\x10\x12\x4e\x70\x15\x4f\x90\xe3\x7c\xc8\x76\x9b\xfd\x5a\x32\xc7\x1e\x9d\xb5\x32\xc1\x13\x6c\x5c\xbc\x24\x62\x90\xce\xbf\x73\xcf\x0d\xef\x24\x3a\xe5\xd8\x60\x4e\x3c\x3f\x13\x37\x04\x14\x81\xa0\xda\xaf\xd5\x36\x3a\x7f\xf3\x8a\xbd\xa0\xda\x54\xca\xbd\xa1\xb5\x88\x10\x07\x57\x2f\xb6\x07\x44\xea\xda\xa7\xd4\x45\x6b\xb9\xaf\x41\x33\x96\x8d\x50\xa9\x9e\xf6\x02\xe7\xa8\x75\xd7\xcb\xcf\xcd\xd6\x8e\x93\x49\x9b\x83\x61\x26\x33\x89\x37\xcc\xc3\x90\x83\x7d\x48\x32\x55\x1a\x4e\x51\xa8\x43\x9b\x7d\xf5\xa7\xc1\xf3\xec\xcd\x04\xe7\xf7\x6f\xb5\x5c\x1f\x3a\xac\x3d\xe7\x33\x17\x0c\x71\x9e\x34\x06\xff\x90\x08\xcd\x67\xf1\x91\xe3\xec\x5e\xda\x6f\xe4\x86\x63\xe6\x10\x00\x22\x8c\x48\x8c\x51\x26\xc7\x89\x3c\xf4\xde\xa9\xad\x01\x84\x03\x9b\x2b\x1a\xa6\xd6\x56\xe9\x56\xb4\x62\x37\x70\x89\x79\x8f\x71\x7b\x1d\x43\x37\x75\xec\x87\x0e\x37\xab\x90\xac\x55\xf3\x9d\x12\x47\x18\x97\xb1\xa2\x4b\x33\xae\xe3\x7d\xc8\x1a\xd2\x59\x2e\x44\xff\xb2\x73\x8e\x90\x81\xdb\x4a\xfd\x1a\x37\x35\x87\x15\x34\xa7\xbc\x3d\xa8\xf3\xa9\x5d\x61\x68\x1c\xc9\x07\xf9\x62\x65\x7f\xd0\xbd\x94\xf0\x58\x88\xce\xf4\x22\x56\xe8\x2b\x57\x04\xeb\x70\x50\x52\x9f\x9c\x3b\x8b\x57\xc4\x01\x00\x48\xac\xfe\x64\xe8\xd9\xeb\x3e\x7f\xaf\x2a\x3b\x2f\x81\x54\x24\x96\xbc\x60\x4a\x8c\x2c\xca\xc6\x8e\x79\x19\x31\x83\x34\x57\xbb\x2b\xc2\x25\x33\x8f\x02\xfe\xf7\x44\xce\xa6\x17\x76\xfb\x03\xf6\x33\x17\xc6\xb1\x05\x9c\x90\xee\x58\x05\xf8\xf1\x6a\x63\xa4\xcf\x5c\xba\x3d\x13\x9a\x2e\x69\x87\xce\x79\x57\x6d\xd5\x3e\xb0\x9a\xf1\xb3\x95\xc4\xc3\x08\x7e\x7a\xfc\xc9\xcd\xb7\x6e\x29\x40\x8b\xc0\xb6\x48\x8a\xdb\x99\x1f\x3c\xe5\x0a\x4b\xae\x30\x0c\x03\x92\xe0\xfd\xd9\x4a\xc8\xaf\xfd\x42\xd8\x07\x30\xef\x5b\xb4\x34\xd9\xe3\x38\xea\xba\x19\xf1\x0b\x23\x06\x5f\x52\x23\x7b\x3d\x05\x86\xd4\x84\xd9\x0d\x45\xac\xc1\x37\x62\xf4\x33\x05\xc9\xda\x0b\xa6\x17\x9f\xdd\xcf\x3e\x90\xd9\xf8\xd4\xd8\x7f\x19\x92\x6a\x49\xe7\x63\x19\x7f\xac\xfc\x09\x47\x45\x31\x63\xac\xa7\x33\x0a\x52\x47\x11\xb4\x5b\x28\x1c\x3d\xe4\x1c\xdb\xf5\x20\x27\x6b\x44\x29\xe8\xe6\x00\x73\xa6\x2f\x6f\x4a\x84\xe1\xce\x21\x02\xd2\x28\xbd\x73\x01\x30\x78\x09\x9b\xad\xbb\x56\x63\x4e\xc2\xa8\x2a\x5e\x26\x73\x9d\x3b\x06\xac\x58\xd3\x62\xdf\x61\x36\x3a\x39\x87\xd2\xb4\x0d\xa5\x0d\xbf\x3b\x72\x27\xaa\xad\xfe\x40\x78\x45\x98\x2e\x26\xee\xe3\x3d\xc0\x55\xfc\x8b\x4a\xe7\xe2\x7b\xc5\xeb\x54\xda\x4e\xfa\xb1\x2a\xa3\xf3\xdd\x20\x37\x28\x14\xa0\xb6\x94\x44\x38\x03\xf4\x5c\xdd\x7f\xf5\x9c\x8c\x49\xb2\xb1\x10\x13\x04\x01\xf4\xc1\xfa\x67\x62\x03\xb0\x2e\x35\xe7\x9b\x67\x99\xe4\xab\xbe\x11\xf9\xa6\xee\x09\x03\x98\x45\x8a\xba\x85\x51\x71\x15\x4c\x77\x6c\x78\xf2\x43\x4c\xff\xe0\x09\x1f\x87\xab\x85\xc8\x4a\x2c\xe4\x49\x2d\x56\xd0\x70\x29\xf0\x8f\x03\xaa\x1f\xf0\x4b\xfc\x1d\xbd\x17\x5b\x40\xd1\x4f\x31\xf7\x94\x08\x6f\xff\x8d\xf8\xa0\xfc\xf8\xbe\xba\xf6\xe5\x4b\x16\x7c\x98\x56\x2e\xdc\xd2\x25\x41\xab\xd2\xcb\x98\x9e\xad\x2e\x08\x1c\xb8\x5c\xf2\xb1\xa1\xc7\x67\xf1\x62\x22\x64\xee\x0f\x91\xef\xbe\xe5\x69\x37\x19\x09\x6a\xfd\x9a\x91\xc0\x66\x6e\x1c\x56\x47\xdf\xa0\x38\x45\x1e\x46\x4c\x1e\x36\xe4\x59\xeb\x88\xdd\xd6\x7b\x2e\x6e\xc1\xfb\xae\x3a\x36\x8f\xb5\x14\x76\x68\x57\x70\x18\x5f\xb0\x99\x30\xc2\x85\x2f\xfe\x2c\x74\x3e\xb3\xed\x47\x15\xf9\x58\xab\x5b\xeb\xc9\x00\xce\x70\xea\xc0\x5a\x9d\xc8\x1c\xe4\xf6\xd3\x65\x1a\x41\x03\x3c\x14\x3b\x82\x4e\xf6\x51\xb7\x98\x62\xcb\x1d\xc2\x72\xa5\xcf\xb7\xd8\x6c\xb8\x47\x98\xb9\x8d\xfc\x97\x57\x60\x13\x72\x92\xad\x66\xa9\x3b\x32\x76\x2c\xf4\x2b\x44\xec\xc0\x46\xcd\x81\x13\xa2\xf2\x35\x31\x82\x7b\xa9\x52\xa0\x2a\xf9\xe9\x93\x05\x4b\x79\x32\xeb\xf1\x99\x81\xd8\x8a\x42\x6d\x0c\xac\xec\x49\x5c\x8e\x92\xf7\x9b\x34\x11\x1a\x70\x1e\x79\x24\xa5\x76\x89\x87\xb9\xab\x29\x49\xb0\x40\xd5\x70\xc0\xb1\x91\xc2\x0b\xfb\x56\x0a\xd9\x3f\xa6\xc2\xcc\x66\x35\xfe\x2f\x26\x51\x50\x5d\xe3\x0d\x98\x9d\x14\x20\xdb\x4c\x3f\x78\x38\xc7\x2a\x29\xad\xe4\xf7\x7a\x18\xc0\xcb\xb9\xb3\x57\x8c\x16\x9b\x04\xce\xa7\x94\xe1\x71\xb4\xe7\xe3\x8f\x11\xef\xa5\xbe\xdf\xe6\x73\xff\x7b\x78\x24\x89\xb2\x37\x30\x3e\x8c\x7d\x53\x31\x17\xf6\x6d\x09\x3a\xd4\x7e\xed\xc0\x03\xd9\x1e\x93\x5a\x4e\xd1\xcb\x8a\xc4\x09\x22\x55\x9d\x8a\x32\xff\x96\x01\xf3\x83\x42\x25\x85\xf1\xd0\x63\x32\xb9\xc3\x22\x88\x07\x1d\x0e\xb4\x47\xde\x91\xf9\x64\x7c\x32\xc9\x2b\x92\xf4\x12\x6a\x1b\x22\x6c\xab\xdc\xc3\x06\x4b\xab\xc4\x20\x86\x6f\xc1\x91\x06\x52\x6c\xa5\x50\x5b\x84\x14\x53\x56\x9d\xab\xbb\xa2\x10\xf1\x03\x1e\x82\x7e\x58\xf5\x85\x10\x85\x47\x63\x35\x23\x3a\x47\xd2\x72\x62\x48\x01\xf4\x84\xf7\x1e\xc5\x2f\xfe\x33\x1a\x17\x4c\x6d\x1f\xd2\xf7\xb3\x2b\x49\x0a\x98\x22\xe5\xa0\xde\x3f\xb3\xa2\x91\x0a\x87\xcd\x7f\x21\x54\x1b\x67\xe6\x36\x3d\xfc\xb6\xa6\x2d\xf0\xd2\x9f\x02\xff\x26\x10\x39\xa3\x2d\x80\x33\x4c\x22\x51\x84\xef\x41\xaa\xea\x75\x63\x4d\xe7\xfb\x89\xf3\xee\x11\x06\x63\xd2\x9a\x41\x50\x24\xc0\x62\x98\x90\xfd\x16\x6b\x94\xf1\xa2\x39\xd8\x37\xde\x2b\x5f\xfc\xfa\xb0\x2e\x2b\x77\x09\x9f\xdb\x7a\xf4\xc6\xd7\x03\xa6\xbd\xeb\x91\xee\xee\xd1\x2e\x0f\x7a\xdf\x96\x2f\x79\x17\x67\xfe\xb4\xc8\x82\xb2\xfe\x13\xaa\x50\x73\x3c\xdc\x59\x87\xf8\x00\xb2\xb9\x14\x26\xe7\x25\xcd\x4e\x63\xe0\x6f\xbd\x8d\x85\xc6\xd9\x36\x79\xb8\xa7\xa3\x5b\x1b\xc7\x0f\x43\xe7\x36\x83\x3b\xf8\xab\xcf\xf0\x5b\xb0\x17\xe5\x31\x24\x49\x0e\x0c\xf3\x12\x55\x58\xc6\x31\x79\x1c\x7a\x5b\xc0\x9c\x6c\x33\xfb\x45\x77\x90\xb4\x6f\x24\x91\x5c\xbc\xf3\x50\x9a\x3c\x67\xa4\x35\xa3\x85\x07\xab\xff\xb1\x95\x8a\x20\x5a\x32\x23\xaf\xe5\x5a\x92\x3c\x1d\x81\xc4\x4a\xba\x28\x8c\xf6\x43\xa0\x05\x65\x4b\xe1\xc3\xdf\xbe\xf5\xed\x99\xdf\xfe\x4b\xee\xba\xb7\x1b\x56\x92\xf4\x77\x00\x71\x5e\xe1\xad\xff\xb8\x0b\x10\x48\x46\xa2\x79\xc6\x31\x10\xdd\x3b\x18\xc4\xf8\xcd\x91\xb9\xa2\x83\x8b\x81\x66\xd1\x3e\x03\xbc\x5f\xb4\x78\x47\x64\x97\x22\xa3\x4d\x1b\xfe\x6e\xfb\x06\x10\xaf\x35\x70\xc5\xa9\x33\x74\xa3\xc0\x04\x2a\xd0\xe4\x64\xd7\xad\x4c\xd9\x1a\x05\xca\x8d\x3f\x80\x53\xef\x8c\xd1\xb9\x28\x8e\x34\xaa\x9f\x29\x23\x7f\x4d\x2c\xe5\x62\x83\xef\x52\xbf\x08\xa6\x70\x76\x52\xe9\x25\xb8\x37\x76\x99\xa2\xd7\xe6\x17\xc0\x98\x42\x33\xd5\x35\x6c\x05\x64\xb9\x45\xb3\x1a\x29\x3e\x17\xb6\xbd\x10\x8f\x6e\x3c\x0e\x50\x75\xc0\x9b\x81\x1a\x0c\x98\x2d\x22\x64\xbe\xfc\x64\xac\x3a\xfb\x53\x3c\xbd\xba\xb1\xef\xa8\x77\xb0\x0a\x50\xf8\xa5\xfc\xc2\x39\xac\x74\x5d\x68\x57\x33\x81\xab\xfa\xf1\x08\xe8\x90\x2b\xd5\xc5\xa7\x75\x71\xbf\xca\x19\xfc\x78\x22\x6c\xbf\x0c\xcb\x7f\xd8\x71\x04\xd4\x1d\xfa\x42\xab\x5d\xf4\x82\xc3\x5c\xf9\x9e\x41\x8c\xa1\xc5\x8b\x27\xd2\x29\x7c\x06\xdb\xb0\xdc\x98\xc8\xf0\x71\xf2\x82\x9a\x6e\x05\x20\x1f\x57\xcc\x87\x4c\xd4\x27\xd3\xfd\x39\x71\x46\xc8\x62\xae\xda\xb6\x3d\x21\xe6\x4c\x82\xde\x22\x6d\x77\xe6\x44\xfa\x0e\x56\x11\xec\x61\x7d\x35\x0a\xcd\x5e\x41\x22\x04\x03\xea\x13\xdb\x31\xa4\x21\x40\xe4\x47\xdb\xaa\x1b\xe2\x77\xdd\xc9\x30\x90\x63\x64\xc9\x0c\x7a\x70\xf8\x36\x9b\x38\xb8\x9e\x90\x12\x6a\x1d\x4e\x13\x24\x92\x01\xcc\x19\xd1\x8c\xe0\xfb\x8d\x2f\x92\xb3\x99\xdd\x2b\x7a\x36\x22\x35\x35\xe3\x06\x0a\x64\x3d\xdf\x7b\xa3\x35\x35\x24\x15\x15\xf9\xef\x0b\xf7\x63\xd5\x83\xc8\x25\x3d\x8a\xff\xf3\xd8\x18\x7b\xac\x91\xc1\x27\x23\x09\xbb\x22\x9a\x3d\x18\xfc\x12\x0a\x29\xa5\xb9\x13\x51\xa4\x9d\xdf\x41\xb3\x26\x24\x07\x8d\x64\x37\xf3\xd9\x99\x2e\x29\xf8\x10\x9a\x4b\xbb\x0f\x3a\x78\x98\x06\xe2\xf6\x50\x56\xf0\xa6\xf9\x23\x89\x78\x50\x7a\x4d\xa6\x08\x0f\x19\x17\x21\x4c\x4d\xcf\xf7\x2f\x12\x7d\x3e\xad\xe3\xf6\xb6\x1a\x6e\x32\xbf\xba\x08\x97\x4e\xba\x10\xa1\xdf\xf0\x67\x63\x01\x32\x3f\xb8\x6f\x8d\xb9\xf2\x15\xcb\x21\x32\xae\xb7\x79\x3a\xbc\x30\x97\xe7\x80\xb7\xb8\x8b\x87\x85\x53\x6b\x29\x09\xb4\xe5\x5e\xc6\xc8\xe0\x2c\x27\x91\x90\xe2\xa4\xca\x98\x10\x9e\xc1\x25\xff\xbd\x2b\xcd\xa8\x2d\x10\x27\x17\x81\x0d\x13\x10\x91\x11\xd2\x6e\x67\xeb\x62\x10\x44\x41\x48\xb6\xfd\x89\x1b\x17\x8b\xff\x27\x3d\x50\xf4\x43\x23\x08\xcb\x5c\x33\x53\xd4\xa7\xf8\x1a\xd2\xad\x0e\x8d\xc2\xff\x81\x15\x49\x84\x43\x8b\x2b\xfc\x25\x9c\xa7\x18\x3c\x54\x5e\xb5\xd2\xaf\x94\x47\x7e\x54\xd0\x80\x17\x93\xb6\x38\xe4\x14\x66\x9d\x29\xb5\x87\xe5\x5b\x4f\x63\xe0\x4e\xf6\x90\xb9\xad\xa7\x92\x27\x27\x72\xcf\xee\x8a\x7b\xf4\x9a\xaf\x56\x64\x1b\x5c\x79\xad\x24\xe4\x65\xf1\x3e\x64\x87\xed\x09\x16\x73\xdd\xa4\xb0\x61\x40\xfe\x92\xbf\x55\x24\x88\x15\xc5\x0d\x14\xdd\x58\x07\xed\x41\xe5\x54\x64\xc3\xd5\x8b\x35\xa6\xbf\x22\x9f\x7a\x36\x85\xf9\xbd\x64\x5a\xef\x30\xff\xb4\x19\x9a\x37\x66\xe3\x8e\xa7\x2a\x2b\x52\x4c\x7c\x9c\x01\x52\xa1\x66\x8f\xd6\x4b\x97\x30\xa2\x2b\xe8\xd9\x3a\x6e\x57\x69\x49\xbf\x3f\x92\x15\x64\x5a\x0f\x3c\x7a\x24\x63\xe6\xb8\xda\xff\xc4\x28\xc4\x93\x1c\xb5\x9d\xa3\xef\xc6\x43\x68\x48\x5d\x3f\x91\x12\x4c\x96\xea\xd0\x37\x04\x2e\x10\x04\x8b\xdf\x39\x34\x69\xcc\x96\x1e\xe2\xef\x2a\xe9\xff\xf5\xac\x93\x2c\x4a\xef\x89\x0c\x24\x8f\x87\x9a\x3e\xe8\x8f\x78\x5e\x5d\x60\xe6\x4f\xf8\x2d\xb7\x72\xec\x04\x16\xaa\x54\x63\xf5\xdf\x33\x01\xa8\xa4\xf3\x78\xa1\xbb\x20\x74\xa3\xe8\xac\x82\x25\xce\x60\x87\xa8\xd7\x52\x08\x21\xf7\x16\x0b\x94\xe5\xc7\xee\xf6\xc1\x64\x2e\xa4\x42\xc6\x28\x38\x02\x05\x95\xc2\xa7\x23\xfd\x49\x86\x5a\x0e\xbb\x08\xba\xc5\x33\xfe\x93\x74\xa1\x1b\xa5\x3c\x30\x7a\xe7\x07\xfb\x0d\x0e\x1b\x88\x01\x3a\x68\xef\x50\x7c\xce\xfe\x6f\xec\xa3\x44\x0f\x84\x52\xe0\x25\xbd\x7e\xd5\x3b\xf2\x71\x33\xd7\x0a\xb8\xe2\x1e\x80\xee\xba\x3a\x78\xf3\xf7\x1c\xcb\xcb\xb1\xa1\x3b\x83\xc0\xb5\xff\x78\x36\x0e\xf4\x1f\x0c\xa4\xe9\xcd\x18\xd9\x4c\xfc\x51\x9f\x85\xc1\xf5\x0e\x69\x59\x8d\xcd\x5a\xc1\xf7\x4c\x41\xa2\xd0\xf8\xf6\xdb\x06\x08\x87\x6f\x4a\x01\xb9\xe8\x73\xa3\x51\xb3\x91\xdb\x11\xa5\x78\xef\x90\x2a\x6b\xce\x0b\xdd\x73\xd1\xed\xba\xbf\x16\x41\x6a\x94\x71\xf0\x0e\xc4\x4c\xdd\x23\xa2\x09\x29\x8f\x72\x2b\x59\xfc\xe7\xd9\xa4\x90\x98\xee\xa9\x68\xbd\x56\x66\xcd\xd2\x26\x49\xec\xf0\xcb\x59\xc2\xca\x80\x97\xd5\x24\xd9\xc2\x15\x55\xd5\x96\x14\x0c\x5c\x73\xef\xbf\x56\xa2\x1b\x65\x70\xa2\xad\xab\x44\xf4\x15\x0b\x0c\x66\x15\x35\x8b\xe6\x25\x08\xa3\x14\xc8\xf4\x92\xc0\x1f\xc5\x5c\x04\x69\xd7\x45\x1b\xec\xfb\xcd\x4f\x79\x81\x2c\x99\x9e\xe6\xde\x7b\x1e\x89\x9a\x16\x7e\x3e\x01\xd4\xe4\x9c\xa5\xc3\xd9\x5d\x39\xcc\x04\xc6\x8e\x7e\x1f\x6d\xe6\xc5\xa1\xfd\x17\x12\xd2\x4c\x67\xb0\xf2\xb5\xe8\x97\x67\x4d\x13\xb9\xbc\x12\xd7\xa5\xb8\x32\xd0\xb2\xfb\xaa\xd2\xec\x96\x24\x11\x80\xc7\xf8\x13\x17\x9a\x76\x40\xce\x6a\xef\x4f\x9d\xc0\x96\x9d\x6d\x42\x76\x71\xa5\x50\x83\x7a\xf1\x8b\x9c\x23\x41\xc8\xae\x5b\x41\x7d\xc5\xb8\xd4\xc2\x93\xbf\xa2\x19\xab\xe2\x75\xd3\x9f\x4d\x7b\x75\x04\x4d\xbd\x5a\xe0\x95\xa5\x5d\xac\x51\xdc\x10\x13\x15\x80\x13\x0a\x03\x53\x0c\xb6\xd7\xea\xfc\x0a\xf2\x21\x66\x3f\xfb\xcf\x55\x8b\x27\x56\x12\xf4\xe9\x60\x29\xbe\xb9\x05\x1b\x67\x60\x43\xc9\x68\x9c\x47\x0e\xcd\x78\x06\xb5\xe1\xc8\x59\xd2\xdb\x78\x47\xa0\x35\x77\x7e\x16\xcb\x20\x18\x20\x5a\xc2\x74\x8a\x6d\x39\xc6\x6c\x3c\xfc\x93\xc7\xea\x4c\x62\x7c\x38\x5b\xfe\xf1\xb1\xb0\x61\xb2\xe4\x0f\x8d\x73\xf5\x57\xf3\x93\x0f\xf9\xcb\xfe\x62\x3f\xb2\xfa\x58\x4a\x3b\x15\xb6\xad\xab\x39\xbc\x33\xf1\x87\x5a\x39\x32\x98\xd0\x7f\xfb\x6a\x53\xaa\xc3\xef\x6a\xb3\x1c\xac\xc4\x1f\x09\xc2\x0e\x3a\xf1\x98\xbe\xa1\x37\xd9\xc7\x64\xe6\xe0\xc6\xb9\x18\x7a\x47\xa6\x06\x8d\x0e\x3a\xe9\xe5\xbe\x11\x0d\x5d\xac\x72\x05\xfc\x75\xd2\x6d\x08\xc0\xca\xe1\xfd\xab\xcc\x68\xcf\x1f\x5d\x54\x70\xd4\x23\x21\x1e\xf7\xce\x33\x7c\xf3\x51\x26\x2e\x63\x52\x4a\xee\xe2\xf4\x09\x21\xc5\x4a\xba\x86\x5b\x53\x7c\xc8\xaa\xe5\x51\x35\x1b\xb0\xf6\x67\xac\x76\x1b\xcb\x2f\xa8\xcb\x52\x5b\xf8\xe2\xac\x45\x60\x93\x10\x08\x41\x6f\xb2\xe9\x5b\xfd\xc2\x0a\x2e\x5f\xa8\x8d\x05\x01\xb0\xdd\x25\x76\x3c\x61\xef\x3f\x97\xb0\xb7\x4c\x0f\x1f\xe6\x54\xec\x1f\x54\xc3\x64\xde\x8b\x16\xfd\x1c\x6d\x91\xab\x6c\x5f\x53\x0c\x2b\x82\xeb\xf9\x0b\x5c\x9d\xa2\xf2\x3f\x2c\x7a\x3d\xcf\x42\x67\xb6\x9b\x1e\x1c\x1c\x18\xf4\xd9\xb8\xaa\xf9\x85\x0c\x83\x8e\x51\x82\x4a\x4f\x24\x2c\xc8\x25\x64\x94\x19\x54\x23\xac\x46\xaf\x80\xc0\xd5\xa1\xd1\xc8\xa2\x36\x90\x50\xfb\xd4\xfd\x9f\xb9\x14\x01\x51\x15\xf5\x76\x15\x2f\x9e\x88\x45\xd1\xcb\xcd\x56\x61\x27\x2f\xd7\x80\x95\xb8\x57\xd3\xc4\x78\x42\x8c\xb5\xc8\xfb\xff\x46\x29\x29\x49\x74\x29\x1f\x02\xe7\x9a\xdd\x6e\x97\x5c\x62\x1f\xef\xbf\xac\x7f\x33\x9f\x52\x86\xaf\x91\x5f\x4b\x69\x1b\xc2\x66\x9b\x7c\xfb\x5d\x1a\xd8\x64\xb4\xd1\xb5\x59\x84\x16\xe3\x02\x9d\xb2\xfc\x78\xa0\xfe\xf1\xf5\x78\x5d\xa5\xed\x65\xe1\x4f\x64\x95\x89\xbf\x53\xbf\xa4\x58\xf5\x60\x9a\x37\x48\x97\x63\x05\x93\x5f\x59\x68\xda\x22\xf2\x48\x03\xa8\xd1\x70\x29\xfa\x78\x39\xa1\x00\x27\x3c\x46\xb3\xc7\xfc\x4c\x5e\xcc\x18\xc9\x45\xd1\x48\x87\x13\xc5\x99\x16\xa4\x00\x67\xeb\xd7\x40\x6a\x72\x5c\x89\xb4\xc0\x9a\xf5\xc3\x55\x05\x03\xd4\x86\x76\xa0\xfb\x4c\x81\x4e\x19\x23\xbd\x86\x58\x64\x48\x13\x2c\xa7\x3b\xfe\x90\x5e\x5e\xac\xeb\x19\x06\xbe\x18\x69\x59\xbe\x80\x00\x00\xb7\x1b\x51\x71\x18\x71\x97\x9a\x46\x6e\x1f\x7f\x0f\xf7\x70\x44\x2d\x82\x87\x34\xe0\x13\xb9\xd3\xa1\x32\x25\x61\x96\xb6\xe3\x34\xb9\xae\xe7\x4b\x94\x50\x76\xc9\x3c\x35\x31\xac\xf4\xa8\x64\xa6\x70\x37\x52\xe7\x2a\x88\xf3\xf6\xb5\xa6\xeb\x2c\x1d\xc2\x49\xd3\x4d\x9e\x56\xbf\x6f\xa6\x40\xf6\x01\x0f\xde\x94\x40\xe2\x02\x56\xd3\xba\xa0\xe3\x8a\xe0\x9c\xe2\xbf\xb8\xab\xcc\x64\xf9\xbc\x09\xba\x97\x6d\xd2\x69\x8e\x2c\xe2\x20\xae\xbd\x72\x01\xcc\x06\x94\x86\xed\x6f\x34\x9c\x5f\x2b\x74\xf3\xb6\x8c\x81\x14\xb3\x28\xbf\xf4\xd3\xb9\x49\xf6\xdf\x59\x21\x9f\xa0\x0c\xb5\xc9\xe1\x57\x91\x40\x25\x40\x4b\x32\xe8\xf3\x36\xd6\x9a\x8e\x13\x7d\x10\x2f\x5b\x1f\x44\x1e\xe9\xd3\x77\x0d\xaf\x73\x18\x1b\xf0\x4e\x6e\x37\x4b\xbd\x5a\x17\x12\xf5\x9d\x71\x31\x82\x4d\x42\x09\x7b\xe9\xa5\xf5\x5a\xbe\xf7\x39\x53\x8e\x5d\xdc\x9f\x58\xaf\x92\xa5\x76\xee\xf2\xc8\x50\x88\xf5\xf6\x20\xa4\xae\x52\x9d\xfa\x31\x18\x3e\x49\x02\xec\x1e\x45\x53\x67\xc9\x0f\xae\x8f\x16\x9b\x6b\x8d\x76\x5b\x08\x52\xed\x9f\x27\xc0\xdd\xd2\x2f\x0c\x6b\xe7\x1b\x33\x47\x75\xa3\x1b\x8e\x7d\x1b\x93\xd1\x8f\x02\xac\xd5\x82\x38\xfe\xd2\xec\x99\xf8\xf4\xc6\x4d\x7d\xd3\xa6\x29\x68\xd3\xe2\x9f\xbb\xfc\xbf\x89\xef\x5d\x11\xac\x9e\x04\xd3\xbe\x17\x76\xcc\x9f\x0b\x13\x0d\xf3\xdb\x2e\xa6\x26\x1e\x69\x43\x08\x5a\x3a\xc9\x8f\x6a\x38\x44\xe7\x49\x18\xd7\xd8\x9b\x34\x6f\xe4\xc4\xd9\x5f\x2c\xc6\x2b\xb9\xc1\x8d\x71\xd0\x17\x93\x84\x22\x1e\xc1\x04\x1a\xbf\x79\x0b\xfa\xaa\x12\x73\xeb\x0a\xb1\xb7\x9e\xdf\xbc\xcf\xd2\x3a\x44\xcb\x5b\x4a\x5f\x52\x67\x0f\x09\x61\xe3\x6a\xe7\xd1\xb9\xa2\x6a\xa6\xfc\xa0\x6e\xfd\x5d\x31\x9b\x23\x49\x14\x17\x1a\xfe\x6e\xa7\x4a\xd1\x09\x42\xc7\x74\x2e\xfd\x8e\xa5\x66\x61\x2e\x72\x43\xb8\xa6\x24\x15\xec\x7c\x5d\x88\xcd\x51\xfd\xf7\xf7\xc5\x11\xd7\xbe\x02\x48\x92\x44\xcc\xe7\xf7\x86\x39\x3b\x1b\x02\xb3\xfe\xfd\x17\x6c\x7c\x53\xf4\x0f\xb4\xb5\x1d\xd8\x1c\x05\xea\xb5\x7d\xb5\x2e\xc0\xc0\x21\xab\x59\x42\x2e\x99\x16\xba\x0c\xe8\x18\x76\x86\x79\x68\xc0\xff\x0d\x45\xca\x22\xbe\x10\x21\x6d\xdc\x51\x90\x0e\x7e\x34\x16\x2e\xdd\xe8\x13\xc9\x5f\xf6\x9e\x13\x69\x4c\x7c\xd3\x85\x95\x59\x99\x8e\x26\x26\x12\x04\x48\x12\x05\x3b\x8a\x86\xab\x07\x0a\xed\x3c\xd2\x5e\x27\x58\x50\x6a\x0e\x54\x73\x01\xc7\xfe\x1b\x9b\x89\xc7\xd1\x73\x6c\x0b\x72\x11\xf1\xcd\xf8\x74\x92\xa3\x06\x9b\x89\x6c\x88\xa3\xb2\xe7\x1e\xf5\xf0\x10\xef\x9e\xbe\xac\xbb\xf3\xa9\x3f\x06\xe5\x29\x04\x29\x32\x9b\xe4\x27\xa7\x43\xdc\x9a\xbf\xda\xf3\x39\xae\x01\x4d\xfb\x51\xd0\x34\x7b\xdb\xad\x8a\x55\xf0\x90\x09\xd8\x8d\x91\x3b\x25\xba\x4a\x5e\xca\xdd\x23\xe7\x76\x0b\x69\x17\x40\x31\xb9\x63\x28\x16\xbc\x19\x6f\x79\x17\x58\x19\x7c\x82\x49\x2e\x9b\xb7\x2d\xdc\xdf\x78\xd3\xb0\x6f\x2b\x5e\x3b\x14\xe9\x17\x88\x87\x57\x33\x04\x7f\x30\xa4\xa2\xb3\xe3\x1a\xb7\x64\x0c\x94\x3d\x09\x6d\xaa\xc8\x94\xc6\xff\x1a\x71\x7b\x9a\x4e\xbf\xdf\x5a\xf2\x38\xcb\x65\xb0\x75\xcc\x0f\x33\xe7\xf1\x63\x40\x5a\x6a\x69\x71\xff\x74\xac\xdf\x75\x42\x81\xb8\x97\xe1\xea\xf6\xb4\x5b\x4c\x9c\x08\xab\x94\x6d\x21\xa5\x56\x1b\xb6\x0f\x0b\x16\x9d\x37\x04\xa0\x14\xfe\x35\xa1\x43\x9e\x68\xac\x04\x31\x8d\x8a\xe5\x44\x1a\x06\x11\xa1\x17\x8f\xea\xf9\x02\xcb\x60\x47\xf6\xed\xee\x4e\x3f\x6e\x77\x9c\xed\xa1\xa2\x0f\xbc\x42\x07\x9f\xe4\x61\x15\xc3\x0e\x93\x7d\xbe\x02\x4e\x6e\xe8\xac\x4b\xbf\x42\xa3\xad\x93\x30\x85\xdc\xdc\xb8\x59\x32\xd9\xcc\x1e\xaa\xf8\x19\xf9\x35\x78\x02\x56\x83\xd6\x81\xea\xdc\xf9\x77\xa0\x9c\x35\xea\x03\x46\x4a\xd6\xa2\xd6\xf9\x8d\x02\x91\x70\x49\xbe\x98\xa0\x7e\xb2\xe6\x84\xa4\x3c\x43\xe9\xee\x56\x58\x5b\xcc\x3e\x49\x6f\x03\xb7\x68\x17\x3e\x69\x81\x88\x81\x7c\x56\x5b\xa8\xea\x4b\x9d\xf4\xdb\x10\x3d\x75\xf0\x16\x54\x63\xb9\xeb\xd9\x57\x8a\xba\xb5\xbb\x76\x36\x71\x76\x75\xb1\xa5\xfe\x86\xda\x55\x4d\x2c\xfc\x8d\xf8\x5f\x90\x9e\x2d\x5a\x47\x12\xc3\xbc\x04\x9c\x81\x6f\x49\x64\x58\xcc\x49\xd5\x14\x4b\x7c\x51\x6e\x3f\xc8\x30\x0d\x3b\x82\x60\x2a\x23\x22\xc7\xcf\x66\x80\x3f\xe3\x9f\xd7\x49\xc9\x9e\xa6\xee\x22\xda\x6b\xb2\x44\x12\x82\x1b\x02\x1b\xa4\xdd\x0e\xfa\x63\xfb\xaa\xe3\xbf\x9c\x48\x18\x52\x3e\xba\x6a\x50\xdc\xf1\xc9\x40\x20\x9e\xbb\xa1\x0f\xd5\xb5\xbb\xef\x37\xfe\x9b\x19\xa3\x3d\x21\x97\x3b\xc0\x77\x00\x47\xdb\x72\xd3\x96\x10\x76\xbf\x5a\x92\xf9\x1a\xa0\x68\xb5\x7a\xb8\x5a\x29\xff\x1b\x59\xdd\xdf\x03\xe3\x47\x0a\x2f\xcf\x77\x81\x99\x77\x0f\x08\x17\x94\x6b\xcb\x48\xbc\x1d\xd9\xb3\x13\xb7\x12\xde\x9f\x07\xf2\x30\xcf\xb7\x54\xa2\x58\x79\x7d\x59\xef\x6a\x0a\x02\x5e\xf9\x86\x2d\x3c\x09\xc4\xce\x67\x89\x4e\xc5\xa5\xf6\x6f\xe5\xc3\xe3\x00\x10\x67\x4b\xb7\x70\xb7\x47\xfc\x48\x4f\xdd\xc1\x4d\x92\xb8\x27\x80\xab\x13\x7f\x21\xeb\x67\xd1\x0d\x05\xc5\xab\x82\xf5\xc8\x43\x41\xc3\x25\x30\x7c\xfe\x9e\x64\x33\xc1\xec\x91\x80\xab\xd6\xb6\xa7\x40\xce\xad\x8b\xa4\xd7\x8f\x15\x68\xa7\x99\xc2\x27\x6e\xa8\x31\xdc\xd4\xab\xbf\x9c\x90\x1e\x03\x99\xbb\x15\xbb\xb4\xe5\xeb\x88\x6b\x7b\x18\x45\xdc\x05\x0f\x90\xce\x61\x19\xc2\xc6\x11\xf5\xd3\x74\x11\x41\xd7\xb1\x43\x10\xbe\xd0\xc6\xe5\x69\x5e\x94\x72\x87\x3e\x85\xd9\xc5\xe0\x7d\x98\xc3\xd4\x80\xd1\x70\xba\x28\x82\xd4\x6f\x29\xd5\xf5\xe3\x26\x87\x56\x8e\x4e\x98\xe3\xfd\xfa\xcd\xc4\xd4\x1a\x5f\x43\x3a\x0e\x78\xb8\xc7\xe3\x6e\x06\x08\xf5\x22\xc6\xb3\xb5\xa5\x55\xc2\x2a\x0a\xb2\x52\xcc\x68\x3f\x3d\xbb\x58\x4e\x18\xe4\x66\x24\xc4\xcf\xa4\x21\x33\x34\xc7\xa6\xb8\xca\x36\xa2\x7e\x76\x3f\x04\x17\x89\x86\xbd\xea\x55\xba\x5d\x15\x84\x2f\x5c\xbc\xb4\x94\x85\x73\x6b\x35\x63\xb7\xd2\xe1\xb6\x18\x1e\x7f\x3a\xde\x28\xa5\xb8\xff\x8f\x4e\x77\x83\x94\x1d\xf8\x15\x31\xce\x08\xe9\x68\xd5\x4e\xfa\x50\x27\xbd\x22\x36\x2a\x54\xab\x2e\xfe\xcf\xcc\x7a\xbd\xe1\x53\x04\x7e\x3d\x88\x34\xdd\x0c\x48\x3f\x03\xfd\x8f\x13\xbe\xee\x9d\xbf\x16\x08\xcd\x77\x93\x98\x44\x11\xb0\x51\x24\x75\xc2\xea\xad\xd7\x12\x01\xbb\x33\xe0\x11\x12\x86\x91\x22\x12\x71\x71\x82\x81\x42\x2f\x5a\x72\xcc\x46\x3a\x68\x7f\x5d\xae\x2f\x87\x95\x46\xd3\xf4\xdf\x94\x76\x31\xa2\xdd\x8f\x92\x52\x19\xb6\xc2\xa2\x5a\xa1\x31\xcf\xcd\x7e\x2b\xd7\xa2\xa1\x1f\x13\xea\x04\x8f\x89\xf7\x5b\x49\xda\x2c\x82\x2c\xdd\xdb\xc1\xc3\xe3\xf1\x1d\x06\x79\x22\x0a\x13\xc4\x3f\x01\x08\x38\x2e\xc8\xcd\x17\xb4\x37\x99\xaf\xc6\x45\x5e\x24\xe8\xdd\xa3\x79\xdd\xb6\x8a\x91\x60\xbd\xb7\x9e\x72\x38\x68\x6b\x44\x3b\x82\x12\xae\xaf\x2e\x34\x51\x98\x6d\xa3\xa0\x8d\xd8\x82\xe2\x3c\x29\x50\x44\xbe\x11\x2f\x7f\x03\xed\x77\xca\x6e\x36\x96\xa0\x6f\x32\x80\xef\x63\x94\x13\xb7\xf0\x1b\x2a\xf4\x08\x94\xad\x1c\x3c\x61\x5a\xc6\x41\x19\x66\x64\xb7\x96\x96\xa7\xf4\x0a\xc3\xdb\xde\xc2\x23\xb7\xf1\xc2\xe7\x7e\xda\x05\x29\x36\xab\x41\x5e\x8b\x22\x43\x95\x9e\x83\x4b\xc7\x4b\xcf\x60\x2d\xc5\x45\xc2\xe5\x25\xf4\xd8\x70\x3b\xc8\xa7\xfa\x8e\xf9\x61\xc7\xe9\x0c\x06\x49\x15\x16\x9c\x32\x88\x59\x22\x6b\xf7\x00\x59\xca\xaf\x02\xf1\x45\x16\x15\x52\x80\x95\x99\xa9\xbb\x7d\x2e\xa6\x94\xfa\x40\xb4\x94\xb2\x84\xde\x42\x97\x1e\x1a\x05\x6e\x53\xd5\xe5\xff\xac\xd3\xc1\x91', 1) ================================================ FILE: environment/world/position_calc.py ================================================ import math def get_distance(a_x, a_y, b_x, b_y): return math.sqrt((a_x - b_x) ** 2 + (a_y - b_y) ** 2) def angle_cal(x1, y1, x2, y2): ''' :param x1, y1 the position of start point :param x2, y2 the position of end point ''' angle = math.degrees(math.atan2(y2 - y1, x2 - x1)) if angle < 0: angle += 360 return int(angle) # TODO: random position def pos_generate(side1_d_num, side1_f_num, side2_d_num, side2_f_num, size_x, size_y, random, random_pos=False): ''' random_pos == True: side1 on the left, side2 on the right random_pos == False: side1 on the right, side2 on the left ''' if random_pos: pos_x = [900] * side1_d_num + [800] * side1_f_num + [100] * side2_d_num + [200] * side2_f_num side1_course = 180 side2_course = 0 else: pos_x = [100] * side1_d_num + [200] * side1_f_num + [900] * side2_d_num + [800] * side2_f_num side1_course = 0 side2_course = 180 side1_d_pos_y = [int(float(size_y) / (side1_d_num + 1) * (index + 1)) for index in range(side1_d_num)] side1_f_pos_y = [int(float(size_y) / (side1_f_num + 1) * (index + 1)) for index in range(side1_f_num)] side2_d_pos_y = [int(float(size_y) / (side2_d_num + 1) * (index + 1)) for index in range(side2_d_num)] side2_f_pos_y = [int(float(size_y) / (side2_f_num + 1) * (index + 1)) for index in range(side2_f_num)] pos_y = side1_d_pos_y + side1_f_pos_y + side2_d_pos_y + side2_f_pos_y return pos_x, pos_y, side1_course, side2_course def pos_update(pos_x, pos_y, course, step, size_x, size_y): pos_x += int(math.cos(math.radians(course)) * step) pos_y += int(math.sin(math.radians(course)) * step) if pos_x < 0: pos_x = 0 if pos_x > size_x: pos_x = size_x if pos_y < 0: pos_y = 0 if pos_y > size_y: pos_y = size_y return pos_x, pos_y ================================================ FILE: environment/world/replay.py ================================================ __pyarmor__(__name__, __file__, b'\xe7\x50\x8c\x64\x26\x42\xd6\x01\xb9\x4e\x3f\xdf\x40\x9b\x76\xef\x72\x6c\x30\x53\x5b\x51\x0e\xf9\x7d\x9a\x1f\x95\x7a\x32\xda\x47\xc0\x21\x4e\x84\xc2\x3c\xf0\x83\x93\x7e\x7e\x7e\x94\x24\x9f\xb7\xc2\x25\xee\xbc\xad\x52\x4c\x9a\xd9\x9e\x8a\xbb\x31\xb9\x38\x87\x31\xc3\x27\x4d\x4a\xd7\xd9\x9f\xe9\xb4\xa4\x99\xa3\x3c\xf3\x83\xa4\xc0\x13\xcf\x7f\x58\x09\x60\xa2\xa1\x15\x51\x95\x47\xb1\x01\x5e\xef\xcf\xde\xa0\xc6\xc7\x4a\xeb\xf4\xeb\xff\x74\x92\x4a\x45\xbf\xdb\x6b\xdf\x05\xb7\xa0\x3b\x26\x8a\x06\x85\xa0\x64\xbd\xa7\x13\x52\xdb\x8d\xda\x06\x03\xa4\xa7\x4d\x8c\xce\x7e\x0e\x21\x92\x75\x80\xb9\x18\xc3\xf9\xd8\x32\x6a\x99\x83\x06\x71\xdd\x7f\x74\xfa\x01\xec\xfa\xe7\x73\xfb\xce\xd7\x39\xfe\x82\xac\x9e\x5c\xcc\x01\x71\xdb\x16\xe6\x3d\x5d\x67\x58\x8c\x5b\x7c\xd7\x39\x98\x79\x5f\xf8\x5f\x9b\x75\xd1\x39\xc5\x46\xee\x73\x25\x53\xde\x11\xe6\xa6\x1a\x15\x87\x0c\x74\xa8\x15\x69\xfd\x97\x0a\x75\x26\xb5\xda\x5c\x92\x8f\x92\x4e\x2d\xf8\x04\xaf\xa9\x9c\x98\x3d\x4b\x05\x87\x41\x65\x5b\x94\xc5\x3e\x08\x2f\x71\x99\xe6\xdd\xe3\x05\x0d\x4c\x8e\x2e\x17\xe3\x4d\xa6\x6c\x09\x98\xc7\x9b\x98\x9c\xc4\x01\x6a\x58\x60\xe3\x0a\x05\x0a\xed\x20\x80\x26\x79\x6f\x4f\x5e\x6a\xed\x63\xe7\x6f\x84\x5c\xab\xc1\x76\xa9\x6f\xcb\x16\x0d\x46\x93\x6e\x66\xda\x6a\x13\xca\x77\x42\xba\xc5\x66\x6d\x96\x2a\x0b\x49\x83\x35\xee\x8a\x17\x3e\x82\xc2\x1d\x7f\xbb\x3c\x5f\xdc\xba\xe8\xf7\x68\x25\x0d\x03\xea\x74\xb0\x2c\x24\xec\x8d\x13\x37\xcb\x5a\x5d\xd0\x9a\x82\x50\x67\x36\x10\x6b\xcf\x82\xaf\xcf\xed\x42\xd3\x41\xae\x0d\x0b\x65\x4e\x62\x3d\x83\x98\x4e\x25\xe7\xcf\x76\x1f\x1e\x5f\xb5\x4d\x3b\x52\x04\x15\xf6\xff\x39\x24\x0c\x5e\x22\x78\x83\x60\xad\xca\xbb\xfb\x8b\x26\xa3\x08\x6a\xc3\xc9\xf2\xf3\xf3\xcc\x8d\x71\xf4\x1d\x0c\x94\x90\x1a\x38\x3e\xf4\x32\xee\xf0\x1d\xa7\x74\x07\xf9\xa0\xdd\xa1\xce\x7d\x61\x88\x08\xc7\x29\xf5\xa1\x28\x0a\x69\xbb\xca\x5a\xd7\x2d\x4d\xf9\xea\xd9\xc7\x23\xf9\xbf\xba\x89\xd3\x6b\xf2\x85\xf3\x2a\x28\x72\x78\x2d\x20\x9f\x69\xe7\x27\xf7\xf0\xb9\xc9\xb7\x31\xd4\xf9\xfc\xce\x58\xe5\x2c\xb9\xee\x73\xae\xe0\x36\xaf\xe6\xc5\xcd\x67\xbd\xad\x0e\x79\xd6\xa2\x27\xd0\x4d\x1b\x90\x13\xb0\xa6\xc5\x1d\x5f\x79\x91\xf7\x5e\x9b\xbd\x4c\xaf\x41\x5c\x1c\x53\x4e\x0e\xe3\xdc\x7b\x57\x05\x7d\x90\x56\x6e\x7b\x22\xe3\x57\x5c\x4e\x5c\x24\xaf\xa6\x04\xf1\x5a\x9a\x5f\xbd\x7e\x4a\x2b\x5d\x12\x6f\x07\xd1\x47\x21\x24\xce\x93\x74\x71\xdd\xd9\xf6\xdd\xa8\x5f\x66\xe1\xfe\x29\x9b\x60\xf3\xbf\xb6\xaa\xc5\x96\x28\xf5\x82\xef\x4a\x82\x25\xb6\x33\xf2\xfc\x0e\x1a\x64\x41\xb8\xf5\x00\xe6\x15\x56\x78\x83\xfc\x50\x92\x14\xad\xbf\xde\x0f\x02\x7d\x70\x03\xd0\x72\x14\xf7\x52\x48\xfb\x41\x4a\xe8\x8c\x76\x39\xbb\x77\x32\x14\xe6\x52\x42\x8f\x1b\x4a\x00\x94\x52\x5f\x14\x6c\x19\x4a\xa5\xc3\x9e\x02\x2e\x0f\x56\x15\x13\x5f\x21\x2a\x31\x4c\x17\x39\x6c\xce\xc0\xc6\xc5\x5b\x69\xf9\x6b\x4f\xcf\xe6\x7d\x44\x65\x6c\x4e\x3f\x67\x06\x53\x9b\x54\x34\x04\x28\xea\x5c\x8a\x05\x1a\xd1\x1a\x26\xf9\x86\x2f\xde\xe1\xf5\x1f\x1c\x01\xbc\xc9\x04\x5f\x3a\x41\x3d\xa7\x48\xe2\xd8\x78\x96\x1a\x60\x28\x4d\x08\x52\xce\x7f\x19\x5c\x52\x35\x09\xbd\xda\x53\x19\x1f\x41\x72\x60\xa7\xc5\x5f\x35\x5b\xa7\x37\x3d\xba\x23\xde\xcc\xa6\x0f\xa7\x7b\x26\xb7\x83\x70\x33\xed\x72\xec\x83\x93\xba\x6e\xa5\x52\x63\xfa\xb0\x1d\x30\xa8\xa3\x23\x30\xa1\x13\xde\x3b\x0f\x2a\x05\x49\xa0\x7b\x49\x6d\x09\x60\x2a\x97\x95\x4d\x65\xcd\x7a\x7f\x54\x6e\xb0\xd3\x52\xad\x21\x10\xdf\xff\x3b\xe2\xb5\x0e\x23\xab\xb4\xc6\xf6\x25\x48\x41\x99\xec\x53\x3c\x5a\xb9\x22\xd6\x54\x1d\x70\xa0\xf8\x98\x78\xe4\x25\xd5\x29\x77\xf2\x4d\xd4\x8b\x4e\x11\xb0\xbe\xfc\xbb\xe8\xbe\xc9\xbd\xf8\x67\xb9\xf7\xc3\x32\x3b\x41\xea\xe6\xa5\x73\x94\x1d\xa3\x75\x41\xd5\x5b\x73\x2c\x38\x00\x37\x3a\x44\xd3\xe0\xe8\xe9\x41\x1b\xb6\x67\x2b\x16\x73\xf6\x0f\x1f\xcf\xcb\x84\x1e\x4c\x52\xb6\xec\x7e\xd2\x73\x51\x41\x5c\x19\xdc\x73\x03\xd7\xd0\x49\x91\x43\x68\x90\x5e\x74\xc7\xd3\x1d\x56\xdb\x6f\xf0\x67\x0b\x7a\x48\x8d\x7e\xde\xe8\x0f\x24\x00\xda\x04\xe0\x9a\xb5\x51\x7c\x30\x88\xaa\xe3\x92\x8f\x9a\xbb\x46\xd1\xd2\x48\xc1\xac\xb6\xb4\x2f\xe1\xd2\x25\x1d\x42\x39\xcc\xfa\x81\x53\xf8\x18\xef\x64\x36\xd8\xe5\xe3\xc4\xd4\x50\xf4\x22\x2b\x55\xc3\x8f\xe3\xd5\x8e\xab\x01\xc0\xcd\x36\x00\x00\xcc\xa3\x12\x9e\x85\x0c\x9d\xe5\x0a\xc0\x39\xe6\xe9\x20\xa7\x87\x1b\xfc\x26\x64\x79\x1f\xec\x8f\x9b\xd8\xfc\xb0\x74\x15\x3d\x4c\x64\x58\x9f\x98\xa6\xb9\xc2\xa2\x5c\x9e\x78\x7d\x1a\xb2\x2e\x04\x40\x4a\x43\xc8\xe4\x75\x97\x20\x49\x07\x53\x0a\xf7\x4f\xae\x21\xee\xb2\x11\x68\xa1\xfe\x2b\x14\xbc\xd6\x91\x43\x26\x00\x26\x50\x81\xc2\xe6\x4c\x7d\xd3\xf5\x33\x39\x28\x60\x70\xfa\xc4\x6f\x9e\x71\x4d\xbc\xf6\x9c\x73\x47\xea\x13\x86\x5f\x90\xa6\x52\x93\xd7\x8f\xfe\x22\x12\x4f\xdf\x91\x72\xe5\x9b\xed\x90\x20\x87\x6d\x0e\x5e\x46\xb1\x5e\xab\xea\x99\x65\xa8\x94\x8c\x67\x2e\x0b\xa8\x4f\x08\xac\x39\xcb\x22\x16\x78\x76\x03\xbc\x17\x20\x86\x56\xc6\x2f\x14\xdf\x68\x45\x51\x1c\xca\x0e\x56\xd1\x12\xeb\xa3\xd7\xa3\x46\xa1\x7f\xc4\xc2\x16\xb1\xe1\xa7\xfa\x25\x25\xda\x9a\x7b\xbf\xa5\x4d\xe6\x75\x52\x65\xd0\x50\x64\x06\xbd\x99\xe6\x37\xa7\xe5\x60\xc1\x24\x3a\x85\xbc\xe3\x4c\x66\x11\xfd\x57\x7d\x5a\x3f\xec\x53\x67\x32\xe8\xf8\x34\xcc\xc1\x01\x20\xc7\xac\xa9\x00\xc0\x31\xe3\xfc\x65\x1e\xd4\x02\x12\xe5\xa6\x67\x42\x64\xbd\x20\xd7\x06\x7f\xfa\xfa\xc4\x46\x97\x39\x07\x10\x3e\x1a\x25\x2f\x9d\x70\x24\x4f\x9a\x69\x55\x0d\x43\xd5\x65\xa2\xa9\x36\x27\x55\xaf\x3f\x65\xb2\x69\xd4\x86\x9a\x6c\xfd\x6e\xd7\xe8\x71\xbf\xeb\xda\x10\x97\x42\x66\x5a\x6a\xc4\xbd\xf6\xd4\x74\x2c\xca\xd2\x17\x02\xf9\xfe\xd3\xb8\x07\x0f\x1d\x2e\x3e\xe8\x6a\xd7\xf3\xdc\x31\x07\x2a\xc4\xbb\x1d\xcd\x63\xa4\xb6\xc8\x9a\x80\xde\xa4\x44\xaa\xf3\xb8\xda\x25\x45\x77\x31\x0f\x36\xe8\x0d\x85\xdd\x26\x1f\x95\xfe\xd7\x1a\xbb\x43\xed\x4f\xc1\x8f\x78\x44\x20\x68\x45\x1b\x03\xe0\x78\x3d\x9a\x89\xbb\x67\x47\x1f\x91\x4a\xc1\x13\xe7\xa2\x3f\x42\x70\xc9\x0d\x02\xcc\xcd\x3b\xe3\x6e\x69\x64\x5e\xf5\x00\x83\x2d\x7e\x88\xf9\x7c\x8a\x70\x28\x4a\x1a\xaf\x3a\xe1\xdb\x60\xcf\xfc\xa7\xda\x63\x7b\xa8\x6e\xd8\xeb\xfe\x86\x24\xcb\xf4\xe7\xbc\x11\x6d\x49\x29\x69\x27\x8b\x9f\xbb\xef\x1f\xf6\x98\x2e\x10\x76\xb4\xb1\x5a\x03\xe6\x25\x6e\x11\xb7\x14\xc6\x82\x81\x01\x75\xb0\x69\x3a\xef\xec\x88\xf9\x8e\xd9\x80\x67\x39\xb2\x68\xc5\x13\xf3\xd3\x22\x83\xda\xa5\xa5\x07\x5c\x8b\xf2\x01\x04\x3f\x66\x3d\xeb\x7c\x3f\xb4\x2c\x38\xa1\x20\x92\x24\x90\x3c\x29\xb8\x02\xa9\x49\xf8\x51\x51\x15\x08\xa0\x38\xe4\x61\x97\xa9\x8a\x27\xc3\x1f\x17\xb0\x39\x47\x5e\x8e\xf1\xef\x0a\x84\xe0\xd5\xda\x7d\xa4\x3e\xe0\x11\x49\xe2\x4e\xa9\x36\xf4\x4b\x2d\xb1\xa3\x28\x9d\x39\xa3\x1d\x2a\x45\xc5\x9d\xa4\x5d\x2a\x8d\x01\xfd\xd3\x59\xd7\x32\xc0\xf3\xb3\xec\x07\x3d\x82\x86\xa2\x56\xf7\xd1\x96\x3b\xb7\x82\xbb\x06\xcc\x73\xe1\xfb\x56\x31\x56\xd4\xa8\xe7\xdb\xde\xe4\x84\x73\xc1\xd1\xd4\x2f\x50\x18\x55\x71\x1a\xe9\x45\xbe\xd9\xba\x6b\x17\x20\x32\x73\x57\xa3\x8a\xef\x8b\x76\x4a\x93\x59\x9c\xf9\xaa\x47\x79\xd5\x5f\x8b\xe4\x65\x71\xef\x70\x66\x43\x11\x7a\xf6\xfe\x61\x3c\xee\xdc\x35\x1a\xc9\x2b\x86\x87\x78\x02\xda\x21\x2c\xb3\x42\xaf\x6e\x0a\x8b\x92\x24\xed\x6e\x88\x91\x51\xf9\xb8\x2d\x37\x40\x03\xd7\x1c\xcd\xa9\x33\xf5\x53\x65\x85\x88\x4f\x35\xf7\xa1\xef\xf9\xbe\x32\x38\x7c\xbf\xfb\xa1\x58\xdd\xc1\x65\x86\x62\x34\x75\x80\xbe\x57\xd4\x35\x75\x02\x3a\x93\x61\x11\x26\x3c\x08\xa2\x19\xf6\xcd\xcf\x9d\xa4\xeb\xbc\x1f\xa1\x77\x75\x06\x00\xf4\x15\x34\xc4\x25\xba\xfe\x65\xfa\x80\x6b\xba\x1e\x68\x86\x2d\x74\xe5\xc7\x12\x48\xdb\x35\xee\x9a\x36\x7f\x87\xba\x64\xee\xc6\x72\xc4\xf5\x83\xb4\xc7\xe2\xfd\xee\x8d\xa2\xe7\xb5\x36\x61\xc8\x8f\x95\x94\xf5\x27\xbe\x96\x7a\x39\x00\xf5\x84\x13\xd9\x39\x17\x86\xa2\x28\xc7\x74\x39\x51\xa5\x5f\xc2\x54\xf5\x18\x9b\xd0\x43\xfe\xb4\x62\xee\x47\x5c\x31\x29\xa0\xcf\x62\xa9\x30\x20\x28\x82\xfc\xa1\x75\x13\xf8\x89\x8f\x0f\xe5\xac\x5a\x37\x46\x5b\xe9\x56\xed\xd0\x97\xbe\x23\xce\x09\xcd\xde\x08\x89\x5b\xfe\x95\x34\xa1\x18\x5c\x8c\x5b\xea\xef\x54\x3a\xdc\x4c\x16\x0b\x1c\x6a\xc5\xfa\x8e\xc8\x56\x6d\x92\x0a\xcd\x27\x57\xa1\xad\xce\x79\x2e\x74\x5e\xf0\xd0\x79\xd4\x2e\xfe\xd7\xd7\x9a\x91\x53\xb3\x5e\x80\xf8\xff\x81\x22\x02\x57\x69\x6a\xb7\x55\xe8\x76\x3f\x24\x53\x4e\xf1\xba\x99\x30\xc3\xe9\x20\xf6\x5f\x80\x2b\x12\xda\x14\xfa\xdd\xb5\x6a\x55\xf4\x37\xcb\x84\xc3\x71\xbb\x02\x07\x90\x0a\xa5\xfa\xb0\x9a\x83\x10\xba\x0e\x41\xa6\xd2\x1d\x29\xd9\x59\x67\xb3\xce\x5b\x68\x8c\xc7\xc3\x69\xc6\x3e\x0c\xbc\x47\x06\x32\x29\x40\x0e\xb9\x80\xa1\x2c\x19\x77\x68\x5d\x58\xdc\x6a\x19\xcf\x32\x29\x2e\xe0\xc3\x9c\x5d\x9a\x35\xdd\x5b\x7b\x78\xfa\xf8\xda\x54\x1e\xa0\x27\x60\xda\x04\x8f\x75\x44\x5d\xc4\x7a\x53\x28\x86\xfc\xfe\x24\xb3\xf3\x3d\xb2\x25\xda\x44\x1e\x29\x8f\x4a\x01\x44\x76\x09\x38\x8f\xff\x82\x04\xd5\x33\xd8\xa2\x66\xb1\x2e\x7a\xca\x84\x97\x80\xd3\x23\x80\xe0\x23\xc5\x9d\x9e\x10\x4f\x27\xfb\xee\xab\xda\x29\x5f\x79\xa6\x4d\x52\xbf\x87\x6f\x34\xe2\x3f\xfc\x3d\xd1\x17\x21\x21\x2a\xd3\x69\xaa\x2a\x32\x59\x56\xbe\x95\x14\x6e\x02\x24\xf5\x13\x1c\x50\x2f\xaf\x51\xc9\xbd\x97\x07\xfd\xf6\x9a\xe0\xee\x14\x92\x84\xac\xb1\x08\x65\x8e\x63\xf3\x53\x76\xaf\x4b\x99\x90\x3c\xb0\x79\xeb\x4e\x1e\xab\x49\x94\x79\x6f\xde\xd8\xb6\x62\xaf\xec\x56\xc6\x6d\x8c\xf3\xfa\x68\x2a\x88\xc9\x80\xab\xc9\x49\x6d\x15\x4b\xc1\x2c\x32\x4f\x0f\xf8\xbd\x40\xee\x2f\x2d\x39\xb9\x6e\xec\x7a\x2c\x24\x42\x66\x11\xef\x65\x74\x41\xd7\x6a\xd1\xdb\x0d\xe8\x2a\xcf\xd2\x09\x75\xd6\x72\xe6\xb3\xc2\xcf\xb6\x3c\x1b\xac\xc1\xf6\x96\x01\xdb\x3f\x24\x2d\xe8\x4f\xab\x5f\x48\x89\x90\x8d\x56\x83\xe0\x75\x8f\xc7\xb5\xe9\x30\x74\x42\xe4\x69\x2f\xf2\xb3\x3f\x96\x2d\xa9\xbe\x25\xba\x06\x5c\x3f\xb9\x69\x4a\x98\x01\xe9\xf7\xec\x0a\x3f\xe6\xef\x4f\xce\xa4\x53\xfe\x58\xe7\x20\x58\x26\xc6\xcc\xbd\xb8\x15\xa2\x4e\xdf\x26\xac\x30\x34\xb6\x23\x87\x3e\x6c\x2d\xb0\x51\xed\x39\xce\x33\x10\x91\x88\xba\x84\x49\xd7\xbe\xe5\x7c\x93\xf1\x6c\x68\xf1\xa8\x77\x3a\x86\x95\x95\x6a\x86\x5c\x7c\x0b\xc5\xe3\x31\x8a\x28\x2e\xe4\xc6\xea\xc2\x4b\x9c\x83\x06\xda\xe7\xfd\xd1\x43\x74\x7f\x78\x78\x98\x15\x06\xf2\x0c\x66\xcb\x24\x69\xf9\xd8\x25\x82\xbd\x6c\xa4\x0e\xfa\x96\xbf\x92\xc6\x13\x2a\xb9\xe9\x07\x37\x3e\x95\x51\x35\xcc\xc4\xab\x30\x26\x50\x6e\x03\x4f\x71\xed\x18\x3e\x9f\xc8\x45\x91\x24\x12\xcc\x8f\xb7\x8c\xac\xe4\x1d\x99\x47\x44\x59\x5c\x14\x81\x36\xee\x94\x32\xf4\xc3\x2e\x36\xf3\xc9\x12\x39\x1d\x69\xe9\xcb\x7b\xdc\x34\x38\x0a\xbb\xe4\x6e\xf5\x8e\x65\x2d\xfc\x7d\x9e\x93\x12\xaf\x1d\x96\x3b\x7f\xaa\x23\x0c\x95\x3d\xf5\x59\x8f\x02\xc9\x9b\xdb\x51\x7b\xae\x18\xa9\x97\x20\x53\x64\x10\x42\x7e\x7d\x36\x75\x33\x39\xbe\x74\x11\xfa\x83\x12\x0a\x66\xb6\xf0\xbf\xc6\x4b\x63\x21\x28\xe2\xb1\x1c\x4e\xa8\xf0\xed\xb9\x0c\xf6\x22\xa7\x9e\xf6\x5c\x43\xb5\x09\x3a\x9c\xcc\xb1\x79\xe6\xa4\x36\x52\x98\x39\x92\x66\x38\x0a\x50\x80\x6d\x2d\xe4\xc8\x01\x54\x47\x06\x3a\xe8\x38\x2d\x91\xfc\xcb\x90\x8b\xaa\xc8\xcf\xdc\xba\xe7\x2d\x5d\x46\x9f\x2a\x2b\x82\x1d\x62\xe3\x90\xe4\xf5\x2b\x42\x73\x20\x7e\xb6\x66\xd0\x80\x81\x51\x5b\xa4\x44\xff\x31\x60\x48\x17\xf6\x76\x6e\xa5\xf2\xa3\xee\xb8\xee\x77\x44\xa4\xe1\x35\x50\xed\x4a\x92\xd4\x82\xf0\x45\xfc\x50\xd8\xe7\xd5\xe4\xd3\x64\x6e\x93\x82\xe0\x73\xb3\x04\xa9\x10\x7f\x7c\x15\xa2\x56\x92\x7c\x8d\x7d\x36\x19\x0f\x93\xfc\x3d\x43\x64\x43\x9d\x5e\xa9\xdd\xaa\x1a\xba\xba\xde\xec\x30\x83\xe9\x84\x24\x7d\xfa\x5d\x7a\x88\xf2\xa7\xbb\x82\x04\xcb\x65\x6c\xb6\x3d\x18\xe6\xe6\xd9\x2c\x8c\xe8\x81\x69\x8e\xb4\xfa\x41\xd9\x45\xce\xcf\xa3\x4f\x3e\xc6\x1b\x38\x07\x55\x07\x3b\xe5\xad\x39\xd2\x9b\x00\x0d\x85\x92\xcb\xff\x29\xf0\xff\x8e\x0f\x47\x57\x8d\xf7\x64\x68\x19\xa3\x8d\x78\x41\x59\xa2\x1a\x8c\x8f\xcc\x15\x01\x37\xea\xc7\x9b\x33\x2a\xf5\xbe\x25\x70\x7c\xe8\xd3\x17\x5e\xba\x8f\x53\xa6\xb5\xaa\x50\x49\x88\x5a\x36\xaf\xb8\xfa\xd5\x1c\x6a\x6f\xbb\xdc\x0d\x8f\x7d\xae\xdc\x57\x1d\xe8\x18\x5f\xc0\x78\x49\x9c\xa7\xd9\xe4\x72\xfb\x9b\xbd\x9a\x8e\x93\xa6\xac\x0c\xed\x1a\xa0\xcb\xea\x86\x93\x9f\x92\xf5\x55\x12\xe7\x19\x0e\xe0\xb1\xd3\xd4\x13\x31\x6a\x18\x14\xf5\x66\xe1\x47\x7f\x67\x78\x48\x9a\x2a\x5f\x4d\x4d\xa5\xe1\x38\x4f\xed\xa4\x4f\x5c\x36\x0e\x2f\xd2\xbe\x1e\x15\x70\x53\xee\x38\xaa\xb3\x8e\x1b\x51\x9c\x37\xf3\x27\x0c\x41\x54\x1f\x85\x99\x9a\x5e\x16\x22\x26\x20\xc5\xbf\x9b\x77\xb7\xf9\x6a\x07\xae\x89\x68\x69\xf8\x7f\x90\x0b\xf8\xa1\xeb\xb9\x8a\xa7\x77\x3d\xbf\x4c\x63\xe5\xcd\x26\x02\x30\x28\xa4\x73\x07\x09\x87\x82\x85\x89\x1a\xba\x29\x04\xc4\xe6\x35\xd2\xc7\x55\x2a\x79\x7a\x63\xac\xa8\x5f\x32\xb7\x36\xc1\xdf\x11\x2a\x1b\x38\xb6\xd3\x33\x86\x6e\x7a\xa5\xc9\x96\x46\xd3\x16\x23\x3e\x45\x42\x58\x6b\x58\xd4\xc4\x41\x01\x7c\xc6\x95\x61\xfd\x47\xf7\x75\xfe\x9a\x9e\x77\x91\x01\x23\x58\x63\xc7\x7f\x2a\x20\x01\xb5\xe3\x74\xb6\xcd\x52\x61\x53\x22\x1a\xb9\x58\xdd\xcc\xce\x3e\xc2\x9e\xe8\x64\xfa\x7f\x6b\x3f\x7d\xb2\x2c\xda\x33\x74\x56\x0b\x05\x21\xa4\xef\xa6\xe7\xa7\x25\xd1\x46\xc9\x6d\x99\x3c\xf9\x31\x16\x37\x4b\xbe\x5a\x70\x75\x94\xbd\xa2\x86\x5c\x1b\x9e\xd6\x2e\x9a\xbb\x09\x3d\xd3\x72\x6b\xbf\xae\x5f\x06\x7b\x91\xd0\x9c\x8f\x6a\x18\x2a\x25\xce\x50\xfd\x69\x66\xb5\xab\x56\x52\x75\xb6\x66\x54\xe3\x9e\xd6\xc5\x63\xc5\x92\x2c\xc3\x7c\x28\xb8\x01\x4e\x0f\xe2\x0d\x89\xf0\xd8\xed\x79\xd2\xeb\x80\x0f\xae\xee\xf8\xd3\x16\x79\xc6\x66\xf4\xf8\x9b\x82\x6c\x78\xbc\x72\x48\xe9\x78\x42\x19\xb2\x5a\xa3\x0b\xc5\x4e\xdc\x93\x39\x0f\x35\x22\x43\xcd\x5e\x4b\x43\xeb\x93\xfe\x5b\x94\xee\xc3\x29\x99\xfc\xc3\x99\x2a\x4a\xbc\x48\x9a\xf8\xd5\xeb\x52\xd6\xe3\x6c\x9d\x3a\x3b\xd0\xcb\x8b\x5b\x0d\x47\x4c\x01\x20\x74\xf4\xdd\xdd\xae\x68\xf3\xda\xfc\x2b\x06\xac\xba\xee\xd9\x17\x87\x32\x59\x42\x22\xb6\x28\x40\x6b\x7b\x2c\x35\xb1\x65\xc2\xe8\x41\xe8\xbf\xf5\x46\x71\x14\xd7\x31\x03\xff\xaf\xec\x66\x41\xa6\x60\x01\x96\xdd\x56\xdd\xc4\x4e\x2d\xad\xdf\x53\xe5\x80\x2f\xe5\x0a\x95\x13\x3a\x56\xc7\x4d\xa0\x0e\x32\xcf\x6e\xd8\xab\xaf\xeb\xbd\xdd\x53\x3c\x0a\xe6\xb2\x81\x59\xf4\x88\xc9\xf2\x59\x53\x31\xe0\x98\xee\x63\xe4\xb8\x05\x2d\xe9\x3d\x6d\x4f\x6d\x6b\x89\xd2\x4f\xa7\x12\xc3\x25\x07\x1a\x08\x16\xfc\x84\xe3\x68\xce\x41\x85', 1) ================================================ FILE: environment/world/strike_calc.py ================================================ import random import copy import world.position_calc import configuration.system as system_config import world.position_calc as position_calc import world.config as config def get_strike_result_by_ratio(strike_ratio, random_obj): result = True if system_config.get_hit_prob_enable(): if random_obj.random() > strike_ratio: result = False return result def strike_act_validation_and_initiation(missile_type, fighter_context, detector_context_list, enemy_status, strike_list): ''' 打击有效性判断,更新打击列表内容,同时返回该打击动作的有效性指示 :param missile_type: 导弹类型 :param fighter_context: 发起攻击的战机上下文 :param detector_context_list: 己方预警机上下文列表 :param enemy_status: 目标状态 :param strike_list: 打击列表[{'attacker_id': 攻击者编号, 'target_id': 目标编号, 'missile_type': 导弹类型(1:远程,2:中程), information_source_type: 信息来源类型(0:L预警机,1:S预警机,2:战机,3:被动侦测) step_count: step计数, op_count: 观测点计数, del_ind: 删除指示(默认false)}] :return: valid:打击动作是否有效(False,无效;True,有效) ''' # print(missile_type,'\n') # print(fighter_context,'\n') # print(detector_context_list,'\n') # print(enemy_status,'\n') # print(strike_list) # b = a # 判断是否超出打击距离 fighter_pos_x = fighter_context['pos_x'] fighter_pos_y = fighter_context['pos_y'] enemy_pos_x = enemy_status['pos_x'] enemy_pos_y = enemy_status['pos_y'] distance = position_calc.get_distance(fighter_pos_x, fighter_pos_y, enemy_pos_x, enemy_pos_y) if missile_type == config.get_s_missile_type(): effective_strike_distance = config.get_s_missile_dis() if missile_type == config.get_l_missile_type(): effective_strike_distance = config.get_l_missile_dis() if distance > effective_strike_distance: return False # 判断enemy是否存活 if not enemy_status['alive']: return False sublist_of_strike = {} sublist_of_strike['attacker_id'] = fighter_context['id'] sublist_of_strike['target_id'] = enemy_status['id'] sublist_of_strike['missile_type'] = missile_type if fighter_context['j_enable']: for jammed in fighter_context['j_receive_list']: if enemy_status['id'] == jammed['id']: sublist_of_strike['information_source_type'] = config.get_hit_detection_source_passive() for detector in detector_context_list: if detector['alive'] and detector['radar_enable']: for radar in detector['radar_visible_list']: if enemy_status['id'] == radar['id']: sublist_of_strike['information_source_type'] = detector['r_band'] if fighter_context['radar_enable']: for radar in fighter_context['radar_visible_list']: if enemy_status['id'] == radar['id']: sublist_of_strike['information_source_type'] = fighter_context['r_band'] if not 'information_source_type' in sublist_of_strike: print("cannot found information source type") print(fighter_context) print(detector_context_list) print(enemy_status) return False sublist_of_strike['step_count'] = 0 sublist_of_strike['op_count'] = 0 sublist_of_strike['del_ind'] = False strike_list.append(sublist_of_strike.copy()) return True def strike_judge(strike_context, fighter_radar_visible_list, random_obj): ''' 在途打击处理,更新打击上下文内容,判断打击成败 规则: 1. 如果是远程导弹,10个step后按概率判断是否打中 2. 如果是近程导弹,4个step后按概率判断是否打中 3. 杀伤概率(信息来源杀伤概率×观测点杀伤概率×导弹杀伤概率) 修正版--杀伤概率(信息来源杀伤概率x导弹杀伤概率-op_countx0.01) :param strike_context:打击列表中项目 :param fighter_radar_visible_list:攻击者雷达可见目标列表 :param random_obj:随机数对象 :return: strike_success, 1,打击成功,-1,打击失败,0,打击依然在途 ''' # print(strike_context) # print(fighter_radar_visible_list) # b = a strike_context['step_count'] += 1 for radar in fighter_radar_visible_list: if strike_context['target_id'] == radar['id']: strike_context['op_count'] += 1 try: missile_type = strike_context['missile_type'] information_source_type = strike_context['information_source_type'] op_count = strike_context['op_count'] step_count = strike_context['step_count'] except KeyError: print('strike_context: ', strike_context) strike_success = 0 kill_prob = 0 if missile_type == config.get_l_missile_type() and step_count >= 10: # kill_prob = config.get_detection_hit_probability(information_source_type) * config.get_l_missile_hit_probability() - \ # (config.get_l_missile_op_req() - strike_context['op_count']) * 0.01 kill_prob = (0.06 + 0.01 * op_count) * (config.get_detection_hit_probability(information_source_type)/config.get_detection_hit_probability(0)) strike = get_strike_result_by_ratio(kill_prob, random_obj) if strike: strike_success = 1 else: strike_success = -1 if missile_type == config.get_s_missile_type() and step_count >= 4: # kill_prob = config.get_detection_hit_probability(information_source_type) * config.get_s_missile_hit_probability() - \ # (config.get_s_missile_op_req() - strike_context['op_count']) * 0.01 kill_prob = (0.14 + 0.01 * op_count) * (config.get_detection_hit_probability(information_source_type)/config.get_detection_hit_probability(0)) strike = get_strike_result_by_ratio(kill_prob, random_obj) if strike: strike_success = 1 else: strike_success = -1 return strike_success # TODO: uncomplete def strike_judge_no_delay(strike_context, random_obj): print("strike_judge_no_delay") return ================================================ FILE: fight.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: fight.py @time: 2018/3/9 0009 16:41 @desc: execution battle between two agents """ import argparse import importlib import os import sys import time import pygame from interface import Environment sys.path.append("/home/boris/MaCA/environment") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--map", type=str, default="1000_1000_fighter10v10", help='map name, only name, not file path') parser.add_argument("--agent1", type=str, default="fix_rule", help='agent 1 name, only name, not path') parser.add_argument("--agent2", type=str, default="fix_rule", help='agent 2 name, only name, not path') parser.add_argument("--round", type=int, default=1, help='play rounds') parser.add_argument("--fps", type=float, default=0, help='display fps') parser.add_argument("--max_step", type=int, default=5000, help='max step in a round') parser.add_argument("--random_pos", action="store_true", help='if the initial positions are random or fix') parser.add_argument("--log", action="store_true", help='saving log') parser.add_argument("--log_path", type=str, default="default_log", help='log folder name') args = parser.parse_args() print('Map:', args.map) print('Side1 agent:', args.agent1) print('Side2 agent:', args.agent2) print('Round number:', args.round) side1_win_times = 0 side2_win_times = 0 draw_times = 0 # file path constructing map_path = 'maps/' + args.map + '.map' agent1_path = 'agent/' + args.agent1 + '/agent.py' agent2_path = 'agent/' + args.agent2 + '/agent.py' agent1_import_path = 'agent.' + args.agent1 + '.agent' agent2_import_path = 'agent.' + args.agent2 + '.agent' if not os.path.exists(map_path): print('Error: map file not exist!') exit(1) if not os.path.exists(agent1_path): print('Error: agent1 file not exist!') exit(1) if not os.path.exists(agent2_path): print('Error: agent2 file not exist!') exit(1) # delay calc if args.fps == 0: step_delay = 0 else: step_delay = 1 / args.fps # laad agents agent1_module = importlib.import_module(agent1_import_path) agent2_module = importlib.import_module(agent2_import_path) agent1 = agent1_module.Agent() agent2 = agent2_module.Agent() agent1_obs_ind = agent1.get_obs_ind() agent2_obs_ind = agent2.get_obs_ind() # environment initiation if args.log: if args.log_path == 'default_log': log_flag = args.agent1 + '_vs_' + args.agent2 else: log_flag = args.log_path else: log_flag = False env = Environment(map_path, agent1_obs_ind, agent2_obs_ind, max_step=args.max_step, render=True, random_pos=args.random_pos, log=log_flag) # get map info size_x, size_y = env.get_map_size() side1_detector_num, side1_fighter_num, side2_detector_num, side2_fighter_num = env.get_unit_num() agent1.set_map_info(size_x, size_y, side1_detector_num, side1_fighter_num) agent2.set_map_info(size_x, size_y, side2_detector_num, side2_fighter_num) # execution step_cnt = 0 round_cnt = 0 agent1_crash_list = [] agent2_crash_list = [] # input("Press the key to continue...") for x in range(args.round): side1_total_reward = 0 side2_total_reward = 0 if x != 0: env.reset() step_cnt = 0 round_cnt += 1 while True: time.sleep(step_delay) step_cnt += 1 # get obs side1_obs_dict, side2_obs_dict = env.get_obs() # get action try: side1_detector_action, side1_fighter_action = agent1.get_action(side1_obs_dict, step_cnt) except: env.set_surrender(0) # reward o_detector_reward, o_fighter_reward, o_game_reward, e_detector_reward, e_fighter_reward, e_game_reward = env.get_reward() agent1_crash_list.append(round_cnt) print('Side 1 crashed!') side1_obs_raw, side2_obs_raw = env.get_obs_raw() side1_detector_obs_raw_list = side1_obs_raw['detector_obs_list'] side1_fighter_obs_raw_list = side1_obs_raw['fighter_obs_list'] side1_joint_obs_raw_dict = side1_obs_raw['joint_obs_dict'] side2_detector_obs_raw_list = side2_obs_raw['detector_obs_list'] side2_fighter_obs_raw_list = side2_obs_raw['fighter_obs_list'] side2_joint_obs_raw_dict = side2_obs_raw['joint_obs_dict'] else: try: side2_detector_action, side2_fighter_action = agent2.get_action(side2_obs_dict, step_cnt) except: env.set_surrender(1) # reward o_detector_reward, o_fighter_reward, o_game_reward, e_detector_reward, e_fighter_reward, e_game_reward = env.get_reward() agent2_crash_list.append(round_cnt) print('Side 2 crashed!') side1_obs_raw, side2_obs_raw = env.get_obs_raw() side1_detector_obs_raw_list = side1_obs_raw['detector_obs_list'] side1_fighter_obs_raw_list = side1_obs_raw['fighter_obs_list'] side1_joint_obs_raw_dict = side1_obs_raw['joint_obs_dict'] side2_detector_obs_raw_list = side2_obs_raw['detector_obs_list'] side2_fighter_obs_raw_list = side2_obs_raw['fighter_obs_list'] side2_joint_obs_raw_dict = side2_obs_raw['joint_obs_dict'] else: # execution env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) # obs side1_obs_raw, side2_obs_raw = env.get_obs_raw() side1_detector_obs_raw_list = side1_obs_raw['detector_obs_list'] side1_fighter_obs_raw_list = side1_obs_raw['fighter_obs_list'] side1_joint_obs_raw_dict = side1_obs_raw['joint_obs_dict'] side2_detector_obs_raw_list = side2_obs_raw['detector_obs_list'] side2_fighter_obs_raw_list = side2_obs_raw['fighter_obs_list'] side2_joint_obs_raw_dict = side2_obs_raw['joint_obs_dict'] # reward o_detector_reward, o_fighter_reward, o_game_reward, e_detector_reward, e_fighter_reward, e_game_reward = env.get_reward() side1_step_reward = 0 side2_step_reward = 0 for y in range(side1_detector_num): side1_step_reward += o_detector_reward[y] for y in range(side1_fighter_num): side1_step_reward += o_fighter_reward[y] for y in range(side2_detector_num): side2_step_reward += e_detector_reward[y] for y in range(side2_fighter_num): side2_step_reward += e_fighter_reward[y] side1_total_reward += side1_step_reward side2_total_reward += side2_step_reward # print('Round %d, Step %d:' % (round_cnt, step_cnt)) # print('Side 1 reward: %d, Side 2 reward: %d' % (side1_step_reward, side2_step_reward)) if env.get_done(): print('Round %d done at step %d!' % (round_cnt, step_cnt)) if o_game_reward > e_game_reward: print('Side 1 WIN!!!') side1_win_times += 1 elif o_game_reward < e_game_reward: print('Side 2 WIN!!!') side2_win_times += 1 else: print('DRAW!!!') draw_times += 1 print('Side 1 total step reward: %d, Side 2 total step reward: %d' % (side1_total_reward, side2_total_reward)) print('Side 1 round reward: %d, Side 2 round reward: %d' % (o_game_reward, e_game_reward)) side1_detector_alive_num = 0 side1_fighter_alive_num = 0 side2_detector_alive_num = 0 side2_fighter_alive_num = 0 side1_missile_left = 0 side2_missile_left = 0 for y in range(side1_detector_num): if side1_detector_obs_raw_list[y]['alive']: side1_detector_alive_num += 1 for y in range(side1_fighter_num): if side1_fighter_obs_raw_list[y]['alive']: side1_fighter_alive_num += 1 side1_missile_left += side1_fighter_obs_raw_list[y]['l_missile_left'] side1_missile_left += side1_fighter_obs_raw_list[y]['s_missile_left'] for y in range(side2_detector_num): if side2_detector_obs_raw_list[y]['alive']: side2_detector_alive_num += 1 for y in range(side2_fighter_num): if side2_fighter_obs_raw_list[y]['alive']: side2_fighter_alive_num += 1 side2_missile_left += side2_fighter_obs_raw_list[y]['l_missile_left'] side2_missile_left += side2_fighter_obs_raw_list[y]['s_missile_left'] print('Side 1: alive detector: %d, alive fighter: %d, missile left: %d' % (side1_detector_alive_num, side1_fighter_alive_num, side1_missile_left)) print('Side 2: alive detector: %d, alive fighter: %d, missile left: %d' % (side2_detector_alive_num, side2_fighter_alive_num, side2_missile_left)) time.sleep(2) break print('FIGHT RESULT:') print('Total rounds: %d. Side1 win: %d. Side2 win: %d. Draw: %d' % (round_cnt, side1_win_times, side2_win_times, draw_times)) print('Side 1 win rate: %.1f%%, Side 2 win rate: %.1f%%.' % (side1_win_times/round_cnt*100, side2_win_times/round_cnt*100)) if len(agent1_crash_list) != 0: print('Side 1 crashed %d times:' % len(agent1_crash_list)) print(agent1_crash_list) if len(agent2_crash_list) != 0: print('Side 2 crashed %d times:' % len(agent2_crash_list)) print(agent2_crash_list) # input("Press the key to continue...") ================================================ FILE: fight_mp.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: fight.py @time: 2018/3/9 0009 16:41 @desc: execution battle between two agents """ import argparse import os import time from interface import Environment from common.agent_process import AgentCtrl if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--map", type=str, default="1000_1000_2_10_vs_2_10", help='map name, only name, not file path') parser.add_argument("--agent1", type=str, default="fix_rule", help='agent 1 name, only name, not path') parser.add_argument("--agent2", type=str, default="fix_rule", help='agent 2 name, only name, not path') parser.add_argument("--round", type=int, default=1, help='play rounds') parser.add_argument("--fps", type=float, default=0, help='display fps') parser.add_argument("--max_step", type=int, default=5000, help='max step in a round') parser.add_argument("--random_pos", action="store_true", help='if the initial positions are random or fix') parser.add_argument("--log", action="store_true", help='saving log') parser.add_argument("--log_path", type=str, default="default_log", help='log folder name') args = parser.parse_args() print('Map:', args.map) print('Side1 agent:', args.agent1) print('Side2 agent:', args.agent2) print('Round number:', args.round) side1_win_times = 0 side2_win_times = 0 draw_times = 0 # file path constructing map_path = 'maps/' + args.map + '.map' agent1_path = 'agent/' + args.agent1 + '/agent.py' agent2_path = 'agent/' + args.agent2 + '/agent.py' if not os.path.exists(map_path): print('Error: map file not exist!') exit(-1) if not os.path.exists(agent1_path): print('Error: agent1 file not exist!') exit(-1) if not os.path.exists(agent2_path): print('Error: agent2 file not exist!') exit(-1) # delay calc if args.fps == 0: step_delay = 0 else: step_delay = 1 / args.fps # environment initiation if args.log: if args.log_path == 'default_log': log_flag = args.agent1 + '_vs_' + args.agent2 else: log_flag = args.log_path else: log_flag = False env = Environment(map_path, 'raw', 'raw', max_step=args.max_step, render=True, random_pos=args.random_pos, log=log_flag) # get map info size_x, size_y = env.get_map_size() side1_detector_num, side1_fighter_num, side2_detector_num, side2_fighter_num = env.get_unit_num() # create agent agent1 = AgentCtrl(args.agent1, size_x, size_y, side1_detector_num, side1_fighter_num) agent2 = AgentCtrl(args.agent2, size_x, size_y, side2_detector_num, side2_fighter_num) if not agent1.agent_init(): print('ERROR: Agent1 init failed!') agent1.terminate() agent2.terminate() exit(-1) else: print('Agent1 init success!') if not agent2.agent_init(): print('ERROR: Agent2 init failed!') agent1.terminate() agent2.terminate() exit(-1) else: print('Agent2 init success!') # execution step_cnt = 0 round_cnt = 0 agent1_crash_list = [] agent2_crash_list = [] agent1_timeout_list = [] agent2_timeout_list = [] # input("Press the key to continue...") for x in range(args.round): side1_total_reward = 0 side2_total_reward = 0 if x != 0: env.reset() step_cnt = 0 round_cnt += 1 while True: time.sleep(step_delay) step_cnt += 1 # get obs side1_obs_dict, side2_obs_dict = env.get_obs() # get action agent1_action, agent1_result = agent1.get_action(side1_obs_dict, step_cnt) if agent1_result == 0: side1_detector_action = agent1_action['detector_action'] side1_fighter_action = agent1_action['fighter_action'] elif agent1_result == 1: agent1_crash_list.append(round_cnt) print('agent1 crashed!') elif agent1_result == 2: agent1_timeout_list.append(round_cnt) print('agent1 timeout!') agent2_action, agent2_result = agent2.get_action(side2_obs_dict, step_cnt) if agent2_result == 0: side2_detector_action = agent2_action['detector_action'] side2_fighter_action = agent2_action['fighter_action'] elif agent2_result == 1: agent2_crash_list.append(round_cnt) print('agent2 crashed!') elif agent2_result == 2: agent2_timeout_list.append(round_cnt) print('agent2 timeout!') # execution if agent1_result == 0 and agent2_result == 0: env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) elif agent1_result != 0 and agent2_result != 0: env.set_surrender(2) elif agent1_result != 0: env.set_surrender(0) else: env.set_surrender(1) # obs side1_obs_raw, side2_obs_raw = env.get_obs_raw() side1_detector_obs_raw_list = side1_obs_raw['detector_obs_list'] side1_fighter_obs_raw_list = side1_obs_raw['fighter_obs_list'] side1_joint_obs_raw_dict = side1_obs_raw['joint_obs_dict'] side2_detector_obs_raw_list = side2_obs_raw['detector_obs_list'] side2_fighter_obs_raw_list = side2_obs_raw['fighter_obs_list'] side2_joint_obs_raw_dict = side2_obs_raw['joint_obs_dict'] # reward o_detector_reward, o_fighter_reward, o_game_reward, e_detector_reward, e_fighter_reward, e_game_reward = env.get_reward() side1_step_reward = 0 side2_step_reward = 0 for y in range(side1_detector_num): side1_step_reward += o_detector_reward[y] for y in range(side1_fighter_num): side1_step_reward += o_fighter_reward[y] for y in range(side2_detector_num): side2_step_reward += e_detector_reward[y] for y in range(side2_fighter_num): side2_step_reward += e_fighter_reward[y] side1_total_reward += side1_step_reward side2_total_reward += side2_step_reward # print('Round %d, Step %d:' % (round_cnt, step_cnt)) # print('Side 1 reward: %d, Side 2 reward: %d' % (side1_step_reward, side2_step_reward)) if env.get_done(): print('Round %d done at step %d!' % (round_cnt, step_cnt)) if o_game_reward > e_game_reward: print('Side 1 WIN!!!') side1_win_times += 1 elif o_game_reward < e_game_reward: print('Side 2 WIN!!!') side2_win_times += 1 else: print('DRAW!!!') draw_times += 1 print('Side 1 total step reward: %d, Side 2 total step reward: %d' % (side1_total_reward, side2_total_reward)) print('Side 1 round reward: %d, Side 2 round reward: %d' % (o_game_reward, e_game_reward)) side1_detector_alive_num = 0 side1_fighter_alive_num = 0 side2_detector_alive_num = 0 side2_fighter_alive_num = 0 for y in range(side1_detector_num): if side1_detector_obs_raw_list[y]['alive']: side1_detector_alive_num += 1 for y in range(side1_fighter_num): if side1_fighter_obs_raw_list[y]['alive']: side1_fighter_alive_num += 1 for y in range(side2_detector_num): if side2_detector_obs_raw_list[y]['alive']: side2_detector_alive_num += 1 for y in range(side2_fighter_num): if side2_fighter_obs_raw_list[y]['alive']: side2_fighter_alive_num += 1 print('Side 1 alive detector: %d, Side 1 alive fighter: %d, Side 2 alive detector: %d, Side 2 alive fighter: %d' % (side1_detector_alive_num, side1_fighter_alive_num, side2_detector_alive_num, side2_fighter_alive_num)) time.sleep(2) break agent1.terminate() agent2.terminate() print('FIGHT RESULT:') print('Total rounds: %d. Side1 win: %d. Side2 win: %d. Draw: %d' % (round_cnt, side1_win_times, side2_win_times, draw_times)) print('Side 1 win rate: %.1f%%, Side 2 win rate: %.1f%%.' % (side1_win_times/round_cnt*100, side2_win_times/round_cnt*100)) if len(agent1_crash_list) != 0: print('Side 1 crashed %d times:' % len(agent1_crash_list)) print(agent1_crash_list) if len(agent2_crash_list) != 0: print('Side 2 crashed %d times:' % len(agent2_crash_list)) print(agent2_crash_list) if len(agent1_timeout_list) != 0: print('Side 1 timeout %d times:' % len(agent1_timeout_list)) print(agent1_timeout_list) if len(agent2_timeout_list) != 0: print('Side 2 timeout %d times:' % len(agent2_timeout_list)) print(agent2_timeout_list) input("Press the key to continue...") ================================================ FILE: log/readme ================================================ ================================================ FILE: model/README ================================================ ================================================ FILE: obs_construct/airc_homo_rule/construct.py ================================================ import numpy as np import copy import utility as ut class ObsConstruct: def __init__(self, size_x, size_y, detector_num, fighter_num): self.battlefield_size_x = size_x self.battlefield_size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num self.img_obs_reduce_ratio = 10 self.obs_dict = {} def obs_construct(self, obs_raw_dict): # self.obs_dict = copy.deepcopy(obs_raw_dict) for i in range(self.fighter_num): self.obs_dict[i] = obs_raw_dict['fighter_obs_list'][i] self.obs_dict['strike_list'] = obs_raw_dict['joint_obs_dict']['strike_list'] # continue if the fighter is dead if not self.obs_dict[i]['alive']: continue detected_enemy_list = [] myself_pos_x = self.obs_dict[i]['pos_x'] myself_pos_y = self.obs_dict[i]['pos_y'] for j in range(len(self.obs_dict[i]['r_visible_list'])): enemy_dict = self.obs_dict[i]['r_visible_list'][j] enemy_pos_x = enemy_dict['pos_x'] enemy_pos_y = enemy_dict['pos_y'] enemy_dict['distance'] = ut.distance(myself_pos_x, myself_pos_y, enemy_pos_x, enemy_pos_y) enemy_dict['angle'] = ut.angle(myself_pos_x, myself_pos_y, enemy_pos_x, enemy_pos_y) detected_enemy_list.append(enemy_dict) ####commented by liyuan. ''' for j in range(len(self.obs_dict[i]['j_recv_list'])): enemy_dict = self.obs_dict[i]['j_recv_list'][j] enemy_pos_x = enemy_dict['pos_x'] enemy_pos_y = enemy_dict['pos_y'] enemy_dict['distance'] = ut.distance(myself_pos_x, myself_pos_y, enemy_pos_x, enemy_pos_y) enemy_dict['angle'] = ut.angle(myself_pos_x, myself_pos_y, enemy_pos_x, enemy_pos_y) detected_enemy_list.append(enemy_dict) ''' if len(detected_enemy_list) > 0: detected_enemy_list = sorted(detected_enemy_list, key=lambda k: k['distance']) self.obs_dict[i]['target_list'] = detected_enemy_list return self.obs_dict @staticmethod def distance_key(ele): return ele['distance'] def obs_construct_old(self, obs_raw_dict): obs_dict = {} detector_obs_list = [] fighter_obs_list = [] detector_data_obs_list = obs_raw_dict['detector_obs_list'] fighter_data_obs_list = obs_raw_dict['fighter_obs_list'] joint_data_obs_dict = obs_raw_dict['joint_obs_dict'] detector_img, fighter_img, joint_img = self.__get_img_obs(detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict) detector_data, fighter_data = self.__get_data_obs(detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict) alive_status = self.__get_alive_status(detector_data_obs_list, fighter_data_obs_list) # o方 # 预警机 for x in range(self.detector_num): img_context = detector_img[x, :, :, :] img_context = np.concatenate((img_context, joint_img[0, :, :, :]), axis=2) data_context = detector_data[x, :] detector_obs_list.append({'info': copy.deepcopy(data_context), 'screen': copy.deepcopy(img_context), 'alive': alive_status[x][0]}) # 战机 for x in range(self.fighter_num): img_context = fighter_img[x, :, :, :] img_context = np.concatenate((img_context, joint_img[0, :, :, :]), axis=2) data_context = fighter_data[x, :] fighter_obs_list.append({'info': copy.deepcopy(data_context), 'screen': copy.deepcopy(img_context), 'alive': alive_status[x + self.detector_num][0]}) obs_dict['detector'] = detector_obs_list obs_dict['fighter'] = fighter_obs_list return obs_dict def __get_alive_status(self, detector_data_obs_list, fighter_data_obs_list): alive_status = np.full((self.detector_num + self.fighter_num, 1), True) for x in range(self.detector_num): if not detector_data_obs_list[x]['alive']: alive_status[x][0] = False for x in range(self.fighter_num): if not fighter_data_obs_list[x]['alive']: alive_status[x + self.detector_num][0] = False return alive_status def __get_img_obs(self, detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict): img_obs_size_x = int(self.battlefield_size_y / self.img_obs_reduce_ratio) img_obs_size_y = int(self.battlefield_size_x / self.img_obs_reduce_ratio) # 个体img:所有己方单位位置 detector_img = np.full((self.detector_num, img_obs_size_x, img_obs_size_y, 3), 0, dtype=np.int32) fighter_img = np.full((self.fighter_num, img_obs_size_x, img_obs_size_y, 3), 0, dtype=np.int32) # 全局img:所有可见敌方单元位置和类型 joint_img = np.full((1, img_obs_size_x, img_obs_size_y, 2), 0, dtype=np.int32) # set all self unit pos, detector: 1, fighter: 2, self: 255 tmp_pos_obs = np.full((img_obs_size_x, img_obs_size_y), 0, dtype=np.int32) for x in range(self.detector_num): if not detector_data_obs_list[x]['alive']: continue self.__set_value_in_img(tmp_pos_obs, int(detector_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 1) for x in range(self.fighter_num): if not fighter_data_obs_list[x]['alive']: continue self.__set_value_in_img(tmp_pos_obs, int(fighter_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 2) # Detector obs for x in range(self.detector_num): # if not alive, skip if not detector_data_obs_list[x]['alive']: continue # self detection target. target: id for y in range(len(detector_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(detector_img[x, :, :, 0], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['id']) # self detection target. target: type (detector: 1, fighter 2) for y in range(len(detector_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(detector_img[x, :, :, 1], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # friendly pos. self: 255, other: type (detector: 1, fighter 2) detector_img[x, :, :, 2] = copy.deepcopy(tmp_pos_obs) self.__set_value_in_img(detector_img[x, :, :, 2], int(detector_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 255) # Fighter obs for x in range(self.fighter_num): # if not alive, skip if not fighter_data_obs_list[x]['alive']: continue # self detection target. target: id for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(fighter_img[x, :, :, 0], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['id']) # self detection target. target: type (detector: 1, fighter 2) for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(fighter_img[x, :, :, 1], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # friendly pos. self: 255, other: type (detector: 1, fighter 2) fighter_img[x, :, :, 2] = copy.deepcopy(tmp_pos_obs) self.__set_value_in_img(fighter_img[x, :, :, 2], int(fighter_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 255) # Global obs # Passive detection for x in range(len(joint_data_obs_dict['passive_detection_enemy_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int( joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_y'] / self.img_obs_reduce_ratio), int(joint_data_obs_dict['passive_detection_enemy_list'][x][ 'pos_x'] / self.img_obs_reduce_ratio), joint_data_obs_dict['passive_detection_enemy_list'][x]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int( joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_y'] / self.img_obs_reduce_ratio), int(joint_data_obs_dict['passive_detection_enemy_list'][x][ 'pos_x'] / self.img_obs_reduce_ratio), joint_data_obs_dict['passive_detection_enemy_list'][x]['type'] + 1) # detector for x in range(self.detector_num): for y in range(len(detector_data_obs_list[x]['r_visible_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # fighter for x in range(self.fighter_num): for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['type'] + 1) return detector_img, fighter_img, joint_img def __set_value_in_img(self, img, pos_x, pos_y, value): """ draw 3*3 rectangle in img :param img: :param pos_x: :param pos_y: :param value: :return: """ img_obs_size_x = int(self.battlefield_size_y / self.img_obs_reduce_ratio) img_obs_size_y = int(self.battlefield_size_x / self.img_obs_reduce_ratio) # 左上角 if pos_x == 0 and pos_y == 0: img[pos_x: pos_x + 2, pos_y: pos_y + 2] = value # 左下角 elif pos_x == 0 and pos_y == (img_obs_size_y - 1): img[pos_x: pos_x + 2, pos_y - 1: pos_y + 1] = value # 右上角 elif pos_x == (img_obs_size_x - 1) and pos_y == 0: img[pos_x - 1: pos_x + 1, pos_y: pos_y + 2] = value # 右下角 elif pos_x == (img_obs_size_x - 1) and pos_y == (img_obs_size_y - 1): img[pos_x - 1: pos_x + 1, pos_y - 1: pos_y + 1] = value # 左边 elif pos_x == 0: img[pos_x: pos_x + 2, pos_y - 1: pos_y + 2] = value # 右边 elif pos_x == img_obs_size_x - 1: img[pos_x - 1: pos_x + 1, pos_y - 1: pos_y + 2] = value # 上边 elif pos_y == 0: img[pos_x - 1: pos_x + 2, pos_y: pos_y + 2] = value # 下边 elif pos_y == img_obs_size_y - 1: img[pos_x - 1: pos_x + 2, pos_y - 1: pos_y + 1] = value # 其他位置 else: img[pos_x - 1: pos_x + 2, pos_y - 1: pos_y + 2] = value def __get_data_obs(self, detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict): detector_data = np.full((self.detector_num, 1), -1, dtype=np.int32) fighter_data = np.full((self.fighter_num, 3), -1, dtype=np.int32) # Detector info for x in range(self.detector_num): if detector_data_obs_list[x]['alive']: detector_data[x, 0] = detector_data_obs_list[x]['course'] # Fighter info for x in range(self.fighter_num): if fighter_data_obs_list[x]['alive']: fighter_data[x, 0] = fighter_data_obs_list[x]['course'] fighter_data[x, 1] = fighter_data_obs_list[x]['l_missile_left'] fighter_data[x, 2] = fighter_data_obs_list[x]['s_missile_left'] return detector_data, fighter_data ================================================ FILE: obs_construct/simple/construct.py ================================================ import numpy as np import copy class ObsConstruct: def __init__(self, size_x, size_y, detector_num, fighter_num): self.battlefield_size_x = size_x self.battlefield_size_y = size_y self.detector_num = detector_num self.fighter_num = fighter_num self.img_obs_reduce_ratio = 10 def obs_construct(self, obs_raw_dict): print("obs_construct") obs_dict = {} detector_obs_list = [] fighter_obs_list = [] detector_data_obs_list = obs_raw_dict['detector_obs_list'] fighter_data_obs_list = obs_raw_dict['fighter_obs_list'] joint_data_obs_dict = obs_raw_dict['joint_obs_dict'] detector_img, fighter_img, joint_img = self.__get_img_obs(detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict) detector_data, fighter_data = self.__get_data_obs(detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict) alive_status = self.__get_alive_status(detector_data_obs_list, fighter_data_obs_list) # o方 # 预警机 for x in range(self.detector_num): img_context = detector_img[x, :, :, :] img_context = np.concatenate((img_context, joint_img[0, :, :, :]), axis=2) data_context = detector_data[x, :] detector_obs_list.append({'info': copy.deepcopy(data_context), 'screen': copy.deepcopy(img_context), 'alive': alive_status[x][0]}) # 战机 for x in range(self.fighter_num): img_context = fighter_img[x, :, :, :] img_context = np.concatenate((img_context, joint_img[0, :, :, :]), axis=2) data_context = fighter_data[x, :] fighter_obs_list.append({'info': copy.deepcopy(data_context), 'screen': copy.deepcopy(img_context), 'alive': alive_status[x + self.detector_num][0]}) obs_dict['detector'] = detector_obs_list obs_dict['fighter'] = fighter_obs_list return obs_dict def __get_alive_status(self,detector_data_obs_list,fighter_data_obs_list): print("__get_alive_status") alive_status = np.full((self.detector_num+self.fighter_num,1),True) for x in range(self.detector_num): if not detector_data_obs_list[x]['alive']: alive_status[x][0] = False for x in range(self.fighter_num): if not fighter_data_obs_list[x]['alive']: alive_status[x+self.detector_num][0] = False return alive_status def __get_img_obs(self, detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict): print("__get_img_obs") img_obs_size_x = int(self.battlefield_size_y / self.img_obs_reduce_ratio) img_obs_size_y = int(self.battlefield_size_x / self.img_obs_reduce_ratio) # 个体img:所有己方单位位置 detector_img = np.full((self.detector_num, img_obs_size_x, img_obs_size_y, 3), 0, dtype=np.int32) fighter_img = np.full((self.fighter_num, img_obs_size_x, img_obs_size_y, 3), 0, dtype=np.int32) # 企鹅据img:所有可见敌方单元位置和类型 joint_img = np.full((1, img_obs_size_x, img_obs_size_y, 2), 0, dtype=np.int32) # set all self unit pos, detector: 1, fighter: 2, self: 255 tmp_pos_obs = np.full((img_obs_size_x, img_obs_size_y), 0, dtype=np.int32) for x in range(self.detector_num): if not detector_data_obs_list[x]['alive']: continue self.__set_value_in_img(tmp_pos_obs, int(detector_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 1) for x in range(self.fighter_num): if not fighter_data_obs_list[x]['alive']: continue self.__set_value_in_img(tmp_pos_obs, int(fighter_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 2) # Detector obs for x in range(self.detector_num): # if not alive, skip if not detector_data_obs_list[x]['alive']: continue # self detection target. target: id for y in range(len(detector_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(detector_img[x, :, :, 0], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['id']) # self detection target. target: type (detector: 1, fighter 2) for y in range(len(detector_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(detector_img[x, :, :, 1], int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # friendly pos. self: 255, other: type (detector: 1, fighter 2) detector_img[x, :, :, 2] = copy.deepcopy(tmp_pos_obs) self.__set_value_in_img(detector_img[x, :, :, 2], int(detector_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 255) # Fighter obs for x in range(self.fighter_num): # if not alive, skip if not fighter_data_obs_list[x]['alive']: continue # self detection target. target: id for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(fighter_img[x, :, :, 0], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['id']) # self detection target. target: type (detector: 1, fighter 2) for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): self.__set_value_in_img(fighter_img[x, :, :, 1], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # friendly pos. self: 255, other: type (detector: 1, fighter 2) fighter_img[x, :, :, 2] = copy.deepcopy(tmp_pos_obs) self.__set_value_in_img(fighter_img[x, :, :, 2], int(fighter_data_obs_list[x]['pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['pos_x'] / self.img_obs_reduce_ratio), 255) # Global obs # Passive detection for x in range(len(joint_data_obs_dict['passive_detection_enemy_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int(joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_y'] / self.img_obs_reduce_ratio), int(joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_x'] / self.img_obs_reduce_ratio), joint_data_obs_dict['passive_detection_enemy_list'][x]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int(joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_y'] / self.img_obs_reduce_ratio), int(joint_data_obs_dict['passive_detection_enemy_list'][x]['pos_x'] / self.img_obs_reduce_ratio), joint_data_obs_dict['passive_detection_enemy_list'][x]['type'] + 1) # detector for x in range(self.detector_num): for y in range(len(detector_data_obs_list[x]['r_visible_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int(detector_data_obs_list[x]['r_visible_list'][y]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y]['pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int(detector_data_obs_list[x]['r_visible_list'][y]['pos_y'] / self.img_obs_reduce_ratio), int(detector_data_obs_list[x]['r_visible_list'][y]['pos_x'] / self.img_obs_reduce_ratio), detector_data_obs_list[x]['r_visible_list'][y]['type'] + 1) # fighter for x in range(self.fighter_num): for y in range(len(fighter_data_obs_list[x]['r_visible_list'])): # Channel: detected enemy pos. value=enemy id self.__set_value_in_img(joint_img[0, :, :, 0], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['id']) # Channe2: detected enemy pos. value=enemy type self.__set_value_in_img(joint_img[0, :, :, 1], int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_y'] / self.img_obs_reduce_ratio), int(fighter_data_obs_list[x]['r_visible_list'][y][ 'pos_x'] / self.img_obs_reduce_ratio), fighter_data_obs_list[x]['r_visible_list'][y]['type'] + 1) return detector_img, fighter_img, joint_img def __set_value_in_img(self, img, pos_x, pos_y, value): print("__set_value_in_img") """ draw 3*3 rectangle in img :param img: :param pos_x: :param pos_y: :param value: :return: """ img_obs_size_x = int(self.battlefield_size_y / self.img_obs_reduce_ratio) img_obs_size_y = int(self.battlefield_size_x / self.img_obs_reduce_ratio) # 左上角 if pos_x == 0 and pos_y == 0: img[pos_x: pos_x + 2, pos_y: pos_y + 2] = value # 左下角 elif pos_x == 0 and pos_y == (img_obs_size_y - 1): img[pos_x: pos_x + 2, pos_y - 1: pos_y + 1] = value # 右上角 elif pos_x == (img_obs_size_x - 1) and pos_y == 0: img[pos_x - 1: pos_x + 1, pos_y: pos_y + 2] = value # 右下角 elif pos_x == (img_obs_size_x - 1) and pos_y == (img_obs_size_y - 1): img[pos_x - 1: pos_x + 1, pos_y - 1: pos_y + 1] = value # 左边 elif pos_x == 0: img[pos_x: pos_x + 2, pos_y - 1: pos_y + 2] = value # 右边 elif pos_x == img_obs_size_x - 1: img[pos_x - 1: pos_x + 1, pos_y - 1: pos_y + 2] = value # 上边 elif pos_y == 0: img[pos_x - 1: pos_x + 2, pos_y: pos_y + 2] = value # 下边 elif pos_y == img_obs_size_y - 1: img[pos_x - 1: pos_x + 2, pos_y - 1: pos_y + 1] = value # 其他位置 else: img[pos_x - 1: pos_x + 2, pos_y - 1: pos_y + 2] = value def __get_data_obs(self, detector_data_obs_list, fighter_data_obs_list, joint_data_obs_dict): print("__get_data_obs") detector_data = np.full((self.detector_num, 1), -1, dtype=np.int32) fighter_data = np.full((self.fighter_num, 3), -1, dtype=np.int32) # Detector info for x in range(self.detector_num): if detector_data_obs_list[x]['alive']: detector_data[x,0] = detector_data_obs_list[x]['course'] # Fighter info for x in range(self.fighter_num): if fighter_data_obs_list[x]['alive']: fighter_data[x, 0] = fighter_data_obs_list[x]['course'] fighter_data[x, 1] = fighter_data_obs_list[x]['l_missile_left'] fighter_data[x, 2] = fighter_data_obs_list[x]['s_missile_left'] return detector_data, fighter_data ================================================ FILE: replay.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: replay_test.py @time: 2018/3/8 0008 17:24 @desc: load a log and replay """ from interface import PlayBack import argparse if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("log", type=str, help='log name') args = parser.parse_args() replay_obj = PlayBack(args.log) replay_obj.start() input("Press the key to continue...") ================================================ FILE: tournament/config.ini ================================================ {"map_name": "1000_1000_fighter10v10", "round_num": 2, "max_step": 1500, "agent_list": ["simple", "fix_rule", "fix_rule_no_att"]} ================================================ FILE: tournament/config_gen.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: config_gen.py @time: 2019/2/26 0026 15:17 @desc: """ import json if __name__ == "__main__": config_dict = {} map_name = '1000_1000_fighter10v10' round_num = 6 # 必须为偶数 max_step = 1500 agent_list = ['fix_rule', 'fix_rule_no_att', 'simple'] config_dict.update({'map_name': map_name}) config_dict.update({'round_num': round_num}) config_dict.update({'max_step': max_step}) config_dict.update({'agent_list': agent_list}) with open('tournament/config.ini', 'w') as f: json.dump(config_dict, f) ================================================ FILE: tournament/tournament_mp.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: tournament.py @time: 2019/2/26 0026 9:13 @desc: """ import importlib import os import time import json import numpy as np import pandas as pd from interface import Environment from common.agent_process import AgentCtrl SCORE_WIN = 3 SCORE_DRAW = 1 SCORE_LOSS = 0 def run(agent1_name, agent2_name, map_name, round_num, max_step, random_pos=False): ''' :param agent1_name: 红方名称 :param agent2_name: 蓝方名称 :param map_name: 地图名称 :param round_num: 对战局数 :param max_step: 单局最大step :param random_pos: 随机起始位置 :return: agent1_win_times, agent2_win_times, draw_times, agent1_crash_times, agent2_crash_times, agent1_timeout_times, agent2_timeout_times, agent1_launch_failure_times, agent2_launch_failure_times ''' side1_win_times = 0 side2_win_times = 0 draw_times = 0 log_flag = agent1_name + '_vs_' + agent2_name agent1_launch_failed = False agent2_launch_failed = False round_cnt = 0 agent1_crash_list = [] agent2_crash_list = [] agent1_timeout_list = [] agent2_timeout_list = [] # file path constructing map_path = 'maps/' + map_name + '.map' agent1_path = 'agent/' + agent1_name + '/agent.py' agent2_path = 'agent/' + agent2_name + '/agent.py' if not os.path.exists(map_path): print('Error: map file not exist!') exit(-1) if not os.path.exists(agent1_path): print('Error: agent1 file not exist!') exit(-1) if not os.path.exists(agent2_path): print('Error: agent2 file not exist!') exit(-1) # make env env = Environment(map_path, 'raw', 'raw', max_step=max_step, render=True, random_pos=random_pos, log=log_flag) # get map info size_x, size_y = env.get_map_size() side1_detector_num, side1_fighter_num, side2_detector_num, side2_fighter_num = env.get_unit_num() # create agent agent1 = AgentCtrl(agent1_name, size_x, size_y, side1_detector_num, side1_fighter_num) agent2 = AgentCtrl(agent2_name, size_x, size_y, side2_detector_num, side2_fighter_num) if not agent1.agent_init(): print('ERROR: Agent1 ' + agent1_name + ' init failed!') agent1.terminate() agent2.terminate() agent1_launch_failed = True if not agent2.agent_init(): print('ERROR: Agent2 ' + agent2_name + ' init failed!') agent1.terminate() agent2.terminate() agent2_launch_failed = True # 若此处一方启动失败,则认为该方全败,启动失败计round_num次,若双方启动失败,则认为双方平局round_num次,其他与前述相同。 if agent1_launch_failed and agent2_launch_failed: return 0, 0, round_num, 0, 0, 0, 0, round_num, round_num elif agent1_launch_failed: return 0, round_num, 0, 0, 0, 0, 0, round_num, 0 elif agent2_launch_failed: return round_num, 0, 0, 0, 0, 0, 0, 0, round_num # execution # input("Press the key to continue...") for x in range(round_num): if x != 0: env.reset() step_cnt = 0 round_cnt += 1 while True: step_cnt += 1 # get obs side1_obs_dict, side2_obs_dict = env.get_obs() # get action agent1_action, agent1_result = agent1.get_action(side1_obs_dict, step_cnt) if agent1_result == 0: side1_detector_action = agent1_action['detector_action'] side1_fighter_action = agent1_action['fighter_action'] elif agent1_result == 1: agent1_crash_list.append(round_cnt) elif agent1_result == 2: agent1_timeout_list.append(round_cnt) agent2_action, agent2_result = agent2.get_action(side2_obs_dict, step_cnt) if agent2_result == 0: side2_detector_action = agent2_action['detector_action'] side2_fighter_action = agent2_action['fighter_action'] elif agent2_result == 1: agent2_crash_list.append(round_cnt) elif agent2_result == 2: agent2_timeout_list.append(round_cnt) # execution if agent1_result == 0 and agent2_result == 0: env.step(side1_detector_action, side1_fighter_action, side2_detector_action, side2_fighter_action) elif agent1_result != 0 and agent2_result != 0: env.set_surrender(2) elif agent1_result != 0: env.set_surrender(0) else: env.set_surrender(1) # get done if env.get_done(): # reward o_detector_reward, o_fighter_reward, o_game_reward, e_detector_reward, e_fighter_reward, e_game_reward = env.get_reward() if o_game_reward > e_game_reward: side1_win_times += 1 elif o_game_reward < e_game_reward: side2_win_times += 1 else: draw_times += 1 break agent1.terminate() agent2.terminate() return side1_win_times, side2_win_times, draw_times, len(agent1_crash_list), len(agent2_crash_list), len(agent1_timeout_list), len(agent2_timeout_list),0, 0 if __name__ == "__main__": map_name = '' round_num = 0 max_step = 0 agent_list = [] summary_table = [] # load config with open('tournament/config.ini', 'r') as f: config_dict = json.load(f) map_name = config_dict['map_name'] round_num = config_dict['round_num'] max_step = config_dict['max_step'] agent_list = config_dict['agent_list'] print('map_name: %s' % map_name) print('round_num: %d' % round_num) print('max_step: %d' % max_step) print('agent_list: ') print(agent_list) if round_num % 2 != 0: print('Error: round_num must be even number') exit(-1) win_rounds_per_agent = [0] * len(agent_list) draw_rounds_per_agent = [0] * len(agent_list) total_rounds_per_agent = [0] * len(agent_list) crash_rounds_per_agent = [0] * len(agent_list) timeout_rounds_per_agent = [0] * len(agent_list) launch_failed_rounds_per_agent = [0] * len(agent_list) win_rounds_against_each_other = np.zeros((len(agent_list), len(agent_list)), dtype='int32') draw_rounds_against_each_other = np.zeros((len(agent_list), len(agent_list)), dtype='int32') crash_rounds_against_each_other = np.zeros((len(agent_list), len(agent_list)), dtype='int32') timeout_rounds_against_each_other = np.zeros((len(agent_list), len(agent_list)), dtype='int32') launch_failed_rounds_against_each_other = np.zeros((len(agent_list), len(agent_list)), dtype='int32') for x in range(len(agent_list)): for y in range(len(agent_list)): if y == x: continue agent1 = agent_list[x] agent2 = agent_list[y] print(agent1 + ' vs ' + agent2) agent1_win_rounds, agent2_win_rounds, draw_rounds, agent1_crash_rounds, agent2_crash_rounds, agent1_timeout_rounds, agent2_timeout_rounds, agent1_launch_failure_rounds, agent2_launch_failure_rounds = run(agent1, agent2, map_name, int(round_num / 2), max_step) # record total_rounds = agent1_win_rounds + agent2_win_rounds + draw_rounds win_rounds_per_agent[x] += agent1_win_rounds win_rounds_per_agent[y] += agent2_win_rounds draw_rounds_per_agent[x] += draw_rounds draw_rounds_per_agent[y] += draw_rounds total_rounds_per_agent[x] += total_rounds total_rounds_per_agent[y] += total_rounds crash_rounds_per_agent[x] += agent1_crash_rounds crash_rounds_per_agent[y] += agent2_crash_rounds timeout_rounds_per_agent[x] += agent1_timeout_rounds timeout_rounds_per_agent[y] += agent1_timeout_rounds launch_failed_rounds_per_agent[x] += agent1_launch_failure_rounds launch_failed_rounds_per_agent[y] += agent2_launch_failure_rounds win_rounds_against_each_other[x][y] += agent1_win_rounds win_rounds_against_each_other[y][x] += agent2_win_rounds draw_rounds_against_each_other[x][y] += draw_rounds draw_rounds_against_each_other[y][x] += draw_rounds crash_rounds_against_each_other[x][y] += agent1_crash_rounds crash_rounds_against_each_other[y][x] += agent2_crash_rounds timeout_rounds_against_each_other[x][y] += agent1_timeout_rounds timeout_rounds_against_each_other[y][x] += agent2_timeout_rounds launch_failed_rounds_against_each_other[x][y] += agent1_launch_failure_rounds launch_failed_rounds_against_each_other[y][x] += agent2_launch_failure_rounds # write file # win detail win_detail_table = pd.DataFrame(win_rounds_against_each_other) win_detail_table.columns = agent_list win_detail_table.index = agent_list win_detail_table.to_html('tournament/result/win_detail.html') # draw detail draw_detail_table = pd.DataFrame(draw_rounds_against_each_other) draw_detail_table.columns = agent_list draw_detail_table.index = agent_list draw_detail_table.to_html('tournament/result/draw_detail.html') # crash detail crash_detail_table = pd.DataFrame(crash_rounds_against_each_other) crash_detail_table.columns = agent_list crash_detail_table.index = agent_list crash_detail_table.to_html('tournament/result/crash_detail.html') # timeout detail timeout_detail_table = pd.DataFrame(timeout_rounds_against_each_other) timeout_detail_table.columns = agent_list timeout_detail_table.index = agent_list timeout_detail_table.to_html('tournament/result/timeout_detail.html') # launch failure detail launch_failure_detail_table = pd.DataFrame(launch_failed_rounds_against_each_other) launch_failure_detail_table.columns = agent_list launch_failure_detail_table.index = agent_list launch_failure_detail_table.to_html('tournament/result/launch_failure_detail.html') # summary summary_table = pd.DataFrame(columns=['agent', 'score', 'win_rate', 'total_rounds', 'win_rounds', 'draw_rounds', 'loss_rounds', 'crash_rounds', 'timeout_rounds', 'launch_failure_rounds']) for z in range(len(agent_list)): if total_rounds_per_agent[z] == 0: win_rate = 0 else: win_rate = win_rounds_per_agent[z] / total_rounds_per_agent[z] score = SCORE_WIN * win_rounds_per_agent[z] + SCORE_DRAW * draw_rounds_per_agent[z] + SCORE_LOSS * (total_rounds_per_agent[z] - win_rounds_per_agent[z] - draw_rounds_per_agent[z]) summary_table = summary_table.append(pd.DataFrame({'agent': [agent_list[z]], 'score': [score], 'win_rate': [win_rate], 'total_rounds': [total_rounds_per_agent[z]], 'win_rounds': [win_rounds_per_agent[z]], 'draw_rounds': [draw_rounds_per_agent[z]], 'loss_rounds': [total_rounds_per_agent[z] - win_rounds_per_agent[z] - draw_rounds_per_agent[z]], 'crash_rounds': [crash_rounds_per_agent[z]], 'timeout_rounds': [timeout_rounds_per_agent[z]], 'launch_failure_rounds': [launch_failed_rounds_per_agent[z]]})) summary_table = summary_table.sort_values(by='score', ascending=False) summary_table.to_html('tournament/result/summary.html', index=False) print('tournament completed!, result:') print(summary_table) ================================================ FILE: train/simple/dqn.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: dqn.py @time: 2018/7/25 0025 13:38 @desc: """ import torch import torch.nn as nn import numpy as np class NetFighter(nn.Module): def __init__(self, n_actions): super(NetFighter, self).__init__() self.conv1 = nn.Sequential( # 100 * 100 * 3 nn.Conv2d( in_channels=5, out_channels=16, kernel_size=5, stride=1, padding=2, ), nn.ReLU(), nn.MaxPool2d(2), ) self.conv2 = nn.Sequential( # 50 * 50 * 16 nn.Conv2d(16, 32, 5, 1, 2), nn.ReLU(), nn.MaxPool2d(2), # 25 * 25 * 32 ) self.info_fc = nn.Sequential( nn.Linear(3, 256), nn.Tanh(), ) self.feature_fc = nn.Sequential( # 25 * 25 * 32 + 256 nn.Linear((25 * 25 * 32 + 256), 512), nn.ReLU(), ) self.decision_fc = nn.Linear(512, n_actions) def forward(self, img, info): img_feature = self.conv1(img) img_feature = self.conv2(img_feature) info_feature = self.info_fc(info) combined = torch.cat((img_feature.view(img_feature.size(0), -1), info_feature.view(info_feature.size(0), -1)), dim=1) feature = self.feature_fc(combined) action = self.decision_fc(feature) return action # Deep Q Network off-policy class RLFighter: def __init__( self, n_actions, learning_rate=0.01, reward_decay=0.9, e_greedy=0.9, replace_target_iter=100, memory_size=500, batch_size=32, e_greedy_increment=None, output_graph=False, ): self.n_actions = n_actions self.lr = learning_rate self.gamma = reward_decay self.epsilon_max = e_greedy self.replace_target_iter = replace_target_iter self.memory_size = memory_size self.batch_size = batch_size self.epsilon_increment = e_greedy_increment self.epsilon = 0 if e_greedy_increment is not None else self.epsilon_max self.s_screen_memory = [] self.s_info_memory = [] self.a_memory = [] self.r_memory = [] self.s__screen_memory = [] self.s__info_memory = [] self.memory_counter = 0 self.gpu_enable = torch.cuda.is_available() # total learning step self.learn_step_counter = 0 self.cost_his = [] self.eval_net, self.target_net = NetFighter(self.n_actions), NetFighter(self.n_actions) if self.gpu_enable: print('GPU Available!!') self.eval_net = self.eval_net.cuda() self.target_net = self.target_net.cuda() self.loss_func = nn.MSELoss() # self.optimizer = torch.optim.Adam(self.eval_net.parameters(), lr=self.lr) self.optimizer = torch.optim.RMSprop(self.eval_net.parameters(), lr=self.lr) def store_transition(self, s, a, r, s_): self.s_screen_memory.append(s['screen']) self.s_info_memory.append(s['info']) self.a_memory.append(a) self.r_memory.append(r) self.s__screen_memory.append(s_['screen']) self.s__info_memory.append(s_['info']) self.memory_counter += 1 def __clear_memory(self): self.s_screen_memory.clear() self.s_info_memory.clear() self.a_memory.clear() self.r_memory.clear() self.s__screen_memory.clear() self.s__info_memory.clear() self.memory_counter = 0 def choose_action(self, img_obs, info_obs): img_obs = torch.unsqueeze(torch.FloatTensor(img_obs), 0) info_obs = torch.unsqueeze(torch.FloatTensor(info_obs), 0) if self.gpu_enable: img_obs = img_obs.cuda() info_obs = info_obs.cuda() if np.random.uniform() < self.epsilon: actions_value = self.eval_net(img_obs, info_obs) action = torch.max(actions_value, 1)[1] if self.gpu_enable: action = action.cpu() action = action.numpy() else: action = np.zeros(1, dtype=np.int32) action[0] = np.random.randint(0, self.n_actions) return action def learn(self): # check to replace target parameters if self.learn_step_counter % self.replace_target_iter == 0: self.target_net.load_state_dict(self.eval_net.state_dict()) print('\ntarget_params_replaced\n') step_counter_str = '%09d' % self.learn_step_counter torch.save(self.target_net.state_dict(), 'model/simple/model_' + step_counter_str + '.pkl') # pre possess mem s_screen_mem = torch.FloatTensor(np.array(self.s_screen_memory)) s_info_mem = torch.FloatTensor(np.array(self.s_info_memory)) a_mem = torch.LongTensor(np.array(self.a_memory)) r_mem = torch.FloatTensor(np.array(self.r_memory)) r_mem = r_mem.view(self.memory_counter, 1) s__screen_mem = torch.FloatTensor(np.array(self.s__screen_memory)) s__info_mem = torch.FloatTensor(np.array(self.s__info_memory)) if self.gpu_enable: s_screen_mem = s_screen_mem.cuda() s_info_mem = s_info_mem.cuda() a_mem = a_mem.cuda() r_mem = r_mem.cuda() s__screen_mem = s__screen_mem.cuda() s__info_mem = s__info_mem.cuda() # sample batch memory from all memory # q_eval w.r.t the action in experience q_eval = self.eval_net(s_screen_mem, s_info_mem).gather(1, a_mem) # shape (batch, 1) q_next = self.target_net(s__screen_mem, s__info_mem).detach() # detach from graph, don't backpropagate q_target = r_mem + self.gamma * q_next.max(1)[0].view(self.memory_counter, 1) # shape (batch, 1) loss = self.loss_func(q_eval, q_target) self.optimizer.zero_grad() loss.backward() self.optimizer.step() self.cost_his.append(loss) # increasing epsilon self.epsilon = self.epsilon + self.epsilon_increment if self.epsilon < self.epsilon_max else self.epsilon_max self.learn_step_counter += 1 self.__clear_memory() class NetDetector(nn.Module): def __init__(self, n_actions): super(NetDetector, self).__init__() self.conv1 = nn.Sequential( # 100 * 100 * 3 nn.Conv2d( in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=2, ), nn.ReLU(), nn.MaxPool2d(2), ) self.conv2 = nn.Sequential( # 50 * 50 * 16 nn.Conv2d(16, 32, 5, 1, 2), nn.ReLU(), nn.MaxPool2d(2), # 25 * 25 * 32 ) self.info_fc = nn.Sequential( nn.Linear(3, 256), nn.Tanh(), ) self.feature_fc = nn.Sequential( # 25 * 25 * 32 + 256 nn.Linear((25 * 25 * 32 + 256), 512), nn.ReLU(), ) self.decision_fc = nn.Linear(512, n_actions) def forward(self, img, info): img_feature = self.conv1(img) img_feature = self.conv2(img_feature) info_feature = self.info_fc(info) combined = torch.cat((img_feature.view(img_feature.size(0), -1), info_feature.view(info_feature.size(0), -1)), dim=1) feature = self.feature_fc(combined) action = self.decision_fc(feature) return action ================================================ FILE: train/simple/main.py ================================================ #! /usr/bin/env python # -*- coding: utf-8 -*- """ @author: Gao Fang @contact: gaofang@cetc.com.cn @software: PyCharm @file: main.py @time: 2018/7/25 0025 10:01 @desc: """ import os import copy import numpy as np from agent.fix_rule_no_att.agent import Agent from interface import Environment from train.simple import dqn MAP_PATH = 'maps/1000_1000_fighter10v10.map' RENDER = True MAX_EPOCH = 1000 BATCH_SIZE = 200 LR = 0.01 # learning rate EPSILON = 0.9 # greedy policy GAMMA = 0.9 # reward discount TARGET_REPLACE_ITER = 100 # target update frequency DETECTOR_NUM = 0 FIGHTER_NUM = 10 COURSE_NUM = 16 ATTACK_IND_NUM = (DETECTOR_NUM + FIGHTER_NUM) * 2 + 1 # long missile attack + short missile attack + no attack ACTION_NUM = COURSE_NUM * ATTACK_IND_NUM LEARN_INTERVAL = 100 if __name__ == "__main__": # create blue agent blue_agent = Agent() # get agent obs type red_agent_obs_ind = 'simple' blue_agent_obs_ind = blue_agent.get_obs_ind() # make env env = Environment(MAP_PATH, red_agent_obs_ind, blue_agent_obs_ind, render=RENDER) # get map info size_x, size_y = env.get_map_size() red_detector_num, red_fighter_num, blue_detector_num, blue_fighter_num = env.get_unit_num() # set map info to blue agent blue_agent.set_map_info(size_x, size_y, blue_detector_num, blue_fighter_num) red_detector_action = [] fighter_model = dqn.RLFighter(ACTION_NUM) # execution for x in range(MAX_EPOCH): step_cnt = 0 env.reset() while True: obs_list = [] action_list = [] red_fighter_action = [] # get obs if step_cnt == 0: red_obs_dict, blue_obs_dict = env.get_obs() # get action # get blue action blue_detector_action, blue_fighter_action = blue_agent.get_action(blue_obs_dict, step_cnt) # get red action obs_got_ind = [False] * red_fighter_num for y in range(red_fighter_num): true_action = np.array([0, 1, 0, 0], dtype=np.int32) if red_obs_dict['fighter'][y]['alive']: obs_got_ind[y] = True tmp_img_obs = red_obs_dict['fighter'][y]['screen'] tmp_img_obs = tmp_img_obs.transpose(2, 0, 1) tmp_info_obs = red_obs_dict['fighter'][y]['info'] tmp_action = fighter_model.choose_action(tmp_img_obs, tmp_info_obs) obs_list.append({'screen': copy.deepcopy(tmp_img_obs), 'info': copy.deepcopy(tmp_info_obs)}) action_list.append(tmp_action) # action formation true_action[0] = int(360 / COURSE_NUM * int(tmp_action[0] / ATTACK_IND_NUM)) true_action[3] = int(tmp_action[0] % ATTACK_IND_NUM) red_fighter_action.append(true_action) red_fighter_action = np.array(red_fighter_action) # step env.step(red_detector_action, red_fighter_action, blue_detector_action, blue_fighter_action) # get reward red_detector_reward, red_fighter_reward, red_game_reward, blue_detector_reward, blue_fighter_reward, blue_game_reward = env.get_reward() detector_reward = red_detector_reward + red_game_reward fighter_reward = red_fighter_reward + red_game_reward # save repaly red_obs_dict, blue_obs_dict = env.get_obs() for y in range(red_fighter_num): if obs_got_ind[y]: tmp_img_obs = red_obs_dict['fighter'][y]['screen'] tmp_img_obs = tmp_img_obs.transpose(2, 0, 1) tmp_info_obs = red_obs_dict['fighter'][y]['info'] fighter_model.store_transition(obs_list[y], action_list[y], fighter_reward[y], {'screen': copy.deepcopy(tmp_img_obs), 'info': copy.deepcopy(tmp_info_obs)}) # if done, perform a learn if env.get_done(): # detector_model.learn() fighter_model.learn() break # if not done learn when learn interval if (step_cnt > 0) and (step_cnt % LEARN_INTERVAL == 0): # detector_model.learn() fighter_model.learn() step_cnt += 1 ================================================ FILE: utility.py ================================================ # utility.py import math def distance(x1, y1, x2, y2): return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) def angle(x1, y1, x2, y2): angle = math.degrees(math.atan2(y2 - y1, x2 - x1)) if angle < 0: angle += 360 return int(angle)