Compute Bowling Scores
This program computes the score of a bowling game.
To quickly recap the rules, bowling is played in 10 frames. At the beginning of each frame, 10 pins are set up, and the bowler is given two rolls to knock them all down. The score for a frame is the total number of pins knocked down by the two rolls. However, if all the pins are knocked down by the first roll (known as a strike), the score for the frame is increased by the total number of pins knocked down by the next two rolls. If all the pins are knocked down by the first and second roll combined (known as a spare), the score for the frame is increased by the number of pins knocked down by the next roll.
Thus, the maximum score for a frame is 30 points, which happens when the bowler records a strike in this frame and in the next two frames. If the bowler records a spare or a strike in the 10th frame, he or she gets to roll one or two more balls, respectively, to have the proper chance to get bonus points added on to the 10th frame score (the pins knocked down on those extra balls don't count by themselves, only as bonuses on the 10th frame score).
The program does not simulate rolling the ball and knocking down pins. It prompts the user with the number of pins left and asks how many were knocked down. It does know when it is time to move to the next frame, and when extra rolls are needed after the 10th frame. It also prints the total score of the game when it's over.
The program reads input using an object declared as the following:
BufferedReader bufrd = new BufferedReader( new InputStreamReader(System.in));
You can assume this works as expected, but if you want more detail: System.in is the "standard input" stream, which is an instance of the class InputStream. BufferedReader, which provides the useful readLine() method, is a subclass of Reader, which is a different class for reading character streams. InputStreamReader is another subclass of Reader, which is passed an InputStream in its constructor and thus converts between the two classes. (The constructor for BufferedReader is defined to take a Reader as a parameter. The fact that it can take an InputStreamReader, which is a subclass of Reader, demonstrates the power of class inheritance.)
In the case of an I/O error, BufferedReader.readLine() throws an IOException exception. Because this is not expected, rather than put the readLine() call inside a try/catch block, the declaraton of main() specifies that it can throw IOException also. On the other hand, the code does catch the NumberFormatException that is thrown by Integer.parseInt() because this occurs if the user enters a non-number, including a blank line.
If you want to delve a bit deeper into Java (and object-oriented programming in general), understand why the Bowling class declares a static member variable called b:
static final Bowling b = new Bowling();
The instance b has to exist to create Frame objects. Because Frame is a nested class within the Bowling class, Java needs to associate an instance of Bowling with each Frame created. This is done automatically when the Frame is created within a non-static Bowling member method, which is why Frame objects have their own creation method, Bowling.newFrame(). But, although main() is a member method of Bowling, it is declared static, so you cannot simply call newFrame() because there is no Bowling object to call it on. The static b is created for this purpose, allowing the program to call b.newFrame() from within main().
Having gone to the trouble of creating b, it would be possible to change the static member variables in Bowling, such as rolls, to be non-static, and then refer to b.rolls instead, but either way works.
Source Code
1. import java.io.*; 2. 3. public class Bowling { 4. 5. static final int MAXFRAMES = 10; 6. static final int MAXROLLS = (MAXFRAMES * 2 + 1); 7. static int[] rolls = new int[MAXROLLS]; 8. static Frame[] frames = new Frame[MAXFRAMES]; 9. 10. static final Bowling b = new Bowling(); 11. 12. class Frame { 13. public int[] rollindex = new int[3]; 14. 15. public Frame() { 16. for (int i = 0; i < 3; i++) { 17. rollindex[i] = -1; 18. } 19. } 20. 21. public int getTotal() { 22. int tot = 0; 23. for (int i = 0; i < 3; i++) { 24. if (rollindex[i] != -1) { 25. tot += rolls[rollindex[i]]; 26. } 27. return tot; 28. } 29. } 30. 31. public Frame newFrame() { 32. return new Frame(); 33. } 34. 35. public static void main(String[] args) 36. throws IOException { 37. 38. String inputline; 39. int nextroll = 0; 40. int i, pinsleft, hitpins; 41. boolean extrarolls = false; 42. 43. for (i = 0; i < MAXROLLS; i++) { 44. rolls[i] = 0; 45. } 46. 47. BufferedReader bufrd = 48. new BufferedReader( 49. new InputStreamReader(System.in)); 50. 51. nextframe: 52. for (int frame = 0; frame < MAXFRAMES; frame++) { 53. 54. frames[frame] = b.newFrame(); 55. pinsleft = 10; 56. for (int roll = 0; roll < 3; roll++ ) { 57. 58. // Get number of pins hit from user 59. while (true) { 60. System.out.println( 61. "Frame " + (frame+1) + 62. ", roll " + (roll+1) + 63. ", pins left " + pinsleft + 64. ". How many hit?"); 65. inputline = bufrd.readLine(); 66. try { 67. hitpins = 68. Integer.parseInt(inputline); 69. } catch (NumberFormatException e) { 70. continue; 71. } 72. if ((hitpins >= 0) && 73. (hitpins <= pinsleft)) { 74. break; 75. } 76. } 77. 78. rolls[nextroll] = hitpins; 79. frames[frame].rollindex[roll] = 80. nextroll; 81. 82. // If all pins down and this is not an 83. // extra roll, set it to add bonus rolls 84. int frametot = frames[frame].getTotal(); 85. if ((frametot == 10) && 86. (extrarolls == false)) { 87. for (int t = roll+1; t < 3; t++) { 88. frames[frame].rollindex[i] = 89. nextroll + (i - roll); 90. } 91. } 92. ++nextroll; 93. pinsleft -= hitpins; 94. 95. // two rolls, pins left, frame over 96. if ((roll == 1) && 97. (frametot < 10)) { 98. continue nextframe; 99. } 100. 101. // all pins knocked down... 102. if (frametot == 10) { 103. if (frame < (MAXFRAMES-1)) { 104. continue nextframe; 105. } else { 106. // ...and last frame 107. extrarolls = true; 108. } 109. } 110. if (extrarolls && (pinsleft == 0)) { 111. // new pins if needed 112. pinsleft = 10; 113. } 114. } 115. } 116. 117. int total = 0; 118. for (i = 0; i < MAXFRAMES; i++) { 119. total += frames[i].getTotal(); 120. } 121. System.out.println("Game total is " + total); 122. } 123. }
Suggestions
Because the Frame class is nested within the Bowling class, it makes sense to understand it first. What exactly is the meaning of the rollindex[] array?
Verify that the comments on lines 59, 8384, and 96 match the code that follows them.
The loop that starts on line 57 terminates when roll reaches 3. Under what conditions will the loop iterate with roll equal to 2?
The loop on lines 8890 is probably the most visually confusing part in the code. How many times will the loop iterate if the bowler has just rolled a spare? What if the bowler has just rolled a strike?
Hints
Walk through the loop that starts on line 57, assuming frame is 0, nextroll is 0, and extrarolls is false, and that the user specifies that 10 pins are hit on the first roll. Continue until frame is incremented.
Walk through the loop that starts on line 57, assuming frame is MAXFRAMES-1, nextroll is frame*2, extrarolls is false, and the user specifies that 10, 10, and 4 pins are knocked down by successive rolls. Continue until the loop that starts on line 57 finishes iterating.
Explanation of the Bug
The code on lines 8890, to set up the addition of the bonus rolls to a frame total, in the event that a spare or strike was rolled
for (int t = roll+1; t < 3; t++) { frames[frame].rollindex[i] = nextroll + (i - roll);
has a B.variable error in it. Unlike the other minor loops in the code, which use the loop variable i, this one uses t, but the code within the loop still uses i, which in this case, will have the value MAXROLLS, left over from the initialization loop that terminated at line 46. This causes an ArrayIndexOutOfBoundsException if a player rolls a spare or strike.