path.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. '''
  2. path.py
  3. Plugs into the routine module to allow for the easy construction of paths and
  4. path following.
  5. '''
  6. import mud, mudsys, room
  7. ################################################################################
  8. # functions
  9. ################################################################################
  10. def leads_to(frm, to):
  11. '''returns whether from leads directly to to'''
  12. for ex in frm.exnames:
  13. if frm.exit(ex).dest == to:
  14. return True
  15. return False
  16. def shortest_path_bfs(frm, to, ignore_doors = False, stay_zone = True,
  17. ignore = None):
  18. '''calculates the shortest path, but uses a breadth first search. More
  19. efficient than depth-first seach for very short paths with lots of
  20. branches or very large muds.
  21. '''
  22. if frm == to:
  23. return [ frm ]
  24. depth = [ [ frm ] ]
  25. if ignore == None:
  26. ignore = set()
  27. ignore.add(frm)
  28. # figure out what zone we're doing this from
  29. zone = None
  30. if stay_zone:
  31. zone = frm.locale
  32. # keep going until we find to, or we can't go any deeper
  33. found = False
  34. while not found:
  35. layer = [ ]
  36. for room in depth[-1]:
  37. for dir in room.exnames:
  38. dest = room.exit(dir).dest
  39. if dest == None or dest in ignore:
  40. pass
  41. elif zone != None and dest.locale != zone:
  42. pass
  43. elif dest == to:
  44. found = True
  45. layer = [ ]
  46. break
  47. else:
  48. layer.append(dest)
  49. ignore.add(dest)
  50. if found == True:
  51. break
  52. if len(layer) > 0:
  53. depth.append(layer)
  54. if found == True or len(layer) == 0:
  55. break
  56. # no path found
  57. if found == False:
  58. return None
  59. # find the rooms that link each other, in reverse order
  60. path = [ to ]
  61. order = range(len(depth))
  62. order.reverse()
  63. for i in order:
  64. for room in depth[i]:
  65. if leads_to(room, path[-1]):
  66. path.append(room)
  67. break
  68. path.reverse()
  69. return path
  70. def shortest_path_dfs(frm, to, ignore_doors = False, stay_zone = True,
  71. ignore = None):
  72. '''returns the steps needed to take to go from one room to another. More
  73. efficient than breadth-first search for very long paths with only a few
  74. branches, or very small muds.'''
  75. path = []
  76. if ignore == None:
  77. ignore = set()
  78. # a list of rooms we ignore for tracking. Add ourself so we don't loop back
  79. ignore.add(frm)
  80. # if we're the target room, return an empty list
  81. if frm == to:
  82. return [frm]
  83. zone = None
  84. if stay_zone:
  85. zone = frm.locale
  86. # build the shortest path
  87. for ex in frm.exnames:
  88. # check if it has a door, and if we ignore it
  89. if frm.exit(ex).is_closed:
  90. continue
  91. # get the dest room. if there is none, skip this exit
  92. next_room = frm.exit(ex).dest
  93. if next_room == None:
  94. continue
  95. # if we already know this is a dead end or a loopback, skip it
  96. if (next_room in ignore or
  97. (stay_zone and not next_room.locale == zone)):
  98. continue
  99. next_path = shortest_path(next_room, to, ignore_doors, stay_zone,ignore)
  100. # dead end
  101. if len(next_path) == 0:
  102. continue
  103. # we found a path, append ourself
  104. next_path.insert(0, frm)
  105. # are we shorter than the previous one?
  106. if len(path) == 0 or len(path) > len(next_path):
  107. path = next_path
  108. return path
  109. # set whether we are using bfs or dfs as our main pathing method
  110. shortest_path = shortest_path_bfs
  111. def path_to_dirs(path):
  112. '''takes a path of rooms and converts it to directions'''
  113. dirs = []
  114. i = 0
  115. while i < len(path) - 1:
  116. for ex in path[i].exnames:
  117. if path[i].exit(ex).dest == path[i+1]:
  118. dirs.append(ex)
  119. break
  120. i = i + 1
  121. # return the directions we generated, if any
  122. return dirs
  123. def build_patrol(rms, reverse = True, ignore_doors = False, stay_zone = True):
  124. '''builds a set of directions that need to be followed to do a patrol
  125. between the rooms. If reverse is true, also supplies the directions
  126. to loop back on itself'''
  127. if reverse == True:
  128. loopback = [x for x in rms]
  129. loopback.reverse()
  130. rms = rms + loopback[1:]
  131. path = []
  132. i = 0
  133. while i < len(rms) - 1:
  134. path.extend(shortest_path(rms[i], rms[i+1], ignore_doors, stay_zone))
  135. i += 1
  136. return path_to_dirs(path)
  137. def step(frm, to, ignore_doors = False, stay_zone = True):
  138. '''returns the first step needed to take to go from one room to another'''
  139. steps = shortest_path(frm, to, ignore_doors, stay_zone)
  140. if steps == None or len(steps) <= 1:
  141. return None
  142. return path_to_dirs(steps)[0]
  143. ################################################################################
  144. # commands
  145. ################################################################################
  146. def cmd_path(ch, cmd, arg):
  147. '''Usage: path <room>
  148. Prints out a Python list of the directions needed to move from your
  149. current location to a specified destination.'''
  150. try:
  151. dest, = mud.parse_args(ch, True, cmd, arg, "room")
  152. except: return
  153. path = build_patrol([ch.room, dest])
  154. if len(path) == 0:
  155. ch.send("Path doesn't exist")
  156. else:
  157. ch.send(str(path))
  158. ################################################################################
  159. # initialization
  160. ################################################################################
  161. # add our commands
  162. mudsys.add_cmd("path", None, cmd_path, "admin", False)
  163. # mud initialization
  164. mud.build_patrol = build_patrol