Simulación del problema de Monty Hall usando Pygame

En este artículo vamos a ver cómo crear juegos de Monty Hall usando Pygame en Python. Monty Hall fue un programa de juegos del programa de juegos de televisión estadounidense Let’s Make a Deal. 

Suponga que está en un programa de juegos y le dan a elegir entre tres puertas. Detrás de una puerta hay un automóvil; detrás de los demás, cabras. Escoges una puerta, dices la número 1, y el anfitrión, que sabe lo que hay detrás de las puertas, abre otra puerta, dices la número 3, que tiene una cabra. Luego te dice: «¿Quieres elegir la puerta número 2?» ¿Te conviene cambiar tu elección?

Demostración de cómo sería el producto final:

La cabra se revela detrás de la puerta 1. La selección original fue la puerta 2. 

Como se tomó la decisión de quedarse, se ganó el juego. 

Asegúrese de que las imágenes y el audio utilizados estén presentes en la misma carpeta que el archivo de Python. Las imágenes y el audio se pueden descargar desde aquí

Flujo de trabajo y lógica 

Toda la simulación se puede resumir en los siguientes puntos:

  • Como hay tres puertas, se generaría una permutación aleatoria de los números 1, 2, 3, cada número representando el número de la puerta. Los dos primeros números de esta permutación generada aleatoriamente corresponderían a las puertas detrás de las cuales hay cabras, y el tercer número sería el número de la puerta detrás de las cuales hay un automóvil.
  • Una vez generada la configuración, la misma se representa gráficamente a través de imágenes. Hay una imagen para cada configuración.
  • Hay un punto que debe tenerse en cuenta: según el rompecabezas, después de que el usuario haya elegido un número de puerta, solo se revelará la puerta detrás de la cual hay una cabra. Entonces, si el usuario selecciona la puerta detrás de la cual hay un automóvil, entonces se puede revelar cualquiera de las dos puertas restantes. Sin embargo, si el usuario selecciona una puerta detrás de la cual hay una cabra, solo se puede revelar una de las dos puertas restantes (ya que no está permitido revelar la puerta que el usuario ha seleccionado).

Haciendo Importaciones y Generando Configuración 

Python3

import pygame
import random
pygame.init()
white = (255, 255, 255)
X = 1200
Y = 650
doors = random.sample(range(1, 4), 3)
goat1 = doors[0]
goat2 = doors[1]
goats = [goat1, goat2]
car = doors[2]

Solo se necesitan dos módulos externos para esta simulación pygame y random. Al comienzo de cada programa de Pygame, es necesario inicializar Pygame escribiendo pygame.init(). Los valores X e Y se refieren a las dimensiones de la ventana en la que se ejecutaría la simulación. Estos valores se pueden modificar según la resolución deseada y las imágenes utilizadas. La lista, puertas, tiene valores entre 1 y 3 (ambos inclusive). El orden de estos valores se mezclaría cada vez que se ejecuta el programa. Esto se hace para asignar las puertas detrás de las cuales habrá cabras. Según el problema, hay un total de tres puertas detrás de dos de estas puertas, habría cabras, y detrás de la tercera puerta habría un automóvil. Las variables cabra1 y cabra2sostenga los números de las puertas detrás de las cuales habría cabras. Estos números luego se agregan a una lista llamada cabras. El tercer número se asigna a la variable coche.

Agregar fondo y música  

Python3

display_surface = pygame.display.set_mode((X, Y))
pygame.display.set_caption('Simulation')
image = pygame.image.load('all_doors.jpg')
change = False
 
 
def music():
    file = 'click.mp3'
    pygame.mixer.init()
    pygame.mixer.music.load(file)
    pygame.mixer.music.play()

La variable display_surface es como un lienzo, sobre el cual agregaríamos entidades (imágenes, texto, etc.) según los requisitos. El argumento pasado a través de la función display.set_caption sería el título de la ventana de simulación. Se carga la imagen que se va a mostrar al principio: las tres puertas cerradas. Se utiliza una variable booleana, change , para registrar los cambios. La función de música es opcional. Su función es producir un sonido de clic cada vez que el usuario presiona una puerta. Asegúrese de tener el archivo mp3 de cualquier música que desee reproducir. La función realiza tres pasos: inicializar, cargar y reproducir. 

