4
\$\begingroup\$

I'm new to coding and this is a text based adventure game with combat. Sleep and choice functions based on idea from https://codereview.stackexchange.com/a/52292/179642. Map idea from Codecademy battleship tutorial.

The actions in the fight function are not in a loop because attacks reduce the opponent's hp but healing affects your own hp.

Any comments on new ideas/features, better coding methods greatly appreciated.

import random, time, sys

#initial variables or settings, and map generation
hp = 100
rMap = [('G', 'G', 'G', 'C', 'W'), ('F', 'F', 'G', 'W', 'M'), ('B', 'W', 'W', 'G', 'M'), ('F', 'F', 'G', 'G', 'G'), ('F', 'F', 'F', 'G', 'V')]
pMap = []
for x in range(5):
    pMap.append(['?'] * 5)
legend = {'X': 'You', '?': 'Unexplored', 'G': 'Grassland', 'V': 'Village', 'F': 'Forest', 'M': 'Mountain', 'B': 'Bridge', 'W': 'Water', 'C': 'Castle'}
enemy = {'G': ('Wolf', 30, 0, 33, 33, 50), 'F': ('Bandit', 30, 1, 50, 33, 50), 'M': ('Troll', 80, 0, 33, 50, 50), 'C': ('Dragon', 170, 2, 0, 0, 100), 'B': ('Ogre', 100, 0.5, 100, 100, 100)}
posR = 4
posC = 4
inv = {'Bow' : 1, 'Arrow' : 0, 'Potion' : 0}
action = {'Punch': (8, 12, 'p'), 'Kick': (6, 20, 'k')}


def wait(value):    #can be commented away for quicker testing
    #time.sleep(value)
    pass


def new_loc():  #whether there is an enemy when the player moves to a new location
    for i in enemy:
        if rMap[posR][posC] == i:
            if random.randint(1, 100) <= enemy[i][5]:
                fight(enemy[i][0], enemy[i][1], enemy[i][2])
                loot(enemy[i][3], enemy[i][4])
                if i == 'C':
                    print ('You win! Congratulations!')
                    sys.exit()
    updatepos()
    turn()


def turn():  #every turn or action by the player
    global hp
    print ('What do you want to do?')
    wait(0.5)
    print ('t to Travel, i to view Inventory, m to view Map')
    c = choice(['t', 'i', 'm'])
    if c == 't':
        print ('Which direction do you want to move?')
        d = []
        if posR != 0:
            if rMap[posR - 1][posC] != 'W':
                print ('f to move forward')
                d.append('f')
        if posR != 4:
            if rMap[posR + 1][posC] != 'W':
                print ('b to move backward')
                d.append('b')
        if posC != 0:
            if rMap[posR][posC - 1] != 'W':
                print ('l to move left')
                d.append('l')
        if posC != 4:
            if rMap[posR][posC + 1] != 'W':
                print ('r to move right')
                d.append('r')
        sel_dir = choice(d)
        if sel_dir == 'f':
            move(-1, 0)
        if sel_dir == 'b':
            move(1, 0)
        if sel_dir == 'l':
            move(0, -1)
        if sel_dir == 'r':
            move(0, 1)
    if c == 'i':
        showInv()
        turn()
    if c == 'm':
        updatepos()
        turn()


def start():    #introduction to the game
    print ('Your village has been burnt down by a dragon!')
    wait(0.5)
    print (' ')
    print ('You have to find it and take revenge!')
    print (' ')
    updatepos()
    turn()


def showAction():   #for debugging player actions in battle
    for i in action:
        print (i, action[i])


def updateAction():   #for updating actions linked to items
    if inv['Potion'] > 0:
        if 'Heal' not in action:
            action['Heal'] = (40, 50, 'h')
    if inv['Potion'] == 0:
        if 'Heal' in action:
            action.pop('Heal')
    if inv['Arrow'] > 0:
        if 'Shoot' not in action:
            action['Shoot'] = (30, 40, 's')
    if inv['Arrow'] == 0:
        if 'Shoot' in action:
            action.pop('Shoot')


def showInv():    #for viewing inventory
    global hp
    for i in inv:
        if inv[i] > 0:
            print (i + ':', inv[i])
    if inv['Potion'] > 0:
        updateAction()
        print('Do you want to drink a Potion? y/n')
        drink = choice(['y', 'n'])
        if drink == 'y':
            h = random.randint(action['Heal'][0], action['Heal'][1])
            hp += h
            print('You used a Potion')
            wait(0.5)
            print('You healed yourself for %d HP!' % h)
            wait(0.5)
            inv['Potion'] -= 1
            print('You have %d Potions left' % (inv['Potion']))
    turn()


def move(r, c):    #when the player moves
    global posR
    global posC
    posR = posR + r
    posC = posC + c
    wait(0.5)
    if r == -1:
        print ('Moving Forward...')
    if r == 1:
        print ('Moving Backward...')
    if c == 1:
        print ('Moving Right...')
    if c == -1:
        print ('Moving Left...')
    new_loc()


def updatepos():    #updating the map position
    pMap[posR][posC] = 'X'
    if posR != 4:
        pMap[posR + 1][posC] = rMap[posR + 1][posC]
    if posR != 0:
        pMap[posR - 1][posC] = rMap[posR - 1][posC]
    if posC != 4:
        pMap[posR][posC + 1] = rMap[posR][posC + 1]
    if posC != 0:
        pMap[posR][posC - 1] = rMap[posR][posC - 1]
    showMap()
    wait(0.5)
    print ('You are now in %s' % legend[rMap[posR][posC]])


