181 lines
5.4 KiB
JavaScript
181 lines
5.4 KiB
JavaScript
function Player(genome){
|
|
this.x = Math.floor(Math.random() * WIDTH);
|
|
this.y = Math.floor(Math.random() * HEIGHT);
|
|
this.vx = 0;
|
|
this.vy = 0;
|
|
|
|
this.brain = genome;
|
|
this.brain.score = 0;
|
|
|
|
this.area = MIN_AREA;
|
|
this.visualarea = this.area;
|
|
|
|
players.push(this);
|
|
}
|
|
|
|
Player.prototype = {
|
|
/** Update the stats */
|
|
update: function(){
|
|
if(this.area > MAX_AREA) this.area = MAX_AREA;
|
|
if(this.area < MIN_AREA) this.area = MIN_AREA;
|
|
|
|
var input = this.detect();
|
|
var output = this.brain.activate(input);
|
|
|
|
var moveangle = output[0] * 2 * PI;
|
|
var movespeed = output[1] > 1 ? 1 : output[1] < 0 ? 0 : output[1];
|
|
|
|
this.vx = movespeed * Math.cos(moveangle) * SPEED;
|
|
this.vy = movespeed * Math.sin(moveangle) * SPEED;
|
|
|
|
// Large blobs move slower
|
|
this.vx *= Math.max(1 - (this.area / MAX_AREA), MIN_SPEED / SPEED);
|
|
this.vy *= Math.max(1 - (this.area / MAX_AREA), MIN_SPEED / SPEED);
|
|
|
|
this.x += this.vx;
|
|
this.y += this.vy;
|
|
|
|
// Limit position to width and height
|
|
this.x = this.x >= WIDTH ? this.x % WIDTH : this.x <= 0 ? this.x + WIDTH : this.x;
|
|
this.y = this.y >= HEIGHT ? this.y % HEIGHT : this.y <= 0 ? this.y + HEIGHT : this.y;
|
|
|
|
this.area *= DECREASE_SIZE;
|
|
|
|
// Replace highest score to visualise
|
|
this.brain.score = this.area;
|
|
highestScore = this.brain.score > highestScore ? this.brain.score : highestScore;
|
|
},
|
|
|
|
/** Restart from new position */
|
|
restart: function(){
|
|
this.x = Math.floor(Math.random() * WIDTH);
|
|
this.y = Math.floor(Math.random() * HEIGHT);
|
|
this.vx = 0;
|
|
this.vy = 0;
|
|
this.area = MIN_AREA;
|
|
this.visualarea = this.area;
|
|
},
|
|
|
|
/** Display the player on the field */
|
|
show: function(){
|
|
this.visualarea = lerp(this.visualarea, this.area, 0.2);
|
|
var radius = Math.sqrt(this.visualarea / PI);
|
|
var color = activationColor(this.brain.score, highestScore);
|
|
|
|
fill(color);
|
|
ellipse(this.x, this.y, radius);
|
|
},
|
|
|
|
/** Visualies the detection of the brain */
|
|
showDetection: function(detected){
|
|
noFill();
|
|
for(var object in detected){
|
|
object = detected[object];
|
|
|
|
if(object != undefined){
|
|
stroke(object instanceof Player ? 'red' : 'lightgreen');
|
|
line(this.x, this.y, object.x, object.y);
|
|
}
|
|
}
|
|
|
|
var color = activationColor(this.brain.score, highestScore);
|
|
stroke(color);
|
|
ellipse(this.x, this.y, DETECTION_RADIUS*2);
|
|
noStroke();
|
|
},
|
|
|
|
/* Checks if object can be eaten */
|
|
eat: function(object){
|
|
var dist = distance(this.x, this.y, object.x, object.y);
|
|
|
|
var radius1 = Math.sqrt(this.area / PI);
|
|
var radius2 = Math.sqrt(object.area / PI);
|
|
if(dist < (radius1 + radius2) / 2 && this.area > object.area * RELATIVE_SIZE){
|
|
this.area += object.area;
|
|
object.restart();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/** Detect other genomes around */
|
|
detect: function(){
|
|
// Detect nearest objects
|
|
var nearestPlayers = [];
|
|
var playerDistances = Array.apply(null, Array(PLAYER_DETECTION)).map(Number.prototype.valueOf, Infinity);
|
|
|
|
for(var player in players){
|
|
player = players[player];
|
|
if(player == this || this.eat(player)) continue;
|
|
|
|
var dist = distance(this.x, this.y, player.x, player.y);
|
|
if(dist < DETECTION_RADIUS){
|
|
// Check if closer than any other object
|
|
var maxNearestDistance = Math.max.apply(null, playerDistances);
|
|
var index = playerDistances.indexOf(maxNearestDistance);
|
|
|
|
if(dist < maxNearestDistance){
|
|
playerDistances[index] = dist;
|
|
nearestPlayers[index] = player;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect nearest foods
|
|
var nearestFoods = [];
|
|
var foodDistances = Array.apply(null, Array(FOOD_DETECTION)).map(Number.prototype.valueOf, Infinity);
|
|
|
|
for(var food in foods){
|
|
food = foods[food];
|
|
if(this.eat(food)) continue;
|
|
|
|
var dist = distance(this.x, this.y, food.x, food.y);
|
|
if(dist < DETECTION_RADIUS){
|
|
// Check if closer than any other object
|
|
var maxNearestDistance = Math.max.apply(null, foodDistances);
|
|
var index = foodDistances.indexOf(maxNearestDistance);
|
|
|
|
if(dist < maxNearestDistance){
|
|
foodDistances[index] = dist;
|
|
nearestFoods[index] = food;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create and normalize input
|
|
var output = [this.area / MAX_AREA];
|
|
|
|
for(var i = 0; i < PLAYER_DETECTION; i++){
|
|
var player = nearestPlayers[i];
|
|
var dist = playerDistances[i];
|
|
|
|
if(player == undefined){
|
|
output = output.concat([0, 0, 0]);
|
|
} else {
|
|
output.push(angleToPoint(this.x, this.y, player.x, player.y) / (2 * PI));
|
|
output.push(dist / DETECTION_RADIUS);
|
|
output.push(player.area / MAX_AREA);
|
|
}
|
|
}
|
|
|
|
for(var i = 0; i < FOOD_DETECTION; i++){
|
|
var food = nearestFoods[i];
|
|
var dist = foodDistances[i];
|
|
|
|
if(food == undefined){
|
|
output = output.concat([0, 0]);
|
|
} else {
|
|
output.push(angleToPoint(this.x, this.y, food.x, food.y) / (2 * PI));
|
|
output.push(dist / DETECTION_RADIUS);
|
|
}
|
|
}
|
|
|
|
if(distance(mouseX, mouseY, this.x, this.y) < Math.sqrt(this.visualarea / PI)){
|
|
var detected = nearestPlayers.concat(nearestFoods);
|
|
this.showDetection(detected);
|
|
}
|
|
|
|
return output;
|
|
},
|
|
};
|