-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase.py
151 lines (119 loc) · 4.26 KB
/
base.py
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
from typing import Any, Optional
import math
class Position:
__slots__ = ("_x","_y")
def __init__(self,x : int, y : int):
self._x = x
self._y = y
pass
@property
def x(self):
return self._x
@property
def y(self):
return self._y
def __getitem__(self, index) -> int:
if index == 0: return self._x
elif index == 1: return self._y
else: raise IndexError("Index out of range")
pass
def __add__(self, other):
return self.__class__(self._x + other._x, self._y + other._y)
pass
def __sub__(self, other):
return self.__class__(self._x - other._x, self._y - other._y)
pass
def __abs__(self):
return math.sqrt(self._x**2 + self._y**2)
pass
def __eq__(self,other):
return self._x == other._x and self._y == other._y
def __neq__(self,other):
return self._x != other._x or self._y != other._y
def __str__(self) -> str:
return f"({self._x},{self._y})"
pass
def __repr__(self) -> str:
return f"<Position ({self._x},{self._y})>"
pass
def __hash__(self) -> int:
return hash((self._x,self._y)) # This is a bad hash function, but I can't figure out a better way
pass
pass
class Board:
__slots__ = ("board","_width","_height")
def __init__(self, width : int, height : int):
self.board : list[list[Any]] = [[None for y in range(height)] for x in range(width)]
self._width = width
self._height = height
pass
@classmethod
def from_list(cls, raw_board : list[list[Any]]):
self = cls(len(raw_board),len(raw_board[0]))
self.board = raw_board
return self
pass
@property
def width(self): return self._width
@property
def height(self): return self._height
def __getitem__(self, index) -> Any:
return self.board[index[0]][index[1]]
pass
def __setitem__(self, index, value):
self.board[index[0]][index[1]] = value
pass
pass
class AStar:
__slots__ = ("start","goal")
def g(self, pos) -> float:
raise NotImplementedError("method g never implemented. Override this method when inheriting from AStar")
pass
def h(self, pos) -> float:
raise NotImplementedError("method h never implemented. Override this method when inheriting from AStar")
pass
def get_neighbors(self, pos) -> tuple[Any]:
raise NotImplementedError("method get_neighbors never implemented. Override this method when inheriting from AStar")
pass
def __init__(self):
self.start : Optional[int] = None
self.goal : Optional[int] = None
pass
def evaluate(self, start, goal) -> tuple:
f = lambda node: self.g(node) + self.h(node)
self.start, self.goal = start, goal
OPEN = set()
CLOSE= set()
PARENTS = dict()
OPEN.add((self.start,f(self.start)))
PARENTS[start] = start
while True:
active_node, cost = min(OPEN,key=lambda data: data[1]) # Find open node with smallest f
if active_node == goal: # If node is goal, finish
del cost
break
OPEN.remove((active_node,cost)) # Remove node from OPEN
if active_node in CLOSE: continue # Don't retread paths
del cost
neighbors = self.get_neighbors(active_node) # Find neighbors of active_node
for pos in neighbors: # Add all neighbors to OPEN and calculate their f-value
if pos in CLOSE: continue # DO NOT RETREAD!
OPEN.add((pos,f(pos)))
if pos not in PARENTS.keys() or self.h(PARENTS[pos]) > self.h(active_node): # Only replace a parent if this new parent has a better g (i.e. )
PARENTS[pos] = active_node
pass
del pos, neighbors
CLOSE.add(active_node)
del active_node
pass
path = []
# Descend parent ladder
while PARENTS[active_node] != active_node:
path.append(active_node)
active_node = PARENTS[active_node]
pass
path.append(start)
path.reverse()
return path
pass
pass