sakuya/node_modules/neataptic/docs/js/articles/agar.ioai/player.js
2022-04-10 00:37:53 +08:00

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;
},
};