¿Cómo modifico una tirada de dados falsa con repeticiones de tirada en Anydice?

Aquí hay una solución alternativa:

FUDGE: {-1, 0, +1}function: ROLL:s reroll up to SKILL:n { N: ] result: NdFUDGE + {1 .. #ROLL-N}@ROLL}loop SKILL over {0..4} { output named "skill "}

La función debería explicarse por sí misma; la única parte que puede requerir explicación es {1 .. #ROLL-N}@ROLL, que suma todos los elementos de la secuencia ROLL excepto los últimos N. Por defecto, AnyDice ordena las tiradas de dados en orden numérico descendente, por lo que los últimos elementos son los más bajos.

En el modo gráfico, las salidas de este programa se ven así:

Graph

Note cómo las diferencias entre los niveles de habilidad 2, 3 y 4 son bastante menores, ya que sacar tres o cuatro -1 en 4dF es bastante improbable para empezar.

BTW, el programa de arriba asume, como dices al final de tu pregunta, que los jugadores son conservadores y sólo volverán a tirar las tiradas negativas. Si a tus jugadores les gusta arriesgarse, podrían decidir lanzar también ceros, en cuyo caso los resultados se verían así:

Gráfico

Nota cómo los promedios siguen siendo los mismos, pero los resultados para las habilidades más altas tienen mucha más variación. En particular, las probabilidades de sacar un cuatro perfecto con una habilidad positiva son mucho más altas de esta manera.

(La única diferencia entre los programas utilizados para generar los dos gráficos anteriores es que el segundo utiliza en lugar de .)

En particular, si tus jugadores están tratando de tirar contra un número objetivo mínimo específico, puede tener sentido que sólo tiren tantos ceros como sea necesario para maximizar su probabilidad de alcanzar el objetivo.

La estrategia óptima en estos casos depende de si los jugadores pueden volver a tirar los dados uno a uno, y decidir después de cada tirada si quieren seguir tirando, o si tienen que decidir primero qué dados quieren volver a tirar y luego tirarlos todos a la vez.

En el primer caso (es decir En el primer caso (es decir, tiradas secuenciales) el proceso óptimo de toma de decisiones puede simularse con una función recursiva AnyDice:

FUDGE: {-1, 0, +1}function: first N:n of SEQ:s { FIRST: {} loop I over {1..N} { FIRST: {FIRST, I@SEQ} } result: FIRST}function: ROLL:s reroll up to SKILL:n target TARGET:n { if ROLL + 0 >= TARGET { result: 1 } \- success -\ if #ROLL = 0 | SKILL = 0 | #ROLL@ROLL = 1 { result: 0 } \- failure -\ FIRST: result: \- reroll -\}loop TARGET over {-3..4} { loop SKILL over {0..4} { output named "target , skill " }}

Aquí, la función principal ROLL reroll up to SKILL target TARGET devuelve 1 si la tirada dada es igual o mayor que el objetivo, y 0 si es menor que el objetivo y no es posible mejorarla (es decir, no quedan más dados en la reserva, no se permiten más tiradas o el dado más bajo ya es un +1). De lo contrario, elimina el dado más bajo de la reserva (utilizando una función de ayuda, ya que AnyDice no tiene una adecuada incorporada), disminuye el número de tiradas restantes en uno, resta 1dF del valor objetivo para simular una única tirada y luego se llama a sí mismo recursivamente.

La salida de este programa es un poco difícil de analizar desde la vista normal de AnyDice, así que la exporté y la pasé por el script de Python de esta respuesta anterior para convertirla en una bonita cuadrícula bidimensional que pudiera importar a Google Sheets. Los resultados, como un mapa de calor y como un gráfico de barras múltiples, se ven así:

Captura de pantalla

En el segundo caso (es decir, todos los rerolls a la vez) primero tenemos que averiguar cuál es la estrategia óptima en realidad. Un momento de reflexión muestra que:

  • Siempre hay que volver a tirar cualquier -1, ya que hacerlo nunca puede disminuir el resultado. Dado que el resultado medio esperado de una nueva tirada es 0, la media esperada después de volver a tirar todos los -1 es igual al número de +1 en la tirada inicial.

  • Volver a tirar un cero no cambia el resultado medio esperado, pero sí aumenta la varianza, es decir, hace que el resultado real sea más probable que se aleje de la media en cualquier dirección. Por lo tanto, sólo hay que volver a sacar ceros si el resultado medio esperado después de volver a sacar todos los -1 (es decir, el número de +1 en la tirada inicial) está por debajo del número objetivo.

Aplicando esta lógica en AnyDice se obtiene algo como este programa:

FUDGE: {-1, 0, +1}function: ROLL:s reroll up to SKILL:n target TARGET:n { if >= TARGET { N: ] } else { N: ] } result: (NdFUDGE + {1 .. #ROLL-N}@ROLL) >= TARGET}loop TARGET over {-3..4} { loop SKILL over {0..4} { output named "target , skill " }}

Exportando la salida de este script y ejecutándolo a través del mismo script de Python y la hoja de cálculo se obtiene el siguiente mapa de calor y gráfico de barras:

Captura de pantalla

Como se puede ver, los resultados no son en realidad tan diferentes del caso de las tiradas secuenciales. Las mayores diferencias se producen con habilidades altas y números de objetivos intermedios: por ejemplo, con una habilidad de 4, poder realizar las tiradas de nuevo de una en una y parar en cualquier punto eleva la tasa media de éxito del 75,3% al 81% para un objetivo de +1, o del 51,6% al 58,3% para un objetivo de +2.

P. Me las arreglé para averiguar una manera de hacer AnyDice recoger la «tasa de éxito frente al objetivo» los valores de los dos programas anteriores en una sola distribución para cada valor de la habilidad, lo que les permite ser dibujado directamente por AnyDice como gráficos de barras o gráficos de líneas (en «al menos» el modo) sin tener que utilizar Python u hojas de cálculo.

Desgraciadamente, el código AnyDice para hacer que es cualquier cosa menos simple. La parte más difícil resultó ser encontrar una manera de hacer que AnyDice reste dos probabilidades (por ejemplo, 1/2 – 1/3 = 1/6). La mejor manera que conozco de realizar esta tarea aparentemente trivial en AnyDice implica una manipulación no trivial de las probabilidades condicionales y un bucle iterado. Y se bloquea AnyDice si se intenta calcular 0 – 0 con él.

De todos modos, sólo para completar, aquí está el código de AnyDice para calcular y trazar la distribución del «objetivo más alto vencible» para varios niveles de habilidad (y para cada una de las dos mecánicas de rerolling descritas anteriormente) con algunos comentarios añadidos para la legibilidad:

\- predefine a fudge die -\FUDGE: d{-1, 0, +1}\- miscellaneous helper functions used in the code below -\function: first N:n of SEQ:s { FIRST: {} loop I over {1..N} { FIRST: {FIRST, I@SEQ} } result: FIRST}function: exclude RANGE:s from ROLL:n { if ROLL = RANGE { result: d{} } else { result: ROLL }}function: sign of NUM:n { result: (NUM > 0) - (NUM < 0)}function: if COND:n then A:d else B:d { if COND { result: A } else { result: B }}\- a helper function to subtract two probabilities (given as {0,1}-valued dice) -\function: P:d minus Q:d { DIFF: P - Q loop I over {1..20} { TEMP: DIFF: (DIFF != 0) * } result: }\- this function calculates the probability of meeting or exceeding the target -\- value, assuming that each die in the initial roll can be rerolled once and -\- that the player may stop rerolling at any point -\function: ROLL:s reroll one at a time up to SKILL:n target TARGET:n { if ROLL + 0 >= TARGET { result: 1 } \- success -\ if #ROLL = 0 | SKILL = 0 | #ROLL@ROLL = 1 { result: 0 } \- failure -\ FIRST: \- remove last (=lowest) original roll -\ TNEW: TARGET - 1dFUDGE \- adjust target value depending on reroll -\ result: \- reroll -\}\- this function calculates the probability of meeting or exceeding the target -\- value, assuming that each die in the initial roll can be rerolled once but -\- the player must decide in advance how many of the dice they'll reroll; the -\- optimal(?) decision rule in this case is to always reroll all -1s and to -\- also reroll 0s if and only if the number of +1s in the initial roll is less -\- than the target number -\function: ROLL:s reroll all at once up to SKILL:n target TARGET:n { if >= TARGET { N: ] } else { N: ] } result: (NdFUDGE + {1 .. #ROLL-N}@ROLL) >= TARGET}\- this function collects the success probabilities given by the two functions -\- above into a single custom die D, such that the probability that D >= N is -\- equal to the probability of the player meeting or exceeding the target N; -\- the SEQUENTIAL flag controls which of the functions above is used -\function: collect results for SKILL:n from MIN:n to MAX:n sequential SEQUENTIAL:n { BOGUS: MAX + 1 DIST: 0 PREV: 1 loop TARGET over {MIN..MAX} { if SEQUENTIAL { PROB: } else { PROB: } DIST: then TARGET else BOGUS]] PREV: PROB } result: }\- finally we just loop over possible skill values and output the results -\loop SKILL over {0..4} { output named "skill , one at a time"}loop SKILL over {0..4} { output named "skill , all at once"}

y una captura de pantalla de la salida (en modo «al menos» gráfico de líneas):

Graph

Una nota sobre la interpretación de la salida generada por el programa anterior: Las distribuciones de probabilidad mostradas en el gráfico anterior no corresponden a los resultados de ninguna estrategia de lanzamiento de dados; más bien, son distribuciones construidas artificialmente (es decir, «dados personalizados» en la jerga de AnyDice) de tal manera que la probabilidad de lanzar al menos \$N\$ en una sola tirada del dado personalizado es igual a la probabilidad de que el jugador pueda lanzar al menos \$N\$ en 4dF con la mecánica de relanzamiento dada (uno a la vez vs. En otras palabras, mirando el resultado en el modo «al menos», podemos ver que un jugador con un nivel de habilidad 4 tiene un 51,62% de posibilidades de sacar con éxito +2 o más (usando la mecánica de tirada única) si está usando sus tiradas disponibles de la manera que maximiza esa posibilidad particular. El resultado también muestra correctamente que el mismo jugador tiene un 75,28% de posibilidades de sacar +1 o más si decide optimizarlo, pero necesitará diferentes estrategias de lanzamiento para alcanzar esos dos objetivos.

Y la «probabilidad» del 23,65% de sacar exactamente +1 en el dado personalizado descrito anteriormente no tiene ningún significado, excepto que es (aproximadamente, debido al redondeo) la diferencia entre el 75,28% y el 51,62%. Supongo que es por eso que es tan difícil de calcular con AnyDice. 😛 Supongo que se podría interpretar como una medida de lo difícil que es alcanzar un objetivo de +2 utilizando la habilidad y la mecánica de reintento dadas que un objetivo de +1, en algún sentido, pero eso es todo.

*) Este fallo podría estar relacionado con lo que estoy bastante seguro de que es un error en AnyDice que encontré mientras desarrollaba este código, causando que uno de mis primeros programas de prueba generara resultados realmente extraños con cosas como 97284,21% de probabilidades (!). El programa de prueba también se bloquea eventualmente si se aumenta la cuenta de iteración más.