def test_socketengine(self): path = os.path.dirname(__file__) adapter = os.path.join(path, "socketadapter.py") eng = aei.get_engine("socket", adapter) self.assertIsInstance(eng, aei.SocketEngine) self._check_engine(eng) eng = aei.get_engine("socket", adapter, "aei") self.assertIsInstance(eng, aei.SocketEngine) self._check_engine(eng) eng = aei.get_engine("2008cc", adapter + " --legacy") self._check_engine(eng)
def run_bot(bot, config, global_options): cmdline = config.get(bot['name'], "cmdline") if config.has_option(bot['name'], "communication_method"): com_method = config.get(bot['name'], "communication_method").lower() else: com_method = "stdio" eng_com = aei.get_engine(com_method, cmdline, "roundrobin.aei") engine = aei.EngineController(eng_com) for option, value in global_options: engine.setoption(option, value) for name, value in config.items(bot['name']): if name.startswith("bot_"): engine.setoption(name[4:], value) return engine
def main(args=None): try: cfg = get_config(args) except SystemExit as exc: return exc.code with open(cfg.position_file, 'r') as pfile: plines = pfile.readlines() try: have_board, start = parse_start(plines, cfg.move_number) except ParseError: print "File %s does not appear to be a board or move list." % ( cfg.position_file, ) return 0 if cfg.strict_checks: print "Enabling full legality checking on moves" if cfg.strict_setup is not None: if cfg.strict_setup: print "Enabling full legality checking on setup" else: print "Disabling full legality checking on setup" eng_com = aei.get_engine(cfg.com_method, cfg.enginecmd, "analyze.aei") try: eng = aei.EngineController(eng_com) except aei.EngineException as exc: print exc.message print "Bot probably did not start. Is the command line correct?" eng_com.cleanup() return 1 try: for option, value in cfg.bot_options: eng.setoption(option, value) eng.newgame() if have_board: pos = start eng.setposition(pos) else: pos = board.Position(board.Color.GOLD, 4, board.BLANK_BOARD) for mnum, move in enumerate(start): move = move[3:] if mnum < 2 and cfg.strict_setup is not None: do_checks = cfg.strict_setup else: do_checks = cfg.strict_checks pos = pos.do_move_str(move, do_checks) eng.makemove(move) print pos.board_to_str() for option, value in cfg.post_options: eng.setoption(option, value) if cfg.search_position: eng.go() while True: try: resp = eng.get_response(10) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % resp.message elif resp.type == "bestmove": print "bestmove: %s" % resp.move break except socket.timeout: if not cfg.search_position: break finally: eng.quit() stop_waiting = time.time() + 20 while time.time() < stop_waiting: try: resp = eng.get_response(1) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % (resp.message) except socket.timeout: try: eng.quit() except IOError: pass if not eng.is_running(): break eng.cleanup() return 0
def main(args=sys.argv): if len(args) < 2: print "usage: analyze <board or movelist file> [move to analyze]" sys.exit() have_board = False pfile = open(args[1], 'r') plines = pfile.readlines() plines = [l.strip() for l in plines] plines = [l for l in plines if l] while plines and not plines[0][0].isalnum(): del plines[0] if not plines: print "File %s does not appear to be a board or move list." % (args[1],) sys.exit() if len(plines) < 2 or plines[1][0] != '+': have_board = False if len(args) > 2: stop_move = args[2] else: stop_move = None move_list = [] while plines and plines[0][0].isdigit(): move = plines.pop(0) if stop_move and move.startswith(stop_move): break move_list.append(move) else: movenum, pos = board.parse_long_pos(plines) have_board = True pfile.close() config = SafeConfigParser() if config.read("analyze.cfg") != ["analyze.cfg"]: print "Could not open 'analyze.cfg'" sys.exit(1) strict_checks = False if config.has_option("global", "strict_checks"): strict_checks = config.getboolean("global", "strict_checks") if strict_checks: print "Enabling full legality checking on moves" strict_setup = None if config.has_option("global", "strict_setup"): strict_setup = config.getboolean("global", "strict_setup") if strict_setup: print "Enabling full legality checking on setup" else: print "Disabling full legality checking on setup" bot_section = config.get("global", "default_engine") if config.has_option(bot_section, "communication_method"): com_method = config.get(bot_section, "communication_method").lower() else: com_method = "stdio" enginecmd = config.get(bot_section, "cmdline") eng_com = aei.get_engine(com_method, enginecmd, log) eng = aei.EngineController(eng_com) for option in config.options(bot_section): if option.startswith("bot_"): value = config.get(bot_section, option) eng.setoption(option[4:], value) eng.newgame() if have_board: eng.setposition(pos) else: pos = board.Position(board.Color.GOLD, 4, board.BLANK_BOARD) for mnum, move in enumerate(move_list): move = move[3:] if mnum < 2 and setup_checks is not None: do_checks = setup_checks else: do_checks = strict_checks pos = pos.do_move_str(move, do_checks) eng.makemove(move) print pos.board_to_str() for option in config.options(bot_section): if option.startswith("post_pos_"): value = config.get(bot_section, option) eng.setoption(option[9:], value) search_position = True if config.has_option("global", "search_position"): sp_str = config.get("global", "search_position") search_position = not (sp_str.lower() in ["false", "0", "no"]) if search_position: eng.go() while True: try: resp = eng.get_response(10) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % resp.message elif resp.type == "bestmove": print "bestmove: %s" % resp.move break except socket.timeout: if not search_position: break eng.quit() stop_waiting = time.time() + 20 while time.time() < stop_waiting: try: resp = eng.get_response(1) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % (resp.message) except socket.timeout: try: eng.quit() except IOError: pass if eng.engine.proc.poll() is not None: break eng.cleanup()
def run_game(options, config): run_dir = config.get("global", "run_dir") if not os.path.exists(run_dir): log.warn( "Run file directory '%s' not found, attempting to create it." % (run_dir)) os.makedirs(run_dir) bot_count = how_many_bots(run_dir) if bot_count >= config.getint("global", "max_bots"): log.info( "Max number of bot limit %d reached, need to wait until some bots finish." % (config.getint("global", "max_bots"))) return if options['bot']: bot_section = options['bot'] else: bot_section = config.get("global", "default_engine") if config.has_option(bot_section, "communication_method"): com_method = config.get(bot_section, "communication_method").lower() else: com_method = "stdio" enginecmd = config.get(bot_section, "cmdline") gameid_or_opponent = options['against'] unknowns_caught = 0 bot_crashes = 0 while True: try: engine_com = aei.get_engine(com_method, enginecmd, logname="gameroom.aei") engine_ctl = aei.EngineController(engine_com) except OSError, exc: log.error("Could not start the engine; exception thrown: %s", exc) return 1 try: for option in config.options(bot_section): if option.startswith("bot_"): value = config.get(bot_section, option) engine_ctl.setoption(option[4:], value) log.info("Setting bot option %s = %s", option[4:], value) resps = engine_ctl.isready() for response in resps: log_response(response, "while sending bot options") try: bot_username = config.get(bot_section, "username") bot_password = config.get(bot_section, "password") except NoOptionError: try: bot_username = config.get("global", "username") bot_password = config.get("global", "password") except NoOptionError: log.error("Could not find username/password in config.") return 1 bot_greeting = config.get(bot_section, "greeting") gameroom = GameRoom(config.get("global", "gameroom_url")) gameroom.login(bot_username, bot_password) side = options['side'] if side == "g": side = "w" elif side == "s": side = "b" table = None if gameid_or_opponent == "": log.info("Starting a new game") if side == "": side = 'b' timecontrol = config.get(bot_section, "timecontrol") rated = config.getboolean(bot_section, "rated") log.info("Will play on side %s, using timecontrol %s" % (side, timecontrol)) table = gameroom.newgame(side, timecontrol, rated) else: # look through my games for correct opponent and side games = gameroom.mygames() for game in games: if (gameid_or_opponent == game['player'].lower() or gameid_or_opponent == game['gid']): if (side == "" or side == game['side'] and not already_playing( run_dir, game['gid'], game['side'])): table = Table(gameroom, game) log.info("Found in progress game") break if table == None: games = gameroom.opengames() for game in games: if (gameid_or_opponent == game['player'].lower() or gameid_or_opponent == game['gid']): if (side == "" or side == game['side'] and not already_playing( run_dir, game['gid'], game['side'])): table = Table(gameroom, game) log.info("Found game to join") break if table == None: log.error("Could not find game against %s with side '%s'", gameid_or_opponent, side) engine_ctl.quit() engine_ctl.cleanup() return 1 # Set the game to play in to current game id in case of a restart gameid_or_opponent = table.gid if options['against'] != "": joinmsg = "Joined game gid=%s side=%s; against %s" % ( table.gid, table.side, options['against']) else: joinmsg = "Created game gid=%s side=%s; waiting for opponent"\ % (table.gid, table.side) log.info(joinmsg) if console is None: print joinmsg # force the std streams to flush so the bot starter script used # on the arimaa.com server can pick up the join message sys.stdout.flush() sys.stderr.flush() if config.has_option(bot_section, "ponder"): table.ponder = config.getboolean(bot_section, "ponder") if table.ponder: log.info("Set pondering on.") else: log.info("Set pondering off.") else: table.ponder = False if config.has_option("global", "min_move_time"): table.min_move_time = config.getint("global", "min_move_time") log.info("Set minimum move time to %d seconds.", table.min_move_time) else: table.min_move_time = 5 if config.has_option("global", "min_time_left"): table.min_timeleft = config.getint("global", "min_time_left") log.info("Setting emergency stop time to %d seconds" % table.min_timeleft) else: table.min_timeleft = 5 except: shutdown_engine(engine_ctl) raise try: try: log.info("Joining game on %s side", table.side) table.reserveseat() table.sitdown() table.updatestate() engine_ctl.setoption("rated", table.state.get('rated', 1)) try: touch_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) time.sleep(1) # Give the server a small break. log.info("Starting play") table.playgame(engine_ctl, bot_greeting, options['onemove']) finally: log.info("Leaving game") remove_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) table.leave() break finally: shutdown_engine(engine_ctl) except EngineCrashException, exc: bot_crashes += 1 if bot_crashes >= 1000: log.error("Bot engine crashed 1000 times, giving up.") return 2 log.error("Bot engine crashed (%s), restarting.", exc.args[0]) time.sleep(1)
pfile.close() config = SafeConfigParser() if config.read("analyze.cfg") != ["analyze.cfg"]: print "Could not open 'analyze.cfg'" sys.exit(1) bot_section = config.get("global", "default_engine") if config.has_option(bot_section, "communication_method"): com_method = config.get(bot_section, "communication_method").lower() else: com_method = "stdio" enginecmd = config.get(bot_section, "cmdline") eng_com = aei.get_engine(com_method, enginecmd, log) eng = aei.EngineController(eng_com) for option in config.options(bot_section): if option.startswith("bot_"): value = config.get(bot_section, option) eng.setoption(option[4:], value) eng.newgame() if have_board: eng.setposition(pos) else: pos = board.Position(board.Color.GOLD, 4, board.BLANK_BOARD) for move in move_list: move = move[3:] pos = pos.do_move_str(move)
def main(args): """Main entry for script. Parses the command line. Reads 'gameroom.cfg' for the configuration. Starts the engine and gives it any initial configuration. Joins or creates the specified game. Then finally controls the engine passing it the game information and sending engine responses back to the server. """ try: options = parseargs(args) except ValueError: print "Command not understood '%s'" % (" ".join(args[1:])) print print __doc__ sys.exit(2) config = SafeConfigParser() try: config.readfp(open("gameroom.cfg", "rU")) except IOError: print "Could not open 'gameroom.cfg'" print "this file must be readable and contain the configuration" print "for connecting to the gameroom." sys.exit(1) aeilog = logging.getLogger("gameroom.aei") if config.has_section("Logging"): logdir = config.get("Logging", "directory") if not os.path.exists(logdir): print "Log directory '%s' not found, attempting to create it." % (logdir) os.makedirs(logdir) logfilename = "%s-%s.log" % (time.strftime("%Y%m%d-%H%M"), str(os.getpid())) logfilename = os.path.join(logdir, logfilename) if config.has_option("Logging", "level"): loglevel = str_loglevel(config.get("Logging", "level")) else: loglevel = logging.WARN logging.basicConfig( level=loglevel, filename=logfilename, datefmt="%Y-%m-%d %H:%M:%S", format="%(asctime)s %(levelname)s:%(name)s:%(message)s", ) if config.has_option("Logging", "console") and config.getboolean("Logging", "console"): global console console = logging.StreamHandler() if config.has_option("Logging", "console_level"): conlevel = str_loglevel(config.get("Logging", "console_level")) else: conlevel = logging.INFO console.setLevel(conlevel) logging.getLogger("").addHandler(console) if config.has_option("Logging", "net_level"): netlevel = str_loglevel(config.get("Logging", "net_level")) netlog.setLevel(netlevel) if config.has_option("Logging", "engine_level"): enginelevel = str_loglevel(config.get("Logging", "engine_level")) enginelog.setLevel(enginelevel) if config.has_option("Logging", "aei_level"): aeilog.setLevel(str_loglevel(config.get("Logging", "aei_level"))) positionlog.setLevel(logging.ERROR) if config.has_option("Logging", "log_position") and config.getboolean("Logging", "log_position"): positionlog.setLevel(logging.INFO) run_dir = config.get("global", "run_dir") if not os.path.exists(run_dir): log.warn("Run file directory '%s' not found, attempting to create it." % (run_dir)) os.makedirs(run_dir) bot_count = how_many_bots(run_dir) if bot_count >= config.getint("global", "max_bots"): log.info( "Max number of bot limit %d reached, need to wait until some bots finish." % (config.getint("global", "max_bots")) ) return bot_section = config.get("global", "default_engine") if config.has_option(bot_section, "communication_method"): com_method = config.get(bot_section, "communication_method").lower() else: com_method = "stdio" enginecmd = config.get(bot_section, "cmdline") gameid_or_opponent = options["against"] unknowns_caught = 0 while True: try: engine_com = aei.get_engine(com_method, enginecmd, log=aeilog) engine_ctl = aei.EngineController(engine_com) except OSError, exc: log.error("Could not start the engine; exception thrown: %s", exc) sys.exit(1) try: for option in config.options(bot_section): if option.startswith("bot_"): value = config.get(bot_section, option) engine_ctl.setoption(option[4:], value) log.info("Setting bot option %s = %s", option[4:], value) engine_ctl.isready() bot_username = config.get(bot_section, "username") bot_password = config.get(bot_section, "password") bot_greeting = config.get(bot_section, "greeting") gameroom = GameRoom(config.get("global", "gameroom_url")) gameroom.login(bot_username, bot_password) side = options["side"] if side == "g": side = "w" elif side == "s": side = "b" table = None if gameid_or_opponent == "": log.info("Starting a new game") if side == "": side = "b" timecontrol = config.get(bot_section, "timecontrol") rated = config.getboolean(bot_section, "rated") log.info("Will play on side %s, using timecontrol %s" % (side, timecontrol)) table = gameroom.newgame(side, timecontrol, rated) else: # look through my games for correct opponent and side games = gameroom.mygames() for game in games: if gameid_or_opponent == game["player"].lower() or gameid_or_opponent == game["gid"]: if ( side == "" or side == game["side"] and not already_playing(run_dir, game["gid"], game["side"]) ): table = Table(gameroom, game) log.info("Found in progress game") break if table == None: games = gameroom.opengames() for game in games: if gameid_or_opponent == game["player"].lower() or gameid_or_opponent == game["gid"]: if ( side == "" or side == game["side"] and not already_playing(run_dir, game["gid"], game["side"]) ): table = Table(gameroom, game) log.info("Found game to join") break if table == None: log.error("Could not find game against %s with side '%s'", gameid_or_opponent, side) engine_ctl.quit() engine_ctl.cleanup() sys.exit(1) # Set the game to play in to current game id in case of a restart gameid_or_opponent = table.gid if options["against"] != "": joinmsg = "Joined game gid=%s side=%s; against %s" % (table.gid, table.side, options["against"]) else: joinmsg = "Created game gid=%s side=%s; waiting for opponent" % (table.gid, table.side) log.info(joinmsg) if console is None: print joinmsg # force the std streams to flush so the bot starter script used # on the arimaa.com server can pick up the join message sys.stdout.flush() sys.stderr.flush() if config.has_option(bot_section, "ponder"): table.ponder = config.getboolean(bot_section, "ponder") if table.ponder: log.info("Set pondering on.") else: log.info("Set pondering off.") else: table.ponder = False if config.has_option("global", "min_move_time"): table.min_move_time = config.getint("global", "min_move_time") log.info("Set minimum move time to %d seconds.", table.min_move_time) else: table.min_move_time = 5 if config.has_option("global", "min_time_left"): table.min_timeleft = config.getint("global", "min_time_left") log.info("Setting emergency stop time to %d seconds" % table.min_timeleft) else: table.min_timeleft = 5 except: shutdown_engine(engine_ctl) raise try: try: log.info("Joining game on %s side", table.side) table.reserveseat() table.sitdown() table.updatestate() engine_ctl.setoption("rated", table.state.get("rated", 1)) try: touch_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) time.sleep(1) # Give the server a small break. log.info("Starting play") table.playgame(engine_ctl, bot_greeting, options["onemove"]) finally: log.info("Leaving game") remove_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) table.leave() break finally: shutdown_engine(engine_ctl) except (KeyboardInterrupt, SystemExit): raise except EngineCrashException, exc: log.error("Bot engine crashed (%s), restarting.", exc.args[0]) time.sleep(1)
def test_stdioengine(self): eng = aei.get_engine("stdio", "simple_engine") self.assertIsInstance(eng, aei.StdioEngine) self._check_engine(eng) eng = aei.get_engine("stdio", "simple_engine", "aei") self._check_engine(eng)
def main(args=None): try: cfg = get_config(args) except SystemExit as exc: return exc.code with open(cfg.position_file, 'r') as pfile: plines = pfile.readlines() try: have_board, start = parse_start(plines, cfg.move_number) except ParseError: print "File %s does not appear to be a board or move list." % ( cfg.position_file, ) return 0 if cfg.strict_checks: print "Enabling full legality checking on moves" if cfg.strict_setup is not None: if cfg.strict_setup: print "Enabling full legality checking on setup" else: print "Disabling full legality checking on setup" eng_com = aei.get_engine(cfg.com_method, cfg.enginecmd, "analyze.aei") try: eng = aei.EngineController(eng_com) except aei.EngineException as exc: print exc.message print "Bot probably did not start. Is the command line correct?" eng_com.cleanup() return 1 try: for option, value in cfg.bot_options: eng.setoption(option, value) eng.newgame() if have_board: pos = start eng.setposition(pos) else: pos = board.Position(board.Color.GOLD, 4, board.BLANK_BOARD) for mnum, full_move in enumerate(start): move = full_move[3:] if mnum < 2 and cfg.strict_setup is not None: do_checks = cfg.strict_setup else: do_checks = cfg.strict_checks try: pos = pos.do_move_str(move, do_checks) except board.IllegalMove as exc: print "Illegal move found \"%s\", %s" % (full_move, exc) return 1 eng.makemove(move) print pos.board_to_str() for option, value in cfg.post_options: eng.setoption(option, value) if cfg.search_position: eng.go() while True: try: resp = eng.get_response(10) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % resp.message elif resp.type == "bestmove": print "bestmove: %s" % resp.move break except socket.timeout: if not cfg.search_position: break finally: eng.quit() stop_waiting = time.time() + 20 while time.time() < stop_waiting: try: resp = eng.get_response(1) if resp.type == "info": print resp.message elif resp.type == "log": print "log: %s" % (resp.message) except socket.timeout: try: eng.quit() except IOError: pass if not eng.is_running(): break eng.cleanup() return 0
def run_game(options, config): run_dir = config.get("global", "run_dir") if not os.path.exists(run_dir): log.warn("Run file directory '%s' not found, attempting to create it." % (run_dir)) os.makedirs(run_dir) bot_count = how_many_bots(run_dir) if bot_count >= config.getint("global", "max_bots"): log.info( "Max number of bot limit %d reached, need to wait until some bots finish." % (config.getint("global", "max_bots"))) return if options['bot']: bot_section = options['bot'] else: bot_section = config.get("global", "default_engine") if config.has_option(bot_section, "communication_method"): com_method = config.get(bot_section, "communication_method").lower() else: com_method = "stdio" enginecmd = config.get(bot_section, "cmdline") gameid_or_opponent = options['against'] unknowns_caught = 0 bot_crashes = 0 while True: try: engine_com = aei.get_engine(com_method, enginecmd, logname="gameroom.aei") engine_ctl = aei.EngineController(engine_com) except OSError, exc: log.error("Could not start the engine; exception thrown: %s", exc) return 1 try: for option in config.options(bot_section): if option.startswith("bot_"): value = config.get(bot_section, option) engine_ctl.setoption(option[4:], value) log.info("Setting bot option %s = %s", option[4:], value) resps = engine_ctl.isready() for response in resps: log_response(response, "while sending bot options") try: bot_username = config.get(bot_section, "username") bot_password = config.get(bot_section, "password") except NoOptionError: try: bot_username = config.get("global", "username") bot_password = config.get("global", "password") except NoOptionError: log.error("Could not find username/password in config.") return 1 bot_greeting = config.get(bot_section, "greeting") gameroom = GameRoom(config.get("global", "gameroom_url")) gameroom.login(bot_username, bot_password) side = options['side'] if side == "g": side = "w" elif side == "s": side = "b" table = None if gameid_or_opponent == "": log.info("Starting a new game") if side == "": side = 'b' timecontrol = config.get(bot_section, "timecontrol") rated = config.getboolean(bot_section, "rated") log.info("Will play on side %s, using timecontrol %s" % (side, timecontrol)) table = gameroom.newgame(side, timecontrol, rated) else: # look through my games for correct opponent and side games = gameroom.mygames() for game in games: if (gameid_or_opponent == game['player'].lower() or gameid_or_opponent == game['gid']): if (side == "" or side == game['side'] and not already_playing( run_dir, game['gid'], game['side'])): table = Table(gameroom, game) log.info("Found in progress game") break if table == None: games = gameroom.opengames() for game in games: if (gameid_or_opponent == game['player'].lower() or gameid_or_opponent == game['gid']): if (side == "" or side == game['side'] and not already_playing( run_dir, game['gid'], game['side'])): table = Table(gameroom, game) log.info("Found game to join") break if table == None: log.error("Could not find game against %s with side '%s'", gameid_or_opponent, side) engine_ctl.quit() engine_ctl.cleanup() return 1 # Set the game to play in to current game id in case of a restart gameid_or_opponent = table.gid if options['against'] != "": joinmsg = "Joined game gid=%s side=%s; against %s" % ( table.gid, table.side, options['against'] ) else: joinmsg = "Created game gid=%s side=%s; waiting for opponent"\ % (table.gid, table.side) log.info(joinmsg) if console is None: print joinmsg # force the std streams to flush so the bot starter script used # on the arimaa.com server can pick up the join message sys.stdout.flush() sys.stderr.flush() if config.has_option(bot_section, "ponder"): table.ponder = config.getboolean(bot_section, "ponder") if table.ponder: log.info("Set pondering on.") else: log.info("Set pondering off.") else: table.ponder = False if config.has_option("global", "min_move_time"): table.min_move_time = config.getint("global", "min_move_time") log.info("Set minimum move time to %d seconds.", table.min_move_time) else: table.min_move_time = 5 if config.has_option("global", "min_time_left"): table.min_timeleft = config.getint("global", "min_time_left") log.info("Setting emergency stop time to %d seconds" % table.min_timeleft) else: table.min_timeleft = 5 except: shutdown_engine(engine_ctl) raise try: try: log.info("Joining game on %s side", table.side) table.reserveseat() table.sitdown() table.updatestate() engine_ctl.setoption("rated", table.state.get('rated', 1)) try: touch_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) time.sleep(1) # Give the server a small break. log.info("Starting play") table.playgame(engine_ctl, bot_greeting, options['onemove']) finally: log.info("Leaving game") remove_run_file(run_dir, "%s%s.bot" % (table.gid, table.side)) table.leave() break finally: shutdown_engine(engine_ctl) except EngineCrashException, exc: bot_crashes += 1 if bot_crashes >= 1000: log.error("Bot engine crashed 1000 times, giving up.") return 2 log.error("Bot engine crashed (%s), restarting.", exc.args[0]) time.sleep(1)