I created this game in Python as a learning experience and was hoping for advice to make it better. I also used global and was hoping for good alternatives.
import random
import pickle
import os
enemy_count = 0
health_pot_count = 5
level_up_counter = 1
class Player:
def __init__(self, name):
self.name = name
self.level = 1
self.health = 20
self.max_attack = 6
self.min_attack = 2
self.armour = 1
def __repr__(self):
return('Player({})'.format(self.name))
def __str__(self):
return('Name: {}, Level: {}, Health: {}, Max Attack: {}, Min Attack {}, Armour: {}'.format(self.name, self.level, self.health, self.max_attack, self.min_attack, self.armour))
def level_up(self):
global level_up_counter
if level_up_counter == self.level:
level_up_counter = 1
self.level += 1
self.max_attack += 3
self.min_attack += 3
self.armour += 2
else: level_up_counter += 1
def armour_up(self):
self.armour += 1
def weapon_up(self):
self.max_attack += 1
class World:
def __init__(self):
self.isenemy = random.choice([True, True, False])
def enter_room(self):
if self.isenemy:
fight()
else:
if random.choice([True, True, False]):
treasure()
else:
print('The room is empty')
class Enemy:
def __init__(self):
attack_buff = random.randint(-2,4)
self.health = 5 * pc.level + random.randint(-5,5)
self.max_attack = 3 * pc.level + attack_buff
self.min_attack = 2 * pc.level + attack_buff
self.armour = pc.level * random.randint(1,2)
def __str__(self):
return(f'Enemy Health {self.health}')
def save():
save_var = [current_room, rooms, pc, enemy_count, health_pot_count, level_up_counter, pc.name, pc.level, pc.health, pc.max_attack, pc.min_attack, pc.armour]
with open('save.pkl', 'wb') as save_pickle:
pickle.dump(save_var, save_pickle)
def get_save():
global current_room, rooms, pc, enemy_count, health_pot_count, level_up_counter
with open('save.pkl', 'rb') as save_pickle:
current_room, rooms, pc, enemy_count, health_pot_count, level_up_counter, pc.name, pc.level, pc.health, pc.max_attack, pc.min_attack, pc.armour = pickle.load(save_pickle)
def save_clear():
try:
os.remove(os.path.abspath('save.pkl'))
except Exception:
pass
def yn(promt):
loop = True
print (promt)
while loop:
yn_output = input("Y/N: ").lower()
if yn_output == 'y':
loop = False
return True
elif yn_output == 'n':
loop = False
return False
else: print('Invalid input!')
def new_game():
global current_room, rooms, pc, enemy_count, health_pot_count, level_up_counter
enemy_count = 0
health_pot_count = 5
level_up_counter = 1
save_clear()
name = input("What is the name of are brave adventurer: ")
#pc stands for player character.
pc = Player(name)
current_room = 0
rooms = ['start_room']
start_room = World()
def start_up():
print('Welcome to text dungeon')
start_loop = True
while start_loop:
new_game_test = yn('Is this a new game.')
if new_game_test == True:
if yn('Are you sure, this will clear all saves.'):
start_loop = False
new_game()
elif new_game_test == False:
start_loop = False
get_save()
def new_room(room, current_room):
room.append(str(current_room) + '_room')
room[current_room] = World()
def fight():
increment_enemy_count()
print('Starting combat!')
enemys = []
enemys.append('{}_enemy'.format(enemy_count))
enemys[0] = Enemy()
combat = True
while combat:
space()
print(pc)
print('Enemy Stats- Health: {}, Max Attack: {}, Min Attack {}, Armour: {}'.format(enemys[0].health, enemys[0].max_attack, enemys[0].min_attack, enemys[0].armour))
print('What is your move.')
action_wait = True
while action_wait:
action = input('1 = attack, 2 = Use health potion: ')
if action == '1':
attack_damage = random.randint(pc.min_attack, pc.max_attack)
if attack_damage < enemys[0].armour:
print(f'You did 0 damage.')
else:
enemys[0].health = (int(enemys[0].health) + int(enemys[0].armour)) - attack_damage
print(f'You did {int(attack_damage) - int(enemys[0].armour)} damage.')
if enemys[0].health < 1:
combat = False
action_wait = False
elif action == '2':
if health_pot_count > 0:
health_pot_use()
action_wait = False
else:
print('You have none.')
else:
print('Try again')
attack_damage = random.randint(enemys[0].min_attack, enemys[0].max_attack)
if attack_damage < pc.armour:
print(f'You took 0 damage.')
else:
pc.health = int((pc.health) + int(pc.armour) - attack_damage)
print(f'You took {int(attack_damage) - int(pc.armour)} damage.')
if (pc.health < 1):
combat = False
death()
print('You win')
pc.level_up()
treasure()
def treasure():
global health_pot_count
loot = random.randint(1, 5)
if loot == 1:
print('You got a health potion!')
health_pot_count += 1
elif loot == 2:
print('You got a better weapon!')
pc.weapon_up()
elif loot == 3:
print('You got better armour!')
pc.armour_up()
else:
print('No Loot')
def death():
print('You lose')
save_clear()
main()
def increment_enemy_count():
global enemy_count
enemy_count += 1
def health_pot_use():
global health_pot_count
health_pot_count -= 1
health_back = pc.level * 2 + random.randint(-2, 6)
pc.health += health_back
print(f'You got {health_back} health.')
def space():
print()
print()
def main():
global pc, current_room, play
start_up()
save()
play = True
while play:
wait_for_action = True
while wait_for_action:
save_clear()
save()
space()
print(str(pc))
print('What do you want to do.')
action = input("1 = continue, 2 = Use health potion: ")
if action == '1':
play = False
current_room += 1
new_room(rooms, current_room)
rooms[current_room].enter_room()
elif action == '2':
play = False
if health_pot_count > 0:
health_pot_use()
else: print('You have none.')
else: print('Invalid action!')
if __name__ == '__main__':
main()
1 Answer 1
Decision loops
Your implementation of a decision loop is... interesting to say the least. If you're using python-3.8
, you can utilize the walrus operator. It's a way to assign variables within an expression. In this case, the while
loop. Have a look:
def choice(prompt: str) -> bool: # replaced "yn" with "choice" #
print(prompt)
while decision := input("Y/N: ").lower():
if decision in "yn":
return decision == "y"
print("Invalid input!")
Instead of just returning True
or False
, you can return the boolean expression that evaluates to a boolean. It's the same thing you're doing, but a lot simpler.
Type Hints
Using type hints help you and other people reading your code know what types of parameters are accepted and what types are returned by functions. If you look at the function above, you can see that prompt
is a str
, and the function returns a bool
value.
Dealing with exceptions
def save_clear():
try:
os.remove(os.path.abspath('save.pkl'))
except Exception:
pass
Passing on an exception isn't a good idea. An Exception is raised, and you're essentially ignoring it. I would print an error message to the console, such as "ERROR: File not found!"
or something related. Just to tell you whats wrong, instead of having an exception and not knowing what specifically is wrong. You should also try to catch specific exceptions if you can.
Consistency
You use three different ways to concatenate strings in your program. .format
, f""
and +
. Use one method and stick to it. I would recommend f""
because it allows you to directly include variables in your strings, rather than having to call a function (.format
) to add them.
Globals
It's not recommended to use globals. They can have unseen consequences, increase the complexity of your program, and can lead to spaghetti code. I would try to find a way to write this program without using globals.
Boolean Comparison
Instead of
if new_game_test == True:
do this
if new_game_test:
new_game_test
is a boolean value in itself, so you can just check that value.
Explore related questions
See similar questions with these tags.