Wednesday, 24 October 2018

A Very Simple Adventure Program

This program was prompted by my browsing a list of challenges for students of computer programming, one of which was "Create a text-based adventure game". My thoughts went to those Fighting Fantasy books by Steve Jackson and Ian Livingstone, such as "Warlock of Firetop Mountain".
To be honest I've started off simple. The only things that the program keeps track of are what section the user is currently on and what their current stamina (life points) score is. The idea of keeping track of non-player creatures (that might move around from one location to another or die) and movable items (that might be picked up, dropped or consumed) I put aside until I had got a grip on the basics.
I decided to do reading the data file (just a text file) and setting up as the main program and the repeated process of going from one section of text to the next as a user-defined function. Here is the whole program:

import sys

def nextparagraph(para, stam):
    print("*************")
    print(paradic[para])
    stam = deathcheck(para, stam)
    if stam >= 1:
        temptable = []
        for entry in optionstable:
            if entry[2] == para:
                temptable.append(entry)
        validchoice = False
        while validchoice == False:
            for entry in temptable:
                optnum = temptable.index(entry)
                print(optnum, entry[0])
            option = int(input("Option: "))
            if option >= 0 and option < (len(temptable)):
                validchoice = True
                newpara = temptable[option][1]
        nextparagraph(newpara, stam)

def deathcheck(para, stam):
    for item in staminaChangeDic:
        if item ==  para:
            stam += staminaChangeDic[item]
    if stam <= 0:
        print("Sorry, you have died. Your stamina is", stam)
        print("Better luck next time!")
        sys.exit()
    elif stam >=1000:
        print("Congratulations, you have survived the cave!")
        sys.exit()
    else:
        print("Current Stamina: ", stam)
        return stam
    
FH = open('caverdata01.txt', 'r')
paralist = [] # list of entire, unparsed paragraphs
parastring = '' #empty string to hold contents of each paragraph
for line in FH:
    #print (line)
    if line == '---\n':
        paralist.append(parastring)
        parastring = ''
    else:
        parastring += line
FH.close()

optionstable = []
paradic = {}
staminaChangeDic = {}
for para in paralist:
    templist = para.split('$')
    pararef = templist[0].rstrip()
    paraprose = templist[1]
    if '//' in paraprose:
        staminaAdjust = int(paraprose.split('//')[1])
        paraprose = paraprose.split('//')[0]
        staminaChangeDic[pararef] = staminaAdjust
    for option in templist[2:]:
        tempopt = option.split('Goto ')
        tempopt[1] = tempopt[1].rstrip()
        tempopt.append(pararef)
        optionstable.append(tempopt)
    paradic[pararef] = paraprose

stamina = 20
carryon = True
para = '0'
print("CAVE ADVENTURE!!!")
nextparagraph(para, stamina)

As you can see, it makes use of three dictionaries, holding the section reference as the key. The values are different for each dictionary - one holds the text description, one holds the options for each section and the third holds any stamina adjustments associated with a section. 

The data file holds the section text , the modification to stamina score on choosing each section, and options for leaving each section. I decided to use symbols to indicate how the character should be split up - a sort of markup, though not as sophisticated as HTML. So in the data file three dashes --- indicate the breaks between sections, while the dollar sign $ is used to split up sections into section number/reference (actually a string, not an integer), the prose that is printed to describe the situation to the player, and the options with the section reference that each option leads to.
A double forward slash // is used at the end of the prose if there is a modification to stamina, and is followed by an integer for how much stamina is gained (or lost if the number is negative).
For each option, I separated the text to be displayed from the reference it leads to by the string "Goto". This is more nostalgia than anything else - it reminds me of my very early days of programming on a Commodore 64, where using GOTO statements was the norm rather than a blasphemy against proper programming. One advantage of doing the data this way is that it is fairly readable for whoever is creating it - it is almost but not quite like those adventure game books.
I'll only give the first three paragraphs, otherwise this post would get very long, and also I wouldn't want to spoil it for you.

0 $You find yourself in a dimly lit cavern. You are not 
sure how you got here but you know you have to find a way 
out. Checking yourself you find you have an electric 
flashlight and a compass with which to navigate. 
$You look around. Goto 1
---
1 $This is a relatively plain, featureless cavern with 
rugged walls and fairly flat, gravel-like floor. Looking 
around the cavern you see there are three tunnels exiting 
this cavern.
$Do you go north? Goto 2
$Do you go east? Goto 3
$Do you go south? Goto 4
---
2 $This cavern has a pool of water in it. At a distance you 
cannot see clearly into the pool. Also this cavern has two 
exits, one heading south, one heading east. 
$Do you investigate the pool? Goto 2a
$Do you go south? Goto 1
$Do you go east? Goto 5
---

I found I was starting to lose track of how each area was located relative to each other. So I did some documentation outside the code in the form of doing a map using presentation software. The result?


No comments:

Post a Comment