|
|
DataMuseum.dkPresents historical artifacts from the history of: Rational R1000/400 Tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Rational R1000/400 Tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: M T
Length: 8396 (0x20cc)
Types: TextFile
Names: »MAGIC_SQUARE_EXERCISE«
└─⟦180fe333a⟧ Bits:30000405 8mm tape, Rational 1000, SW CATALOG, 10_20_0
└─⟦180fe333a⟧ Bits:30000537 8mm tape, Rational 1000, SW Catalog 10_20_0
└─⟦5cb1d1d7f⟧ »DATA«
└─⟦3b1ee7bd8⟧
└─⟦this⟧
=============================================
A FIRST ADA PROGRAM: BUILDING MAGIC SQUARES
=============================================
This is an exercise intended to be the very first program written the
first day of a hands-on Ada course.
The pre-requisites, and therefore the language elements it illustrates are:
compilation units
simple packages and package bodies, with clauses
simple object declaration, enumeration types, simple array declaration
simple statements: assignments, loops, procedure calls
----------------------------
Here is an abstraction of a square, or a checker board, on which one
wants to move a cursor and deposit tokens numbered from 1 to N. This
abstraction is expressed under the form of the following package
specification (built step by step in front of the students):
package Square is
Size : constant := 5; -- dimension must be an odd number
type Direction is (North, South, East, West, North_East,
North_West, South_East, South_West);
procedure Go_To_Center;
procedure Go (Which_Direction : in Direction);
function Is_Already_Occupied return Boolean;
procedure Deposit (Token : Positive);
procedure Display;
end Square;
Moreover, this square has the following interesting characteristic: the
edges "wrap-around"; moving the cursor further east when you are at the
most east edge brings it on the west edge.
Using that abstract square or board, we want to build a "magic square".
A magic square is a n by n square filled with integers from 1 to n**2,
such that all the sums along rows, columns and main diagonals are equals.
For instance:
8 1 6
3 5 7
4 9 2 has the sum 15 in all rows, columns and diag.
A simple algorithm to compute such a magic square, when n is an odd number,
is the following:
1. Go to the center of the board
2. Go one step north, and deposit token #1
3. repeat until all tokens are distributed:
3.1 Go north-east
3.2 If box has already received a token, go north_west (repeat if
necessary)
3.3 deposit the next token
The students are asked:
1. to complete the body of package Square
2. to write a 'main' program: procedure magic_square, to implement
the algorithm
Here is a very simple and naive implementation:
with Square;
use Square;
procedure Magic_Square is
Token : Positive := 1;
begin
Go_To_Center;
Go (North);
loop
Deposit (Token);
exit when Token = Size ** 2;
Token := Token + 1;
Go (North_East);
while Is_Already_Occupied loop
Go (North_West);
end loop;
end loop;
Display;
end Magic_Square;
and for the body of Square:
with Io;
package body Square is
-- definition of the actual 'checker-board'
subtype Edge is Natural range 0 .. Size - 1;
Board : array (Edge, Edge) of Natural := (others => (others => 0));
-- position of the cursor
X, Y : Edge := Size / 2;
procedure Go_To_Center is
begin
X := Size / 2;
Y := Size / 2;
end Go_To_Center;
procedure Go (Which_Direction : in Direction) is
begin
case Which_Direction is
when North => Y := (Y + 1) mod Size;
when South => Y := (Y - 1) mod Size;
when East => X := (X + 1) mod Size;
when West => X := (X - 1) mod Size;
when North_East => Go (North);
Go (East);
when South_East => Go (South);
Go (East);
when North_West => Go (North);
Go (West);
when South_West => Go (South);
Go (West);
end case;
end Go;
function Is_Already_Occupied return Boolean is
begin
return Board (X, Y) > 0;
end Is_Already_Occupied;
procedure Deposit (Token : Positive) is
begin
Board (X, Y) := Token;
end Deposit;
procedure Display is
begin
for Y in reverse Edge loop
for X in Edge loop
Io.Put (Board (X, Y), Width => 3);
end loop;
Io.New_Line;
end loop;
end Display;
end Square;
---------------------------------------------
For fast students, who finish before the others, several improvements
or alternative may be investigated:
A. To show the abstraction and information hiding aspect, one may
wish to substitute another body for the package Square, for instance
using an uni-dimensional array (see below)
B. The Square package is tight to the size 5. Change it in a
generic package, where Size is a generic parameter, and have
the procedure generic square instantiate is for various (odd) sizes from
3 to 13.
The code given below implements suggestion A and B simultaneously. Note also
the non-recursive version of procedure Go.
C. Introduce an exception Not_An_Odd_Number in the package Square and
check in the package body the parameter Size. Handle the exception in
some way in the procedure Magic_square.
D. Get rid of the Go_To_Center procedure, which is given initially just
for the sake of clarity.
E. Adjust automatically the Width parameter in the IO.Put call in the
body of Display to fit the largest token value (Size**2).
generic
Size : in Positive := 5;-- dimension must be an odd number
package Square is
type Direction is (North, South, East, West, North_East,
North_West, South_East, South_West);
procedure Go_To_Center;
procedure Go (Which_Direction : in Direction);
function Is_Already_Occupied return Boolean;
procedure Deposit (Token : Positive);
procedure Display;
end Square;
with Io;
package body Square is
-- definition of the actual 'checker-board' as a vector (just for
-- the fun of it) with constant specification
Size_2 : constant Positive := Size ** 2;
subtype Edge is Natural range 0 .. Size ** 2 - 1;
Board : array (Edge) of Natural := (others => 0);
-- position of the cursor
X : Edge := Size_2 / 2;
procedure Go_To_Center is
begin
X := Size_2 / 2;
end Go_To_Center;
procedure Go (Which_Direction : in Direction) is
begin
case Which_Direction is
when North | North_East | North_West =>
X := (X - Size) mod Size_2;
when South | South_East | South_West =>
X := (X + Size) mod Size_2;
when others =>
null;
end case;
case Which_Direction is
when East | North_East | South_East =>
X := ((X / Size) * Size + (((X mod Size) + 1) mod Size));
when West | North_West | South_West =>
X := ((X / Size) * Size + (((X mod Size) - 1) mod Size));
when others =>
null;
end case;
end Go;
function Is_Already_Occupied return Boolean is
begin
return Board (X) > 0;
end Is_Already_Occupied;
procedure Deposit (Token : Positive) is
begin
Board (X) := Token;
end Deposit;
procedure Display is
begin
for X in Edge loop
if X mod Size = 0 then
Io.New_Line;
end if;
Io.Put (Board (X), Width => 4);
end loop;
Io.New_Line;
end Display;
end Square;
with Square;
procedure Magic_Square is
procedure Do_A_Magic_Square (Size : in Positive) is
package This_Square is new Square (Size);
use This_Square;
Token : Positive := 1;
begin
Go_To_Center;
Go (North);
loop
Deposit (Token);
exit when Token = Size ** 2;
Token := Token + 1;
Go (North_East);
while Is_Already_Occupied loop
Go (North_West);
end loop;
end loop;
Display;
end Do_A_Magic_Square;
begin
for Size in 3 .. 13 loop
if Size mod 2 /= 0 then
Do_A_Magic_Square (Size);
end if;
end loop;
end Magic_Square;
--------------------------------- Et voila !