Anything wrong with my python Monty hall simulation? - python

Did I go wrong somewhere? I keep getting 50% wins even after switching.
import random
def monty_hall():
#-----setup ----#
prizes = ["Car" , "Goat" , "Goat"]
random.shuffle(prizes)
#----person chooses at random----#
choose_index = random.randint(0,2)
##-------host reveals a goat------#
while True:
goat_gate = random.randint(0, 2)
if prizes[goat_gate] == "Goat":
break
##------person switches -------##
while True:
switch_choice = random.randint(0, 2)
if (switch_choice!= choose_index) & (switch_choice!= goat_gate):
break
## -- check if won---#
if prizes[switch_choice] == "Car":
return True
win = 0
games = 100000
for times in range(games):
if monty_hall() == True:
win += 1
print(win/games)

Yes, you didn't include the fact that the host will not reveal what's behind the chosen door.
while True :
goat_gate = random.randint(0, 2)
if prizes[goat_gate] == "Goat" :
break
should be
while True :
goat_gate = random.randint(0, 2)
if prizes[goat_gate] == "Goat" and goat_gate != choose_index:
break

Related

how to make functions cooperate

So I was going to make an arithmetic operator:
import random
solution = 0
summation = False
multiplication_table = False
subtraction = False
the_exercise = False
def arithmetic_operation(arithmetic_type):
stopper = []
exercise = input(
f"This is the {arithmetic_type} generator. Subjects: summation, multiplication table, subtraction.\nWhich of the subjects is "
"the student to practice? Please choose one of these 3 specifically")
if exercise == 'summation':
summation = True
elif exercise == 'multiplication table':
multiplication_table = True
elif exercise == 'subtraction':
subtraction = True
while summation == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(0, 50)
b = random.randint(0, 50)
solution = a + b
print(f"What is {a} + {b}?")
the_exercise = True
return solution
while multiplication_table == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(0, 10)
b = random.randint(0, 10)
solution = a * b
print(f"What is {a} * {b}?")
the_exercise = True
return solution
while subtraction == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(21, 100)
b = random.randint(0, 20)
solution = a - b
print(f"What is {a} - {b}?")
the_exercise = True
return solution
arithmetic_operation("artithmetic exercise")
def main():
while the_exercise == True:
ans = input()
if int(ans) == solution:
print("Correct!")
stopper.append("STOP!")
if stopper.count("STOP!") == 10:
print("That's the end. Until next time!")
summation == False
else:
break
else:
print("Wrong! Try again")
ten_exercises += 1
if __name__ == '__main__':
main()
The problem is that my two functions aren't cooperating. According to the exercise, 'arithmetic_operation' is supposed to have a parameter.
What is it that I fail to understand? I have tried multiple solutions and replacing blocks of code to the main, but it doesn't work...

Python keyboard module not working properly inside loop

