Eli Fulkerson .com HomeProjectsSparse-16k-mud
 


Sparse.py - 16k Mud

Description:

This was a program I wrote to enter in the "16k Mud Competition" back in 2000. I didn't win. This was also by and large my first exposure to network programming. I've tweaked it a tiny bit to work with the new versions of Python.

Platform:

  • no platform specific code, should work anywhere that has Python
  • Language:

  • Python
  • License:

  • I am placing this code (explicitly the file sparse.py.txt on this website) into the public domain. If you find it useful, I'd like to hear from you. If you build it into a larger application I would appreciate compensation and attribution, but am not requiring either.
  • That being said, I really wouldn't recommend basing anything on this though, I don't think it is particularly well written.
  • Code:

    #Eli Fulkerson, 2000
    
    #I unfortunately had to do some minor gymnastics to come in under the limit,
    # I was some 500 bytes over when I had completed my feature list, and have
    # been forced to compress some verbose variable names as a result.
    # conn --> is now co
    # room --> is now rm
    # socket --> is now sk
    
    #see sparse.htm for verbose documentation
    
    from socket import *
    from select import *
    from string import *
    from whrandom import *
    from time import clock
    
    HOST = ''
    PORT = 4000
    
    #color constants
    endl = chr(27) + "[37m\n\r"
    #color implementation
    def c(color):
        return chr(27) + "[" + `color` + "m"
    
    def sellOre(args,curr, rate, co):
        args = int(args)
        if args > curr:
            co.sk.send("You don't have that much.\n\r")
            return curr
        else:
            co.sk.send("You sell " + `args` + "units for " + `args * .01 * rate` + "credits.\n\r")
            co.ship.cash = co.ship.cash + (args * .01 * rate)
            return curr - args
    
    def buyOre(args,curr, rate, co):
        invalid = 0
        for x in args[:]:
            if not x in ["1","2","3","4","5","6","7","8","9","0"]:
                invalid = 1
        if invalid == 0:
            args = int(args)
        else:
            args = 0
        if co.ship.cash < 1:
            co.sk.send("Your credit isnt that good.\n\r")
            return curr
        else:
            if curr + args > 100:
                co.sk.send("You can't hold that much.  Purchasing "  + `(100 - curr)` + " units for " + `(100 - curr) * .01 * rate` + " credits.\n\r")
                co.ship.cash = co.ship.cash - ((100 - curr) * .01 * rate)
                return 100
            else:
                co.sk.send("Purchasing "  + `args` + " units for " + `args * .01 * rate` + "credits.\n\r")
                co.ship.cash = co.ship.cash - (args * .01 * rate)
                return curr + args
    
    class Universe:
        def __init__(self):
            #room generator 
            self.rm  = []
            #create rooms
            for x in range(1,1000):
                newRoom = Room(x)
                self.rm.append(newRoom)
            #core
            for x in range(1,250):
                self.addExit(x, x+1)
                if (x+1) % 5 == 0:
                    self.addExit(x, x+5)
                if (x +1) % 10 == 0:
                    self.addExit(x, x+10)
                if (x+1) % 50 == 0:
                    self.addExit(x, x+50)
            #near worlds
            for x in range(251,505):
                self.addExit(x, x + 1)
                self.addExit(x, x+ 2)
            #nebula
            for x in range(500,999):
                rand =  randint(501,998)
                self.addExit(x, rand)
                rand = randint(501,998)
                self.addExit(x,rand)
            #chaos
            for x in range(1,40):
                rand1 = randint(2,998)
                rand2 = randint(2,998)
                if not rand1 == rand2:
                    self.addExit(rand1,rand2)
    
        print("The Universe Flickers into Being.")
    
        def addExit(self, side1,side2):
            newExit = Exit()
            newExit.right = self.rm[side1]
            self.rm[side2].exit.append(newExit)
            newExit = Exit()
            newExit.right = self.rm[side2]
            self.rm[side1].exit.append(newExit)
    
    class Exit:
        def __init__(self):
            self.right = Room(-1)
            self.mined = 0
    
    class Room:
        def  __init__(self, sector):
            self.name = `sector`
            self.exit = []
            if sector < 251:
                self.desc = "Somewhere Near the Galactic Core"
                self.sun = randint(7,13)
            elif sector < 501:
                self.desc = "Somewhere In the Outer Worlds"
                self.sun = randint(5,13)
            elif sector < 751:
                self.desc = "Somewhere In the Nebula"
                self.sun = randint(0,12)
            else:
                self.desc = "Somewhere Beyond the Nebula"
                self.sun = randint(0,9)
            if self.sun > 7 and randint(1,2) < 2 and not sector == -1:
                self.planet = Planet()
            else:
                self.planet = -1
    
        def sundesc(self):
            if self.sun == 0:
                return "A spiral of rock and gas is being drawn carefully into a black hole."
            elif self.sun < 8:
                return "There is no star nearby."
            elif self.sun < 10:
                return "This system is lit by a dim dwarf star."
            elif self.sun < 12:
                return "This system is lit by an average yellow sun."
            else:
                return "This system is lit by a supergiant."
    
    class Ship:
        def __init__(self):
            self.time = 0
            self.second = 0
            self.docked = 0
            self.name = "unnamed"
            self.rm = Room(-1)
            self.fuel = 1000
            self.hp = 1000
            self.max = 1000
            self.ore1 = 0
            self.ore2 = 0
            self.ore3 = 0
            self.cash = 100
    
        def damage(self,num):
            self.hp = self.hp - num
    
        def useFuel(self, fuel):
            temp = self.fuel - fuel
            if temp< 0:
                return 0
            else:
                self.fuel = self.fuel - fuel
                return 1
    
        def regen(self):
            #regen 1 point of hp and 1 point of fuel per second.
            if not self.second == int(clock()) % 60:
                if self.fuel < self.max:
                    self.fuel = self.fuel + 1
                if self.hp < self.max:
                    self.hp = self.hp + 1
            self.second =  int(clock()) % 60
    
        def rename(self, args, co, players):
            args = split(args)
            if len(args) > 1 :
                old = self.name
                self.name = args[1]
                co.act(players,  c(32) + "Info: " + old + " has been renamed to " + self.name + "." + endl)
            else:
                co.sk.send("You must specify a new name.\n\r")
    
    class Planet:
        def __init__(self):
            self.rm = Room(-1)
            self.ore1 = randint(25,250)
            self.ore2 = randint(25,250)
            self.ore3 = randint(25,250)
            self.name = choice(["Kra", "A", "E", "I", "O", "U", "Thie", "Mar", "Bel", "Ji", "Li", "Hra", "Fra", "Dra"]) + choice(["ll", "lb", "lod", "b", "c", "d", "fr", "m", "v", "r", "rout", "t", "sil", "em"]) + choice(["eth", "son", "tes", "las", "mis", "nos", "al", "is", "oth", "och", "in", "ir", "er", "ud", "ik"])
    
    class Connection:
        def __init__(self, sk, address):
            self.sk = sk
            self.ip = address[0]
            self.dataqueue = ""
    
        def actRoom(self, player, playerlist, message):
            for co in playerlist:
                if player.ship.rm == co.ship.rm and not player.ship == co.ship:
                    co.sk.send(message)
    
        def act(self, playerlist, message):
            for co in playerlist:
                co.sk.send(message)
    
        def enqueue(self, string):
            self.dataqueue = self.dataqueue + string
    
        def queue(self):
            return self.dataqueue
    
        def resetqueue(self):
            self.dataqueue = ""
    
        def fileno(self):
            return self.sk.fileno()
    
        def receive(self):
            return self.sk.recv(1024)
    
        def showRoom(self, players):
            #What area of space is this?
            self.sk.send(self.ship.rm.desc + endl)
    
            #What sector are we in?
            self.sk.send("Sector: " + self.ship.rm.name + endl)
    
            #What wormgates leave from here?
            self.sk.send("Wormgates: ")
            temp = self.ship.rm.exit
            for a in range(len(temp)):
                self.sk.send(temp[a].right.name)
                #Is there a planet there?
                if not temp[a].right.planet == -1:
                    self.sk.send("p")
                #Is there a black hole there?
                if temp[a].right.sun ==  0:
                    self.sk.send("x")
                self.sk.send(" ")
            self.sk.send(endl)
    
            #What does this sector look like?
            temp = self.ship.rm.sundesc()
            self.sk.send(temp + endl)
    
            #Is there a planet here?
            if not self.ship.rm.planet == -1:
                self.sk.send("The planet " + self.ship.rm.planet.name + " is here.\n\r")
    
            #Are there other ships here?
            for co in players:
                if co.ship.rm == self.ship.rm and not co.ship == self.ship:
                    self.sk.send("Another ship, the '"+ co.ship.name + "', is here")
                    if co.ship.docked == 1:
                        self.sk.send(", docked.\n\r")
                    else:
                        self.sk.send(".\n\r")
    
            #Anything else?
            #prompt
            self.sk.send("\n\rFuel: " + `self.ship.fuel` + " Hp: " + `self.ship.hp` + ">")
            self.sk.send(endl)
    
        #if var == 1, then turn mine on.  if var == 0, then turn mine off.
        def mine(self,side1,side2, uni,var):
            temp = uni.rm[int(side1) - 1].exit
            temp2 = uni.rm[int(side2) - 1].exit
            foundGate = 0
            for a in range(len(temp)):
                if temp[a].right.name == side2:
                    temp[a].mined = var
                    foundGate = 1
                    for b in range(len(temp2)):
                        if temp2[b].right.name == side1:
                            temp2[b].mined = var
            return foundGate
    
        def mineExit(self,args,uni,players):
            args = split(args)
            if len(args) > 1:
                gate = args[1]
                if self.mine(gate, self.ship.rm.name, uni, 1) == 1:
                    self.sk.send("You launch a subspace mine, which is quickly swallowed by the wormgate that leads to sector " + gate + ".\n\r")
                    self.actRoom(self, players, "The " + self.ship.name + " fires a subspace mine into the wormgate that leads to sector " + gate + ".\n\r")
                else:
                    self.sk.send("Not a valid gate.\n\r")
    
        def torpedo(self,args,uni,players):
            args = split(args)
            foundship = 0
            if len(args) > 1:
                target = args[1]
                for player in players:
                    if player.ship.rm.name == self.ship.rm.name and target == player.ship.name:
                        if self.ship.useFuel(500) == 1:
                            self.sk.send("You target the '" + player.ship.name + "' and launch a torpedo.  It impacts milliseconds later.\n\r")
                            self.actRoom(player, players, "The '" + player.ship.name + "' rocks as a torpedo strikes it.\n\r")
                            player.sk.send(c(31) + "Your ship rocks violently as the '" + self.ship.name + "' fires upon you!" + endl)
                            player.ship.damage(randint(200,500))
                            foundship = 1
                        else:
                            self.sk.send("You don't have enough fuel.\n\r")
                if foundship == 0:
                    self.sk.send("Invalid target.\n\r")
            else:
                self.sk.send("You must specify a target.\n\r")
    
        def enterGate(self,args,uni, players):
            args = split(args)
            foundGate = -1
            if len(args) > 1 :
                gate = args[1]
                temp = self.ship.rm.exit
                for a in range(len(temp)):
                    if temp[a].right.name == gate:
                        foundGate = temp[a].right
                        mined = temp[a].mined
                        if mined > 0:
                            self.mine(gate, self.ship.rm.name, uni, 0)
                if foundGate == -1:
                    self.sk.send("Not a valid gate.\n\r")
                else:
                    self.actRoom(self, players, "The '" + self.ship.name + "' falls toward the wormgate to sector " + gate + " and is swallowed by darkness.\n\r")
                    self.sk.send("Space ripples as your vessel falls through the wormgate to sector " + gate + ".\n\r")
                    if not mined == 0:
                        self.sk.send(c(31) + "Your vessel is rocked as a subspace mine explodes within the wormhole!" + endl)
                        self.actRoom(self,players, "There is a flash of light deep within the wormhole's maw as it winks shut.\n\r")
                        self.ship.damage(randint(50,300))
                    self.ship.rm = uni.rm[int(gate) - 1]
                    if not mined == 0:
                        self.actRoom(self,players, "A small shockwave rocks your vessel as a wormgate winks open\n\r")
                    self.actRoom(self, players, "A pulsing wormgate opens briefly, disgorging a vessel, the '" + self.ship.name + "'.\n\r")
                    self.showRoom(players)
                #if black hole!
                if self.ship.rm.sun == 0:
                    self.sk.send("Your entire ship screams and strains as it emerges from the wormgate within the grasp of a fearsome black hole!  The wormgate twists upon itself and pulls you back, perhaps to safety...\n\r")
                    self.ship.damage(randint(200, 600))
                    self.ship.rm = uni.rm[randint(2,998)]
                    self.actRoom(self, players, "A pulsing wormgate opens briefly, disgorging a vessel, the '" + self.ship.name + "'.\n\r")
            else:
                self.sk.send("Enter what wormgate?\n\r")
    
    class Handler:
        def __init__(self):
            self.clist = [ ]
            self.listener = socket(AF_INET, SOCK_STREAM)
            self.listener.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
            self.listener.bind((HOST, PORT))
            self.listener.listen(32)
            print "Ready on port " + `PORT` + "."
    
        def run(self, uni):
            ready = self.clist[:]
            #regen and death check
            for co in ready:
                #if you are dead
                if co.ship.hp < 1:
                    co.sk.send("It is over.\n\r")
                    co.actRoom(co, self.clist[:], "With a flash of white light, the " + co.ship.name + "'s shielding finally gives way.  Your scanners are momentarily blinded as the energy of the explosion washes over your vessel.\n\r")
                    self.clist.remove(co)
                    print "Connection closed to " + co.ip + " (" + co.ship.name + ")."
                    co.act(self.clist,  c(32) + "Info: The vessel '" + co.ship.name + "' has been destroyed." + endl)
                co.ship.regen()
    
            #process input
            ready.append(self.listener)
            ready = select(ready, [], [], 0.1)[0]
            for co in ready:
                if co is self.listener:
                    sk, address = self.listener.accept()
                    co = Connection(sk, address)
                    self.clist.append(co)
                    print "Connection accepted from " + co.ip + "."
    
                    co.sk.send("Welcome Aboard.\n\r")
                    co.sk.send(c(33)+ "Please see http://www.crosswinds.net/~forcharity/sparse.htm for documentation." + endl)
                    co.act(self.clist,  c(32) + "Info: Someone has entered the game." + endl)
                    co.ship = Ship()
                    co.ship.time = int(clock())
                    co.ship.rm = uni.rm[501]
                    co.showRoom(self.clist[:])
                else:
                    co.ship.regen()
                    data = co.receive()
                    if not data:
                        print "Connection closed to " + co.ip + "."
                        self.clist.remove(co)
                    else:
                        if not data[-1] == '\n':
                            co.enqueue(data)
                        else:
                            co.enqueue(data)
                            temp = co.queue()
    
                            #'look' command
                            if temp[0]== "l":
                                co.showRoom(self.clist[:])
    
                            #'enter' command
                            elif temp[0] == "e":
                                if co.ship.useFuel(15):
                                    if co.ship.docked == 0:
                                        co.enterGate(temp, uni, self.clist[:])
                                    else:
                                        co.sk.send("Not while docked.\n\r")
                                else:
                                    co.sk.send("You don't have enough fuel.\n\r")
    
                            #'radio' command
                            elif temp[0] == "r":
                                temp = join(split(temp)[1:])
                                if temp:
                                    #fizpop converter
                                    co.act(self.clist[:], c(33) + "The '" + co.ship.name + "' radios, '" + temp  + "'"+ endl)
                                else:
                                    co.sk.send("Radio what?\n\r")
    
                            #'?help' command
                            elif temp[0] == "?":
                                co.sk.send("Your info:\n\r")
                                co.sk.send("\n\rFuel: " + `co.ship.fuel` + "(" + `co.ship.max` + ")\n\r")
                                co.sk.send("Hp: " + `co.ship.hp` + "(" + `co.ship.max` + ")\n\r")
                                co.sk.send("Ore1: " + `co.ship.ore1` + "\n\r")
                                co.sk.send("Ore2: " + `co.ship.ore2` + "\n\r")
                                co.sk.send("Ore3: " + `co.ship.ore3` + "\n\r")
                                co.sk.send("$" + `co.ship.cash ` + "\n\r")
    
                            #'name' command
                            elif temp[0] == "n":
                                co.ship.rename(temp, co, self.clist[:])
    
                            #'dock' command
                            elif temp[0] == "d":
                                if co.ship.useFuel(10):
                                    if co.ship.docked == 1:
                                        co.sk.send("You release your docking clamp, and gently slip away from the station.\n\r")
                                        co.actRoom(co,self.clist[:], "The " + co.ship.name + " disengages its docking clamp and slowly drifts away from the station.\n\r")
                                        co.ship.docked = 0
                                    else:
                                        pl = co.ship.rm.planet
                                        if pl == -1:
                                            co.sk.send("There is no station here to dock with.\n\r")
                                        else:
                                            co.sk.send("You engage your docking clamp, securing your hull against " + pl.name + "'s spacedock.\n\r")
                                            co.sk.send("Trade rates: " + `pl.ore1 * .01` + "% for ore1, "+ `pl.ore2 * .01` + "% for ore2, "+ `pl.ore3 * .01` + "% for ore3.\n\r")
                                            co.actRoom(co,self.clist[:], "The " + co.ship.name + " engages its docking clamp and links with " + pl.name + "'s spacedock.\n\r")
                                            co.ship.docked = 1
                                else:
                                    co.sk.send("You don't have enough fuel to use your docking clamp.\n\r")
    
                            #'quit' command
                            elif temp[0] == "q":
                                co.ship.damage(1000000)
    
                            #'viewer' command
                            elif temp[0] == "v":
                                if co.ship.useFuel(50):
                                    co.sk.send(ljust("Name:", 15) + ljust("Fuel:", 10)+ ljust("Damage:", 10)+"Sector:\n\r")
                                    for player in self.clist[:]:
                                        co.sk.send(ljust(player.ship.name, 15) + ljust(`player.ship.fuel`, 10)  + ljust(`player.ship.hp`, 10)+ player.ship.rm.name + "\n\r")
                                    co.act(self.clist,  "Your sensors pick up a subspace ping.  Someone knows your location.\n\r")
                                else:
                                    co.sk.send("You don't have enough fuel.\n\r")
    
                            #'who' command
                            elif temp[0] == "w":
                                co.sk.send(ljust("Name:", 15)  + ljust("Seconds Online:", 25)+"IP:\n\r")
                                for player in self.clist[:]:
                                    co.sk.send(ljust(player.ship.name, 15) + ljust(`int(clock()) - player.ship.time`, 25)+ player.ip + "\n\r")
    
                            #'buy' command.  purchase upgrade, ore1,ore2,ore3
                            elif temp[0] == "b":
                                if co.ship.docked == 0:
                                    co.sk.send("You aren't docked at a planet.\n\r")
                                else:
                                    args = split(temp)
                                    if len(args) < 2:
                                        co.sk.send("Buy what?\n\r")
                                    else:
                                        found = 0
                                        if len(args) > 2:
                                            if args[1] == "ore1":
                                                co.ship.ore1 = buyOre(args[2], co.ship.ore1, co.ship.rm.planet.ore1, co)
                                            if args[1] == "ore2":
                                                co.ship.ore2 = buyOre(args[2], co.ship.ore2, co.ship.rm.planet.ore2,co)
                                            if args[1] == "ore3":
                                                co.ship.ore3 = buyOre(args[2], co.ship.ore3, co.ship.rm.planet.ore3, co)
                                        elif args[1] == "ship":
                                            if co.ship.cash > co.ship.max:
                                                co.ship.cash = co.ship.cash - 1000
                                                co.ship.max = co.ship.max + 100
                                                co.sk.send("Your max hp and fuel have been increased by 100!\n\r")
                                            else:
                                                co.sk.send("You need " + `co.ship.max` + " credits for an upgrade.\n\r")
                                        else:
                                            co.sk.send("But how many?\n\r")
    
                            #'sell' command.  sell ore1,ore2,ore3
                            elif temp[0] == "s":
                                if co.ship.docked == 0:
                                    co.sk.send("You aren't docked at a planet.\n\r")
                                else:
                                    args = split(temp)
                                    if len(args) < 2:
                                        co.sk.send("Sell what?\n\r")
                                    else:
                                        if len(args) > 2:
                                            if args[1] == "ore1":
                                                co.ship.ore1 = sellOre(args[2], co.ship.ore1, co.ship.rm.planet.ore1, co)
                                            if args[1] == "ore2":
                                                co.ship.ore2 = sellOre(args[2], co.ship.ore2, co.ship.rm.planet.ore2,co)
                                            if args[1] == "ore3":
                                                co.ship.ore3 = sellOre(args[2], co.ship.ore3, co.ship.rm.planet.ore3, co)
                                        else:
                                            co.sk.send("But how many?\n\r")
    
                            #'torpedo' command
                            elif temp[0] == "t":
                                if co.ship.docked == 0:
                                    co.torpedo(temp, uni,self.clist[:])
                                else:
                                    co.sk.send("Not while docked.\n\r")
    
                            #'mine' command
                            elif temp[0] == "m":
                                if co.ship.useFuel(200):
                                    if co.ship.docked == 0:
                                        co.mineExit(temp, uni, self.clist[:])
                                    else:
                                        co.sk.send("Not while docked.\n\r")
                                else:
                                    co.sk.send("You don't have enough fuel.\n\r")
    
                            else:
                                co.sk.send("Huh?\n\r")
                            temp = ""
                            co.resetqueue()
    
    #Main loop
    connections = Handler()
    uni = Universe()
    while 1:
        connections.run(uni)

    Source code without formatting