1
\$\begingroup\$

I am writing a simple text adventure in python that will eventually have the capability of running other text adventures within it, but my code is all over the place and the UI is terrible. What are your recommendations for improving the UI and cleaning this up

To clarify: I will continue using print statements for the UI, and do not plan on switching to ncurses, etc.

Code is below:

 
#Import modules
import pickle
import random
import time
#Take Username Input
username = input('Username: ')
#Load savefile
try:
 with open(username, 'rb') as f:
 data = pickle.load(f)
except FileNotFoundError:
 print('User not found. Creating user \'' + username + '\' ...')
 data = {'inventory_fish': 0, 'inventory_wood': 0,
 'inventory_tomatoes': 0, 'inventory_battle_droid': 0,
 'inventory_droid_parts': 0, 'inventory_power_cell': 0,
 'inventory_gold': 30, 'inventory_mining_droid': 0,
 'inventory_tomato_seeds': 0, 'boatname': 'none', 'gametime': 0,
 'inventory_arena_box_I': 0, 'inventory_arena_box_II': 0, 'inventory_morkydV1_box_I': 0,
 'inventory_morkydV1_box_II': 0}
#Load Inventory
inventory_fish = data['inventory_fish']
#inventory_fish = int(inventory_fish)
inventory_wood = data['inventory_wood']
#inventory_wood = int(inventory_wood)
inventory_tomatoes = data['inventory_tomatoes']
#inventory_tomatoes = int(inventory_tomatoes)
inventory_battle_droid = data['inventory_battle_droid']
inventory_droid_parts = data['inventory_droid_parts']
inventory_power_cell = data['inventory_power_cell']
inventory_gold = data['inventory_gold']
inventory_mining_droid = data['inventory_mining_droid']
inventory_tomato_seeds = data['inventory_tomato_seeds']
boatname = data['boatname']
gametime = data['gametime']
inventory_arena_box_I = data['inventory_arena_box_I']
inventory_arena_box_II = data['inventory_arena_box_II']
inventory_morkydV1_box_I = data['inventory_morkydV1_box_I']
inventory_morkydV1_box_II = data['inventory_morkydV1_box_II']
###########################################################
#HOME ISLAND
###########################################################
def home_island_menu():
 while True:
 print("|\n|\nYou are at your home Island\n|\n|")
 global temp_input
 temp_input = input("Enter \'forest\' to chop wood in the forest\nEnter \'docks\' to go to your docks\nEnter \'buildings\' to go to your buildings menu\nEnter \'inventory\' or \'i\' to see your inventory\nEnter \'boxes\' to go to your boxes menu\nEnter \'quit\' to exit the game\n").lower()
 if temp_input == "forest":
 print("You walk to the forest")
 break
 elif temp_input == "docks":
 print("You walk over to the dock")
 break
 elif temp_input == "buildings":
 print("The buildings menu will be coming in a future update")
 elif temp_input == "quit":
 print("The game will now save and quit. Goodbye, " + str(username))
 break
 elif temp_input == 'inventory' or temp_input == 'i':
 temp_inventoryquitter = ''
 while temp_inventoryquitter != 'return':
 temp_inventoryquitter = input(inventory_displayer)
 elif temp_input == 'boxes':
 print('Opening boxes menu...\n\n')
 break
 else:
 print(str(temp_input) + " is not a valid option.")
def home_island_dock_menu():
 while True:
 print("|\n|\nYou are at your home island dock\n|\n|")
 global temp_input_dock_menu
 temp_input_dock_menu = input("Enter \'fishing\' to start fishing\nEnter \'boats\' to go to your boats menu\nEnter \'home\' to return to your home island menu").lower()
 if temp_input_dock_menu == "fishing":
 print("You take out your fishing gear")
 break
 elif temp_input_dock_menu == "boats":
 print("Entering boats menu")
 break
 elif temp_input_dock_menu == "home":
 print("Returning to home island menu")
 break
 else:
 print(str(temp_input_dock_menu) + " is not a valid option.")
