Xor-life

For discussion of other cellular automata.
Post Reply
User avatar
Felixor
Posts: 9
Joined: April 27th, 2021, 2:50 am

Xor-life

Post by Felixor » April 29th, 2021, 7:52 am

Xor-life was created by a helpful Anon and me on the /sci/ board (archived version of the thread: https://archived.moe/sci/thread/13004818/) in the attempt to look for an interesting self-referential cellular automaton.
Unlike cgol there isn't a global rule that determines cell behaviour, but rather a grid of rules is layed on top of the grid of cells. For every cell there is a corresponding rule-cell that contains the rule this cell follows.

The rules are enumerated from BS to B012345678S012345678 as 18 bit binary numbers (9 bits for B and 9 bits for S), counting all totalistic rules from 0 to 262143. For example, B0S1 would be 000000001000000010 (514 in base 10).

So this is how it works:

Two grids, a statespace and a rulespace.
To advance to the next generation, every rulecell checks a 3x3 area of rulecells around it. It's new value is determined by the bitwise XOR (^) of all the rulecells that correspond to living cells in the statespace, including the value of the rulecell itself.
Then every cell in the statespace evolves according to the rule in its corresponding rulecell.
xor-life.png
xor-life.png (41.2 KiB) Viewed 2011 times
If the statespace is filled with random dead and alive cells and the rulespace is filled with random rules, pretty much always chaos ensures and fills everything with noise. We assumed this had to do with B1 rules (B0 rules are irrelevant because rulecells with no living neighbors that are dead get assigned 0 as rule). So we started restricting the amount of randomness that was used to generate the rulespace of generation 0. We found some very interesting things.

The first discovery was when the ruleset B01S23 was disallowed a lot of still life (not surprising with S0 possible), spaceships, puffers, rakes and replicating patterns formed. We called it NAND 1548 for the time being because you can generate the rulespace by generating a random 18 bit number and taking it bitwise NAND (&~) with 1548.
2d_nand1548.gif
2d_nand1548.gif (1.36 MiB) Viewed 2011 times
Another interesting variant was disallowing B01S236, as well as some others. Both NAND B01S23 and NAND B01S236 seem to only contain spaceships that travel with light space, but no proof for that yet. NAND B136S167 contains at least one diagonal spaceship with 3c28 and NAND B013456S1678 contains at least one knightship with period 7 and speeds of c7 and 2c7 in the vertical and horizontal axis, but non-photon orthogonal spaceships exist as well in this rule.

When soup is subjected to this, many structures form. Single structures are often a lot more restricted than just the rules that are not allowed by the NAND.

It should be noted that a c1 spaceship formed in NAND B01S23 and a 3c28 diagonal glider formed in NAND B136S167 can fly side by side in the same grid and even hit each other and interact. The NAND merely restricts the initial conditions of the soup.

We believe this is a class 4 automaton.

In NAND B01S23 and NAND B01S236 we have found so far:
- oscillators of various periods
- 4p2 orthogonal spaceship guns of firing period 16, 32, but all powers of 2 are possible after that because colliding mechanics allow easily dividing the frequency by 2
- guns that shoot sometimes more than one spaceship with periods of 80, 180, 24 and 120, as well as different spaceships with p14
- NOT-gates, OR-gates, AND-gates, a set-reset latch
- spaceship doublers and quadruplers
- by carefully colliding smaller spaceships we have created factories for ships with periods 2, 4, 8, 10, 14, 16
- and spaceship-collision synthesis for some oscillators
- rakes and backrakes of various periods
- puffers producing oscillators

since there seems to be a limit on how many images I can attach in one post, I have uploaded most of the gifs here: https://imgur.com/a/HvTFv6X

Mod edit: files mirrored here:
xor-life.zip
(9.55 MiB) Downloaded 7 times
The data files for most of these can be found in the /sci/ thread.

Note that all of this was found mainly by hand, and the automaton itself is not older than about a week.

I'm using self-written spaghetti code at the moment, but at least it works. I'm also constantly modifying it as I go. The current source code can be found here, just keep in mind that it is bad: https://pastebin.com/bzy93FK0

Mod edit: code copied below:

Code: Select all

int[][] tab = new int[200][200];
int[][] tab2 = new int[200][200];
int[][] rtab = new int[200][200];
int[][] rtab2 = new int[200][200];
int[][] ptab = new int[200][200];
int[][] prtab = new int[200][200];
int[][] ctab = new int[200][200];
int[][] crtab = new int[200][200];

int[][][] ship = new int[80][20][20];
int[][][] rship = new int[80][20][20];

int n = 34;
int offset = -3;
int j = 0;
int r;
String temp, slog, onscreenconsole;
int stop = 0;
int px1, px2, py1, py2, selection, mark, record, frames, random, oneframe, delayon, periodb, period, movemode, copied, cx1, cx2, cy1, cy2, cmode, cf, cf2, cframes, tmp2, diff;

float sf = 1;
float sx = 0;
float sy = 0;

//String[] readinglist = {"37020_27290_p120gg.txt", "47834_60569_p60.txt", "28621_27721_p120gg_monodirectional.txt",
//"71195_16368_glidersideways.txt", "57758_85936_p128_gun.txt", "57758_76359_p180_gun.txt", "57758_66730_p32_gun.txt",
//"39469_318789_p16_gun_tiny.txt", "41605_70452_p32_gun_tiny_v2.txt", "41605_292692_latch_prototype3.txt", "29256_67675.txt"}; //importable files

String[] readinglist = {"53644_830_knightship.txt", "51943_1138_rake_p24_makes_4p2b.txt", "11932_3499_backrake_p4_makes_4p2b.txt", "95440_19926_p16_wide_factory.txt", "60553_35248_p16_shipfactory_with_p32.txt", "64335_28312_p4_shipfactory_v2_p32.txt", "4023_26238_coolshipfarm_prototype.txt", "44894_49376_p14_factory_with_p64.txt",
"41605_70452_p32_gun_tiny_v2.txt", "9170_14818_squidfarm.txt", "4023_2959_p32_squidfarm.txt", "47822_123223_shipfactory_almost.txt", "81030_49112_p8_shipfactory.txt"};

//String[] readinglist = {"73923_29625_squidcollisions.txt", "73923_46452_squidcollisions2.txt", "73923_50185_squidcollisions3.txt", 
//"73923_52734_squidcollisions4.txt", "73923_58820_squidcollisions5.txt", "73923_62781_squidcollisions6.txt"};

String[] shippinglist = {"shiplist.txt"};

int imported = 0;
  
void setup() {
  size(1000, 1000);
  background(0);
  noStroke();
  selection = 0;
  mark = 0;
  record = 0;
  frames = 0;
  oneframe = 0;
  random = int(random(0,100000));
  delayon = 0; 
  periodb = 0;
  period = 0;
  movemode = 0;
  copied = 0;
  cmode = 0;
  cf = 0;
  cf2 = 0;
  tmp2 = 0;
  slog = "";
  px1 = 0;
  py1 = 0;
  onscreenconsole = "";
  
  cframes = 70;
  
  cx1 = 0; cx2 = 0; cy1 = 0; cy2 = 0;
  
  /*for (int j = 0; j < ship[0][0].length; j++) {
    for (int i = 0; i < ship[0].length; i++) {    
      for (int k = 0; k < ship.length; k++) {    
        ship[k][i][j] = 0;
        rship[k][i][j] = 0;
      }
    }
  } */
  
  //print(convertRules(148269) + " " + convertRules(92209) + " " + convertRules(207644));
  //delay(10000);
  
  scale(sf);
  translate(sx, sy);
  resetfield();
  
  //r = replacerule();
  //r = 91491;
  r = 0;
  
}

void draw() {
  background(0);
  scale(sf);
  translate(sx, sy);
  noFill();
  stroke(255);
  rect(-1, -1, 1002, 1002);
  noStroke();
  
  if (delayon == 1) {delay(100);}
  j++;
  if (stop == 0) {
    make();
    frames++;
    if (record == 1) {
      save(random + "_" + frames + ".png");
    }
    if (periodb == 1) {
      int diff = 0;
      for (int i1 = 0; i1 < 200; i1++) {
        for (int i2 = 0; i2 < 200; i2++) {
           if (ptab[i1][i2] != tab[i1][i2] || prtab[i1][i2] != rtab[i1][i2]) {
             diff = 1;
           }
        }
      }
      period++;
      if (diff == 0) {
        print("period found: " + period + "\n");
        periodb = 0;
      }
    }
    if (cmode == 1) {
      cf++;
      if (cf%cframes == 1) {
        
        //Hier muss die neue Anordnung generiert werden
        
        for (int i1 = 0; i1 < 200; i1++) {
          for (int i2 = 0; i2 < 200; i2++) {
             tab[i1][i2] = 0;
             rtab[i1][i2] = 0;
          }
        }
        slog += " " + str(tmp2) + " " + diff + "\n";
        println(slog.split("\n")[slog.split("\n").length - 1]);
        
        /*if (diff > 0) {
          println(slog.split("\n")[slog.split("\n").length - 1]);
        }*/
        
        //int[][] tmp = {{0, 80+1, 80+0, 0, 0}, {1, 80+1+cf2%6, 80+4+cf2/6, 2, 0}};
        //int[][] tmp = {{22, 80+cf2%10, 80-cf2/10%6, 0, 0}, {22+cf2/360%2, 80, 80+3, 2, 0}, {22+cf2/720%2, 80+4+cf2/60%6, 80, 1, 0}};
        //int[][] tmp = {{24, 87, 76, 0, 0}, {24, 80, 83, 2, 0}};
        //int[][] tmp = {{1, 80, 77, 0, 0}, {24, 78, 82, 3, 0}};
        //int[][] tmp = {{0, 85+0, 85+0, 3, 0}, {0, 85+2+cf2%7, 85-2-cf2/7%7, 0, 0}, {0, 85+2+cf2/49, 85+2, 2, 0}};
        int[][] tmp = {{24, 80+cf2%10, 80-cf2/10%6, 0, 0}, {24+cf2/60, 80-4, 80+4, 3, 0}};
        
        //slist [zeile schiffnummer], dann [0] art/phase, [1] dx, [2] dy, [3] drehung, [4] gespiegelt
        
        slog += "{{";
        for (int i = 0; i < tmp.length; i++) {   
          for (int j = 0; j < tmp[0].length; j++) {        
            slog += tmp[i][j] + ", ";
          }
          slog = slog.substring(0, slog.length() - 2) + "}, {";
        }
        slog = slog.substring(0, slog.length() - 3) + "}";
        gcol(tmp);
        tmp2 = 1;
        cf2++;
      }
      diff = 0;
      for (int i1 = 0; i1 < 200; i1++) { //(int i1 = 0; i1 < 200; i1++)
        for (int i2 = 0; i2 < 200; i2++) { //(int i2 = 0; i2 < 200; i2++)
           diff += tab[i1][i2];
        }
      }
      if (diff == 0) {
        cf += cframes - cf%cframes;
        tmp2 = 0;
      }     
    }
  } else {    
    //n = rtab[max(0, min(mouseX/5, 199))][max(0, min(mouseY/5, 199))];
    for (int y = 0; y < 200; y++){
      for (int x = 0; x < 200; x++){
        noStroke();
        fill(color(255*tab[x][y]));        
        rect(x*5, y*5, 5, 5);
        if (rtab[x][y] == 0 && mark == 1) {
          fill(255, 255, 0);
          rect(x*5+1, y*5+1, 3, 3);
        }
      }
    }
    stroke(255, 0, 0);
    strokeWeight(1/sf);
    line(mouseX/sf-sx,-sy,mouseX/sf-sx, height/sf-sy);
    line(-sx,mouseY/sf-sy,width/sf-sx,mouseY/sf-sy);
    noStroke();
    textAlign(LEFT);
    textSize(20/sf);
    fill(255);   
    fill(255);    
    text("(" + px1 + ", " + py1 + ") " + str(ceil(px2/5-px1/5)+1) + "x" + str(ceil(py2/5-py1/5)+1), (-sx+10/sf), (-sy+30/sf));  
    noStroke();
    
    if (selection == 1) {
      noFill();
      stroke(255);
      rect(5*(px1/5), 5*(py1/5), 5*(px2/5-px1/5)+5, 5*(py2/5-py1/5)+5);
      noStroke();
    }
  }
  if (oneframe == 1) {
    stop = 1;
    oneframe = 0;
  }
  //print(j + "\n");
  //if(j%150==0){r = replacerule(); print(r + "\n"); resetfield();}//save("k2_" + str(j) + ".png");}
}

void make() { 
  for (int y = 0; y < 200; y++){
    for (int x = 0; x < 200; x++){     
      rtab2[x][y] = r;
      for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
          if(tab[(x+i+200)%200][(y+j+200)%200] == 1){
            rtab2[x][y] = rtab2[x][y]^rtab[(x+i+200)%200][(y+j+200)%200];
          } 
        }
      }
     //rtab2[x][y] = rtab2[x][y]&~unbinary("000000011000001100"); //the best xoxo
     //rtab2[x][y] = rtab2[x][y]&~unbinary("000000011000001010");
     //rtab2[x][y] = rtab2[x][y]&~unbinary("000000011000010101");
     //rtab2[x][y] = rtab2[x][y]&~4108;
    }
  }
  for (int y = 0; y < 200; y++){
    for (int x = 0; x < 200; x++){
      n = 0;
      for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
          n += tab[(x+i+200)%200][(y+j+200)%200];
        }
      }
      n -= tab[x][y];      
      rtab[x][y] = rtab2[x][y];
      
      //rtab[x][y] = 4108;
      
      if (tab[x][y] == 0) {
        tab2[x][y] = rule(n, rtab[x][y]>>9);        
      } else {
        tab2[x][y] = rule(n, rtab[x][y]&1023);
      }

      //print(r + "\n");     
    }
  }
  for (int y = 0; y < 200; y++){
    for (int x = 0; x < 200; x++){
      tab[x][y] = tab2[x][y];
      fill(color(255*tab[x][y]));
      
      //fill(255*((rtab[x][y]>>13)%2), 0, 0);
      
      //fill(color(31*((rtab[x][y]>>9)%8), 31*((rtab[x][y]>>12)%8), 31*((rtab[x][y]>>15)%8)));
      //fill(color(31*((rtab[x][y]>>0)%8), 31*((rtab[x][y]>>3)%8), 31*((rtab[x][y]>>15)%6)));
      //if (rtab[x][y] != "00000000000000000000000000000000") {print(rtab[x][y] + "\n");}
      rect(x*5, y*5, 5, 5);
          
      //fill(color(31*((rtab[x][y]>>0)%8), 31*((rtab[x][y]>>3)%8), 31*((rtab[x][y]>>15)%6)));
      //rect(x*5, y*5, 5, 5);
      //fill(color(31*((rtab[x][y]>>9)%8), 31*((rtab[x][y]>>12)%8), 31*((rtab[x][y]>>15)%8)));
      //rect(x*5+1, y*5+1, 3, 3);
      //fill(color(255*tab[x][y]));
      //rect(x*5+2, y*5+2, 1, 1);
    }
  }
}

int rule(int a, int rule) {
  return(rule >> a)%2;
}

int replacerule() {
  return int(random(0,262144))%262144;
}


void resetfield() {
  //randomSeed(226);
  print("Seed: " + j + "\n");
  //n = replacerule();
  for (int y = 0; y < 200; y++){
    for (int x = 0; x < 200; x++){
      tab[x][y] = 0;   
      rtab[x][y] = replacerule();
      fill(color(255*tab[x][y]));
      rect(x*5, y*5, 5, 5);
      //print(tab[i][0]);
      
      rtab[x][y] = rtab[x][y]&~unbinary("000000011001001100");
      //rtab[x][y] = rtab[x][y]&~unbinary("000000011000001010");
      //rtab[x][y] = rtab[x][y]&~unbinary("000000011000010101");     
      //rtab[x][y] = rtab[x][y]&~unbinary("000000011000010110");
      //rtab[x][y] = rtab[x][y]&~unbinary("001011011111000010"); //B01346S1678
      
      //rtab[x][y] = rtab[x][y]&~n;
      //rtab[x][y] = rtab[x][y]&~38018;
      
    } 
  }
  for (int y = 70; y < 130; y++){
    for (int x = 70; x < 130; x++){
      tab[x][y] = int(random(0,2))%2;   
      fill(color(255*tab[x][y]));
      rect(x*5, y*5, 5, 5);
      //print(tab[i][0]);
    } 
  }
}

