# rootulp's solution

## to Tournament in the Python Track

Published at Jan 27 2019 · 0 comments
Instructions
Test suite
Solution

#### Note:

This exercise has changed since this solution was written.

Tally the results of a small football competition.

Based on an input file containing which team played against which and what the outcome was, create a file with a table like this:

``````Team                           | MP |  W |  D |  L |  P
Devastating Donkeys            |  3 |  2 |  1 |  0 |  7
Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6
Blithering Badgers             |  3 |  1 |  0 |  2 |  3
Courageous Californians        |  3 |  0 |  1 |  2 |  1
``````

What do those abbreviations mean?

• MP: Matches Played
• W: Matches Won
• D: Matches Drawn (Tied)
• L: Matches Lost
• P: Points

A win earns a team 3 points. A draw earns 1. A loss earns 0.

The outcome should be ordered by points, descending. In case of a tie, teams are ordered alphabetically.

Input

``````Allegoric Alaskans;Blithering Badgers;win
Devastating Donkeys;Courageous Californians;draw
``````

The result of the match refers to the first team listed. So this line

``````Allegoric Alaskans;Blithering Badgers;win
``````

This line:

``````Courageous Californians;Blithering Badgers;loss
``````

Means that the Blithering Badgers beat the Courageous Californians.

And this line:

``````Devastating Donkeys;Courageous Californians;draw
``````

Means that the Devastating Donkeys and Courageous Californians tied.

## Exception messages

Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include a message.

To raise a message with an exception, just write it as an argument to the exception type. For example, instead of `raise Exception`, you should write:

``````raise Exception("Meaningful message indicating the source of the error")
``````

## Running the tests

To run the tests, run the appropriate command below (why they are different):

• Python 2.7: `py.test tournament_test.py`
• Python 3.4+: `pytest tournament_test.py`

Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): `python -m pytest tournament_test.py`

### Common `pytest` options

• `-v` : enable verbose output
• `-x` : stop running tests on first failure
• `--ff` : run failures from previous test before running other test cases

For other options, see `python -m pytest -h`

## Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the `\$EXERCISM_WORKSPACE/python/tournament` directory.

You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.

For more detailed information about running tests, code style and linting, please see Running the Tests.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### tournament_test.py

``````import unittest

from tournament import tally

# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0

class TournamentTest(unittest.TestCase):
self.assertEqual(
tally(''),
'Team                           | MP |  W |  D |  L |  P'
)

def test_a_win_is_three_points_and_a_loss_is_zero_points(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n'
'Blithering Badgers             |  1 |  0 |  0 |  1 |  0')
self.assertEqual(tally(results), table)

def test_a_win_can_also_be_expressed_as_a_loss(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3\n'
'Blithering Badgers             |  1 |  0 |  0 |  1 |  0')
self.assertEqual(tally(results), table)

def test_a_different_team_can_win(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Blithering Badgers             |  1 |  1 |  0 |  0 |  3\n'
'Allegoric Alaskans             |  1 |  0 |  0 |  1 |  0')
self.assertEqual(tally(results), table)

def test_a_draw_is_one_point_each(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  1 |  0 |  1 |  0 |  1\n'
'Blithering Badgers             |  1 |  0 |  1 |  0 |  1')
self.assertEqual(tally(results), table)

def test_there_can_be_more_than_one_match(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6\n'
'Blithering Badgers             |  2 |  0 |  0 |  2 |  0')
self.assertEqual(tally(results), table)

def test_there_can_be_more_than_one_winner(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  2 |  1 |  0 |  1 |  3\n'
'Blithering Badgers             |  2 |  1 |  0 |  1 |  3')
self.assertEqual(tally(results), table)

def test_there_can_be_more_than_two_teams(self):
table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6\n'
'Blithering Badgers             |  2 |  1 |  0 |  1 |  3\n'
'Courageous Californians        |  2 |  0 |  0 |  2 |  0')
self.assertEqual(tally(results), table)

def test_typical_input(self):
'Devastating Donkeys;Courageous Californians;draw\n'

