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 :)