{------------------------------------- ca -------------------------------------}

{ ca => Cellular Automata demonstration program. DWB 15-May-1986 }
   PROGRAM ca ( INPUT, OUTPUT, infile ) ;

{ External References : }
   %INCLUDE 'INC:UTIL.PAS'
   %INCLUDE 'DWB:LEVEL1.INC'

{ Global Constants : }
   CONST

      max_no_of_states = 8 ;
      max_no_of_choices = ( 3 * ( max_no_of_states - 1 ) ) ;
      ord_zero = 48 ;
      no_of_cells = 1023 ;
      y_pixels = 512 ;
      x_pixels = 688 ;
      display_start = ( no_of_cells + 1 - x_pixels ) DIV 2 ;
      display_end = display_start + x_pixels - 1 ;

{ Global Types : }
   TYPE

      ubyte = 0..255 ; { for unsigned byte integers }

      ubarr = ARRAY [ 0..no_of_cells ] OF ubyte ;

      cell_line = RECORD
                     left  : INTEGER ;
                     data  : ubarr ;
                     right : INTEGER
                  END ;
      cell_line_ptr = ^cell_line ;
      ktxt = VARYING [ 80 ] OF CHAR ;
      ckey = ARRAY [ 0..max_no_of_choices ] OF ubyte ;

      part_cell_array = ARRAY [ 1..20 ] OF INTEGER ;

      automata = RECORD
                    nstates : INTEGER ;
                    key     : ckey ;
                    initial : part_cell_array
                 END ;

      aptr = ^arec ;

      arec = RECORD
                auto : automata ;
                next : aptr
             END ;

{ EDWIN Types : }
   %INCLUDE 'EDWIN:TYPES.PAS'

{ Global Variables : }
   VAR

      infile           : TEXT ; { source file for automata initialisation }
      last_line        : cell_line_ptr ; { last line of cells processed }
      current_line     : cell_line_ptr ; { line of cells being processed }
      save_line        : cell_line_ptr ; { used when swapping cell lines over }
      keyword          : ckey ; { keyword used for automata propogation }
      no_of_states     : INTEGER ; { no of states per automaton cell }
      no_of_choices    : INTEGER ; { no of choices for new cell state }
      display_y_index  : INTEGER ; { current y position on graphics display }
      display_y_offset : INTEGER ; { current y offset for graphics display }
      scrolling        : BOOLEAN ; { flags if display is being scrolled }
      filename         : str255 ; { filename of initialisation source file }
      reply            : CHAR ; { users reply to Repeat prompt }
      ok               : BOOLEAN ; { users reply valid ? }
      auto_demos       : aptr ; { list of demonstartion aytomata }
      choices          : ARRAY [ 0..7 ] OF INTEGER ;

{ exists => Does file with given filename exist ? From SSI library. }
   FUNCTION exists ( fname : str255 ) : BOOLEAN ; EXTERNAL ;

{ EDWIN routine specs : }
   %INCLUDE 'EDWIN:SPECS.PAS'
   %INCLUDE 'SHAPES.PAS'

{----------------------------------- selbox -----------------------------------}

{ selbox => Draw an option selection box with given size & no of partitions. }
   PROCEDURE selbox ( xl, xr, yb, yt, np : INTEGER ) ;

{ Local Variables : }
   VAR

      partsep : REAL ;    { seperation between partitions }
      partpos : INTEGER ; { x position of partition }
      partno  : INTEGER ; { partition number }

   BEGIN { selbox }

   rectangle ( xl, yb, xr, yt ) ;
   partsep := ( xr - xl ) / np ;
   FOR partno := 1 TO ( np - 1 ) DO BEGIN
      partpos := ROUND ( partno * partsep ) + xl ;
      moveabs ( partpos, yb ) ;
      lineabs ( partpos, yt ) ;
   END ;

   END ; { of procedure selbox ------------------------------------------------}

{--------------------------------- select_box --------------------------------}

{ select_box => Select a box using the mouse. }
   PROCEDURE select_box ( xl, xr, yb, yt, np : INTEGER ; VAR sb : INTEGER ) ;

