CSCI A201/A597

Lecture Notes 25

Spring 2000


Solution to assignment 9 (due last night).

We'll start the lecture with a demo (it will be Sangwook's assignment 10, that he turned in on Friday). Once we clarify how the program should run we will start designing it (in stages).

Stage 1 (one): The Basic Outline

Observations: there are a few things that need to be done only once, such as creating the drawing window and drawing the bin (that we want to pack with circles), and a few others that need to be done over and over again (until we reach the end of the game) such as the generation and placement of a new circle. Provided that we can identify the end of game situation we need to generate a new circle and try to place it until the game ends.

Assume a certain boolean variable contains the truth value representing whether the game should go on or not. Whenever we look into it we find out if we should go on or not. In the beginning this variable (call it theGameMustGoOn) is set to true. If nobody changes the value of this variable the game will never end, which for this stage of design is just fine (we type control-C when we want to stop the game).

So this first stage of design looks like this:

  1. get a drawing window (call it board)
  2. decide on a rectangular area where you place the bin (Rect bin)
  3. draw bin on board
  4. start the game (theGameMustGoOn = true)
  5. while (theGameMustGoOn) {
    1. generate new circle (call it circle)
    2. help the user place it, using the mouse
    }
Step 5.2 above is by far the most complex. Let's refine it.

Stage 2 (two): Refining Step 5.2

The user knows that placing the circle is done using the mouse. So the user needs to press the mouse before we can do anything about it. Once the mouse is pressed dragging the mouse will signal the user's intent to move the circle to a certain location on the screen. For as long as the mouse is being dragged the circle should be following the mouse. Our program will have to take care of that, to simulate that the circle is being moved by the mouse.

Of course, the mouse and the circle don't interact directly. We work out a contract with the user of our program as follows: if you want to move the circle press the mouse button then drag the mouse; the circle will follow the mouse pointer as if you'd be dragging the circle with it. We'll take care of updating the situation to look that way. This way (if the update is fast enough) the circle will follow the mouse pointer (and you will see that on the screen) and will be at the mouse location almost all the time during the mouse dragging movement. So you can drop the circle at a particular location by just releasing the mouse button, which means that you no longer want the circle to follow the mouse pointer.

Here's this process in pseudo code:

  1. wait for user to press the mouse
  2. while (mouse is kept pressed) {
    1. get mouse position
    2. erase the circle (wherever it is)
    3. move the circle to the new position
    4. draw the circle (in this new position)
    }
Here's the whole program (with the complex step refined):
  1. get a drawing window (call it board)
  2. decide on a rectangular area where you place the bin (Rect bin)
  3. draw bin on board
  4. start the game (theGameMustGoOn = true)
  5. while (theGameMustGoOn) {
    1. generate new circle (call it circle)

      // help the user place it, using the mouse

    2. wait for user to press the mouse
    3. while (mouse is kept pressed) {
      1. get mouse position (this will be a Pt, call it m)
      2. erase the circle (wherever it is)
      3. move the circle to the new position, m
      4. draw the circle (in its new position)
      }

      // place the circle when you reach this place:
      // probably fill the circle and update the score

    }
How operational is this pseudo code? Here are some considerations: We now turn our attention to whether a circle can be placed or not.

Stage 3 (three): Rejecting Incorrect Locations

Let's say that if the circle is not placed inside the bin we return the (same) circle to the start location. So we have two basic questions:

The first question is purely mathematical: we need to know if the circle's center is within the bin and if the distance from the center to the walls is in each of the four cases smaller than the radius of the circle. We could even write a method that expects a circle and a rectangle and returns either true or false depending on whether the circle is inside the rectangle or not.

So I will assume that this method will be available in a collection of methods that we call Library and its name will be inside. The method expects a circle and a rectangle (in this order) and returns a boolean. This pretty much takes care of the first question. Now how do we return the circle to the start location instead of generating a new circle and resuming the loop?

This second question is hard, and this is where the design that we currently have is breaking up. If we restart the loop a new circle will be generated. We need to avoid doing that if the placement of the circle was not successful. So we investigate ways of generating the new circle only at the end of the body of the loop. Here's the basic outline:

What do we do?

We need a circle generated in the first place, or we wouldn't have one to ask question about and move it with the mouse.

But we don't need it generated unconditionally at the beginning of the loop, instead it appears that we have the information about whether we need to generate a new one or not only after we try to place it on the screen.

We could generate the first circle outside the loop, before we start it, and any new circle, as needed, inside it (as hinted above).

