def getImage(self, zoom=None, x=None, y=None, z=None, callback=None): try: # can't do anything if they don't send the full address if zoom == None or x == None or y == None or z == None: msg = "model-server.getImage: Need full parameters" raise khopeshpy.exceptions.JSONException(msg) # first check that there really isn't an image imageName = "img-world/zoom%d-%dx-%dy-%dz.png" % (int(zoom), int(x), int(y), int(z)) if os.path.isfile(imageName): # file already exists, either it was created by another handler or somebody's mucking around # just return the image name return self.jsonpWrap(imageName, callback) conn = db.connect() # otherwise we have to pull it from the database if int(zoom) == 0: c = db.levelData.columns statment = db.select( [c.baseImage], (c.cellIdX == int(x)) & (c.cellIdY == int(y)) & (c.levelIdZ == int(z)) ) result = conn.execute(statment) level = result.fetchone() result.close() if level == None: ret = "img-src/void.png" else: ret = level["baseImage"] elif int(zoom) == 1: c = db.cellData.columns statment = db.select([c.cellImage], (c.cellIdX == int(x)) & (c.cellIdY == int(y))) result = conn.execute(statment) cell = result.fetchone() result.close() if cell == None: ret = "img-src/void.png" else: ret = cell["cellImage"] else: # note: all zoomed images above level 1 should have been pregenerated ret = "img-src/void.png" conn.close() return self.jsonpWrap(ret, callback) except khopeshpy.exceptions.JSONException as e: return self.errorHandler(e.msg)
def commandNext(self): """ find the next command and set it as the current one """ # get the next command c = db.avatarCmdQueue.columns statement = ( db.select([db.avatarCmdQueue], (c.avatarId == self.avatarId) & (c.done == False)) .order_by(c.sequence.asc()) .limit(1) ) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: # add a default one if there is none self.addCommandDefault() else: if self.mapPreCommand(row["command"])(row["data"]) != False: # only set the command if the precommand succeeded self.commandSet(row["animalCmdId"], row["timeExpected"]) else: # otherwise mark the command as failed and move onto the next one self.markCmdFinished(row["eventId"], False) self.commandNext()
def commandNext(self): ''' find the next command and set it as the current one ''' #get the next command c = db.animalCmdQueue.columns statement = db.select([db.animalCmdQueue], (c.animalId == self.animalId) & (c.done == False)).order_by(c.sequence.asc()).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: #add a default one if there is none self.addCommandDefault() else: #quick point: preCommands can fail (an animal is already being attacked) # how are we going to handle this? #two possibilities for failure, move on to the next command or invalidate # all the commands ##expecting problems with the data field, we're using the same format for ## direct calls as for calls from the database #scratch that, removed the direct call and replaced it with a call to this # function if(self.mapPreCommand(row['command'])(row['data']) != False): #print 'precommand failed: ' + row['command'] #only set the command if the precommand succeeded self.commandSet(row['animalCmdId'], row['timeExpected']) else: #otherwise mark the command as failed and move onto the next one self.markCmdFinished(row['eventId'], False) self.commandNext()
def register(self, username = None, password = None): if username == None or password == None: msg = "User.Control.register needs both a username and a password" print msg return (-1,msg) #first we have to check if a user with that name already exists c = db.userData.columns statment = db.select([db.userData], (c.username == username)) conn = db.connect() result = conn.execute(statment) row = result.fetchone() result.close() conn.close() if row != None: self.valid = False self.loggedIn = False msg = "Player with username: %s already exists" % (username) print msg return (-1, msg) else: conn = db.connect() ins = db.userData.insert().values(username = username, password = password) result = conn.execute(ins) conn.close() #cheap way for now return self.login(username, password)
def pollEventQueue(self): trans = self.conn.begin() try: c1 = db.animalEventQueue.columns c2 = db.animalCmdQueue.columns #find a event that has ended in the eventQueue statement = db.select([c1.eventId, c2.animalCmdId, c2.animalId, c2.timeExpected, c2.timeStarted, c2.command, c2.data],\ (c1.eventEnd < time.time()) & (c1.eventId == c2.eventId)).limit(1) #print statement result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: trans.rollback() return False else: row = dict(row) c = db.animalEventQueue.columns #remove the event from the queue up = db.animalEventQueue.delete().where(c.eventId == (row['eventId'])) result = self.conn.execute(up) #only if we've managed to grab an event self.processEvent(row) trans.commit() return True except Exception as e: print e print "rolling back" trans.rollback() raise
def pollBroken(self): c = db.animalData.columns d = db.animalEventQueue.columns statement = db.select([db.animalData], db.not_(c.eventId.in_(db.select([d.eventId],None)))).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: return False else: row = dict(row) self.broken(row) return True
def commandNextDefault(self): """ find the next command and set it as the current one (this version is only used by addCommandDefault """ # get the next command c = db.avatarCmdQueue.columns statement = ( db.select([db.avatarCmdQueue], (c.avatarId == self.avatarId) & (c.done == False)) .order_by(c.sequence.asc()) .limit(1) ) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: raise Exception("addCommandDefault() did not add any commands") else: if self.mapPreCommand(row["command"])(row["data"]) != False: # only set the command if the precommand succeeded self.commandSet(row["animalCmdId"], row["timeExpected"]) else: print "Command: " + row["command"] + " Data: " + row["data"] raise Exception("addCommandDefault() added a command that could not be executed")
def pollBrokenAvatars(self): c = db.avatarData.columns d = db.avatarEventQueue.columns statement = db.select([db.avatarData], c.eventId != db.select([d.eventId],d.eventId == d.eventId)).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: return False else: row = dict(row) self.brokenAvatar(row) return True
def preCommandAttackCooldown(self, preyId): #check the lock c = db.animalData.columns statement = db.select([db.animalData], c.animalId == preyId) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: #prey no longer exists return False elif row['attackerId'] == self.animalId: #we're already the attacker return True elif row['attackerId'] == None: #lock the prey as ours c = db.animalData.columns up = db.animalData.update().where((c.animalId == preyId)).\ values(attackerId = self.data['animalId']) self.conn.execute(up) return True else: #the prey already has an attacker return False
def login(self, username = None, password = None): if username == None or password == None: msg = "user.Control.login needs both a username and a password" print msg return (-1, msg) c = db.userData.columns statment = db.select([db.userData], (c.username == username) & (c.password == password)) conn = db.connect() result = conn.execute(statment) row = result.fetchone() result.close() conn.close() if row == None: self.loggedIn = False msg = "User failed to login with username: %s password: %s" % (username, password) print msg return (-1, msg) else: self.loggedIn = True self.userId = row['userId'] self.pullData() return (self.userId, 'Ok')
def pollEventQueue(self): trans = self.conn.begin() try: c1 = db.avatarEventQueue.columns c2 = db.avatarCmdQueue.columns #find a event that has ended in the eventQueue statement = db.select([c1.eventId, c2.avatarCmdId, c2.avatarId, c2.timeExpected, c2.timeStarted, c2.command, c2.data],\ (c1.eventEnd < time.time()) & (c1.eventId == c2.eventId)).limit(1) result = self.conn.execute(statement) rows = result.fetchall() result.close() good = False for row in rows: c = db.avatarEventQueue.columns #remove the event from the queue up = db.avatarEventQueue.delete().where(c.eventId == (row['eventId'])) result = self.conn.execute(up) #make sure we were able to delete the row if result.rowcount == 1: #only if we've managed to grab an event self.processEvent(row) good = True trans.commit() return good except Exception as e: print e print "rolling back" trans.rollback() raise
def preCommandScavenge(self, preyId): ''' What to do right before a scavenge command is set ''' #check that the prey.attackerId is free c = db.animalData.columns statement = db.select([db.animalData], c.animalId == preyId) result = self.conn.execute(statement) row = result.fetchone() result.close() #TODO: clean this up if row == None: #prey no longer exists #print "prey id: " + preyId + "no longer exists" #raise Exception("prey no longer exsists") return False elif row['attackerId'] == self.animalId: #we're already the attacker #print "success: we're the attacker" return True elif row['attackerId'] == None: #print "setting self as the atacker" #lock the prey as ours c = db.animalData.columns up = db.animalData.update().where((c.animalId == preyId)).\ values(attackerId = self.data['animalId']) self.conn.execute(up) else: #print "prey's attacker is: " + repr(row['attackerId']) #the prey already has an attacker return False
def selectScavengePrey(self, lvl): ''' Select the best animal to feed on we want to find the animal from which we'll find the most food in the time alotted this can either be limited by the amount of biomass, or by the animal's speed of eating ''' maxBiomass = 0.0 maxPrey = None #get all dead animals in the passed level c = db.animalData.columns statement = db.select([db.animalData], (c.animalX == lvl.cellIdX)\ & (c.animalY == lvl.cellIdY) & (c.animalZ == lvl.levelIdZ)\ & (c.state == base.States.DEAD) & ((c.attackerId == None) | (c.attackerId == self.animalId))) result = self.conn.execute(statement) rows = result.fetchall() result.close() #print "prey: " + repr(self.preySpecies) #select the best one to scavange # note: we could check here if we can break early for row in rows: #print repr(rows) #prey = base.Event(row = row, conn = self.conn) prey = top.eventAnimalFactory(self.conn, row = row) #print "species: " + repr(prey.species) if prey.species in self.preySpecies: #print "prey is a prey species" biomass = (prey.growth + prey.stored) * self.scavengeEfficiency if biomass > maxBiomass: maxBiomass = biomass maxPrey = prey #else: #print "prey is not a prey species" #can't eat more than there is room for room = self.unprocessedMax - self.unprocessed #room in it's stomach amount = min(room, maxBiomass) time = amount / self.scavengeRate if maxBiomass > room: maxBiomass = room if maxBiomass < 0.01: return False else: return (maxPrey, maxBiomass, time)
def pullData(self): #NOTE: selecting old family id's may be a problem if self.familyId > 0: c = db.familyData.columns statment = db.select([db.familyData], c.familyId == self.familyId) conn = db.connect() result = conn.execute(statment) row = result.fetchone() result.close() conn.close() if row == None: msg = "No record found for a family with familyId: %d" % (self.familyId) raise Exception(msg) self.data.update(dict(row)) self.valid = True self.userId = self.data['userId'] elif self.userId > 0: c = db.familyData.columns statment = db.select([db.familyData], (c.userId == self.userId) & (c.state == States.ALIVE)) conn = db.connect() result = conn.execute(statment) row = result.fetchone() result.close() conn.close() if row == None: msg = "No record found for a living family for userId: %d" % (self.userId) raise Exception(msg) self.data.update(dict(row)) self.valid = True self.familyId = self.data['familyId'] else: msg = "Family objects must be initialized with a valid a userId or a valid familyId" raise Exception(msg)
def pullData(self): c = db.userData.columns statment = db.select([db.userData], c.userId == self.userId) result = self.conn.execute(statment) row = result.fetchone() result.close() if row == None: msg = "user.Model.pullData: Did not find userData record for id: %d, exiting." % (self.userId) raise exceptions.JSONException(msg) self.data.update(dict(row))
def getStartingLocation(conn): c = db.cityData.columns statment = db.select([db.cityData], c.cityStarter == True) result = conn.execute(statment) row = result.fetchone() result.close() #just get the first one now, select one randomly later if row != None: return (row['cityStartX'],row['cityStartY'],row['cityStartZ'],) else: return (-1,-1,-1)
def pollIdle(self): c = db.animalData.columns statement = db.select([db.animalData], c.eventId == None).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: return False else: row = dict(row) self.idle(row) return True
def createNew(conn, userId, familyName, avatarName): #first we have to check if a family with that name already exists c = db.familyData.columns statment = db.select([db.familyData], (c.name == familyName)) result = conn.execute(statment) row = result.fetchone() result.close() if row != None: msg = "Family with name: %s already exists" % (familyName) print msg return (-1, msg) else: #Avatar names aren't unique so we don't have to check that. #This is a new family so we don't have to worry about the # second check if an avatar with that name already exits ins = db.familyData.insert()\ .values( userId = userId, name = familyName, state = States.ALIVE ) result = conn.execute(ins) familyId = result.last_inserted_ids()[0] #now that we a family id create the avatar #how to pick the location? # thinking we'll set certain cities to be starter cities (their's # currently a flag to do that....) #woo! cities are defined over a number of cells, just select a random one? avatar.createNew(conn, userId, familyId, avatarName, city.getStartingLocation(conn)) return (familyId, "Ok")
def getPlayerList(self, callback=None): c = db.userData.columns statment = db.select([c.userId, c.username], 1) conn = db.connect() result = conn.execute(statment) rows = result.fetchall() result.close() conn.close() ret = {} for row in rows: ret[row.userId] = row.userName return self.jsonpWrap(ret, callback)
def getControlAvatars(conn, familyId): c = db.avatarData.columns statment = db.select([db.avatarData], c.familyId == familyId) result = conn.execute(statment) rows = result.fetchall() result.close() ret = {} for row in rows: ret[row['avatarId']] = controlAvatarFactory(conn, row = row) return ret
def getNextSequenceNumber(self): """ find the sequence number to add this command to the end of the command queue """ c = db.avatarCmdQueue.columns statement = db.select([db.avatarCmdQueue], (c.avatarId == self.avatarId)).order_by(c.sequence.desc()).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: return 1 if row["sequence"] == None: return 1 else: return row["sequence"] + 1
def getNextSequenceNumber(self): ''' find the sequence number to add this command to the end of the command queue ''' c = db.animalCmdQueue.columns statement = db.select([db.animalCmdQueue], (c.animalId == self.animalId))\ .order_by(c.sequence.desc()).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: return 1 if row['sequence'] == None: return 1 else: return (row['sequence'] + 1)
def preCommandHunt(self, preyId): ''' What to do right before a hunt command is set ''' #check that the prey.attackerId is free c = db.animalData.columns statement = db.select([db.animalData], c.animalId == preyId) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None or row['state'] == base.States.DEAD: #prey no longer exists, or is dead return False elif row['attackerId'] != None and row['attackerId'] != self.data['animalId']: #has has an attacker who isn't us return False #do the actual attack #from khopeshpy.animals import top #prey = top.event(animalId = preyId, conn = self.conn) #prey = base.Event(animalId = preyId, conn = self.conn) prey = top.eventAnimalFactory(self.conn, row = row) #first, attacker gets first strike so do that first self.stamina -= self.attackStaminaCost atkStr = self.attackStrength(self.health) prey.attacked(self.animalId, atkStr) dt = self.attackCooldown(self.stamina) self.addCommandAttackCooldown(dt, preyId) #two options if prey.health > 0.0: #print 'the prey lives' #any code to break off the attack would go here #do we need to be able to set this as un-interruptable? self.addCommandHunt(preyId, 0.0) else: #print 'the prey is dead' #set it to scavange the prey for a token amount of time self.addCommandScavenge(preyId, 1.0)
def pullData(self): if self.userId <= 0: msg = "pullData needs a userId greater than 0, id was: %d" % (self.userId) raise Exception(msg) c = db.userData.columns statment = db.select([db.userData], c.userId == self.userId) conn = db.connect() result = conn.execute(statment) row = result.fetchone() result.close() conn.close() if row == None: msg = "pullData did not find userData record for id: %d" % (self.userId) raise Exception(msg) self.data.update(dict(row)) self.valid = True
def eventAnimalFactory(conn, animalId = None, row = None): ''' Return the correct event animal object if it exists in the registered event animal list ''' if animalId != None: c = db.animalData.columns statment = db.select([db.animalData], c.animalId == animalId) result = conn.execute(statment) row = result.fetchone() result.close() if row == None: msg = "No record found for an animal with animalId: %d" % (animalId) raise Exception(msg) #fall though now that row is populated if row['animalSpecies'] in eventAnimalList: return eventAnimalList[row['animalSpecies']](conn, row) else: msg = "Unknown species: " + repr(row['animalSpecies']) raise Exception(msg)
def commandNextDefault(self): ''' find the next command and set it as the current one (this version is only used by addCommandDefault ''' #get the next command c = db.animalCmdQueue.columns statement = db.select([db.animalCmdQueue], (c.animalId == self.animalId) & (c.done == False)).order_by(c.sequence.asc()).limit(1) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: raise Exception("addCommandDefault() did not add any commands") else: if(self.mapPreCommand(row['command'])(row['data']) != False): #print 'precommand failed: ' + row['command'] #only set the command if the precommand succeeded self.commandSet(row['animalCmdId'], row['timeExpected']) else: print "Command: " + row['command'] + " Data: " + row['data'] raise Exception("addCommandDefault() added a command that could not be executed")
def attacked(self, attackerId, atkStrength): ''' Called by an attacker when it attacks us ''' #being attacked is an interrupt #forage: cancel everything #attack cooldown.... add it to the list of attackers? # defending container needs to handle multiple attackers? # Not yet since we put in the lock, but we may want to account for it anyway #move: cancled #idle: cancled #sleep: cancled #dead: wtf #stack on the attacked command to then end, then delete all interruptable # commands #set the attacker self.data['attackerId'] = attackerId #update our health, can adjust later here for defense self.data['health'] -= atkStrength if self.data['health'] < 0.01: self.data['state'] = base.States.DEAD self.deathCleanup() else: #add the defend command to the end of the queue self.addCmdQueue('defend', 0.0, attackerId) #TODO: generate this automatically interruptibleCommands = ['forage', 'move', 'idle', 'sleep'] #TODO: make sure this is producing the sql we want #remove all interruptible comands c = db.animalCmdQueue.columns up = db.animalCmdQueue.update()\ .where((c.animalId == self.animalId) & (c.done == False) & c.command.in_(interruptibleCommands))\ .values(active = False, done = True, successful = False, timeEnded = time.time()) result = self.conn.execute(up) #pull up the current command, current is different because it has to be # removed from the eventQueue: eventId = self.data['eventId'] c = db.animalCmdQueue.columns statement = db.select([db.animalCmdQueue], c.eventId == eventId) result = self.conn.execute(statement) row = result.fetchone() result.close() if row == None: #...? something went wrong raise Exception('unknown state') if row['command'] in interruptibleCommands: c = db.animalEventQueue.columns up = db.animalEventQueue.delete().where(c.eventId == eventId) result = self.conn.execute(up) #NOTE: need to adjust stats for unfinished command, # also we sould finish up anything we can on the interrupted command #self.updateStats(dt) #execute the next command only if we removed the current command self.commandNext() self.pushData()
def addCommandDefault(self): """ search for the best command for the animal and add it to the command queue """ # death check is done here if self.data["state"] == States.DEAD: cmdId, dt = self.actionMap["dead"]["add"](dt=1.0) # being dead has no preCommand self.commandSet(cmdId, dt) return # get the levels we have access to c = db.levelData.columns # unlike animals, avatars won't automatically move statement = db.select( [db.levelData], (c.cellIdX == self.position[0]) & (c.cellIdY == self.position[1]) & (c.levelIdZ == self.position[2]), ) result = self.conn.execute(statement) rows = result.fetchall() result.close() levels = [] for row in rows: levels.append(level.Event(conn=self.conn, row=row)) # list of sequences of valid actions, parameters, and their need calculation # form of [(needCalc, [(command1, param1), (command2, param2)]] validActionList = [] for lvl in levels: # only need to check if we can move to the level if lvl.walkable: for action in self.actionMap: if "try" in self.actionMap[action]: stateDiff = {"health": 0.0, "stamina": 0.0, "unprocessed": 0.0, "time": 0.0} cmdQueue = self.actionMap[action]["try"](lvl, stateDiff) # if the command is false then it failed for some reason, usually # either ran out of health or stamina if cmdQueue != False and len(cmdQueue) > 0: validActionList.append((self.needCalc(stateDiff), cmdQueue)) # if there was no valid action then kill the animal if len(validActionList) == 0: self.data["state"] = States.DEAD self.deathCleanup() # sort and run the top action queue validActionList.sort(reverse=True) # break out the best action to preform # (don't actually need the needRating) needRating, bestActionSequence = validActionList[0] # iterate over the cmdQueue of the top (best) action, adding each command # in turn # NOTE: could we do this better with closures? for action in bestActionSequence: # the first arguement should be the function to add the command to the # queue, the second should be the arguments to pass to the add command # function cmdId, dt = self.mapAddCommand(action[0])(*action[1]) self.commandNextDefault()
def addCommandDefault(self): ''' search for the best command for the animal and add it to the command queue ''' #death check is done here if self.data['state'] == States.DEAD: cmdId, dt = self.actionMap['dead']['add'](dt = 1.0) #being dead has no preCommand self.commandSet(cmdId, dt) return #get the levels we have access to c = db.levelData.columns statement = db.select([db.levelData], c.cellIdX.between(self.position[0]-1, self.position[0]+1) & c.cellIdY.between(self.position[1]-1, self.position[1]+1) & c.levelIdZ.between(self.position[2]-1, self.position[2]+1)) #statement = db.select([db.levelData], #(c.cellIdX == self.position[0]) & #(c.cellIdY == self.position[1]) & #(c.levelIdZ == self.position[2])) result = self.conn.execute(statement) rows = result.fetchall() result.close() levels = [] for row in rows: levels.append(level.Event(row = row, conn = self.conn)) #list of sequences of valid actions, parameters, and their need calculation #form of [(needCalc, [(command1, param1), (command2, param2)]] validActionList = [] for lvl in levels: #only need to check if we can move to the level if lvl.walkable: #for action in self.getActionList(): for action in self.actionMap: if 'try' in self.actionMap[action]: stateDiff = {'health': 0.0, 'stamina': 0.0, \ 'unprocessed': 0.0, 'time': 0.0} cmdQueue = self.actionMap[action]['try'](lvl, stateDiff) #if the command is false then it failed for some reason, usually # either ran out of health or stamina if cmdQueue != False and len(cmdQueue) > 0: validActionList.append((self.needCalc(stateDiff), cmdQueue)) #NOTE: what happens if the validActionList is empty? pass out or suicide? # right now the only possiblity of this happening is if a level becomes # unwalkable, and (there is either not a walkable level nearby or the\ # animal doesn't have enough stamina to walk there) #if there was no action then kill the animal if len(validActionList) == 0: self.data['state'] = States.DEAD self.deathCleanup() #sort and run the top action queue validActionList.sort(reverse = True) #break out the best action to preform # (don't actually need the needRating) #print repr(validActionList) #pprint.pprint(validActionList) needRating, bestActionSequence = validActionList[0] #iterate over the cmdQueue of the top (best) action, adding each command # in turn #NOTE: could we do this better with closures? for action in bestActionSequence: #the first arguement should be the function to add the command to the # queue, the second should be the arguments to pass to the add command # function #cmdId, dt = action[0](*action[1]) cmdId, dt = self.mapAddCommand(action[0])(*action[1]) #if first: ##if it's the first we want to set the command ##do the preCommand stuff #self.mapPreCommand(action[0])(*action[1]) ## and set the command #self.commandSet(cmdId, dt) #first = False #NOTE: ran into problems while adding the pre command option # Just calling command next after the commands are added # means we don't duplicate code and don't have to have # two interfaces to pre-commands #But gives us one more command against the database # we may want to move it back out again later. # #Finally, this introduces the possibility of an infinite loop because # commandNext calls this function if it can't find any queued commands self.commandNextDefault()
def selectHuntPrey(self, lvl, stateDiff): ''' Select the best animal to attack We want to find the animal which will give the best cost/oppertunity We have to consider how much mass we expect to get if we kill the animal vs. How much mass we'll lose to damage, energy, etc? ''' #print "stateDiff before: " + repr(stateDiff) maxBiomass = 0.0 maxPrey = None #ugh, have to pop these up to this level maxGainedBiomass = None maxNetStamina = None maxNetHealth = None maxNetTime = None #get all live animals, in the passed level, who don't have an attacker # current model can't handle multiple attackers c = db.animalData.columns statement = db.select([db.animalData], (c.animalX == lvl.cellIdX) &\ (c.animalY == lvl.cellIdY) & (c.animalZ == lvl.levelIdZ) &\ (c.state != base.States.DEAD) & (c.attackerId == None) & (c.animalId != self.animalId)) result = self.conn.execute(statement) rows = result.fetchall() result.close() #select the best one to hunt #create a dictionary of cost/advantage? for row in rows: #prey = base.Event(self.conn, animalId = row['animalId']) prey = top.eventAnimalFactory(self.conn, row = row) #only consider if it is a prey species if prey.species in self.preySpecies: #total biomass that will be available having killed the animal gainedBiomass = (prey.growth + prey.stored) * self.scavengeEfficiency #now the hard part #we want to simulate the fight and try to guess how much health we'll lose #the oppertunity cost should be absolute (ratio makes no sense) result = self.simulateFight(self, prey) if result != False: #if we didn't lose the fight lostStamina, lostHealth, lostTime = result netBiomass = gainedBiomass - (lostStamina + lostHealth) if netBiomass > maxBiomass: maxBiomass = netBiomass maxPrey = prey maxGainedBiomass = gainedBiomass maxNetStamina = lostStamina maxNetHealth = lostHealth maxNetTime = lostTime if maxPrey == None: #didn't find anything woth attacking return False #found something we want to attack, adjust the stateDiff stateDiff['stamina'] += maxNetStamina stateDiff['health'] += maxNetHealth stateDiff['time'] += maxNetTime if self.stamina + stateDiff['stamina'] <= 0.0: #ran out of stamina return False elif self.health + stateDiff['health'] <= 0.0: #ran out of health return False #now to calculate how much we gain by scavanging #only test for unprocessedMax because the hunter will be able to sit down and eat until their full #can't eat more than there is room for room = self.unprocessedMax - self.unprocessed #room in it's stomach amount = min(room, maxGainedBiomass) eatingTime = amount / self.scavengeRate #simulate eating until full stateDiff['unprocessed'] += amount stateDiff['time'] += eatingTime #print "stateDiff after: " + repr(stateDiff) totalTime = maxNetTime + eatingTime stateDiff['stamina'] -= self.staminaDrain * totalTime stateDiff['stamina'] -= self.scavengeStaminaCost * eatingTime return maxPrey.data['animalId'], totalTime