def home_island_forest():
 global gametime
 global wood_pile
 wood_pile = 0
 woodcutting_time = int(input("Chopping wood earns 1 wood per 10 seconds.\nHow much wood would you like to gather?\n(Once you start chopping wood, you cannot stop until the amount you chose has been acquired)\n"))
 try:
 verify_woodcutting = int(input("You want to chop " + str(woodcutting_time) + " wood. This will take you " + str(woodcutting_time * 10) + " seconds.\nEnter 1 to continue, or 0 to change your mind\n"))
 except ValueError:
 verify_woodcutting = 0
 if verify_woodcutting == 1:
 for x in range (woodcutting_time):
 #Print ascii woodcutting image
 print("+1 wood")
 wood_pile += 1
 gametime += 10
 else:
 print("You changed your mind. You are now leaving the forest")
def home_island_dock_fishing():
 global fishing_bucket
 global gametime
 fishing_bucket = 0
 fishing_time = int(input("Chopping wood earns 1 fish per 15 seconds.\nHow many fish would you like to catch?\n(Once you start fishing, you cannot stop until the amount you chose has been acquired)\n"))
 try:
 verify_fishing = int(input("You want to catch " + str(fishing_time) + " fish. This will take you " + str(fishing_time * 15) + " seconds.\nEnter 1 to continue, or 0 to change your mind\n"))
 except ValueError:
 verify_fishing = 0
 if verify_fishing == 1:
 for x in range (fishing_time):
 #Print ascii fishing image
 gametime += 15
 print("+1 fish")
 fishing_bucket += 1
 else:
 print("You changed your mind. You are now walking home.")
def BoatsMenu():
 global gametime
 global sail_to_where
 while True:
 global boatname
 global inventory_wood
 global inventory_fish
 global temp_input_dock_menu
 if boatname == 'none':
 print("You don't have a boat! Would you like to build one?")
 try:
 build_new_boat = int(input('Enter 1 to build yourself a boat, or 0 to return to the home island menu\n'))
 except ValueError:
 build_new_boat = 0
 if build_new_boat == 1:
 print("Building a boat costs 18 wood and 12 fish, and takes an hour to build.")
 print("You have" + str(inventory_wood) + " wood, and " + str(inventory_fish) + " fish.")
 if inventory_wood >= 18 and inventory_fish >= 12:
 print("After building a boat, you will have " + str(inventory_wood - 18) + " wood left, and " + str(inventory_fish - 12) + " fish left")
 try:
 build_new_boat = int(input("Enter 1 to build a boat: "))
 except ValueError:
 build_new_boat = 0
 print("You need to type 1 to build a boat")
 if build_new_boat == 1:
 inventory_wood -= 18
 inventory_fish -= 12
 boatname = str(input("What do you want to call your new boat?\n"))
 print("Construction complete. Enjoy your new boat: " + boatname)
 gametime += 3600
 else:
 print("You changed your mind. Boat construction cancelled.")
 break
 else:
 print("You don't have enough wood and fish to build a boat! And no, you can't ask why fish are used in the construction of boats!")
 break
 else:
 print("You changed your mind. Boat construction cancelled")
 break
 if boatname != 'none':
 print("Your boat, " + str(boatname) + ", is ready to set sail.")
 sail_to_where = input("Where would you like to go?\nEnter \'market\' to go to the trade market\nEnter \'arena\' to go to the arena\nEnter \'home\' to go home")
 if sail_to_where == 'market':
 print("The market will be released in a future update.")
 break
 if sail_to_where == 'arena':
 print("Arena will be implemented as soon as this menu is fully made.")
 temp_input_dock_menu = 'arena'
 break
 if sail_to_where == 'home':
 print("Returning home...")
 temp_input_dock_menu = ''
 break
