monty.py 1.82 KB
from collections import Counter
from enum import Enum
from fractions import Fraction
import random

DoorOption = Enum('DoorOption', ['GOAT', 'CAR'])
Situation = list[DoorOption]


def generate_situation(N=3) -> Situation:
    output: list[DoorOption] = []
    car_index = random.randint(0, N - 1)
    for i in range(N):
        output.append(DoorOption.GOAT)
    output[car_index] = DoorOption.CAR
    return output


def choose_door(situation: Situation, disallowed: set[int]):
    choices = list(range(len(situation)))
    for x in disallowed:
        choices.remove(x)
    return random.choice(choices)


def play_game(situation: Situation, use_switch_strategy=True) -> bool:
    choice: int = choose_door(situation, set())
    car: int = situation.index(DoorOption.CAR)
    goat: int = choose_door(situation, set([choice, car]))
    if use_switch_strategy:
        choice = choose_door(situation, set([choice, goat]))
    return situation[choice] == DoorOption.CAR


NUM_DOORS = 8
games_played = 0
wins: Counter[str] = Counter()


def actual_probability(doors):
    return str((doors - 1)) + '/' + str((doors * (doors - 2))) + " = " + str((((doors - 1)) / ((doors * (doors - 2)))))


def frac(num, dem):
    return Fraction(num, dem).limit_denominator(NUM_DOORS * NUM_DOORS)


while True:
    situation = generate_situation(NUM_DOORS)
    if play_game(situation, True):
        wins['switch'] += 1
    if play_game(situation, False):
        wins['stay'] += 1
    games_played += 1

    if games_played % 10000 == 0:
        print("\033c", end="", flush=True)
        print("stay        :", frac(
            wins['stay'], games_played), "= " + str(wins['stay'] / games_played))
        print("switch (exp):", frac(
            wins['switch'], games_played), "= " + str(wins['switch'] / games_played))
        print("switch (act):", actual_probability(NUM_DOORS))