Having trouble getting my function to meet while loop condition in Python - python

my goal is to:
Implement the roll_dice function in hog.py. It takes two arguments: a positive integer called num_rolls (which is the number of dice rolls that will be made) and a dice function (A function that simulates a single dice roll outcome). It returns the number of points scored by rolling the dice that number of times in a turn: either the sum of the outcomes or 1.
It returns 1 when any of the dice outcomes is a 1.
When I debug in PyCharm, it says that current_roll = 2, current_value = 4, num_rolls = 5 and total_sum = 4. This leads me to believe that it only went through the loop once since the current_roll should be 5 since the num_rolls is 5. Also, because the total_sum is only the the current_value. I've tried moving around of the variable assignments but nothing has worked.
Could anyone give me any guidance as to why my function is not looping until current_roll is not < num_rolls? Sorry if this is not clear I am a beginner learner. I can offer any clarification
def roll_dice(num_rolls, dice=six_sided):
current_roll = 1
total_sum = 0
current_value = dice()
while current_roll < num_rolls:
current_roll += 1
if current_value == 1 or total_sum == 1:
return total_sum == 1
else:
total_sum = total_sum + current_value
return total_sum
this is what my code is updated to now:
total_sum = 0
current_roll = 0
while current_roll < num_rolls:
current_roll += 1
current_value = dice()
if current_value == 1 or total_sum == 1:
total_sum = 1
return total_sum
else:
total_sum += current_value
return total_sum
I updated my code, but it is still not passing. I added the dice() function into the while loop and changed the total sum = 1. Do you have any idea why it is still not passing?

You are not even using your dice function within the loop. Try to rewrite your code in this way:
def roll_dice(num_rolls, dice=six_sided):
total_sum = 0
rolled_one = False
# roll a dice num_rolls times
for current_roll in range(num_rolls):
# roll a dice
current_value = dice()
# if value=1 then remember what happened
if current_value == 1:
rolled_one = True
# add the value to the sum
total_sum += current_value
# if you previously rolled a 1 then return 1
if rolled_one:
return 1
# otherwise return the final sum
else:
return total_sum

You are returning inside the while loop,
this will terminate the loop.
move your return statement outside the while loop
Edit:
Move the dice function call within the while loop.
In the current state of your code it will only roll the dice once, and then check that roll num_rolls times.
also you are returning total_sum == 1 this will return True or False not sure if this is intended

Related

Python coin flip with functions

I need to create a python program that will use various functions to simulate flipping a coin 100 times and finding the largest streak of "H"'s out of 10,000 tries. I am stuck on how to finish the def main() function, specifically the counter. I also don't know if my program is calculating the streaks correctly.
def flipCoin() - returns 'H' or 'T' with the same probability as a coin.
def simulate(numFlips) - simulates flipping a coin numFlips(100) times. This function returns a list of length numFlips containing H's and T's.
def countStreak(flips_list) - iterates through the flips list passed to it and counts streaks of 'H's and returns the largest streak it finds. Keep track of the current number of heads and the current largest streak of heads in two separate variables.
As you iterate through the list, keep track of the current number of heads you've seen in a row. If you see a tail, check if the current streak of heads is larger than your current longest streak. If so, save the current streak. Then reset your heads counter.
In the main function, write a testing loop that simulates the process 10000 times.
Keep track of the current largest streak of heads and display this result after the test loop completes.
# import statements
import random
# function defintions
def flip():
coin = random.randint(0, 1)
if coin == 0:
return "H"
else:
return "T"
def simulate(num_flips):
# simulates numFlips coin flips
# returns a list of the flips
numFlips = []
for i in range(100):
numFlips.append(flip())
return numFlips
def countStreak(flips_list):
# iterates through the 'flips' list
# returns number of 'H's
count = 0
maxCount = 0
flips_list = simulate()
for i in flips_list:
if i == "H":
count += 1
if count > maxCount:
maxCount = count
else:
count = 0
return maxCount
def main():
for j in range(10000):
trial = simulate(100)
coinFlip = countStreak(1)
# need something here to track count of streaks for "H"
print("The longest streak of heads was " + str(coinFlip) +".")
if __name__ == "__main__":
main()
So there was a flaw in your code, you were running simulate() function 10000 times. But actually, you had to run it once, but return a list of 10000 items. Also, you need not check the streak every time so the check_streak() need to be out of the loop and we need to pass the result obtained from simulate(10000) into it.
Correct Code:
# import statements
import random
# function defintions
def flip():
coin = random.randint(0, 1) # better option would be to use random.choice()
if coin == 0:
return "H"
else:
return "T"
def simulate(num):
# simulates numFlips coin flips
# returns a list of the flips
numFlips = []
for i in range(num): # this needs to run num times
numFlips.append(flip())
return numFlips
def countStreak(flips_list):
# iterates through the 'flips' list
# returns number of 'H's
count = 0
maxCount = 0
for i in flips_list:
if i == "H":
count += 1
if count > maxCount:
maxCount = count
else:
count = 0
return maxCount
def main():
trial = []
for j in range(10000):
temp2 = simulate(100) # SImulate 10000 coin flips
coinFlip = countStreak(temp2) # Check streak of variable trial
trial.append(coinFlip)
# need something here to track count of streaks for "H"
# print(trial)
print("The longest streak of heads was " + str(max(trial)) +".")
if __name__ == "__main__":
main()
This Part is Optional, For optimisation
Though the logic isn't wrong, you need not make the list 1st and then check streak, you can simply check it together, it will take less time and space.
Also, your logic is correct, but this one would be better:
import random
# function defintions
def flip():
return random.choice(['H', 'T']) # using random.choice()
def simulate(num_flips):
streak = 0
temp = 0
for i in range(num_flips):
if flip() == 'H':
temp+=1 # adding one to temporary streak if it is a heads
else: # this block executes if streak is broken
if temp > streak:
streak = temp
temp = 0
return streak
def main():
trial = []
for i in range(10000):
trial.append(simulate(100))
print("The longest streak of heads was " + str(max(trial)) +".")
if __name__ == "__main__":
main()

