I am trying to come up with a simulation for the Pig dice game. I want it to simulate for the number of games the user wants(each game to 100 points) and report the average points and percent wins for each player. My program is running but it is only running for one game. I think there is something wrong with my loop but i cannot tell. Thank you for your help and time here is my program:
from random import randrange # Use "randrange(1, 7)" to get a random
# number between 1 and 6.
# Takes one turn in the game of pig. Keeps rolling until either
# the holdAmount is reached or a pig (1) is rolled. Returns the
# score accumulated during the turn.
def takeTurn(holdAmount):
totalScore = 0
while totalScore < holdAmount:
rollValue = randrange(1,7)
if rollValue == 1:
totalScore = 0
break
else:
totalScore = totalScore + rollValue
return totalScore
# Start with turn score equal to 0. Repeatedly roll die, adding
# roll value to score each time, until score reaches holdAmount.
# If at any time a pig (1) is rolled, set score to 0 and end the
# turn early.
# Tests the takeTurn function.
def main():
first = eval(input("How many points should the first player try for in each turn?"))
second = eval(input("How many points should the second player try for in each turn?"))
games = eval(input("How many games should be simulated?"))
firstScore = 0
secondScore = 0
turnCount = 0
score = 0
score1 = 0
won = 0
won1 = 0
for i in range(games):
while firstScore < 100 and secondScore < 100:
firstScore = takeTurn(first) + firstScore
secondScore = takeTurn(second) + secondScore
turnCount = turnCount + 1
if firstScore >= 100:
won = won + 1
elif secondScore >= 100:
won1 = won1 + 1
score = score + firstScore
score1 = score1 + secondScore
percent = won / games
percent1 = won1 / games
points = score / games
points2 = score1 / games
print("The average points for player one is",points)
print("The percent of games won for player one is",percent)
print("The average points for player two is",points2)
print("The percent of games won for player two is",percent1)
if __name__ == "__main__":
main()
I was confused for a while when I first looked at this. The reason is that each game ends with the same score since you do not reset the firstScore, etc. values each time. If you set each of those to 0 at the beginning of your for loop, you won't have any problems.
To be more specific, if you move firstScore, secondScore, and turnCount inside your for loop at the very top of it, the code runs properly.
The traditional way to gain a better understanding of what your program is doing is to add some print statements at looping and branching points.
A more advanced technique is to trace through the program using pdb.
You need firstScore and secondScore inside your for-loop.
Related
I'm doing a dice roll assignment. The rules are:
The points rolled on each player’s dice are added to their score.
If the total is an even number, an additional 10 points are added to their score.
If the total is an odd number, 5 points are subtracted from their score.
If they roll a double, they get to roll one extra die and get the number of points rolled added to their score.
The score of a player cannot go below 0 at any point.
The person with the highest score at the end of the 5 rounds wins.
Basically, what I've done is created variables of what 2 separate players roll in 2 rounds of the game, now I'm trying to add these variables together so that the player can have their total for round 1 and 2 combined. My teacher had mentioned something about a while function but that was all he was allowed to say, that plus I'm not sure how to do that.
I feel like what I've done is overly complicated, plus the fact it doesn't even work.
if (round2scoreP1 % 2) == 0 + (round1scoreP1 % 2) == 0:
addedscoreround2P1even=(totalround1scoreP1even)+(totalround2scoreP1even)
print(username1,"'s total for round 1 and 2 is",addedscoreround2P1even,".")
elif (round1scoreP1 % 2) != 0 + (round2scoreP1 % 2) != 0:
addedscoreround2P1odd=(totalround1scoreodd)+(totalround2scoreodd)
print(username1,"'s total for round 1 and 2 is",addedscoreround2P1odd,".")
elif (round1scoreP1 % 2) == 0 + (round2scoreP1 % 2) != 0:
addedscoreround2P1evenodd=(totalround1scoreP1even)+(totalround1scoreP1odd)
print(username1,"'s total for round 1 and 2 is",addedscoreround2P1evenodd,".")
elif (round1scoreP1 % 2) != 0 + (round2scoreP1 % 2) == 0:
addedscoreround2P1oddeven=(totalround1scoreP1odd)+(totalround1scoreP1even)
print(username1," obtained",addedscoreround2P1oddeven,".")
I'm not going to write you the whole assignment, but you could consider using a Player-class to store the rolls and total scores in:
class Player():
def __init__(self):
self.rolls = []
self.total_score = 0
def roll_dice():
self.rolls.append(random.randint(1, 6))
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 have this question for an assignment:
Your friend has devised a game with two players. The two players,
called A and B, take turns rolling an ordinary six-sided die, with A being
the first to roll.
The first player who rolls a six wins the game.
You and your friend do not agree about what the probability of A winning
the game is, and you therefore decide to simulate the game with a
computer.
Thus: write a Python program that performs 10 trials, each consisting of
10000 games, and for each trial prints the fraction of the games won by
player A.
This is the code I've gotten so far, it's just returning a number in and around 1667 every time. I'm mainly wondering how to differentiate between A or B winning the game.
Any help would be appreciated!
EDITED CODE
import random
def rollDie():
return random.choice([1,2,3,4,5,6])
def roll_first():
if random.choice([0,1]) == 0:
return 'A'
else:
return 'B'
def rollSim():
while True:
turn = roll_first()
numTrials = 10
numThrows = 10000
totalThrows = numTrials*numThrows
game_on = True
winsA = 0
winsB = 0
while game_on:
for n in range(numTrials):
games = 0
for i in range(numThrows):
throw = rollDie()
if turn == 'A':
if throw == 6:
winsA += 1
games += 1
break
else:
turn = 'B'
else:
if throw == 6:
winsB += 1
games += 1
break
else:
turn = 'A'
return winsA/totalThrows
The best way to achieve a clean code would be to separate in functions each one of the tasks in hand, meaning:
1. Run a play -> For each dice rolling
2. Run a game -> Alternation of plays between A and B, until the first one gets a 6 on the dice (here considering that if A gets a 6, B doesn't even need to play, as A won)
3. Run a trial -> Consisting of a specific number of plays
4. Run the main program -> Consisting of playing all the number of trials required
So, below, is one of the possible solutions (here you see that my play function already returns the result, meaning if the player won or not):
import random
def play():
won = True
keepPlaying = False
rollDice = random.choice([1,2,3,4,5,6])
if rollDice == 6:
return won
return keepPlaying
def run_game(winsA, winsB):
while True:
playA = play()
playB = play()
if playA:
winsA += 1
return winsA, winsB
elif playB:
winsB += 1
return winsA, winsB
def run_trial(numGames):
winsA = 0
winsB = 0
for i in range(numGames):
wins = run_game(winsA, winsB)
winsA = wins[0]
winsB = wins[1]
print("winsA:", winsA, "| winsB:", winsB, "| Fraction of A wins:", "{} %".format(winsA / ( winsA + winsB ) * 100))
numTrials = 10
numGames = 10000
for i in range(numTrials):
run_trial(numGames)
You really only need to count wins for player A. Since you play 10000 games per trial, if you know the number of games that A won, you know the other games must have been won by B, and A+B=10000.
You seem to decide randomly who has the first turn, but the task states that it should always be player A taking the first turn.
You can use a boolean isPlayerA to know whose turn it is. Start with isPlayerA = True and then toggle it with isPlayerA = not isPlayerA.
Here is how you could code it:
import random
def rollDie():
return random.choice([1,2,3,4,5,6])
def winFraction(): # Perform one trial of 10000 games and return fraction of wins for A
winsA = 0 # only count wins for A
for numTrow in range(10000):
isPlayerA = True # Player A always takes the first turn
throw = rollDie()
while throw != 6: # While the game is not over yet:
isPlayerA = not isPlayerA # Switch to the other player
throw = rollDie()
if isPlayerA:
winsA += 1 # Only count the wins for player A
return winsA/10000.0 # This is the fraction: we know the number of played games
def sim():
for trial in range(10): # Perform 10 trials
print(winFraction()) # Print the result of the trial
sim() # Start the simulation
I'm trying to figure out how to get my bot to distrubute points from a pot from the lottery game when it ends each time. So each game starts out at 1 point but everytime someone buys a ticket it goes up by a random amount. At the end of fifteen minutes it's supposed to retrieve the players and the pot and based off of where you're ranked in the list your points are determined. So basically if you're first in the list you win over the pot value. And when the ranking goes down your points that you win go down and if your near or at last place you lose points. The sum of all the points that the players earn does not have to equal 0 after they are distrubuted.
For example: Tim got 1st in the lottery. He should get 5676*1.66. Also, all the points you receive from the pot should be different based on your rank in the lottery. But if you're at the end of the list in the lottery you should lose points.
This is what I have so far:
lotteryStart = time.time()
players = []
pot = 1
def buyLottery(name):
if name not in players:
amount = int("30")
if Point.getCost(name, amount) == True:
multiplier = random.randint(217, 453)
pot = int(multiplier+pot)
different = float(time.time() - lotteryStart)
years = int(different / Point.YEAR)
days = int((different % Point.YEAR) / Point.DAY)
hours = int((different % Point.DAY) / Point.HOUR)
mins = int((different % Point.HOUR) / Point.MINUTE)
secs = int(different % Point.MINUTE)
if secs <= 0:
if len(players) > 0:
random.shuffle(players)
for i in range(1,len(players)):
if i == 1:
pot2 = int(pot*1.66)
elif i == 2:
pot2 = int(pot*1.33)
elif i == 3:
pot2 = int(pot)
elif i == 4:
pot = int(pot/1.66) #And so on but i dont want to keep doing elif
I do not understand your lottery; I can't imagine a lottery where you buy a ticket for a random amount and could lose money on top of the ticket price being very popular in real life!
That being said, I think you want to do something like this:
ratios = []
for i in range(-2, len(players) - 2):
if i < 0:
ratios.append(1 - (i * 0.33)) # ratio > 1
else:
ratios.append(1 / (1 + (i * 0.33))) # ratio <= 1
winnings = [pot * r for r in ratios]
You can then easily match players[i] with their winnings[i]. Note that I have assumed that you missed out pot / 1.33 accidentally; otherwise, you will have to adjust this slightly.
For 10 players, I get:
ratios == [1.6600000000000001, 1.33, 1.0, 0.7518796992481203,
0.6024096385542168, 0.5025125628140703, 0.43103448275862066,
0.3773584905660377, 0.33557046979865773, 0.3021148036253776]
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))