Bowling - stuck on how to handle error cases

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?

Exercism follows the principle of https://dontasktoask.com/

Show the code :slight_smile:

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 :slight_smile:

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] ))
1 Like

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 :slight_smile:

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 :slight_smile: 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
1 Like
 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 :scream_cat:

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?

grafik