'''
path.py
Plugs into the routine module to allow for the easy construction of paths and
path following.
'''
from mud import *
from mudsys import add_cmd
import mud, mudsys
################################################################################
# functions
################################################################################
def leads_to(frm, to):
'''returns whether from leads directly to to'''
for ex in frm.exnames:
if frm.exit(ex).dest is to:
return True
return False
def shortest_path_bfs(frm, to, ignore_doors = False, ignore = None):
'''calculates the shortest path, but uses a breadth first search. More
efficient than depth-first seach for very short paths with lots of
branches or very large muds.'''
if frm == to:
return [frm]
rooms = []
depth = []
if ignore == None:
ignore = set()
ignore.add(frm)
# what is our highest depth
i = 1
# the index of the room our last depth started at
j = 0
# append ourself and our depth
rooms.append(frm)
depth.append(i)
# keep going until we find To, or we can't go any deeper
found = False
while not found:
prev_depth = rooms[j:]
for rm in prev_depth:
for ex in rm.exnames:
dest = rm.exit(ex).dest
if dest in ignore:
continue
rooms.append(dest)
depth.append(i)
if dest is to:
found = True
break
ignore.add(rm)
if found:
break
i += 1
j += len(prev_depth)
# go backwards from our destination
rooms.reverse()
depth.reverse()
# first step, pull out our destination room
path = [to]
# pull out all other rooms on the shortest path
while len(rooms) > 0:
curr_depth = depth[0]
# figure out which room in our current layer
# links to our previous room in the shortest path
for next in rooms:
if leads_to(next, path[len(path)-1]):
path.append(next)
break
# clear our last layer
i = 0
while i < len(depth) and depth[i] == curr_depth:
i += 1
rooms = rooms[i:]
depth = depth[i:]
# put it back in order
path.reverse()
return path
def shortest_path_dfs(frm, to, ignore_doors = False, ignore = None):
'''returns the steps needed to take to go from one room to another. More
efficient than breadth-first search for very long paths with only a few
branches, or very small muds.'''
path = []
if ignore == None:
ignore = set()
# a list of rooms we ignore for tracking. Add ourself so we don't loop back
ignore.add(frm)
# if we're the target room, return an empty list
if frm is to:
return [frm]
# build the shortest path
for ex in frm.exnames:
# check if it has a door, and if we ignore it
if frm.exit(ex).is_closed:
continue
# get the dest room. if there is none, skip this exit
next_room = frm.exit(ex).dest
if next_room is None:
continue
# if we already know this is a dead end or a loopback, skip it
if next_room in ignore:
continue
next_path = shortest_path(next_room, to, ignore_doors, ignore)
# dead end
if len(next_path) == 0:
continue
# we found a path, append ourself
next_path.insert(0, frm)
# are we shorter than the previous one?
if len(path) == 0 or len(path) > len(next_path):
path = next_path
return path
# set whether we are using bfs or dfs as our main pathing method
shortest_path = shortest_path_bfs
def path_to_dirs(path):
'''takes a path of rooms and converts it to directions'''
dirs = []
i = 0
while i < len(path) - 1:
for ex in path[i].exnames:
if path[i].exit(ex).dest == path[i+1]:
dirs.append(ex)
break
i = i + 1
# return the directions we generated, if any
return dirs
def build_patrol(rms, reverse = True):
'''builds a set of directions that need to be followed to do a patrol
between the rooms. If reverse is true, also supplies the directions
to loop back on itself'''
if reverse == True:
loopback = [x for x in rms]
loopback.reverse()
rms = rms + loopback[1:]
path = []
i = 0
while i < len(rms) - 1:
path = path + shortest_path(rms[i], rms[i+1])
i += 1
return path_to_dirs(path)
def step(frm, to, ignore_doors = False):
'''returns the first step needed to take to go from one room to another'''
steps = shortest_path(frm, to, ignore_doors)
if steps == None or len(steps) <= 1:
return None
return path_to_dirs(steps)[0]
################################################################################
# commands
################################################################################
def cmd_path(ch, cmd, arg):
'''Usage: path <person>
Prints out a Python list of the directions needed to move from your
current location to the location of the specified person.'''
try:
tgt, = parse_args(ch, True, cmd, arg, "ch.world.noself")
except: return
path = build_patrol([ch.room, tgt.room])
if len(path) == 0:
ch.send("Path doesn't exist")
else:
ch.send(str(path))
################################################################################
# initialization
################################################################################
# add our commands
add_cmd("path", None, cmd_path, "admin", False)
# mud initialization
mud.build_patrol = build_patrol