home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MADTRB18.ZIP / WATORC.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1985-08-04  |  14.3 KB  |  606 lines

  1. {  wator program -- December 1984 Scientific American                   }
  2. {  The world of Wa-Tor is torus shaped, like a donut, but it is         }
  3. {  represented here by a flat two dimensional image which wraps around. }
  4. {  The primary species on Wa-Tor resemble earth fishes (*) and earth    }
  5. {  sharks (@).  In this version, the water is blue, fishes are white    }
  6. {  and sharks are red.  There always seem to be enough fish for the     }
  7. {  sharks to eat.  Run this program and find out why...                 }
  8.  
  9. {  Copyright (C) 1985, Jay R. Jaeger, Madison WI                        }
  10. {  This program may be copied for personal enjoyment only.              }
  11.  
  12. {$R+}
  13.  
  14. program wator;
  15.  
  16.  
  17. { these constants describe the size of the wator world and the characteristics }
  18. { of the wator environment.                                                    }
  19.  
  20. const
  21.    _sbreed = 10;      { chronons between a shark breeding               }
  22.    _fbreed = 3;       { chronons between a fish  breeding               }
  23.    _xsize  = 79;      { horizontal size of wator                        }
  24.    _maxx   = 78;      { _xsize -1                                       }
  25.    _ysize  = 20;      { vertical size of wator                          }
  26.    _maxy   = 19;      { _ysize -1                                       }
  27.    _starve = 3;       { how long a shark can go without eating          }
  28.    _fishes = 200;     { initial number of fishes in pond                }
  29.    _sharks = 20;      { initial number of sharks in pond                }
  30.  
  31.  
  32. { these types descirbe the fish lists used to keep track of wator's beings }
  33.  
  34. type
  35.    species = (fish,shark,empty);
  36.    xcoord  = 0.._maxx;
  37.    ycoord  = 0.._maxy;
  38.  
  39.    link = ^fishes;
  40.    fishes = record
  41.       next: link;
  42.       prev: link;
  43.       kind: species;
  44.       age:  integer;
  45.       x:    xcoord;
  46.       y:    ycoord;
  47.       ate:  integer;
  48.    end;
  49.  
  50.  
  51. { string parameter type }
  52.  
  53. type
  54.    anystring = string[255];
  55.  
  56.  
  57. { these variables are the heads and tails of the lists of beings on wator }
  58.  
  59. var
  60.    fish_head: fishes;
  61.    fish_tail: fishes;
  62.    shark_head: fishes;
  63.    shark_tail: fishes;
  64.  
  65.  
  66. { this is the array used to identify what is currently at a place in wator }
  67.  
  68. var
  69.    pond: array [xcoord,ycoord] of species;
  70.  
  71.  
  72. { this array type is used to identify neighbors of a given fish or shark }
  73.  
  74. type
  75.    neighbor = record
  76.       x:     integer;
  77.       y:     integer;
  78.       kind:  species;
  79.    end;
  80.  
  81.    neighborhood = array[1..8] of neighbor;
  82.  
  83.  
  84. { these variables are used to save window states when switching windows }
  85.  
  86. var
  87.    window_pond_x:    integer;
  88.    window_pond_y:    integer;
  89.    window_debug_x:   integer;
  90.    window_debug_y:   integer;
  91.    last_window:      (debug_window,pond_window);
  92.  
  93.  
  94. { main program "local" variables }
  95.  
  96. var
  97.    counts: array[fish..shark] of integer;
  98.    generation: integer;
  99.    neighbors: array[1..4] of neighbor;
  100.  
  101.  
  102. { utility function to implement universe wrapping }
  103.  
  104. function wrap(c,l: integer): integer;
  105.  
  106.    begin
  107.    c := c mod l;
  108.    if c < 0 then
  109.       c := c + l;
  110.    wrap := c;
  111.    end;
  112.  
  113.  
  114. { procedure to wait for the user to press return }
  115.  
  116. procedure wait_user;
  117.  
  118.    begin
  119.    writeln('Press RETURN to continue.');
  120.    readln;
  121.    end;
  122.  
  123.  
  124. { procedure to set window to pond area }
  125.  
  126. procedure set_pond_window;
  127.  
  128.    begin
  129.    if last_window = debug_window then
  130.       begin;
  131.       window_debug_x := wherex;
  132.       window_debug_y := wherey;
  133.       window(1,1,80,_ysize);
  134.       last_window := pond_window;
  135.       gotoxy(window_pond_x,window_pond_y);
  136.       end;
  137.    end;
  138.  
  139.  
  140. { procedure to set window to text area }
  141.  
  142. procedure set_debug_window;
  143.  
  144.    begin
  145.    if last_window = pond_window then
  146.       begin
  147.       window_pond_x := wherex;
  148.       window_pond_y := wherey;
  149.       end;
  150.    window(1,_ysize+2,59,25);
  151.    last_window := debug_window;
  152.    gotoxy(window_debug_x,window_debug_y);
  153.    end;
  154.  
  155.  
  156. { procedure to write an error message to the debugging window }
  157.  
  158. procedure errmsg(s: anystring);
  159.  
  160.    begin
  161.    set_debug_window;
  162.    writeln(s);
  163.    wait_user;
  164.    end;
  165.  
  166.  
  167. { procedure to display a fish (or water) at a given location }
  168.  
  169. procedure dis_fish(x: xcoord; y:ycoord; t:species);
  170.  
  171.    begin
  172.    if (not x in [0.._maxx]) or (not y in [0.._maxy]) then
  173.       errmsg('dis_fish: illegal coordinates.');
  174.    set_pond_window;
  175.    gotoxy(x+1,y+1);
  176.    if t = fish then
  177.       begin
  178.       textcolor(lightgray);
  179.       write('*');
  180.       textcolor(white);
  181.       end
  182.    else if t = shark then
  183.       begin
  184.       textcolor(red);
  185.       write('@');
  186.       textcolor(white);
  187.       end
  188.    else
  189.       write(' ');
  190.    pond[x,y] := t;
  191.    end;
  192.  
  193.  
  194. { procedure to add a new fish (or shark) to the pond }
  195.  
  196. procedure add_fish(p: link; p_kind: species; p_x: xcoord; p_y: ycoord);
  197.  
  198.    var t: link;
  199.  
  200.    begin
  201.    if p^.next = nil then
  202.       errmsg('add_fish: next pointer is nil.')
  203.    else if not (p_kind in [fish,shark]) then
  204.       errmsg('add_fish: illegal species.')
  205.    else
  206.       begin
  207.       new(t);
  208.       counts[p_kind] := counts[p_kind] + 1;
  209.       with t^ do
  210.          begin
  211.          next := p^.next;
  212.          prev := p;
  213.          kind := p_kind;
  214.          age  := 0;
  215.          x    := p_x;
  216.          y    := p_y;
  217.          ate  := 0;
  218.          dis_fish(p_x,p_y,p_kind);
  219.          end;
  220.       p^.next^.prev := t;
  221.       p^.next := t;
  222.       end;
  223.    end;
  224.  
  225.  
  226. { procedure to display an entry on a fish list, for debugging }
  227.  
  228. procedure prt_fish(p: link);
  229.  
  230.    begin
  231.    set_debug_window;
  232.    if p = addr(fish_head) then
  233.       writeln('*** head of fish list')
  234.    else if p = addr(fish_tail) then
  235.       writeln('*** tail of fish list')
  236.    else if p = addr(shark_head) then
  237.       writeln('*** head of shark list')
  238.    else if p = addr(shark_tail) then
  239.       writeln('*** tail of shark list')
  240.    else
  241.       with p^ do
  242.          begin
  243.          if kind = fish then
  244.             write('FISH  ')
  245.          else
  246.             write('SHARK ');
  247.          write('age: ',age,', at x=',x,' y=',y);
  248.          if kind = fish then
  249.             writeln
  250.          else
  251.             writeln(' ate ',ate,' chronons ago');
  252.          if not ((x in [0.._maxx]) and (y in [0.._maxy])) then
  253.             writeln('*** illegal coordinates.');
  254.          end;
  255.    end;
  256.  
  257.  
  258. { procedure to display an entire list of fishes }
  259.  
  260. procedure prt_list(p: link);
  261.  
  262.    begin
  263.    while p <> nil do
  264.       begin
  265.       prt_fish(p);
  266.       p := p^.next;
  267.       end;
  268.    end;
  269.  
  270.  
  271. { procedure to delete an entry from a fish list }
  272.  
  273. procedure del_fish(p: link);
  274.  
  275.    begin
  276.    with p^ do
  277.       begin
  278.       if next = nil then
  279.          errmsg('del_fish: next is nil.')
  280.       else if prev = nil then
  281.          errmsg('del_fish: prev is nil.')
  282.       else if not (p^.kind in [fish,shark]) then
  283.          errmsg('del_fish: deleting bad kind.')
  284.       else
  285.          begin
  286.          counts[p^.kind] := counts[p^.kind] - 1;
  287.          prev^.next := next;
  288.          next^.prev := prev;
  289.          dis_fish(x,y,empty);
  290.          dispose(p);
  291.          end;
  292.       end;
  293.    end;
  294.  
  295.  
  296. { procedure to display wator world on screen }
  297.  
  298. procedure dis_world;
  299.  
  300.    var
  301.       tx:   xcoord;
  302.       ty:   ycoord;
  303.  
  304.    begin
  305.    set_pond_window;
  306.    clrscr;
  307.    for ty := 0 to _maxy do
  308.       for tx := 0 to _maxx do
  309.          if pond[tx,ty] <> empty then
  310.             begin
  311.             gotoxy(tx+1,ty+1);
  312.             if pond[tx,ty] = fish then
  313.                write('*')
  314.             else
  315.                begin
  316.                textcolor(blink+white);
  317.                write('@');
  318.                textcolor(white);
  319.                end;
  320.             end;
  321.    set_debug_window;
  322.    end;
  323.  
  324.  
  325. { procedure to check the pond around a given fish/shark }
  326.  
  327. procedure check_pond(p_x: xcoord; p_y:ycoord; t: species;
  328.                      var n: integer; var a: neighborhood);
  329.    var
  330.       tx: xcoord;
  331.       ty: ycoord;
  332.       i:  integer;
  333.  
  334.    begin
  335.    n := 0;
  336.    for i := 1 to 4 do
  337.       begin
  338.       tx := wrap(p_x+neighbors[i].x,_xsize);
  339.       ty := wrap(p_y+neighbors[i].y,_ysize);
  340.       if pond[tx,ty] = t then
  341.          begin
  342.          n := n+1;
  343.          with a[n] do
  344.             begin
  345.             x := tx;
  346.             y := ty;
  347.             kind := pond[tx,ty];
  348.             end;
  349.          end;
  350.       end;
  351.    end;
  352.  
  353.  
  354. { fish swim process }
  355.  
  356. procedure fish_swim;
  357.  
  358.    var
  359.       f_link:  link;
  360.       f_n:     integer;
  361.       f_nghbr: neighborhood;
  362.       old_x:   xcoord;
  363.       old_y:   ycoord;
  364.       r:       integer;
  365.  
  366.    begin
  367.    f_link := fish_head.next;
  368.    while f_link <> addr(fish_tail) do
  369.       with f_link^ do
  370.          begin
  371.          check_pond(x,y,empty,f_n,f_nghbr);
  372.          if f_n > 0 then
  373.             begin
  374.             old_x := x;
  375.             old_y := y;
  376.             r := random(f_n)+1;
  377.             dis_fish(x,y,empty);
  378.             x := f_nghbr[r].x;
  379.             y := f_nghbr[r].y;
  380.             dis_fish(x,y,fish);
  381.             if age >= _fbreed then
  382.                begin
  383.                add_fish(addr(fish_head),fish,old_x,old_y);
  384.                age := 0;
  385.                end
  386.             else
  387.                age := age + 1;
  388.             end
  389.          else
  390.             age := age + 1;
  391.          f_link := next;
  392.          end;
  393.    end;
  394.  
  395.  
  396. { subroutine where a fish turns into a shark nummy }
  397.  
  398. procedure eat_fish(p_x: xcoord; p_y:ycoord);
  399.  
  400.    var
  401.       f_link: link;
  402.       eaten:  boolean;
  403.  
  404.    begin
  405.    eaten := false;
  406.    f_link := fish_head.next;
  407.    while (f_link <> addr(fish_tail)) and (not eaten) do
  408.       with f_link^ do
  409.          if (x = p_x) and (y = p_y) then
  410.             begin
  411.             del_fish(f_link);
  412.             f_link := nil;
  413.             eaten := true;
  414.             end
  415.          else
  416.             f_link := next;
  417.    if not eaten then
  418.       begin
  419.       set_debug_window;
  420.       writeln('unable to eat imaginary fish at ',p_x,':',p_y);
  421.       wait_user;
  422.       end;
  423.    end;
  424.  
  425.  
  426. { shark hunt and breeding procedure }
  427.  
  428. procedure shark_move;
  429.  
  430.    label
  431.       next_shark;
  432.  
  433.    var
  434.       s_link:  link;
  435.       s_n:     integer;
  436.       s_nghbr: neighborhood;
  437.       old_x:   xcoord;
  438.       old_y:   ycoord;
  439.       r:       integer;
  440.  
  441.    begin
  442.    s_link := shark_head.next;
  443.    while s_link <> addr(shark_tail) do
  444.       with s_link^ do
  445.          begin
  446.          { feeding section }
  447.          check_pond(x,y,fish,s_n,s_nghbr);
  448.          if s_n > 0 then
  449.             begin
  450.             old_x := x;
  451.             old_y := y;
  452.             r := random(s_n)+1;
  453.             dis_fish(x,y,empty);
  454.             x := s_nghbr[r].x;
  455.             y := s_nghbr[r].y;
  456.             eat_fish(x,y);
  457.             dis_fish(x,y,shark);
  458.             ate := 0;
  459.             if age >= _sbreed then
  460.                begin
  461.                add_fish(addr(shark_head),shark,old_x,old_y);
  462.                age := 0;
  463.                end
  464.             else
  465.                age := age + 1;
  466.             s_link := next;
  467.             goto next_shark;
  468.             end;
  469.          { starvation section }
  470.          ate := ate + 1;
  471.          if ate > _starve then
  472.             begin
  473.             set_debug_window;
  474.             writeln('shark at ',x,':',y,' starved...');
  475.             s_link := next;
  476.             del_fish(s_link^.prev);
  477.             goto next_shark;
  478.             end;
  479.          { move to unoccupied section }
  480.          check_pond(x,y,empty,s_n,s_nghbr);
  481.          if s_n > 0 then
  482.             begin
  483.             old_x := x;
  484.             old_y := y;
  485.             r := random(s_n)+1;
  486.             dis_fish(x,y,empty);
  487.             x := s_nghbr[r].x;
  488.             y := s_nghbr[r].y;
  489.             dis_fish(x,y,shark);
  490.             if age >= _sbreed then
  491.                begin
  492.                add_fish(addr(shark_head),shark,old_x,old_y);
  493.                age := 0;
  494.                end
  495.             else
  496.                age := age + 1;
  497.             s_link := next;
  498.             goto next_shark;
  499.             end;
  500.          { if we get here, the shark just gets older }
  501.          age := age + 1;
  502.          s_link := next;
  503.          goto next_shark;
  504.  
  505. next_shark:
  506.           end;
  507.     end;
  508.  
  509.  
  510. { initialization procedure }
  511.  
  512. procedure init;
  513.  
  514.    var i: integer;
  515.        tx: xcoord;
  516.        ty: ycoord;
  517.        tt: boolean;
  518.  
  519.    begin
  520.    textmode(bw80);
  521.    textbackground(blue);
  522.    window_pond_x := 1;
  523.    window_pond_y := 1;
  524.    window_debug_x := 1;
  525.    window_debug_y := 1;
  526.    last_window := pond_window;
  527.    set_debug_window;
  528.    neighbors[1].x := 0;
  529.    neighbors[1].y := -1;
  530.    neighbors[2].x := -1;
  531.    neighbors[2].y := 0;
  532.    neighbors[3].x := 1;
  533.    neighbors[3].y := 0;
  534.    neighbors[4].x := 0;
  535.    neighbors[4].y := 1;
  536.    fish_head.next := addr(fish_tail);
  537.    fish_head.prev := nil;
  538.    fish_tail.next := nil;
  539.    fish_tail.prev := addr(fish_head);
  540.    shark_head.next := addr(shark_tail);
  541.    shark_head.prev := nil;
  542.    shark_tail.next := nil;
  543.    shark_tail.prev := addr(shark_head);
  544.    counts[fish] := 0;
  545.    counts[shark] := 0;
  546.    generation := 1;
  547.    for tx := 0 to _maxx do
  548.       for ty := 0 to _maxy do
  549.          pond[tx,ty] := empty;
  550.    for i := 1 to _fishes do
  551.       begin
  552.       tt := true;
  553.       while tt do
  554.          begin
  555.          tx := random(_xsize);
  556.          ty := random(_ysize);
  557.          if pond[tx,ty] = empty then
  558.             begin
  559.             add_fish(addr(fish_head),fish,tx,ty);
  560.             fish_head.next^.age := random(_fbreed);
  561.             tt := false;
  562.             end;
  563.          end;
  564.       end;
  565.    for i := 1 to _sharks do
  566.       begin
  567.       tt := true;
  568.       while tt do
  569.          begin
  570.          tx := random(_xsize);
  571.          ty := random(_ysize);
  572.          if pond[tx,ty] = empty then
  573.             begin
  574.             add_fish(addr(shark_head),shark,tx,ty);
  575.             with shark_head.next^ do
  576.                begin
  577.                age := random(_sbreed);
  578.                ate := random(_starve);
  579.                end;
  580.             tt := false;
  581.             end;
  582.          end;
  583.       end;
  584.    end;
  585.  
  586. begin
  587.    init;
  588.    while (fish_head.next <> addr(fish_tail)) and
  589.          (shark_head.next <> addr(shark_tail)) do
  590.       begin
  591.       set_debug_window;
  592.       window_debug_x := wherex;
  593.       window_debug_y := wherey;
  594.       window(60,_ysize+2,80,25);
  595.       gotoxy(1,1);
  596.       clrscr;
  597.       writeln('fish   = ',counts[fish]);
  598.       writeln('sharks = ',counts[shark]);
  599.       writeln('avail  = ',maxavail);
  600.       write  ('gen #  = ',generation);
  601.       set_debug_window;
  602.       fish_swim;
  603.       shark_move;
  604.       generation := generation + 1;
  605.       end;
  606. end.