AI Path Finder

This commit is contained in:
Fándly Gergő 2022-05-07 15:08:13 +03:00
commit 06f13c4745
7 changed files with 404 additions and 0 deletions

50
AIPathFinder.pde Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}
}