Are Uppman
Home>bowling>applet>code class
     /**
      *   BowlingGame lets you follow up the game of a bowling player, that
      *   means register throws and establish the frames and the scores of the
      *   game.
      *   
      *   Let's remind of the following :
      *   
      *   The bowling game is played on a lane with at the end 10 pins that 
      *   the player should knock down with a ball that he throws and make roll
      *   on the lane.
      *   
      *   The placing of the ten pins make up what we call a frame. The player 
      *   has two throws to try to knock down the 10 pins of a frame. If he 
      *   knocks down all the ten pins by the first throw we say he makes a 
      *   strike, and the second throw has no reason to be. If he has knocked 
      *   down all the ten pins after the second throw, we say he makes a spare
      *   
      *   We will say that a frame is
      *   
      *     - current if next throw concerns some of its pins
      *     - finished if all its (one or two) throws are done (and so, except 
      *       for the 10th, a next frame is current)
      *     - waiting if it is finished but not yet countable
      *     - ready if all the throws needed for the calculation of its score 
      *       are done (a strike or a spare need further throws to calculate
      *       the score)
      *   
      *   A game is, for each player, made up of 10 frames. The total score is 
      *   calculated as the sum of the (individual) score of each frame. This 
      *   score is calculated as follows :
      *   
      *     - if the player makes a strike the score of the frame is 10 plus 
      *       the number of pins (0 to 20) of the two next throws
      *     - else, if he makes a spare the score of the frame is 10 plus the 
      *       number of pins (0 to 10) of the next throw
      *     - else the score of the frame is the number of pins (0 to 9) 
      *       knocked down by the two throws of the frame.
      *   
      *   This counting may demand to replace the 10 pins one or two more 
      *   times for the 10th frame (1 time in case of spare and two times in 
      *   case of strike). We shall however agree that it's still the 10th
      *   frame that is current.
      */ 

     public class BowlingGame {
       final int maxFrames = 10+2; // 10 ordinary, 2 extra
       final int maxThrows = 21;
       Frame[] frames = new Frame[1 + maxFrames]; // 0 not used 
       int currentFrame = 1; // current index to frames
       int[] pins = new int[1 + maxThrows]; // 0 not used
       int currentThrow = 1; // current index to pins
       
       /**
        *   Creates a BowlingGame ready to begin the counting. 
        *   In particular : the current frame ({@link #currentFrame}) is the 
        *   first one, the state of the frame ({@link #frameState}) is 0.
        */
       public BowlingGame() {
         for (int i = 1; i <= maxFrames; i++) 
           frames[i] = new Frame(pins);
       }
     
       /**
        *   Registers the pins knocked down by a throw. 
        *   @param pins : the number of pins to register.
        *   @throws IndexOutOfBoundsException if the game is complete
        *   @throws IllegalArgumentException if on the first throw for a  
        *           frame the number N = pins does not belong to [0, 10] or if 
        *           on the second throw 'pins' does not belong to [0, 10 - N] 
        */
       public void add(int pins) {
         if ((pins < 0) || (pins > 10))
           throw new IllegalArgumentException("Pins out of bounds "+pins);
         if (isReady(10))
           throw new IndexOutOfBoundsException("Game over!");
           this.pins[currentThrow] = pins;
         for (int i = 0; i <= 2 ; i++) {
           if (currentFrame - i >= 1) {
             frames[currentFrame-i].update(currentThrow);
           }
         }
         currentThrow++;
         if (frames[currentFrame].finished()) {
           currentFrame++;
         }
       }
     
       /**
        *   Gives the cumulated score up to the given frame if the frame 
        *   is ready.
        *   @return the score
        *   @throws IndexOutOfBoundsException if index don't belong to 
        *           [1, a] where a is the number of frames that are ready
        */
       public int cumulatedScore(int index) {
         verifyBounds(index);
         if (!isReady(index))
           throw new IndexOutOfBoundsException("Frame not ready: " + index);
         int sum = 0;
         for ( ; index > 0; index--) 
           sum += frames[index].getScore();
         return sum;
       }
     
       /**
        *   Gives the state of a frame. 
        *   The state is 
        *   
        *     - 0 if first throw is not yet done
        *     - 1 if strike and avaiting second throw
        *     - 2 if strike and avaiting third throw
        *     - 3 if ready after strike
        *     - 4 if first throw done (not strike) and before second
        *     - 5 if spare and avaiting third throw
        *     - 6 if ready after spare
        *     - 7 if ready after neither strike nor spare
        *   
        *   @param index : the index of the frame.
        *   @return the state of the frame
        *   @throws IndexOutOfBoundsException if the index of the frame does 
        *           not belong to [1, 10]
        */
       public int frameState(int index) {
         verifyBounds(index);
         return frames[index].getState();
       }
       
       /**
        *   Gives the number of pins of the n-th contributing throw of a frame.
        *   @param index : the index of the frame
        *   @param n : the rank >= 1 of the contributing throw
        *   @return the number of pins of the n-th contributing throw of the frame
        *   @throws IndexOutOfBoundsException if the corresponding throw does not exist
        *           or does not concern this frame
        */
       public int throwForFrame(int index, int n) {
         verifyBounds(index);
         int f = frames[index].getThrow(n);
         if (f == -1) 
           throw new IndexOutOfBoundsException("Index out of bounds "+index+" and "+n);
         return f;
       }
      
       /** 
           A frame is ready if and only if all throws needed to complete its 
           score have been done.
       */
       boolean isReady(int index) {
         int s = frameState(index);
         return (s == 3) || (s == 6) || (s == 7);
       }
       
       void verifyBounds(int index) {
         if ((index < 1) || (index > 10))
           throw new IndexOutOfBoundsException("Index out of bounds [1, 10]: "+index);
       }
       
       /** 
           The class Frame manages the state and the score of a given frame. It also
           registers the index of the first throw of this frame in the array of 
           throws (pins).
           The class is built as a state machine.
       */
       class Frame {
         int[] pins;
         int state =  0;
         int score =  0;
         int first = -1;
         public Frame(int[] t) {
           pins = t;
         }
         /** 
             This method provokes the transitions of the state machine
          */
         public void update(int index) {
           switch (state) { 
             case 0  : first = index; 
                       score = pins[index];
                       if (score == 10) state = 1;
                       else state = 4;
                       break;
             case 1  : score += pins[index]; state = 2; 
                       break;
             case 2  : score += pins[index]; state = 3; 
                       break;
             case 4  : if (pins[first] + pins[index] > 10) throw new IllegalArgumentException(
                         "Sum of pins out of bounds: "+pins[first]+" "+pins[index]);
                       score += pins[index];
                       if (score == 10) state = 5;
                       else state = 7;
                       break;
             case 5  : score += pins[index]; state = 6; 
                       break;
             default : ;
           }
         }
         /**
             Returns the number of pins of the n-th contributing throw.
             Returns -1 if the corresponding throw does not concern 
             this frame (yet)
          */
         public int getThrow(int n) {
           if (n <= 0) return -1; // -1 means error
           int p = pins[first - 1 + n];
           switch (state) { 
             case 0: p = -1; break;
             case 1:
             case 4: if (n > 1) p = -1; break;
             case 2:
             case 5:
             case 7: if (n > 2) p = -1; break;
             case 3:
             case 6: if (n > 3) p = -1; break;
           }
           return p;
         }
         /**
             Returns the current state of this frame
             @see BowlingGame.frameState
         */
         public int getState() {
           return state;
         }
         /**
             Returns the current individual score of the frame.
         */
         public int getScore() {
           return score;
         }
         /**
             The frame has finished when the one throw (strike) or two throws 
             (not strike) has been done
         */
         public boolean finished() {
           return (state != 0) && (state != 4);
         }
       }
     }
Site updated 08 May, 2017    Remarks and questions? areusite at free dot fr