Actualización de imagen basada en la entrada del usuario 

Python3

def show_car(car, state):
    my_font = pygame.font.SysFont("latoblack", 26)
    display_surface = pygame.display.set_mode((X, Y))
    car1 = pygame.image.load('car_1.jpg')
    car2 = pygame.image.load('car_2.jpg')
    car3 = pygame.image.load('car_3.jpg')
 
    if car == 1:
        display_surface.blit(car1, (0, 0))
        pygame.display.update()
    elif car == 2:
        display_surface.blit(car2, (0, 0))
        pygame.display.update()
    elif car == 3:
        display_surface.blit(car3, (0, 0))
        pygame.display.update()
    if state == 1:
        the_text = my_font.render("You won by switching!", True, (231, 0, 10))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 2:
        the_text = my_font.render(
            "You could've won by staying!", True, (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 3:
        the_text = my_font.render("You won by staying!", True, (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 4:
        the_text = my_font.render(
            "You could've won by switching!", True, (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
 
 
def draw_rect():
    pygame.draw.rect(display_surface, (20, 24, 11),
                     (300, 220, 300, 40), 1)
    pygame.display.update()
    pygame.draw.rect(display_surface, (14, 2, 200),
                     (300, 260, 300, 40), 1)
    pygame.display.update()

La función show_car puede parecer un poco engorrosa, pero su funcionalidad es bastante trivial. Su objetivo es mostrar la imagen de la orientación adecuada, es decir, si el coche está detrás de la puerta 3, se mostrará la imagen correspondiente a esa orientación (coche en la posición 3). Todas las imágenes están disponibles (junto con los nombres utilizados en el código) al final de este artículo. El parámetro de  estado indica el resultado de la simulación.

Hay cuatro resultados posibles: 

Resultado Decisión 
Ganó  Quedarse
Ganó Cambiar
Perdió Quedarse
Perdió Cambiar

Es necesario utilizar pygame.display.update() para que se produzcan cambios en la visualización. La fuente y el tamaño de la fuente se pueden modificar cambiando los parámetros en la variable my_font . El primer argumento de display_surface.blit es la imagen que se va a representar, y el segundo argumento es una tupla que indica las coordenadas X e Y. my_font tiene un atributo renderizado que se usa para el texto mostrado. El primer argumento es el texto que se va a mostrar y el tercer argumento es una tupla que contiene valores RGB para especificar el color del texto.  display_surface.blit se usa para representar el texto especificado por rendersobre el lienzo. El primer argumento son las especificaciones del texto y el segundo argumento tiene las coordenadas X e Y. Para encontrar las coordenadas, utilice:

Python3

x, y = pygame.mouse.get_pos()

Esto creará una cruz y se conocerán las coordenadas de los puntos en los que se hizo clic. 

Ciclo principal y manejo de clics 

Python3

while True:
    music()
    if change == False:
        display_surface.fill(white)
        display_surface.blit(image, (0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
        pygame.display.update()
        clicked = False
 
        if event.type == pygame.MOUSEBUTTONDOWN:
           
            # Check if door 1 is pressed.
            if(event.pos[0] >= 71 and event.pos[0] <= 203
               and event.pos[1] >= 387 and event.pos[1] <= 632):
                user = 1
                clicked = True
                music()
            # Check if door 2 is pressed.
            elif(event.pos[0] >= 353 and event.pos[0] <= 485
                 and event.pos[1] >= 386 and event.pos[1] <= 635):
                user = 2
                clicked = True
                music()
            # Check if door 3 is pressed.
            elif(event.pos[0] >= 938 and event.pos[0] <= 1100
                 and event.pos[1] >= 387 and event.pos[1] <= 633):
                user = 3
                # print("Clicked on door 3.")
                clicked = True
                music()

Cada juego contiene un bucle infinito ( mientras que es verdadero). Se comprueba si el tipo de evento registrado es QUIT, es decir, el usuario ha cerrado la ventana de Pygame. En ese caso, el programa termina. pygame.MOUSEBUTTONDOWN se refiere al evento de hacer clic con el botón izquierdo del ratón. Las condiciones if se utilizan para determinar la puerta que ha presionado el usuario (según las coordenadas) y el número de puerta se asigna a la variable usuario . Por ejemplo, si la coordenada X ( event.pos[0] ) del punto donde se ha producido el clic está entre 71 y 203, y la coordenada Y ( event.pos[1] ) está entre 387 y 633, registre el clic tiene un clic en la puerta 1. El clicBoolean se establece en True cada vez que se hace clic en una de las tres puertas. 

Cargando todas las Configuraciones Válidas (Imágenes) y Generando la Posición de las Cabras

Python3

if clicked:
            image1 = pygame.image.load('goat_1.jpg')
            image2 = pygame.image.load('goat_2.jpg')
            image3 = pygame.image.load('goat_3.jpg')
            image4 = pygame.image.load('car_1.jpg')
            image5 = pygame.image.load('car_2.jpg')
            image6 = pygame.image.load('car_3.jpg')
            wr = random.randint(0, 1)
            if(goats[0] == user):
                g = goats[1]
            elif(goats[1] == user):
                g = goats[0]
            else:
                g = goats[wr]
            if g == 1:
                change = True
                display_surface.blit(image1, (0, 0))
                pygame.display.update()
            elif g == 2:
                change = True
                display_surface.blit(image2, (0, 0))
                pygame.display.update()
            elif g == 3:
                change = True
                display_surface.blit(image3, (0, 0))
                pygame.display.update()
            print(u"There is a goat behind door {}".format(g))
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text = my_font.render("Do you want to:", True, (231, 0, 0))
            display_surface.blit(the_text, (350, 180))
            the_text2 = my_font.render("1.Switch", True, (0, 0, 190))
            display_surface.blit(the_text2, (350, 220))
            the_text3 = my_font.render("2.Stay", True, (190, 0, 0))
            display_surface.blit(the_text3, (350, 260))
            draw_rect()
            clicked2 = False
            print(u"The car is behind door {}".format(car))
 
 
        clicked2 = False
        if event.type == pygame.MOUSEBUTTONDOWN:
          # Compare click coordinates with
          # coordinates where it says 'Switch' and 'Stay'.
            if(event.pos[0] >= 299 and event.pos[0] <= 597
               and event.pos[1] >= 220
               and event.pos[1] <= 260):
               
                  # user2 = 1 means user has chosen to switch.
                user2 = 1 
                clicked2 = True
            elif(event.pos[0] >= 301 and event.pos[0] <= 598
                 and event.pos[1] >= 259 and event.pos[1] <= 297):
                user2 = 2   # user2 = 2 means user has chosen to stay.
                clicked2 = True

Una vez que se registra un clic válido, se cargan las seis imágenes. se comprueba si el usuario ha hecho clic en una puerta que tiene una cabra detrás. Como la puerta seleccionada por el usuario no se puede abrir y la puerta que se puede abrir debe contener una cabra detrás, solo hay una puerta posible que se puede abrir. Digamos, las cabras están detrás de las puertas 1 y 2, y el usuario hace clic en la puerta 1, ya que la puerta 1 no se puede abrir, la puerta 2 es la única puerta que se puede abrir. La variable g almacena la puerta válida que se puede abrir. Si el usuario no seleccionó una puerta que tiene una cabra detrás, se asigna aleatoriamente una cabra usando la función randint y almacenando 1 o 0 en la variable, wr

La variable, usuario2 , almacena si el usuario eligió quedarse o cambiar. El booleano clicked2 almacena si el clic para determinar la elección (permanecer o cambiar) se ha registrado o no. 

Mostrar resultado (texto) basado en la entrada del usuario 

Python3

if clicked2:
 
    if user2 == 1:
        print("You chose to switch!")
        if user in goats:
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text = my_font.render(
                "You won by switching!", True, (231, 0, 0))
            state = 1
            display_surface.blit(the_text, (350, 180))
            pygame.display.update()
            print("You won by switching!")
        else:   # User has chosen the door behind which there is a car.
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text2 = my_font.render(
                "You could've won by staying!", True, (231, 0, 0))
            state = 2
            display_surface.blit(the_text2, (350, 180))
            pygame.display.update()
            print("You could have won by switching!")
    elif user2 == 2:
        print("You chose to stay!")
        if user == car:
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text3 = my_font.render(
                "You won by staying!", True, (231, 0, 0))
            display_surface.blit(the_text3, (350, 180))
            state = 3
            pygame.display.update()
            print("You won by staying!")
        else:
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text4 = my_font.render(
                "You could've won by switching!", True, (231, 0, 0))
            display_surface.blit(the_text4, (350, 180))
            state = 4
            pygame.display.update()
            print("You could have won by switching!")
    show_car(car, state)

Una vez que el usuario ha seleccionado, se determina si ha ganado o no (si hay un coche detrás de la puerta que finalmente eligió). La determinación del ganador se puede entender mediante la siguiente tabla:

Acción Selección inicial (cabra o automóvil) Resultado
Cambiar Cabra Ganó 
Cambiar Coche Perdió
Quedarse Coche Ganó
Quedarse Cabra Perdió

A continuación se muestra la implementación completa: 

Python3

import pygame
import random
pygame.init()
white = (255, 255, 255)
X = 1200
Y = 650
doors = random.sample(range(1, 4), 3)
goat1 = doors[0]
goat2 = doors[1]
goats = [goat1, goat2]
car = doors[2]
display_surface = pygame.display.set_mode((X, Y))
pygame.display.set_caption('Simulation')
image = pygame.image.load('all_doors.jpg')
change = False
msg_disp = False
 
 
def music():
    file = 'click.mp3'
    pygame.mixer.init()
    pygame.mixer.music.load(file)
    pygame.mixer.music.play()
 
 
def show_car(car, state):
    my_font = pygame.font.SysFont("latoblack", 26)
    display_surface = pygame.display.set_mode((X, Y))
    car1 = pygame.image.load('car_1.jpg')
    car2 = pygame.image.load('car_2.jpg')
    car3 = pygame.image.load('car_3.jpg')
 
    if car == 1:
        display_surface.blit(car1, (0, 0))
        pygame.display.update()
    elif car == 2:
        display_surface.blit(car2, (0, 0))
        pygame.display.update()
    elif car == 3:
        display_surface.blit(car3, (0, 0))
        pygame.display.update()
    if state == 1:
        the_text = my_font.render("You won by switching!",
                                  True, (231, 0, 10))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 2:
        the_text = my_font.render(
            "You could've won by staying!", True,
          (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 3:
        the_text = my_font.render("You won by staying!",
                                  True, (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
    elif state == 4:
        the_text = my_font.render(
            "You could've won by switching!", True,
          (231, 0, 0))
        display_surface.blit(the_text, (350, 180))
        pygame.display.update()
 
 
def draw_rect():
    pygame.draw.rect(display_surface, (20, 24, 11),
                     (300, 220, 300, 40), 1)
    pygame.display.update()
    pygame.draw.rect(display_surface, (14, 2, 200),
                     (300, 260, 300, 40), 1)
    pygame.display.update()
 
 
while True:
    music()
    if change == False:
        display_surface.fill(white)
        display_surface.blit(image, (0, 0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
 
        pygame.display.update()
 
        clicked = False
 
        if event.type == pygame.MOUSEBUTTONDOWN:
            # Check if door 1 is pressed.
            if(event.pos[0] >= 71 and event.pos[0] <= 203
               and event.pos[1] >= 387 and event.pos[1] <= 632):
                user = 1
                clicked = True
                music()
            # Check if door 2 is pressed.
            elif(event.pos[0] >= 353 and event.pos[0] <= 485
                 and event.pos[1] >= 386 and event.pos[1] <= 635):
                user = 2
                clicked = True
                music()
            # Check if door 3 is pressed.
            elif(event.pos[0] >= 938 and event.pos[0] <= 1100
                 and event.pos[1] >= 387 and event.pos[1] <= 633):
                user = 3
                 
                clicked = True
                music()
        if clicked:
 
            image1 = pygame.image.load('goat_1.jpg')
            image2 = pygame.image.load('goat_2.jpg')
            image3 = pygame.image.load('goat_3.jpg')
            image4 = pygame.image.load('car_1.jpg')
            image5 = pygame.image.load('car_2.jpg')
            image6 = pygame.image.load('car_3.jpg')
            wr = random.randint(0, 1)
            if(goats[0] == user):
                g = goats[1]
            elif(goats[1] == user):
                g = goats[0]
            else:
                g = goats[wr]
            if g == 1:
                change = True
                display_surface.blit(image1, (0, 0))
                pygame.display.update()
            elif g == 2:
                change = True
                display_surface.blit(image2, (0, 0))
                pygame.display.update()
            elif g == 3:
                change = True
                display_surface.blit(image3, (0, 0))
                pygame.display.update()
            print(u"There is a goat behind door {}".format(g))
 
            my_font = pygame.font.SysFont("mvboli", 26)
            the_text = my_font.render("Do you want to:", True, (231, 0, 0))
            display_surface.blit(the_text, (350, 180))
            the_text2 = my_font.render("1.Switch", True, (0, 0, 190))
            display_surface.blit(the_text2, (350, 220))
            the_text3 = my_font.render("2.Stay", True, (190, 0, 0))
            display_surface.blit(the_text3, (350, 260))
            draw_rect()
            clicked2 = False
            print(u"The car is behind door {}".format(car))
 
    # for event in pygame.event.get():
        clicked2 = False
        if event.type == pygame.MOUSEBUTTONDOWN:
           
            # Compare click coordinates with coordinates
            # where it says 'Switch' and 'Stay'.
            if(event.pos[0] >= 299 and event.pos[0] <= 597
               and event.pos[1] >= 220 and event.pos[1] <= 260):
                   
                # user2 = 1 means user has chosen to switch.
                user2 = 1 
                clicked2 = True
            elif(event.pos[0] >= 301 and event.pos[0] <= 598
                 and event.pos[1] >= 259 and event.pos[1] <= 297):
               
                  # user2 = 2 means user has chosen to stay.
                user2 = 2  
                clicked2 = True
 
        if clicked2:
 
            if user2 == 1:
                print("You chose to switch!")
                if user in goats:
                    my_font = pygame.font.SysFont("mvboli", 26)
                    the_text = my_font.render(
                        "You won by switching!", True, (231, 0, 0))
                    state = 1
                    display_surface.blit(the_text, (350, 180))
                    pygame.display.update()
                    print("You won by switching!")
                     
                # User has chosen the door behind which there is a car.
                else:  
                    my_font = pygame.font.SysFont("mvboli", 26)
                    the_text2 = my_font.render(
                        "You could've won by staying!", True, (231, 0, 0))
                    state = 2
                    display_surface.blit(the_text2, (350, 180))
                    pygame.display.update()
                    print("You could have won by switching!")
            elif user2 == 2:
                print("You chose to stay!")
                if user == car:
                    my_font = pygame.font.SysFont("mvboli", 26)
                    the_text3 = my_font.render(
                        "You won by staying!", True, (231, 0, 0))
                    display_surface.blit(the_text3, (350, 180))
                    state = 3
                    pygame.display.update()
                    print("You won by staying!")
                else:
                    my_font = pygame.font.SysFont("mvboli", 26)
                    the_text4 = my_font.render(
                        "You could've won by switching!", True, (231, 0, 0))
                    display_surface.blit(the_text4, (350, 180))
                    state = 4
                    pygame.display.update()
                    print("You could have won by switching!")
            show_car(car, state)

Producción:

Publicación traducida automáticamente

Artículo escrito por akhilkashyap y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *