Estou no processo de criar um novo jogo simples no celular e passei vários dias na parte seguinte.
Por simplicidade, digamos que tenho dois lutadores. O único atributo deles é Ataque e Defesa. Quando os primeiros ataques, a única coisa que importa é o ataque dele e a defesa do oponente. E vice versa.
Eles não têm equipamentos, itens, resistência ou saúde. Apenas ataque contra defesa.
Exemplo:
Lutador 1:
Ataque: 50, Defesa: 35
Lutador 2:
Ataque 20, Defesa: 80
O processo de luta será apenas um ataque que determinará o vencedor. Portanto, não há múltiplos ataques ou rodadas. Não quero torná-lo determinístico, mas adicione uma versão leve do inesperado. Um lutador com ataque mais baixo será capaz de vencer outro lutador com maior defesa (mas é claro que nem sempre)
Minha primeira idéia foi torná-lo linear e chamar um gerador de números aleatórios uniforme.
If Random() < att1 / (att1 + def2) {
winner = fighter1
} else {
winner = fighter2
}
Exemplo no ataque 50 e defesa 80, o lutador atacante terá cerca de 38% para vencer. No entanto, parece-me que o inesperado é longe demais e os piores lutadores ganharão muito.
Fiquei me perguntando como você trabalhou em situações semelhantes.
PS Pesquisei bastante neste QnA e em outras fontes e encontrei perguntas semelhantes mencionadas como muito amplas para o SE. Mas esses tiveram muitos atributos, armas, itens, classes, etc, que podem tornar muito complicado. Eu acho que minha versão é muito mais simples de encaixar no estilo QnA do SE.
Respostas:
Se você deseja que os resultados da luta sejam mais previsíveis, mas não completamente determinísticos, tenha o melhor sistema de n .
Repita os
n
tempos de luta (onden
deve haver um número desigual) e declare o combatente o vencedor que venceu com mais frequência. Quanto maior o seu valor,n
menos surpresas você terá.Este sistema funciona apenas no caso especial em que uma luta é um resultado binário simples de vitória ou derrota. Quando um combate tem resultados mais complexos, como quando o vencedor ainda perde alguns pontos de vida, dependendo da proximidade da vitória, essa abordagem não funciona mais. Uma solução mais geral é mudar a maneira como você gera números aleatórios. Quando você gera vários números aleatórios e calcula a média, os resultados se agrupam perto do centro do intervalo e resultados mais extremos serão mais raros. Por exemplo:
terá uma curva de distribuição como esta:
(imagem cortesia de anydice - uma ferramenta realmente útil para projetar fórmulas mecânicas de jogos que envolvem aleatoriedade, não apenas para jogos de mesa)
No meu projeto atual, estou usando uma função auxiliar que permite definir um tamanho de amostra arbitrário:
fonte
+
vez de*
ou entendi mal o que faz?Foi isso que eu usei para determinar o vencedor de uma batalha no meu applet Lords of Conquest Imitator. Neste jogo, semelhante à sua situação, há apenas um valor de ataque e um valor de defesa. A probabilidade de o atacante vencer é maior, mais pontos o atacante tem e menos pontos a defesa tem, com valores iguais avaliando a chance de 50% do ataque ter êxito.
Algoritmo
Jogue uma moeda aleatória.
1a. Cabeças: a defesa perde um ponto.
1b. Caudas: as cabeças perdem um ponto.
Se a defesa e o atacante ainda tiverem pontos, volte para a etapa 1.
Quem está abaixo de 0 pontos perde a batalha.
3a. Atacante até 0: o ataque falha.
3b Defesa até 0: O ataque foi bem-sucedido.
Eu escrevi em Java, mas deve ser facilmente traduzível para outras línguas.
Um exemplo
Por exemplo, digamos que att = 2 e def = 2, apenas para garantir que a probabilidade seja de 50%.
A batalha será decidida no máximo de
n = att + def - 1
lançamentos de moedas, ou 3 neste exemplo (é essencialmente o melhor de 3 aqui). Existem 2 n combinações possíveis de moeda vira. Aqui, "W" significa que o atacante ganhou o lançamento da moeda e "L" significa que o atacante perdeu o lançamento da moeda.O atacante vence em 4/8, ou 50% dos casos.
A matemática
As probabilidades matemáticas decorrentes desse algoritmo simples são mais complicadas do que o próprio algoritmo.
O número de combinações em que exatamente x Ls é dado pela função de combinação:
O atacante vence quando há entre
0
eatt - 1
Ls. O número de combinações vencedoras é igual à soma das combinações de0
throughatt - 1
, uma distribuição binomial cumulativa:A probabilidade de o atacante vencer é w dividida por 2 n , uma probabilidade binomial cumulativa:
Aqui está o código em Java para calcular essa probabilidade para valores arbitrários
att
edef
:Código de teste:
Resultado:
Observações
As probabilidades são:
0.0
se o atacante tiver0
pontos,1.0
se o atacante tiver pontos, mas a defesa tiver0
pontos,0.5
se os pontos forem iguais, menor que0.5
se o atacante tiver menos pontos que a defesa e maior que0.5
se o atacante tiver mais pontos que a defesa .Tomando
att = 50
edef = 80
, eu precisava mudar paraBigDecimal
s para evitar transbordamento, mas tenho uma probabilidade de cerca de 0,0040.Você pode tornar a probabilidade mais próxima de 0,5 alterando o
att
valor para ser a média dos valoresatt
edef
. Att = 50, Def = 80 se torna (65, 80), o que gera uma probabilidade de 0,1056.fonte
Você pode modificar o ataque por um número aleatório amostrado de uma distribuição normal. Dessa forma, na maioria das vezes o resultado será o que você espera, mas, ocasionalmente, um ataque mais alto perde contra uma defesa mais baixa ou um ataque mais baixo vence contra uma defesa mais alta. A probabilidade disso acontecer será menor à medida que a diferença entre ataque e defesa aumentar.
A função
norm(x0, sigma)
retorna um float amostrado de uma distribuição normal centralizada em x0, com sigma de desvio padrão. A maioria das linguagens de programação fornece uma biblioteca com essa função, mas se você quiser fazer isso, dê uma olhada nessa pergunta . Você precisaria ajustar o sigma de modo que "pareça certo", mas um valor de 10 a 20 pode ser um bom lugar para começar.Para alguns valores sigma, a probabilidade de vitória para um dado se
att1 - def2
parece com isso:fonte