Reputation: 13
I'm working on making a simple RPG battle system, with this function to decide what types of attacks the player will use. But I have had no luck getting my conditionals to return their designated string. All it ever returns is the word "None".
By the way I just started learning programming about a month ago, so I don't know very much about Python.
def attack_or_critical_attack_or_missed():
#Random number generated from a range and then placed into a list.
import random
number_list = [random.randrange(1, 105)]
print(number_list)
print("")
#Check list to see where my generated number gets put.
#Big Chance For Regular Attack
range1 = []
n = range(1, 76)
for n in n:
range1.append(n)
if range1 in number_list:
return "normal_attack"
#Small Chance For A Critical Attack
range2 = []
j = range(76, 101)
for j in j:
range2.append(j)
if range2 in number_list:
return "critical_attack"
#Tiny Chance For The Attack To Miss
range3 = []
v = range(101, 106)
for v in v:
range3.append(v)
if range3 in number_list:
return "missed_attack"
print(attack_or_critical_attack_or_missed())
Upvotes: 1
Views: 115
Reputation: 91139
You are missing the point of in
.
Let's take the 3rd part: you have a range3
which is filled in a quite weird way. Then you check if this list is contained in number_list
, which contains only one element, a number.
So this condition is probably never fulfilled.
What you probably mean is
randnum = random.randrange(1, 105) # no list, directly the number.
if randnum < 76:
return "normal_attack"
elif randnum < 101:
return "critical_attack"
else:
return "missed_attack"
Upvotes: 4
Reputation: 43497
Try this simplified version of your code:
def attack_or_critical_attack_or_missed():
import random
roll = random.randrange(1, 105)
if roll in range(1, 76):
return "normal_attack"
elif roll in range(76, 101):
return "critical_attack"
elif roll in range(101, 106):
return "missed_attack"
else:
raise RuntimeError('This code should not be reached')
print(attack_or_critical_attack_or_missed())
It might help you understand what you are doing wrong. Note that this is a simple refactoring of your original code, I didn't really add anything new except the exception, but I rearranged the way your code was laid out in order to make it simpler for you to understand.
Here is a more robust method:
import random
def perform_attack(rand_max=105, normal=range(1,76), critical=range(76,101), missed=range(101,106)):
roll = random.randrange(1, rand_max)
outcomes = {'normal': normal, 'critical': critical, 'missed': missed}
for outcome, field in outcomes.items():
if roll in field:
return outcome + '_attack'
raise RuntimeError('Roll was outside of any specified range, check parameters')
And here is a fool-proof method which can be adjusted very dynamically:
import random
def attack(**attack_table):
attack_table.setdefault('normal', 75)
attack_table.setdefault('critical', 25)
attack_table.setdefault('miss', 5)
max_roll = 0
outcomes = {}
for outcome, chance in attack_table.items():
outcomes[outcome] = range(max_roll, max_roll + chance)
max_roll += chance
roll = random.randrange(max_roll)
for outcome, threat_range in outcomes.items():
if roll in threat_range:
return outcome
Usage:
The function automatically builds the threat ranges for each type of attack based on the chance number you assign it, by default there is normal, critical, and miss, all with the same chance as you had for them. if I were to assign a new type of outcome, say "super" with a chance of 500, then it would have a very likely chance of being the result:
>>> attack(super=500)
'super'
I can even change the chance of the miss to be greater:
>>> attack(miss=200)
'miss'
Or even remove the miss chance all-together:
>>> attack(miss=0)
'critical' # lucky shot!
Upvotes: 5
Reputation: 11060
I would strongly advise you to refactor (aka completely change) your code:
Here's how I would start off:
def attack_or_critical_attack_or_missed():
#Random number generated from a range
import random
number = random.randint(1, 105)
print(number)
print()
if number < 76:
return "normal_attack"
elif 76 <= number < 101:
return "critical_attack"
# and so on
print(attack_or_critical_attack_or_missed())
Upvotes: 3
Reputation: 2751
Problem is number_list
contains only one integer. So "if range1 in number_list
" will never be true
and you will never return any string. Rather after checking all conditions it will return None
.
Upvotes: 2
Reputation: 19282
In essence you are getting None
because you haven't specified a return if the conditionals fail (i.e. an else).
Your code is morally equivalent to
def attack_or_critical_attack_or_missed():
if some_condition():
return "something"
#else
# implicitly return None
Why is this happening? You have replaced variables when you try to walk over them, e.g.
n = range(1, 76)
for n in n:
# ... etc
You could avoid this by saying
for n in range(1, 76):
# ... etc
though other answers have given much neater ways of changing the code. You also need to change the way you check if a specific number, rather than a list, is in
another list.
Upvotes: 1