{ Local Variables : }
   VAR

      state : INTEGER ; { used when obtaining mouse input }
      xc    : INTEGER ; { x position of cursor }
      yc    : INTEGER ; { y position of cursor }
      wtime : INTEGER ; { time for end of debouncing wait }

      psize : REAL    ; { size of box partitions }

   BEGIN { select_box }

   sb := 0 ;
   psize := ( xr - xl ) / np ;
   requestinput ( state, xc, yc ) ;
   IF ( ( yc > yb ) AND ( yc < yt ) ) THEN BEGIN
      IF ( ( xc > xl ) AND ( xc < xr ) ) THEN BEGIN
         sb := TRUNC ( ( xc - xl ) / psize ) + 1
      END
   END ;
   wtime := cputime + 100 ;
   REPEAT UNTIL ( cputime > wtime ) ;

   END ; { of procedure select_box --------------------------------------------}

{---------------------------------- fill_box ----------------------------------}

{ fill_box => Fill part of box with specified colour. }
   PROCEDURE fill_box ( xl, xr, yb, yt, np, nb, col : INTEGER ) ;

{ Local Variables : }
   VAR

      psize : REAL ; { partition size }

      pxl, pxr : INTEGER ; { x coords of partition }

   BEGIN { fill_box }

   psize := ( xr - xl ) / np ;
   pxl := xl + ROUND ( ( nb - 1 ) * psize ) ;
   pxr := xl + ROUND ( nb * psize ) ;
   setcolour ( col ) ;
   setshademode ( 1 ) ; { shading on }
   rectangle ( pxl, yb, pxr, yt ) ;
   setshademode ( 0 ) ; { shading off }
   setcolour ( 1 ) ;
   rectangle ( pxl, yb, pxr, yt ) ;

   END ; { of procedure fill_box ----------------------------------------------}

{------------------------------ get_no_of_states ------------------------------}

{ get_no_of_states => Get desired number of states from the user. }
   PROCEDURE get_no_of_states ( VAR nos : INTEGER ) ;

{ Local Variables : }
   VAR

      rbox : INTEGER ; { reply box selected by user }

      box : INTEGER ; { used when referencing a selection box }

      bno : VARYING [ 1 ] OF CHAR ; { used when displaying box nos }

      mess : VARYING [ 80 ] OF CHAR ; { used for info messages }

   BEGIN { get_no_of_states }

   REPEAT
      newframe ;                    { clear screen }
      window ( 1, 1000, 1, 1000 ) ; { define window }
      setcharquality ( 1 ) ;
      setcharsize ( 24 ) ;
      setcolour ( 1 ) ;             { white on video screen }
      moveabs ( 200, 600 ) ;
      drawtext ( 'Select number of states using the mouse.' ) ;
      selbox ( 200, 900, 400, 500, 7 ) ;
      setcharsize ( 48 ) ;
      FOR box := 2 TO 8 DO BEGIN
         moveabs ( ( 26 + ( 100 * box ) ), 425 ) ;
         bno := '' + CHR ( ORD ( '0' ) + box ) ;
         drawtext ( bno )
      END ;
      REPEAT
         select_box ( 200, 900, 400, 500, 7, nos ) ;
         IF ( nos <= 0 ) THEN WRITE ( CHR ( 7 ) ) ;
      UNTIL ( nos > 0 ) ;
      fill_box ( 200, 900, 400, 500, 7, nos, 4 ) ;
      setcolour ( 1 ) ;
      moveabs ( ( 126 + ( 100 * nos ) ), 425 ) ;
      nos := nos + 1 ;
      bno := '' + CHR ( ORD ( '0' ) + nos ) ;
      drawtext ( bno ) ;
      moveabs ( 200, 300 ) ;
      setcharsize ( 24 ) ;
      mess := 'Automata with '+CHR ( ORD ( '0' ) + nos )+' states selected' ;
      drawtext ( mess ) ;
      selbox ( 200, 600, 100, 150, 2 ) ;
      moveabs ( 210, 110 ) ;
      drawtext ( 'Reselect' ) ;
      moveabs ( 410, 110 ) ;
      drawtext ( 'Continue' ) ;
      REPEAT
         select_box ( 200, 600, 100, 150, 2, rbox ) ;
         IF ( rbox <= 0 ) THEN WRITE ( CHR ( 7 ) ) ;
      UNTIL ( rbox > 0 ) ;
   UNTIL ( rbox = 2 ) ;

   END ; { of procedure get_no_of_states --------------------------------------}