def arena():
 while True:
 #Temporary player health setting
 player_health = 80
 global inventory_gold
 global inventory_arena_box_I
 global username
 try:
 which_stage = int(input("Welcome to the arena, " + str(username) + ". Which stage would you like to take on?\nStages:\nStage 1 (enter 1)\nFurther stages are not avalible yet\n\t\tEnter the number of the stage you wish to take on: "))
 except ValueError:
 which_stage = 0
 if which_stage == 1:
 try:
 entering_the_arena = int(input("To enter the arena, you must pay six gold. You have " + str(inventory_gold) + " gold. To pay and enter the arena, enter \'1\': "))
 except ValueError:
 entering_the_arena = 0
 if entering_the_arena == 1:
 if inventory_gold >= 6:
 inventory_gold -= 6
 print("Entering the arena...")
 time.sleep(0.3)
 print("\n\n\n\n\n\n\n\n\n\ndrip")
 time.sleep(0.6)
 print("\n\n\ndrip")
 time.sleep(0.2)
 print("\ndrip")
 time.sleep(1)
 print("You are in a cold, damp room, lit by the light coming through the bars of a metal gate.")
 #ASCII art of the gate, perhaps?
 time.sleep(4)
 print("On the other side of the gate is an arena, the arena where you will soon be fighting")
 time.sleep(3)
 print("With an awful sound, the gate retracts upwards")
 time.sleep(1)
 print("You step out into the arena, and are blinded by the bright sun")
 print("You throw up your arms to shield your eyes")
 print("The crowd seated around the pit cheers as you walk to the middle of the arena")
 print("You hear another awful sound and know your opponent's gate must be opening now")
 time.sleep(4)
 print("When you\'ve adjusted to the light of the arena, you drop your arms and look at your opponent.")
 print("You laugh when you see a small droid with a bag of seeds in hand, and a peice of straw where it's mouth would likely be, were it not a droid.")
 time.sleep(1)
 print("The gardening droid tips it\'s hat to you, and flies back to it's side of the arena.\nYou nod and return to yours")
 time.sleep(1)
 print("A horn sounds. The fight has begun.")
 time.sleep(0.5)
 print("The droid flies towards you as fast as it can. Novice mistake. It's given you the opportunity of the first strike.")
 print("Combat has now begun. The first combatant to have less than 10 health wins.")
 opponent_pos = 1
 player_pos = 1
 opponent_health = 60
 arena_prize = 0
 in_combat = True
 while in_combat == True:
 print("\n\nYou have " + str(player_health) + " health")
 print("\nGardening droid has " + str(opponent_health) + " health\n\n")
 damage_dealt_this_turn = 0
 damage_taken_this_turn = 0
 try:
 strike = int(input("Enter 1 to slap your opponent\nEnter 2 to punch your opponent\nEnter 3 to kick your opponent\nEnter 4 to bite your opponent\nEnter 5 to rip/tear your opponent's circuits\n: "))
 except ValueError:
 strike = random.randint(1, 5)
 print("Invalid input. Your move is being chosen for you.")
 if strike != 1 and strike != 2 and strike != 3 and strike != 4 and strike != 5:
 strike = random.randint(1, 5)
 print("Invalid input. Your move is being chosen for you.")
 if strike == 1:
 print("You slap your opponent")
 damage_dealt_this_turn += 2
 if opponent_pos == 1:
 damage_dealt_this_turn += 2
 player_pos = 2
 print("Your slap does " + str(damage_dealt_this_turn) + " damage")
 elif strike == 2:
 print("You punch your opponent")
 damage_dealt_this_turn += 3
 if opponent_pos == 3 and player_pos != 3:
 damage_dealt_this_turn += 3
 player_pos = 3
 print("Your punch does " + str(damage_dealt_this_turn) + " damage")
 elif strike == 3:
 print("You kick your opponent")
 damage_dealt_this_turn += 3
 print("Your kick does " + str(damage_dealt_this_turn) + " damage")
 elif strike == 4:
 print("You bite your opponent")
 damage_dealt_this_turn += 4
 damage_taken_this_turn += 1
 print("Your bite does " + str(damage_dealt_this_turn) + " damage to your opponent, and " + str(damage_taken_this_turn) + " damage to you")
 elif strike == 5:
 print("You rip/tear your opponent\'s circuits")
 chance_roll = random.randint(1, 10)
 if chance_roll <= 40:
 damage_dealt_this_turn += 6
 else:
 damage_dealt_this_turn += 1
 player_pos = 1
 print("Your rip/tear does " + str(damage_dealt_this_turn) + " damage")
 opponent_strike = random.randint(1, 5)
 if opponent_strike == 1:
 print("Your opponent slaps you")
 damage_taken_this_turn += 2
 if player_pos == 1:
 damage_taken_this_turn += 2
 opponent_pos = 2
 elif opponent_strike == 2:
 print("Your opponent punches you")
 damage_taken_this_turn += 3
 if player_pos == 3 and opponent_pos != 3:
 damage_taken_this_turn += 3
 opponent_pos = 3
 elif opponent_strike == 3:
 print("Your opponent kicks you")
 damage_taken_this_turn += 3
 elif opponent_strike == 4:
 print("Your opponent bites you")
 damage_taken_this_turn += 4
 damage_dealt_this_turn += 1
 elif opponent_strike == 5:
 print("Your opponent tears at your arm")
 chance_roll = random.randint(1, 10)
 if chance_roll <= 40:
 damage_taken_this_turn += 6
 else:
 damage_taken_this_turn += 1
 opponent_pos = 1
 opponent_health -= damage_dealt_this_turn
 player_health -= damage_taken_this_turn
 print("You took " + str(damage_taken_this_turn) + " damage this turn.")
 print("Your opponent took " + str(damage_dealt_this_turn) + " damage this turn.")
 if player_health < 10:
 print("You lost!")
 arena_prize = 0
 in_combat = False
 if opponent_health < 10:
 print("You won! Your prize is: 1 \'Arena box tier I\'")
 arena_prize = 1
 in_combat = False
 if arena_prize > 0:
 for x in range(arena_prize):
 inventory_arena_box_I += 1
 arena_prize -= 1
 sail_to_where = ''
 break
 else:
 print("You don't have enough gold. Aborting")
 break
 else:
 print("You didn't enter 1. Aborting")
 break
 else:
 print("You didn't enter 1. Aborting")
 break
