Simulate several rounds of rock, paper and scissors - python

I am trying to implement a function that takes an integer n and simulates n rounds of Rock, Paper, Scissors between players Player 1 and Player 2. The player who wins the most rounds wins the n-round game, with ties possible. The function should print the result of the game as shown.
>>> simul(1)
Player 1
>>> simul(1)
Tie
>>>simul(100)
Player 2
I think I need to approach this in a modular fashion. In other words, I need to combine at least 2 functions, my problem is that I can't seem to figure out how to do that. How can I activate the result from an embedded function when calling the simul() function?
So I have created a function that simulates the game Rock, Paper, Scissors by execution the function rps(p1, p2). The code is the following:
def rps(p1,p2):
#tie
if (p1==p2):
return 0
# player 1 wins
elif p1+p2 in ['PR','RS','SP']:
return -1
else:
return 1
# player 2 wins
This is where I'm a bit stuck. I need to activate this function when executing the simul() function—how can I do that? What I have so far is the following:
def rps(p1,p2):
#tie
if (p1==p2):
return 0
# player 1 wins
elif p1+p2 in ['PR','RS','SP']:
return -1
else:
return 1
# player 2 wins
def choose_rps():
import random
random.choice('RPS')
def simul(n):
p1_wins, p2_wins = 0, 0
for i in range(n):
p1 = choose_rps()
p2 = choose_rps()
result = rps(p1, p2)
if result == -1:
p1_wins += 1
elif result == 1:
p2_wins += 1
if p1_wins > p2_wins:
return 'Player 1'
elif p1_wins == p2_wins:
return 'Tie'
else:
return 'Player 2'

To "activate" a function, you just call it. For example:
def simul(n):
score = 0
for i in range(n):
p1 = choose_rps()
p2 = choose_rps()
result = rps(p1, p2)
score += result
if score < 0:
print('Player 1')
elif score == 0:
print('Tie')
else:
print('Player 2')
Of course you need to write the choose_rps function (which randomly chooses and returns one of R, P, or S)—but, as you can see, you just call it the same way as the rps function.
To put it all together into a script:
def rps(p1, p2):
# ... your code here
def choose_rps():
# implement this here
def simul(n):
# code from above
And then you'll probably want something to drive it, such as this:
if __name__ == '__main__':
import sys
n = int(sys.argv[1]) if len(sys.argv) > 1 else 5
simul(n)
… or…
while True:
n = int(input('How many trials?')) # raw_input for Python 2.x
simul(n)
If you want, you can simplify this further. For example, you can turn the whole loop into a sum call with a generator expression:
def simul(n):
score = sum(rps(choose_rps(), choose_rps()) for _ in range(n))

Here is an equivalent function without the RPS narrative:
import random
def simul(n):
score = sum([random.choice([-1, 0, 1]) for i in xrange(n)])
winner_idx = 0 if score > 0 \
else 1 if score < 0 \
else 2
print ['Player 1', 'Player 2', 'Tie'][winner_idx]
Here is one that follows the story and is expanded to Rock, Paper, Scissors, Lizard, Spock:
import random
def rand_RPSLK():
while True:
yield random.choice('PSKLR')
def simul(n):
p1 = rand_RPSLK()
p2 = rand_RPSLK()
choice = lambda px: 'PSKLR'.index(next(px))
score = sum([[0,1,-1,1,-1][choice(p1)-choice(p2)] for i in xrange(n)])
winner_idx = 0 if score > 0 \
else 1 if score < 0 \
else 2
print ['Player 1', 'Player 2', 'Tie'][winner_idx]

Related

How to write minimax algorithm accurately?