void mousePressed() {
  if (mouseButton == LEFT) {
    px1 = int(mouseX/sf-sx);
    py1 = int(mouseY/sf-sy); 
    selection = 0;
  } else {
    resetfield();
  }
}

void keyPressed() {
  if (movemode == 0) {
  if (key == 'p') {
    stop = (stop + 1)%2;
    if (stop == 0) {
      print("Unpaused\n");
    } else {
      print("Paused\n");    
    }
  }
  if (key == 'n') {
    mark = (mark + 1)%2;
    if (mark == 0) {
      print("nullrules hidden\n");
    } else {
      print("nullrules shown\n"); 
    }
  }
  if (key == 'd') {
    delayon = (delayon + 1)%2;
    if (delayon == 0) {
      print("Delay off\n");
    } else {
      print("Delay on\n");    
    }
  }
  if (key == 'r') {
    record = (record + 1)%2;
    if (record == 0) {
      print("recording stopped\n");      
    } else {
      print("recording started\n");    
    }
  }
  if (key == 's') {    
    PrintWriter output;
    
    output = createWriter(random + "_" + j + ".txt");
    
    //output.print("Test"); 
    int temp3 = 0;
    String bb = " rulespace \n{";
    for (int i = py1/5; i <= py2/5; i++) {
      bb += "\n{";
      for (int j = px1/5; j <= px2/5; j++) {
        bb += "      ".substring(0, 6-str(rtab[j][i]).length()) + rtab[j][i] + ", ";
        temp3 = temp3|rtab[j][i];
      }
      bb = bb.substring(0, bb.length() - 2) + "},";
    }
    bb = bb.substring(0, bb.length() - 1) + "\n};\n\n statespace \n{";
    for (int i = py1/5; i <= py2/5; i++) {
      bb += "\n{";
      for (int j = px1/5; j <= px2/5; j++) {
        bb += tab[j][i] + ", ";
      }
      bb = bb.substring(0, bb.length() - 2) + "},";
    }
    bb = bb.substring(0, bb.length() - 1) + "\n};";
    temp3 = 524287-temp3;
    bb = "maximum restriction NAND " + convertRules(temp3) + "\n\n" + bb;
    //print("NAND " + convertRules(temp3) + "\n");
    output.print(bb);
    //print(bb);
    print("selection saved!\n");
    
    output.flush();
    output.close();
  }
  if (keyCode == BACKSPACE) {
    for (int i = py1/5; i <= py2/5; i++) {
      for (int j = px1/5; j <= px2/5; j++) {
        //print(j + " " + i + "\n");
        if (i < 200 && j < 200) {
          tab[j][i] = 0;
          rtab[j][i] = 0;
        }
      }
    }
  }
  if (keyCode == ENTER) {
    for (int i = 0; i < 200; i++) {
      for (int j = 0; j < 200; j++) {
        //print(j + " " + i + "\n");
        if (i > py2/5 || i < py1/5 || j > px2/5 || j < px1/5) {
          tab[j][i] = 0;
          rtab[j][i] = 0;
        }
      }
    }
  }
  if (keyCode == RIGHT) {
    oneframe = 1;
    stop = 0;
  }
  if (key == 'z') { //period
    periodb = (periodb + 1)%2;
    if (periodb == 0) {
      print("period measurement stopped\n");
    } else {
      print("period measurement started\n");  
      period = 0;
      for (int i = 0; i < 200; i++) {
        for (int j = 0; j < 200; j++) {
           ptab[i][j] = tab[i][j];
           prtab[i][j] = rtab[i][j];
        }
      }
    }
  }
  if (key == 'c') { //copy
    copied = 1;
    print("copied\n");
    for (int i = py1/5; i <= py2/5; i++) {
        for (int j = px1/5; j <= px2/5; j++) {
          //print(j + " " + i + "\n");
          if (j < 200 && i < 200) {
            ctab[j][i] = tab[j][i];
            crtab[j][i] = rtab[j][i];
          }
        }
     }
    cx1 = px1; cx2 = px2; cy1 = py1; cy2 = py2;
  }
  if (key == 'v') { //paste
    if (copied == 1) {
      print("pasted\n");
      for (int i = cy1/5; i <= cy2/5; i++) {
        for (int j = cx1/5; j <= cx2/5; j++) {
          //print(j + " " + i + "\n");
          if (j < 200 && i < 200 && j-cx1/5+px1/5 < 200 && i-cy1/5+py1/5 < 200 && j-cx1/5+px1/5 >= 0 && i-cy1/5+py1/5 >=0) {
            tab[j-cx1/5+px1/5][i-cy1/5+py1/5] = ctab[j][i];
            rtab[j-cx1/5+px1/5][i-cy1/5+py1/5] = crtab[j][i];
          }
        }
      }
    }
  }
  if (key == '5') {
    int tmp4 = 0;
    for (int i = 0; i < 200; i++) {
        for (int j = 0; j < 200; j++) {
           tmp4 += tab[i][j];
        }
    }
    println("cell count: " + tmp4);
  }
  if (key == ' ') { //rotate 90 CW
    println("Clipboard rotated");
    ctab = rotatecw(ctab);
    crtab = rotatecw(crtab);
    int tmp = cx1;
    cx1 = 1000 - cy1;
    cy1 = tmp;
    tmp = cx2;
    cx2 = 1000 - cy2;
    cy2 = tmp;
    tmp = cx1;
    cx1 = cx2;
    cx2 = tmp;
  }
  if (key == 'm') { //mirror left/right
    println("Clipboard mirrored");
    ctab = mirrorlr(ctab);
    crtab = mirrorlr(crtab);
    int tmp = cx1;
    cx1 = 1000 - cx2;
    cx2 = 1000 - tmp;
  }
  if (key == 'i') {
    String[] lines = loadStrings(readinglist[imported%readinglist.length]);
    println("loaded file " + readinglist[imported%readinglist.length] + " (" + str(imported%readinglist.length+1) + "/" + readinglist.length + ")");
    imported++;
    boolean read = false;
    String a = "";
    String b = "";    
    int temp1, temp2;
    for (int i = 0 ; i < lines.length; i++) {
      if (read) {
        a += lines[i];
      }
      if (lines[i].contains("rulespace")) {
        read = true;
      } else if (lines[i].contains(";")) {
        read = false;
      }
    }  
    for (int i = 0 ; i < lines.length; i++) {
      if (read) {
        b += lines[i];
      }
      if (lines[i].contains("statespace")) {
        read = true;
      } else if (lines[i].contains(";")) {
        read = false;
      }
    }
    a = a.replace(";", "").replace(" ", "").replace("},{", "s").replace(",", "c").replace("}", "").replace("{", "");
    b = b.replace(";", "").replace(" ", "").replace("},{", "s").replace(",", "c").replace("}", "").replace("{", "");
    temp1 = 0;
    temp2 = 0;
    //int[][] tab1 = new int[20][20];
    //int[][] tab2 = new int[20][20];    
    for (String i : a.split("s")) {
      for (String j : i.split("c")) {
        crtab[temp1][temp2] = int(j); 
        temp1++;
      } 
      temp2++;
      temp1 = 0;
    }
    temp2 = 0;
    for (String i : b.split("s")) {
      for (String j : i.split("c")) {
        ctab[temp1][temp2] = int(j); 
        temp1++;
      } 
      temp2++;
      temp1 = 0;
    }
    cx1 = 0;
    cx2 = 5*a.split("s")[0].split("c").length;
    cy1 = 0;
    cy2 = 5*a.split("s").length;
    copied = 1;    
  }
  if (key == 'q') {                                           ///collision mode
    cmode = (cmode+1)%2;
    if (cmode == 1) {
       cf = 0;
       cf2 = 0;
       print("collision mode started\n");
    } else {
       print("collision mode stopped\n");     
       //save slog       
       /*cmode =  0;
       PrintWriter output;   
       output = createWriter("collision_log_" + random + "_" + j + ".txt");
       output.print(slog.substring(3, slog.length()));  
       output.flush();
       output.close();    
       print("collision log saved!\n");*/
    }  
  }
  if (key == 'w') { //save ship
    print("saved ship\n");
    for (int i = py1/5; i <= py2/5; i++) {
        for (int j = px1/5; j <= px2/5; j++) {
          //print(j + " " + i + "\n");
          if (j < 200 && i < 200) {
            ship[0][j-px1/5][i-py1/5] = tab[j][i];
            rship[0][j-px1/5][i-py1/5] = rtab[j][i];
          }
        }
     }
     ship[0][int(mouseX/sf-sx)/5 - px1/5][int(mouseY/sf-sy)/5  - py1/5] += 2;
  }
  
  if (key == 'e') { //import ships
    String[] lines = loadStrings(shippinglist[0]);
    println("loaded shipping list " + shippinglist[0]);
    
    boolean read;
    String a;
    String b;    
    int temp1, temp2;
    int c = 0;
    int d = 0;
    
    while (c < lines.length) {
      read = false;
      a = "";
      b = "";   
      for (int i = c ; i < lines.length; i++) {
        if (read) {
          a += lines[i];
        }
        if (lines[i].contains("rulespace")) {
          read = true;
        } else if (lines[i].contains(";") && read) {
          read = false;
          break;
        }
      }  
      for (int i = c ; i < lines.length; i++) {
        if (read) {
          b += lines[i];
        }
        if (lines[i].contains("statespace")) {
          read = true;
        } else if (lines[i].contains(";") && read) {
          read = false;
          c = i + 1;
          break;
        }
      }
      a = a.replace(";", "").replace(" ", "").replace("},{", "s").replace(",", "c").replace("}", "").replace("{", "");
      b = b.replace(";", "").replace(" ", "").replace("},{", "s").replace(",", "c").replace("}", "").replace("{", "");
      temp1 = 0;
      temp2 = 0;
      //int[][] tab1 = new int[20][20];
      //int[][] tab2 = new int[20][20];  
      //println(d + " " + b);
      for (String i : a.split("s")) {
        for (String j : i.split("c")) {
          rship[d][temp1][temp2] = int(j); 
          temp1++;
          //rship[0][0][0] = 1;           
        } 
        temp2++;
        temp1 = 0;
      }      
      temp2 = 0;
      for (String i : b.split("s")) {
        for (String j : i.split("c")) {
          ship[d][temp1][temp2] = int(j); 
          temp1++;
        } 
        temp2++;
        temp1 = 0;
      }
      d++;      
    } 
    
    for (int j = 0; j < ship[0][0].length; j++) {
      for (int i = 0; i < ship[0].length; i++) {    
        for (int k = 0; k < 1; k++) {    
          //print(ship[k][i][j]);
          //print(rship[k][i][j]);
        }
      }
    } 
  }
  
  } else { //movemode
    if (keyCode == RIGHT) {
      for (int i = py1/5; i <= py2/5; i++) {
        for (int j = px2/5; j >= px1/5; j--) {
          //print(j + " " + i + "\n");
          tab[(j+1+200)%200][(i+200)%200] = tab[(j+200)%200][(i+200)%200];
          rtab[(j+1+200)%200][(i+200)%200] = rtab[(j+200)%200][(i+200)%200];
        }
      }
      px1 += 5;
      px2 += 5;
      if (px1 >= 1000 && px2 >= 1000) {
        px1 = px1%1000;
        px2 = px2%1000;
      }
    }
    if (keyCode == LEFT) {
      for (int i = py1/5; i <= py2/5; i++) {
        for (int j = px1/5; j <= px2/5; j++) {
          //print(j + " " + i + "\n");
          tab[(j-1+200)%200][(i+200)%200] = tab[(j+200)%200][(i+200)%200];
          rtab[(j-1+200)%200][(i+200)%200] = rtab[(j+200)%200][(i+200)%200];
        }
      }
      px1 -= 5;
      px2 -= 5; 
      if (px1 < 0 && px2 < 0) {
        px1 += 1000;
        px2 += 1000;
      }
    }
    if (keyCode == DOWN) {
      for (int i = py2/5; i >= py1/5; i--) {
        for (int j = px1/5; j <= px2/5; j++) {
          //print(j + " " + i + "\n");
          tab[(j+200)%200][(i+1+200)%200] = tab[(j+200)%200][(i+200)%200];
          rtab[(j+200)%200][(i+1+200)%200] = rtab[(j+200)%200][(i+200)%200];
        }
      }
      py1 += 5;
      py2 += 5;
      if (py1 >= 1000 && py2 >= 1000) {
        py1 = py1%1000;
        py2 = py2%1000;
      }
    }
    if (keyCode == UP) {
      for (int i = py1/5; i <= py2/5; i++) {
        for (int j = px1/5; j <= px2/5; j++) {
          //print(j + " " + i + "\n");
          tab[(j+200)%200][(i-1+200)%200] = tab[(j+200)%200][(i+200)%200];
          rtab[(j+200)%200][(i-1+200)%200] = rtab[(j+200)%200][(i+200)%200];
        }
      }
      py1 -= 5;
      py2 -= 5; 
      if (py1 < 0 && py2 < 0) {
        py1 += 1000;
        py2 += 1000;
      }
    }
  }
  if (key == 'x' && selection == 1) {
    movemode = (movemode + 1)%2;
    if (movemode == 0) {
      print("move mode deactivated\n");
    } else {
      print("move mode activated\n");
    }
  }
  if (key == '+') {
    sf *= 1.2;
    sx = -px1 + mouseX/sf;
    sy = -py1 + mouseY/sf;
  }
  if (key == '-') {
    sf /= 1.2;
    sx = -px1 + mouseX/sf;
    sy = -py1 + mouseY/sf;
  }
  if (key == '*') {
    sf = 1;
    sx = 0;
    sy = 0;
  }
  if (key == '0') {
    sx = -px1 + mouseX/sf;
    sy = -py1 + mouseY/sf;
  }
}

