diff --git a/Config.py b/Config.py index 44fb6e3..b84487e 100644 --- a/Config.py +++ b/Config.py @@ -13,6 +13,7 @@ def __init__(self): self.activateAutoExpedition = None self.activateAutoColonization = None self.activatePickingOfficers = None + self.activateAllianceScan = None self.robotRatio = None self.robotStartingLevel = None self.customBuildOrdersDirectoryName = None @@ -27,11 +28,14 @@ def __init__(self): self.minimumSpottingTime = None self.launchExpeditionSeparately = None self.officersPickingOrderFile = None + self.shrib = None + self.shribCookie = None self.watchdogDelay = None self.watchdogExceptionDelay = None self.watchdogEarlyDelay = None self.watchdogWakeDuration = None + @staticmethod def load(): config = Config() config.loadFromFile("defaultConfig.txt") @@ -70,6 +74,8 @@ def loadFromFile(self, filePath): self.activateAutoColonization = value == "True" elif key == "activatePickingOfficers": self.activatePickingOfficers = value == "True" + elif key == "activateAllianceScan": + self.activateAllianceScan = value == "True" elif key == "robotRatio": self.robotRatio = int(value) elif key == "robotStartingLevel": @@ -98,6 +104,10 @@ def loadFromFile(self, filePath): self.officersPickingOrderFile = value elif key == "launchExpeditionSeparately": self.launchExpeditionSeparately = value == "True" + elif key == "shrib": + self.shrib = value + elif key == "shribCookie": + self.shribCookie = value elif key == "watchdogDelay": self.watchdogDelay = int(value) elif key == "watchdogExceptionDelay": @@ -129,6 +139,10 @@ def getError(self): return "Defender ping is activated but the user id to ping isn't configurated !" if self.activatePickingOfficers and self.officersPickingOrderFile is None: return "Officer picking is activated but the order isn't configurated !" + if self.activateAllianceScan and (self.shrib is None or self.shribCookie is None): + return "You need to have a shrib and a cookie !" + if self.activateAllianceScan and not self.activateAutoFleetScan: + return "Alliance scan is activated but not auto fleet scan !" if self.watchdogDelay is None: return "watchdogDelay isn't defined" if self.watchdogDelay <= 0: diff --git a/PickOrder.py b/PickOrder.py index 406685e..fbe2e31 100644 --- a/PickOrder.py +++ b/PickOrder.py @@ -41,6 +41,6 @@ def nextItem(self, idToItem): levelToGet = itemsAtThisStep.get(itemId, 0) + 1 #the level it should be after getting it itemsAtThisStep[itemId] = levelToGet currentItem = idToItem(itemId) - if currentItem is not None and levelToGet > currentItem.level: + if currentItem is None or levelToGet > currentItem.level: return currentItem return None diff --git a/Planet.py b/Planet.py index fc3ebca..61a4512 100644 --- a/Planet.py +++ b/Planet.py @@ -148,7 +148,7 @@ def planHangarsInstead(self, b): return buildingId def upgradableBuildings(self, ressources): - return [bat for bat in self.batimens if bat.upgradable(ressources)] + return [bat for bat in self.batiments if bat.upgradable(ressources)] def scan(self): reqB = Request(self.player.ia.buildingPage + "&cp=" + str(self.id), {}) diff --git a/Player.py b/Player.py index df9ab04..b8dfda3 100644 --- a/Player.py +++ b/Player.py @@ -82,13 +82,16 @@ def extractInfos(self, request=None, darkMatter=False, planets=True): self.planets.append(pl) pl.scan() - def getFleets(self): - techEspionageLevel = self.getTechLevel(106) - fleets = {} + def getFleets(self, page=None): overviewRequest = Request(self.ia.overviewPage, {}) self.ia.execRequest(overviewRequest) - soup = BeautifulSoup(overviewRequest.response.content, "html.parser") - #parse all available buildings + self.getFleetsFromString(overviewRequest.response.content) + + def getFleetsFromString(self, string): + techEspionageLevel = self.getTechLevel(106) + fleets = {} + soup = BeautifulSoup(string, "html.parser") + #parse all fleets fleetsTd = soup.find_all("td", class_="fleets") for fleetTd in fleetsTd[::-1]: #invert the list so the smallest eta overrides the latest etaStr = fleetTd.attrs["data-fleet-end-time"] @@ -134,6 +137,70 @@ def getFleets(self): fleets[fleet.id] = fleet self.fleets = fleets + def getFleetsFromStringFromAlliance(self, string): + techEspionageLevel = self.getTechLevel(106) + fleets = {} + soup = BeautifulSoup(string.replace("\\", ""), "html.parser") + #parse all fleets + table = soup.find_all("table", class_="table519")[0] + trs = table.find_all("tr", recursive=False) + currentPseudo = "" + currentThIndex = -1 + fleetsTd = [] + for tr in trs: + tds = tr.find_all("td", recursive=False) + ths = tr.find_all("th", recursive=False) + if len(ths): + currentThIndex += 1 + if currentThIndex == 1: # if this is an "event" tr + if len(tds) == 1: # this is a pseudo + currentPseudo = tds[0].text + elif len(tds) == 2 and currentPseudo == self.pseudo: # this is a fleet tr and it is concerning us + fleetsTd.append(tds[0]) # yes it's hacky, but should work + for fleetTd in fleetsTd[::-1]: #invert the list so the smallest eta overrides the latest + etaStr = fleetTd.attrs["data-fleet-end-time"] + eta = float(etaStr) + id = fleetTd.attrs["id"].split(etaStr)[1] #The etaStr is appended at the end of the id + secondTd = fleetTd.parent.findAll("td")[1] + fleetsSpans = secondTd.findAll("span", recursive=False) #can be more than 1 if grouped attack + typeList = fleetsSpans[-1].attrs["class"] #the last one has the type + isGoing = (typeList[0] == "flight") + type = typeList[1] + aList = fleetsSpans[-1].findAll("a", class_=type) + ships = {} + for fleetSpan in fleetsSpans: + shipsA = fleetSpan.find("a", class_="tooltip") + if shipsA is not None and techEspionageLevel >= 8: + shipsSoup = BeautifulSoup(shipsA.attrs["data-tooltip-content"], "html.parser") + for tr in shipsSoup.findAll("tr"): + tds = tr.findAll("td") + shipType = Codes.strToId[tds[0].text[:-1]] + shipAmount = int(tds[1].text.replace(".", "")) + ships[shipType] = ships.get(shipType, 0) + shipAmount + aList = [a for a in aList if not "tooltip" in a.attrs["class"]] + originA = aList[0] + targetA = aList[1] + origin = [int(x) for x in originA.text[1:-1].split(":")] + if "Lune" in originA.previous: + origin.append(3) + elif "CDR" in originA.previous: + origin.append(2) + else: + origin.append(1) + target = [int(x) for x in targetA.text[1:-1].split(":")] + if "Lune" in targetA.previous: + target.append(3) + elif "CDR" in targetA.previous: + target.append(2) + else: + target.append(1) + fleet = Fleet(self, id, ships, origin, target, eta, type, isGoing) + ancientFleet = self.fleets.get(fleet.id) + if ancientFleet is not None: + fleet.firstSpotted = ancientFleet.firstSpotted + fleets[fleet.id] = fleet + self.fleets = fleets + def getOwnPlanetByPosition(self, position): for p in self.planets: if p.pos == position: diff --git a/buildOrders/Graviton.txt b/buildOrders/Graviton.txt new file mode 100644 index 0000000..65b8699 --- /dev/null +++ b/buildOrders/Graviton.txt @@ -0,0 +1,3 @@ +#This is a comment +#If all buildings listed here are built, should the ai use it's own build logic ? +useDefaultBuildPlanWhenEmpty=False \ No newline at end of file diff --git a/customBuildOrdersPairingFile.txt b/customBuildOrdersPairingFile.txt index a8318c9..debf611 100644 --- a/customBuildOrdersPairingFile.txt +++ b/customBuildOrdersPairingFile.txt @@ -7,3 +7,4 @@ Main Planet=Mother Colony=Colony Colonie=Colony +Coloniee=Graviton diff --git a/defaultConfig.txt b/defaultConfig.txt index a90a0bf..0a4ddca 100644 --- a/defaultConfig.txt +++ b/defaultConfig.txt @@ -14,6 +14,7 @@ activateAutoEvasion=False activateAutoExpedition=False activateAutoColonization=False activatePickingOfficers=False +activateAllianceScan=False ## AutoBuild Settings # Ratio that will determine the level of your robot factory based on the level of your metal mine. @@ -55,6 +56,11 @@ launchExpeditionSeparately=True ##Getting Officers settings officersPickingOrderFile=officersPickingOrder.txt +##Alliance scan +#Put your url, but don't disclose it to anyone other than your alliance member +shrib=AAAAAAAAAA +shribCookie=AAAAAAAAAA + ## Watchdog # The default delay in seconds between each reinitialization of the AI watchdogDelay=1800 diff --git a/risistar.py b/risistar.py index ec79bfa..d59068a 100644 --- a/risistar.py +++ b/risistar.py @@ -2,6 +2,7 @@ import os import heapq import math +import random import re import sys import threading @@ -51,6 +52,9 @@ class IA: battleRapportPage = "https://" + domain + "/game.php?page=raport&raport=" achievementsPage = "https://" + domain + "/game.php?page=achievements" galaxyPage = "https://" + domain + "/game.php?page=galaxy" + alliancePage = "https://" + domain + "/game.php?page=alliance" + shribPage = "https://shrib.com/zuex/api.php" + shribCookiePage = "https://shrib.com/zuex/api." planetNameParser = re.compile(r'>(.*) \[(.*)\]') buildingNameParser = re.compile(r'\A([^\(]+)(?:\(.* (\d*))?') @@ -60,6 +64,7 @@ class IA: deutProductionParser = re.compile(r'production: ((?:\d|\.)+),\s+valueElem: "current_deuterium"') ressourcesParser = re.compile(r'(\d+) (\w+);') energyParser = re.compile(r'(-?\d+)./.(\d+)') + shribParser = re.compile(r'"text":"(.*)","date_save"', re.DOTALL) def __init__(self, pseudo=None, password=None, lastCookie=None): self.watchdog = None @@ -71,6 +76,7 @@ def __init__(self, pseudo=None, password=None, lastCookie=None): self.tasks = {} self.customBuildOrderDict = {} self._stop = False + self.lastShrib = "" self.initialize() def initialize(self): @@ -80,7 +86,7 @@ def initialize(self): self.session.headers.update({'User-Agent': self.config.userAgent}) if self.lastCookie is not None: self.changeCookie(self.lastCookie) - self.player = Player(self.pseudo, self.password, "Risistar", self) + self.player = Player(self.pseudo, self.password, "1", self) self.player.connexion() self.player.extractInfos(planets=True, darkMatter=True) self.player.scanTechs() @@ -90,6 +96,7 @@ def initialize(self): self.tasks[Task.highPrio ] = [] #evading ennemy fleet self.customBuildOrderDict = {} self._stop = False + self.lastShrib = "" self.addTask(CheckAchievementsTask(time.time(), self.player)) if self.config.activateAutoBuild: self.loadCustomBuildOrders() @@ -105,6 +112,10 @@ def initialize(self): self.addTask(PickOfficerTask(time.time(), self.player)) if self.config.activateAutoColonization: self.addTask(ColonizeTask(time.time(), self.player)) + if self.config.activateAllianceScan: # get the cookie + url = self.shribCookiePage + str(random.random()) + ".svg" + req = Request(url, {}) + req.connect(self.session) def loadCustomBuildOrders(self): buildOrderDirectory = os.path.join(os.path.dirname(os.path.abspath(IA._file)), self.config.customBuildOrdersDirectoryName) @@ -238,6 +249,44 @@ def pingUser(self, message=""): data["content"] = "<@" + self.config.idToPing + "> " + message self.session.post(self.config.webhookUrl, data) + def retrieveShrib(self): + if not "guetsli" in self.session.cookies.keys(): + self.session.cookies["guetsli"] = self.config.shribCookie + payload = { "action": "init", "l": "", "qll": "none", "note": self.config.shrib } + req = Request(self.shribPage, payload) + req.connect(self.session) + string = req.response.content.decode('unicode_escape') + self.lastShrib = self.shribParser.findall(string)[0] + + def storeShrib(self, text): + if not "guetsli" in self.session.cookies.keys(): + self.session.cookies["guetsli"] = self.config.shribCookie + payload = { "ssc": "1", "note": self.config.shrib, "text": text } + req = Request(self.shribPage, payload) + req.connect(self.session) + # print(req) + + def decodeShrib(self): + """ First line pseudo of current scanner + Second line until when it scans + Third line when it will scan next + Fourth line and more : content of the scan""" + lines = self.lastShrib.splitlines() + if len(lines) < 4: + return None, None, None, None + pseudo = lines[0] + stopScan = lines[1] + nextScan = lines[2] + content = "".join(lines[3:]) + return pseudo, float(stopScan), float(nextScan), content + + def encodeShrib(self, pseudo, stopScan, nextScan, content): + """ First line pseudo of current scanner + Second line until when it scans + Third line when it will scan next + Fourth line and more : content of the scan""" + return "%s\n%s\n%s\n%s" % (pseudo, str(stopScan), str(nextScan), content) + def simulateCombat(self, fleet1, fleet2, bonus1=[0, 0, 0], bonus2=[0, 0, 0], ressources=[0, 0, 0]): payload = {} payload["battleinput[0][1][901]"] = ressources[0] diff --git a/tasks/PickTechTask.py b/tasks/PickTechTask.py index 1e0765b..a9da9df 100644 --- a/tasks/PickTechTask.py +++ b/tasks/PickTechTask.py @@ -56,7 +56,7 @@ def execute(self): # We need a bigger lab that isn't upgrading. So just wait for the next building to end. nextBuildingTask = self.player.ia.getNextTaskOfType(PlanningTask) if nextBuildingTask is None: # Shouldn't happen - newTaskTime = time.time() + 30 + newTaskTime = time.time() + 90 else: newTaskTime = nextBuildingTask.t + 1 newTask = PickTechTask(newTaskTime, self.player) diff --git a/tasks/ScanFleetsTask.py b/tasks/ScanFleetsTask.py index 3c9686c..aa673bf 100644 --- a/tasks/ScanFleetsTask.py +++ b/tasks/ScanFleetsTask.py @@ -3,6 +3,7 @@ from tasks.Task import Task from tasks.SendExpeditionTask import SendExpeditionTask from Fleet import Fleet +from Request import Request from UtilitiesFunctions import log class ScanFleetsTask(Task): @@ -15,7 +16,35 @@ def __init__(self, t, player, randomAdditionnalWait=None): def execute(self): ia = self.player.ia #just to make the lines smaller - self.player.getFleets() + if ia.config.activateAllianceScan: + ia.retrieveShrib() + pseudo, stopScan, nextScan, content = ia.decodeShrib() + willLead = False + # if there is currently no one scanning + willLead = willLead or (pseudo is None) + # or it is us + willLead = willLead or (pseudo == self.player.pseudo) + # or it is someone else that wants to stop + willLead = willLead or (pseudo != self.player.pseudo and stopScan < time.time()) + # or it is someone else that slept for too long (crash?) + willLead = willLead or (pseudo != self.player.pseudo and nextScan < time.time()) + if willLead: # We are the scanner => we should scan + if pseudo is None: + log(None, "Shrib empty, we will take the scan lead") + elif pseudo == self.player.pseudo: + log(None, "We are the alliance scanner for %s" % str(stopScan - time.time())) + elif pseudo != self.player.pseudo and stopScan < time.time(): + log(None, "Current scanner wants to stop, we will take the scan lead") + elif pseudo != self.player.pseudo and nextScan < time.time(): + log(None, "Current scanner didn't scan, we will take the scan lead") + allianceRequest = Request(ia.alliancePage, {}) + ia.execRequest(allianceRequest) + self.player.getFleetsFromStringFromAlliance(allianceRequest.content) + else: # there was a scan (probably) + log(None, "We are getting scans from %s" % pseudo) + self.player.getFleetsFromStringFromAlliance(content) + else: + self.player.getFleets() log(None, "Scanned fleets") ennemyFleetInc = False discordMessageToSend = "" @@ -41,7 +70,7 @@ def execute(self): targetPlanet.scanShips() log(None, "Simulating combat") rapport = self.player.ia.simulateCombat(fleet.ships, targetPlanet.ships) #TODO add defense - if rapport.combatResult == 1 or (rapport.combatResult == 0 and not fleet.isDestroy()): + if self.player.getTechLevel(106) < 8 or rapport.combatResult == 1 or (rapport.combatResult == 0 and not fleet.isDestroy()): #if we (the defender) loose, or if it's a tie and it's not a moon destruction mission log(None, "The incoming battle isn't in our favor, initiating evasion") if targetPlanet.ships: #if there are some ships to send @@ -86,4 +115,25 @@ def execute(self): newExpeditionTask = SendExpeditionTask(time.time(), self.player) ia.addTask(newExpeditionTask) newTask = ScanFleetsTask(time.time() + ia.config.minimumTimeBetweenScans, self.player) + if ia.config.activateAllianceScan: + pseudo, stopScan, nextScan, content = ia.decodeShrib() + willTakeLead = False + # if there is currently no one scanning + willTakeLead = willTakeLead or (pseudo is None) + # or it is someone else that wants to stop + willTakeLead = willTakeLead or (pseudo != self.player.pseudo and stopScan < time.time()) + # or it is someone else that slept for too long (crash?) + willTakeLead = willTakeLead or (pseudo != self.player.pseudo and nextScan < time.time()) + if willTakeLead: # we begin to scan for 2 to 6 hours + log(None, "We are taking the lead for scanning for the alliance") + ia.storeShrib(ia.encodeShrib(self.player.pseudo, time.time() + 4*3600*(0.5+random.random()), newTask.t, self.player.lastRequest.content)) + elif (pseudo == self.player.pseudo) : # We are the scanner => we should publish + # even if we wanted to stop, we publish with the expired stopScan time + # that way if there are others that can scan, they will probably take the lead + # and if for some reason they can't, then we are still scanning for them + log(None, "Giving other members the scans") + ia.storeShrib(ia.encodeShrib(self.player.pseudo, stopScan, newTask.t, self.player.lastRequest.content)) + else: + # someone else is scanning, we wait for their scan + newTask = ScanFleetsTask(nextScan + 30, self.player, randomAdditionnalWait=0) ia.addTask(newTask)