Sunday, March 4, 2018

mathematics - Attack vs Defence and who is the winner?



I am in the process of creating a new simple game in mobile and I've spent several days on the following part.


For simplicity, let's say that I have two fighters. The only attribute of them is Attack and Defence. When the first attacks, the only thing that matters is the attack of him and the defence of the opponent. And vice versa.


They don't have equipment, items, stamina or health. Just Attack vs Defence.


Example:




  • Fighter 1:


    Attack:50, Defence: 35





  • Fighter 2:


    Attack 20, Defence: 80




The fighting process will be just a single attack which will determine the winner. So, no multiple attacks or rounds. I don't want to make it deterministic, but add a light version of unexpected. A fighter with lower attack will be able to win another fighter with higher defence (but of course not every time)


My first idea was to make it linear and call a uniform random number generator.


If Random() < att1 / (att1 + def2) {
winner = fighter1

} else {
winner = fighter2
}

Example with attack 50 and defence 80, the attacking fighter will have about 38% to win. However, it seems to me that the unexpected is too far and worst fighters will win a lot.


I was wondering how you have worked on similar situations.


P.S. I searched a lot in this QnA and other sources and I found similar questions mentioned as too broad for SE. But those have had many attributes, weapons, items, classes etc that could make it too complicated. I think my version is far simpler to fit it in the QnA style of the SE.



Answer



If you want your fight results to be more predictable but not completely deterministic, have a best of n system.


Repeat the fight n times (where n should be an uneven number) and declare the combatant the winner who won more often. The larger your value for n the less surprise wins and losses you will have.



const int FIGHT_REPETITONS = 5 // best 3 of 5. Adjust to taste.

int fighter1wins = 0;
int fighter2wins = 0;

for (int i = 0; I < FIGHT_REPETITONS; I++) {

If (Random() < att1 / (att1 + def2)) {
fighter1wins++;
} else {

fighter2wins++;
}

}

If (fighter1wins > fighter2wins) {
winner = fighter1
} else {
winner = fighter2
}


This system only works in the special case where a fight is a simple binary result of win or lose. When a combat has more complex results, like when the winner still loses some hit points depending on how close the win was, this approach doesn't work anymore. A more general solution is to change the way you generate random numbers. When you generate multiple random numbers and then take the average, the results will cluster near the center of the range and more extreme results will be more rare. For example:


double averagedRandom3() {
return (Random() + Random() + Random()) / 3.0;
}

will have a distribution curve like this:


Distribution of 3d20 / 3


(picture courtesy of anydice - a really useful tool for designing game mechanic formulas which involve randomness, not just for tabletop games)


In my current project I am using a helper-function which allows to set an arbitrary sample size:



double averagedRandom(int averageness) {
double result = 0.0;
for (var i = 0; i < averageness; i++) {
result += Random();
}
return result / (double)averageness;
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...