void mouseReleased() {
  if (mouseButton == LEFT) {
    px2 = int(mouseX/sf-sx);
    py2 = int(mouseY/sf-sy);
    int tmp3;
    tmp3 = px1;
    px1 = min(px1, px2);
    px2 = max(tmp3, px2);
    tmp3 = py1;
    py1 = min(py1, py2);
    py2 = max(tmp3, py2);    
    selection = 1;
  }
}

String convertRules(int a) {
  String temp2;
  temp2 = "B";
    for (int i = 0; i < 9; i++) {
      if(((a>>9)>>i)%2 == 1){
        temp2 += i;
      }
    }
    temp2 += "S";
    for (int i = 0; i < 9; i++) {
      if((a>>i)%2 == 1){
        temp2 += i;
      }
    }
  return temp2;
}

int[][] rotatecw(int[][] matrix) {
  int n = matrix.length;
  int res[][] = new int[n][n];
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      res[i][j] = matrix[j][n - i - 1];//matrix[n - j - 1][i];
    }
  }
  return res;
}

int[][] mirrorlr(int[][] matrix) {
  int n = matrix.length;
  int res[][] = new int[n][n];
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      res[i][j] = matrix[n-i-1][j];//matrix[n - j - 1][i];
    }
  }
  return res;
}

