WIP faster paths

wip-faster-paths
magical 2024-02-07 22:34:37 -08:00
parent 6194d555b8
commit ab089feb6e
2 changed files with 126 additions and 14 deletions

View File

@ -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

View File

@ -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)