Kacemath's picture
feat: update with latest changes
47bba68
"""State models for search and planning results."""
from dataclasses import dataclass, field
from typing import List, Optional, Tuple
from .grid import Grid
from .entities import Store, Destination, Tunnel
@dataclass
class SearchState:
"""Represents the complete state for a delivery search problem."""
grid: Grid
stores: List[Store]
destinations: List[Destination]
tunnels: List[Tunnel]
def get_tunnel_at(self, pos: Tuple[int, int]) -> Optional[Tunnel]:
"""Get tunnel with entrance at given position."""
for tunnel in self.tunnels:
if tunnel.has_entrance_at(pos):
return tunnel
return None
def to_dict(self) -> dict:
return {
"grid": self.grid.to_dict(),
"stores": [s.to_dict() for s in self.stores],
"destinations": [d.to_dict() for d in self.destinations],
"tunnels": [t.to_dict() for t in self.tunnels],
}
@dataclass
class PathResult:
"""Result of finding a path from start to goal."""
plan: str # Comma-separated actions: "up,down,left,right,tunnel"
cost: float # Total traffic cost
nodes_expanded: int # Number of nodes expanded during search
path: List[Tuple[int, int]] = field(
default_factory=list
) # Actual positions in path
def to_string(self) -> str:
"""Format as required: plan;cost;nodesExpanded"""
return f"{self.plan};{self.cost};{self.nodes_expanded}"
def to_dict(self) -> dict:
return {
"plan": self.plan,
"cost": self.cost,
"nodes_expanded": self.nodes_expanded,
"path": [{"x": p[0], "y": p[1]} for p in self.path],
}
@dataclass
class DeliveryAssignment:
"""Assignment of a destination to a store/truck."""
store_id: int
destination_id: int
path_result: PathResult
def to_dict(self) -> dict:
return {
"store_id": self.store_id,
"destination_id": self.destination_id,
"path": self.path_result.to_dict(),
}
@dataclass
class PlanResult:
"""Result of the complete delivery planning."""
assignments: List[DeliveryAssignment]
total_cost: float
total_nodes_expanded: int
def to_string(self) -> str:
"""Format output as specified."""
parts = []
for assignment in self.assignments:
parts.append(
f"({assignment.store_id},{assignment.destination_id}):{assignment.path_result.to_string()}"
)
return ";".join(parts)
def to_dict(self) -> dict:
return {
"assignments": [a.to_dict() for a in self.assignments],
"total_cost": self.total_cost,
"total_nodes_expanded": self.total_nodes_expanded,
}
@dataclass
class SearchStep:
"""Represents a single step in the search process for visualization."""
step_number: int
current_node: Tuple[int, int]
action: Optional[str]
frontier: List[Tuple[int, int]]
explored: List[Tuple[int, int]]
current_path: List[Tuple[int, int]]
path_cost: float
def to_dict(self) -> dict:
return {
"stepNumber": self.step_number,
"currentNode": {"x": self.current_node[0], "y": self.current_node[1]},
"action": self.action,
"frontier": [{"x": p[0], "y": p[1]} for p in self.frontier],
"explored": [{"x": p[0], "y": p[1]} for p in self.explored],
"currentPath": [{"x": p[0], "y": p[1]} for p in self.current_path],
"pathCost": self.path_cost,
}
@dataclass
class SearchMetrics:
"""Performance metrics for a search execution."""
runtime_ms: float
memory_kb: float
cpu_percent: float
nodes_expanded: int
path_cost: float
path_length: int
def to_dict(self) -> dict:
return {
"runtime_ms": self.runtime_ms,
"memory_kb": self.memory_kb,
"cpu_percent": self.cpu_percent,
"nodes_expanded": self.nodes_expanded,
"path_cost": self.path_cost,
"path_length": self.path_length,
}