PROGRAM LIFE2; {adpated from TURBO PASCAL LIBRARY by D. Stivison (SYBEX)}
uses Dos, Crt;
{$I Title.inc}
{$I+}
CONST Height = 24;
Width = 80;
MinBound = -1;
Lively = #79; {Try other characters for different effects.}
Deadly = ' ';
TYPE State = (Alive, Dead);
Cell = RECORD
LooksLikeItIs: State;
Nearby: INTEGER
END;
Edges = RECORD Left, Right, Top, Bottom: INTEGER END;
ScreenLine = STRING[80];
UniversalString = string[255];
VAR Board: ARRAY[MinBound..Height] OF ARRAY[MinBound..Width] OF Cell;
Population, Births, Deaths, Gen: INTEGER;
Ch: CHAR;
Quit: BOOLEAN;
Pause: INTEGER;
Edge: Edges;
FUNCTION Min(a,b: INTEGER): INTEGER;
BEGIN
IF a <= b THEN Min := a ELSE Min := b
END; {Min}
FUNCTION Max(a,b: INTEGER): INTEGER;
BEGIN
IF a >= b THEN Max := a ELSE Max := b
END; {Max}
function YesOrNo (PromptMessage: UniversalString): Boolean;
var response: Char;
begin
Write(PromptMessage,' (Y/N): ');
repeat
response := Readkey;
until response in ['Y','y','N','n'];
write (response);
YesOrNo := response in ['Y','y'];
end; {function}
Procedure WaitForAnyKey;
var AnyKey: Char;
begin
Writeln('To continue, please strike any key.');
AnyKey := ReadKey;
end; {procedure}
PROCEDURE ResetEdges;
BEGIN
Edge.Top := Height-1;
Edge.Right := MinBound+1;
Edge.Left := Width-1;
Edge.Bottom := MinBound+1
END; {ResetEdges}
PROCEDURE Initialize;
VAR Down, Across: INTEGER;
BEGIN
For Down := MinBound TO Height DO
For Across := MinBound TO Width DO
BEGIN
Board[Down,Across].LooksLikeItIs := Dead;
Board[Down,Across].Nearby := 0
END;
ResetEdges
END; {Initialize}
PROCEDURE Limits(x,y: INTEGER);
BEGIN
WITH Edge DO
BEGIN
Left := Min(Left,x);
Right := Max(Right,x);
Top := Min(Top,y);
Bottom := Max(Bottom,y)
END
END; {Limits}
PROCEDURE ClearNearby;
VAR Down, Across: INTEGER;
BEGIN
FOR Down := Edge.Top-1 TO Edge.Bottom+1 DO
FOR Across := Edge.Left-1 TO Edge.Right+1 DO
Board[Down,Across].Nearby := 0
END; {ClearNearby}
PROCEDURE CountNeighbors;
VAR Down, Across, DeltaDown, DeltaCross: INTEGER;
BEGIN
ClearNearby;
FOR Down := Edge.Top-1 TO Edge.Bottom+1 DO
FOR Across := Edge.Left-1 TO Edge.Right+1 DO
IF Board[Down][Across].LooksLikeItIs = Alive THEN
FOR DeltaDown := -1 TO 1 DO
FOR DeltaCross := -1 To 1 DO
Board[Down+DeltaDown][Across+DeltaCross].Nearby :=
Board[Down+DeltaDown][Across+DeltaCross].Nearby + 1
END; {CountNeighbors}
PROCEDURE UpDate;
VAR Down, Across: INTEGER;
LocalEdge: Edges;
BEGIN
Births := 0;
Deaths := 0;
LocalEdge := Edge;
ResetEdges;
FOR Down := Max(MinBound+1, LocalEdge.Top-1) TO
Min(Height-1, LocalEdge.Bottom+1) DO
FOR Across := Max(MinBound+1, LocalEdge.Left-1) TO
Min(Width-1, LocalEdge.Right+1) DO
WITH Board[Down][Across] DO
CASE LooksLikeItIs OF
Dead:
IF Nearby = 3 THEN
BEGIN
LooksLikeItIs := Alive;
GOTOXY(Across+1, Down+1);
WRITE(Lively);
Limits(Across, Down);
Births := Births +1
END;
Alive:
IF (Nearby = 3) OR (Nearby = 4) THEN Limits(Across, Down)
ELSE
BEGIN
LooksLikeItIs := Dead;
GOTOXY(Across+1, Down+1);
WRITE(Deadly);
Deaths := Deaths + 1
END
END; {Case}
Population := Population + Births - Deaths;
GOTOXY(1,1)
END; {UpDate}
PROCEDURE GetPositions;
VAR Down, Across, Dx, Dy: INTEGER;
BEGIN
Population := 0;
Dx := 37;
Dy := 12;
CLRSCR;
GOTOXY(Dx,Dy);
REPEAT
Ch := ReadKey;
IF KeyPressed THEN
BEGIN
IF (ORD(Ch) = 27) AND KeyPressed THEN Ch := ReadKey;
END;
CASE ORD(Ch) OF
75: BEGIN Dx := Dx-1; GOTOXY(Dx,Dy) END; {left arrow}
77: BEGIN Dx := Dx+1; GOTOXY(Dx,Dy) END; {right arrow}
72: BEGIN Dy := Dy-1; GOTOXY(Dx,Dy) END; {up arrow}
80: BEGIN Dy := Dy+1; GOTOXY(Dx,Dy) END; {down arrow}
120,88: BEGIN { x to mark }
GOTOXY(Dx,Dy);
WRITE('O'); GOTOXY(Dx,Dy); { reposition cursor }
Board[Dy-1,Dx-1].LooksLikeItIs := Alive;
Limits(Dx-1,Dy-1);
Population := Population+1;
END;
101,69: BEGIN { e to erase }
GOTOXY(Dx,Dy);
WRITE(' '); GOTOXY(Dx,Dy);
Board[Dy-1,Dx-1].LooksLikeItIs := Dead;
{ Limits?}
Population := Population-1;
END
END; {Case}
UNTIL Ch = '.';
END; {GetPositions}
PROCEDURE WindowDressing;
BEGIN
Textbackground (4);
TextColor (14);
CLRSCR;
PrintMessage ('LIFE',9);
GotoXY(5,19);
Writeln('[Arrow keys] to move,[x] to mark, [e] to erase, make start pattern,');
Writeln(' [>] to start generating, select speed: 1 slowest..0 fastest;');
Writeln(' any key except numbers will halt program.');
GOTOXY (22,24);
WaitForAnyKey;
CLRSCR;
END;
BEGIN {main program}
WindowDressing;
REPEAT
Gen := 0;
Initialize;
GetPositions;
Pause := 32;
Quit := FALSE;
WHILE NOT Quit DO
BEGIN
CountNeighbors;
UpDate;
Gen := Gen+1;
WRITE(Gen);
FOR Ch := 'A' TO 'Z' DO DELAY(Pause);
Quit := (Population = 0) OR ((Births = 0) AND (Deaths = 0));
IF KEYPRESSED THEN
BEGIN
Ch := ReadKey;
CASE Ch of
'1': Pause := 80; {tuned to 66 Mhz }
'2': Pause := 50;
'3': Pause := 32;
'4': Pause := 24;
'5': Pause := 16;
'6': Pause := 9;
'7': Pause := 6;
'8': Pause := 3;
'9': Pause := 1;
'0': Pause := 0;
else Quit := TRUE;
end; {case}
END {if keypressed }
END; {While}
GOTOXY (1,22);
IF Population = 0 THEN
BEGIN
Write(#7); { beep }
WRITELN('This colony has died out in ', Gen, ' generations.');
END
ELSE WRITELN
UNTIL NOT YesOrNo ('Would you like to run LIFE again?')
END.
               (
geocities.com/~franzglaser)