Last active
March 5, 2024 05:17
-
-
Save voronaam/b137a9168bf190e67503c88a11a6a558 to your computer and use it in GitHub Desktop.
Model for an unbalanced game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import random | |
LADDER_PLAYERS = 1000 | |
LADDER_GAMES = 1000000 | |
TOURNAMENTS = 20 | |
def win_probability(race1, race2, imbalance, rematch): | |
# If the players have the same race probability is 0.5, otherwise it is 0.5 + imbalance for Terg | |
if race1 == race2: | |
prob = 0.5 | |
elif race1 == 'Terg': | |
prob = 0.5 + imbalance + rematch | |
else: | |
prob = 0.5 - imbalance - rematch | |
# print("Probability of {} winning against {} is {}".format(race1, race2, prob)) | |
return prob | |
def ladder_game(player1, player2, imbalance, rematch): | |
prob = win_probability(player1["race"], player2["race"], imbalance, rematch) | |
# Return tupple (winner, loser) | |
if random.random() < prob: | |
return (player1, player2) | |
else: | |
return (player2, player1) | |
def simulate_ladder(imbalance): | |
# Create array of 500 Protoss and 500 Terg players | |
players = [{'race' : 'Protoss', 'mmr': 0} for i in range(int(LADDER_PLAYERS/2))] + [{'race' : 'Terg', 'mmr': 0} for i in range(int(LADDER_PLAYERS/2))] | |
# loop for a lot of games | |
for i in range(LADDER_GAMES): | |
# Select two random players | |
player1 = random.choice(players) | |
player2 = random.choice(players) | |
# If the players have the same skip the game | |
if player1 == player2: | |
continue | |
# Note the rematch premium is always 0.0 for ladder | |
(winner, looser) = ladder_game(player1, player2, imbalance, 0.0) | |
# Increase MMR of the winner by 1 and decrease looser's MMR by 1 | |
winner["mmr"] += 1 | |
looser["mmr"] -= 1 | |
# Find the top 100 players by MMR | |
top100 = sorted(players, key=lambda x: x['mmr'], reverse=True)[:100] | |
# Count the number of Protoss and Terg players in the top 100 | |
protoss_percent = sum([1 for p in top100 if p["race"] == "Protoss"]) | |
# format protoss percentage as float with 2 decimal places | |
print("Protoss percentage in the ladder Grand Master: {}%".format(protoss_percent)) | |
def tournament_round(players, imbalance, rematch, bo): | |
# Run the round | |
winners = [] | |
for i in range(0, len(players), 2): | |
player1 = players[i] | |
player2 = players[i+1] | |
# run `bo` games and find out the winner | |
player1wins = [random.random() < win_probability(player1, player2, imbalance, i*rematch) for i in range(bo)] | |
if max(set(player1wins), key=player1wins.count): | |
winners += [player1] | |
else: | |
winners += [player2] | |
return winners | |
def tournament(imbalance, rematch): | |
# Create array of 16 Protoss and 16 Terg players | |
players = ['Protoss' for i in range(16)] + ['Terg' for i in range(16)] | |
# Shuffle the players | |
random.shuffle(players) | |
# Round of 32 is bo3 | |
ro32winners = tournament_round(players, imbalance, rematch, 3) | |
# Round of 16 is bo3 | |
ro16winners = tournament_round(ro32winners, imbalance, rematch, 3) | |
# Round of 8 is bo5 | |
ro8winners = tournament_round(ro16winners, imbalance, rematch, 5) | |
# Round of 4 is bo5 | |
ro4winners = tournament_round(ro8winners, imbalance, rematch, 5) | |
# The final is bo7 | |
winner = tournament_round(ro4winners, imbalance, rematch, 7) | |
return winner | |
def simulate_tournaments(imbalance, rematch): | |
protoss_wins = 0 | |
for i in range(TOURNAMENTS): | |
winner = tournament(imbalance, rematch) | |
if winner[0] == 'Protoss': | |
protoss_wins += 1 | |
print("Protoss won {} tournaments out of {}.".format(protoss_wins, TOURNAMENTS)) | |
parser = argparse.ArgumentParser(description='Run the balance model') | |
parser.add_argument('imbalance', type=float, help='The game imbalance (Positive to favor Terg, negative Protoss. 0.1 means 60% winrate for Terg and 40% for Protoss)') | |
parser.add_argument('rematch', type=float, help='Rematch premium. Applied for any rematch between the same players. Positive numbers favour Terg') | |
args = parser.parse_args() | |
# Run the model | |
print("""Running the model with the following parameters | |
Chance of Protoss winning a game on ladder {:.0f}% | |
Chance of Protoss winning a game 1 in a tournament series {:.0f}% | |
Chance of Protoss winning a game 2 in a tournament series {:.0f}% | |
Chance of Protoss winning a game 3 in a tournament series {:.0f}% | |
and so on... | |
""".format( (0.5 - args.imbalance)*100, | |
(0.5 - args.imbalance)*100, | |
(0.5 - args.imbalance - args.rematch )*100, | |
(0.5 - args.imbalance - args.rematch*2 )*100)) | |
simulate_ladder(args.imbalance) | |
simulate_tournaments(args.imbalance, args.rematch) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment