Después de semanas de espera, tú y tus amigos os reunís para vuestra primera partida de Dungeons & Dragons (D&D). Como es la primera sesión de la partida, cada jugador tiene que generar un personaje con el que jugar. Las habilidades del personaje se determinan lanzando dados de 6 caras, pero ¿dónde están los dados? Con un sobresalto, te das cuenta de que tus amigos esperan que tú produzcas los dados; ¡después de todo, fue tu idea jugar a D&D! Entrando en pánico, te das cuenta de que has olvidado traer los dados, lo que significaría que no habría partida de D&D. Como tienes algunos conocimientos básicos de programación, pronto se te ocurre una solución: escribirás un programa para simular tiradas de dados.
En una partida de Dungeons & Dragons, cada jugador empieza generando un personaje con el que poder jugar. Este personaje tiene, entre otras cosas, seis habilidades: fuerza, destreza, constitución, inteligencia, sabiduría y carisma. Estas seis habilidades tienen unas puntuaciones que se determinan de forma aleatoria. Para ello, lanzas cuatro dados de 6 caras y anotas la suma de los tres dados más altos. Esto lo haces seis veces, una por cada habilidad.
Los puntos de golpe iniciales de tu personaje son 10 + el modificador de constitución de tu personaje. Para hallar el modificador de constitución de tu personaje, resta 10 a la constitución de tu personaje, divide entre 2 y redondea hacia abajo.
Escribe un generador de personajes aleatorio que siga las reglas anteriores.
Por ejemplo, las seis tiradas de cuatro dados podrían ser algo así:
Como la constitución es 3, el modificador de constitución es -4 y los puntos de golpe son 6.
La mayoría de los lenguajes de programación incluyen generadores (pseudo)aleatorios, pero pocos lenguajes de programación están diseñados para lanzar dados. Uno de esos lenguajes es Troll.
En los lenguajes funcionales, la mayoría de las funciones son puras. Esto significa que siempre devuelven el mismo resultado para el mismo conjunto de argumentos y que no hacen nada más. En otras palabras, las funciones puras son deterministas.
Sin embargo, por definición, un valor aleatorio es impredecible y no determinista. Incluso la seudualetoriedad, es decir, devolver una secuencia de números que parecen aleatorios, no es tan fácil de conseguir en un contexto puro. Esto se debe a que un generador de números seudualetorios (PRNG) necesita llevar un registro de su estado interno para poder devolver el siguiente número de la secuencia.
Lean ofrece dos enfoques posibles para este problema:
Usar una mónada que permita cambiar el estado interno de un generador de números seudualetorios.
Existe una función ya disponible, IO.rand, que hace exactamente eso.
Usar funciones puras que reciban un generador como argumento y devuelvan no solo el valor seudualetorio producido, sino también un generador actualizado y «preparado» para el siguiente número de la secuencia.
Este ejercicio usa el segundo enfoque. Se pasa un generador a cada función que deba producir un valor seudualetorio, y se espera que la función devuelva tanto el valor como un generador actualizado.
Ten en cuenta que un generador dado es determinista, es decir, siempre produce el mismo valor. Para generar el siguiente valor seudualetorio de una secuencia, es necesario usar el generador actualizado.
En este ejercicio, la aleatoriedad se comprueba mediante una prueba de chi-cuadrado con un nivel de significación de p < 0.0001.
Esto significa que, si se genera una puntuación de característica según las instrucciones, hay menos de un 0,01 % de probabilidad de que una implementación correcta falle la prueba por azar.
Ten en cuenta que, según las instrucciones, una puntuación de característica es la suma de los tres resultados más altos de cuatro tiradas de un d6 no sesgado (dado de seis caras).
Regístrate en Exercism para aprender y dominar Lean con 100 ejercicios y mentoría humana real, todo gratis.
Exploramos cómo generar tiradas de dados (aparentemente) aleatorias. Aprenderás sobre semillas, números pseudoaleatorios y algunos errores comunes. Por último, veremos por qué las pruebas basadas en propiedades son ideales para probar funciones que devuelven valores aleatorios.