Pascal Worked Example: Bouncing Ball: Iteration 4

From SwinBrain

This iteration will be used to tweek a few things, and to use some additional language features to simplify the implementation.

Change 1

Rather than using a white ball, lets draw the ball using a color. This will require the following changes:

  1. Change the DrawAt to include a color parameter
  2. In DrawAt change the color
  3. Update calls to DrawAt

The new code is shown below.

//This is a game where a ball is placed within a designated
//area of the screen. The ball will have a specified velocity
//and will move around within the area. When the ball collides
//with the edge of the area it should change direction. 
//Striking the bottom/top of the area will reverse the y 
//movement, while striking the left/right will reverse the
//x movement.
program BouncingBall;
uses CRT;
 
const
  BORDER_START_X = 2;
  BORDER_START_Y = 2;
  BORDER_WIDTH = 80 - BORDER_START_X - 1; //-1 for right border
  BORDER_HEIGHT = 25 - BORDER_START_Y - 1;
  BORDER_CHARACTER = #178;
  BALL_CHARACTER = #2;
  
//Draws a specified character at a given XY location.
//
// @param X The x location to draw at
// @param Y The y location to draw at
// @param character The character to be drawn
// 
//Side Effects:
// * Draws to the screen
procedure DrawAt(x, y: Integer; character: Char; color: Word);
begin
  TextColor(color);
  GotoXY(x, y);
  Write(character);
end;
 
//Draws the border around the game area.
//
// @param title The title to be written above the border
//
//Side Effects:
// * Writes to the console
procedure DrawBorder(title: String);
var
  x, y: Integer;
begin
  GotoXY(BORDER_START_X, 1);
  Write(Title);
  
  //Draw the top + bottom borders
  for x := BORDER_START_X to BORDER_START_X + BORDER_WIDTH do
  begin
    //Top
    DrawAt(x, BORDER_START_Y, BORDER_CHARACTER, White);
    DrawAt(x, BORDER_START_Y + BORDER_HEIGHT, BORDER_CHARACTER, White);
  end;
  
  for y := BORDER_START_Y to BORDER_START_Y + BORDER_HEIGHT do
  begin
    DrawAt(BORDER_START_X, y, BORDER_CHARACTER, White);
    DrawAt(BORDER_START_X + BORDER_WIDTH, y, BORDER_CHARACTER, White);
  end;
end;
 
//Calculates the new location for the ball given the current
//location and amount to move. The low and high boundary values
//are used to ensure that the ball remains in the screen.
//
// @param originalLocation The current location
// @param change  The current movement amount
// @param lowBoundary  The new location cannot be at or below this value
// @param highBoundary  The new location cannot be at or above this value
// @returns The new location for the ball
function GetNewLocation(originalLocation, change, lowBoundary, highBoundary: Integer): Integer;
var
  newLocation: Integer;
begin
  newLocation := originalLocation + change;
  if (newLocation <= lowBoundary) or (newLocation >= highBoundary) then
  begin
    //movement goes off screen... dont move
    result := originalLocation;
  end
  else
  begin
    result := newLocation;
  end;
end;
 
//Indicates if the location is within one of the lowBoundary or highBoundary.
//
// @param location  The current location
// @param lowBoundary The low boundary value, we will check if the location 
//                    is one larger than this.
// @param highBoundary The high boundary value, we will check if the location 
//                     is one smaller than this.
// @returns true if the location is at the edge
function AtEdge(location, lowBoundary, highBoundary: Integer): Boolean;
begin
	result := (location + 1 = highBoundary) or (location - 1 = lowBoundary);
end;
 
//Draws and moves the ball around the screen until a key is pressed.
//
//Side Effects:
// * Draws to console (in called routines)
procedure DoBouncingBall();
var
  x, y, dx, dy: Integer;
begin
  x := BORDER_START_X + 1;
  y := BORDER_START_Y + 1;
  dx := 1;
  dy := 1;
  
  repeat    
    DrawAt(x, y, BALL_CHARACTER, Yellow);
    Delay(100);
    DrawAt(x, y, ' ', White);
    
    x := GetNewLocation(x, dx, BORDER_START_X, BORDER_START_X + BORDER_WIDTH);
    if AtEdge(x, BORDER_START_X, BORDER_START_X + BORDER_WIDTH) then
    begin
      dx := -dx;
    end;
    
    y := GetNewLocation(y, dy, BORDER_START_Y, BORDER_START_Y + BORDER_HEIGHT);
    if AtEdge(y, BORDER_START_Y, BORDER_START_Y + BORDER_HEIGHT) then
    begin
      dy := -dy;
    end;
  until KeyPressed();
end;
 
//Draws the border and starts the ball bouncing.
//
//Side Effects:
// * Writes to console (in called routines)
procedure Main();
begin
  CursorOff();
  
  DrawBorder('Bouncing Ball');
  DoBouncingBall();
  
  CursorOn();