void gcol(int[][] slist) {
  //slist [zeile schiffnummer], dann [0] art/phase, [1] dx, [2] dy, [3] drehung, [4] gespiegelt
  
  int pcx, pcy;
  int[][] tship;
  int[][] trship;
  
  for (int line = 0; line < slist.length; line++) {
    //slist[line]
    tship = ship[slist[line][0]];
    trship = rship[slist[line][0]];
    if (slist[line][4] == 1) {
      tship = mirrorlr(tship);
      trship = mirrorlr(trship);
    }
    for (int i = 0; i < slist[line][3]; i++) {
      tship = rotatecw(tship);
      trship = rotatecw(trship);
    }    
    pcx = 0; pcy = 0;
    for (int j = 0; j < tship[0].length; j++) {
      for (int i = 0; i < tship.length; i++) {    
        if (tship[i][j]>>1 == 1) {
          pcx = i; pcy = j;
        }
      }
    }    
    for (int j = 0; j < tship[0].length; j++) {
      for (int i = 0; i < tship.length; i++) {    
        if (tship[i][j]>>1 == 1) {
          pcx = i; pcy = j;
        }
      }
    }    
    int tx, ty;
    for (int j = 0; j < tship[0].length; j++) {
      for (int i = 0; i < tship.length; i++) {  
        tx = i-pcx+slist[line][1];
        ty = j-pcy+slist[line][2];
        if (tx >= 0 && tx < 200 && ty >= 0 && ty < 200 && (tship[i][j]%2 == 1 || trship[i][j] != 0)) {
          tab[tx][ty] = tship[i][j]%2;
          rtab[tx][ty] = trship[i][j];
        }
      }
    }   
  }
  
  return;
}
It was pointed out to me that this could be also be stated as a 2^19 state cellular automaton (2^18 for the rules and 2 for dead/alive). To see how this might be justified it's possible to produce maps of all the 19 states separately from each other, which begs the question of what's the (most) correct way of displaying the result of the automaton.

