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