home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / turbopas / tp / utl2 / life.pzs / LIFE.PAS
Pascal/Delphi Source File  |  1994-07-23  |  20KB  |  692 lines

  1. Program Life;
  2.   {[A+,T=3] Instructions to PasMat.}
  3.  
  4.   {$C-  <-- These are instructions   }
  5.   {$I-  <-- from me to optimize the  }
  6.   {$W2  <-- compiler and trap ^C,    }
  7.   {$X+  <-- for Turbo Pascal.        }
  8.  
  9. {
  10.                                 L   I   F   E
  11.                                  Version 2.0
  12.  
  13.     This program is a simulation of cell life on a 2 dimensional board.
  14.  This version is written in Turbo Pascal, for either the IBM-PC, and
  15.  compatables, or CP/M-80 mathines, and will run on both without any
  16.  changes, other than re-compiling the source.  It is highly revised
  17.  version of another public domain LIFE program also written in Turbo
  18.  Pascal for CP/M-80 machines.  Some parts of the source code are Turbo
  19.  Pascal specific, like the KeyPressed, Kbd, GotoXY(), and ClrScr, but
  20.  can easily be changed to any other Pascal compiler.  I have ported
  21.  version from an IBM main frame to a DEC Rainbow, so it shouldn't be
  22.  difficult.  If you have any questions or comments, please feel free
  23.  to call my RCP/M at (312) 386-9271.
  24.  
  25.                                                   Thank you,
  26.  
  27.  
  28.                                                   Cyrus Patel
  29.                                           SYSOP - The Master Silicone
  30. }
  31.  
  32.    Const
  33.       Height = 23;
  34.       Width = 60;
  35.       MinBound = - 1;
  36.       Lively = '+';
  37.       Deadly = ' ';
  38.  
  39.    Type
  40.       State = (Alive, Dead);
  41.       Cell =
  42.          Record
  43.             LooksLikeItIs: State;
  44.             Nearby: Integer;
  45.          End;
  46.       Edges =
  47.          Record
  48.             Left, Right, Top, Bottom: Integer
  49.          End;
  50.       ScreenLine = String [80];
  51.  
  52.    Var
  53.       Ch: Char;
  54.       Edge: Edges;
  55.       Births, Deaths, Generation, Pause, Population: Integer;
  56.       Board: Array [MinBound..Width, MinBound..Height] of Cell;
  57.  
  58.  
  59.    Function Yes(Line: ScreenLine): Boolean;
  60.  
  61.       Var
  62.          Ch: Char;
  63.  
  64.       Begin
  65.          Write(Line, '? ');
  66.          Repeat
  67.             Read(Kbd, Ch)
  68.          Until UpCase(Ch) in ['Y', 'N'];
  69.          Yes := UpCase(Ch) = 'Y'
  70.       End;
  71.  
  72.  
  73.    Function Min(a, b: Integer): Integer;
  74.  
  75.       Begin
  76.          If a <= b then
  77.             Min := a
  78.          else
  79.             Min := b
  80.       End;
  81.  
  82.  
  83.    Function Max(a, b: Integer): Integer;
  84.  
  85.       Begin
  86.          If a >= b then
  87.             Max := a
  88.          else
  89.             Max := b
  90.       End;
  91.  
  92.  
  93.    Procedure ResetEdges;
  94.  
  95.       Begin
  96.          With Edge do
  97.             Begin
  98.             Top := Height - 1;
  99.             Right := MinBound + 1;
  100.             Left := Width - 1;
  101.             Bottom := MinBound + 1
  102.             End
  103.  
  104.       End;
  105.  
  106.  
  107.    Procedure Instructions;
  108.  
  109.       Var
  110.          Ch: Char;
  111.  
  112.  
  113.       Procedure Lecture_On_Life;
  114.  
  115.          Begin
  116.             ClrScr;
  117.             GotoXY(29, 2);
  118.             WriteLn('Instructions for LIFE.');
  119.             WriteLn;
  120.             WriteLn;
  121.             Write(
  122.                  'LIFE simulates the growth of a colony of animalcules in a "'
  123.                   );
  124.             WriteLn(Width: 1, ' by ', Height: 1, ' World".');
  125.             WriteLn;
  126.             WriteLn(
  127.        'Whether a cell is born, lives or dies depends on the number of living'
  128.                     );
  129.             WriteLn(
  130.     'animalcules near by.  If a cell is empty and has exactly 3 neighbors, it'
  131.                     );
  132.             WriteLn(
  133.          'will be born in the next generation.  If it is alive and has 2 or 3'
  134.                     );
  135.             WriteLn(
  136.      'neighbors, it will stay alive.  Otherwise, it either dies of loneliness'
  137.                     );
  138.             WriteLn('or suffocates from overcrowding.');
  139.             WriteLn;
  140.             WriteLn(
  141.         'You type in the starting pattern, going to the location of where you'
  142.                     );
  143.             WriteLn(
  144.        'want to place a cell.  Then press either the SPACE bar, or 5 to place'
  145.                     );
  146.             WriteLn(
  147.         'or take away a seed. To begin the generations press the ESC key. You'
  148.                     );
  149.             WriteLn(
  150.      'can also save and load ''Life'' files from disk.  Press ^L to Load, and'
  151.                     );
  152.             WriteLn(
  153.          '^S to Save. You can also interupt the generations by hitting RETURN'
  154.                     );
  155.             WriteLn('and changing the screen, loading and also saving it.');
  156.             WriteLn;
  157.             WriteLn(
  158.    'The ''<'' key speeds things up a bit, the ''>'' key slows things down. If'
  159.                     );
  160.             WriteLn(
  161.        'the good old days at M.I.T., this game was played with pencil & graph'
  162.                     );
  163.             WriteLn('paper.');
  164.             GotoXY(27, 24);
  165.             Write('Press any key to continue.');
  166.             While KeyPressed do
  167.                Read(Kbd, Ch);
  168.             Read(Kbd, Ch)
  169.          End;
  170.  
  171.       Begin
  172.          ClrScr;
  173.          GotoXY(35, 3);
  174.          Write('The Game of');
  175.          GotoXY(34, 5);
  176.          Write('L   I   F   E');
  177.          GotoXY(35, 7);
  178.          Write('Version 2.0');
  179.          GotoXY(21, 10);
  180.          If Yes('Would you like instructions for Life') then
  181.             Lecture_On_Life;
  182.          ClrScr
  183.       End;
  184.  
  185.  
  186.    Procedure Initialize;
  187.  
  188.       Var
  189.          Across, Down: Integer;
  190.  
  191.       Begin
  192.          For Across := MinBound to Width do
  193.             For Down := MinBound to Height do
  194.                With Board[Across, Down] do
  195.                   Begin
  196.                   LooksLikeItIs := Dead;
  197.                   Nearby := 0
  198.                   End;
  199.  
  200.          ResetEdges
  201.       End;
  202.  
  203.  
  204.    Procedure Limits(Across, Down: Integer);
  205.  
  206.       Begin
  207.          With Edge do
  208.             Begin
  209.             Left := Min(Left, Across);
  210.             Right := Max(Right, Across);
  211.             Top := Min(Top, Down);
  212.             Bottom := Max(Bottom, Down)
  213.             End
  214.  
  215.       End;
  216.  
  217.  
  218.    Procedure ClearNearby;
  219.  
  220.       Var
  221.          Across, Down: Integer;
  222.  
  223.       Begin
  224.          With Edge do
  225.             For Across := Left - 1 to Right + 1 do
  226.                For Down := Top - 1 to Bottom + 1 do
  227.                   Board[Across, Down].Nearby := 0
  228.  
  229.       End;
  230.  
  231.  
  232.    Procedure CountNeighbors;
  233.  
  234.       Var
  235.          Across, DeltAcross, DeltaDown, Down: Integer;
  236.  
  237.       Begin
  238.          ClearNearby;
  239.          With Edge do
  240.             For Across := Left - 1 to Right + 1 do
  241.                For Down := Top - 1 to Bottom + 1 do
  242.                   If Board[Across, Down].LooksLikeItIs = Alive then
  243.                      For DeltAcross := - 1 to 1 do
  244.                         For DeltaDown := - 1 to 1 do
  245.                            With Board[Across + DeltAcross, Down +
  246.                                 DeltaDown] do
  247.                               Nearby := Succ(Nearby)
  248.  
  249.       End;
  250.  
  251.  
  252.    Procedure UpDate;
  253.  
  254.       Var
  255.          LocalEdge: Edges;
  256.          Across, Down: Integer;
  257.  
  258.       Begin
  259.          Births := 0;
  260.          Deaths := 0;
  261.          LocalEdge := Edge;
  262.          ResetEdges;
  263.          For Across := Max(MinBound + 1, LocalEdge.Left - 1) to Min(Width - 1,
  264.            LocalEdge.Right + 1) do
  265.             For Down := Max(MinBound + 1,
  266.               LocalEdge.Top - 1) to Min(Height - 1, LocalEdge.Bottom + 1) do
  267.                With Board[Across, Down] do
  268.                   Case LooksLikeItIs of
  269.                      Dead:
  270.                         If Nearby = 3 then
  271.                            Begin
  272.                            LooksLikeItIs := Alive;
  273.                            GotoXY(Across + 1, Down + 1);
  274.                            Write(Lively);
  275.                            Limits(Across, Down);
  276.                            Births := Births + 1
  277.                            End;
  278.                      Alive:
  279.                         If (Nearby = 3) or (Nearby = 4) then
  280.                            Limits(Across, Down)
  281.                         else
  282.                            Begin
  283.                            LooksLikeItIs := Dead;
  284.                            GotoXY(Across + 1, Down + 1);
  285.                            Write(Deadly);
  286.                            Deaths := Deaths + 1
  287.                            End
  288.                      End;
  289.  
  290.          Generation := Generation + 1;
  291.          Population := Population + Births - Deaths;
  292.          GotoXY(Width + 15, 16);
  293.          Write(Generation: 5);
  294.          GotoXY(Width + 15, 17);
  295.          Write(Population: 5);
  296.          GotoXY(Width + 15, 18);
  297.          Write(Births: 5);
  298.          GotoXY(Width + 15, 19);
  299.          Write(Deaths: 5)
  300.       End;
  301.  
  302.  
  303.    Procedure DrawScreen;
  304.  
  305.       Var
  306.          Index: Integer;
  307.  
  308.       Begin
  309.          GotoXY(Width + 1, 1);
  310.          Write('+');
  311.          For Index := 2 to Height do
  312.             Begin
  313.             GotoXY(Width + 1, Index);
  314.             Write('|')
  315.             End;
  316.          GotoXY(1, Height + 1);
  317.          For Index := 1 to Width do
  318.             Write('-');
  319.          Write('+');
  320.          GotoXY(Width + 4, 1);
  321.          Write('The Game of Life.');
  322.          GotoXY(Width + 7, 2);
  323.          Write('Version 2.0');
  324.          GotoXY(Width + 11, 3);
  325.          Write('by');
  326.          GotoXY(Width + 7, 4);
  327.          Write('Cyrus Patel');
  328.          GotoXY(Width + 6, 6);
  329.          Write('^     ^     ^');
  330.          GotoXY(Width + 7, 7);
  331.          Write('\    |    /');
  332.          GotoXY(Width + 8, 8);
  333.          Write('\   |   /');
  334.          GotoXY(Width + 9, 9);
  335.          Write('7  8  9');
  336.          GotoXY(Width + 4, 10);
  337.          Write('<--- 4  *  6 --->');
  338.          GotoXY(Width + 9, 11);
  339.          Write('1  2  3');
  340.          GotoXY(Width + 8, 12);
  341.          Write('/   |   \');
  342.          GotoXY(Width + 7, 13);
  343.          Write('/    |    \');
  344.          GotoXY(Width + 6, 14);
  345.          Write('v     v     v');
  346.          GotoXY(Width + 4, 16);
  347.          Write('Generation:');
  348.          GotoXY(Width + 15, 16);
  349.          Write(0: 5);
  350.          GotoXY(Width + 4, 17);
  351.          Write('Population:');
  352.          GotoXY(Width + 15, 17);
  353.          Write(0: 5);
  354.          GotoXY(Width + 8, 18);
  355.          Write('Births:');
  356.          GotoXY(Width + 15, 18);
  357.          Write(0: 5);
  358.          GotoXY(Width + 8, 19);
  359.          Write('Deaths:');
  360.          GotoXY(Width + 15, 19);
  361.          Write(0: 5);
  362.          GotoXY(Width + 9, 20);
  363.          Write('Speed:');
  364.          GotoXY(Width + 15, 20);
  365.          Write(0: 5);
  366.          GotoXY(Width + 5, 23);
  367.          Write('ESC to     t.')
  368.       End;
  369.  
  370.  
  371.    Procedure LoadScreen;
  372.  
  373.       Var
  374.          InFile: Text;
  375.          Error: Boolean;
  376.          FileName: String [14];
  377.          Across, Down: Integer;
  378.  
  379.       Begin
  380.          GotoXY(Width + 3, 21);
  381.          If Yes('Reset screen') then
  382.             Begin
  383.             For Across := MinBound to Width do
  384.                For Down := MinBound to Height do
  385.                   With Board[Across, Down] do
  386.                      If LooksLikeItIs = Alive then
  387.                         Begin
  388.                         GotoXY(Across + 1, Down + 1);
  389.                         Write(' ');
  390.                         LooksLikeItIs := Dead;
  391.                         Nearby := 0
  392.                         End;
  393.  
  394.             ResetEdges;
  395.             Population := 0;
  396.             GotoXY(Width + 15, 17);
  397.             Write(Population: 5)
  398.             End;
  399.          GotoXY(Width + 3, 21);
  400.          Write('File name to load:');
  401.          GotoXY(Width + 5, 22);
  402.          BufLen := 14;
  403.          ReadLn(FileName);
  404.          GotoXY(Width + 3, 21);
  405.          ClrEol;
  406.          GotoXY(Width + 5, 22);
  407.          ClrEol;
  408.          If FileName <> '' then
  409.             Begin
  410.             GotoXY(Width + 6, 22);
  411.             Write('Loading...');
  412.             Assign(InFile, FileName);
  413.             Error := IOResult <> 0;
  414.             If Not Error then
  415.                begin
  416.                Reset(InFile);
  417.                Error := IOResult <> 0
  418.                End;
  419.             If Not Error then
  420.                Repeat
  421.                   ReadLn(InFile, Across, Down);
  422.                   If (Across >= MinBound) and (Down >= MinBound) and
  423.                      (Down <= Height) and (Across <= Width) then
  424.                      With Board[Across, Down] do
  425.                         Begin
  426.                         Limits(Across, Down);
  427.                         If LooksLikeItIs = Dead then
  428.                            Begin
  429.                            GotoXY(Across + 1, Down + 1);
  430.                            Write(Lively);
  431.                            LooksLikeItIs := Alive;
  432.                            Population := Population + 1;
  433.                            GotoXY(Width + 15, 17);
  434.                            Write(Population: 5)
  435.                            End
  436.                         End;
  437.  
  438.                   Error := IOResult <> 0
  439.                Until (Eof(InFile)) or (Error);
  440.             Close(InFile);
  441.             If Not Error then
  442.                Error := IOResult <> 0;
  443.             GotoXY(Width + 6, 22);
  444.             If Error then
  445.                Write('Loading Error!', Chr(7))
  446.             else
  447.                ClrEol
  448.             End
  449.       End;
  450.  
  451.  
  452.    Procedure SaveScreen;
  453.  
  454.       Var
  455.          OutFile: Text;
  456.          Error: Boolean;
  457.          FileName: String [14];
  458.          Across, Down: Integer;
  459.  
  460.       Begin
  461.          GotoXY(Width + 3, 21);
  462.          Write('File name to save:');
  463.          GotoXY(Width + 5, 22);
  464.          BufLen := 14;
  465.          ReadLn(FileName);
  466.          GotoXY(Width + 3, 21);
  467.          ClrEol;
  468.          GotoXY(Width + 5, 22);
  469.          ClrEol;
  470.          If FileName <> '' then
  471.             Begin
  472.             GotoXY(Width + 6, 22);
  473.             Write('Saving...');
  474.             Assign(OutFile, FileName);
  475.             Error := IOResult <> 0;
  476.             If Not Error then
  477.                Begin
  478.                ReWrite(OutFile);
  479.                Error := IOResult <> 0
  480.                End;
  481.             If Not Error then
  482.                For Across := MinBound to Width do
  483.                   For Down := MinBound to Height do
  484.                      With Board[Across, Down] do
  485.                         If LooksLikeItIs = Alive then
  486.                            If Not Error then
  487.                               Begin
  488.                               WriteLn(OutFile, Across: 1, ' ', Down: 1);
  489.                               Error := IOResult <> 0
  490.                               End;
  491.  
  492.             Close(OutFile);
  493.             If Not Error then
  494.                Error := IOResult <> 0;
  495.             If Error then
  496.                Erase(OutFile);
  497.             GotoXY(Width + 6, 22);
  498.             ClrEol
  499.             End
  500.       End;
  501.  
  502.  
  503.    Procedure GetPositions;
  504.  
  505.       Var
  506.          Ch: Char;
  507.          Across, Down, Index: Integer;
  508.  
  509.       Begin
  510.          Down := 0;
  511.          Across := 0;
  512.          GotoXY(Width + 12, 23);
  513.          Write('star');
  514.          Repeat
  515.             GotoXY(Across + 1, Down + 1);
  516.             Index := - 15000;
  517.             If Not KeyPressed then
  518.                Repeat
  519.                   If Index <= 32767 then
  520.                      Index := Index + 1;
  521.                   If Index = 0 then
  522.                      Begin
  523.                      GotoXY(Width + 6, 22);
  524.                      ClrEol;
  525.                      GotoXY(Across + 1, Down + 1)
  526.                      End
  527.                   else If Index = 32767 then
  528.                      Begin
  529.                      GotoXY(Width + 6, 22);
  530.                      Write(Chr(7), 'Hurry up!!');
  531.                      GotoXY(Across + 1, Down + 1);
  532.                      Index := - 30000
  533.                      End
  534.                Until KeyPressed;
  535.             Read(Kbd, Ch);
  536.             If (Ch = Chr(27)) and (KeyPressed) then
  537.                Begin
  538.                Read(Kbd, Ch);
  539.                Case Ord(Ch) of
  540.                   71:
  541.                      Ch := '7';
  542.                   72:
  543.                      Ch := '8';
  544.                   73:
  545.                      Ch := '9';
  546.                   75:
  547.                      Ch := '4';
  548.                   77:
  549.                      Ch := '6';
  550.                   79:
  551.                      Ch := '1';
  552.                   80:
  553.                      Ch := '2';
  554.                   81:
  555.                      Ch := '3'
  556.                   end
  557.                End;
  558.             If Ch = ' ' then
  559.                Ch := '5';
  560.             If Index < 1 then
  561.                Begin
  562.                GotoXY(Width + 6, 22);
  563.                ClrEol;
  564.                GotoXY(Across + 1, Down + 1)
  565.                End;
  566.             Case Ch of
  567.                ^L:
  568.                   LoadScreen;
  569.                ^S:
  570.                   SaveScreen;
  571.                '1':
  572.                   Begin
  573.                   Across := Pred(Across);
  574.                   Down := Succ(Down)
  575.                   End;
  576.                '2':
  577.                   Down := Succ(Down);
  578.                '3':
  579.                   Begin
  580.                   Across := Succ(Across);
  581.                   Down := Succ(Down)
  582.                   End;
  583.                '4':
  584.                   Across := Pred(Across);
  585.                '5':
  586.                   With Board[Across, Down] do
  587.                      Begin
  588.                      Limits(Across, Down);
  589.                      If LooksLikeItIs = Alive then
  590.                         Begin
  591.                         Write(Deadly);
  592.                         LooksLikeItIs := Dead;
  593.                         Population := Population - 1
  594.                         End
  595.                      else
  596.                         Begin
  597.                         Write(Lively);
  598.                         LooksLikeItIs := Alive;
  599.                         Population := Population + 1
  600.                         End;
  601.                      GotoXY(Width + 15, 17);
  602.                      Write(Population: 5)
  603.                      End;
  604.  
  605.                '6':
  606.                   Across := Succ(Across);
  607.                '7':
  608.                   Begin
  609.                   Across := Pred(Across);
  610.                   Down := Pred(Down)
  611.                   End;
  612.                '8':
  613.                   Down := Pred(Down);
  614.                '9':
  615.                   Begin
  616.                   Across := Succ(Across);
  617.                   Down := Pred(Down)
  618.                   End
  619.                End;
  620.             If Across > Width - 1 then
  621.                Begin
  622.                Across := 0;
  623.                Down := Succ(Down)
  624.                End
  625.             else If Across < 0 then
  626.                Begin
  627.                Across := Width - 1;
  628.                Down := Pred(Down)
  629.                End;
  630.             If Down > Height - 1 then
  631.                Down := 0
  632.             else If Down < 0 then
  633.                Down := Height - 1
  634.          Until Ch = Chr(27);
  635.          GotoXY(Width + 12, 23);
  636.          Write('abor')
  637.       End;
  638.  
  639.    Begin
  640.       Initialize;
  641.       Instructions;
  642.       DrawScreen;
  643.       Population := 0;
  644.       Generation := 0;
  645.       Pause := 32;
  646.       GetPositions;
  647.       GotoXY(Width + 15, 20);
  648.       Write(Pause Div 16: 5);
  649.       Repeat
  650.          CountNeighbors;
  651.          UpDate;
  652.          If Pause <> 0 then
  653.             For Ch := 'A' to 'Z' do
  654.                Delay(Pause);
  655.          If KeyPressed then
  656.             Begin
  657.             Read(Kbd, Ch);
  658.             Case Ch of
  659.                ^M:
  660.                   GetPositions;
  661.                ^[:
  662.                   If Not KeyPressed then
  663.                      Population := 0;
  664.                '>', '.':
  665.                   Pause := Min(Pause + 16, 255);
  666.                '<', ',':
  667.                   Pause := Max(Pause - 16, 0)
  668.                End;
  669.             If Ch in ['>', '.', '<', ','] then
  670.                Begin
  671.                GotoXY(Width + 15, 20);
  672.                If Pause = 0 then
  673.                   Write(Pause: 5)
  674.                else
  675.                   Write(Pause Div 16: 5)
  676.                End
  677.             End
  678.       Until (Population = 0) or ((Births = 0) and (Deaths = 0));
  679.       GotoXY(Width + 5, 23);
  680.       ClrEol;
  681.       If Ch = Chr(27) then
  682.          Write('   Aborted!!')
  683.       else If Population = 0 then
  684.          Begin
  685.          GotoXY(Width + 3, 22);
  686.          Write('This colony has');
  687.          GotoXY(Width + 6, 23);
  688.          Write('died out.')
  689.          End;
  690.       GotoXY(1, 24)
  691.    End.
  692.