File size: 5,662 Bytes
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
"""DeliverySearch - Search problem for package delivery."""

from typing import List, Tuple, Optional, Dict
from .generic_search import GenericSearch
from ..models.grid import Grid
from ..models.entities import Tunnel
from ..models.state import PathResult, SearchStep
from ..heuristics import create_tunnel_aware_heuristic


class DeliverySearch(GenericSearch):
    """
    Search problem for finding path from a store to a destination.

    Implements the GenericSearch interface for the package delivery problem.
    Supports movement in 4 directions (up/down/left/right) and tunnel travel.
    """

    def __init__(
        self,
        grid: Grid,
        start: Tuple[int, int],
        goal: Tuple[int, int],
        tunnels: Optional[List[Tunnel]] = None,
    ):
        """
        Initialize the delivery search problem.

        Args:
            grid: The city grid with traffic information
            start: Starting position (store location)
            goal: Goal position (destination location)
            tunnels: List of available tunnels
        """
        self.grid = grid
        self.start = start
        self.goal = goal
        self.tunnels = tunnels or []

        # Create tunnel lookup by entrance position
        self._tunnel_entrances: Dict[Tuple[int, int], Tunnel] = {}
        for tunnel in self.tunnels:
            self._tunnel_entrances[tunnel.entrance1] = tunnel
            self._tunnel_entrances[tunnel.entrance2] = tunnel

        # Create tunnel-aware heuristic
        self._tunnel_heuristic = create_tunnel_aware_heuristic(self.tunnels)

    def initial_state(self) -> Tuple[int, int]:
        """Return the starting position."""
        return self.start

    def goal_test(self, state: Tuple[int, int]) -> bool:
        """Check if current state is the goal."""
        return state == self.goal

    def actions(self, state: Tuple[int, int]) -> List[str]:
        """
        Return list of valid actions from current state.

        Actions:
            - up: Move up (y+1)
            - down: Move down (y-1)
            - left: Move left (x-1)
            - right: Move right (x+1)
            - tunnel: Use tunnel if at entrance

        Returns:
            List of valid action strings
        """
        x, y = state
        valid_actions = []

        # Check each direction
        directions = [
            ("up", (x, y + 1)),
            ("down", (x, y - 1)),
            ("left", (x - 1, y)),
            ("right", (x + 1, y)),
        ]

        for action, new_pos in directions:
            if self.grid.is_valid_position(new_pos):
                if not self.grid.is_blocked(state, new_pos):
                    valid_actions.append(action)

        # Check for tunnel
        if state in self._tunnel_entrances:
            valid_actions.append("tunnel")

        return valid_actions

    def result(self, state: Tuple[int, int], action: str) -> Tuple[int, int]:
        """
        Apply action to state and return new state.

        Args:
            state: Current position
            action: Action to take

        Returns:
            New position after action
        """
        x, y = state

        if action == "up":
            return (x, y + 1)
        elif action == "down":
            return (x, y - 1)
        elif action == "left":
            return (x - 1, y)
        elif action == "right":
            return (x + 1, y)
        elif action == "tunnel":
            if state in self._tunnel_entrances:
                tunnel = self._tunnel_entrances[state]
                return tunnel.get_other_entrance(state)
            else:
                raise ValueError(f"No tunnel entrance at {state}")
        else:
            raise ValueError(f"Unknown action: {action}")

    def step_cost(
        self, state: Tuple[int, int], action: str, next_state: Tuple[int, int]
    ) -> float:
        """
        Return the cost of taking an action.

        For regular moves: cost = traffic level of the segment
        For tunnels: cost = Manhattan distance between entrances

        Args:
            state: Current position
            action: Action taken
            next_state: Resulting position

        Returns:
            Cost of the action
        """
        if action == "tunnel":
            tunnel = self._tunnel_entrances.get(state)
            if tunnel:
                return tunnel.cost
            return 0.0
        else:
            # Regular movement - cost is traffic level
            return self.grid.get_traffic(state, next_state)

    def heuristic(self, state: Tuple[int, int]) -> float:
        """
        Tunnel-aware heuristic for A* search.

        Args:
            state: Current position

        Returns:
            Estimated cost to goal
        """
        return self._tunnel_heuristic(state, self.goal)

    @staticmethod
    def path(
        grid: Grid,
        start: Tuple[int, int],
        goal: Tuple[int, int],
        tunnels: List[Tunnel],
        strategy: str,
        visualize: bool = False,
    ) -> Tuple[PathResult, Optional[List[SearchStep]]]:
        """
        Find path from start to goal using specified strategy.

        Args:
            grid: The city grid
            start: Starting position
            goal: Goal position
            tunnels: Available tunnels
            strategy: Search strategy (BF, DF, ID, UC, GR1, GR2, AS1, AS2)
            visualize: If True, collect visualization steps

        Returns:
            Tuple of (PathResult, Optional[List[SearchStep]])
        """
        search = DeliverySearch(grid, start, goal, tunnels)
        return search.solve(strategy, visualize)