-- I1M 2021-22: Relación 25
-- Funciones de entrada/salida: El dilema de Monty Hall
-- Departamento de Ciencias de la Computación e Inteligencia Artificial
-- Universidad de Sevilla
-- ============================================================================
-- ============================================================================
-- Librerías auxiliares
-- ============================================================================
import System.Random
import Data.List
import Data.Char
-- ============================================================================
-- Monty Hall es el nombre de un conocido presentador de televisión en
-- Estados Unidos. En uno de sus concursos, se presenta el siguiente dilema:
--
-- El concursante debe elegir una de entre tres puertas cerradas y el premio
-- consiste en llevarse lo que se encuentra detrás de la puerta elegida.
-- Se sabe con certeza que una de las puertas encierra un coche y que las otras
-- dos tienen sendas cabras (se entiende que el concursante prefiere llevarse
-- el coche antes que una cabra).
--
-- Una vez que el concursante haya elegido una puerta, el presentador, que sabe
-- lo que encierra cada puerta, abrirá una de las otras dos, mostrando siempre
-- una cabra.
--
-- A continuación, le da la opción al concursante de cambiar, si lo desea, de
-- puerta o quedarse con su elección original.
--
-- Vamos a plantear un programa interactivo que permita recrear la
-- participación del concursante en el dilema de Monty Hall
-- ============================================================================
-- ----------------------------------------------------------------------------
-- Representación
-- ----------------------------------------------------------------------------
-- Denominaremos a las puertas por los enteros 1, 2 y 3.
-- ----------------------------------------------------------------------------
-- Ejercicio 1. Definir la acción de E/S
-- escojaPuerta :: IO Int
-- que interactúe con el jugador para que decida qué puerta escoger, dando los
-- correspondientes mensajes (se debe devolver un entero entre 1 y 3),
-- por ejemplo:
-- escojaPuerta =>
-- Que puerta elige? c
-- ERROR: Entrada incorrecta
-- Que puerta elige? 4
-- ERROR: Entrada incorrecta
-- Que puerta elige? 2
-- 2
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
escojaPuerta :: IO Int
escojaPuerta = do putStrLn "Que puerta elige? "
x <- getLine
if x == "1" || x == "2" || x == "3"
then return (read x :: Int)
else do putStrLn "ERROR: Entrada incorrecta"
escojaPuerta
-- ----------------------------------------------------------------------------
-- Ejercicio 2. Definir la acción
-- ponPremio :: IO Int
-- tal que devuelva la puerta que contiene el premio del concurso.
-- Para ello se debe seleccionar de forma aleatoria un valor entero entre 1 y 3:
-- ponPremio =>
-- 3
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
ponPremio :: IO Int
ponPremio = randomRIO (1::Int,3)
-- ----------------------------------------------------------------------------
-- Ejercicio 3. Definir la función
-- abrePuerta :: Int -> Int -> IO Int
-- tal que (abrePuerta concursante premio) devuelve el número de una puerta que
-- abre el presentador. La puerta devuelta nunca puede ser la que contiene el
-- premio ni la que ha escogido el concursante, y, en caso de que el jugador
-- haya elegido la puerta con premio, se debe escoger aleatoriamente entre las
-- otras dos.
-- abrePuerta 1 1 =>
-- 2 (pero podía haber sido la 3)
-- abrePuerta 1 1 =>
-- 3 (pero podía haber sido la 2)
-- abrePuerta 1 2 =>
-- 3 (obligada)
-- abrePuerta 1 3 =>
-- 2 (obligada)
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
abrePuerta :: Int -> Int -> IO Int
abrePuerta c p = if c == p
then do r <- randomRIO (0::Int,1)
return ((filter f [1,2,3])!!r)
else return (head (filter f [1,2,3]))
where f x = x /= c && x /= p
-- ----------------------------------------------------------------------------
-- Ejercicio 4. Definir la acción
-- otraPuerta :: Int -> Int -> Int
-- tal que (otraPuerta concursante abierta) devuelve el número de la puerta que
-- no es igual a la del concursante ni a la que ya está abierta (se supone
-- que "concursante" es distinto de "abierta").
-- otraPuerta 1 2 =>
-- 3
-- otraPuerta 2 1 =>
-- 3
-- otraPuerta 3 1 =>
-- 2
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
otraPuerta :: Int -> Int -> Int
otraPuerta c a = head (filter f [1,2,3])
where f x = x /= c && x /= a
-- ----------------------------------------------------------------------------
-- Ejercicio 5. Definir la acción
-- cambiaPuerta :: Int -> Int -> IO Int
-- tal que (cambiaPuerta concursante cerrada) devuelve el número de la puerta
-- que el concursante al final termina escogiendo después de haberle dado a
-- elegir entre "mantener" la elección original "concursante" o "cambiar" a
-- la "cerrada".
-- cambiaPuerta 1 3 =>
-- Concursante, digame, prefiere mantener su decision original, la puerta 1 [mantener]
-- o quiere cambiar a la puerta 3 [cambiar]? mantener
-- 1
-- Concursante, digame, prefiere mantener su decision original, la puerta 1 [mantener]
-- o quiere cambiar a la puerta 3 [cambiar]? cambiar
-- 3
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
cambiaPuerta :: Int -> Int -> IO Int
cambiaPuerta con cer = do putStr ("Concursante, digame, prefiere mantener su decision original, la puerta "++(show con)++" [mantener] o quiere cambiar a la puerta "++(show cer)++" [cambiar]? ")
x <- getLine
if x == "mantener"
then return con
else if x == "cambiar"
then return cer
else cambiaPuerta con cer
-- ----------------------------------------------------------------------------
-- Ejercicio 6. Definir la acción
-- concurso :: IO ()
-- tal que se describa una ronda completa del concurso de Monty Hall.
-- concurso =>
-- Muy bien, concursante, aqui tiene tres puertas, una de ellas esconde un
-- coche y las otras dos, sendas cabras.
-- Que puerta elige? 1
-- Pues ahora abramos la puerta 2:
-- Vaya, hay una CABRA!
-- Concursante, digame, prefiere mantener su decision original, la puerta 1 [mantener]
-- o quiere cambiar a la puerta 3 [cambiar]? mantener
-- Bien, pues abramos la puerta 1: Y hay un COCHE, enhorabuena!
-- ó
-- Bien, pues abramos la puerta 1: Y hay una CABRA, lo sentimos mucho!
-- Juegue varias veces. ¿Qué estrategia es mejor, mantener o cambiar?
-- ¿Con qué probabilidad nos llevamos el coche si escogemos "mantener"?
-- ¿Con qué probabilidad nos llevamos el coche si escogemos "cambiar"?
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
concurso :: IO ()
concurso = do putStrLn "Muy bien, concursante, aqui tiene tres puertas, una de ellas esconde un coche y las otras dos, sendas cabras."
x <- escojaPuerta
y <- ponPremio
w <- abrePuerta x y
putStrLn ("Pues ahora abramos la puerta " ++ show w ++ ":")
putStrLn "Vaya, hay una CABRA!"
z <- cambiaPuerta x (otraPuerta x w)
if z == y
then putStrLn ("Bien, pues abramos la puerta "++show z++": Y hay un COCHE, enhorabuena!")
else putStrLn ("Bien, pues abramos la puerta "++show z++": Y hay una CABRA, lo sentimos mucho!")
-- ----------------------------------------------------------------------------
-- Ejercicio 7. Definir la acción
-- estrategiaMantener :: IO Int
-- tal que devuelva un 1 si en una ronda del concurso, el jugador se llevó
-- el premio o 0 en caso contrario. Suponga que el jugador siempre escoge
-- mantener su decisión original.
--
-- Para ello:
-- 1. Seleccione de forma aleatoria una puerta para el premio.
-- 2. Seleccione de forma aleatoria una puerta para el concursante.
-- 3. Abra una de las puertas que contiene un cabra sin que sea la del concursante.
-- 4. Mantenga la decisión del concursante.
-- 5. Devuelva el valor adecuado dependiendo del resultado.
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
estrategiaMantener :: IO Int
estrategiaMantener = do p <- ponPremio
c <- randomRIO (1::Int,3)
a <- abrePuerta p c
if p == c
then return 1
else return 0
-- ----------------------------------------------------------------------------
-- Ejercicio 8. Definir la acción
-- estrategiaCambiar :: IO Int
-- tal que devuelva un 1 si en una ronda del concurso, el jugador se llevó
-- el premio o 0 en caso contrario. Suponga que el jugador siempre escoge
-- cambiar su decisión original.
--
-- Para ello:
-- 1. Seleccione de forma aleatoria una puerta para el premio.
-- 2. Seleccione de forma aleatoria una puerta para el concursante.
-- 3. Abra una de las puertas que contiene un cabra sin que sea la del concursante.
-- 4. Cambie la decisión del concursante a la puerta que queda sin abrir.
-- 5. Devuelva el valor adecuado dependiendo del resultado.
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
estrategiaCambiar :: IO Int
estrategiaCambiar = do p <- ponPremio
c <- randomRIO (1::Int,3)
a <- abrePuerta p c
if p == (otraPuerta c a)
then return 1
else return 0
-- ----------------------------------------------------------------------------
-- Ejercicio 9. Definir la acción
-- ejecutaEstrategia :: Int -> IO Int -> IO Int
-- tal que (ejecutaEstrategia n estrategia) ejecuta de forma consecutiva n veces
-- la estrategia pasada como segundo argumento. Devuelve el número de veces que
-- el concursante se llevó el premio. Por ejemplo:
-- ejecuta 100 estrategiaMantener =>
-- 30
-- ejecuta 100 estrategiaCambiar =>
-- 68
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
ejecutaEstrategia :: Int -> IO Int -> IO Int
ejecutaEstrategia n estrategia = ejecutaAux n estrategia 0
ejecutaAux :: Int -> IO Int -> Int -> IO Int
ejecutaAux n e xs = do y <- e
if n == 0
then return xs
else ejecutaAux (n-1) e (xs+y)
-- ----------------------------------------------------------------------------
-- Ejercicio 10.
-- ¿Cuál es número mínimo de personas que debe haber en una fiesta para
-- tener una probabilidad del 50% de que al menos dos de ellas tengan el mismo
-- cumpleaños?
-- ----------------------------------------------------------------------------
-- Álvaro Galisteo:
-- 23 personas