Compare commits
6 Commits
master
...
wip-faster
Author | SHA1 | Date |
---|---|---|
magical | d65898994a | |
magical | 2e972d876f | |
magical | b95fef681f | |
magical | ab089feb6e | |
magical | 6194d555b8 | |
magical | cbb85e3baa |
153
Fusion_Graph.py
153
Fusion_Graph.py
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
|
|
||||||
|
@ -26,20 +27,11 @@ class Game:
|
||||||
self.queue = list()
|
self.queue = list()
|
||||||
self.majorItemLocations = list()
|
self.majorItemLocations = list()
|
||||||
self.minorItemLocations = list()
|
self.minorItemLocations = list()
|
||||||
self.itemLocations = list()
|
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
|
||||||
|
@ -182,29 +176,25 @@ class Game:
|
||||||
|
|
||||||
|
|
||||||
def add_to_majors(self, item):
|
def add_to_majors(self, item):
|
||||||
if item not in self.itemLocations:
|
self.itemLocations.add(item)
|
||||||
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:
|
||||||
if item not in self.itemLocations:
|
self.itemLocations.add(item)
|
||||||
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):
|
||||||
if item not in self.itemLocations:
|
self.itemLocations.add(item)
|
||||||
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:
|
||||||
if item not in self.itemLocations:
|
self.itemLocations.add(item)
|
||||||
self.itemLocations.append(item)
|
|
||||||
if item not in self.minorItemLocations:
|
if item not in self.minorItemLocations:
|
||||||
self.minorItemLocations.append(item)
|
self.minorItemLocations.append(item)
|
||||||
|
|
||||||
|
@ -213,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()
|
||||||
|
@ -230,7 +229,7 @@ class Game:
|
||||||
continue
|
continue
|
||||||
if point in path:
|
if point in path:
|
||||||
continue
|
continue
|
||||||
if LimitArea:
|
if LimitArea and point != end:
|
||||||
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:
|
||||||
|
@ -244,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
|
||||||
|
|
140
Randomizer.py
140
Randomizer.py
|
@ -1624,8 +1624,8 @@ def update_requirements(graph):
|
||||||
|
|
||||||
|
|
||||||
def find_available_areas(graph):
|
def find_available_areas(graph):
|
||||||
check = int(StartLocation[1:2])
|
AreaOpen[int(StartLocation[1:2])] = StartLocation
|
||||||
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,94 +1633,33 @@ 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:
|
|
||||||
check = 'S1-00'
|
def check_sector(area, check, tunnel1, tunnel2):
|
||||||
|
if AreaOpen[area] == None:
|
||||||
path = graph.get_path(StartLocation, check)
|
path = graph.get_path(StartLocation, check)
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(check, StartLocation)
|
path = graph.get_path(check, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[1] = check
|
AreaOpen[area] = check
|
||||||
if ScrewAttack:
|
if ScrewAttack:
|
||||||
if AreaOpen[1] == None:
|
if AreaOpen[area] == None:
|
||||||
check = 'S1-6B'
|
path = graph.get_path(StartLocation, tunnel1)
|
||||||
path = graph.get_path(StartLocation, check)
|
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(check, StartLocation)
|
path = graph.get_path(tunnel1, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[1] = check
|
AreaOpen[area] = tunnel1
|
||||||
if AreaOpen[1] == None:
|
if AreaOpen[area] == None:
|
||||||
check = 'S1-68'
|
path = graph.get_path(StartLocation, tunnel2)
|
||||||
path = graph.get_path(StartLocation, check)
|
|
||||||
if path != None:
|
if path != None:
|
||||||
path = graph.get_path(check, StartLocation)
|
path = graph.get_path(tunnel2, StartLocation)
|
||||||
if path != None:
|
if path != None:
|
||||||
AreaOpen[1] = check
|
AreaOpen[area] = tunnel2
|
||||||
if AreaOpen[2] == None:
|
|
||||||
check = 'S2-00'
|
check_sector(1, 'S1-00', 'S1-6B', 'S1-68')
|
||||||
path = graph.get_path(StartLocation, check)
|
check_sector(2, 'S2-00', 'S2-7F', 'S2-82')
|
||||||
if path != None:
|
check_sector(3, 'S3-00', 'S3-56', 'S3-59')
|
||||||
path = graph.get_path(check, StartLocation)
|
check_sector(4, 'S4-00', 'S4-6A', 'S4-6C')
|
||||||
if path != None:
|
|
||||||
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)
|
||||||
|
@ -1736,28 +1675,9 @@ 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 = 'S6-00'
|
check_sector(6, 'S6-00', 'S6-51', 'S6-54')
|
||||||
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:
|
||||||
|
@ -2253,6 +2173,7 @@ 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)
|
||||||
|
@ -2329,8 +2250,9 @@ 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(location, StartLocation, depth=250)
|
path = graph.get_path(StartLocation, location, 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]:
|
||||||
|
@ -2402,6 +2324,7 @@ 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
|
||||||
|
@ -2928,9 +2851,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()
|
||||||
World.itemLocations.sort()
|
itemLocations = sorted(World.itemLocations)
|
||||||
for x in range(0, len(World.itemLocations)):
|
for x in range(0, len(itemLocations)):
|
||||||
itemDict.update({ World.itemLocations[x]: 0 })
|
itemDict.update({ 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 })
|
||||||
|
@ -3135,6 +3058,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