class DynamicCommander(Commander): """ Initializes everything that the commander needs. This includes loading the different rulesets, distributing the roles and generating scripts for the bots. """ def initialize(self): self.verbose = True self.statistics = Statistics() self.statistics.initialize(self) self.knowledge = Knowledge() self.knowledge.initialize(self) #todo: make this more generic, # i.e. allow for an arbitrary number of roles # load all rule bases self.loadMetaRules() self.loadAttackerRules() self.loadDefenderRules() self.loadCatcherRules() # distribute the roles to the bots roleList = self.metaScript.runDynamicScript([len(self.game.team.members),self.statistics]) if(roleList != None): self.distributeRoles(roleList) else: #If none of the rules apply, use a mixed team. self.distributeRoles(metaRules.mixedAttackers(len(self.game.team.members))) # and generate the corresponding scripts self.initializeRoles() self.initializeBotStats() """ Loads the meta rules with their weights. """ def loadMetaRules(self): self.log.info("Loading the meta rules") conn = open(sys.path[0]+"/dynamicscripting/meta.txt",'r') self.metaRuleBase = jsonpickle.decode(conn.read()) conn.close() # Generate an initial meta script self.metaScript = DynamicScriptingInstance(DynamicScriptingClass(self.metaRuleBase,"metaRules")) self.metaScript.generateScript(3) self.metaScript.printScript() """ Loads the attacker rules with their weights. """ def loadAttackerRules(self): self.log.info("Loading the attacker rules") conn = open(sys.path[0]+"/dynamicscripting/attacker.txt",'r') self.attackerRulebase = jsonpickle.decode(conn.read()) conn.close() """ Loads the defender rules with their weights. """ def loadDefenderRules(self): self.log.info("Loading the defender rules") conn = open(sys.path[0]+"/dynamicscripting/defender.txt",'r') self.defenderRulebase = jsonpickle.decode(conn.read()) conn.close() """ Loads the catcher rules with their weights. """ def loadCatcherRules(self): self.log.info("Loading the catcher rules") conn = open(sys.path[0]+"/dynamicscripting/catcher.txt",'r') self.catcherRulebase = jsonpickle.decode(conn.read()) conn.close() """ Distributes the roles to the bots according to a given distribution. """ def distributeRoles(self,roleList): self.log.info("Distributing the roles") self.log.info("Rolelist: "+ str(roleList)) #assign each bot a role for botIndex in range(len(roleList)): self.game.team.members[botIndex].role = roleList[botIndex] def initializeRoles(self): self.log.info("Initializing roles") # load the rulebases self.dsclassAttacker = DynamicScriptingClass(self.attackerRulebase,"attackerRules") self.dsclassDefender = DynamicScriptingClass(self.defenderRulebase,"defenderRules") self.dsclassCatcher = DynamicScriptingClass(self.catcherRulebase,"catcherRules") i = 1 #assign each bot a role, depending on its role for bot in self.game.team.members: bot.id = i self.log.info("Bot #" + str(i) + ": Generating " + bot.role + " script") if(bot.role == "attacker"): bot.script = DynamicScriptingInstance(self.dsclassAttacker,botRole = bot.role,botNumber = i) bot.script.generateScript(4) # add default rule bot.script.insertInScript(Rule(attackerRules.default_attacker_rule)) elif(bot.role == "defender"): bot.script = DynamicScriptingInstance(self.dsclassDefender,botRole = bot.role,botNumber = i) bot.script.generateScript(4) bot.script.insertInScript(Rule(defenderRules.default_defender_rule)) else: #if(bot.role == "catcher"): #backup bot.script = DynamicScriptingInstance(self.dsclassCatcher,botRole = bot.role,botNumber = i) bot.script.generateScript(4) #for i in bot.script.rules: #to check if there are different type of rules at base # print(i.rule_type) bot.script.insertInScript(Rule(catcherRules.default_catcher_rule)) bot.script.printScript() self.log.info("") i += 1 """ Initialises the statistics for the bots. """ def initializeBotStats(self): self.timeSinceLastCommand = {} # used to detect bots that are stuck for bot in self.game.team.members: resetBotStats(bot) self.timeSinceLastCommand[bot.id] = 0 """ Updates the weights for all bots and the meta script in accordance with the procedure given in Spronck's paper. """ def updateWeights(self): self.log.info("Updating weights!") self.metaScript.adjustWeights(self.metaScript.calculateTeamFitness(self.knowledge),self) for bot in self.game.team.members: fitness = bot.script.calculateAgentFitness(bot, self.knowledge) self.log.info("Bot #" + str(bot.id) + "[" + bot.role + "] fitness:" + str(fitness)) bot.script.adjustWeights(fitness,self) """ Save all the weights back to the files. """ def saveWeights(self): self.saveWeightsRulebase(self.attackerRulebase,"attacker") self.saveWeightsRulebase(self.defenderRulebase,"defender") self.saveWeightsRulebase(self.catcherRulebase,"catcher") self.saveWeightsRulebase(self.metaRuleBase,"meta") """ save a certain rulebase """ def saveWeightsRulebase(self,rulebase,name): conn = open(sys.path[0] + "/dynamicscripting/" + name + ".txt",'w') funcBackup = [] # Replace function by function names for rule in rulebase: funcBackup.append(rule.func) rule.func = rule.func.__name__ rulebaseEncoded = jsonpickle.encode(rulebase) conn.write(rulebaseEncoded) conn.close() # Put them back. for i in range(len(rulebase)): rulebase[i].func = funcBackup[i] """ Update the statistics for the bots. Statistics are used to calculate bot fitness """ def updateBotStats(self): for event in self.game.match.combatEvents: if event.type == event.TYPE_FLAG_RESTORED: if event.subject.team == self.game.team: for bot in self.game.team.members: bot.flag_restored += 1 continue elif event.type == event.TYPE_FLAG_CAPTURED: if event.subject.team == self.game.team: for bot in self.game.team.members: bot.flag_stolen += 1 continue if event.instigator == None: continue if event.instigator.team == self.game.team: if event.type == event.TYPE_KILLED: event.instigator.kills += 1 elif event.type == event.TYPE_FLAG_PICKEDUP: event.instigator.flag_pickedup += 1 elif event.type == event.TYPE_FLAG_DROPPED: event.instigator.flag_dropped += 1 elif event.type == event.TYPE_FLAG_CAPTURED: event.instigator.flag_captured += 1 else: if event.type == event.TYPE_KILLED: event.subject.deaths += 1 """ This method is executed every game tick. It updates the statistics, it can switch the team composition and it gives new orders to the bots (based on their own scripts). """ def tick(self): # self.log.info("Tick at time " + str(self.game.match.timePassed) + "!") self.statistics.tick() # Update statistics self.knowledge.tick() # Update knowledge base # update bot stats self.updateBotStats() # should the commander issue a new strategy? # TODO: # 1. make distributeRoles take the current distribution into account # 2. Let initializeRoles take currently good performing scripts for roles into account roleList = self.metaScript.runDynamicScript([len(self.game.team.members),self.statistics]) if roleList != None: self.log.info("Switching tactics and adjusting weights") self.updateWeights() self.distributeRoles(roleList) self.initializeRoles() # Generate a new script based on latest weights for the next time. self.metaScript = DynamicScriptingInstance(DynamicScriptingClass(self.metaRuleBase,"metaRules")) self.metaScript.generateScript(3) # a bot that has the flag needs to be a catcher for bot in self.game.team.members: if bot.flag is not None and bot.role != "catcher": bot.role = "catcher" bot.script = DynamicScriptingInstance(self.dsclassCatcher,botRole = bot.role,botNumber = bot.id) bot.script.generateScript(4) bot.script.insertInScript(Rule(catcherRules.default_catcher_rule)) # give the bots new commands for bot in self.game.bots_available: bot.script.runDynamicScript([bot,self,self.knowledge]) self.timeSinceLastCommand[bot.id] = self.game.match.timePassed # detect bots that are stuck for bot in self.game.team.members: # more than 30 seconds (90 ticks) since bot has been available # it is probably stuck doing something silly, so reactivate it. if self.game.match.timePassed - self.timeSinceLastCommand[bot.id] > 30: print "REACTIVATING BOT", bot.id bot.script.runDynamicScript([bot,self,self.knowledge]) self.timeSinceLastCommand[bot.id] = self.game.match.timePassed """ This method is executed at the end of the game. """ def shutdown(self): self.updateWeights() self.saveWeights() print "statistics:",self.statistics.ourScore,"-",self.statistics.theirScore
class Sugarscape(): #---------------------------------------------------------------------------# # Tile Data # #---------------------------------------------------------------------------# class Tile(): def __init__(self, sugar, spice): self.sugar = float(sugar) self.spice = float(spice) self.max_sugar = self.sugar self.max_spice = self.spice self.pollution = 0.0 self.agent = None self.region = None class Season(): # Seasons is an array of dictionaries where each dictionary where each # dictionary defines a season. A season defines growth rates for a # number of ticks. Each dictionary should contain: # ["ticks"] the number of ticks for the rates to be active # ["sugar_growth"] the sugar growth # ["spice_growth"] the spice growth # # When a season has been active for its defined number of ticks, the # current season is set to the next season in the array, wrapping back # to the start if necessary. def __init__(self, ticks, sugar_growth, spice_growth): self.ticks = ticks self.sugar_growth = sugar_growth self.spice_growth = spice_growth class Region(): # x1, y1, x2, y2 specify a rectangular region of tiles that make up the # region. # # Tiles are inclusive. # x1 < x2 and y1 < y2 # # seasons is an array of Seasons. Initially the current_season is set to # the first Season in the array. # When a season has been active for its defined number of ticks, the # current season is set to the next season in the array, wrapping back # to the start if necessary. def __init__(self, seasons): #self.x1, self.y1 = x1, y1 #self.x2, self.y2 = x2, y2 if seasons: self.seasons = seasons else: self.seasons = [Sugarscape.Season(1000, 1, 1)] self.time = 0 self.current_index = 0 self.current_season = self.seasons[0] def tick(self): if self.time >= self.current_season.ticks: self.time = 0 self.current_index += 1 if self.current_index >= len(self.seasons): self.current_index = 0 self.current_season = self.seasons[self.current_index] self.time += 1 #---------------------------------------------------------------------------# # # # initialization/shutdown # # # #---------------------------------------------------------------------------# def __init__(self): self.window = sf.RenderWindow() def init(self): self.rand = Random() # tilemap self.loadData("sugarscape.yaml") data = self.data_file # agent properties self.num_agents = data["num_agents"] self.vision_range = data["vision_range"] self.sugar_metabolism_range = data["sugar_metabolism_range"] self.spice_metabolism_range = data["spice_metabolism_range"] self.initial_sugar_range = data["initial_sugar_range"] self.initial_spice_range = data["initial_spice_range"] self.max_age_range = data["max_age_range"] self.m_fertile_start_range = data["m_fertile_start_range"] self.m_fertile_end_range = data["m_fertile_end_range"] self.f_fertile_start_range = data["f_fertile_start_range"] self.f_fertile_end_range = data["f_fertile_end_range"] self.num_culture_tags = data["num_culture_tags"] # agent mutations self.vision_mutation_chance = data["vision_mutation_chance"] self.global_vision_range = data["global_vision_range"] self.vision_mutation_range = data["vision_mutation_range"] self.sugar_metabolism_mutation_chance = data["sugar_metabolism_mutation_chance"] self.global_sugar_metabolism_range = data["global_sugar_metabolism_range"] self.sugar_metabolism_mutation_range = data["sugar_metabolism_mutation_range"] self.spice_metabolism_mutation_chance = data["spice_metabolism_mutation_chance"] self.global_spice_metabolism_range = data["global_spice_metabolism_range"] self.spice_metabolism_mutation_range = data["spice_metabolism_mutation_range"] self.age_mutation_chance = data["age_mutation_chance"] self.global_age_range = data["global_age_range"] self.age_mutation_range = data["age_mutation_range"] # pollution properties self.pollution_ticks = data["pollution_ticks"] self.pollution_sugar_metabolism = data["pollution_sugar_metabolism"] self.pollution_spice_metabolism = data["pollution_spice_metabolism"] self.pollution_harvest = data["pollution_harvest"] self.pollution_decay = data["pollution_decay"] # diseases self.diseases = [] self.num_initial_diseases = data["num_initial_diseases"] self.disease_string_length_range = data["disease_string_length_range"] self.disease_extra_sugar_range = data["disease_extra_sugar_range"] self.disease_extra_spice_range = data["disease_extra_spice_range"] self.disease_infliction_ticks = data["disease_infliction_ticks"] self.disease_infliction_agents = data["disease_infliction_agents"] self.next_disease_infliction_tick = self.disease_infliction_ticks for i in range(self.num_initial_diseases): d = Disease(self.rand, self.rand.randint(self.disease_string_length_range[0], self.disease_string_length_range[1]), self.rand.randint(self.disease_extra_sugar_range[0], self.disease_extra_sugar_range[1]), self.rand.randint(self.disease_extra_spice_range[0], self.disease_extra_spice_range[1])) self.diseases.append(d) # simulation variables self.current_tick = 0 self.next_pollution_tick = self.pollution_ticks - 1 self.num_births = 0 self.num_deaths = 0 self.death_causes = {} self.paused = False # agents self.agents = [] for i in range(min(self.tiles_x * self.tiles_y, self.num_agents)): args = { "sugar_metabolism" : self.rand.randint(self.sugar_metabolism_range[0], self.sugar_metabolism_range[1]), "spice_metabolism" : self.rand.randint(self.spice_metabolism_range[0], self.spice_metabolism_range[1]), "vision" : self.rand.randint(self.vision_range[0], self.vision_range[1]), "sugar" : self.rand.randint(self.initial_sugar_range[0], self.initial_sugar_range[1]), "spice" : self.rand.randint(self.initial_spice_range[0], self.initial_spice_range[1]), "max_age" : self.rand.randint(self.max_age_range[0], self.max_age_range[1]), "gender" : self.rand.choice(("male", "female")), } if args["gender"] == "male": fertile_start_range = self.m_fertile_start_range fertile_end_range = self.m_fertile_end_range else: fertile_start_range = self.f_fertile_start_range fertile_end_range = self.f_fertile_end_range args["fertile_age_start"] = self.rand.randint(fertile_start_range[0], fertile_start_range[1]) args["fertile_age_end"] = self.rand.randint(fertile_end_range[0], fertile_end_range[1]) args["culture_tags"] = [self.rand.choice((0, 1)) for _ in range(0, self.num_culture_tags)] args["culture"] = "BasicCulture" self.rand.shuffle(self.diseases) args["diseases"] = self.diseases[:data["num_agent_diseases"]] args["immune_sys"] = [self.rand.choice((0, 1)) for _ in range(0, 50)] # find an unoccupied tile location_taken = True while location_taken: x = self.rand.randint(0, self.tiles_x-1) y = self.rand.randint(0, self.tiles_y-1) location_taken = self.isTileOccupied(x, y) args["x"], args["y"] = x, y self.spawnAgent(args) # rendering self.window.Create(sf.VideoMode(800, 600), "sugarscape") self.window.SetActive(True) self.window.PreserveOpenGLStates(True) self.window.SetFramerateLimit(60) self.cam = Camera(0, 0, 800, 600) self.resized(800, 600) self.resetCamera() glDisable(GL_DEPTH_TEST) self.tile_size = 10 self.agent_size = 6 self.draw_tiles_func = self.drawSugarTiles self.draw_agents_func = self.drawAgentsGender # statistics self.stats = Statistics(self) self.data_vis = DataVis(self.stats) # Gather stats for the initial simulation state (tick 0). # Do this here since the simulation is ticked, then stats are gathered # for it and displayed. self.stats.tick() def deinit(self): self.window.Close() #---------------------------------------------------------------------------# # # # main loop/ticking # # # #---------------------------------------------------------------------------# def run(self): self.init() self.running = True while self.running: self.doEvents() if not self.paused: self.tick() self.stats.tick() self.data_vis.tick() self.draw() self.data_vis.draw() self.deinit() def doEvents(self): event = sf.Event() while self.window.GetEvent(event): if event.Type in [sf.Event.KeyPressed, sf.Event.KeyReleased]: self.keyEvent(event) elif event.Type == sf.Event.Closed: self.running = False elif event.Type == sf.Event.Resized: self.resized(event.Size.Width, event.Size.Height) #---------------------------------------------------------------------------# # input events # #---------------------------------------------------------------------------# def keyEvent(self, evt): c = evt.Key.Code shift, ctrl, alt = evt.Key.Shift, evt.Key.Control, evt.Key.Alt if evt.Type == sf.Event.KeyPressed: if c == sf.Key.V: self.data_vis.setRunning(not self.data_vis.running) elif c == sf.Key.Space: self.paused = not self.paused elif c == sf.Key.Escape: self.running = False # tile drawing modes elif c == sf.Key.Q: print "draw sugar" self.draw_tiles_func = self.drawSugarTiles elif c == sf.Key.W: print "draw spice" self.draw_tiles_func = self.drawSpiceTiles elif c == sf.Key.E: print "draw occupied" self.draw_tiles_func = self.drawOccupiedTiles elif c == sf.Key.R: print "draw pollution" self.draw_tiles_func = self.drawPollution # agent drawing modes elif c == sf.Key.Num1: print "draw agents" self.draw_agents_func = self.drawAgents elif c == sf.Key.Num2: print "draw agents gender" self.draw_agents_func = self.drawAgentsGender elif c == sf.Key.Num3: print "draw agents culture" self.draw_agents_func = self.drawAgentsCulture elif c == sf.Key.Num4: print "draw agents sugar" self.draw_agents_func = self.drawAgentsSugar elif c == sf.Key.Num5: print "draw agents spice" self.draw_agents_func = self.drawAgentsSpice elif c == sf.Key.Num6: print "draw agents health" self.draw_agents_func = self.drawAgentsHealth elif c == sf.Key.F1: img = self.window.Capture() num = 0 name = "screenshots/" + str(num).zfill(6) + ".png" while os.path.exists(name): num = num + 1 name = "screenshots/" + str(num).zfill(6) + ".png" print "saving screenshot '{0}'".format(name) img.SaveToFile(name) if(self.data_vis.running): img = self.data_vis.window.Capture() name = "screenshots/" + str(num).zfill(6) + "_data.png" img.SaveToFile(name) #---------------------------------------------------------------------------# # ticking # #---------------------------------------------------------------------------# def tick(self): self.num_births = 0 self.num_deaths = 0 self.death_causes.clear() for r in self.regions.itervalues(): r.tick() self.growResourcesRegional() if self.next_pollution_tick <= self.current_tick: self.pollutionDecay() self.pollutionDiffusion() self.next_pollution_tick = self.current_tick + self.pollution_ticks if self.current_tick >= self.next_disease_infliction_tick: self.inflictDiseaseOnAgents() for a in self.agents: a.tick() if a.dead: self.setAgentAt(None, a.x, a.y) self.num_deaths += 1 self.death_causes.setdefault(a.death_cause, 0) self.death_causes[a.death_cause] += 1 self.agents = [a for a in self.agents if not a.dead] self.rand.shuffle(self.agents) self.current_tick += 1 #print "births={0} deaths={1}".format(self.num_births, self.num_deaths) if self.death_causes: print self.death_causes #---------------------------------------------------------------------------# # # # Tilemap functions # # # #---------------------------------------------------------------------------# def loadData(self, data_file_path): self.data_file = yaml.load(file(data_file_path, 'r')) if "random_seed" in self.data_file: seed = self.data_file["random_seed"] else: seed = self.rand.randint(0, sys.maxint) print "seeding RNG with {0}".format(seed) self.rand.seed(seed) sugar_img, spice_img, region_img = sf.Image(), sf.Image(), sf.Image() sugar_img.LoadFromFile(self.data_file["sugar_file"]) spice_img.LoadFromFile(self.data_file["spice_file"]) region_img.LoadFromFile(self.data_file["regions_file"]) self.tiles_x = sugar_img.GetWidth() self.tiles_y = spice_img.GetHeight() self.tiles = [] self.max_sugar_level = 1.0 self.max_spice_level = 1.0 for x in range(self.tiles_x): self.tiles.append([]) for y in range(self.tiles_y): c_sugar = sugar_img.GetPixel(x, sugar_img.GetHeight()-y-1) c_spice = spice_img.GetPixel(x, spice_img.GetHeight()-y-1) t = Sugarscape.Tile(c_sugar.r, c_spice.r) self.tiles[x].append(t) self.max_sugar_level = float(max(self.max_sugar_level, t.max_sugar)) self.max_spice_level = float(max(self.max_spice_level, t.max_spice)) #s1 = Sugarscape.Season(50, 1.0, 1.0) #s2 = Sugarscape.Season(50, 1.0, 1.0) #r1 = Sugarscape.Region(0,0,self.tiles_x-1,(self.tiles_y-1)/2, [s1, s2]) #r2 = Sugarscape.Region(0,(self.tiles_y-1)/2,self.tiles_x-1,self.tiles_y-1, [s2, s1]) #self.regions = [] #self.regions.append(r1) #self.regions.append(r2) #for r in self.regions: # for x in range(r.x1, r.x2+1): # for y in range(r.y1, r.y2+1): # self.tiles[x][y].region = r seasons = {} for s_data in self.data_file["seasons"]: seasons[s_data["name"]] = Sugarscape.Season(s_data["ticks"], s_data["sugar_growth"], s_data["spice_growth"]) self.regions = {} for r_data in self.data_file["regions"]: S = [] for s_name in r_data["season_names"]: if s_name in seasons: S.append(seasons[s_name]) if not S: print "region {0} has no associated seasons!".format(r_data["region_id"]) self.regions[r_data["region_id"]] = Sugarscape.Region(S) for x in range(self.tiles_x): for y in range(self.tiles_y): r_id = region_img.GetPixel(x, region_img.GetHeight()-y-1).r if r_id in self.regions: self.tiles[x][y].region = self.regions[r_id] else: print "tile[{0}][{1}] has no associated region!".format(x, y) def growResourcesGlobal(self, growth): for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] t.sugar = min(t.sugar + growth, t.max_sugar) t.spice = min(t.spice + growth, t.max_spice) def growResourcesRegional(self): for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] g_sugar = t.region.current_season.sugar_growth g_spice = t.region.current_season.spice_growth t.sugar = min(t.sugar + g_sugar, t.max_sugar) t.spice = min(t.spice + g_spice, t.max_spice) def pollutionDiffusion(self): averages = util.create2DArray(self.tiles_x, self.tiles_y, 0.0) for x in range(self.tiles_x): for y in range(self.tiles_y): avg = self.getTileAt(x-1, y).pollution avg += self.getTileAt(x+1, y).pollution avg += self.getTileAt(x, y-1).pollution avg += self.getTileAt(x, y+1).pollution averages[x][y] = avg / 4.0 for x in range(self.tiles_x): for y in range(self.tiles_y): self.getTileAt(x, y).pollution = averages[x][y] def pollutionDecay(self): for x in range(self.tiles_x): for y in range(self.tiles_y): p = self.tiles[x][y].pollution self.tiles[x][y].pollution = max(p - self.pollution_decay, 0) def harvestSugarAt(self, x, y): t = self.getTileAt(x, y) s = t.sugar t.sugar = 0 self.addPollution(x, y, self.pollution_harvest * s) return s def harvestSpiceAt(self, x, y): t = self.getTileAt(x, y) s = t.spice t.spice = 0 self.addPollution(x, y, self.pollution_harvest * s) return s def getTileAt(self, x, y): return self.tiles[x % self.tiles_x][y % self.tiles_y] def getSugarAt(self, x, y): return self.getTileAt(x, y).sugar def getSpiceAt(self, x, y): return self.getTileAt(x, y).spice def getPollutionAt(self, x, y): return self.getTileAt(x, y).pollution def addPollution(self, x, y, amount): self.getTileAt(x, y).pollution += amount def addSugarMetabolismPollution(self, x, y, sugar): self.addPollution(x, y, self.pollution_sugar_metabolism * sugar) def addSpiceMetabolismPollution(self, x, y, spice): self.addPollution(x, y, self.pollution_spice_metabolism * spice) def spawnAgent(self, args): a = Agent(self, **args) self.agents.append(a) self.setAgentAt(a, a.x, a.y) self.num_births += 1 return a def setAgentAt(self, agent, x, y): self.getTileAt(x, y).agent = agent return x % self.tiles_x, y % self.tiles_y def getAgentAt(self, x, y): return self.getTileAt(x, y).agent def getNeighbourAgents(self, x, y): ns = [ self.getAgentAt(x-1, y), self.getAgentAt(x+1, y), self.getAgentAt(x, y-1), self.getAgentAt(x, y+1), ] return [a for a in ns if a is not None] def updateAgentPosition(self, agent, old_x, old_y, new_x, new_y): self.setAgentAt(None, old_x, old_y) return self.setAgentAt(agent, new_x, new_y) def isTileOccupied(self, x, y): return self.getAgentAt(x, y) is not None def tileHasEmptyNeighbour(self, x, y): return (self.getAgentAt(x-1, y) is None or self.getAgentAt(x+1, y) is None or self.getAgentAt(x, y-1) is None or self.getAgentAt(x, y+1) is None) def getEmptyNeighbourTiles(self, x, y): n_locs = [(x-1, y), (x+1, y), (x, y-1), (x, y+1)] return [l for l in n_locs if not self.isTileOccupied(l[0], l[1])] def getWorldSize(self): return self.tiles_x, self.tiles_y #---------------------------------------------------------------------------# # # # Agent functions # # # #---------------------------------------------------------------------------# def getNumAgents(self): return len(self.agents) def inflictDiseaseOnAgents(self): self.next_disease_infliction_tick = self.current_tick + self.disease_infliction_ticks if not self.agents: return for i in range(self.disease_infliction_agents): a = self.rand.choice(self.agents) d = self.rand.choice(self.diseases) a.infect(d) #---------------------------------------------------------------------------# # # # drawing/window functions # # # #---------------------------------------------------------------------------# def draw(self): self.window.SetActive(True) glLoadIdentity() glClear(GL_COLOR_BUFFER_BIT) # center the world if smaller than the screen win_w, win_h = self.window.GetWidth(), self.window.GetHeight() world_w, world_h = self.getWorldSize() world_w *= self.tile_size world_h *= self.tile_size if world_w < win_w: glTranslatef((win_w - world_w) / 2, 0.0, 0.0) if world_h < win_h: glTranslatef(0.0, (win_h - world_h) / 2, 0.0) # apply camera transform glTranslatef(-self.cam.x, -self.cam.y, 0.0) # draw the world self.draw_tiles_func() self.draw_agents_func() # swap buffers self.window.Display() #print self.window.GetFrameTime() def drawSugarTiles(self): s = self.tile_size glBegin(GL_QUADS) for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] glColor3f(t.sugar/self.max_sugar_level, t.sugar/self.max_sugar_level, 0.0) glVertex2f(x*s, y*s) glVertex2f((x+1)*s, y*s) glVertex2f((x+1)*s, (y+1)*s) glVertex2f(x*s, (y+1)*s) glEnd() def drawSpiceTiles(self): s = self.tile_size glBegin(GL_QUADS) for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] glColor3f(t.spice/self.max_spice_level, t.spice/self.max_spice_level, 0.0) glVertex2f(x*s, y*s) glVertex2f((x+1)*s, y*s) glVertex2f((x+1)*s, (y+1)*s) glVertex2f(x*s, (y+1)*s) glEnd() def drawOccupiedTiles(self): s = self.tile_size glColor3ub(255, 0, 0) glBegin(GL_QUADS) for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] if self.isTileOccupied(x, y): glVertex2f(x*s, y*s) glVertex2f((x+1)*s, y*s) glVertex2f((x+1)*s, (y+1)*s) glVertex2f(x*s, (y+1)*s) glEnd() def drawPollution(self): s = self.tile_size glBegin(GL_QUADS) for x in range(self.tiles_x): for y in range(self.tiles_y): t = self.tiles[x][y] c = t.pollution/255.0 glColor3f(c,c,c) glVertex2f(x*s, y*s) glVertex2f((x+1)*s, y*s) glVertex2f((x+1)*s, (y+1)*s) glVertex2f(x*s, (y+1)*s) glEnd() def drawAgents(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: glColor3ub(0, 0, 255) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def drawAgentsGender(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: if a.gender == "male": glColor3ub(24, 116, 205) else: glColor3ub(255, 20, 147) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def drawAgentsCulture(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: cul = a.culture.getGroup() col = a.culture.COLORS.get(cul, (1.0, 1.0, 1.0, 1.0)) glColor4f(col[0], col[1], col[2], col[3]) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def drawAgentsSugar(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: max_s = 100.0 s = max(min(a.sugar, max_s), 0.0) s /= max_s r, g = (1.0 - s), s glColor4f(r, g, 0.0, 1.0) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def drawAgentsSpice(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: max_s = 100.0 s = max(min(a.spice, max_s), 0.0) s /= max_s r, g = (1.0 - s), s glColor4f(r, g, 0.0, 1.0) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def drawAgentsHealth(self): t_s = self.tile_size a_s = self.agent_size off = (t_s - a_s) / 2 glBegin(GL_QUADS) for a in self.agents: if a.diseases: glColor4f(1.0, 0.0, 0.0, 1.0) else: glColor4f(0.0, 1.0, 0.0, 1.0) x,y = a.x*t_s+off, a.y*t_s+off glVertex2f(x, y) glVertex2f(x+a_s, y) glVertex2f(x+a_s, y+a_s) glVertex2f(x, y+a_s) glEnd() def resetCamera(self): win_w, win_h = self.window.GetWidth(), self.window.GetHeight() world_w, world_h = self.getWorldSize() self.cam.width = min(win_w, world_w) self.cam.height = min(win_h, world_h) def resized(self, width, height): self.window.SetView(sf.View(sf.FloatRect(0, 0, width, height))) self.window.SetActive(True) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, width, 0, height, -1, 1) glViewport(0, 0, width, height) glMatrixMode(GL_MODELVIEW)