Initial project structure: reusable isometric bot engine with D2R implementation

This commit is contained in:
Hoid 2026-02-14 08:50:36 +00:00
commit e0282a7111
44 changed files with 3433 additions and 0 deletions

View file

@ -0,0 +1,78 @@
"""Pathfinding for isometric game navigation.
Implements A* and click-to-move navigation for isometric games
where the bot needs to move between known locations.
"""
from typing import List, Tuple, Optional, Dict
from dataclasses import dataclass
import heapq
import math
import logging
logger = logging.getLogger(__name__)
@dataclass
class Waypoint:
"""A named location in the game world."""
name: str
screen_x: int
screen_y: int
metadata: Dict = None
class WaypointGraph:
"""Graph of connected waypoints for navigation."""
def __init__(self):
self._waypoints: Dict[str, Waypoint] = {}
self._edges: Dict[str, List[str]] = {}
def add_waypoint(self, waypoint: Waypoint) -> None:
self._waypoints[waypoint.name] = waypoint
self._edges.setdefault(waypoint.name, [])
def connect(self, name_a: str, name_b: str, bidirectional: bool = True) -> None:
self._edges.setdefault(name_a, []).append(name_b)
if bidirectional:
self._edges.setdefault(name_b, []).append(name_a)
def find_path(self, start: str, goal: str) -> Optional[List[Waypoint]]:
"""A* pathfinding between waypoints."""
if start not in self._waypoints or goal not in self._waypoints:
return None
goal_wp = self._waypoints[goal]
def heuristic(name: str) -> float:
wp = self._waypoints[name]
return math.hypot(wp.screen_x - goal_wp.screen_x, wp.screen_y - goal_wp.screen_y)
open_set = [(heuristic(start), 0, start)]
came_from: Dict[str, str] = {}
g_score: Dict[str, float] = {start: 0}
while open_set:
_, cost, current = heapq.heappop(open_set)
if current == goal:
path = []
while current in came_from:
path.append(self._waypoints[current])
current = came_from[current]
path.append(self._waypoints[start])
return list(reversed(path))
for neighbor in self._edges.get(current, []):
n_wp = self._waypoints[neighbor]
c_wp = self._waypoints[current]
edge_cost = math.hypot(n_wp.screen_x - c_wp.screen_x, n_wp.screen_y - c_wp.screen_y)
tentative_g = g_score[current] + edge_cost
if tentative_g < g_score.get(neighbor, float('inf')):
came_from[neighbor] = current
g_score[neighbor] = tentative_g
heapq.heappush(open_set, (tentative_g + heuristic(neighbor), tentative_g, neighbor))
return None