// Self-Replicating Loops & Ant, Programmed by Eli Bachmutsky, Copyleft Feb.1999
import java.awt.*;    

public class loops extends java.applet.Applet implements Runnable {
 Thread    runner;
 int       width=500,height=500, max,square,fringe, delay, refreshSteps;
 int       grid[][][], cd, steps, stepLeft, looptype=7, rule [][][][][];
 Color     loopColors[]={ Color.black,Color.blue,Color.red,Color.green,
            Color.yellow,Color.magenta,Color.white,Color.cyan,Color.orange};
 Button    pausebutton;
 boolean   cyclic, running, started=true;
 public    final int byl=0,chou1=1,chou2=2,langton=3,sdsr=4,evo=5, ants=6;
 int       xx,yy,empty=0,full=1,up=0,left=1,down=2,right=3,direction=up;
 Choice    choice;

  String langtonLoop[] = {
     " 22222222",
     "2170140142",
     "2022222202",
     "272    212",
     "212    212",
     "202    212",
     "272    212",
     "21222222122222",
     "207107107111112",
     " 2222222222222"};
  String langtonRules = new String(     // Langton Loop rules
"000000 000012 000020 000030 000050 000063 000071 000112 000122 000132 000212 "+
"000220 000230 000262 000272 000320 000525 000622 000722 001022 001120 002020 "+
"002030 002050 002125 002220 002322 005222 012321 012421 012525 012621 012721 "+
"012751 014221 014321 014421 014721 016251 017221 017255 017521 017621 017721 "+
"025271 100011 100061 100077 100111 100121 100211 100244 100277 100511 101011 "+
"101111 101244 101277 102026 102121 102211 102244 102263 102277 102327 102424 "+
"102626 102644 102677 102710 102727 105427 111121 111221 111244 111251 111261 "+
"111277 111522 112121 112221 112244 112251 112277 112321 112424 112621 112727 "+
"113221 122244 122277 122434 122547 123244 123277 124255 124267 125275 200012 "+
"200022 200042 200071 200122 200152 200212 200222 200232 200242 200250 200262 "+
"200272 200326 200423 200517 200522 200575 200722 201022 201122 201222 201422 "+
"201722 202022 202032 202052 202073 202122 202152 202212 202222 202272 202321 "+
"202422 202452 202520 202552 202622 202722 203122 203216 203226 203422 204222 "+
"205122 205212 205222 205521 205725 206222 206722 207122 207222 207422 207722 "+
"211222 211261 212222 212242 212262 212272 214222 215222 216222 217222 222272 "+
"222442 222462 222762 222772 300013 300022 300041 300076 300123 300421 300622 "+
"301021 301220 302511 401120 401220 401250 402120 402221 402326 402520 403221 "+
"500022 500215 500225 500232 500272 500520 502022 502122 502152 502220 502244 "+
"502722 512122 512220 512422 512722 600011 600021 602120 612125 612131 612225 "+
"700077 701120 701220 701250 702120 702221 702251 702321 702525 702720 ");

