xxxxxxxxxx
165
class World {
constructor(num) {
this.food = new Food(num*2);
this.bloops = [];
for (let i = 0; i < num; i++) {
let l = createVector(random(width), random(height));
let dna = new DNA();
this.bloops.push(new Bloop(l, dna, random(500, 900)));
}
this.best = this.bloops[0];
this.CROSSOVER_RATE = 0.9;
this.popFitness = [];
this.generation = 0;
}
born(x, y) {
let l = createVector(x, y);
let dna = new DNA();
this.bloops.push(new Bloop(l, dna, random(500, 900)));
}
run() {
this.generation += 1;
this.display();
this.food.run();
for (let i = this.bloops.length - 1; i >= 0; i--) {
let b = this.bloops[i];
b.run(this.best);
b.eat(this.food);
if (b.dead()) {
this.bloops.splice(i, 1);
this.food.add(b.position);
}
}
this.getPopulationFitness();
this.selection();
this.bloops = this.oneCutCrossover(this.bloops);
}
getPopulationFitness() {
let score = [];
for (let i = 0; i < this.bloops.length; i++) {
score[i] = this.fitness(this.bloops[i]);
if (score[i] > this.fitness(this.best)) {
this.best = this.bloops[i];
}
}
this.popFitness = score;
}
fitness(bloop){
let score = bloop.health;
for(let f of this.food.food){
const distance = p5.Vector.dist(f, bloop.position);
if(distance < bloop.visionRadius){
score += 1/distance;
}
}
return score;
}
generateParents(crossoverRate) {
const parents = [];
const bloopsCount = this.bloops.length;
for (let i = 0; i < bloopsCount; i++) {
const currentBloop = this.bloops[i];
const eligibleBloops = [];
for (let j = i + 1; j < bloopsCount; j++) {
const otherBloop = this.bloops[j];
const distance = dist(currentBloop.x, currentBloop.y, otherBloop.x, otherBloop.y);
if (distance <= bloop.visionRadius) {
eligibleBloops.push({
bloop: otherBloop,
distance,
fitness: otherBloop.fitness,
geneVariety: otherBloop.getGeneVariety()
});
}
}
if (eligibleBloops.length > 0) {
eligibleBloops.sort((a, b) => {
const distanceScoreA = 1 / a.distance;
const distanceScoreB = 1 / b.distance;
const fitnessScoreA = a.fitness * 3;
const fitnessScoreB = b.fitness * 3;
const geneVarietyScoreA = a.geneVariety * 2;
const geneVarietyScoreB = b.geneVariety * 2;
const totalScoreA = distanceScoreA + fitnessScoreA + geneVarietyScoreA;
const totalScoreB = distanceScoreB + fitnessScoreB + geneVarietyScoreB;
return totalScoreB - totalScoreA; // Ordenar em ordem decrescente
});
const r = random(0, 1);
if (r < crossoverRate) {
parents.push(eligibleBloops[0].bloop);
}
}
}
return parents;
}
oneCutCrossover(bloops) {
const parents = this.generateParents(this.CROSSOVER_RATE);
for (let i = 0; i < parents.length; i++) {
const cut = Math.floor(random(1, 3));
for (let k = cut; k < bloops[parents[i]].length; k++) {
bloops[parents[i]][k] = bloops[parents[(i + 1) % parents.length]][k];
}
}
return bloops;
}
selection() {
this.bloops = this.rouletteSelection(this.popFitness);
//this.bloops = this.bloops.sort((a,b) => this.fitness(a) - this.fitness(b));
}
rouletteSelection(fit) {
const total = fit.reduce((acc, val) => acc + val, 0);
const normalizedFitness = fit.map((fit) => fit / total);
const cumulativeFitness = [];
let prev = 0;
for (const fit of normalizedFitness) {
prev += fit;
cumulativeFitness.push(prev);
}
const newPopulation = [];
for (let i = 0; i < this.bloops.length; i++) {
const r = random(0, 1);
for (let j = 0; j < this.bloops.length; j++) {
if (j === 0) {
if (0 <= r && r < cumulativeFitness[j]) {
newPopulation.push(this.bloops[j]);
}
} else {
if (cumulativeFitness[j - 1] <= r && r < cumulativeFitness[j]) {
newPopulation.push(this.bloops[j]);
}
}
}
}
return newPopulation;
}
display(){
fill(0, 0, 255);
text(`Generation #${this.generation}`, 2, 22);
textSize(20);
}
}