Skip to content

Commit ff0efe4

Browse files
authored
Add tournament (#347)
* Add `tournament` * Resolve apparent bug where glibc and musl may be using different sorting algorithms and not returning -1 became a problem for the test runner.
1 parent 7a2261b commit ff0efe4

7 files changed

Lines changed: 350 additions & 0 deletions

File tree

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,14 @@
560560
"prerequisites": [],
561561
"difficulty": 2
562562
},
563+
{
564+
"slug": "tournament",
565+
"name": "Tournament",
566+
"uuid": "452756cd-f85d-4853-8775-59a15abdc61e",
567+
"practices": [],
568+
"prerequisites": [],
569+
"difficulty": 3
570+
},
563571
{
564572
"slug": "triangle",
565573
"name": "Triangle",
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Instructions
2+
3+
Tally the results of a small football competition.
4+
5+
Based on an input file containing which team played against which and what the outcome was, create a file with a table like this:
6+
7+
```text
8+
Team | MP | W | D | L | P
9+
Devastating Donkeys | 3 | 2 | 1 | 0 | 7
10+
Allegoric Alaskans | 3 | 2 | 0 | 1 | 6
11+
Blithering Badgers | 3 | 1 | 0 | 2 | 3
12+
Courageous Californians | 3 | 0 | 1 | 2 | 1
13+
```
14+
15+
What do those abbreviations mean?
16+
17+
- MP: Matches Played
18+
- W: Matches Won
19+
- D: Matches Drawn (Tied)
20+
- L: Matches Lost
21+
- P: Points
22+
23+
A win earns a team 3 points.
24+
A draw earns 1.
25+
A loss earns 0.
26+
27+
The outcome is ordered by points, descending.
28+
In case of a tie, teams are ordered alphabetically.
29+
30+
## Input
31+
32+
Your tallying program will receive input that looks like:
33+
34+
```text
35+
Allegoric Alaskans;Blithering Badgers;win
36+
Devastating Donkeys;Courageous Californians;draw
37+
Devastating Donkeys;Allegoric Alaskans;win
38+
Courageous Californians;Blithering Badgers;loss
39+
Blithering Badgers;Devastating Donkeys;loss
40+
Allegoric Alaskans;Courageous Californians;win
41+
```
42+
43+
The result of the match refers to the first team listed.
44+
So this line:
45+
46+
```text
47+
Allegoric Alaskans;Blithering Badgers;win
48+
```
49+
50+
means that the Allegoric Alaskans beat the Blithering Badgers.
51+
52+
This line:
53+
54+
```text
55+
Courageous Californians;Blithering Badgers;loss
56+
```
57+
58+
means that the Blithering Badgers beat the Courageous Californians.
59+
60+
And this line:
61+
62+
```text
63+
Devastating Donkeys;Courageous Californians;draw
64+
```
65+
66+
means that the Devastating Donkeys and Courageous Californians tied.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"authors": [
3+
"BNAndras"
4+
],
5+
"files": {
6+
"solution": [
7+
"tournament.vim"
8+
],
9+
"test": [
10+
"tournament.vader"
11+
],
12+
"example": [
13+
".meta/example.vim"
14+
]
15+
},
16+
"blurb": "Tally the results of a small football competition."
17+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
let s:HEADER = 'Team | MP | W | D | L | P'
2+
3+
function! Tally(lines) abort
4+
if empty(a:lines)
5+
return [s:HEADER]
6+
endif
7+
8+
let l:team_data = s:ProcessGameResults(a:lines)
9+
let l:team_standings = s:PrepareStandings(l:team_data)
10+
let l:rows = s:BuildTableRows(l:team_standings)
11+
12+
return [s:HEADER] + l:rows
13+
endfunction
14+
15+
function! s:ProcessGameResults(lines) abort
16+
let l:teams = {}
17+
for l:line in a:lines
18+
let [l:team1, l:team2, l:result] = split(l:line, ';')
19+
20+
if !has_key(l:teams, l:team1)
21+
let l:teams[l:team1] = {'name': l:team1, 'W': 0, 'D': 0, 'L': 0}
22+
endif
23+
24+
if !has_key(l:teams, l:team2)
25+
let l:teams[l:team2] = {'name': l:team2, 'W': 0, 'D': 0, 'L': 0}
26+
endif
27+
28+
if l:result ==? 'win'
29+
let l:teams[l:team1]['W'] += 1
30+
let l:teams[l:team2]['L'] += 1
31+
elseif l:result ==? 'loss'
32+
let l:teams[l:team1]['L'] += 1
33+
let l:teams[l:team2]['W'] += 1
34+
elseif l:result ==? 'draw'
35+
let l:teams[l:team1]['D'] += 1
36+
let l:teams[l:team2]['D'] += 1
37+
endif
38+
endfor
39+
40+
return l:teams
41+
endfunction
42+
43+
function! s:PrepareStandings(teams) abort
44+
let l:standings = []
45+
for l:team in keys(a:teams)
46+
let l:team_data = a:teams[l:team]
47+
let l:team_data['MP'] = l:team_data['W'] + l:team_data['D'] + l:team_data['L']
48+
let l:team_data['P'] = l:team_data['W'] * 3 + l:team_data['D']
49+
call add(l:standings, l:team_data)
50+
endfor
51+
52+
call sort(l:standings, 's:SortTeams')
53+
54+
return l:standings
55+
endfunction
56+
57+
function! s:BuildTableRows(teams) abort
58+
let l:rows = []
59+
for l:team_data in a:teams
60+
let l:row = printf('%-30s | %2d | %2d | %2d | %2d | %2d',
61+
\ l:team_data['name'],
62+
\ l:team_data['MP'],
63+
\ l:team_data['W'],
64+
\ l:team_data['D'],
65+
\ l:team_data['L'],
66+
\ l:team_data['P'])
67+
call add(l:rows, l:row)
68+
endfor
69+
70+
return l:rows
71+
endfunction
72+
73+
let s:HEADER = 'Team | MP | W | D | L | P'
74+
75+
function! Tally(lines) abort
76+
if empty(a:lines)
77+
return [s:HEADER]
78+
endif
79+
80+
let l:team_data = s:ProcessGameResults(a:lines)
81+
let l:team_standings = s:PrepareStandings(l:team_data)
82+
let l:rows = s:BuildTableRows(l:team_standings)
83+
84+
return [s:HEADER] + l:rows
85+
endfunction
86+
87+
function! s:ProcessGameResults(lines) abort
88+
let l:teams = {}
89+
for l:line in a:lines
90+
let [l:team1, l:team2, l:result] = split(l:line, ';')
91+
92+
if !has_key(l:teams, l:team1)
93+
let l:teams[l:team1] = {'name': l:team1, 'W': 0, 'D': 0, 'L': 0}
94+
endif
95+
96+
if !has_key(l:teams, l:team2)
97+
let l:teams[l:team2] = {'name': l:team2, 'W': 0, 'D': 0, 'L': 0}
98+
endif
99+
100+
if l:result ==? 'win'
101+
let l:teams[l:team1]['W'] += 1
102+
let l:teams[l:team2]['L'] += 1
103+
elseif l:result ==? 'loss'
104+
let l:teams[l:team1]['L'] += 1
105+
let l:teams[l:team2]['W'] += 1
106+
elseif l:result ==? 'draw'
107+
let l:teams[l:team1]['D'] += 1
108+
let l:teams[l:team2]['D'] += 1
109+
endif
110+
endfor
111+
112+
return l:teams
113+
endfunction
114+
115+
function! s:PrepareStandings(teams) abort
116+
let l:standings = []
117+
for l:team in keys(a:teams)
118+
let l:team_data = a:teams[l:team]
119+
let l:team_data['MP'] = l:team_data['W'] + l:team_data['D'] + l:team_data['L']
120+
let l:team_data['P'] = l:team_data['W'] * 3 + l:team_data['D']
121+
call add(l:standings, l:team_data)
122+
endfor
123+
124+
call sort(l:standings, 's:SortTeams')
125+
126+
return l:standings
127+
endfunction
128+
129+
function! s:BuildTableRows(teams) abort
130+
let l:rows = []
131+
for l:team_data in a:teams
132+
let l:row = printf('%-30s | %2d | %2d | %2d | %2d | %2d',
133+
\ l:team_data['name'],
134+
\ l:team_data['MP'],
135+
\ l:team_data['W'],
136+
\ l:team_data['D'],
137+
\ l:team_data['L'],
138+
\ l:team_data['P'])
139+
call add(l:rows, l:row)
140+
endfor
141+
142+
return l:rows
143+
endfunction
144+
145+
function! s:SortTeams(teamA, teamB) abort
146+
if a:teamA['P'] == a:teamB['P']
147+
return a:teamA['name'] > a:teamB['name'] ? 1 : -1
148+
endif
149+
150+
return a:teamA['P'] < a:teamB['P'] ? 1 : -1
151+
endfunction
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[67e9fab1-07c1-49cf-9159-bc8671cc7c9c]
13+
description = "just the header if no input"
14+
15+
[1b4a8aef-0734-4007-80a2-0626178c88f4]
16+
description = "a win is three points, a loss is zero points"
17+
18+
[5f45ac09-4efe-46e7-8ddb-75ad85f86e05]
19+
description = "a win can also be expressed as a loss"
20+
21+
[fd297368-efa0-442d-9f37-dd3f9a437239]
22+
description = "a different team can win"
23+
24+
[26c016f9-e753-4a93-94e9-842f7b4d70fc]
25+
description = "a draw is one point each"
26+
27+
[731204f6-4f34-4928-97eb-1c307ba83e62]
28+
description = "There can be more than one match"
29+
30+
[49dc2463-42af-4ea6-95dc-f06cc5776adf]
31+
description = "There can be more than one winner"
32+
33+
[6d930f33-435c-4e6f-9e2d-63fa85ce7dc7]
34+
description = "There can be more than two teams"
35+
36+
[97022974-0c8a-4a50-8fe7-e36bdd8a5945]
37+
description = "typical input"
38+
39+
[fe562f0d-ac0a-4c62-b9c9-44ee3236392b]
40+
description = "incomplete competition (not all pairs have played)"
41+
42+
[3aa0386f-150b-4f99-90bb-5195e7b7d3b8]
43+
description = "ties broken alphabetically"
44+
45+
[f9e20931-8a65-442a-81f6-503c0205b17a]
46+
description = "ensure points sorted numerically"
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
Execute (just the header if no input):
2+
let g:rows = []
3+
let g:expected = ['Team | MP | W | D | L | P']
4+
AssertEqual g:expected, Tally(g:rows)
5+
6+
Execute (a win is three points, a loss is zero points):
7+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;win']
8+
let g:expected = ['Team | MP | W | D | L | P', 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3', 'Blithering Badgers | 1 | 0 | 0 | 1 | 0']
9+
AssertEqual g:expected, Tally(g:rows)
10+
11+
Execute (a win can also be expressed as a loss):
12+
let g:rows = ['Blithering Badgers;Allegoric Alaskans;loss']
13+
let g:expected = ['Team | MP | W | D | L | P', 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3', 'Blithering Badgers | 1 | 0 | 0 | 1 | 0']
14+
AssertEqual g:expected, Tally(g:rows)
15+
16+
Execute (a different team can win):
17+
let g:rows = ['Blithering Badgers;Allegoric Alaskans;win']
18+
let g:expected = ['Team | MP | W | D | L | P', 'Blithering Badgers | 1 | 1 | 0 | 0 | 3', 'Allegoric Alaskans | 1 | 0 | 0 | 1 | 0']
19+
AssertEqual g:expected, Tally(g:rows)
20+
21+
Execute (a draw is one point each):
22+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;draw']
23+
let g:expected = ['Team | MP | W | D | L | P', 'Allegoric Alaskans | 1 | 0 | 1 | 0 | 1', 'Blithering Badgers | 1 | 0 | 1 | 0 | 1']
24+
AssertEqual g:expected, Tally(g:rows)
25+
26+
Execute (There can be more than one match):
27+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;win', 'Allegoric Alaskans;Blithering Badgers;win']
28+
let g:expected = ['Team | MP | W | D | L | P', 'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6', 'Blithering Badgers | 2 | 0 | 0 | 2 | 0']
29+
AssertEqual g:expected, Tally(g:rows)
30+
31+
Execute (There can be more than one winner):
32+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;loss', 'Allegoric Alaskans;Blithering Badgers;win']
33+
let g:expected = ['Team | MP | W | D | L | P', 'Allegoric Alaskans | 2 | 1 | 0 | 1 | 3', 'Blithering Badgers | 2 | 1 | 0 | 1 | 3']
34+
AssertEqual g:expected, Tally(g:rows)
35+
36+
Execute (There can be more than two teams):
37+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;win', 'Blithering Badgers;Courageous Californians;win', 'Courageous Californians;Allegoric Alaskans;loss']
38+
let g:expected = ['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']
39+
AssertEqual g:expected, Tally(g:rows)
40+
41+
Execute (typical input):
42+
let g:rows = ['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']
43+
let g:expected = ['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']
44+
AssertEqual g:expected, Tally(g:rows)
45+
46+
Execute (incomplete competition (not all pairs have played)):
47+
let g:rows = ['Allegoric Alaskans;Blithering Badgers;loss', 'Devastating Donkeys;Allegoric Alaskans;loss', 'Courageous Californians;Blithering Badgers;draw', 'Allegoric Alaskans;Courageous Californians;win']
48+
let g:expected = ['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']
49+
AssertEqual g:expected, Tally(g:rows)
50+
51+
Execute (ties broken alphabetically):
52+
let g:rows = ['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']
53+
let g:expected = ['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']
54+
AssertEqual g:expected, Tally(g:rows)
55+
56+
Execute (ensure points sorted numerically):
57+
let g:rows = ['Devastating Donkeys;Blithering Badgers;win', 'Devastating Donkeys;Blithering Badgers;win', 'Devastating Donkeys;Blithering Badgers;win', 'Devastating Donkeys;Blithering Badgers;win', 'Blithering Badgers;Devastating Donkeys;win']
58+
let g:expected = ['Team | MP | W | D | L | P', 'Devastating Donkeys | 5 | 4 | 0 | 1 | 12', 'Blithering Badgers | 5 | 1 | 0 | 4 | 3']
59+
AssertEqual g:expected, Tally(g:rows)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function! Tally(lines) abort
2+
" your code goes here
3+
endfunction

0 commit comments

Comments
 (0)