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