def boxes_menu():
 global inventory_arena_box_I
 global inventory_arena_box_II
 global inventory_morkydV1_box_I
 global inventory_morkydV1_box_II
 global inventory_gold
 global inventory_wood
 global inventory_power_cell
 global inventory_droid_parts
 global inventory_mining_droid
 global inventory_battle_droid
 while True:
 print("You are in the boxes menu. Skip opening boxes to leave.\n You have the following boxes: ")
 print("Arena Box I: " + str(inventory_arena_box_I))
 print("Arena Box II: " + str(inventory_arena_box_II))
 print("MorkydV1 Box I: " + str(inventory_morkydV1_box_I))
 print("MorkydV1 Box II: " + str(inventory_morkydV1_box_II))
 if inventory_arena_box_I > 0:
 opening_arena_I = True
 while opening_arena_I is True:
 try:
 open_arena_I = int(input("You have " + str(inventory_arena_box_I) + " boxes of this type.\nDo you wish to open \'Arena Box I\'?\nEnter \'1\' to open the box.\t\t"))
 except ValueError:
 print("You must enter 1 to open a box, or 0 to cancel\n")
 if open_arena_I == 1:
 inventory_arena_box_I -= 1
 chance1 = random.randint(1, 1000)
 if chance1 == 1:
 inventory_mining_droid += 1
 print("+1 mining droid! (0.1% chance)")
 chance2 = random.randint(1, 10)
 if chance2 != 10:
 inventory_droid_parts += 2
 print("+2 droid parts! (90% chance)")
 chance3 = random.randint(1, 10)
 if chance3 <= 2:
 inventory_wood += 16
 print("+16 wood! (20% chance)")
 chance4 = random.randint(1, 10)
 if chance4 <= 9:
 inventory_gold += 4
 print("+4 gold! (90% chance)")
 if chance4 <= 6:
 inventory_gold += 2
 print("+2 gold! (60% chance)")
 if chance4 <= 2:
 inventory_gold += 6
 print("+6 gold! (20% chance)")
 chance5 = random.randint(1, 20)
 if chance5 == 1:
 inventory_power_cell += 2
 print("+2 power cell! (5% chance)")
 chance6 = random.randint(1, 100)
 if chance6 == 1:
 inventory_battle_droid += 1
 print("+1 battle droid! (1% chance)")
 else:
 print("Skipping Arena Box 1...\n")
 opening_arena_I = False
 if inventory_arena_box_I <= 0:
 opening_arena_I = False
 if inventory_arena_box_II > 0:
 opening_arena_II = True
 while opening_arena_II is True:
 try:
 open_arena_II = int(input("You have " + str(inventory_arena_box_II) + " boxes of this type.\nDo you wish to open \'Arena Box II\'?\nEnter \'1\' to open the box.\t\t"))
 except ValueError:
 print("You must enter 1 to open a box, or 0 to cancel\n")
 if open_arena_II == 1:
 inventory_arena_box_II -= 1
 #Loot Table Here
 print("Missing Loot Table for inventory_arena_box_II")
 else:
 print("Skipping Arena Box 2...\n")
 opening_arena_II = False
 if inventory_arena_box_II <= 0:
 opening_arena_II = False
 if inventory_morkydV1_box_I > 0:
 opening_morkydV1_I = True
 while opening_morkydV1_I is True:
 try:
 open_morkydV1_I = int(input("You have " + str(inventory_morkydV1_box_I) + " boxes of this type.\nDo you wish to open \'MorkydV1 Box I\'?\nEnter \'1\' to open the box.\t\t"))
 except ValueError:
 print("You must enter 1 to open a box, or 0 to cancel\n")
 if open_morkydV1_I == 1:
 inventory_morkydV1_box_I -= 1
 #Loot Table Here
 print("Missing Loot Table for inventory_morkydV1_box_I")
 else:
 print("Skipping MorkydV1 Box 1...\n")
 opening_morkydV1_I = False
 if inventory_morkydV1_box_I <= 0:
 opening_morkydV1_I = False
 if inventory_morkydV1_box_II > 0:
 opening_morkydV1_II = True
 while opening_morkydV1_II is True:
 try:
 open_morkydV1_II = int(input("You have " + str(inventory_morkydV1_box_II) + " boxes of this type.\nDo you wish to open \'MorkydV1 Box II\'?\nEnter \'1\' to open the box.\t\t"))
 except ValueError:
 print("You must enter 1 to open a box, or 0 to cancel\n")
 if open_morkydV1_II == 1:
 inventory_morkydV1_box_II -= 1
 #Loot Table Here
 print("Missing Loot Table for inventory_morkydV1_box_II")
 else:
 print("Skipping MorkydV1 Box 2...\n")
 opening_morkydV1_II = False
 if inventory_morkydV1_box_II <= 0:
 opening_morkydV1_II = False
 print("Exiting Menu")
 time.sleep(3)
 break
