Compare commits
No commits in common. "wip-faster-paths" and "master" have entirely different histories.
wip-faster
...
master
153
Fusion_Graph.py
153
Fusion_Graph.py
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
|
|
||||||
|
@ -27,11 +26,20 @@ class Game:
|
||||||
self.queue = list()
|
self.queue = list()
|
||||||
self.majorItemLocations = list()
|
self.majorItemLocations = list()
|
||||||
self.minorItemLocations = list()
|
self.minorItemLocations = list()
|
||||||
self.itemLocations = set()
|
self.itemLocations = list()
|
||||||
self.patcher = dict()
|
self.patcher = dict()
|
||||||
self.oldtime = 0.0
|
self.graph.clear()
|
||||||
self.bfstime = 0.0
|
self.areaConnections.clear()
|
||||||
self.dfstime = 0.0
|
self.areaConnectionOffsets.clear()
|
||||||
|
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')
|
||||||
|
|
||||||
|
@ -88,8 +96,6 @@ 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
|
||||||
|
@ -176,25 +182,29 @@ class Game:
|
||||||
|
|
||||||
|
|
||||||
def add_to_majors(self, item):
|
def add_to_majors(self, item):
|
||||||
self.itemLocations.add(item)
|
if item not in self.itemLocations:
|
||||||
|
self.itemLocations.append(item)
|
||||||
if item not in self.majorItemLocations:
|
if item not in self.majorItemLocations:
|
||||||
self.majorItemLocations.append(item)
|
self.majorItemLocations.append(item)
|
||||||
|
|
||||||
|
|
||||||
def add_list_to_majors(self, locations):
|
def add_list_to_majors(self, locations):
|
||||||
for item in locations:
|
for item in locations:
|
||||||
self.itemLocations.add(item)
|
if item not in self.itemLocations:
|
||||||
|
self.itemLocations.append(item)
|
||||||
if item not in self.majorItemLocations:
|
if item not in self.majorItemLocations:
|
||||||
self.majorItemLocations.append(item)
|
self.majorItemLocations.append(item)
|
||||||
|
|
||||||
def add_to_minors(self, item):
|
def add_to_minors(self, item):
|
||||||
self.itemLocations.add(item)
|
if item not in self.itemLocations:
|
||||||
|
self.itemLocations.append(item)
|
||||||
if item not in self.minorItemLocations:
|
if item not in self.minorItemLocations:
|
||||||
self.minorItemLocations.append(item)
|
self.minorItemLocations.append(item)
|
||||||
|
|
||||||
def add_list_to_minors(self, locations):
|
def add_list_to_minors(self, locations):
|
||||||
for item in locations:
|
for item in locations:
|
||||||
self.itemLocations.add(item)
|
if item not in self.itemLocations:
|
||||||
|
self.itemLocations.append(item)
|
||||||
if item not in self.minorItemLocations:
|
if item not in self.minorItemLocations:
|
||||||
self.minorItemLocations.append(item)
|
self.minorItemLocations.append(item)
|
||||||
|
|
||||||
|
@ -203,15 +213,6 @@ 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()
|
||||||
|
@ -229,7 +230,7 @@ class Game:
|
||||||
continue
|
continue
|
||||||
if point in path:
|
if point in path:
|
||||||
continue
|
continue
|
||||||
if LimitArea and point != end:
|
if LimitArea:
|
||||||
for area in range(0, 7):
|
for area in range(0, 7):
|
||||||
if 'S{}'.format(area) in start:
|
if 'S{}'.format(area) in start:
|
||||||
if 'S{}'.format(area) not in point:
|
if 'S{}'.format(area) not in point:
|
||||||
|
@ -243,118 +244,12 @@ 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
|
|
||||||
|
|
150
Randomizer.py
150
Randomizer.py
|
@ -1624,8 +1624,8 @@ def update_requirements(graph):
|
||||||
|
|
||||||
|
|
||||||
def find_available_areas(graph):
|
def find_available_areas(graph):
|
||||||
AreaOpen[int(StartLocation[1:2])] = StartLocation
|
check = int(StartLocation[1:2])
|
||||||
|
AreaOpen[check] = StartLocation
|
||||||
if AreaOpen[0] == None:
|
if AreaOpen[0] == None:
|
||||||
check = 'S0-32'
|
check = 'S0-32'
|
||||||
path = graph.get_path(StartLocation, check)
|
path = graph.get_path(StartLocation, check)
|
||||||
|
@ -1633,33 +1633,94 @@ def find_available_areas(graph):
|
||||||
path = graph.get_path(check, StartLocation)
|
path = graph.get_path(check, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[0] = check
|
AreaOpen[0] = check
|
||||||
|
if AreaOpen[1] == None:
|
||||||
def check_sector(area, check, tunnel1, tunnel2):
|
check = 'S1-00'
|
||||||
if AreaOpen[area] == None:
|
path = graph.get_path(StartLocation, check)
|
||||||
path = graph.get_path(StartLocation, check)
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(check, StartLocation)
|
AreaOpen[1] = check
|
||||||
|
if ScrewAttack:
|
||||||
|
if AreaOpen[1] == None:
|
||||||
|
check = 'S1-6B'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[area] = check
|
path = graph.get_path(check, StartLocation)
|
||||||
if ScrewAttack:
|
|
||||||
if AreaOpen[area] == None:
|
|
||||||
path = graph.get_path(StartLocation, tunnel1)
|
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(tunnel1, StartLocation)
|
AreaOpen[1] = check
|
||||||
if path != None:
|
if AreaOpen[1] == None:
|
||||||
AreaOpen[area] = tunnel1
|
check = 'S1-68'
|
||||||
if AreaOpen[area] == None:
|
path = graph.get_path(StartLocation, check)
|
||||||
path = graph.get_path(StartLocation, tunnel2)
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(tunnel2, StartLocation)
|
AreaOpen[1] = check
|
||||||
if path != None:
|
if AreaOpen[2] == None:
|
||||||
AreaOpen[area] = tunnel2
|
check = 'S2-00'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
check_sector(1, 'S1-00', 'S1-6B', 'S1-68')
|
if path != None:
|
||||||
check_sector(2, 'S2-00', 'S2-7F', 'S2-82')
|
path = graph.get_path(check, StartLocation)
|
||||||
check_sector(3, 'S3-00', 'S3-56', 'S3-59')
|
if path != None:
|
||||||
check_sector(4, 'S4-00', 'S4-6A', 'S4-6C')
|
AreaOpen[2] = check
|
||||||
|
if ScrewAttack:
|
||||||
|
if AreaOpen[2] == None:
|
||||||
|
check = 'S2-7F'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[2] = check
|
||||||
|
if AreaOpen[2] == None:
|
||||||
|
check = 'S2-82'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[2] = check
|
||||||
|
if AreaOpen[3] == None:
|
||||||
|
check = 'S3-00'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[3] = check
|
||||||
|
if ScrewAttack:
|
||||||
|
if AreaOpen[3] == None:
|
||||||
|
check = 'S3-56'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[3] = check
|
||||||
|
if AreaOpen[3] == None:
|
||||||
|
check = 'S3-59'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[3] = check
|
||||||
|
if AreaOpen[4] == None:
|
||||||
|
check = 'S4-00'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[4] = check
|
||||||
|
if ScrewAttack:
|
||||||
|
if AreaOpen[4] == None:
|
||||||
|
check = 'S4-6A'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[4] = check
|
||||||
|
if AreaOpen[4] == None:
|
||||||
|
check = 'S4-6C'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[4] = check
|
||||||
if AreaOpen[5] == None:
|
if AreaOpen[5] == None:
|
||||||
check = 'S5-00'
|
check = 'S5-00'
|
||||||
path = graph.get_path(StartLocation, check)
|
path = graph.get_path(StartLocation, check)
|
||||||
|
@ -1675,9 +1736,28 @@ def find_available_areas(graph):
|
||||||
path = graph.get_path(check, StartLocation)
|
path = graph.get_path(check, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[5] = check
|
AreaOpen[5] = check
|
||||||
|
if AreaOpen[6] == None:
|
||||||
check_sector(6, 'S6-00', 'S6-51', 'S6-54')
|
check = 'S6-00'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[6] = check
|
||||||
|
if ScrewAttack:
|
||||||
|
if AreaOpen[6] == None:
|
||||||
|
check = 'S6-51'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[6] = check
|
||||||
|
if AreaOpen[6] == None:
|
||||||
|
check = 'S6-54'
|
||||||
|
path = graph.get_path(StartLocation, check)
|
||||||
|
if path != None:
|
||||||
|
path = graph.get_path(check, StartLocation)
|
||||||
|
if path != None:
|
||||||
|
AreaOpen[6] = check
|
||||||
if BlueDoors == False:
|
if BlueDoors == False:
|
||||||
path = graph.get_path(StartLocation, 'Security-Level-1', depth=250)
|
path = graph.get_path(StartLocation, 'Security-Level-1', depth=250)
|
||||||
if path != None:
|
if path != None:
|
||||||
|
@ -2173,7 +2253,6 @@ def randomize_game(graph):
|
||||||
|
|
||||||
find_available_areas(graph)
|
find_available_areas(graph)
|
||||||
for location in MajorLocations:
|
for location in MajorLocations:
|
||||||
# potential find_all_available_items
|
|
||||||
path = graph.get_path(StartLocation, location)
|
path = graph.get_path(StartLocation, location)
|
||||||
if path != None:
|
if path != None:
|
||||||
AccessibleLocations.append(location)
|
AccessibleLocations.append(location)
|
||||||
|
@ -2250,9 +2329,8 @@ def randomize_game(graph):
|
||||||
if location not in UsedLocations:
|
if location not in UsedLocations:
|
||||||
if location not in AccessibleLocations:
|
if location not in AccessibleLocations:
|
||||||
if location == 'Data S0' or location == 'Item S0-05-16':
|
if location == 'Data S0' or location == 'Item S0-05-16':
|
||||||
path = graph.get_path(StartLocation, location, depth=250)
|
path = graph.get_path(location, StartLocation, depth=250)
|
||||||
else:
|
else:
|
||||||
# potential find_all_available_items
|
|
||||||
for area in range(0, 7):
|
for area in range(0, 7):
|
||||||
if location in AreaItemLocations[area]:
|
if location in AreaItemLocations[area]:
|
||||||
if AreaOpen[area]:
|
if AreaOpen[area]:
|
||||||
|
@ -2324,7 +2402,6 @@ def randomize_game(graph):
|
||||||
tankCheck = math.ceil(healthCheck / MissileDamage / 5)
|
tankCheck = math.ceil(healthCheck / MissileDamage / 5)
|
||||||
PossibleMissileTanks.clear()
|
PossibleMissileTanks.clear()
|
||||||
if SeedSettings['MajorMinor']:
|
if SeedSettings['MajorMinor']:
|
||||||
# potential find_all_available_items + could probably reuse previous result
|
|
||||||
for missileLocation in MinorLocations:
|
for missileLocation in MinorLocations:
|
||||||
if missileLocation not in UsedLocations:
|
if missileLocation not in UsedLocations:
|
||||||
missilePath = None
|
missilePath = None
|
||||||
|
@ -2851,9 +2928,9 @@ def patch_game():
|
||||||
itemProgression.update({ UsedLocations[x]: PlacedItems[x] })
|
itemProgression.update({ UsedLocations[x]: PlacedItems[x] })
|
||||||
spoilerLog.update({ 'Item order': itemProgression })
|
spoilerLog.update({ 'Item order': itemProgression })
|
||||||
itemDict = dict()
|
itemDict = dict()
|
||||||
itemLocations = sorted(World.itemLocations)
|
World.itemLocations.sort()
|
||||||
for x in range(0, len(itemLocations)):
|
for x in range(0, len(World.itemLocations)):
|
||||||
itemDict.update({ itemLocations[x]: 0 })
|
itemDict.update({ World.itemLocations[x]: 0 })
|
||||||
for x in range(0, len(PlacedItems)):
|
for x in range(0, len(PlacedItems)):
|
||||||
itemDict.update({ UsedLocations[x]: PlacedItems[x] })
|
itemDict.update({ UsedLocations[x]: PlacedItems[x] })
|
||||||
spoilerLog.update({ 'Items': itemDict })
|
spoilerLog.update({ 'Items': itemDict })
|
||||||
|
@ -3058,9 +3135,6 @@ 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