Update of marching band controllers

[From Bill Powers (2004.06.11.1132 MDT)]

To whom it may concern:

Attached is a small modification of the I5.pas program, call I5a.pas. The
two matrices DiffX and DiffY have been eliminated. Inspection of the
program showed that the difference perceptions could be computed on the fly
rather than being stored, which reduced the storage requirements enough
that the matrices can be as large as 45 x 45 in size. The procedure
"PerceiveDiff" has been eliminated. A delay of 67 milliseconds on each
iteration prevents bands from moving through the display. The matrix size
is set to 10 x 10.

Best,

Bill P.

···

===========================================================================
program I5a; { was Index_zm2; { 14 December 2002 }
            {was I4 11 June 2004 WTP}
            {was I5 11 june 2004 WTP}

{CONTROLS PERCEIVED EQUALITY OF DISTANCE TO
  NEIGHBORS, LEFT-RIGHT AND UP-DOWN.
  TYPE D TO TOGGLE X DISTURBANCE OF
  POSITION 5,5 ON AND OFF}

   uses crt, graph;
   const maxrow = 10; { number of entries between borders}
          maxcol = 10;
var
     PosX,PosY,ox,oy : array[0..maxcol+1,0..maxrow+1] of real;
     Cx, Cy, x, y, x0, y0: integer;
     gx, gy, r, p, e, o, d, d0 : real ;
     key : char;
     Hsize,vsize,i : integer;
     Graphmode, Graphdriver: integer;
     maxcolor : integer;
     s: string;

Procedure InitScreen;
   begin
       GraphDriver := Detect;
       Initgraph(Graphdriver, graphmode,'c:\TP\bgi');
       hsize := getmaxx + 1; vsize := getmaxy + 1;
       maxcolor := getmaxcolor;
    end;

Procedure display;
begin
  for x := 1 to maxcol do { Plot circles}
   for y := 1 to maxrow do
    begin
     if (x = 5) and (y = 5) then setcolor(White) else setcolor(Green);
     circle(round(Cx + PosX[y,x]),round(Cy + PosY[y,x]),3);
    end;
  setcolor(lightgray); {Draw connecting lines in y}
  for x := 1 to maxcol do
   for y := 1 to maxrow do
    if y = 1 then moveto(round(Cx + PosX[y,x]),round(Cy + PosY[y,x]))
     else lineto(round(Cx + Posx[y,x]),round(Cy + PosY[y,x]));
  for y := 1 to maxrow do { Draw connecting lines in x}
   for x := 1 to maxcol do
    if x = 1 then moveto(round(Cx + PosX[y,x]),round(Cy + PosY[y,x]))
     else lineto(round(Cx + Posx[y,x]),round(Cy + PosY[y,x]));

end;

{ In one dimension,
   Distance to right neighbor is PosX[x+1] - PosX[x]
   Distance to left neighbor is PosX[x] - PosX[x-1]
   Difference in distances is (PosX[x+1] - PosX[x]) - (PosX[x] - PosX[x-1])
    which reduces to PosX[x+1] - 2*PosX[x] + PosX[x-1].
   The perceptual signal represents the difference (inequality) of distances.

   NOTE that the matrices go from 0 to max+1, where the first and
   last entries are initialized to constants defining the border
   of the field (the "chalk marks").
}

Procedure loop;
  begin
    r := 0; { ref level for difference in X and Y distance}
    for x := 1 to maxcol do
    for y := 1 to maxrow do
     begin
      if (x = 5) and (y = 5) then d := d0 else d := 0; {disturbance}

      {Perceived X difference of distances}
      p := PosX[y,x + 1] - 2*PosX[y,x] + PosX[y,x - 1];
      e := (r - p);
      ox[y,x] := ox[y,x] - gx*e; { integrating output function}
      PosX[y,x] := ox[y,x] + d; {Add disturbance to position}

      {Perceived Y difference of distances}
      p := PosY[y + 1,x] - 2*PosY[y,x] + PosY[y - 1,x];
      e := r - p;
      oy[y,x] := oy[y,x] - gy*e; { integrating output function}
      PosY[y,x] := oy[y,x] - d;
    end;
end;

procedure initprogram;
begin
  for y := 0 to maxcol+1 do
   begin
    PosX[y,0] := -200;
    PosX[y,maxcol+1] := 200;
    PosY[0,y] := -200;
    PosY[maxrow+1,y] := 200;
   end;
  d0 := 0.0;
  key := ' ';
  InitScreen;
  Cx := 200;
  Cy := 200; { defines center of the screen }
       { coefficients for control loops }
  gx := 0.2;
  gy := 0.2;
  x0 := hsize div 2; y0 := vsize div 2+20;
  setviewport(x0 - 201, y0 - 201,x0 + 201,y0 + 201,false);
end;

{-----------------------------------------------------------------------}

Begin { Main }
  initprogram;
  Repeat { Main loop }
   loop; { control loops }
   display;
   Delay(67);
   clearviewport;
   if keypressed then key := readkey else key := ' ';
   if key in ['d','D'] then
     begin { toggle disturbance on and off}
      if d0 = 0 then d0 := 100.0 else d0 := 0.0;
      setviewport(0,0,hsize-1,20,false);
      clearviewport;
      moveto(250,0);
      outtext('DISTURBANCE');
      moveto(350,0);
      if d0 > 0.0 then
       outtext('_ON') else
       outtext('OFF');
      setviewport(x0 - 201, y0 - 201,x0 + 201,y0 + 201,false);
     end;
  until key in ['q','Q'];
  closegraph;
end.