Software Development, Design Choices, Class Design,
Recursion and Iteration, Testing, and Command-line Arguments
2011-2012
A Racquetball or Volleyball Simulation
 
 

Overal Program Orgnization and Preliminaries

Quick Index

Design Choices

In creating a racquetball/volleyball simulation, a program developer must make numerous design and implementation decisions. Some early decisions address the following questions.

The remainder of this reading identifies ways to address each of these questions.

Specifying Game Parameters

Since the problem asks for simulations of either racquetball or volleyball, we want our program to simulate either game. Further, we want to make it easy for a user to specify how many games should be simulated in determining results.

From a design perspective, these program parameters might be specified in at least four ways. These possibilities are discussed in several of labs, as outlined in the following table.

Option Means of Specifying Game Parameters Lab Discussing Option
A Specify game paramters in a constructor that creates a simulation object Discussed later in this lab
B Specify game parameters in static variables that can be edited by a user Discussed in Lab 3
C Ask user for game parameters at run time An option in this lab and Lab 3
D Use command-line parameters Discussed in Lab 5

Placing the Simulation within an Object-oriented Framework

A common approach in object-oriented problem solving involves collecting relevant data and operations within a class. In running the program, separate objects correspond to specific data elements.

From this perspective, it seems natural to create a separate object for a simulation for a specific game (e.g., racquetball or volleyball) and a specific number of games. Each object would be devoted to "racquetball" or "volleyball", and a field within the object would record the number of games to be simulated (1000 in the problem as stated).

This perspective of connecting the type of game to an object yields the following fields and constructor:

    public String game; // either "racquetball" or "volleyball"
    public boolean winByTwo;
    public int numberOfGames;

    /**                         
     * Constructor specifies whether racquetball or volleyball
     * will be simulated (default racquetball) and     
     * the number of games to be simulated      
     * Constructor also uses the racquetball/volleyball
     * information to determine if players of the game 
     * must win by 2 or 1, respectively.
     */
    public Game (String simGame, int simNumGames)
    {   game = simGame;
        numberOfGames = simNumGames;
        winByTwo = simGame.equals("volleyball");
    }

Overall Program Structure

After deciding that a separate object will be used for a simulation, we must decide how to organize processing of many games with varying probabilities. For simplicity, we separate the playing of a single game from the overall structure of simulating 1000 games for varying probabilities. This suggests a method simulateGames for the overall simulation itself, while another method playUntilWin might simulate a specific game.

In considering return types, simulateGames should return void, because it will handle the simulation and print the results; there is nothing for simulateGames to return! In contrast, playUntilWin will simulate an individual game. When we use playUntilWin, we will not care about the details of the simulation, but we will need to know the winner. Thus, playUntilWin should return the string "A" or "B".

Within simulateGames, the output can guide the basic structure as suggested by the following basic outline:

    Print header of table
    For probability being 0.40, 0.41, ..., 0.60, 
        Number of wins for A and for B starts at 0-0
        For each of 1000 games with the given probability
            Determine winner and increment win for A or B, as appropriate
	Print a line of the table, giving the percentage of wins for both A and B

To implement this outline, we note that neither floats or doubles (e.g., 0.01) are stored exactly. Thus, rather than using a double variable for the loop, we use an int and divide by 100.0 to simulate each probability for 1000 games.

Output Formatting

Before tackling the main simulation, we observe the output requires each percentage be printed in a column. Since 100% is possible, we must allocate 3 characters for each percentage followed by a percent sign. Thus, somewhere we will need to compute and format a percentage. The task could be associated with a Game object, but it uses no data structures from the object. Hence, we declare the method as static (although the code would work with static omitted).

    /**                         
    * Format a probability (a number between 0.0 and 1.0)   
    * as a 2-character integer percentage, followed by a 
    * "%" character.
    */
    public static String formatPercent (double value)
    {   String str = "" + Math.round(value * 100.0);
        while (str.length() < 3)
            str = " " + str;
        return str + "%";
    }

Method simulateGames

The following code combines several elements discussed above in this lab.

/**                         
 * Run simulation of 1000 games for probability of "A" winning
 *   a volley covering the range 0.40, 0.41, ..., 0.59, 0.60.
 * For each probability of "A" winning,           
 *   simulate games with Player/Team A always serving first  
 *   print one line with the percentage of volleys won 
 *     by A and B and percentage of games won by A and B     
 */
public void simulateGames ()
{   // print headings             
    System.out.println ("\nSimulation of " + game
                    + " based on " + numberOfGames + " games");
    System.out.println ("Must win by 2:  " + winByTwo);
    System.out.println ();
    System.out.println ("   Probabilities       Percentage");
    System.out.println ("for winning volley      of Wins");
    System.out.println ("    A      B           A       B");
    System.out.println ();
    // Simulate games for 40% to 60% probabilities for A     
    for (int prob40To60 = 40; prob40To60 <= 60; prob40To60++)
      { double probWinVolley = prob40To60 / 100.0;

        // Simulate games for a given probability     
        int AWins = 0;  // at first neither A nor B  
        int BWins = 0;  //   has won any games
        for (int i = 0; i < numberOfGames; i++)
           { // tally winner of game   
             if (playUntilWin ("A", "B", probWinVolley, 
                               0, 0).equals("A"))
                AWins++;
             else
                BWins++;
           }
        System.out.println ("   "
             + formatPercent(probWinVolley)   + "    "
             + formatPercent(1-probWinVolley) + "         "
             + formatPercent(((double) AWins) / numberOfGames)
             + "     "
             + formatPercent(((double) BWins) / numberOfGames));
      }
    System.out.println ("\nEnd of Simulation\n");
}

Steps for this Lab

  1. As the sample output illustrates, typical results from this simulation are very sensitive to the initial probabilities. For example, if A has a 40% chance of winning a volley, then A will win almost no games. Since these results may not be intuitive, it is natural to to wonder how this simulation might be tested. Brainstorm several ways that could help test this type of simulation.

    1. How does one know if this simulation program is correct?
      • Are there some cases for which you can be confident of the results?
      • Are there cases for which you have a strong intuition about what results might be obtained? (You should be able to provide a justification about these results expected.)
    2. Since the code obtains results through random numbers, the results will change from one run to the next, and exact results are not known beforehand.
      • Are there trends that help provide confidence in the results?
      • Are there types of results that, if present, would suggest errors in the code?
  2. Within simulateGames, the main loop begins

         for (int prob40To60 = 40; prob40To60 <= 60; prob40To60++)
         {  double probWinVolley = prob40To60 / 100.0;
    

    Alternatively, this loop could begin

         for (double probWinVolley = 0.40; probWinVolley <= 0.60; 
         {  probWinVolley += 0.01)
    

    Explain the advantages of an int control variable rather than a double in this context.

  3. The simulateGames method contains the expression

            formatPercent(((double) AWins) / numberOfGames)
    

    Why is the phrase (double) used? (The Java code would compile and run without it.)

  4. The code to format output, formatPercent, is given as a static method, but the method would work if static is removed (so the method is no longer static). Explain why this method can be either static or not.

  5. Combine the class variables with the methods Game, formatPercent, and simulateGames into a full Java program. Add a stub for method playUntilWin that always returns that "A" wins (details of playUntilWin will be discussed in Lab 2). Add any needed additional details so the Java program compiles and runs — printing the full table (with all wins for "A").