I'm trying to create a simple game that creates math problems and the users task is to decide if they are true or false. (eg. 2 + 2 = 6, True or False?)
I am using the keyboard module and I want to have the user press the left arrow key if he thinks that the problem is true, and the right one if he thinks that it's false.
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
The thing is, after I paste this function into a loop, I can only press left or right once. After that the rest of the code is executed without waiting for my keypress.
from problems import addition_easy
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()
This returns (for input of 5):
How many exercises would you like to solve? 5
6 + 1 = 9
True or False? //(Here it waits for me to press "left" or "right")
3 + 3 = 8
True or False? //(From here it doesn't stop to wait for a keypress)
4 + 3 = 7
True or False? //(Same here and so on...)
2 + 3 = 3
True or False?
1 + 2 = 3
True or False?
How can I make it wait for a keypress every time it prints out a math problem?
If the user holds down "left" for half a second, and addition_easy executes a hundred times in that half second, then keyboard.is_pressed('left') will evaluate to True for every one of them, even though the user only pressed "left" once.
You can verify that is_pressed doesn't permanently consider "left" to be pressed by telling your program to do 1000 problems. Pressing left will only answer about 20 of them.
One possible solution is to alter your loop so it waits until the key is subsequently released before continuing.
while True:
if keyboard.is_pressed('left'):
user_answer = True
while keyboard.is_pressed("left"):
pass
break
elif keyboard.is_pressed('right'):
user_answer = False
while keyboard.is_pressed("right"):
pass
break
Another possible design is to use keyboard.on_press_key, which should only fire when the key changes state from "not pressed" to "pressed" (or when the auto repeat time elapses, which probably won't happen unless the user is doing it intentionally). You can abstract this out to a function to keep your addition_easy function clean:
import random
import keyboard
import time
def wait_for_keys(keys):
key_pressed = None
def key_press_event(key):
nonlocal key_pressed
key_pressed = key.name
for key in keys:
keyboard.on_press_key(key, key_press_event)
while key_pressed is None:
time.sleep(0.01)
return key_pressed
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
key = wait_for_keys(["left", "right"])
user_answer = (key == "left")
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = 1000
for exercise in range(1, exercise_amount + 1):
addition_easy()
Not sure if you indented your function correctly. Try:
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()

Why does this implementation of the Monty Hall give the probability to be 50%?

I recently watched a video about the Monty Hall Problem and was interested in the fact that the contestant had a 2/3 chance of winning when switching. So, I decided to write a simulation for it to see it for myself. However, my simulation gave the answer to be 50%. Could somebody point out why? Note: the number of doors can be adjusted by changing num_of_doors.
from random import randint
num_of_doors = 3
num_of_simulations = 0
wins = 0
while True:
num_of_simulations += 1
doors = {k: "Donkey" for k in range(1, num_of_doors + 1)}
car = randint(1, num_of_doors)
doors[car] = "Car"
choice = randint(1, num_of_doors)
while len(doors) > 2:
reveal = randint(1, num_of_doors)
if reveal in doors:
if reveal != choice and doors[reveal] != "Car":
del doors[reveal]
for k in doors:
if k != choice:
choice = k
if doors[choice] == "Car":
wins += 1
print(100 * wins / num_of_simulations)
You've found the answer, but I thought I'd just post my shot at this:
from random import randint
def monty(n_doors):
car = randint(0, n_doors - 1)
first_choice = 0 # always pick 0 for convenience
remaining_door = car if first_choice != car else 1 # 1 for convenience
return remaining_door == car
total_runs = 10000
trials = [monty(3) for x in range(total_runs)]
print(sum(trials) / total_runs)
Gives:
0.6705

Micro:bit classes instead of global variables - memory allocation error in micropython

I've just created my first little truth or dare/spin the bottle game for a senior/high school coding club on the Micro:bit. I would like to introduce using oop/classes/objects instead of (dreaded) global vars. The game works great on emulators such as https://create.withcode.uk/, but on the Micro:bit itself I encounter memory allocation errors as soon as I try to put pretty much anything into classes. Is the microbit's 16KB of RAM not enough? Or am I declaring classes incorrectly?
Coming from front-end and a bit of PHP/SQL, I'm a Python/memory knowledge noob so any help is appreciated.
If I use global vars in each of the functions it works without issue.
Here is the code:
from microbit import *
import random
#timer current function
#Global variables
class game:
gsTime = 3000
timerPrev = 0
numOfPlayers = 0
maxPlayers = 8
stage = 'start'
minSpinTime = 3000
class player:
#Which player is currently selected
selected = 0
#Player position display
def pos1(): display.set_pixel(2, 4, 9)
def pos2(): display.set_pixel(2, 0, 9)
def pos3(): display.set_pixel(0, 2, 9)
def pos4(): display.set_pixel(4, 2, 9)
def pos5(): display.set_pixel(0, 4, 9)
def pos6(): display.set_pixel(4, 0, 9)
def pos7(): display.set_pixel(0, 0, 9)
def pos8(): display.set_pixel(4, 4, 9)
#Array of all player positions
positions = [
[pos1, 1, Image.ARROW_S],
[pos2, 5, Image.ARROW_N],
[pos3, 3, Image.ARROW_W],
[pos4, 7, Image.ARROW_E],
[pos5, 2, Image.ARROW_SW],
[pos6, 6, Image.ARROW_NE],
[pos7, 4, Image.ARROW_NW],
[pos8, 8, Image.ARROW_SE]
]
positionsOrdered = []
class buttons:
pressed = False
class spinner:
completeSpins = 0
isCompleteSpin = False
rdTime = 0
stage = 'start'
stageStarted = False
gameCall = game()
playerCall = player()
buttonsCall = buttons()
spinnerCall = spinner()
#Return a random range of numbers
def rdRange(minMult,maxMult,base):
return random.randint(base*minMult, base*maxMult)
#return sort key of list
def getKey(item):
return item[1]
#Timer function
def timer(timeElapsed, onCompleteFunc):
if running_time() - gameCall.timerPrev >= timeElapsed:
onCompleteFunc()
#Position Players Start is true
def positionPlayersStartTrue():
game.stage = 'positionPlayers'
def selectNumOfPlayers(gteOrLte):
game.timerPrev = running_time()
if gteOrLte == 'gte':
if gameCall.numOfPlayers >= gameCall.maxPlayers:
game.numOfPlayers = 1
else:
game.numOfPlayers += 1
else:
if gameCall.numOfPlayers <= 1:
game.numOfPlayers = maxPlayers
else:
game.numOfPlayers -= 1
display.show(str(gameCall.numOfPlayers)) #Have to convert int to string before passing to display.show func
buttons.pressed = True
#Ask for number of players up to maxPlayers.
def setPlayerNum():
#If B is pressed increment by 1 up the max players and cycle back to 1
if button_b.was_pressed():
selectNumOfPlayers('gte')
#If A is pressed decrement by 1 down to 1 and then cycle back to maxPlayers var
elif button_a.was_pressed():
selectNumOfPlayers('lte')
elif buttonsCall.pressed == False:
#Ask how many players
display.show('#?')
else:
timer(gameCall.gsTime, positionPlayersStartTrue)
#display the position of players
def positionPlayers():
buttons.pressed = False
display.clear()
for i in range(gameCall.numOfPlayers):
el = player.positions[i]
player.positionsOrdered.append(el)
el[0]()
player.positionsOrdered.sort(key=getKey)
while buttonsCall.pressed == False:
startSpin()
#start the spin - useful for starting the spin after one spin was complete too
def startSpin():
if button_a.was_pressed() or button_b.was_pressed():
buttons.pressed = True
game.stage = 'spin'
#Spin start
def spin():
if spinnerCall.stage == 'start' and spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(200, 700, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinner.rdTime)
for i in range(gameCall.numOfPlayers):
display.clear()
el = player.positionsOrdered[i]
el[0]()
if i + 1 == gameCall.numOfPlayers:
spinner.completeSpins += 1
spinner.isCompleteSpin = True
else:
spinner.isCompleteSpin = False
if spinnerCall.stage == 'start':
if (running_time() - gameCall.timerPrev >= (gameCall.minSpinTime + spinnerCall.rdTime)) and (spinnerCall.isCompleteSpin == True):
spinner.stage = 'slow'
spinner.stageStarted = False
sleep(200)
#Slower spin to emulate spinner slowing down as it comes near to stopping. Should probably use some clever-er maths here instead.
elif spinner.stage == 'slow':
if spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(500, 900, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinnerCall.rdTime)
if running_time() - gameCall.timerPrev >= spinnerCall.rdTime:
spinner.stage = 'stop'
spinner.stageStarted = False
sleep(400)
elif spinner.stage == 'stop':
player.selected = i
game.stage = 'selectedPlayer'
# reset spinner stage for next spin
spinner.stage = 'start'
break
#Player has been selected
def selectedPlayer():
el = playerCall.positionsOrdered[playerCall.selected]
sleep(200)
display.show(el[2])
sleep(200)
display.clear()
while True:
#CALL FUNCTIONS
if gameCall.stage == 'start':
setPlayerNum()
elif gameCall.stage == 'positionPlayers' and buttonsCall.pressed == True:
positionPlayers()
elif gameCall.stage == 'spin':
spin()
elif gameCall.stage == 'selectedPlayer':
#print('this one is selected ', playerCall.selected)
selectedPlayer()
#start spin again if button is pressed
startSpin()
Your code is too big for microbit. Microbit is limited by 16KB of RAM. To decrease size of your code you can:
minify it directly from Mu editor or use any other minifier lib
Shrink variable names
Delete comments

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