Source code for barebones_rpg.entities.ai
"""AI systems for entities.
This module provides AI behavior for NPCs and enemies, including
pathfinding-based movement and decision making using the AIInterface.
"""
from typing import Optional, Dict, Any
from barebones_rpg.entities.entity import Entity
from barebones_rpg.world.tilemap_pathfinding import TilemapPathfinder
from barebones_rpg.entities.ai_interface import AIInterface, AIContext
[docs]
class SimplePathfindingAI(AIInterface):
"""Simple pathfinding-based AI for enemies.
This AI will:
- Move toward a target using pathfinding
- Attack if adjacent to target
- Spend available action points efficiently
Args:
pathfinder: The pathfinder to use for navigation
attack_range: Range at which entity can attack (default: 1)
max_moves: Maximum moves per turn (default: 3)
"""
[docs]
def __init__(
self, pathfinder: TilemapPathfinder, attack_range: int = 1, max_moves: int = 3
):
"""Initialize the AI.
Args:
pathfinder: The pathfinder to use for navigation
attack_range: Range at which entity can attack
max_moves: Maximum moves per turn
"""
self.pathfinder = pathfinder
self.attack_range = attack_range
self.max_moves = max_moves
[docs]
def decide_action(self, context: AIContext) -> dict:
"""Decide what action to take based on context.
This implementation:
1. Finds nearest enemy in nearby_entities
2. If in attack range, returns attack action
3. If not in range, returns move action toward target
4. If no enemies nearby, returns wait action
Args:
context: AI context with entity and surroundings
Returns:
Dict with action information
"""
entity = context.entity
location = context.metadata.get("location")
if not context.nearby_entities:
return {"action": "wait"}
target = context.nearby_entities[0]
ex, ey = entity.position
tx, ty = target.position
distance = abs(ex - tx) + abs(ey - ty)
if distance <= self.attack_range:
return {"action": "attack", "target": target}
if not location:
return {"action": "wait"}
path = self.pathfinder.find_path(entity.position, target.position)
if not path or len(path) <= 1:
return {"action": "wait"}
next_pos = path[1]
if not location.is_walkable(next_pos[0], next_pos[1]):
return {"action": "wait"}
entity_at_pos = location.get_entity_at(next_pos[0], next_pos[1])
if entity_at_pos is not None:
return {"action": "wait"}
return {
"action": "move",
"position": next_pos,
"max_moves": self.max_moves,
}
[docs]
class TacticalAI(AIInterface):
"""More advanced tactical AI with behavior modes.
This AI can:
- Chase and attack
- Flee when low health
- Patrol between points
- Guard a specific location
Args:
pathfinder: The pathfinder to use for navigation
flee_hp_threshold: HP percentage threshold for fleeing (default: 0.3)
attack_range: Attack range (default: 1)
max_moves: Maximum moves per turn (default: 3)
"""
[docs]
def __init__(
self,
pathfinder: TilemapPathfinder,
flee_hp_threshold: float = 0.3,
attack_range: int = 1,
max_moves: int = 3,
):
"""Initialize the tactical AI.
Args:
pathfinder: The pathfinder to use for navigation
flee_hp_threshold: HP percentage threshold for fleeing
attack_range: Attack range
max_moves: Maximum moves per turn
"""
self.pathfinder = pathfinder
self.behavior_mode = "aggressive" # aggressive, defensive, patrol, guard
self.flee_hp_threshold = flee_hp_threshold
self.attack_range = attack_range
self.max_moves = max_moves
[docs]
def decide_action(self, context: AIContext) -> dict:
"""Decide what action to take based on context.
This implementation considers health status:
- If HP below threshold, flees from nearest enemy
- Otherwise, behaves like SimplePathfindingAI
Args:
context: AI context with entity and surroundings
Returns:
Dict with action information
"""
entity = context.entity
if self.should_flee(entity):
if context.nearby_entities:
threat = context.nearby_entities[0]
return {
"action": "flee",
"target": threat,
"max_moves": self.max_moves,
}
return {"action": "wait"}
simple_ai = SimplePathfindingAI(
self.pathfinder, self.attack_range, self.max_moves
)
return simple_ai.decide_action(context)
[docs]
def should_flee(self, entity: Entity) -> bool:
"""Check if entity should flee based on HP.
Args:
entity: The entity to check
Returns:
True if entity should flee
"""
if not hasattr(entity, "stats"):
return False
hp_percent = entity.stats.hp / entity.stats.max_hp
return hp_percent < self.flee_hp_threshold
[docs]
def set_behavior(self, mode: str, flee_threshold: Optional[float] = None):
"""Set AI behavior mode.
Args:
mode: Behavior mode ("aggressive", "defensive", "patrol", "guard")
flee_threshold: HP threshold for fleeing (0.0-1.0)
"""
self.behavior_mode = mode
if flee_threshold is not None:
self.flee_hp_threshold = flee_threshold