def main(): # Config is a wrapper for a socket, and a channel (for now) _config = config.Config(socketutils.connect_to_config(cf), cf.CHANNEL) # This is to determine whether we log information or not if cf.DO_LOG: print("Doing logging.") logging.set_logfile_func(cf.get_log_filename) if not cf.DO_STDOUT: logging.set_print_function(logging.no_print) else: print("Doing printing.") log = logging.log log_all = False # API takes an object with socket and channel members _api = ut.API(_config) _manager = EventManager(_api) # _manager.add_event(EveryLoopEvent(_callable=None, _api=_api, _manager=_manager, runs_till_event=30, # extra_event=functools.partial(_api.send, "There have been 10 loops!"))) can_make_commanders = [ 'dfolder' ] # This could be loaded from a configuration file & saved back to there commanders = ['dfolder'] log('Starting bot. Information:\n\tSocket:', str(_api.socket), '\n\tChannel:', _api.channel) while True: do_events(_api, _manager) full_response = _api.resp() _api.extend_resp_list(full_response) while full_response is not None and len(full_response) > 0: response = full_response.pop(0) did_something = False # We got a response from the server! # First, let's clean it up if we're in full mode. # This means that the response looks really ugly & includes a bunch of unnecessary information at the start. if _api.full_mode: try: full_information = re.search( r'^@badges=[\w/0-9,]*;color=[\w/0-9,]*;display-name=(\w*);.*?user-type=[\w/0-9,]* (.*)', response) log('[FULL RESPONSE]', full_information.strip('\r\n')) except TypeError: full_information = None log("Got erroneous response: ") log(str(response)) if full_information is not None: log('[SENDER]', full_information.group(1)) response = full_information.group(2) log('[RESPONSE]', response.strip('\r\n')) command = re.search( r':(\w*)!\1@\1\.tmi\.twitch\.tv PRIVMSG #\w* : *~(.+)$', response) if command is not None: _command = command.group(2).strip('\r\n ') _caller = command.group(1) _args = None if len(_command.split( ' ', 1)) <= 1 else _command.split(' ', 1)[1] _command = _command.split(' ')[0].lower() if _caller in can_make_commanders: if log_all: log('[2] Executing command: ' + _command + ' with args: ' + str(_args)) if _command == 'conscript': commanders.append(_args.lower()) did_something = True elif _command == 'decommission': commanders.remove(_args.lower()) did_something = True if _caller in commanders: # This should be improved later, but we're going to just check the command here if log_all: log('[1] Executing command: ' + _command + ' with args: ' + str(_args)) if _command == 'stop': _manager.add_event_t( SendMessageEvent, message="Why don't you love me...", after_run=functools.partial( ut.safe_exit, _config, 0)) elif _command == 'print_full_debug': log_all = True log("Enabled full debugging mode.") elif _command == 'no_full_debug': log_all = False elif _command == 'debug': log("Attempting to print debug messages:") log("Manager debug:") log(_manager.dump_debug()) log("Command arguments:") log(str(_args)) log("Current commanders:") log(str(commanders)) # _manager.add_event_t(GetNoticesEvent) elif _command == 'say' and _args is not None: _manager.add_event_t(SendMessageEvent, message=_args) elif _command == 'cap_req' and _args is not None: _manager.add_event_t(SendCapReqEvent, message=_args) elif _command == 'send_exact' or _command == 'say_exact' and _args is not None: _manager.add_event_t(SendExactEvent, message=_args) elif _command == 'enable_full': _manager.add_event( Event(_event=ut.enable_full, _api=_api, _manager=_manager)) elif _command == 'flush_log': log("Flushing log.") logging.flush() elif not did_something: # We didn't set moderator privileges and we don't have command use if log_all: log('The user ' + _caller + ' attempted to use ' + _command + ' with args: ' + str(_args)) _manager.add_event_t( SendMessageEvent, message="Please stop trying to abuse me, " + _caller + ".") # This is to avoid making Twitch angry time.sleep(0.75)
class TapiocaBot(sc2.BotAI): def __init__(self, verbose=False, visual_debug=False): self.verbose = verbose self.visual_debug = visual_debug ipdb.launch_ipdb_on_exception() # Control Stuff self.researched_warpgate = False # Remove me later # Managers and controllers self.worker_controller = WorkerController(bot=self, verbose=self.verbose) self.army_controller = ArmyController(bot=self, verbose=self.verbose) self.scouting_controller = ScoutingController(bot=self, verbose=self.verbose) self.upgrades_controller = UpgradesController(bot=self, verbose=self.verbose) self.robotics_facility_controller = RoboticsFacilitiyController( bot=self, verbose=self.verbose, ) self.gateway_controller = GatewayController( bot=self, verbose=self.verbose, auto_morph_to_warpgate=True) self.building_controller = BuildingController(bot=self, verbose=self.verbose) self.event_manager = EventManager() self.build_order_controller = BuildOrderController( verbose=self.verbose, bot=self) self.coordinator = Coordinator(bot=self, verbose=self.verbose, build_order='three_gate_blink_all_in') self.order_queue = [] def on_start(self): self.army_controller.init() self.event_manager.add_event(self.worker_controller.step, 0.1, jitter=0) self.event_manager.add_event( self.building_controller.update_nexus_list, 2.5) self.event_manager.add_event(self.build_order_controller.step, 0.5) self.event_manager.add_event(self.army_controller.step, 0.1, jitter=0) self.event_manager.add_event(self.coordinator.step, 1) self.coordinator.on_start() async def on_step(self, iteration): if iteration == 0: # Do nothing on the first iteration to avoid # everything being done at the same time if self.verbose: print('\n------------------------\n') print('%8.2f %3d Rise and Shine' % (self.time, self.supply_used)) await self.chat_send( "Cry 'havoc', and let slip the Tapiocas of war!") return events = self.event_manager.get_current_events(self.time) for event in events: await event() await self.debug() await self.execute_order_queue() if self.verbose: sys.stdout.flush() async def do(self, action): self.order_queue.append(action) async def execute_order_queue(self): await self._client.actions(self.order_queue, game_data=self._game_data) self.order_queue = [] async def debug(self): if not self.visual_debug: return # Setup and info font_size = 14 total_units = 0 for unit_type in self.army_controller.units_available_for_attack.keys( ): total_units += self.units(unit_type).idle.amount # Text messages = [ ' n_workers: %3d' % self.units(UnitTypeId.PROBE).amount, ' n_stalkers: %3d' % self.units(UnitTypeId.STALKER).amount, ' militia_size: %3d' % len(self.worker_controller.militia_controller.militia), '', 'ememy_structures_nearby: %3d' % len(self.worker_controller.militia_controller. nearby_enemy_structures_found), ' ememy_workers_nearby: %3d' % len(self.worker_controller.militia_controller. nearby_enemy_workers_found), ' ememy_units_nearby: %3d' % len(self.worker_controller.militia_controller. nearby_enemy_units_found), '', ' know_enemy_structures: %3d' % len(self.known_enemy_structures) ] y = 0 inc = 0.018 for message in messages: self._client.debug_text_screen(message, pos=(0.001, y), size=font_size) y += inc # 3D text debug_army_state = False if debug_army_state: for tag in self.army_controller.soldiers: unit = self.units.find_by_tag(tag) if unit is not None: message = self.army_controller.soldiers[tag]['state'] self._client.debug_text_world(message, pos=unit.position3d, size=font_size) # Spheres debug_army_groups = False if debug_army_groups: leader_tag = self.army_controller.leader for soldier_tag in self.army_controller.soldiers: soldier_unit = self.units.find_by_tag(soldier_tag) if soldier_unit is not None: if soldier_tag == leader_tag: self._client.debug_sphere_out(soldier_unit, r=1, color=(255, 0, 0)) else: self._client.debug_sphere_out(soldier_unit, r=1, color=(0, 0, 255)) # Lines if debug_army_groups: if self.army_controller.army_size() > 0: leader_tag = self.army_controller.leader leader_unit = self.units.find_by_tag(leader_tag) for soldier_tag in self.army_controller.soldiers: if soldier_tag == leader_tag: continue soldier_unit = self.units.find_by_tag(soldier_tag) if soldier_unit is not None: leader_tag = self.army_controller.leader leader_unit = self.units.find_by_tag(leader_tag) if leader_unit is not None: self._client.debug_line_out(leader_unit, soldier_unit, color=(0, 255, 255)) # Attack Lines debug_army_attack = False if debug_army_attack: if self.army_controller.army_size() > 0: for soldier_tag in self.army_controller.soldiers: soldier_unit = self.units.find_by_tag(soldier_tag) if soldier_unit is not None and \ self.army_controller.soldiers[soldier_tag]['state'] == 'attacking' and \ self.army_controller.attack_target is not None: self._client.debug_line_out( soldier_unit, self.army_controller.attack_target, color=(255, 127, 0)) # Sens the debug info to the game await self._client.send_debug() def get_unit_info(self, unit, field="food_required"): assert isinstance(unit, (Unit, UnitTypeId)) if isinstance(unit, Unit): unit = unit._type_data._proto else: unit = self._game_data.units[unit.value]._proto if hasattr(unit, field): return getattr(unit, field) else: return None