table = ('Team                           | MP |  W |  D |  L |  P\n'
'Devastating Donkeys            |  3 |  2 |  1 |  0 |  7\n'
'Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6\n'
'Blithering Badgers             |  3 |  1 |  0 |  2 |  3\n'
'Courageous Californians        |  3 |  0 |  1 |  2 |  1')

self.assertEqual(tally(results), table)

def test_incomplete_competitionnot_not_all_pairs_have_played(self):

table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6\n'
'Blithering Badgers             |  2 |  1 |  1 |  0 |  4\n'
'Courageous Californians        |  2 |  0 |  1 |  1 |  1\n'
'Devastating Donkeys            |  1 |  0 |  0 |  1 |  0')

self.assertEqual(tally(results), table)

def test_ties_broken_alphabetically(self):
results = ('Courageous Californians;Devastating Donkeys;win\n'

table = ('Team                           | MP |  W |  D |  L |  P\n'
'Allegoric Alaskans             |  3 |  2 |  1 |  0 |  7\n'
'Courageous Californians        |  3 |  2 |  1 |  0 |  7\n'
'Blithering Badgers             |  3 |  0 |  1 |  2 |  1\n'
'Devastating Donkeys            |  3 |  0 |  1 |  2 |  1')

self.assertEqual(tally(results), table)

if __name__ == '__main__':
unittest.main()``````
``````class Team:

POINTS_PER_WIN = 3
POINTS_PER_DRAW = 1

def __init__(self, name):
self.name = name
self.wins = 0
self.draws = 0
self.losses = 0

@property
def matches_played(self):
return self.wins + self.losses + self.draws

@property
def points(self):
return (self.wins * self.POINTS_PER_WIN) + (self.draws * self.POINTS_PER_DRAW)

def tally_win(self):
self.wins += 1

def tally_draw(self):
self.draws += 1

def tally_loss(self):
self.losses += 1

def __str__(self):
return '{:<30} | {:^3}| {:^3}| {:^3}| {:^3}| {:>2}'.format(self.name, self.matches_played, self.wins, self.draws, self.losses, self.points)

class Tournament:

WIN = 'win'
DRAW = 'draw'
LOSS = 'loss'
RESULT_SEPERATOR = ';'
COLUMN_HEADERS = ['Team', 'MP', 'W', 'D', 'L', 'P']

def __init__(self, results):
self._teams = {}
if results:
self.parse(results)

def results_table(self):
for team in self.sorted_teams():
table.append(str(team))
return "\n".join(table)

def sorted_teams(self):
alphabetic = sorted(self._teams.values(), key=lambda team: team.name)
alphabetic_descending_points = sorted(alphabetic, key=lambda team: team.points, reverse=True)
return alphabetic_descending_points

def parse(self, results):
for result in results.split("\n"):
team_a, team_b, outcome = result.split(self.RESULT_SEPERATOR)
self.maybe_initialize_teams(team_a, team_b)
self.tally_outcome(team_a, team_b, outcome)

def tally_outcome(self, team_a, team_b, outcome):
if outcome == self.WIN:
self.tally_win(team_a, team_b)
if outcome == self.LOSS:
self.tally_loss(team_a, team_b)
if outcome == self.DRAW:
self.tally_draw(team_a, team_b)

def tally_win(self, winner, loser):
self._teams[winner].tally_win()
self._teams[loser].tally_loss()

def tally_loss(self, loser, winner):
self._teams[loser].tally_loss()
self._teams[winner].tally_win()

def tally_draw(self, team_a, team_b):
self._teams[team_a].tally_draw()
self._teams[team_b].tally_draw()

def maybe_initialize_teams(self, team_a, team_b):
self.maybe_initialize_team(team_a)
self.maybe_initialize_team(team_b)

def maybe_initialize_team(self, name):
if name not in self._teams:
self._teams[name] = Team(name)

return '{:<30} | {:^3}| {:^3}| {:^3}| {:^3}| {:>2}'.format(*self.COLUMN_HEADERS)

def tally(results):