Photos, ramblings, whatever

Thursday, November 26, 2009

That code in my last post

Turns out that code in my last post worked pretty well despite having a couple of bugs which I would have expected to make the whole thing not work. In fact, now they're fixed, it doesn't even work much better due to the low probability of any mutations causing the model organisms to take advantage of this afternoon's bugfixes.

As well as the bugfixes, I've now set it up so the virtual organisms get saved to the disk. This does make it more practical as a spamfilter, but I'll want to change the save location when I start integrating it with an emailer.

Here is the code. It still works basically as described in the previous post. But now the virtual organisms can actually do arithmetic. However, from what I can figure out, if I start the program from scratch (including deleting all previous virtual organisms), the ones which try to use the arithmetic unit get out-competed by the ones which do everything just by shifting round chunks of data. Some of them even manage to return different values even though their code doesn't contain a return instruction. I suspect most of them do this by putting the zero they get fed at the end of each file into the destination value of an instruction that gets executed later, turning it into a return instruction.

Anyway, here's the updated code:
using GLib;

namespace tta {
 public static delegate void sexecF(short value, void* target);
 public struct setWordFunc {
  sexecF exec;
  void* target;
 }
 public static delegate short gexecF(void * target);
 public struct getWordFunc {
  gexecF exec;
  void * target;
 }
 
 public class VM : GLib.Object {
  private setWordFunc[] outputMaps;
  private getWordFunc[] inputMaps;
  private short[] bytes;
  private int pc = 1;
  private bool state = false;
  public int timeout { get; set; }
  
  public VM(int size) {
   bytes = new short[size];
   outputMaps = new setWordFunc[size];
   inputMaps = new getWordFunc[size];
   timeout = -1; // if negative, leave alone. If positive, decriment each cycle until 0,
     // then halt.
  }
  
  public VM.array(short[] ram) {
   bytes = ram;
   outputMaps = new setWordFunc[bytes.length];
   inputMaps = new getWordFunc[bytes.length];
  }
  
  public void mapInput(getWordFunc input, int address) {
   inputMaps[address % inputMaps.length] = input;
  }
  
  public void mapOutput(setWordFunc output, int address) {
   outputMaps[address % outputMaps.length] = output;
  }
  
  public short getWord(int address) {
   int a = address % bytes.length;
   a = a < 0 ? -a : a;
   if(inputMaps[a].exec != null) {
    return inputMaps[a].exec(inputMaps[a].target);
   } else {
    return bytes[a];
   }
  }
  
  public void setWord(int address, short value) {
   int b = address % bytes.length;
   b = b < 0 ? -b : b;
   if(outputMaps[b].exec != null) {
    outputMaps[b].exec(value, outputMaps[b].target);
   } else {
    bytes[b] = value;
   }
  }
  
  public int size { get { return bytes.length; } }
  
  public static void jump(short value, void* target) {
   ((VM)target).pc = value - (((VM)target).state? 2 : 0); // - 2 so advancing will bring it to the right place
  }
  public static void halt(short value, void* target) {
   ((VM)target).state = false;
  }
  
  public void run() {
   state = true;
   while(state == true && timeout != 0) {
    execute(getWord(pc), getWord(pc + 1));
    pc = (pc + 2) % bytes.length;
    if(timeout > 0) {
     timeout--;
    }
   }
  }
  
  public void execute(int src, int dest) {
   setWord(dest, getWord(src));
  }
 }
 
 public class ALU : GLib.Object {
  private short a = 0;
  private short b = 0;
  
  public static void setOperandA(short value, void* target) {
   ((ALU)target).a = value;
  }
  
  public static void setOperandB(short value, void* target) {
   ((ALU)target).b = value;
  }
  
  public static short add(void* target) {
   var x = (ALU)target;
   return x.a + x.b;
  }
  
  public static short subtract(void* target) {
   var x = (ALU)target;
   return x.a - x.b;
  }
  
  public static short multiply(void* target) {
   var x = (ALU)target;
   return x.a * x.b;
  }
  
  public static short modulus(void* target) {
   var x = (ALU)target;
   return x.a % x.b;
  }
  
  public static short divide(void * target) {
   var x = (ALU)target;
   return ( x.b == 0 ) ? 0 : ( x.a / x.b);
  }
  
  public static short gt(void* target) {
   var x = (ALU)target;
   return (short)(x.b > x.a);
  }
  
  public static short eq(void* target) {
   var x = (ALU)target;
   return (short)(x.b == x.a);
  }
 }
 
}

namespace evospam {
 class Bot : GLib.Object {
  public int score { get; set; }
  private short[] dna = new short[512];
  
  public Bot() {
   for(int i = 0; i < 512; i++) {
    dna[i] = (short)(GLib.Random.next_int() % short.MAX);
   }
  }
  
  public Bot.load(string filename) {
   try {
    string temp;
    ulong length;
    FileUtils.get_contents(filename, out temp, out length);
    GLib.Memory.copy(dna, temp, (length > 1023) ? 1023 : length);
   } catch ( FileError e ) {
    // unloaded or terminally corrupted bots will be replaced as the population evolves.
   }
  }
  
  public void save(string filename) {
   try {
    FileUtils.set_contents(filename, (string)dna, 1024);
   } catch(FileError e) {
    // don't worry. Be happy.
   }
  }
  
