
PROGRAM Life (Input,Output);

CONST
   MaxRows = 10;
   MaxColumns = 25;
   Live = '*';
   Dead = ' ';     (* Blank *)

TYPE
   Location = Record
        Organism : Char;
        Count : Integer;
    End;
   RowRange = 1..MaxRows;
   ColumnRange = 1..MaxColumns;
   LifeBoard = ARRAY[RowRange,ColumnRange] OF Location;
   Neighborhood = 0..8;
   NeighborSet = SET OF Neighborhood;

VAR
   World : LifeBoard;
   Birth, ContinueLife : NeighborSet;
   Ch : Char;




(********************************************************************
Get set of number of neighbors that cause a birth to occur.
Get set of number of neighbors that cause organism to continue living *)

PROCEDURE GetParameters (Var BirthConditions, LivingConditions : NeighborSet);

Var
   I : Integer;

Begin
   BirthConditions := [];
   Write('Enter birth conditions on this line only: ');
   WHILE NOT EOLN DO
     Begin
       Read(I);
       IF (I > 0) AND (I <= 8) THEN
          BirthConditions := BirthConditions + [I];
     End;
   Readln;
   LivingConditions := [];
   Write('Enter live conditions on this line only: ');
   WHILE NOT EOLN DO
     Begin
       Read(I);
       IF (I >= 0) AND (I <= 8) THEN
          LivingConditions := LivingConditions + [I];
     End;
End;




(*************************************************
Create a dead world *)

PROCEDURE ClearWorld (Var World : LifeBoard);
Var
   I : RowRange;
   J : ColumnRange;
Begin
   FOR I := 1 TO MaxRows DO
      FOR J := 1 TO MaxColumns DO
        Begin
          World[I,J].Organism := Dead;       (* nothing living *)
          World[I,J].Count := 0;
        End;
End;







(**********************************************************
Create initial world of organisms. *)

PROCEDURE Genesis (Var World : LifeBoard);

Var
   I, J : Integer;

Begin  (* Genesis *)
   ClearWorld(World);

   WHILE NOT EOF DO
     Begin
        Write('Enter co-ords of an organism: ');
        Readln(I,J);
        IF (I > MaxRows) OR (J > MaxColumns) THEN
           Writeln('UNACCEPTABLE. REJECTED. AGAIN PLEASE.')
        ELSE
          Begin
            World[I,J].Organism := Live;
            World[I,J].Count := 1;
          End;
     End;
End;





(********************************************************
Display the world *)

PROCEDURE PrintWorld (World : LifeBoard; Births, Lives : NeighborSet);

Var
   I : RowRange;
   J : ColumnRange;


(***************************************************)
PROCEDURE PrintNeighbors (Nums : NeighborSet);

Var
   I : Neighborhood;

Begin
   FOR I := 0 TO 8 DO
      IF I IN Nums THEN
         Write(I,' ');
End;


Begin  (* PrintWorld *)
   Write('Birthing conditions: ');
   PrintNeighbors(Births);
   Writeln;
   Write('Continue living conditions: ');
   PrintNeighbors(Lives);
   Writeln;

   FOR I := 1 TO MaxRows DO
     Begin
       FOR J := 1 TO MaxColumns DO
          Write(World[I,J].Organism);
       Writeln;
     End;
End;





(***********************************************************
Generate the next generation *)

PROCEDURE NextGeneration (Var ThisOne : LifeBoard;
                           BirthConditions, LifeConditions : NeighborSet);

Var
   NextOne : LifeBoard;
   NumNeighbors : Neighborhood;
   I : RowRange;
   J : ColumnRange;

(*********************************************************************)
FUNCTION Neighbors (World : LifeBoard; R : RowRange; C : ColumnRange)
                  : Neighborhood;
Var
   N : Neighborhood;
Begin
   N := 0;
   IF World[R-1,C-1].Organism = Live THEN
      N := N + 1;
   IF World[R-1,C].Organism = Live THEN
      N := N + 1;
   IF World[R-1,C+1].Organism = Live THEN
      N := N + 1;
   IF World[R,C-1].Organism = Live THEN
      N := N + 1;
   IF World[R,C+1].Organism = Live THEN
      N := N + 1;
   IF World[R+1,C-1].Organism = Live THEN
      N := N + 1;
   IF World[R+1,C].Organism = Live THEN
      N := N + 1;
   IF World[R+1,C+1].Organism = Live THEN
      N := N + 1;
   Neighbors := N;
End;


Begin  (* NextGeneration *)
   ClearWorld(NextOne);

   FOR I := 2 TO MaxRows-1 DO
      FOR J := 2 TO MaxColumns-1 DO
        Begin
          NumNeighbors := Neighbors(ThisOne,I,J);
          IF ThisOne[I,J].Organism = Dead THEN
             IF NumNeighbors IN BirthConditions THEN
               Begin
                  NextOne[I,J].Organism := Live;
                  ThisOne[I,J].Count := ThisOne[I,J].Count + 1;
               End
             ELSE
                NextOne[I,J].Organism := Dead
          ELSE
             IF NumNeighbors IN LifeConditions THEN
               Begin
                 NextOne[I,J].Organism := Live;
                 ThisOne[I,J].Count := ThisOne[I,J].Count + 1;
               End
             ELSE
                NextOne[I,J].Organism := Dead;
        End;

   FOR I := 1 TO MaxRows DO         (* copy into formal parameter *)
      FOR J := 1 TO MaxColumns DO
         ThisOne[I,J].Organism := NextOne[I,J].Organism;
End;





(**********************************************************************
Display count of living on each cell *)

PROCEDURE PrintCount (World : LifeBoard);

Var
   I : RowRange;
   J : ColumnRange;

Begin
   FOR I := 1 TO MaxRows DO
     Begin
       FOR J := 1 TO MaxColumns DO
          Write(World[I,J].Count:3);
       Writeln;
     End;
End;





BEGIN
   GetParameters(Birth,ContinueLife);
   Genesis(World);
   PrintWorld(World,Birth,ContinueLife);
   REPEAT
      Write('Next generation or Quit? ');
      Readln(Ch);
      IF Ch IN ['n','N'] THEN
        Begin
          NextGeneration(World,Birth,ContinueLife);
          PrintWorld(World,Birth,ContinueLife);
        End;
   UNTIL Ch IN ['q','Q'];
   PrintCount(World);
END.
