-- I1M 2021-22: Relación 24
-- Funciones de entrada/salida: El juego Nim
-- Departamento de Ciencias de la Computación e Inteligencia Artificial
-- Universidad de Sevilla
-- ============================================================================
-- ============================================================================
-- Librerías auxiliares
-- ============================================================================
import Data.Char
import Data.List
-- ============================================================================
-- El Nim es un juego en el que se dispone de un conjunto de montones de
-- piezas y dos jugadores retiran alternativamente por turno cualquier cantidad
-- de piezas de un único montón. El ganador es el que retira la última pieza.
--
-- En nuestra versión del juego se pide a los jugadores que indiquen la lista
-- de números de piezas de cada montón.
-- ============================================================================
-- ----------------------------------------------------------------------------
-- Representación
-- ----------------------------------------------------------------------------
-- El conjunto de montones (que llamaremos tablero de juego o simplemente
-- tablero) se representará como una lista de números indicando el número de
-- piezas de cada montón. Con esta representación, un conjunto de montones
-- podría ser [5,4,3,2,1].
type Tablero = [Int]
-- ----------------------------------------------------------------------------
-- Ejercicio 1. Definir la función
-- finalizado :: Tablero -> Bool
-- tal que '(finalizado t)' se verifica si 't' es el tablero de un juego
-- finalizado; es decir, sin piezas. Por ejemplo,
-- finalizado [0,0,0,0,0] == True
-- finalizado [1,3,0,0,1] == False
-- ----------------------------------------------------------------------------
finalizado :: Tablero -> Bool
finalizado t = all (==0) t
-- ----------------------------------------------------------------------------
-- Ejercicio 2. Definir la función
-- jugada :: Tablero -> Int -> Int -> Tablero
-- tal que '(jugada t m n)' es el tablero obtenido a partir del tablero 't'
-- eliminando 'n' piezas del montón 'm'. Se considera que 'm' es un montón
-- válido para el tablero 't' con al menos 'n' piezas. Por ejemplo,
-- jugada [4,3,2,1,0] 2 1 == [4,2,2,1,0]
-- ----------------------------------------------------------------------------
jugada :: Tablero -> Int -> Int -> Tablero
jugada (x:t) 1 n = (x-n):t
jugada (x:t) m n = x:(jugada t (m-1) n)
-- ----------------------------------------------------------------------------
-- Ejercicio 3. Definir la función
-- piezas :: Int -> String
-- tal que '(piezas n)' es una cadena que representa un montón con 'n' piezas.
-- Por ejemplo,
-- piezas 3 == " * * *"
-- ----------------------------------------------------------------------------
piezas :: Int -> String
piezas n = concat (map (\ (x,y) -> [x,y]) (zip (replicate n ' ') (replicate n '*')))
-- ----------------------------------------------------------------------------
-- Ejercicio 4. Definir la acción
-- escribeMonton :: Int -> Int -> IO ()
-- tal que '(escribeMonton m n)' muestra en pantalla el contenido del montón
-- 'm' con 'n' piezas. Por ejemplo,
-- escribeMonton 2 3 =>
-- 2: * * *
-- ----------------------------------------------------------------------------
escribeMonton :: Int -> Int -> IO ()
escribeMonton m n = do
putStr (show m)
putStr ":"
putStrLn (piezas n)
-- ----------------------------------------------------------------------------
-- Ejercicio 5. Definir la acción
-- escribeTablero :: Tablero -> IO ()
-- tal que '(escribeTablero t)' muestra en pantalla el contenido del tablero
-- 't'. Por ejemplo,
-- escribeTablero [3,4,1,0,1] =>
-- 1: * * *
-- 2: * * * *
-- 3: *
-- 4:
-- 5: *
-- ----------------------------------------------------------------------------
escribeTablero :: Tablero -> IO ()
escribeTablero t = escribeTableroAux 1 t
escribeTableroAux :: Int -> Tablero -> IO ()
escribeTableroAux i [] = return ()
escribeTableroAux i (x:t) = do
escribeMonton i x
escribeTableroAux (i+1) t
-- ----------------------------------------------------------------------------
-- Ejercicio 6. Definir la acción
-- leeNumero :: String -> IO Int
-- tal que '(leeNumero xs)' muestra en pantalla una nueva línea con la cadena
-- 'xs', después lee una cadena y comprueba que la cadena leida es un número
-- natural. En este caso, devuelve el número natural correspondiente y en caso
-- contrario debe mostrar un mensaje informativo y volver a ejecutarse. Por
-- ejemplo,
-- leeNumero "Qué montón eliges? " =>
-- Qué montón eliges? 4
-- 4
-- leeNumero "Cuántas piezas coges? " =>
-- Cuantas piezas coges? c
-- ERROR: Entrada incorrecta
-- Cuantas piezas coges?
-- 3
-- ----------------------------------------------------------------------------
leeNumero :: String -> IO Int
leeNumero xs = do
putStr xs
nStr <- getLine
if (all isDigit nStr) then return (read nStr)
else do
putStrLn "ERROR: Entrada incorrecta"
leeNumero xs
-- ----------------------------------------------------------------------------
-- Ejercicio 7. Definir la acción
-- eligeMonton :: Tablero -> IO Int
-- tal que '(eligeMonton t)' devuelve un número entero correspondiente a un
-- montón de los del tablero 't' que tenga un número positivo (no nulo) de
-- piezas, leido con la función 'leeNumero'. Si el valor leido no se
-- corresponde con un montón del tablero 't' o se corresponde con un montón
-- que no tiene piezas, se debe mostrar un mensaje informativo y volver a
-- ejecutarse. Por ejemplo,
-- eligeMonton [1,2,0,2,1] =>
-- Qué montón eliges? c
-- ERROR: Entrada incorrecta
-- Qué montón eliges? 6
-- ERROR: Número de montón incorrecto
-- Qué montón eliges? 3
-- ERROR: El montón está vacío
-- Qué montón eliges? 2
-- 2
-- ----------------------------------------------------------------------------
eligeMonton :: Tablero -> IO Int
eligeMonton t = do
monton <- leeNumero "Elige uno de los montones: "
if monton == 0 || monton > (length t) then do
putStrLn "Error: Monton incorrecto."
eligeMonton t
else if t !! (monton-1) == 0 then do
putStrLn "Error: El monton esta vacio."
eligeMonton t
else
return monton
-- ----------------------------------------------------------------------------
-- Ejercicio 8. Definir la acción
-- eligePiezas :: Tablero -> Int -> IO Int
-- tal que '(eligePiezas t m)' devuelve un número entero correspondiente a un
-- conjunto de piezas del montón 'm' en el tablero 't', leido con la función
-- 'leeNumero'. Si el valor leido es cero o excede del número de piezas del
-- montón 'm' en el tablero 't', se debe mostrar un mensaje informativo y
-- volver a ejecutarse. Por ejemplo,
-- eligePiezas [1,2,0,2,1] 2 =>
-- Cuántas piezas coges? c
-- ERROR: Entrada incorrecta
-- Cuántas piezas coges? 0
-- ERROR: Número de piezas incorrecto
-- Cuántas piezas coges? 4
-- ERROR: Número de piezas incorrecto
-- Cuántas piezas coges? 2
-- 2
-- ----------------------------------------------------------------------------
eligePiezas :: Tablero -> Int -> IO Int
eligePiezas t m = do
piezas <- leeNumero "Elige el numero de piezas: "
if piezas == 0 || piezas > (t !! (m-1)) then do
putStrLn "Numero de piezas incorrecto"
eligePiezas t m
else
return piezas
-- ----------------------------------------------------------------------------
-- Ejercicio 9. Los jugadores se representan por los números 1 y 2. Definir
-- la función
-- cambiaJugador :: Int -> Int
-- tal que '(cambiaJugador j)' es el jugador distinto a 'j'.
-- ----------------------------------------------------------------------------
cambiaJugador :: Int -> Int
cambiaJugador j = 1 + (j `mod` 2)
-- ----------------------------------------------------------------------------
-- Ejercicio 10. Definir la acción
-- juegoNim :: Tablero -> Int -> IO ()
-- tal que '(juegoNim t j)' es el juego para dos jugadores desarrollado a
-- partir del tablero 't' y el turno del jugador 'j'. Por ejemplo,
-- juegoNim [0,1,0,1,0] 2 =>
--
-- 1:
-- 2: *
-- 3:
-- 4: *
-- 5:
--
-- Turno del jugador 2
-- Qué montón eliges? 2
-- Cuántas piezas coges? 1
--
-- 1:
-- 2:
-- 3:
-- 4: *
-- 5:
--
-- Turno del jugador 1
-- Qué montón eliges? 4
-- Cuántas piezas coges? 1
--
-- 1:
-- 2:
-- 3:
-- 4:
-- 5:
--
-- Ha ganado el jugador 1
-- ----------------------------------------------------------------------------
juegoNim :: Tablero -> Int -> IO ()
juegoNim t j =
do
putStrLn ""
escribeTablero t
putStrLn ""
if finalizado t then do
putStrLn ("Ha ganado el jugador " ++ (show (cambiaJugador j)))
return ()
else do
putStrLn ("Turno del jugador " ++ (show j))
monton <- eligeMonton t
piezas <- eligePiezas t monton
juegoNim (jugada t monton piezas) (cambiaJugador j)
-- ----------------------------------------------------------------------------
-- Ejercicio 11. Definir la función
-- listaNumeros :: String -> [Int]
-- tal que '(listaNumeros xs)' es la lista de números enteros positivos que
-- forman la cadena 'xs', separados por espacios en blanco. En caso de que la
-- cadena 'xs' contenga algo que no sea un número o espacios en blanco, debe
-- devolver la lista vacía. Por ejemplo,
-- listaNumeros "1 2 3 4 5" == [1,2,3,4,5]
-- listaNumeros "1 2 a b c" == []
-- listaNumeros "" == []
-- ----------------------------------------------------------------------------
listaNumeros :: String -> [Int]
listaNumeros [] = []
listaNumeros (x:xs) | any (\ e -> (not (isDigit e)) && (e /= ' ')) (x:xs) = []
| isDigit x = (cogeNumero (x:xs)):listaNumeros (quitaNumero (x:xs))
| otherwise = listaNumeros (quitaOtraCosa (x:xs))
where cogeNumero xs = read (takeWhile isDigit xs)
quitaNumero xs = dropWhile isDigit xs
quitaOtraCosa xs = dropWhile (not . isDigit) xs
-- ----------------------------------------------------------------------------
-- Ejercicio 12. Definir la acción
-- nim :: IO ()
-- consistente en una partida del Nim entre dos jugadores. Por ejemplo,
-- nim =>
-- Escribe la lista de números de piezas de cada montón: a b
-- ERROR: La lista de números es incorrecta
-- Escribe la lista de números de piezas de cada montón: 2 3 4
--
-- 1: * *
-- 2: * * *
-- 3: * * * *
--
-- Turno del jugador 1
-- Qué montón eliges? 3
-- Cuántas piezas coges? 3
--
-- 1: * *
-- 2: * * *
-- 3: *
--
-- Turno del jugador 2
-- Qué montón eliges? 3
-- Cuántas piezas coges? 1
--
-- 1: * *
-- 2: * * *
-- 3:
--
-- Turno del jugador 1
-- Qué montón eliges? 2
-- Cuántas piezas coges? 2
--
-- 1: * *
-- 2: *
-- 3:
--
-- Turno del jugador 2
-- Qué montón eliges? 1
-- Cuántas piezas coges? 2
--
-- 1:
-- 2: *
-- 3:
--
-- Turno del jugador 1
-- Qué montón eliges? 2
-- Cuántas piezas coges? 1
--
-- 1:
-- 2:
-- 3:
--
-- Ha ganado el jugador 1
-- ----------------------------------------------------------------------------
nim :: IO ()
nim = do
montones <- nimAux
juegoNim montones 1
nimAux :: IO [Int]
nimAux = do
putStr "Escribe los montones iniciales: "
montonesStr <- getLine
if null (listaNumeros montonesStr) then do
putStrLn "La lista contiene algun error"
nimAux
else do
return (listaNumeros montonesStr)
-- ----------------------------------------------------------------------------
-- En el juego del Nim, un tablero perdedor es un tablero en el que el jugador
-- siempre pierde haga lo que haga, por ejemplo [1,1] o incluso [0], siempre
-- que el contrincante juege de la mejor manera posible. Por otro lado, un
-- tablero ganador es un tablero en el que el jugador puede realizar una
-- elección de piezas que da lugar a un tablero perdedor, por ejemplo [1,2] o
-- incluso [2].
--
-- Una estrategia ganadora consiste en identificar una propiedad que se cumpla
-- únicamente en los tableros perdedores y tal que, ante un tablero ganador,
-- permita decidir qué movimiento se ha de hacer para dar lugar a un tablero
-- perdedor. De esta forma, siempre que no tengamos un tablero ganador,
-- podremos hacer un movimiento que lo transforme en un tablero perdedor,
-- dejando dicha situación al contrincante.
--
-- En el juego del Nim, la propiedad que da lugar a la estrategia ganadora
-- consiste en comprobar que en las descomposiciones como suma de potencias
-- distintas de base 2 de las cantidades de piezas de cada montón, hay una
-- cantidad par de potencias del mismo exponente. Por ejemplo,
-- ·) En el tablero [1,2,3], 1 y 2 ya son potencias de 2 y 3 = 1+2. Por tanto
-- en las descomposiciones como suma de potencias distintas de base 2 de las
-- cantidades de este tablero hay dos 1 y dos 2. De aquí que el tablero sea
-- perdedor.
-- ·) En el tablero [3,5,6], 3 = 1+2, 5 = 1+4 y 6 = 2+4. Por tanto en las
-- descomposiciones como suma de potencias distintas de base 2 de las
-- cantidades de este tablero hay dos 1, dos 2 y dos 4. De aquí que el
-- tablero sea perdedor.
--
-- Para identificar la elección adecuada que da lugar a un tablero perdedor a
-- partir de un tablero ganador se actúa de la siguiente forma:
-- ·) Se descomponen como suma de potencias distintas de base 2 todas las
-- cantidades del tablero
-- ·) Se eliminan todas las parejas de potencias iguales, dejando sólo una
-- ocurrencia de las potencias que aparecen un número impar de veces
-- ·) Se escoge el montón al que pertenezca la potencia más alta que haya
-- quedado
-- ·) Se toma de dicho montón la diferencia entre la suma de las potencias que
-- hayan quedado en el montón elegido y la suma de las potencias que hayan
-- quedado en los montones restantes
-- ·) De esta forma en las descomposiciones como suma de potencias distintas de
-- base 2 de las cantidades del tablero resultante se dejan una cantidad par
-- de potencias del mismo exponente
--
-- Por ejemplo, dado el tablero [3,5,7], se tiene:
-- 3 = 1 + 2
-- 5 = 1 + 4
-- 7 = 1 + 2 + 4
-- Si quitamos las parejas de potencias iguales nos queda 1 en el tercer
-- montón. La elección adecuada sería una pieza del tercer montón.
--
-- Otro ejemplo, dado el tablero [3,6,8], se tiene:
-- 3 = 1 + 2
-- 6 = 2 + 4
-- 8 = 8
-- Si quitamos las parejas de potencias iguales nos queda 1 en el primer
-- montón, 4 en el segundo montón y 8 en el tercer montón. La elección adecuada
-- sería 8-(1+4) = 3 piezas del tercer montón.
--
-- En los siguientes ejercicios se desarrolla esta estrategia ganadora.
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Ejercicio 13: Definir la función
-- descomposicionBase2 :: Int -> [Int]
-- tal que '(descomposicionBase2 n)' es una lista con la descomposición del
-- número natural 'n' como suma de potencias distintas de base 2. Por ejemplo:
-- descomposicionBase2 8 == [8]
-- descomposicionBase2 7 == [4,2,1]
-- descomposicionBase2 10 == [8,2]
-- ----------------------------------------------------------------------------
descomposicionBase2 :: Int -> [Int]
descomposicionBase2 0 = []
descomposicionBase2 n = reverse (descomposicionBase2Aux n 0)
descomposicionBase2Aux :: Int -> Int -> [Int]
descomposicionBase2Aux 0 i = []
descomposicionBase2Aux n i | (n `mod` 2) == 0 = descomposicionBase2Aux (n `div` 2) (i + 1)
| otherwise = 2^i : descomposicionBase2Aux (n `div` 2) (i + 1)
-- ----------------------------------------------------------------------------
-- Ejercicio 14: Definir la función
-- descomposicionTablero :: Tablero -> [(Int,Int)]
-- tal que '(descomposicionTablero t)' es la lista de parejas '(p,m)' formadas
-- por cada uno de los montones 'm' del tablero 't' y cada uno de los valores
-- que surgen de la descomposición en potencias distintas de base 2 del número
-- de piezas de dicho montón. Por ejemplo:
-- descomposicionTablero [8] == [(8,1)]
-- descomposicionTablero [8,5] == [(8,1),(4,2),(1,2)]
-- descomposicionTablero [8,5,3] == [(8,1),(4,2),(1,2),(2,3),(1,3)]
-- ----------------------------------------------------------------------------
descomposicionTablero :: Tablero -> [(Int,Int)]
descomposicionTablero t = concat (zipWith descomposicionAux t [1..])
descomposicionAux :: Int -> Int -> [(Int,Int)]
descomposicionAux n i = [(x,i) | x <- descomposicionBase2 n]
-- ----------------------------------------------------------------------------
-- Ejercicio 15: Definir la función
-- reduceParejas :: [(Int,Int)] -> [(Int,Int)]
-- tal que '(reduceParejas pms)' es el resultado de eliminar de la lista de
-- parejas 'pms', todos los pares de elementos que representen la misma
-- potencia de 2 en dos montones de un tablero. Por ejemplo:
-- reduceParejas [(8,1),(4,2),(1,2),(2,3),(1,3)] == [(2,3),(4,2),(8,1)]
-- reduceParejas [(8,1),(2,2),(1,2),(2,3),(1,3)] == [(8,1)]
-- reduceParejas [(2,2),(1,2),(2,3),(1,3)] == []
-- ----------------------------------------------------------------------------
reduceParejas :: [(Int,Int)] -> [(Int,Int)]
reduceParejas ps = reduceParejasAux (reverse ps)
reduceParejasAux :: [(Int,Int)] -> [(Int,Int)]
reduceParejasAux [] = []
reduceParejasAux (p:ps) | odd (cuentaOcurrencias p ps) = reduceParejasAux (eliminaParejas p ps)
| otherwise = p:(reduceParejasAux (eliminaParejas p ps))
cuentaOcurrencias :: (Int,Int) -> [(Int,Int)] -> Int
cuentaOcurrencias p ps = length (filter (\ x -> fst x == fst p) ps)
eliminaParejas :: (Int,Int) -> [(Int,Int)] -> [(Int,Int)]
eliminaParejas p ps = filter (\ x -> fst x /= fst p) ps
-- ----------------------------------------------------------------------------
-- Ejercicio 16: Definir la función
-- montonConMayorPotenciaDesparejada :: [(Int,Int)] -> Int
-- tal que '(montonConMayorPotenciaDesparejada pms)' es el número 'm' para el
-- que existe una pareja '(p,m)' en la lista no vacía de parejas 'pms', con un
-- mayor valor de 'p'. Por ejemplo,
-- montonConMayorPotenciaDesparejada [(2,3),(4,2),(8,1)] == 1
-- montonConMayorPotenciaDesparejada [(8,2),(4,1)] == 2
-- montonConMayorPotenciaDesparejada [(2,3)] == 3
-- ----------------------------------------------------------------------------
montonConMayorPotenciaDesparejada :: [(Int,Int)] -> Int
montonConMayorPotenciaDesparejada ps = snd (maximum ps)
-- ----------------------------------------------------------------------------
-- Ejercicio 17: Definir la función
-- piezasSobrantes :: [(Int,Int)] -> Int -> Int
-- tal que '(piezasSobrantes pms m)' es la diferencia entre la suma de los
-- valores 'p' de las parejas de la forma '(p,m)' de la lista de parejas 'pms'
-- y la suma de los valores 'p' de las restantes parejas de 'pms'. Por ejemplo,
-- piezasSobrantes [(2,3),(4,2),(8,1)] 1 == 2
-- piezasSobrantes [(8,2),(4,1)] 2 == 4
-- piezasSobrantes [(2,3)] 3 == 2
-- ----------------------------------------------------------------------------
piezasSobrantes :: [(Int,Int)] -> Int -> Int
piezasSobrantes ps m = (sum (map fst (filter (\ x -> snd x == m) ps))) -
(sum (map fst (filter (\ x -> snd x /= m) ps)))
-- ----------------------------------------------------------------------------
-- Ejercicio 18: Definir la función
-- buscaEleccionGanadora :: Tablero -> (Int,Int)
-- tal que '(buscaEleccionGanadora t)' es un par '(m,n)' donde 'm' es un número
-- de montón válido en el tablero 't', 'n' es un número de piezas menor o igual
-- que el que tiene el montón 'm' en 't' y, si el tablero 't' es ganador
-- entonces tras tomar 'n' piezas del montón 'm' en el tablero 't', el tablero
-- es perdedor. En caso de que el tablero original sea perdedor entonces se
-- devuelve el par '(m,1)' donde 'm' es un montón del tablero 't' con un número
-- de piezas maximal. Por ejemplo,
-- buscaEleccionGanadora [7,5,3] == (3,1)
-- buscaEleccionGanadora [8,4] == (1,4)
-- buscaEleccionGanadora [8,8] == (2,1)
-- buscaEleccionGanadora [7] == (1,7)
-- ----------------------------------------------------------------------------
buscaEleccionGanadora :: Tablero -> (Int,Int)
buscaEleccionGanadora t | null parejas = (buscaMontonMayor t,1)
| otherwise = (monton,piezas)
where parejas = reduceParejas (descomposicionTablero t)
monton = montonConMayorPotenciaDesparejada parejas
piezas = piezasSobrantes parejas monton
buscaMontonMayor :: Tablero -> Int
buscaMontonMayor t = snd (last (filter (\ x -> fst x == m) (zip t [1..])))
where m = maximum t
-- ----------------------------------------------------------------------------
-- Ejercicio 19: Definir la acción
-- juegoNimC :: Tablero -> Int -> IO ()
-- tal que '(juegoNimC t j)' es el juego para un jugador contra la computadora,
-- desarrollado a partir del tablero 't' y el turno del jugador 'j' (1 si es el
-- turno del jugador y 2 si es el turno de la computadora), en el que la
-- computadora utiliza la heurística ganadora. Por ejemplo,
-- juegoNimC [3,6,8] 1 =>
--
-- 1: * * *
-- 2: * * * * * *
-- 3: * * * * * * * *
--
-- Te toca jugar
-- Qué montón eliges? 3
-- Cuántas piezas coges? 3
--
-- 1: * * *
-- 2: * * * * * *
-- 3: * * * * *
--
-- Me toca jugar
--
-- 1: * * *
-- 2: * * * * *
-- 3: * * * * *
--
-- Te toca jugar
-- Qué montón eliges? 2
-- Cuántas piezas coges? 3
--
-- 1: * * *
-- 2: * *
-- 3: * * * * *
--
-- Me toca jugar
--
-- 1: * * *
-- 2: * *
-- 3: *
--
-- Te toca jugar
-- Qué montón eliges? 1
-- Cuántas piezas coges? 2
--
-- 1: *
-- 2: * *
-- 3: *
--
-- Me toca jugar
--
-- 1: *
-- 2:
-- 3: *
--
-- Te toca jugar
-- Qué montón eliges? 1
-- Cuántas piezas coges? 1
--
-- 1:
-- 2:
-- 3: *
--
-- Me toca jugar
--
-- 1:
-- 2:
-- 3:
--
-- He ganado
-- ----------------------------------------------------------------------------
juegoNimC :: Tablero -> Int -> IO ()
juegoNimC t j =
do
putStrLn ""
escribeTablero t
putStrLn ""
if finalizado t then do
if j == 1 then do
putStrLn "He ganado "
return ()
else do
putStrLn "Has ganado, no me lo puedo creer"
return ()
else do
if j == 1 then do
putStrLn "Te toca jugar"
monton <- eligeMonton t
piezas <- eligePiezas t monton
juegoNimC (jugada t monton piezas) (cambiaJugador j)
else do
putStrLn "Me toca jugar "
let (monton,piezas) = buscaEleccionGanadora t
juegoNimC (jugada t monton piezas) (cambiaJugador j)
-- ----------------------------------------------------------------------------
-- Ejercicio 20: Definir la acción
-- nimC :: IO ()
-- consistente en una partida del Nim entre un jugador y la computadora, que
-- usa la estrategia ganadora descrita anteriormente. Por ejemplo,
-- nimC =>
-- Escribe la lista de números de piezas de cada montón: 3 5 7
-- Quién empieza a jugar, tú (1) o yo (2)? 2
-- Suerte, te va a hacer falta ...
--
-- 1: * * *
-- 2: * * * * *
-- 3: * * * * * * *
--
-- Me toca jugar
--
-- 1: * * *
-- 2: * * * * *
-- 3: * * * * * *
--
-- Te toca jugar
-- Qué montón eliges? 3
-- Cuántas piezas coges? 2
--
-- 1: * * *
-- 2: * * * * *
-- 3: * * * *
--
-- Me toca jugar
--
-- 1: *
-- 2: * * * * *
-- 3: * * * *
--
-- Te toca jugar
-- Qué montón eliges? 2
-- Cuántas piezas coges? 2
--
-- 1: *
-- 2: * * *
-- 3: * * * *
--
-- Me toca jugar
--
-- 1: *
-- 2: * * *
-- 3: * *
--
-- Te toca jugar
-- Qué montón eliges? 2
-- Cuántas piezas coges? 2
--
-- 1: *
-- 2: *
-- 3: * *
--
-- Me toca jugar
--
-- 1: *
-- 2: *
-- 3:
--
-- Te toca jugar
-- Qué montón eliges? 1
-- Cuántas piezas coges? 1
--
-- 1:
-- 2: *
-- 3:
--
-- Me toca jugar
--
-- 1:
-- 2:
-- 3:
--
-- He ganado
-- ----------------------------------------------------------------------------
nimC :: IO ()
nimC = do
montones <- nimAux
jugadorInicial <- escogeJugador
putStrLn "Suerte, la vas a necesitar"
juegoNimC montones jugadorInicial
escogeJugador :: IO Int
escogeJugador = do
n <- leeNumero "Quien empieza a jugar, tu (1) o yo (2)?"
if n /= 1 && n /= 2 then do
putStrLn "Vuelve a intentarlo, anda"
escogeJugador
else do
return n
-- ============================================================================