Counting Heads and Tails in a coin flip program

For my assignment, I have to use Functions within Python to simulate a coin flip. I managed to get the coin flip to showcase heads and tales for the amount the user has inputted. However, for the next portion, I have to get the program to read how many times Heads and Tails appeared. The error I am getting is
'NameError: name 'heads' is not defined'.
import random
def main():
tosses = int(input("Please enter the amount of coin tosses:"))
coin(tosses)
count = 0
heads = 0
tails = 0
def coin(tosses):
for toss in range(tosses):
if random.randint(1, 2) == 1:
print('Heads')
heads += 1
count += 1
else:
print('Tails')
heads += 1
count += 1
print (heads)
print (tails)
main()
Problem
heads was defined out of the scope of the function coin.
Solution
Try defining the variables inside the function, then returning them. Note: the same had to be done with main.
import random
def main():
tosses = int(input("Please enter the amount of coin tosses:"))
coin(tosses)
heads, tails, count = coin(tosses)
return heads, tails
def coin(tosses):
count = 0
heads = 0
tails = 0
for toss in range(tosses):
if random.randint(1, 2) == 1:
print('Heads')
heads += 1
count += 1
else:
print('Tails')
tails += 1 # note you had this say heads before
count += 1
return heads, tails, count
heads, tails = main()
print(heads)
print(tails)
More on why this problem occurred
When you use def and create a new function, all of the variables in that function are accessible just to that function.
What was happening to you, is that you were trying to update the heads variable with heads += 1, but the function literally didn't know what the variable you were referring to is! (That variable was defined in main, and is only accessible from within the main function.)

How can I use the results in functions in a loop?

