In honour of the Raspberry Pi, I have rewritten an old program I first wrote over thirty years ago on a time-shared computer my school shared with every other school in Birmingham, using a 110 baud teletype.
History
Back in 1979 I was doing my A-level in Computer Science, and this was one of the programs I submitted. I wrote it on a 110baud teletype talking to a time-shared computer somewhere in the Birmingham Council Treasury department. As I left school, they took possession of a new Research Machines 380Z that the PTA had bought them for around £10,000. But, with only a longing glance over my shoulder, I got my A-level and proceeded to Manchester University. That's relevant because that is where the JMB exam board was based, which came in very handy when on registering for our courses we found that we did not have the right paperwork and had to queue up outside the JMB office to get it.
So I registered on the course and started lectures, and was introduced to the computer centre's computer. As part of that I was looking around to see what was there and how it was used and stuff. Imagine my surprise, when I found the program I had written, and sent to the JMB office just around the corner, was already on the computer!
After a few enquiries, it turned out that, in the intervening time between June and October, an enterprising pupil at my old school had converted the program to run on the 380Z and submitted it to a magazine. A staff member in the computer centre had seen it and reconverted it to run on the rather more impressive computer there.
I've not got the original listing unfortunately, so I've re-written it in Python.
Backstory
You fall down a pit into a dangerous dungeon. The dungeon is a grid of identical rooms, some of which contain monsters. It is dark and you start stumbling around. If you can survive long enough you can gather items to help you find your way out, some of the monsters are friendly and will give you stuff and if you can beat the evil ones you can take stuff off them, but as you work your way up and out, the monster become more evil and more difficult to beat.
In the centre of the dungeon is a deep well of healing water, but there is a cost.
The Code
You don't have to type this in if you don't want to; copy-and-paste if you must.
There are still a few bugs in there and there is plenty of room for enhancements. I've used BASIC style for the most part, except where I could not bear not to use objects, so it should be accessible to those of you who learned to program on the Spectrum or BBC Micro by typing in programs from magazines. There are no comments; there never were in the magazines.
#! /usr/bin/python from __future__ import absolute_import, division, generators, unicode_literals, print_function, nested_scopes, with_statement from random import randint, seed, randrange import sys from math import sqrt,atan2 from time import sleep boardsize = 100 exitlevel = 6 if sys.version_info < (3,): input = raw_input class MonsterClass(object): def __init__(self, name, probgood, health, offence=0, defence=0): self.name = name self.probgood = probgood self.health = health self.offence = offence self.defence = defence MonsterClasses = [ MonsterClass("Gold Horse", 80, 100,10,10), \ MonsterClass("Goblin", 20, 30), \ MonsterClass("Ghost", 20, 80,5,5), \ MonsterClass("Demon", 10, 200,15,15), \ MonsterClass("Knight", 100, 100,5,5), \ MonsterClass("Mage", 50, 100,0,0), \ MonsterClass("Kobold", 20, 10,-3,-3), \ MonsterClass("Elf", 80, 100,0,0), \ MonsterClass("Dragon", 50, 300,20,20), \ MonsterClass("Vampire", 20, 200,10,10), \ MonsterClass("Giant Spider", 20, 100,0,0), \ MonsterClass("Dwarf", 80, 100,0,0), \ MonsterClass("Mummy", 50, 100,5,5), \ MonsterClass("Goblin", 20, 30,3,3)] class Monster(object): def __init__(self, mclass, level): self.mclass = mclass self.name = mclass.name probgood = mclass.probgood/level if randint(1,100) < probgood: self.align = "good" elif randint(1,100) < 50/level: self.align = "neutral" else: self.align = "evil" self.health = mclass.health self.offence = mclass.offence self.defence = mclass.defence def __str__(self): return self.name def take_damage(self,damage): self.health = self.health - damage def isalive(self): return self.health > 0 def populate_board(): num_monsters = boardsize**2 / ((exitlevel-level)/2+1) for m in range(int(num_monsters)): m_x, m_y = (randint(1,boardsize), randint(1,boardsize)) while (m_x, m_y) in board: m_x, m_y = (randint(1,boardsize), randint(1,boardsize)) monster = Monster(MonsterClasses[randrange(len(MonsterClasses))], level) board[(m_x, m_y)] = monster def new_level(): global x, y, exit_x, exit_y, board x = randint(1,100) y = randint(1,100) exit_x = x exit_y = y while exit_x == x and exit_y == y: exit_x = randint(1,100) exit_y = randint(1,100) board = dict() populate_board() def get_move(): global x, y, health if "light" not in inventory.get_capabilities(): input("Hit Enter to enter a room") nx = x ny = y while nx == x and ny == y: nx = randint(1,boardsize) ny = randint(1,boardsize) x = nx y = ny else: gotit = False while not gotit: try: x, y = [int(n) for n in input("Which room do you want to enter? (x,y): ").split(",")] if not (1 <= x <= boardsize and 1 <= y <= boardsize): raise ValueError() except ValueError: print("Oops! Enter two signed numbers (1 -",boardsize,") separated by a comma. Try again...") else: gotit = True class Item(object): def __init__(self, name, defence=0, offence=0, capabilities=None, gold=0): self.name = name self.defence = defence self.offence = offence self.capabilities = set() if capabilities != None: self.capabilities.add(capabilities) print( self.name, "capabilities=", capabilities, self.capabilities) self.gold = gold def __str__(self): s = "a " + self.name if self.gold > 0: s = s + " containing "+str(self.gold)+" gold pieces" return s class Inventory(object): def __init__(self): self.items = [] self.wealth = 0 def print(self): print (strlist(self.items)) def append(self, item): if type(item) is list: self.items = self.items + item else: self.items.append(item) def get_capabilities(self): capabilities = set() for item in self.items: capabilities = capabilities | item.capabilities return capabilities def len(self): return len(self.items) def lose(self): i = randrange(len(self.items)) item = self.items[i] del self.items[i] return item def defence(self): d = 0 for item in self.items: d = d + item.defence return d def offence(self): a = 0 for item in self.items: a = a + item.offence return a def minimum(x,y): return x if x < y else y def booty(): items = [] if randrange(0,100) < 75: capabilities = inventory.get_capabilities() if "light" not in capabilities: items.append(Item("lantern", 0,0, "light")) elif "distance" not in capabilities: items.append(Item("distance meter", 0,0, "distance")) elif "compass" not in capabilities and level > 1: items.append(Item("compass", 0,0, "compass")) elif "bearing" not in capabilities and level > 2: items.append(Item("compass calibration", 0,0, "bearing")) if randrange(0,100) < 50: if randrange(0,100) < 25: items.append(Item("sword", 3,0)) elif randrange(0,100) < 25: items.append(Item("spear", 2,0)) elif randrange(0,100) < 25: items.append(Item("dagger", 1,0)) elif randrange(0,100) < 25: items.append(Item("mace", 4,0)) elif randrange(0,100) < 25: items.append(Item("pike", 3,0)) if randrange(0,100) < 50: if randrange(0,100) < 50: items.append(Item("piece of leather armour", 0,1)) elif randrange(0,100) < 50: items.append(Item("piece of chain armour", 0,2)) elif randrange(0,100) < 50: items.append(Item("piece of plate armour", 0,3)) money = randint(1,10) items.append(Item("Money Pouch",gold=money)) return items def strlist(items): i = len(items) if i == 0: s = "nothing" elif i == 1: s = str(items[0]) elif i == 2: s = str(items[0])+" and "+str(items[1]) else: s = "" for j in range(i-2): s = s + str(items[j]) + ", " s = s + strlist(items[-2:]) return s def good_encounter(monster): global health print ("The", str(monster), "is friendly") if health < 100: extra = minimum(100-health, monster.health/2) print ("The", str(monster), "heals you of", extra, "points") health = health + extra return True items = booty() print ("The", str(monster), "gives you", strlist(items)) inventory.append(items) return True def attack(offence, defence): damage = randint(1,20)+offence-defence if damage < 0: damage = 0 return damage def evil_encounter(monster, surprise=False): global health while monster.isalive() and health > 0: if surprise: damage = attack(inventory.offence(), monster.defence) print ("You attack the", str(monster), "and wound it for", damage,"points") monster.take_damage(damage) if not monster.isalive(): print ("You killed it!") items = booty() print ("You find", strlist(items)) inventory.append(items) return True if monster.isalive() and health > 0: print ("The", str(monster), "attacks") surprise = True damage = attack(monster.offence, inventory.defence()) print ("The", str(monster), "attacks you and wounds you for", damage,"points") health = health - damage print ("Your health is", health,"%") if health <= 0: print ("You are dead!") if monster.isalive() and health > 0: action = "" while len(action) == 0 or action[0:1] not in "raRA": action = input("Do you want to Run or Attack? ") if action[0] in "Rr": damage = attack(monster.offence, inventory.defence()) print ("The", str(monster), "attacks you and wounds you for", damage,"points") health = health - damage print ("Your health is", health,"%") if health <= 0: print ("You are dead!") return False else: get_move() if randrange(100) < 50: print ("The ", str(monster)," follows you") else: encounter() return False return False def well_encounter(): global health print ("In the room there is a deep well.") action = "" while len(action) == 0 or action[0:1] not in "yYnN": action = input("Do you want to drink from the well? ") if action[0] in "yY": if health == 100 or inventory.len() == 0: print ("It is very refreshing") else: print("As you take a long refreshing drink, ",end="") item = inventory.lose() print(str(item), "falls down the well!") health = 100 def encounter(): if (x,y) in board: monster = board[(x,y)] mx,my = (x,y) print ("In the room there is a ", str(monster)) if monster.align == "evil": if evil_encounter(monster): del board[(mx,my)] elif monster.align == "good": if good_encounter(monster): del board[(mx,my)] else: if randrange(100) < 50: if good_encounter(monster): del board[(mx,my)] elif randrange(100) < 50 and "light" in inventory.get_capabilities(): if evil_encounter(monster): del board[(mx,my)] else: action = "" while len(action) == 0 or action[0:1] not in "iaIA": action = input("Do you want to Ignore or Attack? ") if action[0] in "Aa": if evil_encounter(monster, surprise = True): del board[(mx,my)] if x == 50 and y == 50 and health > 0: well_encounter() def print_position(): capabilities = inventory.get_capabilities() dx = x - exit_x dy = y - exit_y if "light" in capabilities: print ("You are in room (", x, ",", y, ")") if "compass" in capabilities: dir ="" if abs(dy) > abs(dx)/3: if dy < 0: dir = dir + "N" else: dir = dir + "S" if abs(dx) > abs(dy)/3: if dx < 0: dir = dir + "E" else: dir = dir + "W" print ("You need to head", dir) if "bearing" in capabilities: b = atan2(-dy,-dx) print (b) b = b/3.1415926/2*360 b = b - 90 b = 360 - b print (b) if b < 0: b = b + 360 if b > 360: b = b - 360 print ("The exit is on a bearing of", int(b), "degrees") if "distance" in capabilities: print ("The exit is ", sqrt(dx*dx + dy*dy), "away") level = 1 moves = 0 health = 100 alive = True seed() new_level() inventory = Inventory() #magic_box = Item("magic box", 0,0, {"light", "compass", "distance", "bearing"}) #inventory.append(magic_box) while health > 0 and level < exitlevel: moves = moves + 1 print ("\nMove ", moves, ":") print("Your health is ", health, "%") old_health = health sleep(0.5) print_position() sleep(0.5) get_move() if health > 0: encounter() if x == exit_x and y == exit_y: print ("**** Well done! you have found the stairs ****") sleep(3) level = level + 1 new_level() if health < 100 and old_health == health: health = health + 1 sleep(2) if health > 0: print ("Sunlight! You got out alive after", moves, "moves, with...") inventory.print() else: print ("Sorry; you died on level ", level) input("(Hit Enter to Exit)")
What is a Gold Horse? Blowed if I know. Back in the 1970's I simply made it up when I ran out of monster names.
Line 100 of the Gold Horse program is
if not (1 0:
which gets a syntax error, What should it be?
Maybe it's been fixed in the past few weeks? but I don't see that on line 100. I do see that line 101 is
if not (1 <= x <= boardsize and 1 <= y <= boardsize):
There have been a few fixes. In fact I've just had to make a fix to let it work with Python 2.6, because that's what is on the Raspberry Pi. The if statement in line 101 (or wherever I've just made it move to) is valid Python.
There seems to be an erroneous indentation on the line that says:
self.capabilities = set()