#!/usr/bin/python # # http://demon.3x.ro/mud/ # # $Revision: 1.11 $ $Date: 2004/02/16 02:15:33 $ from sys import argv from sys import stdout from sys import stderr from sys import stdin import string import re COMMAND_QUEUE_SIZE = 10 # remember last 10 commands LOCATION_QUEUE_SIZE = 10 INV_PATH_ANNIHILATION = 1 # move east will annihilate move west, etc AUTOGET_STAFF=1 REprompt = re.compile("HP:([0-9\-]+) EP:([0-9\-]+)>") REroomTitle = re.compile(".+\(.*\)") REnumcmd = re.compile("([0-9]*)([a-zA-z][a-zA-z ]*)") REwalkcmd = re.compile("(do )*(([ ]*[0-9]*)([a-zA-z ]+),)*(([ ]*[0-9]*)([a-zA-z ]+))*") REnumber = re.compile("[0-9\-]") REshootFireball = re.compile("A blast of fire shoots from your hand and strikes (.+)!!!") REnoGet = re.compile("There is no (.+) here to get\.") REnoGetFromPack = re.compile("There is no (.+) in the pack to get\.") movement={"n":"s","s":"n","e":"w","w":"e","ne":"sw","sw":"ne","nw":"se","se":"nw", "enter":"out","cave":"out","out":"enter","u":"d","d":"u","city":"out","gate":"gate"} movementShort={"south":"s","north":"n","east":"e","west":"w","southeast":"se","southwest":"sw", "northeast":"ne","northwest":"nw","up":"u","down":"d"} blockedMove=[ "You can't go that way!", "A burly sailor blocks your path, growling: We don't allow your kind aboard our ship." ] locationsFull={ "The Bank(w)":"bywater.bank", "The tower of Orthanc(se)":"treebeard", "The bank(e)":"edoras.bank", "The Bank(se)":"thran.bank", "Thranduil shop(w)":"thran.shop", "A cleft in the mountainside(n)":"gundabad", "The infirmary(s)":"dale.heal", "A large open space on the top of the tower(stairs)":"dawyin", "A dirt path(se and ne)":"spy", "The Healing Chambers(w and s)":"riv.heal", "The armoury wagon(out)":"frams.shop", "A supply tent(nw)":"dale.shop", "":"moria", "Back of the tent(out)":"moria.shop", "A deep, dark cave(deeper and tunnel)":"dwh" } locations={ "The north gate of Adornas(":"adornas", "West Gate of Bree(":"bree", "The Stone Bridge(":"riv", "Outside Rhosgobel(":"rhos", "Path to Dale(":"dale", "Entrance to Framsburg(":"frams", "The Lothlorien Bridge(":"loth", "The war camp(":"loth.war", "Inside an armoury(":"loth.shop", "Weapons and armour shop(":"caras.shop", "The healing house(":"loth.heal", "A small logging camp(":"loth.camp", "A Bridge(":"thran", "The north gate(":"edoras", "A Healer's Home(":"edoras.heal", "A large tent(":"tenzek", "Outside the gate to the castle(":"dolamroth", "The Great Gates of Minas Tirith(":"mt", "A busy auction house(":"alexa", "The gate to the keep(":"saph", "Linhir(":"linhir", "The center of Calembel(":"calembel", "An aged pathway(":"moria", " ":"cutlass" #"A dirt path(":"spy" } travelto={ "bree":{"rhos":"riv","edoras":"adornas","mt":"adornas","loth":"riv","dolamroth":"adornas"}, "edoras":{"bree":"adornas","rhos":"loth","dolamroth":"mt"}, "adornas":{"mt":"edoras","dolamroth":"mt","loth":"edoras"}, "rhos":{"bree":"riv","mt":"loth","edoras":"loth"}, "mt":{"bree":"edoras","rhosgobel":"edoras"}, "loth":{"mt":"edoras","bree":"rhos","frams":"rhos"}, "riv":{"edoras":"rhos","mt":"rhos","loth":"rhos"}, } paths={ "frams": {"rhos":"do 32s,17e,2n","gundabad":"do w,7n,5w,nw,climb rocks","frams.shop":"do 2n,w,wagon"}, "frams.shop": {"frams":"do out,e,2s"}, "gundabad": {"frams":"do climb down,se,5e,7s,e"}, "rhos": {"frams":"do 2s,17w,32n","loth": "travelto lothlorien", "thran":"travelto thranduils", "riv":"travelto rivendell", "encampment":"do 4s,40e,4e,40s,40s,17s,4n,10w"}, "encampment": {"rhos":"do 10e,4s,17n,40n,40n,4w,40w,4n"}, "edoras": {"loth":"travelto lothlorien","mt":"travelto minastirith", "adornas":"travelto adornas", "tenzek":"do 13n,13w,2n,e,put staff in pack,get rope,throw rope,climb rope,e,n", "treebeard":"do 13n,13w,w,2nw,2n,enter,e,nw", "edoras.heal":"do 3s,3e,3s,w","edoras.bank":"do w"}, "edoras.heal": {"edoras":"do e,3n,3w,3n"}, "edoras.bank": {"edoras":"do e"}, "bree": {"adornas":"travelto adornas","riv":"travelto rivendell","serpent":"do 37s,12w", "bywater.bank":"do 10w,s,9w,n,4e,3n,3e"}, "bywater.bank": {"bree":"do 3w,3s,4w,s,9e,n,10e"}, "serpent": {"bree":"do 12e,37n"}, "mt": { "edoras":"travelto edoras", "alexa":"do enter,5s,2sw,gate,4ne,7n,4nw,gate,2se,5s,2sw,gate,ne,4n,2nw,2w,n,back", "linhir":"travelto linhir","saph":"do 2n,2ne,2n,3nw,4n,nw,2w,n"}, "saph": {"mt":"do s,2e,se,4s,3se,2s,2sw,2s"}, "riv": {"bree":"travelto bree","rhos":"travelto rhosgobel","riv.heal":"do 2ne,5e,n,e"}, "riv.heal": {"riv":"do w,s,5w,2sw"}, "adornas": {"bree":"travelto bree","edoras":"travelto edoras"}, "alexa": {"mt":"do w,out,2e,2se,4s,sw,gate,2ne,4n,2nw,gate,4se,7s,4sw,gate,2ne,5n,pelennor"}, "loth": {"edoras":"travelto edoras","rhos":"travelto rhosgobel","loth.war":"do 6w,2n", "loth.camp":"do 9e,14s", "gbp":"do 4w,4n,11w,4n,nw,n,nw,2n,2nw,3w,nw,2w,sw,3w,s,e", "dwh":"do 10w,10n,3w,3n,enter,push brush,press crevice,passage,d" }, "dwh": {"loth":"do tunnel,tree,out,out,3s,3e,10s,10e"}, "loth.camp": {"loth":"do 14n,9w"}, "loth.shop": {"loth.war":"do w,out,w,s"}, "loth.heal": {"caras.shop":"do d,2w,s,u"}, "loth.war": { "loth":"do 2s,6e", "loth.shop":"do n,e,enter,e", "caras.shop":"do 3s,cave,5e,n,2e,2u,out,d,2w,2s,u", "dawyin":"do s,4w,nw,ne,nw,enter,2u", "moria":"do 2w,2n,7w,5n,w,n,nw"}, "moria": {"moria.shop":"do se,s,e,tent,backroom","loth.war":"do se,s,e,5s,7e,2s,2e"}, "moria.shop": {"moria":"do out,out,w,n,nw"}, "dawyin": {"loth.war":"do 2d,out,se,sw,se,4e,n"}, "caras.shop": {"loth.war":"do d,2s,2sw,3s,e,2n,3w,2n","loth.heal":"do d,n,2e,u"}, "gbp": {"loth":"do w,n,3e,ne,2e,se,3e,2se,2s,se,s,se,4s,11e,4s,4e"}, "tenzek": {"edoras":"do s,out,2w,2s,13e,13s"}, "thran": { "thran.shop":"do stairs,terrace,gates,knock on gates,2deeper,w,nw,n,nw,w,2sw,e", "rhos":"travelto rhosgobel", "dale":"travelto dale"}, "thran.shop": {"thran":"do w,2ne,e,se,s,se,e,3out,terrace,stairs,bridge","thran.bank":"do w,sw,se,2e,nw"}, "thran.bank": {"thran.shop":"do se,2w,nw,ne,e"}, "dale": { "thran":"travelto thranduils", "thran.shop":"do 7w,s,sw,stairs,terrace,gates,knock on gates,2deeper,w,nw,n,nw,w,2sw,e", "dale.camp":"do 2w,2n", "dale.heal":"do n,5nw,w,nw,3n,3ne,5n,2nw,2ne,3n,w,n"}, "dale.camp": {"dale":"do 2s,2e"}, "dale.heal": {"dale":"do s,e,3s,2sw,2se,5s,3sw,3s,se,e,5se,s","dale.shop":"do s,e,3s,sw,se"}, "dale.shop": {"dale.heal":"do nw,ne,3n,w,n"}, "treebeard": {"edoras":"do se,w,out,2s,2se,e,13e,13s"}, "dolamroth": {"linhir":"travelto linhir","calembel":"do out,s,13e,13n,6e,2n"}, "linhir": {"dolamroth":"travelto dolamroth","mt":"travelto minastirith", "cutlass":"do city,4s,enter,5se,out,5e,3s,cabin"}, "cutlass": {"linhir":"do out,3n,5w,enter,5nw,out,4n,out"}, "calembel": {"dolamroth":"do 2s,6w,13s,13w,n,enter","spy":"do s,se,3e,2se,e,15s,11w,2n,nw"}, "spy": {"calembel":"do se,2s,11e,15n,w,2nw,3w,nw,n"} } specials=["shop","heal","bank"] ##################################################################################################333 ## movement # inverted path with compression (do n,n => do 2n) and 40 command count limit def getInvPath(x): if len(x)>0: y = "do " lastDir=x[-1] cnt=1 first=1 if len(x)>1: for i in range(len(x)-2,-1,-1): if lastDir == x[i]: cnt += 1 #there's a do 40n limit if cnt>40: if first: first=0 else: y += "," y+="40"+movement[lastDir] cnt = cnt-40 else: if first: first=0 else: y += "," if cnt>1: y += str(cnt) y+=movement[lastDir] lastDir=x[i] cnt=1 if not first: y+="," if cnt>1: y+=str(cnt) y += movement[lastDir] else: y += movement[lastDir] return y else: return #constructs a string path from a given sequence of movements def getPath(x): if len(x)>0: y = "do " lastDir=x[0] cnt=1 first=1 if len(x)>1: for i in range(1,len(x)-1): if lastDir == x[i]: cnt += 1 #there's a do 40n limit if cnt>40: if first: first=0 else: y += "," y+="40"+lastDir cnt = cnt-40 else: if first: first=0 else: y += "," if cnt>1: y += str(cnt) y += lastDir lastDir=x[i] cnt=1 if not first: y+="," if cnt>1: y+=str(cnt) y += lastDir else: y += lastDir return y else: return #breadth-first search, much better and shorter than DF def getNextPath(src,dest): global paths if not src: return queue=[] parent=[] for q in paths[src]: if q==dest: return q if dest in specials: if q.find(dest)>=0: return q queue.append(q) parent.append(q) ind=0 while ind=0: return parent[ind] queue.append(q) parent.append(parent[ind]) ind += 1 return ################################################################################################## ## def walkInvDirs(dirs): invPath = getInvPath(dirs) if (invPath): stdout.write(invPath+"\n") stdout.flush() def goTo(src,dest): global nextLocation,ignoreNextTravelto,inTravelto,travelto global locationLastStack,locationPathStack,doingStackPop,doingLocationPrev if (src == dest or (dest in specials and src.find(dest)>=0)): inTravelto = 0 if doingStackPop: w=locationStack.pop() if (w and ("path" in w) and w["path"] and len(w["path"])>0): stdout.write(w["path"]+"\n") stdout.flush() doingStackPop=0 if doingLocationPrev: w=locationQueue.pop(0) if (w and ("path" in w) and w["path"]): stdout.write(w["path"]+"\n") stdout.flush() doingLocationPrev=0 return if (src in travelto and dest in travelto[src]): nextLocation = travelto[src][dest] else: q = getNextPath(src,dest); if (q): nextLocation = q inTravelto=1 if (nextLocation): stdout.write(paths[src][nextLocation]+"\n") stdout.flush() ############################################################################################ ## location related def doLocationPush(): global locationStack,moveConf,lastLocation locationStack.append({"last":lastLocation,"path":getPath(moveConf)}) stderr.write("--> pushed path from "+lastLocation+" "+locationStack[-1]["path"]+"\n") stderr.flush() def doLocationPop(): global locationStack,doingStackPop,doingLocationPrev global traveltoLocation,inTravelto if (len(locationStack)>0 and not doingStackPop and not inTravelto and not doingLocationPrev): doingStackPop = 1 traveltoLocation = locationStack[-1]["last"] goTo(lastLocation,traveltoLocation) else: stderr.write("--> location stack empty or pop not allowed\n") stderr.flush() def doLocationSave(): global locationQueue,LOCATION_QUEUE_SIZE,moveConf,lastLocation locationQueue.insert(0,{"last":lastLocation,"path":getPath(moveConf)}) if len(locationQueue)>LOCATION_QUEUE_SIZE: locationQueue.pop(-1) def doLocationPrev(): global locationStack,doingStackPop,doingLocationPrev global traveltoLocation,inTravelto if (len(locationQueue)>0 and (not doingStackPop) and (not inTravelto) and (not doingLocationPrev)): doingLocationPrev = 1 traveltoLocation = locationQueue[0]["last"] goTo(lastLocation,traveltoLocation) else: stderr.write("--> location queue empty or prev not allowed\n") stderr.flush() ##################################################################################################333 ## notifications def confirmMove(direction): global INV_PATH_ANNIHILATION processed=0 if INV_PATH_ANNIHILATION: if len(moveConf)>0 and moveConf[-1] in movement and movement[moveConf[-1]]==direction: moveConf.pop(-1) processed=1 if not processed: moveConf.append(direction) #print >> stderr, "--> moveConf:",getPath(moveConf) #gives the prompt string def promptNotification(x): pass #called when we enter a new room, or look at it def blockedMoveNotification(): global moveNew if (len(moveNew)>0): moveNew.pop(0) def invalidCommandNotification(): global moveNew if (len(moveNew)>0 and moveNew[0]=="enter"): moveNew.pop(0) def noGetFromPackNotification(x): global ignoreNextAutogetStaff # we've tried to get a staff, but it's not there, so we should stop the infinte cycle if x=="staff": ignoreNextAutogetStaff=1 def noGetNotification(x): global ignoreNextAutogetStaff # we've tried to get a staff, but it's not there, so we should stop the infinte cycle if x=="staff": ignoreNextAutogetStaff=1 def tooBulkyNotification(): global ignoreNextAutogetStaff # if we've tried to get a staff, we should stop the infinte cycle ignoreNextAutogetStaff=1 def noStaffNotification(): global ignoreNextAutogetStaff if AUTOGET_STAFF: if ignoreNextAutogetStaff: ignoreNextAutogetStaff=0 else: print "get staff from pack" print commandQueue[0] stdout.flush() def fireballShootNotification(x): pass def youRunNotification(x): runDir=x[8:len(x)-1] if runDir in movementShort: confirmMove(movementShort[runDir]) else: confirmMove(runDir) stderr.write("--> you run "+runDir+"\n") stderr.flush() def roomTitleNotification(x): global parsingRoom,currentLocation,titleLocation,locations,moveNew,moveConf,lastLocation global locationsFull global roomTitle,roomItems parsingRoom = 1 currentLocation = x; titleLocation = x[0:x.find("(")+1] gotLocation = 0 if (x in locationsFull): lastLocation = locationsFull[x] gotLocation = 1 elif (titleLocation in locations): lastLocation = locations[titleLocation] gotLocation = 1 if gotLocation: moveConf=[] if (len(moveNew)>0): moveNew.pop(0) stderr.write("--> now at "+lastLocation+"\n") q = lastLocation[lastLocation.find(".")+1:] if q in specials: specialRoomNotification(q) def roomEndNotification(): global moveNew,moveConf if (len(moveNew)>0): w=moveNew.pop(0) confirmMove(w) def specialRoomNotification(x): global doingSelloff,inTravelto if x=="shop": if doingSelloff: print "do sell all,sell all from pack" stdout.flush() doingSelloff=0 inTravelto=0 doLocationPrev() ################################################################################################ ## def processline(x): global roomDesc,lastHP,lastEP,log,lastLocation,locations,inTravelto,ignoreNextTravelto,nextLocation global traveltoLocation,ramblings,currentLocation global moveNew,moveConf,parsingRoom mo = REprompt.match(x); if (mo): lastHP = int(mo.group(1)) lastEP = int(mo.group(2)) promptNotification(x) if (parsingRoom): # we had a room 'header' roomEndNotification() parsingRoom = 0 if (mo.end() < len(x)): processline(x[mo.end()+1:]) if (x in blockedMove): blockedMoveNotification() if (x=="What?"): invalidCommandNotification() if (x=="You must have a staff in your hands to cast any spells."): noStaffNotification() q = REshootFireball.match(x) if q: fireballShootNotification(q.group(1)) if (x=="Too bulky."): tooBulkyNotification() q = REnoGet.match(x) if q: noGetNotification(q.group(1)) q = REnoGetFromPack.match(x) if q: noGetFromPackNotification(q.group(1)) if (len(x)>6 and x[0:7]=="You run"): youRunNotification(x) elif (REroomTitle.match(x)): #roomDesc = [] #roomDesc.append(x) #stderr.write("this is a room: ["+x+"] ??\n") #stderr.flush() roomTitleNotification(x) if (inTravelto and lastLocation == nextLocation): if (lastLocation != traveltoLocation): stderr.write("--> now at ["+lastLocation+"], going to ["+traveltoLocation+"]\n") stderr.flush() goTo(lastLocation,traveltoLocation) ############################################################################################## ## user input processing def processUInput(x): global traveltoLocation,inTravelto,lastLocation,ignoreNextTravelto,nextLocation global parsingRoom,moveNew,moveConf global locationStack,doingStackPop,COMMAND_QUEUE_SIZE,doingSelloff w = string.split(x) commandQueue.insert(0,x) if len(commandQueue)>COMMAND_QUEUE_SIZE: commandQueue.pop(-1) if (len(x)>0 and REwalkcmd.match(x)): if x[0:3]=="do ": q=REnumcmd.findall(x[3:]) else: q=REnumcmd.findall(x) for i in q: if (i[0]==""): cnt=1 else: cnt = int(i[0]) st=string.strip(i[1]) for j in range(cnt): if (st in movement): #print >> stderr,"-->",st moveNew.append(st) if (len(w)>1 and w[0]=="l"): if (w[1] == "paths"): print >> stderr,"-->",paths.keys() stderr.flush() if (w[1] == "prev" and len(locationQueue)>0): print >> stderr,"--> from",locationQueue[0]["last"],locationQueue[0]["path"] if (w[1] == "ret"): print >> stderr,"--> ret:",getInvPath(moveConf) if (len(w)>1 and w[0]=="g"): if w[1]=="push": doLocationPush() elif w[1]=="pop": doLocationPop() elif w[1]=="prev": doLocationPrev() elif w[1]=="ret": if len(moveNew)==0: walkInvDirs(moveConf) moveConf=[] elif w[1]=="selloff": if (getNextPath(lastLocation,"shop")): doingSelloff=1 doLocationSave() walkInvDirs(moveConf) moveConf=[] traveltoLocation="shop" goTo(lastLocation,traveltoLocation) else: if len(lastLocation)<=0: stderr.write("--> lastLocation is not know\n") stderr.flush() else: if (getNextPath(lastLocation,w[1])): doLocationSave() walkInvDirs(moveConf) moveConf=[] if (lastLocation in travelto and w[1] in travelto[lastLocation]): stderr.write("--> travelto from "+lastLocation+" to "+w[1]+" via "+travelto[lastLocation][w[1]]+"\n") stderr.flush() traveltoLocation=w[1] goTo(lastLocation,traveltoLocation) else: stderr.write("--> I don't know how to get from "+lastLocation+" to "+w[1]+"\n") stderr.flush() ######################################################################## # main stderr.write("--> mys started\n") stderr.flush() stdout.write("glance\n"); stdout.flush() log = file("log","a+"); lastLocation="" ignoreNextTravelto=0 inTravelto=0 parsingRoom=0 currentLocation="" commandQueue=[] parsingRoom = 0 ignoreNextAutogetStaff=0 moveNew=[] moveConf=[] locationStack=[] locationQueue=[] doingStackPop=0 doingLocationPrev=0 doingSelloff=0 for i in range(COMMAND_QUEUE_SIZE): commandQueue.append("nop") while (1): x=stdin.readline() #log.write(x) #log.flush() if (x[0]=="1" or x[0]=="3"): processline(x[2:len(x)-1]) elif (x[0]=="2"): processUInput(x[2:len(x)-1])