AI Path Finder
This commit is contained in:
commit
06f13c4745
50
AIPathFinder.pde
Normal file
50
AIPathFinder.pde
Normal file
@ -0,0 +1,50 @@
|
||||
PVector goal;
|
||||
Obstacles obstacles;
|
||||
Genetic AI;
|
||||
|
||||
void setup(){
|
||||
size(800, 800);
|
||||
frameRate(100000);
|
||||
goal=new PVector(400, 10);
|
||||
obstacles=new Obstacles();
|
||||
AI=new Genetic(true);
|
||||
|
||||
obstacles.add(new Obstacle(300, 580, 800, 600));
|
||||
obstacles.add(new Obstacle(350, 30, 450, 50));
|
||||
obstacles.add(new Obstacle(600, 10, 620, 300));
|
||||
obstacles.add(new Obstacle(300, 650, 320, 800));
|
||||
obstacles.add(new Obstacle(100, 400, 800, 420));
|
||||
obstacles.add(new Obstacle(0, 520, 200, 540));
|
||||
}
|
||||
|
||||
void draw(){
|
||||
background(255);
|
||||
|
||||
//draw goal
|
||||
fill(255, 0, 0);
|
||||
ellipse(goal.x, goal.y, 10, 10);
|
||||
|
||||
//draw obstacles
|
||||
obstacles.display();
|
||||
|
||||
//run genetica
|
||||
if(!AI.isDone()){
|
||||
AI.update();
|
||||
}
|
||||
else{
|
||||
AI.nextGeneration();
|
||||
}
|
||||
|
||||
//print infos
|
||||
fill(0);
|
||||
text("Generation: "+AI.generation, 10, 10);
|
||||
text("Minimal steps: "+AI.lessSteps, 10, 20);
|
||||
text("Minimal distance: "+AI.lessDistance, 10, 30);
|
||||
text("Max fitness: "+AI.maxFitness, 10, 40);
|
||||
text("Goal reached: "+AI.goalReached, 10, 50);
|
||||
text("Steps/s: "+frameRate, 10, 60);
|
||||
}
|
||||
|
||||
void mouseClicked(){
|
||||
AI.toggleDisplay();
|
||||
}
|
47
Brain.pde
Normal file
47
Brain.pde
Normal file
@ -0,0 +1,47 @@
|
||||
class Brain{
|
||||
PVector[] directions;
|
||||
int step=0;
|
||||
|
||||
//Constructor
|
||||
Brain(int size){
|
||||
directions=new PVector[size];
|
||||
|
||||
//random fill
|
||||
for(int i=0; i<directions.length; i++){
|
||||
float randomAngle=random(2*PI);
|
||||
directions[i]=PVector.fromAngle(randomAngle);
|
||||
}
|
||||
}
|
||||
|
||||
//Clone brain
|
||||
Brain clone(){
|
||||
Brain cpy=new Brain(directions.length);
|
||||
for(int i=0; i<directions.length; i++){
|
||||
cpy.directions[i]=directions[i].copy();
|
||||
}
|
||||
|
||||
return cpy;
|
||||
}
|
||||
|
||||
//Mutate brain
|
||||
void mutate(){
|
||||
float mutateRate=0.01;
|
||||
for(int i=0; i<directions.length; i++){
|
||||
if(random(1)<mutateRate){
|
||||
float randomAngle=random(2*PI);
|
||||
directions[i]=PVector.fromAngle(randomAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get next step
|
||||
PVector next(){
|
||||
if(step<directions.length){
|
||||
step++;
|
||||
return directions[step-1];
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
99
Dot.pde
Normal file
99
Dot.pde
Normal file
@ -0,0 +1,99 @@
|
||||
class Dot{
|
||||
PVector pos;
|
||||
PVector vel;
|
||||
boolean dead=false;
|
||||
boolean deadByObstacle=false;
|
||||
boolean reachedGoal=false;
|
||||
boolean champ=false;
|
||||
float fitness;
|
||||
Brain brain;
|
||||
|
||||
//constructor
|
||||
Dot(){
|
||||
brain=new Brain(1000);
|
||||
|
||||
//start position (bottom center)
|
||||
pos=new PVector(width/2, height-10);
|
||||
vel=new PVector(0, 0);
|
||||
}
|
||||
|
||||
//do movement
|
||||
void move(){
|
||||
PVector accl=brain.next(); //get next acceleration
|
||||
if(accl==null){
|
||||
dead=true; //we're dead. No solution
|
||||
}
|
||||
else{
|
||||
vel.add(accl); //add acceleration
|
||||
vel.limit(20); //limit speed
|
||||
pos.add(vel); //update position
|
||||
}
|
||||
|
||||
//detect if exiting area or is intersecting
|
||||
if(pos.x <= 2 || pos.y <= 2 || pos.x >= width-2 || pos.y >= height-2){
|
||||
dead=true;
|
||||
}
|
||||
//check if intersects obstacle
|
||||
else if(obstacles.isIntersecting(this)){
|
||||
dead=true;
|
||||
deadByObstacle=true;
|
||||
}
|
||||
|
||||
//detect if reached goal
|
||||
if(dist(pos.x, pos.y, goal.x, goal.y) < 2){
|
||||
reachedGoal=true;
|
||||
}
|
||||
}
|
||||
|
||||
//update point
|
||||
boolean update(){
|
||||
if(!dead && !reachedGoal){
|
||||
move();
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate fitness
|
||||
float calcFitness(){
|
||||
if(reachedGoal){
|
||||
fitness=10000.0/(brain.step*brain.step);
|
||||
}
|
||||
else if(!deadByObstacle){
|
||||
float distToGoal=dist(pos.x, pos.y, goal.x, goal.y);
|
||||
fitness=2.0/(distToGoal*distToGoal);
|
||||
}
|
||||
else{
|
||||
float distToGoal=dist(pos.x, pos.y, goal.x, goal.y);
|
||||
fitness=1.0/(distToGoal*distToGoal);
|
||||
}
|
||||
|
||||
return fitness;
|
||||
}
|
||||
|
||||
//get children
|
||||
Dot getChild(){
|
||||
Dot child=new Dot();
|
||||
child.brain=brain.clone();
|
||||
return child;
|
||||
}
|
||||
|
||||
//is alive
|
||||
boolean alive(){
|
||||
return !(dead || reachedGoal);
|
||||
}
|
||||
|
||||
//display dot
|
||||
void display(){
|
||||
if(champ){
|
||||
fill(0, 255, 0);
|
||||
ellipse(pos.x, pos.y, 8, 8);
|
||||
}
|
||||
else{
|
||||
fill(0);
|
||||
ellipse(pos.x, pos.y, 4, 4);
|
||||
}
|
||||
}
|
||||
}
|
55
Genetic.pde
Normal file
55
Genetic.pde
Normal file
@ -0,0 +1,55 @@
|
||||
class Genetic{
|
||||
int generation=1;
|
||||
boolean display=false;
|
||||
Population population;
|
||||
int lessSteps=0;
|
||||
float lessDistance=0;
|
||||
float maxFitness=0;
|
||||
boolean goalReached=false;
|
||||
|
||||
//constructor
|
||||
Genetic(boolean disp){
|
||||
population=new Population(500);
|
||||
display=disp;
|
||||
}
|
||||
|
||||
//check if finished with current population
|
||||
boolean isDone(){
|
||||
return !population.hasDotAlive();
|
||||
}
|
||||
|
||||
void toggleDisplay(){
|
||||
display=!display;
|
||||
}
|
||||
|
||||
//update population
|
||||
void update(){
|
||||
if(!isDone()){
|
||||
population.update();
|
||||
if(display){
|
||||
population.display();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//generate next population
|
||||
void nextGeneration(){
|
||||
//claculate the fitness of points
|
||||
population.calcFitness();
|
||||
|
||||
//get best datas
|
||||
lessSteps=population.getChampion().brain.step;
|
||||
lessDistance=dist(population.getChampion().pos.x, population.getChampion().pos.y, goal.x, goal.y);
|
||||
maxFitness=population.getChampion().fitness;
|
||||
goalReached=population.getChampion().reachedGoal;
|
||||
|
||||
//naturally select best dots
|
||||
population.naturalSelection();
|
||||
|
||||
//mutate the next generation
|
||||
population.mutate();
|
||||
|
||||
//increment counter
|
||||
generation++;
|
||||
}
|
||||
}
|
26
Obstacle.pde
Normal file
26
Obstacle.pde
Normal file
@ -0,0 +1,26 @@
|
||||
class Obstacle{
|
||||
PVector c1;
|
||||
PVector c2;
|
||||
|
||||
//constructor
|
||||
Obstacle(int x1, int y1, int x2, int y2){
|
||||
c1=new PVector(x1, y1);
|
||||
c2=new PVector(x2, y2);
|
||||
}
|
||||
|
||||
//check intersection with a dot
|
||||
boolean isIntersecting(Dot dot){
|
||||
if(dot.pos.x >= c1.x && dot.pos.x <= c2.x && dot.pos.y >= c1.y && dot.pos.y <= c2.y){
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//display it
|
||||
void display(){
|
||||
fill(255, 255, 0);
|
||||
rect(c1.x, c1.y, c2.x-c1.x, c2.y-c1.y);
|
||||
}
|
||||
}
|
30
Obstacles.pde
Normal file
30
Obstacles.pde
Normal file
@ -0,0 +1,30 @@
|
||||
class Obstacles{
|
||||
ArrayList<Obstacle> obs;
|
||||
|
||||
//constructor
|
||||
Obstacles(){
|
||||
obs=new ArrayList<Obstacle>();
|
||||
}
|
||||
|
||||
//add obstacle
|
||||
void add(Obstacle o){
|
||||
obs.add(o);
|
||||
}
|
||||
|
||||
//check intersection
|
||||
boolean isIntersecting(Dot dot){
|
||||
for(int i=0; i<obs.size(); i++){
|
||||
if(obs.get(i).isIntersecting(dot)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//display all
|
||||
void display(){
|
||||
for(int i=0; i<obs.size(); i++){
|
||||
obs.get(i).display();
|
||||
}
|
||||
}
|
||||
}
|
97
Population.pde
Normal file
97
Population.pde
Normal file
@ -0,0 +1,97 @@
|
||||
class Population{
|
||||
int champIndex=0;
|
||||
float sumOfFitness;
|
||||
Dot[] dots;
|
||||
|
||||
//constructor
|
||||
Population(int size){
|
||||
dots=new Dot[size];
|
||||
for(int i=0; i<dots.length; i++){
|
||||
dots[i]=new Dot();
|
||||
}
|
||||
}
|
||||
|
||||
//update
|
||||
void update(){
|
||||
for(int i=0; i<dots.length; i++){
|
||||
dots[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasDotAlive(){
|
||||
for(int i=0; i<dots.length; i++){
|
||||
if(dots[i].alive()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//calculate fitnesses
|
||||
void calcFitness(){
|
||||
sumOfFitness=0;
|
||||
for(int i=0; i<dots.length; i++){
|
||||
sumOfFitness+=dots[i].calcFitness();
|
||||
}
|
||||
}
|
||||
|
||||
//get best dot as parent
|
||||
Dot getChampion(){
|
||||
float big=0;
|
||||
for(int i=0; i<dots.length; i++){
|
||||
if(dots[i].fitness>big){
|
||||
big=dots[i].fitness;
|
||||
champIndex=i;
|
||||
}
|
||||
}
|
||||
|
||||
return dots[champIndex];
|
||||
}
|
||||
|
||||
//get a perfect parent based on their fitness
|
||||
Dot getAParent(){
|
||||
float rand=random(sumOfFitness);
|
||||
|
||||
float sum=0;
|
||||
for(int i=0; i<dots.length; i++){
|
||||
sum+=dots[i].fitness;
|
||||
if(sum>rand){
|
||||
return dots[i];
|
||||
}
|
||||
}
|
||||
|
||||
//shouldn't get here
|
||||
return null;
|
||||
}
|
||||
|
||||
//natural selection
|
||||
void naturalSelection(){
|
||||
Dot[] nextGen=new Dot[dots.length];
|
||||
|
||||
calcFitness();
|
||||
|
||||
nextGen[0]=getChampion().getChild();
|
||||
nextGen[0].champ=true;
|
||||
|
||||
for(int i=1; i<dots.length; i++){
|
||||
nextGen[i]=getAParent().getChild();
|
||||
}
|
||||
|
||||
dots=nextGen.clone();
|
||||
}
|
||||
|
||||
//mutate the next generation
|
||||
void mutate(){
|
||||
for(int i=1; i<dots.length; i++){
|
||||
dots[i].brain.mutate();
|
||||
}
|
||||
}
|
||||
|
||||
//display
|
||||
void display(){
|
||||
for(int i=1; i<dots.length; i++){
|
||||
dots[i].display();
|
||||
}
|
||||
dots[0].display();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user