def computePath(self,coord): pf = PathFinder(self.map.successors, self.map.move_cost, self.map.move_cost) path_list = list(pf.compute_path(coord, self.goal) ) for i, path_coord in enumerate(path_list): next_i = i if i==len(path_list)-1 else i+1 self.path_cache[path_coord] = path_list[next_i]
def computePath(self, coord): pf = PathFinder(self.map.successors, self.map.move_cost, self.map.move_cost) path_list = list(pf.compute_path(coord, self.goal)) for i, path_coord in enumerate(path_list): next_i = i if i == len(path_list) - 1 else i + 1 self.path_cache[path_coord] = path_list[next_i]
def _recompute_path(self, map, start, end): pf = PathFinder(map.successors, map.move_cost, map.move_cost) t = time.clock() pathlines = list(pf.compute_path(start, end)) dt = time.clock() - t if pathlines == []: print "No path found" return pathlines else: print "Found path (length %d)" % len(pathlines) return pathlines
def _recompute_path(self, _map, start, end): pf = PathFinder(_map.successors, _map.move_cost, _map.move_cost) # t = time.process_time() pathlines = list(pf.compute_path(start, end)) # dt = time.process_time() - t if pathlines == []: print("No path found") return pathlines else: print("Found path (length %d)" % len(pathlines)) return pathlines
def _compute_path(self, coord): pf = PathFinder(self.map.successors, self.map.move_cost, self.map.move_cost) # Get the whole path from coord to the goal into a list, # and for each coord in the path write the next coord in # the path into the path cache # path_list = list(pf.compute_path(coord, self.goal)) for i, path_coord in enumerate(path_list): next_i = i if i == len(path_list) - 1 else i + 1 self._path_cache[path_coord] = path_list[next_i]
def _recompute_path(self): self.blocked_list = self.map.blocked pf = PathFinder(self.map.successors, self.map.move_cost, self.map.move_cost) t = time.clock() self.path = list(pf.compute_path(self.start_pos, self.goal_pos)) dt = time.clock() - t if self.path == []: self.msg1 = "No path found" else: self.msg1 = "Found path (length %d)" % len(self.path) self.msg2 = "Elapsed: %s seconds" % dt self.path_valid = True
class Brain(object): def __init__(self, owner): self.owner = owner self.walls = set() self.floors = set() self.entities = set() self.path = [] self.pathfinder = PathFinder(self.get_adjacents, self.get_cost, self.get_heuristic) def observe(self): self.area = self.owner.owner.owner self.entities = set() for point in self.owner.fov: if point in self.area.terrain and self.area.terrain[point]['chasm'] == False: self.floors.add(point) else: self.floors.discard(point) if point in self.area.features: self.walls.add(point) else: self.walls.discard(point) if point in self.area.entities: self.entities.add(point) entities = self.area.entities.get_nodes(self.owner.fov) for node in entities: if 'player' in node and 'player' not in self.owner: self.entities.discard(node.pos) self.path = [] path = self.pathfinder.compute_path(self.owner.pos, node.pos) for point in path: if point == self.owner.pos: continue self.path.append(point) def get_adjacents(self, point): return [pos for pos in M_NEIGHBORS[point] if pos in self.floors and pos not in self.walls and pos not in self.entities] def get_cost(self, start, end): return 1 def get_heuristic(self, start, end): return len(build_line(start, end)) - 1
def autoUpdate(self, ticks): """this is the master update routine for the NPC AI""" # possible random activities: swirl_activation_chance = AUTO_SWIRL_ACTIVATION_CHANCE if self.in_dance(): # ignore everything else while dancing return # ACTIVITY: change swirls if len(self.swirls) > 0 and \ ('last-swirl-change' not in self.auto_status.keys() or self.auto_status['last-swirl-change'] + AUTO_SWIRL_CHANGE_MINTICKS < ticks) \ and random.random() < AUTO_SWIRL_CHANGE_CHANCE: if(random.randint(0,1) == 0): self.trySwirlLeft() else: self.trySwirlRight() self.auto_status['last-swirl-change'] = ticks swirl_activation_chance = 0.5 # if we've just changed swirls, there's a high chance that we'll immediately activate it # ACTIVITY: activate a swirl if len(self.swirls) > 0 and \ ('last-swirl-activation' not in self.auto_status.keys() or self.auto_status['last-swirl-activation'] + AUTO_SWIRL_ACTIVATION_MINTICKS < ticks) \ and random.random() < swirl_activation_chance: #logging.debug("[Shape {1}] self-activating current swirl at {0}".format(ticks, self.id)) self.activateSwirl(random.randint(0,1) == 0) self.auto_status['last-swirl-activation'] = ticks # ACTIVITY: stop to think # TODO: modify probability based on interesting things in environment if self.in_head(): if self.auto_status['thoughtform_starttick'] + self.auto_status['thoughtform_complexity'] * 3 < ticks: self.debug("Thoughtform {0} has expired at {1}.".format(self.auto_status['thoughtform_id'], ticks)) del self.auto_status['thoughtform_id'] del self.auto_status['thoughtform_complexity'] del self.auto_status['thoughtform_starttick'] if 'thoughtform_target' in self.auto_status: del self.auto_status['thoughtform_target'] else: # still in an ongoing thought if self.turning is None and 'thoughtform_target' in self.auto_status: # and we're not currently turning self.faceTo(self.auto_status['thoughtform_target']) # make sure we turn to face the object we're considering elif not self.in_head() and random.random() < AUTO_THOUGHT_CREATION_CHANCE: self.spawnThoughtform(ticks) # turn to look at something nearby object_of_interest = None # close people first radius = 2 while(object_of_interest is None and radius <= 6): nearby_shapes = self.map.nearShapes(self.getCenter(), self.map.character_size * radius, self) if len(nearby_shapes) > 0: object_of_interest = nearby_shapes[0] radius += 2 # then art, if no nearby people found if object_of_interest is None: nearby_art = self.art_onscreen() if len(nearby_art) > 0: object_of_interest = random.choice(nearby_art) if object_of_interest is not None: self.debug("Found a nearby object of interest, turning to face...") self.faceTo(object_of_interest) self.auto_status['thoughtform_target'] = object_of_interest # ACTIVITY: move in a direction # if we're already moving to a known destination, carry on elif self.in_move(): # move along the path # destination in X,Y coords is the next point in the path nextnodeGridYX = self.auto_status['movement_path'][self.auto_status['movement_path_curidx']] destGridLeftTopXY = self.map.gridToScreenCoord((nextnodeGridYX[1], nextnodeGridYX[0])) # adjust dest to center shape on dest grid square xoffset = (self.map.grid_cellwidth) / 2 yoffset = (self.map.grid_cellheight) / 2 destXY = (destGridLeftTopXY[0] + xoffset, destGridLeftTopXY[1] + yoffset) dest_distance = self.map.world.map_distance(self.getCenter(), list(destXY)) #logging.debug("moving from {0} towards destination at {1} (based on destTopLeft of {2} adjusted by offset {5}) via node {3}, distance to target is {4}".format(self.getCenter(), destXY, destGridLeftTopXY, nextnodeGridYX, dest_distance, (xoffset, yoffset))) self.moveTowards(destXY) # if we're at the node (or close enough), move to the next node if dest_distance < pacdefs.WALL_LINE_WIDTH + self.linearSpeed: self.debug("reached node {0}, moving to next node in path (out of {1} total nodes)".format(self.auto_status['movement_path_curidx'], len(self.auto_status['movement_path']))) # if we're at our destination, clear the destination & path self.auto_status['movement_path_curidx'] += 1 if self.auto_status['movement_path_curidx'] == len(self.auto_status['movement_path']): self.debug("reached destination, clearing path") # if we just finished "wandering", then stop and have a think if type(self.auto_status['movement_destination']) is str: self.spawnThoughtform(ticks) del self.auto_status['movement_destination'] del self.auto_status['movement_path'] del self.auto_status['movement_path_curidx'] elif(self.last_artsearch_position != list(self.getCenter())): # if we have moved since the last look, or have never looked # else, look for a new destination # if something interesting is onscreen self.last_artsearch_position = list(self.getCenter()) self.debug("Searching for nearby art...") art_on_screen = self.art_onscreen() random.shuffle(art_on_screen) for art in art_on_screen: # if artpiece is on the screen, # and we haven't seen it yet if art.id in self.last_touched_art.keys(): continue # skip arts that we've already seen # then look for a path from current pos to artpiece start = self.get_gridCoordsYX() goal = (art.top, art.left) # grid square of art piece; NOTE: pathfinder takes (y,x) coordinates self.debug("[FORMAT (y,x)] looking for path from {0} to {1}".format(start, goal)) pf = PathFinder(self.map.world.successors, self.map.world.move_cost, self.map.world.move_cost) path = list(pf.compute_path(start, goal)) if(path): # if we can get to it, set our destination if(len(path) > 1): # but only if it's more than 1 step away self.debug("setting destination as art {0}, via path: {1}".format(art,path)) self.auto_status['movement_destination'] = art self.auto_status['movement_path'] = path self.auto_status['movement_path_curidx'] = 1 # destination starts at node 1 since node 0 is starting point break else: # no path is possible, mark this destination as inaccessible self.last_touched_art[art.id] = None # adding the key to the dictionary marks this as "seen" # if we finish the for loop, there is no art on screen else: # ACTIVITY: go to a random accessible square on screen, with preference for unvisited squares # wander around the map - use an exploratory algorithm ? destination = None while(destination is None): path = None #get random destination on screen winRect = self.getWindowRect() # left, top, right, bottom grid_minx = int(winRect[0] / self.map.grid_cellwidth) grid_miny = int(winRect[1] / self.map.grid_cellheight) grid_maxx = int((winRect[0]+winRect[2]) / self.map.grid_cellwidth) grid_maxy = int((winRect[1]+winRect[3]) / self.map.grid_cellheight) #self.debug("on-screen grid coords are: {0} (topLeft) to {1} (botRight)".format((grid_minx,grid_miny), (grid_maxx, grid_maxy))) destx = random.randint(grid_minx,grid_maxx) desty = random.randint(grid_miny,grid_maxy) #self.debug("testing grid spot: {0},{1} (x,y)".format(destx, desty)) destination = str(destx)+','+str(desty) if(self.map_knowledge[desty][destx] is None): # try and compute path to destination if not already known... start = self.get_gridCoordsYX() goal = (desty, destx) # NOTE: pathfinder takes (y,x) coordinates pf = PathFinder(self.map.world.successors, self.map.world.move_cost, self.map.world.move_cost) path = list(pf.compute_path(start, goal)) # keep track of visited (and inaccessible) squares in the grid... if(path): # if we can get to it, set our destination self.map_knowledge[desty][destx] = 0 else: self.map_knowledge[desty][destx] = -1 #self.debug("grid spot: {0},{1} (x,y) is INACCESSIBLE".format(destx, desty)) # destination is INaccessible destination = None elif(self.map_knowledge[desty][destx] == -1): # known destination, but inaccessible, try again... destination = None # good destination, not inaccessible... #TODO: create preference for unvisited squares # go to destination.... self.debug("wandering to grid spot: {0},{1} (x,y)".format(destx, desty)) if(path is None): # going to previously computed destination start = self.get_gridCoordsYX() goal = (desty, destx) # NOTE: pathfinder takes (y,x) coordinates pf = PathFinder(self.map.world.successors, self.map.world.move_cost, self.map.world.move_cost) path = list(pf.compute_path(start, goal)) if(len(path) > 1): # if len(path) <= 1 then we're already there self.auto_status['movement_path'] = path self.auto_status['movement_path_curidx'] = 1 # destination starts at node 1 since node 0 is starting point self.auto_status['movement_destination'] = destination
class MyField(Field): """An object representing the field. """ cellClass = MyCell connectorClass = MyConnector def __init__(self): self.m_xmin_field = XMIN_FIELD self.m_ymin_field = YMIN_FIELD self.m_xmax_field = XMAX_FIELD self.m_ymax_field = YMAX_FIELD self.m_xmin_vector = XMIN_VECTOR self.m_ymin_vector = YMIN_VECTOR self.m_xmax_vector = XMAX_VECTOR self.m_ymax_vector = YMAX_VECTOR self.m_xmin_screen = XMIN_SCREEN self.m_ymin_screen = YMIN_SCREEN self.m_xmax_screen = XMAX_SCREEN self.m_ymax_screen = YMAX_SCREEN self.m_path_unit = PATH_UNIT self.m_output_mode = MODE_DEFAULT self.m_path_scale = 1 self.m_screen_scale = 1 self.m_vector_scale = 1 # our default margins, one will be overwriten below self.m_xmargin = int(self.m_xmax_screen*DEF_MARGIN) self.m_ymargin = int(self.m_ymax_screen*DEF_MARGIN) self.set_scaling() self.m_screen = object self.path_grid = object self.pathfinder = object super(MyField, self).__init__() self.make_path_grid() # Screen Stuff def init_screen(self): # initialize window #(xmax_screen,ymax_screen) = self.screenMax() #self.m_screen = pyglet.window.Window(width=xmax_screen,height=ymax_screen) width = self.m_xmax_screen - self.m_xmin_screen height = self.m_ymax_screen - self.m_ymin_screen if dbug.LEV & dbug.FIELD: print "field:init_screen" self.m_screen = Window(self,width=width,height=height) # set window background color = r, g, b, alpha # each value goes from 0.0 to 1.0 # ... perform some additional initialisation pyglet.gl.glClearColor(*DEF_BKGDCOLOR) self.m_screen.clear() # register draw routing with pyglet # TESTED: These functions are being called correctly, and params are # being passed correctly self.m_screen.set_minimum_size(XMAX_SCREEN/4, YMAX_SCREEN/4) self.m_screen.set_visible() # Scaling def set_scaling(self,pmin_field=None,pmax_field=None,pmin_vector=None,pmax_vector=None, pmin_screen=None,pmax_screen=None,path_unit=None,output_mode=None): """Set up scaling in the field. A word about graphics scaling: * The vision tracking system (our input data) measures in meters. * The laser DAC measures in uh, int16? -32,768 to 32,768 * Pyglet measures in pixels at the screen resolution of the window you create * The pathfinding units are each some ratio of the smallest expected radius So we will keep eveything internally in centemeters (so we can use ints instead of floats), and then convert it to the appropriate units before display depending on the output mode """ if pmin_field is not None: self.m_xmin_field = pmin_field[0] self.m_ymin_field = pmin_field[1] if pmax_field is not None: self.m_xmax_field = pmax_field[0] self.m_ymax_field = pmax_field[1] if pmin_vector is not None: self.m_xmin_vector = pmin_vector[0] self.m_ymin_vector = pmin_vector[1] if pmax_vector is not None: self.m_xmax_vector = pmax_vector[0] self.m_ymax_vector = pmax_vector[1] if pmin_screen is not None: self.m_xmin_screen = pmin_screen[0] self.m_ymin_screen = pmin_screen[1] if pmax_screen is not None: self.m_xmax_screen = pmax_screen[0] self.m_ymax_screen = pmax_screen[1] if path_unit is not None: self.m_path_unit = path_unit self.m_path_scale = float(1)/path_unit if output_mode is not None: self.m_output_mode = output_mode xmin_field = self.m_xmin_field ymin_field = self.m_ymin_field xmax_field = self.m_xmax_field ymax_field = self.m_ymax_field xmin_vector = self.m_xmin_vector ymin_vector = self.m_ymin_vector xmax_vector = self.m_xmax_vector ymax_vector = self.m_ymax_vector xmin_screen = self.m_xmin_screen ymin_screen = self.m_ymin_screen xmax_screen = self.m_xmax_screen ymax_screen = self.m_ymax_screen # in order to find out how to display this, # 1) we find the aspect ratio (x/y) of the screen or vector (depending on the # mode). # 2) Then if the aspect ratio (x/y) of the reported field is greater, we # set the x axis to stretch to the edges of screen (or vector) and then use # that value to determine the scaling. # 3) But if the aspect ratio (x/y) of the reported field is less than, # we set the y axis to stretch to the top and bottom of screen (or # vector) and use that value to determine the scaling. # aspect ratios used only for comparison field_aspect = float(xmax_field-xmin_field)/(ymax_field-ymin_field) if self.m_output_mode == MODE_SCREEN: display_aspect = float(xmax_screen-xmin_screen)/(ymax_screen-ymin_screen) else: display_aspect = float(xmax_vector-xmin_vector)/(ymax_vector-ymin_vector) if field_aspect > display_aspect: if dbug.LEV & dbug.FIELD: print "Field:SetScaling:Longer in the x dimension" field_xlen=xmax_field-xmin_field if field_xlen: self.m_xmargin = int(xmax_screen*DEF_MARGIN) # scale = vector_width / field_width self.m_vector_scale = \ float(xmax_vector-xmin_vector)/field_xlen # scale = (screen_width - margin) / field_width self.m_screen_scale = \ float(xmax_screen-xmin_screen-(self.m_xmargin*2))/field_xlen self.m_ymargin = \ int(((ymax_screen-ymin_screen)-((ymax_field-ymin_field)*self.m_screen_scale)) / 2) else: if dbug.LEV & dbug.FIELD: print "Field:SetScaling:Longer in the y dimension" field_ylen=ymax_field-ymin_field if field_ylen: self.m_ymargin = int(ymax_screen*DEF_MARGIN) self.m_vector_scale = \ float(ymax_vector-ymin_vector)/field_ylen self.m_screen_scale = \ float(ymax_screen-ymin_screen-(self.m_ymargin*2))/field_ylen self.m_xmargin = \ int(((xmax_screen-xmin_screen)-((xmax_field-xmin_field)*self.m_screen_scale)) / 2) if dbug.LEV & dbug.MORE: print "Field dims:",(xmin_field,ymin_field),(xmax_field,ymax_field) if dbug.LEV & dbug.MORE: print "Screen dims:",(xmin_screen,ymin_screen),(xmax_screen,ymax_screen) #print "Screen scale:",self.m_screen_scale #print "Screen margins:",(self.m_xmargin,self.m_ymargin) if dbug.LEV & dbug.MORE: print "Used screen space:",self.rescale_pt2out((xmin_field,ymin_field)),self.rescale_pt2out((xmax_field,ymax_field)) # Everything def render_all(self): """Render all the cells and connectors.""" self.render_all_cells() self.render_all_connectors() def draw_all(self): """Draw all the cells and connectors.""" self.draw_guides() self.draw_all_cells() self.draw_all_connectors() # Guides def draw_guides(self): # draw boundaries of field (if in screen mode) if self.m_output_mode == MODE_SCREEN: pyglet.gl.glColor3f(DEF_GUIDECOLOR[0],DEF_GUIDECOLOR[1],DEF_GUIDECOLOR[2]) points = [(self.m_xmin_field,self.m_ymin_field), (self.m_xmin_field,self.m_ymax_field), (self.m_xmax_field,self.m_ymax_field), (self.m_xmax_field,self.m_ymin_field)] if dbug.LEV & dbug.GRAPH: print "boundary points (field):",points index = [0,1,1,2,2,3,3,0] screen_pts = self.rescale_pt2out(points) if dbug.LEV & dbug.GRAPH: print "boundary points (screen):",screen_pts # boundary points (screen): [(72, 73), (72, 721), (1368, 721), (1368, 73)] if dbug.LEV & dbug.GRAPH: print "proc screen_pts:",tuple(chain(*screen_pts)) # proc screen_pts: (72, 73, 72, 721, 1368, 721, 1368, 73) if dbug.LEV & dbug.GRAPH: print "PYGLET:pyglet.graphics.draw_indexed(",len(screen_pts),", pyglet.gl.GL_LINES," if dbug.LEV & dbug.GRAPH: print " ",index if dbug.LEV & dbug.GRAPH: print " ('v2i',",tuple(chain(*screen_pts)),")," if dbug.LEV & dbug.GRAPH: print " )" pyglet.graphics.draw_indexed(len(screen_pts), pyglet.gl.GL_LINES, index, ('v2i',tuple(chain(*screen_pts))), ) #point = (self.m_xmin_field,self.m_ymin_field) #radius = self.rescale_num2out(DEF_RADIUS) #shape = Circle(self,point,radius,DEF_LINECOLOR,solid=False) #shape.render() #shape.draw() if dbug.LEV & dbug.MORE: print "Field:drawGuides" # Cells #def create_cell(self, id): # moved to superclass #def update_cell(self, id, p=None, r=None, effects=None, color=None): # moved to superclass #def is_cell_good_to_go(self, id): # moved to superclass #def del_cell(self, id): # moved to superclass #def check_people_count(self,reported_count): # moved to superclass #def hide_cell(self, id): # moved to superclass #def hide_all_cells(self): # moved to superclass def render_cell(self,cell): """Render a cell. We first check if the cell is good. If not, we increment its suspect count If yes, render it. """ if self.is_cell_good_to_go(cell.m_id): cell.render() #del self.m_suspect_cells[cell.m_id] else: if dbug.LEV & dbug.FIELD: print "Field:renderCell:Cell",cell.m_id,"is suspected lost for",\ self.m_suspect_cells[cell.m_id],"frames" if self.m_suspect_cells[cell.m_id] > MAX_LOST_PATIENCE: self.del_cell(cell.m_id) else: self.m_suspect_cells[cell.m_id] += 1 def render_all_cells(self): # we don't call the Cell's render-er directly because we have some # logic here at this level for cell in self.m_cell_dict.values(): self.render_cell(cell) def draw_cell(self,cell): cell.draw() def draw_all_cells(self): # we don't call the Cell's draw-er directly because we may want # to introduce logic at this level for cell in self.m_cell_dict.values(): self.draw_cell(cell) # Connectors #def create_connector(self, id, cell0, cell1): # moved to superclass #def del_connector(self,conxid): # moved to superclass def render_connector(self,connector): """Render a connector. We first check if the connector's two cells are both good. If not, we increment its suspect count If yes, render it. """ if self.is_cell_good_to_go(connector.m_cell0.m_id) and \ self.is_cell_good_to_go(connector.m_cell1.m_id): connector.render() if connector.m_id in self.m_suspect_conxs: del self.m_suspect_conxs[connector.m_id] else: if dbug.LEV & dbug.FIELD: print "Field:renderConnector:Conx",connector.m_id,"between",\ connector.m_cell0.m_id,"and",connector.m_cell1.m_id,"is suspected lost" if self.m_suspect_conxs[connector.m_id] > MAX_LOST_PATIENCE: self.del_connector(connector.m_id) else: self.m_suspect_conxs[connector.m_id] += 1 def render_all_connectors(self): # we don't call the Connector's render-er directly because we have some # logic here at this level for connector in self.m_connector_dict.values(): self.render_connector(connector) def draw_connector(self,connector): connector.draw() def draw_all_connectors(self): # we don't call the Connector's draw-er directly because we may want # to introduce logic at this level for connector in self.m_connector_dict.values(): self.draw_connector(connector) # Distances - TODO: temporary -- this info will come from the conduction subsys #def dist_sqd(self,cell0,cell1): # moved to superclass #def calc_distances(self): # moved to superclass # Paths # should the next two functions be in the gridmap module? No, because the GridMap # and Pathfinder classes have to be instantiated from somewhere. And if not # here they have to be called from the main loop. Better here. def make_path_grid(self): # for our pathfinding, we're going to overlay a grid over the field with # squares that are sized by a constant in the config file self.path_grid = GridMap(self.scale2path(self.m_xmax_field), self.scale2path(self.m_ymax_field)) self.pathfinder = PathFinder(self.path_grid.successors, self.path_grid.move_cost, self.path_grid.estimate) def reset_path_grid(self): self.path_grid.reset_grid() # we store the results of all the paths, why? Not sure we need to anymore #self.allpaths = [] def path_score_cells(self): #print "***Before path: ",self.m_cell_dict for cell in self.m_cell_dict.values(): self.path_grid.set_blocked(self.scale2path(cell.m_location), self.scale2path(cell.m_radius),BLOCK_FUZZ) def path_find_connectors(self): """ Find path for all the connectors. We sort the connectors by distance and do easy paths for the closest ones first. """ #connector_dict_rekeyed = self.m_connector_dict #for i in connector_dict_rekeyed.iterkeys(): connector_dict_rekeyed = {} for connector in self.m_connector_dict.values(): p0 = connector.m_cell0.m_location p1 = connector.m_cell1.m_location # normally we'd take the sqrt to get the distance, but here this is # just used as a sort comparison, so we'll not take the hit for sqrt score = ((p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2) # here we save time by sorting as we go through it connector_dict_rekeyed[score] = connector for i in sorted(connector_dict_rekeyed.iterkeys()): connector = connector_dict_rekeyed[i] print "findpath--id:",connector.m_id,"dist:",i**0.5 connector.add_path(self.find_path(connector)) def find_path(self, connector): """ Find path in path_grid and then scale it appropriately.""" start = self.scale2path(connector.m_cell0.m_location) goal = self.scale2path(connector.m_cell1.m_location) # TODO: Either here or in compute_path we first try several simple/dumb # paths, reserving A* for the ones that are blocked and need more # smarts. We sort the connectors by distance and do easy paths for the # closest ones first. #path = list(self.path_grid.easy_path(start, goal)) #print "connector:id",connector.m_id,"path:",path #if not path: path = list(self.pathfinder.compute_path(start, goal)) # take results of found paths and block them on the map self.path_grid.set_block_line(path) #self.allpaths = self.allpaths + path return self.path2scale(path) def print_grid(self): self.path_grid.printme() # Scaling conversions def _convert(self,obj,scale,min1,min2): """Recursively converts numbers in an object. This function accepts single integers, tuples, lists, or combinations. """ if isinstance(obj, (int, float)): #return(int(obj*scale) + min) return int((obj-min1)*scale) + min2 elif isinstance(obj, list): mylist = [] for i in obj: mylist.append(self._convert(i,scale,min1,min2)) return mylist elif isinstance(obj, tuple): mylist = [] for i in obj: mylist.append(self._convert(i,scale,min1,min2)) return tuple(mylist) def scale2out(self,n): """Convert internal unit (cm) to units usable for the vector or screen. """ if self.m_output_mode == MODE_SCREEN: return self._convert(n,self.m_screen_scale,self.m_xmin_field,self.m_xmin_screen) return self._convert(n,self.m_vector_scale,self.m_xmin_field,self.m_xmin_vector) def scale2path(self,n): """Convert internal unit (cm) to units usable for pathfinding. """ return self._convert(n,self.m_path_scale,self.m_xmin_field,0) def path2scale(self,n): """Convert pathfinding units to internal unit (cm). """ #print "m_path_scale",self.m_path_scale return self._convert(n,1/self.m_path_scale,0,self.m_xmin_field) def _rescale_pts(self,obj,scale,orig_pmin,new_pmin): """Recursively rescales points or lists of points. This function accepts single integers, tuples, lists, or combinations. """ # if this is a point, rescale it if isinstance(obj, tuple) and len(obj) == 2 and \ isinstance(obj[0], (int,float)) and isinstance(obj[1], (int,float)): x = int((obj[0]-orig_pmin[0])*scale) + new_pmin[0] y = int((obj[1]-orig_pmin[1])*scale) + new_pmin[1] return x,y # if this is a list, examine each element, return list elif isinstance(obj, (list,tuple)): mylist = [] for i in obj: mylist.append(self._rescale_pts(i,scale,orig_pmin,new_pmin)) return mylist # if this is a tuple, examine each element, return tuple elif isinstance(obj, tuple): mylist = [] for i in obj: mylist.append(self._rescale_pts(i,scale,orig_pmin,new_pmin)) return tuple(mylist) # otherwise, we don't know what to do with it, return it # TODO: Consider throwing an exception else: print "ERROR: Can only rescale a point, not",obj return obj def rescale_pt2out(self,p): """Convert coord in internal units (cm) to units usable for the vector or screen. """ orig_pmin = (self.m_xmin_field,self.m_ymin_field) if self.m_output_mode == MODE_SCREEN: scale = self.m_screen_scale new_pmin = (self.m_xmin_screen+self.m_xmargin,self.m_ymin_screen+self.m_ymargin) else: scale = self.m_vector_scale new_pmin = (self.m_xmin_vector,self.m_ymin_vector) return self._rescale_pts(p,scale,orig_pmin,new_pmin) def rescale_num2out(self,n): """Convert num in internal units (cm) to units usable for the vector or screen. """ if self.m_output_mode == MODE_SCREEN: scale = self.m_screen_scale else: scale = self.m_vector_scale return n*scale
class Hero(): """Class holding unique hero object""" def __init__(self): self.hero_id = None self.alive = True self.location = [None, None] self.shop_location = None self.shop_destination = None self.pathfinder = None self.path = None self.name = None self.home = None self.inventory = [] self.coins = { 'copper': 0, 'silver': 0, 'gold': 0 } self.kills = [] self.size = 100 #represents percent of "normal" size self.goals = [] self.needs = [] self.wants = [] self.personality = None self.experience = None self.perceptions = None self.boredom = 0 self.traveling = False self.destination = [None, None] self.coins = { 'copper': 0, 'silver': 0, 'gold': 0 } def tick(self, game): self.think() if self.location == entities.town['object'].location: if 'shopping' in self.wants: self.enter_shop(game) self.wants.remove('shopping') if self.shop_location == None: if randint(1, 5) == 1: self.boredom += randint(1, 50) if self.boredom >= 100 and 'adventure' not in self.wants: #if self.boredom >= 1 and 'adventure' not in self.wants: #TODO: Remove line; just for testing self.wants.append('adventure') else: if ('transaction' in self.wants and entities.shop['object'].get_shop_tile(self.get_shop_grid_location()) == 'transaction' ): if self not in entities.shop['object'].transaction_queue: entities.shop['object'].transaction_queue.append(self) if self.traveling: self.step_to(self.destination) if self.location == self.destination: self.traveling = False if self.location == entities.town['object']: self.perceptions.economy.be_influenced(entities.town['object'].economy) for site in entities.sites['object list']: if self.destination == site.location: site.structure.add_hero(self.hero_id) self.destination = None break if 'adventure' in self.wants: shuffle(entities.sites['object list']) for site in entities.sites['object list']: if (site.structure.get_attribute('site type') == 'adventure' and site.structure.worker_capacity > 0 ): self.traveling = True self.wants.remove('adventure') self.boredom = 0 self.destination = site.location break def think(self): if self.shop_location == None and len(self.inventory) >= 2 and 'shopping' not in self.wants: self.wants.append('shopping') if self.shop_location != None: #if hero is in the shop if self.inventory >= 2 and 'transaction' not in self.wants: self.wants.append('transaction') if 'transaction' in self.wants and self.pathfinder == None: transaction_tiles = self.get_tiles('transaction') self.shop_destination = choice(transaction_tiles) self.get_path(self.shop_destination) def get_path(self, destination): gridmap = GridMap( len(entities.shop['object'].shop_grid[0]), len(entities.shop['object'].shop_grid) ) blocked_tiles = self.get_tiles('passable', False) for blocked_tile in blocked_tiles: gridmap.set_blocked(blocked_tile) self.pathfinder = PathFinder(gridmap.successors, gridmap.move_cost, gridmap.move_cost) self.path = self.pathfinder.compute_path( self.get_shop_grid_location(), destination ) def get_shop_grid_location(self): return tuple(map(lambda x: x/ref.tile_size, self.shop_location)) def step_to(self, destination=None): x = False y = False if destination[0] != self.location[0]: x = True if destination[1] != self.location[1]: y = True if x and randint(1,3) != 3: self.location[0] = self.location[0] - int( copysign(1, self.location[0]-destination[0]) ) if y and randint(1,3) != 3: self.location[1] = self.location[1] - int( copysign(1, self.location[1]-destination[1]) ) def get_tiles(self, tile_type, positive=True): """Returns a list of coordinates where that type of tile exists in the shop grid""" if tile_type not in ref.tile_type_list: tester = tile_type tile_type = positive else: tester = 'tile type' tiles = [] for row in xrange(len(entities.shop['object'].shop_grid)): for column in xrange(len(entities.shop['object'].shop_grid[row])): tile = entities.shop['object'].shop_grid[row][column] if tile in ref.shop_tile_dct.keys(): if ref.shop_tile_dct[tile][tester] == tile_type: tiles.append((column, row)) return tiles def enter_shop(self, game): shop_grid = entities.shop['object'].shop_grid door_locs = [] for y in xrange(len(shop_grid)): for x in xrange(len(shop_grid[0])): if shop_grid[y][x] != 0: if ref.shop_tile_dct[shop_grid[y][x]]['tile type'] == 'door': door_locs.append((x, y)) entrance_loc = choice(door_locs) self.shop_location = [ entrance_loc[0] * ref.tile_size, entrance_loc[1] * ref.tile_size ] game.message_log += "%s has entered your shop." % self.name game.screens['world'].initialize_shop_sprites(game) def generate(self, location='random', weapon='random'): """Generates a hero.""" import item_data import language self.personality = Personality() self.perceptions = Perceptions().generate(self) self.coins['copper'] = randint(10,300) if location == 'town': self.home = entities.town['object'] self.location = copy.deepcopy(self.home.location) if weapon != None: if weapon == 'random': self.inventory.append(item_data.Weapon().generate('random')) else: return NotImplementedError(weapon) #TODO self.name = language.create_name('human') if type(self.location) == list: entities.town['object'].population += 1 entities.town['object'].occupations['adventurer'] += 1 self.set_hero_id() return self def set_hero_id(self): """Gives hero object uniquue ID.""" self.hero_id = entities.heroes['next id'] entities.heroes['object list'].append(self) entities.heroes['next id'] += 1 def __repr__(self): return 'Hero(ID: %r, Name:%r)' % (self.hero_id, self.name)