Simulation of hero vs monster

I fairly started coding no more than 2 weeks now; not even for long, and a thread inspired me to make a random back-to-forth game between a Hero and a Monster where it randomly choose a certain number to decrease the health of either character and go back and forth until once below 0, the survivor wins. Since I am still very new, I haven't been able to use functions to my advantage to simplify and shorten my code, is there any advice or altercations you would add to the code?

How could one make this code more compact and simpler to run?

#Randomly generated battle between monster and hero
import random
import time

print('A monster ambushed you hero! Fight back!')
time.sleep(1.2)

hero = 100
monster = 100

chance1 = 1
chance2 = 2
chance3 = 3

while hero and monster > 0:
print('The hero strikes the monster!')
number1 = random.randint(1, 3)
if number1 == 1:
damage = random.randint(4, 10)
if number1 == 2:
damage = random.randint(11, 16)
if number1 == 3:
damage = random.randint(17, 25)
sum1 = monster - damage
monster = sum1
time.sleep(1.5)
print('Monster HP:' + str(sum1))
if monster <= 1:
print('Hero Wins!')
break
print('The monster attacks the hero!')
number2 = random.randint(1, 3)
if number2 == 1:
damage = random.randint(4, 10)
if number2 == 2:
damage = random.randint(11, 16)
if number2 == 3:
damage = random.randint(17, 25)
sum2 = hero - damage
hero = sum2
time.sleep(1.5)
print('Hero HP:' + str(sum2))
if hero <= 1:
print('Monster Wins!')
break


It may seem scrambled but it's my first small project and I'd like any feedback so I can expand my knowledge to fluently make similar commands.

• I guessed that you're using python, and added the appropriate tag for you. Commented Dec 15, 2022 at 7:59
• "How can I use functions to simplify my code" is exactly the question you should be considering as a beginner; keep doing that and it will serve you well in your coding journey! Commented Dec 15, 2022 at 23:44
• What version of Python are you targeting? Commented Dec 16, 2022 at 22:23

Reminds me of one of my first programs from a couple years ago. :)

Here is some basic, (hopefully) uncontroversial feedback.

Small bug: "while hero and monster > 0"

You are probably thinking "while hero and monster > 0" means while hero > 0 and monster > 0. It doesn't!

The operator and doesn't combine hero and monster such that > applies to both of them. What it actually does is check that two separate conditions are both true: 1) hero is True and 2) monster > 0 is True.

This could lead to unintended behavior when you run your program; In what may come as a surprise, hero is always True if it is an integer not equal to 0. With that said, you won't see this bug in your program, thanks to your if hero <= 1 statement. You should still want to clean it up though.

When I was first learning, I used to imagine parentheses around every condition in the statement. So I'd look at it as while (hero) and (monster > 0). You can actually do this in the code if it helps you.

Shortening: Eliminate redundant logic and avoid repeating yourself

If you think about it, the logic for inflicting damage can be simplified to random.randint(4,25). (Edit: As someone astutely observed the comments, I made a mistake here: Your logic is slightly different because it favors inflicting lower damage levels. To keep true to your logic, you would use the function below instead of random.randint(4,25)).

If you want to repeat logic in multiple places in a program, you may consider using functions to help make your code more understandable. To illustrate this while keeping your current code, you could add the following function to the top of your file, and then simplify your damage calculations to sum1 = monster - decide_attack_damage().

def decide_attack_damage():
number1 = random.randint(1, 3)
if number1 == 1:
damage = random.randint(4, 10)
if number1 == 2:
damage = random.randint(11, 16)
if number1 == 3:
damage = random.randint(17, 25)
return damage


Functions are great for breaking code logic into smaller, named parts. They are especially useful in large, complex programs.

Shortening: Eliminate variables you aren't using

Chance1, Chance2, and Chance3 aren't used, but they take up space: They are thus distracting.

If you want to remind yourself to use them in the future, you have the option of commenting them out by putting a # at the start of those lines.

