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

\$\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\$ Feb 25 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\$
    – PeakMars
    Feb 25 at 15:46
  • \$\begingroup\$ You should probably read about reducing cyclomatic complexity to reduce nesting: blog or video \$\endgroup\$
    – Cryptc
    Feb 26 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.

\$\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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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