from random import choice import pickle import heapq from collections import defaultdict import re from time import time, sleep def __init__(self): self.write("Addon Loaded.") reloadHooks(self) self.world = world() self.fighting = False self.health_percent = .8 self.thinking = False self.last_combat = 10 self.last_consider = 10 self.last_stats = 10 self.last_move = 10 self.buffer = "" self.room = False self.cooldowns = {} self.action_queue = [] def reloadHooks(self): # do most of the work here on the fly so you don't have to keep reloading the plugin # #if not hasattr(self, "world"): # self.world = world() pass def cooldown(self, cmd, duration): if self.cooldowns.has_key(cmd): if time() - self.cooldowns[cmd] > duration: return True else: return False else: return True def popCooldown(self, cmd, duration): if cooldown(self, cmd, duration): self.cooldowns[cmd] = time() self.send(cmd) class PathFind(object): def __init__(self): self.G = defaultdict(dict) self.E = defaultdict(dict) def addEdge(self, va, vb, cost, edge, way): if way == -1: (vb, va) = (va, vb) self.G [va][vb] = edge if not way: self.G[vb][va] = edge self.E[edge] = cost def findPath(self, start, end): def flatten(L): # Flatten linked list of form [0,[1,[2,[]]]] while len(L) > 0: yield L[0] L = L[1] q = [(0, start, ())] # Heap of (cost, path_head, path_rest). visited = set() # Visited vertices. # Eliminate the dots pattern push, pop, add = heapq.heappush, heapq.heappop, visited.add G, E = self.G, self.E while True: (cost, v1, path) = pop(q) if v1 not in visited: add(v1) path = (v1, path) if v1 == end: return list(flatten(path))[::-1] for (v2, e) in G[v1].iteritems(): if v2 not in visited: push(q, (cost + E[e], v2, path)) def getEdges(self, path): edges = [] for ix in xrange(len(path) - 1): edges.append(self.G[path[ix]][path[ix + 1]]) return edges def findPath(rooms, x, y): g = getPathFindFromRooms(rooms) try: return g.findPath(x, y) except: return None def getEdges(rooms, x, y): g = getPathFindFromRooms(rooms) try: h = g.findPath(x, y) return g.getEdges(h) except: return None def getPathFindFromRooms(rooms): x = PathFind() for rid in rooms: for d in rooms[rid].doors: if d.destination is not False and d.closed is False: print "adding ", rooms[rid].name, " - ", d.destination.name x.addEdge(rooms[rid], d.destination, 1, d, 1) return x class door: def __init__(self, name, destination=False): self.name = name self.explored = False # all doors start opened # and we close them when we go through # and reopen when we map the exit on the other side self.closed = False self.destination = destination class room: def __init__(self, name): self.name = name self.doors = [] self.stuff = [] self.seen = 1 class thing: def __init__(self, name): self.name = name self.seen = 1 self.considered = False self.danger = False class world: def __init__(self): self.rooms = {} self.bookmarks = {} def tick(self): if self.fighting: if time() - self.last_combat > 2: self.write("------------------------Leaving battle.") self.fighting = False else: if self.thinking: if time () - self.last_stats > 2: if self.health_percent < 1: self.last_stats = time() self.c.send("hp\n") if self.thinking: self.write("ACTION QUEUE: " + str(len(self.action_queue)) + " items.") for fun, fargs in self.action_queue: self.write(" * " + fun.__name__) # if we have queued actiosn do them if len(self.action_queue) > 0: fun, fargs = self.action_queue.pop() self.write("FARGS ARE " + str(fargs)) fun(self, *fargs) self.write('sleeping') sleep(5) handleWalkAndKill(self) # change below to False to stop after every thought #self.thinking = False for fun, fargs in self.action_queue: tfargs = [] for x in fargs: tfargs.append(str(x.name)) self.write(fun.__name__ + " - " + ", ".join(tfargs)) def handleWalkAndKill(self): if not self.fighting: if self.health_percent > .95: if time() - self.last_move > .5: cmdExplore(self) else: #self.write("CONSDIERING IN ROMM WITH " + str(len(self.room.stuff))) if time() - self.last_consider > 2: need_check = False for x in self.room.stuff: if not x.considered: need_check = True if need_check: pass #self.send("consider") self.last_consider = time() self.last_combat = time() + 1 def serverText(self, txt): self.buffer += txt if self.buffer.endswith("\n> "): handleServerChunk(self, self.buffer[:-3]) self.buffer = "" handleStats(self, txt) handleInCombatCheck(self, txt) handleBadMoveCheck(self, txt) def handleServerChunk(self, txt): #self.write("-------LOGGING --------") #self.write(txt) #f = open("/tmp/output.txt", "w") #f.write(txt) #f.close() #self.write("---------------") handleroomDesc(self, txt) handleConsider(self, txt) def handleBadMoveCheck(self, txt): x = re.compile(r"You cannot go (\S+).", re.MULTILINE) for l in txt.splitlines(): r = x.match(l) if r: self.write("Bad move detected going " + r.group(1) + ", stopping thining.") self.thinking = False def handleStats(self, txt): x = re.compile(r"\s+HP:\s+(\d+)/(\d+)", re.MULTILINE) for l in txt.splitlines(): r = x.match(l) if r: self.health_percent = float(r.group(1)) / int(r.group(2)) if self.health_percent < 1: if popCooldown(self, "eat berry", 45) or popCooldown(self, "salve", 1.5): pass self.write("health percent " + str(self.health_percent)) self.last_stats = time() def handleInCombatCheck(self, txt): x = re.compile(r".*Attacking:.*", re.MULTILINE) for l in txt.splitlines(): r = x.match(l) if r: self.last_combat = time() self.last_move = time() self.fighting = True def handleConsider(self, txt): if self.fighting or not self.room: return needle = "consider " if self.last_command == "consider": t = self.last_command[len(needle):] dangerous = 0 lines = txt.splitlines() needle = "You size up " for i in range(len(lines)): if lines[i].startswith(needle): mob_name = lines[i][len(needle):-1] desc = lines[i+1].strip() danger_dict = {} danger_dict["You are a little stronger than they are."] = 4 danger_dict["Closest thing to a fair fight you've seen in a while."] = 5 danger_dict["This opponent may be a little tougher than you are."] = 6 if danger_dict.has_key(desc): danger = danger_dict[desc] else: danger = 10 self.write("danger is " + str(danger)) # find stuff with the same mob_name for x in self.room.stuff: self.write(x.name + " VS " + mob_name) if x.name.startswith(mob_name): x.danger = danger self.write("assigning danger to " + mob_name) if self.health_percent > .9 and danger < 7: #self.send("kill "+mob_name) break for x in self.room.stuff: x.considered = True def explore_door(door): pass #self.send(door.name) # need to hook into future reads to confirm and then nav back to check def playerText(self, txt): pass def cmdNewDoor(self, targs): if targs: d = door(targs) self.room.doors.append(d) else: self.write("What is the exit name that leaves from this room?") def cmdCloseDoor(self, targs): if targs: for d in self.room.doors: if d.name == targs: self.write("closed door") d.closed = True self.room.doors.append(d) else: self.write("What door are you closing?") rooms = self.world.rooms for rid in rooms: for door in rooms[rid].doors: if not door.closed: self.write(rooms[rid].name + " - " + door.name) def cmdOpenDoor(self, targs): if targs: for d in self.room.doors: if d.name == targs: self.write("opened door") d.closed = False self.room.doors.append(d) else: self.write("What door are you opening?") rooms = self.world.rooms for rid in rooms: for door in rooms[rid].doors: if door.closed: self.write(rooms[rid].name + " - " + door.name) def cmdStuff(self): self.write("stuff in " + self.room.name + ":") for x in self.room.stuff: self.write(x.name + " - " + str(x.danger)) def cmdOpenDoor(self, targs): if targs: for d in self.room.doors: if d.name == targs: self.write("opened door") d.closed = False self.room.doors.append(d) else: self.write("What door are you opening?") rooms = self.world.rooms for rid in rooms: for door in rooms[rid].doors: if door.closed: self.write(rooms[rid].name + " - " + door.name) def cmdStuff(self): self.write("stuff in " + self.room.name + ":") for x in self.room.stuff: self.write(x.name + " - " + str(x.danger)) def cmdExplore(self): if not self.world.bookmarks.has_key("cot"): self.write("can't explore no cot mark to use as home-base") return False elif not self.room: self.write("you aren't in a room yet, can't do anything, try 'look'") return False else: cot = self.world.bookmarks["cot"] rooms = self.world.rooms # make a list of unexplored rooms unexplored_rooms = [] for rid in rooms: room = rooms[rid] for door in room.doors: if not door.destination and not door.closed: unexplored_rooms.append(room) break g = getPathFindFromRooms(rooms) try: here_to_cot = g.findPath(self.room, cot) except: here_to_cot = False # get closest path from cot to unexplored cot_to_unexplored = False cot_to_unexplored_destination = False for room in unexplored_rooms: try: path = g.findPath(cot, room) if not cot_to_unexplored or len(path) < len(cot_to_unexplored): cot_to_unexplored = path cot_to_unexplored_destination = room except: pass # get closest path from here to unexplored here_to_unexplored = False for room in unexplored_rooms: try: path = g.findPath(self.room, room) if here_to_unexplored and len(path) > len(here_to_unexplored): pass else: here_to_unexplored = path except: pass # of our paths, figure out which is the best option dest_path = None if here_to_cot and cot_to_unexplored and here_to_unexplored: if len(here_to_cot) - 3 > len(here_to_unexplored): dest_path = g.findPath(self.room, cot_to_unexplored_destination) else: dest_path = here_to_unexplored elif here_to_unexplored: dest_path = here_to_unexplored # if we picked a path go there, otherwise wander if dest_path: # pick out an unexplored door in destination room for door in dest_path[-1].doors: if not door.destination and not door.closed: unexplored_door = door break # convert our path to a list of exits path_doors = g.getEdges(dest_path) self.write("start room is " + dest_path[0].name + ", going to explore " + unexplored_door.name + " in " + dest_path[-1].name) for d in path_doors: queue(self, goDoor, d) queue(self, exploreDoor, unexplored_door) return True else: queue(self, cmdWander) return False def queue(self, fun, *fargs): self.action_queue.append((fun, fargs)) def scriptText(self, txt): self.write("\n-----------------------------------------------------") x = txt.find(" ") if x == -1: targs = False cmd = txt.strip() else: cmd = txt[0:x] targs = txt[x+1:] if txt.strip() == "": cmdExplore(self) elif txt.strip() == "think": if self.thinking is True: self.thinking = False else: self.action_queue = [] self.thinking = True elif txt.strip() == "test_queue": queue(self, cmdWander) elif txt.strip() == "test_path": x = PathFind() rooms = self.world.rooms for rid in rooms: for d in rooms[rid].doors: if d.destination is not False: self.write("adding " + rooms[rid].name + " - " + d.destination.name + " via " + d.name) x.addEdge(rooms[rid], d.destination, 1, d, 1) for ax in rooms: for bx in rooms: a = rooms[ax] b = rooms[bx] try: p = x.findPath(a,b) ds = x.getEdges(p) self.write(a.name+ " to "+ b.name) for d in ds: self.write( " - "+ d.name) except: self.write( "NO PATH "+ a.name+ " - "+ b.name) elif txt.strip() == "rooms": rooms = self.world.rooms self.write("list of rooms:\n") for rid in rooms: #self.write(" - source is " + self.room.name + " dest is " + rooms[rid].name) if rooms[rid] is not self.room: p = getEdges(self.world.rooms,self.room,rooms[rid]) door_names = [] if p is not None: for door in p: door_names.append(door.name) t = ", ".join(door_names) self.write(rooms[rid].name + " - " + ", ".join(door_names)) else: self.write(rooms[rid].name + " - no path") else: self.write(rooms[rid].name + " - you are here") elif cmd == "newdoor": cmdNewDoor(self, targs) elif cmd == "stuff": cmdStuff(self) elif cmd == "closedoor": cmdCloseDoor(self, targs) elif cmd == "opendoor": cmdOpenDoor(self, targs) elif cmd == "explore": cmdExplore(self) elif txt.strip() == "alldoors": for rid in self.world.rooms: room = self.world.rooms[rid] self.write("doors in " + room.name) for door in room.doors: if door.destination is False: desc = "Unexplored" else: desc = door.destination.name self.write("---" + door.name + " - " + desc) elif txt.strip() == "doors": cmdDoors(self) elif txt.strip() == "saveworld": pkl_file = open('data.pkl', 'wb') pickle.dump(self.world, pkl_file) pkl_file.close() elif txt.strip() == "loadworld": pkl_file = open('data.pkl', 'rb') self.world = pickle.load(pkl_file) for rid in self.world.rooms: self.write(self.world.rooms[rid].name) for d in self.world.rooms[rid].doors: self.write("--- " + str(d.destination)) self.room = False self.send("look") pkl_file.close() elif txt.strip().startswith("mark"): s = txt.strip() x = s.find(" ") if x == -1: self.write("Bookmarks: ") for name in self.world.bookmarks: p = getEdges(self.world.rooms,self.room,self.world.bookmarks[name]) door_names = [] if p is not None: for door in p: door_names.append(door.name) t = ", ".join(door_names) self.write(name + " - " + ", ".join(door_names)) else: self.write(name + " - no path") else: m = s[x+1:] if self.world.bookmarks.has_key(m): dest = self.world.bookmarks[m] p = getEdges(self.world.rooms, self.room, dest) if p is not None: self.write("going on path" + dest.name) for d in p: self.send(d.name) else: self.write("can't get there from here") else: self.world.bookmarks[m] = self.room self.write("recorded as " + m) elif txt.strip().startswith("resetmark"): s = txt.strip() x = s.find(" ") if x == -1: self.write("what mark do you want to clear?") else: m = s[x+1:] if self.world.bookmarks.has_key(m): del(self.world.bookmarks[m]) self.write("cleared") else: self.write("no mark by that name was set") elif txt.strip() == "resetworld": self.world = world() self.room = False self.send("look") self.write("world reset.") else: self.send(txt) self.write("-----------------------------------------------------") def exploreDoor(self, door): if not door.destination: door.closed = True self.send(door.name) def goDoor(self, door): self.send(door.name) def cmdDoors(self): self.write("doors in " + self.room.name + ": \n") for door in self.room.doors: if door.destination is False: desc = "Unexplored" else: desc = door.destination.name if door.closed: desc += " - closed" self.write(door.name + " - " + desc) def cmdWander(self): room = self.room if room: unexplored_doors = [] for door in room.doors: if door.explored is False: unexplored_doors.append(door) if len(unexplored_doors) > 0: r = choice(unexplored_doors) self.write("Exploring the unexplored.") r.explored = True else: r = choice(room.doors) self.write("Going to a random door " + r.name + ".") self.last_move = time() exploreDoor(self, r) else: self.write("Can't wander because you aren't in a room.") class roomDescription: def isIndented(self, line): return line[0:2] == " " def isStartOfDoors(self, line): return line.find("obvious exit") > -1 def __init__(self, txt): self.name = False self.doors = False self.stuff = False self.description = False self.txt = txt self.debug_text = [] bits = txt.splitlines() name_index = False for i in range(len(bits)): if bits[i].find("-*-") != -1: name_index = i parseType = 0 handler = {} handler[0] = self.handleDescription handler[1] = self.handleDoors handler[2] = self.handleStuff currentIndentStatus = False if name_index: self.name = bits[name_index].split(" ")[0] self.debug_text.append("name - " + self.name) for i in range(name_index+4, len(bits)): if self.isIndented(bits[i]) != currentIndentStatus: if parseType > 0 or self.isStartOfDoors(bits[i]): parseType += 1 currentIndentStatus = self.isIndented(bits[i]) handler[parseType](bits[i]) def handleDescription(self, txt): if self.description is False: self.description = "" else: self.description += "\n" self.description += txt self.debug_text.append("DESC - " + txt) def handleDoors(self, txt): if self.doors is False: self.doors = [] for needle in ["obvious exit: ", "obvious exits: "]: i = txt.find(needle) if i != -1: for d in txt[i + len(needle):].split(", "): self.doors.append(d.strip()) self.debug_text.append("DOOR - " + txt) def handleStuff(self, txt): if self.stuff is False: self.stuff = [] self.stuff.append(txt) self.debug_text.append("STUFF - " + txt) def handleroomDesc(self, txt): try: d = roomDescription(txt) doors = False stuff = False name = False if d.name: name = d.name if d.doors: doors = [] for dr in d.doors: doors.append(door(dr)) if d.name and doors: rid = d.name + d.description if not self.world.rooms.has_key(rid): r = room(name) r.doors = doors self.world.rooms[rid] = r #self.write("CHARTING " + r.name + str(rid)) else: r = self.world.rooms[rid] r.seen += 1 # if we came from another room connect exits if d.name and self.room: #self.write("CAME FROM " + self.room.name + " via " + self.last_command.strip()) for dr in self.room.doors: #self.write("Checking " + d.name + " against " + self.last_command.strip()) if self.last_command.strip() == dr.name: self.write("FOUND MATCH DOOR " + dr.name) if dr.destination is False: self.write("CONNECTING THE DOORS") dr.destination = r dr.closed = False self.room = r self.last_move = time() self.last_consider = 10 consider_sent = False if d.stuff: stuff = [] for x in d.stuff: thing_name = x.strip() found_thing = False for t in self.room.stuff: if t.name == thing_name: found_thing = t if found_thing: found_thing.seen += 1 else: t = thing(thing_name) self.room.stuff.append(t) for x in self.room.stuff: if not x.considered: if not consider_sent: self.send("consider") consider_sent = True x.considered = True except: return self.write("Couldn't parse room.\n") self.write("--START----") self.write(txt) self.write("--END----")