-- FILE: grid.adb -- AUTHOR: E. Burke -- DATE: November, 1996 -- PURPOSE: Procedures to initialize and display generic grid-box package body GRID is -- Set all cells to 'dead' procedure Initialize (Grid_Box : out BOARD) is begin for i in Grid_Box'Range(1) loop for j in Grid_Box'Range(2) loop Grid_Box(i,j) := FALSE; end loop; end loop; end Initialize; -- Ask user for coordinates of cells to be initially 'alive' procedure Input (Grid_box : out BOARD) is subtype Input_Value is NATURAL range 0..MAX_SIZE; package Number_IO is new Text_IO.Integer_IO(Input_Value); Index : Input_Value; Jndex : Input_Value; I : POSITIVE; J : POSITIVE; begin Text_IO.Put(Item => "On each line give a pair of"); Text_IO.Put(Item => " coordinates for a living cell"); Text_IO.New_Line; Text_IO.Put(Item => "Terminate the list by entering 0"); Text_IO.New_Line; loop -- outer loop continues until 0 is entered to exit begin -- exception-handler block Text_IO.Put(Item => "Row number : "); Number_IO.Get(Item => Index); Text_IO.Put(Item => "Column number: "); Number_IO.Get(Item => Jndex); exit when Index = 0; exit when Jndex = 0; I := Index; J := Jndex; exception when Text_IO.Data_Error => Text_IO.Put (Item => "Value entered is out of range"); Text_IO.New_line; when Constraint_Error => Text_IO.Put (Item => "Value entered is out of range"); Text_IO.New_line; end; -- end of exception handler block Grid_Box (I,J) := TRUE; -- process input end loop; end Input; -- Display cells procedure Output (Grid_Box : in BOARD) is begin for i in Grid_Box'Range(1) loop for j in Grid_Box'Range(2) loop if Grid_Box(i,j) then TEXT_IO.PUT('*'); else TEXT_IO.PUT(' '); end if; end loop; TEXT_IO.New_Line; end loop; end Output; -- Change each cell to 'alive' or 'dead' depending on status of -- neighboring cells procedure Update_Box (Grid_Box : in out BOARD) is Newmap : BOARD; begin for I in Grid_Box'Range(1) loop for J in Grid_Box'Range(2) loop case Neighbor_Count(I,J,Grid_Box) is when 0 | 1 | 4 | 5 | 6 | 7 | 8 => Newmap(I,J) := FALSE; when 2 => Newmap(I,J) := Grid_Box(I,J); when 3 => Newmap(I,J) := TRUE; when others => NULL; end case; end loop; end loop; Grid_Box := Newmap; end Update_Box; -- Count number of neighbors of a cell that are alive function Neighbor_Count (I : NATURAL; J : NATURAL; Grid_Box : BOARD) return NATURAL is Count : NATURAL; Xlow : NATURAL; Xhigh : NATURAL; Ylow : NATURAL; Yhigh : NATURAL; begin Xlow := I-1; -- set coordinates of eight neighboring cells Xhigh := I+1; -- of cell I,J Ylow := J-1; Yhigh := J+1; if Xlow < 1 then -- Reset border if cell I,J is at edge of board Xlow := 1; end if; if Ylow < 1 then Ylow := 1; end if; if Xhigh > MAX_ROWS then Xhigh := MAX_ROWS; end if; if Yhigh > MAX_COLUMNS then Yhigh := MAX_COLUMNS; end if; Count := 0; for X in Xlow..Xhigh loop -- Increment count for each alive neighbor for Y in Ylow..Yhigh loop if Grid_Box(X,Y) = TRUE then Count := Count + 1; end if; end loop; end loop; if Grid_Box(I,J) = TRUE then -- Exclude target cell from count Count := Count - 1; end if; return Count; end Neighbor_Count; -- Ask user whether to run another or update cycle or terminate function Enquire return BOOLEAN is Run_again : BOOLEAN; Response : Character; begin while Response /= 'Y' and Response /= 'y' and Response /= 'N' and Response /= 'n' loop Text_IO.Put (Item => "Continue (y / n)"); Text_IO.New_Line; Text_IO.Get (Item => Response); end loop; if Response ='Y' or Response = 'y' then Run_Again := TRUE; else Run_Again := FALSE; end if; return Run_Again; end Enquire; end Grid; -- Comments by G. Levine -- I recommend that you use the short circuit operators (and then) and -- (or else), instead of (and) and (or) -- I recommend that you use the expression -- Run_Again := (Response = 'Y') or else (Response = 'y'); -- I recommend that you clear the screen before the program starts- -- see Dr. Feldman's screen package, or I can mail you one of my own. -- You could test -- if Grid_Box (i,j), instead of Grid_Box (i,j) = TRUE -- Be consistent in your style - are type names all upper case or not. -- If BOARD, POSITIVE and NATURAL are upper case, then so should -- INPUT_VALUE and CHARACTER be. -- Add to this package a procedure defining the game of life for -- players and telling them the constraints on the coordinates that -- they enter. -- Let's look at this code. -- First, according to your board size, if Xlow is less than one -- Xhigh cannot be greater than MAX_ROWS (same for Ylow) and -- therefore you can eliminate two checks with an -- else if clause. -- if Xlow < 1 then -- Reset border if cell I,J is at edge of board -- Xlow := 1; -- end if; -- if Ylow < 1 then -- Ylow := 1; -- end if; -- if Xhigh > MAX_ROWS then -- Xhigh := MAX_ROWS; -- end if; -- if Yhigh > MAX_COLUMNS then -- Yhigh := MAX_COLUMNS; -- end if; -- I repeatedly suggested that you eliminate the whole 4 checks by -- inserting an empty border around the BOARD. You can still -- use (0,0) as a terminator, or better yet, let any 0 input terminate the -- game. -- Why do you need these two sets of input values? -- Index : Input_Value; -- Jndex : Input_Value; -- I : POSITIVE; -- J : POSITIVE; -- If you just check that any 0 input terminates the game, Index and Jndex are -- better than I and J, since they deliminate the upper edges. -- I insist that you do not use the liters '*' and ' '; -- either declare them in that procedure as constants - -- constant -- STAR = '*' -- or make then generic. -- You also must fix the code around the exception handlers below: -- exception -- when Text_IO.Data_Error => -- Text_IO.Put (Item => "Value entered is out of range"); -- Text_IO.New_line; -- when Constraint_Error => -- Text_IO.Put (Item => "Value entered is out of range"); -- Text_IO.New_line; -- end; -- end of exception handler block -- Grid_Box (I,J) := TRUE; -- process input -- end loop; -- -- First, when a nonnumeric is input, you must clear that value from the buffer -- else it becomes an infinite loop. -- when Text_IO.Data_Error => -- Text_IO.Put (Item => "Value entered is out of range"); -- Text_IO.New_line; -- Text_IO.Skip_Line; -- Second, when the input value is numeric but out of range, if that is the -- first input value for I and J, I and J will be undefined, and -- Grid_Box (I,J) := TRUE will almost certainly crash- and you -- have no exception handler for that in a containing module. -- Place the statement -- Grid_Box (I,J) := TRUE; -- right after the assignment -- J := Jndex; -- It will not be executed if an incorrect value is input.