I am stuck on the bowling exercise. 21 tests passed, 10 tests failed. Failed test 18 and tests 20, 22, 25, 26-31, that is the error cases. Is anyone willing to look at my code and help me figure out how to complete the exercise?
OK great. My code is below:
invalid_roll () {
echo "Negative roll is invalid"
exit 1
}
pin_count_exceeds_ten () {
echo "Pin count exceeds pins on the lane"
exit 1
}
incomplete_game () {
echo "Score cannot be taken until the end of the game"
exit 1
}
cannot_roll_game_over () {
echo "Cannot roll after game is over"
exit 1
}
roll () {
(( $1 < 0 )) && invalid_roll
(( $1 > 10 )) && pin_count_exceeds_ten
rolls[$current_roll]=$1
(( current_roll++ ))
}
score () {
_score=0
frame_index=0
for ((frame=0; frame<10; frame++)); do
if [[ ${rolls[$frame_index]} == 10 ]]; then # strike
_score=$(( _score + 10 + $(( ${rolls[frame_index+1]} + ${rolls[$frame_index+2]} ))
))
(( frame_index++ ))
elif [[ $(( ${rolls[$frame_index]} + ${rolls[$frame_index+1]} )) == 10 ]]; then # spare
_score=$(( _score + 10 + ${rolls[$frame_index+2]} ))
frame_index=$(( frame_index + 2 ))
else
_score=$((_score + ${rolls[$frame_index]} + ${rolls[$frame_index+1]} ))
frame_index=$(( frame_index + 2 ))
fi
done
echo "$_score"
}
game () {
[[ -z "$1" ]] && incomplete_game
current_roll=0
declare -a rolls
while [[ $# -gt 0 ]]; do
roll $1
shift
done
score
}
game "$@"
It would be helpful if you could also share the test output
I can only submit via the editor and I do not see how to copy and paste or attach that output to this reply.
Cut paste from one of the failed tests is:
CODE RUN
run bash bowling.sh 5 6
assert_failure
assert_output --partial "Pin count exceeds pins on the lane"
TEST FAILURE
(from function `assert_output' in file bats-extra.bash, line 386,
in test file bowling.bats, line 135)
`assert_output --partial "Pin count exceeds pins on the lane"' failed
-- output does not contain substring --
substring : Pin count exceeds pins on the lane
output : bowling.sh: line 38: + : syntax error: operand expected (error token is "+ ")
--
Basically I have been getting tired and frustrated. I can for example insert a line of code into my solution to fix the above failure but that then causes one of the other tests to fail - so it is like waka-mole !!
To be honest I admit I am being lazy and really need to take a break from the exercise. Come back to it fresh and think it through again. For example, looking back at it now I can see I am not handling the tenth frame as a special case so I need to figure out how to go about that.
Also although I update the score in my code appropriately I do not track the number of pins knocked down within a given frame to be able to test whether or not the number of pins knocked down within a frame exceeds 10.
It also looks to me like there is one test case not included. What if on the tenth frame the first roll was 0 and the second one was a strike. Would that then not mean 2 fill rolls and therefore possibly 22 rolls in total? None of the test cases include this.
Hard to tell which line is line 38. However the error message indicates the variable expansion on the right-hand side of the +
is empty. Perhaps ${rolls[$frame_index+2]}
doesn’t exist.
_score=$(( _score + 10 + $(( ${rolls[frame_index+1]} + ${rolls[$frame_index+2]} )) ))
Note that inside an arithmetic expression, you can drop the $
for variables, and that applies to arrays too. And the index part of a numerically-indexed array is also an arithmetic expression. Also, you don’t need to nest the arithmetic expansions
_score=$(( _score + 10 + rolls[frame_index+1] + rolls[frame_index+2] ))
Variables can be modified inside an arithmetic expression
(( _score += 10 + rolls[frame_index+1] + rolls[frame_index+2] ))
Copy/pasting your code into an editor shows me that line 38 is this one:
frame_index=$(( frame_index + 2 ))
Except I’m pretty sure that line wouldn’t cause that error. Glenn probably identified the correct line, even if the line number doesn’t line up properly
The line Glenn identified is on line 37. Unless you forgot to copy/paste the shebang when you copy/pasted your code.
Yes I should have remembered that by now as several times when submitting solutions to other exercises in the editor “Shellcheck” has highlighted that to me.
Yes for that test case
${rolls[$frame_index+2]}
doesn’t exit because my for loop assumes 10 frames
I did not copy the shebang from my local editor. It is
#!/bin/bash
The shebang in the editor differs.
My struggles with this exercise are less about syntax and semantics and more about getting my head around how to structure my code to handle the different error cases. What variables to keep track of, what functions to use and so forth. I have tried to follow the suggestions in README.md, i.e. use roll() call it for each ball throw and score() call it once.
This is certainly one of the more complex exercises! It’s not an easy one.
Sharing the complete code makes it much easier for others to see what “line 38” means It’s good to share all the information completely when asking for help. Help others help you!
I apologise. Not intentional. The shebang line was to only line I did not copy. I did not think about the effect on the line numbering in the editor test output.
Code again copied from the editor:
#!/usr/bin/env bash
invalid_roll () {
echo "Negative roll is invalid"
exit 1
}
pin_count_exceeds_ten () {
echo "Pin count exceeds pins on the lane"
exit 1
}
incomplete_game () {
echo "Score cannot be taken until the end of the game"
exit 1
}
cannot_roll_game_over () {
echo "Cannot roll after game is over"
exit 1
}
roll () {
(( $1 < 0 )) && invalid_roll
(( $1 > 10 )) && pin_count_exceeds_ten
rolls[$current_roll]=$1
(( current_roll++ ))
}
score () {
_score=0
frame_index=0
for ((frame=0; frame<10; frame++)); do
if [[ ${rolls[$frame_index]} == 10 ]]; then # strike
_score=$(( _score + 10 + $(( ${rolls[frame_index+1]} + ${rolls[$frame_index+2]} ))
))
(( frame_index++ ))
elif [[ $(( ${rolls[$frame_index]} + ${rolls[$frame_index+1]} )) == 10 ]]; then # spare
_score=$(( _score + 10 + ${rolls[$frame_index+2]} ))
frame_index=$(( frame_index + 2 ))
else
_score=$((_score + ${rolls[$frame_index]} + ${rolls[$frame_index+1]} ))
frame_index=$(( frame_index + 2 ))
fi
done
echo "$_score"
}
game () {
[[ -z "$1" ]] && incomplete_game
current_roll=0
declare -a rolls
while [[ $# -gt 0 ]]; do
roll $1
shift
done
score
}
game "$@"
Line 38:
elif [[ $(( ${rolls[$frame_index]} + ${rolls[$frame_index+1]} )) == 10 ]]; then # spare
elif [[ $(( ${rolls[$frame_index]} + ${rolls[$frame_index+1]} )) == 10 ]]; then # spare
Another bit of code review: [[ ... ]]
is a string-oriented conditional. There exists the arithmetic conditional (( ... ))
elif (( rolls[frame_index] + rolls[frame_index+1] == 10 )); then # spare
Thank you for that bit of code review. Looks much better.
Did you get the error handling to work as expected? Good looking but non-working code is
No I did not. I tried but just could not translate the different cases to working code.
I could get code that computed the score correctly IF the inputs were correct and handle the simple error cases such as -1; 11; 5 6. Cases such as
two bonus rolls after the strike in the last frame can score more that 10 points if one is after a strike
nope.
Looks like you need a special case for handling the last frame in score
. And there especially the 10
roll in that frame. It’s the hardest part of the exercise, I guess. Maybe it helps to write the steps to calculate onto paper first.
I did find a solution that passed all tests, although it was far from elegant.
@colinmcnicholl Maybe you want to try our code reviews to develop your solution into something you like?