I mainly write code for data analysis so don't think about or use OOP at all on the day job. I thought I'd have a go at a simple Monty Hall simulation in an object-oriented style to figure out how it works - specifically I based it on this: https://www.reddit.com/r/dailyprogrammer/comments/n94io8/20210510_challenge_389_easy_the_monty_hall_problem/
Appreciate this project has been done to death but I just want to get a bit of feedback on whether I 'get' OOP or not. Having written a lot of code in a procedural style, I found it quite confusing. Note that the strategies in the strategise
method are from the link above. If an unrecognised name is used, it just picks a second door randomly.
What I specifically struggled with are things like:
- What should be an object? There were lots of things that could have been. Doors, maybe a game show host, maybe the prizes... in the end I kept it simple but that was only after a few iterations and getting stuck.
- Am I using the classes/methods correctly when running the simulation? Would you surface more of the methods rather than using a 'wrapper' method like I've done with
Contestant.play_the_game
? - The
strategise
method is a bit of a mess. Is there a better way of implementing this that isn't just a load ofif...else
?
Any other thoughts/criticisms on general style and anything else are welcome - I don't generally code in Python either so this was all very new.
My class definitions are in a file called monthHall.py:
import random
class MontyHall:
def __init__(self):
'''The Monty Hall game! Three doors, 2 goats, one car.'''
self.doors = {1: "goat", 2: "goat", 3: "goat"}
prize_door = random.randint(1, 3)
self.doors[prize_door] = "car"
def check_door(self, door_number):
'''Checks what is behind a given door.'''
return self.doors[door_number]
def first_reveal(self, door_number):
'''Takes a door number picked by the contestant and opens one of other doors with a goat.'''
doors = [1, 2, 3]
doors.remove(door_number)
prizes = list(map(self.check_door, doors))
if "car" in prizes:
del doors[prizes.index("car")]
door = random.choice(doors)
return door
def second_reveal(self, door_number):
'''Takes the contestant's second pick and reveals their prize.'''
prize = self.check_door(door_number)
return prize
class Contestant:
def __init__(self, name):
'''A contestant to play the Monty Hall game.'''
self.name = name
self.wins = []
self.second_door = 0
if self.name in ["Alice", "Bob", "Frank", "Gina"]:
self.first_door = 1
else:
self.first_door = random.choice([1, 2, 3])
def strategise(self, doors):
'''Picks a second door. If the contestant name is not recognised, it just picks randomly.'''
if self.name in ["Alice", "Dave"]:
self.second_door = self.first_door
elif self.name in ["Bob", "Erin"]:
self.switch_doors(doors)
elif self.name == "Frank":
if 2 in doors:
self.second_door = 2
else:
self.second_door = self.first_door
elif self.name == "Gina":
if not self.wins:
self.second_door = self.first_door
else:
if self.wins[-1]:
if self.second_door != self.first_door:
self.switch_doors(doors)
else:
if self.second_door != self.first_door:
self.second_door = self.first_door
else:
self.switch_doors(doors)
else:
self.second_door = random.choice(doors)
def switch_doors(self, doors):
'''Switches doors for the second contestant pick.'''
doors.remove(self.first_door)
self.second_door = doors[0]
def first_pick(self, competition):
'''Pass the first door choice to the competition to open one of the other doors, and then pick a strategy.'''
open_door = competition.first_reveal(self.first_door)
doors = [1, 2, 3]
doors.remove(open_door)
self.strategise(doors)
def second_pick(self, competition):
'''Pass the second door choice to the competition to reveal the prize!'''
prize = competition.second_reveal(self.second_door)
if (prize == "car"):
self.wins.append(1)
else:
self.wins.append(0)
def play_the_game(self, competition):
'''Play the game.'''
self.first_pick(competition)
self.second_pick(competition)
And I run the simulation like this:
from montyHall import *
def main():
alice = Contestant("Alice")
bob = Contestant("Bob")
carol = Contestant("Carol")
dave = Contestant("Dave")
erin = Contestant("Erin")
frank = Contestant("Frank")
gina = Contestant("Gina")
for i in range(1000):
for contestant in [alice, bob, carol, dave, erin, frank, gina]:
competition = MontyHall()
contestant.play_the_game(competition)
for contestant in [alice, bob, carol, dave, erin, frank, gina]:
print(contestant.name + " win rate: " + str(100 * sum(contestant.wins)/len(contestant.wins)) + "%")
if __name__ == "__main__":
main()