def showMap():  #prints the map and legend (for revealed areas)
    print ('Map:')
    for row in pMap:
        for i in row:
            print (i, end = ' ')
        print()
    print (' ')
    legList = []
    for row in pMap:
        for i in row:
            legList.append(i)
    for i in legend:
        if i in legList:
            print (i, legend[i])
    print (' ')


def choice(list):   #get a valid choice from the user
    while True:
        userChoice = input('Choose an action:')
        userChoice = userChoice.lower()
        if userChoice in list:
            return userChoice
        else:
            print ('Invalid choice. Try again.')


def fight(enemy, eHP, level):   #fighting an enemy
    print ('%s appeared!' % enemy)
    wait(0.5)
    global hp
    enemyHP = eHP
    while True:
        if hp <= 0:
            print ("You died :(. Game over")
            sys.exit()
        wait(1)
        print ('Your hp: %d, %s hp: %d' % (hp, enemy, enemyHP))
        wait(1)
        updateAction()
        act = []
        for i in action:
            print (i + ': ' + str(action[i][0]) + ' - ' + str(action[i][1]))
            act.append(action[i][2])
        act_str = ', '.join(str(x) for x in act)
        print ('Choices: ' + act_str)
        pmove = choice(act)
        wait(0.7)
        if pmove == 'p':
            d = random.randint(action['Punch'][0], action['Punch'][1])
            enemyHP -= d
            print ('You punch %s for %d damage!' % (enemy, d))
        elif pmove == 'k':
            d = random.randint(action['Kick'][0], action['Kick'][1])
            enemyHP -= d
            print ('You kick %s for %d damage!' % (enemy, d))
        elif pmove == 'h':
            h = random.randint(action['Heal'][0], action['Heal'][1])
            print ('You used a Potion')
            print ('You heal yourself for %d HP!' % h)
            inv['Potion'] -= 1
            print ('You have %d Potions left' % (inv['Potion']))
            updateAction()
            hp += h
        elif pmove == 's':
            d = random.randint(action['Shoot'][0], action['Shoot'][1])
            print ('You used an Arrow')
            print ('You shoot %s for %d damage!' % (enemy, d))
            inv['Arrow'] -= 1
            print('You have %d Arrows left' % (inv['Arrow']))
            updateAction()
            enemyHP -= d

        if enemyHP <= 0:
            print("You defeated %s!" % enemy)
            return

        wait(1)
        emove = random.randint (1, 3)
        if emove == 1:
            dam = random.randint(1, 5) + 10 * level
            print ('%s scratches you for %d damage!' % (enemy, dam))
            hp -= dam
        elif emove == 2:
            dam = random.randint(6, 10) + 0.5 * level
            print ('%s bites you for %d damage!' % (enemy, dam))
            hp -= dam
        else:
            heal = random.randint(9, 15) + 1 * level
            print ('%s healed for %d HP!' % (enemy, heal))
            enemyHP += heal


def loot(a, p): #probability of getting loot from enemy
    wait(1)
    pArrow = random.randint(1, 100)
    pPotion = random.randint(1, 100)
    if pArrow <= a:
        inv['Arrow'] += 1
        print ('You found an Arrow!')
        wait(0.5)
        print('You now have %d Arrows' % inv['Arrow'])
        wait(0.7)

    if pPotion <= p:
        inv['Potion'] += 1
        print ('You found a Potion!')
        wait(0.5)
        print('You now have %d Potions' % inv['Potion'])
        wait(0.7)


start()
\$\endgroup\$
  • 2
    \$\begingroup\$ I'm not very good at Python so I don't have any criticism other than to say, I've briefly played your game, got into a fight with a wolf, defeated the wolf and thoroughly enjoyed it. :) \$\endgroup\$ – I0_ol Sep 14 '18 at 22:58
  • \$\begingroup\$ One thing you might consider is putting another new line after Choose an action: to give a little separation between each turn. \$\endgroup\$ – I0_ol Sep 15 '18 at 4:33
  • \$\begingroup\$ @I0_ol Glad you enjoyed it! I'll implement a turn counter along with the other updates so the user knows which turn they are on. \$\endgroup\$ – Ben Sep 15 '18 at 5:54
1
\$\begingroup\$

Nice work!

Here are some things you can improve:

  • Style: PEP8 - you can check it with http://pep8online.com/ and there are many editors / editor-plugins that autocorrect or at least complain.
  • #can be commented away for quicker testing is useless
  • Variable names:
    • inv: inventory would be better
    • What is an rMap, what a pMap?
  • Documentation: Adding docstrings is usually a good idea.

Object Orientation

You could create a couple of objects and assign attributes / functions to them:

  • Game: Command line interactions, contains the other objects.
    • Has the map(s) as a property
  • Player: Has an intentory, a position, hp, ...
\$\endgroup\$
  • \$\begingroup\$ Thanks for the feedback!. Forgot to remove the # in wait. rMap is the real map, but is not shown to the player so there is some element of exploration. pMap is the map shown to the player, and is updated to reveal the adjacent areas whenever the player moves to a new location. I'll update their names to reflect that. Right, realised that I used an object oriented language but did not use any objects... Silly me! Creating a class would also be a better way to manage the enemies' stats instead of a list within a dictionary. \$\endgroup\$ – Ben Sep 15 '18 at 5:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.