initial commit
This commit is contained in:
108
bash/tournament/README.md
Normal file
108
bash/tournament/README.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# 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.
|
||||
|
||||
|
||||
Run the tests with:
|
||||
|
||||
```bash
|
||||
bats tournament_test.sh
|
||||
```
|
||||
|
||||
After the first test(s) pass, continue by commenting out or removing the
|
||||
`[[ $BATS_RUN_SKIPPED == true ]] || skip`
|
||||
annotations prepending other tests.
|
||||
|
||||
To run all tests, including the ones with `skip` annotations, run:
|
||||
|
||||
```bash
|
||||
BATS_RUN_SKIPPED=true bats tournament_test.sh
|
||||
```
|
||||
|
||||
|
||||
|
||||
## External utilities
|
||||
`Bash` is a language to write "scripts" -- programs that can call
|
||||
external tools, such as
|
||||
[`sed`](https://www.gnu.org/software/sed/),
|
||||
[`awk`](https://www.gnu.org/software/gawk/),
|
||||
[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html)
|
||||
and even programs written in other programming languages,
|
||||
like [`Python`](https://www.python.org/).
|
||||
This track does not restrict the usage of these utilities, and as long
|
||||
as your solution is portable between systems and does not require
|
||||
installation of third party applications, feel free to use them to solve
|
||||
the exercise.
|
||||
|
||||
For an extra challenge, if you would like to have a better understanding
|
||||
of the language, try to re-implement the solution in pure `Bash`,
|
||||
without using any external tools. Note that there are some types of
|
||||
problems that bash cannot solve, such as performing floating point
|
||||
arithmetic and manipulating dates: for those, you must call out to an
|
||||
external tool.
|
||||
|
||||
## Submitting Incomplete Solutions
|
||||
It's possible to submit an incomplete solution so you can see how others
|
||||
have completed the exercise.
|
132
bash/tournament/tournament.sh
Executable file
132
bash/tournament/tournament.sh
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env bash
|
||||
# v1: - initial version
|
||||
# v2-3: - backport bubble sort (while -> for) & debug system from next exercises
|
||||
# - always read from stdin
|
||||
# - some syntax improvement
|
||||
# v4: - cosmetic/syntax changes
|
||||
# - change from empty file handling (empty string becomes valid filename)
|
||||
# v5: - cosmetic changes following "shellcheck" usage.
|
||||
# v6: - removed debug calls.
|
||||
# v7-8: - cosmetic changes
|
||||
|
||||
die() {
|
||||
echo "${@}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# the results, associative arrays with team name as key. Some tables look
|
||||
# redundant, but easier to understand.
|
||||
declare -A mp # matches played
|
||||
declare -A win # matches won
|
||||
declare -A draw # matches tied
|
||||
declare -A loss # matches lost
|
||||
declare -A points # points
|
||||
declare -a order # final order.
|
||||
|
||||
nteams=0
|
||||
|
||||
# create a team entry in arrays if non existent. $1: team
|
||||
create_team() {
|
||||
if ! [[ -v "mp[$1]" ]]; then
|
||||
(( nteams++ ))
|
||||
(( mp["$1"]=0 ))
|
||||
(( win["$1"]=0 ))
|
||||
(( loss["$1"]=0 ))
|
||||
(( draw["$1"]=0 ))
|
||||
(( points["$1"]=0 ))
|
||||
order[$nteams]="$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# update results for a team: $1, $2: teams, $3: result for $1: win/draw/loss
|
||||
update_results() {
|
||||
create_team "$1"
|
||||
create_team "$2"
|
||||
|
||||
((mp["$1"]++))
|
||||
((mp["$2"]++))
|
||||
case "$3" in
|
||||
win)
|
||||
((win["$1"]++))
|
||||
((loss["$2"]++))
|
||||
((points["$1"]+=3))
|
||||
;;
|
||||
loss)
|
||||
((win["$2"]++))
|
||||
((loss["$1"]++))
|
||||
((points["$2"]+=3))
|
||||
;;
|
||||
draw)
|
||||
((draw["$1"]++))
|
||||
((draw["$2"]++))
|
||||
((points["$1"]++))
|
||||
((points["$2"]++))
|
||||
;;
|
||||
*) # should not happen
|
||||
die "fatal: invalid result $3, exiting."
|
||||
esac
|
||||
}
|
||||
|
||||
# inspired from https://stackoverflow.com/questions/7442417/how-to-sort-an-array-in-bash
|
||||
# (bubble sort)
|
||||
sort_teams()
|
||||
{
|
||||
local max i
|
||||
|
||||
for ((max=nteams; max>0; max--)); do
|
||||
for ((i=1; i<max; ++i )); do
|
||||
local team1=${order[$i]} team2=${order[$i + 1]} switch=0
|
||||
|
||||
if (( ${points["$team1"]} < ${points["$team2"]} )) || \
|
||||
( (( ${points["$team1"]} == ${points["$team2"]} )) && \
|
||||
[[ "$team1" > "$team2" ]] ); then
|
||||
order[$i]="$team2"
|
||||
order[$i + 1]="$team1"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
output() {
|
||||
printf "%-30s |%3s |%3s |%3s |%3s |%3s\n" "${@:1:6}"
|
||||
}
|
||||
|
||||
output_team() {
|
||||
output "$1" "${mp[$1]}" "${win[$1]}" "${draw[$1]}" "${loss[$1]}" "${points[$1]}"
|
||||
}
|
||||
|
||||
# output teams, according to "order" table
|
||||
output_teams() {
|
||||
local i
|
||||
# header
|
||||
output "Team" "MP" "W" "D" "L" "P"
|
||||
for (( i=1; i<= nteams; ++i )); do
|
||||
output_team "${order[$i]}"
|
||||
done
|
||||
}
|
||||
|
||||
load_teams() {
|
||||
# set separator. Note we ignore escaping ';': a\;a;b;win wont work
|
||||
local team1 team2 result
|
||||
local IFS=$';\n'
|
||||
|
||||
while read -r team1 team2 result ; do
|
||||
# should empty result considered as error or ignored? Ignored here.
|
||||
[[ -z "$team1" || -z "$team2" || -z "$result" ]] && continue
|
||||
update_results "$team1" "$team2" "$result"
|
||||
done
|
||||
}
|
||||
|
||||
# we will accept both argument (file name) or stdin input
|
||||
if (( $# == 0 )); then
|
||||
load_teams
|
||||
elif [[ -f "$1" ]]; then
|
||||
load_teams < "$1"
|
||||
else
|
||||
die Invalid "[$1]" file. Exiting.
|
||||
fi
|
||||
|
||||
sort_teams
|
||||
output_teams
|
||||
|
||||
exit 0
|
Reference in New Issue
Block a user