{----------------------------------- get_key ----------------------------------}

{ get_key => Gey key from user. }
   PROCEDURE get_key ( VAR kw : ckey ) ;

{ Local Variables : }
   VAR

      index  : INTEGER ; { index into key cells }
      xend   : INTEGER ; { x coord of end of box }
      box    : INTEGER ; { box selected by user }

   BEGIN { get_key }

{ initialise returned key }
   FOR index := 0 TO max_no_of_choices DO
      kw [ index ] := 0 ;

{ clear screen and setup window }
   newframe ;
   window ( 1, 1000, 1, 1000 ) ;
   moveabs ( 100, 925 ) ;
   drawtext ( 'Possible Automata states :' ) ;
   xend := 100 + ( no_of_states * 100 ) ;
   FOR index := 0 TO ( no_of_states - 1 ) DO
      fill_box ( 100, xend, 800, 900, no_of_states, index+1, choices [ index ] ) ;

{ get user to define key }
   moveabs ( 100, 600 ) ;
   drawtext ( 'Define key using mouse.' ) ;
   moveabs ( 100, 550 ) ;
   drawtext ( 'Clicking mouse in box moves to next' ) ;
   moveabs ( 100, 500 ) ;
   drawtext ( 'possible state.' ) ;
   moveabs ( 100, 450 ) ;
   drawtext ( 'Click outside boxes to finish.' ) ;
   selbox ( 100, 900, 300, 400, no_of_choices ) ;
   REPEAT
      select_box ( 100, 900, 300, 400, no_of_choices, box ) ;
      IF ( box > 0 ) THEN BEGIN
         kw [ box-1 ] := kw [ box-1 ] + 1 ;
         IF ( kw [ box-1 ] = no_of_states ) THEN kw [ box-1 ] := 0 ;
         fill_box ( 100, 900, 300, 400, no_of_choices, box,
           choices [ kw [ box - 1 ] ] ) ;
      END ;
   UNTIL ( box = 0 ) ;

   END ; { of procedure get_key -----------------------------------------------}

{--------------------------- get_initial_conditions ---------------------------}

{ get_initial_conditions => Get initial conditions from user. }
   PROCEDURE get_initial_conditions ( VAR partic : part_cell_array ) ;

{ Local Variables : }
   VAR

      index  : INTEGER ; { index to cell }
      xend   : INTEGER ; { x coord of end of box }
      box    : INTEGER ; { box selected by user }


   BEGIN { get_initial_conditions }

{ initialise returned partial initial conditions array }
   FOR index := 1 TO 20 DO partic [ index ] := 0 ;

{ clear screen and setup window }
   newframe ;
   window ( 1, 1000, 1, 1000 ) ;
   moveabs ( 100, 925 ) ;
   drawtext ( 'Possible Automata states :' ) ;
   xend := 100 + ( no_of_states * 100 ) ;
   FOR index := 0 TO ( no_of_states - 1 ) DO
      fill_box ( 100, xend, 800, 900, no_of_states, index+1, choices [ index ] ) ;

