CMPS 134 Fall 2022
Test #2 Sample Solutions

The correct answers in Problems 1 and 2 were marked on the students' test papers.


3. This was the most difficult problem on the test. Perhaps the most common error made by students was to create (and make use of) a new instance of the PairOfDice class. Doing that meant that the incoming parameter, dice, was ignored. Only a poorly-designed method has a parameter that is ignored.

Another common error was to ignore the rules of the game that were described in the problem and to instead try to cobble together an answer out of code from a solution to Prog. Assg. #5 (Dice Game). While it may be possible to arrive at a reasonable solution to an exam problem by adapting Java code that was presented in lecture or that comes from a programming assignment solution, you should not expect such an adaptation to require only minimal changes. Indeed, a student who answers an exam problem by plugging in Java code from lecture or from a programming assignment is signaling to the instructor that their skills are weak.

/* Given a pair of dice, this method plays a game and reports whether it was
** won (true) or lost (false).  Rules:  Roll the dice.  A result-sum of 3 or 11
** (on the 1st roll) is an immediate win.  Otherwise, keep rolling the dice
** until either you've rolled them nine times or the result-sum of the very 
** 1st roll has been duplicated.  In the latter case (duplication is achieved),
** the game is won; in the former case (nine rolls without duplication), it is
** lost.
*/
public static boolean playGoofyDiceGame(PairOfDice dice) {
   boolean gameWon;
   dice.roll();
   int firstRoll = dice.pips();
   if (firstRoll == 3  ||  firstRoll == 11) {
      gameWon = true;
   }
   else {
      int rollCntr = 1;
      // Roll the dice until either they've been rolled nine times 
      // or the result-sum of the first roll occurs again.
      do {
         dice.roll();
         rollCntr = rollCntr + 1;
      } while (rollCntr != 9  &&  dice.pips() != firstRoll);

      // The game has been won if (and only if) the last roll's 
      // result-sum matches that of the first roll.
      gameWon = dice.pips() == firstRoll;
   }
   return gameWon;
}

What follows is a solution that your instructor views as being inferior to that above, in that it uses "short-circuiting" to terminate the method prior to it reaching its end.

public static boolean playGoofyDiceGame(PairOfDice dice) {
   dice.roll();
   int firstRoll = dice.pips();
   if (firstRoll == 3  ||  firstRoll == 11) {
      return true;
   }

   // Execution reaches here only if game was not won on the first roll.
   int rollCntr = 1;
   // Roll the dice until they've been rolled up to nine times, but if any
   // roll's result-sum matches the first roll, immediately report that the
   // game has been won.
   do {
      dice.roll();
      if (dice.pips() == firstRoll) { return true; }
      rollCntr = rollCntr + 1;
   } while (rollCntr != 9);

   // Execution reaches here only if the game is lost.
   return false;
}


4. Here is one good solution:

/* Given an e-mail address in <First>.<Last>@<Domain> form,
** returns the corresponding name in <Last>, <First> form.
*/
public static String emailAddrToFormalName(String emailAddr) {
   int posOfDot = emailAddr.indexOf('.');
   int posOfAt = emailAddr.indexOf('@');
   String firstName = emailAddr.substring(0, posOfDot);
   String lastName = emailAddr.substring(posOfDot+1, posOfAt);
   return lastName + ", " + firstName;
}

One could argue that the problem of translating an e-mail address into a "formal name" is worthy of being decomposed into simpler subproblems, namely those of

A few students seemingly tried to follow this approach, but most got the syntax all wrong by nesting the methods that extracted the two names inside the method that was to be completed. Here is a syntactically correct solution that follows this approach:

/* Given an e-mail address in <First>.<Last>@<Domain> form,
** returns the corresponding name in ,  form.
*/
public static String emailAddrToFormalName(String emailAddr) {
   int firstName = firstNameOf(emailAddr);
   int lastName = lastNameOf(emailAddr);
   return lastName + ", " + firstName;
}

/* Returns that substring of the given e-mail address that corresponds
** to the first name.  Assumes that the given e-mail address is of the
** form <First>.<Last>@<Domain>.
**
*/
public static String firstNameOf(String emailAddr) {
   int posOfDot = emailAddr.indexOf('.');
   return emailAddr.substring(0, posOfDot);
}

/* Returns that substring of the given e-mail address that corresponds
** to the last name.  Assumes that the given e-mail address is of the
** form <First>.<Last>@<Domain>.
**
*/
public static String lastNameOf(String emailAddr) {
   int posOfDot = emailAddr.indexOf('.');
   int posOfAt = emailAddr.indexOf('@');
   return emailAddr.substring(posOfDot+1, posOfAt);
}


5. The Java statements in blue are those that would be inserted into the relevant methods in order to effect the "better" functionality.

/* Advances the ball count and sets the strike count 
** to zero if appropriate.
*/
public void advanceBallCount() {
   if (ballCntr == BALL_MAX) {
      ballCntr = 0;  strikeCntr = 0;
   }
   else {
      ballCntr = ballCntr + 1;
   }
}

/* Advances the strike count and sets the ball count 
** to zero if appropriate.
*/
public void advanceStrikeCount() {
   if (strikeCntr == STRIKE_MAX) {
      strikeCntr = 0;  ballCntr = 0;
   }
   else {
      strikeCntr = strikeCntr + 1;
   }
}

Oddly, a few students gave versions of advanceBallCount() that did not mention strikeCntr at all and, similarly, versions of advanceStrikeCount() that did not mention ballCntr at all. Doing that totally ignored the underlying reason for devising "better" advanceBallCount() and advanceStrikeCount() methods (and the comments describing their intended behavior).