CPS 4 Spring 2002 : Exam 3 Review Solutions


Collections

  1. Explain what casting is and why it is necessary to cast the value returned from a collection in the following statement:
    ((GP.Shape)myShapes.GetRandomElement()).SetColor(new GP.Attributes.Colors.Random());
    Casting changes the type of an object to something more specific than it is currently. For example, from a general real number to a specific integer number or from a generic Object to a more specific GP.Shape. Less formally, it is the act of telling Java to trust that we know the type of the expression better than it does. Because a collection needs to be able to hold any kind of object, it cannot make any assumptions about what has been put into it. The only way to do this is to return the most generic thing possible in Java: an Object. However, we cannot tell a generic Object to SetColor, only GP.Shapes. Thus, since we know shapes were put into the collection, we can cast the result returned from the collection to a shape in order to tell it to change its color.
     
  2. Explain the purpose of using a collection in the Blinker example.
    To remember a set of things from which we can later choose to flash over time.
     
  3. Explain the purpose of using a collection in the Nibble example.
    To remember a set of things into which the head of the snake might later crash.
     
  4. What is the difference in your whack-a-mole program if your collection remembers the holes or the moles? Make a case for storing one over the other.
    If you remember holes, then you need to create a new mole an position it at that hole each time the hole is chosen randomly. If you remember moles, then you need to create a set of moles at the beginning of the program while you are creating the holes; presumably these moles will be hidden in some way until chosen. I would argue the the former is easier to code and has more intuitive behavior if the same hole is chosen twice while the mole is visible. I might change my mind if the game lasted longer than thirty seconds, but I doubt it.

Understanding the Examples

Blinker

  1. Explain the purpose of the Starter class.
    The Starter class simply starts a shape spinning when it is clicked on, allowing the shape to spin once, many times, or never.
     
  2. Explain the purpose of the GetShapes method of the TriBoard class.
    The TriBoard class creates and maintains the collection of shapes that represent it. The Blinker behavior needs access to a collection of shapes in order to choose shapes to blink. The GetShapes function allows some other object, in this case the applet, to get access to those shapes.
     
  3. Explain how the flash behavior works. In particular, consider what would happen if it were run twice on the same shape --- both starting at the same time? one starting a half-second later? one starting a full second later?
    The flash behavior works by remembering the current color of the shape, darkening it, waiting for 0.8 seconds, then setting it back to the remembered color. If two flashes were started at the same time, it would look as if only flash had occurred. If two flashes were started a half second apart, the shape would darken, then darken further because the first flash is not yet finished, then return to its original color, then finally return permanently to the darkened color. If two flashes were started one second apart, it would look as if the shape flashed twice in quick succession.
     
  4. Explain the possible consequences of changing the delay in the flash behavior from 0.8 seconds to 1.5 seconds. Does the number of shapes in the board make any difference?
    Increasing the delay of the flash would mean that multiple shapes would be blinking at the same time. The fewer shapes in the board, the more likely it is that the same shape will be told to flash twice before it is finished with its first flash. As described above, this means more shapes would be getting darker instead of simply blinking.

Nibble

  1. Explain the purpose of the Reset class.
    The Reset class starts the game over by putting the snake back in its initial state whenever the space bar is pressed.
     
  2. Explain how the GrowTail function of the Snake class works. How does it differ between the basic and optimized versions of the program?
    The GrowTail function lengthens the tail that follows the snake's head and remembers the tail in a collection so it can be checked later to see if the head has intersected with any part of the tail. In the basic version, it does this by creating and remembering a new rectangle where the head was that is as tall as the head but only as wide as the head has moved since the last step. In the optimized version, a new rectangle is created and remembered only when the snake turns. While the snake is going straight, the most recent rectangle created is retrieved from the collection and updated to reflect the head's progress.
     
  3. Explain how the HideTail function of the Snake class works.
    The HideTail function clears the tail by hiding each of the rectangles used to display it in the applet and removing all memory of them from the snake's collection. This ensures the player can no longer see the tail and that it will not be hit by the head while it moves around the applet.
     
  4. Explain how the Reset function of the Snake class works. In particular, how does one decide what to put in the Snake's constructor instead of the Reset function?
    The Reset function starts the game over by putting the snake back in its initial state. In particular, it resets the values of the already existing objects in the snake that might have changed during the game. I would put code that created objects or that only needed to be run once no matter how many games were played in the constructor and all other work in the Reset function. Note that creating the snake implies a new game is about to start, so Reset is called automatically at the end of the constructor. Also note that if I truly followed my advice, I would have set the head's size in the constructor instead of the Reset function because the head's size never changes.

