How to assign strength to a list of players in a game?

Viewed 252

So I have a list that has been imported from a CSV file Player & Ranking so the list goes something like:

Players = [['Tony', 3], ['Mike', 6], ['John', 9], ['Will', 7]]

I want to use the ranking of the players to create a strength for each of these players so that the person with higher strength has a better chance of winning e.g. player with rank 1 has a better chance to win the game but I have no idea how to implement this.

This is the code I have so far to just import the csv:

def import():
    players = []
    with open('players.csv', 'r') as csvfile:
        reader = csv.DictReader(csvfile, delimiter=',')
        for row in reader:
            player.append([row['Player'], int(row['Ranking'])])
    for item in player:
        print(item)
3 Answers

Here's one way to do it (without creating a class Player although doing that would likely be the better way to do it in the long run):

import csv

def import_players(filename):
    players_list = []
    with open(filename, 'r', newline='') as csvfile:
        reader = csv.DictReader(csvfile, delimiter=',')
        for row in reader:
            rank = int(row['Ranking'])
            strength = rank*2 - 1  # Calculate from rank somehow...
            players_list.append([row['Player'], rank, strength])

    return players_list

players = import_players('players.csv')
for player in players:
    print(player)

Output:

['Tony', 3, 5]
['Mike', 6, 11]
['John', 9, 17]
['Will', 7, 13]

For comparison, here's how to do it could be done by first defining a Player class:

import csv

class Player:
    def __init__(self, name, rank):
        self.name = name
        self.rank = rank
        self.strenth = rank*2 - 1  # Calculate from rank somehow...

    def __repr__(self):
        return '{}(name={!r}, rank={!r}, strength={!r})'.format(
                    type(self).__name__, self.name, self.rank, self.strenth)

def import_players(filename):
    players_list = []
    with open(filename, 'r', newline='') as csvfile:
        for row in csv.DictReader(csvfile, delimiter=','):
            rank = int(row['Ranking'])
            # Note strength will be calculated and added by the class constructor.
            players_list.append(Player(row['Player'], rank))

    return players_list

players = import_players('players.csv')
for player in players:
    print(player)

Output:

Player(name='Tony', rank=3, strength=5)
Player(name='Mike', rank=6, strength=11)
Player(name='John', rank=9, strength=17)
Player(name='Will', rank=7, strength=13)

As others have suggested, I would strongly suggest creating a class for each of your players.

class Player:
    def __init__(self, name, strength):
        self.name = name
        self.strength = strength

Once you have created your Player class, you can create your players list using the following:

players = [Player('Tony',3), Player('Mike',6), Player('John',9), Player('Will',7)]

Finishing up, I would suggest that you sort your players list so that the first instance of a Player in your list has the highest strength.

players.sort(key=lambda x: x.strength, reverse=True)

To view how the order has changed, you can do the following:

for player in players:
    print(json.dumps(player.__dict__))

You will see the following output:

{"name": "John", "strength": 9}
{"name": "Will", "strength": 7}
{"name": "Mike", "strength": 6}
{"name": "Tony", "strength": 3}

Now you just need to determine how you would like to calculate the winner, for which we would need more information.

One solution would be to redefine the Player class so that each Player has a points property, which increases for each iteration. The strength property would become a maximum for the number of points that can result from each iteration. The first player to reach 100 points wins.

A example implementation of this approach might look like this:

import json
import random

class Player:
    def __init__(self, name, strength):
        self.name = name
        self.strength = strength
        self.points = 0
        self.point_history = list()

players = [Player('Tony',3), Player('Mike',6), Player('John',9), Player('Will',7)]

players.sort(key=lambda x: x.strength, reverse=True)

while not any(player.points >= 100 for player in players):
    for player in players:
        points = random.randint(0, player.strength)
        player.points += points
        player.point_history.append(points)

        print(f'{player.name} earned {points} points and now has a total of {player.points} points!')

        if player.points >= 100:
            break

for player in players:
    print(json.dumps(player.__dict__))

If your question is how to represent strengths based on rankings so that the probability that a player wins is based on his rank, you can represent strength as a range tuple (e.g. (3, 9)).

One rule to assign these ranges to players can be:

  • sort players by rank
  • create p ranges from 0 to sum of all ranks (where p is the total number of players) so that ranges are created based on a player's rank number
  • assign ranges to players in reverse order so that a player with a higher rank (lower rank number) gets a higher range tuple

Here's an implementation:

players = [['Tony', 3], ['Mike', 6], ['John', 9], ['Will', 7]]
players.sort(key=lambda x: x[1])

# create strength ranges
strengths = []
i = 0
for p in players:
    strengths.append((i, i + p[1]))
    i += p[1]

# assign strengths in reverse order
for p in players:
    p.append(strengths.pop())

print(players)
# [['Tony', 3, (16, 25)], ['Mike', 6, (9, 16)], ['Will', 7, (3, 9)], ['John', 9, (0, 3)]]

Now suppose we play a random dice game with random.randint, we can see how these ranges determine winners:

import random
from collections import Counter

sum_ranks = sum(p[1] for p in players)
winners = []
for game in range(100):
    dice_roll = random.randint(0, sum_ranks - 1)
    winning_player = [p for p in players if dice_roll >= p[2][0] and dice_roll < p[2][1]][0]
    winners.append(winning_player[0])

print(Counter(winners))
# Counter({'Tony': 39, 'Mike': 35, 'Will': 14, 'John': 12})

For several more experiments, we see the following results (the number in the counter object represents how many games that player won):

Counter({'Tony': 32, 'Mike': 29, 'Will': 27, 'John': 12})
Counter({'Tony': 33, 'Will': 29, 'Mike': 28, 'John': 10})
Counter({'Tony': 45, 'Mike': 28, 'Will': 16, 'John': 11})
Counter({'Tony': 34, 'Will': 28, 'Mike': 27, 'John': 11})
Counter({'Tony': 39, 'Mike': 35, 'Will': 20, 'John': 6})
Related