I've found a small MUD-like single player game that I did with Python several years ago. Thought it would be nice to share the source. So here it is.
First of all, a live demo (type help. gone if my internet connection or home server is down.)
server down
And a "screenshot" (sorry for my bad sense of humor):
> help
welcome to mud. available commands are:
go, move, help, exit, look, say, take, drop, inventory, use
>
Unknown Command
> look
spajus sees deep green woods. There also seems to be: flower, bird
> touch bird
looks like a rainbow
> use bird
you do not have bird
> take bird
spajus puts bird in his inventory
> use bird
cuckarekoo! motherfucka!?!
> drop bird
bird was dropped..
> kill bird
Unknown Command
> look north
spajus sees shallow river. There also seems to be: object
> go north
spajus moves to shallow river
> look object
spajus sees a stinky one
> touch object
ewww...
> take object
spajus puts shit in his inventory
> use shit
you are sick, you know that?
Now, the
engine.py
class MudObject:
def __init__(self, name, sight, collide = 'nothing happens', usability = 'unusable'):
self.name = name
self.sight = sight
self.collide = collide
self.usability = usability
def view(self):
return self.sight
def touch(self):
return self.collide
def use(self):
return self.usability
class MudPlayer:
def __init__(self, name):
self.inventory = {}
self.name = name
self.health = 100
def move(self, area):
return self.name + ' moves to ' + area.sight
def take(self, obj):
self.inventory[obj.name] = obj
return self.name + ' puts ' + obj.name + ' in his inventory'
def drop(self, name):
if self.inventory.has_key(name):
return self.inventory.pop(name)
def say(self, what):
return self.name + ' says: ' + what
def use(self, what):
if self.inventory.has_key(what):
return self.inventory[what].use()
else:
return 'you do not have ' + what
class MudArea:
def __init__(self, sight):
self.objects = {}
self.panorama = {}
self.sight = sight
self.inverted_directions = {'north':'south', 'south':'north', 'east':'west', 'west':'east'}
def addArea(self, direction, area):
area.panorama[self.inverted_directions[direction]] = self
self.panorama[direction] = area
def relocate(self, args):
try:
return self.panorama[args]
except KeyError:
return None
def addObject(self, name, obj):
if obj != None:
self.objects[name] = obj
return name + ' was dropped..'
def getObject(self, name):
if self.objects.has_key(name):
return self.objects.pop(name)
else:
return 'there is no ' + name + ' arround!'
def touchObject(self, name):
if self.objects.has_key(name):
return self.objects[name].touch()
else:
return 'there is no ' + name + ' arround!'
def view(self, args = 'arround'):
if (args != '' and args != 'arround'):
try:
return self.panorama[args].view()
except KeyError:
try:
return self.objects[args].view()
except KeyError:
return 'nothing.'
else:
objects = ', '.join([k for k, v in self.objects.items()])
if (objects != ''):
obsight = '. There also seems to be: ' + objects
else:
obsight = ''
return self.sight + obsight
import sys
class MudCommand:
""" welcome to mud. available commands are:
go, move, help, exit, look, touch, say, take, drop, inventory, use """
def __init__(self, char, area):
self.char = char
self.area = area
def go(self, args):
""" alias of move """
return self.move(args)
def use(self, args):
""" uses item from inventory """
return self.char.use(args)
def inventory(self, args):
""" displays inventory """
return self.char.name + ' has: ' + ', '.join(self.char.inventory)
def help(self, args):
""" gives you help on a topic"""
if args == '':
return self.__doc__
else:
try:
return getattr(self, args).__doc__
except AttributeError:
return 'help topic not found'
def exit(self, args):
""" exits game """
print 'bye bye!'
sys.exit()
def look(self, args):
""" lets you look arround """
return self.char.name + ' sees ' + self.area.view(args)
def take(self, args):
""" takes item from the ground """
try:
return self.char.take(self.area.getObject(args))
except AttributeError:
return 'you cannot take ' + args
def touch(self, args):
""" touches item from the ground """
return self.area.touchObject(args)
def drop(self, args):
""" drops item from inventory to current area """
return self.area.addObject(args, self.char.drop(args))
def move(self, args):
""" moves arround """
area = self.area.relocate(args)
if area != None:
self.area = area
return self.char.move(self.area)
else:
return 'There seems to be nothing that way.'
def say(self, args):
""" makes character talk """
return self.char.say(args)
class MudGame:
def __init__(self, char, area):
self.cmd = MudCommand(char, area)
def run(self):
while True:
command = raw_input('> ');
self.parse(command)
def parse(self, command):
comm = command.lower().split(' ')
try:
cmd = comm[0]
except IndexError:
cmd = 'help'
try:
args = comm[1:]
except IndexError:
args = []
try:
result = getattr(self.cmd, cmd)(' '.join(args).strip())
except AttributeError:
result = 'Unknown Command'
print result
And the main game script -
mud.py
:
from engine import *
#objects (name, description, on touch, on use)
rose = MudObject('rose', 'a red blossom with spikes', 'bites fingers!', 'wanna eat it or what?')
shit = MudObject('shit', 'a stinky one', 'ewww...', 'you are sick, you know that?')
gaidys = MudObject('bird', 'oh, a cock!', 'looks like a rainbow', 'cuckarekoo! motherfucka!?!')
#areas
woods = MudArea('deep green woods')
river = MudArea('shallow river')
hills = MudArea('orc hills')
house = MudArea('house of all gay')
meadow = MudArea('a green smelly meadow')
#attaching interactive stuff to areas
river.addObject('object', shit)
woods.addObject('flower', rose)
woods.addObject('bird', gaidys)
meadow.addObject('animal', gaidys)
#link all areas with bidirectional references
river.addArea('south', hills)
woods.addArea('north', river)
woods.addArea('west', house)
hills.addArea('east', meadow)
meadow.addArea('north', woods)
#create a player
char = MudPlayer('spajus')
#create a game with player and starting area
game = MudGame(char, woods)
#lets go!
game.run()
OK, and of course, unit tests:
import unittest
from engine import *
class TestMudObject(unittest.TestCase):
def setUp(self):
self.o = MudObject('object1', 'sight1', 'collision1', 'usage1')
self.o2 = MudObject('object2', 'sight2')
def test_view(self):
self.assertEqual(self.o.view(), 'sight1')
self.assertEqual(self.o2.view(), 'sight2')
self.assertNotEqual(self.o.view(), 'c')
self.assertNotEqual(self.o.view(), self.o2.view())
def test_touch(self):
self.assertEqual(self.o.touch(), 'collision1')
self.assertEqual(self.o2.touch(), 'nothing happens')
self.assertNotEqual(self.o.touch(), 'sight1')
self.assertNotEqual(self.o.touch(), self.o2.touch())
def test_use(self):
self.assertEqual(self.o.use(), 'usage1')
self.assertEqual(self.o2.use(), 'unusable')
self.assertNotEqual(self.o.use(), 'unsuable')
self.assertNotEqual(self.o.use(), self.o2.use())
class TestMudPlayer(unittest.TestCase):
def setUp(self):
self.p1 = MudPlayer('player1')
self.p2 = MudPlayer('player2')
self.area1 = MudArea('area1')
self.area2 = MudArea('area2')
self.o = MudObject('object1', 'sight1', 'collision1', 'usage1')
self.o2 = MudObject('object2', 'sight2')
def test_move(self):
f1 = self.p1.move
f2 = self.p2.move
a1 = self.area1
a2 = self.area2
self.assertEqual(f1(a1), 'player1 moves to area1')
self.assertEqual(f2(a1), 'player2 moves to area1')
self.assertEqual(f1(a2), 'player1 moves to area2')
self.assertEqual(f2(a2), 'player2 moves to area2')
self.assertNotEqual(f1(a1), 'player1 moves to area2')
def test_take_drop(self):
take = self.p1.take
use = self.p1.use
drop = self.p1.drop
inven = self.p1.inventory
o1 = self.o
o2 = self.o2
self.assertEqual(inven, {})
self.assertEqual(take(o1), 'player1 puts object1 in his inventory')
self.assertEqual(inven, {'object1':o1})
self.assertEqual(take(o2), 'player1 puts object2 in his inventory')
self.assertEqual(inven, {'object1':o1, 'object2':o2})
self.assertEqual(drop('object1'), o1)
#neesamo objekto dropint neina
self.assertRaises(TypeError, drop('object1'))
self.assertEqual(inven, {'object2':o2})
self.assertEqual(drop('object2'), o2)
self.assertEqual(inven, {})
def test_use(self):
p1 = self.p1
o1 = self.o
self.assertNotEqual(p1.use('object1'), 'usage1')
self.assertEqual(p1.use('object1'), 'you do not have object1')
p1.take(o1)
self.assertEqual(p1.use('object1'), 'usage1')
class TestMudArea(unittest.TestCase):
def setUp(self):
self.a1 = MudArea('area1')
self.a2 = MudArea('area2')
self.o1 = MudObject('obj1', 'sight1', 'collide1', 'use1')
self.o2 = MudObject('obj2', 'sight2')
def test_addArea(self):
#assignmentas turi buti veidrodinis
self.a1.addArea('north', self.a2)
self.assertEqual(self.a1.panorama, {'north':self.a2})
self.assertEqual(self.a2.panorama, {'south':self.a1})
def test_relocate(self):
self.a1.addArea('north', self.a2)
self.assertEqual(self.a1.relocate('north'), self.a2)
self.assertEqual(self.a2.relocate('north'), None)
self.assertEqual(self.a2.relocate('south'), self.a1)
def test_addObject(self):
self.assertEqual(self.a1.objects, {})
#dropped returninama todel, kad paprastai objectas zaidimo metu addinamas tada, kai playeris dropina ji.
#kreivai biski, bet ka padarysi :)
self.assertEqual(self.a1.addObject('something', self.o1), 'something was dropped..')
self.assertEqual(self.a1.objects, {'something':self.o1})
self.a1.addObject('other', self.o2)
self.assertEqual(self.a1.objects, {'something':self.o1, 'other':self.o2})
self.assertEqual(self.a1.addObject('something_clone', self.o1), 'something_clone was dropped..')
self.assertEqual(self.a1.objects, {'something':self.o1, 'other':self.o2, 'something_clone':self.o1})
def test_getObject(self):
self.assertEqual(self.a1.objects, {})
self.a1.addObject('something', self.o1)
self.a1.addObject('other', self.o2)
self.assertEqual(self.a1.getObject('something'), self.o1)
self.assertEqual(self.a1.objects, {'other':self.o2})
self.assertEqual(self.a1.getObject('something'), 'there is no something arround!')
def test_touchObject(self):
self.assertEqual(self.a1.objects, {})
self.a1.addObject('something', self.o1)
self.a1.addObject('other', self.o2)
self.assertEqual(self.a1.touchObject('something'), self.o1.touch())
self.assertNotEqual(self.a1.touchObject('obj2'), self.o2.touch())
self.assertEqual(self.a1.touchObject('ass'), 'there is no ass arround!')
def test_view(self):
view = self.a1.view
self.assertEqual(view(), 'area1')
#unindentified object/panorama
self.assertEqual(view('my brain'), 'nothing.')
self.a1.addObject('my brain', self.o1)
self.assertEqual(view('my brain'), self.o1.view())
#padarom dar idomiau. kadangi pridejom my brain, reikia parodyti ir tai..
self.assertNotEqual(view(), 'area1')
self.assertEqual(view(), 'area1. There also seems to be: my brain')
self.a1.addObject('duck', self.o2)
self.assertEqual(view(), 'area1. There also seems to be: my brain, duck') #', '.join(self.a1.objects)
self.assertEqual(view('north'), 'nothing.')
self.a1.addArea('north', self.a2)
self.assertEqual(view('north'), 'area2')
#kadangi priskyrem area2, turejo atsispindeti ir is ten paziurejus i priesinga north krypti (south), turi matytis area1 viewas
self.assertEqual(self.a2.view('south'), view())
#akurat.. matosi :)
class MudCommandTest(unittest.TestCase):
def setUp(self):
self.p1 = MudPlayer('player1')
self.a1 = MudArea('area1')
self.a2 = MudArea('area2')
self.o1 = MudObject('obj1', 'sight1', 'collide1', 'use1')
self.o2 = MudObject('obj2', 'sight2', 'collide2', 'use2')
self.a2.addObject('bread', self.o1)
self.a2.addObject('pig', self.o2)
self.a1.addArea('east', self.a2)
self.c = MudCommand(self.p1, self.a1)
def test_go_move(self):
#MudArea.go === MudArea.move
#test wrong way
self.assertEqual(self.c.go('somewhere'), 'There seems to be nothing that way.')
#test walk arround
self.assertEqual(self.c.go('east'), 'player1 moves to area2')
self.assertEqual(self.c.go('west'), 'player1 moves to area1')
def test_use(self):
self.assertEqual(self.c.use('bla'), 'you do not have bla')
#lets go east and take something to test using
self.c.go('east')
self.c.take('bread')
#as bread was only the looks, we know it's actually obj1, so lets use it
self.assertEqual(self.c.use('obj1'), self.o1.use())
def test_inventory(self):
self.c.go('east')
self.c.take('bread')
self.assertEqual(self.c.inventory(None), 'player1 has: obj1')
def test_help(self):
self.assertEqual(self.c.help(''), self.c.__doc__)
self.assertEqual(self.c.help('move'), self.c.move.__doc__)
self.assertEqual(self.c.help('blabla'), 'help topic not found')
def test_look(self):
self.assertEqual(self.c.look(''), 'player1 sees ' + self.a1.view())
self.assertEqual(self.c.look('at my balls'), 'player1 sees nothing.')
self.assertEqual(self.c.look('east'), 'player1 sees ' + self.a2.view())
def test_take(self):
self.c.go('east')
self.assertEqual(self.c.take('bread'), 'player1 puts obj1 in his inventory')
self.assertEqual(self.p1.inventory, {'obj1':self.o1})
#already taken!
self.assertEqual(self.c.take('bread'), 'you cannot take bread')
def test_touch(self): #perv test.. :)
self.assertEqual(self.c.touch('self'), 'there is no self arround!')
self.assertNotEqual(self.c.touch('bread'), self.o1.touch())
self.c.go('east')
#kad paliesti reik pirma nueiti
self.assertEqual(self.c.touch('bread'), self.o1.touch())
def test_drop(self):
self.assertEqual(self.c.drop('smelly thing'), None)
self.c.go('east')
self.c.take('bread')
self.c.go('west')
self.assertEqual(self.c.drop('obj1'), 'obj1 was dropped..')
self.assertEqual(self.a1.objects, {'obj1':self.o1})
def test_say(self):
self.assertEqual(self.c.say('i love this game'), 'player1 says: i love this game')
self.assertNotEqual(self.c.say('python sucks'), 'player1 says: that\'s true!')
if __name__ == '__main__':
unittest.main()
To run the game:
python mud.py
To run the tests:
python testengine.py
Damn, I really miss such coding activities :)