242 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
	
# object to handle the smbools and optimize them
 | 
						|
 | 
						|
from logic.cache import Cache
 | 
						|
from logic.smbool import SMBool, smboolFalse
 | 
						|
from logic.helpers import Bosses
 | 
						|
from logic.logic import Logic
 | 
						|
from utils.doorsmanager import DoorsManager
 | 
						|
from utils.parameters import Knows, isKnows
 | 
						|
import logging
 | 
						|
import sys
 | 
						|
 | 
						|
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, player=0, maxDiff=sys.maxsize):
 | 
						|
        self._items = { }
 | 
						|
        self._counts = { }
 | 
						|
 | 
						|
        self.player = player
 | 
						|
        self.maxDiff = maxDiff
 | 
						|
 | 
						|
        # 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 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, player):
 | 
						|
        # for each knows we have a function knowsKnows (ex: knowsAlcatrazEscape()) which
 | 
						|
        # take no parameter
 | 
						|
        for knows in Knows.__dict__:
 | 
						|
            if isKnows(knows):
 | 
						|
                if knows in Knows.knowsDict[player].__dict__:
 | 
						|
                    setattr(self, 'knows'+knows, lambda knows=knows: SMBool(Knows.knowsDict[player].__dict__[knows].bool,
 | 
						|
                                                                            Knows.knowsDict[player].__dict__[knows].difficulty,
 | 
						|
                                                                            knows=[knows]))
 | 
						|
                else:
 | 
						|
                    # if knows not in preset, use default values
 | 
						|
                    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
 | 
						|
        #self.state.item_count(item, self.player)
 | 
						|
        return self._counts[item]
 | 
						|
 | 
						|
    def haveItem(self, item):
 | 
						|
        #return self.state.has(item, self.player)
 | 
						|
        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
 | 
						|
 | 
						|
class SMBoolManagerPlando(SMBoolManager):
 | 
						|
    def __init__(self):
 | 
						|
        super(SMBoolManagerPlando, self).__init__()
 | 
						|
 | 
						|
    def addItem(self, item):
 | 
						|
        # a new item is available
 | 
						|
        already = self.haveItem(item)
 | 
						|
        isCount = self.isCountItem(item)
 | 
						|
        if isCount or not already:
 | 
						|
            self._items[item] = SMBool(True, items=[item])
 | 
						|
        else:
 | 
						|
            # handle duplicate major items (plandos)
 | 
						|
            self._items['dup_'+item] = True
 | 
						|
        if isCount:
 | 
						|
            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:
 | 
						|
            dup = 'dup_'+item
 | 
						|
            if self._items.get(dup, None) is None:
 | 
						|
                self._items[item] = smboolFalse
 | 
						|
                self.computeNewCacheKey(item, 0)
 | 
						|
            else:
 | 
						|
                del self._items[dup]
 | 
						|
                self.computeNewCacheKey(item, 1)
 | 
						|
 | 
						|
        Cache.update(self.cacheKey)
 |