We are about to switch to a new forum software. Until then we have removed the registration on this forum.
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.
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. :-@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.
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?