Combining Behaviors

Consider the following program that demonstrates a ball bouncing around within an applet by adding three small behaviors to a single shape rather than using one more complex behavior (as most of you did in Pong):

Explain how this version works. Is one easier to understand than the other? In particular, what difference does the order the behaviors are added matter or whether each behavior shares the same GP.Attributes.Vector or creates its own separate copy.

The applet adds three behaviors to an oval: the first makes it move forever according to the given velocity; the second changes the direction of that velocity only if the shape's x-coordinate is outside the applet; and the third changes the direction of that velocity only if the shape's y-coordinate is outside the applet. Arguably, this version is easier to understand because each of the behaviors is extremely simple --- only one or two lines of code. However, it can be difficult to predict perfectly how they will all work together and one might need to  be careful when adding more behaviors to the shape. This is the difference between a centralized program, where all the logic is in one place, and a decentralized program, where the logic is spread over many places. For example, the questions of order and sharing would not even come up in the original version because both are hard-coded into the program.

Since they all step at the same rate, i.e., with the same delay, it would be hard to detect any visible difference in the shapes movement if the MoveBehavior is added first or added last. However, if the MoveBehavior is added in between the two checkng behaviors, then the ball will move differently only when it hits a corner. In the original case, the ball bounces directly back into the applet the way it came. In the case where the checking behaviors are split, a move happens between the checks causing the ball to move slightly out of the corner. Over time this causes the ball to follow a very different path than the original case.

Finally, this example would not work at all if all three behaviors did not share the same vector. If each behavior had its own separate copy, it would be as if the checking behaviors did not even exist! Whatever initial velocity was given to the MoveBehavior would remain unchanged forever, causing the ball to move off the applet and never come back. Once outside the applet, the checking behaviors would  constantly change their own velocity, but it would have no affect on the copy used by the MoveBehavior.

Reactions

  1. Write a reaction class that randomly changes the color of the given target shape when the mouse is clicked within the shape.
    public class ColorRandomly extends GP.Reactions.MouseClicked
    {
        GP.Shape myTarget;
    
        public ColorRandomly (GP.Shape target)
        {
            myTarget = target;
        }
    
        public void React ()
        {
            myTarget.SetColor(new GP.Attributes.Colors.Random());
        }
    }
  2. Write a reaction class that counts how many times the mouse has been clicked within a shape using the given GP.Attributes.Values.Counter to keep track of the number of clicks rather than an int or a double.
    public class Counter extends GP.Reactions.MouseClicked
    {
        GP.Attributes.Values.Counter myTarget;
    
        public Counter (GP.Attributes.Values.Counter count)
        {
            myTarget = count;
        }
    
        public void React ()
        {
            myTarget.Increment(1);
        }
    }
  3. Write a reaction that changes the color of the given target shape when the mouse is clicked within it based on a sequence of two colors given within a collection. The first time the mouse is clicked within the shape, it should turn the first color in the collection, the next time it should turn the last color in the collection, then it should turn the first color again, and on and on. In other words, it should alternate colors when clicked.
    public class ColorAlternately extends GP.Reactions.MouseClicked
    {
        GP.Shape myTarget;
        GP.Collections.Sequence myColors;
        GP.Attributes.Values.Counter myNumClicks;
    
        public ColorAlternately (GP.Shape target, GP.Collections.Sequence twoColors)
        {
            myTarget = target;
            myColors = twoColors;
            myNumClicks = new GP.Attributes.Values.Counter(0);
        }
        
        public void React ()
        {
            myNumClicks.Increment(1);
            if (myNumClicks.GetValue() % 2 == 0)
            {
                myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(0));
            }
            else
            {
                myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(1));
            }
        }
    }
  4. Write a reaction class that changes the color of the given target shape when the mouse is clicked within the shape based on a given sequence of colors that has as few as two colors and as many as twelve colors. Specifically, the first time the mouse is clicked within the shape, it should turn the color of the first one in the collection, the next time it should turn the second color, and so on. When all the colors in the collection have been displayed, the cycle should start again from the first color in the collection.
    public class ColorSequentially extends GP.Reactions.MouseClicked
    {
        GP.Shape myTarget;
        GP.Collections.Sequence myColors;
        GP.Attributes.Values.Counter myNumClicks;
    
        public ColorSequentially (GP.Shape target, GP.Collections.Sequence colors)
        {
            myTarget = target;
            myColors = colors;
            myNumClicks = new GP.Attributes.Values.Counter(0);
        }
    
        public void React ()
        {
            myNumClicks.Increment(1);
    
            double index = myNumClicks.GetValue() % myColors.NumElements();
            myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(index));
        }
    }