#Game Loop
game_running = True
while game_running == True:
 #Inventory Displayer Updates
 inventory_displayer = (
 "--------------------------\n| fish: \t\t\t" + str(inventory_fish) + "\n| wood: \t\t\t" + str(
 inventory_wood) + "\n| tomatoes: \t\t" + str(inventory_tomatoes) + "\n| battle droids: \t" + str(
 inventory_battle_droid) + "\n| droid parts: \t\t" + str(
 inventory_droid_parts) + "\n| power cells: \t\t" + str(inventory_power_cell) + "\n| gold: \t\t\t" + str(
 inventory_gold) + "\n| mining droids: \t" + str(inventory_mining_droid) + "\n| boxes: \t" + str(
 inventory_arena_box_I + inventory_arena_box_II + inventory_morkydV1_box_II + inventory_morkydV1_box_I) + "\n| tomato seeds: \t" + str(
 inventory_tomato_seeds) + "\n--------------------------\n|Your player's game time is: " + str(
 gametime) + "\nEnter \'return\' to return home\n")
 #Main Game
 home_island_menu()
 if temp_input == "forest":
 home_island_forest()
 inventory_wood += wood_pile
 wood_pile = 0
 if temp_input =="docks":
 home_island_dock_menu()
 if temp_input_dock_menu == "fishing":
 home_island_dock_fishing()
 print("Fishing complete. You caught " + str(fishing_bucket) + " fish. You are now walking home.")
 inventory_fish += fishing_bucket
 fishing_bucket = 0
 if temp_input_dock_menu == 'boats':
 BoatsMenu()
 if temp_input_dock_menu == "home":
 temp_input = ''
 temp_input_dock_menu = ''
 if temp_input_dock_menu == 'arena':
 arena()
 if temp_input == 'boxes':
 boxes_menu()
 if temp_input == "quit":
 game_running = False
