WIP faster paths
parent
6194d555b8
commit
ab089feb6e
137
Fusion_Graph.py
137
Fusion_Graph.py
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
|
|
||||||
|
@ -28,18 +29,9 @@ class Game:
|
||||||
self.minorItemLocations = list()
|
self.minorItemLocations = list()
|
||||||
self.itemLocations = set()
|
self.itemLocations = set()
|
||||||
self.patcher = dict()
|
self.patcher = dict()
|
||||||
self.graph.clear()
|
self.oldtime = 0.0
|
||||||
self.areaConnections.clear()
|
self.bfstime = 0.0
|
||||||
self.areaConnectionOffsets.clear()
|
self.dfstime = 0.0
|
||||||
self.doorConnections.clear()
|
|
||||||
self.rooms.clear()
|
|
||||||
self.requirements.clear()
|
|
||||||
self.visited.clear()
|
|
||||||
self.queue.clear()
|
|
||||||
self.majorItemLocations.clear()
|
|
||||||
self.minorItemLocations.clear()
|
|
||||||
self.itemLocations.clear()
|
|
||||||
self.patcher.clear()
|
|
||||||
|
|
||||||
# print('DEBUG: Opening ROM to pick stuff')
|
# print('DEBUG: Opening ROM to pick stuff')
|
||||||
|
|
||||||
|
@ -96,6 +88,8 @@ class Game:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def print_stats(self):
|
||||||
|
print("Path search time: old search = {}s, bfs time = {}s, dfs time = {}s".format(self.oldtime, self.bfstime, self.dfstime))
|
||||||
|
|
||||||
def set_setting(self, setting, value):
|
def set_setting(self, setting, value):
|
||||||
self.settings[setting] = value
|
self.settings[setting] = value
|
||||||
|
@ -209,6 +203,15 @@ class Game:
|
||||||
return self.requirements.get(checkRequirement)
|
return self.requirements.get(checkRequirement)
|
||||||
|
|
||||||
def get_path(self, start, end, LimitArea=False, path=None, depth=100):
|
def get_path(self, start, end, LimitArea=False, path=None, depth=100):
|
||||||
|
#return [start,end] if self.has_path(start,end,LimitArea, depth) else None
|
||||||
|
|
||||||
|
t = time.perf_counter()
|
||||||
|
path = self._get_path(start, end, LimitArea, path, depth)
|
||||||
|
self.oldtime += time.perf_counter() - t
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _get_path(self, start, end, LimitArea, path, depth):
|
||||||
if path == None:
|
if path == None:
|
||||||
self.visited.clear()
|
self.visited.clear()
|
||||||
self.queue.clear()
|
self.queue.clear()
|
||||||
|
@ -240,12 +243,118 @@ class Game:
|
||||||
node = edge[1]
|
node = edge[1]
|
||||||
pathReqs = self.get_requirements(start, node)
|
pathReqs = self.get_requirements(start, node)
|
||||||
if pathReqs == None:
|
if pathReqs == None:
|
||||||
newpath = self.get_path(node, end, LimitArea, path, depth)
|
newpath = self._get_path(node, end, LimitArea, path, depth)
|
||||||
if newpath:
|
if newpath:
|
||||||
path = path + [node]
|
path = path + [node]
|
||||||
return newpath
|
return newpath
|
||||||
elif pathReqs == True:
|
elif pathReqs == True:
|
||||||
newpath = self.get_path(node, end, LimitArea, path, depth)
|
newpath = self._get_path(node, end, LimitArea, path, depth)
|
||||||
if newpath:
|
if newpath:
|
||||||
path = path + [node]
|
path = path + [node]
|
||||||
return newpath
|
return newpath
|
||||||
|
|
||||||
|
def has_path(self, start, end, LimitArea=False, depth=100):
|
||||||
|
if LimitArea:
|
||||||
|
area = self.itemArea.get(start)
|
||||||
|
if area == None:
|
||||||
|
for n in range(0, 7):
|
||||||
|
if 'S{}'.format(n) in start:
|
||||||
|
area = n
|
||||||
|
if area == None:
|
||||||
|
for n in range(0, 7):
|
||||||
|
if 'S{}'.format(n) in end:
|
||||||
|
area = n
|
||||||
|
else:
|
||||||
|
area = None
|
||||||
|
t = time.perf_counter()
|
||||||
|
result = self.has_path_bfs(start, end, area=area, max_depth=depth)
|
||||||
|
self.bfstime += time.perf_counter() - t
|
||||||
|
t = time.perf_counter()
|
||||||
|
r2 =self.has_path_dfs(start, end, area=area)
|
||||||
|
assert result == r2, (start, end, result, r2, area)
|
||||||
|
self.dfstime += time.perf_counter() - t
|
||||||
|
return result
|
||||||
|
|
||||||
|
# NOTE: both the start and end node need to be excluded from
|
||||||
|
# area checks because they can be things like bosses or the Water Pump
|
||||||
|
# that aren't associated with an area. all the intermediate nodes
|
||||||
|
# should be doors, and doors all have their sector number in their names.
|
||||||
|
|
||||||
|
def has_path_bfs(self, start, end, area=None, max_depth=100):
|
||||||
|
if start not in self.graph:
|
||||||
|
return False
|
||||||
|
if start == end:
|
||||||
|
return True
|
||||||
|
if area != None:
|
||||||
|
areaStr = 'S{}'.format(area)
|
||||||
|
seen = {start}
|
||||||
|
frontier = [start]
|
||||||
|
next_frontier = []
|
||||||
|
depth = 0
|
||||||
|
while frontier:
|
||||||
|
depth += 1
|
||||||
|
#if depth > max_depth: break
|
||||||
|
for node in frontier:
|
||||||
|
for neighbor in self.graph[node]:
|
||||||
|
if neighbor in seen:
|
||||||
|
continue
|
||||||
|
if neighbor in self.itemLocations:
|
||||||
|
if neighbor == end:
|
||||||
|
pathReqs = self.get_requirements(node, neighbor)
|
||||||
|
if pathReqs == None or pathReqs == True:
|
||||||
|
return True
|
||||||
|
#seen.add(neighbor)
|
||||||
|
continue
|
||||||
|
if area is not None:
|
||||||
|
if areaStr not in neighbor and neighbor != end:
|
||||||
|
continue
|
||||||
|
pathReqs = self.get_requirements(node, neighbor)
|
||||||
|
if pathReqs != None and pathReqs != True:
|
||||||
|
continue
|
||||||
|
if neighbor == end:
|
||||||
|
return True
|
||||||
|
|
||||||
|
next_frontier.append(neighbor)
|
||||||
|
|
||||||
|
# since this is a breadth-first search on an unweighted graph,
|
||||||
|
# we know that no other paths can be shorter than this one,
|
||||||
|
# so we can immediately mark the node as seen to prevent
|
||||||
|
# adding it to the frontier again
|
||||||
|
seen.add(neighbor)
|
||||||
|
|
||||||
|
frontier.clear()
|
||||||
|
frontier, next_frontier = next_frontier, frontier
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_path_dfs(self, start, end, area=None):
|
||||||
|
if start not in self.graph:
|
||||||
|
return False
|
||||||
|
if start == end:
|
||||||
|
return True
|
||||||
|
visited = set()
|
||||||
|
stack = [start]
|
||||||
|
if area != None:
|
||||||
|
areaStr = 'S{}'.format(area)
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
if node in visited:
|
||||||
|
continue
|
||||||
|
visited.add(node)
|
||||||
|
if node == end:
|
||||||
|
return True
|
||||||
|
if node is not start:
|
||||||
|
if node in self.itemLocations:
|
||||||
|
continue
|
||||||
|
if area is not None:
|
||||||
|
if areaStr not in node:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for neighbor in reversed(self.graph[node]):
|
||||||
|
if neighbor in visited:
|
||||||
|
continue
|
||||||
|
pathReqs = self.get_requirements(node, neighbor)
|
||||||
|
if pathReqs != None and pathReqs != True:
|
||||||
|
continue
|
||||||
|
stack.append(neighbor)
|
||||||
|
return False
|
||||||
|
|
|
@ -3135,6 +3135,9 @@ def start_randomizer(rom, settings):
|
||||||
seedTime = time.time() - startTime
|
seedTime = time.time() - startTime
|
||||||
print(str(FileName))
|
print(str(FileName))
|
||||||
print('Randomized in:', seedTime)
|
print('Randomized in:', seedTime)
|
||||||
|
World.print_stats()
|
||||||
|
print("water lowered:", WaterLowered)
|
||||||
|
print("len(world.graph) = ", len(World.graph))
|
||||||
|
|
||||||
totalRandoTime = time.time() - totalRandoTime
|
totalRandoTime = time.time() - totalRandoTime
|
||||||
print('All seeds took:', totalRandoTime)
|
print('All seeds took:', totalRandoTime)
|
||||||
|
|
Loading…
Reference in New Issue