I am building a simple grid-based game and I want to implement different pathfinding behaviors for different types of NPCs. For example, some should move using A* (for efficiency), while others might use Dijkstra or even a simple BFS.
I am trying to use the Strategy Pattern to keep my NPC class decoupled from the specific algorithm implementation. However, I’m struggling with how to pass the grid data and start/end points to the strategy without making the classes too tightly coupled.
What I have tried:
I’ve researched the Strategy Pattern and understand that I need a common interface.
I looked into
abc.abstractmethodto define the contract.I checked similar questions on SO, but most examples use very simple math (like
AdditionvsSubtraction) rather than passing complex objects like a 2D grid.
my code
from abc import ABC, abstractmethod
# The Strategy Interface
class PathfindingStrategy(ABC):
@abstractmethod
def find_path(self, grid, start, end):
pass
# Concrete Strategy 1: BFS (Simplified)
class BFSStrategy(PathfindingStrategy):
def find_path(self, grid, start, end):
print("Executing BFS logic...")
# Imagine BFS logic here
return [(0,0), (0,1), (0,2)]
# Concrete Strategy 2: A* (Placeholder)
class AStarStrategy(PathfindingStrategy):
def find_path(self, grid, start, end):
print("Executing A* logic...")
# Imagine A* logic here
return [(0,0), (1,1), (0,2)]
# Context class
class NPC:
def __init__(self, name, strategy: PathfindingStrategy):
self.name = name
self.strategy = strategy
self.grid = [[0] * 5 for _ in range(5)] # Sample 5x5 grid
def move(self, destination):
start = (0, 0)
# Is this the right way to pass the grid and coordinates?
path = self.strategy.find_path(self.grid, start, destination)
print(f"{self.name} moves along: {path}")
# Usage
fast_npc = NPC("Runner", AStarStrategy())
fast_npc.move((0, 2))
The Problem:
While this code works, I am concerned about the grid being passed into the find_path method every time.
Is it architecturally sound to pass the entire environment (the grid) into the strategy method?
If my grid becomes very large (e.g., $1000 \times 1000$), would this approach cause performance issues due to Python's handling of method arguments, or is it just passing a reference?
Should the
NPCown the strategy, or should theGridcontroller handle the path calculation?
I am looking for best practices on applying this pattern specifically for algorithmic tasks in Python.