Acciones

Relación 24 Sol

De Informática de 1º de Matemáticas [Curso 2021-22, Grupo 3]

Revisión del 11:26 14 abr 2022 de Jpro (discusión | contribs.) (Página creada con «<source lang='haskell'> -- I1M 2021-22: Relación 24 -- Funciones de entrada/salida: El juego Nim -- Departamento de Ciencias de la Computación e Inteligencia Artificial -…»)
(difs.) ← Revisión anterior | Revisión actual (difs.) | Revisión siguiente → (difs.)
-- 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

-- ============================================================================