home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / LIFE4.ZIP / LIFE4.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  17.0 KB  |  551 lines

  1. program life (input, output, config);
  2.    { Simulation of Conway's game of Life on a bounded grid
  3.    { Version 3.
  4.    { This version employs
  5.    {    direct cursor addressing, and
  6.    {    doubly linked lists, with header cells.
  7.    { It uses a data file called config.dat which contains the coordinates
  8.    { of the starting life configuration.  A sample file would be
  9.    10 10
  10.    10 11
  11.    10 12
  12.  
  13.    }
  14. const
  15.         T_DELAY = 5;                 { delay constant }
  16.         SUB     = 26;                { ASCII CODE, clears screen    }
  17.         RS      = 30;                { ASCII CODE, home up cursor   }
  18.         ESC     = 27;                { ASCII CODE, escape character }
  19.         EQUAL   = 61;                { ASCII CODE, equals character }
  20.  
  21.         MAXROW  = 24;
  22.         MAXCOL  = 79;
  23. type
  24.         row      = 1..MAXROW;
  25.         col      = 1..MAXCOL;
  26.         status   = (dead, alive);
  27.         grid     = array[row,col] of status;
  28.         intarray = array[row,col] of integer;
  29.  
  30.         coord  = record         { Typical element specified as x,y coordinates }
  31.                  x : row;
  32.                  y : col;
  33.                  end;
  34.         list    = ^element;
  35.         ptr     = ^element;
  36.         element = record                        { Typical list element }
  37.                   inlink  : ptr;
  38.                   entry   : coord;
  39.                   outlink : ptr;
  40.                  end;
  41.  
  42. var
  43.         config: text;         { file for reading initial configuration }
  44.  
  45. { - - - - - - - - - - - - END of GLOBAL DEFINITIONS - - - - - - - - - - - - }
  46.  
  47. procedure freecell (cell : ptr);
  48. begin
  49. mark (cell);
  50. release (cell);
  51. end; {freecell}
  52.  
  53. procedure stop_with_message ( message : integer );
  54.  
  55. begin
  56.   case message of
  57.   1  : write ('Must have a positive number of generations ');
  58.   2  : write ('Illegal input');
  59.   3  : write ('Error in birtheffects');
  60.   end; {case }
  61.   halt;
  62.  
  63. end; { stop with message }
  64.  
  65. procedure open_file;
  66. begin
  67. assign (config, 'config.dat');
  68. reset  (config);
  69. end; { open_file }
  70.  
  71. { - - - - - - - - - - - - - END of Machine Dependent Code - - - - - - - - - }
  72. procedure clearscreen;
  73.     { clear entire screen }
  74. begin
  75. write(chr(SUB));
  76. end; 
  77.  
  78. procedure home;
  79.    { Move the cursor to the "home" position (1,1) }
  80. begin
  81. write(chr(RS));
  82. end; 
  83.  
  84. procedure gotoxy ( x,y : integer );
  85.    { this will put the cursor at the specified screen position with
  86.    { 1,1 at the top left and 24,80 at the lower right. for the ADM 5
  87.    }
  88.    var  rowchar, colchar : char;
  89. begin
  90. rowchar := chr ( x + 31 );
  91. colchar := chr ( y + 31 );
  92. write (chr(ESC), chr(EQUAL), rowchar, colchar);
  93. end; 
  94.  
  95. { - - - - - - - - - - END of DIRECT SCREEN CONTROL - - - - - - - - - - - }
  96.  
  97. procedure add (L:ptr; point : coord);
  98.   { Add the specified element 'point' to the list 'L'
  99.   { NOTE: L is known to have a header cell.
  100.   }
  101.   var
  102.     cell,
  103.     temp   : ptr;
  104.  
  105. begin { add }
  106. new (cell);
  107. if L^.outlink <> nil
  108.   then begin
  109.        temp := L^.outlink;
  110.        temp^.inlink := cell;
  111.        end
  112.   else temp := nil;
  113.  
  114. L^.outlink := cell;
  115. cell^.inlink := L;
  116. cell^.outlink := temp;
  117. cell^.entry.x := point.x;
  118. cell^.entry.y := point.y;
  119. end; { add }
  120.  
  121. procedure delete (var element : ptr);
  122.   { delete the indicated "element" from any list it is in.
  123.   { "element" is left pointing at the "next" element in the list that replaced
  124.   { it, if any.
  125.   }
  126.   var
  127.     temp_front,   { points to next cell }
  128.     temp_back     { points to previous cell }
  129.                 : ptr;
  130.  
  131. begin { delete }
  132. if element^.outlink <> nil
  133.   then begin
  134.        temp_front := element^.outlink;
  135.        temp_front^.inlink := element^.inlink;
  136.        temp_back := element^.inlink;
  137.        temp_back^.outlink := temp_front;
  138.        freecell (element);
  139.        element := temp_front;
  140.        end { then }
  141.   else begin
  142.        temp_back := element^.inlink;
  143.        temp_back^.outlink := nil;
  144.        freecell (element);
  145.        element := nil;
  146.        end; { else }
  147.  
  148. end; { delete }
  149.  
  150. function first (list : ptr) : ptr;
  151.   { Returns a pointer to the first element in a list. }
  152. begin
  153. if list <> nil
  154.    then first := list^.outlink
  155.    else first := nil;
  156. end;
  157.  
  158. function next (element : ptr) : ptr;
  159.   { Given a pointer to an "element" in a list, this function returns 
  160.   { a pointer to the next element.
  161.   }
  162. begin
  163. next := element^.outlink;
  164. end;
  165.  
  166. procedure emptylist (var x:ptr);
  167.   { Make "x" an empty list with zero elements 
  168.   { consisting of a single "header" cell.
  169.   }
  170.  
  171. begin { emptylist }
  172. new (x);
  173. x^.outlink := nil;
  174. x^.inlink  := nil;
  175.  
  176. end; { emptylist }
  177.  
  178. procedure copylist (var x,y : ptr);
  179.   { Simply make "y" a duplicate of "x"
  180.   { That is x -> y, except that first we must return any storage that
  181.   { may have been associated with y.
  182.   }
  183.   var  cell, oldcell : ptr;
  184. begin
  185. cell := y;
  186. while cell <> nil do
  187.         begin                   { Dispose of all cells formerly in the list y }
  188.         oldcell := cell;
  189.         cell := next(cell);
  190.         freecell (oldcell);
  191.         end;
  192. y := x;
  193. end;
  194.  
  195. { - - - - - - - - - - END of low-level LIST routines - - - - - - - - - - }
  196.  
  197. function xmin (x : row) : row;
  198. begin
  199. if x = 1
  200.    then xmin := 1
  201.    else xmin := x-1;
  202. end;
  203.  
  204. function xmax (x : row) : row;
  205. begin
  206. if x = MAXROW
  207.    then xmax := MAXROW
  208.    else xmax := x+1;
  209. end;
  210.  
  211. function ymin (y : col) : col;
  212. begin
  213. if y = 1
  214.    then ymin := 1
  215.    else ymin := y-1;
  216. end;
  217.  
  218. function ymax (y : col) : col;
  219. begin
  220. if y = MAXCOL
  221.    then ymax := MAXCOL
  222.    else ymax := y+1;
  223. end;
  224.  
  225. function neighborcount(var cellmap: grid; i: row; j: col): integer;
  226.   { This procedure counts the number of alive neighbors of 
  227.   { cellmap[i,j] and returns this number as its function value.
  228.   }
  229. var
  230.    x,
  231.    xlow, xhigh: row;            { limits for row loop }
  232.    y, 
  233.    ylow, yhigh: col;            { limits for column loop }
  234.    count: integer; 
  235. begin                           { Handle boundary elements differently }
  236. xlow  := xmin(i);
  237. xhigh := xmax(i);
  238. ylow  := ymin(j);
  239. yhigh := ymax(j);
  240.  
  241. count := 0;
  242. for x := xlow to xhigh do
  243.      for y := ylow to yhigh do
  244.          if (cellmap[x,y] = alive) 
  245.             then count := count + 1;
  246. if (cellmap[i,j] = alive) 
  247.    then count := count - 1;
  248. neighborcount := count;
  249. end; 
  250.  
  251. procedure initialize (var universe:grid; var numgenerations:integer;
  252.                         var births, deaths:ptr;
  253.                         var no_neighbors:intarray);
  254.   { Create an initial universe for the game of LIFE, in which
  255.   { all cells are "dead", except for those specified by the user
  256.   { (in the file "config").
  257.   { Then examine this initial configuration and determine which
  258.   { cells should be "born" and should "die" in the next generation.
  259.   { These are put in the lists "births" and "deaths" respectively.
  260.   {
  261.   { Note that "initialize" does two things, it initializes the
  262.   { actual universe, and it initializes the first "increment".
  263.   }
  264. var
  265.    cell : coord;
  266.    x, y   : integer;                     { coordinates of cell }
  267.    lineno : integer;
  268.  
  269. begin
  270. writeln ('This program is a simulation of the game of Life');
  271. write   ('Enter number of generations to run - ');
  272. readln (numgenerations);
  273. if (numgenerations < 0)
  274.    then stop_with_message (1);
  275. for x := 1 to MAXROW do
  276.      for y := 1 to MAXCOL do
  277.          begin
  278.          universe[x,y] := dead;
  279.          no_neighbors[x,y] := 0;
  280.          end;
  281.  
  282. {  read file that contains initial configuration }
  283.  
  284. emptylist (births);
  285. emptylist (deaths);
  286. clearscreen;
  287. home;
  288. write ('The map at generation    0  ');
  289. lineno := 0;
  290.  
  291. open_file;
  292. while (not eof(config)) do
  293.       begin
  294.       lineno := lineno +1;
  295.       readln (config, x, y);
  296.       if ((x >= 1) and (x <= MAXROW) and (y >= 1) and (y <= MAXCOL)) 
  297.          then begin
  298.               universe[x,y] := alive;
  299.               gotoxy (x,y);
  300.               write ('*');
  301.               end
  302.          else begin
  303.               writeln ('Input values are not within range');
  304.               writeln ('At line ', lineno:3, ' values are ', x:2, ',', y:2);
  305.               stop_with_message(2);
  306.               end;
  307.       end; 
  308. for x := 1 to MAXROW do
  309.       for y := 1 to MAXCOL do
  310.             begin
  311.             no_neighbors[x,y] := neighborcount (universe, x, y);
  312.             if (universe[x,y] = alive) and ((no_neighbors[x,y] < 2)
  313.                                              or (no_neighbors[x,y] > 3))
  314.                then begin
  315.                     cell.x := x;
  316.                     cell.y := y;
  317.                     add (deaths, cell);
  318.                     end;
  319.             if (universe[x,y] = dead) and (no_neighbors[x,y] = 3)
  320.                then begin
  321.                     cell.x := x;
  322.                     cell.y := y;
  323.                     add (births, cell);
  324.                     end;
  325.             end;
  326. end;
  327.  
  328. procedure writemap (var universe: grid; generation: integer;
  329.                     var births, deaths: ptr);
  330.   { Display the current generation of a LIFE sequence by putting a * where
  331.   { a cell is borne and a blank where a cell dies.  This involves marching
  332.   { down both the birth and death lists and getting the coordinates and
  333.   { printing the character.  }
  334.   const
  335.     full = '*';
  336.     empty = ' ';
  337.   var
  338.     cell : ptr;
  339.     i,
  340.     j    : integer;
  341.  
  342. begin
  343. home;
  344. write ('The map at generation', generation:5 );
  345. cell := first (births);
  346. while cell <> nil do
  347.   begin { while }
  348.   with cell^.entry do
  349.     gotoxy (x,y);
  350.   write (full);
  351.   cell := next (cell);
  352.   end; { while }
  353.  
  354. cell := first (deaths);
  355. while cell <> nil do
  356.   begin { while }
  357.   with cell^.entry do
  358.     gotoxy (x,y);
  359.   write (empty);
  360.   cell := next (cell);
  361.   end; { while }
  362.  
  363. gotoxy (1,1);
  364. for i := 1 to t_delay do
  365.   for j := 1 to t_delay do
  366.      gotoxy (1,1);
  367. end; { writemap }
  368.  
  369. procedure thisgeneration (var map:grid; var births,deaths:ptr;
  370.                           var no_neighbors:intarray);
  371.   { Given the "map" of the universe after the previous generation,
  372.   { and lists of the births and deaths that should occur, this
  373.   { procedure updates the map to create the "new generation"
  374.   }
  375.  
  376.         procedure givebirth (var map:grid; var births:ptr;
  377.                              var no_neighbors:intarray);
  378.           { This is the procedure 'vivify' in Kruse, but now
  379.           { it 'merely' works through "births" eliminating duplicates!
  380.           }
  381.         var  x, y : integer;
  382.              baby : ptr;
  383.         begin
  384.         baby := first(births);          { Get the first baby on births list }
  385.         while baby <> nil do
  386.                 begin
  387.                 x := baby^.entry.x;
  388.                 y := baby^.entry.y;
  389.                 if (map[x,y] = dead) and (no_neighbors[x,y] = 3)
  390.                    then begin
  391.                         map[x,y] := alive;
  392.                         baby := next(baby);
  393.                         end
  394.                    else begin           { Spurious "birth entry" delete it }
  395.                         delete (baby);
  396.                         end;
  397.                 end;
  398.         end;    { givebirth }
  399.  
  400.         procedure kill (var map:grid; var deaths:ptr;
  401.                         var no_neighbors:intarray);
  402.           { This is the procedure 'kill' in Kruse }
  403.         var  x, y : integer;
  404.              corpse : ptr;
  405.         begin
  406.         corpse := first(deaths);        { Get the first corpse on deaths list }
  407.         while corpse <> nil do
  408.                 begin
  409.                 x := corpse^.entry.x;
  410.                 y := corpse^.entry.y;
  411.                 if (map[x,y] = alive) and ((no_neighbors[x,y] <> 3)
  412.                                       and  (no_neighbors[x,y] <> 2))
  413.                    then begin
  414.                         map[x,y] := dead;
  415.                         corpse := next(corpse);
  416.                         end
  417.                    else begin           { Spurious "death entry" delete it }
  418.                         delete (corpse);
  419.                         end;
  420.                 end;
  421.         end;    { kill }
  422.  
  423. begin
  424. givebirth (map, births, no_neighbors);    { Reflect births and deaths     }
  425. kill (map, deaths, no_neighbors);         { from last generation          }
  426. end;
  427.  
  428. procedure nextgeneration (var map : grid; var births,deaths:ptr;
  429.                           var no_neighbors:intarray);
  430.   { Given a "map" of the universe and two list of the "births" and 
  431.   { "deaths" that were recorded in the last generation, this procedure
  432.   { first creates lists of the births and deaths that should occur in
  433.   { the next_generation, and then updates "map" to reflect them.
  434.   }
  435.    var  
  436.         newbirths, newdeaths : ptr;
  437.  
  438.         procedure birtheffects (var universe:grid; births:ptr;
  439.                                 var nextbirths, nextdeaths:ptr;
  440.                                 var no_neighbors:intarray);
  441.            { This is the procedure "addneighbors" in Kruse }
  442.         var   cell : ptr;
  443.               i, xlow, xhigh : row;
  444.               j, ylow, yhigh : col;
  445.               nbr : coord;
  446.         begin
  447.         cell := first(births);
  448.         while cell <> nil do
  449.                 with cell^.entry do
  450.                 begin           { Determine the limits of the neighborhood }
  451.                 xlow  := xmin(x);
  452.                 xhigh := xmax(x);
  453.                 ylow  := ymin(y);
  454.                 yhigh := ymax(y);
  455.                 for i := xlow to xhigh do
  456.                         for j := ylow to yhigh do
  457.                                 if (i <> x) or (j <> y) 
  458.                                    then begin
  459.                                         nbr.x := i;
  460.                                         nbr.y := j;
  461.                                         no_neighbors[i,j] := no_neighbors[i,j]+1;
  462.                                         case no_neighbors[i,j] of
  463.                                           0: stop_with_message(3);
  464.                                           1,2: ;
  465.                                           3: if map[i,j] = dead 
  466.                                                 then add (nextbirths, nbr);
  467.                                           4: if map[i,j] = alive
  468.                                                 then add (nextdeaths, nbr);
  469.                                           5,6,7,8: ;
  470.                                           end;
  471.                                         end;
  472.                 cell := next(cell);
  473.                 end;
  474.         end;    { of birtheffects }
  475.  
  476.         procedure deatheffects (var universe:grid; deaths:ptr;
  477.                                 var nextbirths, nextdeaths:ptr;
  478.                                 var no_neighbors:intarray);
  479.            { This is the procedure "subtractneighbors" in Kruse }
  480.         var   cell : ptr;
  481.               i, xlow, xhigh : row;
  482.               j, ylow, yhigh : col;
  483.               nbr : coord;
  484.         begin
  485.         cell := first(deaths);
  486.         while cell <> nil do
  487.                 with cell^.entry do
  488.                 begin           { Determine the limits of the neighborhood }
  489.                 xlow  := xmin(x);
  490.                 xhigh := xmax(x);
  491.                 ylow  := ymin(y);
  492.                 yhigh := ymax(y);
  493.                 for i := xlow to xhigh do
  494.                         for j := ylow to yhigh do
  495.                                 if (i <> x) or (j <> y) 
  496.                                    then begin
  497.                                         nbr.x := i;
  498.                                         nbr.y := j;
  499.                                         no_neighbors[i,j] := no_neighbors[i,j]-1;
  500.                                         case no_neighbors[i,j] of
  501.                                           0,1: if map[i,j] = alive
  502.                                                   then add (nextdeaths, nbr);
  503.                                           2: ;
  504.                                           3: if map[i,j] = dead 
  505.                                                 then add (nextbirths, nbr);
  506.                                           4,5,6,7,8: ;
  507.                                           end;
  508.                                         end;
  509.                 cell := next(cell);
  510.                 end;
  511.         end;    { of deatheffects }
  512.  
  513. begin   { - - - - - - - BODY OF NEXTGENERATION - - - - - - - - }
  514. emptylist (newbirths);
  515. emptylist (newdeaths);
  516. birtheffects (map, births, newbirths, newdeaths, no_neighbors);
  517. deatheffects (map, deaths, newbirths, newdeaths, no_neighbors);
  518. copylist (newbirths, births);
  519. copylist (newdeaths, deaths);
  520. end;
  521.  
  522. procedure main;
  523.   {  This "main" procedure, drives the entire simulation of 
  524.   {  the game of LIFE
  525.   }
  526. var
  527.       universe : grid;
  528.       no_neighbors : intarray;
  529.       generation, lastgeneration: integer;
  530.       births, deaths : ptr;
  531.       next : char;
  532. begin 
  533. clearscreen;
  534. home;
  535. emptylist (births);
  536. emptylist (deaths);
  537. initialize (universe, lastgeneration, births, deaths, no_neighbors);
  538. generation := 0;
  539. for generation := 1 to lastgeneration do
  540.      begin
  541.      gotoxy (2,1);
  542.      thisgeneration (universe, births, deaths, no_neighbors);
  543.      writemap (universe, generation, births, deaths);
  544.      nextgeneration (universe, births, deaths, no_neighbors);
  545.      end;
  546. end;
  547.  
  548. begin           { Dummy "program" that invokes "main" }
  549. main;
  550. end.
  551.