Multiple changes to the same variable within different if statements

Question:

SOLVED: I read through my code, it was a ‘bug’. When I copied the dice roll method from the ‘player character’, since it uses the same mechanics for the enemies, I set the damage to 0 if it rolls with one die on accident.

Beginner here. (Python crash course halfway of chapter 9)

I am trying to build a simple turn based text game to practice (classes,if statement, modifying dictionaries/lists etc).

I will copy two snippets from my code, so you can understand my problem better.

(I’m really sorry that I can’t give a short description, my best try was the title, but that still doesn’t make it good enough. If you want an abridged tldr, go to the bottom with the bold texts.)

First, I have two characters, that you can choose from as an if-elif-else statement.

I used the same "player_xy" (xy being like health, damage etc) for the two characters, but assigning different values to them based on the player’s choice. (My reasoning being is so I only have to reference the same variable in the code later in the battle system, making my job easier.)

(The variables fighter_max_hp.. etc are defined earlier, but it doesn’t matter (tried moving it to before/inside the if statements.)

while select_repeat == True:
    print("Type 'f' for fighter , 'm' for mage, or 'q' to quit!")
    character = input("TYPE: ")
    #player chooses fighter
    if character == 'f':
        player_max_hp = fighter_max_hp
        player_max_mana = fighter_max_mana
        #this goes on for a while, setting up all the stats
    #player chooses mage
    elif character == 'm':
        player_max_hp = mage_max_hp
        player_max_mana = mage_max_mana
        #this goes on for a while, setting up all the stats
    #player chooses to quit
    elif character == 'q':
        select_repeat = False
    #invalid input
    else:
        print("nPlease choose a valid option!")

Later in the code, I have a part where a randomizer sets up enemies to fight.

I used the same "enemy_xy" (xy being like health, damage etc) for the enemies. (My reasoning was the same here as for the characters.)

(Same, as with the player variables (tried moving it to before/inside the if statements.)

while enemy_select == True:
    #game chooses an enemy to fight!
    min = 1
    max = 3
    enemy_chooser = int(random.randint(min, max))
    if enemy_chooser == 1:
        #choose werewolf
        enemy_hp = werewolf_hp
        enemy_dice = werewolf_dice
        #this goes on for a while, setting up all the stats
    if enemy_chooser == 2:
        #choose lesser mimic
        enemy_hp = int(player_max_hp / 2)
        enemy_dice = player_dice
    elif enemy_chooser == 3:
        #choose zombie
        enemy_hp = zombie_hp
        enemy_dice = zombie_dice
        #this goes on for a while, setting up all the stats

Keep in mind, all of these enemies use the same "enemy_hp", "enemy_dice" etc. variables, within the same battle system, just assigned as "enemy_hp = werewolf_hp" or "enemy_hp = "zombie_hp".

The fight happens, and:

  • If your enemy is the werewolf:

    • you deal damage to it
    • you receive damage from it
    • you can kill it
    • you can get killed by it
  • If your enemy is the lesser mimic:

    • you deal damage to it
    • you can ONLY receive damage from it if you are a fighter (mage’s hp doesn’t decrease)
    • you can kill it
    • you can ONLY get killed by it if you are a fighter (obviously, since it doesn’t deal damage to mage hp)
  • If your enemy is the zombie:

    • you deal damage to it
    • you CAN NOT receive damage from it (not the fighter, or the mage)
    • you can kill it
    • you can not get killed by it (obviously, since no damage)

Otherwise, it prints out the different variable values as assigned (different stats for each monster) as expected, and it uses correct calculations to deal damage.. it just can’t in the two cases mentioned above.

Now comes the main part of my question…

If I change the variables like this:

elif enemy_chooser == 2:
    #choose zombie
    enemy_hp = werewolf_hp ##CHANGE
    enemy_dice = werewolf_dice ##CHANGE
    #this goes on for a while, setting up all the stats

Then the zombie can finally deal damage to the player (with the werewolf’s stats).

It’s as if because the lines

enemy_hp = werewolf_hp
enemy_dice = werewolf_dice
#etc

are written earlier than:

enemy_hp = zombie_hp
enemy_dice = zombie_dice
#etc

it somehow effects the variable (regardless or not if the "if" statement is true).

because werewolf_xy was defined earlier than zombie_xy

#enemy werewolf defined first in the code
werewolf_hp = 20
werewolf_dice = 2
#etc
#enemy zombie defined right after
zombie_hp = 35
zombie_dice = 1
#etc

Same happens with the fighter and mage selection.
Somehow the player_hp = xy_hp only works if xy = fighter, because the fighters variables are defined earlier in the code, and thus making the "lesser mimic" deal damage only to the fighter.

My question is "simply".. why?

I tried everything in my power, to no avail.

As you have seen, I could identify what causes the problem (and thus I >could< potentionally work around it), but I still don’t know why Python does what it does, and that bothers me.

Any help or input from more experienced users would be greatly appreciated.

Thank you in advance!

Tankerka

Asked By: Tankerka

||

Answers:

You have a bug.

There’s not enough details in this (long!) narrative to identify the bug.

Here’s how you fix it:

breakpoint()

Put that near the top of your code,
and use n next, plus p print var,
to see what your code is doing.
It is quicker and more flexible than print( ... ).

Read up on that pair of commands here:

https://docs.python.org/3/library/pdb.html


Separate item: refactor your code as you go along.

You’re starting to have enough if / elif logic
that it won’t all fit in a single screenful
with no scrolling.
That suggests that it’s a good time to use def
to break out a helper function.

You might def get_enemy_hp( ... ):, for example,
and also define get_enemy_dice().

Other things you might choose to study:

  • a class can be a good way to organize the variables you’re defining — embrace the self syntax!
  • a dict could map from enemy type to hp, or to dice

The nice thing about helper functions is they’re
the perfect target for unit tests.
It takes a minute to write a test, but it winds up saving you time.

https://docs.python.org/3/library/unittest.html


When you identify and fix the problem, let us know!

https://stackoverflow.com/help/self-answer

Answered By: J_H