Spider | Previous | Next | Full Table of Contents | Index | Program List | Copyright
You have seen a number of examples of how to use the spider
package, whose specification was given as
Program 3.11. You have not yet
looked inside the body of the package; you now have enough background
to understand the body. This package uses much of the material
discussed in this chapter, including numeric subtypes, Boolean
expressions, CASE statements, and the screen
package. Program 7.11 shows the body of this
package. It is quite long, but we can examine it in sections.
Program 7.11
Body of the Spider Package
WITH Ada.Text_IO;
WITH Screen;
PACKAGE BODY Spider IS
------------------------------------------------------------------------
--| This package provides procedures to emulate "Spider"
--| commands. The spider can move around
--| the screen drawing simple patterns.
--| Author: John Dalbey, Cal Poly San Luis Obispo, 1992
--| Adapted by: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------
TYPE ScreenColors IS (Red, Blue, Green, Black); -- available colors
-- Spider's View of her Room - rows and cols both numbered 1..20
SUBTYPE Rows IS Positive RANGE 1..20;
SUBTYPE Cols IS Positive RANGE 1..20;
RowsInRoom : CONSTANT Positive := Rows'Last;
ColsInRoom : CONSTANT Positive := Cols'Last;
-- Spider State
Spidersym : CONSTANT character := '*'; -- asterisk
CurrentColumn: Cols; -- spider's position
CurrentRow : Rows; -- in the room.
Heading : Directions; -- spider's direction
Ink : ScreenColors; -- spider's color
-- Screen Description Constants: for 24 x 80 screen,
-- 1 spider row = 1 screen row, 1 spider col = 2 screen cols
RowLow : CONSTANT Screen.Depth := 2; -- room row bounds
RowHigh : CONSTANT Screen.Depth := RowLow + Rows'Last;
ColLow : CONSTANT Screen.Width := 21; -- lower column bound
ColHigh : CONSTANT Screen.Width := ColLow + 2*Cols'Last;
DebugFlag : Boolean := False; -- Is single stepping on?
-- internal procedures and functions, not in specification
-- and therefore not available to client program
PROCEDURE DrawSymbol (Which: Character) IS
-- Pre: Which is defined
-- Post: Which appears in its proper position on the screen
BEGIN
Screen.MoveCursor (Row => (RowLow - 1) + CurrentRow,
Column => (ColLow - 2) + (2 * CurrentColumn));
Ada.Text_IO.Put (Item => Which);
Ada.Text_IO.New_Line;
END DrawSymbol;
FUNCTION ColorSymbols (Color: ScreenColors) RETURN Character IS
-- Pre: Color is defined
-- Post: Returns the drawing character corresponding to Color
BEGIN
CASE Color IS
WHEN Red => RETURN '+';
WHEN Blue => RETURN 'X';
WHEN Green => RETURN 'O';
WHEN Black => RETURN '.';
END CASE;
END ColorSymbols;
FUNCTION Compass (Direction: Directions) RETURN Character IS
-- Pre: Direction is defined
-- Post: Returns drawing character corresponding to Direction
BEGIN
CASE Direction IS
WHEN North => RETURN '^';
WHEN East => RETURN '>';
WHEN South => RETURN 'v';
WHEN West => RETURN '<';
END CASE;
END Compass;
PROCEDURE DrawStatus IS
-- Pre: None
-- Post: Status Box appears on the screen
BEGIN
Screen.MoveCursor (Row => 2, Column => 1);
Ada.Text_IO.Put (" --- ");
Screen.MoveCursor (Row => 3, Column => 1);
Ada.Text_IO.Put ("| |");
Screen.MoveCursor (Row => 4, Column => 1);
Ada.Text_IO.Put ("| |");
Screen.MoveCursor (Row => 5, Column => 1);
Ada.Text_IO.Put (" --- ");
END DrawStatus;
PROCEDURE DrawRoom IS
-- Pre: None
-- Post: Room appears on the screen
BEGIN
Screen.ClearScreen;
Screen.MoveCursor (Row => 1, Column => 1);
-- Top Bar
Ada.Text_IO.Put (" ");
Ada.Text_IO.Put (" --------------------------------------- ");
Ada.Text_IO.New_Line;
FOR I in 1..20 LOOP
Ada.Text_IO.Put (" ");
Ada.Text_IO.Put ("|. . . . . . . . . . . . . . . . . . . .|");
Ada.Text_IO.New_Line;
END LOOP;
Ada.Text_IO.Put (" ");
Ada.Text_IO.Put (" --------------------------------------- ");
DrawStatus;
END DrawRoom;
PROCEDURE ChgColor (NewColor : ScreenColors) IS
-- Pre: NewColor is defined
-- Post: Ink is changed to NewColor and displayed in status box
BEGIN
Ink := NewColor;
Screen.MoveCursor ( Row => 4, Column => 3);
Ada.Text_IO.Put (ColorSymbols(Ink));
END ChgColor;
PROCEDURE ShowDirection IS
-- Pre: None
-- Post: Heading is displayed in the status box
BEGIN
Screen.MoveCursor(Row => 3,Column => 3);
Ada.Text_IO.Put (Compass(Heading));
END ShowDirection;
PROCEDURE ShowSpider IS
-- Pre: None
-- Post: The spider symbol appears in its current position
BEGIN
DrawSymbol (SpiderSym);
END ShowSpider;
-- These procedures are in the package specification
-- and implement the "official" spider commands
PROCEDURE Start IS
BEGIN
DrawRoom;
CurrentColumn := 10; -- these are in the spider's view
CurrentRow := 11;
Heading := North;
Green;
ShowSpider;
ShowDirection;
END Start;
PROCEDURE Blue IS
BEGIN
ChgColor (blue);
END Blue;
PROCEDURE Green IS
BEGIN
ChgColor (green);
END Green;
PROCEDURE Red IS
BEGIN
ChgColor (red);
END Red;
PROCEDURE Black IS
BEGIN
ChgColor (black);
END Black;
PROCEDURE Right IS
BEGIN
IF Heading = Directions'Last THEN
Heading := Directions'First;
ELSE
Heading := Directions'Succ (Heading);
END IF;
ShowDirection;
END Right;
PROCEDURE Face (WhichWay: IN Directions) IS
BEGIN
Heading := WhichWay;
ShowDirection;
END Face;
FUNCTION IsFacing RETURN Directions IS
BEGIN
RETURN Heading;
END IsFacing;
FUNCTION AtWall RETURN Boolean IS
BEGIN
-- Check for out of bounds (in the spider's view)
CASE Heading IS
WHEN North =>
RETURN CurrentRow <= Rows'First;
WHEN East =>
RETURN CurrentColumn >= Cols'Last;
WHEN South =>
RETURN CurrentRow >= Rows'Last;
WHEN West =>
RETURN CurrentColumn <= Cols'First;
END CASE;
END AtWall;
PROCEDURE Step IS
BEGIN
-- leave a track where spider is standing
DrawSymbol (ColorSymbols (Ink) );
-- If out of bounds raise exception.
IF AtWall THEN
Screen.Beep;
Ada.Text_IO.New_Line;
RAISE Hit_the_Wall;
END IF;
-- change the spider's location
CASE Heading IS
WHEN North =>
CurrentRow := CurrentRow - 1;
WHEN East =>
CurrentColumn := CurrentColumn + 1;
WHEN South =>
CurrentRow := CurrentRow + 1;
WHEN West =>
CurrentColumn := CurrentColumn - 1;
END CASE;
-- draw the spider in her new location
ShowSpider;
-- if debug mode, wait for user to press RETURN
IF Debugging = On THEN
Ada.Text_IO.Skip_Line;
ELSE
DELAY 0.5;
Ada.Text_IO.New_Line;
END IF;
END Step;
PROCEDURE Quit IS
-- Quit command.
BEGIN
Screen.MoveCursor(Row => 23,Column => 1);
END Quit;
PROCEDURE Debug (Setting: Switch) is
-- Toggle debugging mode
BEGIN
IF Setting = ON THEN
DebugFlag := true;
Screen.MoveCursor (Row => 10,Column => 1);
Ada.Text_IO.Put ("-- DEBUG ON -- ");
Ada.Text_IO.New_Line;
Ada.Text_IO.Put (" Press Enter");
ELSE
DebugFlag := false;
Screen.MoveCursor (Row => 10,Column => 1);
Ada.Text_IO.Put (" ");
Ada.Text_IO.New_Line;
Ada.Text_IO.Put (" ");
END IF;
END Debug;
FUNCTION Debugging RETURN Switch IS
BEGIN
IF DebugFlag THEN
RETURN On;
ELSE
RETURN Off;
END IF;
END Debugging;
END Spider;
The first few lines of the package body define a type
ScreenColors as an enumeration type, then describe the
spider's view of its environment. The spider's room has 20 rows
(RowsInRoom) and 20 columns (ColsInRoom),
defined in terms of the positive subtypes Rows and
Cols. Next we have the spider's own symbol, an asterisk
('*'), and four variables which describe the current
location, direction, and color of the spider. These variables
together comprise the spider's state, that is, all its
characteristics that can change during the life of the program. The
variables are therefore called state variables.
The next four lines of the package describe the location and size of the actual room picture on the screen. The upper left corner of the room is at row = 2, column = 21. This corresponds to the spider's row = 1, column = 1. The spider's row = 20, column = 20, corresponds to the screen coordinates row = 22 (2 + 20), column 61 (21 + 2*20).
Why are we multiplying the columns by 2? On the terminal screen, columns are narrower than rows, so to make the room look square, we use alternating screen columns.
We have two sets of coordinates, the spider's coordinates (a row/column pair as viewed by the spider) and the room's physical coordinates on the screen (a different row/column pair as seen on the screen). As we will see below, several of the package procedures have the responsibility to convert between the coordinate systems. This is a simple example of coordinate transformation, a concept often used in computer graphics and other engineering applications.
Now let's move on to the first groups of subprograms. These are
included in the package only to provide services to other subprograms
and are not seen by a program that uses the package. The first
procedure is DrawSymbol, which draws a character in the
spot on the screen that represents the spider's current location.
Note how the parameters to Screen.MoveCursor are
computed. For example, the spider's row 1 is the screen's row 2; the
spider's column 10 is the screen's column 39 (19 + 2*10). The call to
Ada.Text_IO.New_Line is present because in many
operating systems output is buffered, that is, nothing is
actually displayed on the screen until a line is complete. Including
the New_Line call therefore indicates a complete line
to the operating system, and the character is displayed immediately.
You have noticed that the spider's color is displayed as a
specific character, and its direction by an "arrow" pointing in the
correct direction. The next two functions, ColorSymbols
and Compass, take care of the necessary transformations,
each function using a CASE statement to determine the
appropriate character.
The next two procedures, DrawStatus and
DrawRoom, display the status box and the picture of the
room, respectively, using Screen.MoveCursor, as we have
seen before. Note that DrawRoom calls DrawStatus.
ChgColor changes the spider's ink color and displays the new
symbol in the status box; ShowDirection displays the
appropriate direction indicator in that box. Finally,
ShowSpider displays the spider's asterisk in its
current location, calling DrawSymbol to do the
coordinate transformation.
Having examined all the service subprograms, we proceed to the
actual spider commands, that is, the ones given in the package
specification. Start sets everything up, draws the
room, and places the spider in the center of the room (according to
the spider's view). Next, the four color commands all call
ChgColor to change the ink and display the new
character in the status box.
Right changes the spider's heading, using the
enumeration attribute function that finds the successor of a value,
then calls ShowDirection to display the arrow.
Face is similar, and IsFacing just
returns the spider's current heading. AtWall uses a
CASE statement again to determine which way the spider
is facing, which is necessary in order to detect whether the spider
is about to hit the wall.
Step is a bit longer than the other operations,
first leaving a track in the spider's position, then checking whether
the spider has hit the wall. If so, the terminal is made to beep (as
with DrawSymbol, the New_Line makes the
beep occur immediately). The next statement,
RAISE Hit_the_Wall;
is the first time we have seen RAISE used; in this
case, it causes Hit_the_Wall to be raised immediately.
Because there is no exception handler in Step, the
procedure halts and returns to its caller, where the exception is
raised again. This is a good time to look back at
Drunken_Spider ( Program
7.7) to review how the exception is handled there.
If the spider is not hitting the wall, it can take a step forward.
This is done by the CASE statement in
Step, just changing the current row or column according
to the spider's heading. Finally, the spider symbol is displayed in
its new location. If the user's program has called Spider.Debug
to set the debugging switch, the program now waits for the
user to press Enter (using
Ada.Text_IO.Get_Line, which we will examine in more
detail in Chapter 9); if debugging is not turned on, the program just
delays one-half second and continues.
This has been a fairly long but interesting trip through the spider package; as you have seen, this packages pulls together many of the concepts introduced in this chapter and earlier ones.
Copyright ©1996 by Addison-Wesley Publishing Company, Inc.