I've made MiniMax function but it seems it doesn't work,
It should return the optimal play in form [1,2] for example,
def minimax(board,player_to_play):
"""
Returns the optimal action for the current player on the board.
"""
available = actions(board) ##empty slots
to_move = None
if player_to_play == "X": #maxmizing
current_best = -100
for play in available:
temp_board = copy.deepcopy(board)
temp_board[play[0]][play[1]] = "X"
o_move = minimax(temp_board,"O")
score = utility(o_move)
if score > current_best :
to_move = play
else: #minmizing
current_best = 100
for play in available:
temp_board = copy.deepcopy(board)
temp_board[play[0]][play[1]] = "O"
x_move = minimax(temp_board,"X")
score = utility(x_move)
if score < current_best:
to_move = play
return to_move
where utility() return score of board
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
if terminal(board):
if winner(board) == "X":
return 1
elif winner(board) == "O":
return -1
else:
return 0
else:
return 0
Is that logically right or what I'm miss understanding?

Python - LOGIC ERROR - on racquetball simulation

I just started to learn python(self-taught) and I'm trying to do a simulation of racquetball game.
I can run the simulation with: a==15 or b==15, but when I add the extra condition: abs(a-b)>=2 IT GOES CRAZY(the simOneGame turns to an infinite loop). Here is what I tried so far:
return a == 15 or b == 15 ------> this one works perfectly
return a == 15 or b == 15 and abs(a-b) >= 2 --> this doesn't freeze but the logic is wrong(I think)
return (a == 15 or b == 15) and abs(a-b) >= 2 --> freezing
return (a == 15 or b == 15) and (abs(a-b) >= 2) --> freezing
return ((a == 15 or b == 15) and (abs(a-b) >= 2)) --> freezing
Thanks a lot and sorry if this seems too long(maybe I should've typed just the function gameOver()?...)
Here is my code so far:
from random import random
def main():
printIntro()
n, probA, probB = getInputs()
winsA, winsB = simNGames(n, probA, probB)
displayResults(winsA, winsB)
def printIntro():
print("\nVOLLEYBALL SIMULATION\n")
print("The inputs will be the number of simulations(n),")
print(" the probability player A wins his serve(probA)")
print(" and the probability player B wins his serve(probB).")
print("The program will display how many games won player A(winsA),")
print(" how many games won player B(winsB),")
print(" and the percentages of winnings for both players.\n")
def getInputs():
n = int(input("Enter the number of simulations: "))
probA = float(input("Enter the probability player A wins his serve: "))
probB = float(input("Enter the probability player B wins his serve: "))
return n, probA, probB
def simNGames(n, probA, probB):
winsA, winsB = 0, 0
for i in range(n):
# player A serves on odd games
if i % 2 == 0:
serving = "A"
# player B serves on even games
else:
serving = "B"
scoreA, scoreB = simOneGame(probA, probB, serving)
if scoreA > scoreB:
winsA = winsA + 1
else:
winsB = winsB + 1
return winsA, winsB
def simOneGame(probA, probB, serving):
scoreA, scoreB = 0, 0
while not gameOver(scoreA, scoreB):
if serving == "A":
if random() < probA:
scoreA = scoreA + 1
else:
serving = "B"
else:
if random() < probB:
scoreB = scoreB + 1
else:
serving = "A"
return scoreA, scoreB
def gameOver(a, b):
# the game is over if a player gets to 15 AND the difference is at least 2.
# this shoud work... BUT IT DOESN'T... simOneGame turns to an infinete loop with a large number of simulations...
return (a == 15 or b == 15) and abs(a-b) >= 2
def displayResults(winsA, winsB):
print("Player A won {0} games({1:1.0%})".format(winsA, winsA/(winsA+winsB)))
print("Player B won {0} games({1:0.0%})".format(winsB, winsB/(winsA+winsB)))
if __name__ == "__main__":
main()

Table tennis simulator

I edited my previous question because I came up with the code I think is correct.
The logic behind this should be:
while the set is not over and it's not a tie 10:10: player A starts serving and does it twice regardless he wins points or not, then player B takes serve and does it twice also. It continues until the set is over, except there is a tie 10:10 when servers change each point scored.
Can anyone check if the code is flawless? thank you.
def simOneSet(probA, probB):
serving = "A"
scoreA = scoreB = 0
while not setOver(scoreA, scoreB):
if scoreA != 10 and scoreB != 10:
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
serving = "B"
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
serving = "A"
# when there is a tie 10:10
else:
if serving == "A":
if random() < probA:
scoreA += 1
serving = "B"
else:
scoreB += 1
serving = "B"
else:
if random() < probB:
scoreB += 1
serving = "B"
else:
scoreA += 1
serving = "A"
return scoreA, scoreB
I would use a dict to "switch" between players:
other = {'A':'B', 'B':'A'}
Then, if serving equals 'A', then other[serving] would equal 'B', and if serving equals 'B', then other[serving] would equal 'A'.
You could also use a collections.Counter to keep track of the score:
In [1]: import collections
In [2]: score = collections.Counter()
In [3]: score['A'] += 1
In [4]: score['A'] += 1
In [5]: score['B'] += 1
In [6]: score
Out[6]: Counter({'A': 2, 'B': 1})
Also notice how in this piece of code
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
there are two blocks which are basically the same idea repeated twice. That's a sign that the code can be tightened-up by using a function. For example, we could define a function serve which when given a probability prob and a player (A or B) returns the player who wins:
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
then the above code would become
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
Thus, you can compactify your code quite a bit this way:
import random
import collections
other = {'A':'B', 'B':'A'}
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
def simOneSet(probA, probB):
prob = {'A':probA, 'B':probB}
score = collections.Counter()
serving = "A"
while not setOver(score['A'], score['B']):
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
if score['A'] == 10 and score['B'] == 10:
winner = serve(prob[serving], serving)
score[winner] += 1
serving = winner
return score['A'], score['B']
def setOver(scoreA, scoreB):
return max(scoreA, scoreB) >= 21
print(simOneSet(0.5,0.5))
Here is a hint:
If you have the roundnumber in the set and know which player started serving, you have everything you need to know who is serving.
Then a simple if statement at the start or end of your loop should be enough.
If this is too complicated, try starting by simulating a game where the server starts every round.
Something that might help is the syntax
var = 1 if var == 2 else 2
Which will make var be 1 if var is 2, and var be 2 if var is 1. I feel as though this is a school problem, so I don't want to totally give away the answer :)
Hint: You're on the right track with your thinking.
from random import *
P1=P2=0
while 1 :
p1=p2=0
while 2 :
if random() < 0.5 : p1 +=1
else : p2 +=1
if(p1 >=11 or p2 >=11) and abs(p1-p2) > 1: break
P1 += p1 > p2; P2 += p2 > p1
print "%2d : %2d (%d : %d)" % (p1, p2, P1, P2)
if P1 == 4 or P2 == 4 : break
i hope this helps, it worked for me

craps in python

I'm trying to simulate n games of craps. The code seems to make sense to me but I never get the right result. For example, if I put in n = 5 i.e. fives games the wins and losses sum to something greater than 5.
Here's how it's supposed to work: if initial roll is 2, 3, or 12, the player loses. If the roll is 7 or 11, the player wins. Any other initial roll causes the player to roll again. He keeps rolling until either he rolls a 7 or the value of the initial roll. If he re-rolls the initial value before rolling a 7, it's a win. Rolling a 7 first is a loss.
from random import randrange
def roll():
dice = randrange(1,7) + randrange (1,7)
return dice
def sim_games(n):
wins = losses = 0
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
return wins, losses
#simulate one game
def game():
dice = roll()
if dice == 2 or dice == 3 or dice == 12:
return False
elif dice == 7 or dice == 11:
return True
else:
dice1 = roll()
while dice1 != 7 or dice1 != dice:
if dice1 == 7:
return False
elif dice1 == dice:
return True
else:
dice1 = roll()
def main():
n = eval(input("How many games of craps would you like to play? "))
w, l = sim_games(n)
print("wins:", w,"losses:", l)
The problem is with
if game():
wins = wins + 1
if not game():
losses = losses + 1
Instead, it should be
if game():
wins = wins + 1
else:
losses = losses + 1
In your code, you are simulating two games instead of one (by calling game() twice). This gives four possible outcomes instead of two (win/loss), giving inconsistent overall results.
In this code
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
you call game() twice, so you play two games right there. What you want is a else block:
for i in range(n):
if game():
wins = wins + 1
else:
losses = losses + 1
Btw, you can simplify the logic with in:
def game():
dice = roll()
if dice in (2,3,12):
return False
if dice in (7,11):
return True
# keep rolling
while True:
new_roll = roll()
# re-rolled the initial value => win
if new_roll==dice:
return True
# rolled a 7 => loss
if new_roll == 7:
return False
# neither won or lost, the while loop continues ..
The code is quite literally the description you gave.
Don't do this
for i in range(n):
if game():
wins = wins + 1
if not game():
losses = losses + 1
It doesn't work out well at all.
There are numerous problems with this code. Most importantly, you're calling game() twice per loop. You need to call it once and store the result, and switch based on that.
An OO rewrite:
import random
try:
rng = xrange # Python 2.x
inp = raw_input
except NameError:
rng = range # Python 3.x
inp = input
def makeNSidedDie(n):
_ri = random.randint
return lambda: _ri(1,n)
class Craps(object):
def __init__(self):
super(Craps,self).__init__()
self.die = makeNSidedDie(6)
self.firstRes = (0, 0, self.lose, self.lose, 0, 0, 0, self.win, 0, 0, 0, self.win, self.lose)
self.reset()
def reset(self):
self.wins = 0
self.losses = 0
def win(self):
self.wins += 1
return True
def lose(self):
self.losses += 1
return False
def roll(self):
return self.die() + self.die()
def play(self):
first = self.roll()
res = self.firstRes[first]
if res:
return res()
else:
while True:
second = self.roll()
if second==7:
return self.lose()
elif second==first:
return self.win()
def times(self, n):
wins = sum(self.play() for i in rng(n))
return wins, n-wins
def main():
c = Craps()
while True:
n = int(inp("How many rounds of craps would you like to play? (0 to quit) "))
if n:
print("Won {0}, lost {1}".format(*(c.times(n))))
else:
break
print("Total: {0} wins, {1} losses".format(c.wins, c.losses))
if __name__=="__main__":
main()

Is this a good or bad 'simulation' for Monty Hall? How come? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
Through trying to explain the Monty Hall problem to a friend during class yesterday, we ended up coding it in Python to prove that if you always swap, you will win 2/3 times. We came up with this:
import random as r
#iterations = int(raw_input("How many iterations? >> "))
iterations = 100000
doors = ["goat", "goat", "car"]
wins = 0.0
losses = 0.0
for i in range(iterations):
n = r.randrange(0,3)
choice = doors[n]
if n == 0:
#print "You chose door 1."
#print "Monty opens door 2. There is a goat behind this door."
#print "You swapped to door 3."
wins += 1
#print "You won a " + doors[2] + "\n"
elif n == 1:
#print "You chose door 2."
#print "Monty opens door 1. There is a goat behind this door."
#print "You swapped to door 3."
wins += 1
#print "You won a " + doors[2] + "\n"
elif n == 2:
#print "You chose door 3."
#print "Monty opens door 2. There is a goat behind this door."
#print "You swapped to door 1."
losses += 1
#print "You won a " + doors[0] + "\n"
else:
print "You screwed up"
percentage = (wins/iterations) * 100
print "Wins: " + str(wins)
print "Losses: " + str(losses)
print "You won " + str(percentage) + "% of the time"
My friend thought this was a good way of going about it (and is a good simulation for it), but I have my doubts and concerns. Is it actually random enough?
The problem I have with it is that the all choices are kind of hard coded in.
Is this a good or bad 'simulation' for the Monty Hall problem? How come?
Can you come up with a better version?
Your solution is fine, but if you want a stricter simulation of the problem as posed (and somewhat higher-quality Python;-), try:
import random
iterations = 100000
doors = ["goat"] * 2 + ["car"]
change_wins = 0
change_loses = 0
for i in xrange(iterations):
random.shuffle(doors)
# you pick door n:
n = random.randrange(3)
# monty picks door k, k!=n and doors[k]!="car"
sequence = range(3)
random.shuffle(sequence)
for k in sequence:
if k == n or doors[k] == "car":
continue
# now if you change, you lose iff doors[n]=="car"
if doors[n] == "car":
change_loses += 1
else:
change_wins += 1
print "Changing has %s wins and %s losses" % (change_wins, change_loses)
perc = (100.0 * change_wins) / (change_wins + change_loses)
print "IOW, by changing you win %.1f%% of the time" % perc
a typical output is:
Changing has 66721 wins and 33279 losses
IOW, by changing you win 66.7% of the time
You mentioned that all the choices are hardcoded in. But if you look closer, you'll notice that what you think are 'choices' are actually not choices at all. Monty's decision is without loss of generality since he always chooses the door with the goat behind it. Your swapping is always determined by what Monty chooses, and since Monty's "choice" was actually not a choice, neither is yours. Your simulation gives the correct results..
I like something like this.
#!/usr/bin/python
import random
CAR = 1
GOAT = 0
def one_trial( doors, switch=False ):
"""One trial of the Monty Hall contest."""
random.shuffle( doors )
first_choice = doors.pop( )
if switch==False:
return first_choice
elif doors.__contains__(CAR):
return CAR
else:
return GOAT
def n_trials( switch=False, n=10 ):
"""Play the game N times and return some stats."""
wins = 0
for n in xrange(n):
doors = [CAR, GOAT, GOAT]
wins += one_trial( doors, switch=switch )
print "won:", wins, "lost:", (n-wins), "avg:", (float(wins)/float(n))
if __name__=="__main__":
import sys
n_trials( switch=eval(sys.argv[1]), n=int(sys.argv[2]) )
$ ./montyhall.py True 10000
won: 6744 lost: 3255 avg: 0.674467446745
Here's my version ...
import random
wins = 0
for n in range(1000):
doors = [1, 2, 3]
carDoor = random.choice(doors)
playerDoor = random.choice(doors)
hostDoor = random.choice(list(set(doors) - set([carDoor, playerDoor])))
# To stick, just comment out the next line.
(playerDoor, ) = set(doors) - set([playerDoor, hostDoor]) # Player swaps doors.
if playerDoor == carDoor:
wins += 1
print str(round(wins / float(n) * 100, 2)) + '%'
Here is an interactive version:
from random import shuffle, choice
cars,goats,iters= 0, 0, 100
for i in range(iters):
doors = ['goat A', 'goat B', 'car']
shuffle(doors)
moderator_door = 'car'
#Turn 1:
selected_door = choice(doors)
print selected_door
doors.remove(selected_door)
print 'You have selected a door with an unknown object'
#Turn 2:
while moderator_door == 'car':
moderator_door = choice(doors)
doors.remove(moderator_door)
print 'Moderator has opened a door with ', moderator_door
#Turn 3:
decision=raw_input('Wanna change your door? [yn]')
if decision=='y':
prise = doors[0]
print 'You have a door with ', prise
elif decision=='n':
prise = selected_door
print 'You have a door with ', prise
else:
prise = 'ERROR'
iters += 1
print 'ERROR:unknown command'
if prise == 'car':
cars += 1
elif prise != 'ERROR':
goats += 1
print '==============================='
print ' RESULTS '
print '==============================='
print 'Goats:', goats
print 'Cars :', cars
My solution with list comprehension to simulate the problem
from random import randint
N = 1000
def simulate(N):
car_gate=[randint(1,3) for x in range(N)]
gate_sel=[randint(1,3) for x in range(N)]
score = sum([True if car_gate[i] == gate_sel[i] or ([posible_gate for posible_gate in [1,2,3] if posible_gate != gate_sel[i]][randint(0,1)] == car_gate[i]) else False for i in range(N)])
return 'you win %s of the time when you change your selection.' % (float(score) / float(N))
print simulate(N)
Not mine sample
# -*- coding: utf-8 -*-
#!/usr/bin/python -Ou
# Written by kocmuk.ru, 2008
import random
num = 10000 # number of games to play
win = 0 # init win count if donot change our first choice
for i in range(1, num): # play "num" games
if random.randint(1,3) == random.randint(1,3): # if win at first choice
win +=1 # increasing win count
print "I donot change first choice and win:", win, " games"
print "I change initial choice and win:", num-win, " games" # looses of "not_change_first_choice are wins if changing
I found this to be the most intuitive way of solving the problem.
import random
# game_show will return True/False if the participant wins/loses the car:
def game_show(knows_bayes):
doors = [i for i in range(3)]
# Let the car be behind this door
car = random.choice(doors)
# The participant chooses this door..
choice = random.choice(doors)
# ..so the host opens another (random) door with no car behind it
open_door = random.choice([i for i in doors if i not in [car, choice]])
# If the participant knows_bayes she will switch doors now
if knows_bayes:
choice = [i for i in doors if i not in [choice, open_door]][0]
# Did the participant win a car?
if choice == car:
return True
else:
return False
# Let us run the game_show() for two participants. One knows_bayes and the other does not.
wins = [0, 0]
runs = 100000
for x in range(0, runs):
if game_show(True):
wins[0] += 1
if game_show(False):
wins[1] += 1
print "If the participant knows_bayes she wins %d %% of the time." % (float(wins[0])/runs*100)
print "If the participant does NOT knows_bayes she wins %d %% of the time." % (float(wins[1])/runs*100)
This outputs something like
If the participant knows_bayes she wins 66 % of the time.
If the participant does NOT knows_bayes she wins 33 % of the time.
Read a chapter about the famous Monty Hall problem today. This is my solution.
import random
def one_round():
doors = [1,1,0] # 1==goat, 0=car
random.shuffle(doors) # shuffle doors
choice = random.randint(0,2)
return doors[choice]
#If a goat is chosen, it means the player loses if he/she does not change.
#This method returns if the player wins or loses if he/she changes. win = 1, lose = 0
def hall():
change_wins = 0
N = 10000
for index in range(0,N):
change_wins += one_round()
print change_wins
hall()
Updated solution
Update, this time using the enum module. Again, going for brevity while using the most expressive features of Python for the problem at hand:
from enum import auto, Enum
from random import randrange, shuffle
class Prize(Enum):
GOAT = auto()
CAR = auto()
items = [Prize.GOAT, Prize.GOAT, Prize.CAR]
num_trials = 100000
num_wins = 0
# Shuffle prizes behind doors. Player chooses a random door, and Monty chooses
# the first of the two remaining doors that is not a car. Then the player
# changes his choice to the remaining door that wasn't chosen yet.
# If it's a car, increment the win count.
for trial in range(num_trials):
shuffle(items)
player = randrange(len(items))
monty = next(i for i, p in enumerate(items) if i != player and p != Prize.CAR)
player = next(i for i in range(len(items)) if i not in (player, monty))
num_wins += items[player] is Prize.CAR
print(f'{num_wins}/{num_trials} = {num_wins / num_trials * 100:.2f}% wins')
Previous solution
Yet another "proof," this time with Python 3. Note the use of generators to select 1) which door Monty opens, and 2) which door the player switches to.
import random
items = ['goat', 'goat', 'car']
num_trials = 100000
num_wins = 0
for trial in range(num_trials):
random.shuffle(items)
player = random.randrange(3)
monty = next(i for i, v in enumerate(items) if i != player and v != 'car')
player = next(x for x in range(3) if x not in (player, monty))
if items[player] == 'car':
num_wins += 1
print('{}/{} = {}'.format(num_wins, num_trials, num_wins / num_trials))
Monty never opens the door with the car - that's the whole point of the show (he isn't your friend an has knowledge of what is behind each door)
Here is different variant I find most intuitive. Hope this helps!
import random
class MontyHall():
"""A Monty Hall game simulator."""
def __init__(self):
self.doors = ['Door #1', 'Door #2', 'Door #3']
self.prize_door = random.choice(self.doors)
self.contestant_choice = ""
self.monty_show = ""
self.contestant_switch = ""
self.contestant_final_choice = ""
self.outcome = ""
def Contestant_Chooses(self):
self.contestant_choice = random.choice(self.doors)
def Monty_Shows(self):
monty_choices = [door for door in self.doors if door not in [self.contestant_choice, self.prize_door]]
self.monty_show = random.choice(monty_choices)
def Contestant_Revises(self):
self.contestant_switch = random.choice([True, False])
if self.contestant_switch == True:
self.contestant_final_choice = [door for door in self.doors if door not in [self.contestant_choice, self.monty_show]][0]
else:
self.contestant_final_choice = self.contestant_choice
def Score(self):
if self.contestant_final_choice == self.prize_door:
self.outcome = "Win"
else:
self.outcome = "Lose"
def _ShowState(self):
print "-" * 50
print "Doors %s" % self.doors
print "Prize Door %s" % self.prize_door
print "Contestant Choice %s" % self.contestant_choice
print "Monty Show %s" % self.monty_show
print "Contestant Switch %s" % self.contestant_switch
print "Contestant Final Choice %s" % self.contestant_final_choice
print "Outcome %s" % self.outcome
print "-" * 50
Switch_Wins = 0
NoSwitch_Wins = 0
Switch_Lose = 0
NoSwitch_Lose = 0
for x in range(100000):
game = MontyHall()
game.Contestant_Chooses()
game.Monty_Shows()
game.Contestant_Revises()
game.Score()
# Tally Up the Scores
if game.contestant_switch and game.outcome == "Win": Switch_Wins = Switch_Wins + 1
if not(game.contestant_switch) and game.outcome == "Win": NoSwitch_Wins = NoSwitch_Wins + 1
if game.contestant_switch and game.outcome == "Lose": Switch_Lose = Switch_Lose + 1
if not(game.contestant_switch) and game.outcome == "Lose": NoSwitch_Lose = NoSwitch_Lose + 1
print Switch_Wins * 1.0 / (Switch_Wins + Switch_Lose)
print NoSwitch_Wins * 1.0 / (NoSwitch_Wins + NoSwitch_Lose)
The learning is still the same, that switching increases your chances of winning, 0.665025416127 vs 0.33554730611 from the above run.
Here's one I made earlier:
import random
def game():
"""
Set up three doors, one randomly with a car behind and two with
goats behind. Choose a door randomly, then the presenter takes away
one of the goats. Return the outcome based on whether you stuck with
your original choice or switched to the other remaining closed door.
"""
# Neither stick or switch has won yet, so set them both to False
stick = switch = False
# Set all of the doors to goats (zeroes)
doors = [ 0, 0, 0 ]
# Randomly change one of the goats for a car (one)
doors[random.randint(0, 2)] = 1
# Randomly choose one of the doors out of the three
choice = doors[random.randint(0, 2)]
# If our choice was a car (a one)
if choice == 1:
# Then stick wins
stick = True
else:
# Otherwise, because the presenter would take away the other
# goat, switching would always win.
switch = True
return (stick, switch)
I also had code to run the game many times, and stored this and the sample output in this repostory.
Here is my solution to the MontyHall problem implemented in python.
This solution makes use of numpy for speed, it also allows you to change the number of doors.
def montyhall(Trials:"Number of trials",Doors:"Amount of doors",P:"Output debug"):
N = Trials # the amount of trial
DoorSize = Doors+1
Answer = (nprand.randint(1,DoorSize,N))
OtherDoor = (nprand.randint(1,DoorSize,N))
UserDoorChoice = (nprand.randint(1,DoorSize,N))
# this will generate a second door that is not the user's selected door
C = np.where( (UserDoorChoice==OtherDoor)>0 )[0]
while (len(C)>0):
OtherDoor[C] = nprand.randint(1,DoorSize,len(C))
C = np.where( (UserDoorChoice==OtherDoor)>0 )[0]
# place the car as the other choice for when the user got it wrong
D = np.where( (UserDoorChoice!=Answer)>0 )[0]
OtherDoor[D] = Answer[D]
'''
IfUserStays = 0
IfUserChanges = 0
for n in range(0,N):
IfUserStays += 1 if Answer[n]==UserDoorChoice[n] else 0
IfUserChanges += 1 if Answer[n]==OtherDoor[n] else 0
'''
IfUserStays = float(len( np.where((Answer==UserDoorChoice)>0)[0] ))
IfUserChanges = float(len( np.where((Answer==OtherDoor)>0)[0] ))
if P:
print("Answer ="+str(Answer))
print("Other ="+str(OtherDoor))
print("UserDoorChoice="+str(UserDoorChoice))
print("OtherDoor ="+str(OtherDoor))
print("results")
print("UserDoorChoice="+str(UserDoorChoice==Answer)+" n="+str(IfUserStays)+" r="+str(IfUserStays/N))
print("OtherDoor ="+str(OtherDoor==Answer)+" n="+str(IfUserChanges)+" r="+str(IfUserChanges/N))
return IfUserStays/N, IfUserChanges/N
I just found that global ratio of winning is 50% and ratio of losing is 50%... It is how the proportion on winning or losing based on selected final option.
%Wins (staying): 16.692
%Wins (switching): 33.525
%Losses (staying) : 33.249
%Losses (switching) : 16.534
Here is my code, that differs from yours + with commented comments so you can run it with small iterations :
import random as r
#iterations = int(raw_input("How many iterations? >> "))
iterations = 100000
doors = ["goat", "goat", "car"]
wins_staying = 0
wins_switching = 0
losses_staying = 0
losses_switching = 0
for i in range(iterations):
# Shuffle the options
r.shuffle(doors)
# print("Doors configuration: ", doors)
# Host will always know where the car is
car_option = doors.index("car")
# print("car is in Option: ", car_option)
# We set the options for the user
available_options = [0, 1 , 2]
# The user selects an option
user_option = r.choice(available_options)
# print("User option is: ", user_option)
# We remove an option
if(user_option != car_option ) :
# In the case the door is a goat door on the user
# we just leave the car door and the user door
available_options = [user_option, car_option]
else:
# In the case the door is the car door
# we try to get one random door to keep
available_options.remove(available_options[car_option])
goat_option = r.choice(available_options)
available_options = [goat_option, car_option]
new_user_option = r.choice(available_options)
# print("User final decision is: ", new_user_option)
if new_user_option == car_option :
if(new_user_option == user_option) :
wins_staying += 1
else :
wins_switching += 1
else :
if(new_user_option == user_option) :
losses_staying += 1
else :
losses_switching += 1
print("%Wins (staying): " + str(wins_staying / iterations * 100))
print("%Wins (switching): " + str(wins_switching / iterations * 100))
print("%Losses (staying) : " + str(losses_staying / iterations * 100))
print("%Losses (switching) : " + str(losses_switching / iterations * 100))

Categories