# 14.6. The Tournament¶

The Tournament class encapsulates the details of the PD competition:

payoffs = {('C', 'C'): (3, 3),
('C', 'D'): (0, 5),
('D', 'C'): (5, 0),
('D', 'D'): (1, 1)}

num_rounds = 6

def play(self, agent1, agent2):
agent1.reset()
agent2.reset()

for i in range(self.num_rounds):
resp1 = agent1.respond(agent2)
resp2 = agent2.respond(agent1)

pay1, pay2 = self.payoffs[resp1, resp2]

agent1.append(resp1, pay1)
agent2.append(resp2, pay2)

return agent1.score, agent2.score


payoffs is a dictionary that maps from the agents’ choices to their rewards. For example, if both agents cooperate, they each get 3 points. If one defects and the other cooperates, the defector gets 5 and the cooperator gets 0. If they both defect, each gets 1. These are the payoffs Axelrod used in his tournaments.

The play method runs several rounds of the PD game. It uses the following methods from the Agent class:

• reset: Initializes the agents before the first round, resetting their scores and the history of their responses.

• respond: Asks each agent for their response, given the opponent’s previous responses.

• append: Updates each agent by storing the choices and adding up the scores from successive rounds.

After the given number of rounds, play returns the total score for each agent. We chose num_rounds=6 so that each element of the genotype is accessed with roughly the same frequency. The first element is only accessed during the first round, or one sixth of the time. The next two elements are only accessed during the second round, or one twelfth each. The last four elements are accessed four of six times, or one sixth each, on average.

Tournament provides a second method, melee, that determines which agents compete against each other:

def melee(self, agents, randomize=True):
if randomize:
agents = np.random.permutation(agents)

n = len(agents)
i_row = np.arange(n)
j_row = (i_row + 1) % n

totals = np.zeros(n)

for i, j in zip(i_row, j_row):
agent1, agent2 = agents[i], agents[j]
score1, score2 = self.play(agent1, agent2)
totals[i] += score1
totals[j] += score2

for i in i_row:
agents[i].fitness = totals[i] / self.num_rounds / 2


melee takes a list of agents and a boolean, randomize, that determines whether each agent fights the same neighbors every time, or whether the pairings are randomized.

i_row and j_row contain the indices of the pairings. totals contains the total score of each agent.

Inside the loop, we select two agents, invoke play, and update totals. At the end, we compute the average number of points each agent got, per round and per opponent, and store the results in the fitness attribute of each agent.