tournament exercise

This commit is contained in:
Xevion
2019-07-14 00:48:24 -05:00
parent 6cfda7de65
commit 0efaf06cd0
4 changed files with 252 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"track":"python","exercise":"tournament","id":"6617ee41fac443be992d3090e650a16e","url":"https://exercism.io/my/solutions/6617ee41fac443be992d3090e650a16e","handle":"Xevion","is_requester":true,"auto_approve":false}

110
python/tournament/README.md Normal file
View File

@@ -0,0 +1,110 @@
# Tournament
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:
```text
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
Your tallying program will receive input that looks like:
```text
Allegoric Alaskans;Blithering Badgers;win
Devastating Donkeys;Courageous Californians;draw
Devastating Donkeys;Allegoric Alaskans;win
Courageous Californians;Blithering Badgers;loss
Blithering Badgers;Devastating Donkeys;loss
Allegoric Alaskans;Courageous Californians;win
```
The result of the match refers to the first team listed. So this line
```text
Allegoric Alaskans;Blithering Badgers;win
```
Means that the Allegoric Alaskans beat the Blithering Badgers.
This line:
```text
Courageous Californians;Blithering Badgers;loss
```
Means that the Blithering Badgers beat the Courageous Californians.
And this line:
```text
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:
```python
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](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)):
- 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](http://exercism.io/tracks/python/tests).
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@@ -0,0 +1,24 @@
def tally(rows):
# Subcommand that adds teams if not yet added, and processes the result from them
def process(team, result):
if team not in teams.keys():
teams[team] = {'MP' : 0, 'W' : 0, 'D' : 0, 'L' : 0, 'P' : 0}
if result == 'win':
teams[team]['W'] += 1
teams[team]['P'] += 3
elif result == 'draw':
teams[team]['D'] += 1
teams[team]['P'] += 1
elif result == 'loss':
teams[team]['L'] += 1
teams[team]['MP'] += 1
teams, output = {}, ['Team'.ljust(31) + '| MP | W | D | L | P']
# Quick lambda that swaps the result for the enemy team
swap = lambda result : 'win' if result == 'loss' else 'loss' if result == 'win' else 'draw'
for row in rows:
team1, team2, result = row.split(';')
process(team1, result); process(team2, swap(result))
# Process all the data with formatting. rjust and ljust add proper spacing for the scoring, so it allows double digit scores
for teamname, data in sorted(teams.items(), key=lambda item : (max([t['P'] for t in teams.values()]) - int(item[1]['P']), item[0])):
output.append('{}| {} | {} | {} | {} | {}'.format(teamname.ljust(31), str(data['MP']).rjust(2), str(data['W']).rjust(2), str(data['D']).rjust(2), str(data['L']).rjust(2), str(data['P']).rjust(2)))
return output

View File

@@ -0,0 +1,117 @@
import unittest
from tournament import tally
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0
class TournamentTest(unittest.TestCase):
def test_just_the_header_if_no_input(self):
self.assertEqual(
tally([]),
['Team | MP | W | D | L | P']
)
def test_a_win_is_three_points_a_loss_is_zero_points(self):
results = ['Allegoric Alaskans;Blithering Badgers;win']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3',
'Blithering Badgers | 1 | 0 | 0 | 1 | 0']
self.assertEqual(tally(results), table)
def test_a_win_can_also_be_expressed_as_a_loss(self):
results = ['Blithering Badgers;Allegoric Alaskans;loss']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3',
'Blithering Badgers | 1 | 0 | 0 | 1 | 0']
self.assertEqual(tally(results), table)
def test_a_different_team_can_win(self):
results = ['Blithering Badgers;Allegoric Alaskans;win']
table = ['Team | MP | W | D | L | P',
'Blithering Badgers | 1 | 1 | 0 | 0 | 3',
'Allegoric Alaskans | 1 | 0 | 0 | 1 | 0']
self.assertEqual(tally(results), table)
def test_a_draw_is_one_point_each(self):
results = ['Allegoric Alaskans;Blithering Badgers;draw']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 1 | 0 | 1 | 0 | 1',
'Blithering Badgers | 1 | 0 | 1 | 0 | 1']
self.assertEqual(tally(results), table)
def test_there_can_be_more_than_one_match(self):
results = ['Allegoric Alaskans;Blithering Badgers;win',
'Allegoric Alaskans;Blithering Badgers;win']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6',
'Blithering Badgers | 2 | 0 | 0 | 2 | 0']
self.assertEqual(tally(results), table)
def test_there_can_be_more_than_one_winner(self):
results = ['Allegoric Alaskans;Blithering Badgers;loss',
'Allegoric Alaskans;Blithering Badgers;win']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 2 | 1 | 0 | 1 | 3',
'Blithering Badgers | 2 | 1 | 0 | 1 | 3']
self.assertEqual(tally(results), table)
def test_there_can_be_more_than_two_teams(self):
results = ['Allegoric Alaskans;Blithering Badgers;win',
'Blithering Badgers;Courageous Californians;win',
'Courageous Californians;Allegoric Alaskans;loss']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6',
'Blithering Badgers | 2 | 1 | 0 | 1 | 3',
'Courageous Californians | 2 | 0 | 0 | 2 | 0']
self.assertEqual(tally(results), table)
def test_typical_input(self):
results = ['Allegoric Alaskans;Blithering Badgers;win',
'Devastating Donkeys;Courageous Californians;draw',
'Devastating Donkeys;Allegoric Alaskans;win',
'Courageous Californians;Blithering Badgers;loss',
'Blithering Badgers;Devastating Donkeys;loss',
'Allegoric Alaskans;Courageous Californians;win']
table = ['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']
self.assertEqual(tally(results), table)
def test_incomplete_competition_not_all_pairs_have_played(self):
results = ['Allegoric Alaskans;Blithering Badgers;loss',
'Devastating Donkeys;Allegoric Alaskans;loss',
'Courageous Californians;Blithering Badgers;draw',
'Allegoric Alaskans;Courageous Californians;win']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 3 | 2 | 0 | 1 | 6',
'Blithering Badgers | 2 | 1 | 1 | 0 | 4',
'Courageous Californians | 2 | 0 | 1 | 1 | 1',
'Devastating Donkeys | 1 | 0 | 0 | 1 | 0']
self.assertEqual(tally(results), table)
def test_ties_broken_alphabetically(self):
results = ['Courageous Californians;Devastating Donkeys;win',
'Allegoric Alaskans;Blithering Badgers;win',
'Devastating Donkeys;Allegoric Alaskans;loss',
'Courageous Californians;Blithering Badgers;win',
'Blithering Badgers;Devastating Donkeys;draw',
'Allegoric Alaskans;Courageous Californians;draw']
table = ['Team | MP | W | D | L | P',
'Allegoric Alaskans | 3 | 2 | 1 | 0 | 7',
'Courageous Californians | 3 | 2 | 1 | 0 | 7',
'Blithering Badgers | 3 | 0 | 1 | 2 | 1',
'Devastating Donkeys | 3 | 0 | 1 | 2 | 1']
self.assertEqual(tally(results), table)
if __name__ == '__main__':
unittest.main()