-
Notifications
You must be signed in to change notification settings - Fork 0
/
Population.js
129 lines (115 loc) · 3.56 KB
/
Population.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* @constructor
* @param {p2.World} world
* @param {number} robotCount
* @param {number} eliteClones
* @param {number} startX
* @param {number} startY
*/
function Population(world, robotCount, eliteClones,
startX, startY) {
this.world = world;
this.startX = startX;
this.startY = startY;
this.eliteClones = eliteClones;
this.robotCount = robotCount;
this.baseMutationRate = 0.02;
this.mutationChance = 0.4;
this.mutationRate = 0.1;
this.extremeMutationRate = 0.6;
this.extremeMutationChance = 0.05;
this.crossoverRate = 0.2;
this.selfCrossoverRate = 0.1;
this.robots = [];
}
/**
* Synthesize a new population from scratch
* @param {RobotGenome} baseGenome
* @return {Array<Robot>}
*/
Population.prototype.synthesize = function(baseGenome) {
this.robots = [];
for (var i = 0; i < this.robotCount; i++) {
var robotGenome = this.mutateRobotGenome(baseGenome);
this.robots.push(
new Robot(this.world, this.startX, this.startY, robotGenome));
}
return this.robots;
};
/**
* Spawn a new population of robots based on the old one
* @param {Array<Robot>} deadRobots
* @return {Array<Robot>}
*/
Population.prototype.spawn = function(deadRobots) {
deadRobots = deadRobots.sort(function(robotA, robotB) {
return robotB.maxX - robotA.maxX;
});
var eliteRobots = deadRobots.slice(0, this.eliteClones);
function getRandomElite() {
if (eliteRobots.length > 0) {
return eliteRobots[Math.floor(Math.random() *
eliteRobots.length)];
}
for (let i = 0; i < deadRobots.length; i++) {
if (Math.random() < 0.5) {
return deadRobots[i];
}
}
return deadRobots[0];
}
this.robots = [];
for (var i = 0; i < this.eliteClones; i++) {
var robotGenome = eliteRobots[i].genome;
var newRobot = new Robot(this.world, this.startX, this.startY, robotGenome);
this.robots.push(newRobot);
}
for (var i = this.eliteClones; i < this.robotCount; i++) {
var randomElite = getRandomElite();
var robotGenome = this.mutateRobotGenome(randomElite.genome);
if (Math.random() < this.selfCrossoverRate) {
if (Math.random() < 0.5) {
robotGenome.frontGenome = robotGenome.rearGenome;
} else {
robotGenome.rearGenome = robotGenome.frontGenome;
}
}
if (Math.random() < this.crossoverRate) {
var otherRandomElite = getRandomElite();
var otherRobotGenome = this.mutateRobotGenome(otherRandomElite.genome);
if (Math.random() < 0.5) {
robotGenome.frontGenome = otherRobotGenome.frontGenome;
} else {
robotGenome.rearGenome = otherRobotGenome.rearGenome;
}
}
var newRobot = new Robot(this.world, this.startX, this.startY, robotGenome);
this.robots.push(newRobot);
}
return this.robots;
};
/**
* @param {RobotGenome} robotGenome original genome
* @return {RobotGenome} Single-parent mutated genome
*/
Population.prototype.mutateRobotGenome = function(robotGenome) {
var newGenome = robotGenome.mutateWith(function(value) {
return this.mutate(value);
}.bind(this));
return newGenome;
};
/**
* @param {number} value - Original value
* @return {number} Mutated value
*/
Population.prototype.mutate = function(value) {
var mutationRate = this.baseMutationRate;
if (Math.random() < this.mutationChance) {
mutationRate = this.mutationRate;
}
if (Math.random() < this.extremeMutationChance) {
mutationRate = this.extremeMutationRate;
}
var mutationFactor = 1 + (Math.random() - 0.5) * 2 * mutationRate;
return value * mutationFactor;
};