User avatar
PHPBB12345
Posts: 1096
Joined: August 5th, 2015, 11:55 pm
Contact:

Re: Xor-life

Post by PHPBB12345 » April 29th, 2021, 8:47 am

Do you have javascript code? I'm running on web browser.
EDIT: yes!

Code: Select all

<html>
<head>
<title></title>
</head>
<body style="background:#000">
<div style="text-align:center;">
<canvas id="canvas" width="640" height="640"></canvas>
</div>
<script>
var c = document.getElementById("canvas"), W = 128, H = 128;
var ctx = c.getContext("2d");
var celc = [];
var celp = [];
var rulc = [];
var rulp = [];

var renderCell = function(x, y, n) {
  ctx.fillStyle = n ? "#FFFFFF" : "#000000";
  ctx.fillRect(x * 5, y * 5, 5, 5);
};
var renderAll = function() {
  for (var x = 0; x < W; x++) {
    for (var y = 0; y < H; y++) {
      renderCell(x, y, celc[y][x]);
    }
  }
};
var update = function() {
  var x, y, xt, yt, i, j, c, t, shift, rnext;
  t = rulc; rulc = rulp; rulp = t;
  t = celc; celc = celp; celp = t;
  for (x = 0; x < W; x++) {
    for (y = 0; y < H; y++) {
      c = (celp[y][x] ? 1 : 0);
      shift = c ? 0 : 9;
      rnext = 0;
      for (i = -1; i < 2; i++) {
        for (j = -1; j < 2; j++) {
          xt = (x+i+W)%W; yt = (y+j+H)%H;
          t = celp[yt][xt];
          rnext ^= t ? rulp[yt][xt] : 0;
          shift += ((i || j) && t) ? 1 : 0;
        }
      }
      rulc[y][x] = rnext;
      celc[y][x] = (rnext >> shift) & 1;
    }
  }
  renderAll();
};
(function() {
  for (var i = 0; i < W; ++i) {
    celc[i] = [];
    celp[i] = [];
    rulc[i] = [];
    rulp[i] = [];
    for (var j = 0; j < H; ++j) {
      celc[i][j] = (i >= W * 0.25 && i < W * 0.75 && j >= H * 0.25 && j < H * 0.75 && Math.random() < 0.5) ? 1 : 0;
      celp[i][j] = 0;
      rulc[i][j] = (Math.random() * 262144) &~ 1548;
      rulp[i][j] = 0;
    }
  }
  renderAll();
  setInterval(update, 100);
})();
</script>
</body>
</html>
Last edited by PHPBB12345 on April 30th, 2021, 6:25 am, edited 3 times in total.

