File size: 4,163 Bytes
e067c2d
47bba68
e067c2d
 
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
47bba68
e067c2d
 
 
47bba68
 
 
e067c2d
 
 
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
 
 
 
47bba68
e067c2d
47bba68
e067c2d
 
 
 
 
 
 
 
47bba68
e067c2d
 
 
47bba68
e067c2d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""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,
        }