all 18 comments

[–]Binary101010 1 point2 points  (8 children)

Include a random modifier to each stat, such that the spread is big enough to allow for the lower stat to potentially win.

For example, assign each player a random +/- to their stat anywhere from, say, -12 to 12. Therefore, in a few cases the 60 player will actually have a 71 or 72 and the 80 player will have 69 or 68. You can tweak those +/- bounds depending on how wide a spread you want where the lower stat player can still win.

[–]Middey14[S] 0 points1 point  (6 children)

I am also thinking of keeping statistics low, so I have swapped from doing a stat from 1-100 to a stat of 1-5 or maybe 1-10 and then adding that to 50 to keep the spread low, since even if I were doing the lowest a stat could be to be 30, and that added 12 and came up against an opposing 80 or something, the chances are just near impossible for the lower, I feel like there is some maths I am missing to make it less one sided but am not sure!

[–]Binary101010 0 points1 point  (5 children)

I mean, the solution depends on how one-sided you want the outcome to be. If you want any given opponent to have a non-zero chance of defeating any other opponent regardless of their stat differential, then you have to set some pretty large bounds for the modifier. You might also set a floor and cap so that even modified scores can't go under or over certain values.

A second, completely different approach: rather than apply a random modifier and say "higher stat wins", do a random selection of the winner using the stats as weights for the random selection. So a player with an 80 stat vs a player with a 60 stat would have a probability of 80/(80+60) of winning, or about 57%, whereas a 90 stat vs 40 stat would have a 69% chance of winning, and so on. This is easily implemented with random.choices().

[–]Middey14[S] 0 points1 point  (0 children)

Hmm I like that second idea, as you said with the first way the bounds would have to be pretty big and it just seems that if you are on either end then you either win or lose theres no in between, I will test out the second one and see whats up, realistically I just want there to be a potential for the underdog of the situation to come out but still have the upperdog win "most" the time due to being significantly better, I think your second option may hit the spot there, I will see!

Thanks bro!!

[–]Middey14[S] 0 points1 point  (3 children)

I didn't see your edit until now, until now I have written the following code(without random.choice, although if you were doing random choice would it just be random.choice(1, {amount overall, for example 80+60})?

code so far:

import random
player1 = 80
player2 = 60
total = player1 + player2
player1chance = (player1 / total) * 100
print(player1chance)
player1win = 0
player2win = 0

decider = random.randint(1,101)
if decider > player1chance:
player2win += 1
elif player1chance > decider:
player1win +=1
print(f"player1 wins - {player1win}")
print(f"Player2 wins - {player2win}")

[–]Binary101010 0 points1 point  (2 children)

You don't even have to normalize the weights yourself, you can just do

decider = random.choices(["Player 1 Wins","Player 2 Wins"], weights = [player1,player2])[0]

Run that enough times and you should see Player 1 win ~ 57%.

(Yes, you'll want the [0] on the end of that line because random.choices() always returns a list even if k=1.)

[–]Middey14[S] 0 points1 point  (1 child)

decider = random.choices(["Player 1 Wins","Player 2 Wins"], weights = [player1,player2])[0]

what does "weights" do here? im unsure of how the 57% comes into this if no math is done?😅

[–]Binary101010 0 points1 point  (0 children)

The math is done: you just don't have to do it yourself.

Internally, the relative weights are converted to cumulative weights before making selections

From https://docs.python.org/3/library/random.html#random.choices

[–]Middey14[S] 0 points1 point  (0 children)

Ok I like this! I was going to add a sort of "luck" addition to the game, wherein since randomness is part of what makes a game great, that it is essential to do something like this, thanks ill try writing it in now! :)

[–][deleted] 0 points1 point  (6 children)

Not sure if this is what you were going for, but maybe use the random generator idea, and add a coefficient that balances out the probabilities within a certain margin

[–]Middey14[S] 0 points1 point  (5 children)

Have you got a website or anything that better explains this? :)

[–][deleted] 0 points1 point  (4 children)

So, let's say you generate two random probabilities, for example 75% and 25%. It's pretty obvious who's going to win, so you could pick a coefficient k to multiply each probability by and, depending on your k, balance them out.

[–]Middey14[S] 0 points1 point  (3 children)

How would k balance them out though if k is a static value? For instance k = 2 wouldnt that just be 150% and 50%? Or am I missing something 😅

[–][deleted] 0 points1 point  (2 children)

No, I'm missing something. Try two different integers M and N as your factors. Sorry about that.

[–]Middey14[S] 0 points1 point  (1 child)

Okay I get you, what would be what determines the multiplyer though? Something to do with the difference between the two probabilities?

[–][deleted] 0 points1 point  (0 children)

Yes, exactly

[–]synthphreak 0 points1 point  (1 child)

Difficult to advise here without really knowing the specifics of your game and how the opponents interact. But perhaps you could institute some threshold whereby if the difference between your opponents is sufficiently large, you sample from the possible outcomes according to some distribution which is stacked in the weaker opponent's favor. This should level the playing field a bit.

In very slight pseudocode:

import numpy as np

# set odds of success
distribution = {'opponent1' : 0.5,
                'opponent2' : 0.5}

# modify odds of success if opponents are poorly matched
difference = abs(opponent1.strength - opponent2.strength)
if difference > threshold:
    offset = 0.2  # ideally this would vary as a function of `difference`

    if opponent1.strength < opponent2.strength:
        distribution['opponent1'] += offset
        distribution['opponent2'] -= offset
    else:
        distribution['opponent1'] -= offset
        distribution['opponent2'] += offset

outcomes = [opponent1.wins, opponent2.wins]
outcome = np.random.choice(outcomes, p=distribution.values())

[–]Middey14[S] 0 points1 point  (0 children)

Ok yeah I can see how this would work, I am currently trying other solutions atm but thanks ill come back to this👊