def get_up_down_left_right_neighbors(self, coord): tiles = self.level.tiles neighbors = ( tiles[add_vector(coord, Dir.North)], tiles[add_vector(coord, Dir.South)], tiles[add_vector(coord, Dir.West)], tiles[add_vector(coord, Dir.East)], ) return neighbors
def look(self): coord = self.coord drawline_flag = False direction = Dir.Stay while True: new_coord = add_vector(coord, direction) if self.actions.level.is_legal(new_coord): coord = new_coord self.io.msg(self.actions.level.look_information(coord)) if drawline_flag: self.io.draw_line(self.coord, coord, ("*", Pair.Yellow)) self.io.draw_line(coord, self.coord, ("*", Pair.Yellow)) self.io.msg("LoS: {}".format(self.actions.level.check_los(self.coord, coord))) if coord != self.coord: symbol, (foreground, background) = self.actions.level.visible_char(coord) char = symbol, (foreground, Color.Green) self.io.draw_char(char, coord) self.io.draw_char(self.actions.level.visible_char(self.coord), self.coord, reverse=True) key = self.io.get_key() self.actions.redraw() direction = Dir.Stay if key in Bind.Directions: direction = Dir.from_key[key] elif key == 'd': drawline_flag = not drawline_flag elif key == 'b': from generic_algorithms import bresenham for coord in bresenham(self.actions.level.get_coord(self.coord), coord): self.io.msg(coord) elif key == 's': if coord in self.actions.level.creatures: self.actions.game.register_status_texts(self.actions.level.creatures[coord]) elif key in Bind.Cancel or key in Bind.Look_Mode: break
def _move_towards(self, target_coord): best_action = Action.Move best_direction = Dir.Stay best_cost = None for direction in Dir.AllPlusStay: coord = add_vector(self.coord, direction) if self.level.is_passable(coord): action = Action.Move elif coord in self.level.creatures and self.actions.willing_to_swap( self.level.creatures[coord]): action = Action.Swap else: continue cost = self.level.distance_heuristic(coord, target_coord) if best_cost is None or cost < best_cost: best_action = action best_direction = direction best_cost = cost if best_action == Action.Move: return self.actions.move(best_direction) elif best_action == Action.Swap: return self.actions.swap(best_direction) else: assert False, "AI state bug. Best action was: {}".format( best_action)
def _move_towards(self, target_coord): best_action = Action.Move best_direction = Dir.Stay best_cost = None for direction in Dir.AllPlusStay: coord = add_vector(self.coord, direction) if self.level.is_passable(coord): action = Action.Move elif coord in self.level.creatures and self.actions.willing_to_swap(self.level.creatures[coord]): action = Action.Swap else: continue cost = self.level.distance_heuristic(coord, target_coord) if best_cost is None or cost < best_cost: best_action = action best_direction = direction best_cost = cost if best_action == Action.Move: return self.actions.move(best_direction) elif best_action == Action.Swap: return self.actions.swap(best_direction) else: assert False, "AI state bug. Best action was: {}".format(best_action)
def movement_multiplier(self, coord, direction): origin_multiplier = self.tiles[coord].movement_multiplier target_coord = add_vector(coord, direction) target_multiplier = self.tiles[target_coord].movement_multiplier tile_multiplier = (origin_multiplier + target_multiplier) / 2 return tile_multiplier * Dir.move_mult(direction)
def act_to_dir(self, direction): target_coord = add_vector(self.coord, direction) if target_coord in self.level.creatures: return self.attack(direction) elif self.can_move(direction): return self.move(direction) else: return feedback(ActionError.IllegalMove)
def can_move(self, direction): """Free action.""" if direction not in Dir.AllPlusStay: raise ValueError("Illegal movement direction: {}".format(direction)) elif direction == Dir.Stay: return True else: coord = add_vector(self.coord, direction) return self.level.is_legal(coord) and self.level.is_passable(coord)
def _finalize_tile(coord, tile, tiles): if tile != PyrlTile.Dynamic_Wall: return tile neighbor_coords = (add_vector(coord, direction) for direction in Dir.All) neighbor_tiles = (tiles[coord] for coord in neighbor_coords if tiles.is_legal(coord)) rocks = (PyrlTile.Dynamic_Wall, PyrlTile.Wall, PyrlTile.Rock) if any(handle not in rocks for handle in neighbor_tiles): return PyrlTile.Wall else: return PyrlTile.Rock
def act(self, game_actions, alert_coord): self.actions = game_actions if self.creature in self.ai_state: chase_coord, chase_vector = self.ai_state[self.creature] else: chase_coord, chase_vector = None, None if self.actions.target_in_sight(alert_coord): # passive actions if chase_coord is not None and chase_coord != alert_coord: chase_vector = get_vector(chase_coord, alert_coord) chase_coord = alert_coord # actions if self.actions.can_reach(alert_coord): feedback = self.actions.attack( get_vector(self.coord, alert_coord)) else: feedback = self._move_towards(alert_coord) else: # chasing and already at the target square and has a chase vector to pursue if chase_coord == self.coord: if chase_vector is not None: # resize the chase vector to the creatures sight so the self.creature can just go there chase_vector = resize_vector_to_len( chase_vector, self.creature.sight) # calculate a new target square overarching_target = add_vector(self.coord, chase_vector) chase_coord = self.level.get_last_pathable_coord( self.coord, overarching_target) # if an obstacle is hit end chase if self.coord == chase_coord: chase_coord, chase_vector = None, None else: chase_coord = None # actions if chase_coord is not None: feedback = self._move_towards(chase_coord) else: feedback = self._move_random() if chase_coord is not None or chase_vector is not None: self.ai_state[self.creature] = chase_coord, chase_vector else: self.remove_creature_state(self.creature) assert feedback.type not in ActionError, \ "AI state bug. Got error from game: {} {}".format(feedback.type, feedback.params)
def act(self, game_actions, alert_coord): self.actions = game_actions if self.creature in self.ai_state: chase_coord, chase_vector = self.ai_state[self.creature] else: chase_coord, chase_vector = None, None if self.actions.target_in_sight(alert_coord): # passive actions if chase_coord is not None and chase_coord != alert_coord: chase_vector = get_vector(chase_coord, alert_coord) chase_coord = alert_coord # actions if self.actions.can_reach(alert_coord): feedback = self.actions.attack(get_vector(self.coord, alert_coord)) else: feedback = self._move_towards(alert_coord) else: # chasing and already at the target square and has a chase vector to pursue if chase_coord == self.coord: if chase_vector is not None: # resize the chase vector to the creatures sight so the self.creature can just go there chase_vector = resize_vector_to_len(chase_vector, self.creature.sight) # calculate a new target square overarching_target = add_vector(self.coord, chase_vector) chase_coord = self.level.get_last_pathable_coord(self.coord, overarching_target) # if an obstacle is hit end chase if self.coord == chase_coord: chase_coord, chase_vector = None, None else: chase_coord = None # actions if chase_coord is not None: feedback = self._move_towards(chase_coord) else: feedback = self._move_random() if chase_coord is not None or chase_vector is not None: self.ai_state[self.creature] = chase_coord, chase_vector else: self.remove_creature_state(self.creature) assert feedback.type not in ActionError, \ "AI state bug. Got error from game: {} {}".format(feedback.type, feedback.params)
def swap(self, direction): if self.already_acted(): return feedback(ActionError.AlreadyActed) target_coord = add_vector(self.coord, direction) if target_coord not in self.level.creatures: return feedback(ActionError.NoSwapTarget) target_creature = self.level.creatures[target_coord] if not self.willing_to_swap(target_creature): return feedback(ActionError.SwapTargetResists) self.level.swap_creature(self.creature, target_creature) move_multiplier = self.level.movement_multiplier(self.coord, direction) self._do_action(self.creature.action_cost(Action.Move, move_multiplier)) return feedback(Action.Swap, direction, target_creature)
def attack(self, direction): if self.already_acted(): return feedback(ActionError.AlreadyActed) target_coord = add_vector(self.coord, direction) if target_coord in self.level.creatures: target = self.level.creatures[target_coord] else: target = self.level.tiles[target_coord] succeeds, damage = get_melee_attack_cr(self.creature, target) died = False if damage: target.receive_damage(damage) died = target.is_dead() if died: self.game.creature_death(target) self._do_action(self.creature.action_cost(Action.Attack)) self._attack_user_message(succeeds, damage, died, target) return feedback(Action.Attack, target, succeeds, damage, died)
def get_passable_neighbors(self, coord): for direction in Dir.All: neighbor_coord = add_vector(coord, direction) if self.is_legal(neighbor_coord) and self.tiles[neighbor_coord].is_passable: yield direction
def move_creature_to_dir(self, creature, direction): target_coord = add_vector(creature.coord, direction) self.move_creature(creature, target_coord)
def get_neighbor_location_coords_and_costs(self, coord): for direction in self.get_passable_neighbors(coord): neighbor_coord = add_vector(coord, direction) multiplier = self.movement_multiplier(coord, direction) yield neighbor_coord, round(multiplier * Action.Move.base_cost)