view interps/bf_txtgen/textgen.java @ 12500:e48c08805365 draft default tip

<b_jonas> ` learn \'The password of the month is Cthulhuquagdonic Mothraquagdonic Narwhalicorn.\' # https://logs.esolangs.org/libera-esolangs/2024-04.html#lKE Infinite craft
author HackEso <hackeso@esolangs.org>
date Wed, 01 May 2024 06:39:10 +0000
parents 859f9b4339e6
children
line wrap: on
line source

//====================================================================
// TEXTGEN -- BrainF*** Genetic Text Generator
// textgen.java
// Copyright (C) 2003, 2004 by Jeffry Johnston
// Version 0.20
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation. See the file LICENSE
// for more details.
//
// Online resource used:
// http://www.genetic-programming.com/gpflowchart.html
//
// I have not read any books and have seen very little information
// online about GP implementation.  Any suggestions are most welcome.
//
// Inspiration:
// 111-byte "Hello World!" with newline (assumed optimal)
// ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.
// <<+++++++++++++++.>.+++.------.--------.>+.>.
//
// Version history:
// 0.20    11 Aug 2004 Added -i and -o options
// 0.10    01 Dec 2003 Initial release
//====================================================================

import java.io.*;
import java.util.*;

//********************************************************************
// textgen
//********************************************************************
public class textgen {
  static final String VERSION = "0.20";
  static Random r = new Random();

  //------------------------------------------------------------------
  // main
  //------------------------------------------------------------------
  public static void main(String[] cmdline) {
    int terms = 4, population = 500, generations = 0;
    String text = "Default text?!";
    double toppercent = 0.10;
    String fileout = null;

    // display copyright text
    System.out.println("TEXTGEN BrainF*** Genetic Text Generator, version " + VERSION + ".");
    System.out.println("Copyright (c) 2003 Jeffry Johnston.");
    System.out.println();

    // process command line
    for (int n = 0; ; n++) {
      try {
        if (cmdline[n].equals("-?") || cmdline[n].equals("/?") || cmdline[n].equals("--?")
            || cmdline[n].equals("--help") ) {
          usage();
          System.exit(0);
        } else if (cmdline[n].equalsIgnoreCase("-i")) {
          // open file
          DataInputStream filein = null;
          try {
            filein = new DataInputStream(new FileInputStream(cmdline[n + 1]));
          } catch (FileNotFoundException e) {
            System.out.println("Error opening input file.  File not found");
            System.exit(1);
          }
          // read file
          byte[] data = null;
          int size = 0;
          try {
            size = filein.available();
            data = new byte[size];
            filein.readFully(data);
          } catch (IOException e) {
            System.out.println("Error reading input file.");
            System.exit(1);
          }
          // close file
          try {
            filein.close();
          } catch (IOException e) {
            System.out.println("Error closing input file.");
            System.exit(1);
          }
          try {
            text = new String(data, "ISO-8859-1");
          } catch (UnsupportedEncodingException e) {
            System.out.println("This program requires ISO-8859-1 character set support.");
          }
          n++;
        } else if (cmdline[n].equalsIgnoreCase("-o")) {
          fileout = cmdline[n + 1];
          n++;
        } else if (cmdline[n].equalsIgnoreCase("-g")) {
          generations = Integer.parseInt(cmdline[n + 1]);
          if (generations < 1) {
            System.out.println("Must have at least 1 generation.");
            System.exit(1);
          }
          n++;
        } else if (cmdline[n].equalsIgnoreCase("-t")) {
          terms = Integer.parseInt(cmdline[n + 1]);
          if (terms < 1) {
            System.out.println("Must have at least 1 term.");
            System.exit(1);
          }
          n++;
        } else if (cmdline[n].equalsIgnoreCase("-p")) {
          population = Integer.parseInt(cmdline[n + 1]);
          if (terms < 2) {
            System.out.println("Must have population of at least 10.");
            System.exit(1);
          }
          n++;
        } else if (cmdline[n].equalsIgnoreCase("-%")) {
          int tp = Integer.parseInt(cmdline[n + 1]);
          if (tp < 0 || tp > 100) {
            System.out.println("Percentage out of range (0, 100).");
            System.exit(1);
          }
          toppercent = (double) tp / 100;
          n++;
        } else {
          text = cmdline[n];
          if (text.charAt(0) == '\"') {
            text = text.substring(1);
          }
          if (text.charAt(text.length() - 1) == '\"') {
            text = text.substring(0, text.length());
          }
        }
      } catch (ArrayIndexOutOfBoundsException e) {
        break;
      } catch (NumberFormatException e) {
        System.out.println("Invalid number");
        System.exit(1);
      }
    }
    int top = (int) (toppercent * population);
    int bottom = population - top;

    if (fileout == null) {
      System.out.println("Text: \"" + text + "\"");
    }
    System.out.print("Generations: ");
    if (generations == 0) {
      System.out.println("Infinite");
    } else {
      System.out.println(generations);
    }
    System.out.println("Terms: " + terms);
    System.out.println("Population: " + population);
    System.out.println("Top %: " + toppercent);
    System.out.println();

    CompareIndividuals compare = new CompareIndividuals();

    // generate initial population
    Individual[] citizen = new Individual[population];
    for (int n = 0; n < population; n++) {
      citizen[n] = new Individual(text, terms);
    }

    // keep looking for the best program
    int best = Integer.MAX_VALUE;
    for(int generation = 1; (generation <= generations) || (generations == 0);
        generation++) {
      // choose a top citizen to copy and tweak number order
      for (int n = bottom; n < population; n++) {
        citizen[n].tweak(citizen[r.nextInt(top)]);
      }

      // genetic stimulation
      for (int n = 1; n < population; n++) {
        int p = r.nextInt(population);
        if (p < n) { // less mutation with good, more mutation with bad
          citizen[n].mutate();
        } else {
          p = r.nextInt(top);
          citizen[n].crossover(citizen[p]);
        }
      }

      // get rid of the duds
      Arrays.sort(citizen, compare);
      for (int n = bottom; n < population; n++) {
        citizen[n].restart();
      }

      // display progress
      Arrays.sort(citizen, compare);
      if (citizen[0].getFitness() < best) {
        String temptext;
        System.out.println((best = citizen[0].getFitness()) + " " + (temptext = citizen[0].getCode())
                           + " [" + generation + "]");
        System.out.println();
        if (fileout != null) {
          PrintStream out;
          try {
            out = new PrintStream(new FileOutputStream(fileout));
            out.print(temptext);
            out.close();
          } catch (IOException e) {
            System.out.println("Error writing output file.");
            System.exit(1);
          }
        }
      }

    }
  }

  //------------------------------------------------------------------
  // getRandomBoolean()
  //------------------------------------------------------------------
  public static boolean getRandomBoolean() {
    return r.nextBoolean();
  }

  //------------------------------------------------------------------
  // getRandomInt()
  //------------------------------------------------------------------
  public static int getRandomInt(int n) {
    return r.nextInt(n);
  }

  //------------------------------------------------------------------
  // usage -- prints usage information
  //------------------------------------------------------------------
  public static void usage() {
    System.out.println("Usage: textgen [-i file] [-o file] [-t #] [-p #] [-% #] \"text\" [-?]");
    System.out.println("Where: \"text\"       Text to generate");
    System.out.println("       -i           Input filename (in place of \"text\")");
    System.out.println("       -o           Output filename, default: screen only");
    System.out.println("       -g           Generations, default: infinite");
    System.out.println("       -t           Number of terms, default: 4");
    System.out.println("       -p           Population, default: 500");
    System.out.println("       -%           Percent to rank as top, default: 10");
    System.out.println("       -?           Display usage information");
  }

}

//********************************************************************
// CompareIndividuals
//********************************************************************
class CompareIndividuals implements Comparator {
  public int compare(Object o1, Object o2) {
    return (((Individual) o1).getFitness() - ((Individual) o2).getFitness());
  }
}

//********************************************************************
// Individual
//********************************************************************
class Individual {
  int letters, mult, terms, fitness = -1;
  int[] term, finder, fixer;
  String expected;

  //------------------------------------------------------------------
  // (constructor)
  //------------------------------------------------------------------
  Individual(String expected, int terms) {
    this.expected = expected;
    letters = expected.length();
    this.terms = terms;
    term = new int[terms];
    restart();
  }

  //------------------------------------------------------------------
  // restart()
  //------------------------------------------------------------------
  public void restart() {
    // multiplier (1 to 15)
    mult = textgen.getRandomInt(15) + 1;
    // value of each term (0 to 15)
    for (int n = 0; n < terms; n++) {
      term[n] = textgen.getRandomInt(16);
    }
    // ><+- multiple before each dot (one for each letter)
    finder = new int[letters];
    fixer = new int[letters];
    for (int n = 0; n < letters; n++) {
      finder[n] = textgen.getRandomInt(terms);
    }
    fitness = -1;
  }

  //------------------------------------------------------------------
  // mutate()
  //------------------------------------------------------------------
  public void mutate() {
    int n;
    // mutate item
    if (textgen.getRandomBoolean()) {
      // term
      n = textgen.getRandomInt(terms);
      term[n] = textgen.getRandomInt(16);
    } else {
      // finder
      n = textgen.getRandomInt(letters);
      finder[n] = textgen.getRandomInt(terms);
    }
    fitness = -1;
  }

  //------------------------------------------------------------------
  // tweak()
  //------------------------------------------------------------------
  public void tweak(Individual giver) {
    if ((textgen.r).nextBoolean()) {
      // copy the giver exactly
      mult = giver.getMult();
      for (int n = 0; n < terms; n++) {
        term[n] = giver.getTerm(n);
      }
    } else {
      // copy the giver, but use a new multiplier, adjust the terms accordingly
      mult = textgen.getRandomInt(15) + 1;
      for (int n = 0; n < terms; n++) {
        term[n] = Math.round(giver.getTerm(n) * giver.getMult() / (float) mult);
      }
    }
    // copy the finder
    for (int n = 0; n < letters; n++) {
      finder[n] = giver.getFinder(n);
    }

    // swap two of the terms to see if it helps (can be the same term)
    int n1 = textgen.getRandomInt(terms);
    int n2 = textgen.getRandomInt(terms);
    int n3 = term[n2];
    term[n2] = term[n1];
    term[n1] = n3;

    // swap finders to match swapped terms
    for (int n = 0; n < letters; n++) {
      if (finder[n] == n1) {
        finder[n] = n2;
      } else if (finder[n] == n2) {
        finder[n] = n1;
      }
    }

    fitness = -1;
  }

  //------------------------------------------------------------------
  // crossover()
  //------------------------------------------------------------------
  public void crossover(Individual giver) {
    int n;
    if ((textgen.r).nextBoolean()) {
      // term
      n = textgen.getRandomInt(terms);
      int n2 = textgen.getRandomInt(terms);
      term[n2] = giver.getTerm(n);
    } else {
      // finder
      n = textgen.getRandomInt(letters);
      finder[n] = giver.getFinder(n);
    }
    fitness = -1;
  }

  //------------------------------------------------------------------
  // getMult()
  //------------------------------------------------------------------
  public int getMult() {
    return mult;
  }

  //------------------------------------------------------------------
  // getTerms()
  //------------------------------------------------------------------
  public int getTerms() {
    return terms;
  }

  //------------------------------------------------------------------
  // getTerm()
  //------------------------------------------------------------------
  public int getTerm(int n) {
    return term[n];
  }

  //------------------------------------------------------------------
  // getFinder()
  //------------------------------------------------------------------
  public int getFinder(int n) {
    return finder[n];
  }

  //------------------------------------------------------------------
  // getFitness()
  //------------------------------------------------------------------
  public int getFitness() {
    if (fitness < 0) {
      int length = 4 + mult + terms * 2 + letters; // mult [ terms> terms< -]> letters.
      int[] memory = new int[terms];
      for (int n = 0; n < terms; n++) {
        length += term[n];
        memory[n] = (mult * term[n]) % 256;
      }
      int mp = 0;
      for (int n = 0; n < letters; n++) {
        length += Math.abs(finder[n] - mp);
        mp = finder[n];
        fixer[n] = expected.charAt(n) - memory[mp];
        int n2 = Math.abs(fixer[n]);
        if (Math.abs(fixer[n]) > 128) {
          n2 = 256 - n2;
          if (fixer[n] > 0) {
            n2 = -n2;
          }
        }
        length += Math.abs(fixer[n]);
        memory[mp] = expected.charAt(n);
      }

      // combine results
      fitness = length;

      if (fitness < 0) { fitness = Integer.MAX_VALUE; }

    }
    return fitness;
  }

  //------------------------------------------------------------------
  // getCode()
  //------------------------------------------------------------------
  public String getCode() {
    String code = getBF(mult, '+', '+') + "[";
    for (int n = 0; n < terms; n++) {
      code += ">" + getBF(term[n], '+', '+');
    }
    code += getBF(terms, '<', '<') + "-]>";
    int mp = 0;
    for (int n = 0; n < letters; n++) {
      code += getBF(finder[n] - mp, '>', '<');
      mp = finder[n];
      code += getBF(fixer[n], '+', '-') + ".";
    }
    return code;
  }

  //------------------------------------------------------------------
  // getBF() {
  //------------------------------------------------------------------
  public String getBF(int n, char pos, char neg) {
    char ch = ' ';
    if (n > 0) {
      ch = pos;
    } else if (n < 0) {
      ch = neg;
    }
    n = Math.abs(n);
    if (n != 0) {
      char[] c = new char[Math.abs(n)];
      Arrays.fill(c, ch);
      return new String(c);
    } else {
      return "";
    }
  }

}