#END OF MAIN FILE
data = {'inventory_fish': inventory_fish, 'inventory_wood': inventory_wood, 'inventory_tomatoes': inventory_tomatoes, 'inventory_battle_droid': inventory_battle_droid, 'inventory_droid_parts': inventory_droid_parts, 'inventory_power_cell': inventory_power_cell, 'inventory_gold': inventory_gold, 'inventory_mining_droid': inventory_mining_droid, 'inventory_tomato_seeds': inventory_tomato_seeds, 'boatname': boatname, 'gametime': gametime, 'inventory_arena_box_I': inventory_arena_box_I, 'inventory_arena_box_II': inventory_arena_box_II, 'inventory_morkydV1_box_I': inventory_morkydV1_box_I, 'inventory_morkydV1_box_II': inventory_morkydV1_box_II}
with open(username, 'wb') as f:
 pickle.dump(data, f)

basically I'm looking for a way to have an ncurses-like ui while keeping the simplicity I have now

asked Feb 25, 2023 at 15:16
\$\endgroup\$
3
  • 1
    \$\begingroup\$ We can review what you have, but that seems a bit pointless if you plan to move to a different UI (curses or similar). \$\endgroup\$ Commented Feb 25, 2023 at 15:41
  • \$\begingroup\$ I would rather keep the current print-based UI, if possible, but I don't know how to make it look good enough to use. It's currently a mess, but I don't intend on using a different UI, just optimizing what is already there. \$\endgroup\$ Commented Feb 25, 2023 at 15:46
  • \$\begingroup\$ You should probably read about reducing cyclomatic complexity to reduce nesting: blog or video \$\endgroup\$ Commented Feb 26, 2023 at 14:54

2 Answers 2

2
\$\begingroup\$

Delete comments like this:

#Import modules

since they're obvious and don't clarify the code beyond what someone can understand by just reading the code.

Replace expressions like this:

'User not found. Creating user \'' + username + '\' ...

with interpolated f-strings:

f"User not found. Creating user '{username}'..."

Replace your initialisation of data with a call to dict.fromkeys.

Do not unpack your data to individual variables like inventory_battle_droid. Since there should be no "special logic" between items, there shouldn't be any special variables, either.

Don't make temp_input a global. In fact, all of your globals should go away and you should use more robust state management, potentially a game state class but there are other solutions.

Refactor loops like this:

 temp_inventoryquitter = ''
 while temp_inventoryquitter != 'return':
 temp_inventoryquitter = input(inventory_displayer)

so that no variables are used:

while input(inventory_displayer) != 'return':
 pass

Get a PEP8 linter and formatter. Among other failure, you need two empty lines between functions, and your functions such as BoatsMenu need to be renamed to be snake_case, i.e. boats_menu.

Since you don't use this iteration variable x:

for x in range (fishing_time):

replace it with _.

In print statements like this:

+1 battle droid! (1% chance)

don't hard-code the +1, and don't hard-code the chance. Derive those from variables that are defined in one place and used for both printing and logic.

answered Feb 26, 2023 at 14:13
\$\endgroup\$
1
\$\begingroup\$

The format of this question currently does not fit well on Code Review, we don't answer how to questions (you might want to read our help pages).

This is general advice since I don't know python.

Right now most of your code is mixed together with both logic and display being co-located. This makes the code very complex. One of the key parts of programming is to keep breaking problems up into smaller and smaller parts until each part is easy to solve.

Games generally benefit by being object oriented. Most software benefits by separating the display of the data from the logic of the program. There are design patterns for this, one of these design patterns is the Model View Controller (MVC) design pattern. Another is the Model View View Model (MVVM) design pattern.

An object that you may want to have is the user object.

answered Feb 25, 2023 at 17:17
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.