Repository: hardmaru/neuralslimevolley Branch: master Commit: b35201a61bf0 Files: 20 Total size: 1.1 MB Directory structure: gitextract_1prx5pti/ ├── Readme.md ├── index.html ├── lib/ │ ├── box2d-camera.js │ ├── box2d-helper.js │ ├── box2d-html5.js │ ├── convnetjs/ │ │ ├── convnet-min.js │ │ ├── convnet.js │ │ ├── deepqlearn.js │ │ ├── ga.js │ │ ├── util.js │ │ └── vis.js │ ├── orientation-0.03.js │ ├── p5.dom.js │ ├── p5.js │ └── p5.sound.js ├── pro/ │ └── index.html ├── pro.html ├── slimevolley.js ├── slimevolley_pro.js └── useful.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: Readme.md ================================================ # Neural Slime Volleyball HTML5-JS Slime Volleyball clone. AI agent is a trained recurrent neural network, trained using basic conventional neuroevolution techniques. Neural network implemented using the [convnetjs](http://cs.stanford.edu/people/karpathy/convnetjs/) library. It is very difficult to win! See my blog post at [blog.otoro.net](http://blog.otoro.net/2015/03/28/neural-slime-volleyball/) for more information, or [otoro.net](http://otoro.net/slimevolley/) to actually play the game. ## online demo - [Neural Slime Volleyball](http://otoro.net/slimevolley) ## Training If you wish to experiment with adding extra AI modules, or just to see how the learning works, please edit both pro.html and the slimevolley_pro.js. They are the versions I will use in the future. Inside pro.html, you can switch on/off the training mode by changing trainingVersion = true/false If it is running on training version, the most capable neural net, in the form of a JSON array is dumped to nn_gene on the screen every 50 training generations. You can copy and paste that blob back into initGeneJSON as a quoted text inside slimevolley_pro.js to incorporate back into the game, and switching training mode back to false to play with the new trained network. Have fun- ## License GNU GPL v3 ================================================ FILE: index.html ================================================
================================================
FILE: lib/box2d-camera.js
================================================
// -----------------------------------------------------------------------------
// Scale Methods
// -----------------------------------------------------------------------------
// supposed to translate (x_b2, y_b2) -> (x_pixel, y_pixel). everything else scaled by a factor of scaleFactor
var b2Camera = {
scaleFactor : 10,
x_b2: 0,
y_b2: 0,
x_pixel: 0,
y_pixel: 0
};
var gravity;
var gravityStrength = 20;
var scaleToWorld = function(a,b) {
var newv;
if (a instanceof box2d.b2Vec2) {
newv = new box2d.b2Vec2();
newv.x = (a.x-b2Camera.x_pixel)/b2Camera.scaleFactor+b2Camera.x_b2;
newv.y = (a.y-b2Camera.y_pixel)/b2Camera.scaleFactor+b2Camera.y_b2;
return newv;
} else if ("undefined"!=typeof b) {
newv = new box2d.b2Vec2();
newv.x = (a-b2Camera.x_pixel)/b2Camera.scaleFactor+b2Camera.x_b2;
newv.y = (b-b2Camera.y_pixel)/b2Camera.scaleFactor+b2Camera.y_b2;
return newv;
} else {
return a/b2Camera.scaleFactor;
}
};
var makeB2Vec2 = function(a,b) {
var newv;
newv = new box2d.b2Vec2();
newv.x = (a)/1;
newv.y = (b)/1;
return newv;
};
var scaleToPixels = function(a,b) {
var newv;
if (a instanceof box2d.b2Vec2) {
newv = new box2d.b2Vec2();
newv.x = (a.x-b2Camera.x_b2)*b2Camera.scaleFactor+b2Camera.x_pixel;
newv.y = (a.y-b2Camera.y_b2)*b2Camera.scaleFactor+b2Camera.y_pixel;
return newv;
} else if ("undefined"!=typeof b) {
newv = new box2d.b2Vec2();
newv.x = (a-b2Camera.x_b2)*b2Camera.scaleFactor+b2Camera.x_pixel;
newv.y = (b-b2Camera.y_b2)*b2Camera.scaleFactor+b2Camera.y_pixel;
return newv;
} else {
return a*b2Camera.scaleFactor;
}
};
// -----------------------------------------------------------------------------
// Create Methods
// -----------------------------------------------------------------------------
var createWorld = function() {
var worldAABB = new box2d.b2AABB();
worldAABB.lowerBound.SetXY(-this.bounds, -this.bounds);
worldAABB.upperBound.SetXY(this.bounds, this.bounds);
gravity = new box2d.b2Vec2(0,gravityStrength);
var doSleep = true;
return new box2d.b2World(gravity, doSleep);
};
// -----------------------------------------------------------------------------
// Draw Methods
// -----------------------------------------------------------------------------
var debugDraw = function(canvas, scale, world) {
var context = canvas.getContext('2d');
var j, b, f;
context.fillStyle = '#DDD';
context.fillRect(0, 0, canvas.width, canvas.height);
// Draw joints
for( j=world.m_jointList; j; j=j.m_next) {
context.lineWidth = 0.25;
context.strokeStyle = '#00F';
drawJoint(context, scale, world, j);
}
// Draw body shapes
for( b=world.m_bodyList; b; b=b.m_next) {
for( f = b.GetFixtureList(); f!==null; f=f.GetNext()) {
context.lineWidth = 0.5;
context.strokeStyle = '#F00';
drawShape(context, scale, world, b, f);
}
}
};
var drawJoint = function(context, scale, world, joint) {
context.save();
context.scale(scale,scale);
context.lineWidth /= scale;
var b1 = joint.m_bodyA;
var b2 = joint.m_bodyB;
var x1 = b1.GetPosition();
var x2 = b2.GetPosition();
var p1 = joint.GetAnchorA();
var p2 = joint.GetAnchorB();
context.beginPath();
switch (joint.m_type) {
case box2d.b2Joint.e_distanceJoint:
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
break;
default: {
if (b1 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
}
else if (b2 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x1.x, x1.y);
}
else {
context.moveTo(x1.x, x1.y);
context.lineTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
context.lineTo(p2.x, p2.y);
}
} break;
}
context.closePath();
context.stroke();
context.restore();
};
var drawShape = function(context, scale, world, body, fixture) {
context.save();
context.scale(scale,scale);
var bPos = body.GetPosition();
context.translate(bPos.x, bPos.y);
context.rotate(body.GetAngleRadians());
context.beginPath();
context.lineWidth /= scale;
var shape = fixture.m_shape;
var i;
switch(shape.m_type) {
case box2d.b2ShapeType.e_circleShape: {
var r = shape.m_radius;
var segments = 16.0;
var theta = 0.0;
var dtheta = 2.0 * Math.PI / segments;
context.moveTo(r, 0);
for (i = 0; i < segments; i++) {
context.lineTo(r + r * Math.cos(theta), r * Math.sin(theta));
theta += dtheta;
}
context.lineTo(r, 0);
} break;
case box2d.b2ShapeType.e_polygonShape:
case box2d.b2ShapeType.e_chainShape: {
var vertices = shape.m_vertices;
var vertexCount = shape.m_count;
if (!vertexCount) return;
context.moveTo(vertices[0].x, vertices[0].y);
for (i = 0; i < vertexCount; i++)
context.lineTo(vertices[i].x, vertices[i].y);
} break;
}
context.closePath();
context.stroke();
context.restore();
};
================================================
FILE: lib/box2d-helper.js
================================================
// -----------------------------------------------------------------------------
// Scale Methods
// -----------------------------------------------------------------------------
var scaleFactor;
var scaleToWorld = function(a,b) {
var newv;
if (a instanceof box2d.b2Vec2) {
newv = new box2d.b2Vec2();
newv.x = (a.x)/scaleFactor;
newv.y = (a.y)/scaleFactor;
return newv;
} else if ("undefined"!=typeof b) {
newv = new box2d.b2Vec2();
newv.x = (a)/scaleFactor;
newv.y = (b)/scaleFactor;
return newv;
} else {
return a/scaleFactor;
}
};
var scaleToPixels = function(a,b) {
var newv;
if (a instanceof box2d.b2Vec2) {
newv = new box2d.b2Vec2();
newv.x = a.x*scaleFactor;
newv.y = a.y*scaleFactor;
return newv;
} else if ("undefined"!=typeof b) {
newv = new box2d.b2Vec2();
newv.x = a*scaleFactor;
newv.y = b*scaleFactor;
return newv;
} else {
return a*scaleFactor;
}
};
// -----------------------------------------------------------------------------
// Create Methods
// -----------------------------------------------------------------------------
var createWorld = function() {
var worldAABB = new box2d.b2AABB();
worldAABB.lowerBound.SetXY(-this.bounds, -this.bounds);
worldAABB.upperBound.SetXY(this.bounds, this.bounds);
var gravity = new box2d.b2Vec2(0,20);
var doSleep = true;
scaleFactor = 10;
return new box2d.b2World(gravity, doSleep);
};
// -----------------------------------------------------------------------------
// Draw Methods
// -----------------------------------------------------------------------------
var debugDraw = function(canvas, scale, world) {
var context = canvas.getContext('2d');
var j, b, f;
context.fillStyle = '#DDD';
context.fillRect(0, 0, canvas.width, canvas.height);
// Draw joints
for( j=world.m_jointList; j; j=j.m_next) {
context.lineWidth = 0.25;
context.strokeStyle = '#00F';
drawJoint(context, scale, world, j);
}
// Draw body shapes
for( b=world.m_bodyList; b; b=b.m_next) {
for( f = b.GetFixtureList(); f!==null; f=f.GetNext()) {
context.lineWidth = 0.5;
context.strokeStyle = '#F00';
drawShape(context, scale, world, b, f);
}
}
};
var drawJoint = function(context, scale, world, joint) {
context.save();
context.scale(scale,scale);
context.lineWidth /= scale;
var b1 = joint.m_bodyA;
var b2 = joint.m_bodyB;
var x1 = b1.GetPosition();
var x2 = b2.GetPosition();
var p1 = joint.GetAnchorA();
var p2 = joint.GetAnchorB();
context.beginPath();
switch (joint.m_type) {
case box2d.b2Joint.e_distanceJoint:
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
break;
default: {
if (b1 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
}
else if (b2 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x1.x, x1.y);
}
else {
context.moveTo(x1.x, x1.y);
context.lineTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
context.lineTo(p2.x, p2.y);
}
} break;
}
context.closePath();
context.stroke();
context.restore();
};
var drawShape = function(context, scale, world, body, fixture) {
context.save();
context.scale(scale,scale);
var bPos = body.GetPosition();
context.translate(bPos.x, bPos.y);
context.rotate(body.GetAngleRadians());
context.beginPath();
context.lineWidth /= scale;
var shape = fixture.m_shape;
var i;
switch(shape.m_type) {
case box2d.b2ShapeType.e_circleShape: {
var r = shape.m_radius;
var segments = 16.0;
var theta = 0.0;
var dtheta = 2.0 * Math.PI / segments;
context.moveTo(r, 0);
for (i = 0; i < segments; i++) {
context.lineTo(r + r * Math.cos(theta), r * Math.sin(theta));
theta += dtheta;
}
context.lineTo(r, 0);
} break;
case box2d.b2ShapeType.e_polygonShape:
case box2d.b2ShapeType.e_chainShape: {
var vertices = shape.m_vertices;
var vertexCount = shape.m_count;
if (!vertexCount) return;
context.moveTo(vertices[0].x, vertices[0].y);
for (i = 0; i < vertexCount; i++)
context.lineTo(vertices[i].x, vertices[i].y);
} break;
}
context.closePath();
context.stroke();
context.restore();
};
================================================
FILE: lib/box2d-html5.js
================================================
var COMPILED=!0,goog=goog||{};goog.global=this;goog.DEBUG=!1;goog.LOCALE="en";goog.TRUSTED_SITE=!0;goog.provide=function(a){if(!COMPILED){if(goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');delete goog.implicitNamespaces_[a];for(var b=a;(b=b.substring(0,b.lastIndexOf(".")))&&!goog.getObjectByName(b);)goog.implicitNamespaces_[b]=!0}goog.exportPath_(a)};
goog.setTestOnly=function(a){if(COMPILED&&!goog.DEBUG)throw a=a||"",Error("Importing test-only code into non-debug environment"+a?": "+a:".");};COMPILED||(goog.isProvided_=function(a){return!goog.implicitNamespaces_[a]&&!!goog.getObjectByName(a)},goog.implicitNamespaces_={});goog.exportPath_=function(a,b,c){a=a.split(".");c=c||goog.global;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&goog.isDef(b)?c[e]=b:c=c[e]?c[e]:c[e]={}};
goog.getObjectByName=function(a,b){for(var c=a.split("."),e=b||goog.global,d;d=c.shift();)if(goog.isDefAndNotNull(e[d]))e=e[d];else return null;return e};goog.globalize=function(a,b){var c=b||goog.global,e;for(e in a)c[e]=a[e]};
goog.addDependency=function(a,b,c){if(!COMPILED){var e;a=a.replace(/\\/g,"/");for(var d=goog.dependencies_,f=0;e=b[f];f++)d.nameToPath[e]=a,a in d.pathToNames||(d.pathToNames[a]={}),d.pathToNames[a][e]=!0;for(e=0;b=c[e];e++)a in d.requires||(d.requires[a]={}),d.requires[a][b]=!0}};goog.ENABLE_DEBUG_LOADER=!0;
goog.require=function(a){if(!COMPILED&&!goog.isProvided_(a)){if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b){goog.included_[b]=!0;goog.writeScripts_();return}}a="goog.require could not find: "+a;goog.global.console&&goog.global.console.error(a);throw Error(a);}};goog.basePath="";goog.nullFunction=function(){};goog.identityFunction=function(a,b){return a};goog.abstractMethod=function(){throw Error("unimplemented abstract method");};
goog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];
!COMPILED&&goog.ENABLE_DEBUG_LOADER&&(goog.included_={},goog.dependencies_={pathToNames:{},nameToPath:{},requires:{},visited:{},written:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return"undefined"!=typeof a&&"write"in a},goog.findBasePath_=function(){if(goog.global.CLOSURE_BASE_PATH)goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName("script"),b=a.length-1;0<=b;--b){var c=a[b].src,e=c.lastIndexOf("?"),e=
-1==e?c.length:e;if("base.js"==c.substr(e-7,7)){goog.basePath=c.substr(0,e-7);break}}},goog.importScript_=function(a){var b=goog.global.CLOSURE_IMPORT_SCRIPT||goog.writeScriptTag_;!goog.dependencies_.written[a]&&b(a)&&(goog.dependencies_.written[a]=!0)},goog.writeScriptTag_=function(a){if(goog.inHtmlDocument_()){var b=goog.global.document;if("complete"==b.readyState){if(/\bdeps.js$/.test(a))return!1;throw Error('Cannot write "'+a+'" after document load');}b.write('