Program Video_Automata_13; {Copyright Albert K. Harris May 2000}
{Written in Turbo Pascal, for Macintosh Computers; Down With CCI!)

uses memtypes, quickdraw, OSIntf,ToolIntf; {special for Turbo Pascal}
const Sq_Width=20;MAXPATM=10;MAXPATN=MAXPATM;
      MAXMi=87; MAXNi=87;  {has to do with allocating memory}
var myport:GrafPort;        {Don't worry about any of this}

var MAXM, MAXN: integer;
     choose_Rect : array[1..MAXPATM,1..MAXPATN] of rect;
     Filled_Choice : array[1..MAXPATM,1..MAXPATN] of boolean;
     Rules : array[1..4,0..8] of rect;
     goRect, OKRect:rect;
     Rules_Choice : array[1..4,0..8] of boolean;
      mouseLoc: Point; {so we can use mouse & cursor positions to control}
    
type field = array [1..MAXMi,1..MAXMi] of integer;
var old_value, new_value: field; {more memory allocation stuff}
var choice:array [0..8] of char;
var p, m, n, i, j, count, sum_neighbors, sum_far_neighbors:integer;
var xd, yd, side:integer;
var ch: char;      {you have to name all the variables you will ever use}


procedure draw_Init_Pattern; {beginning of first procedure}
  begin
   for m:=1 to MAXPATM do   {sets left to right squares}
   begin
     for n:=1 to MAXPATN do  {sets top to bottom squares}
       begin  
         setrect(choose_Rect[m,n],150+m*Sq_Width,n*Sq_Width,150+m*Sq_Width+Sq_Width,n*Sq_Width+Sq_Width);
         framerect(choose_Rect[m,n]);                                  
        end;
    end;
     setrect(OKRect,230,220,280,250);framerect(OKRect);
      moveto(240, 240);drawstring(' OK ?');
  end;       {and this is the end of that first procedure}

procedure random_pattern; {another procedure, never actually used!!}
     begin;              {actually, maybe it is sometimes used}
      for m:= 2 to MAXM-1 do
         begin
           for n:= 2 to MAXN-1 do
             begin
              old_value[m,n]:=0;
             if random mod 10>5 then
                old_value[m,n]:=1;
              end;                
         end;                    
      end;  {The boundary of the end of that last procedure}


procedure draw_Rules_Pattern; {another procedure, that DOES get used}
begin
begin
   for m:=1 to 4 do
   begin
     for n:=1 to 9 do
      begin  
setrect(Rules[m,n], m*Sq_Width,n*Sq_Width, m*Sq_Width+Sq_Width,n*Sq_Width+Sq_Width);
     framerect(Rules[m,n]);
      end;
  end; {notice the somewhat different ways that the word "end" gets used}
end;
      setrect(goRect,30,220,80,250);framerect(goRect);
      moveto(40, 240);drawstring(' GO');
end;      {this is the end of the "draw_Rules_Pattern" procedure}

procedure Label_Rule_Chooser;  {start of yet another procedure}
begin
   moveto(10,12); drawstring('Empt ');
   moveto(40,12); drawstring('Same');
   moveto(70,12); drawstring('Fill');
   moveto(100,12); drawstring('Reverse');
  
   moveto(10,30); drawstring('0');
   moveto(10,50); drawstring('1');
   moveto(10,70); drawstring('2');
   moveto(10,90); drawstring('3');
   moveto(10,110); drawstring('4');
   moveto(10,130); drawstring('5');
   moveto(10,150); drawstring('6');
   moveto(10,170); drawstring('7');
   moveto(10,190); drawstring('8');
end; {the end of a procedure that definitely DOES get used}

procedure Fill_Current_Rules; {yet another procedure}
begin   {can you figure out what this one does?}
  for i:=0 to 8 do
   begin
    if choice[i]='e' then
          begin
            fillrect(rules[1,i+1],black);
            fillrect(rules[2,i+1],white);
            fillrect(rules[3,i+1],white);
            fillrect(rules[4,i+1],white);
          end;
    if choice[i]='s' then
          begin
            fillrect(rules[1,i+1],white);
            fillrect(rules[2,i+1],black);
            fillrect(rules[3,i+1],white);
            fillrect(rules[4,i+1],white);
          end;
    if choice[i]='f' then
          begin
            fillrect(rules[1,i+1],white);
            fillrect(rules[2,i+1],white);
            fillrect(rules[3,i+1],black);
            fillrect(rules[4,i+1],white);
          end;
    if choice[i]='r' then
          begin
            fillrect(rules[1,i+1],white);
            fillrect(rules[2,i+1],white);
            fillrect(rules[3,i+1],white);
            fillrect(rules[4,i+1],black);
          end;
       end;                        
end; {The last of this procedure, that I want you to try and recognize}

procedure choose_rules;
begin  
  repeat
    getmouse(mouseLoc);{asks the computer where the cursor currently is}
  if button then {and if the mouse button is pushed, does the following}
  begin {note how "begin" and "end" are like parentheses in multiplying}
    getmouse(mouseLoc);
      if (abs(mouseLoc.h-50)<50) and (abs(mouseloc.v-110)<90)  then
          begin
            i:= 1+ (mouseloc.v-20) div 20;
            if abs(mouseloc.h-30)<10 then begin
                choice[i-1]:='e';fillrect (Rules[1,i],black);
                fillrect (Rules[2,i],white);fillrect (Rules[3,i],white);
                draw_Rules_Pattern;
                end;
     if abs(mouseloc.h-50)<10 then begin
choice[i-1]:='s';fillrect (Rules[2,i],black);
fillrect (Rules[1,i],white);fillrect (Rules[3,i],white);fillrect (Rules[4,i],white);
          draw_Rules_Pattern;
      end;
       if abs(mouseloc.h-70)<10 then begin  
choice[i-1]:='f';fillrect (Rules[3,i], black);
fillrect (Rules[1,i],white);fillrect (Rules[2,i],white);fillrect (Rules[4,i],white);
            draw_Rules_Pattern;
        end;
             if abs(mouseloc.h-90)<10 then begin
                 choice[i-1]:='r';fillrect (Rules[4,i], black);
fillrect (Rules[1,i],white);fillrect (Rules[2,i],white);fillrect (Rules[3,i],white);
                 draw_Rules_Pattern;
                 end;

           if choice[0]='f' then begin choice[0]:='s';fillrect (Rules[2,1],black); fillrect (Rules[1,1],white);fillrect (Rules[3,1],white);
draw_Rules_Pattern;end;
           end;
       end;    
      
       if mouseloc.h>300 then random_pattern;
          {maybe we use that procedure after all!!}     
until ((abs(mouseloc.v-235))<15) and ((abs(mouseloc.h-55))<25) and button ;    
end;

procedure initial_pattern; var mm,nn: integer;
begin   {resets all values to zero=empty every time we start over}
        for m:= 1 to MAXM do
         begin
             for n:= 1 to MAXN do
             begin
                new_value[m,n]:= 0;
                old_value[m,n]:= 0;
              end;                
          end;        
  repeat
        getmouse(mouseLoc);  
        if button then
            begin
             getmouse(mouseLoc);
if   (abs(mouseLoc.h-150-MAXPATM*Sq_Width/2) and (abs(mouseloc.v-20-MAXPATM*Sq_Width/2)                  begin
                   m:=(mouseloc.h-170) div 20 +1 + round(MAXM/2) ;
                   n:=(mouseloc.v-20 ) div 20 +1 + round(MAXN/2) ;  {writeln('m = ',m,'n = ',n);}
{this previous comment was left over from debugging, when I first wrote it}
                   old_value[m,n]:=(old_value[m,n]+ 1) mod 2;
if old_value[m,n]=1 then fillrect(choose_Rect[m-round(MAXM/2),n-round(MAXN/2)],black);
if old_value[m,n]=0 then fillrect(choose_Rect[m-round(MAXM/2),n-round(MAXN/2)],white);
                  draw_Init_Pattern;
               end; end;
until ((abs(mouseloc.v-235))<15) and ((abs(mouseloc.h-255))<25) and button;

eraserect(OKRect);    
setrect(OKRect,230,220,280,250);framerect(OKRect);
moveto(240, 240);drawstring(' DONE');
moveto(75,270);drawstring('Now choose your rules on the grid at the left');
end;   {That procedure sure was long and confusing, wasn't it?}

    procedure Pen_draw_dots;
    begin
    {side := 200 div MAXM;}
               PenSize(SIDE-1,SIDE);            
               moveto(round(xd+SIDE*m),   yd+SIDE*n-130);
               Line(1,0);          
    end;
    
   procedure sum_neighbor_values; {Figure out why I wrote it this way!}
   begin;                        {I mean part just after "sum_neighbors"}
      for m:= 2 to MAXM-1 do
         begin
           for n:= 2 to MAXN-1 do
             begin
sum_neighbors:=old_value[m-1,n-1] + old_value[m,n-1] + old_value[m+1,n-1]+
               old_value[m-1,n ]                     + old_value[m+1,n  ]+
               old_value[m-1,n+1] + old_value[m,n+1] + old_value[m+1,n+1] ;
    
                         for j:= 0 to 8 do
                           begin
                             if sum_neighbors= j then
                             begin
                    if Choice[j]= 'e' then new_value[m,n]:=0;
                    if Choice[j]= 'f' then new_value[m,n]:=1;
                    if Choice[j]= 's' then new_value[m,n]:= old_value[m,n];
                               if Choice[j]= 'r' then
                                   begin
                      if old_value[m,n]=0 then new_value[m,n]:=1;
                      if old_value[m,n]=1 then new_value[m,n]:=0;
                                   end;
                             end;
                           end;
             end;
        end;
    end;  
  
   procedure sum_farther_neighbors; {we don't use this}
   begin;       {but can you figure it out?}
      for m:= 3 to MAXM-2 do
         begin
           for n:= 3 to MAXN-2 do
             begin
sum_far_neighbors:=  old_value[m-1,n-2] +  old_value[m,n-2] + old_value[m+1,n-2]+
                        old_value[m-2,n-1 ]                     + old_value[m+2,n-1  ]+
                        old_value[m-2,n ]                     + old_value[m+2,n  ]+
                        old_value[m-2,n+1 ]                     + old_value[m+2,n+1  ]+
                        old_value[m-1,n+2]  + old_value[m,n+2]   + old_value[m+1,n+2] ;
    
                         for j:= 0 to 8 do
                           begin
                             if sum_neighbors= j then
                             begin
                      if Choice[j]= 'e' then new_value[m,n]:=0;
                      if Choice[j]= 'f' then new_value[m,n]:=1;
                      if Choice[j]= 's' then new_value[m,n]:= old_value[m,n];
               if Choice[j]= 'r' then
                       begin
                    if old_value[m,n]=0 then new_value[m,n]:=1;
                     if old_value[m,n]=1 then new_value[m,n]:=0;
                        end;
                             end;
                           end;
             end;
        end;
    end;  

   procedure old_new;  {we have to do this each cycle, can you see why?}
     begin;
      for m:= 2 to MAXM-1 do
         begin
           for n:= 2 to MAXN-1 do
             begin
                old_value[m,n]:=new_value[m,n]
              end;                
         end;                    
      end;   {The end of a rather short procedure, isn't it?}
  
   procedure plot_pattern; {This makes the pattern appear on the screen!}
    begin;
       for m:= 2 to MAXM-1 do
         begin
           for n:= 2 to MAXN-1 do
             begin
              if old_value[m,n]= 0 then PenPat(white);
              if old_value[m,n]= 1 then PenPat(black);
              Pen_draw_dots {NEW_draw_dots} {draw_dots};          
             end;                
          end;                    
   end;
  
   procedure summarize_rules;
    begin;
    Writeln('           ');  Writeln('           ');
    for j:=0 to 8 do
    begin
      writeln (' ',j,' ',Choice[j]);
      end;
    end;  

begin {main program}  {AT LONG LAST!!  THE ACTUAL PROGRAM!}

writeln('How big a field do you want?  Type a number from 10 to 87.');
readln(MAXM); MAXN:=MAXM;
writeln('MAXN= ',MAXN);

InitGraf(@thePort);OpenPort(@MyPort);  {never mind about this stuff here}
FillRect(myport.portbits.Bounds,white);initCursor;

textfont(monaco); textsize(9); textface([]);

side:=350 div MAXM;

draw_Init_Pattern; {the names of procedures that you defined above}
draw_Rules_Pattern; {become like magic incantations!  In the program itself}
       {you just say the name, and the computer does whatever you told it to}
Label_Rule_Chooser;   initial_pattern;   choose_rules;

       count:= 0; clearscreen;xd:=120; yd:=110;
       summarize_rules; plot_pattern;
      
       while not keypressed do
         begin;
   {writeln('Just about to sum neighbors');} {left over from debugging}
            sum_neighbor_values;  
            old_new;
{writeln('Just about to plot patterns'); } {left over from debugging}
            plot_pattern;        
           if button {and (mouseloc.h<10)} then  
           {if (mouseloc.h>350) then
            random_pattern;} {take off the brackets & you can use this}            
            begin
             InitGraf(@thePort);OpenPort(@MyPort);
             FillRect(myport.portbits.Bounds,white);initCursor;
             textfont(monaco);textsize(9);textface([]);
             Label_Rule_Chooser; Fill_Current_Rules;
             choose_rules;
           end;      
         end;
readln;  {gives you a last look at the pattern, if you want to copy it}
{It causes the last pattern to stay on the screen until you type "return"}

end.   {this means the end of the whole program}

{please notice what happens when "end" has a period after it,
instead of a semicolon!}     {a BIG difference}