def start_update(self, build=False): main_data = self.wrapper.get_action(village_id=self.village_id, action="main") self.costs = Extractor.building_data(main_data) self.game_state = Extractor.game_state(main_data) if self.resman: self.resman.update(self.game_state) if not self.logger: self.logger = logging.getLogger("Builder: %s" % self.game_state['village']['name']) self.logger.debug("Updating building levels") tmp = self.game_state['village']['buildings'] for e in tmp: tmp[e] = int(tmp[e]) self.levels = tmp existing_queue = self.get_existing_items(main_data) if existing_queue == 0: self.waits = [] if self.is_queued(): self.logger.info("No build operation was executed: queue full, %d left" % len(self.queue)) if not build: return False if existing_queue != 0 and existing_queue != len(self.waits): self.logger.warning("Building queue out of sync, waiting until %d manual actions are finished!" % existing_queue) return False for x in range(self.max_queue_len): result = self.get_next_building_action() if not result: self.logger.info("No build operation was executed %d left" % len(self.queue)) return False return True
def get_map(self): if self.last_fetch + (self.fetch_delay * 3600) > time.time(): return self.last_fetch = time.time() res = self.wrapper.get_action(village_id=self.village_id, action="map") game_state = Extractor.game_state(res) self.map_data = Extractor.map_data(res) if self.map_data: for tile in self.map_data: data = tile['data'] x = int(data['x']) y = int(data['y']) for lon in data['villages']: try: for lat in data['villages'][lon]: coords = [x + int(lon), y + int(lat)] entry = data['villages'][lon][lat] if entry[0] == str(self.village_id): self.my_location = coords self.build_cache_entry(location=coords, entry=entry) except: continue if not self.my_location: self.my_location = [ game_state['village']['x'], game_state['village']['y'] ] if not self.map_data or not self.villages: return self.get_map_old(game_state=game_state) return True
def update_totals(self): main_data = self.wrapper.get_action(action="overview", village_id=self.village_id) self.game_data = Extractor.game_state(main_data) if not self.logger: self.logger = logging.getLogger("Recruitment: %s" % self.game_data['village']['name']) self.troops = {} for u in Extractor.units_in_village(main_data): k, v = u self.troops[k] = v self.logger.debug("Units in village: %s" % str(self.troops)) if not self.can_recruit: return get_all = "game.php?village=%s&screen=place&mode=units&display=units" % self.village_id result_all = self.wrapper.get_url(get_all) self.total_troops = {} for u in Extractor.units_in_total(result_all): k, v = u if k in self.total_troops: self.total_troops[k] = self.total_troops[k] + int(v) else: self.total_troops[k] = int(v) self.logger.debug("Village units total: %s" % str(self.total_troops))
def attack(self, vid, troops=None): url = "game.php?village=%s&screen=place&target=%s" % (self.village_id, vid) pre_attack = self.wrapper.get_url(url) pre_data = {} for u in Extractor.attack_form(pre_attack): k, v = u pre_data[k] = v if troops: pre_data.update(troops) else: pre_data.update(self.troopmanager.troops) if vid not in self.map.map_pos: return False x, y = self.map.map_pos[vid] post_data = { 'x': x, 'y': y, 'target_type': 'coord', 'attack': 'Aanvallen' } pre_data.update(post_data) confirm_url = "game.php?village=%s&screen=place&try=confirm" % self.village_id conf = self.wrapper.post_url(url=confirm_url, data=pre_data) if '<div class="error_box">' in conf.text: return False duration = Extractor.attack_duration(conf) self.logger.info("[Attack] %s -> %s duration %f.1 h" % (self.village_id, vid, duration / 3600)) confirm_data = {} for u in Extractor.attack_form(conf): k, v = u if k == "support": continue confirm_data[k] = v new_data = { 'building': 'main', 'h': self.wrapper.last_h, } confirm_data.update(new_data) result = self.wrapper.get_api_action(village_id=self.village_id, action="popup_command", params={"screen": "place"}, data=confirm_data) return result
def start_update(self, build=False, set_village_name=None): main_data = self.wrapper.get_action(village_id=self.village_id, action="main") if self.complete_actions(main_data.text): return self.start_update(build=build, set_village_name=set_village_name) self.costs = Extractor.building_data(main_data) self.game_state = Extractor.game_state(main_data) if self.resman: self.resman.update(self.game_state) if 'building' in self.resman.requested: # new run, remove request self.resman.requested['building'] = {} vname = self.game_state['village']['name'] if set_village_name and vname != set_village_name: self.wrapper.post_url( url="game.php?village=%s&screen=main&action=change_name" % self.village_id, data={"name": set_village_name, "h": self.wrapper.last_h} ) if not self.logger: self.logger = logging.getLogger("Builder: %s" % vname) self.logger.debug("Updating building levels") tmp = self.game_state['village']['buildings'] for e in tmp: tmp[e] = int(tmp[e]) self.levels = tmp existing_queue = self.get_existing_items(main_data) if existing_queue == 0: self.waits = [] self.waits_building = [] if self.is_queued(): self.logger.info("No build operation was executed: queue full, %d left" % len(self.queue)) return False if not build: return False if existing_queue != 0 and existing_queue != len(self.waits): self.logger.warning("Building queue out of sync, waiting until %d manual actions are finished!" % existing_queue) return True for x in range(self.max_queue_len - len(self.waits)): result = self.get_next_building_action() if not result: self.logger.info("No build more operations where executed (%d current, %d left)" % (len(self.waits), len(self.queue))) return False return True
def get_next_building_action(self, index=0): if index >= self.max_lookahead: self.logger.debug("Not building anything because insufficient resources") return False queue_check = self.is_queued() if queue_check: self.logger.debug("Not building because of queued items: %s" % self.waits) return False if self.resman and self.resman.in_need_of("pop"): build_data = 'farm:%d' % (int(self.levels['farm']) + 1) if len(self.queue) and "farm" not in [x.split(':')[0] for x in self.queue[0:self.max_lookahead]] \ and int(self.levels['farm']) != 30: self.queue.insert(0, build_data) self.logger.info("Adding farm in front of queue because low on pop") return self.get_next_building_action(0) if len(self.queue): entry = self.queue[index] entry, min_lvl = entry.split(':') min_lvl = int(min_lvl) if min_lvl <= self.levels[entry]: self.queue.pop(index) return self.get_next_building_action(index=index) if entry not in self.costs: self.logger.debug("Ignoring %s because not yet available" % entry) return self.get_next_building_action(index + 1) check = self.costs[entry] if 'max_level' in check and min_lvl > check['max_level']: self.logger.debug("Removing entry %s because max_level exceeded" % entry) self.queue.pop(index) return self.get_next_building_action(index=index) if check['can_build'] and self.has_enough(check) and 'build_link' in check: queue = self.put_wait(check['build_time']) self.logger.info("Building %s %d -> %d (finishes: %s)" % (entry, self.levels[entry], self.levels[entry]+1, self.readable_ts(queue))) self.wrapper.reporter.report(self.village_id, "TWB_BUILD", "Building %s %d -> %d (finishes: %s)" % (entry, self.levels[entry], self.levels[entry]+1, self.readable_ts(queue))) self.levels[entry] += 1 result = self.wrapper.get_url(check['build_link'].replace('amp;', '')) self.game_state = Extractor.game_state(result) self.costs = Extractor.building_data(result) return True else: return self.get_next_building_action(index + 1)
def attempt_recruit(self, amount): result = self.wrapper.get_action(action="snob", village_id=self.village_id) if '"id":"coin"' in result.text: self.using_coin_system = True game_data = Extractor.game_state(result) self.resman.update(game_data) can_recruit = re.search(r'(?s)</th><th>(\d+)</th></tr>\s*</table><br />', result.text) if not can_recruit or int(can_recruit.group(1)) == 0: nres = self.need_reserve(result.text) if nres > 0: self.logger.debug("Not enough resources available, still %d needed, attempting storage" % nres) cres = self.storage_item(result.text) if not self.using_coin_system else self.coin_item(result.text) if cres: return self.attempt_recruit(amount) else: self.is_incomplete = True self.logger.debug("Not enough resources available") return False self.is_incomplete = False r_num = int(can_recruit.group(1)) if r_num == 0: self.logger.debug("No more snobs available, awaiting snob creating, snob death or village loss") return False train_snob_url = "game.php?village=%s&screen=snob&action=train&h=%s" % (self.village_id, self.wrapper.last_h) self.wrapper.get_url(train_snob_url) return True
def attempt_research(self, unit_type, smith_data=None): if not smith_data: result = self.wrapper.get_action(village_id=self.village_id, action="smith") smith_data = Extractor.smith_data(result) if not smith_data or unit_type not in smith_data['available']: self.logger.warning( "Unit %s does not appear to be available or smith not built yet" % unit_type) return data = smith_data['available'][unit_type] if 'can_research' in data and data['can_research']: if 'research_error' in data and data['research_error']: return False if 'error_buildings' in data and data['error_buildings']: return False res = self.wrapper.get_api_action(village_id=self.village_id, action="research", params={"screen": "smith"}, data={ "tech_id": unit_type, "source": self.village_id, 'h': self.wrapper.last_h }) if res: self.logger.info("Started research of %s" % unit_type) self.resman.update(res['game_data']) return True self.logger.info("Research of %s not yet possible" % unit_type)
def attempt_recruit(self, amount): result = self.wrapper.get_action(action="snob", village_id=self.village_id) game_data = Extractor.game_state(result) self.resman.update(game_data) nres = self.need_reserve(result.text) if nres > 0: self.logger.debug("Not enough resources available, still %d needed, attempting storage" % nres) cres = self.coin(result.text) if cres: return self.attempt_recruit(amount) else: self.is_incomplete = True self.logger.debug("Not enough resources available") return False self.is_incomplete = False can_recruit = re.search(r'(?s)<th>Er kan nog geproduceerd worden:</th>\s*<th>(\d+)<', result.text) if not can_recruit: self.logger.warning('Error fetching current snob number') return False r_num = int(can_recruit.group(1)) if r_num == 0: self.logger.debug("No more snobs available, awaiting snob creating, snob death or village loss") return False return False
def get_quests(self): result = Extractor.get_quests(self.wrapper.last_response) if result: qres = self.wrapper.get_api_action(action="quest_complete", village_id=self.village_id, params={'quest': result, 'skip': 'false'}) if qres: self.logger.info("Completed quest: %s" % str(result)) return True self.logger.debug("There where no completed quests") return False
def attack(self, source, vid, troops=None): url = "game.php?village=%s&screen=place&target=%s" % (source, vid) pre_attack = self.wrapper.get_url(url) pre_data = {} for u in Extractor.attack_form(pre_attack): k, v = u pre_data[k] = v if troops: pre_data.update(troops) if vid not in self.game_map.map_pos: return False x, y = self.game_map.map_pos[vid] post_data = { 'x': x, 'y': y, 'target_type': 'coord', 'attack': 'Aanvallen' } pre_data.update(post_data) confirm_url = "game.php?village=%s&screen=place&try=confirm" % self.village_id conf = self.wrapper.post_url(url=confirm_url, data=pre_data) if '<div class="error_box">' in conf.text: return False duration = Extractor.attack_duration(conf) confirm_data = {} for u in Extractor.attack_form(conf): k, v = u if k == "support": continue confirm_data[k] = v new_data = { 'building': 'main', 'h': self.wrapper.last_h, } confirm_data.update(new_data) return confirm_data, duration
def read(self, page=0, full_run=False): if not self.logger: self.logger = logging.getLogger("Reports") if len(self.last_reports) == 0: self.logger.info("First run, re-reading cache entries") self.last_reports = ReportCache.cache_grab() self.logger.info("Got %d reports from cache" % len(self.last_reports)) url = "game.php?village=%s&screen=report&mode=all&from=%d" % ( self.village_id, page * 12) result = self.wrapper.get_url(url) self.game_state = Extractor.game_state(result) new = 0 ids = Extractor.report_table(result) for report_id in ids: if report_id in self.last_reports: continue new += 1 url = "game.php?village=%s&screen=report&mode=all&group_id=0&view=%s" % ( self.village_id, report_id) data = self.wrapper.get_url(url) get_type = re.search(r'class="report_(\w+)', data.text) if get_type: report_type = get_type.group(1) if report_type == "ReportAttack": self.attack_report(data.text, report_id) continue else: res = self.put(report_id, report_type=report_type) self.last_reports[report_id] = res if new == 12 or full_run and page < 20: page += 1 self.logger.debug( "%d new reports where added, also checking page %d" % (new, page)) return self.read(page, full_run=full_run)
def attempt_upgrade(self): self.logger.debug("Managing Upgrades") if self._research_wait > time.time(): self.logger.debug("Smith still busy for %d seconds" % int(self._research_wait - time.time())) return unit_levels = self.wanted_levels if not unit_levels: self.logger.debug("Not upgrading because nothing is requested") return result = self.wrapper.get_action(village_id=self.village_id, action="smith") smith_data = Extractor.smith_data(result) if not smith_data: self.logger.debug("Error reading smith data") return False for unit_type in unit_levels: if not smith_data or unit_type not in smith_data['available']: self.logger.warning( "Unit %s does not appear to be available or smith not built yet" % unit_type) continue wanted_level = unit_levels[unit_type] current_level = int(smith_data['available'][unit_type]['level']) data = smith_data['available'][unit_type] if current_level < wanted_level and 'can_research' in data and data[ 'can_research']: if 'research_error' in data and data['research_error']: self.logger.debug( "Skipping research of %s because of research error" % unit_type) continue if 'error_buildings' in data and data['error_buildings']: self.logger.debug( "Skipping research of %s because of building error" % unit_type) continue attempt = self.attempt_research(unit_type, smith_data=smith_data) if attempt: self.logger.info( "Started smith upgrade of %s %d -> %d" % (unit_type, current_level, current_level + 1)) self.wrapper.reporter.report( self.village_id, "TWB_UPGRADE", "Started smith upgrade of %s %d -> %d" % (unit_type, current_level, current_level + 1)) return True return False
def get_overview(self, config): result_get = self.wrapper.get_url("game.php?screen=overview_villages") result_villages = None if 'add_new_villages' in config['bot'] and config['bot'][ 'add_new_villages']: result_villages = Extractor.village_ids_from_overview(result_get) for found_vid in result_villages: if found_vid not in config['villages']: print( "Village %s was found but no config entry was found. Adding automatically" % found_vid) self.add_village(vid=found_vid) return self.get_overview(self.config()) return result_villages, result_get
def attempt_upgrade(self, unit_levels): self.logger.debug("Managing Upgrades") result = self.wrapper.get_action(village_id=self.village_id, action="smith") smith_data = Extractor.smith_data(result) for unit_type in unit_levels: if not smith_data or unit_type not in smith_data['available']: self.logger.warning( "Unit %s does not appear to be available or smith not built yet" % unit_type) continue wanted_level = unit_levels[unit_type] current_level = int(smith_data['available'][unit_type]['level']) if current_level < wanted_level: attempt = self.attempt_research(unit_type, smith_data=smith_data) if attempt: self.logger.info( "Started smith upgrade of %s %d -> %d" % (unit_type, current_level, current_level + 1)) return True return False
def do_premium_stuff(self): gpl = self.get_plenty_off() if gpl and self.do_premium_trade: url = "game.php?village=%s&screen=market&mode=exchange" % self.village_id res = self.wrapper.get_url(url=url) data = Extractor.premium_data(res.text) if not data: self.logger.warning("Error reading premium data!") price_fetch = ["wood", "stone", "iron"] prices = {} for p in price_fetch: prices[p] = data['stock'] * data['rates'] self.logger.info("Actual premium prices: %s" % prices) if gpl in prices and prices[gpl] * 1.1 < self.actual[gpl]: self.logger.info( "Attempting trade of %d %s for premium point" % (prices[gpl], gpl)) self.wrapper.get_api_action(self.village_id, action="exchange_begin", params={'screen': "market"}, data={"sell_%s" % gpl: "1"})
def get_existing_items(self, text): waits = Extractor.active_building_queue(text) return waits
def recruit(self, unit_type, amount=10, wait_for=False, building="barracks"): data = self.wrapper.get_action(action=building, village_id=self.village_id) existing = Extractor.active_recruit_queue(data) if existing: self.logger.warning( "Building Village %s %s recruitment queue out-of-sync" % (self.village_id, building)) if not self.can_fix_queue: return True for entry in existing: self.cancel(building=building, id=entry) self.logger.info("Canceled recruit item %s on building %s" % (entry, building)) return self.recruit(unit_type, amount, wait_for, building) self.recruit_data = Extractor.recruit_data(data) self.game_data = Extractor.game_state(data) self.logger.info("Attempting recruitment of %d %s" % (amount, unit_type)) if amount > self.max_batch_size: amount = self.max_batch_size if unit_type not in self.recruit_data: self.logger.warning( "Recruitment of %d %s failed because it is not researched" % (amount, unit_type)) self.attempt_research(unit_type) return False resources = self.recruit_data[unit_type] if not resources: self.logger.warning( "Recruitment of %d %s failed because invalid identifier" % (amount, unit_type)) return False if not resources['requirements_met']: self.logger.warning( "Recruitment of %d %s failed because it is not researched" % (amount, unit_type)) self.attempt_research(unit_type) return False get_min = self.get_min_possible(resources) if get_min == 0: self.logger.info( "Recruitment of %d %s failed because of not enough resources" % (amount, unit_type)) return False if get_min < amount: if wait_for: self.logger.warning( "Recruitment of %d %s failed because of not enough resources" % (amount, unit_type)) return False if get_min > 0: self.logger.info( "Recruitment of %d %s was set to %d because of resources" % (amount, unit_type, get_min)) amount = get_min result = self.wrapper.get_api_action( village_id=self.village_id, action="train", params={ "screen": building, "mode": "train" }, data={"units[%s]" % unit_type: str(amount)}) if 'game_data' in result: self.resman.update(result['game_data']) self.wait_for[self.village_id][building] = int( time.time()) + (amount * int(resources['build_time'])) # self.troops[unit_type] = str((int(self.troops[unit_type]) if unit_type in self.troops else 0) + amount) self.logger.info("Recruitment of %d %s started (%s idle till %d)" % (amount, unit_type, building, self.wait_for[self.village_id][building])) self.wrapper.reporter.report( self.village_id, "TWB_RECRUIT", "Recruitment of %d %s started (%s idle till %d)" % (amount, unit_type, building, self.wait_for[self.village_id][building])) return True return False
def attack_report(self, report, report_id): from_village = None from_player = None to_village = None to_player = None extra = {} losses = {} attacker = re.search(r'(?s)(<table id="attack_info_att".+?</table>)', report) if attacker: attacker_data = re.search(r'data-player="(\d+)" data-id="(\d+)"', attacker.group(1)) if attacker_data: from_player = attacker_data.group(1) from_village = attacker_data.group(2) units = re.search( r'(?s)<table id="attack_info_att_units"(.+?)</table>', attacker.group(1)) if units: sent_units = re.findall('(?s)<tr>(.+?)</tr>', units.group(1)) extra['units_sent'] = self.re_unit( Extractor.units_in_total(sent_units[0])) if len(sent_units) == 2: extra['units_losses'] = self.re_unit( Extractor.units_in_total(sent_units[1])) if from_player == self.game_state['player']['id']: losses = extra['units_losses'] defender = re.search(r'(?s)(<table id="attack_info_def".+?</table>)', report) if defender: defender_data = re.search(r'data-player="(\d+)" data-id="(\d+)"', defender.group(1)) if defender_data: to_player = defender_data.group(1) to_village = defender_data.group(2) units = re.search( r'(?s)<table id="attack_info_def_units"(.+?)</table>', defender.group(1)) if units: def_units = re.findall('(?s)<tr>(.+?)</tr>', units.group(1)) extra['defence_units'] = self.re_unit( Extractor.units_in_total(def_units[0])) if len(def_units) == 2: extra['defence_losses'] = self.re_unit( Extractor.units_in_total(def_units[1])) if to_player == self.game_state['player']['id']: losses = extra['defence_losses'] results = re.search(r'(?s)(<table id="attack_results".+?</table>)', report) report = report.replace('<span class="grey">.</span>', '') if results: loot = {} for loot_entry in re.findall( r'<span class="icon header (wood|stone|iron)".+?</span>(\d+)', report): loot[loot_entry[0]] = loot_entry[1] extra['loot'] = loot self.logger.info("attack report %s -> %s" % (from_village, to_village)) scout_results = re.search( r'(?s)(<table id="attack_spy_resources".+?</table>)', report) if scout_results: self.logger.info("scout report %s -> %s" % (from_village, to_village)) scout_buildings = re.search( r'(?s)<input id="attack_spy_building_data" type="hidden" value="(.+?)"', report) if scout_buildings: raw = scout_buildings.group(1).replace('"', '"') extra['buildings'] = self.re_building(json.loads(raw)) loot = {} for loot_entry in re.findall( r'<span class="icon header (wood|stone|iron)".+?</span>(\d+)', report): loot[loot_entry[0]] = loot_entry[1] extra['resources'] = loot units_away = re.search( r'(?s)(<table id="attack_spy_away".+?</table>)', report) if units_away: data_away = self.re_unit( Extractor.units_in_total(units_away.group(1))) extra['units_away'] = data_away attack_type = "scout" if scout_results and not results else "attack" res = self.put(report_id, attack_type, from_village, to_village, data=extra, losses=losses) self.last_reports[report_id] = res return True
def run(self, config=None, first_run=False): # setup and check if village still exists / is accessible self.config = config self.wrapper.delay = self.get_config(section="bot", parameter="delay_factor", default=1.0) if not self.village_id: data = self.wrapper.get_url("game.php?screen=overview&intro") if data: self.game_data = Extractor.game_state(data) if self.game_data: self.village_id = str(self.game_data['village']['id']) self.logger = logging.getLogger( "Village %s" % self.game_data['village']['name']) self.logger.info("Read game state for village") else: data = self.wrapper.get_url("game.php?village=%s&screen=overview" % self.village_id) if data: self.game_data = Extractor.game_state(data) self.logger = logging.getLogger( "Village %s" % self.game_data['village']['name']) self.logger.info("Read game state for village") self.wrapper.reporter.report( self.village_id, "TWB_START", "Starting run for village: %s" % self.game_data['village']['name']) if not self.game_data: self.logger.error("Error reading game data for village %s" % self.village_id) return None if self.village_set_name and self.game_data['village'][ 'name'] != self.village_set_name: self.logger.name = "Village %s" % self.village_set_name if not self.get_config(section="villages", parameter=self.village_id): return None if self.get_config(section="server", parameter="server_on_twplus", default=False): self.twp.run( world=self.get_config(section="server", parameter="world")) vdata = self.get_config(section="villages", parameter=self.village_id) if not self.get_village_config( self.village_id, parameter="managed", default=False): return False if not self.game_data: return False if not self.resman: self.resman = ResourceManager(wrapper=self.wrapper, village_id=self.village_id) self.resman.update(self.game_data) self.wrapper.reporter.report(self.village_id, "TWB_PRE_RESOURCE", str(self.resman.actual)) if not self.rep_man: self.rep_man = ReportManager(wrapper=self.wrapper, village_id=self.village_id) self.rep_man.read(full_run=False) if not self.def_man: self.def_man = DefenceManager(wrapper=self.wrapper, village_id=self.village_id) self.def_man.map = self.area if not self.def_man.units: self.def_man.units = self.units last_attack = self.def_man.under_attack self.def_man.manage_flags_enabled = self.get_config( section="world", parameter="flags_enabled", default=False) self.def_man.support_factor = self.get_village_config( self.village_id, "support_others_factor", default=0.25) self.def_man.allow_support_send = self.get_village_config( self.village_id, parameter="support_others", default=False) self.def_man.allow_support_recv = self.get_village_config( self.village_id, parameter="request_support_on_attack", default=False) self.def_man.auto_evacuate = self.get_village_config( self.village_id, parameter="evacuate_fragile_units_on_attack", default=False) self.def_man.update(data.text, with_defence=self.get_config( section="units", parameter="manage_defence", default=False)) disabled_units = [] if not self.get_config( section="world", parameter="archers_enabled", default=True): disabled_units.extend(["archer", "marcher"]) if not self.get_config(section="world", parameter="building_destruction_enabled", default=True): disabled_units.extend(["ram", "catapult"]) if self.def_man.under_attack and not last_attack: self.logger.warning("Village under attack!") self.wrapper.reporter.report( self.village_id, "TWB_ATTACK", "Village: %s under attack" % self.game_data['village']['name']) # setup and check if village still exists / is accessible if self.get_config(section="world", parameter="quests_enabled", default=False): if self.get_quests(): self.logger.info( "There where completed quests, re-running function") self.wrapper.reporter.report(self.village_id, "TWB_QUEST", "Completed quest") return self.run(config=config) if not self.builder: self.builder = BuildingManager(wrapper=self.wrapper, village_id=self.village_id) self.builder.resman = self.resman # manage buildings (has to always run because recruit check depends on building levels) build_config = self.get_village_config(self.village_id, parameter="building", default=None) if not build_config: self.logger.warning( "Village %d does not have 'building' config override!" % self.village_id) build_config = self.get_config(section="building", parameter="default", default="purple_predator") new_queue = TemplateManager.get_template(category="builder", template=build_config) if not self.builder.raw_template or self.builder.raw_template != new_queue: self.builder.queue = new_queue self.builder.raw_template = new_queue if not self.get_config(section="world", parameter="knight_enabled", default=False): self.builder.queue = [ x for x in self.builder.queue if "statue" not in x ] self.builder.max_lookahead = self.get_config(section="building", parameter="max_lookahead", default=2) self.builder.max_queue_len = self.get_config( section="building", parameter="max_queued_items", default=2) self.builder.start_update(build=self.get_config( section="building", parameter="manage_buildings", default=True), set_village_name=self.village_set_name) if not self.units: self.units = TroopManager(wrapper=self.wrapper, village_id=self.village_id) self.units.resman = self.resman self.units.max_batch_size = self.get_config(section="units", parameter="batch_size", default=25) # set village templates unit_config = self.get_village_config(self.village_id, parameter="units", default=None) if not unit_config: self.logger.warning( "Village %d does not have 'building' config override!" % self.village_id) unit_config = self.get_config(section="units", parameter="default", default="basic") self.units.template = TemplateManager.get_template( category="troops", template=unit_config, output_json=True) entry = self.units.get_template_action(self.builder.levels) if entry and self.units.wanted != entry["build"]: # update wanted units if template has changed self.logger.info("%s as wanted units for current village" % (str(entry["build"]))) self.units.wanted = entry["build"] if self.units.wanted_levels != {}: self.logger.info("%s as wanted upgrades for current village" % (str(self.units.wanted_levels))) # get total amount of troops in village self.units.update_totals() if self.get_config(section="units", parameter="upgrade", default=False) and self.units.wanted_levels != {}: self.units.attempt_upgrade() if self.get_village_config( self.village_id, parameter="snobs", default=None) and self.builder.levels['snob'] > 0: if not self.snobman: self.snobman = SnobManager(wrapper=self.wrapper, village_id=self.village_id) self.snobman.troop_manager = self.units self.snobman.resman = self.resman self.snobman.wanted = self.get_village_config(self.village_id, parameter="snobs", default=0) self.snobman.building_level = self.builder.get_level("snob") self.snobman.run() # recruitment management if self.get_config(section="units", parameter="recruit", default=False): self.units.can_fix_queue = self.get_config( section="units", parameter="remove_manual_queued", default=False) self.units.randomize_unit_queue = self.get_config( section="units", parameter="randomize_unit_queue", default=True) # prioritize_building: will only recruit when builder has sufficient funds for queue items if self.get_village_config( self.village_id, parameter="prioritize_building", default=False) and not self.resman.can_recruit(): self.logger.info( "Not recruiting because builder has insufficient funds") elif self.get_village_config( self.village_id, parameter="prioritize_snob", default=False ) and self.snobman and self.snobman.can_snob and self.snobman.is_incomplete: self.logger.info( "Not recruiting because snob has insufficient funds") else: # do a build run for every for building in self.units.wanted: if not self.builder.get_level(building): self.logger.debug( "Recruit of %s will be ignored because building is not (yet) available" % building) continue self.units.start_update(building) self.logger.debug("Current resources: %s" % str(self.resman.actual)) self.logger.debug("Requested resources: %s" % str(self.resman.requested)) # attack management if self.units.can_attack: if not self.area: self.area = Map(wrapper=self.wrapper, village_id=self.village_id) self.area.get_map() if self.area.villages: self.units.can_scout = self.get_config( section="farms", parameter="force_scout_if_available", default=True) self.logger.info( "%d villages from map cache, (your location: %s)" % (len(self.area.villages), ':'.join( [str(x) for x in self.area.my_location]))) if not self.attack: self.attack = AttackManager(wrapper=self.wrapper, village_id=self.village_id, troopmanager=self.units, map=self.area) self.attack.repman = self.rep_man self.attack.target_high_points = self.get_config( section="farms", parameter="attack_higher_points", default=False) self.attack.farm_minpoints = self.get_config( section="farms", parameter="min_points", default=24) self.attack.farm_maxpoints = self.get_config( section="farms", parameter="max_points", default=1080) self.attack.farm_default_wait = self.get_config( section="farms", parameter="default_away_time", default=1200) self.attack.farm_high_prio_wait = self.get_config( section="farms", parameter="full_loot_away_time", default=1800) self.attack.farm_low_prio_wait = self.get_config( section="farms", parameter="low_loot_away_time", default=7200) if entry: self.attack.template = entry['farm'] if self.get_config( section="farms", parameter="farm", default=False) and not self.def_man.under_attack: self.attack.extra_farm = self.get_village_config( self.village_id, parameter="additional_farms", default=[]) self.attack.max_farms = self.get_config( section="farms", parameter="max_farms", default=25) self.attack.run() self.units.can_gather = self.get_village_config( self.village_id, parameter="gather_enabled", default=False) if not self.def_man or not self.def_man.under_attack: self.units.gather(selection=self.get_village_config( self.village_id, parameter="gather_selection", default=1), disabled_units=disabled_units) # market management if self.get_config(section="market", parameter="auto_trade", default=False) and self.builder.get_level("market"): self.logger.info("Managing market") self.resman.trade_max_per_hour = self.get_config( section="market", parameter="trade_max_per_hour", default=1) self.resman.trade_max_duration = self.get_config( section="market", parameter="max_trade_duration", default=1) if self.get_config(section="market", parameter="trade_multiplier", default=False): self.resman.trade_bias = self.get_config( section="market", parameter="trade_multiplier_value", default=1.0) self.resman.manage_market(drop_existing=self.get_config( section="market", parameter="auto_remove", default=True)) res = self.wrapper.get_action(village_id=self.village_id, action="overview") self.game_data = Extractor.game_state(res) self.resman.update(self.game_data) if self.get_config(section="world", parameter="trade_for_premium", default=False) and self.get_village_config( self.village_id, parameter="trade_for_premium", default=False): self.resman.do_premium_stuff() self.set_cache_vars() self.logger.info("Village cycle done, returning to overview") self.wrapper.reporter.report(self.village_id, "TWB_POST_RESOURCE", str(self.resman.actual)) self.wrapper.reporter.add_data(self.village_id, data_type="village.resources", data=json.dumps(self.resman.actual)) self.wrapper.reporter.add_data(self.village_id, data_type="village.buildings", data=json.dumps(self.builder.levels)) self.wrapper.reporter.add_data(self.village_id, data_type="village.troops", data=json.dumps( self.units.total_troops)) self.wrapper.reporter.add_data(self.village_id, data_type="village.config", data=json.dumps(vdata))
def run(self, config=None): # setup and check if village still exists / is accessible self.config = config if not self.village_id: data = self.wrapper.get_url("game.php?screen=overview&intro") if data: self.game_data = Extractor.game_state(data) if self.game_data: self.village_id = str(self.game_data['village']['id']) self.logger = logging.getLogger("Village %s" % self.game_data['village']['name']) self.logger.info("Read game state for village") else: data = self.wrapper.get_url("game.php?village=%s&screen=overview" % self.village_id) if data: self.game_data = Extractor.game_state(data) self.logger = logging.getLogger("Village %s" % self.game_data['village']['name']) self.logger.info("Read game state for village") if str(self.village_id) not in self.config['villages']: return False self.twp.run(world=self.config['server']['world']) vdata = self.config['villages'][str(self.village_id)] if not vdata['managed']: return False if not self.game_data: return False # setup modules if not self.resman: self.resman = ResourceManager(wrapper=self.wrapper, village_id=self.village_id) self.resman.update(self.game_data) if not self.rep_man: self.rep_man = ReportManager(wrapper=self.wrapper, village_id=self.village_id) self.rep_man.read(full_run=False) if not self.def_man: self.def_man = DefenceManager(wrapper=self.wrapper, village_id=self.village_id) self.def_man.update(data.text) if self.def_man.under_attack: self.logger.warning("Village under attack!") # setup and check if village still exists / is accessible if self.config['world']['quests_enabled']: if self.get_quests(): self.logger.info("There where completed quests, re-running function") return self.run() if not self.builder: self.builder = BuildingManager(wrapper=self.wrapper, village_id=self.village_id) self.builder.resman = self.resman # manage buildings (has to always run because recruit check depends on building levels) build_config = vdata['building'] if vdata['building'] else self.config['building']['default'] self.builder.queue = TemplateManager.get_template(category="builder", template=build_config) self.builder.max_lookahead = self.config['building']['max_lookahead'] self.builder.max_queue_len = self.config['building']['max_queued_items'] self.builder.start_update(build=self.config['building']['manage_buildings']) if not self.units: self.units = TroopManager(wrapper=self.wrapper, village_id=self.village_id) self.units.max_batch_size = self.config['units']['batch_size'] self.units.resman = self.resman # set village templates unit_config = vdata['units'] if vdata['units'] else self.config['units']['default'] self.units.template = TemplateManager.get_template(category="troops", template=unit_config, output_json=True) entry = self.units.get_template_action(self.builder.levels) if entry and self.units.wanted != entry["build"]: # update wanted units if template has changed self.logger.info("%s as wanted units for current village" % (str(entry["build"]))) self.units.wanted = entry["build"] if entry and 'upgrades' in entry and self.units.wanted_levels != entry['upgrades']: self.logger.info("%s as wanted upgrades for current village" % (str(entry["upgrades"]))) self.units.wanted_levels = entry['upgrades'] # get total amount of troops in village self.units.update_totals() if self.config['units']['upgrade'] and self.units.wanted_levels != {}: self.units.attempt_upgrade(self.units.wanted_levels) if 'snobs' in vdata and self.builder.levels['snob'] > 0: if not self.snobman: self.snobman = SnobManager(wrapper=self.wrapper, village_id=self.village_id) self.snobman.troop_manager = self.units self.snobman.resman = self.resman self.snobman.wanted = vdata['snobs'] self.snobman.building_level = self.builder.levels['snob'] self.snobman.run() if self.config['units']['recruit']: # prioritize_building: will only recruit when builder has sufficient funds for queue items if vdata['prioritize_building'] and not self.resman.can_recruit(): self.logger.info("Not recruiting because builder has insufficient funds") elif vdata['prioritize_snob'] and self.snobman and self.snobman.can_snob and self.snobman.is_incomplete: self.logger.info("Not recruiting because snob has insufficient funds") else: # do a build run for every for building in self.units.wanted: if building not in self.builder.levels or self.builder.levels[building] == 0: self.logger.debug("Recruit of %s will be ignored because building is not available" % building) continue self.units.start_update(building) self.logger.debug("Current resources: %s" % str(self.resman.actual)) self.logger.debug("Requested resources: %s" % str(self.resman.requested)) if self.units.can_attack: if not self.area: self.area = Map(wrapper=self.wrapper, village_id=self.village_id) self.area.get_map() if self.area.villages: self.logger.info("%d villages from map cache, (your location: %s)" % ( len(self.area.villages), ':'.join([str(x) for x in self.area.my_location]))) if not self.attack: self.attack = AttackManager(wrapper=self.wrapper, village_id=self.village_id, troopmanager=self.units, map=self.area) self.attack.repman = self.rep_man if entry: self.attack.template = entry['farm'] if self.config['farms']['farm']: self.attack.extra_farm = vdata['additional_farms'] self.attack.max_farms = self.config['farms']['max_farms'] self.attack.run() if not self.def_man.under_attack: self.units.gather() if self.config['market']['auto_trade'] and "market" in self.builder.levels and self.builder.levels["market"] > 0: self.logger.info("Managing market") self.resman.trade_max_per_hour = self.config['market']['trade_max_per_hour'] self.resman.trade_max_duration = self.config['market']['max_trade_duration'] if self.config['market']['trade_multiplier']: self.resman.trade_bias = self.config['market']['trade_multiplier_value'] self.resman.manage_market(drop_existing=self.config['market']['auto_remove']) self.logger.info("Village cycle done, returning to overview") res = self.wrapper.get_action(village_id=self.village_id, action="overview") self.game_data = Extractor.game_state(res) self.resman.update(self.game_data)