整理桌面
This commit is contained in:
99
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/field.js
generated
vendored
Normal file
99
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/field.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/* Global vars */
|
||||
var players = [];
|
||||
var walker = new Walker();
|
||||
var iteration = 0;
|
||||
var highestScore = 0;
|
||||
|
||||
/** Setup the canvas */
|
||||
function setup(){
|
||||
var canvas = createCanvas(WIDTH, HEIGHT);
|
||||
canvas.parent('field');
|
||||
initNeat();
|
||||
|
||||
// Do some initial mutation
|
||||
if(!USE_TRAINED_POP){
|
||||
for(var i = 0; i < 1; i++) neat.mutate();
|
||||
}
|
||||
|
||||
startEvaluation();
|
||||
}
|
||||
|
||||
function draw(){
|
||||
clear();
|
||||
squareGrid();
|
||||
|
||||
// Check if evaluation is done
|
||||
if(iteration == ITERATIONS){
|
||||
endEvaluation();
|
||||
iteration = 0;
|
||||
}
|
||||
|
||||
// Update and visualise players
|
||||
for(var i = players.length - 1; i >= 0; i--){
|
||||
var player = players[i];
|
||||
|
||||
// Some players are eaten during the iteration
|
||||
player.update();
|
||||
player.show();
|
||||
}
|
||||
|
||||
walker.update();
|
||||
walker.show();
|
||||
|
||||
iteration++;
|
||||
}
|
||||
|
||||
/** Draw a square grid with grey lines */
|
||||
function squareGrid(){
|
||||
stroke(204, 204, 204, 160);
|
||||
strokeWeight(1);
|
||||
fill(255);
|
||||
for(var x = 0; x < WIDTH/40; x++){
|
||||
line(x * 40, 0, x * 40, HEIGHT);
|
||||
}
|
||||
for(var y = 0; y < HEIGHT/40; y++){
|
||||
line(0, y * 40, WIDTH, y * 40);
|
||||
}
|
||||
noStroke();
|
||||
}
|
||||
|
||||
/** Calculate distance between two points */
|
||||
function distance(x1, y1, x2, y2){
|
||||
var dx = x1 - x2;
|
||||
var dy = y1 - y2;
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/** Get a relative color between red and green */
|
||||
var activationColor = function(value, max){
|
||||
var power = 1 - Math.min(value/max, 1);
|
||||
var color = [255, 255, 0]
|
||||
|
||||
if(power < 0.5){
|
||||
color[0] = 2 * power * 255;
|
||||
} else {
|
||||
color[1] = (1.0 - 2 * (power - 0.5)) * 255;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/** Get the angle from one point to another */
|
||||
function angleToPoint(x1, y1, x2, y2){
|
||||
d = distance(x1, y1, x2, y2);
|
||||
dx = (x2-x1) / d;
|
||||
dy = (y2-y1) / d;
|
||||
|
||||
a = Math.acos(dx);
|
||||
a = dy < 0 ? 2 * Math.PI - a : a;
|
||||
return a;
|
||||
}
|
||||
|
||||
/** Set the walker to a new location */
|
||||
function mouseClicked(){
|
||||
if(mouseX >= 0 && mouseX <= WIDTH && mouseY >= 0 && mouseY <= HEIGHT){
|
||||
walker.x = mouseX;
|
||||
walker.y = mouseY;
|
||||
}
|
||||
}
|
158
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/graph.js
generated
vendored
Normal file
158
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/graph.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
var colorTable = [
|
||||
'#2124FF', // input
|
||||
'#FF2718', // output
|
||||
'#1F22C1', // logistic sigmoid
|
||||
'#EE8A2A', // tanh
|
||||
'#B17516', // identity
|
||||
'#B1B0AA', // hlim
|
||||
'#2CB11F', // relu
|
||||
'#C5B12C', // softsign
|
||||
'#E685E7', // sinusoid
|
||||
'#257580', // gaussian
|
||||
'#B0484B', // softplus
|
||||
'#4CB148', // bent_identity
|
||||
'#000000' // GATE
|
||||
];
|
||||
|
||||
var NODE_RADIUS = 7;
|
||||
var REPEL_FORCE = 10;
|
||||
var LINK_DISTANCE = 100;
|
||||
|
||||
var drawGraph = function(graph, panel, activation) {
|
||||
var d3cola = cola.d3adaptor()
|
||||
.avoidOverlaps(true)
|
||||
.size([$('.best').width(), $('.best').height()]);
|
||||
|
||||
var svg = d3.select(panel);
|
||||
|
||||
d3.selectAll(panel + "> *").remove();
|
||||
|
||||
// define arrow markers for graph links
|
||||
svg.append('svg:defs').append('svg:marker')
|
||||
.attr('id', 'end-arrow')
|
||||
.attr('viewBox', '0 -5 10 10')
|
||||
.attr('refX', 6)
|
||||
.attr('markerWidth', 5)
|
||||
.attr('markerHeight', 5)
|
||||
.attr('orient', 'auto')
|
||||
.append('svg:path')
|
||||
.attr('d', 'M0,-5L10,0L0,5')
|
||||
.attr('fill', '#000');
|
||||
|
||||
graph.nodes.forEach(function (v) { v.height = v.width = 2 * NODE_RADIUS; });
|
||||
|
||||
d3cola
|
||||
.nodes(graph.nodes)
|
||||
.links(graph.links)
|
||||
.symmetricDiffLinkLengths(REPEL_FORCE)
|
||||
.linkDistance(LINK_DISTANCE)
|
||||
.start(10, 15, 20);
|
||||
|
||||
var path = svg.selectAll(".link")
|
||||
.data(graph.links)
|
||||
.enter().append('svg:path')
|
||||
.attr('class', 'link')
|
||||
.style("stroke-width", function (d) {
|
||||
if(activation){
|
||||
return 1.5;
|
||||
} else {
|
||||
return 1.5 + Math.sqrt(d.weight * 5);
|
||||
}
|
||||
})
|
||||
.style("stroke", function (d) {
|
||||
if(activation){
|
||||
return activationColor(d.source.activation * d.weight, graph.main.maxActivation * graph.main.maxWeight);
|
||||
} else if(d.gate){
|
||||
if(d.source.activation){
|
||||
return activationColor(d.source.activation, graph.main.maxActivation);
|
||||
} else{
|
||||
return 'rgb(255,0,0)';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var node = svg.selectAll(".node")
|
||||
.data(graph.nodes)
|
||||
.enter().append("circle")
|
||||
.attr("class", "node")
|
||||
.attr("r", function(d) { return NODE_RADIUS; })
|
||||
.style("fill", function (d) {
|
||||
if(activation){
|
||||
return activationColor(d.activation, graph.main.maxActivation);
|
||||
} else {
|
||||
return colorTable[d.type];
|
||||
}
|
||||
|
||||
})
|
||||
.call(d3cola.drag);
|
||||
|
||||
node.append("title")
|
||||
.text(function (d) { return d.id; });
|
||||
|
||||
var label = svg.selectAll(".label")
|
||||
.data(graph.nodes)
|
||||
.enter().append("text")
|
||||
.attr("class", "label")
|
||||
.text(function (d) { return '(' + d.index + ') ' + d.name; })
|
||||
.call(d3cola.drag)
|
||||
|
||||
d3cola.on("tick", function () {
|
||||
// draw directed edges with proper padding from node centers
|
||||
path.attr('d', function (d) {
|
||||
var deltaX = d.target.x - d.source.x,
|
||||
deltaY = d.target.y - d.source.y,
|
||||
dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
|
||||
normX = deltaX / dist,
|
||||
normY = deltaY / dist;
|
||||
|
||||
if(isNaN(normX)) normX = 0;
|
||||
if(isNaN(normY)) normY = 0;
|
||||
|
||||
sourcePadding = NODE_RADIUS,
|
||||
targetPadding = NODE_RADIUS + 2,
|
||||
sourceX = d.source.x + (sourcePadding * normX),
|
||||
sourceY = d.source.y + (sourcePadding * normY),
|
||||
targetX = d.target.x - (targetPadding * normX),
|
||||
targetY = d.target.y - (targetPadding * normY);
|
||||
|
||||
// Defaults for normal edge.
|
||||
drx = 0,
|
||||
dry = 0,
|
||||
xRotation = 0, // degrees
|
||||
largeArc = 0, // 1 or 0
|
||||
sweep = 1; // 1 or 0
|
||||
|
||||
// Self edge.
|
||||
if (d.source.x === d.target.x && d.source.y === d.target.y) {
|
||||
drx = dist;
|
||||
dry = dist;
|
||||
// Fiddle with this angle to get loop oriented.
|
||||
xRotation = -45;
|
||||
|
||||
// Needs to be 1.
|
||||
largeArc = 1;
|
||||
|
||||
// Change sweep to change orientation of loop.
|
||||
//sweep = 0;
|
||||
|
||||
// Make drx and dry different to get an ellipse
|
||||
// instead of a circle.
|
||||
drx = 20;
|
||||
dry = 20;
|
||||
|
||||
// For whatever reason the arc collapses to a point if the beginning
|
||||
// and ending points of the arc are the same, so kludge it.
|
||||
targetX = targetX + 1;
|
||||
targetY = targetY + 1;
|
||||
}
|
||||
return 'M' + sourceX + ',' + sourceY + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + targetX + ',' + targetY;
|
||||
});
|
||||
|
||||
node.attr("cx", function (d) { return d.x; })
|
||||
.attr("cy", function (d) { return d.y; })
|
||||
|
||||
label
|
||||
.attr("x", function (d) { return d.x + 10; })
|
||||
.attr("y", function (d) { return d.y - 10; });
|
||||
});
|
||||
};
|
37
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/import.js
generated
vendored
Normal file
37
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/import.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
var scripts = [
|
||||
{ type: 'script', url: "https://cdn.rawgit.com/wagenaartje/neataptic/a7610e38/dist/neataptic.js"},
|
||||
{ type: 'script', url: "https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/p5.js"},
|
||||
{ type: 'script', url: "../../js/articles/target-seekingai/main.js"},
|
||||
{ type: 'script', url: "../../js/articles/target-seekingai/population.js"},
|
||||
{ type: 'script', url: "../../js/articles/target-seekingai/player.js"},
|
||||
{ type: 'script', url: "../../js/articles/target-seekingai/walker.js"},
|
||||
{ type: 'script', url: "../../js/articles/target-seekingai/field.js"}
|
||||
];
|
||||
|
||||
/** https://stackoverflow.com/questions/33330636/load-javascript-dynamically-and-sequentially **/
|
||||
function require(list) {
|
||||
function loadScript(link) {
|
||||
return new Promise(function(fulfill, reject) {
|
||||
if(link.type == 'script'){
|
||||
var script = document.createElement("script");
|
||||
script.addEventListener("load", fulfill);
|
||||
script.src = link.url;
|
||||
document.head.appendChild(script);
|
||||
} else if(link.type == 'css'){
|
||||
var stylesheet = document.createElement('link');
|
||||
stylesheet.rel = 'stylesheet';
|
||||
stylesheet.type = 'text/css';
|
||||
stylesheet.href = link.url;
|
||||
stylesheet.media = "screen,print";
|
||||
document.head.appendChild(stylesheet);
|
||||
}
|
||||
});
|
||||
}
|
||||
loadScript(list.shift()).then(function() {
|
||||
if (list.length > 0) {
|
||||
require(list);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
require(scripts);
|
108
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/main.js
generated
vendored
Normal file
108
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/main.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/** Rename vars */
|
||||
var Neat = neataptic.Neat;
|
||||
var Methods = neataptic.Methods;
|
||||
var Config = neataptic.Config;
|
||||
var Architect = neataptic.Architect;
|
||||
|
||||
/** Turn off warnings */
|
||||
Config.warnings = false;
|
||||
|
||||
/** Settings */
|
||||
var WIDTH = $('#field').width();
|
||||
var HEIGHT = 500;
|
||||
var MAX_SPEED = 5;
|
||||
var START_X = WIDTH/2;
|
||||
var START_Y = HEIGHT/2;
|
||||
var SCORE_RADIUS = 100;
|
||||
|
||||
// GA settings
|
||||
var PLAYER_AMOUNT = Math.round(2.3e-4 * WIDTH * HEIGHT);
|
||||
var ITERATIONS = 10e6; // should be ~250 for real use
|
||||
var MUTATION_RATE = 0.3;
|
||||
var ELITISM = Math.round(0.1 * PLAYER_AMOUNT);
|
||||
|
||||
// Trained population
|
||||
var USE_TRAINED_POP = true;
|
||||
|
||||
/** Global vars */
|
||||
var neat;
|
||||
|
||||
/** Construct the genetic algorithm */
|
||||
function initNeat(){
|
||||
neat = new Neat(
|
||||
6, 1,
|
||||
null,
|
||||
{
|
||||
mutation: [
|
||||
Methods.Mutation.ADD_NODE,
|
||||
Methods.Mutation.SUB_NODE,
|
||||
Methods.Mutation.ADD_CONN,
|
||||
Methods.Mutation.SUB_CONN,
|
||||
Methods.Mutation.MOD_WEIGHT,
|
||||
Methods.Mutation.MOD_BIAS,
|
||||
Methods.Mutation.MOD_ACTIVATION,
|
||||
Methods.Mutation.ADD_GATE,
|
||||
Methods.Mutation.SUB_GATE,
|
||||
Methods.Mutation.ADD_SELF_CONN,
|
||||
Methods.Mutation.SUB_SELF_CONN,
|
||||
Methods.Mutation.ADD_BACK_CONN,
|
||||
Methods.Mutation.SUB_BACK_CONN
|
||||
],
|
||||
popsize: PLAYER_AMOUNT,
|
||||
mutationRate: MUTATION_RATE,
|
||||
elitism: ELITISM
|
||||
}
|
||||
);
|
||||
|
||||
if(USE_TRAINED_POP){
|
||||
neat.population = population;
|
||||
}
|
||||
}
|
||||
|
||||
/** Start the evaluation of the current generation */
|
||||
function startEvaluation(){
|
||||
players = [];
|
||||
highestScore = 0;
|
||||
|
||||
for(var genome in neat.population){
|
||||
genome = neat.population[genome];
|
||||
new Player(genome);
|
||||
}
|
||||
|
||||
walker.reset();
|
||||
}
|
||||
|
||||
/** End the evaluation of the current generation */
|
||||
function endEvaluation(){
|
||||
console.log('Generation:', neat.generation, '- average score:', Math.round(neat.getAverage()));
|
||||
console.log('Fittest score:', Math.round(neat.getFittest().score));
|
||||
|
||||
// Networks shouldn't get too big
|
||||
for(var genome in neat.population){
|
||||
genome = neat.population[genome];
|
||||
genome.score -= genome.nodes.length * SCORE_RADIUS / 10;
|
||||
}
|
||||
|
||||
// Sort the population by score
|
||||
neat.sort();
|
||||
|
||||
// Init new pop
|
||||
var newPopulation = [];
|
||||
|
||||
// Elitism
|
||||
for(var i = 0; i < neat.elitism; i++){
|
||||
newPopulation.push(neat.population[i]);
|
||||
}
|
||||
|
||||
// Breed the next individuals
|
||||
for(var i = 0; i < neat.popsize - neat.elitism; i++){
|
||||
newPopulation.push(neat.getOffspring());
|
||||
}
|
||||
|
||||
// Replace the old population with the new population
|
||||
neat.population = newPopulation;
|
||||
neat.mutate();
|
||||
|
||||
neat.generation++;
|
||||
startEvaluation();
|
||||
}
|
91
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/player.js
generated
vendored
Normal file
91
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/player.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
function Player(genome){
|
||||
this.x = START_X;
|
||||
this.y = START_Y;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.r = 6;
|
||||
|
||||
this.brain = genome;
|
||||
this.brain.score = 0;
|
||||
|
||||
players.push(this);
|
||||
}
|
||||
|
||||
Player.prototype = {
|
||||
/** Update the stats */
|
||||
update: function(){
|
||||
var input = this.detect();
|
||||
var output = this.brain.activate(input);
|
||||
|
||||
var moveangle = output[0] * 2 * PI;
|
||||
|
||||
// Calculate next position
|
||||
this.ax = Math.cos(moveangle);
|
||||
this.ay = Math.sin(moveangle);
|
||||
this.vx += this.ax;
|
||||
this.vy += this.ay;
|
||||
|
||||
// Limit speeds to maximum speed
|
||||
this.vx = this.vx > MAX_SPEED ? MAX_SPEED : this.vx < -MAX_SPEED ? -MAX_SPEED : this.vx;
|
||||
this.vy = this.vy > MAX_SPEED ? MAX_SPEED : this.vy < -MAX_SPEED ? -MAX_SPEED : this.vy;
|
||||
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// Limit position to width and height
|
||||
this.x = this.x >= WIDTH ? WIDTH : this.x <= 0 ? 0 : this.x;
|
||||
this.y = this.y >= HEIGHT ? HEIGHT : this.y <= 0 ? 0 : this.y;
|
||||
|
||||
if(this.x == 0 || this.x == WIDTH) this.vx = -this.vx;
|
||||
if(this.y == 0 || this.y == HEIGHT) this.vy = -this.vy;
|
||||
|
||||
this.score();
|
||||
},
|
||||
|
||||
/** Calculate fitness of this players genome **/
|
||||
score: function(){
|
||||
var dist = distance(this.x, this.y, walker.x, walker.y);
|
||||
if(!isNaN(dist) && dist < SCORE_RADIUS){
|
||||
this.brain.score += SCORE_RADIUS - dist;
|
||||
}
|
||||
|
||||
// Replace highest score to visualise
|
||||
highestScore = this.brain.score > highestScore ? this.brain.score : highestScore;
|
||||
},
|
||||
|
||||
/** Display the player on the field, parts borrowed from the CodingTrain */
|
||||
show: function(){
|
||||
// Draw a triangle rotated in the direction of velocity
|
||||
var angle = angleToPoint(this.x, this.y, this.x + this.vx, this.y + this.vy) + HALF_PI;
|
||||
var color = activationColor(this.brain.score, highestScore);
|
||||
|
||||
push();
|
||||
translate(this.x, this.y);
|
||||
rotate(angle);
|
||||
|
||||
fill(color);
|
||||
beginShape();
|
||||
vertex(0, -this.r * 2);
|
||||
vertex(-this.r, this.r * 2);
|
||||
vertex(this.r, this.r * 2);
|
||||
endShape(CLOSE);
|
||||
|
||||
pop();
|
||||
},
|
||||
|
||||
/** Detect and normalize inputs */
|
||||
detect: function(){
|
||||
var dist = Math.sqrt(this.x, this.y, walker.x, walker.y) / Math.sqrt(WIDTH**2 + HEIGHT**2);
|
||||
var targetAngle = angleToPoint(this.x, this.y, walker.x, walker.y) / TWO_PI;
|
||||
var vx = (this.vx + MAX_SPEED) / MAX_SPEED;
|
||||
var vy = (this.vy + MAX_SPEED) / MAX_SPEED;
|
||||
var tvx = (walker.vx + MAX_SPEED) / MAX_SPEED;
|
||||
var tvy = (walker.vy + MAX_SPEED) / MAX_SPEED;
|
||||
|
||||
// NaN checking
|
||||
targetAngle = isNaN(targetAngle) ? 0 : targetAngle;
|
||||
dist = isNaN(dist) ? 0 : dist;
|
||||
|
||||
return [vx, vy, tvx, tvy, targetAngle, dist];
|
||||
},
|
||||
};
|
50723
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/population.js
generated
vendored
Normal file
50723
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/population.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
68
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/walker.js
generated
vendored
Normal file
68
node_modules/neataptic/mkdocs/theme/js/articles/target-seekingai/walker.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
function Walker(){
|
||||
this.x = START_X;
|
||||
this.y = START_Y;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
|
||||
this.r = 10;
|
||||
|
||||
this.angle = Math.random() * Math.PI * 2;
|
||||
}
|
||||
|
||||
Walker.prototype = {
|
||||
/** Update the stats */
|
||||
update: function(){
|
||||
if(Math.random() > 0.5){
|
||||
this.angle += Math.random()* 2 -1;
|
||||
}
|
||||
|
||||
// Calculate next position
|
||||
this.ax = Math.cos(this.angle);
|
||||
this.ay = Math.sin(this.angle);
|
||||
this.vx += this.ax;
|
||||
this.vy += this.ay;
|
||||
|
||||
// Limit speeds to maximum speed
|
||||
this.vx = this.vx > MAX_SPEED/2 ? MAX_SPEED/2 : this.vx < -MAX_SPEED/2 ? -MAX_SPEED/2 : this.vx;
|
||||
this.vy = this.vy > MAX_SPEED/2 ? MAX_SPEED/2 : this.vy < -MAX_SPEED/2 ? -MAX_SPEED/2 : this.vy;
|
||||
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// Limit position to width and height
|
||||
this.x = this.x >= WIDTH ? WIDTH : this.x <= 0 ? 0 : this.x;
|
||||
this.y = this.y >= HEIGHT ? HEIGHT : this.y <= 0 ? 0 : this.y;
|
||||
|
||||
if(this.x == 0 || this.x == WIDTH){
|
||||
this.vx = -this.vx;
|
||||
this.angle += PI;
|
||||
}
|
||||
if(this.y == 0 || this.y == HEIGHT){
|
||||
this.vy = -this.vy;
|
||||
this.angle += PI;
|
||||
}
|
||||
},
|
||||
|
||||
reset: function(){
|
||||
this.x = START_X;
|
||||
this.y = START_Y;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
|
||||
this.angle = Math.random() * Math.PI * 2;
|
||||
},
|
||||
|
||||
|
||||
/** Display the walker on the field */
|
||||
show: function(){
|
||||
fill(0);
|
||||
ellipse(this.x, this.y, this.r*2);
|
||||
|
||||
// Score radius
|
||||
noFill();
|
||||
stroke('lightgreen');
|
||||
strokeWeight(2);
|
||||
ellipse(this.x, this.y, SCORE_RADIUS*2);
|
||||
noStroke();
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user