bprentice
Posts: 920
Joined: September 10th, 2009, 6:20 pm
Location: Coos Bay, Oregon

Re: Xor-life

Post by bprentice » April 29th, 2021, 9:15 am

PHPBB12345 wrote:
April 29th, 2021, 8:47 am
Do you have javascript code? I'm running on web browser.
I'm running the code successfully using Processing.

https://processing.org/

Brian Prentice

User avatar
Felixor
Posts: 9
Joined: April 27th, 2021, 2:50 am

Re: Xor-life

Post by Felixor » April 29th, 2021, 10:52 am

This is correct, it is a processing sketch. I don't know how to code js so I sadly can't provide code there.

I should note some things about the usage of the code as it is:
After running the script you have to click the window once to gain focus. You have the following options:
[p] - pauses the simulation, press it again to resume. Once you pressed [p] the mouse will be highlighted with two red lines.
[5] - counts the number of living cells on screen
[left mouse + drag] - selects cells, the top left coordinate and the dimensions are displayed
[+] - zooms in and centers the top left corner of the selection on the mouse cursor
[-] - zooms out and centers the top left corner of the selection on the mouse cursor
[0] - just centers the top left corner of the selection on the mouse cursor
[*] - resets all changes to the view window
[n] - shows all empty rule cells (containing 0)
[c] - if you selected something you can copy it
[v] - pastes at the top left corner of your current selection
[space] - rotates the clipboard 90° clockwise
[m] - mirrors the clipboard left-right
[s] - saves the current selection
[z] - starts a period measurement, i.e. when run on an oscillator give a notice if the pattern repeats. Doesn't work on spaceships or with other soup at the same time
[right-arrow] - advances one generation
[r] - starts a recording (saves each generation as an image) until you press [r] again
[x] - starts movement mode where you can move the selection around with the arrow keys, note that most functions don't work until you exit movement mode with [x] again
[enter] - if something is selected everything outside the selection is set to zero
[backspace] - just the selection is set to zero
[ i] - imports the reading list which you can set at the top of the code, keep in mind that if a file can't be found it causes a crash. The imported file is put in the clipboard for you to paste it with [v]
[e] - loads a list of ships you can make
[q] - starts collision mode which simulates collisions (complicated)
[w] - saves the selection as a ship with the mouse position as the pivot, but it overrides the first slot and I rarely used it after making a shiplist file
[d] - toggles the a delay of 100 ms on and off
Last edited by Felixor on April 30th, 2021, 4:42 am, edited 1 time in total.

