Description
The game of Yahtzee is played by rolling five 6-sided dice, and scoring the results in a number of ways. You are given a Yahtzee dice roll, represented as a sorted list of 5 integers, each of which is between 1 and 6 inclusive. Your task is to find the maximum possible score for this roll in the upper section of the Yahtzee score card. Here's what that means.
For the purpose of this challenge, the upper section of Yahtzee gives you six possible ways to score a roll. 1 times the number of 1's in the roll, 2 times the number of 2's, 3 times the number of 3's, and so on up to 6 times the number of 6's. For instance, consider the roll [2, 3, 5, 5, 6]. If you scored this as 1's, the score would be 0, since there are no 1's in the roll. If you scored it as 2's, the score would be 2, since there's one 2 in the roll. Scoring the roll in each of the six ways gives you the six possible scores:
0 2 3 0 10 6
it has been 3 days i am trying to program it right, but i have a problem in the elif len() == 1, when there is for example [1,1,2,6,4] it prints out 6 while it has to print 2 (bcs 1 is repeated twice)
import random
dice1 = random.randint(1,6)
dice2 = random.randint(1,6)
dice3 = random.randint(1,6)
dice4 = random.randint(1,6)
dice5 = random.randint(1,6)
result = [dice1,dice2,dice3,dice4,dice5]
one_rep = result.count(1)
two_rep = result.count(2)
three_rep = result.count(3)
four_rep = result.count(4)
five_rep = result.count(5)
six_rep = result.count(6)
reps = [one_rep,two_rep,three_rep,four_rep,five_rep,six_rep]
repsnum = [one_rep*1,two_rep*2,three_rep*3,four_rep*4,five_rep*5,six_rep*6]
repeated= []
test = [1]
def yahtzee():
for x in reps:
if x > 1:
repeated.append(x*(reps.index(x)+1))
s = repeated[0]
if reps.count(1) == 5:
score = max(result)
elif len(repeated) == len(test):
score = s
else:
score = max(repsnum)
return f'Your result is {dice1},{dice2},{dice3},{dice4},{dice5}\nYour score is {score}'
yahtzee()
print(yahtzee())
i have found the solution and here is the code:
import random
def yahtzee(result = [random.randint(1,6),random.randint(1,6),random.randint(1,6),random.randint(1,6),random.randint(1,6)]):
reps = [result.count(1),result.count(2),result.count(3),result.count(4),result.count(5),result.count(6)]
repsnum = [result.count(1)*1,result.count(2)*2,result.count(3)*3,result.count(4)*4,result.count(5)*5,result.count(6)*6]
repeated= []
for x in reps:
if x > 1:
repeated.append(x)
if reps.count(1) == 5:
score = max(result)
elif len(repeated) == 1:
score = repeated[0]*(reps.index(repeated[0])+1)
else:
score = max(repsnum)
return f'Your result is {result[0]},{result[1]},{result[2]},{result[3]},{result[4]}\nYour score is {score}'
print(yahtzee())
Related
I'm a novice making a small game for learning purposes.
The game rolls dice based on the inputs of the user.
The part I'm having trouble with is I want to check for patterns in the list "rolls"
patterns include:
all dice are the same value and the # of sides >=4 EXAMPLE [1, 1, 1, 1] < values are the same, atleast 4 sides. If true then mutiply user_Score by 10
at least half of the dice are >= "average_sum" with the condition that the list must have >= 5 dice
EXAMPLE if avg_sum = 2 and rolls = [2,3,4,1,1,] If true then mutiply user_Score by 5
all of the dice are different values with the conditions # of dice > 4 and # of sides > # of dice
[10, 11, 12, 13, 14]
No pattern matches. -> Multiply user_Score by 1
number_dice = int( input( "How many dice are you using? Must be between 3-6 inclusive" ) )
faces = int( input( "how many sides are on your die? enter a number between 2-20 inclusive: "))
# Set range for number of dice
#generate a random number between 1 and faces
#Add dice_roll to the list
rolls = []
for die in range(number_dice):
dice_roll = random.randint(1, faces)
rolls.append(dice_roll)
#print the score from each dice rolled
print("You have rolled: " + str(rolls))
#calculate sum of score
sum = sum(rolls)
#calculate the average and round to the nearest integer
average_sum = round(sum / number_dice)
print("These die sum to: " + str(sum) + " and have an average value of: " + str(average_sum))
#Calculate the max possible score
max_score = (number_dice * faces)
#calculate the users score
user_score = float( sum / max_score )
print("your max possible score is " + str(max_score))
print("your score is " + str(user_score))
#-----------------------------------------------------------------------------------
#now calculate the bonus factor
#Check if the list "rolls" contains the same value for each index
if rolls == {repeatingvalues???} and rolls {number_dice>=4}:
user_Score * 10
elif rolls == {half of dice > average} and {number_dice >=5}:
user_Score * 5
elif rolls == {all dice have different values} and { number_dice > 4}{faces> number_dice}:
user_score * 8
else:
user_score * 1
Not sure how to make this statement search the list for a pattern^^^^^^
Define a function to check for a repeating pattern that returns True or False
def has_repeats(my_list):
first = my_list[0]
for item in mylist:
if not item == first:
return False
return True
And then define a function to check for a no duplicates that returns True or False
def all_different(my_list):
# Remove duplicates from list
my_list2 = list(dict.fromkeys(my_list))
return len(my_list) == len(my_list2)
And finally define a function to check if half the dice are greater than the average:
def half_greater_than_averge(my_list, average):
a = 0
b = 0
for item in my_list:
if item > average:
a += 1
else:
b += 1
return a > b
So you final checks will be:
if has_repeats(rolls) and number_dice >= 4:
user_Score * 10
elif half_greater_than_averge(rolls, average_sum) and number_dice >= 5:
user_Score * 5
elif all_different(rolls) and number_dice > 4 and faces > number_dice:
user_score * 8
else:
user_score * 1
Check out my solution where add # ------ SOLUTION STARTS HERE -------
I helped refactor your code too. You shouldn't be using sum as a variable name (or identifier) in your code cos it's a reserved python keyword. So I changed it to my_sum. Check if it still works as desired.
import math # import math at the top
import random
number_dice = int( input( "How many dice are you using? Must be between 3-6 inclusive" ) )
faces = int( input( "how many sides are on your die? enter a number between 2-20 inclusive: "))
# Set range for number of dice
#generate a random number between 1 and faces
#Add dice_roll to the list
for die in range(number_dice):
dice_roll = random.randint(1, faces)
rolls.append(dice_roll)
#print the score from each dice rolled
print("You have rolled: " + str(rolls))
#calculate sum of score
my_sum = sum(rolls)
#calculate the average and round to the nearest integer
average_sum = round(my_sum / number_dice)
print("These die sum to: " + str(my_sum) + " and have an average value of: " + str(average_sum))
#Calculate the max possible score
max_score = (number_dice * faces)
#calculate the users score
user_score = float( my_sum / max_score )
print("your max possible score is " + str(max_score))
# ------ SOLUTION STARTS HERE------
rolls.sort()
rolls.reverse()
for item in rolls:
if (rolls.count(item) >= 4) and (number_dice >= 4):
user_score *= 10
break
elif (rolls[math.ceil(len(rolls)/2) -1] >= average_sum ) and (number_dice >= 5):
user_score *= 5
break
elif (sorted(rolls)==sorted(list(set(rolls))))and (number_dice > 4) and (faces > number_dice):
user_score *= 8
break
else:
user_score *= 1
# ------ SOLUTION ENDS HERE------
print("your score is " + str(user_score))
Here is a simple, faster and more 'Pythonic' way of doing it.
if all(x == rolls[0] for x in rolls):
print("Same")
elif len(rolls) == len(set(rolls)):
print("Unique")
elif number_dice/2 <= [x > avg_sum for x in rolls].count(True):
print("Half")
else:
print("No match")
The 'and' conditions are missing. Please feel free to add them.
Bonus
from random import randint
faces = int(input('Number of faces:'))
number_dice = int(input('Number of dice:'))
rolls = [randint(1, faces) for _ in range(number_dice)]
Feel free to explore
I am writing a program that is to repeatedly roll two dice until the user input target value is reached. I can not figure out how to make the dice roll happen repeatedly. Please, help...
from random import randrange
print ("This program rolls two 6-sided dice until their sum is a given target value.")
input = int (input("Enter the target sum to roll for:"))
#def main():
dice1 = randrange (1,7)
dice2 = randrange (1,7)
sumofRoll = dice1 + dice2
output = "Roll: {} and {}, sum is {}".format (dice1,dice2,sumofRoll)
print (output)
if sumofRoll == input:
print ("All done!!")
if sumofRoll != input:
#how do I make this repeatedly run until input number is reached?!
Here is a complete working code which takes care of invalid sum entered as input as well. The sum of two dice rolls cannot be either less than 2 or more than 13. So a check for this condition makes your code slightly more robust. You need to initialize sumofRoll = 0 before entering the while loop as this would be needed to first enter the while loop. The value of 0 is a safe value because we excluded the value of 0 entered by user to be a valid sum.
from random import randrange
print ("This program rolls two 6-sided dice until their sum is a given target value.")
input_sum = int(input("Enter the target sum to roll for:"))
def main():
sumofRoll = 0
if input_sum < 2 or input_sum > 13:
print ("Enter a valid sum of dices")
return
while sumofRoll != input_sum:
dice1 = randrange (1,7)
dice2 = randrange (1,7)
sumofRoll = dice1 + dice2
output = "Roll: {} and {}, sum is {}".format (dice1,dice2,sumofRoll)
print (output)
if sumofRoll == input_sum:
print ("All done!!")
main()
This program rolls two 6-sided dice until their sum is a given target value.
Enter the target sum to roll for:10
Roll: 3 and 5, sum is 8
Roll: 6 and 6, sum is 12
Roll: 5 and 1, sum is 6
Roll: 2 and 5, sum is 7
Roll: 6 and 6, sum is 12
Roll: 3 and 5, sum is 8
Roll: 1 and 2, sum is 3
Roll: 6 and 4, sum is 10
All done!!
I took your game and added some elements for you to peek through. One is you could use random.choice and select from a die list instead of generating new random ints repeatedly. Second you can use a try/except block to only accept an int and one that is within the range of (2, 13). Next we can add a roll_count to track the amount of rolls to hit our target. Our while loop will continue until target == roll_sum and then we can print our results.
from random import choice
die = [1, 2, 3, 4, 5, 6]
print('This program rolls two 6-sided dice until their sum is a given target.')
target = 0
while target not in range(2, 13):
try:
target = int(input('Enter the target sum to roll (2- 12): '))
except ValueError:
print('Please enter a target between 2 and 12')
roll_sum = 0
roll_count = 0
while target != roll_sum:
die_1 = choice(die)
die_2 = choice(die)
roll_sum = die_1 + die_2
roll_count += 1
print('You rolled a total of {}'.format(roll_sum))
print('You hit your target {} in {} rolls'.format(target, roll_count))
...
You rolled a total of 4
You rolled a total of 12
You hit your target 12 in 29 rolls
This is a simple while loop:
sumofRoll = -1; #a starting value!
while sumofRoll != input:
#Put the code that updates the sumofRoll variable here
count = 0
while(sumOfRoll != input):
dice1 = randrange(1,7)
dice2 = rangrange(1,7)
count = count + 1
sumOfRoll = dice1 + dice2
print("sum = %s, took %s tries" % (sumOfRoll, count))
Use a do while statement
do:
dice1=randrange(1,7)
...
while(sumofroll != input)
print("all done")
import random
sample_size = int(input("Enter the number of times you want me to roll the die: "))
if (sample_size <=0):
print("Please enter a positive number!")
else:
counter1 = 0
counter2 = 0
final = 0
while (counter1<= sample_size):
dice_value = random.randint(1,6)
if ((dice_value) == 6):
counter1 += 1
else:
counter2 +=1
final = (counter2)/(sample_size) # fixing indention
print("Estimation of the expected number of rolls before pigging out: " + str(final))
Is the logic used here correct? It will repeat rolling a die till a one is rolled, while keeping track of the number of rolls it took before a one showed up. It gives a value of 0.85 when I run it for high values(500+)
Thanks
import random
while True:
sample_size = int(input("Enter the number of times you want me to roll a die: "))
if sample_size > 0:
break
roll_with_6 = 0
roll_count = 0
while roll_count < sample_size:
roll_count += 1
n = random.randint(1, 6)
#print(n)
if n == 6:
roll_with_6 += 1
print(f'Probability to get a 6 is = {roll_with_6/roll_count}')
One sample output:
Enter the number of times you want me to roll a dile: 10
Probability to get a 6 is = 0.2
Another sample output:
Enter the number of times you want me to roll a die: 1000000
Probability to get a 6 is = 0.167414
Sticking with your concept, I would create a list that contains each roll then use enumerate to count the amount of indices between each 1 and sum those, using the indicies as markers.
the variable that stores the sum of the number of rolls it took before a 1 showed up - OP
from random import randint
sample_size = 0
while sample_size <= 0:
sample_size = int(input('Enter amount of rolls: '))
l = [randint(1, 6) for i in range(sample_size)]
start = 0
count = 0
for idx, item in enumerate(l):
if item == 1:
count += idx - start
start = idx + 1
print(l)
print(count)
print(count/sample_size)
Enter amount of rolls: 10
[5, 3, 2, 6, 2, 3, 1, 3, 1, 1]
7
0.7
Sameple Size 500:
Enter amount of rolls: 500
406
0.812
For an introductionary course in Python I got an assignment to make a simulation for eolling dice
You want all of your dices (5 in total) to get the value six, and count how many throws in total it takes for a person to get all sixes. I need a loop that simulates this problem 100.000 times and then need to divide the total amount of counts by 100.000 to get the outcome. I know that the final outcome should be something around 13, but I am not getting that and I am not sure why.
I know something is wrong in my approach to this problem , but what?
import random
count1=0
count2=0
count3=0
count4=0
count5=0
loopcounter = 0
for loopcouter in range (1,100000):
dice1=int( random.random()*6)+1
if dice1 != 6:
#reroll
while dice1 != 6:
dice1=int( random.random()*6)+1
#set counter1
count1 = count1+1
else:
count1 = 1
dice2=int( random.random()*6)+1
if dice2 != 6:
#reroll while not six
while dice2 != 6:
dice2=int( random.random()*6)+1
#set counter2
count2 = count2+1
else:
count2 = 1
dice3=int( random.random()*6)+1
if dice3 != 6:
#reroll while not six
while dice3 != 6:
dice3=int( random.random()*6)+1
#set counter3
count3 = count3+1
else:
count3 = 1
dice4=int( random.random()*6)+1
if dice4 != 6:
#reroll while not six
while dice4 != 6:
dice4=int( random.random()*6)+1
#set counter4
count4 = count4+1
else:
count4 = 1
dice5=int( random.random()*6)+1
if dice5 != 6:
#reroll while not six
while dice5 != 6:
dice5=int( random.random()*6)+1
#set counter5
count5 = count5+1
else:
count5 = 1
#print (dice1)
print (count1)
#print (dice2)
print (count2)
#print (dice3)
print (count3)
#print (dice4)
print (count4)
#print (dice5)
print (count5)
allcount = count1+count2+count3+count4+count5
averagecount = int(allcount / 100000)
print ("the total number of throws is",allcount)
print ("the average number of throws is",averagecount)
So, if anyone could tell me what I am doing wrong, that would be perfect!
Here is a very different approach.
Let's make an object, die, that will roll until it hits the target:
import random
class die(object):
def __init__(self, sides=6):
self.sides=sides
self.count=0
def roll(self):
self.count+=1
return random.randint(1,self.sides)
def roll_until(self, tgt, giveup=100000):
result=0
self.tgt=tgt
while result!=tgt and self.count<giveup:
result=self.roll()
if self.count<giveup:
return self.count
Then you can just create an instance of that (a single die) and tell it to roll itself until a target is hit:
>>> d=die()
>>> d.roll_until(6)
2
>>> d.tgt
6
>>> d.count
2
2 is the number of times d needed to be rolled until 6 is the result with a 6 sided die.
Why do it this way? Now you can easily create a list of die:
>>> dice=[die().roll_until(6) for i in range(6)]
>>> dice
[15, 3, 3, 4, 5, 2]
And easily answer your questions.
Take the max of this list:
>>> max(die().roll_until(6) for i in range(6))
9
n times divided by float(n) for the result:
>>> n=100000
>>> sum(max(die().roll_until(6) for i in range(6)) for i in range(n))/float(n)
13.95879
Whoops! Only five dice in a game of Yatzee. Easy change:
>>> sum(max(die().roll_until(6) for i in range(5)) for i in range(n))/float(n)
13.0032
According to the instructions you need the maximum of count for each round as this will tell you how many rolls you needed to get all 6s.
This is a re-write of your code using a loop for each dice:
import random
allcount = 0
for loopcouter in range(100000): # 1,100000 would only loop 99999 times
count = [0]*5
for i in range(5): # 5 dice
while True:
dice = random.randint(1,6) # Use randint
count[i] += 1
if dice == 6:
break
allcount += max(count) # The number of rolls needed to get all 6s
averagecount = allcount // 100000
print("the total number of throws is", allcount)
print("the average number of throws is", averagecount)
And this seems to average in 12/13 range.
There are many ways to solve this for example you can use iter and an anonymous function lambda to replace the inner while loop. These start to use more advanced features of python (iterators and generators):
from random import randint
allcount = 0
for _ in range(100000):
counts = [1]*5
for i in range(5):
dice = list(iter(lambda: randint(1,6), 6))
counts[i] += len(dice)
allcount += max(counts)
averagecount = allcount // 100000
In fact you can completely collapse this into one line of code but it gets increasingly harder to read and breaks all sorts of manner of style:
allcount = sum(max((1 + sum(1 for _ in iter(lambda: randint(1, 6), 6)))
for _ in range(5)) for _ in range(100000))
averagecount = allcount // 100000
With your new edit, the counts should still start with 0 but you should also increase your counters once before you go into the while loop, because this already marks one roll.
Also, you should use a different function to determine your random integer. Instead of using
(random.random() * 6) + 1
use:
random.randint(1,6)
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))