  public Bot.mutate(Bot parent) {
   for(int i = 0; i < 512; i++){
    if(GLib.Random.next_double() > 0.05) {
     dna[i] = parent.dna[i];
    } else {
     dna[i] = (short)(GLib.Random.next_int() % short.MAX);
    }
   }
  }
  
  public Bot.crossover(Bot mother, Bot father) {
   int point = (int)(GLib.Random.next_int() % 512);
   int point2 = (int)(GLib.Random.next_int() % (512 - point)) + point;
   for(int i = 0; i < point; i++){
    if(GLib.Random.next_double() > 0.05) {
     dna[i] = mother.dna[i];
    } else {
     dna[i] = (short)(GLib.Random.next_int() % short.MAX);
    }
   }
   for(int i = point; i < point2; i++){
    if(GLib.Random.next_double() > 0.05) {
     dna[i] = father.dna[i];
    } else {
     dna[i] = (short)(GLib.Random.next_int() % short.MAX);
    }
   }
   for(int i = point2; i < 512; i++){
    if(GLib.Random.next_double() > 0.05) {
     dna[i] = mother.dna[i];
    } else {
     dna[i] = (short)(GLib.Random.next_int() % short.MAX);
    }
   }
  }
  
  private static struct retParam {
   short* retptr;
   tta.VM haltTgt;
  }
  public static void ret(short value, void* target) {
   retParam* a = (retParam*)target;
   *(a->retptr) = value;
   tta.VM.halt(value, a->haltTgt);
  }
  
  public static short readStream(void* target) {
   unowned GLib.FileStream io = (GLib.FileStream)target;
   if(!io.eof()) {
    return (short)io.getc();
   } else {
    return 0;
   }
  }
  
  public short run(GLib.FileStream message) {
   var tmp = new short[512];
   var alu = new tta.ALU();
   GLib.Memory.copy(tmp, dna, 1023);
   var vm = new tta.VM.array(tmp);
   vm.mapOutput({vm.jump, vm}, 503);
   vm.mapOutput({alu.setOperandA, alu}, 504);
   vm.mapOutput({alu.setOperandB, alu}, 505);
   vm.mapInput({alu.add, alu}, 506);
   vm.mapInput({alu.subtract, alu}, 507);
   vm.mapInput({alu.multiply, alu}, 508);
   vm.mapInput({alu.divide, alu}, 509);
   vm.mapInput({alu.gt, alu}, 510);
   vm.mapInput({alu.eq, alu}, 511);
   short r = 0;
   retParam xt = {&r, vm};
   vm.mapOutput({ret, &xt}, 0);
   vm.mapInput({readStream, message}, 0);
   vm.timeout = 2048;
   vm.run();
   return r;
  }
 }
}

int evaluate(evospam.Bot b) {
try{
 GLib.Dir ham = GLib.Dir.open("ham");
 GLib.Dir spam = GLib.Dir.open("spam");
 int[] score = {0, 0};
 int finalscore;
 for(int c = 0; c < 2; c++) {
  weak GLib.Dir cd = c == 0 ? ham : spam;
  string filename = cd.read_name();
  while(filename != null) {
   int guess = b.run(FileStream.open("%s/%s".printf(c == 0 ? "ham" : "spam", filename), "r"));
   int s = guess - 16383 * c;
   s = s < 0 ? -s : s;
   if(s < (2000)) {
    score[c]++;
   }
   filename = cd.read_name();
  }
 }
 finalscore = score[0] < score[1] ? score[0] : score[1];
 b.score = finalscore;
 return finalscore;
} catch(GLib.FileError e) { stderr.printf("Cannot open directory\n"); return 0; }
}

int main(string[] args) {
 evospam.Bot[] bots = new evospam.Bot[300];
 int top_score = 0;
 for(int i = 0; i < 300; i++) {
  string fn = "bots/%d.bot".printf(i);
  if(FileUtils.test(fn, FileTest.EXISTS)) {
   bots[i] = new evospam.Bot.load(fn);
  } else {
   bots[i] = new evospam.Bot();
  }
  int score = evaluate(bots[i]);
  top_score = score > top_score ? score : top_score;
 }
 stdout.printf("Dummy run top score = %d\n", top_score);
 while(top_score < 32) {
  int lowest = 0;
  int lowscore = bots[0].score;
  for(int i = 1; i < 300; i++) {
   if(bots[i].score < lowscore || (bots[i].score == lowscore && GLib.Random.next_double() < 0.03)) {
    lowscore = bots[i].score;
    lowest = i;
   }
  }
  if(top_score == 0) {
   bots[lowest] = new evospam.Bot();
  } else {
   if(GLib.Random.next_double() > 0.9) {
    bots[lowest] = new evospam.Bot.crossover(bots[GLib.Random.next_int() % 300], bots[GLib.Random.next_int() % 300]);
   } else {
    bots[lowest] = new evospam.Bot.mutate(bots[GLib.Random.next_int() % 300]);
   }
  }
  lowscore = evaluate(bots[lowest]);
  bots[lowest].save("bots/%d.bot".printf(lowest));
  if(lowscore > top_score) {
   stdout.printf("Highscore: %d; index: %d\n", lowscore, lowest);
   top_score = lowscore;
  }
 }
 return 0;
}



Ah, Finally found out how to get indentation to appear in HTML!

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home