  String evoLoop[] = {      // 13-2 loop according to Sayama's definition
     " 222222222222222",
     "27017017017011112",
     "21222222222222212",
     "202           212",
     "272           212",
     "212           212",
     "202           212",
     "272           212",
     "212           212",
     "202           212",
     "272           272",
     "212           202",
     "202           212",
     "272           272",
     "21222222222222202",
     "20710710710410412",
     " 222222222222225"};
  String evoRules = new String(     // EvoLoop rules
"000012 000043 000122 000152 000212 000242 000422 000452 000752 001022 002141 "+
"002171 002322 011221 012121 012321 012421 012451 012526 012626 012721 012751 "+
"013421 013721 014221 014251 014321 014351 014421 014621 017221 017251 017561 "+
"017621 017721 100011 100121 100211 100244 100277 101211 101244 101277 102021 "+
"102111 102121 102131 102211 102244 102277 102324 102414 102424 102434 102511 "+
"102527 102543 102577 102717 102727 102735 105121 105424 105727 106211 106244 "+
"106277 111121 111221 111244 111251 111277 111621 112121 112131 112151 112221 "+
"112244 112277 112321 112424 112434 112527 112543 112577 112626 112727 112735 "+
"113221 113321 115424 115727 116244 116277 122244 122277 122434 122737 123244 "+
"123277 124266 124333 126276 200012 200022 200042 200052 200060 200071 200122 "+
"200152 200212 200222 200232 200242 200260 200272 200324 200423 200452 200545 "+
"200575 200620 200722 200752 201022 201122 201222 201422 201722 202022 202032 "+
"202052 202065 202073 202122 202152 202212 202222 202232 202323 202422 202452 "+
"202525 202620 202650 202722 202752 203122 203222 203422 203452 203722 204122 "+
"204222 204422 205122 205425 205725 206125 206212 206425 206725 207122 207222 "+
"207722 211222 212222 212232 212242 212272 212323 213222 214222 216222 217222 "+
"222242 222272 222342 222372 222432 222442 222732 222772 223243 223273 300013 "+
"300022 300032 300043 300074 300123 300322 300421 301021 301250 302123 302423 "+
"302521 302723 303321 312123 312423 312521 312723 324243 324251 324273 325271 "+
"327273 400001 400021 401020 401120 401220 401250 401620 402120 402150 402221 "+
"402321 402626 403120 403221 500025 500125 500215 500232 500245 500275 500425 "+
"500725 502022 502052 502125 502152 502425 502725 503120 602022 602122 602220 "+
"602422 602722 612220 622240 622270 701020 701120 701220 701250 701620 702120 "+
"702150 702221 702320 702626 703120 ");

  String bylLoop[] = {" 22",
                      "2312",
                      "2342",
                      " 25"};
 String bylRules = new String(     // Byl loop rules
"000000 000010 000031 000420 000110 000120 000311 000330 000320 000200 000242 "+
"000233 004330 005120 001400 001120 001305 001310 001320 003020 003400 003420 "+
"003120 002040 002050 002010 002020 002420 002500 002520 002200 002250 002220 "+
"040000 050000 050500 010052 010022 010400 010100 020055 400233 405235 401233 "+
"442024 452020 415233 411233 412024 412533 432024 421433 422313 501302 502230 "+
"540022 542002 512024 530025 520025 520442 523242 522020 100000 100010 100033 "+
"100330 101233 103401 103244 111244 113244 112404 133244 121351 123444 123543 "+
"122414 122434 300100 300300 300211 300233 304233 301100 301211 303211 303233 "+
"302233 344233 343233 351202 353215 314233 311233 313251 313211 312211 335223 "+
"333211 320533 324433 325415 321433 321511 321321 323411 200000 200042 200032 "+
"200022 200442 200515 200112 200122 200342 200332 200242 200212 201502 203202 "+
"202302 244022 245022 242042 242022 254202 255042 252025 210042 214022 215022 "+
"212052 212055 212022 230052 234022 235002 235022 232042 232022 220042 220020 "+
"220533 221552");

  String chou1Loop[] = {"11",
                        "3411"};
  String chou1Rules = new String(     // Chou-Reggia loop rules
"000000 000200 000110 004420 006301 001447 003030 046000 060400 062102 051060 "+
"010420 010360 012152 032000 401033 410011 431206 640406 660630 616600 636000 "+
"000440 000260 000330 004512 005103 001540 003100 045002 060600 061110 051710 "+
"010506 010320 030400 076010 460313 410633 600000 640106 610006 616100 636100 "+
"000420 000231 000705 006030 002020 001210 040000 045600 060100 050006 020320 "+
"010140 016000 036000 400035 466633 410100 606100 660060 610106 613001 676600 "+
"000600 000100 004040 006440 001010 001110 040160 041040 060166 050306 024200 "+
"010154 016100 036606 400313 423106 411033 606313 660460 614004 630160 500000 "+
"000540 000152 004010 006110 001030 003000 044400 060066 062030 054040 023010 "+
"010340 012000 036300 405033 410061 431033 640006 660615 616000 630360 500044 "+
"500011 230000 100111 101044 146004 143321 166611 121121 117044 300111 340066 "+
"366611 374017 200100 100044 104414 101601 141403 160414 161101 112244 130414 "+
"304011 340611 314001 703010 203010 100011 104674 101301 141424 160111 161301 "+
"111044 133001 301471 340261 314676 703330 240400 100441 106101 103011 141141 "+
"164104 120414 113011 133011 301303 340211 313023 733630 210102 100414 105011 "+
"103103 141121 166644 124104 113601 300101 307141 344011 331001 ");