I want to define two functions, one inside the other, and loop it until it reaches the value I'm looking for. However, the values in the function are not accessible from outside the function, so the codes do not work properly. Can I get some help with that? Is there a way to achieve this?
def count_div(number):
counter = 0
for ite in range(1,number+1):
if number % ite == 0:
counter += 1
else:
continue
else:
#print(number, counter)
return counter
def sum_of(order):
num = 0
for a in range(order+1):
num = num + a
count_div(num)
#sum_of(7)
for o1 in range(100):
if sum_of(o1) == 20:
print(o1)
break
else:
continue
else:
print('can\'t reach')
there is my work but in for loop sum_of func. not give an usable result for comparison..
Thanks You.
You need to return the result of the function you call.
def sum_of(order):
num = 0
for a in range(order+1):
num = num + a
return count_div(num)
When there is no explicit return statement in a function python automatically returns None. Therefore the line if sum_of(o1) == 20: was comparing None to 20 every iteration.
Just FYI for the function count_div you can use sum for a more pythonic approach:
sum(not number%(n+1) for n in range(number)
And sum_of this is just adding all positive integers from 1-n. This can also be done using this equation n*(n+1)//2 for sake of efficiency vs a looping method, thus making sum_of pretty much irrelevant.
def count_div(num):
return sum(not num%(n+1) for n in range(num*(num+1)//2))
Then for your last loop:
for o1 in range(100):
if count_div(o1) == 20:
print(o1)
break
Your sum_of function is not returning anything which is why your if condition will never work. Add a return on the last line in sum_of function:
return count_div(num)
Also you have lots of unnecessary else: statements which you should remove, here is an example:
def count_div(number):
counter = 0
for ite in range(1,number+1):
if number % ite == 0:
counter += 1
#print(number, counter)
return counter
def sum_of(order):
num = 0
for a in range(order+1):
num = num + a
return count_div(num)
#sum_of(7)
for o1 in range(100):
if sum_of(o1) == 20:
print(o1)
break
else:
print('can\'t reach')

Python - rolling a dice fairly and counting how many 4's I get

So I had to make code that roll a die fairly and counted how many 4's I got. With the help of you all on here I got it to work. Well now I have to created another die and roll them and then add they products together. This is the instructions I've been given.
"Then write another function that simulates rolling two fair dice. The
easy way is to call the function you just wrote, twice, and add the
numbers you get. This should return a number between 2 and 12. It
should calculate BOTH numbers in ONE run – you are counting two
distinct things."
And this is my code freshly fixed.
from random import randrange
def roll():
rolled = randrange(1,7)
if rolled == 1:
return "1"
if rolled == 2:
return "2"
if rolled == 3:
return "3"
if rolled == 4:
return "4"
if rolled == 5:
return "5"
if rolled == 6:
return "6"
def rollManyCountTwo(n):
twoCount = 0
for i in range (n):
if roll() == "2":
twoCount += 1
print ("In", n,"rolls of a pair of dice, there were",twoCount,"twos.")
rollManyCountTwo(6000)
Your code is bad, and hence, not as random as it should be.
randrange(low,up) includes both boundaries.
Look at what you do in the roll function:
you roll a dice. If it is 1, you return "1".
If it is not 1, you roll the dice again! That's simply wrong, and in a game would be considered cheating.
Your roll function is absolutely unnecessary, and can be replaced by
str(randrange(1,6)).
Also, why would you need the numbers as strings? You don't actually use these strings, so just stick with the result of randrange as it is -- a number.
EDIT:
You say
Yeah I'm assuming some of it is unnecessary but it's for a class and this is the way he wants it so I'm trying to keep it how he wants. I thought that's what I was doing but I could find a way around it!
All of it is unnecessary. There is a function that does what you want, so no need to encapsulate this in a class.
Well, you can just
class ash_class(object):
def roll():
return str(randrange(1,6))
For your counting problem: Python comes with all the tools.
counts = [0] * 6 # this is [0,0,0,0,0,0]
for i in range(n):
counts[randrange(1,6)] += 1
print counts
As you notice, it's really handy if the randrange numbers are numbers and not strings, as you can then use them like numbers to index an array.
As a personal advice: There should always be the freedom to discuss bad design choice. One such bad design choice is forcing you to convert numbers to strings, when you clearly need to work with them as numbers.
You shouldn't be calling randrange() inside the roll() in every if condition, instead you should call it once and save it in a variable and check.
The code would look like -
from random import randrange
def roll():
rolled = randrange(1,7)
if rolled == 1:
return "1"
if rolled == 2:
return "2"
if rolled == 3:
return "3"
if rolled == 4:
return "4"
if rolled == 5:
return "5"
if rolled == 6:
return "6"
def rollManyCountFour(n):
fourCount = 0
for i in range (n):
if roll() == "6":
fourCount += 1
print ("In", n,"rolls of a due, there were",fourCount,"fours.")
rollManyCountFour(6000)
I have used np.random.randint(1, 7, N), with N being the number of rolls and have used the following system to store it (if this helps):
R1s = 0
R2s = 0
R3s = 0
R4s = 0
R5s = 0
R6s = 0
for i in range(0, N):
if x[i] == 1:
R1s += 1
elif x[i] == 2:
R2s += 1
elif x[i] == 3:
R3s += 1
elif x[i] == 4:
R4s += 1
elif x[i] == 5:
R5s += 1
else:
R6s += 1
print(R1s)
# Ans ≈ N/6

Probability Dice Game in Python with two dices

I want to interate 1000 times over the following function to find out if you win or loose money in this game.
The game is designed as such that you throw a pair of dice and get money back or loose money. Let's say we start with 5 coins.
Throwing a 12 yields 1.5 coins.
Throwing an 11 yields 1 coins.
Throwing a 10 yields 0.5 coins.
Throwing a 9,8 or 7 yields nothing.
Throwing a 6,5,4,3,2 or 1 deducts 0.5 coins from your amount of coins.
This is what my implementation looks like so far:
def luckCalc():
amount = 5
# if 12 then 1/36 chance
if random.randrange(1,7) == 6 and random.randrange(1,7) == 6:
amount = amount + 1.5
# if 11 then 2/36 chance
elif (random.randrange(1,7) == 5 and random.randrange(1,7) == 6) or (random.randrange(1,7) == 6 and random.randrange(1,7) == 5):
amount = amount + 1
# if 10 then 3/36 chance
elif (random.randrange(1,7) == 5 and random.randrange(1,7) == 5) or (random.randrange(1,7) == 4 and random.randrange(1,7) == 6) or (random.randrange(1,7) == 6 and random.randrange(1,7) == 4):
amount = amount + 0.5
# if 9,8,7
# 4/36 + 5/36 + 6/36 chance
# 1+6, 2+5, 3+4, 4+3, 5+2, 6+1 chance
# 2+6, 3+5, 4+4, 5+3, 6+2 chance
# 3+6, 4+5, 5+4, 6+3 chance
# then no change in amount
# if 6,5,4,3,2,1
# chances...
# then amount -0.5
return amount
# Iterate over the dice throwing simulator and calculate total
total = 0.0
for a in range(1000):
total = total + luckCalc()
print (total)
I stopped coding towards the end of the function, because I recognised that there must be a more elegant solution on how to achieve this. Any interesting suggestions, what is this Monte Carlo I keep hearing about?
Each time you call random.randrange(1,7), you generate a new random number. Since you're testing a single "turn", roll twice:
def roll_die():
return random.randrange(1, 7)
total = roll_die() + roll_die()
And see if the sum is in a range:
def play_turn():
total = roll_die() + roll_die()
if total == 12:
return 1.5
elif total == 11:
return 1.0
elif total == 10:
return 0.5
elif total <= 6:
return -0.5
else: # total is 7, 8, or 9
return 0
Here's the result of 100,000 rounds:
>>> from collections import Counter
>>> counts = Counter(play_turn() for i in xrange(100000))
>>> counts
Counter({-0.5: 41823, 0: 41545, 0.5: 8361, 1.0: 5521, 1.5: 2750})
>>> probabilities = {score: count / 100000.0 for score, count in counts.items()}
>>> probabilities
{-0.5: 0.41823, 0: 0.41545, 0.5: 0.08361, 1.0: 0.05521, 1.5: 0.0275}
You can actually roll (ha!) everything you are doing into a single function:
from random import randrange
def play_game(rolls=1000, amount=5, n=6):
"""Play game 'rolls' times, starting with 'amount' on 'n'-sided dice."""
for i in range(rolls):
roll = randrange(1, n+1) + randrange(1, n+1)
if roll == 12:
amount += 1.5
elif roll == 11:
amount += 1
elif roll == 10:
amount += 0.5
elif roll < 7:
amount -= 0.5
return amount
I notice a few things in your code. First, for the 6-1 cases you're not actually subtracting 0.5 from the amount. Second, since you don't pass in the initial amount each loop you're adding between 5 and 6.5 to your total, which makes the total pretty pointless.
A more effective total would be to pass in the amount each time:
def luckCalc( amount ):
And then for your loop:
total = 5.0
for a in range(1000):
total = luckCalc(total)
Blender's answer, which just posted as I was writing this, is a great way to simplify your main function.
I personally like setting up my results table as an array (or a dictionary, but this suited my purpose better since every result was one of a small number of possible integers), with the index of each dice roll set to the value of the resulting change. See below.
import random
def luckCalc(coins=5):
diceroll = random.randint(1,6)+random.randint(1,6) #roll them bones
#here's that table I was talking about....
results_table = ['index 0 is blank',"you can't roll a one on two dice",-.5,-.5,-.5,-.5,-.5,0,0,0,.5,1,1.5]
coins += results_table[diceroll] #changes your coins value based on your roll (as an index of results_table)
if results_table[diceroll] > 0: #change the string if your result was + or -
result = "gained {}".format(results_table[diceroll])
else:
result = "lost {}".format(results_table[diceroll]*-1)
print("You {} coins, putting you at {}".format(result,coins)) #report back to the user
return coins #this is how you save your output
#CONSTANTS GO HERE -- YOU CAN CHANGE THESE TO CHANGE YOUR PROGRAM
STARTING_COINS = 5
HOW_MANY_ITERATIONS = 1000
#this way we don't modify a constant
coins = STARTING_COINS
#do it how many times?
for _ in range(HOW_MANY_ITERATIONS): #oh yeah that many times
coins = luckCalc(coins) #runs the function and saves the result back to coins
#report to the user your final result.
print("After {} rolls, your final total is {}".format(HOW_MANY_ITERATIONS,coins))

Categories