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...
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á).
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.
"finished" o "loop"
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!
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.
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.
Regístrate en Exercism para aprender y dominar Roc con 135 ejercicios y mentoría humana real, todo gratis.