class TestGraphUtils(TestCase): def setUp(self): controller = Controller() files = ['assets/data/map_data.json', 'assets/data/cmi_hub.json'] self.g = Graph() controller.build_graph_from_files(self.g, files) self.utils = GraphUtils() def testMerge(self): print(self.utils.smallest_city(self.g)) assert self.utils.smallest_city(self.g)[0] == u'CMI'
def escapeGraph(self, emptyContainer, graph, maxDiff): if not self.escapeRando: return possibleTargets, dst, path = self.getPossibleEscapeTargets(emptyContainer, graph, maxDiff) # update graph with escape transition graph.addTransition(escapeSource, dst) # get timer value self.escapeTimer(graph, path) self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer'])) # animals GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst)
def updateLocationsClass(self, split): if split != 'Full' and split != 'Scavenger': startAP = getAccessPoint(self.graphSettings.startAP) possibleMajLocs, preserveMajLocs, nMaj, nChozo = Logic.LocationsHelper.getStartMajors( startAP.Name) if split == 'Major': n = nMaj elif split == 'Chozo': n = nChozo GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n)
def setUp(self): f = open('assets/data/map_data.json', 'r') decoded = json.loads(f.read()) self.g = graph.Graph() self.g.build_nodes(decoded['metros']) self.g.build_edges(decoded['routes']) self.utils = GraphUtils() f = open('assets/data/test1_map_data.json', 'r') decoded = json.loads(f.read()) self.gSmall = graph.Graph() self.gSmall.build_nodes(decoded['metros']) self.gSmall.build_edges(decoded['routes'])
def testSave(self): self.utils.save_to_disk(self.g) f2 = open('assets/data/map_data_written.json', 'r') decoded = json.loads(f2.read()) self.g2 = graph.Graph() self.g2.build_nodes(decoded['metros']) self.g2.build_edges(decoded['routes']) self.utils = GraphUtils() assert (self.utils.longest_flight(self.g2) == ('SYD', 'LAX', 12051)) assert (self.utils.shortest_flight(self.g2) == ('NYC', 'WAS', 334)) assert (self.utils.average_distance(self.g2) == 2300) assert self.utils.biggest_city(self.g2) == ('TYO', 34000000) assert self.utils.smallest_city(self.g2) == ('ESS', 589900)
def loadTransitions(self, tourian): # return the transitions rooms = GraphUtils.getRooms() bossTransitions = {} areaTransitions = {} for accessPoint in Logic.accessPoints: if accessPoint.isInternal() == True: continue key = self.getTransition(accessPoint.ExitInfo['DoorPtr']) if key not in rooms: # can happen with race mode seeds continue destAP = rooms[key] if accessPoint.Boss == True or destAP.Boss == True: bossTransitions[accessPoint.Name] = destAP.Name else: areaTransitions[accessPoint.Name] = destAP.Name def removeBiTrans(transitions): # remove bidirectionnal transitions # can't del keys in a dict while iterating it transitionsCopy = copy.copy(transitions) for src in transitionsCopy: if src in transitions: dest = transitions[src] if dest in transitions: if transitions[dest] == src: del transitions[dest] return [(t, transitions[t]) for t in transitions] # get escape transition if tourian == 'Disabled': escapeSrcAP = getAccessPoint('Climb Bottom Left') else: escapeSrcAP = getAccessPoint('Tourian Escape Room 4 Top Right') key = self.getTransition(escapeSrcAP.ExitInfo['DoorPtr']) # may not be set in plandomizer if key in rooms: escapeDstAP = rooms[key] escapeTransition = [(escapeSrcAP.Name, escapeDstAP.Name)] else: escapeTransition = [] areaTransitions = removeBiTrans(areaTransitions) bossTransitions = removeBiTrans(bossTransitions) return (areaTransitions, bossTransitions, escapeTransition, GraphUtils.hasMixedTransitions(areaTransitions, bossTransitions))
def createGraph(self): transitions = self.graphSettings.plandoRandoTransitions if transitions is None: transitions = [] if self.minimizerN is not None: transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN) else: if not self.bossRando: transitions += vanillaBossesTransitions else: transitions += GraphUtils.createBossesTransitions() if not self.areaRando: transitions += vanillaTransitions else: transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando) return AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
def setDefaultPatches(startLocation): # called by the isolver in seedless mode. # activate only layout patch (the most common one), red tower blue doors and the startLocation's patches. from graph.graph_utils import GraphUtils RomPatches.ActivePatches[0] = [ RomPatches.RedTowerBlueDoors ] + RomPatches.TotalLayout + GraphUtils.getGraphPatches(startLocation)
def createItemLocContainer(self): self.getForbidden() self.log.debug("LAST CHECKPOOL") if not self.checkPool(): self.log.debug("createItemLocContainer: checkPool fail") return None self.checkDoorBeams() self.container = ItemLocContainer(self.sm, self.getItemPool(), self.locations) if self.restrictions.isLateMorph(): self.restrictions.lateMorphInit(self.startAP, self.container, self.services) isStdStart = GraphUtils.isStandardStart(self.startAP) # ensure we have an area layout that can put morph outside start area # TODO::allow for custom start which doesn't require morph early if self.graphSettings.areaRando and isStdStart and not self.restrictions.suitsRestrictions and self.restrictions.lateMorphForbiddenArea is None: self.container = None self.log.debug("createItemLocContainer: checkLateMorph fail") return None # checkStart needs the container if not self.checkStart(): self.container = None self.log.debug("createItemLocContainer: checkStart fail") return None # add placement restriction helpers for random fill if self.settings.progSpeed == 'speedrun': restrictionDict = self.getSpeedrunRestrictionsDict() self.restrictions.addPlacementRestrictions(restrictionDict) self.settings.collectAlreadyPlacedItemLocations(self.container) self.fillRestrictedLocations() self.settings.updateSuperFun(self.superFun) return self.container
def getSpeedrunRestrictionsDict(self): itemTypes = { item.Type for item in self.container.itemPool if item.Category not in Restrictions.NoCheckCat } allAreas = {loc.GraphArea for loc in self.locations} items = [ self.container.getNextItemInPool(itemType) for itemType in itemTypes ] restrictionDict = {} for area in allAreas: restrictionDict[area] = {} for itemType in itemTypes: restrictionDict[area][itemType] = set() for item in items: itemType = item.Type poss = self.services.possibleLocations(item, self.startAP, self.container) for loc in poss: restrictionDict[loc.GraphArea][itemType].add(loc.Name) if self.restrictions.isEarlyMorph() and GraphUtils.isStandardStart( self.startAP): morphLocs = ['Morphing Ball'] if self.restrictions.split in ['Full', 'Major']: dboost = self.sm.knowsCeilingDBoost() if dboost.bool == True and dboost.difficulty <= self.settings.maxDiff: morphLocs.append('Energy Tank, Brinstar Ceiling') for area, locDict in restrictionDict.items(): if area == 'Crateria': locDict['Morph'] = set(morphLocs) else: locDict['Morph'] = set() return restrictionDict
def applyStartAP(self, apName, plms, doors): ap = getAccessPoint(apName) if not GraphUtils.isStandardStart(apName): # not Ceres or Landing Site, so Zebes will be awake plms.append('Morph_Zebes_Awake') (w0, w1) = getWord(ap.Start['spawn']) if 'doors' in ap.Start: doors += ap.Start['doors'] doors.append(0x0) addr = 0x10F200 patch = [w0, w1] + doors assert (addr + len(patch)) < 0x10F210, "Stopped before new_game overwrite" patchDict = { 'StartAP': { addr: patch }, } self.applyIPSPatch('StartAP', patchDict) # handle custom saves if 'save' in ap.Start: self.applyIPSPatch(ap.Start['save']) plms.append(ap.Start['save']) # handle optional rom patches if 'rom_patches' in ap.Start: for patch in ap.Start['rom_patches']: self.applyIPSPatch(patch)
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity): super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate) self.choice = ItemThenLocChoice(restrictions) self.stdStart = GraphUtils.isStandardStart(self.startAP)
def run(self): # init session if self.session.plando is None: self.session.plando = { "state": {}, "preset": "regular", "seed": None, "startLocation": "Landing Site", # rando params "rando": {}, # set to False in plando.html "firstTime": True } # load presets list (stdPresets, tourPresets, comPresets) = loadPresetsList(self.cache) # access points vanillaAPs = [] for (src, dest) in vanillaTransitions: vanillaAPs += [transition2isolver(src), transition2isolver(dest)] vanillaBossesAPs = [] for (src, dest) in vanillaBossesTransitions: vanillaBossesAPs += [ transition2isolver(src), transition2isolver(dest) ] escapeAPs = [] for (src, dest) in vanillaEscapeTransitions: escapeAPs += [transition2isolver(src), transition2isolver(dest)] # generate list of addresses to read in the ROM addresses = getAddressesToRead(plando=True) startAPs = GraphUtils.getStartAccessPointNamesCategory() startAPs = [ OPTGROUP(_label="Standard", *startAPs["regular"]), OPTGROUP(_label="Custom", *startAPs["custom"]), OPTGROUP(_label="Custom (Area rando only)", *startAPs["area"]) ] return dict(stdPresets=stdPresets, tourPresets=tourPresets, comPresets=comPresets, vanillaAPs=vanillaAPs, vanillaBossesAPs=vanillaBossesAPs, escapeAPs=escapeAPs, curSession=self.session.plando, addresses=addresses, startAPs=startAPs, version=displayedVersion)
def loadTransitions(self): # return the transitions rooms = GraphUtils.getRooms() bossTransitions = {} areaTransitions = {} for accessPoint in Logic.accessPoints: if accessPoint.isInternal() == True: continue key = self.getTransition(accessPoint.ExitInfo['DoorPtr']) destAP = rooms[key] if accessPoint.Boss == True or destAP.Boss == True: bossTransitions[accessPoint.Name] = destAP.Name else: areaTransitions[accessPoint.Name] = destAP.Name def removeBiTrans(transitions): # remove bidirectionnal transitions # can't del keys in a dict while iterating it transitionsCopy = copy.copy(transitions) for src in transitionsCopy: if src in transitions: dest = transitions[src] if dest in transitions: if transitions[dest] == src: del transitions[dest] return [(t, transitions[t]) for t in transitions] # get escape transition escapeSrcAP = getAccessPoint('Tourian Escape Room 4 Top Right') key = self.getTransition(escapeSrcAP.ExitInfo['DoorPtr']) escapeDstAP = rooms[key] escapeTransition = [(escapeSrcAP.Name, escapeDstAP.Name)] areaTransitions = removeBiTrans(areaTransitions) bossTransitions = removeBiTrans(bossTransitions) return (areaTransitions, bossTransitions, escapeTransition, GraphUtils.hasMixedTransitions(areaTransitions, bossTransitions))
def escapeGraph(self, container, graph, maxDiff, escapeTrigger): if not self.escapeRando: return True emptyContainer = copy.copy(container) emptyContainer.resetCollected(reassignItemLocs=True) dst = None if escapeTrigger is None: possibleTargets, dst, path = self.getPossibleEscapeTargets(emptyContainer, graph, maxDiff) # update graph with escape transition graph.addTransition(escapeSource, dst) paths = [path] else: possibleTargets, paths = self.escapeTrigger(emptyContainer, graph, maxDiff, escapeTrigger) if paths is None: return False # get timer value self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None) self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer'])) # animals GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst) return True
def testSave(self): self.utils.save_to_disk(self.g) f2 = open('assets/data/map_data_written.json', 'r') decoded = json.loads(f2.read()) self.g2 = graph.Graph() self.g2.build_nodes(decoded['metros']) self.g2.build_edges(decoded['routes']) self.utils = GraphUtils() assert (self.utils.longest_flight(self.g2) == ('SYD', 'LAX', 12051)) assert (self.utils.shortest_flight(self.g2) == ('NYC', 'WAS', 334)) assert (self.utils.average_distance(self.g2) == 2300) assert self.utils.biggest_city(self.g2) == ('TYO', 34000000) assert self.utils.smallest_city(self.g2) == ( 'ESS', 589900)
def getDefaultMultiValues(): from graph.graph_utils import GraphUtils defaultMultiValues = { 'startLocation': GraphUtils.getStartAccessPointNames(), 'majorsSplit': ['Full', 'FullWithHUD', 'Major', 'Chozo', 'Scavenger'], 'progressionSpeed': [ 'slowest', 'slow', 'medium', 'fast', 'fastest', 'basic', 'VARIAble', 'speedrun' ], 'progressionDifficulty': ['easier', 'normal', 'harder'], 'morphPlacement': ['early', 'late', 'normal'], 'energyQty': ['ultra sparse', 'sparse', 'medium', 'vanilla'], 'gravityBehaviour': ['Vanilla', 'Balanced', 'Progressive'] } return defaultMultiValues
def createItemLocContainer(self, endDate, vcr=None): self.getForbidden() self.log.debug("LAST CHECKPOOL") if not self.checkPool(): self.log.debug("createItemLocContainer: last checkPool fail") return None # reset restricted in locs from previous attempt for loc in self.locations: loc.restricted = False for loc in self.restrictedLocs: self.log.debug( "createItemLocContainer: loc is restricted: {}".format( loc.Name)) loc.restricted = True self.checkDoorBeams() self.container = ItemLocContainer(self.sm, self.getItemPool(), self.locations) if self.restrictions.isLateMorph(): self.restrictions.lateMorphInit(self.startAP, self.container, self.services) isStdStart = GraphUtils.isStandardStart(self.startAP) # ensure we have an area layout that can put morph outside start area # TODO::allow for custom start which doesn't require morph early if self.graphSettings.areaRando and isStdStart and not self.restrictions.suitsRestrictions and self.restrictions.lateMorphForbiddenArea is None: self.container = None self.log.debug("createItemLocContainer: checkLateMorph fail") return None # checkStart needs the container if not self.checkStart(): self.container = None self.log.debug("createItemLocContainer: checkStart fail") return None self.settings.collectAlreadyPlacedItemLocations(self.container) self.fillRestrictedLocations() if self.restrictions.split == 'Scavenger': # initScavenger will actually fill up the container using random fill, # the scavenger "filler" will focus on determining mandatory route self.container = self.initScavenger(endDate, vcr) if self.container is None: self.log.debug("createItemLocContainer: initScavenger fail") return None elif self.settings.progSpeed == 'speedrun': # add placement restriction helpers for random fill self.restrictions.setPlacementRestrictions( self.getRestrictionsDict()) self.settings.updateSuperFun(self.superFun) return self.container
def setUp(self): controller = Controller() files = ['assets/data/map_data.json', 'assets/data/cmi_hub.json'] self.g = Graph() controller.build_graph_from_files(self.g, files) self.utils = GraphUtils()
def savePlando(self, lock, escapeTimer): # store filled locations addresses in the ROM for next creating session from rando.Items import ItemManager locsItems = {} itemLocs = [] for loc in self.visitedLocations: locsItems[loc.Name] = loc.itemName for loc in self.locations: if loc.Name in locsItems: itemLocs.append( ItemLocation(ItemManager.getItem(loc.itemName), loc)) else: # put nothing items in unused locations itemLocs.append( ItemLocation(ItemManager.getItem("Nothing"), loc)) # patch the ROM if lock == True: import random magic = random.randint(1, 0xffff) else: magic = None romPatcher = RomPatcher(magic=magic, plando=True) patches = [ 'credits_varia.ips', 'tracking.ips', "Escape_Animals_Disable" ] if DoorsManager.isRandom(): patches += RomPatcher.IPSPatches['DoorsColors'] patches.append("Enable_Backup_Saves") if magic != None: patches.insert(0, 'race_mode.ips') patches.append('race_mode_credits.ips') romPatcher.addIPSPatches(patches) plms = [] if self.areaRando == True or self.bossRando == True or self.escapeRando == True: doors = GraphUtils.getDoorConnections( AccessGraph(Logic.accessPoints, self.fillGraph()), self.areaRando, self.bossRando, self.escapeRando, False) romPatcher.writeDoorConnections(doors) if magic == None: doorsPtrs = GraphUtils.getAps2DoorsPtrs() romPatcher.writePlandoTransitions( self.curGraphTransitions, doorsPtrs, len(vanillaBossesTransitions) + len(vanillaTransitions)) if self.escapeRando == True and escapeTimer != None: # convert from '03:00' to number of seconds escapeTimer = int(escapeTimer[0:2]) * 60 + int( escapeTimer[3:5]) romPatcher.applyEscapeAttributes( { 'Timer': escapeTimer, 'Animals': None }, plms) # write plm table & random doors romPatcher.writePlmTable(plms, self.areaRando, self.bossRando, self.startAP) romPatcher.writeItemsLocs(itemLocs) romPatcher.writeItemsNumber() romPatcher.writeSpoiler(itemLocs) # plando is considered Full romPatcher.writeSplitLocs(itemLocs, "Full") romPatcher.writeMajorsSplit("Full") class FakeRandoSettings: def __init__(self): self.qty = {'energy': 'plando'} self.progSpeed = 'plando' self.progDiff = 'plando' self.restrictions = {'Suits': False, 'Morph': 'plando'} self.superFun = {} randoSettings = FakeRandoSettings() romPatcher.writeRandoSettings(randoSettings, itemLocs) if magic != None: romPatcher.writeMagic() else: romPatcher.writePlandoAddresses(self.visitedLocations) romPatcher.commitIPS() romPatcher.end() data = romPatcher.romFile.data preset = os.path.splitext(os.path.basename(self.presetFileName))[0] seedCode = 'FX' if self.bossRando == True: seedCode = 'B' + seedCode if DoorsManager.isRandom(): seedCode = 'D' + seedCode if self.areaRando == True: seedCode = 'A' + seedCode from time import gmtime, strftime fileName = 'VARIA_Plandomizer_{}{}_{}.sfc'.format( seedCode, strftime("%Y%m%d%H%M%S", gmtime()), preset) data["fileName"] = fileName # error msg in json to be displayed by the web site data["errorMsg"] = "" with open(self.outputFileName, 'w') as jsonFile: json.dump(data, jsonFile)
def loadPlandoTransitions(self): # add escape transition transitionsAddr = self.romLoader.getPlandoTransitions( len(vanillaBossesTransitions) + len(vanillaTransitions) + 1) return GraphUtils.getTransitions(transitionsAddr)
def __init__(self, graphSettings, areaGraph, restrictions, container, endDate): super(FillerProgSpeed, self).__init__(graphSettings.startAP, areaGraph, restrictions, container, endDate) distanceProp = 'GraphArea' if graphSettings.areaRando else 'Area' self.stdStart = GraphUtils.isStandardStart(self.startAP) self.progSpeedParams = ProgSpeedParameters(self.restrictions, len(container.unusedLocations)) self.choice = ItemThenLocChoiceProgSpeed(restrictions, self.progSpeedParams, distanceProp, self.services)
def run(self): # init session if self.session.tracker is None: self.session.tracker = { "state": {}, "preset": "regular", "seed": None, "startLocation": "Landing Site", # set to False in tracker.html "firstTime": True } # load presets list (stdPresets, tourPresets, comPresets) = loadPresetsList(self.cache) # access points vanillaAPs = [] for (src, dest) in vanillaTransitions: vanillaAPs += [transition2isolver(src), transition2isolver(dest)] vanillaBossesAPs = [] for (src, dest) in vanillaBossesTransitions: vanillaBossesAPs += [ transition2isolver(src), transition2isolver(dest) ] escapeAPs = [] for (src, dest) in vanillaEscapeTransitions: escapeAPs += [transition2isolver(src), transition2isolver(dest)] # generate list of addresses to read in the ROM addresses = getAddressesToRead() startAPs = GraphUtils.getStartAccessPointNamesCategory() startAPs = [ OPTGROUP(_label="Standard", *startAPs["regular"]), OPTGROUP(_label="Custom", *startAPs["custom"]), OPTGROUP(_label="Custom (Area rando only)", *startAPs["area"]) ] # get ap -> grapharea for auto tracker apsGraphArea = { locName4isolver(ap.Name): ap.GraphArea for ap in accessPoints } return dict(stdPresets=stdPresets, tourPresets=tourPresets, comPresets=comPresets, vanillaAPs=vanillaAPs, vanillaBossesAPs=vanillaBossesAPs, escapeAPs=escapeAPs, curSession=self.session.tracker, addresses=addresses, startAPs=startAPs, areaAccessPoints=InteractiveSolver.areaAccessPoints, bossAccessPoints=InteractiveSolver.bossAccessPoints, escapeAccessPoints=InteractiveSolver.escapeAccessPoints, nothingScreens=InteractiveSolver.nothingScreens, doorsScreen=InteractiveSolver.doorsScreen, bossBitMasks=InteractiveSolver.bossBitMasks, apsGraphArea=apsGraphArea)
class TestGraphUtils(TestCase): def setUp(self): f = open('assets/data/map_data.json', 'r') decoded = json.loads(f.read()) self.g = graph.Graph() self.g.build_nodes(decoded['metros']) self.g.build_edges(decoded['routes']) self.utils = GraphUtils() f = open('assets/data/test1_map_data.json', 'r') decoded = json.loads(f.read()) self.gSmall = graph.Graph() self.gSmall.build_nodes(decoded['metros']) self.gSmall.build_edges(decoded['routes']) def testSave(self): self.utils.save_to_disk(self.g) f2 = open('assets/data/map_data_written.json', 'r') decoded = json.loads(f2.read()) self.g2 = graph.Graph() self.g2.build_nodes(decoded['metros']) self.g2.build_edges(decoded['routes']) self.utils = GraphUtils() assert (self.utils.longest_flight(self.g2) == ('SYD', 'LAX', 12051)) assert (self.utils.shortest_flight(self.g2) == ('NYC', 'WAS', 334)) assert (self.utils.average_distance(self.g2) == 2300) assert self.utils.biggest_city(self.g2) == ('TYO', 34000000) assert self.utils.smallest_city(self.g2) == ('ESS', 589900) def testLongestFlight(self): assert (self.utils.longest_flight(self.g) == ('SYD', 'LAX', 12051)) assert (self.utils.longest_flight(self.gSmall) == ('MEX', 'LIM', 24530)) def testShortestFlight(self): assert (self.utils.shortest_flight(self.g) == ('NYC', 'WAS', 334)) assert (self.utils.shortest_flight(self.gSmall) == ('SCL', 'LIM', 2453)) def testMapUrl(self): assert ( self.utils.get_map_string(self.g) == 'PAR-ALG,+PAR-MAD,+PAR-LON,+PAR-ESS,+PAR-MIL,+MIL-PAR,+MIL-ESS,+MIL-IST,+MIA-MEX,+MIA-BOG,+MIA-ATL,+MIA-WAS,+LIM-SCL,+LIM-MEX,+LIM-BOG,+ATL-CHI,+ATL-WAS,+ATL-MIA,+PEK-SHA,+PEK-ICN,+LON-MAD,+LON-NYC,+LON-ESS,+LON-PAR,+IST-CAI,+IST-ALG,+IST-MIL,+IST-LED,+IST-MOW,+IST-BGW,+LOS-SAO,+LOS-KRT,+LOS-FIH,+CAI-KRT,+CAI-ALG,+CAI-IST,+CAI-BGW,+CAI-RUH,+DEL-THR,+DEL-KHI,+DEL-CCU,+DEL-MAA,+DEL-BOM,+BOM-KHI,+BOM-DEL,+BOM-MAA,+BGW-CAI,+BGW-IST,+BGW-THR,+BGW-KHI,+BGW-RUH,+NYC-MAD,+NYC-LON,+NYC-WAS,+NYC-YYZ,+BOG-LIM,+BOG-MEX,+BOG-MIA,+BOG-SAO,+BOG-BUE,+SCL-LIM,+SAO-BOG,+SAO-BUE,+SAO-MAD,+SAO-LOS,+KHI-BGW,+KHI-THR,+KHI-RUH,+KHI-DEL,+KHI-BOM,+JKT-MAA,+JKT-BKK,+JKT-SGN,+JKT-SYD,+BKK-MAA,+BKK-CCU,+BKK-HKG,+BKK-SGN,+BKK-JKT,+CCU-DEL,+CCU-MAA,+CCU-HKG,+CCU-BKK,+MNL-HKG,+MNL-TPE,+MNL-SFO,+MNL-SYD,+MNL-SGN,+SGN-BKK,+SGN-HKG,+SGN-MNL,+SGN-JKT,+OSA-TYO,+OSA-TPE,+HKG-CCU,+HKG-BKK,+HKG-SHA,+HKG-TPE,+HKG-MNL,+HKG-SGN,+BUE-BOG,+BUE-SAO,+TPE-HKG,+TPE-SHA,+TPE-OSA,+TPE-MNL,+ESS-LON,+ESS-PAR,+ESS-MIL,+ESS-LED,+ICN-SHA,+ICN-PEK,+ICN-TYO,+JNB-FIH,+JNB-KRT,+CHI-MEX,+CHI-LAX,+CHI-SFO,+CHI-YYZ,+CHI-ATL,+THR-MOW,+THR-BGW,+THR-DEL,+THR-KHI,+THR-RUH,+KRT-LOS,+KRT-FIH,+KRT-JNB,+KRT-CAI,+SHA-HKG,+SHA-PEK,+SHA-ICN,+SHA-TYO,+SHA-TPE,+FIH-LOS,+FIH-KRT,+FIH-JNB,+WAS-ATL,+WAS-MIA,+WAS-YYZ,+WAS-NYC,+RUH-CAI,+RUH-BGW,+RUH-THR,+RUH-KHI,+TYO-SHA,+TYO-ICN,+TYO-SFO,+TYO-OSA,+LED-ESS,+LED-MOW,+LED-IST,+SYD-MNL,+SYD-JKT,+SYD-LAX,+ALG-CAI,+ALG-MAD,+ALG-PAR,+ALG-IST,+MOW-LED,+MOW-THR,+MOW-IST,+YYZ-CHI,+YYZ-WAS,+YYZ-NYC,+MAA-DEL,+MAA-BOM,+MAA-CCU,+MAA-BKK,+MAA-JKT,+SFO-TYO,+SFO-MNL,+SFO-LAX,+SFO-CHI,+LAX-MEX,+LAX-SYD,+LAX-SFO,+LAX-CHI,+MAD-SAO,+MAD-ALG,+MAD-NYC,+MAD-LON,+MAD-PAR,+MEX-LIM,+MEX-LAX,+MEX-CHI,+MEX-MIA,+MEX-BOG' ) def testAverageDist(self): assert (self.utils.average_distance(self.g) == 2300) assert (self.utils.average_distance(self.gSmall) == (2453 + (24530 * 2)) / 3) def testBiggestPop(self): assert self.utils.biggest_city(self.g) == ('TYO', 34000000) assert self.utils.biggest_city(self.gSmall) == ('MEX', 23400000) def testSmallestPop(self): assert self.utils.smallest_city(self.g) == ('ESS', 589900) assert self.utils.smallest_city(self.gSmall) == ('SCL', 6000000) def testAveragePop(self): assert self.utils.average_city(self.g) == 11796143 assert self.utils.average_city( self.gSmall) == (6000000 + 23400000 + 9050000) / 3 def testContinents(self): assert self.utils.get_continents_and_cities(self.g) == { u'Europe': [ u'Paris', u'Milan', u'London', u'Istanbul', u'Essen', u'St. Petersburg', u'Moscow', u'Madrid' ], u'Australia': [u'Sydney'], u'Africa': [ u'Lagos', u'Cairo', u'Johannesburg', u'Khartoum', u'Kinshasa', u'Algiers' ], u'Asia': [ u'Beijing', u'Delhi', u'Mumbai', u'Bagdad', u'Karachi', u'Jakarta', u'Bangkok', u'Calcutta', u'Manila', u'Ho Chi Minh City', u'Osaka', u'Hong Kong', u'Taipei', u'Seoul', u'Tehrah', u'Shanghai', u'Riyadh', u'Tokyo', u'Chennai' ], u'North America': [ u'Miami', u'Atlanta', u'New York', u'Chicago', u'Washington', u'Toronto', u'San Francisco', u'Los Angeles', u'Mexico City' ], u'South America': [u'Lima', u'Bogota', u'Santiago', u'Sao Paulo', u'Buenos Aires'] } def testHubs(self): assert self.utils.get_hub_cities(self.g) == ['IST', 'HKG'] def testShortestPath(self): print(self.utils.shortestPath(self.g, 'CHI', 'TYO')) assert self.utils.shortestPath(self.g, 'CHI', 'TYO') == ['CHI', 'SFO', 'TYO'] def testRouteDistance(self): assert sum(self.utils.route_distance( self.g, ['CHI', 'SFO', 'TYO'])) == (8296 + 2994) def testShortestPath(self): assert (self.utils.shortestPath(self.g, 'CHI', 'TYO') == ['CHI', 'SFO', 'TYO']) assert (self.utils.shortestPath(self.gSmall, 'SCL', 'MEX') == ['SCL', 'LIM', 'MEX'])
def run(self): self.initRandomizerSession() (stdPresets, tourPresets, comPresets) = loadPresetsList(self.cache) randoPresetsDesc = { "all_random": "all the parameters set to random", "Chozo_Speedrun": "speedrun progression speed with Chozo split", "default": "VARIA randomizer default settings", "doors_long": "be prepared to hunt for beams and ammo to open doors", "doors_short": "uses Chozo/speedrun settings for a quicker door color rando", "free": "easiest possible settings", "hardway2hell": "harder highway2hell", "haste": "inspired by DASH randomizer with Nerfed Charge / Progressive Suits", "highway2hell": "favors suitless seeds", "hud": "Full rando with remaining major upgrades in the area shown in the HUD", "hud_hard": "Low resources and VARIA HUD enabled to help you track of actual items count", "hud_start": "Non-vanilla start with Major or Chozo split", "minimizer":"Typical 'boss rush' settings with random start and nerfed charge", "minimizer_hardcore":"Have fun 'rushing' bosses with no equipment on a tiny map", "minimizer_maximizer":"No longer a boss rush", "quite_random": "randomizes a few significant settings to have various seeds", "scavenger_hard":"Pretty hostile Scavenger mode", "scavenger_random":"Randomize everything within Scavenger mode", "scavenger_speedrun":"Quickest Scavenger settings", "scavenger_vanilla_but_not":"Items are vanilla, but area and bosses are not", "stupid_hard": "hardest possible settings", "surprise": "quite_random with Area/Boss/Doors/Start settings randomized", "vanilla": "closest possible to vanilla Super Metroid", "way_of_chozo": "chozo split with boss randomization", "where_am_i": "Area mode with random start location and early morph", "where_is_morph": "Area mode with late Morph", "Multi_Category_Randomizer_Week_1": "Multi-Category Randomizer Tournament week 1", "Multi_Category_Randomizer_Week_2": "Multi-Category Randomizer Tournament week 2", "Multi_Category_Randomizer_Week_3": "Multi-Category Randomizer Tournament week 3", "Multi_Category_Randomizer_Week_4": "Multi-Category Randomizer Tournament week 4", "Multi_Category_Randomizer_Week_5": "Multi-Category Randomizer Tournament week 5", "Multi_Category_Randomizer_Week_6": "Multi-Category Randomizer Tournament week 6", "Multi_Category_Randomizer_Week_7": "Multi-Category Randomizer Tournament week 7", "Season_Races": "rando league races (Majors/Minors split)", "SGLive2022_Race_1": "SGLive 2022 Super Metroid randomizer tournament race 1", "SGLive2022_Race_2": "SGLive 2022 Super Metroid randomizer tournament race 2", "SGLive2022_Race_3": "SGLive 2022 Super Metroid randomizer tournament race 3", "SMRAT2021": "Super Metroid Randomizer Accessible Tournament 2021", "VARIA_Weekly": "Casual logic community races" } randoPresetsCategories = { "Standard": ["", "default", "Chozo_Speedrun", "free", "haste", "vanilla"], "Hud": ["hud", "hud_hard", "hud_start"], "Scavenger": ["scavenger_hard", "scavenger_random", "scavenger_speedrun", "scavenger_vanilla_but_not"], "Area": ["way_of_chozo", "where_am_i", "where_is_morph"], "Doors": ["doors_long", "doors_short"], "Minimizer": ["minimizer", "minimizer_hardcore", "minimizer_maximizer"], "Hard": ["hardway2hell", "highway2hell", "stupid_hard"], "Random": ["all_random", "quite_random", "surprise"], "Tournament": ["Season_Races", "SMRAT2021", "VARIA_Weekly", "SGLive2022_Race_1", "SGLive2022_Race_2", "SGLive2022_Race_3", "Multi_Category_Randomizer_Week_1", "Multi_Category_Randomizer_Week_2", "Multi_Category_Randomizer_Week_3", "Multi_Category_Randomizer_Week_4", "Multi_Category_Randomizer_Week_5", "Multi_Category_Randomizer_Week_6", "Multi_Category_Randomizer_Week_7"] } startAPs = GraphUtils.getStartAccessPointNamesCategory() startAPs = [OPTGROUP(_label="Standard", *startAPs["regular"]), OPTGROUP(_label="Custom", *startAPs["custom"]), OPTGROUP(_label="Custom (Area rando only)", *startAPs["area"])] # get multi currentMultiValues = self.getCurrentMultiValues() defaultMultiValues = getDefaultMultiValues() # objectives self exclusions objectivesExclusions = Objectives.getExclusions() objectivesTypes = Objectives.getObjectivesTypes() objectivesSort = Objectives.getObjectivesSort() objectivesCategories = Objectives.getObjectivesCategories() # check if we have a guid in the url url = self.request.env.request_uri.split('/') if len(url) > 0 and url[-1] != 'randomizer': # a seed unique key was passed as parameter key = url[-1] # decode url key = urllib.parse.unquote(key) # sanity check if IS_MATCH('^[0-9a-z-]*$')(key)[1] is None and IS_LENGTH(maxsize=36, minsize=36)(key)[1] is None: with DB() as db: seedInfo = db.getSeedInfo(key) if seedInfo is not None and len(seedInfo) > 0: defaultParams = getRandomizerDefaultParameters() defaultParams.update(seedInfo) seedInfo = defaultParams # check that the seed ips is available if seedInfo["upload_status"] in ['pending', 'uploaded', 'local']: # load parameters in session for key, value in seedInfo.items(): if key in ["complexity", "randoPreset", "raceMode"]: continue elif key in defaultMultiValues: keyMulti = key + 'MultiSelect' if keyMulti in seedInfo: if key == 'objective' and value == 'nothing': self.session.randomizer[key] = "" else: self.session.randomizer[key] = seedInfo[key] valueMulti = seedInfo[keyMulti] if type(valueMulti) == str: valueMulti = valueMulti.split(',') self.session.randomizer[keyMulti] = valueMulti currentMultiValues[key] = valueMulti elif key in self.session.randomizer and 'MultiSelect' not in key: self.session.randomizer[key] = value return dict(stdPresets=stdPresets, tourPresets=tourPresets, comPresets=comPresets, randoPresetsDesc=randoPresetsDesc, randoPresetsCategories=randoPresetsCategories, startAPs=startAPs, currentMultiValues=currentMultiValues, defaultMultiValues=defaultMultiValues, maxsize=sys.maxsize, displayNames=displayNames, objectivesExclusions=objectivesExclusions, objectivesTypes=objectivesTypes, objectivesSort=objectivesSort, objectivesCategories=objectivesCategories)
def loadRom(self, rom, interactive=False, magic=None, startAP=None): # startAP param is only use for seedless if rom == None: # TODO::add a --logic parameter for seedless Logic.factory('vanilla') self.romFileName = 'seedless' self.majorsSplit = 'Full' self.masterMajorsSplit = 'Full' self.areaRando = True self.bossRando = True self.escapeRando = False self.escapeTimer = "03:00" self.startAP = startAP RomPatches.setDefaultPatches(startAP) self.startArea = getAccessPoint(startAP).Start['solveArea'] # in seedless load all the vanilla transitions self.areaTransitions = vanillaTransitions[:] self.bossTransitions = vanillaBossesTransitions[:] self.escapeTransition = [vanillaEscapeTransitions[0]] # in seedless we allow mixing of area and boss transitions self.hasMixedTransitions = True self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.locations = Logic.locations for loc in self.locations: loc.itemName = 'Nothing' # set doors related to default patches DoorsManager.setDoorsColor() self.doorsRando = False self.hasNothing = False else: self.romFileName = rom self.romLoader = RomLoader.factory(rom, magic) Logic.factory(self.romLoader.readLogic()) self.romLoader.readNothingId() self.locations = Logic.locations (self.majorsSplit, self.masterMajorsSplit) = self.romLoader.assignItems(self.locations) (self.startAP, self.startArea, startPatches) = self.romLoader.getStartAP() if not GraphUtils.isStandardStart(self.startAP) and self.majorsSplit != 'Full': # update major/chozo locs in non standard start self.romLoader.updateSplitLocs(self.majorsSplit, self.locations) (self.areaRando, self.bossRando, self.escapeRando) = self.romLoader.loadPatches() RomPatches.ActivePatches += startPatches self.escapeTimer = self.romLoader.getEscapeTimer() self.doorsRando = self.romLoader.loadDoorsColor() self.hasNothing = self.checkLocsForNothing() if interactive == False: print("ROM {} majors: {} area: {} boss: {} escape: {} patches: {} activePatches: {}".format(rom, self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(self.romLoader.getPatches()), sorted(RomPatches.ActivePatches))) else: print("majors: {} area: {} boss: {} escape: {} activepatches: {}".format(self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(RomPatches.ActivePatches))) (self.areaTransitions, self.bossTransitions, self.escapeTransition, self.hasMixedTransitions) = self.romLoader.getTransitions() if interactive == True and self.debug == False: # in interactive area mode we build the graph as we play along if self.areaRando == True and self.bossRando == True: self.curGraphTransitions = [] elif self.areaRando == True: self.curGraphTransitions = self.bossTransitions[:] elif self.bossRando == True: self.curGraphTransitions = self.areaTransitions[:] else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions if self.escapeRando == False: self.curGraphTransitions += self.escapeTransition else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.smbm = SMBoolManager() self.areaGraph = AccessGraph(Logic.accessPoints, self.curGraphTransitions) # store at each step how many locations are available self.nbAvailLocs = [] if self.log.getEffectiveLevel() == logging.DEBUG: self.log.debug("Display items at locations:") for loc in self.locations: self.log.debug('{:>50}: {:>16}'.format(loc.Name, loc.itemName))
class TestGraphUtils(TestCase): def setUp(self): f = open('assets/data/map_data.json', 'r') decoded = json.loads(f.read()) self.g = graph.Graph() self.g.build_nodes(decoded['metros']) self.g.build_edges(decoded['routes']) self.utils = GraphUtils() f = open('assets/data/test1_map_data.json', 'r') decoded = json.loads(f.read()) self.gSmall = graph.Graph() self.gSmall.build_nodes(decoded['metros']) self.gSmall.build_edges(decoded['routes']) def testSave(self): self.utils.save_to_disk(self.g) f2 = open('assets/data/map_data_written.json', 'r') decoded = json.loads(f2.read()) self.g2 = graph.Graph() self.g2.build_nodes(decoded['metros']) self.g2.build_edges(decoded['routes']) self.utils = GraphUtils() assert (self.utils.longest_flight(self.g2) == ('SYD', 'LAX', 12051)) assert (self.utils.shortest_flight(self.g2) == ('NYC', 'WAS', 334)) assert (self.utils.average_distance(self.g2) == 2300) assert self.utils.biggest_city(self.g2) == ('TYO', 34000000) assert self.utils.smallest_city(self.g2) == ( 'ESS', 589900) def testLongestFlight(self): assert (self.utils.longest_flight(self.g) == ('SYD', 'LAX', 12051)) assert (self.utils.longest_flight(self.gSmall) == ('MEX', 'LIM', 24530)) def testShortestFlight(self): assert (self.utils.shortest_flight(self.g) == ('NYC', 'WAS', 334)) assert (self.utils.shortest_flight(self.gSmall) == ('SCL', 'LIM', 2453)) def testMapUrl(self): assert (self.utils.get_map_string( self.g) == 'PAR-ALG,+PAR-MAD,+PAR-LON,+PAR-ESS,+PAR-MIL,+MIL-PAR,+MIL-ESS,+MIL-IST,+MIA-MEX,+MIA-BOG,+MIA-ATL,+MIA-WAS,+LIM-SCL,+LIM-MEX,+LIM-BOG,+ATL-CHI,+ATL-WAS,+ATL-MIA,+PEK-SHA,+PEK-ICN,+LON-MAD,+LON-NYC,+LON-ESS,+LON-PAR,+IST-CAI,+IST-ALG,+IST-MIL,+IST-LED,+IST-MOW,+IST-BGW,+LOS-SAO,+LOS-KRT,+LOS-FIH,+CAI-KRT,+CAI-ALG,+CAI-IST,+CAI-BGW,+CAI-RUH,+DEL-THR,+DEL-KHI,+DEL-CCU,+DEL-MAA,+DEL-BOM,+BOM-KHI,+BOM-DEL,+BOM-MAA,+BGW-CAI,+BGW-IST,+BGW-THR,+BGW-KHI,+BGW-RUH,+NYC-MAD,+NYC-LON,+NYC-WAS,+NYC-YYZ,+BOG-LIM,+BOG-MEX,+BOG-MIA,+BOG-SAO,+BOG-BUE,+SCL-LIM,+SAO-BOG,+SAO-BUE,+SAO-MAD,+SAO-LOS,+KHI-BGW,+KHI-THR,+KHI-RUH,+KHI-DEL,+KHI-BOM,+JKT-MAA,+JKT-BKK,+JKT-SGN,+JKT-SYD,+BKK-MAA,+BKK-CCU,+BKK-HKG,+BKK-SGN,+BKK-JKT,+CCU-DEL,+CCU-MAA,+CCU-HKG,+CCU-BKK,+MNL-HKG,+MNL-TPE,+MNL-SFO,+MNL-SYD,+MNL-SGN,+SGN-BKK,+SGN-HKG,+SGN-MNL,+SGN-JKT,+OSA-TYO,+OSA-TPE,+HKG-CCU,+HKG-BKK,+HKG-SHA,+HKG-TPE,+HKG-MNL,+HKG-SGN,+BUE-BOG,+BUE-SAO,+TPE-HKG,+TPE-SHA,+TPE-OSA,+TPE-MNL,+ESS-LON,+ESS-PAR,+ESS-MIL,+ESS-LED,+ICN-SHA,+ICN-PEK,+ICN-TYO,+JNB-FIH,+JNB-KRT,+CHI-MEX,+CHI-LAX,+CHI-SFO,+CHI-YYZ,+CHI-ATL,+THR-MOW,+THR-BGW,+THR-DEL,+THR-KHI,+THR-RUH,+KRT-LOS,+KRT-FIH,+KRT-JNB,+KRT-CAI,+SHA-HKG,+SHA-PEK,+SHA-ICN,+SHA-TYO,+SHA-TPE,+FIH-LOS,+FIH-KRT,+FIH-JNB,+WAS-ATL,+WAS-MIA,+WAS-YYZ,+WAS-NYC,+RUH-CAI,+RUH-BGW,+RUH-THR,+RUH-KHI,+TYO-SHA,+TYO-ICN,+TYO-SFO,+TYO-OSA,+LED-ESS,+LED-MOW,+LED-IST,+SYD-MNL,+SYD-JKT,+SYD-LAX,+ALG-CAI,+ALG-MAD,+ALG-PAR,+ALG-IST,+MOW-LED,+MOW-THR,+MOW-IST,+YYZ-CHI,+YYZ-WAS,+YYZ-NYC,+MAA-DEL,+MAA-BOM,+MAA-CCU,+MAA-BKK,+MAA-JKT,+SFO-TYO,+SFO-MNL,+SFO-LAX,+SFO-CHI,+LAX-MEX,+LAX-SYD,+LAX-SFO,+LAX-CHI,+MAD-SAO,+MAD-ALG,+MAD-NYC,+MAD-LON,+MAD-PAR,+MEX-LIM,+MEX-LAX,+MEX-CHI,+MEX-MIA,+MEX-BOG') def testAverageDist(self): assert (self.utils.average_distance(self.g) == 2300) assert (self.utils.average_distance(self.gSmall) == (2453 + (24530 * 2)) / 3) def testBiggestPop(self): assert self.utils.biggest_city(self.g) == ('TYO', 34000000) assert self.utils.biggest_city(self.gSmall) == ('MEX', 23400000) def testSmallestPop(self): assert self.utils.smallest_city(self.g) == ( 'ESS', 589900) assert self.utils.smallest_city(self.gSmall) == ( 'SCL', 6000000) def testAveragePop(self): assert self.utils.average_city(self.g) == 11796143 assert self.utils.average_city(self.gSmall) == (6000000+23400000+9050000)/3 def testContinents(self): assert self.utils.get_continents_and_cities(self.g) == { u'Europe': [u'Paris', u'Milan', u'London', u'Istanbul', u'Essen', u'St. Petersburg', u'Moscow', u'Madrid'], u'Australia': [u'Sydney'], u'Africa': [u'Lagos', u'Cairo', u'Johannesburg', u'Khartoum', u'Kinshasa', u'Algiers'], u'Asia': [u'Beijing', u'Delhi', u'Mumbai', u'Bagdad', u'Karachi', u'Jakarta', u'Bangkok', u'Calcutta', u'Manila', u'Ho Chi Minh City', u'Osaka', u'Hong Kong', u'Taipei', u'Seoul', u'Tehrah', u'Shanghai', u'Riyadh', u'Tokyo', u'Chennai'], u'North America': [u'Miami', u'Atlanta', u'New York', u'Chicago', u'Washington', u'Toronto', u'San Francisco', u'Los Angeles', u'Mexico City'], u'South America': [u'Lima', u'Bogota', u'Santiago', u'Sao Paulo', u'Buenos Aires']} def testHubs(self): assert self.utils.get_hub_cities(self.g) == ['IST', 'HKG'] def testShortestPath(self): print(self.utils.shortestPath(self.g, 'CHI', 'TYO')) assert self.utils.shortestPath(self.g, 'CHI', 'TYO') == ['CHI', 'SFO', 'TYO'] def testRouteDistance(self): assert sum(self.utils.route_distance(self.g, ['CHI', 'SFO', 'TYO'])) == (8296 + 2994) def testShortestPath(self): assert (self.utils.shortestPath(self.g, 'CHI', 'TYO') == ['CHI', 'SFO', 'TYO']) assert (self.utils.shortestPath(self.gSmall, 'SCL', 'MEX') == ['SCL', 'LIM', 'MEX'])
parser.add_argument('--lightArea', help="keep number of transitions between vanilla areas", action='store_true', dest='lightArea', default=False) parser.add_argument('--escapeRando', help="Randomize the escape sequence", dest='escapeRando', nargs='?', const=True, default=False) parser.add_argument('--noRemoveEscapeEnemies', help="Do not remove enemies during escape sequence", action='store_true', dest='noRemoveEscapeEnemies', default=False) parser.add_argument('--bosses', help="randomize bosses", dest='bosses', nargs='?', const=True, default=False) parser.add_argument('--minimizer', help="minimizer mode: area and boss mixed together. arg is number of non boss locations", dest='minimizerN', nargs='?', const=35, default=None, choices=[str(i) for i in range(30,101)]+["random"]) parser.add_argument('--startLocation', help="Name of the Access Point to start from", dest='startLocation', nargs='?', default="Landing Site", choices=['random'] + GraphUtils.getStartAccessPointNames()) parser.add_argument('--startLocationList', help="list to choose from when random", dest='startLocationList', nargs='?', default=None) parser.add_argument('--debug', '-d', help="activate debug logging", dest='debug', action='store_true') parser.add_argument('--maxDifficulty', '-t', help="the maximum difficulty generated seed will be for given parameters", dest='maxDifficulty', nargs='?', default=None, choices=['easy', 'medium', 'hard', 'harder', 'hardcore', 'mania', 'random']) parser.add_argument('--minDifficulty', help="the minimum difficulty generated seed will be for given parameters (speedrun prog speed required)", dest='minDifficulty', nargs='?', default=None, choices=['easy', 'medium', 'hard', 'harder', 'hardcore', 'mania']) parser.add_argument('--seed', '-s', help="randomization seed to use", dest='seed', nargs='?', default=0, type=int) parser.add_argument('--rom', '-r',