Na verdade, eu escrevi algum código para fazer isso . A essência disso é usar estatísticas para corrigir riscos infelizes. A maneira de fazer isso é acompanhar quantas vezes o evento ocorreu e usá-lo para influenciar o número gerado pelo PRNG.
Em primeiro lugar, como acompanhamos a porcentagem de eventos? A maneira ingênua de fazer isso seria manter todos os números já gerados na memória e calculá-los em média: o que funcionaria, mas é terrivelmente ineficiente. Depois de pensar um pouco, criei o seguinte (que é basicamente uma média móvel acumulada ).
Pegue as seguintes amostras de PRNG (onde procuramos se a amostra for> = 0,5):
Values: 0.1, 0.5, 0.9, 0.4, 0.8
Events: 0 , 1 , 1 , 0 , 1
Percentage: 60%
Observe que cada valor contribui para 1/5 do resultado final. Vejamos de outra maneira:
Values: 0.1, 0.5
Events: 0 , 1
Observe que 0
contribui com 50% do valor e 1
contribui com 50% do valor. Levado um pouco mais longe:
Values: [0.1, 0.5], 0.9
Events: [0 , 1 ], 1
Agora, os primeiros valores contribuem com 66% do valor e os últimos 33%. Podemos basicamente destilar isso até o seguinte processo:
result = // 0 or 1 depending on the result of the event that was just generated
new_samples = samples + 1
average = (average * samples / new_samples) + (result * 1 / new_samples)
// Essentially:
average = (average * samples / new_samples) + (result / new_samples)
// You might want to limit this to, say, 100.
// Leaving it to carry on increasing can lead to unfairness
// if the game draws on forever.
samples = new_samples
Agora precisamos polarizar o resultado do valor amostrado no PRNG, porque estamos buscando uma porcentagem de chance aqui, as coisas são muito mais fáceis (em comparação, digamos, quantidades aleatórias de dano em um RTS). Isso vai ser difícil de explicar, porque "apenas me ocorreu". Se a média for menor, significa que precisamos aumentar a chance do evento ocorrer e vice-versa. Então, alguns exemplos
average = 0.1
desired = 0.5
corrected_chance = 83%
average = 0.2
desired = 0.5
corrected_chance = 71%
average = 0.5
desired = 0.5
corrected_change = 50%
Agora, o que 'me ocorreu' é que, no primeiro exemplo, 83% eram apenas "0,5 de 0,6" (em outras palavras, "0,5 de 0,5 mais 0,1"). Em termos de eventos aleatórios, isso significa:
procced = (sample * 0.6) > 0.1
// or
procced = (sample * 0.6) <= 0.5
Portanto, para gerar um evento, você basicamente usaria o seguinte código:
total = average + desired
sample = rng_sample() * total // where the RNG provides a value between 0 and 1
procced = sample <= desired
E, portanto, você recebe o código que eu coloquei na essência. Tenho certeza de que tudo isso pode ser usado no cenário de caso de dano aleatório, mas não tomei tempo para descobrir isso.
Isenção de responsabilidade: estas são todas as estatísticas caseiras, não tenho formação em campo. Meus testes de unidade passam embora.