Ejemplo n.º 1
0
 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"]
Ejemplo n.º 2
0
 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()
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
 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
Ejemplo n.º 11
0
 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)
Ejemplo n.º 12
0
 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))
Ejemplo n.º 14
0
    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
Ejemplo n.º 16
0
                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
Ejemplo n.º 17
0
 def writeDoorsColor(self, doors):
     DoorsManager.writeDoorsColor(self.romFile, doors)
Ejemplo n.º 18
0
    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))
Ejemplo n.º 19
0
    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)
Ejemplo n.º 20
0
 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