initial commit
This commit is contained in:
107
bash/bowling/README.md
Normal file
107
bash/bowling/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Bowling
|
||||
|
||||
Score a bowling game.
|
||||
|
||||
Bowling is a game where players roll a heavy ball to knock down pins
|
||||
arranged in a triangle. Write code to keep track of the score
|
||||
of a game of bowling.
|
||||
|
||||
## Scoring Bowling
|
||||
|
||||
The game consists of 10 frames. A frame is composed of one or two ball
|
||||
throws with 10 pins standing at frame initialization. There are three
|
||||
cases for the tabulation of a frame.
|
||||
|
||||
* An open frame is where a score of less than 10 is recorded for the
|
||||
frame. In this case the score for the frame is the number of pins
|
||||
knocked down.
|
||||
|
||||
* A spare is where all ten pins are knocked down by the second
|
||||
throw. The total value of a spare is 10 plus the number of pins
|
||||
knocked down in their next throw.
|
||||
|
||||
* A strike is where all ten pins are knocked down by the first
|
||||
throw. The total value of a strike is 10 plus the number of pins
|
||||
knocked down in the next two throws. If a strike is immediately
|
||||
followed by a second strike, then the value of the first strike
|
||||
cannot be determined until the ball is thrown one more time.
|
||||
|
||||
Here is a three frame example:
|
||||
|
||||
| Frame 1 | Frame 2 | Frame 3 |
|
||||
| :-------------: |:-------------:| :---------------------:|
|
||||
| X (strike) | 5/ (spare) | 9 0 (open frame) |
|
||||
|
||||
Frame 1 is (10 + 5 + 5) = 20
|
||||
|
||||
Frame 2 is (5 + 5 + 9) = 19
|
||||
|
||||
Frame 3 is (9 + 0) = 9
|
||||
|
||||
This means the current running total is 48.
|
||||
|
||||
The tenth frame in the game is a special case. If someone throws a
|
||||
strike or a spare then they get a fill ball. Fill balls exist to
|
||||
calculate the total of the 10th frame. Scoring a strike or spare on
|
||||
the fill ball does not give the player more fill balls. The total
|
||||
value of the 10th frame is the total number of pins knocked down.
|
||||
|
||||
For a tenth frame of X1/ (strike and a spare), the total value is 20.
|
||||
|
||||
For a tenth frame of XXX (three strikes), the total value is 30.
|
||||
|
||||
## Requirements
|
||||
|
||||
Write code to keep track of the score of a game of bowling. It should
|
||||
support two operations:
|
||||
|
||||
* `roll(pins : int)` is called each time the player rolls a ball. The
|
||||
argument is the number of pins knocked down.
|
||||
* `score() : int` is called only at the very end of the game. It
|
||||
returns the total score for that game.
|
||||
|
||||
|
||||
Run the tests with:
|
||||
|
||||
```bash
|
||||
bats bowling_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 bowling_test.sh
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
The Bowling Game Kata at but UncleBob [http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata](http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata)
|
||||
|
||||
|
||||
## 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.
|
86
bash/bowling/bowling.sh
Executable file
86
bash/bowling/bowling.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# V1: initial version
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
# set to mask to enable logs. 0: none, 255: all
|
||||
(( debug=2#00000000 ))
|
||||
(( debug=2#00001111 ))
|
||||
# $1: log level (mask), then strings to display.
|
||||
debug () {
|
||||
(( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2
|
||||
}
|
||||
|
||||
die () {
|
||||
echo "${@}" >&2
|
||||
exit 1
|
||||
}
|
||||
usage() {
|
||||
die "usage: bowling.sh r1-20 [r21 [r22]]"
|
||||
}
|
||||
# pincount > 10. $1: frame
|
||||
highroll() {
|
||||
die Frame "Frame $1: Pin count exceeds pins on the lane."
|
||||
}
|
||||
# pincount > 10. $1: frame
|
||||
negativeroll() {
|
||||
die Frame "Frame $1: Negative roll is invalid."
|
||||
}
|
||||
|
||||
|
||||
# args test: 10 rolls mandatory
|
||||
#(($# < 20)) && usage
|
||||
rolls=($@)
|
||||
debug 1 rolls=${rolls[@]}
|
||||
|
||||
((score = 0)) # total score
|
||||
((tempscore = 0)) # tmp score (for spare/strike)
|
||||
|
||||
declare -a frames # frames scores
|
||||
declare -a opened # spare:2, strike:1
|
||||
|
||||
((frame=-1)) # current frame
|
||||
|
||||
((curroll=1)) # roll in frame: 1:1st, 2:2nd
|
||||
((i=0))
|
||||
for ((roll=0; roll<$#; ++roll)); do
|
||||
debug 2 loop roll="$roll"
|
||||
|
||||
(( ! roll % 2 && frame++ ))
|
||||
exit 0
|
||||
(( score=${rolls[$i]} ))
|
||||
#(( score < 0 )) && || score > 10)) && echo 1
|
||||
#if ((!roll % 2)); then # first roll in frame
|
||||
|
||||
|
||||
((framescore=0)) # current frame score
|
||||
|
||||
|
||||
for j in 0 1; do
|
||||
(( roll = ${rolls[$i]} ))
|
||||
((roll < 0)) && die Frame $((curframe+1)): Negative roll is invalid.
|
||||
(( framescore += roll ))
|
||||
((framescore == 10)) && ((opened[$curframe] = j+1))
|
||||
(( i++ ))
|
||||
done
|
||||
debug 2 curframe=$curframe score=$framescore
|
||||
|
||||
#if ((roll))
|
||||
|
||||
((framescore > 10)) && wrongroll $curframe $curroll $framescore
|
||||
|
||||
frame[$curframe]=$framescore
|
||||
|
||||
|
||||
((curroll = -curroll))
|
||||
done
|
||||
|
||||
# emacs/vim settings.
|
||||
# Local Variables:
|
||||
# sh-basic-offset: 4
|
||||
# indent-tabs-mode: nil
|
||||
# comment-column: 50
|
||||
# fill-column: 80
|
||||
# End:
|
||||
# vim: set tabstop=4 expandtab:
|
Reference in New Issue
Block a user