User avatar
muzik
Posts: 5614
Joined: January 28th, 2016, 2:47 pm
Location: Scotland

Re: Xor-life

Post by muzik » April 30th, 2021, 2:14 am

While I personally wouldn't know how to create one myself, this rule family does seem simple enough that it could be supported through rule tables.

User avatar
yujh
Posts: 3066
Joined: February 27th, 2020, 11:23 pm
Location: I'm not sure where I am, so please tell me if you know
Contact:

Re: Xor-life

Post by yujh » April 30th, 2021, 3:51 am

muzik wrote:
April 30th, 2021, 2:14 am
While I personally wouldn't know how to create one myself, this rule family does seem simple enough that it could be supported through rule tables.
I think create one is easy, but probably the symmetry is known!
Rule modifier

B34kz5e7c8/S23-a4ityz5k
b2n3-q5y6cn7s23-k4c8
B3-kq6cn8/S2-i3-a4ciyz8
B3-kq4z5e7c8/S2-ci3-a4ciq5ek6eik7

Bored of Conway's Game of Life? Try Pedestrian Life -- not pedestrian at all!

User avatar
Felixor
Posts: 9
Joined: April 27th, 2021, 2:50 am

Re: Xor-life

Post by Felixor » May 1st, 2021, 4:13 am

It's turing complete, since all gates are possible
ezgif.com-gif-maker(16).gif
ezgif.com-gif-maker(16).gif (391.64 KiB) Viewed 1849 times

GUYTU6J
Posts: 2200
Joined: August 5th, 2016, 10:27 am
Location: 拆哪!I repeat, CHINA! (a.k.a. 种花家)
Contact:

Re: Xor-life

Post by GUYTU6J » May 1st, 2021, 4:40 am

For me who has not completely understand the rule, it features some curious anisotropicity. For example, there are replicators running in different orthogonal directions in the shown guns, whose basic unit is a hollow 3×3 square. But after drawing one of this D8_1 symmetric octomino in a grid filled with vaccum (i.e. no other cells exist), I cannot determine its travelling direction by simply looking at the pattern. So what's going on?

User avatar
Felixor
Posts: 9
Joined: April 27th, 2021, 2:50 am

Re: Xor-life

Post by Felixor » May 1st, 2021, 5:19 am

GUYTU6J wrote:
May 1st, 2021, 4:40 am
For me who has not completely understand the rule, it features some curious anisotropicity. For example, there are replicators running in different orthogonal directions in the shown guns, whose basic unit is a hollow 3×3 square. But after drawing one of this D8_1 symmetric octomino in a grid filled with vaccum (i.e. no other cells exist), I cannot determine its travelling direction by simply looking at the pattern. So what's going on?
The reason for this apparent anisotropicity is that not all states are visible. In the gifs it is always the alive/dead state that determines the color, but the underlying rules determine the behaviour. If I examine the rules for one of the hollow square replicators that travels vertically, I get the following:

Code: Select all

maximum restriction NAND B01S02346

 rulespace 
{
{     0,      0,      0,      0,      0},
{235906, 190626,  94496, 190626, 235906},
{235906, 190626,  94496, 190626, 235906},
{235906, 190626,  94496, 190626, 235906},
{     0,      0,      0,      0,      0}
};

 statespace 
{
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 1, 0, 1, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0}
};
So these hollow squares look 4 axis symmetrical in the dead/alive space, but their rulespace is actually only 2 axis symmetrical, and this is the reason they can travel both vertically and horizontally.

The automaton with all the states (rulespace + statespace) is isotropic.

bprentice
Posts: 920
Joined: September 10th, 2009, 6:20 pm
Location: Coos Bay, Oregon

Re: Xor-life

Post by bprentice » May 4th, 2021, 6:26 am

Felixor wrote:
April 29th, 2021, 10:52 am
it is a processing sketch
Another CA Simulator written as a Processing sketch:

viewtopic.php?f=12&t=3541&p=63791

Brian Prentice

Post Reply