end;
 
begin
  Main();
end.

The execution is shown below.

Change 2

In this change we can take advantage of pass by reference parameters. This allows us to update the value of the variable passed to the parameter in the calling routine. In this way we can modify the structure of the program to use an UpdateLocation procedure that will update both the location and the direction of movement. The modified structure chart and code is shown below.

Center

//This is a game where a ball is placed within a designated
//area of the screen. The ball will have a specified velocity
//and will move around within the area. When the ball collides
//with the edge of the area it should change direction. 
//Striking the bottom/top of the area will reverse the y 
//movement, while striking the left/right will reverse the
//x movement.
program BouncingBall;
uses CRT;
 
const
  BORDER_START_X = 2;
  BORDER_START_Y = 2;
  BORDER_WIDTH = 80 - BORDER_START_X - 1; //-1 for right border
  BORDER_HEIGHT = 25 - BORDER_START_Y - 1;
  BORDER_CHARACTER = #178;
  BALL_CHARACTER = #2;
  
//Draws a specified character at a given XY location.
//
// @param X The x location to draw at
// @param Y The y location to draw at
// @param character The character to be drawn
// 
//Side Effects:
// * Draws to the screen
procedure DrawAt(x, y: Integer; character: Char; color: Word);
begin
  TextColor(color);
  GotoXY(x, y);
  Write(character);
end;
 
//Draws the border around the game area.
//
// @param title The title to be written above the border
//
//Side Effects:
// * Writes to the console
procedure DrawBorder(title: String);
var
  x, y: Integer;
begin
  GotoXY(BORDER_START_X, 1);
  Write(Title);
  
  //Draw the top + bottom borders
  for x := BORDER_START_X to BORDER_START_X + BORDER_WIDTH do
  begin
    //Top
    DrawAt(x, BORDER_START_Y, BORDER_CHARACTER, White);
    DrawAt(x, BORDER_START_Y + BORDER_HEIGHT, BORDER_CHARACTER, White);
  end;
  
  for y := BORDER_START_Y to BORDER_START_Y + BORDER_HEIGHT do
  begin
    DrawAt(BORDER_START_X, y, BORDER_CHARACTER, White);
    DrawAt(BORDER_START_X + BORDER_WIDTH, y, BORDER_CHARACTER, White);
  end;
end;
 
//Indicates if the location is within one of the lowBoundary or highBoundary.
//
// @param location  The current location
// @param lowBoundary The low boundary value, we will check if the location 
//                    is one larger than this.
// @param highBoundary The high boundary value, we will check if the location 
//                     is one smaller than this.
// @returns true if the location is at the edge
function AtEdge(location, lowBoundary, highBoundary: Integer): Boolean;
begin
  result := (location + 1 = highBoundary) or (location - 1 = lowBoundary);
end;
 
//Updates the location of the ball given the current
//location and amount to move. The low and high boundary values
//are used to ensure that the ball remains in the screen.
//
// @param location The location to move
// @param change  The current movement amount (may be changed)
// @param lowBoundary  The new location cannot be at or below this value
// @param highBoundary  The new location cannot be at or above this value
//
//Side Effects:
// * Changes the location
// * Changes the change value (when colliding with boundary)
procedure UpdateLocation(var location, change: Integer; lowBoundary, highBoundary: Integer);
begin
  location += change;
  
  if AtEdge(location, lowBoundary, highBoundary) then
  begin
    change := -change;
  end;
end;
 
//Draws and moves the ball around the screen until a key is pressed.
//
//Side Effects:
// * Draws to console (in called routines)
procedure DoBouncingBall();
var
  x, y, dx, dy: Integer;
begin
  x := BORDER_START_X + 1;
  y := BORDER_START_Y + 1;
  dx := 1;
  dy := 1;
  
  repeat    
    DrawAt(x, y, BALL_CHARACTER, Yellow);
    Delay(100);
    DrawAt(x, y, ' ', White);
    
    UpdateLocation(x, dx, BORDER_START_X, BORDER_START_X + BORDER_WIDTH);
    UpdateLocation(y, dy, BORDER_START_Y, BORDER_START_Y + BORDER_HEIGHT);
  until KeyPressed();
end;
 
//Draws the border and starts the ball bouncing.
//
//Side Effects:
// * Writes to console (in called routines)
procedure Main();
begin
  CursorOff();
  
  DrawBorder('Bouncing Ball');
  DoBouncingBall();
  
  CursorOn();
end;
 
begin
  Main();
end.

Summary

This is the end of the Bouncing Ball sample.

[edit]Bouncing Ball: Overview | Iteration 1 | Iteration 2 | Iteration 3 | Iteration 4
Sample Code » C | C++ | .NET | Pascal | Perl | T-Sql | XHTML | Python | others to come