Inspired by some projects, I have decided to work on a calculator project based on Python.
Essentially, I have 5 teams in a fantasy league, with points assigned to these teams based on their current standings. Teams A-E.
Assuming the league has 10 more matches to be played, my main aim is to calculate the probability that a team makes it to the top 3 in their league given the matches have a 33.3% of either going:
A win to the team (which adds 2 points to the winning team)
A lose to the team (which awards 0 points to the losing team)
A draw (which awards 1 point to both teams)
This also in turn means there will be 3^10 outcomes for the 10 matches to be played.
For each of these 3^10 scenarios, I will also compute how the final points table will look, and from there, I will be able to sort and figure out which are the top 3 teams in the fantasy league.
I've worked halfway through the project, as shown:
Points = { "A":12, "B":14, "C":8, "D":12, "E":6} #The current standings
RemainingMatches = [
A:B
B:D
C:E
A:E
D:C
B:D
C:D
A:E
C:E
D:C
]
n=len(RemainingMatches) # Number of matches left
combinations = pow(3,n) # Number of possible scenarios left assumes each game has 3 outcomes
print( "Number of remaining matches = %d" % n )
print( "Number of possible scenarios = %d" % combinations )
for i in range(0,combinations)
...
for i in range(0,n)
I am currently wondering how do I get these possible combinations to match a certain scenario? For example, when i = 0, it points to the first matchup where A wins, B losses. Hence, Points[A] = Points[A] + 2 etc. I know there will be a nested loop since I have to consider the other matches too. But how do I exactly map each scenario, and then nest it?
Apologies if I am being unclear here, struggling with this part at the moment.
Thinking Process:
3 outcomes per game.
for i to combinations:
#When i =1, means A win/B lost?
#When i =2, means B win/A lost?
#When i =3, means A/B drew?
for i to n:
#Go to next match?
Not exactly sure what is the logic flow for this kind of scenario. Thank you.
Here is a different way to write the code. If you knew in advance the outcome of each of the ten remaining games, you could compute which teams would finish in top three. This is done with the play_out function, which takes current standings, remaining games, and the known outcomes of future games.
Now all that remains is to loop over all possible future outcomes and tally the winning probabilities. This is done in the simulate_league function that takes in current standings and remaining games, and returns a probability that a given team finishes in top 3.
There may be situations where two teams are tied for the third place. In cases like this, the code below allows for four teams or more to be in "top 3". To use a different rule, you can change the top3scores = nlargest(3, set(pts.values())) line.
In terms of useful Python functions, itertools.product is used to generate all possible win/draw/loss outcomes, and heapq.nlargest is used to find the largest 3 scores out of a bunch. The collections.Counter class is used to count the number of possibilities in which a given team finishes in top 3.
from itertools import product
from heapq import nlargest
from collections import Counter
Points = {"A":12, "B":14, "C":8, "D":12, "E":6} # The current standings
RemainingMatches = ["A:B","B:D","C:E","A:E","D:C","B:D","C:D","A:E","C:E","D:C"]
# reformat remaining matches
RemainingMatches = [tuple(s.split(":")) for s in RemainingMatches]
def play_out(points, remaining_matches, winloss_schedule):
pts = points.copy()
# simulate remaining games given predetermine win/loss/draw outcomes
# given by winloss_schedule
for (team_a, team_b), outcome in zip(remaining_matches, winloss_schedule):
if outcome == -1:
pts[team_a] += 2
elif outcome == 0:
pts[team_a] += 1
pts[team_b] += 1
else:
pts[team_b] += 2
# top 3 scores (allows for ties)
top3scores = nlargest(3, set(pts.values()))
return {team: score in top3scores for team, score in pts.items()}
def simulate_league(points, remaining_matches):
top3counter = Counter()
for winloss_schedule in product([-1, 0, 1], repeat=len(remaining_matches)):
top3counter += play_out(points, remaining_matches, winloss_schedule)
total_possibilities = 3 ** len(remaining_matches)
return {team: top3count / total_possibilities
for team, top3count in top3counter.items()}
# simulate_league(Points, RemainingMatches)
# {'A': 0.9293637487510372,
# 'B': 0.9962573455943369,
# 'C': 0.5461057765584515,
# 'D': 0.975088485833799,
# 'E': 0.15439719554945894}
Related
I’m trying to apply programming to a real-life problem that comes around each year for my tennis team. It’s about picking the best team for a team match, given some rules. I thought I’d have a go at formalising the rules and automating the player selection process.
I’m used to using Matlab from uni, but have dabbled in Python with numpy before so I went with that. I’m trying to formulate the problem as an optimization, and am using a toolkit called puan as a way of “writing rules”. I went through the puan tutorials successfully and thought it should be possible to apply similar logic to my problem.
Bear with me, I’m not a star programmer! Hope you can follow the reasoning.
Prerequisites:
The team has 5 possible players.
The matches to be played are 2 singles matches and 1 doubles match.
Therefore, two players need to be picked to play a singles match each and two players to play the doubles match together.
Rules:
A player cannot play both singles and doubles.
Question:
I have managed to formulate the problem, and to get the program to pick players so that the matches can be played. I can also set a preference for if I know beforehand that I want a specific player to definitely play the doubles. But my end goal is to automatically get the program to select a constellation of players that is based on a ranking in singles and a ranking in doubles (i.e put the best singles players on the singles matches, and the doubles specialists in the doubles match). How would I do that, attach a ranking which is used as a parameter in the player selection? That’s where I’m stumped.
Here is the code right now:
import puan
import puan.logic.plog as pg
import puan.modules.configurator as cc
import puan_solvers as ps
Define variables true/false to reply to the questions "Does this player play singles?" and "Does this player play doubles?"
p1s = puan.variable(id="p1s") #Does player 1 play the singles match?
p2s = puan.variable(id="p2s")
p3s = puan.variable(id="p3s")
p4s = puan.variable(id="p4s")
p5s = puan.variable(id="p5s")
p1d = puan.variable(id="p1d") #Does player 1 play the doubles match?
p2d = puan.variable(id="p2d")
p3d = puan.variable(id="p3d")
p4d = puan.variable(id="p4d")
p5d = puan.variable(id="p5d")
Combine "at most" and "at least" propositions to get exactly 2 singles matches.
singles_matches_max = pg.AtMost(propositions=[p1s,p2s,p3s,p4s,p5s],value=2)
singles_matches_min = pg.AtLeast(propositions=[p1s,p2s,p3s,p4s,p5s],value=2)
Combine "at most" and "at least" propositions to get exactly two players who play doubles, i.e. one doubles pairing.
doubles_matches_max = pg.AtMost(propositions=[p1d,p2d,p3d,p4d,p5d],value=2)
doubles_matches_min = pg.AtLeast(propositions=[p1d,p2d,p3d,p4d,p5d],value=2)
Define that players 1-5 may not play both singles and doubles.
only_one_match1 = pg.Not(pg.All(p1s,p1d))
only_one_match2 = pg.Not(pg.All(p2s,p2d))
only_one_match3 = pg.Not(pg.All(p3s,p3d))
only_one_match4 = pg.Not(pg.All(p4s,p4d))
only_one_match5 = pg.Not(pg.All(p5s,p5d))
Combine all of the above rules/criteria into a configurator model of the team.
team_model = cc.StingyConfigurator(
singles_matches_max,
singles_matches_min,
doubles_matches_max,
doubles_matches_min,
only_one_match1,
only_one_match2,
only_one_match3,
only_one_match4,
only_one_match5
)
I'm not sure what this does but I kept it from the tutorial:
team_ph = team_model.to_ge_polyhedron(active=True)
Formulate a solution based off of one initial truth, that player 4 plays doubles.
team_solution = next(
team_model.select(
{"p4d": 1}, #I know that player 4 is the best doubles player, so I want them to play doubles.
solver = ps.glpk_solver,
only_leafs = True
)
)
print(team_solution)
This gives an answer of:
{'p1d': 0.0, 'p1s': 1.0, 'p2d': 1.0, 'p2s': 0.0, 'p3d': 0.0, 'p3s': 1.0, 'p4d': 1.0, 'p4s': 0.0, 'p5d': 0.0, 'p5s': 0.0}
Which makes sense to me, because you have Players 1 and 3 playing singles, and Players 2 and 4 teaming up to play the doubles match. The one left out is Player 5.
So now my wish is to connect a singles and doubles ranking to each of the players, and get the optimizer to choose the team that is strongest on each match. I would have liked to add it somehow as an attribute to each variable but I can't see that that's possible!
In my country we have a ranking system where the lower your number is, the better you are. For example, I’d like to define the following for my players:
p1 = {"s_rank": 1.9225, "d_rank": 1.8000} #Singles rank and doubles rank, lower number = better
p2 = {"s_rank": 3.8156, "d_rank": 3.7765}
p3 = {"s_rank": 4.0611, "d_rank": 2.3133}
p4 = {"s_rank": 3.0974, "d_rank": 3.9666}
p5 = {"s_rank": 2.3790, "d_rank": 2.5672}
As you may guess, I want the optimizer to recognize that Player 4 is better at singles than doubles. And that Player 3 is quite terrible at singles but good in doubles. But also: you can see that Player 1 and Player 5 are the best players ranking-wise overall. But if I were to put both of them on the doubles, that would only be one match win for the team. Putting them on a singles match each would probably win the team two matches. So I’d like to write a rule that the best 2 players on the team should avoid being placed in the doubles.
Further actual criteria I’d like to add down the line is that: there is a limit on the number of foreign players a team can play with (because it’s the national team championships). I should be able to add this info too and get the optimizer to take it into consideration. Another dimension is “home” matches and “away” matches, with some players either not able to travel to other cities to play, or they simply compete a lot worse without home arena support…
I’m running Python 3.9.2 right now, I don’t know if that might be relevant to running this! Thanks for any suggestions!
I have used Google-ortool's CP-SAT solver (python API) to solve the above optimization problem. CP-SAT solver cannot handle float values, so I have converted the ranks into integer by multiplying with a large scalar value.
You can do a "pip install ortools" to install the ortools package in your python environment (in case you don't have that already
from ortools.sat.python import cp_model as cp
players = {"p1" : {"s_rank": 19225, "d_rank": 18000}, # multiplied all ranks with a scalar to convert them
"p2" : {"s_rank": 38156, "d_rank": 37765},# into integers. This will not impact the solution
"p3" : {"s_rank": 40611, "d_rank": 23133},
"p4" : {"s_rank": 30974, "d_rank": 39666},
"p5" : {"s_rank": 23790, "d_rank": 25672},
"p6" : {"s_rank": 56745, "d_rank": 23456},
"p7" : {"s_rank": 15645, "d_rank": 21345}
}
nationality = {'p1' : ("domestic","international"), # can play both domestic and international
'p2' : ("domestic"), # can play only domestic
'p3' : ("domestic"),
'p4' : ("international"), # can play only international
'p5' : ("domestic","international"),
'p6' : ("domestic"),
'p7' : ("international")}
game_location = {'p1' : ("home"), # can play only home games
'p2' : ("home", "away"), # can play both home and away games
'p3' : ("home", "away"),
'p4' : ("home", "away"),
'p5' : ("away"), # can play only away games
'p6' : ("away"),
'p7' : ("home")}
model = cp.CpModel()
# decision variable : whether a player will play a single or double
dv_player_sngl_dbl = {}
for plry in players:
for typ in ["single", "double"]:
dv_player_sngl_dbl[(plry, typ)] = model.NewBoolVar(plry + "_" + typ)
# constraint a player can play either a single or double (and not both).
# The player can be out from both single or double also
for plry in players:
model.Add(sum(dv_player_sngl_dbl[(plry, typ)] for typ in ["single", "double"]) <= 1)
total_players_singles = 2
total_players_doubles = 2
num_single_games = 2
num_double_games = 1
# match attributes:
matches = {
"match_1" : ("single", "home", "domestic"), # match 1 is a singles match to be played at home. Only domestic player can participate
"match_2" : ("single", "home", "domestic"),
"match_3" : ("double", "home", "international")
}
dv_total_singles_score = model.NewIntVar(0, 100000000, "")
dv_total_doubles_score = model.NewIntVar(0, 100000000, "")
dv_avg_doubles_score = model.NewIntVar(0, 100000000, "")
# constraint : select 2 single players
model.Add(sum(dv_player_sngl_dbl[(plry, "single")] for plry in players) == total_players_singles)
# constraint : select 2 doubles players
model.Add(sum(dv_player_sngl_dbl[(plry, "double")] for plry in players) == total_players_doubles)
# calculate the rank scores when selecting 2 single players (2 matches)
# rank scores of these players will be added to the objective function, which we will minimize at the end
model.Add(dv_total_singles_score == sum(dv_player_sngl_dbl[(plry, "single")] * players[plry]["s_rank"] for plry in players))
# calculate the rank scores when selecting 2 double players (1 match)
# summation of the double scores for 2 players will be added and then divided by 2
# because its a single match
model.Add(dv_total_doubles_score == sum(dv_player_sngl_dbl[(plry, "double")] * players[plry]["d_rank"] for plry in players))
# divivding the above summed score for 2 players by 2 (since its one match)
model.AddDivisionEquality(dv_avg_doubles_score, dv_total_doubles_score, num_double_games * 2)
# addiing game location (home / away) and nationality (domestic / international) constraint
for match in matches:
match_details = matches[match]
single_or_double = match_details[0]
location = match_details[1]
nation = match_details[2]
# players who cannot play at the above game location
plyrs_not_in_loc = [k for k,v in game_location.items() if location not in v]
# constraint : players such as above cannot be selected for the match
model.Add(sum(dv_player_sngl_dbl[(plry, single_or_double)] for plry in plyrs_not_in_loc) == 0)
# players who are not eligible to play per nationality status
plyrs_not_in_nationality = [k for k,v in nationality.items() if nation not in v]
# constraint : players such as above cannot be selected for the match
model.Add(sum(dv_player_sngl_dbl[(plry, single_or_double)] for plry in plyrs_not_in_nationality) == 0)
# defining the objective function
# this is summation of ranks, and we will minimize (since lower rank is better)
objective_function = dv_total_singles_score + dv_avg_doubles_score
model.Minimize(objective_function)
solver = cp.CpSolver()
solver.Solve(model)
# inspect decision variable values
for plry in players:
for typ in ["single", "double"]:
print(str((plry, typ)) + " : " + str(solver.Value(dv_player_sngl_dbl[(plry, typ)])))
Imagine I have 2 teams of 2 players. For each player, there exist two probability statistics-- call one an attempt % and another a score %.
To calculate the odds of a player scoring, I would multiply the attempt % by the scoring %. How could I create a program that simulates turns from each team that continues until a player scores 15 times?
recursion would be one way to do it:
do_stuff <- function(payload, turn = 1, max_turns = 15){
if(turn >= max_turns) return(payload)
payload <- recalculate_payload(payload) ## desired recalculations here
turn = turn + 1
do_stuff(payload, turn, max_turns)
}
do_stuff(payload = pi) ## instead of Pi, could be a `data.frame` or other
Goal -
I am trying to implement a genetic algorithm to optimise the fitness of a
species of creatures in a simulated two-dimensional world. The world contains edible foods, placed at random, and a population of monsters (your basic zombies). I need the algorithm to find behaviours that keep the creatures well fed and not dead.
What i have done -
So i start off by generating a 11x9 2d array in numpy, this is filled with random floats between 0 and 1. I then use np.matmul to go through each row of the array and multiply all of the random weights by all of the percepts (w1+p1*w2+p2....w9+p9) = a1.
This first generation is run and I then evaluate the fitness of each creature using (energy + (time of death * 100)). From this I build a list of creatures who performed above the average fitness. I then take the best of these "elite" creatures and put them back into the next population. For the remaining space I use a crossover function which takes two randomly selected "elite" creatures and mixes their genes. I have tested two different crossover functions one which does a two point crossover on each row and one which takes a row from each parent until the new child has a complete chromosome. My issue is that the creatures just don't really seem to be learning, at 75 turns I will only get 1 survivor every so often.
I am fully aware this might not be enough to go off but I am truly stuck on this and cannot figure out how to get these creatures to learn even though I think I am implementing the correct procedures. Occasionally I will get a 3-4 survivors rather than 1 or 2 but it appears to occur completely randomly, doesn't seem like there is much learning happening.
Below is the main section of code, it includes everything I have done but none of the provided code for the simulation
#!/usr/bin/env python
from cosc343world import Creature, World
import numpy as np
import time
import matplotlib.pyplot as plt
import random
import itertools
# You can change this number to specify how many generations creatures are going to evolve over.
numGenerations = 2000
# You can change this number to specify how many turns there are in the simulation of the world for a given generation.
numTurns = 75
# You can change this number to change the world type. You have two choices - world 1 or 2 (described in
# the assignment 2 pdf document).
worldType=2
# You can change this number to modify the world size.
gridSize=24
# You can set this mode to True to have the same initial conditions for each simulation in each generation - good
# for development, when you want to have some determinism in how the world runs from generation to generation.
repeatableMode=False
# This is a class implementing you creature a.k.a MyCreature. It extends the basic Creature, which provides the
# basic functionality of the creature for the world simulation. Your job is to implement the AgentFunction
# that controls creature's behaviour by producing actions in response to percepts.
class MyCreature(Creature):
# Initialisation function. This is where your creature
# should be initialised with a chromosome in a random state. You need to decide the format of your
# chromosome and the model that it's going to parametrise.
#
# Input: numPercepts - the size of the percepts list that the creature will receive in each turn
# numActions - the size of the actions list that the creature must create on each turn
def __init__(self, numPercepts, numActions):
# Place your initialisation code here. Ideally this should set up the creature's chromosome
# and set it to some random state.
#self.chromosome = np.random.uniform(0, 10, size=numActions)
self.chromosome = np.random.rand(11,9)
self.fitness = 0
#print(self.chromosome[1][1].size)
# Do not remove this line at the end - it calls the constructors of the parent class.
Creature.__init__(self)
# This is the implementation of the agent function, which will be invoked on every turn of the simulation,
# giving your creature a chance to perform an action. You need to implement a model here that takes its parameters
# from the chromosome and produces a set of actions from the provided percepts.
#
# Input: percepts - a list of percepts
# numAction - the size of the actions list that needs to be returned
def AgentFunction(self, percepts, numActions):
# At the moment the percepts are ignored and the actions is a list of random numbers. You need to
# replace this with some model that maps percepts to actions. The model
# should be parametrised by the chromosome.
#actions = np.random.uniform(0, 0, size=numActions)
actions = np.matmul(self.chromosome, percepts)
return actions.tolist()
# This function is called after every simulation, passing a list of the old population of creatures, whose fitness
# you need to evaluate and whose chromosomes you can use to create new creatures.
#
# Input: old_population - list of objects of MyCreature type that participated in the last simulation. You
# can query the state of the creatures by using some built-in methods as well as any methods
# you decide to add to MyCreature class. The length of the list is the size of
# the population. You need to generate a new population of the same size. Creatures from
# old population can be used in the new population - simulation will reset them to their
# starting state (not dead, new health, etc.).
#
# Returns: a list of MyCreature objects of the same length as the old_population.
def selection(old_population, fitnessScore):
elite_creatures = []
for individual in old_population:
if individual.fitness > fitnessScore:
elite_creatures.append(individual)
elite_creatures.sort(key=lambda x: x.fitness, reverse=True)
return elite_creatures
def crossOver(creature1, creature2):
child1 = MyCreature(11, 9)
child2 = MyCreature(11, 9)
child1_chromosome = []
child2_chromosome = []
#print("parent1", creature1.chromosome)
#print("parent2", creature2.chromosome)
for row in range(11):
chromosome1 = creature1.chromosome[row]
chromosome2 = creature2.chromosome[row]
index1 = random.randint(1, 9 - 2)
index2 = random.randint(1, 9 - 2)
if index2 >= index1:
index2 += 1
else: # Swap the two cx points
index1, index2 = index2, index1
child1_chromosome.append(np.concatenate([chromosome1[:index1],chromosome2[index1:index2],chromosome1[index2:]]))
child2_chromosome.append(np.concatenate([chromosome2[:index1],chromosome1[index1:index2],chromosome2[index2:]]))
child1.chromosome = child1_chromosome
child2.chromosome = child2_chromosome
#print("child1", child1_chromosome)
return(child1, child2)
def crossOverRows(creature1, creature2):
child = MyCreature(11, 9)
child_chromosome = np.empty([11,9])
i = 0
while i < 11:
if i != 10:
child_chromosome[i] = creature1.chromosome[i]
child_chromosome[i+1] = creature2.chromosome[i+1]
else:
child_chromosome[i] = creature1.chromosome[i]
i += 2
child.chromosome = child_chromosome
return child
# print("parent1", creature1.chromosome[:3])
# print("parent2", creature2.chromosome[:3])
# print("crossover rows ", child_chromosome[:3])
def newPopulation(old_population):
global numTurns
nSurvivors = 0
avgLifeTime = 0
fitnessScore = 0
fitnessScores = []
# For each individual you can extract the following information left over
# from the evaluation. This will allow you to figure out how well an individual did in the
# simulation of the world: whether the creature is dead or not, how much
# energy did the creature have a the end of simulation (0 if dead), the tick number
# indicating the time of creature's death (if dead). You should use this information to build
# a fitness function that scores how the individual did in the simulation.
for individual in old_population:
# You can read the creature's energy at the end of the simulation - it will be 0 if creature is dead.
energy = individual.getEnergy()
# This method tells you if the creature died during the simulation
dead = individual.isDead()
# If the creature is dead, you can get its time of death (in units of turns)
if dead:
timeOfDeath = individual.timeOfDeath()
avgLifeTime += timeOfDeath
else:
nSurvivors += 1
avgLifeTime += numTurns
if individual.isDead() == False:
timeOfDeath = numTurns
individual.fitness = energy + (timeOfDeath * 100)
fitnessScores.append(individual.fitness)
fitnessScore += individual.fitness
#print("fitnessscore", individual.fitness, "energy", energy, "time of death", timeOfDeath, "is dead", individual.isDead())
fitnessScore = fitnessScore / len(old_population)
eliteCreatures = selection(old_population, fitnessScore)
print(len(eliteCreatures))
newSet = []
for i in range(int(len(eliteCreatures)/2)):
if eliteCreatures[i].isDead() == False:
newSet.append(eliteCreatures[i])
print(len(newSet), " elites added to pop")
remainingRequired = w.maxNumCreatures() - len(newSet)
i = 1
while i in range(int(remainingRequired)):
newSet.append(crossOver(eliteCreatures[i], eliteCreatures[i-1])[0])
if i >= (len(eliteCreatures)-2):
i = 1
i += 1
remainingRequired = w.maxNumCreatures() - len(newSet)
# Here are some statistics, which you may or may not find useful
avgLifeTime = float(avgLifeTime)/float(len(population))
print("Simulation stats:")
print(" Survivors : %d out of %d" % (nSurvivors, len(population)))
print(" Average Fitness Score :", fitnessScore)
print(" Avg life time: %.1f turns" % avgLifeTime)
# The information gathered above should allow you to build a fitness function that evaluates fitness of
# every creature. You should show the average fitness, but also use the fitness for selecting parents and
# spawning then new creatures.
# Based on the fitness you should select individuals for reproduction and create a
# new population. At the moment this is not done, and the same population with the same number
# of individuals is returned for the next generation.
new_population = newSet
return new_population
# Pygame window sometime doesn't spawn unless Matplotlib figure is not created, so best to keep the following two
# calls here. You might also want to use matplotlib for plotting average fitness over generations.
plt.close('all')
fh=plt.figure()
# Create the world. The worldType specifies the type of world to use (there are two types to chose from);
# gridSize specifies the size of the world, repeatable parameter allows you to run the simulation in exactly same way.
w = World(worldType=worldType, gridSize=gridSize, repeatable=repeatableMode)
#Get the number of creatures in the world
numCreatures = w.maxNumCreatures()
#Get the number of creature percepts
numCreaturePercepts = w.numCreaturePercepts()
#Get the number of creature actions
numCreatureActions = w.numCreatureActions()
# Create a list of initial creatures - instantiations of the MyCreature class that you implemented
population = list()
for i in range(numCreatures):
c = MyCreature(numCreaturePercepts, numCreatureActions)
population.append(c)
# Pass the first population to the world simulator
w.setNextGeneration(population)
# Runs the simulation to evaluate the first population
w.evaluate(numTurns)
# Show the visualisation of the initial creature behaviour (you can change the speed of the animation to 'slow',
# 'normal' or 'fast')
w.show_simulation(titleStr='Initial population', speed='normal')
for i in range(numGenerations):
print("\nGeneration %d:" % (i+1))
# Create a new population from the old one
population = newPopulation(population)
# Pass the new population to the world simulator
w.setNextGeneration(population)
# Run the simulation again to evaluate the next population
w.evaluate(numTurns)
# Show the visualisation of the final generation (you can change the speed of the animation to 'slow', 'normal' or
# 'fast')
if i==numGenerations-1:
w.show_simulation(titleStr='Final population', speed='normal')
We represent scores of batsmen across a sequence of matches in a two level dictionary as follows:
{'match1':{'player1':57, 'player2':38}, 'match2':{'player3':9, 'player1':42}, 'match3':{'player2':41, 'player4':63, 'player3':91}
Each match is identified by a string, as is each player. The scores are all integers. The names associated with the matches are not fixed (here they are 'match1','match2','match3'), nor are the names of the players. A player need not have a score recorded in all matches
Define a Python function "orangecap(d)" that reads a dictionary d of this form and identifies the player with the highest total score. Your function should return a pair (playername,topscore) where playername is a string, the name of the player with the highest score, and topscore is an integer, the total score of playername.
The input will be such that there are never any ties for highest total score.
For instance:
orangecap({'match1':{'player1':57, 'player2':38}, 'match2':{'player3':9, 'player1':42}, 'match3':{'player2':41, 'player4':63, 'player3':91}})
('player3', 100)
orangecap({'test1':{'Ashwin':84, 'Kohli':120}, 'test2':{'ashwin':59, 'Pujara':42}})
('Kohli', 120)
This is my code:
def orangecap(d):
s=[]
t=[]
for i in sorted(d.keys()):
for j in sorted(d[i].keys()):
flag=0
for k in range(len(s)):
if(s[k]==j):
t[k:k]+=[d[i][j]]
flag=1
break
if(flag==0):
s.append(j)
t.append(d[i][j])
m=max(t)
for i in range(len(t)):
if t[i]==m:
return (s[i],m)
You didn't quite get a question out there, in future try be specific in what you are asking or try https://codereview.stackexchange.com/ for general pointers etc.
First to note in python, as in all programming languages try to be as expressive as possible when naming variables, it will make it much easier for everyone (most often yourself) to work out what is going on (and wrong).
So in the question we are trying to work out the total scores for each player. In other words we are combining the dictionary for each match into a single dictionary, adding if we have a duplicate key. So
def orangecap(d):
# Make a new dict to count the totals
totals = {}
# Loop over every players score in every match.
for match in d.values():
for player in match:
# Check if player is in totals dictionary. If not add them.
if player not in totals:
totals[player] = 0
# Add players score in this match to there total score.
totals[player] += match[player]
#initialise highest_total as we will use it later for comparison.
highest_total = -1
#loop through totals to find highest score
for player in totals:
# if players score is greater than current highest, set highest_player
# to be current player, and set the highest total to players total
if totals[player] > highest_total:
highest_player = player
highest_total = totals[player]
# return the highest player and highest total
return highest_player, highest_total
Let's say I have a list of soccer players. For now, I only have four players. [Messi, Iniesta, Xavi, Neymar]. More players will be added later on. I want to keep track of the number of times these soccer players pass to each other during the course of a game. To keep track of the passes, I believe I'll need a data structure similar to this
Messi = {Iniesta: 4, Xavi: 5 , Neymar: 8}
Iniesta = {Messi: 4, Xavi: 10 , Neymar: 5}
Xavi = {Messi: 5, Iniesta: 10 , Neymar: 6}
Neymar = {Messi: 8, Iniesta: 5 , Xavi: 6}
Am I right to use a dictionary? If not, what data structure would be better suited? If yes, how do I approach this using a dictionary though? How do I address the issue of new players being included from time to time, and creating a dictionary for them as well.
As an example, If I get the first element in the list, List(i) in the first iteration is Messi, how do i use the value stored in it to create a dictionary with the name Messi. That is how do i get the line below.
Messi = [Iniesta: 4, Xavi: 5 , Neymar: 8]
It was suggested I try something like this
my_dynamic_vars = dict()
string = 'someString'
my_dynamic_vars.update({string: dict()})
Python and programming newbie here. Learning with experience as I go along. Thanks in advance for any help.
This is a fun question, and perhaps a good situation where something like a graph might be useful. You could implement a graph in python by simply using a dictionary whose keys are the names of the players and whose values are lists players that have been passed the ball.
passes = {
'Messi' : ['Iniesta', 'Xavi','Neymar', 'Xavi', 'Xavi'],
'Iniesta' : ['Messi','Xavi', 'Neymar','Messi', 'Xavi'],
'Xavi' : ['Messi','Neymar','Messi','Neymar'],
'Neymar' : ['Iniesta', 'Xavi','Iniesta', 'Xavi'],
}
To get the number of passes by any one player:
len(passes['Messi'])
To add a new pass to a particular player:
passes['Messi'].append('Xavi')
To count the number of times Messi passed to Xavi
passes['Messi'].count('Xavi')
To add a new player, just add him the first time he makes a pass
passes['Pele'] = ['Messi']
Now, he's also ready to have more passes 'appended' to him
passes['Pele'].append['Xavi']
What's great about this graph-like data structure is that not only do you have the number of passes preserved, but you also have information about each pass preserved (from Messi to Iniesta)
And here is a super bare-bones implementation of some functions which capture this behavior (I think a beginner should be able to grasp this stuff, let me know if anything below is a bit too confusing)
passes = {}
def new_pass(player1, player2):
# if p1 has no passes, create a new entry in the dict, else append to existing
if player1 not in passes:
passes[player1] = [player2]
else:
passes[player1].append(player2)
def total_passes(player1):
# if p1 has any passes, return the total number; otherewise return 0
total = len(passes[player1]) if player1 in passes else 0
return total
def total_passes_from_p1_to_p2(player1, player2):
# if p1 has any passes, count number of passes to player 2; otherwise return 0
total = passes[player1].count(player2) if player1 in passes else 0
return total
Ideally, you would be saving passes in some database that you could continuously update, but even without a database, you can add the following code and run it to get the idea:
# add some new passes!
new_pass('Messi', 'Xavi')
new_pass('Xavi', 'Iniesta')
new_pass('Iniesta', 'Messi')
new_pass('Messi', 'Iniesta')
new_pass('Iniesta', 'Messi')
# let's see where we currently stand
print total_passes('Messi')
print total_passes('Iniesta')
print total_passes_from_p1_to_p2('Messi', 'Xavi')
Hopefully you find this helpful; here's some more on python implementation of graphs from the python docs (this was a fun answer to write up, thanks!)
I suggest you construct a two dimensional square array. The array should have dimensions N x N. Each index represents a player. So the value at passes[i][j] is the number of times the player i passed to player j. The value passes[i][i] is always zero because a player can't pass to themselves
Here is an example.
players = ['Charles','Meow','Rebecca']
players = dict( zip(players,range(len(players)) ) )
rplayers = dict(zip(range(len(players)),players.keys()))
passes = []
for i in range(len(players)):
passes.append([ 0 for i in range(len(players))])
def pass_to(f,t):
passes[players[f]][players[t]] += 1
pass_to('Charles','Rebecca')
pass_to('Rebecca','Meow')
pass_to('Charles','Rebecca')
def showPasses():
for i in range(len(players)):
for j in range(len(players)):
print("%s passed to %s %d times" % ( rplayers[i],rplayers[j],passes[i][j],))
showPasses()