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.