Array of points, nullpointer exception

edited August 2016 in Library Questions

Hello all; If you look at another one of my discussions you will see I have been working on a genetic algorithm. I currently am experiencing a problem whilst trying to plot points of data in another window I have created. I get a null pointer exception after a little bit while running the code. The points do not show up. Please take a look at the code and please advise. Thanks.

    import org.gicentre.utils.stat.*;  //import graph library

    import g4p_controls.*;

    ArrayList<Ball> balls = new ArrayList(); // arraylist for ball objects
    ArrayList<Point> points = new ArrayList();
    float gcorX, gcorY; // goal coords
    int timeIter = 300; //time step- internal clock

    float bscore; //best score

    String[] str_a;   //one copy of scorelist (use to copies to determine best and worst)
    String[] str_b;   //one copy of scorelist (use to copies to determine best and worst)
    float[] scores;   //scorelist
    final int pop = 10; //population size- final bc it shouldnt be changed in the program
    int cur_sub;      //current iteration- OLD METHOD, but still in just in case
    public int gen_num = 0; //count generation number
    int countDone; // count how many balls in the population finished their run in that generation
    float diameter = 30;
    float globmin = 1000; //base var
    float [] generations; // array of generations

    GWindow window;

    Ball globalBall = new Ball(-1, "0"); // starter/ global best ball


    public void windowDraw(PApplet app, GWinData data) {

      app.background(0);
      app.stroke(255);
      app.strokeWeight(8);
      //app.point((gen_num*2), min(scores));
      for ( int j = 0; j < gen_num; j++ ) {
        points.add( new Point(gen_num*2, min(scores) ) );
      }

      for ( int i=0; i < points.size(); i++ ) points.get(i).display();
    }

    public void windowMouse(PApplet app, GWinData data, MouseEvent event) {
    }

    public void windowKey(PApplet app, GWinData data, KeyEvent event) {
    }

    void setup() {
      size(800, 800);
      background(0);

      generations = new float[gen_num]; // init generations with size

      window =  GWindow.getWindow(this, "Graph", 100, 50, 500, 500, JAVA2D);

      window.addDrawHandler(this, "windowDraw");
      window.addMouseHandler(this, "windowMouse");
      window.addKeyHandler(this, "windowKey");

      bscore = dist(0, 0, width, height); // calc score
      gcorX= random(width);
      gcorY= random(height);

      ellipseMode(CENTER);

      //begin init scorestrings
      str_a = new String[pop];
      str_b = new String[pop];
      scores = new float[pop];
      for (int j = 0; j<pop; j++) {
        str_a[j] = starterString();
        scores[j] = 0;
      }
      //end init scorestrings

      //for every done ball, add a new one/replace it
      cur_sub = -1;
      countDone = 0;
      for ( int i=0; i < pop; i++ ) balls.add( new Ball( i, str_a[i] ) );
    }


    //begin init of starter string( Initial) so that the mutation has a place to begin

    String starterString() {
      String mut = "" + char( '0' + int( random(4) ) ); //  + "00000000" ;
      return( mut );
    }

    //end init

    //begin init instructions and strings of values- cr and hr refer to the textlines explaining each movement. r is a placeholder string so that the strings return a value to it based on the results of the cr loop

    String hr( String cr ) {
      String r = "";
      for ( int t = 0; t < cr.length(); t++) {
        if ( cr.charAt(t) == '0' ) r+= 'N';
        if ( cr.charAt(t) == '1' ) r+= 'L';
        if ( cr.charAt(t) == '2' ) r+= 'R';
        if ( cr.charAt(t) == '3' ) r+= 'A';
      }
      return( r );
    }

    //end init instructions and cr/hr

    void draw() {
      background(0); //layer 2
      fill(255, 0, 0); //red
      //starting pad
      pushMatrix();
      stroke(255, 200);
      fill(0, 255, 0, 200);
      ellipse(width/2, height/2, 25, 25);
      popMatrix();
      //end starting pad


      String extra = ""; //message
      if ( bscore < 20 ) extra = "That's close enough! Click the mouse to move the goal."; //message

      //text inits and placements
      pushMatrix();
      fill(255, 0, 0);
      text("| Best ever: (" + hr(globalBall.genome) + ") = " + int(bscore) + " |", 50, 20 );
      popMatrix();

      pushMatrix();
      fill(255, 200);
      text("| key: A = accelerate, R = right, L = left, N = do nothing | ", 500, height-20);
      popMatrix();

      pushMatrix();
      fill(0, 0, 255);
      text(extra, 30, height -20);
      popMatrix();

      pushMatrix();
      fill(#3FB73F);
      text("| Generation " + gen_num+ " |", 280, 20 );
      text("| minimum of gen:" + (min(scores))+ " |", 400, 20); //why does this get a NullPointer Exception? and why does it not plot the points? Frustrated. 
      text("| best:" + (bscore)+ " |", 600, 20);

      popMatrix();
      //end text inits and placements

      //globmin = min(globmin, min(scores));
      //text("globmin: " + globmin, 250, 34);


      //text for genome (hr/cr);
      for ( int tt=0; tt< pop; tt++) {
        fill (balls.get(tt).col);
        text( "(" + hr( balls.get(tt).genome ) + ")", 10, 48 + 14 * tt);
      }
      //text for genome (hr/cr);

      // If no ball, last ball is done, simulate next one.
      // If all balls done, simulate next generation.
      if ( countDone == pop ) { // balls.size() == 0 ) {
        while ( balls.size () > 0 ) balls.remove(0); 
        //cur_sub++;
        // If all balls in this generation simulated, create next generation.
        //if ( cur_sub == gen_size ) {
        countDone = 0; // cur_sub = 0;
        //println( "Making next generation..." );


        // Always keep the best one, and toss the worse.
        arrayCopy( str_a, str_b ); //copy for scrambling later
        int bestIndex = 0;
        int worstIndex = 0;
        for ( int j = 0; j < pop; j++ ) {
          if ( scores[bestIndex] > scores[j] ) {
            bestIndex = j;
          } //getting best score for index

          if ( scores[worstIndex] < scores[j] ) {
            worstIndex = j;
          }
          //getting worst score for index
        }  

        str_b[worstIndex] = str_b[bestIndex]; // Replace the worst with a copy of the best for breeding.

        // Scramble.
        for ( int j = 0; j < pop; j++ ) {
          str_a[j] = scramble( j, str_b[int(random(pop))], str_b[int(random(pop))] ); //scrambles initial a(stored in b) and replaces old a with new values
        }
        str_a[0] = str_b[bestIndex]; // Always keep the best.
        //Add all the balls to simulate.

        for ( int j = 0; j < pop; j++ ) {
          balls.add( new Ball( j, str_a[j] ) );
        }

        gen_num++; //increment generation counter

        //}
      }

      globalBall.render(); // draw best as red(see coloring above in beginning of draw)


      for ( int i=0; i < balls.size(); i++ ) balls.get(i).move(); //init move function for balls

      //begin goal init

      stroke(255);
      fill(255, 140);
      ellipse(gcorX, gcorY, diameter, diameter);

      //end goal init
    } // end draw

    String scramble( int which, String a, String b ) { // begin switching random values with substring r as the intermediate
      String r;
      int pos = int(random(min(a.length(), b.length())));
      r = a.substring(0, pos) + b.substring(pos, b.length());

      // Mutation! //change or add a random value between position 0 and length of r
      if ( random(1) < .5 ) {
        pos = int(random(r.length()));
        if ( random(1) < .5 ) { 
          String mut = "" + char( '0' + int( random(4) ) );
          //println( "New Ball #" + which + ": Mutation! Adding a char: '" + mut + "' at position " + pos );
          r = r.substring(0, pos) + mut + r.substring(pos, r.length());
        } else {
          if ( r.length() > 0 ) {
            pos = int(random(r.length()-1));
            //println( "New Ball #" + which + ": Mutation! Losing the char at position " + pos );
            r = r.substring(0, pos) + r.substring(pos+1, r.length());
          }
        }
      }
      return( r ); //return the value to change the initial strings, reset w/change basically
    }

    //begin goal changing 
    void mousePressed() {
      if (mouseButton == LEFT) {
        gcorX = random(width);
        gcorY = random(height);
        bscore = dist(0, 0, width, height);
        gen_num=0;
      }
    }
    //end goal changing


    class Ball {
      float x, y, z, v; //position related
      color col; //color of balls
      int current_inst;//current instruction
      int inst_time;//time between instructions
      int[] instructions; //array of instructions
      boolean dis = false; //disable ball
      int num; //uuuhhhh... i forget
      String genome; //GENOME
      int checknum; //check number for genome val to ensure smooth transition

      boolean inst_a, inst_b, inst_c; //instructions

      Ball(int cknum, String input) {
        //x = width/2;
        //y = height-20;
        x = width/2;
        y = height/2;
        if (cknum == -1) {  
          x = -20;
          y = -20;
        }
        z =0;
        v = 0;

        col=color(random(40, 255), random(40, 255), random(40, 255), 200);
        inst_a = inst_b = inst_c = false; 
        checknum = cknum; //making sure the numbers are the same to avoid glitches
        current_inst = 0;
        checkString( input );
        inst_time = millis() + timeIter;
        dis = false; //not disabled
        genome = input; //read input (parse/checkstring) and input it into the genome
      }

      void checkString(String in) {    //parsing function
        instructions = new int[in.length()];
        for ( int j = 0; j < in.length(); j++ ) {
          instructions[j] = int( in.charAt(j) - '0' );
        }
        //reads each character in the input string and puts it into instructions to be enacted
      }

      //begin scoreReport- uses an array to store the scores of the generation so that a best and a worst can be calculated

      void scoreRep() {
        scores[checknum] = dist(x, y, gcorX, gcorY) + genome.length(); //uses the distance between the translated ball(at x, y) and the goal at the gcords. //also, just added a penalty for length of genome, to try and promote a more direct route
        if ( scores[checknum] < bscore ) { // if the score is not the best score, change the scores to be recalculated by the next generation
          bscore = scores[checknum];
          globalBall.x = x;
          globalBall.y = y;
          globalBall.z = z;
          globalBall.col = col;
          globalBall.genome = genome; //checks the best aspects and records the genome, moves the values for position
        }
        countDone++;
        //if ( scores[checknum] < 20 ) {
        //  gcorX = random(width);
        //  gcorY = random(height);
        //}
        //balls.remove(0);
      }

      void move() {
        if ( millis() > inst_time && !dis) { // if the millisecond function (internal clock) is greater than the instance time and not disabled, 
          inst_time = millis() + timeIter; //time between things
          if ( current_inst < instructions.length ) { //for every instruction in the instruction array 
            int i = instructions[current_inst++]; //i is used to represent the value of the instruction at the current instruction value
            switch( i ) { //switches between actions for instruction. For example, if the value "i" is 2, it would enact instruction case 2. it reads and enacts on the instructions based on their value and reads them in sequence based on their position in the array
            case 0:
              // Do nothing.
              break;
            case 1:
              inst_a = !inst_a;
              break;
            case 2:
              inst_b = !inst_b;
              break;
            case 3:
              inst_c = !inst_c;
              break;
            default:
              //println( "Bum instruction: " + i )//instruction does not exist
              break;
            }
          } else { // Terminate run.
            inst_a=inst_b=inst_c=false;
            v=0;
            dis = true; // disable is true, end run, set v to 0, set instructions to false, and statify the scoreReport
            scoreRep();
          }
        }
        // movement systems follow.
        z+=(inst_a?-.1:0)+(inst_b?.1:0); //conditional operator/ternary operator. I dont want to explain it here, it is a shorthand. Go here for more: http://stackoverflow.com/questions/798545/what-is-the-java-operator-called-and-what-does-it-do
        v+=(inst_c?.1:0); //conditional
        //x=(x+width+v*cos(z))%width;
        //y=(y+height+v*sin(z))%height;
        x=x+v*cos(z); //math for x based on angle of movement (v is basically serving as a hypotenuse here)
        y=y+v*sin(z); //math for y based on angle of movement (v is basically serving as a hypotenuse here)
        v*=.99;
        render(); //calls a display function
      }
      void render() {
        pushMatrix(); //layer 4
        translate(x, y); //move the thing by the x and y calculated in move
        rotate(z); //rotate by z
        noStroke();
        fill(col); //fill with color 

        if ( checknum==-1) fill(255, 0, 0); //if it is the best ball, color it red
        ellipseMode(CENTER); //put measurements in center
        ellipse(0, 0, 25, 25); //ball size
        stroke(0);//black edge

        noFill(); //triangle no fill
        triangle(-6, -5, 8, 0, -6, 5); //  triangle(-5, -8.66, 0, 10, 5, -8.66);
        popMatrix();
      }
    }

    class Point {

      float xpos;
      float  ypos;


      Point(float x, float y) {
        xpos = x;
        ypos = y;
      }

      void display() {
        pushMatrix();
        fill(255, 0, 0);
        strokeWeight(6);
        point(xpos, ypos);
        popMatrix();
      }
    }

Answers

  • Which of the 388 lines is the exception on?

  • Apologies. line 140. You might have to run the code to test it.

  • edited August 2016 Answer ✓

    We have to pass at least 2 arguments or 1 array w/ at least 2 elements to min():
    https://Processing.org/reference/min_.html

    It doesn't make much sense otherwise.
    Although returning the only passed value rather than null woulda been better. :-@

  • edited August 2016

    The array "scores" should always have more than two values :/. What do you mean by " returning the only passed value rather than null woulda been better." ?- and thanks for the response as well.

  • Answer ✓

    That's easy enough to check yourself though, just add a line of debug before the call, printing scores, or the length of scores.

    GOTO loop is suggesting better behaviour for the max() method. You can easily write a replacement that does this and checks the input for null.

  • Are there any other ways I can graph the minimum of the scores against the generation number?

Sign In or Register to comment.