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 == 'plando' or self.mode == 'seedless':
                        if params['loc'] != None:
                            if self.mode == 'plando':
                                self.setItemAt(params['loc'], params['item'],
                                               params['hide'])
                            else:
                                self.setItemAt(params['loc'],
                                               params.get('item', 'Nothing'),
                                               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.doors[doorName].setColor(newColor)
            elif action == 'toggle':
                doorName = params['doorName']
                DoorsManager.switchVisibility(doorName)

        self.areaGraph = AccessGraph(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()
# V + G + bomb: 3 energy
# 0 + 0 + bomb: dead with 13 energy
# V + 0 + bomb: 6 energy
# 0 + G + bomb: 3 energy
# V + G + 3 PB: 2 energy
#  from etank to landing site:
# V + G + bomb: 4 energy
# V + G + 5 PB: 3 energy
# V + 0 + 5 PB: 7 energy
#    lambda sm: sm.wor(sm.canEnterAndLeaveGauntlet(),
#                      sm.wand(sm.canShortCharge(),
#                              sm.canEnterAndLeaveGauntletQty(1, 0)), # thanks ponk! https://youtu.be/jil5zTBCF1s
#                      sm.canDoLowGauntlet())
#)
locationsDict["Bomb"].AccessFrom = {
    'Landing Site': lambda sm: SMBool(True)
}
locationsDict["Bomb"].Available = (
    lambda sm: sm.wand(sm.haveItem('Morph'),
                       sm.traverse('FlywayRight'))
)
locationsDict["Energy Tank, Terminator"].AccessFrom = {
    'Landing Site': lambda sm: sm.canPassTerminatorBombWall(),
    'Lower Mushrooms Left': lambda sm: sm.canPassCrateriaGreenPirates(),
#    'Gauntlet Top': lambda sm: sm.haveItem('Morph')
}
locationsDict["Energy Tank, Terminator"].Available = (
    lambda sm: SMBool(True)
)
locationsDict["Reserve Tank, Brinstar"].AccessFrom = {
    'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight'))
Beispiel #3
0
 def canPassTerminatorBombWall(self, fromLandingSite=True):
     sm = self.smbm
     return sm.wor(sm.wand(sm.haveItem('SpeedBooster'),
                           sm.wor(SMBool(not fromLandingSite, 0), sm.knowsSimpleShortCharge(), sm.knowsShortCharge())),
                   sm.canDestroyBombWalls())
Beispiel #4
0
    def escapeTrigger(self, emptyContainer, graph, maxDiff, escapeTrigger):
        container = emptyContainer
        sm = container.sm
        allItemLocs,progItemLocs,split = escapeTrigger[0],escapeTrigger[1],escapeTrigger[2]
        # check if crateria is connected, if not replace Tourian
        # connection with Climb and add special escape patch to Climb
        if not any(il.Location.GraphArea == "Crateria" for il in allItemLocs):
            escapeAttr = graph.EscapeAttributes
            if "patches" not in escapeAttr:
                escapeAttr['patches'] = []
            escapeAttr['patches'] += ['climb_disable_bomb_blocks.ips', "Climb_Asleep"]
            src, _ = next(t for t in graph.InterAreaTransitions if t[1].Name == "Golden Four")
            graph.removeTransitions("Golden Four")
            graph.addTransition(src.Name, "Climb Bottom Left")
            # disconnect the other side of G4
            graph.addTransition("Golden Four", "Golden Four")
        # remove vanilla escape transition
        graph.addTransition('Tourian Escape Room 4 Top Right', 'Tourian Escape Room 4 Top Right')
        # filter garbage itemLocs
        ilCheck = lambda il: not il.Location.isBoss() and not il.Location.restricted and il.Item.Category != "Nothing"
        # update item% objectives
        accessibleItems = [il.Item for il in allItemLocs if ilCheck(il)]
        majorUpgrades = [item.Type for item in accessibleItems if item.BeamBits != 0 or item.ItemBits != 0]
        sm.objectives.setItemPercentFuncs(len(accessibleItems), majorUpgrades)
        if split == "Scavenger":
            # update escape access for scav with last scav loc
            lastScavItemLoc = progItemLocs[-1]
            sm.objectives.updateScavengerEscapeAccess(lastScavItemLoc.Location.accessPoint)
            sm.objectives.setScavengerHuntFunc(lambda sm, ap: sm.haveItem(lastScavItemLoc.Item.Type))
        else:
            # update "collect all items in areas" funcs
            availLocsByArea=defaultdict(list)
            for itemLoc in allItemLocs:
                if ilCheck(itemLoc) and (split.startswith("Full") or itemLoc.Location.isClass(split)):
                    availLocsByArea[itemLoc.Location.GraphArea].append(itemLoc.Location.Name)
            self.log.debug("escapeTrigger. availLocsByArea="+str(availLocsByArea))
            sm.objectives.setAreaFuncs({area:lambda sm,ap:SMBool(len(container.getLocs(lambda loc: loc.Name in availLocsByArea[area]))==0) for area in availLocsByArea})
        self.log.debug("escapeTrigger. collect locs until G4 access")
        # collect all item/locations up until we can pass G4 (the escape triggers)
        itemLocs = allItemLocs[:]
        ap = "Landing Site" # dummy value it'll be overwritten at first collection
        while len(itemLocs) > 0 and not (sm.canPassG4() and graph.canAccess(sm, ap, "Landing Site", maxDiff)):
            il = itemLocs.pop(0)
            if il.Location.restricted:
                continue
            self.log.debug("collecting " + getItemLocStr(il))
            container.collect(il)
            ap = il.Location.accessPoint
        # final update of item% obj
        collectedLocsAccessPoints = {il.Location.accessPoint for il in container.itemLocations}
        sm.objectives.updateItemPercentEscapeAccess(list(collectedLocsAccessPoints))
        possibleTargets = self._getTargets(sm, graph, maxDiff)
        # try to escape from all the possible objectives APs
        possiblePaths = []
        for goal in Objectives.activeGoals:
            n, possibleAccessPoints = goal.escapeAccessPoints
            count = 0
            for ap in possibleAccessPoints:
                self.log.debug("escapeTrigger. testing AP " + ap)
                path = graph.accessPath(sm, ap, 'Landing Site', maxDiff)
                if path is not None:
                    self.log.debug("escapeTrigger. add path from "+ap)
                    possiblePaths.append(path)
                    count += 1
            if count < n:
                # there is a goal we cannot escape from
                self.log.debug("escapeTrigger. goal %s: found %d/%d possible escapes, abort" % (goal.name, count, n))
                return (None, None)
        # try and get a path from all possible areas
        self.log.debug("escapeTrigger. completing paths")
        allAreas = {il.Location.GraphArea for il in allItemLocs if not il.Location.restricted and not il.Location.GraphArea in ["Tourian", "Ceres"]}
        def getStartArea(path):
            return path[0].GraphArea
        def apCheck(ap):
            nonlocal graph, possiblePaths
            apObj = graph.accessPoints[ap]
            return apObj.GraphArea not in [getStartArea(path) for path in possiblePaths]
        escapeAPs = [ap for ap in collectedLocsAccessPoints if apCheck(ap)]
        for ap in escapeAPs:
            path = graph.accessPath(sm, ap, 'Landing Site', maxDiff)
            if path is not None:
                self.log.debug("escapeTrigger. add path from "+ap)
                possiblePaths.append(path)
        def areaPathCheck():
            nonlocal allAreas, possiblePaths
            startAreas = {getStartArea(path) for path in possiblePaths}
            return len(allAreas - startAreas) == 0
        while not areaPathCheck() and len(itemLocs) > 0:
            il = itemLocs.pop(0)
            if il.Location.restricted:
                continue
            self.log.debug("collecting " + getItemLocStr(il))
            container.collect(il)
            ap = il.Location.accessPoint
            if apCheck(ap):
                path = graph.accessPath(sm, ap, 'Landing Site', maxDiff)
                if path is not None:
                    self.log.debug("escapeTrigger. add path from "+ap)
                    possiblePaths.append(path)

        return (possibleTargets, possiblePaths)
 def canClearGoals(smbm, ap):
     result = SMBool(True)
     for goal in Objectives.activeGoals:
         result = smbm.wand(result, goal.canClearGoal(smbm, ap))
     return result
 def isVanillaDraygon(self):
     return SMBool(self.getDraygonConnection() == 'DraygonRoomIn')
 def canExitPreciousRoomVanilla(self):
     return SMBool(True)  # handled by canExitDraygonVanilla
 def computeLocDiff(self, tdiff, diff, pdiff):
     allDiff = SMBool.wandmax(tdiff, diff, pdiff)
     return (allDiff, None)
from logic.helpers import Bosses
from utils.parameters import Settings
from rom.rom_patches import RomPatches
from logic.smbool import SMBool
from graph.location import locationsDict

locationsDict["Energy Tank, Gauntlet"].AccessFrom = {
    'Landing Site': lambda sm: SMBool(True)
}
locationsDict["Energy Tank, Gauntlet"].Available = (
    lambda sm: sm.wor(
        sm.canEnterAndLeaveGauntlet(),
        sm.wand(sm.canShortCharge(), sm.canEnterAndLeaveGauntletQty(1, 0)
                ),  # thanks ponk! https://youtu.be/jil5zTBCF1s
        sm.canDoLowGauntlet()))
locationsDict["Bomb"].AccessFrom = {'Landing Site': lambda sm: SMBool(True)}
locationsDict["Bomb"].Available = (
    lambda sm: sm.wand(sm.haveItem('Morph'), sm.traverse('FlywayRight')))
locationsDict["Bomb"].PostAvailable = (
    lambda sm: sm.wor(sm.knowsAlcatrazEscape(), sm.canPassBombPassages()))
locationsDict["Energy Tank, Terminator"].AccessFrom = {
    'Landing Site': lambda sm: sm.canPassTerminatorBombWall(),
    'Lower Mushrooms Left': lambda sm: sm.canPassCrateriaGreenPirates(),
    'Gauntlet Top': lambda sm: sm.haveItem('Morph')
}
locationsDict["Energy Tank, Terminator"].Available = (lambda sm: SMBool(True))
locationsDict["Reserve Tank, Brinstar"].AccessFrom = {
    'Green Brinstar Elevator':
    lambda sm: sm.wor(RomPatches.has(RomPatches.BrinReserveBlueDoors),
                      sm.traverse('MainShaftRight'))
}
 def xBossesDead(smbm, target):
     count = sum([1 for boss in Bosses.Golden4() if Bosses.bossDead(smbm, boss)])
     return SMBool(count >= target)
 def xMiniBossesDead(smbm, target):
     count = sum([1 for miniboss in Bosses.miniBosses() if Bosses.bossDead(smbm, miniboss)])
     return SMBool(count >= target)
Beispiel #12
0
 def haveItems(self, items):
     for item in items:
         if not self.haveItem(item):
             return smboolFalse
     return SMBool(True)
Beispiel #13
0
 def _setKnowsFunction(self, knows, k):
     setattr(self, 'knows' + knows,
             lambda: SMBool(k.bool, k.difficulty, knows=[knows]))
Beispiel #14
0
 def hasItemsPercent(self, percent, totalItemsCount=None):
     if totalItemsCount is None:
         totalItemsCount = self.objectives.getTotalItemsCount()
     currentItemsCount = self.getCollectedItemsCount()
     return SMBool(100 * (currentItemsCount / totalItemsCount) >= percent)
    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.curGraphTransitions,
            "patches": RomPatches.ActivePatches,
            "doors": DoorsManager.serialize()
        }

        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"]
                    if itemName == "Boss":
                        itemName = "Nothing"
                    loc.itemName = itemName
                    loc.accessPoint = itemLoc["Location"]["accessPoint"]
                    self.collectMajor(loc)
              "kill spore spawn", "kill botwoon", "kill crocomire",
              "kill golden torizo", "kill one miniboss",
              "kill two minibosses", "kill three minibosses"
          ]
      },
      items=["SporeSpawn", "Botwoon", "Crocomire", "GoldenTorizo"],
      text="{} all mini bosses",
      expandableList=[
          "kill spore spawn", "kill botwoon", "kill crocomire",
          "kill golden torizo"
      ],
      category="Minibosses"),
 Goal(
     "finish scavenger hunt",
     "other",
     lambda sm, ap: SMBool(True),
     "scavenger_hunt_completed",
     exclusion={"list": []},  # will be auto-completed
     available=False),
 Goal("nothing",
      "other",
      lambda sm, ap: Objectives.canAccess(sm, ap, "Landing Site"),
      "nothing_objective",
      escapeAccessPoints=(1, ["Landing Site"])
      ),  # with no objectives at all, escape auto triggers only in crateria
 Goal("collect 25% items",
      "items",
      lambda sm, ap: SMBool(True),
      "collect_25_items",
      exclusion={
          "list":
Beispiel #17
0
 def canPassZebetites(self):
     sm = self.smbm
     return sm.wor(sm.wand(sm.haveItem('Ice'), sm.knowsIceZebSkip()),
                   sm.wand(sm.haveItem('SpeedBooster'), sm.knowsSpeedZebSkip()),
                   # account for one zebetite, refill may be necessary
                   SMBool(self.canInflictEnoughDamages(1100, charge=False, givesDrops=False, ignoreSupers=True)[0] >= 1, 0))
 def canAccess(sm, src, dst):
     return SMBool(
         Objectives.graph.canAccess(sm, src, dst, Objectives.maxDiff))
 def isVanillaCroc(self):
     crocRoom = getAccessPoint('Crocomire Room Top')
     return SMBool(crocRoom.ConnectedTo == 'Crocomire Speedway Bottom')
 def canAccessLocation(sm, ap, locName):
     loc = locationsDict[locName]
     availLocs = Objectives.graph.getAvailableLocations([loc], sm,
                                                        Objectives.maxDiff,
                                                        ap)
     return SMBool(loc in availLocs)
 def has(patch):
     return SMBool(patch in RomPatches.ActivePatches)
Beispiel #22
0
 def has(player, patch):
     return SMBool(patch in RomPatches.ActivePatches[player])