Monty Hall simulator has a wrong outcome - python

I wanted to do something seemingly simple and at the same time prove Monty Hall by code, and sadly what I got was not a proof of the problem, but the complete opposite. No matter if I switch or not, I get ~33% odds of winning across 10000 simulations. Could you check the code and see what's wrong with it? Keep in mind that I'm a web-dev so my Python skills are not great and the code might not look fantastic (for example, I made a removable_doors list where just a variable would've worked fine). Cheers!
import random
av_doors = [1, 2, 3]
door_v = [1, 2, 3]
removable_doors = [1, 2, 3]
score = 0
loses = 0
door = int(input("Choose your door (1-3)"))
pick = int(door)-1
dec = input("Stay or switch? (0/1)")
n = 0
while n < 10000:
removable_doors = [1, 2, 3]
av_doors = [1, 2, 3]
val = random.randint(1,3)
if val == 1:
door_v[0] = 1
door_v[1] = 0
door_v[2] = 0
removable_doors.remove(1)
elif val == 2:
door_v[0] = 0
door_v[1] = 1
door_v[2] = 0
removable_doors.remove(2)
elif val == 3:
door_v[0] = 0
door_v[1] = 0
door_v[2] = 1
removable_doors.remove(3)
try:
removable_doors.remove(int(door))
except:
pass
av_doors.remove(door)
if len(removable_doors) == 2:
rand_door = random.randint(0,1)
if rand_door == 0:
del av_doors[0]
elif rand_door ==1:
del av_doors[1]
else:
del av_doors[0]
if dec == "1":
door = av_doors[0]
else:
pass
pick = door-1
if door_v[pick] == 1:
score+=1
else:
loses+=1
n+=1
print(score)
print(loses)

Your host sometimes removes a door with the car behind it... Here is a solution that works, and (I think) a bit better written, while keeping your structures (in python3 but that should not raise troubles):
import random
win = 0
lose = 0
switch = int(input("Stay or switch? (0-stay/1-switch):")) == 1
n = 0
while n < 10000:
# Init
door_values = [0, 0, 0]
removable_doors = [0, 1, 2]
available_doors = [0, 1, 2]
# Placing the reward somewhere
car_place = random.randint(0, 2)
door_values[car_place] = 1
removable_doors.remove(car_place)
# Choose a door
door_chosen = random.randint(0, 2)
available_doors.remove(door_chosen)
if door_chosen != car_place:
removable_doors.remove(door_chosen)
# Host removes a door that does not have the car and has not been chosen by the player
door_removed_by_host = removable_doors[random.randint(0, len(removable_doors)-1)]
available_doors.remove(door_removed_by_host)
# Switch if specified
if switch:
assert(len(available_doors) == 1)
door_chosen = available_doors[0]
# Check the result
if car_place == door_chosen:
win += 1
else:
lose += 1
n+=1
print('win=%s'%str(win))
print('lose=%s'%str(lose))
print('ratio=%s'%str(win/(win+lose)))
It gives me 0.3332 when switch=False, 0.6738 for switch=True, I guess this rather confirms Monty Hall's solution :)

Related

how to make an if statement based on a list

so I have to make a rock paper scissors game in my class today and was wondering how to make an if statement based on a list's order than setup a loop for that
here is the code
def RockPaperScissorsFunction4(roundnumberforcurrentpairing, mychoices[], opponentchoices[]):
import random
first_random = random.randint(1, 3)
# 1 = rock 2 = paper 3 = scissors
if (roundnumberforcurrentpairing == 1 ):
mynextmove = 1
# mynextmove = opponentchoices[roundnumberforcurrentpairing - 2]
if roundnumberforcurrentpairing < 3:
mynextmove = 1
if opponentchoices[1, 3, 3, 3,]:
if opponentchoices[2, 3, 3, 3,]:
if opponentchoices[3, 3, 3, 3,]:
Is that you want to For loop with Array child ?
If you want opporentchoice [1,3,3,3] to came out as
1
3
3
3
then compare with first_random
here example :
opponentchoices = [1,3,3,3]
for x in opponentchoices:
first_random = random.randint(1, 3)
if(first_random > x):
print("You win")
else:
print("Opporent win")
Anyway you need to us more detail in this question
update
import random
def RockPaperScissorsFunction4(totalround, MyChoices, opponentchoices):
RoundCount = 2
MaxRound = len(opponentchoices) # We do this because we Don't need Max round more than opponent's choice
if(totalround > MaxRound): # if total round is more than choice Set total round = length of opponent's Array
totalround = MaxRound
print("Set totalround = ",totalround)
for opponent_answer in opponentchoices:
if RoundCount >= totalround:
break
if opponent_answer > opponent_answer:
print("opponent win!")
else :
print("Player win!")
RoundCount = RoundCount + 1
player_answer = random.randint(1, 3)
opponent_array = [1, 3, 3, 3]
RockPaperScissorsFunction4(5,player_answer,opponent_array)
player_answer = random.randint(1, 3)
opponent_array = [2, 3, 3, 3]
RockPaperScissorsFunction4(5,player_answer,opponent_array)
player_answer = random.randint(1, 3)
opponent_array = [3, 3, 3, 3]
RockPaperScissorsFunction4(5,player_answer,opponent_array)

minimax algorithm not working in a two-player turn-based game

I am trying to use minimax algo to find a best move for a game.
The game is 2 players take turns to pick a number from a pile [0,1,2,,,8]
Winning Condition: player wins the game if the player contains three numbers and their sum is 14.
The initial game state is given by the argument like:
4 1 2 3 4
The first number indicate the total length and the following numbers are the numbers taken by player each turn. In this example the player A has [1,3] and player B has [2,4]
My problem is the algo can't give the correct answer:
Like when the input is 4 4 1 7 8 my program cannot gives the correct move: it takes 0 not 3 for next move.
import sys
import math
totalMoves = int(sys.argv[1])
pile = [0, 1, 2, 3, 4, 5, 6, 7, 8]
# who are u
you = []
opponent = []
# cards in hand
playerA = []
playerB = []
# get all the cards into hand
for i in range(totalMoves):
# save card into playerB
if i % 2 != 0:
playerB.append(int(sys.argv[i+2]))
# save card into playerA
if i % 2 == 0:
playerA.append(int(sys.argv[i+2]))
# remove sent card in list
pile.remove(int(sys.argv[i+2]))
# identify which player you are
if totalMoves % 2 == 0:
you = playerA
opponent = playerB
else:
you = playerB
opponent = playerA
def find3Numbers(player): # calcluate the card in hand is equal to 14
arr_size = len(player)
if (len(player) >= 3):
for i in range(0, arr_size-2):
for j in range(i + 1, arr_size-1):
for k in range(j + 1, arr_size):
if (player[i] + player[j] + player[k] == 14):
return True
else:
return False
def isNumberLeft(board):
if (len(board) == 0):
return False
else:
return True
def evaluate(player, oppo):
if (find3Numbers(player) == True):
return 10
elif (find3Numbers(oppo) == True):
return -10
else:
return 0
def minimax(board, player, oppo, depth, isMax):
score = evaluate(player, oppo)
if (score == 10):
return score
if (score == -10):
return score
if (isNumberLeft(board) == False):
return 0
if (isMax):
best = -math.inf
for card in board:
player.append(card)
board.remove(card)
best = max(best, minimax(board, player, oppo, depth+1, not isMax))
board.append(card)
player.remove(card)
return best
else:
best = +math.inf
for card in board:
oppo.append(card)
board.remove(card)
best = min(best, minimax(board, player, oppo, depth+1, not isMax))
board.append(card)
oppo.remove(card)
return best
def bestMove():
bestScore = -math. inf
bestNum = -math.inf
for card in pile:
you.append(card)
pile.remove(card)
score = minimax(pile, you, opponent, 0, False)
pile.append(card)
you.remove(card)
if (bestScore < score):
bestNum = card
bestScore = score
return bestNum
you.append(bestMove())

operating function in another function

I'm trying to operate 50/50 player win/lose game based on random.sample([0,1],1).
def player_win():
global player_win_count
player_win_count += 1
return player_win_count
def player_lose():
global dealer_win_count
dealer_win_count += 1
return dealer_win_count
def game_draw():
draw_game_count += 1
return draw_game_count
def gameplay():
i = 0
while i <= 300:
test01 = random.sample([0,1],1)
i += 1
if test01 == 1:
player_win()
else:
player_lose()
print("player_win",player_win_count)
print("dealer_win",dealer_win_count)
gameplay()
The 'random.sample' code works fine, but instead of expected result, it returns
player_win 0
dealer_win 301
what's wrong with my code?
random.sample will return a list of the samples so test01 == 1 is always False.
Example:
In [181]: random.sample([0, 1], 1)
Out[181]: [1]
In [182]: random.sample([0, 1], 1)
Out[182]: [0]
In [183]: random.sample([0, 1], 1)
Out[183]: [1]
In [184]: random.sample([0, 1], 1)
Out[184]: [0]
One solution is test01[0] == 1.
Alternatively, one could use random.choices instead and generate all the flips at once:
In [188]: wins = 0
...: losses = 0
...: for flip in random.choices([0, 1], k=20):
...: if flip:
...: wins += 1
...: else:
...: losses += 1
In [189]: print(wins, losses)
7 13
As #salt-die has noted, random.sample() is designed to return a sample of the population and does so as a list.
To achieve the intended result, you could substitute random.choice() for random.sample(), which will return a single selection from the population:
def gameplay():
i = 0
while i <= 300:
test01 = random.choice([0,1]) # use random.choice()
i += 1
if test01 == 1:
player_win()
else:
player_lose()
print("player_win",player_win_count)
print("dealer_win",dealer_win_count)
If for some reason, you are required to use random.sample(), then you can use the following modification to select the first element out of the list of sample results:
while i <= 300:
test01 = random.sample([0,1], 1)[0] # use list indexing to get
# the zeroeth item from the list
Lastly, as a point of style:
It is sometimes considered Pythonic to simply test for truthiness/falsiness and thus this code could be refined a bit, if desired:
while i <= 300:
i += 1
if random.choice([0, 1]): # A `1` is considered equivalent to True
# in Python, so we can skip setting the value
# of test01 to being either 1 OR 0 and
# simply return a 1 or 0 into the if statement.
Best practice in Python is to avoid global scopes so something like this is better if you really had to use a function.
import random
def _counter(count: int):
count += 1
return count
def gameplay():
draw_game_count = 0
dealer_win_count = 0
player_win_count = 0
for i in range(300):
test01 = random.randint(0, 2)
if test01 == 0:
draw_game_count = _counter(draw_game_count)
elif test01 == 1:
player_win_count = _counter(player_win_count)
else:
dealer_win_count = _counter(dealer_win_count)
print("draw", draw_game_count)
print("player_win", player_win_count)
print("dealer_win", dealer_win_count)
if __name__ == "__main__":
gameplay()
Otherwise you could simply do this:
import random
def gameplay():
draw_game_count = 0
dealer_win_count = 0
player_win_count = 0
for i in range(300):
test01 = random.randint(0, 2)
if test01 == 0:
draw_game_count += 1
elif test01 == 1:
player_win_count += 1
else:
dealer_win_count += 1
print("draw", draw_game_count)
print("player_win", player_win_count)
print("dealer_win", dealer_win_count)
if __name__ == "__main__":
gameplay()

Mastermind Python coding

Ok I have a feeling that this is a simple simple issue but I have been staring at this code for about 10 hours now.
The issue I am having is in mastermind is that once I get it to recognize that I have the correct colors in the right spot I can get it to display the right spots with X and the wrong spots with O. I need to be able to convert that so instead of X and O I need it to tell the user that he/she has 2 blacks and one white
For example: The secret code is RGYB The user enters RGOY so then Python relays "You have 2 blacks(The R and G spots) and one 1 White (The Y because it's the right color just in the wrong index) As of right now I got it to display X for the right color in the right spot and anything else it is an O
I will post what I have been working with now but today I am at my wit's end
https://pastebin.com/HKK0T7bQ
if correctColor != "XXXX":
for i in range(4):
if guess[i] == tempCode[i]:
correctColor += "X"
if guess[i] != tempCode[i] in tempCode:
correctColor += "O"
print (correctColor + "\n")
if correctColor == "XXXX":
if attempts == 1:
print ("You think you are sweet because you got it right on the first try? Play me again!")
else:
print ("Well done... You needed " + str(attempts) + " attempts to guess.")
game = False
A few comments
X and O
you use X and 0 to denote the success, it will be easier and faster to use a list or tuple or booleans for this, that way you can use sum() to count how many colors and locations were correct. Then whether you represent that with X and O or red and white pins is a matter for later
compartmentalization
Your game logic (guess input, input validation, do you want to continue, etc) is mixed with the comparison logic, so it would be best to separate the different functions of your program into different methods.
This is an fineexample to introduce object oriented programming, but is so simple it doesn't need OO, but it can help. What you need is a method which takes a series of colours and compares it to another series of colours
Standard library
Python has a very extended standard library, so a lot of stuff you want to do probably already exists
Correct colours
to count the number of letters which occur in 2 strings, you can use collections.Counter
guess = "RGOY "
solution = "RGYB"
a = collections.Counter(guess)
b = collections.Counter(solution)
a & b
Counter({'G': 1, 'R': 1, 'Y': 1})
correct_colours = sum((a & b).values())
3
So the user guessed 3 colours correctly
Correct locations
can be solved with an easy list comprehension
[g == s for g, s in zip(guess, solution)]
[True, True, False, False]
sum(g == s for g, s in zip(guess, solution))
2
so the used put 2 colours on the correct location
This is a MasterMind I made in Python. Hope you like it and it helped you! :)
import random
import time
from tkinter import *
def select_level():
global level
level = level_selector.get()
root.destroy()
root = Tk()
level_selector = Scale(root, from_=1, to=3, tickinterval=1)
level_selector.set(0)
level_selector.pack()
Button(root, text="Select a difficulty level", command=select_level).pack()
mainloop()
cpc_1_digit = 0
cpc_2_digit = 0
cpc_3_digit = 0
cpc_4_digit = 0
p_1_digit = 0
p_2_digit = 0
p_3_digit = 0
p_4_digit = 0
correct_correct = 0
correct_wrong = 0
chances = 0
if level == 1:
chances = 15
elif level == 2:
chances = 10
else:
chances = 7
cpc_1_digit = random.randint(0, 9)
while cpc_2_digit == cpc_1_digit or cpc_2_digit == cpc_3_digit or cpc_2_digit ==
cpc_4_digit:
cpc_2_digit = random.randint(0, 9)
while cpc_3_digit == cpc_1_digit or cpc_3_digit == cpc_2_digit or cpc_3_digit ==
cpc_4_digit:
cpc_3_digit = random.randint(0, 9)
while cpc_4_digit == cpc_1_digit or cpc_4_digit == cpc_2_digit or cpc_4_digit ==
cpc_3_digit:
cpc_4_digit = random.randint(0, 9)
while chances > 0:
correct_correct = 0
correct_wrong = 0
answer = input("Enter a four-digit number with different digits (e.g 1476): ")
p_1_digit = int(answer[0])
p_2_digit = int(answer[1])
p_3_digit = int(answer[2])
p_4_digit = int(answer[3])
if p_1_digit == cpc_1_digit:
correct_correct = int(correct_correct) + 1
elif p_1_digit == cpc_2_digit or p_1_digit == cpc_3_digit or p_1_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_2_digit == cpc_2_digit:
correct_correct = correct_correct + 1
elif p_2_digit == cpc_1_digit or p_2_digit == cpc_3_digit or p_2_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_3_digit == cpc_3_digit:
correct_correct = int(correct_correct) + 1
elif p_3_digit == cpc_1_digit or p_3_digit == cpc_2_digit or p_3_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_4_digit == cpc_4_digit:
correct_correct = int(correct_correct) + 1
elif p_4_digit == cpc_1_digit or p_4_digit == cpc_3_digit or p_4_digit ==
cpc_2_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
print("")
if int(correct_correct) == 4:
print("Congratsulations! You found the computer's number!")
break
elif int(correct_wrong) > 0 or int(correct_correct) >= 1 and int(correct_correct)
< 4:
print("You got " + str(correct_correct) + " correct digit(s) in the correct
place, and " + str(correct_wrong) + " correct digit(s) but in wrong place.")
elif int(correct_correct) == 0 and int(correct_wrong) == 0:
print("You didn't guess any number, try again!")
else:
raise Exception("CheckError: line 69, something went wrong with the
comparings.")
exit()
print("")
chances = chances - 1
if chances == 0:
print("You lost... The secret number was " + str(cpc_1_digit) + str(cpc_2_digit)
+ str(cpc_3_digit) + str(cpc_4_digit) + ". Try again by rerunning the program.")
time.sleep(4)

Monty Hall simulation not working as intended

I've been trying out to solve the monty hall problem in Python in order to advance in coding, which is why I tried to randomize everything. The thing is: I've been running into some trouble. As most of you probably know the monty problem is supposed to show that changing the door has a higher winrate (66%) than staying on the chosen door (33%). For some odd reason though my simulation shows a 33% winrate for both cases and I am not really sure why.
Here's the code:
from random import *
def doorPriceRandomizer():
door1 = randint(0,2) #If a door is defined 0, it has a price in it
door2 = randint(0,2) #If a door is defined either 1 or 2, it has a goat in it.
door3 = randint(0,2)
while door2 == door1:
door2 = randint(0,2)
while door3 == door2 or door3 == door1:
door3 = randint(0,2)
return door1,door2,door3 #This random placement generator seems to be working fine.
while True:
loopStart = 0
amountWin = 0
amountLose = 0
try:
loopEnd = int(input("How often would you like to run this simulation: "))
if loopEnd < 0:
raise ValueError
doorChangeUser = int(input("[0] = Do not change door; [1] = Change door: "))
if doorChangeUser not in range(0,2):
raise ValueError
except ValueError:
print("Invalid input. Try again.\n")
else:
while loopStart != loopEnd:
gameDoors = doorPriceRandomizer()
inputUser = randint(0,2)
if doorChangeUser == 0:
if gameDoors[inputUser] == 0:
amountWin += 1
loopStart += 1
else:
amountLose += 1
loopStart += 1
elif doorChangeUser == 1:
ChangeRandom = 0
while gameDoors[ChangeRandom] == gameDoors[inputUser]:
ChangeRandom = randint(0,2)
if gameDoors[ChangeRandom] == 0:
amountWin += 1
loopStart += 1
else:
amountLose += 1
loopStart += 1
print("Win amount: ",amountWin,"\tLose amount: ",amountLose)
What am I doing wrong? I really appreciate all help! Thanks in advance!
ChangeRandom = 0
while gameDoors[ChangeRandom] == gameDoors[inputUser]:
ChangeRandom = randint(0,2)
This doesn't do what you think it does. Instead of checking if the ChangeRandom door is the same as the inputUser door, this checks if the ChangeRandom door and the inputUser door have the same value -- that is to say they're either both winners or both losers.
That said, that's not even what you want to do. What you want to do is to find a door that's not the user's input that IS a loser door, then switch to the OTHER one that isn't the user's input. This could be implemented with minimal change to your code as:
other_wrong_door = next(c for c, v in enumerate(gameDoors) if v != 0 and c != inputUser)
new_door = next(c for c, _ in enumerate(gameDoors) if c != inputUser and c != other_wrong_door)
But honestly this merits a re-examining of your code's structure. Give me a few minutes to work something up, and I'll edit this answer to give you an idea of how I'd implement this.
import random
DOORS = [1, 0, 0]
def runonce(switch=False):
user_choice = random.choice(DOORS)
if user_choice == 1:
# immediate winner
if switch:
# if you won before and switch doors, you must lose now
return False
else:
new_doors = [0, 0] # remove the user-selected winner
new_doors = [0] # remove another loser
return bool(random.choice(new_doors))
# of course, this is always `0`, but
# sometimes it helps to show it. In production you
# wouldn't bother writing the extra lines and just return False
else:
if switch:
new_doors = [1, 0] # remove the user-selected loser
new_doors = [1] # remove another loser
return bool(random.choice(new_doors))
# as above: this is always True, but....
else:
return False # if you lost before and don't switch, well, you lost.
num_trials = int(input("How many trials?"))
no_switch_raw = [run_once(switch=False) for _ in range(num_trials)]
switch_raw = [run_once(switch=True) for _ in range(num_trials)]
no_switch_wins = sum(1 for r in no_switch_raw if r)
switch_wins = sum(1 for r in switch_raw if r)
no_switch_prob = no_switch_wins / num_trials * 100.0
switch_prob = switch_wins / num_trials * 100.0
print( " WINS LOSSES %\n"
f"SWITCH: {switch_wins:>4} {num_trials-switch_wins:>6} {switch_prob:.02f}\n"
f"NOSWITCH:{no_switch_wins:>4} {num_trials-no_switch_wins:>6} {no_switch_prob:.02f}")
You have gotten the mechanics of the problem wrong so you are getting the wrong result. I have rewritten the choice mechanics, but I am leaving the user input stuff to you so that you can continue to learn python. This is one of many ways to solve the problem, but hopefully it demonstrates some things to you.
def get_choices():
valid_choices = [0, 1, 2] # these are the values for a valid sample
shuffle(valid_choices) # now randomly shuffle that list
return valid_choices # return the shuffled list
def get_door(user_choice):
return user_choice.index(0)
def monty_sim(n, kind):
"""
:param n: number of runs in this simulation
:param kind: whether to change the door or not, 0 - don't change, 1 = change door
:return: (win_rate, 1 - win_rate)
"""
wins = 0
for i in range(0, n):
game_doors = get_choices()
user_choice = get_door(get_choices()) # use the same method and find user door choice
# so there are two branches.
# In both, a door with a goat (game_door = 1) is chosen, which reduce the result to
# a choice between two doors, rather than 3.
if kind == 0:
if user_choice == game_doors.index(0):
wins += 1
elif kind == 1:
# so now, the user chooses to change the door
if user_choice != game_doors.index(0):
wins += 1
# Because the original choice wasn't the right one, then the new
# must be correct because the host already chose the other wrong one.
win_rate = (wins / n) * 100
return win_rate, 100 - win_rate
if __name__ == '__main__':
n = 1000
kind = 1
wins, loses = monty_sim(n, kind)
print(f'In a simulation of {n} experiments, of type {kind} user won {wins:02f} of the time, lost {loses:02f} of the time')

Categories

Resources