This is a powerful thought as it realizes an important distinction between the circles:

  1. the generation of the first circle is unconditional
  2. all the other circles will be generated conditionally (if the circle that had been generated before them was placed correctly then a new circle is needed otherwise we don't need a new circle as we still have this one to place somewhere)
So here's how the pseudo code looks now:
  1. generate new circle (this is our first)
  2. while (theGameMustGoOn) {
    1. let the user choose a placement location for it
    2. if (placement location not appropriate) {
      1. don't place the circle there
      2. don't generate a new circle: keep working with this one
      } else {
      1. go ahead and place the circle there
      2. generate a new circle and work with that one from now on
      }
    } Stage 4 (four): Overlapping Circles

    Here's what we have right now.

    1. get a drawing window, generate and draw a bin on it
    2. generate new circle (call it c) // this is our first circle
    3. start the game (theGameMustGoOn = true)
    4. while (theGameMustGoOn) {
      1. place c in the starting location and draw it there
        // now let the user choose a placement location for it
      2. wait for user to press the mouse
      3. while (mouse is kept pressed) {
        1. get mouse position (this will be a Pt, call it m)
        2. erase the circle (wherever it is)
        3. move the circle to the new position, m
        4. draw the circle (in its new position)
        }
      4. // at this point the circle has been dropped at a location
        if (placement location not appropriate) {
        1. don't place the circle there
        2. don't generate a new circle: keep working with this one
        } else {
        1. go ahead and place the circle there
        2. generate a new circle and work with that one from now on
        }
      } If we look closely at this code we realize we only need one piece of detail in step 3.4.2:
      c = new Circle(...);
      All the rest is already in place for the whole program to work well.

      Now that the basic loop has been take care of, let's see how we can acknowledge the circles that we are successfully placing in the bin.

      • First of all, how do we remember them?
      • Second, how do we check for overlap?
      To answer the first question we could decide to use an array to store the circles.

      Let's take care of this problem first.

      1. get a drawing window, generate and draw a bin on it
      2. declare and allocate an array to store the placed circles
        // the array is empty in the beginning, and we store circles
        // in it only when we know they can be placed in the bin
      3. generate new circle (call it c) // this is our first circle
      4. start the game (theGameMustGoOn = true)
      5. while (theGameMustGoOn) {
        1. place c in the starting location and draw it there
          // now let the user choose a placement location for it
        2. wait for user to press the mouse
        3. while (mouse is kept pressed) {
          1. get mouse position (this will be a Pt, call it m)
          2. erase the circle (wherever it is)
          3. move the circle to the new position, m
          4. draw the circle (in its new position)
          }
        4. // at this point the circle has been dropped at a location
          if (placement location not appropriate) {
          1. don't place the circle there
          2. don't generate a new circle (keep working with this one)
            // what do we do next? where do we go from here?
            // (hint: what else is left to be done in this loop?)
          } else {
          1. go ahead and place the circle there
          2. also store the circle in the array
            // we need a number for that, who's counting the circles?
          3. generate a new circle c
          }
        } So we need to count the circles.

        Let's use an int variable (call it number) for that.

        We start with number being 0 (zero).

        Let's add these new things to our design:

        1. get a drawing window, generate and draw a bin on it
        2. declare and allocate an array (call it circles) to store the placed circles
        3. generate new circle (call it c) // this is our first circle
        4. int number = 0; // that's the number of the circle we're working on
        5. boolean theGameMustGoOn = true; // start the game
        6. while (theGameMustGoOn) {
          1. place c in the starting location and draw it there
            // now let the user choose a placement location for it
          2. wait for user to press the mouse
          3. while (mouse is kept pressed) {
            1. get mouse position (this will be a Pt, call it m)
            2. erase the circle (wherever it is)
            3. move the circle to the new position, m
            4. draw the circle (in its new position)
            }
          4. // at this point the circle has been dropped at a location
            // we need to check here if the placement is appropriate.
          5. if (placement location not appropriate) {
            1. don't place the circle there
            2. don't generate a new circle (keep working with this one)
              // what do we do next? where do we go from here?
              // (hint: what else is left to be done in this loop?)
            } else {
            1. go ahead and place the circle there
            2. also store the circle in the array
              // we need a number for that, who's counting the circles?
              circles[number] = c;
            3. generate a new circle c
            4. number = number + 1; // the number of the circle we're working with
            }
          } Most of the steps above are fairly straightorward, except for step 6.4.

          Here's a design for it.

          Let's say the answer will be in a variable placementOK.

          If there's nothing to check then the answer is true.

          That is, if nobody complains we can take up that space.

          So we initialize this variable to true.

          Who could complain?

          Well, the other circles, if any.

          So we need to go and ask them, one by one.

          If one or more complain then we can't take the space.

          How do we get to them?

          Well, the current circle has a number (number).

          The previously placed circles also had numbers and they were placed in the array according to their numbers at the time.

          What are their numbers?

          They are:

          • 0
          • 1
          • ...
          • number - 1
          That's because the current circle is numbered number.

          So let's go through all of them and ask them one by one:

          boolean placementOK = true; 
          for (int k = 0; k < number; k++) {
            if (Library.overlap(c[k], c[i])) {
              placementOK = false; 
            } 
          }
          After this the variable placementOK will have the answer.

          We use this in the program and the design is pretty much finished.

          1. get a drawing window, generate and draw a bin on it
          2. declare and allocate an array (call it circles) to store the placed circles
          3. generate new circle (call it c) // this is our first circle
          4. int number = 0; // that's the number of the circle we're working on
          5. boolean theGameMustGoOn = true; // start the game
          6. while (theGameMustGoOn) {
            1. place c in the starting location and draw it there
              // now let the user choose a placement location for it
            2. wait for user to press the mouse
            3. while (mouse is kept pressed) {
              1. get mouse position (this will be a Pt, call it m)
              2. erase the circle (wherever it is)
              3. move the circle to the new position, m
              4. draw the circle (in its new position)
              }
            4. // at this point the circle has been dropped at a location
              // we need to check here if the placement is appropriate.
            5. boolean placementOK = true; // perhaps there's nothing to check
            6. if (circle outside bin) {
                placementOK = false;
                // you could, if you want, stop the game here
              } // otherwise it's still OK to place it here
            7. for each of the previously placed circles j (j from 0 to number - 1)
                if (circles[j] overlaps c) then
                  placementOK = false
            8. if (placement location not appropriate) {
              1. don't place the circle there
              2. don't generate a new circle (keep working with this one)
                // what do we do next? where do we go from here?
                // (hint: what else is left to be done in this loop?)
              } else {
              1. go ahead and place the circle there
              2. also store the circle in the array
                // we need a number for that, who's counting the circles?
                circles[number] = c;
              3. generate a new circle c
              4. number = number + 1; // the number of the circle we're working with
              }
            }
            Last updated: Apr 11, 2000 by Adrian German