  String chou2Loop[] = {"11",
                        "341"};
 String chou2Rules = new String(     // Chou-Reggia loop rules
"000000 000440 000547 000100 000110 000330 004040 004445 004100 001040 001010 "+
"001740 003000 003010 003030 007040 007030 007104 007110 040000 040070 "+
"047100 050007 017000 070000 070077 071010 400101 400313 401033 407103 411033 "+
"431033 500033 503330 100045 100011 100414 101044 101301 103011 107131 140414 "+
"141044 111044 133011 177711 304011 305011 305141 307141 344011 345011 354010 "+
"314001 377711 700000 700337 707100 707141 707110 717000 730007 770070 770711 ");

public void init(){
    resize(width,height);

    pausebutton = new Button("Start");
    setLayout(new BorderLayout());
    Panel p = new Panel();
    p.add(pausebutton);
    p.add(new Button("Step"));  
    choice =  new Choice();
    choice.addItem("Byl Loop");
    choice.addItem("Chou/Reggia 1");
    choice.addItem("Chou/Reggia 2");
    choice.addItem("Langton Loop");
    choice.addItem("SDSR Sayama");
    choice.addItem("Evoloop Sayama");
    choice.addItem("Langton Ant");
    p.add(choice);
    add("South",p);

    reinit();
}
public void alloc (int mx,int sq,int fr, int dly, int refresh,boolean cycl) {
    max    = mx;   // size of grid (in cells)
    square = sq;   // square size of grid's cell
    fringe = fr;   // size of fringe for grid view
    delay  = dly;  // sleep timeout im msec between generations
    refreshSteps = refresh; // repaint grid every 'refresh' generation only
    cyclic = cycl; // true for cyclic/tori grid w/t bounds
    grid   = new int[2][max][max];
    rule   = new int [9][9][9][9][9];
    for (int a = 0; a < 9; a ++)
      for (int b = 0; b < 9; b ++)
        for (int c = 0; c < 9; c ++)
          for (int d = 0; d < 9; d ++)
            for (int e = 0; e < 9; e ++)
               rule[a][b][c][d][e] = -1; 
}
public void reinit(){
    running = false;
    started = true;
    stepLeft = steps = cd = 0;
    looptype = choice.getSelectedIndex();
    switch (looptype) {
     case chou1 :
           alloc (50,7,1,200,1,true);
           readLoopAndRules (chou1Loop,chou1Rules);
           clearundefrules();
           break;
     case chou2 :
           alloc (50,7,1,200,1,true);
           readLoopAndRules (chou2Loop,chou2Rules);
           clearundefrules();
           break;
     case byl:
           alloc (50,7,1,200,1,true);
           readLoopAndRules (bylLoop,bylRules);
           clearundefrules();
           break;
     case langton:
          alloc (120,4,0,10,1,false);
          readLoopAndRules (langtonLoop, langtonRules);
          clearundefrules();
          break;
     case sdsr:
          alloc (120,4,0,10,10,true);
          readLoopAndRules (langtonLoop, langtonRules);
          set_undefined_rule();   //  For SDSR/Evoloop only
          break;
     case evo:
          alloc (150,3,0,20,10,true);
          readLoopAndRules (evoLoop,evoRules);
          set_undefined_rule();   //  For SDSR/Evoloop only
          break;
     case ants:
          alloc (100,4,0,1,1,false);
          xx = yy = max/2;
          break;
    }
}
public void clearundefrules() { // Clearance of undefined rules
      for (int a = 0; a < 9; a ++)
       for (int b = 0; b < 9; b ++)
        for (int c = 0; c < 9; c ++)
         for (int d = 0; d < 9; d ++)
          for (int e = 0; e < 9; e ++) 
           if (rule[a][b][c][d][e] == -1) rule[a][b][c][d][e] = a;
}
public void readLoopAndRules(String loop[], String looprules) {
  for (int i=0,y=max/2; i < loop.length; i++,y++) 
     for (int j=0, x=max/2; j < loop[i].length(); j++,x++) {
        String s =  loop[i].substring(j,j+1);
        if (s.equals(" ")) s = "0";
        grid[cd][x][y] = Integer.parseInt (s);
     }
  int c,t,r,b,l,i;
  for (int len=0; len <looprules.length(); len +=7 )   {
     c = Integer.parseInt(looprules.substring(len  ,len+1));
     t = Integer.parseInt(looprules.substring(len+1,len+2));
     r = Integer.parseInt(looprules.substring(len+2,len+3));
     b = Integer.parseInt(looprules.substring(len+3,len+4));
     l = Integer.parseInt(looprules.substring(len+4,len+5));
     i = Integer.parseInt(looprules.substring(len+5,len+6));
     rule[c][t][r][b][l] = i;     //       T 
     rule[c][l][t][r][b] = i;     //    L  C R   >>=>> I (next state)
     rule[c][b][l][t][r] = i;     //       B
     rule[c][r][b][l][t] = i;     //   (with rotations)
  }
}
public boolean inSheath(int t, int r, int b, int l)
{ int f = 0;
  if ((t == 1) || (t == 2) || (t == 4) || (t == 6) || (t == 7)) f ++;
  if ((r == 1) || (r == 2) || (r == 4) || (r == 6) || (r == 7)) f ++;
  if ((b == 1) || (b == 2) || (b == 4) || (b == 6) || (b == 7)) f ++;
  if ((l == 1) || (l == 2) || (l == 4) || (l == 6) || (l == 7)) f ++;
  return (f > 1) ? true : false;
}
public void set_undefined_rule()  // Sayama's rules
{ int a, b, c, d, e;
  for (a = 0; a < 9; a ++)
   for (b = 0; b < 9; b ++)
    for (c = 0; c < 9; c ++)
     for (d = 0; d < 9; d ++)
      for (e = 0; e < 9; e ++)
       if (rule[a][b][c][d][e] == -1) {
         // Definition of rules which concern the state 8 
         if (a == 8) rule[a][b][c][d][e] = 0;
         else if ((b == 8) || (c == 8) || (d == 8) || (e == 8))
              switch(a) {
               case 0: case 1:
		    if (((b >= 2) && (b <= 7)) || ((c >= 2) && (c <= 7)) ||
			((d >= 2) && (d <= 7)) || ((e >= 2) && (e <= 7)))
                         rule[a][b][c][d][e] = 8;
                    else rule[a][b][c][d][e] = a;
		    break;
               case 2: case 3: case 5:
                    rule[a][b][c][d][e] = 0;
		    break;
               case 4: case 6: case 7:
                    rule[a][b][c][d][e] = 1;
		    break;
               }
         else    // Extension of rules
           switch (a) {
           case 0:
             if(((b==1)||(c==1)||(d==1)||(e==1)) && inSheath(b,c,d,e))
                    rule[a][b][c][d][e] = 1;
             else
                    rule[a][b][c][d][e] = 0;
            break;
           case 1:
            if(((b==7)||(c==7)||(d==7)||(e==7)) && inSheath(b,c,d,e))
                    rule[a][b][c][d][e] = 7;
            else if(((b==6)||(c==6)||(d==6)||(e==6)) && inSheath(b,c,d,e))
                    rule[a][b][c][d][e] = 6;
            else if(((b==4)||(c==4)||(d==4)||(e==4)) && inSheath(b,c,d,e))
                    rule[a][b][c][d][e] = 4;
            break;
           case 2:
            if ((b == 3) || (c == 3) || (d == 3) || (e == 3))
                    rule[a][b][c][d][e] = 1;
            else if ((b == 2) || (c == 2) || (d == 2) || (e == 2))
                    rule[a][b][c][d][e] = 2;
            break;
           case 4: case 6: case 7:
            if(((b==0) || (c==0) || (d==0) || (e==0)) && inSheath(b,c,d,e))
                    rule[a][b][c][d][e] = 0;
            break;
          }
       }
  rule[1][1][1][5][2] = 8;
  rule[1][1][5][2][1] = 8;
  rule[1][5][2][1][1] = 8;
  rule[1][2][1][1][5] = 8;
       // Clearance of undefined rules 
  for (a = 0; a < 9; a ++)
    for (b = 0; b < 9; b ++)
      for (c = 0; c < 9; c ++)
        for (d = 0; d < 9; d ++)
          for (e = 0; e < 9; e ++) 
            if (rule[a][b][c][d][e] == -1) rule[a][b][c][d][e] = 8;
}
public void SetNextGeneration() {
  int xp1, yp1, xm1, ym1;
  if ( ! cyclic)
   for (int X=1;X<max-1;X++) 
    for (int Y=1;Y<max-1;Y++) 
     grid[1-cd][X][Y] = rule [grid[cd][X][Y]] [grid[cd][X][Y-1]]
               [grid[cd][X+1][Y]] [grid[cd][X][Y+1]] [grid[cd][X-1][Y]];
 else
   for (int X=0;X<max;X++) 
    for (int Y=0;Y<max;Y++) {
      xp1 =  (X == max-1) ? 0     : X+1;
      yp1 =  (Y == max-1) ? 0     : Y+1;
      xm1 =  (X == 0)     ? max-1 : X-1; 
      ym1 =  (Y == 0)     ? max-1 : Y-1;
      grid[1-cd][X][Y] = rule [grid[cd][X][Y]] [grid[cd][X][ym1]]
              [grid[cd][xp1][Y]] [grid[cd][X][yp1]] [grid[cd][xm1][Y]];
  }
  cd = 1-cd;
}
public boolean action(Event ev, Object arg){
 if ( ev.target instanceof Button) {
        String button = (String) arg;
        if      (button.equals("Stop")) {
            pausebutton.setLabel("Start");
            running = false;
        }
        else if (button.equals("Start")) {
            pausebutton.setLabel("Stop");
            running = true;
        }
        else if (button.equals("Step")) {
            stepLeft = 1;
            if (running) {
              pausebutton.setLabel("Start");
              running = false;
            }
        }
        return true;
 } else  if ( ev.target instanceof Choice) {
       looptype = choice.getSelectedIndex();
       if (pausebutton.getLabel().equals("Start")) {
             reinit();
             repaint();
       } 
       return true;
 } else return false;
}
public void start(){if(runner==null){runner=new Thread(this);runner.start();}}
public void stop() {runner = null;}
public void run()   {
  repaint();
  while (true) {
    if (running || stepLeft > 0) {
      if (looptype == ants){
        if ( grid[cd][xx][yy] == empty) {
            direction++;
            grid[cd][xx][yy]=full;    
        } else {    
            direction--;
            grid[cd][xx][yy]=empty;
        }
	repaint();
        if (direction>=4)   direction%=4;
        if (direction< 0)   direction+=4;
        if (direction == up)    { if (yy>0) yy--; }
        if (direction == left)  { if (xx>0) xx--; }
        if (direction == down)  { if (yy<max-1) yy++;}
        if (direction == right) { if (xx<max-1) xx++;}
	try{Thread.sleep(1);}catch(InterruptedException e){};
      } else {
           SetNextGeneration();
           if( (steps % refreshSteps == 0)|| (stepLeft > 0)) repaint();   
      }
      if (stepLeft > 0) stepLeft--;
      showStatus("Steps:"+(steps++));
      try{Thread.sleep(delay);}catch(InterruptedException e){};
    }
    else try{Thread.sleep(500);}catch(InterruptedException e){};
  }
}
public void update(Graphics g) {  paint(g);}
public void paint (Graphics g) {
 if (looptype == ants) {
    if (started) {
       started=false;
       g.clearRect(0,0,width,height);
       for (int X=0;X<max;X++) 
         for (int Y=0;Y<max;Y++) {
          g.setColor( (grid[cd][X][Y] == empty) ? Color.black : Color.red );
          g.fillRect(X*(square+fringe),Y*(square+fringe),square,square);
        }
    } else {
        g.setColor( (grid[cd][xx][yy] == empty) ? Color.black : Color.red );
        g.fillRect(xx*(square+fringe),yy*(square+fringe),square,square);
    }
 } else {
     if (started) {
        g.clearRect(0,0,width,height);
        started = false;
     }
     for (int X=0;X<max;X++)
       for (int Y=0;Y<max;Y++) {
             g.setColor( loopColors [grid[cd][X][Y]]);
             g.fillRect(X*(square+fringe),Y*(square+fringe),square,square);
       }
 }
}
} 