{ get user to define initial conditions }
   moveabs ( 100, 600 ) ;
   drawtext ( 'Define initial conditions using mouse.' ) ;
   moveabs ( 100, 550 ) ;
   drawtext ( 'Clicking mouse in box moves to next' ) ;
   moveabs ( 100, 500 ) ;
   drawtext ( 'possible state.' ) ;
   moveabs ( 100, 450 ) ;
   drawtext ( 'Click outside boxes to finish.' ) ;
   selbox ( 100, 900, 300, 400, 20 ) ;
   REPEAT
      select_box ( 100, 900, 300, 400, 20, box ) ;
      IF ( box > 0 ) THEN BEGIN
         partic [ box ] := partic [ box ] + 1 ;
         IF ( partic [ box ] = no_of_states ) THEN partic [ box ] := 0 ;
         fill_box ( 100, 900, 300, 400, 20, box,
           choices [ partic [ box ] ] ) ;
      END ;
   UNTIL ( box = 0 ) ;

   END ; { of procedure get_initial_conditions --------------------------------}

{------------------------------ display_example -------------------------------}

{ display_exaple => Display an example automata's parameters. }
   PROCEDURE display_example ( n : INTEGER ; a : automata ) ;

{ Local Variables : }
   VAR

      key_states : INTEGER ; { number of states in the key }
      index      : INTEGER ; { index used when plotting boxes }
      xend       : INTEGER ; { x coord of end of display boxes }
      mess       : VARYING [ 80 ] OF CHAR ; { message text }

   BEGIN { display_example }

   WITH a DO BEGIN
      key_states := ( 3 * ( nstates - 1 ) ) + 1 ;
      moveabs ( 100, 925 ) ;
      mess := 'Possible states for example Automata '+CHR(ORD(0)+n)+' :' ;
      drawtext ( mess ) ;
      xend := 100 + ( nstates * 100 ) ;
      setshademode ( 1 ) ;
      setcolour ( 0 ) ;
      rectangle ( 100, 800, 900, 900 ) ;
      setcolour ( 1 ) ;
      setshademode ( 0 ) ;
      FOR index := 0 TO ( nstates - 1 ) DO
         fill_box ( 100, xend, 800, 900, nstates, index+1, choices [ index ] ) ;
      moveabs ( 100, 725 ) ;
      drawtext ( 'Key used to generate new state.' ) ;
      setshademode ( 1 ) ;
      setcolour ( 0 ) ;
      rectangle ( 100, 600, 900, 700 ) ;
      setcolour ( 1 ) ;
      setshademode ( 0 ) ;
      FOR index := 0 TO ( key_states - 1 ) DO
         fill_box ( 100, 900, 600, 700, key_states, index + 1,
           choices [ key [ index ] ] ) ;
      moveabs ( 100, 525 ) ;
      drawtext ( 'Initial Automata state.' ) ;
      FOR index := 1 TO 20 DO
         fill_box ( 100, 900, 400, 500, 20, index,
           choices [ initial [ index ] ] ) ;
   END ;

   END ; { of procedure display_example ---------------------------------------}

{-------------------------------- get_example ---------------------------------}

{ get_example => Get parameters of an example automata. }
   PROCEDURE get_example ( VAR nos : INTEGER ; VAR k : ckey ;
     VAR init : part_cell_array ) ;

{ Local Variables : }
   VAR

      counter : INTEGER ; { which example is being displayed }
      example : aptr ;    { pointer to demo list element }
      box     : INTEGER ; { which selection box user has chosen }
      done    : BOOLEAN ; { flags when user has made a choice }

   BEGIN { get_example }

   newframe ;
   window ( 1, 1000, 1, 1000 ) ;
   done := FALSE ;
   REPEAT
      counter := 1 ;
      example := auto_demos ;
      REPEAT
         display_example ( counter, example^.auto ) ;
         moveabs ( 100, 300 ) ;
         drawtext ( 'Run this example or choose another ?' ) ;
         moveabs ( 100, 250 ) ;
         drawtext ( 'Select option from boxes.' ) ;
         selbox ( 100, 900, 100, 200, 2 ) ;
         moveabs ( 125, 125 ) ;
         drawtext ( 'Run' ) ;
         moveabs ( 525, 125 ) ;
         drawtext ( 'Choose' ) ;
         REPEAT
            select_box ( 100, 900, 100, 200, 2, box ) ;
         UNTIL ( box <> 0 ) ;
         done := ( box = 1 ) ;
         IF ( done ) THEN BEGIN
            WITH example^.auto DO BEGIN
              nos := nstates ;
              k := key ;
              init := initial
            END
         END
         ELSE BEGIN
            counter := counter + 1 ;
            example := example^.next
         END ;
      UNTIL ( done OR ( example = NIL ) ) ;
   UNTIL ( done ) ;

   END ; { of procedure get_example -------------------------------------------}

{--------------------------------- initialise ---------------------------------}

{ initialise => Get initial state for cellular automaton from file infile. }
   PROCEDURE initialise ;

{ Breaks : }
   LABEL 1 ;

{ Local Variables : }
   VAR

      index  : INTEGER ;         { index into cell arrays }
      first  : INTEGER ;         { index to first initial conditions cell }
      last   : INTEGER ;         { index to last initial conditions cell }
      partic : part_cell_array ; { specified part of initial conditions }
      box    : INTEGER ;         { selction box chosen by user }

   BEGIN { initialise }

   setterminalmode ( nopage ) ;

{ setup the two cell line arrays }
   FOR index := 0 TO no_of_cells DO current_line^.data [ index ] := 0 ;
   last_line^.left := 1 ;
   last_line^.right := no_of_cells - 1 ;
   FOR index := 0 TO no_of_cells DO last_line^.data[ index ] := 0 ;

{ put display offset back to 0,0 }
   offset ( 0, 0 ) ;

{ check for examples available }
   IF ( auto_demos <> NIL ) THEN BEGIN

{    sort out what user wants to do }
      newframe ;
      window ( 1, 1000, 1, 1000 ) ;
      moveabs ( 100, 700 ) ;
      drawtext ( 'To select option using mouse click in box.' ) ;
      selbox ( 100, 900, 400, 500, 2 ) ;
      moveabs ( 125, 425 ) ;
      drawtext ( 'Examples' ) ;
      moveabs ( 525, 425 ) ;
      drawtext ( 'Define' ) ;
      REPEAT
         select_box ( 100, 900, 400, 500, 2, box ) ;
      UNTIL ( box <> 0 ) ;
   END

{ no examples, so user must define own automata }
   ELSE box := 2 ;

{ user wants to define own automata }
   IF ( box = 2 ) THEN BEGIN

{    get no of allowed states & work out no of choices for new state }
      get_no_of_states ( no_of_states ) ;
      no_of_choices := ( 3 * ( no_of_states - 1 ) + 1 ) ;

{    get key }
      get_key ( keyword ) ;

{    get initial conditions }
      get_initial_conditions ( partic ) ;
   END

{ user wants to select an example }
   ELSE get_example ( no_of_states, keyword, partic ) ;

   first := ( ( no_of_cells + 1 ) DIV 2 ) - 10 ;
   last  := first + 19 ;
   FOR index := first TO last DO last_line^.data[ index ] :=
      partic [ index - first + 1 ] ;
   display_y_index := 0 ;
   display_y_offset := 0 ;
   scrolling := FALSE ;

{ set up left & right indices for the initial conditions }
   WITH last_line^ DO BEGIN
      WHILE ( data [ left ] = 0 ) DO BEGIN
         left := left + 1 ;
         IF ( left > no_of_cells ) THEN GOTO 1
      END ;
      WHILE ( data [ right ] = 0 ) DO right := right - 1 ;
   END ;

1:

{ copy left & right indices into current cell line }
   current_line^.left := last_line^.left ;
   current_line^.right := last_line^.right ;

   END ; { of procedure initialise --------------------------------------------}

{ colfill => Reference to level1 colfill routine. }
   PROCEDURE colfill %ALIAS 'FRED_GRAPHICS_CFILL'
     ( x0, y0, x1, y1 : INTEGER ; VAR data : ubyte ) ; EXTERNAL ;

{------------------------------- display_state --------------------------------}

{ display_state => Display current cellular automaton state. }
   PROCEDURE display_state ;

{ Local Variables : }
   VAR

      index      : INTEGER ; { index to cell }
      curr_col   : INTEGER ; { current colour being plotted }
      start_fill : INTEGER ; { index to start of fill position }
      finish     : INTEGER ; { index to last cell to be plotted }

   BEGIN { display_state }

{ check whether automata has died out }
   WITH current_line^ DO IF ( right > left ) THEN BEGIN

{    set up limits of data to be displayed }
      IF ( display_start > left ) THEN index := display_start
      ELSE index := left ;
      IF ( display_end < right ) THEN finish := display_end
      ELSE finish := right ;

      colfill ( index - display_start, display_y_index, finish - display_start,
        display_y_index, data [ index ] ) ;

{    update display index }
      display_y_index := display_y_index + 1 ;
      IF ( NOT scrolling ) THEN
         scrolling := display_y_index > ( y_pixels - 1 )
      ELSE BEGIN
         IF ( display_y_index > ( ( y_pixels * 2 ) - 1 ) ) THEN
            display_y_index := 0 ;
         display_y_offset := display_y_offset + 1 ;
         IF ( display_y_offset > ( ( y_pixels * 2 ) - 1 ) ) THEN
            display_y_offset := 0 ;
         offset ( 0, display_y_offset ) ;
      END
   END

   END ; { of procedure display_state -----------------------------------------}

{------------------------------------ run -------------------------------------}

{ run => Run Cellular Automaton demonstration with given starting conditions. }
   PROCEDURE run ;

{ Breaks : }
   LABEL 2 ;

{ Local Variables : }
   VAR

      sum        : INTEGER ; { sum of 3 adjacent cells }
      index      : INTEGER ; { index to cell }
      start      : INTEGER ; { index to first possible non-zero cell }
      finish     : INTEGER ; { index to last possible non-zero cell }
      iterations : INTEGER ; { no of iterations performed }
      start_time : INTEGER ; { time automata started up }
      end_time   : INTEGER ; { time automata stopped }
      frequency  : INTEGER ; { no of iterations per second }

   BEGIN { run }

{ remember start time }
   start_time := elapsedtime ;

{ clear screen }
   clear ;
   iterations := 0 ;

{ set up end points of cell line }
   current_line^.data[ 0 ] := 0 ;
   current_line^.data[ no_of_cells ] := 0 ;
   WHILE ( ( last_line^.right >= last_line^.left ) AND ( testsymbol = -1 ) ) DO
   BEGIN

{    locate position of first possible non-zero cell, zero any cells if needed }
      start := last_line^.left - 1 ;
      IF ( start < 1 ) THEN start := 1
      ELSE IF ( start > current_line^.left ) THEN WITH current_line^ DO
         FOR index := left TO ( start - 1 ) DO data [ index ] := 0 ;

{    locate position of last possible non-zero cell, zero any cells if needed }
      finish := last_line^.right + 1 ;
      IF ( finish > ( no_of_cells - 1 ) ) THEN finish := no_of_cells - 1
      ELSE IF ( finish < current_line^.right ) THEN WITH current_line^ DO
         FOR index := ( finish + 1 ) TO right DO data [ index ] := 0 ;

{    calculate new values for all possible non-zero cells }
      WITH last_line^ DO FOR index := start TO finish DO BEGIN
         sum := data [ index - 1 ] + data [ index ] + data [ index + 1 ] ;
         current_line^.data[ index ] := keyword [ sum ]
      END ;

{    locate left & right indices for current line }
      WITH current_line^ DO BEGIN
         left := start ;
         WHILE ( data [ left ] = 0 ) DO BEGIN
            left := left + 1 ;
            IF ( left > no_of_cells ) THEN GOTO 2
         END ;
         right := finish ;
         WHILE ( data [ right ] = 0 ) DO right := right - 1
      END ;

{    display current cell line }
2:    display_state ;
      iterations := iterations + 1 ;

{    make current cell line the last cell line ready for next iteration }
      save_line := last_line ;
      last_line := current_line ;
      current_line := save_line ;
   END ;

{ remember end time & calculate frequency }
   end_time := elapsedtime ;
   frequency := ( iterations * 1000 ) DIV ( end_time - start_time ) ;

{ check if automaton died out }
   WRITELN ;
   IF ( last_line^.right < last_line^.left ) THEN
      WRITELN ( 'Automaton has died out after ', iterations, ' iterations.' )
   ELSE WRITELN ( iterations, ' iterations completed.' ) ;
   WRITELN ( frequency, ' iterations per second.' ) ;
   WRITELN

   END ; { of procedure run ---------------------------------------------------}

{------------------------------- read_automata --------------------------------}

{ read_automata => Read an automata specification from infile & add to list. }
   PROCEDURE read_automata ( VAR a : automata ) ;

{ Local Variables : }
   VAR

      n_key_values : INTEGER ; { no of key values for this automata }
      index        : INTEGER ; { index to array elements }
      c            : CHAR    ; { character read from input file }

   BEGIN { read_automata }

   WITH a DO BEGIN
      READLN ( infile, nstates ) ;
      n_key_values := ( 3 * ( nstates - 1 ) ) + 1 ;
      FOR index := 0 TO ( n_key_values - 1 ) DO BEGIN
         READ ( infile, c ) ;
         key [ index ] := ORD ( c ) - ORD ( '0' ) ;
      END ;
      READLN ( infile ) ;
      FOR index := 1 TO 20 DO BEGIN
         READ ( infile, c ) ;
         initial [ index ] := ORD ( c ) - ORD ( '0' )
      END ;
      READLN ( infile ) ;
   END ;

   END ; { of procedure read_automata -----------------------------------------}

{-------------------------------- add_to_list ---------------------------------}

{ add_to_list => Add given automata spec to given list. }
   PROCEDURE add_to_list ( VAR list : aptr ; a : automata ) ;

   BEGIN { add_to_list }

   IF ( list = NIL ) THEN BEGIN
      NEW ( list ) ;
      list^.auto := a ;
      list^.next := NIL
   END
   ELSE add_to_list ( list^.next, a ) ;

   END ; { of procedure add_to_list -------------------------------------------}

{---------------------------------- start_up ----------------------------------}

{ start_up => Perform program start up initialisations. }
   PROCEDURE start_up ;

{ Local Variables : }
   VAR

      a_demo : automata ; { demonstration automata read from file }

   BEGIN { start_up }

{ setup edwin }
   initialisefor ( defaultdevice ) ;

{ create current & last cell line records }
   NEW ( current_line ) ;
   NEW ( last_line ) ;

{ set up colours for states }
   choices [ 0 ] := 0 ;
   choices [ 1 ] := 4 ;
   choices [ 2 ] := 3 ;
   choices [ 3 ] := 6 ;
   choices [ 4 ] := 2 ;
   choices [ 5 ] := 5 ;
   choices [ 6 ] := 7 ;
   choices [ 7 ] := 1 ;

{ create demonstration automata list & read in specs of demos }
   auto_demos := NIL ;
   IF ( exists ( 'cademos.dat' ) ) THEN BEGIN
      RESET ( infile, 'cademos.dat' ) ;
      WHILE ( NOT EOF ( infile ) ) DO BEGIN
         read_automata ( a_demo ) ;
         add_to_list ( auto_demos , a_demo ) ;
      END ;
   END

   END ; { of procedure startup -----------------------------------------------}

   BEGIN { main program ca }

{ perform program start up initialisations }
   start_up ;

{ loop until user gets fed up }
   REPEAT

      initialise ;
      WRITELN ( 'To interrupt program press any key.' ) ;
      run ;

{    check whether user wants to have another go }
      REPEAT
         WRITE ( 'Do you want to have another go Y/N ? ' ) ;
         READLN ( reply ) ;
         ok := reply IN [ 'Y', 'y' ] ;
      UNTIL ( ok OR ( reply IN [ 'N', 'n' ] ) ) ;
   UNTIL ( NOT ok ) ;

   terminateedwin ;

   END. { of program ca -------------------------------------------------------}
