Camicia

Camicia

Media

Introducción

Una tarde lluviosa, te sientas a la mesa de la cocina a jugar a las cartas con tu abuela. El juego es su particular versión de Camicia.

Al principio parece una partida amistosa más: cartas que caen de golpe, risas alrededor de la mesa y alguna sonrisa victoriosa de Nonna. Pero a medida que la partida se alarga, ocurre algo extraño. Las mismas cartas no dejan de volver una y otra vez. Juegas carta tras carta y, aun así, el final no parece llegar nunca.

Empiezas a preguntarte. ¿Terminará algún día esta partida? ¿O podríamos seguir jugando para siempre?

Más tarde, llevado por la curiosidad, buscas en internet y, para tu sorpresa, descubres que lo que pasó no fue solo mala suerte. ¡Es posible que tu abuela y tú hayáis topado con una de las secuencias más largas posibles! De repente, quedas enganchado. Lo que empezó como una partida informal se ha convertido en una misión: ¿cuánto puede durar realmente una partida así? ¿Puedes encontrar una secuencia aún más larga que la que jugaste en la mesa de la cocina? ¿Quizá lo bastante larga como para batir un nuevo récord mundial?

Y así, armado solo con una baraja de cartas y algo de ingenio algorítmico, decides investigar...

Instrucciones

En este ejercicio, vas a simular un juego muy similar al clásico juego de cartas Camicia. Tu programa recibirá la configuración inicial de las barajas de dos jugadores y debe simular la partida hasta que termine (o detectar que nunca terminará).

Reglas

  • La baraja se reparte entre dos jugadores. Las cartas de cada jugador se leen de izquierda a derecha, donde la carta más a la izquierda es la parte superior de la baraja.
  • Una ronda consiste en que ambos jugadores jueguen al menos una carta.
  • Los jugadores se turnan para colocar la carta superior de su baraja en una pila central.
  • Si la carta es una carta numérica (2-10), el turno simplemente pasa al otro jugador.
  • Si la carta es una carta de pago, hay que pagar una penalización:
    • J → el oponente debe pagar 1 carta
    • Q → el oponente debe pagar 2 cartas
    • K → el oponente debe pagar 3 cartas
    • A → el oponente debe pagar 4 cartas
  • Si el jugador que paga una penalización revela otra carta de pago, ese jugador deja de pagar la penalización. El otro jugador debe pagar entonces una penalización basada en la nueva carta de pago.
  • Si la penalización se paga por completo sin interrupción, el jugador que colocó la última carta de pago recoge la pila central y la coloca en la parte inferior de su baraja. Ese jugador empieza entonces la siguiente ronda.
  • Si un jugador se queda sin cartas y no puede jugar ninguna (ya sea mientras paga una penalización o cuando le toca el turno), el otro jugador recoge la pila central.
  • El momento en que un jugador recoge cartas de la pila central se llama baza.
  • Si un jugador tiene todas las cartas en su posesión después de una baza, el juego termina.
  • El juego entra en un bucle en cuanto las barajas son idénticas a como eran antes durante la partida, ¡sin contar las cartas numéricas!

Ejemplos

Un pequeño ejemplo de una partida que termina.

Ronda Jugador A Jugador B Pila Penalización pendiente
1 2 A 7 8 Q 10 3 4 5 6 K 9 J -
1 A 7 8 Q 10 3 4 5 6 K 9 J 2 -
1 A 7 8 Q 10 4 5 6 K 9 J 2 3 -
1 7 8 Q 10 4 5 6 K 9 J 2 3 A Jugador B: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Jugador B: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Jugador B: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Jugador B: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Jugador A: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Jugador A: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Jugador A: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Jugador B: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Jugador B: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Jugador A: 1
1 - - 2 3 A 4 5 6 K 7 8 Q 9 J 10 -
2 - 2 3 A 4 5 6 K 7 8 Q 9 J 10 - -

estado: "finished", cartas: 13, bazas: 1

Este es un pequeño ejemplo de una partida que entra en bucle.

Ronda Jugador A Jugador B Pila Penalización pendiente
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Jugador B: 1
1 2 3 J 5 J 4 -
2 2 3 J 4 J 5 - -
2 3 J 4 J 5 2 -
2 3 J 4 5 2 J Jugador A: 1
2 J 4 5 2 J 3 -
3 J 4 5 2 J 3 - -
3 J 4 2 J 3 5 -
3 4 2 J 3 5 J Jugador B: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

estado: "loop", cartas: 8, bazas: 3

El comienzo de la ronda 4 coincide con el comienzo de la ronda 2. Recuerda: el valor de las cartas numéricas no importa.

Tu tarea

  • A partir de la entrada, simula el juego siguiendo las reglas anteriores.
  • Determina la siguiente información sobre el juego:
    • Estado: "finished" o "loop"
    • Cartas: número total de cartas jugadas a lo largo del juego
    • Bazas: número de veces que se recogió la pila central
Advanced

Para quienes quieran aceptar un desafío más emocionante, la búsqueda de otros récords del juego más largo con final sigue abierta. Hay 653.534.134.886.878.245.000 (aproximadamente 654 trillones) posibilidades, ¡y todavía no las hemos calculado todas!

Ventajas e inconvenientes de from_quote

La función Card.from_quote analiza un string y lo convierte en una Card. Esto permite escribir código como el siguiente:

card : Card
card = "Q"

Este código llama implícitamente a Card.from_quote("Q") en tiempo de compilación. Ejecutar código en tiempo de compilación con este enfoque (u otros métodos descritos a continuación) puede acelerar potencialmente el programa, ya que no necesita encargarse del análisis ni de la validación de datos en tiempo de ejecución. Además, garantiza que los datos no válidos (como un formato de fecha incorrecto) se rechacen en tiempo de compilación en lugar de en tiempo de ejecución, lo que hace que tu aplicación sea más robusta. Por último, hace que el código sea agradable y legible. Por ejemplo, este es uno de los tests de este ejercicio:

result = simulate_game({
    player_a: ["2", "A"],
    player_b: ["3", "4", "5", "6", "K"],
})

Sin embargo, alguien que lea este código podría suponer que la función simulate_game recibe un registro que contiene dos List(Str), cuando en realidad es un registro que contiene dos List(Card). Esto puede dar lugar a confusión y errores. Por este motivo, puede que prefieras el formato un poco más largo pero más explícito: "Q".Card, que también llama a from_quote en tiempo de compilación.

En tus propios programas, puede que prefieras usar from_str en su lugar:

Card := { ... }.{
    from_str : Str -> Try(Card, [InvalidCard])
    from_str = |card_str| ...
}

También puedes llamar a from_str (o a cualquier otra función pura) en tiempo de compilación, así:

Ok(card) = Card.from_str("Q")

Nota: colocar esta definición en el nivel superior hace que se ejecute en tiempo de compilación (ya que la función es pura y su entrada se conoce en tiempo de compilación). Dentro de una función, puedes usar from_str en tiempo de ejecución.

Pistas

Mostrar pistas

Usa match con [] y [card, .. as rest] para distinguir un mazo vacío de su siguiente carta y de las cartas restantes. Las actualizaciones de registros como { ..state, pile: new_pile } pueden mantener explícitas las transiciones del juego sin estado mutable. Un Set puede almacenar tuplas o listas como claves al hacer un seguimiento de estados del juego ya vistos.

Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Roc Exercism

¿Listo para empezar Camicia?

Regístrate en Exercism para aprender y dominar Roc con 135 ejercicios y mentoría humana real, todo gratis.