def toSolver(self, solver): solver.majorsSplit = self.state["majorsSplit"] solver.masterMajorsSplit = self.state["masterMajorsSplit"] solver.areaRando = self.state["areaRando"] solver.bossRando = self.state["bossRando"] solver.escapeRando = self.state["escapeRando"] solver.escapeTimer = self.state["escapeTimer"] RomPatches.ActivePatches = self.state["patches"] solver.startLocation = self.state["startLocation"] solver.startArea = self.state["startArea"] self.setLocsData(solver.locations) solver.areaTransitions = self.state["areaTransitions"] solver.bossTransitions = self.state["bossTransitions"] solver.escapeTransition = self.state["escapeTransition"] solver.curGraphTransitions = self.state["curGraphTransitions"] solver.hasMixedTransitions = self.state["hasMixedTransitions"] # preset solver.presetFileName = self.state["presetFileName"] # items collected / locs visited / bosses killed solver.collectedItems = self.state["collectedItems"] (solver.visitedLocations, solver.majorLocations) = self.setLocations( self.state["visitedLocations"], self.state["availableLocations"], solver.locations) solver.lastAP = self.state["lastAP"] solver.mode = self.state["mode"] solver.seed = self.state["seed"] solver.hasNothing = self.state["hasNothing"] DoorsManager.unserialize(self.state["doors"]) solver.doorsRando = self.state["doorsRando"] solver.plandoScavengerOrder = self.state["plandoScavengerOrder"] solver.tourian = self.state["tourian"] solver.objectives.setState(self.state["objectives"]) solver.majorUpgrades = self.state["majorUpgrades"] solver.splitLocsByArea = self.state["splitLocsByArea"]
def randomize(self): vcr = VCR(self.seedName, 'rando') if self.vcr == True else None self.errorMsg = "" split = self.randoSettings.restrictions['MajorMinor'] graphBuilder = GraphBuilder(self.graphSettings) container = None i = 0 attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1 now = time.process_time() endDate = sys.maxsize if self.randoSettings.runtimeLimit_s < endDate: endDate = now + self.randoSettings.runtimeLimit_s self.updateLocationsClass(split) while container is None and i < attempts and now <= endDate: self.restrictions = Restrictions(self.randoSettings) if self.graphSettings.doorsColorsRando == True: DoorsManager.randomize(self.graphSettings.allowGreyDoors) self.areaGraph = graphBuilder.createGraph( self.randoSettings.maxDiff) services = RandoServices(self.areaGraph, self.restrictions) setup = RandoSetup(self.graphSettings, Logic.locations, services) container = setup.createItemLocContainer(endDate, vcr) if container is None: sys.stdout.write('*') sys.stdout.flush() i += 1 else: self.errorMsg += '\n'.join(setup.errorMsgs) now = time.process_time() if container is None: if self.graphSettings.areaRando: self.errorMsg += "Could not find an area layout with these settings\n" if self.graphSettings.doorsColorsRando: self.errorMsg += "Could not find a door color combination with these settings\n" if split == "Scavenger": self.errorMsg += "Scavenger seed generation timed out\n" if self.errorMsg == "": self.errorMsg += "Unable to process settings\n" return (True, [], []) self.areaGraph.printGraph() filler = self.createFiller(container, endDate) self.log.debug("ItemLocContainer dump before filling:\n" + container.dump()) ret = filler.generateItems(vcr=vcr) if not ret[0]: itemLocs, progItemLocs = (ret[1], ret[2]) escapeTrigger = ( itemLocs, progItemLocs, split ) if self.randoSettings.restrictions["EscapeTrigger"] else None escapeOk = graphBuilder.escapeGraph(container, self.areaGraph, self.randoSettings.maxDiff, escapeTrigger) if not escapeOk: self.errorMsg += "Could not find a solution for escape" ret = (True, ret[1], ret[2]) self.errorMsg += filler.errorMsg return ret
def __init__(self): self._items = {} self._counts = {} # cache related self.cacheKey = 0 self.computeItemsPositions() Cache.reset() self.helpers = Logic.HelpersGraph(self) self.doorsManager = DoorsManager() self.createFacadeFunctions() self.createKnowsFunctions() self.resetItems()
def initialize(self, mode, rom, presetFileName, magic, debug, fill, startAP, trackerRace): # load rom and preset, return first state self.debug = debug self.mode = mode if self.mode != "seedless": self.seed = os.path.basename(os.path.splitext(rom)[0]) + '.sfc' else: self.seed = "seedless" self.smbm = SMBoolManager() self.presetFileName = presetFileName self.loadPreset(self.presetFileName) self.loadRom(rom, interactive=True, magic=magic, startAP=startAP) # in plando/tracker always consider that we're doing full self.majorsSplit = 'Full' # hide doors if self.doorsRando and mode == 'standard': DoorsManager.initTracker() self.clearItems() # in debug mode don't load plando locs/transitions if self.mode == 'plando' and self.debug == False: if fill == True: # load the source seed transitions and items/locations self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.areaGraph = AccessGraph(accessPoints, self.curGraphTransitions) self.fillPlandoLocs() else: if self.areaRando == True or self.bossRando == True: plandoTrans = self.loadPlandoTransitions() if len(plandoTrans) > 0: self.curGraphTransitions = plandoTrans self.areaGraph = AccessGraph(accessPoints, self.curGraphTransitions) self.loadPlandoLocs() # compute new available locations self.computeLocationsDifficulty(self.majorLocations) if trackerRace == True: self.mode = 'seedless' self.dumpState()
def getAddressesToRead(plando=False): addresses = { "locations": [], "patches": [], "transitions": [], "misc": [], "ranges": [] } # locations for loc in locations: addresses["locations"].append(loc.Address) # patches for (patch, values) in RomReader.patches.items(): addresses["patches"].append(values["address"]) # transitions for ap in accessPoints: if ap.Internal == True: continue addresses["transitions"].append(0x10000 | ap.ExitInfo['DoorPtr']) # misc # majors split addresses["misc"] += Addresses.getWeb('majorsSplit') # escape timer addresses["misc"] += Addresses.getWeb('escapeTimer') # start ap addresses["misc"] += Addresses.getWeb('startAP') # random doors addresses["misc"] += DoorsManager.getAddressesToRead() # objectives addresses["misc"] += Objectives.getAddressesToRead() # ranges [low, high] ## old doorasm for old seeds addresses["ranges"] += [snes_to_pc(0x8feb00), snes_to_pc(0x8fee60)] maxDoorAsmPatchLen = 22 customDoorsAsm = Addresses.getOne('customDoorsAsm') addresses["ranges"] += [ customDoorsAsm, customDoorsAsm + (maxDoorAsmPatchLen * len([ap for ap in accessPoints if ap.Internal == False])) ] # split locs addresses["ranges"] += Addresses.getRange('locIdsByArea') addresses["ranges"] += Addresses.getRange('scavengerOrder') if plando == True: # plando addresses addresses["ranges"] += Addresses.getRange('plandoAddresses') # plando transitions (4 bytes per transitions, ap#/2 transitions) plandoTransitions = Addresses.getOne('plandoTransitions') addresses["ranges"] += [ plandoTransitions, plandoTransitions + ((len(addresses["transitions"]) / 2) * 4) ] return addresses
def __init__(self, player=0, maxDiff=sys.maxsize, onlyBossLeft=False): self._items = {} self._counts = {} self.player = player self.maxDiff = maxDiff self.onlyBossLeft = onlyBossLeft # cache related self.cacheKey = 0 self.computeItemsPositions() Cache.reset() Logic.factory('vanilla') self.helpers = Logic.HelpersGraph(self) self.doorsManager = DoorsManager() self.createFacadeFunctions() self.createKnowsFunctions(player) self.resetItems()
def randomize(self): self.restrictions = Restrictions(self.randoSettings) self.errorMsg = "" graphBuilder = GraphBuilder(self.graphSettings) container = None i = 0 attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando else 1 now = time.process_time() endDate = sys.maxsize if self.randoSettings.runtimeLimit_s < endDate: endDate = now + self.randoSettings.runtimeLimit_s self.updateLocationsClass() while container is None and i < attempts and now <= endDate: if self.graphSettings.doorsColorsRando == True: DoorsManager.randomize(self.graphSettings.allowGreyDoors) self.areaGraph = graphBuilder.createGraph() services = RandoServices(self.areaGraph, self.restrictions) setup = RandoSetup(self.graphSettings, Logic.locations, services) container = setup.createItemLocContainer() if container is None: sys.stdout.write('*') sys.stdout.flush() i += 1 else: self.errorMsg += '\n'.join(setup.errorMsgs) now = time.process_time() if container is None: if self.graphSettings.areaRando: self.errorMsg += "Could not find an area layout with these settings" else: self.errorMsg += "Unable to process settings" return (True, [], []) graphBuilder.escapeGraph(container, self.areaGraph, self.randoSettings.maxDiff) self.areaGraph.printGraph() filler = self.createFiller(container, endDate) vcr = VCR(self.seedName, 'rando') if self.vcr == True else None self.log.debug("ItemLocContainer dump before filling:\n" + container.dump()) ret = filler.generateItems(vcr=vcr) self.errorMsg += filler.errorMsg return ret
def randomize(self): vcr = VCR(self.seedName, 'rando') if self.vcr == True else None self.errorMsg = "" split = self.randoSettings.restrictions['MajorMinor'] graphBuilder = GraphBuilder(self.graphSettings) container = None i = 0 attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1 now = time.process_time() endDate = sys.maxsize if self.randoSettings.runtimeLimit_s < endDate: endDate = now + self.randoSettings.runtimeLimit_s self.updateLocationsClass(split) while container is None and i < attempts and now <= endDate: self.restrictions = Restrictions(self.randoSettings) if self.graphSettings.doorsColorsRando == True: DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player) self.areaGraph = graphBuilder.createGraph() services = RandoServices(self.areaGraph, self.restrictions) setup = RandoSetup(self.graphSettings, Logic.locations, services, self.player) self.setup = setup container = setup.createItemLocContainer(endDate, vcr) if container is None: sys.stdout.write('*') sys.stdout.flush() i += 1 else: self.errorMsg += '\n'.join(setup.errorMsgs) now = time.process_time() if container is None: if self.graphSettings.areaRando: self.errorMsg += "Could not find an area layout with these settings" else: self.errorMsg += "Unable to process settings" self.areaGraph.printGraph() return container
def iterate(self, stateJson, scope, action, params): self.debug = params["debug"] self.smbm = SMBoolManager() state = SolverState() state.fromJson(stateJson) state.toSolver(self) self.loadPreset(self.presetFileName) # add already collected items to smbm self.smbm.addItems(self.collectedItems) if scope == 'item': if action == 'clear': self.clearItems(True) else: if action == 'add': if self.mode in ['plando', 'seedless', 'race', 'debug']: if params['loc'] != None: if self.mode == 'plando': self.setItemAt(params['loc'], params['item'], params['hide']) else: itemName = params.get('item', 'Nothing') if itemName is None: itemName = 'Nothing' self.setItemAt(params['loc'], itemName, False) else: self.increaseItem(params['item']) else: # pickup item at locName self.pickItemAt(params['loc']) elif action == 'remove': if 'loc' in params: self.removeItemAt(params['loc']) elif 'count' in params: # remove last collected item self.cancelLastItems(params['count']) else: self.decreaseItem(params['item']) elif action == 'replace': self.replaceItemAt(params['loc'], params['item'], params['hide']) elif action == 'toggle': self.toggleItem(params['item']) elif scope == 'area': if action == 'clear': self.clearTransitions() else: if action == 'add': startPoint = params['startPoint'] endPoint = params['endPoint'] self.addTransition(self.transWeb2Internal[startPoint], self.transWeb2Internal[endPoint]) elif action == 'remove': if 'startPoint' in params: self.cancelTransition( self.transWeb2Internal[params['startPoint']]) else: # remove last transition self.cancelLastTransition() elif scope == 'door': if action == 'replace': doorName = params['doorName'] newColor = params['newColor'] DoorsManager.setColor(doorName, newColor) elif action == 'toggle': doorName = params['doorName'] DoorsManager.switchVisibility(doorName) self.areaGraph = AccessGraph(Logic.accessPoints, self.curGraphTransitions) if scope == 'common': if action == 'save': return self.savePlando(params['lock'], params['escapeTimer']) elif action == 'randomize': self.randoPlando(params) # if last loc added was a sequence break, recompute its difficulty, # as it may be available with the newly placed item. if len(self.visitedLocations) > 0: lastVisited = self.visitedLocations[-1] if lastVisited.difficulty.difficulty == -1: self.visitedLocations.remove(lastVisited) self.majorLocations.append(lastVisited) else: lastVisited = None else: lastVisited = None # compute new available locations self.clearLocs(self.majorLocations) self.computeLocationsDifficulty(self.majorLocations) # put back last visited location if lastVisited != None: self.majorLocations.remove(lastVisited) self.visitedLocations.append(lastVisited) if lastVisited.difficulty == False: # if the loc is still sequence break, put it back as sequence break lastVisited.difficulty = SMBool(True, -1) # return them self.dumpState()
def fromSolver(self, solver): self.state = {} # string self.state["majorsSplit"] = solver.majorsSplit # string self.state["masterMajorsSplit"] = solver.masterMajorsSplit # bool self.state["areaRando"] = solver.areaRando # bool self.state["bossRando"] = solver.bossRando # bool self.state["escapeRando"] = solver.escapeRando # string "03:00" self.state["escapeTimer"] = solver.escapeTimer # list of active patches self.state["patches"] = RomPatches.ActivePatches # start ap self.state["startLocation"] = solver.startLocation # start area self.state["startArea"] = solver.startArea # dict {locName: {itemName: "xxx", "accessPoint": "xxx"}, ...} self.state["locsData"] = self.getLocsData(solver.locations) # list [(ap1, ap2), (ap3, ap4), ...] self.state["areaTransitions"] = solver.areaTransitions # list [(ap1, ap2), (ap3, ap4), ...] self.state["bossTransitions"] = solver.bossTransitions # list [(ap1, ap2)] self.state["escapeTransition"] = solver.escapeTransition # list [(ap1, ap2), ...] self.state["curGraphTransitions"] = solver.curGraphTransitions # bool self.state["hasMixedTransitions"] = solver.hasMixedTransitions # preset file name self.state["presetFileName"] = solver.presetFileName ## items collected / locs visited / bosses killed # list [item1, item2, ...] self.state["collectedItems"] = solver.collectedItems # dict {locName: {index: 0, difficulty: (bool, diff, ...), ...} with index being the position of the loc in visitedLocations self.state["visitedLocations"] = self.getVisitedLocations( solver.visitedLocations) # dict {locName: (bool, diff, [know1, ...], [item1, ...]), ...} self.state["availableLocations"] = self.getAvailableLocations( solver.majorLocations) # string of last access point self.state["lastAP"] = solver.lastAP # dict {locNameWeb: {infos}, ...} self.state["availableLocationsWeb"] = self.getAvailableLocationsWeb( solver.majorLocations, solver.masterMajorsSplit) # dict {locNameWeb: {infos}, ...} self.state["visitedLocationsWeb"] = self.getAvailableLocationsWeb( solver.visitedLocations, solver.masterMajorsSplit) # dict {locNameWeb: {infos}, ...} self.state["remainLocationsWeb"] = self.getRemainLocationsWeb( solver.majorLocations, solver.masterMajorsSplit) # string: standard/seedless/plando/race/debug self.state["mode"] = solver.mode # string: self.state["seed"] = solver.seed # dict {point: point, ...} / array of startPoints (self.state["linesWeb"], self.state["linesSeqWeb"]) = self.getLinesWeb( solver.curGraphTransitions) # bool self.state["allTransitions"] = len( solver.curGraphTransitions) == len(solver.areaTransitions) + len( solver.bossTransitions) + len(solver.escapeTransition) self.state["errorMsg"] = solver.errorMsg if len(solver.visitedLocations) > 0: self.state["last"] = { "loc": solver.visitedLocations[-1].Name, "item": solver.visitedLocations[-1].itemName } else: self.state["last"] = "" # store the inner graph transitions to display in vcr if self.debug == True: self.state["innerTransitions"] = self.getInnerTransitions( solver.areaGraph.availAccessPoints, solver.curGraphTransitions) else: self.state["innerTransitions"] = [] # has nothing: bool self.state["hasNothing"] = solver.hasNothing # doors colors: dict {name: (color, facing, hidden)} self.state["doors"] = DoorsManager.serialize() # doorsRando: bool self.state["doorsRando"] = solver.doorsRando # allDoorsRevealed: bool self.state["allDoorsRevealed"] = DoorsManager.allDoorsRevealed() # roomsVisibility: array of string ['landingSiteSvg', 'MissileCrateriamoatSvg'] self.state["roomsVisibility"] = self.getRoomsVisibility( solver, solver.areaGraph, solver.smbm) # for plando scav hunt, store the list of scav locs self.state["plandoScavengerOrder"] = solver.plandoScavengerOrder # for disabled tourian self.state["tourian"] = solver.tourian # custom objectives self.state["objectives"] = solver.objectives.getState() self.state["majorUpgrades"] = solver.majorUpgrades self.state["splitLocsByArea"] = solver.splitLocsByArea
def loadDoorsColor(self): rom = self.getROM() if self.romReader.race is None: return DoorsManager.loadDoorsColor(rom, rom.readWord) else: return DoorsManager.loadDoorsColor(rom, self.romReader.readPlmWord)
def loadDoorsColor(self): return DoorsManager.loadDoorsColor(self.romReader.romFile)
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('varia') self.romFileName = 'seedless' self.majorsSplit = '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 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.romLoader.assignItems(self.locations) (self.startAP, self.startArea, startPatches) = self.romLoader.getStartAP() (self.areaRando, self.bossRando, self.escapeRando) = self.romLoader.loadPatches() RomPatches.ActivePatches += startPatches self.escapeTimer = self.romLoader.getEscapeTimer() self.doorsRando = self.romLoader.loadDoorsColor() 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))
def randoPlando(self, parameters): # if all the locations are visited, do nothing if len(self.majorLocations) == 0: return plandoLocsItems = {} for loc in self.visitedLocations: plandoLocsItems[loc.Name] = loc.itemName plandoCurrent = { "locsItems": plandoLocsItems, "transitions": self.fillGraph(), "patches": RomPatches.ActivePatches, "doors": DoorsManager.serialize(), "forbiddenItems": parameters["forbiddenItems"] } plandoCurrentJson = json.dumps(plandoCurrent) pythonExec = "python{}.{}".format(sys.version_info.major, sys.version_info.minor) params = [ pythonExec, os.path.expanduser("~/RandomMetroidSolver/randomizer.py"), '--runtime', '10', '--param', self.presetFileName, '--output', self.outputFileName, '--plandoRando', plandoCurrentJson, '--progressionSpeed', 'speedrun', '--minorQty', parameters["minorQty"], '--maxDifficulty', 'hardcore', '--energyQty', parameters["energyQty"], '--startAP', self.startAP ] import subprocess subprocess.call(params) with open(self.outputFileName, 'r') as jsonFile: data = json.load(jsonFile) self.errorMsg = data["errorMsg"] # load the locations if "itemLocs" in data: self.clearItems(reload=True) itemsLocs = data["itemLocs"] # create a copy because we need self.locations to be full, else the state will be empty self.majorLocations = self.locations[:] for itemLoc in itemsLocs: locName = itemLoc["Location"]["Name"] loc = self.getLoc(locName) # we can have locations from non connected areas if "difficulty" in itemLoc["Location"]: difficulty = itemLoc["Location"]["difficulty"] smbool = SMBool(difficulty["bool"], difficulty["difficulty"], difficulty["knows"], difficulty["items"]) loc.difficulty = smbool itemName = itemLoc["Item"]["Type"] loc.itemName = itemName loc.accessPoint = itemLoc["Location"]["accessPoint"] self.collectMajor(loc)
class SMBoolManager(object): items = [ 'ETank', 'Missile', 'Super', 'PowerBomb', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Reserve', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack', 'Nothing', 'NoEnergy', 'MotherBrain', 'Hyper' ] + Bosses.Golden4() countItems = ['Missile', 'Super', 'PowerBomb', 'ETank', 'Reserve'] def __init__(self): self._items = {} self._counts = {} # cache related self.cacheKey = 0 self.computeItemsPositions() Cache.reset() self.helpers = Logic.HelpersGraph(self) self.doorsManager = DoorsManager() self.createFacadeFunctions() self.createKnowsFunctions() self.resetItems() def computeItemsPositions(self): # compute index in cache key for each items self.itemsPositions = {} maxBitsForCountItem = 7 # 128 values with 7 bits for (i, item) in enumerate(self.countItems): pos = i * maxBitsForCountItem bitMask = (2 << (maxBitsForCountItem - 1)) - 1 bitMask = bitMask << pos self.itemsPositions[item] = (pos, bitMask) for (i, item) in enumerate(self.items, (i + 1) * maxBitsForCountItem + 1): if item in self.countItems: continue self.itemsPositions[item] = (i, 1 << i) def computeNewCacheKey(self, item, value): # generate an unique integer for each items combinations which is use as key in the cache. if item in ['Nothing', 'NoEnergy']: return (pos, bitMask) = self.itemsPositions[item] # print("--------------------- {} {} ----------------------------".format(item, value)) # print("old: "+format(self.cacheKey, '#067b')) self.cacheKey = (self.cacheKey & (~bitMask)) | (value << pos) # print("new: "+format(self.cacheKey, '#067b')) # self.printItemsInKey(self.cacheKey) def printItemsInKey(self, key): # for debug purpose print("key: " + format(key, '#067b')) msg = "" for (item, (pos, bitMask)) in self.itemsPositions.items(): value = (key & bitMask) >> pos if value != 0: msg += " {}: {}".format(item, value) print("items:{}".format(msg)) def isEmpty(self): for item in self.items: if self.haveItem(item): return False for item in self.countItems: if self.itemCount(item) > 0: return False return True def getItems(self): # get a dict of collected items and how many (to be displayed on the solver spoiler) itemsDict = {} for item in self.items: itemsDict[item] = 1 if self._items[item] == True else 0 for item in self.countItems: itemsDict[item] = self._counts[item] return itemsDict def withItem(self, item, func): self.addItem(item) ret = func(self) self.removeItem(item) return ret def resetItems(self): self._items = {item: smboolFalse for item in self.items} self._counts = {item: 0 for item in self.countItems} self.cacheKey = 0 Cache.update(self.cacheKey) def addItem(self, item): # a new item is available self._items[item] = SMBool(True, items=[item]) if self.isCountItem(item): count = self._counts[item] + 1 self._counts[item] = count self.computeNewCacheKey(item, count) else: self.computeNewCacheKey(item, 1) Cache.update(self.cacheKey) def addItems(self, items): if len(items) == 0: return for item in items: self._items[item] = SMBool(True, items=[item]) if self.isCountItem(item): count = self._counts[item] + 1 self._counts[item] = count self.computeNewCacheKey(item, count) else: self.computeNewCacheKey(item, 1) Cache.update(self.cacheKey) def removeItem(self, item): # randomizer removed an item (or the item was added to test a post available) if self.isCountItem(item): count = self._counts[item] - 1 self._counts[item] = count if count == 0: self._items[item] = smboolFalse self.computeNewCacheKey(item, count) else: self._items[item] = smboolFalse self.computeNewCacheKey(item, 0) Cache.update(self.cacheKey) def createFacadeFunctions(self): for fun in dir(self.helpers): if fun != 'smbm' and fun[0:2] != '__': setattr(self, fun, getattr(self.helpers, fun)) def traverse(self, doorName): return self.doorsManager.traverse(self, doorName) def createKnowsFunctions(self): # for each knows we have a function knowsKnows (ex: knowsAlcatrazEscape()) which # take no parameter for knows in Knows.__dict__: if isKnows(knows): setattr(self, 'knows' + knows, lambda knows=knows: SMBool(Knows.__dict__[knows].bool, Knows.__dict__[knows]. difficulty, knows=[knows])) def isCountItem(self, item): return item in self.countItems def itemCount(self, item): # return integer return self._counts[item] def haveItem(self, item): return self._items[item] wand = staticmethod(SMBool.wand) wandmax = staticmethod(SMBool.wandmax) wor = staticmethod(SMBool.wor) wnot = staticmethod(SMBool.wnot) def itemCountOk(self, item, count, difficulty=0): if self.itemCount(item) >= count: if item in ['ETank', 'Reserve']: item = str(count) + '-' + item return SMBool(True, difficulty, items=[item]) else: return smboolFalse def energyReserveCountOk(self, count, difficulty=0): if self.energyReserveCount() >= count: nEtank = self.itemCount('ETank') if nEtank > count: nEtank = int(count) items = str(nEtank) + '-ETank' nReserve = self.itemCount('Reserve') if nEtank < count: nReserve = int(count) - nEtank items += ' - ' + str(nReserve) + '-Reserve' return SMBool(True, difficulty, items=[items]) else: return smboolFalse
ctrlDict[k] = b i += 1 else: raise ValueError("Invalid button name : " + str(b)) plandoSettings = None if args.plandoRando is not None: forceArg('progressionSpeed', 'speedrun', "'Progression Speed' forced to speedrun") progSpeed = 'speedrun' forceArg('majorsSplit', 'Full', "'Majors Split' forced to Full") forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal") forceArg('progressionDifficulty', 'normal', "'Progression difficulty' forced to normal") progDiff = 'normal' args.plandoRando = json.loads(args.plandoRando) RomPatches.ActivePatches = args.plandoRando["patches"] DoorsManager.unserialize(args.plandoRando["doors"]) plandoSettings = {"locsItems": args.plandoRando['locsItems'], "forbiddenItems": args.plandoRando['forbiddenItems']} randoSettings = RandoSettings(maxDifficulty, progSpeed, progDiff, qty, restrictions, args.superFun, args.runtimeLimit_s, plandoSettings, minDifficulty) # print some parameters for jm's stats if args.jm == True: print("startLocation:{}".format(args.startLocation)) print("progressionSpeed:{}".format(progSpeed)) print("majorsSplit:{}".format(args.majorsSplit)) print("morphPlacement:{}".format(args.morphPlacement)) print("gravity:{}".format(gravityBehaviour)) print("maxDifficulty:{}".format(maxDifficulty)) dotFile = None
def writeDoorsColor(self, doors): DoorsManager.writeDoorsColor(self.romFile, doors)
def loadRom(self, rom, interactive=False, magic=None, startLocation=None): self.scavengerOrder = [] self.plandoScavengerOrder = [] # startLocation 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.startLocation = startLocation RomPatches.setDefaultPatches(startLocation) self.startArea = getAccessPoint(startLocation).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 self.objectives.setVanilla() self.tourian = 'Vanilla' else: self.romFileName = rom self.romLoader = RomLoader.factory(rom, magic) Logic.factory(self.romLoader.readLogic()) self.locations = Logic.locations (self.majorsSplit, self.masterMajorsSplit) = self.romLoader.assignItems( self.locations) (self.startLocation, self.startArea, startPatches) = self.romLoader.getStartAP() if not GraphUtils.isStandardStart( self.startLocation) 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, hasObjectives, self.tourian) = self.romLoader.loadPatches() RomPatches.ActivePatches += startPatches self.escapeTimer = self.romLoader.getEscapeTimer() self.doorsRando = self.romLoader.loadDoorsColor() self.hasNothing = self.checkLocsForNothing() if self.majorsSplit == 'Scavenger': self.scavengerOrder = self.romLoader.loadScavengerOrder( self.locations) if hasObjectives: self.romLoader.loadObjectives(self.objectives) else: if self.majorsSplit == "Scavenger": self.objectives.setScavengerHunt() self.objectives.tourianRequired = not self.romLoader.hasPatch( 'Escape_Trigger') else: self.objectives.setVanilla() self.majorUpgrades = self.romLoader.loadMajorUpgrades() self.splitLocsByArea = self.romLoader.getSplitLocsByArea( self.locations) self.objectives.setSolverMode(self) 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( self.tourian) 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.buildGraph() # 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))
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 fromSolver(self, solver): self.state = {} # string self.state["majorsSplit"] = solver.majorsSplit # bool self.state["areaRando"] = solver.areaRando # bool self.state["bossRando"] = solver.bossRando # bool self.state["escapeRando"] = solver.escapeRando # string "03:00" self.state["escapeTimer"] = solver.escapeTimer # list of active patches self.state["patches"] = RomPatches.ActivePatches # start ap self.state["startAP"] = solver.startAP # start area self.state["startArea"] = solver.startArea # dict {locName: {itemName: "xxx", "accessPoint": "xxx"}, ...} self.state["locsData"] = self.getLocsData(solver.locations) # list [(ap1, ap2), (ap3, ap4), ...] self.state["areaTransitions"] = solver.areaTransitions # list [(ap1, ap2), (ap3, ap4), ...] self.state["bossTransitions"] = solver.bossTransitions # list [(ap1, ap2), ...] self.state["curGraphTransitions"] = solver.curGraphTransitions # bool self.state["hasMixedTransitions"] = solver.hasMixedTransitions # preset file name self.state["presetFileName"] = solver.presetFileName ## items collected / locs visited / bosses killed # list [item1, item2, ...] self.state["collectedItems"] = solver.collectedItems # dict {locName: {index: 0, difficulty: (bool, diff, ...), ...} with index being the position of the loc in visitedLocations self.state["visitedLocations"] = self.getVisitedLocations( solver.visitedLocations) # dict {locName: (bool, diff, [know1, ...], [item1, ...]), ...} self.state["availableLocations"] = self.getAvailableLocations( solver.majorLocations) # string of last access point self.state["lastAP"] = solver.lastAP # dict {locNameWeb: {infos}, ...} self.state["availableLocationsWeb"] = self.getAvailableLocationsWeb( solver.majorLocations) # dict {locNameWeb: {infos}, ...} self.state["visitedLocationsWeb"] = self.getAvailableLocationsWeb( solver.visitedLocations) # dict {locNameWeb: {infos}, ...} self.state["remainLocationsWeb"] = self.getRemainLocationsWeb( solver.majorLocations) # string: standard/seedless/plando self.state["mode"] = solver.mode # string: self.state["seed"] = solver.seed # dict {point: point, ...} / array of startPoints (self.state["linesWeb"], self.state["linesSeqWeb"]) = self.getLinesWeb( solver.curGraphTransitions) # bool self.state["allTransitions"] = len(solver.curGraphTransitions) == len( solver.areaTransitions) + len(solver.bossTransitions) self.state["errorMsg"] = solver.errorMsg if len(solver.visitedLocations) > 0: self.state["last"] = { "loc": solver.visitedLocations[-1].Name, "item": solver.visitedLocations[-1].itemName } else: self.state["last"] = "" # store the inner graph transitions to display in vcr if self.debug == True: self.state["innerTransitions"] = self.getInnerTransitions( solver.areaGraph.availAccessPoints, solver.curGraphTransitions) else: self.state["innerTransitions"] = [] # doors colors: dict {name: (color, facing, hidden)} self.state["doors"] = DoorsManager.serialize() # doorsRando: bool self.state["doorsRando"] = solver.doorsRando