Shortening: Eliminate the sum1, sum2 variables

These are an extra step. You can do either of the following:

monster = monster - damage


or

monster -= damage # Note: Check out *=, +=, and /= as well!


Either of these replacements make your code simpler.

Suggestion: Use elif

This doesn't make your code shorter, but it is something you'll probably want to know is an option. Most Python programmers would expect an elif where you've used your second and third if in each of the two if number == sections.

Demonstration

I'm not claiming this is the best way to write the program. It does, however, demonstrate several of my comments, while keeping your overall structure and style:

#Randomly generated battle between monster and hero
import random
import time

def decide_attack_damage():
'Returns an amount of damage.'
return random.randint(4,25)

print('A monster ambushed you hero! Fight back!')
time.sleep(1.2)
hero = 100
monster = 100

while hero > 0 and monster > 0:
print('The hero strikes the monster!')
monster -= decide_attack_damage()
time.sleep(1.5)
print('Monster HP: ' + str(monster))
if monster <= 1: # If monster's HP reaches 1 or less, hero wins
print('Hero Wins!')
break
print('The monster attacks the hero!')
hero -= decide_attack_damage()
time.sleep(1.5)
print('Hero HP: ' + str(hero))
if hero <= 1: # If hero's HP reaches 1 or less, monster wins
print('Monster Wins!')
break


Edit: Simplifying: while true

A comment below makes a good point: The hero > 0 and monster > 0 condition is redundant. The program will never evaluate hero > 0 and monster > 0 as False because the break condition within the loop always exits the loop as soon as hero <= 1 or monster <= 1.

A while (condition) statement's condition isn't useful if we never actually use it to exit the loop. If it isn't useful, it's redundant. To avoid this redundancy, Python provides a useful alternative: while True. If you replaced while hero > 0 and monster > 0 with while True in your code, it would behave exactly the same in practice.

Edit: Suggestion: Practice/Challenges

You might consider challenging yourself to add more features to your game. Here are some that I, personally, would find fun (or did, once!):

1. The possibility that attacks can randomly miss
2. The hero doesn't always attack first
3. Using the input() function to make the game interactive, letting users pick attacks or use turns to heal
4. Building a more complex model for attacks and healing using data structures like lists, dictionaries, tuples, or sets. Early in my learning, I did something similar using nested dictionaries (see bottom of the page).
• Your simplification of the damage calculation is actually not exactly equivalent. The ranges are not of equal size, so OP’s version is slightly biased towards smaller numbers compared to yours. Still though, if that’s OP’s intended behaviour, it’s better solved by weighting a distribution.
– Seb
Commented Dec 15, 2022 at 15:22
• Is there any point to while hero > 0 and monster > 0 instead of while true: if you check separately after each damage event whether you should break? Since those conditions aren't relevant to how the program actually works, it's probably better to simplify. In a while true loop, human readers will go looking for other exit conditions, without spending time wondering if the loop condition itself can ever become false. Commented Dec 15, 2022 at 20:39

How could one make this code more compact and simpler to run?

rh-calvin's answer gives some good advice on how to simplify this particular program; let me give you some general advice on how to achieve this laudable goal.

• Look for repeated code. That's an excellent candidate for moving to a function. Even better, if you feel the urge to cut-n-paste code, consider whether you ought to be pasting it into a function of its own instead of duplicating it.

The damage computation code is the most obvious example of duplication. Can you see others?

• Look for code that computes a value, particularly when the result is based on some other value. The "other value" is the argument to the function.

The damage computation code, again, is the most obvious example.

• Look for code that performs a "business task". The "business" of your program is the hero, the monster, the fight, the damage. The "mechanism" of your program is the variables, the additions, subtractions, random number generators and so on. Move the "mechanism" code into a function and give that function a "business" name. Now the main line of your program is easier to read because it is written in the jargon of the business, rather than a bunch of mechanisms.

The damage computation, again, is an obvious example, but we can go farther. I might write your program main loop like this:

...
while true:
hero_attacks_monster()