def handle_user_input(player): ''' continuously gets the command from user for move, also simulates the position change move command can be rejected (player re-prompted) if it would move player out of the map in that case, user is reprompted until a valid command is issued :param player: Player :returns Save if save command was issued Load is load command was issued None if move command was issued (and successfully simulated) ''' while True: command = parse_user_input() if command == Save or command == Load: return command delta = command if player.try_move(delta): dlog.debug(f'moved to {player.position}') return None else: olog.info("Can't move there - a wall blocks the path")
def __init__(self, size): ''' creates a size * size dungeon map where 1/10 tiles are treasures and other 2/10 - traps :param size: int, > 0 ''' if size <= 0: raise ValueError('size must be > 0') self.size = size count = size * size trap_count = floor(count * TRAP_QUOTA) treasure_count = floor(count * TREASURE_QUOTA) empty_count = count - trap_count - treasure_count treasures = [Treasure] * treasure_count traps = [Trap] * trap_count empties = [Empty] * empty_count dlog.debug( f'Created {treasure_count} treasures, {trap_count} traps, {empty_count} empties' ) combined = treasures combined.extend(traps) combined.extend(empties) shuffle(combined) self.tiles = list(chunks(combined, size)) dlog.debug( f'Created a map of {len(combined)} elements (with size {size})')
def try_attack_player(self, player): ''' unleashes the whole might of our 1 dmg attack on the unsuspecting player, if he is in reach (on the same tile as enemy) ''' if self.gotcha_player(player): dlog.debug('damaged player') olog.info('the grue\'s got you!') player.lose_health()
def get_random_empty_tile(self): ''' :param map: dungeon map object :returns: random Tile with empty type ''' dlog.debug('get_random_empty_tile') rand_index = lambda: randint(0, len(self.tiles) - 1) while True: maybe_empty = (rand_index(), rand_index()) dlog.debug(f'randomed tile: {maybe_empty}') if self.at(maybe_empty) == Empty: return maybe_empty
def move_randomly(self): ''' moves one delta in the random direction (not going into walls) ''' if not self.dungeon_map.in_bounds(self.position): ValueError( 'enemie\'s position is out of bounds of argument DungeonMap') while True: rand_move_dt = move_directions[randrange(len(move_directions))] new_pos = tuple_add(self.position, rand_move_dt) if self.dungeon_map.in_bounds(new_pos): self.position = new_pos dlog.debug(f'enemy moved to {self.position}') break
def handle_load(): ''' tries to load, telling user if the savefile does not exist :returns: tuple (dmap, player) - deserialized DungeonMap and Player or None if the load was unsuccessful ''' if not save_exists(): dlog.debug('Tried to load when savefile does not exist') olog.info('You haven\'t saved it yet!') return None try: deserialized = load() dlog.debug('changed state to loaded:') olog.info('Loaded your game!\n') return deserialized except UnpicklingError: olog.info('Could not load the save, savefile corrupted') return None
def play_game(size): dmap = DungeonMap(size) player_dead = Event() player_won = Event() player = Player(TREAUSURES_FOR_WIN, dmap, lambda: player_dead.set()) start = dmap.get_random_empty_tile() dlog.debug(f'Starting at {start}') olog.info( 'Note: you can input \'save\' any time to save the game, or \'load\' to load last saved one' ) player.position = start enemy = Enemy(dmap) enemy.start_patroling(player) print(dmap.map_to_str(player.position, enemy.position)) while not player_dead.is_set() and not player_won.is_set(): run_turn(player_dead, player_won, player, dmap) # uncomment for perfect debug experience print(dmap.map_to_str(player.position, enemy.position)) enemy.stop_patroling() won = player_won.is_set() lost = player_dead.is_set() assert won != lost if won: win(dmap, player.position, enemy.position) else: assert lost lose(dmap, player.position, enemy.position)
def patrol(self, player): ''' a single turn of patrol attacks player if standing on the same tile, moves randomly and tries to attack again ''' self.try_attack_player(player) self.move_randomly() self.try_attack_player(player) tile_val = self.dungeon_map.at(self.position) if tile_val == dm.Treasure: dlog.debug(f'enemy ate treasure at {self.position}') elif tile_val == dm.Trap: dlog.debug(f'enemy died from a trap at {self.position}') self.position = self.dungeon_map.get_random_empty_tile() dlog.debug(f'enemy respawned to {self.position}') # eats our treasure, or destroys the trap by activating it self.dungeon_map.set_tile(self.position, dm.Empty)