def find_path(self): """Find a path to the destination. Destination (PathFinder.destination) has to be set beforehand. This method implements a Breadth First Search algorithm (`Wikipedia <https://en.wikipedia.org/wiki/Breadth-first_search>`_) to find the shortest path to destination. Example:: mykillernpc.actuator = PathFinder(game=mygame,actuated_object=mykillernpc) mykillernpc.actuator.set_destination( mygame.player.pos[0], mygame.player.pos[1] ) mykillernpc.actuator.find_path() .. warning:: PathFinder.destination is a tuple! Please use PathFinder.set_destination(x,y) to avoid problems. """ if self.actuated_object == None: raise HacException('actuated_object is not defined','PathFinder.actuated_object has to be defined.') if not isinstance(self.actuated_object, Movable): raise HacException('actuated_object not a Movable object','PathFinder.actuated_object has to be an instance of a Movable object.') if self.destination == None: raise HacException('destination is not defined','PathFinder.destination has to be defined.') queue = collections.deque([[(self.actuated_object.pos[0],self.actuated_object.pos[1])]]) seen = set([(self.actuated_object.pos[0],self.actuated_object.pos[1])]) while queue: path = queue.popleft() x, y = path[-1] if (x,y) == self.destination: self._current_path = path # We return only a copy of the path as we need to keep the real one untouched for our own needs. return path.copy() for x2, y2 in ((x+1,y), (x-1,y), (x,y+1), (x,y-1)): if 0 <= y2 < self.game.current_board().size[0] and 0 <= x2 < self.game.current_board().size[1] and self.game.current_board().item(x2,y2).overlappable() and (x2, y2) not in seen: queue.append(path + [(x2, y2)]) seen.add((x2, y2)) return []
def change_level(self, level_number): """ Change the current level, load the board and place the player to the right place. Example:: game.change_level(1) :param level_number: the level number to change to. :type level_number: int :raises HacInvalidTypeException: If parameter is not an int. """ if type(level_number) is int: if self.player == None: raise HacException( 'undefined_player', 'Game.player is undefined. We cannot change level without a player. Please set player in your Game object: mygame.player = Player()' ) if level_number in self._boards.keys(): if self.player.pos[0] != None or self.player.pos[1] != None: self._boards[self.current_level]['board'].clear_cell( self.player.pos[0], self.player.pos[1]) self.current_level = level_number b = self._boards[self.current_level]['board'] b.place_item(self.player, b.player_starting_position[0], b.player_starting_position[1]) else: raise HacInvalidLevelException( f"Impossible to change level to an unassociated level (level number {level_number} is not associated with any board).\nHave you called:\ngame.add_board({level_number},Board()) ?" ) else: raise HacInvalidTypeException( 'level_number needs to be an int in change_level(level_number).' )
def display_menu(self, category, orientation=Constants.ORIENTATION_VERTICAL, paginate=10): """ .. TODO:: Documentation """ line_end = '\n' if orientation == Constants.ORIENTATION_HORIZONTAL: line_end = ' | ' if category not in self._menu: raise HacException( 'invalid_menu_category', f"The '{category}' category is not registered in the menu. Did you add any menu entry in that category with Game.add_menu_entry('{category}','some shortcut','some message') ? If yes, then you should check for typos." ) pagination_counter = 1 for k in self._menu[category]: if k['shortcut'] == None: print(k['message'], end=line_end) else: print(f"{k['shortcut']} - {k['message']}", end=line_end) pagination_counter += 1 if pagination_counter > paginate: print('') pagination_counter = 1
def __init__(self, name="projectile", direction=Constants.RIGHT, step=1, range=5, model="\U00002301", movement_animation=None, hit_animation=None, hit_model=None, hit_callback=None, is_aoe=False, aoe_radius=0, parent=None, *args): if range % step != 0: raise HacException( "incorrect_range_step", "range must be a factor of step" " in Projectile", ) Movable.__init__(self, model=model, step=step, name=name, parent=parent) self.direction = direction self.range = range self.movement_animation = movement_animation self._directional_animations = {} self._directional_models = {} self.hit_animation = hit_animation self.hit_model = hit_model self.hit_callback = hit_callback self.callback_parameters = args self.actuator = UnidirectionalActuator(direction=direction) self.is_aoe = is_aoe self.aoe_radius = aoe_radius self.parent = parent
def check_sanity(self): """Check the board sanity. This is essentially an internal method called by the constructor. """ sanity_check = 0 if type(self.size) is list: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'size' parameter must" " be a list.")) if len(self.size) == 2: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'size' parameter must" " be a list of 2 elements.")) if type(self.size[0]) is int: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The first element of the " "'size' list must be an integer.")) if type(self.size[1]) is int: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The second element of the 'size' " "list must be an integer.")) if type(self.name) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', "The 'name' parameter must be a string.") if type(self.ui_border_bottom) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'ui_border_bottom' parameter " "must be a string.")) if type(self.ui_border_top) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'ui_border_top' parameter must " "be a string.")) if type(self.ui_border_left) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'ui_border_left' parameter must " "be a string.")) if type(self.ui_border_right) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'ui_border_right' parameter must " "be a string.")) if type(self.ui_board_void_cell) is str: sanity_check += 1 else: raise HacException('SANITY_CHECK_KO', ("The 'ui_board_void_cell' parameter must " "be a string.")) if self.size[0] > 80: warn((f"The first dimension of your board is {self.size[0]}. " "It is a good practice to keep it at a maximum of 80 for " "compatibility with older terminals.")) if self.size[1] > 80: warn((f"The second dimension of your board is {self.size[1]}. " "It is a good practice to keep it at a maximum of 80 for " "compatibility with older terminals.")) # If all sanity check clears return True else raise a general error. # I have no idea how the general error could ever occur but... # better safe than sorry! if sanity_check == 10: return True else: raise HacException('SANITY_CHECK_KO', "The board data are not valid.")